EOS多节点组网:商业场景分析以及节点启动时序
区块链公链都是基于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,在这过程中,也会包括我结合源码位置 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函数的文档来研究它具体的思想以及实现方法。
创建股权账户
下面使用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
更多文章请转到醒者呆的博客园。
EOS多节点组网:商业场景分析以及节点启动时序的更多相关文章
- Redis集群案例与场景分析
1.背景 Redis的出现确实大大地提高系统大并发能力支撑的可能性,转眼间Redis的最新版本已经是3.X版本了,但我们的系统依然继续跑着2.8,并很好地支撑着我们当前每天5亿访问量的应用系统.想当年 ...
- 数据结构之链表C语言实现以及使用场景分析
牢骚:本篇博客两个星期前已经存为草稿,鉴于发生一些糟糕的事情,今天才基本完成.本人6月份应届毕业生一枚,毕业后当天来到帝都,之后也非常顺利,面试了俩家公司都成功了.一家做C++方面电商ERP,一家做w ...
- Java 常用List集合使用场景分析
Java 常用List集合使用场景分析 过年前的最后一篇,本章通过介绍ArrayList,LinkedList,Vector,CopyOnWriteArrayList 底层实现原理和四个集合的区别.让 ...
- MySQL死锁系列-常见加锁场景分析
在上一篇文章<锁的类型以及加锁原理>主要总结了 MySQL 锁的类型和模式以及基本的加锁原理,今天我们就从原理走向实战,分析常见 SQL 语句的加锁场景.了解了这几种场景,相信小伙伴们也能 ...
- Unity遮罩之Mask、RectMask2D与Sprite Mask适用场景分析
遮罩,顾名思义是一种可以掩盖其它元素的控件.常用于修改其它元素的外观,或限制元素的形状.比如ScrollView或者圆头像效果都有用到遮罩功能.本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实 ...
- 4种Kafka网络中断和网络分区场景分析
摘要:本文主要带来4种Kafka网络中断和网络分区场景分析. 本文分享自华为云社区<Kafka网络中断和网络分区场景分析>,作者: 中间件小哥. 以Kafka 2.7.1版本为例,依赖zk ...
- TYPESDK手游聚合SDK服务端设计思路与架构之一:应用场景分析
TYPESDK 服务端设计思路与架构之一:应用场景分析 作为一个渠道SDK统一接入框架,TYPESDK从一开始,所面对的需求场景就是多款游戏,通过一个统一的SDK服务端,能够同时接入几十个甚至几百个各 ...
- Oracle dbms_lock.sleep()存储过程使用技巧-场景-分析-实例
<Oracle dbms_lock.sleep()存储过程使用技巧>-场景-分析-实例 摘要:今天是2014年3月10日,北京,雾霾,下午组织相关部门开会.会议的结尾一名开发工程师找到了我 ...
- 理解 python metaclass使用技巧与应用场景分析
理解python metaclass使用技巧与应用场景分析 参考: decorator与metaclass:http://jfine-python-classes.readthedocs. ...
随机推荐
- Django跨域(前端跨域)
前情回顾 在说今天的问题之前先来回顾一下有关Ajax的相关内容 Ajax的优缺点 AJAX使用Javascript技术向服务器发送异步请求: AJAX无须刷新整个页面: 因为服务器响应内容不再是整个页 ...
- 阿里云ECS装LAMP环境
学生计划9.9买个一个ECS,要做PHP开发,所以搭建一个Lamp的环境 1.使用镜像,附件 sh-1.5.5附于文后. 2.一键安装 2.1 输入命令:chmod –R 777 sh-1.5.5 c ...
- dpkg: error: dpkg status database is locked by another process
First run: lsof /var/lib/dpkg/lock Then make sure that process isn't running: ps cax | grep PID If i ...
- sendmail发送邮件
#echo 'hello!' | mail -s "hello test" 119733@qq.com
- Python:每日一题003
题目: 一个整数,它加上100和加上268后都是一个完全平方数,请问该数是多少? 程序分析: 在10000以内判断,将该数加上100后再开方,加上268后再开方,如果开方后的结果满足如下条件,即是结果 ...
- caoni大业 spring boot 跳坑记
IDEA环境 win10 跑得刚刚,到xp系统就戈壁 报错 Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.g ...
- 64位版本为什么叫amd64,而不是intel64
64位版本为什么叫amd64,而不是intel64? 首先了解下常见的几个架构: X86是一个指令集,是刚有个人电脑时候的什么8086,286,386的那个兼容的指令集. “x86-64”,有时会 ...
- MVC 移动识别
ASP.NET MVC移动端识别 上面我们已经说了 响应式布局,但那是客户端的,针对于同一个视图页面的.不过 同一个视图页面 通过响应式布局 也是有缺点的 会导致页面 样式十分庞大 页面加载效率降低, ...
- python基本数据类型之字符串(五)
python基本数据类型之字符串(五) 遍历与查找 python中的字符串属于可迭代对象,通过一些方法可以遍历字符串中的每一个字符.而查找的方法主要有两个:find与index. 1.字符串的遍历 字 ...
- python之路(六)-函数相关
在没有学习函数之前我们的程序是面向过程的,不停的判断,不停的循环,同样的代码重复出现在我们的代码里.函数可以更好的提高我们的 代码质量,避免同样的代码重复出现,而只需要在用的时候调用函数即可执行.此为 ...