资讯专栏INFORMATION COLUMN

Facebook:Libra之Move入门

Julylovin / 2471人阅读

摘要:未来,我们将拥有更高层次的源语言。这种源语言将被设计成安全而容易地表达常见的惯用语和编程模式。目前,源语言还处于开发的早期阶段,我们还没有为它准备好发布时间表。

Move是一种新的编程语言,它为Libra区块链提供了一个安全和可编程的基础。Libra区块链中的账户是任意数量Move资源及Move模块的容器。提交至Libra 区块链的每笔交易,都是用以Move语言编写的交易脚本对其逻辑进行编码。这个交易脚本可调用模块声明的过程来更新区块链的全局状态。

在本指南的第一部分内容中,我们将概括性地介绍Move语言的主要特点:

Move交易脚本启用可编程交易

Move模块允许组合型智能合约

Move语言具有第一类资源

对于求知欲强的读者来说,Move编程语言的技术论文包含了更多关于该语言的细节信息:

在本指南的第二部分,我们将向你展示如何在Move中间代码优化(Move intermediate representation)的环境下编写自己的应用。初始的测试网并不支持自定义Move程序,但这些功能可供你在本地试用。

Move语言的主要特点 Move交易脚本启用可编程交易

每个Libra交易都包含一个Move交易脚本,该脚本对验证者应代表客户端执行的逻辑进行编码(例如,将Libra币从A的账户移动到B的账户);

交易脚本通过调用一个或多个Move模块的过程,与Libra区块链全局存储中发布的Move资源进行交互;

交易脚本不会存储在全局状态当中,因此其它交易脚本无法调用它,这是一个一次性程序;

我们在编写交易脚本时,提供了几个交易脚本示例;

Move 模块允许组合型智能合约

Move模块定义了更新Libra区块链全局状态的规则。Move模块与其它区块链中的智能合约一样都是解决相同的问题。模块声明了可在用户账户下发布的资源类型。Libra区块链中的每个账户都是任意数量资源和模块的容器。

模块声明结构类型(包括资源,这是一种特殊的结构)以及过程;

Move模块的过程,定义了创建、访问以及销毁其声明类型的规则。

模块是可重用的。一个模块中声明的结构类型,可以使用另一个模块中声明的结构类型,并且一个模块中声明的过程可以调用另一个模块中声明的公共过程。模块可以调用在其他Move模块中声明的过程。交易脚本可以调用已发布模块的任何公共过程。

最终,Libra用户将能在自己的帐户下发布模块。

Move语言具有第一类资源

Move的主要功能是定义自定义资源类型。资源类型用于编码具有丰富可编程性的安全数字资产。

资源是语言中的普通值,它们可存储为数据结构,作为参数传递给procedure,从procedure返回,等等;

Move类型系统为资源提供了特殊的安全保障。Move资源不能复制、重复使用或丢弃。资源类型只能由定义该类型的模块创建或销毁。这些保障是由Move虚拟机通过bytecode验证静态地强制执行的。Move虚拟机将拒绝运行尚未通过bytecode检验器的代码;

ibra币作为一种资源类型,其名称为LibraCoin.T。LibraCoin.T在语言中没有特殊的地位,每种资源都享有相同的保护待遇;

Move语言底层 Move中间代码优化(Move Intermediate Representation)

本节介绍如何使用Move IR 编写交易脚本以及模块。先提醒下读者,这个Move IR 目前还处于早期的阶段,因此并不稳定,它也是接下来会介绍的Move 源语言的前身(有关详细信息,请参阅未来开发者体验部分内容)。Move IR是在Move bytecode之上的一个很薄的语法层,用于测试bytecode验证者以及虚拟机,它对开发者而言不是特别友好。Move IR足以用于编写人类可读的代码,但无法直接转换为Move bytecode。尽管Move IR还是有些粗糙,我们还是对这个Move语言感到兴奋,并希望开发者们可以尝试一下它。

我们会介绍关于Move IR的重要演示代码段,并鼓励读者通过在本地编译、运行和修改示例来了解它。libra/language/README.md 以及 libra/language/ir_to_bytecode/README.md 的说明文件解释了如何执行此操作。

编写交易脚本

