The Libra Blockchain-開發(fā)者文檔-(五)了解Move語言

Move是一種新的編程語言,在為Libra

區(qū)塊鏈提供安全、可編程的基礎(chǔ)。 Libra區(qū)塊鏈中的帳戶作為容器,包含了任意數(shù)量的Move資源和Move模塊。 提交給Libra

區(qū)塊鏈的每個交易都使用Move中編寫的交易腳本來實現(xiàn)其邏輯。 交易腳本可以通過調(diào)用模塊聲明的過程來更新區(qū)塊鏈的全局狀態(tài)。

在本指南的第一部分中,我們將介紹Move語言的主要功能進行深入介紹:

可編程的Move交易腳本

支持智能合約的Move模塊

Move的一等資源

有興趣的讀者,Move技術(shù)手冊包含了有關(guān)該語言的各種細節(jié)。

在本指南的第二部分中,我們將“深入”并向您展示如何在Move表現(xiàn)層中編寫自己的Move程序. 初始testnet版本不支持自定義Move程序,但這些功能可供您在本地試用。

Move的主要特性

可編程的Move交易腳本

每一個Libra區(qū)塊鏈上交易都包含Move交易腳本客戶端針對驗證器進行交易邏輯的編碼(例如,將Libra從Alice的帳戶移動到Bob的帳戶)。

交易腳本通過調(diào)用一個或者多個Move模塊和Libra區(qū)塊鏈全局存儲中發(fā)布的Move資源進行交互。

交易腳本不存儲在區(qū)塊鏈的全局狀態(tài)中,其他的交易腳本也無法調(diào)用它,這是一次性程序。

我們在編寫交易腳本中提供了幾個交易腳本示例。

支持智能合約的Move模塊

Move模塊定義了更新Libra 區(qū)塊鏈全局狀態(tài)的規(guī)則。 這些模塊與其他區(qū)塊鏈系統(tǒng)中與智能合約相同。 模塊聲明可以在用戶帳戶下發(fā)布的資源類型。Libra 區(qū)塊鏈中的每個帳戶都是一個容器,可以容納任意數(shù)量的資源和模塊。

模塊聲明兩種結(jié)構(gòu)類型(包括資源,這是一種特殊的結(jié)構(gòu))和過程。

Move模塊的過程定義了創(chuàng)建,訪問和銷毀它聲明的類型的規(guī)則。

模塊可重復使用。 在一個模塊中聲明的結(jié)構(gòu)類型可以使用在另一個模塊中,并且在一個模塊中聲明的過程可以在另一個模塊中聲明的公共過程中調(diào)用。 模塊可以調(diào)用其他Move模塊中聲明的過程。 交易腳本可以調(diào)用已發(fā)布模塊的任何公共過程。

最終,Libra用戶將能夠在自己的帳戶下發(fā)布模塊。

Move的一流資源

Move的關(guān)鍵功能是能自定義資源類型。 資源類型通過編碼具有豐富可編程性和安全性。

資源是語言中的普通類型值。 它們可以存儲為數(shù)據(jù)結(jié)構(gòu),作為參數(shù)傳遞給過程,從過程返回,等等。

Move的系統(tǒng)為資源提供特殊的安全性保證。 Move資源永遠不會被復制,重用或丟棄。 資源類型只能由定義類型的模塊創(chuàng)建或銷毀。 這些保證由Move虛擬機通過字節(jié)碼靜態(tài)驗證,Move虛擬機將拒絕運行未通過字節(jié)碼驗證的程序代碼。

Libra貨幣實現(xiàn)為名為LibraCoin.T的資源類型。LibraCoin.T在語言中沒有特殊的地位; 每個Move資源都享有相同的保護。

Move: 底層

Move表現(xiàn)層

本節(jié)介紹如何在Move表示層(IR)中編寫交易腳本模塊我們提醒讀者,IR是即將推出的Move語言的早期(且不穩(wěn)定) (詳見開發(fā)經(jīng)驗更新更新相關(guān)詳細信息). Move IR是一個覆蓋于Move字節(jié)碼之上的輕量語法層,用于測試字節(jié)碼驗證器和虛擬機,它對開發(fā)人員不是特別友好。開發(fā)人員可基于它編寫高級別的可讀代碼,但又足夠底層可以直接編譯Move字節(jié)碼。盡管如此,我們還是對Move語言感到興奮,并希望開發(fā)人員能夠嘗試一下IR,盡管它還存在一些不足之處。

我們將繼續(xù)介紹重要評論的Move IR的細節(jié)。 我們鼓勵讀者通過在本地編譯,運行和修改示例來跟隨這些示例。libra/language/README.md和libra/language/ir_to_bytecode/README.md下的README文件解釋了如何執(zhí)行此操作。

編寫交易腳本

正如我們在啟用可編程Move交易腳本中所描述的, 用戶編寫交易腳本以請求更新Libra 區(qū)塊鏈的全局存儲。 幾乎任何交易腳本中都會出現(xiàn)兩個重要的構(gòu)建塊LibraAccount.T和LibraCoin.T。LibraAccount是模塊的名稱,T是該模塊聲明的資源的名稱。這是Move中常見的命名規(guī)則; 模塊聲明的“main”類型通常命名為T.

