第一笔交易

假定

  • 运行的是Linux或者macOS系统
  • 网络连接正常
  • git已安装
  • macOS中安装了Homebrew
  • Linux中安装了yum或者apt-get

提交一笔交易的步骤

  1. 克隆并构建Libra Core
  2. 构建Libra命令行客户端并且连接到测试网络
  3. 创建Alice和Bob的账户
  4. 挖矿并添加到Alice与Bob的账户中
  5. 提交一笔交易

克隆并构建Libra Core

克隆Libra Core仓库

git clone https://github.com/libra/libra.git

启动Libra Core

切换到libra文件夹并且运行启动脚本来安装依赖:

cd libra
./scripts/dev_setup.sh

这个安装脚本里面有以下几步

  • 安装rustup - rustup是一个Rust语言的安装器,Libra Core就是用其实现的
  • 安装rust-toolchain的正确版本
  • 安装CMake - 管理构建过程
  • 安装protoc - 一个protocol buffers的编译器
  • 安装Go - 构建protocol buffers

如果遇到问题,详见解决问题

构建Libra命令行客户端并且连接到测试网络

为了连接到Libra测试网络的验证节点,按照如下方式运行客户端

./scripts/cli/start_cli_testnet.sh

这条命令使用cargo(Rust的包管理器)来构建并运行客户端,并把客户端连接到测试网络上的一个验证节点。

一旦客户端连接到测试网络上的节点,你将会看到如下输出。退出可以使用 quit 命令

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%

如果在构建客户端与连接测试网时出现问题,详见解决问题

创建Alice和Bob的账户

一旦你的客户端连接到了测试网,你就可以运行CLI命令来创建新账户了。我们将会为两个用户创建账户(就叫Alice和Bob吧)

第一步:检查你的系统上是否运行着CLI客户端

一个 libra% 的命令行前缀说明你的Libra CLI客户端正在运行中。按照下面这样输入“account”可以看到关于__account__命令的帮助信息

libra% account
usage: 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 name>
  Save Libra wallet mnemonic recovery seed to disk
mint | mintb | m | mb <receiver account> <number of coins>
  Mint coins to the account. Suffix 'b' is for blocking

第二步:创建Alice的账户

注意用CLI创建一个账户并不会更新到区块链上,只是创建了一个本地的钥匙对。

为了创建Alice的账户,输入下面的命令:

libra% account create

成功的话输出就像下面这样:

>> Creating/retrieving next account from wallet
Created/retrieved account #0 address 3ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a8

0#是Alice账户的索引,这个16进制的字符串就是Alice的账户地址。索引只是一种指向Alice账户的方式。账户索引可以让用户在其他的CLI命令中便捷地指向他们所创建账户的一个本地CLI索引。索引对于区块链来说是没什么意义的。只有当通过挖矿或者他人转账方式,有资金转入到Alice的账户的时候,Alice的账户才会在区块链上面被创建。注意你可能使用了CLI中的16进制地址。这个账户索引只是对于账户地址的一种便利的包装而已。

第三步:创建Bob的账户

为了创建Bob的账户,重复下面的账户创建命令:

libra% account create

成功的话输出就像下面这样:

>> Creating/retrieving next account from wallet
Created/retrieved account #1 address 8337aac709a41fe6be03cad8878a0d4209740b1608f8a81566c9a7d4b95a2ec7

#1就是Bob的账户索引,这个16进制字符串就是Bob的账户。

第四步(可选):账户列表

输入下面的命令可以列出你所创建的账户:

libra% account list

成功的话输出就像下面这样:

User account index: 0, address: 3ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a8, sequence number: 0
User account index: 1, address: 8337aac709a41fe6be03cad8878a0d4209740b1608f8a81566c9a7d4b95a2ec7, sequence number: 0

这个账户的序列号(sequence number)代表的是从这个账户发送的交易的数量。每当这个账户发送了一笔交易被执行并且存储到了区块链中,这个数字就会增加。了解更多详见序列号

给Alice和Bob的账户中添加Libra币

在测试网上挖矿并给账户添加币是通过Faucet完成的。Faucet是一个和测试网一同运行的服务。这个服务存在的目的只是为了促进测试网上的挖矿速度,而且主网是没有的。它创建的Libra是没有实际价值的。假定你已经创建了Alice和Bob的账户,各自的索引是0和1。那么你可以按照下面的步骤来添加Libra到各个账户中

第一步:添加110Libra到Alice的账户中

为了挖取Libra并添加到Alice的账户中,输入下面的命令:

libra% account mint 0 110
  • 0是Alice账户的索引
  • 110是添加到Alice账户中的Libra数量

一个成功的账户挖取命令同时会在区块脸上创建Alice的账户。

成功的话输出就像下面这样:

>> Minting coins
Mint request submitted

注意当请求提交的时候,意味着它被成功地添加到了(测试网上的验证节点的)内存池中。这并不意味着它就会被成功执行。后面,我们将会查询账户余额来确认挖矿是否成功。

如果你的账户挖矿命令没有成功提交你的请求,详见问题

第二步:添加52Libra到Bob的账户中

为了挖取Libra并添加到Bob的账户中,输入下面的命令:

libra% account mint 1 51
  • 1是Bob账户的索引
  • 52是添加到Bob账户中的Libra数量
  • 一个成功的账户挖取命令同时会在区块脸上创建Bob的账户。另一个在区块链上创建Bob账户的方法是从Alice的账户向Bob的账户转钱。

成功的话输出就像下面这样:

>> Minting coins
Mint request submitted

如果你的账户挖矿命令没有成功提交你的请求,详见问题

第三步:检查余额

输入下面的命令检查Alice的账户余额:

libra% query balance 0

成功的话输出就像下面这样:

Balance is: 110

输入下面的命令检查Bob的账户余额:

libra% query balance 1

成功的话输出就像下面这样:

Balance is: 52

提交交易

在我们提交从Libra到Bob账户的转账交易之前,我们先去查询每个账户的序列号(sequence number)。这将帮助我们理解交易的执行是如果改变每个账户的序列号的。

查询账户的序列号

libra% query sequence 0
>> Getting current sequence number
Sequence number is: 0
libra% query sequence 1
>> Getting current sequence number
Sequence number is: 0

query sequence 0 中,0是Alice账户的索引。0这个序列号说明到目前为止,Alice和Bob的账户都没有发送过交易。

转账

为了提交一笔从Alice到Bob的账户10和Libra的交易,输入下面的命令:

libra% transfer 0 1 10
  • 0是Alice账户的索引
  • 1是Bob账户的索引
  • 10是Alice转账给Bob账户的Libra数量

成功的话输出像下面这样:

>> Transferring
Transaction submitted to validator
To query for transaction status, run: query txn_acc_seq 0 0 <fetch_events=true|false>

你可以使用 query txn_acc_seq 0 0 true (通过账户和序列号的交易)这条命令来追溯刚刚提交的交易的相关信息。第一个参数是发送者账户的本地索引,第二个参数是账户的序列号(sequence number)。

你刚刚向测试网上的验证节点提交了一笔交易,它被包含在了验证节点的内存池中。这并不意味着你的交易已经被执行了。理论上来说,如果系统太慢了或者负载太高,要看到结果需要一些时间,你可能需要检查好多次账户信息。为了查询一个索引是0的账户,你可以使用 query account_state 0 这条命令。

关于相关的转账问题,可以看看问题

阻塞转账命令(The Blocking Transfer command): 可以使用 transferb 命令来代替 transfer 命令。 transferb 会提交交易并且只有当交易被提交到区块链上的时候才会返回到客户端提示符上。例子如下:

libra% transferb 0 1 10

交易的生命周期描述了一笔交易从提交到执行存储的生命周期。

转账后查询序列号(sequence number)

libra% query sequence 0
>> Getting current sequence number
Sequence number is: 1
libra% query sequence 1
>> Getting current sequence number
Sequence number is: 0

Alice账户(索引0)的序列号是1代表了到目前为止从Alice的账户发出了一笔交易。Bob账户(索引1)的序列号是0代表了到目前为止Bob的账户还没有发送过交易。每当一个账户发送了一笔交易。它的序列号都会增加1。

转账后检查账户的余额

为了检查两个账户的余额,再一次像上面一样查询余额。如果交易成功执行,你将会看到Alice账户有100Libra同时Bob的账户有62Libra。

libra% query balance 0
Balance is: 100
libra% query balance 1
Balance is: 62

恭喜

你已经成功地在测试网上执行了一笔从Alice到Bob账户10个Libra的交易。

问题处理

启动

  • 更新Rust:
    • 在libra文件夹中运行 rustup update
  • 更新protoc:
    • 更新protoc到3.6.0版本或者更高
  • 在libra文件夹中重新运行启动脚本
    • ./scripts/dev_setup.sh

客户端构建与运行

如果出现了构建错误,尝试移除libra文件夹的cargo的lock文件

  • rm Cargo.lock

如果客户端没有连接到测试网:

  • 检查网络连接
  • 确保使用了最新版本的客户端。拉取最新的Libra Core并重新运行客户端
    • ./scripts/cli/start_cli_testnet.sh

挖矿并添加钱款到账户

  • 如果你连接的测试网验证节点不可用,你会得到一个"Server unavailable" (服务不可用)消息,就像下面这样:
