Skip to content

网络协议

Erik Zhang edited this page Oct 20, 2016 · 8 revisions

在网络结构上,小蚁采用点对点网络结构,并使用TCP协议进行通讯。网络中存在两种节点类型,分别是普通节点和记账节点。普通节点可以广播、接收和转发交易、区块等,而记账节点可以创建区块。

小蚁的网络协议规范与比特币的协议大致类似,但在区块、交易等的数据结构上有很大的不同。

约定

  1. 字节序

    小蚁系统中所有的整数类型都是采用小端序(Little Endian)编码,只有IP地址和端口号采用大端序(Big Endian)编码。

  2. 散列

    小蚁系统中会用到2种不同的散列函数:SHA256和RIPEMD160。前者用于生成较长的散列值,而后者用于生成较短的散列值。通常生成一个对象的散列值时,会运用两次散列函数,例如要生成区块或交易的散列时,会计算两次SHA256;生成合约地址时,会先计算脚本的SHA256散列,然后再计算上一个散列的RIPEMD160散列。

    此外,区块中还会用到一种散列树(Merkle Tree)的结构,它将每一笔交易的散列两两相接后再计算一次散列,并重复以上过程直到只剩下一个根散列(Merkle Root)。

  3. 变长类型

    • varint:变长整数,可以根据表达的值进行编码以节省空间。

      长度 格式
      < 0xfd 1 uint8
      <= 0xffff 3 0xfd + uint16
      <= 0xffffffff 5 0xfe + uint32
      > 0xffffffff 9 0xff + uint64
    • varstr:变长字符串,由一个变长整数后接字符串构成。字符串采用UTF8编码。

      尺寸 字段 数据类型 说明
      ? length varint 字符串的长度,以字节为单位
      length string uint8[length] 字符串本身
    • array:数组,由一个变长整数后接元素序列构成。

  4. 定点数

    小蚁系统中的金额、价格等数据,统一采用64位定点数,小数部分精确到10-8,可表示的范围是:[-263/108, +263/108)

