Overview of Move — a new language for Libra Blockchain
Libra is permissioned blockchain proposed by Facebook. For transactions and smart contracts on this platform was created a special new language — Move. At the moment there is available Move intermediate representation (IR). In this article, you can read a short overview of key Move goals and features (modules, transactions scripts, resource types). It is based on the Libra documentation and Move Technical Paper.
1. Key Design goals of Move language
1.1. First-Class Resources
In other smart contract languages (for example in Solidity for Ethereum Blockchain) resources are defined similarly as any other values. To ensure maximum safety, Move implements custom resource types that can never be copied, reused or discarded. So what we can do with resource is moving it between program storage locations.
1.2 Flexibility
Flexibility is added to Libra Blockchain via Move transaction scripts and modules. Move transaction script is the part of every Libra transaction and it allows customizable transactions. Modules, in turn, provide flexible code composition.
1.3 Safety
Move ensures security by using an intermediate solution between two approaches:
-using a high-level programming language with a compiler checking key properties
-using a low-level untyped assembly checking key properties at runtime.
In Move bytecode verifier checks on-chain the bytecode for resource, type and memory safety and then bytecode interpreter executes it directly. Thanks to this, Move ensures safety just like source languages, but without adding the source compiler to the trusted computing base or the cost of compilation to the critical path for transaction execution.
1.4 Verifiability
Move supports lightweight on-chain verification of key safety properties and advanced off-chain static verification. Language properties which support static verification are: no dynamic dispatch, limited mutability and modularity.
2. Main structures in Move language
2.1 Move Modules
Move modules are the equivalent of smart contracts in other blockchain languages.
In Move module we can declare:
- structs of resource types :
module LibraCoin {
resource T {
value: u64,
}
resource MintCapability {}
resource MarketCap {
total_value: u64,
}
...
}
- “normal” structs:
module LibraAccount {
...
struct SentPaymentEvent {
payee: address,
amount: u64,
}
...}
- procedures that encode the rules for creating, destroying, and updating its declared resources
module LibraCoin {
...
public market_cap(): u64 {
let market_cap_ref: &mut R#Self.MarketCap;
market_cap_ref = borrow_global<MarketCap>(0x0);
return *&move(market_cap_ref).total_value;
}...}
Reading data from the module and calling its procedures is possible through move transactions scripts.
2.2 Move Scripts
Move transaction scripts are used for updating the Libra Blockchain global storage. They can read the data from the modules and call its procedures. Typical Move script consists of:
script:
main() {
return;
}
In the beginning, before “script”, we often import two basic elements: LibraAccount
and LibraCoin
. LibraAccount
is almost always needed and the use of LibraCoin
depends on the type of transaction we want to create.
import 0x0.LibraAccount;
import 0x0.LibraCoin;
At first, in the procedure, we have to declare our local variables. In Move IR declaration i initialization are separated.
The following example comes from the Libra documentation. At first, we import our account and coin modules. Then we pass two parameters in `main` function and declare local variables. #R
annotation in R#LibraCoin.T
is "Resource" and it means that in the LibraCoin
module we have the declaration of resource T
(instead of struct T
that would have annotation #V
- for "unrestricted Value").
Next we initialize our local variables using LibraAccount
procedures — withdraw_from_sender
and exists
. Inside if statement we use Move built in function create_account
.
Every time we use a variable we have to wrap it in withcopy
(leaving variable available for continued use) or move
(rendering variable unavailable). Resource types variables can be only moved. Unrestriced type variables can be moved and copied.
import 0x0.LibraAccount;
import 0x0.LibraCoin;
main(payee: address, amount: u64) {
let coin: R#LibraCoin.T;
let account_exists: bool; coin = LibraAccount.withdraw_from_sender(move(amount)); account_exists = LibraAccount.exists(copy(payee)); if (!move(account_exists)) {
create_account(copy(payee));
} LibraAccount.deposit(move(payee), move(coin));
return;
}
In the end of every procedure there must be a return
statement.
3. Libra Account and Libra Coin modules overview
3.1 Libra Account Module
Every nonempty address has a LibraAccount.T
resource. We can interact with an account in two ways:
-read the data from the LibraAccount
resource (LibraAccount.T
)
-call the procedures of the LibraAccount
module.
module LibraAccount {
import 0x0.LibraCoin;
import 0x00.Hash;
resource T {
balance: R#LibraCoin.T,
authentication_key: bytearray,
sequence_number: u64,
sent_events_count: u64,
received_events_count: u64
}
...
}
As we can see every LibraAccount
has LibraAccount.T
resource which stores the following values:
-Libra Coin balance for the account
-authentication key
-sequence number (transactions counter)
-sent and received events count
The full code of LibraAccount
module you can find on Libra GitHub page in libra/language/stdlib/modules/
directory.
3.2 Libra Coin Module
Libra Coin is implemented as “normal” resource type LibraCoin.T
LibraCoin
is the name of the moduleT
means the main type declared in this module (this is a Move naming convention)
module LibraCoin {
resource T {
value: u64,
}...
}
The full code of LibraCoin
module you can find on Libra GitHub page in libra/language/stdlib/modules/
directory.
You can check how to test/compile Move scripts and modules in our previous article.