Facebook幣Libra學習-4.新的智能合約語言Move入門


Move是一種新的編程語言,旨在為Libra Blockchain提供安全可編程的基礎。Libra Blockchain中的帳戶是任意數量的Move資源和Move模塊的容器。提交給Libra Blockchain的每個事務都使用Move中編寫的事務腳本來編碼其邏輯。事務腳本可以調用模塊聲明的過程來更新區塊鏈的全局狀態。

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

  1. 移動事務腳本啟用可編程事務
  2. 移動模塊允許可組合智能合約
  3. 移動有一流的資源

對於好奇的讀者,Move技術論文包含有關該語言的更多細節。

在本指南的第二部分中,我們將“深入了解”並向您展示如何在Move中間表示中編寫您自己的Move程序初始testnet版本不支持自定義移動程序,但這些功能可供您在本地試用。

移動的主要特點

移動事務腳本啟用可編程事務

  • 每個Libra事務都包含一個Move事務腳本,該腳本對驗證器應代表客戶端執行的邏輯進行編碼(例如,將Libra從Alice的帳戶移動到Bob的帳戶)。
  • 事務腳本通過調用一個或多個Move模塊的過程與Libra Blockchain的全局存儲中發布的Move資源進行交互
  • 事務腳本不存儲在全局狀態中,並且其他事務腳本無法調用它。這是一次性使用的程序。
  • 我們在編寫事務腳本中提供了幾個事務腳本示例

移動模塊允許可組合智能合約

移動模塊定義了更新Libra Blockchain全局狀態的規則。模塊在其他區塊鏈系統中與智能合約相同。模塊聲明可以在用戶帳戶下發布的資源類型。Libra Blockchain中的每個帳戶都是一個容器,可以容納任意數量的資源和模塊。

  • 模塊聲明兩種結構類型(包括資源,這是一種特殊的結構)和過程。
  • Move模塊的過程定義了創建,訪問和銷毀它聲明的類型的規則。
  • 模塊可重復使用。在一個模塊中聲明的結構類型可以使用在另一個模塊中聲明的結構類型,並且在一個模塊中聲明的過程可以調用在另一個模塊中聲明的公共過程。模塊可以調用其他Move模塊中聲明的過程。事務腳本可以調用已發布模塊的任何公共過程。
  • 最終,Libra用戶將能夠在自己的帳戶下發布模塊。

移動有一流的資源

  • Move的關鍵功能是定義自定義資源類型的能力。資源類型用於編碼具有豐富可編程性的安全數字資產。
  • 資源是語言中的普通值。它們可以存儲為數據結構,作為參數傳遞給過程,從過程返回,等等。
  • Move類型系統為資源提供特殊的安全保證。移動資源永遠不會被復制,重用或丟棄。資源類型只能由定義類型的模塊創建或銷毀。這些保證由Move虛擬機通過字節碼驗證靜態實施Move虛擬機將拒絕運行未通過字節碼驗證程序的代碼。
  • Libra貨幣實現為名為的資源類型LibraCoin.TLibraCoin.T在該語言中沒有特殊的地位; 每個Move資源都享有相同的保護。

移動:在引擎蓋下

移動中間表示

本節介紹如何在Move中間表示(IR)中編寫事務腳本模塊我們提醒讀者,IR是即將推出的Move源語言的早期(且不穩定)前體(有關更多詳細信息,請參閱Future Developer Experience)。移動IR是移動字節碼上的一個薄語法層,用於測試字節碼驗證器和虛擬機,它不是特別適合開發人員。它的高級別足以編寫人類可讀的代碼,但低級別足以直接編譯為移動字節碼。盡管如此,我們對Move語言感到很興奮,並希望開發人員盡可能地嘗試IR,盡管邊緣粗糙。

我們將繼續介紹重要評論的Move IR的片段。我們鼓勵讀者通過在本地編譯,運行和修改示例來跟隨這些示例。README文件libra/language/README.mdlibra/language/compiler/README.md解釋如何執行此操作。

編寫交易腳本

