NULS设计文档解读——交易管理模块

摘要:NULS,让区块链更简单!

为什么要有交易管理模块

区块链是一种加密的分布式记账技术,通过分布式的节点来共同完成记账。想要明白什么是分布式记账,你可以先搞懂什么是中心式记账。中心式记账,是当前互联网系统中广泛采用的记账方式。

举个例子,我们的资金是存放在银行的,比如我在XX银行向你转账5000块钱,这在银行其实表现为一条数据,银行是用一个中心数据库来存储这个数据的,为了防止意外和灾害,它还建有备份的数据库,来存放这个数据的副本,这种记账方式就是中心式记账。

但在分布式记账中,我向你转账5000块钱这条数据,不是记录在中心化机构(例如银行)的数据库中的,而是记录在全网所有的计算机节点上的。

在区块链中,由全网节点共同记录并保持一致的资产(数据)往来,我们称之为交易,根据交易对每个账户所做的资产(数据)统计结果,就构成了账本。记录区块链中的交易,通常需要完成收集、验证、存储、转发等操作,这些操作过程是相同的,因此我们单独划分了交易管理模块,来专门处理这些操作。

在NULS2.0的技术体系中,由于会涉及到跨链交易,所以交易会在链中或者链与链之间流转,各条链的节点,不仅要处理链内的交易,还可能会处理跨链的交易,每个节点处理的交易,可能会越来越多。跨链交易的增加,使得单独开发交易管理模块,来处理交易的相关操作,变得更加重要。

交易管理模块功能

交易管理模块功能体现了该模块的作用,也是开发该模块需要达到的目标。读者阅读本文档的目标是,理解交易管理模块具有哪些功能,以及这些功能的实现流程。

在NULS2.0中,我们将一笔交易的生命周期,分为主要的四个阶段:

产生:交易有多种类型,每个模块都有可能发起一笔交易,交易的生命周期,从模块发起一笔交易开始。交易一旦产生,交易管理模块就会对其进行交易基础验证、业务验证、账本验证,只要验证不通过,交易就会被视为发起失败,验证通过的交易,交易管理模块会收集起来;广播:当一个节点的模块产生一笔交易之后,节点会把交易及时广播出去,其他节点会收集这笔交易,然后进行后续的操作。其他节点收到这笔交易,并验证通过之后,也会广播给与自己进行了连接的节点;已打包:一笔合法的交易,会被打包进区块中,交易打包由共识模块、区块管理模块、交易管理模块共同完成。在NULS2.0中,想要完成打包,虽然交易已经进行过验证,但是在打包前,仍然需要再次进行相关验证;已存储:在完成打包之后,交易最终会被存储下来。交易的存储是以区块为单位的,在对区块中的交易进行存储时,会对区块中的交易进行验证,只有区块中的全部交易都验证通过,交易才会被最终存储下来。在从已打包到已存储的过程中,区块管理模块和交易管理模块都会参与。

一笔交易的生命周期,会有多个模块参与,交易管理模块在交易的整个生命周期中,主要会进行收集、打包、验证、存储等操作,所以交易管理模块主要分为四个功能:

交易收集交易打包区块交易验证区块交易存储

下面我们将详细讲解以上四类功能的作用及实现流程。

交易收集

NULS2.0的节点是模块化运行的,所以节点是由多个核心模块组成的一个整体,节点的各种交易是由对应的各个功能模块所创建的,而交易管理模块作为节点的交易处理中心,却并不是所有交易的组装者。因此交易管理模块的首要工作,就是要收集各个模块所组装的交易。

交易收集主要分为两种情况:

收集节点自身提交的交易收集其他节点发送来的交易

收集节点自身提交的交易

如下流程图是收集节点自身提交的交易流程图。根据此流程图,你可以看到此种情况下,收集交易的关键流程,接下来,我们对这些关键流程,进行逐一讲解。

新交易:交易管理模块会收到来自其他模块提交的交易。交易基础验证:对交易做基础的合法性验证,包括手续费验证、签名验证,以及根据交易协议对交易数据格式的验证。各模块的交易业务验证:因为每个交易是由其他模块发送来的,交易管理模块收到交易之后,会调用其他模块的交易业务验证器,让其他模块进行交易业务的验证。交易业务验证,是验证该笔交易是否服务对应的业务规则,例如,交易管理模块收到一笔别名交易,交易业务验证器会根据业务规则,验证该地址是否设置了别名,该别名是否已经存在。账本验证:调用账本模块的验证器接口,账本模块会对交易中对应地址的资产类型、余额等资产属性进行验证,保证对应地址的资产和账本的正确性。判断是否是出块节点:判断节点在本轮共识中,是否是出块节点。如果是,需要将交易放到待打包交易池中,然后再存储到未确认交易数据库中。广播完整交易:交易存储到未确认交易数据库中之后,会进行交易广播,让其他节点对交易进行验证和存储,让其他节点在进行打包出块时,可以尽快的将交易进行打包。

