跨链三部曲之:Basecoin

前面我们讲到 Basecoin 是一个基于 tendermint 的 ABCI 应用,同时它也是一个跨链应用。这节,我给大家介绍如何利用它在不同的账户之间发送交易,并试着探索下其中的技术内幕。

Install

墙外的用户直接运行:

1
go get -u github.com/tendermint/basecoin/cmd/...

像我们被困在墙内的用户怎么办呢,Basecoin 是用 glide 管理依赖包的,老司机都知道通过 glide miror set 来设置那些被墙包的镜像,具体请看我的另一篇 《我是如何管理goalng包的》。

安装好后,会有两个工具:

  • basecoin 运行区块链节点
  • basecli 轻客户端命令行

Generate some keys

首先,生成 2 组 key,一个用来接收初始分配的币,一个用来发送币。

1
2
3
4
5
6
7
8
# WARNING: this will wipe out any existing info in the ~/.basecli dir
# including private keys, don't run if you have lots of local state already
# while we're at it let's remove the working directory for the full node too
basecli reset_all
rm -rf ~/.basecoin

basecli keys new cool
basecli keys new friend

生成 key 时会提示你输入密码,生成好后,查看已有的 key 列表:

1
2
3
4
AllendeMacBook-Pro:~ $ basecli keys list
All keys:
cool CD86946FB29D3F9666294BEAC036062F4340FD1F
friend 851010D4FDA491244ACAAD5B0E97E71DBFD85625

你可以看到在目录下已经有 cool 和 friend 两组 key 了:

1
2
3
4
5
6
AllendeMacBook-Pro:~ $ ls -l ~/.basecli/keys/
total 32
-rw------- 1 hxz staff 140 4 26 11:44 cool.pub
-rw------- 1 hxz staff 238 4 26 11:44 cool.tlc
-rw------- 1 hxz staff 142 4 26 11:45 friend.pub
-rw------- 1 hxz staff 240 4 26 11:45 friend.tlc

Initialize Basecoin

初始化 Basecoin 区块链:

1
2
3
4
# WARNING: this will wipe out any existing info in the ~/.basecoin dir
# don't run if you have lots of local state already
rm -rf ~/.basecoin
basecoin init $(basecli keys get cool | awk '{print $2}')

basecoin 会创建出运行区块链必须的文件,在 ~/.basecoin 目录下,有一个 validator(验证节点)和一个账户(cool)。

1
2
3
4
5
6
7
AllendeMacBook-Pro:~ $ ls -l ~/.basecoin/
total 32
-rw-r--r-- 1 hxz staff 309 4 26 11:47 config.toml
drwx------ 2 hxz staff 64 4 26 11:47 data
-rw-r--r-- 1 hxz staff 529 4 26 11:47 genesis.json
-r-------- 1 hxz staff 368 4 26 11:47 key.json
-r-------- 1 hxz staff 475 4 26 11:47 priv_validator.json

Start

准备就绪!现在,我们可以启动 Basecoin 区块链了:

1
basecoin start

你可以从控制台上看到,Basecoin 会每隔一秒出一个新块。

Initialize Light-Client

现在,Basecoin 区块链已经运行,我们可以初始化 basecli了,它可以发送交易给 basecoin,并查询 basecoin 区块链的状态。

打开一个新的命令行窗口:

1
basecli init --node=tcp://localhost:46657 --genesis=$HOME/.basecoin/genesis.json

通过提供 genesis 配置文件给 basecli,它会解析出区块链的 chainID 和 validator 集合,basecli 需要这些信息才能通过 basecoin 区块链的密码学验证逻辑。

注意:--genesis 只有在 validator 集合没有变动的情况下才能生效,如果之前 validator 集合发生过更改,你需要通过其他方式来找到当前区块链的 validator 集合。

Send transactions

现在,我们可以发送交易了。

首先,来看下我们之前创建的两个账户的资产信息:

1
2
3
4
ME=$(basecli keys get cool | awk '{print $2}')
YOU=$(basecli keys get friend | awk '{print $2}')
basecli query account $ME
basecli query account $YOU

第一个账户显示出了它的资产信息,第二个账户显示账户不存在。让我们从第一个账户发点币给第二个账户:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ basecli tx send --name=cool --amount=1000mycoin --to=$YOU --sequence=1
{
"check_tx": {
"code": 0,
"data": "",
"log": ""
},
"deliver_tx": {
"code": 0,
"data": "96905F5B71953A6C9BF20ABF05B2C614176BC0A9",
"log": ""
},
"hash": "743D40A6EB009E18C162FA7DA98B57FBC324187E",
"height": 1013
}

第二个账户现在有 1000 个 mycoin 了,我们可以从第二个账户发回 500 个给第一个账户:

1
basecli tx send --name=friend --amount=500mycoin --to=$ME --sequence=1

如果我们发送的币超出发送者拥有的数量,毫无疑问,会报错:

1
basecli tx send --name=friend --amount=500000mycoin --to=$ME --sequence=2

从上面例子都可以看出,每次发送交易时,需指定发送者名字 --name,发送币及数量 --amount,接收者名字 --to 以及本次交易的序列号 --sequence(有序递增),区块链则返回本次交易 hash,我们可以根据这个 hash 查询到本次交易在区块链上的记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
basecli query tx 743D40A6EB009E18C162FA7DA98B57FBC324187E
{
"height": 1013,
"data": {
"type": "send",
"data": {
"gas": 0,
"fee": {
"denom": "mycoin",
"amount": 0
},
"inputs": [
{
"address": "CD86946FB29D3F9666294BEAC036062F4340FD1F",
"coins": [
{
"denom": "mycoin",
"amount": 1000
}
],
"sequence": 1,
"signature": {
"type": "ed25519",
"data": "66EB328A0CD5AF23C7740CF833E20D3D7A424D3535528EBCDFACB39EA92FE354DDF5EBE7B04D4BB064D10AA675F561E9EC94FCB896DE830234F5E191B1815C09"
},
"pub_key": {
"type": "ed25519",
"data": "DB09171E2557503457F8C4DD518A0B030089F0A967068C8F8C69EBC59CCFF0F9"
}
}
],
"outputs": [
{
"address": "851010D4FDA491244ACAAD5B0E97E71DBFD85625",
"coins": [
{
"denom": "mycoin",
"amount": 1000
}
]
}
]
}
}
}

Accounts and Transactions

为了更好地理解 basecli 以及 basecoin 是如何工作的,我们需要先补点跟账户、交易相关的基础知识

Accounts

受以太坊的账户系统的启发,Basecoin 维护了一个账户系统,每个账户包含一个公钥、不同币种的资产信息以及一个严格递增的交易序列号。请注意:Basecoin 是一个多数字货币资产钱包,因此每个账户可以有许多种不同的 token。

1
2
3
4
5
6
7
8
9
10
11
12
type Account struct {
PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known.
Sequence int `json:"sequence"`
Balance Coins `json:"coins"`
}

type Coins []Coin

type Coin struct {
Denom string `json:"denom"`
Amount int64 `json:"amount"`
}

如果你想往区块链里添加更多的币种,只需要在区块链初始化前手动编辑 ~/.basecoin/genesis.json

账户被序列号保存在默克尔树中,键为 base/a/<address>,其中<address>是账户的地址。一般地,账户地址是一个 20 字节的公钥哈希(RIPEMD160算法),当然,其他格式也支持。Basecoin 中用到的默克尔树是一个平衡二叉查找树,也称 IAVL树。

Transactions

Basecoin 定义里一个简单的交易类型 SendTx,它包含一个或多个输入和输出,允许 token 从一个账户转移到其他账户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type SendTx struct {
Gas int64 `json:"gas"`
Fee Coin `json:"fee"`
Inputs []TxInput `json:"inputs"`
Outputs []TxOutput `json:"outputs"`
}

type TxInput struct {
Address []byte `json:"address"` // Hash of the PubKey
Coins Coins `json:"coins"` //
Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput
Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx
PubKey crypto.PubKey `json:"pub_key"` // Is present if Sequence == 0
}

type TxOutput struct {
Address []byte `json:"address"` // Hash of the PubKey
Coins Coins `json:"coins"` //
}

注意到SendTx还包含 GasFee字段,Gas 限制了每次交易最多允许执行的计算次数,Fee 指这比交易的交易费,validator 根据交易费大小对接收到的交易进行打包排序,这有点类似于比特币中手续费概念。

和以太坊中的 Gas、GasPrice 概念稍微有点不同,以太坊中的交易费 Fee = Gas x GasPrice,在 Basecoin 中,Gas 和 Fee 是两个独立的概念。

只有在 Sequence == 0 的时候,PubKey才是必选项。之后,它会存储在默克尔树该账户对应的 key 中,后续的交易只需要引用PubKey对应的 Address就可以指定发送者了。

最后,多个输入账户和多个输出账户可以让我们在一笔交易中就实现在不同账户间多个 token 的转移,当然,你必须保证”价值守恒”(输入=输出,交易费已经在 Fee 中指定),并且,每个输入都要有对应账户的私钥签名。

Conclusion

本章介绍了 basecoin 和 basecli 两个命令行工具及其基本用法,演示了如何启动一条 basecoin 区块链,怎样在账户间发送 token,并讨论了 basecoin 中账户和交易类型,下一章,我们将介绍 Basecoin 的插件系统,实现不同插件的扩展。

hxzqlh wechat