正如我们在Move交易脚本启用可编程交易部分内容中所解释的,用户编写交易脚本,以请求对Libra区块链的全局存储进行更新。几乎任何交易脚本中都会出现两个重要的构建块:LibraAccount.T和LibraCoin.T资源类型,LibraAccount是模块的名称,T是该模块声明的资源的名称。这是在Move中常见的命名规则。模块声明的“main”类型通常命名为T.

当我们说一个用户“在Libra区块链上拥有一个地址为0xff的帐户”时,我们的意思是,这个0xff地址持有LibraAccount.T资源的实例。每个非空地址都有一个LibraAccount.T资源。此资源存储账户数据,如序列号、验证密钥和余额。要与帐户交互的Libra系统的任何部分,都必须通过从LibraAccount.T资源中读取数据或调用LibraAccount模块的过程。

账户余额是LibraCoin.T的一种类型资源。正如我们在Move具有第一类资源部分内容中解释的,这是Libra币的一种类型。这种类型是语言中的“第一类公民”,就像其他Move资源一样。LibraCoin.T的类型的资源可以存储在过程变量中,在过程之间传递,等等。

我们鼓励感兴趣的读者在 libra/language/stdlib/modules/directory 目录下检查LibraAccount和LibraCoin模块中这两个关键资源的Move IR定义,

现在,让我们看看程序员如何在一个交易脚本中与这些模块和资源交互。

// Simple peer-peer payment example.

// Use LibraAccount module published on the blockchain at account address
// 0x0...0 (with 64 zeroes). 0x0 is shorthand that the IR pads out to
// 256 bits (64 digits) by adding leading zeroes.
import 0x0.LibraAccount;
import 0x0.LibraCoin;
main(payee: address, amount: u64) {
  // The bytecode (and consequently, the IR) has typed locals.  The scope of
  // each local is the entire procedure. All local variable declarations must
  // be at the beginning of the procedure. Declaration and initialization of
  // variables are separate operations, but the bytecode verifier will prevent
  // any attempt to use an uninitialized variable.
  let coin: R#LibraCoin.T;
  // The R# part of the type above is one of two *kind annotation* R# and V#
  // (shorthand for "Resource" and "unrestricted Value"). These annotations
  // must match the kind of the type declaration (e.g., does the LibraCoin
  // module declare `resource T` or `struct T`?).

  // Acquire a LibraCoin.T resource with value `amount` from the sender"s
  // account.  This will fail if the sender"s balance is less than `amount`.
  coin = LibraAccount.withdraw_from_sender(move(amount));
  // Move the LibraCoin.T resource into the account of `payee`. If there is no
  // account at the address `payee`, this step will fail
  LibraAccount.deposit(move(payee), move(coin));

  // Every procedure must end in a `return`. The IR compiler is very literal:
  // it directly translates the source it is given. It will not do fancy
  // things like inserting missing `return`s.
  return;
}

此交易脚本存在着一个问题:如果地址接收方没有账户,它将执行失败。我们将通过修改脚本来解决这个问题,为接收方创建一个账户(如果接收方还不具备账户的话)。

// A small variant of the peer-peer payment example that creates a fresh
// account if one does not already exist.

import 0x0.LibraAccount;
import 0x0.LibraCoin;
main(payee: address, amount: u64) {
  let coin: R#LibraCoin.T;
  let account_exists: bool;

  // Acquire a LibraCoin.T resource with value `amount` from the sender"s
  // account.  This will fail if the sender"s balance is less than `amount`.
  coin = LibraAccount.withdraw_from_sender(move(amount));

  account_exists = LibraAccount.exists(copy(payee));

  if (!move(account_exists)) {
    // Creates a fresh account at the address `payee` by publishing a
    // LibraAccount.T resource under this address. If theres is already a
    // LibraAccount.T resource under the address, this will fail.
    create_account(copy(payee));
  }

  LibraAccount.deposit(move(payee), move(coin));
  return;
}

让我们看一个更复杂的例子。在这个例子中,我们将使用交易脚本为多个接收方进行支付(而不是单个接收方)。

// Multiple payee example. This is written in a slightly verbose way to
// emphasize the ability to split a `LibraCoin.T` resource. The more concise
// way would be to use multiple calls to `LibraAccount.withdraw_from_sender`.