数据结构

  1. 区块链

    区块链是一种逻辑结构,它以单向链表的形式将区块串联起来,用于存放全网的交易、资产等数据。

  2. 区块

    尺寸 字段 数据类型 说明
    4 Version uint32 区块版本,目前为0
    32 PrevBlock uint256 前一个区块的散列值
    32 MerkleRoot uint256 交易列表的根散列
    4 Timestamp uint32 时间戳
    4 Height uint32 区块高度
    8 Nonce uint64 随机数
    20 NextMiner uint160 下一个记账合约地址
    1 - uint8 固定为1
    ? Script script 用于验证该区块的脚本
    ?*? Transactions tx[] 交易列表

    在计算区块散列时,并不会把整个区块都计算在内,而是只计算区块头的前7个字段:Version, PrevBlock, MerkleRoot, Timestamp, Height, Nonce, NextMiner。由于MerkleRoot已经包含了所有交易的散列值,因此修改交易也会改变区块的散列值。

    区块头的数据结构如下:

    尺寸 字段 数据类型 说明
    4 Version uint32 区块版本,目前为0
    32 PrevBlock uint256 前一个区块的散列值
    32 MerkleRoot uint256 交易列表的根散列
    4 Timestamp uint32 时间戳
    4 Height uint32 区块高度
    8 Nonce uint64 随机数
    20 NextMiner uint160 下一个记账合约地址
    1 - uint8 固定为1
    ? Script script 用于验证该区块的脚本
    1 - uint8 固定为0

    每个区块的时间戳必须晚于前一个区块的时间戳,一般两个区块的时间戳相差15秒左右,但是也允许出现不精确的情况。区块的高度值必须恰好等于前一个区块的高度值加一。

  3. 交易

    尺寸 字段 数据类型 说明
    1 Type uint8 交易类型
    1 Version uint8 交易版本,目前为0
    ? - - 特定于交易类型的数据
    ?*? Attributes tx_attr[] 该交易所具备的额外特性
    34*? Inputs tx_in[] 输入
    60*? Outputs tx_out[] 输出
    ?*? Scripts script[] 用于验证该交易的脚本列表

    小蚁系统中的一切事务都以交易为单位进行记录。交易有以下几种类型:

    名称 系统费用 说明
    0x00 MinerTransaction 0 分配字节费
    0x01 IssueTransaction 500|0 资产发行
    0x02 ClaimTransaction 0 分配小蚁币
    0x20 EnrollmentTransaction 1000 记账人报名
    0x40 RegisterTransaction 10000|0 资产登记
    0x80 ContractTransaction 0 合约交易
    0xb0 AgencyTransaction 0 委托交易
    0xd0 PublishTransaction 500*n 智能合约发布

    每一种类型的交易除了具有交易的公共字段之外,还会具有自己的专属字段。关于不同类型交易的专属字段,下文会有详细说明。

    • MinerTransaction

      尺寸 字段 数据类型 说明
      - - - 交易的公共字段
      4 Nonce uint32 随机数
      - - - 交易的公共字段

      每一个区块的第一笔交易必然是MinerTransaction。它用于将当前区块中所有的交易手续费奖励给记账人。

      交易中的随机数用于防止出现散列冲突。

    • IssueTransaction

      资产发行交易没有额外的特殊字段。

      资产管理员可以通过资产发行交易,将已经登记过的资产在小蚁区块链上制造出来,并发送到任意地址。

      特别的,如果发行的资产是小蚁股,那么这笔交易将可以免费发送。

    • ClaimTransaction

      尺寸 字段 数据类型 说明
      - - - 交易的公共字段
      34*? Claims tx_in[] 用于分配的小蚁股
      - - - 交易的公共字段
    • EnrollmentTransaction

      尺寸 字段 数据类型 说明
      - - - 交易的公共字段
      33 PublicKey ec_point 记账人的公钥
      - - - 交易的公共字段

      该交易表示一张报名表,表明交易的发起人想要报名成为记账人。

      报名的方式为:构造一笔EnrollmentTransaction类型的交易,并将一笔押金发送到PublicKey所属的地址。

      取消报名的方式为:将押金从PublicKey所属的地址上花费掉。

    • RegisterTransaction

      尺寸 字段 数据类型 说明
      - - - 交易的公共字段
      1 AssetType uint8 资产类别
      ? Name varstr 资产名称
      8 Amount int64 总量
      1 Precision uint8 精度
      33 Issuer ec_point 发行者的公钥
      20 Admin uint160 资产管理员的合约散列值
      - - - 交易的公共字段

      当需要在小蚁的区块链上创造一种新的资产时,就要进行资产登记。

      可选的资产类别有以下几种:

      名称 说明
      0x00 AntShare 小蚁股
      0x01 AntCoin 小蚁币
      0x08 Currency 法币
      0x60 Token 代币
      0x90 Share 股权/股份
      0x98 Invoice 电子发票

      每一种类别的资产都有其特定的限制。

      小蚁股和小蚁币是系统内置资产,除了在创世区块(即高度为0的区块)中创设之外,无法再次创设;

      附带法定义务的资产类别,在进行交易时需要发送方和接收方都对交易进行签名;

      其它资产没有任何限制。

      关于总量,有两种模型:限量模式和无限量模式。当Amount为正数时,该资产属于限量资产;当Amount等于-10-8时,该资产属于无限量资产。

    • ContractTransaction

      合约交易没有任何特殊的地方。

    • AgencyTransaction

      尺寸 字段 数据类型 说明
      - - - 交易的公共字段
      32 AssetId uint256 资产编号
      32 ValueAssetId uint256 货币编号
      20 Agent uint160 代理人的合约地址
      ?*? Orders order[] 订单列表
      1 - uint8 固定为1
      36 SplitOrder split_order 部分成交的订单
      - - - 交易的公共字段

      委托交易中的ValueAssetId必须为货币类别,且不能与AssetId相同;

      买盘和卖盘两者都至少需要包含一笔订单;

      交易中不能包含完全未成交的订单,且至多只能包含一笔部分成交的订单;

      如果存在部分成交的订单,则该订单的价格必须是最差的,即:对于买单,它的价格是最低价格;对于卖单,它的价格是最高价格;

      对于买单,需以不高于委托方所指定的价格成交;对于卖单,需以不低于委托方所指定的价格成交;

      交易数量精确到10-4,交易价格精确到10-4

      下面是订单的数据结构:

      尺寸 字段 数据类型 说明
      32 AssetId uint256 资产编号
      32 ValueAssetId uint256 货币编号
      20 Agent uint160 代理人的合约地址
      8 Amount int64 买入或卖出的数量
      8 Price int64 价格
      20 Client uint160 委托人的合约地址
      34*? Inputs tx_in[] 输入
      ?*? Scripts script[] 用于验证该订单的脚本列表

      如果订单是包含在委托交易中一起传输,由于交易中已经包含资产、货币、代理人等信息,所以可以压缩为以下格式:

      尺寸 字段 数据类型 说明
      8 Amount int64 买入或卖出的数量
      8 Price int64 价格
      20 Client uint160 委托人的合约地址
      34*? Inputs tx_in[] 输入
      ?*? Scripts script[] 用于验证该订单的脚本列表

      部分成交订单的数据结构为:

      尺寸 字段 数据类型 说明
      8 Amount int64 买入或卖出的数量
      8 Price int64 价格
      20 Client uint160 委托人的合约地址

      对于所有的订单,Amount为正数表示买入,Amount为负数表示卖出。

  4. 交易特性

    尺寸 字段 数据类型 说明
    1 Usage uint8 用途
    0|1 length uint8 数据长度(特定情况下会省略)
    length Data uint8[length] 特定于用途的外部数据

    有时候交易中会需要包含一些供外部使用的数据,这些数据将统一被放置在交易特性字段中。

    每个交易特性可以有不同的用途:

    名称 说明
    0x00 ContractHash 外部合同的散列值
    0x02-0x03 ECDH02-ECDH03 用于ECDH密钥交换的公钥
    0x20 Script 用于对交易进行额外的验证
    0x30 Vote 用于投票选出记账人
    0x80 CertUrl 证书地址
    0x81 DescriptionUrl 外部介绍信息地址
    0x90 Description 简短的介绍信息
    0xa1-0xaf Hash1-Hash15 用于存放自定义的散列值
    0xf0-0xff Remark-Remark15 备注

    对于ContractHash,ECDH系列,Vote,Hash系列,数据长度固定为32字节,length字段省略;

    对于Script,必须明确给出数据长度,且长度不能超过65535;

    对于CertUrl,DescriptionUrl,Description,Remark系列,必须明确给出数据长度,且长度不能超过255。

  5. 交易输入

    尺寸 字段 数据类型 说明
    32 PrevHash uint256 引用交易的散列值
    2 PrevIndex uint16 引用交易输出的索引
  6. 交易输出

    尺寸 字段 数据类型 说明
    32 AssetId uint256 资产编号
    8 Value int64 金额
    20 ScriptHash uint160 收款地址

    每个交易中最多只能包含65536个输出。

  7. 验证脚本

    尺寸 字段 数据类型 说明
    ? StackScript uint8[] 栈脚本代码
    ? RedeemScript uint8[] 合约脚本代码

    栈脚本中只能包含压栈操作指令,用于向合约脚本传递参数(如签名等)。脚本解释器会先执行栈脚本代码,然后执行合约脚本代码。

    在一笔交易中,合约脚本代码的散列值必须与交易输出中的一致,这是验证的一部分。关于脚本执行的过程,后文会详细阐述。

