区块链公链都是基于 p2p 网络, 本篇文章将建立一个多节点不同职责参与的 EOS 的测试网络, 根据路上发现的可做文章的技术点大做文章.
关键字: EOS 组网, 全节点, 交易确认, boot sequence,stake, 帕累托分配模型, 竞选出块节点, EOS 出块奖励, 代理投票, resign
构建源节点
源节点就是第一个 EOS 节点(Genesis node), 也可以叫主节点, EOS 多节点组网的前提是已经对单机环境非常熟悉, 我们的架构如下:
配置 config.ini, 默认位置: ~/.local/share/nodeos/config/config.ini, 需要解释的几个配置项:
http-server-address = 0.0.0.0:8888, 这里设置四个 0 代表本地可以通过 localhost 或者 127.0.0.1 调用 http 接口, 同时外部可以通过本机固定 ip 访问.
p2p-listen-endpoint = 0.0.0.0:9876,p2p 网络本机监听端口, 监听外部接入的 p2p 节点, 这里的四个 0 的 ip 配置意义同上.
bnet-endpoint = 0.0.0.0:4321,bnet 是使用一个非常简单的算法来同步两条区块链. 主要工作是两条链上的确权, 共识, 广播, 同步区块, 保持默认配置即可.
p2p-peer-address = ip:port, 对端 p2p 节点地址, 可以设置多个.
enable-stale-production = true, 意思是可以不经过确权直接出块, 单节点时要配置为 true, 多节点出块由于需要各方确权共识, 要配置为 false.
producer-name = eosio, 出块者, 创世块, 默认 eosio 账户
signature-provider = EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV=KEY:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 , 密钥对, 公钥加私钥, 对应 eosio 账户, 这对秘钥是写死的, 不可改变.
注意对钱包进行修改时, 例如删除钱包数据, 重新创建, 要预先手动 kill 掉 keosd 进程.
采用后台进程的方式启动节点, 同时保存日志.
nohup nodeos>logs/nodeos-log.log 2>&1&
采用后台进程的方式启动钱包, 同时保存日志.
nohup keosd>logs/keosd-log.log 2>&1&
查看日志, 通过 tail -500f logs/[filename] 的方式动态追踪日志.
查看进程
- liuwenbin@liuwenbin:~$ pgrep nodeos
- 1959
- liuwenbin@liuwenbin:~$ pgrep keosd
- 1978
为 cleos 设置携带 keosd 的别名(keosd 服务一般会与 nodeos 部署在同一台机器上, 如果是普通用户的业务场景, 则与 nodeos 服务不在一台机器, 需要指定 ip), 保险起见, 我们直接将其设置到. bashrc 文件中, 并 source 使其生效.
alias cleos='cleos --wallet-url="http://localhost:8889"'
注意这里的 --wallet-url 的值要与钱包目录, 默认是用户根目录下的 eosio-wallet/config.ini 中的 http-server-address 配置相同, 从而保证我们访问的钱包是同一个. 如果要更换 ip 或端口的话, 首先要修改 config.ini 的配置, 然后启动 keosd, 然后 alias 别名覆盖设置即可.
停止进程, 注意由于我们使用的
其他更详细的描述请转到启动一个单独节点.
单机准备就完成了, 可以看出 nodeos 和 keosd 是分开的进程, 最后通过 alias 别名将他们结合在了一起.
构建全节点
全节点不出块但会保持同步完整区块数据到本地. 在另一台机器上, 同样的拉取同版本源码构建安装命令, 然后修改配置文件 config.ini. 这里我们要修改的是:
去掉 producer-name 以及 signature-provider 配置项.
enable-stale-production 配置在全节点无所谓, 因为它只约束出块者, 所以在这里可以去掉.
p2p-peer-address = ip:port, 对端 p2p 节点地址, 可以设置多个.
sync-fetch-span = 1000, 同步区块的速度, 步进.
直接键入命令 nodeos 启动节点.
全节点版本更新
源码拉取 -> checkout 最新版本号 -> 构建执行环境 -> 修改本地配置文件
然后使用命令:
nodeos --delete-all-block
清空旧的区块数据, 重新启动链.
全节点日志分析
- 3435662ms thread-0 net_plugin.cpp:3055 plugin_startup ] starting listener, max clients is 2
- 3435671ms thread-0 net_plugin.cpp:749 connection ] created connection to 47.93.127.182:9876
- 3435672ms thread-0 net_plugin.cpp:1969 connect ] host: 47.93.127.182 port: 9876
- 3449419ms thread-0 net_plugin.cpp:773 connection ] accepted network connection
- 3449851ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block 5f1a90b86a211c11... #1000 @ 2018-06-21T03:24:52.500 signed by eosio [trxs: 0, lib: 999, conf: 0, latency: 109957351 ms]
- 3450291ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block 1476bbf0e003fcf4... #2000 @ 2018-06-21T03:33:12.500 signed by eosio [trxs: 0, lib: 1999, conf: 0, latency: 109457791 ms]
- 3450785ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block 8c4bea86b3433b78... #3000 @ 2018-06-21T03:41:32.500 signed by eosio [trxs: 0, lib: 2999, conf: 0, latency: 108958285 ms]
- 3451298ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block ddd23622b0174f2c... #4000 @ 2018-06-21T03:49:52.500 signed by eosio [trxs: 0, lib: 3999, conf: 0, latency: 108458798 ms]
- 3451794ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block 37d4aa8148f4a429... #5000 @ 2018-06-21T03:58:12.500 signed by eosio [trxs: 0, lib: 4999, conf: 0, latency: 107959294 ms]
全节点重新启动以后, 可以观察到日志的内容与主节点有所不同:
首先它的区块的状态是 on_incoming_block, 而不是 produce_block
Received block 5f1a90b86a211c11... #1000, 后面是 #2000, #3000 可以看出它是一千一千在同步的, 这是依据 config.ini 的配置 "sync-fetch-span = 1000" 决定的. 在追上主节点出块以后, 全节点就开始正常逐个同步了.
全节点服务
全节点只同步区块, 不生成区块, 它拥有完整的区块数据, 因此可以通过全节点暴露的接口对链上数据进行查询,
- http:// 全节点 IP:8888/v1/chain/get_info
- {
- "server_version": "79651199",
- "chain_id": "cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f",
- "head_block_num": 3381,
- "last_irreversible_block_num": 3380,
- "last_irreversible_block_id": "00000d349eb386e22de1b2bdde422377d49b3d3e997af25de1124ff41bad8eb8",
- "head_block_id": "00000d3500aec9d772fa3c0801b79366e76dc5e4426a574e6b14a56e220b865e",
- "head_block_time": "2018-06-25T08:06:55.000",
- "head_block_producer": "eosio",
- "virtual_block_cpu_limit": 5869808,
- "virtual_block_net_limit": 30835535,
- "block_cpu_limit": 199900,
- "block_net_limit": 1048576
- }
全节点功能模拟测试
现在有一台出块节点别名 239, 一台全节点别名 182.
此时全网只有一个 eosio 账户, 我们要使用它创建一个新的账户, 我们期望出块节点维护自己的秘钥, 所以在 239 上用钱包导入 eosio 的私钥(虽然 eosio 的秘钥是公开的, 这里只是模拟)
182 上导入一个新生成的私钥 A, 然后在 239 上创建账户 jack,creator 是 eosio, 公钥是 A 的公钥.
**@jack**
182(全节点) | 239(出块节点) |
---|---|
私钥 A | eosio |
这样一来, 182 模拟是小白客户端, 239 是业务带头人, 小白的秘钥完全是由自己创建自己保存, 而小白的账户是业务带头人来创建的, 小白只需要提供自己的公钥即可. 这样一来, 小白很有安全感, 因为账户完全是自己的, 自己做任何事情都需要本地私钥签名, 不会被冒名. 而业务带头人也会很开心, 因为他仍旧可以经营自己的社区, 知道谁是通过自己创建的, 是自己的用户, 但也仅此而已, 业务带头人并不能对小白有任何多余的管辖.
交易确认的方案分析
下面从区块数据上面研究以上行为:
方法一
账户被创建是一个行为或者一个事务, 创建时会返回一个 transaction id, 我们手动去查一下这个 transaction,
cleos get transaction 3b0b14a72cc4a98dd9145989xxxxxxxx
返回的数据非常庞大, 其中包含了该 transaction 被记录的区块号, 我们通过区块号去查找区块信息,
cleos get block 203xx
返回的数据中, 可以看到很多字段信息, 其中有一个 confirmed 字段.
猜想 TODO: 这是区块确权的值, 只要超过出块节点总数的 2/3 + 1, 就可以被认定为不可逆区块. 但由于目前只有一个出块节点, 该字段为 0, 所以这个猜想仍需要测试.
方法二
在 239 上的事务的确认, 我们是否可以通过 182 上对事务结果的查询进行验证呢? 答案是肯定的. 以上我们在 239 上创建了账户 jack, 我们转到 182, 去查询
cleos get account jack
结果返回 jack 账户是正常有效的, 这就可以确定另一台机器 239 上的创建账户的事务被确认. 这种交易确认的方法要简单的多, 属于结果验证论, 意思也就是通过结果来判断是否完成一个动作. EOS 就有可逆区块大小的配置, 可逆区块就是未经确权的区块, 一经确权就会变为不可逆区块上链.
实际上, 在可逆区块确权的过程中, 以太坊是会全网广播的, 但 EOS 只会 BP 之间广播, 因此全节点接收到的一定是不可逆区块, 通过全节点来确认交易是个不错的方法.
方法二逆证
我们在 182 上使用刚刚创建的 jack 来创建一个新的 bob 账户,
- root@iZ2ze5wsiqz8cj0lqgf73xZ:~/182# cleos create account jack bob EOS5MLNon1NFXqnS4koDiKdVg2iTuu5ZS2NeZxve1RHTTifiCUfjg
- executed transaction: 0e95f8e9f3abdfbada4f1c10304f04f052a0b58364c3165da4551e9275ab86bb 200 bytes 298 us
- # eosio <= eosio::newaccount {"creator":"jack","name":"bob","owner":{"threshold":1,"keys":[{"key":"EOS5MLNon1NFXqnS4koDiKdVg2iTuu...
- warning: transaction executed locally, but may not be confirmed by the network yet
然后在 239 上查询,
cleos get account bob
结果是同样的. 也就是说,
方法二的交易确认重点不在于是否全节点来确认交易, 出块节点同样可以确认交易. 所以重点是是否可在其他机器上查到结果. 只不过是因为 EOS BP 之间广播可逆区块的特性, 所以去全节点上查询结果显得更稳妥.
启动序列
前面我们的主节点 + 全节点的测试采用的是按需研究, 下面我们整理一下真正去完整地启动一条链的步骤, 也叫 boot sequence, 在这过程中, 也会包括我结合源码位置 https://github.com/EOSIO/eos/blob/master/tutorials/bios-boot-tutorial/bios-boot-tutorial.py 脚本进行某些操作的具体实现的分析, 步骤如下:
Base 阶段
首先我定义为 base 阶段, 因为这些操作我们耳熟能详, 这里进行一个总结:
kill 所有 nodeos 以及 keosd 进程.
killall keosd nodeos || true
删除原钱包目录, 再创建一个钱包目录, 启动 keosd, 创建钱包, 导入 keys
配置 config.ini(之前提到多次了, 可以翻阅查看), 启动链进程
使用 eosio 创建系统级用户:'eosio.bpay','eosio.msig','eosio.names','eosio.ram','eosio.ramfee','eosio.saving','eosio.stake','eosio.token','eosio.vpay'(必须全部创建, 否则暂时不报错, 但后面会有很多坑)
使用对应的系统级用户部署 eosio.token 和 eosio.msig 合约
创建 token 并 issue, 注意: 创建者为 eosio 的 token 就是主币的概念, 默认的符号是 SYS, 是配置在源码中的, 如果我们需要修改主币符号需要更改源码重新部署.
eosio 账户部署 system 合约(部署 system 合约成功以后, 无法再使用 cleos create account 了), 然后开启多签名账户授权: cleos push action eosio setpriv '["eosio.msig", 1]' -p eosio, 这是上一篇文章中的坑.
Advance 阶段
启动序列运行到这里, 我们就已经拥有了一个独立节点, 它安装了 eosio.token, eosio.msig, eosio.system 三个合约, 目前它有 eosio 和 eosio.msig 两个特权账户(eosio.msig 账户是 eosio.msig 合约的 owner), 以及其他 eosio.* 系统级用户, 目前无普通用户. 下面的操作因为我们之前的研究中未涉及, 所以这里另起一小节进行描述.
一, 股权账户
股权账户 staked accounts, 就是我们理解的普通用户.
持股动作就是为 EOSIO 区块链系统中的一个账户分配 token, 从而获取一个实体 (真实世界的实体, 例如 ICO 中个人购买的某些东西) 的过程.
反之, 抛股就是回购账户的 token, 从而使其放弃某个实体的拥有权的过程.
持股和抛股是区块链整个生命周期中的重要行为模式. 但是在启动阶段时的持股初始化操作是特殊的, 账户通过他们的 token 持股, 然而直到 producer 选举出来之前, token 都是冻结状态, 也就是说账户的持股身份不可抛弃. 因此启动阶段的初始化持股操作的目的是:
分配 token 到账户, 准备使用, 然后是参与投票过程, producer 才能被选举出来, 整个区块链才算是 "活了".
Stake 流程
0.1 个 token(准确来讲, 不超过账户 token 总数的 10%)被用来持股内存 Ram 资源. 默认情况下, cleos 程序会持有 8KB 的内存在账户的创建上, 是由账户创建者来支付的. 在初始化阶段, 账户创建者都是 eosio.
0.45 个 token 抵押用来持有 CPU 资源, 额外的, 0.45 个 token 用来持有 network 资源.
共需要 9 个 token 作为流动 liquid token.
剩余的 token 均分为两部分, 用来持有 CPU 和 network 各一半.
举例说明: 账户 A 共拥有 100 个 SYS, 初始化持股操作为:
entity | staked |
---|---|
RAM | 0.1 SYS |
CPU | (0.45+45) SYS |
network | (0.45+45) SYS |
liquid | 9 SYS |
这个抵押 token 置换资源使用权的过程很清晰, 因为用户的 token 量是足够的, 可以完全按照上面的流程操作.
举例说明: 账户 B 共拥有 5 个 SYS, 初始化持股操作为:
entity | staked |
---|---|
RAM | 0.1 SYS |
CPU | (0.45+0) SYS |
network | (0.45+0) SYS |
liquid | 4 SYS |
这个抵押 token 置换资源使用权的过程与上面稍有不同, 因为用户的 token 量不足, 所以按照上面流程操作, 第三步流动 liquid token 的个数不足 9 个, 经历前两步以后, 账户 B 仅剩余 4 个 SYS, 免为其难地, liquid token 只能抵押 4 个 SYS. 而第四步, 由于没有剩余 token, 所以也不必执行了.
帕累托分配模型
根据以上对账户持股抵押的研究结果, 我们翻回来说 boot sequence base 阶段的 token SYS 的分配策略, 这个过程是夹在 SYS create 和 issue 的中间. 是使用帕累托分配模型 (Pareto distribution) 将 十亿个 SYS 分发出去.
帕累托分配模型: 是一个 80-20 规则, 即 80% 的 token 被 20% 的人持有. 具体实现过程在脚本 bios-boot-tutorial.py 中是通过 Python Numpy 库来生成帕累托分配的.
- def allocateFunds(b, e):
- dist = numpy.random.pareto(1.161, e - b).tolist() # 1.161 = 80/20 rule
- dist.sort()
- dist.reverse()
- factor = 1_000_000_000 / sum(dist)
- total = 0
- for i in range(b, e):
- funds = round(factor * dist[i - b] * 10000)
- if i>= firstProducer and i <firstProducer + numProducers:
- funds = max(funds, round(args.min_producer_funds * 10000))
- total += funds
- accounts[i]['funds'] = funds
- return total
通过对源码的分析, 可以知道 accounts 是 accounts.json 数据的集合, 包含字段 name,pub 以及 ppvt, 分别代表账户名称, 公钥和私钥的属性. 然而, allocateFunds 函数要做的事情是为 accounts 集合的每一个对象增加一个字段'funds', 这个字段的值是通过帕累托分配模型计算出来的, 可以使众多的普通账户的 fund 值呈现 80-20 规则. 而目前 funds 的值并未真正是账户所拥有的 token, 而是相当于一个计划! 后面会有使用到的地方, 这里系个扣 b1.
感兴趣的同学可以通过 Python numpy.random.pareto 函数的文档 https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.pareto.html#numpy.random.pareto 来研究它具体的思想以及实现方法.
创建股权账户
下面使用 system newaccount 创建账户, 并抵押资产.
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/239# cleos system newaccount eosio --transfer accountnum11 "EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb" --stake-net "100000.0000 SYS" --stake-cpu "100000.0000 SYS" --buy-ram="0.1 SYS"
- 1601937ms thread-0 main.cpp:429 create_action ] result: {"binargs":"0000000000ea30551082d4334f4d1132e8030000000000000453595300000000"} arg: {"code":"eosio","action":"buyram","args":{"payer":"eosio","receiver":"accountnum11","quant":"0.1000 SYS"}}
- 1601938ms thread-0 main.cpp:429 create_action ] result: {"binargs":"0000000000ea30551082d4334f4d113200ca9a3b00000000045359530000000000ca9a3b00000000045359530000000001"} arg: {"code":"eosio","action":"delegatebw","args":{"from":"eosio","receiver":"accountnum11","stake_net_quantity":"100000.0000 SYS","stake_cpu_quantity":"100000.0000 SYS","transfer":true}}
- executed transaction: 24a805a6a582a35ddd594ae25b7cf4a506244201d3fbcb4cfb4d079bf582764d 344 bytes 6072 us
- # eosio <= eosio::newaccount {"creator":"eosio","name":"accountnum11","owner":{"threshold":1,"keys":[{"key":"EOS8aCaHAARJvWqD7Xsb...
- # eosio <= eosio::buyram {"payer":"eosio","receiver":"accountnum11","quant":"0.1000 SYS"}
- # eosio.token <= eosio.token::transfer {"from":"eosio","to":"eosio.ram","quantity":"0.0995 SYS","memo":"buy ram"}
- # eosio <= eosio.token::transfer {"from":"eosio","to":"eosio.ram","quantity":"0.0995 SYS","memo":"buy ram"}
- # eosio.ram <= eosio.token::transfer {"from":"eosio","to":"eosio.ram","quantity":"0.0995 SYS","memo":"buy ram"}
- # eosio.token <= eosio.token::transfer {"from":"eosio","to":"eosio.ramfee","quantity":"0.0005 SYS","memo":"ram fee"}
- # eosio <= eosio.token::transfer {"from":"eosio","to":"eosio.ramfee","quantity":"0.0005 SYS","memo":"ram fee"}
- # eosio.ramfee <= eosio.token::transfer {"from":"eosio","to":"eosio.ramfee","quantity":"0.0005 SYS","memo":"ram fee"}
- # eosio <= eosio::delegatebw {"from":"eosio","receiver":"accountnum11","stake_net_quantity":"100000.0000 SYS","stake_cpu_quantity...
- # eosio.token <= eosio.token::transfer {"from":"eosio","to":"eosio.stake","quantity":"200000.0000 SYS","memo":"stake bandwidth"}
- # eosio <= eosio.token::transfer {"from":"eosio","to":"eosio.stake","quantity":"200000.0000 SYS","memo":"stake bandwidth"}
- # eosio.stake <= eosio.token::transfer {"from":"eosio","to":"eosio.stake","quantity":"200000.0000 SYS","memo":"stake bandwidth"}
- warning: transaction executed locally, but may not be confirmed by the network yet
注意这里 --stake-net, --stake-cpu, --buy-ram 的值都是手动随意填的, 并不是自动算出来的.
关于这个问题, 我们就可以解扣了, 扣 b1 提到的填充组装到 accounts 集合的字段'funds', 就是用来计算这些参数的值的, 具体计算方式, 可以通过脚本源码来看:
- def createStakedAccounts(b, e):
- ramFunds = round(args.ram_funds * 10000) # 通过参数 ram_funds 设置用于购买内存的资金
- configuredMinStake = round(args.min_stake * 10000) # 通过参数 min_stake 设置最小抵押值
- maxUnstaked = round(args.max_unstaked * 10000) # 最大非抵押值
- for i in range(b, e):
- a = accounts[i]
- funds = a['funds'] # 获取'funds'值
- print('#' * 80)
- print('# %d/%d %s %s' % (i, e, a['name'], intToCurrency(funds)))
- print('#' * 80)
- if funds < ramFunds:
- print('skipping %s: not enough funds to cover ram' % a['name'])
- continue
- minStake = min(funds - ramFunds, configuredMinStake) # 最小抵押值
- unstaked = min(funds - ramFunds - minStake, maxUnstaked) # 非抵押值
- stake = funds - ramFunds - unstaked # 剩余可分配抵押值总数
- stakeNet = round(stake / 2) # net 和 cpu 均分, 各抵押一半.
- stakeCpu = stake - stakeNet
- print('%s: total funds=%s, ram=%s, net=%s, cpu=%s, unstaked=%s' % (a['name'], intToCurrency(a['funds']), intToCurrency(ramFunds), intToCurrency(stakeNet), intToCurrency(stakeCpu), intToCurrency(unstaked)))
- assert(funds == ramFunds + stakeNet + stakeCpu + unstaked)
- retry(args.cleos + 'system newaccount --transfer eosio %s %s --stake-net"%s"--stake-cpu"%s"--buy-ram"%s" ' %
- (a['name'], a['pub'], intToCurrency(stakeNet), intToCurrency(stakeCpu), intToCurrency(ramFunds)))
- if unstaked: # 用完资源以后, 再还回账户.
- retry(args.cleos + 'transfer eosio %s"%s"'% (a['name'], intToCurrency(unstaked)))
三个变量 stakeNet,stakeCpu,ramFunds 就是我们用来抵押资源的值, 这个策略与前面提到的 "Stake 流程" 有些不同, 我们通过脚本参数 --ram-funds 指定了内存购买数, 默认值是上面提到的 0.1 SYS, 另外还有最小抵押值和最大非抵押值等, 所以这个流程更加复杂, 具备生产可行性.
二, 注册区块生产者的候选人
我们可以指定某个或某些个股权账户作为区块生产者. 这个过程首先要先将股权账户注册为一个区块生产者候选人, 通过以下命令执行:
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/239# cleos system regproducer accountnum11 EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb https://accountnum11.com/EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb
- 400025ms thread-0 main.cpp:429 create_action ] result: {"binargs":"1082d4334f4d11320003e5419cfdd7d6d511bc2c2f7f88c0e93432cf0ff39718fe99491e18e2069dd2674e68747470733a2f2f6163636f756e746e756d31312e636f6d2f454f5338614361484141524a7657714437587362714b323563346168444b543454776d716a76534346624433626f66384c313646620000"} arg: {"code":"eosio","action":"regproducer","args":{"producer":"accountnum11","producer_key":"EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb","url":"https://accountnum11.com/EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb","location":0}}
- executed transaction: 8181fe1cd180afeae280b8f8f2ffc735aa63cb10a8c0cf12a86198e179203228 216 bytes 1481 us
- # eosio <= eosio::regproducer {"producer":"accountnum11","producer_key":"EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb","u...
- warning: transaction executed locally, but may not be confirmed by the network yet
参数介绍:
指定股权账户名称
指定该账户未来作为区块生产者的公钥(这个可以与账户本身的公钥不同)
url, 一般由生产者账户名加公钥组成. 用来展示区块生产者的信息的网址, 这个网址是我们自己维护的, 相当于我们的官网, 主要介绍一些区块生产者的名称, 愿景, 意义等, 让其他节点更加了解自己, 从而为自己投票.
候选人列表展示
下面我们再用相同的流程多注册几个候选人, 然后展示候选人列表:
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/239# cleos system listproducers
- Producer Producer key Url Scaled votes
- a EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb https://www.baidu.com 0.0000
- accountnum1 EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb https://www.google.com 0.0000
- accountnum11 EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4TwmqjvSCFbD3bof8L16Fb https://accountnum11.com/EOS8aCaHAARJvWqD7XsbqK25c4ahDKT4Tw 0.0000
两个问题:
我注册的三个候选人的生产公钥都是相同的, 好像也没有失败, 看看后面有没有坑吧.
使用 system newaccount 时并未有账户长度的限制, 没有让我去 bid 名字, 这个后面再观察研究.
三, 候选人启动链
使用一个候选人账户开启一条链, 配置 config.ini, 手动去写比较复杂. 我们直接使用脚本执行, 执行前先安装 numpy
sudo apt-get install python3-numpy
然后修改脚本中的一些数字为有效值, 开始执行(我们约束只要 3 个 bp,8 个普通账户),
./bios-boot-tutorial.py -a --user-limit 8 --producer-limit 3
前面提到的流程全都跑完了, 跑到当前位置停下, 可以看到, 先来查一下候选人列表:
- bios-boot-tutorial.py: ../../build/programs/cleos/cleos --wallet-url http://localhost:6666 --url http://localhost:8000 system listproducers
- Producer Producer key Url Scaled votes
- producer111a EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz https://producer111a.com/EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8 0.0000
- producer111b EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC https://producer111b.com/EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuE 0.0000
- producer111c EOS5n442Qz4yVc4LbdPCDnxNSseAiUCrNjRxAfPhUvM8tWS5svid6 https://producer111c.com/EOS5n442Qz4yVc4LbdPCDnxNSseAiUCrNj 0.0000
然后分别开启这三个账户的链,
- bios-boot-tutorial.py: rm -rf ./nodes/01-producer111a/
- bios-boot-tutorial.py: mkdir -p ./nodes/01-producer111a/
- bios-boot-tutorial.py: ../../build/programs/nodeos/nodeos --max-irreversible-block-age 9999999 --contracts-console --genesis-json /root/lwb-work/eos/tutorials/bios-boot-tutorial/genesis.json --blocks-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/01-producer111a/blocks --config-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/01-producer111a --data-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/01-producer111a --chain-state-db-size-mb 1024 --http-server-address 127.0.0.1:8001 --p2p-listen-endpoint 127.0.0.1:9001 --max-clients 13 --p2p-max-nodes-per-host 13 --enable-stale-production --producer-name producer111a --private-key '["EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz","5KLGj1HGRWbk5xNmoKfrcrQHXvcVJBPdAckoiJgFftXSJjLPp7b"]' --plugin eosio::http_plugin --plugin eosio::chain_api_plugin --plugin eosio::producer_plugin --p2p-peer-address localhost:9000 2>>./nodes/01-producer111a/stderr
- bios-boot-tutorial.py: rm -rf ./nodes/02-producer111b/
- bios-boot-tutorial.py: mkdir -p ./nodes/02-producer111b/
- bios-boot-tutorial.py: ../../build/programs/nodeos/nodeos --max-irreversible-block-age 9999999 --contracts-console --genesis-json /root/lwb-work/eos/tutorials/bios-boot-tutorial/genesis.json --blocks-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/02-producer111b/blocks --config-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/02-producer111b --data-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/02-producer111b --chain-state-db-size-mb 1024 --http-server-address 127.0.0.1:8002 --p2p-listen-endpoint 127.0.0.1:9002 --max-clients 13 --p2p-max-nodes-per-host 13 --enable-stale-production --producer-name producer111b --private-key '["EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC","5K6qk1KaCYYWX86UhAfUsbMwhGPUqrqHrZEQDjs9ekP5j6LgHUu"]' --plugin eosio::http_plugin --plugin eosio::chain_api_plugin --plugin eosio::producer_plugin --p2p-peer-address localhost:9000 --p2p-peer-address localhost:9001 2>>./nodes/02-producer111b/stderr
- bios-boot-tutorial.py: rm -rf ./nodes/03-producer111c/
- bios-boot-tutorial.py: mkdir -p ./nodes/03-producer111c/
- bios-boot-tutorial.py: ../../build/programs/nodeos/nodeos --max-irreversible-block-age 9999999 --contracts-console --genesis-json /root/lwb-work/eos/tutorials/bios-boot-tutorial/genesis.json --blocks-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/03-producer111c/blocks --config-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/03-producer111c --data-dir /root/lwb-work/eos/tutorials/bios-boot-tutorial/nodes/03-producer111c --chain-state-db-size-mb 1024 --http-server-address 127.0.0.1:8003 --p2p-listen-endpoint 127.0.0.1:9003 --max-clients 13 --p2p-max-nodes-per-host 13 --enable-stale-production --producer-name producer111c --private-key '["EOS5n442Qz4yVc4LbdPCDnxNSseAiUCrNjRxAfPhUvM8tWS5svid6","5JCStvbRgUZ6hjyfUiUaxt5iU3HP6zC1kwx3W7SweaEGvs4EPfQ"]' --plugin eosio::http_plugin --plugin eosio::chain_api_plugin --plugin eosio::producer_plugin --p2p-peer-address localhost:9000 --p2p-peer-address localhost:9001 --p2p-peer-address localhost:9002 2>>./nodes/03-producer111c/stderr
启动候选人链时有几点注意:
要保证路径下包含 genesis.json 文件, 用于描述启动初始化链属性信息.
命令中组装的参数作用域仅对当下生效, 与在 / nodes/01-producer111a 目录下的 config.ini 文件中的配置不同.
这三个候选人分别占用了 http 的端口 8001,8002,8003,p2p 端口 9001,9002,9003, 分别监听其他 p2p 地址.
三个候选人的出块账户均设为自己, 同时设置了对应的密钥对.
每个链的日志, 包括源节点和三个候选人的都时刻同步在各自节点目录下的文件 stderr 中.
四, 为候选人投票
任意一个股权用户均可以投票,
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 system voteproducer prods useraaaaaaab producer111a
- 2190240ms thread-0 main.cpp:429 create_action ] result: {"binargs":"708c31c6187315d600000000000000000160420857219de8ad"} arg: {"code":"eosio","action":"voteproducer","args":{"voter":"useraaaaaaab","proxy":"","producers":["producer111a"]}}
- executed transaction: 729381cc691690061d9724b3553e1eca834317d9b4ebf8067f5093a97345d056 120 bytes 2242 us
- # eosio <= eosio::voteproducer {"voter":"useraaaaaaab","proxy":"","producers":["producer111a"]}
- warning: transaction executed locally, but may not be confirmed by the network yet
再来查看候选人列表:
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 system listproducers
- Producer Producer key Url Scaled votes
- producer111a EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz https://producer111a.com/EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8 1.0000
- producer111b EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC https://producer111b.com/EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuE 0.0000
- producer111c EOS5n442Qz4yVc4LbdPCDnxNSseAiUCrNjRxAfPhUvM8tWS5svid6 https://producer111c.com/EOS5n442Qz4yVc4LbdPCDnxNSseAiUCrNj 0.0000
可以看到候选人 producer111a 的 Scaled 项变为 1. 那么什么时候算投票结束呢?
直到有效投票数超过总可投票的 15%, 排在前面的候选者就开始出块.
候选人竞选成功, 开始出块
注意一个账户只能头一次票给一个候选人, 多次投票可以执行成功, 但票数仅第一次有效.
给 producer111a 投票以后, 我一直在监控几个日志平台, 发现不知什么时候, 00-eosio 节点已经不出块了, 开始接受块, 而 01-producer111a 节点显示开始出块, 其他候选人仍旧接受块. 这时候我更换策略, 开始用股权账户为 producer111b 投票, 投完以后, 没过多久, 让我捕捉到 01-producer111a 节点的日志和 producer111b 节点的日志变化了.
01-producer111a 节点的日志:
- 2887500ms thread-0 producer_plugin.cpp:1073 produce_block ] Produced block 00000a7bd2326f26... #2683 @ 2018-06-27T11:48:07.500 signed by producer111a [trxs: 0, lib: 2682, confirmed: 0]
- 2887504ms thread-0 controller.cpp:752 start_block ] promoting proposed schedule (set in block 2683) to pending; current block: 2684 lib: 2683 schedule: {"version":2,"producers":[{"producer_name":"producer111a","block_signing_key":"EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz"},{"producer_name":"producer111b","block_signing_key":"EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC"}]}
- 2888000ms thread-0 producer_plugin.cpp:1073 produce_block ] Produced block 00000a7c22b963b7... #2684 @ 2018-06-27T11:48:08.000 signed by producer111a [trxs: 0, lib: 2683, confirmed: 0]
- 2888500ms thread-0 producer_plugin.cpp:1073 produce_block ] Produced block 00000a7d96adbfc0... #2685 @ 2018-06-27T11:48:08.500 signed by producer111a [trxs: 0, lib: 2684, confirmed: 0]
- 2889003ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block 6189af49442e9971... #2686 @ 2018-06-27T11:48:09.000 signed by producer111b [trxs: 0, lib: 2684, conf: 0, latency: 3 ms]
02-producer111b 节点的日志:
- 2887510ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block d2326f262fefb570... #2683 @ 2018-06-27T11:48:07.500 signed by producer111a [trxs: 0, lib: 2682, conf: 0, latency: 10 ms]
- 2887510ms thread-0 controller.cpp:752 start_block ] promoting proposed schedule (set in block 2683) to pending; current block: 2684 lib: 2683 schedule: {"version":2,"producers":[{"producer_name":"producer111a","block_signing_key":"EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz"},{"producer_name":"producer111b","block_signing_key":"EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC"}]}
- 2888002ms thread-0 controller.cpp:752 start_block ] promoting proposed schedule (set in block 2683) to pending; current block: 2684 lib: 2683 schedule: {"version":2,"producers":[{"producer_name":"producer111a","block_signing_key":"EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz"},{"producer_name":"producer111b","block_signing_key":"EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC"}]}
- 2888004ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block 22b963b7ef2954ae... #2684 @ 2018-06-27T11:48:08.000 signed by producer111a [trxs: 0, lib: 2683, conf: 0, latency: 4 ms]
- 2888503ms thread-0 producer_plugin.cpp:290 on_incoming_block ] Received block 96adbfc0c04b06eb... #2685 @ 2018-06-27T11:48:08.500 signed by producer111a [trxs: 0, lib: 2684, conf: 0, latency: 3 ms]
- 2889000ms thread-0 producer_plugin.cpp:1073 produce_block ] Produced block 00000a7e6189af49... #2686 @ 2018-06-27T11:48:09.000 signed by producer111b [trxs: 0, lib: 2684, confirmed: 0]
可以看出,
01-producer111a 节点由 produce_block, 经历 start_block 以后, 改为 on_incoming_block.
02-producer111b 节点由 on_incoming_block, 经历 start_block 以后, 改为 produce_block.
那么这个 start_block 事件的内容在两个节点里报出来的都是相同的内容:
promoting proposed schedule (set in block 2683) to pending; current block: 2684 lib: 2683 schedule: {"version":2,"producers":[{"producer_name":"producer111a","block_signing_key":"EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz"},{"producer_name":"producer111b","block_signing_key":"EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC"}]}
意思就是 producer111b 晋升为出块节点. 接着我们再继续观察日志, 会发现:
producer111a 和 producer111b 是交替出块, producer111a 节点并没有因为 producer111b 的晋升而不再出块.
producer111c 没有人给他投票, 所以继续接收.
查看所有候选人状态
通过 table 来查询所有候选人 (包含出块者) 目前的状态,
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 get table eosio eosio producers
- {
- "rows": [{
- "owner": "producer111a",
- "total_votes": "140.00000000000000000",
- "producer_key": "EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz",
- "is_active": 1,
- "url": "https://producer111a.com/EOS8imf2TDq6FKtLZ8mvXPWcd6EF2rQwo8zKdLNzsbU9EiMSt9Lwz",
- "unpaid_blocks": 1124,
- "last_claim_time": "1530101102000000",
- "location": 0
- },{
- "owner": "producer111b",
- "total_votes": "3767537711703276032.00000000000000000",
- "producer_key": "EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC",
- "is_active": 1,
- "url": "https://producer111b.com/EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC",
- "unpaid_blocks": 2138,
- "last_claim_time": 0,
- "location": 0
- },{
- "owner": "producer111c",
- "total_votes": "0.00000000000000000",
- "producer_key": "EOS5n442Qz4yVc4LbdPCDnxNSseAiUCrNjRxAfPhUvM8tWS5svid6",
- "is_active": 1,
- "url": "https://producer111c.com/EOS5n442Qz4yVc4LbdPCDnxNSseAiUCrNjRxAfPhUvM8tWS5svid6",
- "unpaid_blocks": 0,
- "last_claim_time": 0,
- "location": 0
- }
- ],
- "more": false
- }
通过打印结果可以观察到三个候选节点的收到的投票数, 公钥, url 等属性, 其中 unpaid_blocks 属性是还未申领奖励的区块数(属于该节点出的块),last_claim_time 属性是上一次申领时间.
五, 区块生产者认领奖励
与比特币和以太坊相同的是, EOS 的出块者也有挖矿奖励, 只是比起前二者自动发放奖励, EOS 出块者需要自行申领奖励,
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 system claimrewards producer111a
- 301762ms thread-0 main.cpp:429 create_action ] result: {"binargs":"60420857219de8ad"} arg: {"code":"eosio","action":"claimrewards","args":{"owner":"producer111a"}}
- executed transaction: 4b7e9b1bec0f04f4d96aa4e61f9bc45516411cf6be3f82720e9c8cb6dfb7a162 104 bytes 6343 us
- # eosio <= eosio::claimrewards {"owner":"producer111a"}
- # eosio.token <= eosio.token::issue {"to":"eosio","quantity":"1855.4398 SYS","memo":"issue tokens for producer pay and savings"}
- # eosio.token <= eosio.token::transfer {"from":"eosio","to":"eosio.saving","quantity":"1484.3519 SYS","memo":"unallocated inflation"}
- # eosio <= eosio.token::transfer {"from":"eosio","to":"eosio.saving","quantity":"1484.3519 SYS","memo":"unallocated inflation"}
- # eosio.saving <= eosio.token::transfer {"from":"eosio","to":"eosio.saving","quantity":"1484.3519 SYS","memo":"unallocated inflation"}
- # eosio.token <= eosio.token::transfer {"from":"eosio","to":"eosio.bpay","quantity":"92.7719 SYS","memo":"fund per-block bucket"}
- # eosio <= eosio.token::transfer {"from":"eosio","to":"eosio.bpay","quantity":"92.7719 SYS","memo":"fund per-block bucket"}
- # eosio.bpay <= eosio.token::transfer {"from":"eosio","to":"eosio.bpay","quantity":"92.7719 SYS","memo":"fund per-block bucket"}
- # eosio.token <= eosio.token::transfer {"from":"eosio","to":"eosio.vpay","quantity":"278.3160 SYS","memo":"fund per-vote bucket"}
- # eosio <= eosio.token::transfer {"from":"eosio","to":"eosio.vpay","quantity":"278.3160 SYS","memo":"fund per-vote bucket"}
- # eosio.vpay <= eosio.token::transfer {"from":"eosio","to":"eosio.vpay","quantity":"278.3160 SYS","memo":"fund per-vote bucket"}
- # eosio.token <= eosio.token::transfer {"from":"eosio.bpay","to":"producer111a","quantity":"53.4400 SYS","memo":"producer block pay"}
- # eosio.bpay <= eosio.token::transfer {"from":"eosio.bpay","to":"producer111a","quantity":"53.4400 SYS","memo":"producer block pay"}
- # producer111a <= eosio.token::transfer {"from":"eosio.bpay","to":"producer111a","quantity":"53.4400 SYS","memo":"producer block pay"}
- warning: transaction executed locally, but may not be confirmed by the network yet
从打印结果可以看到申领奖励的执行路径, 这时候我们再来检查一下 producer111a 账户的余额,
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 get currency balance eosio.token producer111a
- 63.4400 SYS
刚刚发放的奖励 53.4400 SYS 已打入余额中, 而之前的 10 SYS 是哪里来的?
脚本参数 --max-unstaked, 默认值为 10, 在股权账户被创建的时候, 会读取这个参数的值, 根据这个值来计算抵押创建账户消耗的资源, 账户创建过程中, 资源抵押的 token 是由 eosio 支付的, 当账户创建完毕, 资源被释放(即 unstake), 则会将 10 SYS 从 eosio 转账到账户中去.
六, 代理投票
投票过程说实在有点麻烦, 因此有了代理投票的功能, 代理投票分为两步:
注册代理
我们通过命令将某个股权账户注册为一个代理, 可接受小白的授权.
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 system regproxy useraaaaaaab
- 2212425ms thread-0 main.cpp:429 create_action ] result: {"binargs":"708c31c6187315d601"} arg: {"code":"eosio","action":"regproxy","args":{"proxy":"useraaaaaaab","isproxy":true}}
- executed transaction: 4a8f2bad3a6f2e0d34d5ec1134e241f850e8a0c659cc65ce3cf4bedfaf28c97c 104 bytes 1216 us
- # eosio <= eosio::regproxy {"proxy":"useraaaaaaab","isproxy":1}
- warning: transaction executed locally, but may not be confirmed by the network yet
可以看到 useraaaaaaab 账户的 isproxy 项已置为 1, 成为代理.
代理授权
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 system voteproducer proxy useraaaaaaaa useraaaaaaab
- 2402472ms thread-0 main.cpp:429 create_action ] result: {"binargs":"608c31c6187315d6708c31c6187315d600"} arg: {"code":"eosio","action":"voteproducer","args":{"voter":"useraaaaaaaa","proxy":"useraaaaaaab","producers":[]}}
- executed transaction: c5501c7487d9ffcf6ce86b76f5c75d9fd68e22c84b376612d5266ea76199d37e 112 bytes 3062 us
- # eosio <= eosio::voteproducer {"voter":"useraaaaaaaa","proxy":"useraaaaaaab","producers":[]}
- # useraaaaaaab <= eosio::voteproducer {"voter":"useraaaaaaaa","proxy":"useraaaaaaab","producers":[]}
- warning: transaction executed locally, but may not be confirmed by the network yet
我们成功将账户 useraaaaaaaa 的投票权代理给了代理账户 useraaaaaaab.
代理投票
由于每个账户给候选者只能投一次票, 我们可以通过这个特性来验证代理投票. 首先我们先通过 get table 查询三个候选者的票数, 然后使用 useraaaaaaab 账户为 producer111a 投票, 再次 get table 查询可以发现 producer111a 的票数升高了, 此时再使用 useraaaaaaaa 账户为 producer111a 进行投票, 操作成功, 但 get table 去查询发现 producer111a 的票数不变, 这说明 useraaaaaaaa 账户的票数已经通过代理账户 useraaaaaaab 成功代理投票.
七, resign eosio 以及 eosio.* 系统级账户
当我们已经选举出来称职的出块者以后, 出块者已经由原来的 eosio 变为众多出块者轮番出块, eosio 变为接收块, 随着启动时序接近尾声, eosio 的作用越来越小, 但它的权限仍是公开的密钥对, 这是一件很有风险的事, 所以综合考量, 这一步骤, 我们要改造 eosio 的权限.
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 push action eosio updateauth '{"account":"eosio","permission":"owner","parent":"","auth":{"threshold":1,"keys":[],"waits":[],"accounts":[{"weight":1,"permission":{"actor":"eosio.prods","permission":"active"}}]}}' -p eosio@owner
- executed transaction: f738e289214b31b43255cd562bf12ac811f2c7b4cc0acc0b887a8ec7db603679 144 bytes 869 us
- # eosio <= eosio::updateauth {"account":"eosio","permission":"owner","parent":"","auth":{"threshold":1,"keys":[],"accounts":[{"pe...
- warning: transaction executed locally, but may not be confirmed by the network yet
通过为 eosio 账户更新 permission, 这里没有使用'cleos set account permission ...', 是因为部署 system 合约以后, 绝大部分的直接操作都失效了, 所以转而使用 system 合约的 push action eosio updateauth 来更新 eosio 的 permission 为:
- {
- "account": "eosio",
- "permission": "owner",
- "parent": "",
- "auth": {
- "threshold": 1,
- "keys": [],
- "waits": [],
- "accounts": [
- {
- "weight": 1,
- "permission": {
- "actor": "eosio.prods",
- "permission": "active"
- }
- }
- ]
- }
- }
最终, 我们检查 eosio 的 owner 权限为:
- privileged: true
- permissions:
- owner 1: 1 eosio.prods@active,
然后对 eosio.prods 账户产生了好奇, 那么我们就继续查看这个账户:
- root@iZ2ze5wsiqz8cj0lqgf73tZ:~/lwb-work/eos/tutorials/bios-boot-tutorial/nodes# cleos --wallet-url http://localhost:6666 --url http://localhost:8000 get account eosio.prods
- permissions:
- owner 1:
- active 2: 1 producer111a@active, 1 producer111b@active,
- prod.major 2: 1 producer111a@active, 1 producer111b@active,
- prod.minor 1: 1 producer111a@active, 1 producer111b@active,
- memory:
- quota: unlimited used: 2.594 KiB
- net bandwidth:
- used: unlimited
- available: unlimited
- limit: unlimited
- cpu bandwidth:
- used: unlimited
- available: unlimited
- limit: unlimited
可以发现这个账户 eosio.prods 已经完全被出块者 (注意不是候选者, 而是成功出块的节点账户) 占据, 并且有了 prod.major 和 prod.minor 两个自定义权限, 这里不展开了.
这样一来, 我们已经干掉了 eosio 的 owner 权限, 同理, 干掉 eosio 的 active 权限.
resign 系统账户
eosio 账户的权限被 resign 以后, 正像使用 eosio.prods 账户来 resign eosio 一样, 我们使用 eosio 来 resign 所有的系统账户 eosio.*.
resign 目标: eosio 账户以及 eosio.* 账户的权限被最终下发到区块生产者账户.
启动脚本
到目前为止, 我们已完成了所有启动阶段的操作的研究, 可以看出这个启动流程有些麻烦, 我们第一次去手动操作是为了理解每一步的具体含义和内容, 而如果之后的生产阶段仍旧采取手动配置的方式无疑效率太低, 因此上面反复提及的源码自带的脚本 bios-boot-tutorial.py 很好的解决了这个问题. 前面的分析已基本覆盖脚本的内容, 这里介绍上面未涵盖的三个步骤, 这三个步骤是在以上内容的最后来执行的:
使用多签名来替换 eosio 对 system 合约的控制, 这个不难理解, system 合约相当于 "系统设置", 这个权限层级很高, 我们已经 resign 了 eosio, 以后 system 合约相关的操作需要通过提 propose, 然后经由参与 resign eosio 的权限账户的审批来最终执行成功. 这个过程不介绍了, 可以参考EOS 商业落地利器: 多签名操作与应用来自己实践.
通过随机转账的压测, 每次转账 0.0001 SYS, 可以通过启动参数 --num-senders 来控制参与压测的账户数量, 从而控制压测的最大范围. 通过压测, 可以看出 EOS 区块链在 tps 上的表现等指标.
追踪日志, 实际上这部分工作我在前面分析候选人出块选举部分时, 已经手动做了: 在启动链的时候, 有一堆参数, 其中最末尾会将输出重定向到节点目录的一个文件位置, 我们可以通过命令来时刻追踪这个日志文件.
总结
本文首先分为两大部分: 第一部分介绍了手动启动一个源节点, 全节点以及如何将这两个节点组网, 并实现一些业务逻辑的设计, 例如交易确认. 第二部分, 我们完整详细地分析了启动一个节点的所有必须动作序列(我们前面研究多签名也好, 上面手动组网也好, 碰到太多由于初始化 节点时缺乏必要步骤所导致的问题, 在这种情况下, 我决定系统地研究 eos 的启动序列). 首先重点介绍了股权账户的概念, 其中在分配股权账户的策略上, 我们也引入了帕累托分配模型; 接着就是非常重要的出块者竞选的部分, 包括如何注册, 启动出块节点, 投票, 代理投票, 成功出块, 申领奖励一系列操作; 最后, 我们分析了 eos 的风险模型, 将 eosio 账户以及其他 eosio.* 系统账户进行 resign, 也引出了 resign 之后 system 合约的多签名方式调用, 对于 eos 的性能表现, 也给出了压测方案, 日志分析办法.
参考资料
bios-boot-sequence.py 脚本
eos 官方文档
本文基于 EOS v1.0.7
更多文章请转到醒者呆的博客园.
来源: https://www.cnblogs.com/Evsward/p/eos-boot.html