Your First Transaction on Facebook Libra — 動手玩 Libra

hydai
Taipei Ethereum Meetup
19 min readJun 19, 2019

Libra 是 Facebook 在 6/18 發表的區塊鏈解決方案。提出 LibraCoin 加密貨幣來為金融基礎建設賦能。以 Facebook 與這次參與的合作夥伴,我相信在未來能讓支付、跨國跨境金融交易都能更加地便利。

本篇文章將帶著大家一起進行 Libra 的教學,如何在 Libra 上面發送自己的第一筆交易吧!

Agenda

  • 環境設置
  • 下載與編譯 Libra
  • 連上 Libra testnet
  • 建立自己的 Libra account 與透過水龍頭 (Faucet) 取得 LibraCoin
  • 查詢 Libra account 的餘額
  • 發送 LibraCoin
  • 查詢發送的 transaction

環境設置

目前 Libra 只支援 Linux 與 MacOS 兩種環境,以下將以 Linux Ubuntu 18.04 作為示範。

Environment: Ubuntu 18.04 on AWS EC2 t3.xlarge

# 更新一下 apt 與安裝 git,其他的環境 Libra 有提供他的 script 來安裝。
sudo apt update
sudo apt upgrade
sudo apt install git

下載與編譯 Libra

Step1. Clone lastest Libra repository

$ git clone https://github.com/libra/libra.git && cd libraCloning into 'libra'...
remote: Enumerating objects: 133, done.
remote: Counting objects: 100% (133/133), done.
remote: Compressing objects: 100% (105/105), done.
remote: Total 1717 (delta 40), reused 55 (delta 25), pack-reused 1584
Receiving objects: 100% (1717/1717), 1.89 MiB | 1.88 MiB/s, done.
Resolving deltas: 100% (340/340), done.

Step2. 安裝 Libra Core 所需的 dependencies

$ ./scripts/dev_setup.shWelcome to Libra!This script will download and install the necessary dependencies needed to
build Libra Core. This includes:
* Rust (and the necessary components, e.g. rust-fmt, clippy)
* CMake, protobuf, go (for building protobuf)
If you'd prefer to install these dependencies yourself, please exit this script
now with Ctrl-C.
Proceed with installing necessary dependencies? (y) >
...
...
Installing CMake......
CMake is already installed
Installing Go......
Go is already installed
Installing Protobuf......
Protobuf is already installed

Step2-optional. 在安裝 dependencies 的過程間我有遇到 protoc 安裝失敗,原因是 18.04 的 apt 上面的 protobuf 版本是 3.0.0,而 Libra 所需的版本為 >= 3.6.0 ,所以導致他安裝失敗。這時我們就需要手動安裝最新版本的 protobuf 來解決這個問題。

sudo apt install unzip
PROTOC_ZIP=protoc-3.8.0-linux-x86_64.zip
curl -OL https://github.com/google/protobuf/releases/download/v3.8.0/$PROTOC_ZIP
sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc
sudo unzip -o $PROTOC_ZIP -d /usr/local include/*
rm -f $PROTOC_ZIP

Step3. 編譯並執行 Libra Cli 來連上 testnet

$ ./scripts/cli/start_cli_testnet.shBuilding and running client in debug mode.
Updating crates.io index
Updating git repository `https://github.com/pingcap/rust-rocksdb.git`
Updating git repository `https://github.com/alexcrichton/bzip2-rs.git`
Updating git repository `https://github.com/busyjay/lz4-rs.git`
Updating git repository `https://github.com/busyjay/rust-snappy.git`
Updating git repository `https://github.com/gyscos/zstd-rs.git`
Compiling proc-macro2 v0.4.30
Compiling unicode-xid v0.1.0
Compiling syn v0.15.36
...
...
...
Finished dev [unoptimized + debuginfo] target(s) in ...s
Running `target/debug/client --host ac.testnet.libra.org --port 80 -s ./scripts/cli/trusted_peers.config.toml`
Connected to validator at: ac.testnet.libra.org:80
usage: <command> <args>
Use the following commands:account | a
Account operations
query | q
Query operations
transfer | transferb | t | tb
<sender_account_address>|<sender_account_ref_id> <receiver_account_address>|<receiver_account_ref_id> <number_of_coins> [gas_unit_price (default=0)] [max_gas_amount (default 10000)] Suffix 'b' is for blocking.
Transfer coins from account to another.
help | h
Prints this help
quit | q!
Exit this client
Please, input commands:libra%

看到 libra% 出現就代表我們已經大功告成連上 testnet 了!

連接上 testnet

其實跟編譯 libra cli 是同一個 script ,除非原始碼有更動,他就不會浪費時間在重複編譯上了。

$ ./scripts/cli/start_cli_testnet.shBuilding and running client in debug mode.
...
...
...
...
Finished dev [unoptimized + debuginfo] target(s) in ...s
Running `target/debug/client --host ac.testnet.libra.org --port 80 -s ./scripts/cli/trusted_peers.config.toml`
Connected to validator at: ac.testnet.libra.org:80
usage: <command> <args>
Use the following commands:account | a
Account operations
query | q
Query operations
transfer | transferb | t | tb
<sender_account_address>|<sender_account_ref_id> <receiver_account_address>|<receiver_account_ref_id> <number_of_coins> [gas_unit_price (default=0)] [max_gas_amount (default 10000)] Suffix 'b' is for blocking.
Transfer coins from account to another.
help | h
Prints this help
quit | q!
Exit this client
Please, input commands:libra%

建立自己的 Libra account

所有跟 account 相關的功能都在 account 下面,我們可以先下 account 這個指令來看看有哪些功能可以使用吧!

libra% accountusage: account <arg>Use the following args for this command:create | c
Create an account. Returns reference ID to use in other operations
list | la
Print all accounts that were created or loaded
recover | r <file_path>
Recover Libra wallet from the file path
write | w <file_path>
Save Libra wallet mnemonic recovery seed to disk
mint | mintb | m | mb <receiver_account_ref_id>|<receiver_account_address> <number_of_coins>
Mint coins to the account. Suffix 'b' is for blocking

他主要有五大功能:

  • Create: 建立一個帳號,每個帳號在 local 會給一個 reference ID,舉例來說,第一個被建立的帳號就是 ID=0。
libra% account create>> Creating/retrieving next account from wallet
Created/retrieved account #0 address fafbbc8aeed4b019ee8d1be854aa5279b26a07d5957b3a5a04e9aaa351bea2f0
# 可以看到我建立的第一個帳號,他的 reference ID #0
# 以及我的 address 就是下面那一大串 hex string
# 如果是在同一個 libra cli 操作,我們都可以使用 reference ID 作為帳號的 alias。如果是要請別人從他的 libra cli 發 transaction 給你的帳號,那就需要直接給對方你的 address 了。
  • List: 印出所有被建立或者被載入的帳號。
libra% account listUser account index: 0, address: fafbbc8aeed4b019ee8d1be854aa5279b26a07d5957b3a5a04e9aaa351bea2f0, sequence number: 0, status: Persisted
# 列出現有的帳號。Sequence number 則是代表了這個 account 發起過幾個 transaction。
  • Recover: 從一個檔案還原一個錢包帳號。像是 import wallet。
  • Write: 將錢包帳號儲存到一個檔案。像是 export wallet。
  • Mint: 請水龍頭 (Faucet) 發錢給一個指定的帳號。
# 請水龍頭發錢的指令是
# account mint <account address/ref_id> <number_of_coin>
# 以下示範發 320 顆 Libra Token 給第一個帳號。
libra% account mint 0 320
>> Minting coins
Mint request submitted

Hint: 跟 transaction 相關的操作(mint/transfer)都有提供 blocking 與 non-blocking 的版本。想要使用 blocking 的版本,比如說要等水龍頭真的發錢給你,那我們可以使用下面這種語法:

libra% account mintb 1 1205
>> Minting coins
[waiting
Transaction completed, found sequence number 9357
Finished minting!

他就會直接卡住並印出 waiting 直到這個 Transaction 被完成為止。

查詢 Libra account 的餘額

語法是 query balance <account address/ref_id>

libra% query balance 0Balance is: 320

發送 LibraCoin

轉帳的語法如下:

transfer <sender address/ref_id> <receiver address/ref_id> <number_of_coins> [gas_unit_price (default=0)] [max_gas_amount (default 10000)]

tranfer 一共有五個參數:

  1. Sender Account 的 address 或 reference id
  2. Receiver Account 的 address 或 reference id
  3. 要發送多少數量的 libra
  4. 這次 transaction 的 gas 手續費,預設是 0。這個參數可以不給。
  5. 這次 transaction 的最高 gas 限制,預設是 10000。這個參數可以不給。

舉例而言,如果我想從 Account #0 轉 100 Libra 給 Account #1,我可以這樣寫 transfer 0 1 100

查詢發送的 transaction

查詢交易的指令為:

query txn_acc_seq <account address/ref_id> <sequence_number> <fetch_events=true|false>

一共有三個參數:

  1. Account 的 address 或 reference ID
  2. Sequence Number: 在 Libra 的設計中,sequence number 代表了該 account 發起了幾個交易,每發起一次交易且被收進 Libra blockchain 中這個數字就會 +1 。這個設計是為了避免 replay 攻擊,每個發送出去的 transaction 帶有當前 account 的 sequence number ,而發送出去的 transaction 會先被放入 mempool 中等待被執行。當 Libra blockchain 從 mempool 中拿 transaction 出來執行的時候會去比對當前的 account sequence number 是否跟 transaction 的 sequence number 相同。若相同才能執行,且馬上會把 account 的 sequence number +1 ,這樣即使在 mempool 中有多個重複的 transaction 也只會有一個合法的能被執行,其他的將會被丟棄。
  3. 是否需要抓取 events

完整情境的範例

在發送 LibraCoin 之前,我們需要有兩個帳號來進行轉帳。以下將示範從建立兩個帳號、各自從水龍頭領到 LibraCoin 以及轉帳的過程吧!

# 建立第一個帳號 alice
libra% account create
>> Creating/retrieving next account from wallet
Created/retrieved account #0 address 7faff73ba54fc82503ecd6438455e8a4ab0a26dc35d399e6593bb1058338e140
# 建立第二個帳號 bob
libra% account create
>> Creating/retrieving next account from wallet
Created/retrieved account #1 address bf2df78de21ea3727e0d48ae7142718f8c33f7eca854ac81b8866361fa2bd15d
# 幫 alice 要 320 個 libra
libra% account mint 0 320
>> Minting coins
Mint request submitted
# 查詢 alice 的 balance
libra% query balance 0
Balance is: 320
# 幫 bob 要 1205 個 libra
libra% account mint 1 1205
>> Minting coins
Mint request submitted
# 查詢 bob 的 balance
libra% query balance 1
Balance is: 1205
# 從 alice 轉 20 個 libra 給 bob
libra% transfer 0 1 20
>> Transferring
Transaction submitted to validator
To query for transaction status, run: query txn_acc_seq 0 0 <fetch_events=true|false>
# 查詢 transaction
libra% query txn_acc_seq 0 0 true
>> Getting committed transaction by account and sequence number
Committed transaction: SignedTransaction {
raw_txn: RawTransaction {
sender: 7faff73ba54fc82503ecd6438455e8a4ab0a26dc35d399e6593bb1058338e140,
sequence_number: 0,
payload: {,
transaction: peer_to_peer_transaction,
args: [
{ADDRESS: bf2df78de21ea3727e0d48ae7142718f8c33f7eca854ac81b8866361fa2bd15d},
{U64: 20000000},
]
},
max_gas_amount: 10000,
gas_unit_price: 0,
expiration_time: 1560912163s,
},
public_key: a2b9f68d1abbc887a39789a7f428df68adabfff395fff71bd0d0fe5674bdfbad,
signature: Signature( R: CompressedEdwardsY: [74, 249, 58, 185, 235, 16, 175, 193, 158, 86, 31, 249, 215, 97, 138, 153, 65, 183, 161, 229, 162, 198, 30, 107, 246, 191, 44, 250, 12, 96, 20, 55], s: Scalar{
bytes: [170, 99, 70, 224, 9, 167, 230, 162, 110, 38, 87, 53, 164, 36, 91, 34, 116, 52, 73, 91, 169, 141, 159, 194, 40, 206, 16, 151, 5, 101, 91, 1],
} ),
}
Events:
ContractEvent { access_path: AccessPath { address: 7faff73ba54fc82503ecd6438455e8a4ab0a26dc35d399e6593bb1058338e140, type: Resource, hash: "217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc97", suffix: "/sent_events_count/" } , index: 0, event_data: AccountEvent { account: bf2df78de21ea3727e0d48ae7142718f8c33f7eca854ac81b8866361fa2bd15d, amount: 20000000 } }
ContractEvent { access_path: AccessPath { address: bf2df78de21ea3727e0d48ae7142718f8c33f7eca854ac81b8866361fa2bd15d, type: Resource, hash: "217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc97", suffix: "/received_events_count/" } , index: 0, event_data: AccountEvent { account: 7faff73ba54fc82503ecd6438455e8a4ab0a26dc35d399e6593bb1058338e140, amount: 20000000 } }

常見錯誤

Libra testnet 有擋一個區間內的爆量 request,如果你在短時間內送一堆 transaction (mint or transfer) 有機會拿到下面這個 error:

[ERROR] Error minting coins: Failed to query remote faucet server[status=429 Too Many Requests]: "<html><body><h1>429 Too Many Requests</h1>\nYou have sent too many requests in a given amount of time.\n</body></html>\n"

這時候只要稍等一下再重新送那筆 transaction 就能解除了。

--

--