官方文档翻译《The Libra Blockchain》之执行交易(二)

译自:官方文档翻译《The Libra Blockchain》之执行交易,第三小节。本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可。

执行交易

执行一次交易在 VM 内部按照六个步骤有序进行。执行交易与更新账本的两个动作是分开的。先尝试在一部分的序列上执行看交易是否能达成通过。由于执行过程是封闭的,因不会有外界插手所引致的副作用产生。随后如果达成协议,则将其输出写入账本历史记录。

执行一次交易需要下面六个步骤:

(1)检查签名。交易的签名必须与发送者的公钥和交易数据相匹配。这是交易本身的步骤,不需要从发送者账号上读取任何数据。
(2)运行初始化。初始化程序认证交易发送者的身份,确保发送者账上有足够的 Libra 币来支付这次交易的气化值最大数,另外还检查这次交易不是重复执行之前的交易。所有的这些检查用 Move 写成,也就是所谓的初始化过程,属于 LibraAccount 模块的方法(译注:或说过程 procedure)。执行初始化期间禁止气化值计费。具体来说,初始化工作有以下这些:

  • 检查发送者公钥的 hash 是否和发送者账号的认证密钥相等。即使与该帐户关联的密钥没有对应,VM 也会错误地接受签名加密有效的交易。
  • 判断是否 gas_price * max_gas_amount <= sender_account_balance,否则 VM 会最终执行失败,因为气化值不够支付。
  • 确保交易的序列号与用户账户的序列号一致,否则可导致非法执行者再次执行交易(例如,Alice 转账给 Bob 10 个 Libra 币,但是 Bob 之后可以再次执行交易,那当然是不行的)

(3)验证交易脚本和模块。一旦交易初始化通过,VM 的 Move 字节码验证器就对交易脚本和模块进行格式检查。在 Move 代码被实际发布或者运行之前,字节码验证器检查相关必要的属性如 类型安全、引用安全(看是否有空的引用等)和资源安全(例如不能复制资源和重用资源,或者任意消耗)

(4)发布模块。程序中每个模块会发布到发送者账户的交易中。禁止重复的模块名称——例如,如果交易尝试发布名称为 M 的模块到一个账户,但该账户已经有 M 的模块了,那会导致失败。

(5)运行交易脚本。VM 把交易参数绑定到交易脚本的正式参数中并执行之。如果执行脚本成功,脚本执行的写操作和触发的时间都提交到全局状态。如果脚本执行失败(例如没有足够的气化值导致了失败),那么不会提交任何内容到全局状态。

(6)运行收尾程序(epilogue)。最后 VM 运行交易的收尾程序,向用户收取所用的气化值,并增加发送方帐号序列号。就像初始化(prologue)一样,收尾程序也是 Move LibraAccount 模块下的一个过程,同时不会进行气化值的计费。如果跳过了步骤(2)、步骤(3)、步骤(4)或步骤(5)失败,收尾程序这一步骤依然会运行。收尾程序和初始化会一起工作,以确保在账本历史中的所有交易都是有计费的。如果没有执行步骤(2),那么交易不会记录到账本历史中。这类交易虽然执行了但不会被记录。虽然有些交易跳过了步骤(2),但是初始化程序会确保账户拥有足够的 Libra 币以支付交易最大气化值。即使交易过程消耗尽了气化值,收尾程序也能收取这个最大的气化值费用。

Move 编程语言

如上所述,一个交易,本质就是一段 Move 字节码程序,再加上一层认证的包装器在外面。Move 是根据 Libra 协议而诞生的新型编程语言。在该系统中 Move 扮演以下三个角色。

  1. 通过交易脚本实现灵活的交易;
  2. 模块 modules 相当于类,是用户自定义的数据类型和代码,智能合约就是通过 module 引入;
  3. 实现 Libra 协议的可配置以及扩展性。

Move 语言的一大作用就是允许我们自定义资源类型,由线性逻辑所启发的语义(Linear Logic,译注:线性逻辑表示,如果某个变量符合某种特定的“结构”,它就内含一种规则:必须且只能使用一次。该特性也是 Rust 语言特性)。资源类型与一般变量无异,可在编码中作为参数传来传去。不过,Move 类型系统有特殊的机制保证资源的安全。首先资源不能复制,只能移动;其次是只能由模块创建或销毁资源,这确保了 Move VM 能够静态地强制执行检查。Move 语言中的 Libra 货币就是一种资源类型。对比其他区块链如 Ether 和比特币,它们不是那样的概念而是对应特定的状态。

