Fabric區塊哈希值計算

Fabric區塊哈希值計算

1. 區塊哈希

1.1 區塊哈希介紹

區塊結構示意圖:
區塊結構示意圖javascript

具體的區塊結構介紹請參見此篇博文html

在區塊頭中包含有三個字段,即區塊序號number、前一個區塊(頭)哈希previous_hash、當前區塊的數據哈希data_hash(數據哈希即爲當前區塊中全部交易的哈希值)。java

當前區塊哈希即爲當前區塊頭哈希,二者在fabric中是一致的,因此區塊頭中的previous_hash字段即爲前一個區塊哈希。要計算區塊哈希,計算區塊頭哈希便可。python

1.2 區塊哈希值計算方式

通過查閱資料得知,當前區塊哈希的計算方式爲區塊頭的三個字段(即numberprevious_hashdata_hash)首先使用ASN.1中的DER編碼規則進行編碼(在下文中介紹),而後進行SHA256哈希值計算便可得出。json

2. ASN.1編碼標準

ASN.1(Abstract Syntax Notation One)即抽象語法標記,它是一種ISO/ITU-T聯合標準,描述了對數據進行表示、編碼、傳輸和解碼的數據格式。後端

簡而言之,ASN.1 就是一種數據編碼的標準形式。數據結構

2.1 編碼規則

ASN.1自己只定義了表示信息的抽象句法,但沒有限定其編碼的方法。各類ASN.1編碼規則提供了由ASN.1描述其抽象句法的數據的值的傳送語法(具體表達)。標準的ASN.1編碼規則有基本編碼規則(BER,Basic Encoding Rules)、規範編碼規則(CER,Canonical Encoding Rules)、惟一編碼規則(DER,Distinguished Encoding Rules)、壓縮編碼規則(PER,Packed Encoding Rules)和XML編碼規則(XER,XML Encoding Rules)(來源於維基百科)。函數

2.2 DER編碼規則

DER是ASN.1衆多編碼方案中的一個。DER編碼規則是在BER的基礎上增長了必定的約束髮展而來的,它使用定長格式實現數據的編碼。ui

3. 基於python的具體實現

首先使用ASN.1編寫一個特定的數據結構(主要爲了搞清楚須要編碼的對象及其內部的字段類型):this

BlockHashModule DEFINITIONS ::=
BEGIN
BlockHash ::= SEQUENCE {
    number  INTEGER, // 區塊號,類型爲integer
    previoushash  OCTET STRING, // 前一個區塊頭哈希,類型爲octet string
    datahash OCTET STRING // 數據哈希,類型爲octet string
}
END // octet string是字節串,即用八位組表示字節序列

python具體實現:pyasn1包在pyasn1.type中定義了ASN.1標準中的全部類型,對於結構化的類型,咱們能夠用類的方法來定義一個模板。好比下面的ASN.1結構:

from pyasn1.type import univ, namedtype
class BlockHash(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('Number', univ.Integer()),
        namedtype.NamedType('PreviousHash', univ.OctetString()),
        namedtype.NamedType('DataHash', univ.OctetString()),
    )

假設獲取的區塊頭信息以下:

header = {
	"number": "19",
	"previous_hash": "8ba6531304e7f74afae1fa7457c329f30e5d37328bd4e855c0a7e4e49f0948b8",
	"data_hash": "fa2e496406962ff92e32ee6e2a0edbeea3eb4c27e8ac5ffc348ffab3fbd3ec62"
}

定義transform函數預先進行數據格式轉換:

def transform(header):
    return {
        "Number": int(header['number']), # 將number字段從string轉換爲int
        "PreviousHash": bytes.fromhex(header['previous_hash']), # 將previous_hash字段從十六進制字符串轉換爲bytes,只有先轉成bytes才能按照ASN.1標準再轉成八位組串,這是規定操做
        "DataHash": bytes.fromhex(header['data_hash']) # 將data_hash字段從十六進制字符串轉換爲bytes
    }
blockHeader = transform(header) # 轉換成特定的數據格式

以以前定義的BlockHash編碼標準,運用DER編碼規則進行編碼:

from pyasn1.codec.der.encoder import encode
blockHeader_DER = encode(blockHeader, asn1Spec=BlockHash()) # DER編碼

對編碼後的區塊頭進行sha256哈希運算獲得256位二進制哈希字符串(即64位十六進制哈希字符串):

