Fabric区块哈希值计算

Fabric区块哈希值计算

1. 区块哈希

1.1 区块哈希介绍

区块结构示意图:
区块结构示意图

具体的区块结构介绍请参见此篇博文

在区块头中包含有三个字段,即区块序号number、前一个区块(头)哈希previous_hash、当前区块的数据哈希data_hash(数据哈希即为当前区块中所有交易的哈希值)。

当前区块哈希即为当前区块头哈希,两者在fabric中是一致的,所以区块头中的previous_hash字段即为前一个区块哈希。要计算区块哈希,计算区块头哈希即可。

1.2 区块哈希值计算方式

经过查阅资料得知,当前区块哈希的计算方式为区块头的三个字段(即numberprevious_hashdata_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的实现方法中看到他们的实现思路都是几乎相同的,即:

  1. 首先定义Asn.1标准
  2. 用DER方式进行编码
  3. 进行SHA256哈希运算得出最终的256位哈希值。

大家如果想要实现其他语言的求区块哈希的编码(例如Go),都与此思路类似。

到此本文就结束了!希望本文能给大家带来帮助,谢谢!

坚持不懈地努力才能成为大神!

posted @ 2021-02-02 21:29  己平事  阅读(1611)  评论(2编辑  收藏  举报