BAR主链交易所/DApp接入文档

一、 介绍

BAR钱包类资产的对接需要运行以下两个程序:见证节点 witness_node 和命令行钱包 cli_wallet。

witness_node 通过 P2P 方式连接到均衡链网络,从网络接收最新区块,向网络广播本地签署的交易包。

cli_wallet 通过 websocket 方式连接到 witness_node, 管理钱包文件; 提供交易签名功能,签名后通过 witness_node 向外广播; 通过 http rpc 的方式提供 API 供其他程序调用。

二、 软硬件需求

操作系统: Ubuntu 16.04 LTS 64位系统,内核版本4.10.0-28-generic 以上。

硬件: 内存 8GB+,硬盘100GB+。

依赖:安装 NTP 服务:

sudo apt-get install ntp

三、 部署和启动程序

1. 安装包获取

到官方git下载源码编译安装 https://github.com/bar-chain/bar-core

git clone https://github.com/bar-chain/bar-core.git

2. 编译程序

进入目录,然后执行命令:

# 安装依赖(如果已经安装过可以跳过)
sudo apt-get install autoconf cmake make automake libtool git libboost-all-dev libssl-dev g++ libcurl4-openssl-dev
# 编译
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .
make

编译成功后copy可执行文件到需要安装的目录

cp programs/witness_node/witness_node /data/barnode/
cp programs/cli_wallet/cli_wallet /data/barnode/

3. 启动见证节点witness_node, 同步数据

进入BAR目录,启动均衡链见证节点witness_node

cd /data/barnode/
./witness_node --data-dir=trusted_node --rpc-endpoint="127.0.0.1:7002" &

witness_node主要参数介绍如下:

# 数据及配置文件的目录
--data-dir=trusted_node

# rpc服务侦听地址及端口(端口可修改)
--rpc-endpoint=127.0.0.1:7002

首次启动后程序会生成数据目录即data-dir指向的目录,在目录里有config.ini,将seed-nodes参数配置改为如下,当有新的可用节点也可以用下面的方式进行添加,重启后就会与配置中的这些节点进行连接

seed-nodes = ["node0.barc.io:7002" "node1.barc.io:7002" "node2.barc.io:7002" "node3.barc.io:7002" "node4.barc.io:7002" "node5.barc.io:7002" "node6.barc.io:7002"]

完全同步区块需要1小时左右。通过后台日志文件tail -f trusted_node/logs/witness.log可查看区块同步进度, 如果在正常同步区块,日志信息大致是这样的:

root@xxxxx:/data/barnode# tail -f trusted_node/logs/witness.log
3119932ms th_a       application.cpp:511           handle_block         ] Got block: #76175 0001298fa7c03875fcde284ee2c588c71b00545d time: 2018-10-24T03:52:00 latency: -67 ms from: init1  irreversible: 76167 (-8)
3129926ms th_a       application.cpp:511           handle_block         ] Got block: #76176 00012990ae430c06f4ad0a66169cf27aa9b46e6a time: 2018-10-24T03:52:10 latency: -73 ms from: init2  irreversible: 76168 (-8)
3139920ms th_a       application.cpp:511           handle_block         ] Got block: #76177 00012991d57358f220fee70ea363d6c29b53aa2f time: 2018-10-24T03:52:20 latency: -79 ms from: init7  irreversible: 76168 (-9)
3149916ms th_a       application.cpp:511           handle_block         ] Got block: #76178 00012992f4492b8f3811f6010b8e81fa6341a20e time: 2018-10-24T03:52:30 latency: -83 ms from: init4  irreversible: 76168 (-10)
3159912ms th_a       application.cpp:511           handle_block         ] Got block: #76179 00012993cbaefe29e0604129fc0ef0fef21427b1 time: 2018-10-24T03:52:40 latency: -87 ms from: init9  irreversible: 76170 (-9)
3169904ms th_a       application.cpp:511           handle_block         ] Got block: #76180 000129945a270ea4f784eccb084825c689781a86 time: 2018-10-24T03:52:50 latency: -95 ms from: init6  irreversible: 76171 (-9)
3179898ms th_a       application.cpp:511           handle_block         ] Got block: #76181 0001299571d6b97afd3ce505b327887f884199cd time: 2018-10-24T03:53:00 latency: -101 ms from: init8  irreversible: 76173 (-8)
3189892ms th_a       application.cpp:511           handle_block         ] Got block: #76182 0001299660af2420fe4e8795b61f3c3af3404713 time: 2018-10-24T03:53:10 latency: -107 ms from: init3  irreversible: 76174 (-8)
3199885ms th_a       application.cpp:511           handle_block         ] Got block: #76183 0001299796f31a8032530eeb985e2aa7ce1726ce time: 2018-10-24T03:53:20 latency: -114 ms from: init0  irreversible: 76175 (-8)
3209882ms th_a       application.cpp:511           handle_block         ] Got block: #76184 00012998c88e8ffe202dcc07752ee6c4ce737f15 time: 2018-10-24T03:53:30 latency: -117 ms from: init1  irreversible: 76176 (-8)

[注意] 以上witness_node启动,默认不保存帐户交易历史,如果需要保存帐户交易历史,可以增加启动参数:

# 只保存交易所/DApp帐户的交易历史(其中1.2.xxx替换为交易所/DApp帐户id)
./witness_node --data-dir=trusted_node --rpc-endpoint="127.0.0.1:7002"  --track-account "\"1.2.xxx\"" --partial-operations=true &

# 保存所有帐户的交易历史(会占用大量内存,建议机器物理内存配置64GB)
./witness_node --data-dir=trusted_node --rpc-endpoint="127.0.0.1:7002" --max-ops-per-account=10000000 --partial-operations=false &

4. 运行命令行钱包cli_wallet

命令行钱包cli_wallet连接witness_node:

# 钱包接点连接到witness_node的rpc-endpoint地址和端口,并对外暴露出8091端口用于HTTP RPC请求
./cli_wallet -s ws://127.0.0.1:7002 -H 127.0.0.1:8091 

首先需要为钱包设置一个钱包密码(这个密码是本地的,用来解锁钱包):

new >>> set_password my_password
# 执行成功后会显示:
locked >>>
# 然后解锁钱包:
locked >>> unlock my_password
# 解锁成功会显示:
unlocked >>>

使用 info 命令可以查看当前区块同步情况

unlocked >>> info
info
{
  "head_block_num": 283961,
  "head_block_id": "00045539758e8123573e66115a9d20727eaf752f",
  "head_block_age": "4 seconds old",
  "next_maintenance_time": "19 hours in the future",
  "chain_id": "32a81eefffe46f2b486d9c7c6b3990f8a1c1d97713eed46bfbb3925d2a4e986e",
  "participation": "100.00000000000000000",
  ...
 }
# head_block_age表示最新的区块时间,系统每10秒出一块

更多cli_wallet接口,可以查看wallet api说明文档

5. 设置命令行钱包cli_wallet, 导入帐户活跃权限私钥

如果你还没有帐户,请到u.barc.io创建账户

在命令行钱包执行命令:

# 导入帐户的活跃权限私钥
# 执行该命令后,私钥会加密保存在当前目录下的wallet.json文件中,此文件可作为钱包备份
unlocked >>> import_key account_name wif_key true
# 例如 import_key bar-test 5KVqQFa1hcRsNHyqX1Nh8CgY7AzwoHwpxKx5eTwxv7FegUBLjjg true

[注意] 新创建的帐户,需要等区块同步完成后,才能够导入帐户私钥。

// 列出钱包已导入的帐户
unlocked >>> list_my_accounts
// 查看帐户信息
unlocked >>> get_account account_name
// 根据帐户名,查询帐户id
unlocked >>> get_account_id account_name
// 查看帐户余额
// list_account_balances 命令会列出BAR上所有资产的余额
unlocked >>> list_account_balances account_name
// 转帐(需要帐户有余额)
// 其中BAR代表BAR资产
// transfer2 <from帐户> <to帐户> <转帐数量>  <转帐资产> <转账备注> true
unlocked >>> transfer2 from_account to_account 100 BAR "" true
# 例如:transfer2 bar-test bar-test2 100 BAR "" true
// 查询最新区块高度, 其中返回结果中head_block_number即最新区块高度, last_irreversible_block_num为最大的不可逆区块高度
unlocked >>> get_dynamic_global_properties
{
  "id": "2.1.0",
  "head_block_number": 232953,
  "head_block_id": "00038df911b9ee9dc2c0c4c1453ddf30f28b020c",
  "time": "2019-05-29T05:54:00",
  "current_witness": "1.6.4",
  "next_maintenance_time": "2019-05-29T06:00:00",
  "last_budget_time": "2019-05-29T05:50:10",
  "witness_budget": 400000000,
  "accounts_registered_this_interval": 0,
  "recently_missed_count": 1747247,
  "current_aslot": 2185571,
  "recent_slots_filled": "255190924871116727680100571361552891775",
  "dynamic_flags": 0,
  "last_irreversible_block_num": 232945
}
// 查询区块信息,查询时指定区块号
unlocked >>> get_block 225087
// 根据资产名字,查询资产信息,其中返回的id表示资产id
locked >>> get_asset BAR
// 解密备注信息
unlocked >>> read_memo {"from": "BAR83LRs1kzefCXJEeT4Wy393KoLf2cJvbDU1EGh3yoo7WkYv82Mn","to": "BAR6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV","nonce": "3748536725483201275","message": "1bbad0d1f239ee9c252fe4dbc6c9e953"}

转帐命令行接口transfer2, 其中transfer2执行成功后,返回当前transaction的id

6. 后台运行cli_wallet

在导入钱包私钥完成后,ctrl + c退出,此时会生成本地的钱包文件wallet.json,文件中保存了导入帐户信息、加密的私钥和server地址等信息,cli_wallet重新启动时会加载wallet.json文件。

重新启动cli_wallet,启动时带上参数 -d &, 以daemon模式运行,如下:

cd /data/barnode/
nohup ./cli_wallet --data-dir=trusted_node -s ws://127.0.0.1:7002 -H 127.0.0.1:8091 -d >>wallet.out 2>1 &

上述命令启动后,需要再多敲一次回车,然后在shell中输入exit来退出终端。相关的控制台输出被重定向到wallet.out文件。

四、 交易所/DApp冲提币指南

1. 用户向交易所/DApp充值流程

  1. 使用均衡链钱包转帐,每一笔交易都可以带一个备注(又称memo),备注是加密的,只有转帐双方才能解密查看。
  2. 交易所/DApp可以为每个注册用户分配一个唯一的备注(比如可以使用帐户名、手机号、user id等),备注长度和内容无限制。
  3. 用户向交易所/DApp的均衡链钱包帐户转帐,带上自己的备注。
  4. BAR上发行的资产,资产名字和资产ID是唯一的。比如BAR的资产ID是1.3.0。可以使用cli_wallet的get_asset接口查询资产ID。
  5. 交易所/DApp通过扫描自己帐户的交易历史,根据转帐的备注,处理用户充值。
  6. 已经处理过的交易历史,记录下对应的txID,避免重复入帐
  7. 对于备注为空以及未成功入帐的交易历史,记录下txID,便于后续人工处理工单。

具体步骤如下:

1. 解锁钱包:

[注意] 这里调用的RPC接口为cli_wallet暴露出来的http接口(即上文中的8091端口)

request:
// unlock解锁钱包,其中my_password为解锁密码
curl --data '{"jsonrpc": "2.0", "method": "unlock", "params": ["my_password"], "id": 1}' http://127.0.0.1:8091/rpc

解锁成功,返回:

{"id":1,"jsonrpc":"2.0","result":null}

2. 获取最大不可逆区块高度

调用cli_wallet的get_dynamic_global_properties接口,查看当前最大不可逆区块高度(也即最大的不可回退区块高度)。 小于此区块高度的区块,其包含的交易都是已经被确认不可回退的,即是最终被确认的。

返回结果:

{
  "id": 1,
  "result": {
    "id": "2.1.0",
    "head_block_number": 284283,// 当前最大区块号(区块高度)
    "head_block_id": "0004567b992e6cc53153d3927b14a11f02ad611d",
    "time": "2018-11-17T06:08:40",
    "current_witness": "1.6.1",
    "next_maintenance_time": "2018-11-18T00:00:00",
    "last_budget_time": "2018-11-17T00:00:00",
    "witness_budget": "6431000000",
    "accounts_registered_this_interval": 0,
    "recently_missed_count": 0,
    "current_aslot": 526827,
    "recent_slots_filled": "340282366920938463463374607431768211455",
    "dynamic_flags": 0,
    "last_irreversible_block_num": 284276 // 最大的不可回退区块号(区块高度)
  }
}
curl --data '{"jsonrpc": "2.0", "method": "get_dynamic_global_properties", "params": [], "id": 1}' http://127.0.0.1:8091/rpc

3. 指定区块高度,获取区块:

当前扫描到的区块高度小于最大的不可回退区块高度时,依次获取区块内容,解析区块中operation id为0 (0代表转帐)的operation,记录接收方帐户id等于交易所/DApp账户id的记录以及对应的transaction_id。

request:
// unlock解锁钱包,其中my_password为解锁密码
curl --data '{"jsonrpc": "2.0", "method": "get_block", "params": ["225087"], "id": 1}' http://127.0.0.1:8091/rpc
response:
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "previous": "00036f3e91543775e6cdcb2b6d46d60aab736817",
    "timestamp": "2018-11-10T09:39:10",
    "witness": "1.6.7",
    "transaction_merkle_root": "46e1639f2bdeaf99edacd44dd680ac7ca82141e4",
    "extensions": [],
    "witness_signature": "20232542ca916e7c14261d4c8132101b602fff889f66db13cf037dac5b644d992f649821482a64c3266348edd5d1ce4db86258710a9faa2182359eaff40422f6ab",
    "transactions": [{
        "ref_block_num": 28478,
        "ref_block_prefix": 1966560401,
        "expiration": "2018-11-10T09:39:26",
        "operations": [[
            0,{                         // operation id为0的,表示转帐
              "fee": {                  // 矿工费
                "amount": 150000,
                "asset_id": "1.3.0"
              },
              "from": "1.2.18",         // 转出方帐户id
              "to": "1.2.17",           // 接收方帐户id
              "amount": {
                "amount": 100000,       // 转帐数量,区块上的数量是放大的,需要除以10万,实际转帐数量为: 100000 / 100000 = 1
                "asset_id": "1.3.0"     // 转帐的资产id, 其中1.3.0表示BAR;  可以使用cli_wallet的get_symbol方法获取资产id
              },
              "memo": {                 // 转帐的备注(memo) ,区块上是加密的,需要调用cli_wallet解密(只有转帐双方才可以解密)
                "from": "BAR83LRs1kzefCXJEeT4Wy393KoLf2cJvbDU1EGh3yoo7WkYv82Mn",
                "to": "BAR6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
                "nonce": "3748536725483201275",
                "message": "1bbad0d1f239ee9c252fe4dbc6c9e953"
              },
              "extensions": []
            }
          ]
        ],
        "extensions": [],
        "signatures": [
          "20406b50879d6aabaac9be7ef4d52b0c216abb6ca8329893b18499c41a6812fff45f8c277047aba498fcc77b889a9a338ac411c8fbfd7cd784910f71ee18a8ca6b"
        ],
        "operation_results": [[
            0,{}
          ]
        ]
      }
    ],
    "block_id": "00036f3faae2b1fbf1b25037bac4a49ecb9bc0ef",
    "signing_key": "BAR6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
    "transaction_ids": [              // 交易的txID,和上面的transactions顺序一一对应
      "cf986e2cdea77ff0f06ddcad1b28ac2bb2d86cbf"
    ]
  }
}

3. 调用钱包的read_memo接口, 解密备注(memo):

若没有调用unlock解锁钱包,则查询出的转帐交易memo是无法解密的。此处,只有交易双方才能解密memo。

request:
curl --data '{"jsonrpc": "2.0", "method": "read_memo", "params": [{"from": "BAR83LRs1kzefCXJEeT4Wy393KoLf2cJvbDU1EGh3yoo7WkYv82Mn","to": "BAR6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV","nonce": "3748536725483201275","message": "1bbad0d1f239ee9c252fe4dbc6c9e953"}], "id": 1}' http://127.0.0.1:8091/rpc
response:
{"id":1,"jsonrpc":"2.0","result":"test"}

2. 提币

1. 转账到用户提供的账号及标签

request:
curl --data '{"jsonrpc": "2.0", "method": "transfer2", "params": ["bar-test", "bar-test2", 100, "BAR", "tag", true], "id": 1}' 
http://127.0.0.1:8091/rpc 
response:
{
    "id": 1,
    "result": [
    "1828e269b52b5f4f6da1fcc0040267f024a77215", // 交易的txID
  {
        "ref_block_num": 44331,
        "ref_block_prefix": 1235770902,
        "expiration": "2019-05-30T07:21:50",
        "operations": [
            [0, {
                "fee": {
                    "amount": 167968,
                    "asset_id": "1.3.0"
                },
                "from": "1.2.18",
                "to": "1.2.7",
                "amount": {
                    "amount": 10000000,
                    "asset_id": "1.3.0"
                },
                "memo": {
                    "from": "BAR7bk5C5oPo64amTHKB51kuBxpUVEKKmpqYKXiHj3MVoG8SQBPVB",
                    "to": "BAR6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
                    "nonce": "5838224317957728303",
                    "message": "85556c8cf78999373c2b146cf8267c81"
                },
                "extensions": []
            }]
        ],
        "extensions": [],
        "signatures": ["20563868329f1e475d8cb7a5e7b0c4d3b0edb6d5aac332b48ccc172dbdd82f68cf1d7d8d4fd2ed7b8c2c0d5d1ac62cd9ad1953f6c6a05b879e27c7cd2a6d252238"]
    }]
}

[注意] 提币后需要跑和充币类似的流程,当在扫描到不可回退块为止扫描到转账的txID,才算实际转账成功

五、测试链搭建

#生成测试链的json文件
./witness_node --create-genesis-json test.json
#加载测试链的json文件
./witness_node --genesis-json test.json --data-dir=trusted_node --rpc-endpoint=0.0.0.0:7002 >> witness.out 2>&1 &

在trusted_node/config.ini里更改以下配置内容

seed-nodes = []
enable-stale-production = true
witness-id = "1.6.1"
witness-id = "1.6.2"
witness-id = "1.6.3"
witness-id = "1.6.4"
witness-id = "1.6.5"
witness-id = "1.6.6"
witness-id = "1.6.7"
witness-id = "1.6.8"
witness-id = "1.6.9"
witness-id = "1.6.10"
witness-id = "1.6.11"

查看witness.out,找到chain-id,也可以直接用cli_wallet连接,在报错信息里会提示remote chain-id

#钱包连接节点
./cli_wallet --chain-id 8ea03b828b419028be4227454a4f58b54ed85053c1c16443634e82d3e7d612c2 -s ws://127.0.0.1:7002 -H 127.0.0.1:8091

首次连上后老样子:set_password、unlock然后导入私钥,程序可以连接8091端口进行rpc操作

#导入私钥
import_key nathan 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
import_key init0 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
#首次导入余额
import_balance nathan ["5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"] true
#转账 从nathan转100枚BAR到init0账号
transfer2 nathan init0 100 BAR "test memo" true

BAR代币的对接也可以参照此方法,最终对接正式链时候只需要注意asset_id就行

六、常见问题

  1. 区块同步时报错

观察后台日志文件trusted_node/logs/witness.log, 如果日志持续报错,如"unlinkable block", "block does not link to known chain",这是区块同步出错了。

解决方法:

  1. 区块同步异常,有可能本地的区块链文件坏掉了。需要停止witness_node程序,然后删除trusted_node, 重新启动witness_node。

  2. 或者不需要删除trusted_node目录,启动命令加上参数--resync-blockchain, 会重新同步区块。

  3. 如何正常关闭witness_node

  4. 如果witness_node没有后台运行,则执行一次Ctrl + C, 然后等待程序保存内存数据后自动退出。

  5. 如果witness_node运行在后台, 执行kill -s SIGINT $(pgrep witness_node),等待程序保存内存数据后自动退出。不能使用kill -9, 否则下次启动会重建索引,启动比较慢。

  6. witness_node重启以后,需要重新启动cli_wallet。因为cli_wallet后台运行时,不会自动退出。关闭cli_wallet的方法:执行
    kill -s SIGINT $(pgrep cli_wallet)

  7. 如果异常退出,则重新启动时,很可能需要重建索引,启动比较慢。如果 witness_node 出现异常,一般先尝试带--replay-blockchain 参数重启,即手工触发重建索引。

  8. witness_node重启后,需要重启cli_wallet。如果cli_wallet是后台运行的,cli_wallet不会因witness_node退出而自动退出,也需要重启。

  9. cli_wallet进程偶尔退出问题

如果遇到cli_wallet后台运行一段时间后退出的情况,可能的原因是终端掉线,建议后台运行成功后,关闭当前终端。或者在启动命令行之前加上nohup:

nohup ./cli_wallet -s ws://127.0.0.1:7002  --enable-rpc-log -H 127.0.0.1:8091 -d >>wallet.out 2>1 &

七、注意事项

  1. 用户充值。每笔转账可以带一个备注(memo),交易所/DApp通过这个备注来区分是哪个用户的充值。具体备注与交易所/DApp用户关联关系,请交易所/DApp自行设定。备注是加密的,只有转帐双方才可以解密。
  2. 转帐手续费问题。转帐的手续费由2部分组成:基本手续费 + memo费用,其中基本手续费为1.5BAR, memo费用按总字节长度收费,2BAR/KB。一次转帐手续费计算:1 + 2*(KBs of memo) BAR , 取KB时截断取整。 带备注的转帐,手续费一般在[1, 2] BAR之间(假设备注长度不会超过32字节)。
  3. 交易所/DApp查询交易历史,处理用户充值时,需要处理用户充值时填入memo的特殊情况。比如交易所/DApp提供给用户的memo是1234567,而用户充值填写的memo是"我的memo是1234567!",类似情况需要考虑。
  4. 不建议交易所/DApp提供类似BAR123456的memo,把memo作为帐户进行充值。memo长度和内容无限制,建议memo长度超过10位,并且以数字开头
  5. 钱包的json rpc接口,帐户名需要传入小写,不能有大写字母。用户提现到均衡链钱包时,需要将绑定的帐户名转为小写。
  6. 人工处理用户充值问题:未正确入帐的交易信息,交易所/DApp需要妥善保存下来,以备客服人工入帐。用户提交工单时,需要用户提供txID(用户可以通过钱包查看当前转帐信息获得),txID可以保证用户充值转帐的唯一性。务必注意:平台保存好人工处理过的txID, 同一txID只处理1次。如果不同的注册用户使用同一txID提交工单的,属于欺骗!
  7. 系统中存在多种资产,其中资产ID(asset_id)1.3.0为BAR。监听不同的用户资产充值时,请务必校验转帐交易中的asset_id字段。

  8. 用户提现。调用transfer2处理用户提现时,转帐数量请传入字段串,加双引号。如下: curl --data '{"jsonrpc": "2.0", "method": "transfer2", "params": ["from_account", "to_account", "100", "BAR", "", true], "id": 1}' http://127.0.0.1:8091/rpc

  9. 资产精度,通常为小数点后5位(get_asset接口中的precision字段为资产精度),即最小单位为0.00001。均衡链中没有小数,数字在系统中被放大了10万倍,所以get_block接口返回的数字,比如转帐的数量,需要除以10万,才是真正的数量。
  10. 转帐数量不能超出资产精度,否则会失败。
  11. 钱包状态为locked状态时只能查询,不能转帐,不能解密转帐备注。如果需要转帐或者查询交易历史,需要先unlock。
相关文档:
  1. cli_wallet API
  2. witness_node API