3. 数据结构

长安链(ChainMaker)核心数据结构皆采用protobuf3语法进行定义,以方便在不同的语言之间进行通信。长安链数据结构模型定义在pb项目下,并按功能和使用范围,分为:

  • accesscontrol 与访问控制相关的用户账户、组织角色等对象

  • api 与客户端SDK相关的对象

  • common 核心区块头、交易、结果对象

  • config 区块节点配置和网络配置等相关对象

  • consensus 与共识相关的对象,不同的共识算法会有不同的共识对象

  • discovery 用于P2P网络发现的对象

  • net 用于P2P网络广播的消息对象

  • store 进行账本和状态数据存储使用的对象

  • sync 同步数据使用的相关对象

  • txpool 交易池相关对象

ChainMaker官方提供ChainMaker协议的Golang实现,根据protobuf3数据模型定义,生成Golang代码在同级的pb-go目录下。

3.1. 核心数据模型

3.1.1. 区块

3.1.1.1. 结构示意图

3.1.1.2. 整体结构

// Block definition
message Block {
    // header of the block
    BlockHeader header = 1;

    // execution sequence of intra block transactions, generated by proposer
    DAG dag = 2;

    // transaction list in this block
    repeated Transaction txs = 3;

    // stores the voting information of the current block
    // not included in block hash value calculation
    AdditionalData additional_data = 4;
}

// block additional data
message AdditionalData {
    // extra data, with map type, excluded in hash calculation
    map<string, bytes> extra_data = 1;
}

// transaction execution sequence
// Using adjacency table storage
message DAG {
    // Neighbor node: related party transactions with reading and writing conflicts
    message Neighbor {
        repeated uint32 neighbors = 1;
    }

    // sequence number of transaction topological sort
    //the sequence number of the transaction topological sort associated with the transaction
    repeated Neighbor vertexes = 2;
}
  • Header:区块头

  • Dag:块内交易的执行依赖顺序,由Proposer生成,如果为空则表示本区块的所有交易都可以并行执行,不存在前后依赖关系

  • Txs:块内交易列表

  • AdditionalData:区块产生以后附加的数据,不参与区块的散列值计算。可用于存储当前区块的投票信息,交易过滤等,具体内容根据链上配置的内容而决定

3.1.1.3. 区块头

// header of the block
message BlockHeader {
    // block version
    uint32 block_version = 1;

    // config block or normal block or other else
    BlockType block_type = 2;

    // blockchain identifier
    string chain_id = 3;

    // block height
    uint64 block_height = 4;

    // block hash (block identifier)
    bytes block_hash = 5;

    // previous block hash
    bytes pre_block_hash = 6;

    // previous config block height, used to trace and check if chain config is valid
    uint64 pre_conf_height = 7;

    // count of transactions
    uint32 tx_count = 8;

    // merkle root of transactions
    // used to verify the existence of this transactions
    bytes tx_root = 9;

    // Save the DAG feature summary, and hash the DAG after Pb serialization
    // hash of serialized DAG
    bytes dag_hash = 10;

    // The root hash of Merkle tree generated by read_write_set_digest in the result of each transaction in the block
    // used to verify the read-write set of the block
    bytes rw_set_root = 11;

    // the time stamp of the block
    int64 block_timestamp = 12;

    // consensus parameters
    // used to store information, include in block hash calculation
    bytes consensus_args = 13;

    // proposal node identifier
    accesscontrol.Member proposer = 14;

    // signature of proposer
    bytes signature = 15;
}

// BlockType specify block pack txs type
enum BlockType {
    // Normal block, pack multi txs into one block
    NORMAL_BLOCK = 0;

    // Config block, only include 1 chain config update tx in this block
    CONFIG_BLOCK = 1;

    // Sql Contract init or upgrade block, only include 1 sql contract init or upgrade tx in this block
    CONTRACT_MGR_BLOCK = 2;

    // block.Txs[0] is a coinbase tx
    HAS_COINBASE = 4;
}
  • BlockVersion:区块版本

  • BlockType: 区块的类型,按打包的交易类型,分为普通区块、链配置区块、合约管理区块和拥有Coinbase交易的区块等。

  • ChainId:链标识,用于区分不同的链,在多子链的情况下可区分不同的子链

  • BlockHeight:区块高度,创世区块高度为0

  • BlockHash:本区块的散列值

  • PreBlockHash:上个区块的散列值

  • PreConfHeight:上一次修改链配置的区块高度,在这个高度的区块中,只存在一笔交易,为配置交易,其中保存了区块链的配置信息,包括本区块应该采用的共识算法,加密算法等

  • TxCount:交易数量

  • TxRoot:区块交易的Merkle Root

  • DagHash:当前区块Dag的散列值

  • RwSetRoot:区块读写集的Merkle Root

  • BlockTimestamp:区块的时间戳

  • ConsensusArgs:共识参数

  • Proposer:区块的生成者标识

  • Signature:区块生成者的签名

