【劉文彬】EOS多節點組網:商業場景分析以及節點啟動時序

圓方圓區塊鏈發表於2018-12-11

原文連結:醒者呆的部落格園,www.cnblogs.com/Evsward/p/e…

區塊鏈公鏈都是基於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

相關文章和視訊推薦

圓方圓學院彙集大批區塊鏈名師,打造精品的區塊鏈技術課程。 在各大平臺都長期有優質免費公開課,歡迎報名收看。

公開課地址:ke.qq.com/course/3451…

相關文章