libra% account mint 0 110
>> Minting coins
[ERROR] Error minting coins: Server unavailable, please retry and/or check **if** host passed to the client is running
  • 如果在交易提交之后你的余额仍然没有更新,等一会然后再去查看余额。如果区块链中正在进行的交易很多的话可能会有一点的延迟。如果你的余额还是没有变化,尝试着再去挖矿。
  • 为了检查一个账户是否存在,需要查询账户状态。如果是一个索引是0的账户,输入这个:
libra% query account_state 0

转账命令

如果(你的客户端连接到的)测试网络节点不可用,或者连接超时,你会看到这样的错误:

libra% transfer 0 1 10
>> Transferring
[ERROR] Failed to perform transaction: Server unavailable, please retry and/or check if host passed to the client is running

为了解决转账错误:

  • 检查到测试网的网络连接
  • 检查发送者账户,确定它是存在的。对于索引是0的账户,使用下面的命令:
    • query account_state 0
  • 你可以使用 quit 或者 q 来尝试退出客户端,然后重新运行下面的命令来连接到测试网络
    • libra文件夹中运行 ./scripts/cli/start_cli_testnet.sh

额外查询命令的样例输出

通过账户和序列号查询交易

这个例子将会使用账户和序列号查询一笔单独的交易。

libra% query txn_acc_seq 0 0 true
>> Getting committed transaction by account and sequence number
Committed transaction: SignedTransaction {
 { raw_txn: RawTransaction {
    sender: 3ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a8,
    sequence_number: 0,
    payload: {,
      transaction: peer_to_peer_transaction,
      args: [
        {ADDRESS: 8337aac709a41fe6be03cad8878a0d4209740b1608f8a81566c9a7d4b95a2ec7},
        {U64: 10000000},
      ]
    },
    max_gas_amount: 10000,
    gas_unit_price: 0,
    expiration_time: 1560466424s,
},
 public_key: 55af3fe3f28550a2f1e5ebf073ef193feda44344d94c463b48be202aa0b3255d,
 signature: Signature( R: CompressedEdwardsY: [210, 23, 214, 62, 228, 179, 64, 147, 81, 159, 180, 138, 100, 211, 111, 139, 178, 148, 81, 1, 240, 135, 148, 145, 104, 234, 227, 239, 198, 153, 13, 199], s: Scalar{
  bytes: [203, 76, 105, 49, 64, 130, 162, 81, 22, 237, 159, 26, 80, 181, 111, 94, 84, 6, 152, 126, 181, 192, 62, 103, 130, 94, 246, 174, 139, 214, 3, 15],
} ),
 }
 }
Events:
ContractEvent { access_path: AccessPath { address: 3ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a8, type: Resource, hash: "217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc97", suffix: "/sent_events_count/" } , index: 0, event_data: AccountEvent { account: 8337aac709a41fe6be03cad8878a0d4209740b1608f8a81566c9a7d4b95a2ec7, amount: 10000000 } }
ContractEvent { access_path: AccessPath { address: 8337aac709a41fe6be03cad8878a0d4209740b1608f8a81566c9a7d4b95a2ec7, type: Resource, hash: "217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc97", suffix: "/received_events_count/" } , index: 0, event_data: AccountEvent { account: 3ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a8, amount: 10000000 } }

注意交易数量使用 microlibra(应该是libra的一种计量单位) 展示的。

查询结果

在下面的这个例子中,我们将会查询索引为0这个账户的"sent"事件。你会注意到在我们从这个账户发送了一笔账户之后会有一个事件。当前状态的证明(proof)也被返回了,因此可以验证没有丢失事件 - 当查询没有返回"limit"事件的时候会完成此事件。

