Fabric区块哈希值计算
Fabric区块哈希值计算
1. 区块哈希
1.1 区块哈希介绍
区块结构示意图:
具体的区块结构介绍请参见此篇博文。
在区块头中包含有三个字段,即区块序号
number
、前一个区块(头)哈希previous_hash
、当前区块的数据哈希data_hash
(数据哈希即为当前区块中所有交易的哈希值)。当前区块哈希即为当前区块头哈希,两者在fabric中是一致的,所以区块头中的
previous_hash
字段即为前一个区块哈希。要计算区块哈希,计算区块头哈希即可。
1.2 区块哈希值计算方式
经过查阅资料得知,当前区块哈希的计算方式为区块头的三个字段(即
number
、previous_hash
、data_hash
)首先使用ASN.1中的DER编码规则进行编码(在下文中介绍),然后进行SHA256哈希值计算即可得出。
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的基础上增加了一定的约束发展而来的,它使用定长格式实现数据的编码。
3. 基于python的具体实现
首先使用ASN.1编写一个特定的数据结构(主要为了搞清楚需要编码的对象及其内部的字段类型):
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的实现方法中看到他们的实现思路都是几乎相同的,即:
- 首先定义Asn.1标准
- 用DER方式进行编码
- 进行SHA256哈希运算得出最终的256位哈希值。
大家如果想要实现其他语言的求区块哈希的编码(例如Go),都与此思路类似。
到此本文就结束了!希望本文能给大家带来帮助,谢谢!
坚持不懈地努力才能成为大神!