3.1.2. 交易结构

3.1.2.1. 交易结构示意图

3.1.2.2. 交易结构定义

// a transaction includes request and its result
message Transaction {
    // payload
    Payload payload = 1;

    // sender account and signature
    EndorsementEntry sender = 2;

    // endorser accounts and signatures
    repeated EndorsementEntry endorsers = 3;

    // result of the transaction
    Result result = 4;
}
  • Payload:交易的载荷数据,交易的核心信息

  • Sender:交易的发起者信息和签名信息

  • Endorsers:多签情况下,多个背书人的身份信息和其签名信息

  • Result:交易结果,由Proposer生成区块时进行计算、赋值

3.1.2.3. 交易载荷

//transaction payload
message Payload {
    // blockchain identifier
    string chain_id = 1;

    // transaction type
    TxType tx_type = 2;

    // transaction id set by sender, should be unique
    string tx_id = 3;

    // transaction timestamp, in unix timestamp format, seconds
    int64 timestamp = 4;

    // expiration timestamp in unix timestamp format
    // after that the transaction is invalid if it is not included in block yet
    int64 expiration_time = 5;

    // smart contract name
    string contract_name = 6;

    // invoke method
    string method = 7;

    // invoke parameters in k-v format
    repeated KeyValuePair parameters = 8;

    // sequence number, default is 0
    uint64 sequence = 9;

    // gas price+gas limit; fee; timeout seconds;
    bytes limit = 10;
}
// transaction type definition
enum TxType {
    // call a pre created contract, tx included in block
    INVOKE_CONTRACT = 0;

    // query a pre-created  contract, tx not included in block
    QUERY_CONTRACT = 1;

    // subscribe block info,tx info and contract info. tx not included in block
    SUBSCRIBE = 2;

    // archive/restore block, tx not included in block
    ARCHIVE = 3;
}
// a k-v pair
message KeyValuePair {
    string key = 1;
    bytes value = 2;
}
  • ChainId:链标识,表明本交易是针对哪条链的,防止一个交易在多个链中被打包

  • TxType:交易类型,目前有4种,合约调用、合约查询、订阅、归档。其中只有合约调用会被打包上链,另外几种只用于SDK与节点之间的通信协议

  • TxId:交易ID,用做该交易的全局唯一性标识

  • Timestamp:生成交易的unix时间戳,当proposer从交易池获取交易时,用来检测该交易是否超时未上链;如果超时,该交易将从交易池删除

  • ExpirationTime:交易的到期的unix时间,单位秒,不为0时,交易必须在该时间戳之前被打包上链

  • ContractName:别调用的合约名

  • Method:被调用的合约方法名

  • Parameters:调用合约方法时传入的参数列表,为Key/Value列表类型

  • Sequence:交易的顺序号,0表示该交易没有顺序要求;大于0则按交易发起人顺序递增

  • Limit:交易执行的限制,对于有Gas限制的场景,可以是GasLimit+GasPrice,对于有超时限制的场景,可以是Timeout毫秒数

3.1.2.4. 交易发送者与签名

// endorsement info, including a signer and his signature
message EndorsementEntry {
    // signer
    accesscontrol.Member signer = 1;

    // signature
    bytes signature = 2;
}
// member of blockchain
message Member {
    // organization identifier of the member
    string org_id = 1;

    // member type
    MemberType member_type = 2;

    // member identity related info bytes
    bytes member_info = 3;
}
enum MemberType {
    //X509 cert
    CERT = 0;
    //cert hash
    CERT_HASH = 1;
    //public key
    PUBLIC_KEY = 2;
    //did
    DID = 3;
}
  • Sender:交易发送者信息,由Signer和Signature组成

  • Signer为Member类型,主要包含以下信息:

    • OrgId:成员所属机构编号

    • MemberType:成员的类型,主要有:X509证书、证书哈希、公钥、DID等几种类型。

    • MemberInfo:成员的身份信息,可以是证书信息也可以是证书哈希,或者是公钥、DID等,依赖于MemberType字段

  • Signature:交易发送者对Payload的签名,具体签名算法取决与链配置和X509证书中的签名算法

