5. 账本状态机

众所周知,程序=数据结构+算法,了解一个程序的数据结构有助于掌握一个程序的关键设计。本文从背景、功能以及各个字段的用意来剖析XuperChain底层账本的核心数据结构,从而方便XuperChain开发者以及用户更深入地了解XuperChain底层框架的核心数据结构的设计缘由,有助于提高XuperChain开发者更高效的开发,有助于XuperChain用户更好的使用XuperChain来服务自己的业务。

5.1. 核心数据结构

涉及到账本的核心数据结构包括:区块、交易、UTXO、读写集。

5.1.1. 区块

  • 背景:所谓区块链,简单来说就是不同的区块以DAG方式链接起来形成的链。因此,区块是区块链的基本单元。

  • 功能:区块是区块链的基本单元,通常为了提高区块链网络的吞吐,矿工会在一个区块中打包若干个交易。一个区块通常由区块头以及区块体组成。

../_images/block-img.png

  • 代码:区块的Proto如下
  1. 1message InternalBlock {
  2. 2 // block version
  3. 3 // 区块版本
  4. 4 int32 version = 1;
  5. 5 // Random number used to avoid replay attacks
  6. 6 // 随机数,用来避免重放攻击
  7. 7 int32 nonce = 2;
  8. 8 // blockid generate the hash sign of the block used by sha256
  9. 9 // 区块的唯一标识
  10. 10 bytes blockid = 3;
  11. 11 // pre_hash is the parent blockid of the block
  12. 12 // 区块的前置依赖区块ID
  13. 13 bytes pre_hash = 4;
  14. 14 // The miner id
  15. 15 // 矿工ID
  16. 16 bytes proposer = 5;
  17. 17 // 矿工对区块的签名
  18. 18 // The sign which miner signed: blockid + nonce + timestamp
  19. 19 bytes sign = 6;
  20. 20 // The pk of the miner
  21. 21 // 矿工公钥
  22. 22 bytes pubkey = 7;
  23. 23 // The Merkle Tree root
  24. 24 // 默克尔树树根
  25. 25 bytes merkle_root = 8;
  26. 26 // The height of the blockchain
  27. 27 // 区块所在高度
  28. 28 int64 height = 9;
  29. 29 // Timestamp of the block
  30. 30 // 打包区块的时间戳
  31. 31 int64 timestamp = 10;
  32. 32 // Transactions of the block, only txid stored on kv, the detail information
  33. 33 // stored in another table
  34. 34 // 交易内容
  35. 35 repeated Transaction transactions = 11;
  36. 36 // The transaction count of the block
  37. 37 // 区块中包含的交易数量
  38. 38 int32 tx_count = 12;
  39. 39 // 所有交易hash的merkle tree
  40. 40 repeated bytes merkle_tree = 13;
  41. 41 // 采用DPOS共识算法时,当前是第几轮
  42. 42 int64 curTerm = 16;
  43. 43 int64 curBlockNum = 17;
  44. 44 // 区块中执行失败的交易以及对应的失败原因
  45. 45 map<string, string> failed_txs = 18; // txid -> failed reason
  46. 46 // 采用POW共识算法时,对应的挖矿难度值
  47. 47 int32 targetBits = 19;
  48. 48 // 下面的属性会动态变化
  49. 49 // If the block is on the trunk
  50. 50 // 该区块是否在主干上
  51. 51 bool in_trunk = 14;
  52. 52 // Next next block which on trunk
  53. 53 // 当前区块的后继区块ID
  54. 54 bytes next_hash = 15;
  55. 55}

5.1.2. 交易

  • 背景:区块链网络中的每个节点都是一个状态机,为了给每个节点传递状态,系统引入了交易,作为区块链网络状态更改的最小操作单元。

  • 功能:通常表现为普通转账以及智能合约调用。

  • 代码:交易的Proto如下

  1. 1message Transaction {
  2. 2 // txid is the id of this transaction
  3. 3 // 交易的唯一标识
  4. 4 bytes txid = 1;
  5. 5 // the blockid the transaction belong to
  6. 6 // 交易被打包在哪个区块中
  7. 7 bytes blockid = 2;
  8. 8 // Transaction input list
  9. 9 // UTXO来源
  10. 10 repeated TxInput tx_inputs = 3;
  11. 11 // Transaction output list
  12. 12 // UTXO去处
  13. 13 repeated TxOutput tx_outputs = 4;
  14. 14 // Transaction description or system contract
  15. 15 // 交易内容描述或系统合约
  16. 16 bytes desc = 6;
  17. 17 // Mining rewards
  18. 18 // 矿工奖励
  19. 19 bool coinbase = 7;
  20. 20 // Random number used to avoid replay attacks
  21. 21 // 随机数
  22. 22 string nonce = 8;
  23. 23 // Timestamp to launch the transaction
  24. 24 // 发起交易的时间戳
  25. 25 int64 timestamp = 9;
  26. 26 // tx format version; tx格式版本号
  27. 27 int32 version = 10;
  28. 28 // auto generated tx
  29. 29 // 该交易是否属于系统自动生成的交易
  30. 30 bool autogen = 11;
  31. 31 // 读写集中的读集
  32. 32 repeated TxInputExt tx_inputs_ext = 23;
  33. 33 // 读写集中的写集
  34. 34 repeated TxOutputExt tx_outputs_ext = 24;
  35. 35 // 该交易包含的合约调用请求
  36. 36 repeated InvokeRequest contract_requests = 25;
  37. 37 // 权限系统新增字段
  38. 38 // 交易发起者, 可以是一个Address或者一个Account
  39. 39 string initiator = 26;
  40. 40 // 交易发起需要被收集签名的AddressURL集合信息,包括用于utxo转账和用于合约调用
  41. 41 repeated string auth_require = 27;
  42. 42 // 交易发起者对交易元数据签名,签名的内容包括auth_require字段
  43. 43 repeated SignatureInfo initiator_signs = 28;
  44. 44 // 收集到的签名
  45. 45 repeated SignatureInfo auth_require_signs = 29;
  46. 46 // 节点收到tx的时间戳,不参与签名
  47. 47 int64 received_timestamp = 30;
  48. 48 // 统一签名(支持多重签名/环签名等,与initiator_signs/auth_require_signs不同时使用)
  49. 49 XuperSignature xuper_sign = 31;
  50. 50}