收集其他节点发送来的交易

如下流程图是收集其他节点发送来的交易的流程图。该种情况下,与收集节点自身提交的交易情况,有许多关键流程是相同的,相同的部分我们不做重复描述,下面,针对不同的地方,我们进行讲解。

放入待处理队列:在此种情况下,完成交易基础验证之后,交易会被放入到待处理队列中。设置该流程,是因为节点可能会同时收到其他节点发送过来的很多交易,如果没有该流程进行缓冲,可能节点会处理不过来。

放入孤儿交易池:

在完成账本验证之后,其他节点发送过来的交易,可能是孤儿交易。如果在账本验证环节,发现是孤儿交易,该笔交易就会被放到孤儿交易池中。放到孤儿交易池中的孤儿交易,会按照交易的nonce值进行排序,并且每隔一段时间,孤儿交易会按顺序从孤儿交易池中取出来,再次进行账本验证。如果验证通过,进入下一流程,没有验证通过,继续放回孤儿交易池中。如果重复以上两个步骤达到一定次数,仍然验证不通过,这样的孤儿交易,将会被丢弃。

孤儿交易:是指交易数据能通过验证,交易nonce值未被其他交易使用,但不能连上已确认的交易的nonce值,会被判定为孤儿交易。例如,某地址已经确认出块的最近一笔交易的nonce值是1,而目前收到的交易nonce值是3,则该笔交易会被认为是孤儿交易。

转发交易:在完成未确认交易存储流程之后,交易管理模块会将收到的交易进行转发,让更多节点也可以对交易进行验证,从而让交易能更快地被打包。

交易打包

交易打包操作是由共识模块发起,当交易管理模块收到共识模块的交易打包指令之后,会开始对交易进行打包。共识模块发出的交易打包指令中,会包含执行打包的截止时间、当前区块可打包交易数据量大小、交易数量等限制条件,这些限制条件是为了保证交易管理模块能在规定的出块时间内,正常出块。

如下流程图是交易打包环节的主要流程,下面对这些主要流程进行讲解。

接收交易打包指令:交易管理模块接收到共识模块的交易打包指令,启动交易打包环节流程。从待打包交易池中提取一组交易:交易管理模块首先会从待打包交易池中取出一组交易,进行打包。之所以是提取一组交易进行后面的账本验证,是因为批量进行账本验证,可以减少模块间的RPC调用。同时,每个区块可以打包的交易数量是有限制的,在不包含系统交易的情况下,默认一个区块可以打包的交易数量为1万笔。在进行账本验证时,只要进行批量验证的所有交易中,有一条未验证通过,其余交易都要重新放回待打包交易池中,并重新进行验证,未验证通过的交易将会从待打包交易池中移除。如果本组交易全部验证通过,则进入下一流程。各模块的交易业务验证:调用其他模块的交易业务验证器,让其他模块进行交易业务的验证。只要进行批量验证的所有交易中,有一条未验证通过,其余交易都要重新放回待打包交易池中,并重新开始流程,未验证通过的交易将会从待打包交易池中移除。如果本组交易全部验证通过,则进入下一流程。放入可打包交易集合:将全部通过交易业务验证的一组交易,放入到可打包交易集合中。放入到可打包交易集合中的交易,将等待进行交易存储。后面章节将对交易存储环节进行讲解。

区块交易验证

区块交易验证是指,当交易管理模块收到区块管理模块,发出的对区块中的交易进行验证的指令后,交易管理模块对一个完整区块中的所有交易进行的验证操作。区块交易验证是为之后的出块做准备,以保证区块中的所有交易正确有效。

区块交易验证环节,交易管理模块以一个区块为单元,如果一个区块中有一条交易验证不通过,整个区块交易验证都将被判定为失败,直接返回不通过。交易管理模块处理的每条交易,有可能是已经存储在本地的未确认交易数据库,也可能未存储在本地

如下流程图是区块交易验证环节的主要流程,下面我们对这些关键流程进行介绍。

接收区块交易验证指令:接收区块管理模块发起区块交易验证的指令,开启区块交易验证流程;判断交易是否已确认:检查区块中的所有交易是否已经被出块确认,如果有一条交易已被确认,则直接返回验证不通过给共识模块(共识模块负责出块);判断是否在未确认交易数据库:判断区块中的所有交易是否在本地存储的未确认交易数据库中,如果不存在,则需要进行交易基础验证;会出现有交易不在未确认交易数据库中的情况,是因为收到的区块可能是其他节点发送来的。账本验证:进行账本验证,如果区块中有一条交易验证不通过,则直接返回验证不通过给共识模块;各模块的交易业务验证:进行各模块的交易业务验证,如果区块中有一条交易验证不通过,则直接返回验证不通过给共识模块;返回验证通过给共识模块:如果以上区块中的所有交易,全部验证通过,则返回验证通过给共识模块。

