Show / Hide Table of Contents

交易

NEO区块去掉区块头部分就是一串交易构成的区块主体,因而交易是整个NEO系统的基础部件。钱包、智能合约、账户和交易相互作用但最终都转化成交易被记入区块链中。在NEO的P2P网络传输中,信息被打包成 InvPayload 信息包来传送(Inv即Inventory)。不同信息包有自己需要的特定数据,因此衍生出三种类型的数据包。 InventoryType = 0x01 来标定网络中的InvPayload信息包内装的是交易数据。除交易数据包之外,还有块数据包( InventoryType = 0x02 )和共识数据包( InventoryType = 0xe0 )。

数据结构

一笔普通交易的数据结构如下:

字节数字段类型描述
1Typebyte交易类型
1Versionbyte交易版本号,目前为0
?--特定交易的数据
?*?Attributestx_attr[]该交易所具备的额外特性
34*?Inputstx_in[]输入
60 * ?Outputstx_out[]输出
?*?ScriptsWitness[]用于验证该交易的脚本列表

Input

Input数组中存放了每一个输入的信息。每笔交易中可以有多个Input,也可能没有Input。在之后会提到的MinerTransaction中Input就为空。Input的数据结构如下:

字节数字段类型描述
32PrevHashUInt256被引用交易的散列值
2PrevIndexushort被引用交易输出的索引

PrevHash和PrevIndex合起来就可以找到这个Input对应于哪个交易的第几个Output。从而Input和Output之间可以连接起来,构成了UTXO模型的基础。UTXO模型的具体信息请参见 UTXO模型 部分。

Output

每个交易中最多只能包含 65536 个Output,代表资金转出。Output的数据结构如下:

字节数字段类型描述
32AssetIdUIntBase资产Id
?ValueBigDecimal转账金额
20ScriptHashUInt160地址,即账户地址或合约地址

Attribute

字节数字段类型描述
1Usagebyte属性类型
0|1lengthuint8数据长度(特定情况下会省略)
?Databyte[length]特定用途的外部数据

TransactionAttributeUsage,交易属性使用表数据结构如下:

字段描述
ContractHash0x00外部合同的散列值
ECDH020x02用于ECDH密钥交换的公钥,该公钥的第一个字节为0x02
ECDH030x03用于ECDH密钥交换的公钥,该公钥的第一个字节为0x03
Script0x20用于对交易进行额外的验证, 如股权类转账,存放收款人的脚本hash
Vote0x30
DescriptionUrl0x81外部介绍信息地址
Description0x90简短的介绍信息
Hash1 - Hash150xa1-0xaf用于存放自定义的散列值
Remark-Remark150xf0-0xff备注

ContractHash、ECDH02-03、Vote和Hash1-15的数据长度固定为 32 字节,所以省略length字段。
Script固定20字节,存放地址。
DescriptionUrl必须明确给出数据长度,且长度不能超过 255字节。
Description和Remark1-15,也必须明确给出数据长度,且最大存储不超过 65535字节。

Witness

每笔交易(transaction,tx)对象在被放进block时,需经过数字签名,确保在后续传输和处理中能随时验证交易是否被篡改。Neo采用的ECDSA数字签名方法。交易的转帐转出方地址,为ECDSA签名时所用的公钥publicKey。Neo系统没有使用比特币中的SegWit,每笔交易都包含自己的Script.witness,而Script.Witness使用的是智能合约。

见证人,实际上是可执行的验证脚本。 InvocationScript 脚本传递了 VerificationScript 脚本所需要的参数。只有当脚本执行返回真时,验证成功。

字节数字段类型描述
?InvocationScriptbyte[]调用脚本,补全脚本参数
?VerificationScriptbyte[]验证脚本

调用脚本进行压栈操作相关的指令,用于向验证脚本传递参数(如签名等)。脚本解释器会先执行调用脚本代码,然后再执行验证脚本代码。

Block.NextConsensus 所代表的多方签名脚本,填充签名参数后的可执行脚本,如下图所示, Opt.CHECKMULTISIG 在NVM内部执行时,完成对签名以及公钥之间的多方签名校验。

nextconsensus_witness

交易类型

NEO 中一共定义了9种不同类型的交易,如下表所示。

编号类型名系统费用(GAS)描述
1MinerTransaction0x000块的第一条交易,用于分配字节费的交易
2RegisterTransaction0x4010000/0(已弃用)注册资产,仅用于NEO和GAS
3IssueTransaction0x01500/0分发资产
4ClaimTransaction0x020提取GAS
5StateTransaction0x901000/0申请见证人或共识节点投票
6EnrollmentTransaction0x201000(已弃用) 报名成为共识候选人
7ContractTransaction0x800合约交易,这是最常用的一种交易
8PublishTransaction0xd0500*n(已弃用) 智能合约发布
9InvocationTransaction0xd1具体的指令GAS消耗调用合约,部署合约后或生成新资产之后会使用