针对 Move 的设计,我们另文再述(参见文档《Move: A language with programmable resources》)。在本节的余下部分中,我们谈谈执行交易的过程中,Move 的关键概念:编写 Move 交易脚本,交易模块,以及用虚拟机来执行 Move 字节码。

编写 Move 应用程序。 不同场合下,Move 可表征为:源码本身,IR 中间层或字节码。目前我们还处于 Move 语言的设计阶段,目的就是为了怎么简单地编写安全可靠的程序。与此同时,开发者可以开发模块而且用 Move IR 编写交易脚本。Move IR 同时具备这样的特点,不但是人们可读的高级源代码,还能直接翻译到低级的 Move 字节码。无论是 Move 源代码还是 Move IR 中间层代码,最终都编译到 Move 字节码,字节码本身符合 Libra 协议。

字节码本身可验证的,之所以那样子设计,理由是:

  • 上述提到的安全性,所有的 Move 的程序必须能够保证到。单纯编译器层面强制保证还不够。攻击者总是可以选择通过字节码直接编写恶意代码来绕过编译器(作为执行交易的一部分运行编译器会使执行变得更慢,更复杂,并且需要验证器信任完整编译器代码库的正确性)。因此,该协议通过字节码验证强制执行 Move 的所有安全保证,从而避免单一信任编译器:类型安全,引用安全和资源安全。
  • Move 的字节码是基于栈的,其所拥有的指令比高级源码少很多。此外,每条指令的意思都很简单,可以通过更少的原子步骤来表示。其好处就是减少了 Libra 协议规范占用空间,并且更容易发现实现错误。

交易脚本。 Libra 协议中的 main 方法就是交易脚本。交易脚本是一段任意的 Move 代码,编译为 Move 字节码程序。任意的代码可以说会比较灵活了,它可以调用账本状态里面多个不同的模块,使用条件语句,执行本地计算等。我们可以我们的需求要制定流程跟逻辑,例如批量转账等的任务,都可灵活实现。

我们希望多数交易脚本只会调用总功能的那个方法,例如 LibraAccount.pay_from_sender(recipient_address, amount),该方法封装了点对点的转账逻辑,有 recipient_address 和交易数量两个参数,就像以太坊的转账那样。

模块。 模块的代码保存在账本状态中。结构体(struct)类型和过程(procedure)皆在模块中定义。一个结构化值可以是保存原始值的数据字段,例如整型等的结构化值。

每个结构必须标记为资源或不受限制(即非资源)。 不受限制的结构体不受上述复制和销毁的限制。 但是不受限制的结构不能包含资源结构(直接或可传递),也不能发布在账本的帐户中。

总的来说,模块、结构体、过程就像面向对象编程中的类、对象和方法。然而有显著不同的是,一个模块可以声明多个结构体类型(或零个)。模块里面声明的数据字段不能供外界访问(也就是没有 public 字段)。模块的过程都是静态的过程,没有实例方法。也没有 this 或 self 指针。Move 模块有点像 ML 风格的模块,但是没有高阶函数。

Move 的模块与以太坊的智能合约有点类似,但不是一回事。以太坊的智能合约不但包含了代码还包含账本里面的数据。在 Libra 模块包含代码值,资源包含数据值。按照面向对象术语讲,以太坊的智能合约像是一个在单独账户地址下的单例对象。模块是创建资源的模版,可以创建不同的资源,然后发布到不同的账号地址。

Move 虚拟机。 Move VM 实现了验证器和字节码编译器。字节码运行于基于栈的虚拟机,通过 a procedure-local operand stack and registers(不会译)。非结构化的流程控制被编码为 goto 和 label 语句。

开发者所编写的,无论是交易脚本还是Move IR 中的模块都会被编译为 Move 字节码。编译器将结构化的流程控制语句(例如 if 条件、循环)转换为非结构化语句,并讲复杂的表达式转换为一小段的字节码指令,那样就可以操控运算栈(an operand stack)。Move VM 验证完交易之后就接着真正开始执行交易。

Move VM 支持的类型或值不算多,它们是:布尔型 booleans,无符号的64位整数,256 位的地址,固定长度的字节数组,结构体(包括资源)和引用。结构体字段不能是引用类型,从而杜绝了账本状态中的引用存储。

Move VM没有堆(heap)。本地数据在堆栈上分配,并在分配过程返回时释放。所有持久花的数据必须存储在账本状态之中。

发布了293 篇原创文章 · 获赞 260 · 访问量 232万+

猜你喜欢

转载自blog.csdn.net/zhangxin09/article/details/96112293