网络消息

所有的网络消息都通过以下消息结构来发送:

尺寸 字段 数据类型 说明
4 Magic uint32 协议标识号
12 Command char[12] 命令
4 length uint32 Payload的长度
4 Checksum uint32 校验和
length Payload uint8[length] 消息内容

已定义的Magic值:

说明
0x00746e41 正式网
0x74746e41 测试网

Command采用utf8编码,长度为12字节,多余部分用0填充。

Checksum是Payload两次SHA256散列后的前4个字节。

Payload根据不同的命令有不同的详细格式,见下文。

  1. version

    尺寸 字段 数据类型 说明
    4 Version uint32 协议版本,目前为0
    8 Services uint64 节点提供的服务,目前为1
    4 Timestamp uint32 当前时间
    2 Port uint16 监听的端口,如果不监听则为0
    4 Nonce uint32 用于区分相同公网IP的节点
    ? UserAgent varstr 客户端标识
    4 StartHeight uint32 区块链高度
    1 Relay bool 是否接收并转发

    一个节点收到连接请求时,它立即宣告其版本。在通信双方都得到对方版本之前,不会有其他通信。

  2. verack

    节点收到version消息后,立刻回复一个verack作为应答。

    此消息没有payload。

  3. getaddr

    向一个节点请求一批新的活动节点,以增加自身的连接数。

    此消息没有payload。

  4. addr

    尺寸 字段 数据类型 说明
    30*? AddressList net_addr[] 网络上其他节点的地址

    节点收到getaddr消息后,返回一个addr消息作为应答,提供网络上已知节点的信息。

  5. getheaders

    尺寸 字段 数据类型 说明
    32*? HashStart uint256[] 节点已知的最新block散列
    32 HashStop uint256 请求的最后一个block的散列

    向一个节点请求包含编号HashStart到HashStop的至多2000个block的header包。要获取之后的block散列,需要重新发送getheaders消息。这个消息用于快速下载不包含相关交易的blockchain。

  6. headers

    尺寸 字段 数据类型 说明
    ?*? Headers header[] 区块头

    节点收到getheaders消息后,返回一个headers消息作为应答,提供请求的区块头。

  7. getblocks

    尺寸 字段 数据类型 说明
    32*? HashStart uint256[] 节点已知的最新block散列
    32 HashStop uint256 请求的最后一个block的散列

    向一个节点请求包含编号从HashStart到HashStop的block列表的inv消息。若HashStart到HashStop的block数超过500,则在500处截止。欲获取后面的block散列,需要重新发送getblocks消息。

  8. inv

    尺寸 字段 数据类型 说明
    1 Type uint8 清单类型
    32*? Hashes uint256[] 清单

    节点通过此消息可以广播它拥有的对象信息。这个消息可以主动发送,也可以用于应答getbloks消息。

    清单类型有以下几种:

    名称 说明
    0x01 TX 交易
    0x02 Block 区块
    0xe0 Consensus 共识数据
  9. getdata

    尺寸 字段 数据类型 说明
    1 Type uint8 清单类型
    32*? Hashes uint256[] 清单

    向一个节点请求指定的对象,它通常在接收到inv包并滤去已知元素后发送。

  10. block

    尺寸 字段 数据类型 说明
    ? Block block 区块

    向一个节点发送一个区块,用于响应请求数据的getdata消息。

  11. tx

    尺寸 字段 数据类型 说明
    ? Transaction tx 交易

    向一个节点发送一笔交易,用于响应请求数据的getdata消息。