区块链设计模式系列(三)协议分解

CITA-Cloud 目前是在协议中直接把交易和区块等数据结构固定下来的。

message BlockHeader {
    // hash of previous BlockHeader
    bytes prevhash = 1;
    uint64 timestamp = 2;
    uint64 height = 3;
    bytes transactions_root = 4;
    bytes proposer = 5;
}

message Transaction {
    uint32 version = 1;
    // 1. length is 20 bytes for evm.
    // 2. if executor is multi-vm, it will be a path.
    bytes to = 2;
    // length is less than 128
    string nonce = 3;
    uint64 quota = 4;
    uint64 valid_until_block = 5;
    bytes data = 6;
    // length is 32 bytes.
    bytes value = 7;
    // length is 32 bytes.
    bytes chain_id = 8;
}

但是最近的思考发现,其中的很多字段都是为了实现某种应用层面的协议而存在的。而且实现方式也有多种,可以根据不同情况进行选择。

比如,BlockHeader中的timestamp,就是为了实现一个分布式的时间同步协议,多个节点商议出一个全局的时间。这不仅仅是一个共识的问题,还有跟时间相关的一些特定检查。比如,当前块的时间戳必须大于上一个块,但是又不能大太多,否则很快这个时间就脱离现实时间了。

如果合约和上层应用中不需要跟现实时间这么紧密的时间戳,只需要一个粗略的逻辑时间,那么使用height就够了,就不需要timestamp字段了。

prevhash字段表达了区块间连接的拓扑结构。就像区块链设计模式系列(二)本地优先里提到的,如果是传统的链式结构,那么这个字段就只是一个hash;如果是DAG结构,那么就是一组hash

transactions_root字段用于轻节点协议,表达了块中的交易和块头是如何关联的。如果这里采用了默克尔树的组织方式,那么就可以用默克尔路径证明来判断一个交易是否在区块中。如果不需要轻节点,也可以简单的实现成交易数组的hash

交易中的字段可以参见 https://docs.citahub.com/zh-CN/cita/rpc-guide/rpc#关于签名交易
比如交易中的 nonce 字段就是为了实现应用层面的去重协议。这里也有多种实现方式可以选择,比如以太坊就选择了自增的序号,而CITA为了解决前一种方案不能并发发送交易的缺陷,改用随机nonce

因此,我们希望可以让用户来自己定义这些协议的组合以及实现方式,这样可以定制出更加贴近实际业务场景的区块链。
具体方式就是让用户来自定义核心的交易和区块等数据结构,并通过protobuf扩展增加一些标注,方便自动生成相关的代码。
技术方面的信息可以参见 https://rink1969.github.io/protobuf-ext
Demo工程:https://github.com/rink1969/proto_desc_printer