關於 Libra 幣交易,你需要了解的一切...

在上一篇文章中,我們初步探索了 Libra & Move 語言 。在這篇文章中,我們將探討使用者如何跟 Libra 進行互動。

本文以向 Libra 水龍頭(Faucet)索取 Libra 幣為例子,解釋 Libra Client 與 Validator 的內部是如何處理與執行交易的。


關於 Libra 幣交易,你需要了解的一切...



與 Libra 互動


Libra 技術白皮書用下面這張圖來解釋 Libra Client 與 Validator 之間的關係:


關於 Libra 幣交易,你需要了解的一切...



Libra 將參與區塊鏈的人分成兩類:Client(普通的使用者) 與 Validator(LibraBFT 共識節點)。

普通的使用者通過 Client 向 Validator 提交交易與 query。而 Validators 的任務就是處理好共識與執行 Client 發出的請求。

使用者向 Faucet 索取 LibraCoin


1、啟動 Libra CLI (Libra command line interface)以後,等待 Libra CLI 連上 測試網(testnet) ,並提示使用者可以進行輸入 libra%

2、使用者輸入account mint <Address> <Number of Coins> 後, Libra CLI 首先會將 <Address>, <Number of Coins> 打包成交易中的參數數據(parameter data),並從language/stdlib/transaction_scripts 下載入 mint 程式的 Move IR 檔案,作為交易中的交易腳本。(關於交易格式請參考文末的參考資料)。

3、打包好一筆交易以後,Libra CLI 便會將這筆交易提交給 Validator。

4、Validator 收到這筆交易時,並不會立刻執行它,而是先將其放入 mempool 裡面,並與其他的 Validator 分享。

5、當 Validator 要執行這筆交易時,每位 Validator 將輪流成為領導者(Leader),領導者 會從 mempool 裡面挑選等待執行的交易做成一個 擬議區塊(proposed block),並廣播給其他的 validators,等待投票。在至少 2f+1 個 validator 投票同意擬議區塊,領導者將會製作法定人數證書( quorum certificate )並廣播給全部的 validators。

6、經過這個過程,在這個擬議區塊中的交易就會被執行,並且提交到版本數據庫(versioned database)。

Validator 是如何執行交易的?



關於 Libra 幣交易,你需要了解的一切...



Libra 專門為 Move 語言設計了 Move 虛擬機。當 Validator 要執行交易時,需要通過 Move 虛擬機來執行。

1. 檢查簽名: 檢查交易簽名是否跟交易數據以及發送者的公鑰吻合。此階段是直接比對交易的資料,尚未跟版本數據庫與 Move 虛擬機互動。

2. 運行預處理程序(Prologue):

依序會進行以下三個檢查:a)檢查交易中的交易發起者公鑰是否與版本數據庫中交易發起者賬戶中留存的驗證密鑰一致。


HASH(Sender Public Key) == SenderAddr.LibraAccount.T.AuthenticationKe


b)檢查交易發起者是否有足夠的 Libra coin 來支付 gas 費


Transaction.gasPrice * Transaction.maxGasAmount <= SenderAddr.LibraAccount.T.balance


c)檢查交易的序列號是否跟當前版本數據庫中交易發起者賬戶的序列號一致。


Transaction.SequenceNumber == SenderAddr.LibraAccount.T.SequenceNumber


這三個檢查都是通過執行 built-in Module 0x0.LibraAccount 的procedure - prologue()

因為此階段為必要的檢查,雖然通過 Move 虛擬機執行了0x0.LibraAccount.prologue() 但並不會向交易發起者收取執行的 gas 費。

3. 驗證交易腳本(Transaction Script)和模塊(Module)

安全性是 Move 非常重要的設計原則,因此在 Move 提供了字節碼驗證器來檢查即將執行的交易腳本或者部署的模塊,以確保 type, reference, resource 的安全。

4. 發佈模塊:若交易中有需要部署的模塊,則在此階段將模塊部署到該地址中。(請參考文末參考資料中的 Libra 存儲佈局)

5. 運行交易腳本:Move 虛擬機將交易中的參數綁定進交易腳本中的參數(arguments)後,便會開始執行。

若執行成功,將會產生事件(events)並將此次執行結果寫回全球狀態(global state)中。若執行失敗(包含 out of gas、執行錯誤等),則會還原本次執行對全球狀態的修改。

6. 運行收尾程序(Epilogue):不論執行成功或失敗,都會觸發此階段。

通過呼叫 built-in Module 0x0.LibraAccount 的procedures - Epilogue() 來進行收取gas 費和調整序列號的操作:

收取 gas 費


SenderAddr.LibraAccount.T.Balance -= Transaction.gasPrice * Transaction.gasUse



調整序列號


