区块链公链都是基于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 流程

  1. 0.1个token(准确来讲,不超过账户token总数的10%)被用来持股内存Ram资源。默认情况下,cleos程序会持有8KB的内存在账户的创建上,是由账户创建者来支付的。在初始化阶段,账户创建者都是eosio。
  2. 0.45个token抵押用来持有CPU资源,额外的,0.45个token用来持有network资源。
  3. 共需要9个token作为流动liquid token。
  4. 剩余的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

启动候选人链时有几点注意:

  1. 要保证路径下包含genesis.json文件,用于描述启动初始化链属性信息。
  2. 命令中组装的参数作用域仅对当下生效,与在/nodes/01-producer111a目录下的config.ini文件中的配置不同。
  3. 这三个候选人分别占用了http的端口8001,8002,8003,p2p端口9001,9002,9003,分别监听其他p2p地址。
  4. 三个候选人的出块账户均设为自己,同时设置了对应的密钥对。
  5. 每个链的日志,包括源节点和三个候选人的都时刻同步在各自节点目录下的文件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多节点组网:商业场景分析以及节点启动时序的更多相关文章

  1. Redis集群案例与场景分析

    1.背景 Redis的出现确实大大地提高系统大并发能力支撑的可能性,转眼间Redis的最新版本已经是3.X版本了,但我们的系统依然继续跑着2.8,并很好地支撑着我们当前每天5亿访问量的应用系统.想当年 ...

  2. 数据结构之链表C语言实现以及使用场景分析

    牢骚:本篇博客两个星期前已经存为草稿,鉴于发生一些糟糕的事情,今天才基本完成.本人6月份应届毕业生一枚,毕业后当天来到帝都,之后也非常顺利,面试了俩家公司都成功了.一家做C++方面电商ERP,一家做w ...

  3. Java 常用List集合使用场景分析

    Java 常用List集合使用场景分析 过年前的最后一篇,本章通过介绍ArrayList,LinkedList,Vector,CopyOnWriteArrayList 底层实现原理和四个集合的区别.让 ...

  4. MySQL死锁系列-常见加锁场景分析

    在上一篇文章<锁的类型以及加锁原理>主要总结了 MySQL 锁的类型和模式以及基本的加锁原理,今天我们就从原理走向实战,分析常见 SQL 语句的加锁场景.了解了这几种场景,相信小伙伴们也能 ...

  5. Unity遮罩之Mask、RectMask2D与Sprite Mask适用场景分析

    遮罩,顾名思义是一种可以掩盖其它元素的控件.常用于修改其它元素的外观,或限制元素的形状.比如ScrollView或者圆头像效果都有用到遮罩功能.本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实 ...

  6. 4种Kafka网络中断和网络分区场景分析

    摘要:本文主要带来4种Kafka网络中断和网络分区场景分析. 本文分享自华为云社区<Kafka网络中断和网络分区场景分析>,作者: 中间件小哥. 以Kafka 2.7.1版本为例,依赖zk ...

  7. TYPESDK手游聚合SDK服务端设计思路与架构之一:应用场景分析

    TYPESDK 服务端设计思路与架构之一:应用场景分析 作为一个渠道SDK统一接入框架,TYPESDK从一开始,所面对的需求场景就是多款游戏,通过一个统一的SDK服务端,能够同时接入几十个甚至几百个各 ...

  8. Oracle dbms_lock.sleep()存储过程使用技巧-场景-分析-实例

    <Oracle dbms_lock.sleep()存储过程使用技巧>-场景-分析-实例 摘要:今天是2014年3月10日,北京,雾霾,下午组织相关部门开会.会议的结尾一名开发工程师找到了我 ...

  9. 理解 python metaclass使用技巧与应用场景分析

    理解python metaclass使用技巧与应用场景分析       参考: decorator与metaclass:http://jfine-python-classes.readthedocs. ...

随机推荐

  1. PHP开发——超全局数组变量

    概述 l  JS中的变量分两类:局部变量.全局变量. l  PHP中的变量分三类:局部变量.全局变量.超全局变量. l  局部变量:在函数内部声明的变量,就是局部变量.函数执行完毕,局部变量就消失了. ...

  2. 5. Redis持久化

    5. Redis持久化5.1 RDB5.1.1 触发机制5.1.2 流程说明5.1.3 RDB文件的处理5.1.4 RDB的优缺点5.2 AOF5.2.1 使用AOF5.2.2 命令写入5.2.3 文 ...

  3. JMD Handy Baby 2 to Decode & Adding New BMW 525 ID46 Key

    Here OBD2TOOL share the guide on how to use JMD Handy Baby II to decode and add new keys for BMW 525 ...

  4. 利用maven将项目打包成一个可以运行的独立jar包

    目标:希望把Java项目打包成一个完整的jar包,可以独立运行,不需要再依赖其他jar包. 我们在用eclipse中mvn创建mvn项目的时候,选择非webapp,会默认的以jar打包形式,如下图: ...

  5. 《笨方法学Python》加分题1

    1. 让你的脚本再多打印一行 我将这个题目理解为在脚本中增加一个空行,因此,在脚本中增加一条 print “\n”命令即可python中\为转义符,‘\n’为换行, ‘\t’是tab,‘\\’才是普通 ...

  6. gitlab 10汉化

    记得备份 先检查一下版本,好下载对应的汉化包 cat /opt/gitlab/embedded/service/gitlab-rails/VERSION 1)然后下载10.0.x.diff 文件到服务 ...

  7. 【轻松前端之旅】CSS入门

    编写css,很自然的思路: 1.给哪些元素添加样式呢?选择器技术就解决这个问题. 2.添加哪些样式?这就要了解css样式属性及它的值对应的显示规则了. 因此,学习css首先要学的就是选择器,至于样式属 ...

  8. Linux 第六天

    1)locate 在文件资料库中查找文件(需要文件资料库中有,新建的文件查不到,需要手动更新,updatedb.查不到/tmp目录下的文件) 语法:locate 文件名 常用选项: -i:无视大小写查 ...

  9. 学以致用二十八-----win10安装mysql5.7.24及卸载

    1.在windows环境下安装mysql,需要下载相对应的版本. ------------------------> 这里我下载的是mysql-5.7.24-win64.zip 2.下载后解压, ...

  10. nginx安装以及调优

    目录: 1.安装nginx 2.配置nginx 3.调优nginx 4.性能测试 ps:为了方便,文档使用docker容器来操作的. 1.安装nginx 1.1 启动容器.download nginx ...