move language learning (basics)

Move language

Move is a new programming language designed to provide a secure and programmable foundation for the Libra blockchain. Accounts in the Libra blockchain are composed of any number of Move resources and Move modules. Every transaction submitted to the Libra blockchain uses transaction scripts written by Move to encode its logic.

The transaction script updates the global state of the blockchain by calling the procedures declared by the module.

Move trading script

Each Libra transaction contains a Move transaction script, which encodes the logic that the validator performs on behalf of the client (for example, moving Libra from A's account to B's account).

Transaction scripts interact with Move resources published in the global storage of the Libra blockchain by calling the procedures of one or more Move modules.

The transaction script is not stored in the global state, and other transaction scripts cannot call it. It is a one-time procedure.

Move modules

Move modules define rules for updating the global state of the Libra blockchain. Modules are equivalent to smart contracts in other blockchains. It declares the types of resources that can be published under the user's account. Each account in the Libra blockchain is a container for any number of resources and modules.

Module is mainly used to declare structure types (including resources, which are a special structure) and procedures.

The Move module's procedures define the rules for creating, accessing, and destroying its declared types.

modules are reusable. Structural types declared in one module can use structural types declared in another module, and procedures declared in one module can call public procedures declared in another module. A module can call procedures declared in other Move modules. Transaction scripts can call any public procedures of the published module.

Finally, Libra users will be able to publish modules using their own accounts.

Move resources

The main feature of Move is the ability to define custom resource types. Resource types primarily encode digital assets.

Resources are everywhere in Libra. They can be stored as data structures, passed as parameters to procedures, returned from procedures, etc.

The Move type system provides special security guarantees for resources. Move resources can never be copied, reused or discarded. Resource types can only be created or destroyed by the module that defines the type. This is enforced by the Move virtual machine through bytecode verification. The Move virtual machine will refuse to run code that has not passed the bytecode verifier.

Libra currency is implemented through the resource type of LibraCoin.T. Like other resources, LibraCoin.T is also a resource.

Write a Move program

In this section I will introduce how to use Move IR to write transaction scripts and modules. IR is a preview (unstable) version of the upcoming Move source language. Move IR is a thin syntax layer on Move bytecode for testing bytecode verifiers and virtual machines, and it's not particularly developer-friendly. It's high enough to write human-readable code, but low enough to compile directly to Move bytecode.

Write transaction scripts

Users request updates to the Libra blockchain's global storage through transaction scripts. Two important resources appear in almost all transaction scripts: LibraAccount.T and LibraCoin.T resource types. LibraAccount is the name of the module, and T is the name of the resource declared by the module. This is a common naming convention in Move. The "main" type declared by a module is usually called T.

When we say that a user "owns an account at address 0xff on the Libra blockchain," we mean that address 0xff owns an instance of the LibraAccount.T resource. There is a LibraAccount.T resource for each non-null address. This resource stores account data such as serial numbers, authentication keys, and balances. Any part of the Libra system that wants to interact with an account must do so by reading data from the LibraAccount.T resource or by calling the LibraAccount module's procedures.

The account balance is a resource of type LibraCoin.T. This is the type of Libra currency. Like any other Move resource, this type is a first-class citizen in the language.

Resources of type LibraCoin.T can be stored in program variables, passed between procedures, etc.

// 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: LibraCoin.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;
}

There is an unfortunate problem with the transaction script above - if there is no account under the payee, it will fail. We'll fix this by modifying the script to create an account for the payee if it doesn't already exist.

// 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: 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;
}

In the following example, we will use a transaction script to pay multiple recipients, not just one.

// 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: LibraCoin.T;
  let coin2: 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;
}

Write your own Modules

The above transaction script uses existing LibraAccount and LibraCoin modules, so how do we write our own Move modules?

Consider this scenario: B will create an account at address a in the future. A wants to "earmark" some funds for B so that he can deposit them into his account once created. However, if B never created the account, she also wants to be able to recover the funds herself.

In order to solve this problem of A, we will write a module EarmarkedLibraCoin:

声明一个新的资源类型EarmarkedLibraCoin.T,该资源类型包装了Libra coin和收件人地址。
1.允许A创建此类类型并将其发布到她的帐户下(创建过程)。
2.允许B声明资源(claim_for_recipient过程)。
3.允许拥有EarmarkedLibraCoin.T的任何人销毁它并获得相应的coin(拆包程序)。
// 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: 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: LibraCoin.T, recipient: address) {
    let t: Self.T;

    // Construct or "pack" a new resource of type T. Only procedures of the
    // `EarmarkedLibraCoin` module can create an `EarmarkedLibraCoin.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<T>(move(t));
    return;
  }

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

    // Remove the earmarked coin resource published under `earmarked_coin_address`.
    // If there is no resource of type T published under the address, this will fail.
    t = move_from<T>(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(): Self.T acquires T {
    
    
    let t: Self.T;
    let sender: address;

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

  // Extract the Libra coin from its wrapper and return it to the caller.
  public unwrap(t: Self.T): LibraCoin.T {
    
    
    let coin: 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);
  }

}

A can create a dedicated coin for B by creating a transaction script that calls create on B's address a and the LibraCoin.T she owns. After a is created, B can claim coins by sending a transaction from a. This will call claim_for_recipient, pass the result to unwrap, and store the returned LibraCoin wherever he wishes.

If B takes too long to create an account under a, and A wants to get its funds back, it can do so by using Claim_for_creator and then unwrap.

おすすめ

転載: blog.csdn.net/leaning_java/article/details/126371557