-
Notifications
You must be signed in to change notification settings - Fork 1k
网络协议
在网络结构上,小蚁采用点对点网络结构,并使用TCP协议进行通讯。网络中存在两种节点类型,分别是普通节点和记账节点。普通节点可以广播、接收和转发交易、区块等,而记账节点可以创建区块。
小蚁的网络协议规范与比特币的协议大致类似,但在区块、交易等的数据结构上有很大的不同。
-
字节序
小蚁系统中所有的整数类型都是采用小端序(Little Endian)编码,只有IP地址和端口号采用大端序(Big Endian)编码。
-
散列
小蚁系统中会用到2种不同的散列函数:SHA256和RIPEMD160。前者用于生成较长的散列值,而后者用于生成较短的散列值。通常生成一个对象的散列值时,会运用两次散列函数,例如要生成区块或交易的散列时,会计算两次SHA256;生成合约地址时,会先计算脚本的SHA256散列,然后再计算上一个散列的RIPEMD160散列。
此外,区块中还会用到一种散列树(Merkle Tree)的结构,它将每一笔交易的散列两两相接后再计算一次散列,并重复以上过程直到只剩下一个根散列(Merkle Root)。
-
变长类型
-
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:数组,由一个变长整数后接元素序列构成。
-
-
定点数
小蚁系统中的金额、价格等数据,统一采用64位定点数,小数部分精确到10-8,可表示的范围是:[-263/108, +263/108)
-
区块链
区块链是一种逻辑结构,它以单向链表的形式将区块串联起来,用于存放全网的交易、资产等数据。
-
区块
尺寸 字段 数据类型 说明 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秒左右,但是也允许出现不精确的情况。区块的高度值必须恰好等于前一个区块的高度值加一。
-
交易
尺寸 字段 数据类型 说明 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为负数表示卖出。
-
-
交易特性
尺寸 字段 数据类型 说明 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。
-
交易输入
尺寸 字段 数据类型 说明 32 PrevHash uint256 引用交易的散列值 2 PrevIndex uint16 引用交易输出的索引 -
交易输出
尺寸 字段 数据类型 说明 32 AssetId uint256 资产编号 8 Value int64 金额 20 ScriptHash uint160 收款地址 每个交易中最多只能包含65536个输出。
-
验证脚本
尺寸 字段 数据类型 说明 ? 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根据不同的命令有不同的详细格式,见下文。
-
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 是否接收并转发 一个节点收到连接请求时,它立即宣告其版本。在通信双方都得到对方版本之前,不会有其他通信。
-
verack
节点收到version消息后,立刻回复一个verack作为应答。
此消息没有payload。
-
getaddr
向一个节点请求一批新的活动节点,以增加自身的连接数。
此消息没有payload。
-
addr
尺寸 字段 数据类型 说明 30*? AddressList net_addr[] 网络上其他节点的地址 节点收到getaddr消息后,返回一个addr消息作为应答,提供网络上已知节点的信息。
-
getheaders
尺寸 字段 数据类型 说明 32*? HashStart uint256[] 节点已知的最新block散列 32 HashStop uint256 请求的最后一个block的散列 向一个节点请求包含编号HashStart到HashStop的至多2000个block的header包。要获取之后的block散列,需要重新发送getheaders消息。这个消息用于快速下载不包含相关交易的blockchain。
-
headers
尺寸 字段 数据类型 说明 ?*? Headers header[] 区块头 节点收到getheaders消息后,返回一个headers消息作为应答,提供请求的区块头。
-
getblocks
尺寸 字段 数据类型 说明 32*? HashStart uint256[] 节点已知的最新block散列 32 HashStop uint256 请求的最后一个block的散列 向一个节点请求包含编号从HashStart到HashStop的block列表的inv消息。若HashStart到HashStop的block数超过500,则在500处截止。欲获取后面的block散列,需要重新发送getblocks消息。
-
inv
尺寸 字段 数据类型 说明 1 Type uint8 清单类型 32*? Hashes uint256[] 清单 节点通过此消息可以广播它拥有的对象信息。这个消息可以主动发送,也可以用于应答getbloks消息。
清单类型有以下几种:
值 名称 说明 0x01 TX 交易 0x02 Block 区块 0xe0 Consensus 共识数据 -
getdata
尺寸 字段 数据类型 说明 1 Type uint8 清单类型 32*? Hashes uint256[] 清单 向一个节点请求指定的对象,它通常在接收到inv包并滤去已知元素后发送。
-
block
尺寸 字段 数据类型 说明 ? Block block 区块 向一个节点发送一个区块,用于响应请求数据的getdata消息。
-
tx
尺寸 字段 数据类型 说明 ? Transaction tx 交易 向一个节点发送一笔交易,用于响应请求数据的getdata消息。