SenderAddr.LibraAccount.T.SequenceNumber += 1


此階段跟預處理程序階段相同,雖然會通過 Move 虛擬機執行LibraAccount.Epilogue(),但不會向交易發起者收取運行收尾程序的 gas 費。

Libra CLI 如何處理

account mint <address> <number of coin>



關於 Libra 幣交易,你需要了解的一切...



當我們啟動 Libra CLI 時,會經過下面的流程來初始化 client ,讓使用者得以輸入指令與 Libra 測試網互動。

在使用者看到libra% 的輸入提示前, Libra CLI 會從配置文件中將 genesis, faucet account, local account 等信息載入,並進行 ClientProxy 的初始化,來連接上測試網。在後續的操作中,使用者的指令將全部通過 ClientProxy 來包裝,將使用者的指令與對應的交易合成,併發送給 Libra 的 Validator。

LibraCommand

當使用者希望為自己的第一個賬戶索取 100 個 Libra coin 時,輸入account mint 0 100,此時 Libra CLI 會解析整個輸入字串並調用對應的 LibraCommand 來執行。

指令中的account 代表著 Command 要調用CommandAccount::execute()來執行mint 0 100。此時 CommandAccount又看見指令中的mint ,

因此會再解析一次,以0 100`0 100` 去調用subcommand - CommandAccountMint::execute() 來執行 。

ClientProxy

但AccountCommandMint::execute()並不是直接發送交易給 Libra validators,而是通過 Libra CLI 最重要的核心 ClientProxy 代為操作,下面是具體的流程。

AccountCommandMint::execute()會將 0(第一個賬號), 100(libra coin 的數量) 發送給ClientProxy::mint_coins()來執行。

在執行時, ClientProxy 會先確認在本機上的帳號是否存在水龍頭(faucet)賬號。

a) 若本機不存在水龍頭賬號,ClientProxy 則會調用mint_coins_with_faucet_service(),將mint 0 100 打包成一個鏈接 (https://<faucet server>?amount=100&account=0),來對遠程水龍頭服務器發送mint 的請求。

b) 當本機存在水龍頭賬號時,ClientProxy 則會採用完全不同的路徑來執行 mint 請求,

改為調用:

ClientProxy::mint_coins_with_local_faucet_account(),

此時會真的建構一個 Libra 交易:

通過vm_genesis::encode_mint_program()方法,取得放置在

“language/stdlib/transaction_scripts” 下 mint.mvir。

mint.mvir 是 Libra 團隊用 Move 語言預先寫好的交易腳本。

接著調用 ClientProxy::create_submit_transaction_req() ,

將交易腳本 (mint.mvir), sender address, gas price, max gas amount 等信息打包到 Libra 交易。


c) 使用GRPCClient::submit_transaction()將這個交易發送給 Validator。

注:如果使用者在調用account mint 0 100 時,要求等交易完成後才返回,這時ClientProxy 會調用wait_for_transaction()卡住輸入區並顯示`waiting…` 直到交易完成後才會再次提示使用者可以進行下一步的操作。

附:參考資料

1、交易格式

Libra 詳細定義了一筆交易應該有以下欄位:

關於 Libra 幣交易,你需要了解的一切...


Sender Address:交易發起者地址,將用來查詢在 Ledger 中此地址的 LibraAccount 的資訊。

Sender Public Key:交易發起者公鑰,將用來驗證此交易是否由發送者所簽署;以及檢查此公鑰是否與此地址中 LibraAccount 留存的驗證密鑰相符。

Program Move:模塊(Move Module) 或者交易腳本(Transaction Script)。

Gas Price:此交易的單位 Gas 的價格。

Max Gas Amount:此交易的 Gas 上限。

Sequence Number:序列號,需要與當前地址中的SLibraAccount.T.SequenceNumber 相符,是用來檢查是否發生重播攻擊(replay attack )等攻擊的驗證欄位。

2、Libra 存儲佈局

每個地址分別存在模塊部分(Module Section)與資源部分(resource section)。


模塊部分代表了每個地址可以擁有多種 Libra 模塊,唯一的限制是在該地址中只能存在一個同名的 Module。

以下圖為例,0x0 已經存在了 Account 模塊,當使用者將發佈在模塊的交易發佈到另一個Account 模塊到 0x0 時,執行交易會中止並報錯。


關於 Libra 幣交易,你需要了解的一切...



在 Libra 中,不同地址擁有完全獨立的 namespace ,因此在不同的地址部署同一個模塊會產生不同的模塊名字。比如上圖中雖然0x0 跟0x4 都有同名的Account 模塊,但實際上在使用時,我們可以看到0x4 裡面存在著0x0.Account.T {...} 與0x4.Account.T {... } ,他們分別表示完全不同的資源空間。

相關推薦

推薦中...