Transactions是什么
我们讲到了Libra是一个分布式账本,存储着账本状态,从账本状态里面,我们可以获取现在每一个账户的资金情况和存储的相应资源。
而这个账本状态就是通过执行Transactions来进行改变的。
用户定义的Transactions是通过Move来编写的,目前来说Libra处于早期版本,只有有限的功能开放给Move语言(不如用户不能自定义资源类型等),但是随着Libra的发展,我相信Move语言将会得到更多的功能支持。
Transactions运行的基础条件
在Libra中,我们运行Transactions,这里面包含着如下几个基础条件:
-
账本的初始状态。所有的验证者节点都必须接受账本的初始状态,并在该初始状态之上进行后续的验证操作。
在账本的初始状态中,我们需要定义Libra的核心组件(比如: 账户的逻辑,交易的验证,验证者的选择,Libra币等),这些核心组件都是以Move modules的形式存在的。
同样的,要保证交易的执行,我们还需要一个初始账户,和初始的验证节点,这些都是需要在初始状态中定义。
为了简便起见,我们假设账本的初始状态为空,然后通过执行一个Transaction T0来生成上面提到的modules。注意这里的T0是一个特殊的交易,他和普通的交易不一样,他只能通过配置来实现。
-
交易的确定性。Libra的所有交易都必须是确定的并且和重复发送的。就是说如果知道了给定交易的输入,则必定能得到相同的输出。
前面的文章我也提到过函数式编程,这里的作用和函数式编程很像。交易没有任何副作用。我们可以通过重新执行交易的历史记录来得到和现在账本状态一样的账本。
-
可度量的。同其他区块链一样,为了防止DDOS攻击,Libra引入了gas的概念。每个交易都必须花费一定的gas,这样可以有效的阻止无效的交易产生。
Libra的gas和其他的区块链的gas有一点不同的是,Libra的gas只是用来减少系统在高负载的情况运行的可能性,对于正常交易来说,Libra的gas花费很少。
gas包括gas价格和gas数目。验证节点会优先执行gas价格高的交易,而丢弃gas价格低的交易,这样就可以有效的防止系统在高负载的情况下运行。
同样的,我们也有一个最大的gas数目,如果交易执行超过了gas的最大数目,则Libra虚拟机会停止执行,交易结果不会写入到账本状态中,但是会被记录在交易记录中。
-
资产特性。我们知道Libra币是要与真实世界的金钱挂钩的,所以Libra币必须不能重复,不能丢失,也不能未授权被使用。这些特征都是通过Move虚拟机来实现的。
Transactions的结构
在Libra中,一个Transaction就是一个签名过的数据,它包含如下内容:
-
发送者地址。交易发送者的地址,VM可以通过读取该地址内的LibraAccount.T资源来获取该地址的序列号,认证密钥和余额等信息。
-
发送者公钥。和该公钥相匹配的私钥用来对交易进行签名。上面提到的LibraAccount.T资源里面的认证密钥就是该公钥的hash值。
-
程序。程序是一个Move字节码的脚本代码,有可能还包括所需额参数列表。
-
最大gas数目。VM允许的最大gas数目。
-
序列号。 序列号是一个无符号整数,每次交易后,账户的序列号都会加1。序列号主要用来防止重放攻击。
执行Transactions
执行Transactions通常来说,有6个步骤。
-
检查签名。签名主要检查Transaction是否跟发送者的public key和实际数据相匹配。
-
运行prologue。运行prologue阶段主要检查交易的发送者(是否有足够的Libra币),该交易是否是一个重放交易。所有的功能都是通过LibraAccount模块的prologue过程来实现的。
-
验证交易脚本和模块。VM会使用Move字节码验证器来验证交易的脚本和模块是不是有效的(类型安全,引用安全,资源安全等)。
-
发布模块。程序里面的模块都会被发布到交易发起者的账户中。注意:模块的名字必须是唯一的,否则交易会失败。
-
运行交易脚本。VM构建脚本参数,并运行交易脚本。如果交易成功,将会把交易和相关的事件写入账本状态中。如果交易失败,账本状态不会改变。
-
运行epilogue。 最后VM运行epilogue来计算相应的gas并减掉相对应的数值,并增加发送者账户的序列号。和prologue一样,epilogue也是LibraAccount模块的一个过程。