區塊結構示意圖:
javascript
具體的區塊結構介紹請參見此篇博文。html
在區塊頭中包含有三個字段,即區塊序號
number
、前一個區塊(頭)哈希previous_hash
、當前區塊的數據哈希data_hash
(數據哈希即爲當前區塊中全部交易的哈希值)。java當前區塊哈希即爲當前區塊頭哈希,二者在fabric中是一致的,因此區塊頭中的
previous_hash
字段即爲前一個區塊哈希。要計算區塊哈希,計算區塊頭哈希便可。python
通過查閱資料得知,當前區塊哈希的計算方式爲區塊頭的三個字段(即
number
、previous_hash
、data_hash
)首先使用ASN.1中的DER編碼規則進行編碼(在下文中介紹),而後進行SHA256哈希值計算便可得出。json
ASN.1(Abstract Syntax Notation One)即抽象語法標記,它是一種ISO/ITU-T聯合標準,描述了對數據進行表示、編碼、傳輸和解碼的數據格式。後端
簡而言之,ASN.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)(來源於維基百科)。函數
DER是ASN.1衆多編碼方案中的一個。DER編碼規則是在BER的基礎上增長了必定的約束髮展而來的,它使用定長格式實現數據的編碼。ui
首先使用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)
參考博客: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()));
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的實現方法中看到他們的實現思路都是幾乎相同的,即:
- 首先定義Asn.1標準
- 用DER方式進行編碼
- 進行SHA256哈希運行得出最終的256位哈希值。
你們若是想要實現其餘語言的求區塊哈希的編碼(例如Go),都與此思路相似。
到此本文就結束了!但願本文能給你們帶來幫助,謝謝!
堅持不懈地努力才能成爲大神!