正如我們在Move Transaction Scripts Enable Programmable Transactions中所述,用戶編寫事務腳本以請求更新Libra Blockchain的全局存儲。幾乎所有事務腳本中都會出現兩個重要的構建塊:資源類型LibraAccount.TLibraCoin.T資源類型。LibraAccount是模塊T的名稱,是該模塊聲明的資源的名稱。這是Move中常見的命名約定; 模塊聲明的“main”類型通常是命名的T

When we say that a user "has an account at address 0xff on the Libra Blockchain", what we mean is that the address 0xff holds an instance of the LibraAccount.T resource. Every nonempty address has a LibraAccount.T resource. This resource stores account data, such as the sequence number, authentication key, and balance. 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 invoking procedures of the LibraAccount module.

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

我們鼓勵感興趣的讀者檢查目錄LibraAccountLibraCoin模塊中這兩個關鍵資源的Move IR定義libra/language/stdlib/modules/

現在讓我們看看程序員如何在事務腳本中與這些模塊和資源進行交互。

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

此事務腳本有一個不幸的問題 - 如果地址下沒有帳戶,它將失敗payee我們將通過修改腳本來修復此問題,以便為payee尚不存在的帳戶創建帳戶

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

這就結束了我們對事務腳本的“瀏覽”。有關更多示例,包括初始testnet中支持的事務腳本,請參閱libra/language/stdlib/transaction_scripts

編寫模塊

我們現在將注意力轉向編寫自己的Move模塊,而不是僅重用現有模塊LibraAccountLibraCoin模塊。考慮這種情況:Bob將在未來的某個時刻在地址a創建一個帳戶Alice希望為Bob“收取”一些資金,以便他們可以在創建后將其提取到他的帳戶中。但如果鮑勃從未創建帳戶,她也希望能夠為自己收回資金。

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

  • 聲明一個新的資源類型EarmarkedLibraCoin.T,用於包裝Libra硬幣和收件人地址。
  • 允許Alice創建此類型並在其帳戶下發布(create程序)。
  • 允許Bob聲明資源(claim_for_recipient過程)。
  • 允許任何人EarmarkedLibraCoin.T摧毀它並獲得底層硬幣(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可以通過創建一個調用createBob的地址aLibraCoin.T她擁有的事務腳本為Bob創建一個專用硬幣一旦被創建,鮑勃可以通過從發送交易權利要求的硬幣一個這會調用claim_for_recipient,將結果傳遞給unwrap,並將返回的內容存儲LibraCoin在他希望的任何地方。如果Bob花費太長時間在a下創建一個帳戶並且Alice想要收回她的資金,那么她可以通過使用claim_for_creator后續來完成unwrap

細心的讀者可能已經注意到,該模塊中的代碼與內部結構無關LibraCoin.T它可以像使用通用編程一樣容易地編寫(例如resource T<AnyResource: R> { coin: AnyResource, ... })。我們目前正致力於為Move添加對這種參數多態的支持。

未來的開發經驗

在不久的將來,IR將穩定下來,編譯和驗證程序將變得更加用戶友好。此外,將跟蹤來自IR源的位置信息並將其傳遞給驗證程序,以使錯誤消息更易於調試。但是,IR將繼續作為測試Move字節碼的工具。它意味着是底層字節碼的語義透明表示。為了進行有效的測試,IR編譯器必須生成錯誤的代碼,這些代碼將被字節碼驗證程序拒絕或在運行時在編譯器中失敗。用戶友好的源語言會做出不同的選擇; 它應該拒絕編譯將在管道的后續步驟中失敗的代碼。

將來,我們將擁有更高級別的Move源語言。該源語言旨在安全輕松地表達常見的Move習語和編程模式。由於Move字節碼是一種新語言,而Libra Blockchain是一種新的編程環境,我們對應該支持的習語和模式的理解仍在不斷發展。源語言還處於開發的早期階段,我們還沒有發布它的時間表。

 

Libra國內開發者微信交流群:

不能入群請加管理微信,拉你進群=>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM