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 流程

  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

更多文章请转到醒者呆的博客园

posted @ 2018-06-28 10:37  一面千人  阅读(4516)  评论(4编辑  收藏  举报