区块结构示意图:
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),都与此思路相似。
到此本文就结束了!但愿本文能给你们带来帮助,谢谢!
坚持不懈地努力才能成为大神!