from hashlib import sha256
currentBlockHash = sha256(block_DER).hexdigest()  # 求sha256
print(currentBlockHash)

所有代碼:

from hashlib import sha256
from pyasn1.codec.der.encoder import encode
from pyasn1.type import univ, namedtype
class BlockHash(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('Number', univ.Integer()),
        namedtype.NamedType('PreviousHash', univ.OctetString()),
        namedtype.NamedType('DataHash', univ.OctetString()),
    )

def transform(header):
    return {
        "Number": int(header['number']), # 將number字段從string轉換爲int
        "PreviousHash": bytes.fromhex(header['previous_hash']), # 將previous_hash字段從十六進制字符串轉換爲bytes,只有先轉成bytes才能按照ASN.1標準再轉成八位組串,這是規定操做
        "DataHash": bytes.fromhex(header['data_hash']) # 將data_hash字段從十六進制字符串轉換爲bytes
    }
header = {
	"number": "19",
	"previous_hash": "8ba6531304e7f74afae1fa7457c329f30e5d37328bd4e855c0a7e4e49f0948b8",
	"data_hash": "fa2e496406962ff92e32ee6e2a0edbeea3eb4c27e8ac5ffc348ffab3fbd3ec62"
}
blockHeader = transform(header) # 轉換成特定的數據格式
blockHeader_DER = encode(blockHeader, asn1Spec=BlockHash()) # DER編碼
currentBlockHash = sha256(blockHeader_DER).hexdigest()  # 求sha256
print(currentBlockHash)

4. 基於Java的具體實現

參考博客:https://blog.csdn.net/qq_27348837/article/details/107201872

public  static String caculateCurrentBlockhash(BlockInfo blockInfo) throws IOException, IllegalAccessException, InvocationTargetException, InvalidArgumentException, InstantiationException, NoSuchMethodException, CryptoException, ClassNotFoundException {
        CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
        ByteArrayOutputStream s = new ByteArrayOutputStream();
        DERSequenceGenerator seq = new DERSequenceGenerator(s);
        seq.addObject(new ASN1Integer(blockInfo.getBlockNumber()));
        seq.addObject(new DEROctetString(blockInfo.getPreviousHash()));
        seq.addObject(new DEROctetString(blockInfo.getDataHash()));
        seq.close();
        byte[] hash = cryptoSuite.hash(s.toByteArray());
        return Hex.encodeHexString(hash);
    }

若是使用SDKUtils包能夠更加簡單計算出來,你們能夠嘗試一下。代碼以下:

String currentHash = Hex.encodeHexString(SDKUtils.calculateBlockHash(this.client,
                            blockInfo.getBlockNumber(), blockInfo.getPreviousHash(), blockInfo.getDataHash()));

5. 基於Javascript的具體實現

var sha = require('js-sha256');
var asn = require('asn1.js');
var calculateBlockHash = function(header) {
  let headerAsn = asn.define('headerAsn', function() {
    this.seq().obj(
      this.key('Number').int(),
      this.key('PreviousHash').octstr(),
     this.key('DataHash').octstr()
   );
 });

  let output = headerAsn.encode({
      Number: parseInt(header.number),
      PreviousHash: Buffer.from(header.previous_hash, 'hex'),
      DataHash: Buffer.from(header.data_hash, 'hex')
    }, 'der');
  let hash = sha.sha256(output);
  return hash;
};

header = {
	"number": "19",
	"previous_hash": "8ba6531304e7f74afae1fa7457c329f30e5d37328bd4e855c0a7e4e49f0948b8",
	"data_hash": "fa2e496406962ff92e32ee6e2a0edbeea3eb4c27e8ac5ffc348ffab3fbd3ec62"
}
var hash = calculateBlockHash(header)
console.log(hash)

結語

由於我所完成的項目是使用Python做爲後端的,因此在本文中注重介紹了Python的實現方法。可是你們也能從基於Java和Javascript的實現方法中看到他們的實現思路都是幾乎相同的,即:

  1. 首先定義Asn.1標準
  2. 用DER方式進行編碼
  3. 進行SHA256哈希運行得出最終的256位哈希值。

你們若是想要實現其餘語言的求區塊哈希的編碼(例如Go),都與此思路相似。

到此本文就結束了!但願本文能給你們帶來幫助,謝謝!

堅持不懈地努力才能成爲大神!

相關文章
相關標籤/搜索