import 0x0.LibraAccount;
import 0x0.LibraCoin;
main(payee1: address, amount1: u64, payee2: address, amount2: u64) {
  let coin1: R#LibraCoin.T;
  let coin2: R#LibraCoin.T;
  let total: u64;

  total = move(amount1) + copy(amount2);
  coin1 = LibraAccount.withdraw_from_sender(move(total));
  // This mutates `coin1`, which now has value `amount1`.
  // `coin2` has value `amount2`.
  coin2 = LibraCoin.withdraw(&mut coin1, move(amount2));

  // Perform the payments
  LibraAccount.deposit(move(payee1), move(coin1));
  LibraAccount.deposit(move(payee2), move(coin2));
  return;
}

好了,到这里,我们就结束了交易脚本部分的展示,有关更多例子,包括初始测试网中支持的交易脚本,请参阅libra/language/stdlib/transaction_scripts

编写模块

现在,我们把注意力集中到编写自己的Move模块上,而不仅仅是重用现有的LibraAccount和LibraCoin模块。考虑这样一个情况:Bob将来某个时候将在地址a创建一个帐户,Alice想要“指定”Bob一笔资金,以便他可以在账户创建后将其存入自己的帐户。但她也希望,如果Bob一直不创建一个账户,她就能收回这笔资金。

为了解决Alice的这个问题,我们将编写一个专用的EarmarkedLibraCoin模块,它会:

声明一个新的资源类型EarmarkedLibraCoin.T,它封装了一笔Libra币以及接收方地址;

允许Alice创建此类型资源,并在其账户下发布(create过程);

允许Bob声明资源(claim_for_recipient过程);

允许任何拥有EarmarkedLibraCoin.T资源类型的人销毁它,并获取底层的Libra币(unwrap过程);

Allows anyone with an EarmarkedLibraCoin.T to destroy it and acquire the underlying coin (the unwrap procedure).

// A module for earmarking a coin for a specific recipient
module EarmarkedLibraCoin {
  import 0x0.LibraCoin;

  // A wrapper containing a Libra coin and the address of the recipient the
  // coin is earmarked for.
  resource T {
    coin: R#LibraCoin.T,
    recipient: address
  }

  // Create a new earmarked coin with the given `recipient`.
  // Publish the coin under the transaction sender"s account address.
  public create(coin: R#LibraCoin.T, recipient: address) {
    let t: R#Self.T;

    // Construct or "pack" a new resource of type T. Only procedures of the
    // `EarmarkedCoin` module can create an `EarmarkedCoin.T`.
    t = T {
      coin: move(coin),
      recipient: move(recipient),
    };

    // Publish the earmarked coin under the transaction sender"s account
    // address. Each account can contain at most one resource of a given type; 
    // this call will fail if the sender already has a resource of this type.
    move_to_sender(move(t));
    return;
  }

  // Allow the transaction sender to claim a coin that was earmarked for her.
  public claim_for_recipient(earmarked_coin_address: address): R#Self.T {
    let t: R#Self.T;
    let t_ref: &R#Self.T;
    let sender: address;

    // Remove the earmarked coin resource published under `earmarked_coin_address`.
    // If there is resource of type T published under the address, this will fail.
    t = move_from(move(earmarked_coin_address));

    t_ref = &t;
    // This is a builtin that returns the address of the transaction sender.
    sender = get_txn_sender();
    // Ensure that the transaction sender is the recipient. If this assertion
    // fails, the transaction will fail and none of its effects (e.g.,
    // removing the earmarked coin) will be committed.  99 is an error code
    // that will be emitted in the transaction output if the assertion fails.
    assert(*(&move(t_ref).recipient) == move(sender), 99);

    return move(t);
  }

  // Allow the creator of the earmarked coin to reclaim it.
  public claim_for_creator(): R#Self.T {
    let t: R#Self.T;
    let coin: R#LibraCoin.T;
    let recipient: address;
    let sender: address;

    sender = get_txn_sender();
    // This will fail if no resource of type T under the sender"s address.
    t = move_from(move(sender));
    return move(t);
  }

  // Extract the Libra coin from its wrapper and return it to the caller.
  public unwrap(t: R#Self.T): R#LibraCoin.T {
    let coin: R#LibraCoin.T;
    let recipient: address;

    // This "unpacks" a resource type by destroying the outer resource, but
    // returning its contents. Only the module that declares a resource type
    // can unpack it.
    T { coin, recipient } = move(t);
    return move(coin);
  }

}