如果是跨链交易,还要核对跨链交易的跨链验证结果;如果是智能合约交易,需要执行一次智能合约,然后比较已有的结果,和新产生的结果是否一致,最后进行分组,调用模块统一验证器进行验证。相关细节将会在跨链模块和智能合约模块进行阐述。

区块交易存储

区块交易存储是指,区块的所有交易已经通过验证,接下来将对区块中的交易进行保存。区块交易存储环节由区块管理模块发起,交易管理模块执行。

如下流程图是区块交易存储环节的主要流程,下面我们对这些关键流程进行讲解。

接收区块交易存储指令:交易管理模块接收区块管理模块发起的区块交易存储指令,开启流程;保存交易到已确认交易数据库:对通过区块交易验证的区块交易进行存储;调用交易业务提交接口:分别调用所有交易的业务提交接口,对交易的业务数据进行处理;提交账本数据:提交区块交易到账本模块,对账本和nonce值进行保存;删除未确认交易数据库中对应的交易:完成如上流程之后,将未确认交易数据库中的交易进行删除。

如果中途有操作失败,将对已操作成功的流程进行回滚,回滚区块交易的流程就是倒序执行区块交易存储的流程。

其他

通用交易协议

NULS采用通用的交易协议格式,主要由以下字段组成:

Len Fields Data Type Remark 2 type uint16 交易类型 4 time uint32 时间,精确到秒 ? txData VarByte 业务数据 ? coinData VarByte 资产数据 ? remark VarString 备注 ? sigData VarByte 包含公钥和签名数据

type

用于区分不同的业务交易,每个模块可以注册多个交易类型,每个交易类型可以有不同的验证逻辑、处理逻辑。取值范围是1~65535。 不同的交易类型不应该设置重复的type,系统不允许重复的type进行注册。 系统支持扩展大于100的type。

time

交易发生的时间,精确到秒,不做强制限制,取值范围可以是任何uint32内的数字。

txData

用于扩展业务数据,账本不验证txData内容,这里可以存放任何数据。目前NULS内置的交易类型中的业务数据,都是存储在txData字段中。业务模块在注册了交易类型后,会提供三个接口来验证和处理txData中的数据(verifyTx,commitTx,rollbackTx)。

CoinData

交易的资产数据,NULS目前定义了一套通用的CoinData格式,具体如下:

froms://List<CoinFrom>格式,
tos://List<CoinTo>格式

注:支持多个账户同一笔交易中,转出不同资产到不同的账户中

CoinFrom结构[70]

address: //byte[24] 账户地址
assetsChainId://uint16 资产发行链的ID
assetsId: //uint16 资产
amount: //uint128,转出数量
nonce : //byte[8] 交易顺序号,前一笔交易的hash的后8个字节
locked : //byte 是否是锁定状态(locktime:-1),1代表锁定,0代表非锁定

CoinTo结构[68]

address: //byte[24],目标地址
assetsChainId://uint16 资产发行链的ID
assetsId: //uint16 资产
amount : //uint128,转账金额
lockTime://uint32,解锁高度或解锁时间,-1为永久锁定

手续费

froms-tos剩余的部分就是手续费(模型中支持多种资产缴纳手续费,约束条件由经济模型设计决定)

remark

备注,此内容的数据,会通过utf-8编码转换为字符串,显示在浏览器和钱包中。也可以用remark字段进行交易的业务扩展。

sigData

签名数据支持多个账户的签名,每个签名包括四个部分:公钥长度、公钥、签名数据长度、签名数据。

交易的Hash计算

将交易除sigData外的数据进行序列化,获得完整的字节数组。使用Sha-256对数据进行两次计算,得到32位的Hash值。

模块服务

ForwardTxMessage

消息说明:节点接收并验证网络其他节点发送的新交易之后,转发交易hash给其他节点cmd:newHash

Length Fields Type Remark 32 hash byte[] transaction hash

消息处理

newHash处理该消息,发送索要该完整交易的消息

BroadcastTxMessage

消息说明:向链内其他节点广播完整交易cmd:receiveTx

Length Fields Data Type Remark 2 type uint16 交易类型 4 time uint32 交易时间 ? txData VarByte 交易数据 ? coinData VarByte 交易输入和输出 ? remark VarString 备注 ? transactionSignature VarByte 交易签名

消息处理

receiveTx进入新的交易流程

GetTxMessage

消息说明:收到其他节点转发的交易hash,向发送者索要完整交易cmd:askTx

Length Fields Type Remark 32 hash byte[] transaction hash

消息处理

askTx处理该消息,发送完整交易回去

交易管理模块启动时需要依赖的模块