aelf区块数据结构和ChainId

Block & BlockHeader & BlockBody

跟其他主流公链一样,aelf的Block结构也分为Block Header和Block Body两部分。

message Block {
    BlockHeader header = 1;
    BlockBody body = 2;
}

Block Header数据较为轻量,能够涵盖该区块主要信息,和用于验证的其他信息。

message BlockHeader {
    int32 version = 1;
    int32 chain_id = 2;
    Hash previous_block_hash = 3;
    Hash merkle_tree_root_of_transactions = 4;
    Hash merkle_tree_root_of_world_state = 5;
    bytes bloom = 6;
    int64 height = 7;
    map<string, bytes> extra_data = 8;
    google.protobuf.Timestamp time = 9;
    Hash merkle_tree_root_of_transaction_status = 10;
    bytes signer_pubkey = 9999;
    bytes signature = 10000;
}
  • version字段迄今为止从来没用过,也就是说,aelf目前的BlockHeader的version都还是0。

  • chain_id:代表该区块位于哪一条链上(aelf有一条主链和多条侧链),这里使用int类型的ChainId,详情和计算方式见下。

  • previous_block_hash:前置区块的哈希值。

  • bloom:这个区块中所有事件的布隆过滤器。

  • height:区块高度。

  • time:区块时间戳。

  • signer_pubkey:产生区块的节点的公钥。

  • signature:区块生产者对这个区块的签名。

  • extra_data:区块头中需要包含的其他用于验证的信息,例如AEDPoS共识的信息,就会放在这个map里,在同步区块的时候验证其中的共识数据是否有效。另外,系统交易的数量也会包含在其中。

  • Merkle Tree Roots:

    • merkle_tree_root_of_transactions:把该区块打包的所有交易的哈希值作为叶子结点,组成的Merkle Tree的Root。

    • merkle_tree_root_of_world_state:该区块中所有交易执行结束以后,把实际修改的State的哈希值作为叶子结点,组成的Merkle Tree的Root。

    • merkle_tree_root_of_transaction_status:把该区块打包的所有交易的Id,和这个交易的执行结果(Transaction Result中的status)合并到一起的哈希值作为叶子结点,组成的Merkle Tree的Root。与merkle_tree_root_of_transactions的不同点在于,叶子结点多了一个交易执行结果的信息,这个信息有助于做跨链验证:跨链验证不仅需要知道某个交易在另外一条链上是否存在,还需要知道存在的这个交易是否执行成功了。

    • 附上跨链验证中重新计算merkle_tree_root_of_transaction_status的叶子节点的逻辑,辅助理解:

再附一个这个Root的计算过程:

而BlockBody就比较简单了,只是把交易的Id列表放了进去。

ChainId的两种表现形式

在aelf的体系中,ChainId有两种形式。

  • 一种是string,是一个长度为4的Base58字符;

  • 另一种是int,是把Base58进行Decode以后得到bytes,再把bytes转为int32。

aelf的主链使用AELF这一Base58字符串作为ChainId,把AELF当作Base58编码格式Decode再转为Int32后,就得到了int类型的ChainId:9992731。

这个过程的代码位于ChainHelper.cs中,逻辑如下:

当然,int类型的ChainId也可以转回Base58字符串:

circle-info

aelf使用长度为4的Base58字符串作为ChainId,而长度为4的Base58字符串的取值空间为[2111, zzzz]

此时,int类型的ChainId的取值空间就为[1*58*58*58+0*58*58+0*58+0, 57*58*58*58+57*58*58+57*58+57],即:[195112, 11316496]

侧链名tDVV是怎么来的

我们是这样得到tDVV这么一个Base58字符串作为第一条侧链的ChainId的:

  1. 通过AELF的到int类型的主链的ChainId:9992731;

  2. 把上面的主链ChainId序号增加1,得到9992732;

  3. 把9992732转换为byte[],得到{0, 152, 122, 28};

  4. 去掉第一位0,在数组最后再补一位0,得到{152, 122, 28, 0};

  5. 把二进制数组转换为Int32,得到1866392,把这个值作为第一条侧链的int类型的ChainId;

  6. 把1866392转为Base58,得到tDVV。

如果要作为AELF的侧链,tDVV之后的每一条侧链的ChainId,都采取类似的方式进行计算,只不过把第2步中的增加1改成了增加n,n为AELF的侧链的序号,从1开始。

AELF的第二条侧链的ChainId就是1931928/tDVW。

相关逻辑:

Last updated