3.1.2.5. 交易结果

// tx result, part of a transaction in block
message Result {
    // response code
    TxStatusCode code = 1;
    // returned data, set in smart contract
    ContractResult contract_result = 2;
    // hash of the transaction's read-write set
    bytes rw_set_hash = 3;

    string message = 4;
}

// invoke user contract method return UserContractReturnPayload
// Unmarshal from TransactResult.TxResponse.payload
message ContractResult {
    // user contract defined return code, 0-ok, >0 user define error code. for example, insufficient balance in token transfer
    uint32 code = 1;
    // user contract defined result
    bytes result = 2;
    // user contract defined result message
    string message = 3;
    // gas used by current contract(include contract call)
    uint64 gas_used = 4;
    // contract events
    repeated ContractEvent contract_event = 5;
}

// contract event saved in block chain
message ContractEvent {
    string topic = 1;
    string tx_id = 2;
    string contract_name = 3;
    string contract_version = 4;
    repeated string event_data = 5;
}
  • TxStatusCode:交易执行结果的状态

  • ContractResult:合约执行结果

    • Code:用户自定义的合约执行结果的状态,0表示成功,>0表示异常

    • Result:合约执行返回的结果

    • Message:合约执行后的消息

    • GasUsed:合约执行消耗的Gas数量

    • ContractEvent:合约执行产生的事件日志

  • RwSetHash:交易执行结果的读写集哈希

  • Message:合约执行前的消息

3.1.3. 交易请求结构

// transaction request proposed by user
message TxRequest {
    // payload
    Payload payload = 1;

    // sender account and sender's signature
    EndorsementEntry sender = 2;

    // endorsers account and signatures
    repeated EndorsementEntry endorsers = 3;
}

用户发起一个交易请求的完整结构,包括:

  • Payload:交易的载荷数据,交易的核心信息

  • Sender:交易的发起者信息和发起者对Payload的签名信息

  • Endorsers:多签情况下,多个背书人的身份信息和其对Payload的签名信息

3.1.4. 交易响应结构

// tx request - tx response, only for RPC response
message TxResponse {
    // response code
    TxStatusCode code = 1;
    // response message
    string message = 2;
    // returned data, set in smart contract
    ContractResult contract_result = 3;
    //tx id of request
    string tx_id = 4;
}
  • Code:交易执行结果的状态

  • Message:交易执行后,合约输出的消息

  • ContractResult:合约执行结果

  • TxId:对应的交易ID

3.1.5. 交易执行结果的读写集

3.1.5.1. 读写集

// TxRWSet describes all the operations of a transaction on ledger
message TxRWSet {
    // transaction identifier
    string tx_id = 1;
    // read set
    repeated TxRead tx_reads = 2;
    // write set
    repeated TxWrite tx_writes = 3;
}

读写集为交易合约正常执行后对其世界状态的改变情况的反应,主要包括:

  • TxId 本读写集是由哪个交易产生的

  • TxRead 读集列表

  • TxWrite 写集列表

3.1.5.2. 读对象

读集主要用于在并发执行合约交易后判断并发的交易之间是否存在数据(版本)冲突,如果冲突则需要在DAG结构中反映交易之间的依赖情况,并调整冲突交易的执行顺序,重新执行合约。

// TxRead describes a read operation on a key
message TxRead {
    // read key
    bytes key = 1;
    // the value of the key
    bytes value = 2;
    // contract name, used in cross-contract calls
    // set to null if only the contract in transaction request is called
    string contract_name = 3;
    // read key version
    KeyVersion version = 4;
}

一条交易读主要包括:

  • ContractName 本读记录是读的哪个合约的状态数据

  • Key 本记录读取的状态键值信息

  • Version 读取到的版本

  • Value 读取到的值,后期考虑将Value移除,只需要依靠Version即可进行冲突判断。

3.1.5.3. 写对象

交易正常执行完毕后,如果世界状态发生了更改,那么写集就会记录详细的更改记录。

// TxRead describes a write/delete operation on a key
message TxWrite {
    // write key
    bytes key = 1;
    // write value
    bytes value = 2;
    // contract name, used in cross-contract calls
    // set to null if only the contract in transaction request is called
    string contract_name = 3;
}

交易写包括以下内容:

  • ContractName 对哪个合约的状态数据进行了更改

  • Key 被修改的状态键值

  • Value 修改后的状态值