當我們說用戶 "在Libra區(qū)塊鏈上有一個地址為0xff開頭的帳號" 時, 我們的意思是地址0xff擁有LibraAccount.T資源的實例。 每個非空地址都有一個LibraAccount.T資源。 此資源存儲帳戶數(shù)據(jù),例如序列號,身份驗證密鑰和余額。 在Libra區(qū)塊鏈上想要與帳戶有任何交互都必須通過從LibraAccount.T資源讀取數(shù)據(jù)或調(diào)用LibraAccount模塊來實現(xiàn).

賬戶余額是LibraCoin.T類型的資源. 正如我們在Move的一等資源中所解釋的那樣,這是LibraCoin類型。 與任何其他Move資源一樣,此類型是語言中的“一等公民”。LibraCoin.T類型的資源可以存儲在程序變量中,在程序之間傳遞,等等。

我們鼓勵感興趣的讀者在libra/language/stdlib/modules/目錄下的LibraAccount和LibraCoin模塊中檢查這兩個關(guān)鍵資源的Move IR定義。

現(xiàn)在讓我們看看程序員如何在交易腳本中與這些模塊和資源進行交互。

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

}

這個交易腳本有一個不好的地方 — 如果收款人地址下沒有帳戶,它將失敗。 我們將通過修改腳本來為收款人創(chuàng)建帳戶(如果賬戶不存在)來解決此問題。

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

}

這就結(jié)束了我們對交易腳本的“瀏覽”。 有關(guān)更多示例,包括初始testnet中支持的交易腳本,請參閱libra/language/stdlib/transaction_scripts.

編寫模塊

我們現(xiàn)在將注意力轉(zhuǎn)向編寫自己的Move模塊,而不是僅僅重用現(xiàn)有的LibraAccount和LibraCoin模塊。 考慮這種情況:Bob將在未來的某個時間點在地址a創(chuàng)建一個帳戶。 Alice希望向Bob“指定”一些資金,這樣一旦Bob的賬戶創(chuàng)建好了,她就可以把這些資金轉(zhuǎn)到他的賬戶中。但她也希望,如果Bob從未創(chuàng)建過賬戶,她也能收回自己的資金。

為了解決Alice的這個問題,我們將編寫一個模塊EarmarkedLibraCoin:

聲明一個新的資源類型EarmarkedLibraCoin.T包含Libra Coin和接收人地址。

允許Alice創(chuàng)建這樣的類型并在她的帳戶下發(fā)布它(create過程)。

允許Bob聲明資源(claim_for_recipient過程)。

允許任何擁有EarmarkedLibraCoin.T的人銷毀它并獲得之前“指定”(質(zhì)押)資金(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<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可以通過創(chuàng)建一個交易腳本為Bob創(chuàng)建一個專用幣,該腳本包括Bob的地址a和同時在她擁有的LibraCoin.T上調(diào)用create。 一旦a創(chuàng)建,Bob就可以通過從a發(fā)送交易來獲得幣。 這將調(diào)用claim_for_recipient,將結(jié)果傳遞給unwrap,并將返回的LibraCoin的結(jié)果存儲。 如果Bob花費太長時間在a下創(chuàng)建一個帳戶,這時候Alice想要收回她的資金,她可以通過使用claim_for_creator然后使用unwrap來實現(xiàn)。

細心的讀者可能已經(jīng)注意到,該模塊中的代碼與LibraCoin.T的內(nèi)部結(jié)構(gòu)無關(guān)。 它可以很容易地使用程序泛型來編寫(例如,resource T <AnyResource:R> {coin:AnyResource,...})。 我們目前正致力于為Move添加對這種參數(shù)多態(tài)的支持。

開發(fā)經(jīng)驗更新

在不久的將來,IR將穩(wěn)定下來,編譯和驗證程序的用戶體檢將更好。

此外,將跟蹤來自IR源的位置信息并將其傳遞給驗證程序,使得錯誤消息更易于調(diào)試。 但是,IR將繼續(xù)作為測試Move字節(jié)碼的工具。

它意味著是底層字節(jié)碼的語義更加透明。

為了進行有效的測試,IR編譯器會生成一些錯誤的代碼,這些代碼將被字節(jié)碼驗證程序拒絕或在運行時在編譯器中失敗。 友好的用戶源語言會做出不同的選擇;

它應該拒絕編譯將在后續(xù)步驟中失敗的代碼。

將來,我們將擁有更高級別的Move源語言。

該源語言旨在安全輕松地表達常見的Move慣用語法和編程模式。 由于Move是一種新語言,而Libra

區(qū)塊鏈是一種新的編程環(huán)境,我們對應該支持的慣用語法和模式的理解仍在不斷發(fā)展。 Move源語言還處于開發(fā)的早期階段,我們還沒有發(fā)布它的時間表。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容