关于详细的交易处理流程,请参见 交易流程

系统手续费: 不同的交易类型,不同的收费标准,设置在配置文件 protocol.json 中,最后分红给持有NEO用户。

交易网络费: NetworkFee = tx.inputs.GAS - tx.outputs.GAS - tx.SystemFee , 共识过程中,对议长打包交易的奖励,存于共识新块的第一笔交易 MinerTransaction 中。交易的网络费设置得越高,越容易被打包。

如何使用交易

以上这9种交易并不能完成所有的功能实现,比如部署合约和生成NEO和GAS以外的NEP5新资产时,通过系统调用来完成,以InvocationTransaction交易的形式来将这个事情加入到区块链中。下面给出的创世块的生成例子,展示了使用提供的交易类型完成资产注册。

例1:生成创始块

创世块(GenesisBlock)是默认已经定义在代码中不可修改的区块链的第一个区块,高度为0。在创世块中注册了NEO和GAS资产,并分发了NEO资产。注意,只有NEO和GAS是使用RegisterTransaction完成注册,其他全局资产和NEP5代币都是通过系统调用的方式生成。

创始块的区块头信息如下:

尺寸字段名称类型
4Version区块版本uint 0
32PrevHash上一个区块HashUInt256 0x0000000000000000000000000000000000000000000000000000000000000000
32MerkleRootMerkle树Rootuint256 0x803ff4abe3ea6533bcc0be574efa02f83ae8fdc651c879056b0d9be336c01bf4
4Timestamp创世时间uint创世时间为: 2016-07-15 23:08:21
4Index创世块高度uint 0
8ConsensusDataNonceulong 2083236893 , 比特币创世块nonce值,向比特币致敬
20NextConsensus下一个共识地址UInt160参与下一轮出块的共识节点的多方签名合约地址
1--uint8固定为 1
?Witness见证人Witness 0x51 , 代表 PUSHT 指令,返回永真
?*?Transactions交易Transaction[]目前存了4笔交易, 见后续表

第一笔交易,MinerTransaction,即“挖矿”交易。所有的block的第一笔交易,都必须是MinerTransaction。Neo中没有挖矿的概念,这里主要记录一个区块的网络费奖励。

尺寸字段名称类型
1Typeuint8交易类型 0x00
1Versionuint8交易版本号 0
8Nonceulongnonce 2083236893
?*?Attributestx_attr[]该交易所具备的额外特性
34*?Inputstx_in[]输入
60*?Outputstx_out[]输出
?*?ScriptsWitness[]用于验证该交易的脚本列表

第二笔交易,RegisterTransaction,注册NEO代币

尺寸字段名称类型
1Typebyte交易类型 0x40
1Versionbyte交易版本号 0
1AssetTypebyte资产类型 0x00
?Namestring资产名字 NEO
8AmountFix8总量 100000000
1Precisionbyte精度 0
?OwnerECPoint所有者公钥
32AdminUInt160管理者 0x51 .toScriptHash
?*?Attributestx_attr[]该交易所具备的额外特性
34*?Inputstx_in[]输入
60*?Outputstx_out[]输出
?*?ScriptsWitness[]用于验证该交易的脚本列表

NEO 名称定义 = [{"lang":"zh-CN","name":"小蚁股"},{"lang":"en","name":"AntShare"}]

第三笔交易,RegisterTransaction,注册GAS代币

尺寸字段名称类型
1Typebyte交易类型 0x40
1Versionbyte交易版本号 0
1AssetTypebyte资产类型 0x01
?Namestring资产名字 GAS
8AmountFix8总量 100000000
1Precisionbyte精度 8
?OwnerECPoint所有者公钥
32AdminUInt160管理者 0x00 .toScriptHash, 即 OpCode.PUSHF 指令脚本
?*?Attributestx_attr[]该交易所具备的额外特性
34*?Inputstx_in[]输入
60*?Outputstx_out[]输出
?*?ScriptsWitness[]用于验证该交易的脚本列表

GAS 名称定义 = [{"lang":"zh-CN","name":"小蚁币"},{"lang":"en","name":"AntCoin"}]

第四笔交易,IssueTransaction,发放NEO到合约地址

尺寸字段名称类型
1Typebyte交易类型 0x01
1Versionbyte交易版本号 0
?*?Attributestx_attr[]该交易所具备的额外特性
34*?Inputstx_in[]输入
60 * ?Outputstx_out[]输出有一笔output,见下表
?*?ScriptsWitness[]用于验证该交易的脚本列表 0x51 , 代表 OpCode.PUSHT

其中,Output定义了将所有的NEO代币,转移到共识节点多方签名合约地址上。而Scripts为空,表示交易因为是创世块交易,不需要再验证。

尺寸字段名称类型
1AssetIdbyte资产类型 0x00 , 即NEO代币
8ValueFix8转账总量 100000000
20ScriptHashUInt160收款脚本hash备用共识节点多方签名合约地址