5.1.3. UTXO

  • 背景:区块链中比较常见的两种操作,包括普通转账以及合约调用,这两种操作都涉及到了数据状态的引用以及更新。为了描述普通转账涉及到的数据状态的引用以及更新,引入了UTXO(Unspent Transaction Output)。

  • 功能:一种记账方式,用来描述普通转账时涉及到的数据状态的引用以及更新。通常由转账来源数据(UtxoInput)以及转账去处数据(UtxoOutput)组成。

../_images/tx-img.png

  • 代码:UTXO的Proto如下
  1. 1message Utxo {
  2. 2 // 转账数量
  3. 3 bytes amount = 1;
  4. 4 // 转给谁
  5. 5 bytes toAddr = 2;
  6. 6 // 转给谁的公钥
  7. 7 bytes toPubkey = 3;
  8. 8 // 该Utxo属于哪一个交易
  9. 9 bytes refTxid = 4;
  10. 10 // 该Utxo数据哪一个交易的哪一个offset
  11. 11 int32 refOffset = 5;
  12. 12}
  13. 13// UtxoInput query info to query utxos
  14. 14// UTXO的转账来源
  15. 15message UtxoInput {
  16. 16 Header header = 1;
  17. 17 // which bcname to select
  18. 18 // UTXO来源属于哪一条链
  19. 19 string bcname = 2;
  20. 20 // address to select
  21. 21 // UTXO来源属于哪个address
  22. 22 string address = 3;
  23. 23 // publickey of the address
  24. 24 // UTXO来源对应的公钥
  25. 25 string publickey = 4;
  26. 26 // totalNeed refer the total need utxos to select
  27. 27 // 需要的UTXO总额
  28. 28 string totalNeed = 5;
  29. 29 // userSign of input
  30. 30 // UTXO来源的签名
  31. 31 bytes userSign = 7;
  32. 32 // need lock
  33. 33 // 该UTXO是否需要锁定(内存级别锁定)
  34. 34 bool needLock = 8;
  35. 35}
  36. 36// UtxoOutput query results
  37. 37// UTXO的转账去处
  38. 38message UtxoOutput {
  39. 39 Header header = 1;
  40. 40 // utxo list
  41. 41 // UTXO去处
  42. 42 repeated Utxo utxoList = 2;
  43. 43 // total selected amount
  44. 44 // UTXO去处总额
  45. 45 string totalSelected = 3;
  46. 46}

5.1.4. 读写集

  • 背景:区块链中比较常见的两种操作,包括普通转账以及合约调用,这两种操作都涉及到了数据状态的引用以及更新。为了描述合约调用涉及到的数据状态的引用以及更新,引入了读写集。

  • 功能:一种用来描述合约调用时涉及到的数据状态的引用以及更新的技术。通常由读集(TxInputExt)以及写集(TxOutputExt)组成。

../_images/xupermodel.png

  • 代码:读写集的Proto如下
  1. 1// 扩展输入
  2. 2message TxInputExt {
  3. 3 // 读集属于哪一个bucket
  4. 4 string bucket = 1;
  5. 5 // 读集对应的key
  6. 6 bytes key = 2;
  7. 7 // 读集属于哪一个txid
  8. 8 bytes ref_txid = 3;
  9. 9 // 读集属于哪一个txid的哪一个offset
  10. 10 int32 ref_offset = 4;
  11. 11}
  12. 12// 扩展输出
  13. 13message TxOutputExt {
  14. 14 // 写集属于哪一个bucket
  15. 15 string bucket = 1;
  16. 16 // 写集对应的key
  17. 17 bytes key = 2;
  18. 18 // 写集对应的value
  19. 19 bytes value = 3;
  20. 20}

5.2. XuperModel

XuperChain能够支持合约链内并行的很大的原因是由于其底层自研的XuperModel数据模型。

XuperModel是一个带版本的存储模型,支持读写集生成。该模型是比特币utxo模型的一个演变。在比特币的utxo模型中,每个交易都需要在输入字段中引用早期交易的输出,以证明资金来源。同样,在XuperModel中,每个事务读取的数据需要引用上一个事务写入的数据。在XuperModel中,事务的输入表示在执行智能合约期间读取的数据源,即事务的输出来源。事务的输出表示事务写入状态数据库的数据,这些数据在未来事务执行智能合约时将被引用,如下图所示:

XuperModel事务

XuperModel事务

为了在运行时获取合约的读写集,在预执行每个合约时XuperModel为其提供智能缓存。该缓存对状态数据库是只读的,它可以为合约的预执行生成读写集和结果。验证合约时,验证节点根据事务内容初始化缓存实例。节点将再次执行一次合约,但此时合约只能从读集读取数据。同样,写入数据也会在写入集中生效。当验证完生成的写集和事务携带的写集一致时合约验证通过,将事务写入账本,cache的原理如下所示,图中左边部分是合约预执行时的示意图,右边部分是合约验证时的示意图:

XuperModel合约验证

XuperModel合约验证