libra% query event 0 sent 0 true 10
>> Getting events by account and event type.
EventWithProof {
  transaction_version: 3,
  event_index: 0,
  event: ContractEvent { access_path: AccessPath { address: e7460e02058b36d28e8eef03f0834c605d3d6c57455b8ec9c3f0a3c8b89f248b, type: Resource, hash: "217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc97", suffix: "/sent_events_count/" } , index: 0, event_data: AccountEvent { account: 46efbad798a739c088e0e98dd9d592c27c7eb45ba1f8ccbdfc00bd4d7f2947f3, amount: 10000000 } },
  proof: EventProof { ledger_info_to_transaction_info_proof: AccumulatorProof { siblings: [HashValue(62570ae9a994bcb20c03c055667a4966fa50d0f17867dd5819465072fd2c58ba), HashValue(cce2cf325714511e7d04fa5b48babacd5af943198e6c1ac3bdd39c53c87cb84c)] }, transaction_info: TransactionInfo { signed_transaction_hash: HashValue(69bed01473e0a64140d96e46f594bc4b463e88e244b694e962b7e19fde17f30d), state_root_hash: HashValue(5809605d5eed94c73e57f615190c165b11c5e26873012285cc6b131e0817c430), event_root_hash: HashValue(645df3dee8f53a0d018449392b8e9da814d258da7346cf64cd96824f914e68f9), gas_used: 0 }, transaction_info_to_event_proof: AccumulatorProof { siblings: [HashValue(5d0e2ebf0952f0989cb5b38b2a9b52a09e8d804e893cb99bf9fa2c74ab304bb1)] } }
}
Last event state: Some(
    AccountStateWithProof {
        version: 3,
        blob: Some(
            AccountStateBlob {
             Raw: 0x010000002100000001217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc974400000020000000e7460e02058b36d28e8eef03f0834c605d3d6c57455b8ec9c3f0a3c8b89f248b00e1f50500000000000000000000000001000000000000000100000000000000
             Decoded: Ok(
                AccountResource {
                    balance: 100000000,
                    sequence_number: 1,
                    authentication_key: 0xe7460e02058b36d28e8eef03f0834c605d3d6c57455b8ec9c3f0a3c8b89f248b,
                    sent_events_count: 1,
                    received_events_count: 0,
                },
            )
             },
        ),
        proof: AccountStateProof {
            ledger_info_to_transaction_info_proof: AccumulatorProof {
                siblings: [
                    HashValue(62570ae9a994bcb20c03c055667a4966fa50d0f17867dd5819465072fd2c58ba),
                    HashValue(cce2cf325714511e7d04fa5b48babacd5af943198e6c1ac3bdd39c53c87cb84c),
                ],
            },
            transaction_info: TransactionInfo {
                signed_transaction_hash: HashValue(69bed01473e0a64140d96e46f594bc4b463e88e244b694e962b7e19fde17f30d),
                state_root_hash: HashValue(5809605d5eed94c73e57f615190c165b11c5e26873012285cc6b131e0817c430),
                event_root_hash: HashValue(645df3dee8f53a0d018449392b8e9da814d258da7346cf64cd96824f914e68f9),
                gas_used: 0,
            },
            transaction_info_to_account_proof: SparseMerkleProof {
                leaf: Some(
                    (
                        HashValue(c0fbd63b0ae4abfe57c8f24f912f164ba0537741e948a65f00d3fae0f9373981),
                        HashValue(fc45057fd64606c7ca40256b48fbe486660930bfef1a9e941cafcae380c25871),
                    ),
                ),
                siblings: [
                    HashValue(4136803b3ba779bb2c1daae7360f3f839e6fef16ae742590a6698b350a5fc376),
                    HashValue(5350415253455f4d45524b4c455f504c414345484f4c4445525f484153480000),
                    HashValue(a9a6bda22dd6ee78ddd3a42da152b9bd39797b7da738e9d6023f407741810378),
                ],
            },
        },
    },
)

查询账户状态

在这个账户中,我们将会查询一个账户的状态。

libra% query account_state 0
>> Getting latest account state
Latest account state is:
 Account: 3ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a8
 State: Some(
    AccountStateBlob {
     Raw: 0x010000002100000001217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc9744000000200000003ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a800e1f50500000000000000000000000001000000000000000100000000000000
     Decoded: Ok(
        AccountResource {
            balance: 100000000,
            sequence_number: 1,
            authentication_key: 0x3ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a8,
            sent_events_count: 1,
            received_events_count: 0,
        },
    )
     },
)
 Blockchain Version: 3

运行一个本地的验证节点

为了在你的电脑上启动一个本地的验证节点并创建你自己本地的区块链网络(没有连接到Libra测试网络),需要确保你已经运行了之前所说的构建脚本,变更到Libra Core仓库的根路径,然后运行 libra_swarm ,就像下面这样:

$ cd ~/libra
$ cargo run -p libra_swarm -- -s

-p libra_swarm 是cargo开始运行 libra_swarm 包,这样会启动一个包含一个节点的本地区块链。

-s 选项启动一个连接到本地区块链上的本地客户端

为了看到开启节点与连接到Libra区块脸上的额外选项,运行:

$ cargo run -p libra_swarm -- -h

cargo运行命令可能需要一些时间来运行。如果这条命令的执行了且没有错误,那么一个Libra CLI客户端的实例和一个本地的验证节点就已经运行在你的系统中了。在成功执行的基础上,你将会看到包含着CLI客户端菜单和 libra 提示符的输出。

交易的生命周期

一旦你已经完成了你的第一笔交易,你可以去看看 交易的生命周期 的文档了,目的在于:

  • 深入了解交易从提交到执行的过程。
  • 了解在交易在Libra生态系统中提交与执行过程中,Libra验证器的各个逻辑组件之间的交互。
07-17 06:50