火了,挡不住了:Facebook Move编程语言入门
火了,擋不住了:Facebook Move編程語言入門
Facebook區塊鏈項目Libra的其中一個技術亮點,就是它使用了一種稱為Move的新編程語言,那么這種語言是怎樣的呢,今天我們就從其官方的概述資料入手,近距離了解這種新的語言。
以下內容為譯文:
Move是一種新的編程語言,它為Libra區塊鏈提供了一個安全和可編程的基礎。Libra區塊鏈中的賬戶是任意數量Move資源及Move模塊的容器。提交至Libra 區塊鏈的每個事務,都使用以 Move語言編寫的事務腳本對其邏輯進行編碼。
這個事務腳本可調用模塊聲明的過程來更新區塊鏈的全局狀態。
在本指南的第一部分內容中,我們將概括性地介紹Move語言的主要特點:
1. Move事務腳本啟用可編程事務;
2. Move模塊允許組合型智能合約;
3. Move語言具有第一類資源(First Class Resource);
對于求知欲強的讀者來說,Move編程語言的技術論文包含了更多關于該語言的細節信息:
?
在本指南的第二部分,我們將向你展示如何在Move中間代碼優化(IR)的環境下編寫自己的應用。初始的測試網并不支持自定義Move程序,但這些功能可供你在本地試用。
一、Move語言的主要特點
?
1、1 Move事務腳本啟用可編程事務
1. 每個Libra事務都包含一個Move事務腳本,該腳本對驗證者應代表客戶端執行的邏輯進行編碼(例如,將Libra幣從Alice的賬戶移動到Bob的賬戶);
2. 事務腳本通過調用一個或多個Move模塊的過程,與Libra區塊鏈全局存儲中發布的Move資源進行交互;
3. 事務腳本不會存儲在全局狀態當中,因此其它事務腳本無法調用它,這是一個一次性程序;
4. 我們在編寫事務腳本時,提供了幾個事務腳本示例;
1、2 Move 模塊允許組合型智能合約
Move模塊定義了更新Libra區塊鏈全局狀態的規則。Move模塊與其它區塊鏈中的智能合約一樣都是解決相同的問題。模塊聲明了可在用戶賬戶下發布的資源類型。Libra區塊鏈中的每個賬戶都是任意數量資源和模塊的容器。
1. 模塊聲明結構類型(包括資源,這是一種特殊的結構)以及過程;
2. Move模塊的過程,定義了創建、訪問以及銷毀其聲明類型的規則。
3. 模塊是可重用的。一個模塊中聲明的結構類型,可以使用另一個模塊中聲明的結構類型,并且一個模塊中聲明的過程可以調用另一個模塊中聲明的公共過程。模塊可以調用在其他Move模塊中聲明的過程。事務腳本可以調用已發布模塊的任何公共過程。
4. 最終,Libra用戶將能在自己的帳戶下發布模塊。
1、3 Move語言具有第一類資源
1. Move的主要功能是定義自定義資源類型。資源類型用于編碼具有豐富可編程性的安全數字資產。
2. 資源是語言中的普通值,它們可存儲為數據結構,作為參數傳遞給procedure,從procedure返回,等等;
3. Move類型系統為資源提供了特殊的安全保障。Move資源不能復制、重復使用或丟棄。資源類型只能由定義該類型的模塊創建或銷毀。這些保障是由Move虛擬機通過bytecode驗證靜態地強制執行的。Move虛擬機將拒絕運行尚未通過bytecode檢驗器的代碼;
4. Libra幣作為一種資源類型,其名稱為LibraCoin.T。LibraCoin.T在語言中沒有特殊的地位,每種資源都享有相同的保護待遇;
二、 Move語言底層
?
2、1 Move中間代碼優化(IR)
本節介紹如何使用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的說明文件解釋了如何執行此操作。
2、2 編寫事務腳本
正如我們在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
2、3 編寫模塊
現在,我們把注意力集中到編寫自己的Move模塊上,而不僅僅是重用現有的LibraAccount和LibraCoin模塊。考慮這樣一個情況:Bob將來某個時候將在地址a創建一個帳戶,Alice想要“指定”Bob一筆資金,以便他可以在賬戶創建后將其存入自己的帳戶。但她也希望,如果Bob一直不創建一個賬戶,她就能收回這筆資金。
為了解決Alice的這個問題,我們將編寫一個專用的EarmarkedLibraCoin模塊,它會:
1. 聲明一個新的資源類型EarmarkedLibraCoin.T,它封裝了一筆Libra幣以及接收方地址;
2. 允許Alice創建此類型資源,并在其賬戶下發布(create過程);
3. 允許Bob聲明資源(claim_for_recipient過程);
4. 允許任何擁有EarmarkedLibraCoin.T資源類型的人銷毀它,并獲取底層的Libra幣(unwrap過程);
// 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增加這種參量多態性。
2、4 未來開發者體驗
在不久的將來,Move IR將穩定下來,編譯和驗證程序將變得更加對用戶友好。此外,IR源的位置信息將被跟蹤,然后傳遞給驗證者,以使錯誤消息更容易排錯。然而,IR將繼續作為測試Move bytecode的工具。它是作為底層bytecode的一種語義透明的表示。
為了允許有效的測試, IR編譯器需生成錯誤的代碼,這些代碼將被bytecode驗證者拒絕,或在編譯器的運行時失敗。
而對用戶友好的源語言則是另一種選擇,它應該拒絕編譯在管道的后續步驟中將失敗的代碼。
未來,我們將擁有更高層次的Move源語言。這種源語言將被設計成安全而容易地表達常見的Move慣用語和編程模式。由于Move bytecode是一種新語言,而Libra區塊鏈是一種新的編程環境,我們對應支持的習慣用法和模式的理解,仍在不斷發展。目前,源語言還處于開發的早期階段,我們還沒有為它準備好發布時間表。
總結
以上是生活随笔為你收集整理的火了,挡不住了:Facebook Move编程语言入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FACEBOOK’S CALIBRA
- 下一篇: Rust语言之HelloWorld We