使用 Move 语言快速入门
Move is a new programming language developed to provide a safe and programmable foundation for the Libra Blockchain. An account in the Libra Blockchain is a container for an arbitrary number of Move resources and Move modules. Every transaction submitted to the Libra Blockchain uses a transaction script written in Move to encode its logic. The transaction script can call procedures declared by a module to update the global state of the blockchain.
In the first part of this guide, we will provide a high-level introduction to the key features of the Move language:
- Key Features of Move
Move Transaction Scripts Enable Programmable Transactions
- Each Libra transaction includes a Move transaction script that encodes the logic a validator should perform on the client's behalf (for example, to move Libra from Alice's account to Bob's account).
- The transaction script interacts with Move Modules Allow Composable Smart Contracts
Move modules define the rules for updating the global state of the Libra Blockchain. Modules fill the same niche as smart contracts in other blockchain systems. Modules declare Move Has First Class Resources
- The key feature of Move is the ability to define custom resource types. Resource types are used to encode safe digital assets with rich programmability.
- Resources are ordinary values in the language. They can be stored as data structures, passed as arguments to procedures, returned from procedures, and so on.
- The Move type system provides special safety guarantees for resources. Move resources can never be duplicated, reused, or discarded. A resource type can only be created or destroyed by the module that defines the type. These guarantees are enforced statically by the Move virtual machine via bytecode verification. The Move virtual machine will refuse to run code that has not passed through the bytecode verifier.
- The Libra currency is implemented as a resource type named
LibraCoin.T
.LibraCoin.T
has no special status in the language; every Move resource enjoys the same protections.
Move: Under the Hood
Move Intermediate Representation
This section describes how to write Writing Transaction Scripts
As we explained in Writing Modules
We will now turn our attention to writing our own Move modules instead of just reusing the existing
LibraAccount
andLibraCoin
modules. Consider this situation: Bob is going to create an account at address a at some point in the future. Alice wants to "earmark" some funds for Bob so that he can pull them into his account once it is created. But she also wants to be able to reclaim the funds for herself if Bob never creates the account.To solve this problem for Alice, we will write a module
EarmarkedLibraCoin
which:- Declares a new resource type
EarmarkedLibraCoin.T
that wraps a Libra coin and recipient address. - Allows Alice to create such a type and publish it under her account (the
create
procedure). - Allows Bob to claim the resource (the
claim_for_recipient
procedure). - Allows anyone with an
EarmarkedLibraCoin.T
to destroy it and acquire the underlying coin (theunwrap
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<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): 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<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(): 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<T>(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 can create an earmarked coin for Bob by creating a transaction script that invokes
create
on Bob's address a and aLibraCoin.T
that she owns. Once a has been created, Bob can claim the coin by sending a transaction from a. This invokesclaim_for_recipient
, passes the result tounwrap
, and stores the returnedLibraCoin
wherever he wishes. If Bob takes too long to create an account under a and Alice wants to reclaim her funds, she can do so by usingclaim_for_creator
followed byunwrap
.The observant reader may have noticed that the code in this module is agnostic to the internal structure of
LibraCoin.T
. It could just as easily be written using generic programming (e.g.,resource T<AnyResource: R> { coin: AnyResource, ... }
). We are currently working on adding support for exactly this sort of parametric polymorphism to Move.Future Developer Experience
In the near future, the IR will stabilize, and compiling and verifying programs will become more user-friendly. Additionally, location information from the IR source will be tracked and passed to the verifier to make error messages easier to debug. However, the IR will continue to remain a tool for testing Move bytecode. It is meant to be a semantically transparent representation of the underlying bytecode. To allow effective tests, the IR compiler must produce bad code that will be rejected by the bytecode verifier or fail at runtime in the compiler. A user-friendly source language would make different choices; it should refuse to compile code that will fail at a subsequent step in the pipeline.
In the future, we will have a higher-level Move source language. This source language will be designed to express common Move idioms and programming patterns safely and easily. Since Move bytecode is a new language and the Libra Blockchain is a new programming environment, our understanding of the idioms and patterns we should support is still evolving. The source language is in the early stages of development, and we do not have a timetable for its release yet.