Alice可以为Bob创建一种预先安排的币,方法是创建一个交易脚本,调用Bob的地址a的create,以及她所拥有的LibraCoin.T。一旦地址 a 被创建,Bob就可以通过从 a 发送一个交易来领取这笔币,这会调用claim_for_recipient,将结果传递给unwrap,并将返回的LibraCoin存储在他希望的任何地方。如果Bob在创建 a 的过程中花费的时间太长,而Alice想要收回她的资金,那么Alice可以使用 claim_for_creator,然后unwrap

观察型读者可能已经注意到,本模块中的代码对LibraCoin.T的内部结构不可知。它可以很容易地使用泛型编程(例如,resource T { coin: AnyResource, ... })编写。我们目前正致力于为Move增加这种参量多态性。

未来开发者体验

在不久的将来,Move IR将稳定下来,编译和验证程序将变得更加对用户友好。此外,IR源的位置信息将被跟踪,然后传递给验证者,以使错误消息更容易排错。然而,IR将继续作为测试Move bytecode的工具。它是作为底层bytecode的一种语义透明的表示。
为了允许有效的测试, IR编译器需生成错误的代码,这些代码将被bytecode验证者拒绝,或在编译器的运行时失败。而对用户友好的源语言则是另一种选择,它应该拒绝编译在管道的后续步骤中将失败的代码。

未来,我们将拥有更高层次的Move源语言。这种源语言将被设计成安全而容易地表达常见的Move惯用语和编程模式。由于Move bytecode是一种新语言,而Libra区块链是一种新的编程环境,我们对应支持的习惯用法和模式的理解,仍在不断发展。目前,源语言还处于开发的早期阶段,我们还没有为它准备好发布时间表。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/63815.html

相关文章

  • gulp 入门配置

    摘要:使用进行本地开发自动刷新,配合对和进行压缩处理,最终打包到目录安装以及其他插件代码示例安装依赖本地安装所用到的地方同步执行任务获取组件是否开启浏览器提示端口监听目录下所有文件清楚目录下所有文件定 使用 browser-sync 进行本地开发自动刷新,配合gulp-uglify、gulp-minify-css对js和css进行压缩处理,最终打包到dist目录 安装gulp 以及其他插件 ...

    AlienZHOU 评论0 收藏0
  • ECMAScript 6入门读书笔记二

    摘要:变量的解构赋值更加便利的从匿名对象或者数组中,对变量进行赋值数组的解构赋值基本样式,右边数据类型必须是可迭代的类型。 变量的解构赋值 更加便利的从匿名对象或者数组中,对变量进行赋值; 数组的解构赋值 基本样式,右边数据类型必须是可迭代的类型。 let a = 1, b = 2, c = 3; //等价于 //let [a, b, c] = [1, 2, 3]; 对...

    wawor4827 评论0 收藏0
  • ECMAScript6标准入门(一)新增变量与数据结构

    摘要:一简介与的关系是的规格,是的一种实现另外的方言还有和转码器命令行环境安装直接运行代码命令将转换成命令浏览器环境加入,代码用环境安装,,根目录建立文件加载为的一个钩子设置完文件后,在应用入口加入若有使用,等全局对象及上方法安装 一、ECMAScript6 简介 (1) 与JavaScript的关系 ES是JS的规格,JS是ES的一种实现(另外的ECMAScript方言还有Jscript和...

    Tangpj 评论0 收藏0
  • 初窥基于 react-art 库的 React Native SVG

    摘要:语法更近似于移动端。当参数为两个时,等同于,绘制光滑二次贝塞尔曲线。有些精通的同学这时候可能就要问我了,不对啊,二次贝塞尔曲线和光滑三次贝塞尔曲线的参数都是个,你这里没有光滑三次啊因为开发的同学留坑没写了呀微笑。和则是用于指定旋转的原点。 技术背景 在移动应用的开发过程中,绘制基本的二维图形或动画是必不可少的。然而,考虑到Android和iOS均有一套各自的API方案,因此采用一种更普...

    xiaowugui666 评论0 收藏0
  • C语言实现入门级小游戏——三子棋(万字长篇)

    摘要:今天博主将为大家带来语言入门级小游戏三子棋井字棋的详细介绍,希望这篇文章对大家能有帮助。在这里,我们使用函数来实现这一功能。        今天博主将为大家带来C语言入门级小游戏——三子棋(井字棋)的详细介绍,希望这篇文章对大家能有帮助 。       在编程的学习过程中,编写一些中这样...

    meteor199 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<