zoukankan      html  css  js  c++  java
  • Fabric区块哈希计算

    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),都与此思路类似。

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

  • 相关阅读:
    bzoj4010 [HNOI2015]菜肴制作
    PHP--------TP中的ajax请求
    二维数组去重
    手机号138-0013-8000格式存储
    spring4-2-bean配置-1-依赖注入
    spring4-1-Spring的简单介绍
    Result Grouping / Field Collapsing-结果分组
    vim自动补全
    vim配置-程序员【转】
    服务端程序设计和实现总结 【转】
  • 原文地址:https://www.cnblogs.com/jiftle/p/14675992.html
Copyright © 2011-2022 走看看