eos主網上線在即,它之所以能受到各方青睞,主要是看中了它在未來商業應用落地的潛力。在這期間,完善的賬戶與權限系統是必要條件。
關鍵字:eos,賬戶,錢包,權限,多重簽名,eosio.msig,proposal,權重,閾值
概況
- 賬戶:是存儲在區塊鏈上的人們可識別的ID。
- 權限:每個事務都有,它是由已配置許可的賬戶所評估的。
- 閾值:每個被命名的權限都有一個有效范圍,必須滿足是在許可下的一個簽名事務,將被視為有效。
- 簽名:事務的簽名是通過利用一個客戶端來執行,該客戶端擁有一個已加載並已解鎖的錢包。
- 錢包:可以保護及使用你的keys的一個軟件。這些keys可能是也可能不是區塊鏈授權的一個許可賬戶。
錢包
錢包是一個客戶端,用於存儲keys,可能與也可能不與一個或多個賬戶進行關聯。一個錢包會有鎖定和解鎖兩種狀態,理想狀態下,它是被一個高熵密碼所保護。EOSIO有一個命令行客戶端cleos,與一個輕客戶端keosd交互,它倆構成了eos錢包的這種模式。
賬戶
一個私人個體或者一個組織都可以擁有賬戶,在交易或者其他push一個事務到區塊鏈上的時候,賬戶是必須的。
Authorities(權限) 和 Permissions(許可)
這兩個概念特別容易搞混為一談,這里給做一下區分。
Authorities決定了是否每一個給出的action都被正確授權。
每個賬戶都有兩個與生俱來的權限命名:
- owner,象征着一個賬戶的所有權。只有很少的事務會需要這種權限。但最值得注意的是,actions可以對owner的權限做出各種改變。所以一般來說,建議owner被冷藏存儲,不告訴任何人。owner可以用來回復另一個可能已遭泄露的許可。
- active,用於轉移資金,投票生產者以及做其他高級賬戶的變更操作。唯一不同的是,它不能夠改變owner。
除了這兩個與生俱來的權限。一個賬戶也能擁有自定義命名的權限,它可以進一步擴展賬戶管理。自定義權限非常靈活並且提出了眾多可能的用例實現。在開發者社區,這些問題已經被提了出來,包括他們是如何工作的,以及如果有的話是什么約定被采用了。許可對於任何給定的權限可以被分配到一個或多個公鑰或者一個有效的賬戶名稱。
綜合實踐
下面是結合所有上述概念以及一些小例子,用來展示如何實際使用它們。
默認賬戶配置(單簽名)
這里主要講述當一個賬戶被創建以后,它是如何配置的。它分別擁有owner和active權限,可獨立設置key,兩個keys都是權重為1,且許可的閾值也都是1。該賬戶在執行某個操作action時,需要通過默認的配置,單獨簽名授權一個本機權限。
創建賬戶演示
Usage: cleos create account [OPTIONS] creator name OwnerKey [ActiveKey]
(在eos正式版本中,activeKey已不強制,變為可選)在創建賬戶時,需要指定其owner和active兩個權限的key,這里要使用公鑰。所以,
liuwenbin@liuwenbin:~$ cleos create account eosio lllwwwbbb "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
executed transaction: 7afb37faf4fadaf1b23bbf33a2ec316baf41fcd8e5587f4b2307db091c372829 200 bytes 209 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"lllwwwbbb","owner":{"threshold":1,"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnV...
warning: transaction executed locally, but may not be confirmed by the network yet
這個版本只需要指定一個owner權限的key即可創建賬戶。active權限是小於owner的,所以顯得不那么重要。
多簽名賬戶
上面單簽名的內容很簡單,之所以介紹它,是為了與本節多簽名賬戶進行比較學習。
多簽名賬戶,一個賬戶假設為Jack的owner和active權限分別被授權為其他兩個賬戶Alice和Bob的許可。
@Jack
permission | account | weight | threshold |
---|---|---|---|
owner | 2 | ||
@Alice | 1 | ||
@Bob | 1 | ||
active | 1 | ||
@Alice | 1 | ||
@Bob | 1 |
多賬戶權限演示
首先,我們創建至少三對key:
liuwenbin@liuwenbin:~$ cleos wallet keys
[
"EOS5WsMPtPqW8d3yxfCWP9WJcbkD3wDBWDfyV2CLagC7tWKT9wGkX",
"EOS5nrEfZ4wcnmt2u2PGWDneZDNevQ6MoiF4RE3hpLKHk2rWpBB8x",
"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"EOS6PTrfoaTYRHUhCedBf6b9CX3KDpJoWry6WQHe3WkpdFTMV2f78"
]
然后,分別使用不同的public key創建三個賬戶:jack, alice和bob,創建成功以后,我們先來查看當前jack賬戶的permission內容:
liuwenbin@liuwenbin:~$ cleos get account jack
permissions:
owner 1: 1 EOS5WsMPtPqW8d3yxfCWP9WJcbkD3wDBWDfyV2CLagC7tWKT9wGkX
active 1: 1 EOS5WsMPtPqW8d3yxfCWP9WJcbkD3wDBWDfyV2CLagC7tWKT9wGkX
接着,按照上面我們設計的表格內容,我們先對jack的owner權限進行修改:
liuwenbin@liuwenbin:~$ cleos set account permission jack owner '{"threshold":2,"keys":[],"accounts":[{"permission":{"actor":"alice","permission":"owner"},"weight":1},{"permission":{"actor":"bob","permission":"owner"},"weight":1}],"waits":[]}' -p jack@owner
executed transaction: 494777ac55fa19144027ba5fdf75dcdaab4c4d52cdcaa13c0b6ecabfe622ffd1 160 bytes 136 us
# eosio <= eosio::updateauth {"account":"jack","permission":"owner","parent":"","auth":{"threshold":2,"keys":[],"accounts":[{"per...
warning: transaction executed locally, but may not be confirmed by the network yet
注意這里使用到了 cleos set account permission 命令。這條命令中比較重要的部分是第三個參數authority的內容。
authority的內容可以是:
- NULL,代表刪除權限
- public key,將該權限的內容設置為一個公鑰
- JSON字符串,完整地復雜地設置一個賬戶某個權限的內容
- 一個文件名,同上,只不過改為將完整配置放在文件中讀取
根據我們以上對jack owner權限的設計方案,是屬於比較復雜的權限內容,因此這里采用的是第三種方式,及JSON字符串的方式。那么JSON字符串的格式是什么?我們例舉本次生效的配置格式化如下:
{
"threshold": 2,
"keys": [],
"accounts": [
{
"permission": {
"actor": "alice",
"permission": "owner"
},
"weight": 1
},
{
"permission": {
"actor": "bob",
"permission": "owner"
},
"weight": 1
}
],
"waits": []
}
我們分別來介紹這里面的字段:
threshold
目前最多只有兩個賬戶,每個賬戶的權重都是1,所以threshold的取值范圍是1或者2。
- 1:在@Jack賬戶上執行任何需要owner權限的操作必須由@Alice或者@Bob賬戶任意一個賬戶進行簽名授權。
- 2:在@Jack賬戶上執行任何需要owner權限的操作必須由@Alice和@Bob賬戶進行多簽名授權。
keys
也可以通過JSON字符串設置秘鑰,這與在命令中第三個參數直接使用公鑰是相同的效果。但是要注意的是,如果該賬戶已經通過JSON設置了復雜的權限內容,再次修改的時候一定還需要使用原JSON數據進行增量修改,若使用命令行加公鑰的方式會直接覆蓋掉原JSON設置的復雜權限內容。
accounts
權限是通過其他賬戶來授權,其中包括:
- permission:某個賬戶actor的某個權限
- weight,與上面的threshold呼應使用
我們這里就是通過設置該字段來實現的多賬戶簽名。
waits
設置延時時間,最大不超過max_transacton_delay的配置值。
下面來設置其active權限,active權限與owner權限不同的是threshold的值,以及
賬戶除了owner權限是根權限以外,active本身是其子權限,而 cleos set account permission 命令設置的權限默認都是active的子權限,注意,我們在設置active權限本身的時候,要手動指定其父權限為owner,否則會默認指向自己,報錯。
liuwenbin@liuwenbin:~$ cleos set account permission jack active '{"threshold":1,"keys":[],"accounts":[{"permission":{"actor":"alice","permission":"owner"},"weight":1},{"permission":{"actor":"bob","permission":"owner"},"weight":1}],"waits":[]}' owner -p jack@owner
executed transaction: 0865e7e9122354a2c29fd9ab48bb39b8a7bd758fdd7c4dc376e21ce45d8e23fb 176 bytes 461 us
# eosio <= eosio::updateauth {"account":"jack","permission":"active","parent":"owner","auth":{"threshold":1,"keys":[],"accounts":...
warning: transaction executed locally, but may not be confirmed by the network yet
這樣,jack賬戶的權限就全部按照以上權限設計表格設置完畢了,下面我們使用命令來驗證一下目前jack賬戶的權限內容:
liuwenbin@liuwenbin:~$ cleos get account jack
permissions:
owner 2: 1 alice@owner, 1 bob@owner,
active 1: 1 alice@owner, 1 bob@owner,
多簽名演示
一. 部署eosio.msig合約
首先要注意的是,EOS中對多簽名的支持是通過eosio.msig智能合約來支持的,因此秉承eos"專人專責"的設計規范,我們先創建一個賬戶eosio.msig,並用該賬戶部署eosio.msig合約。
如果不是自己新增或者對原合約有修改的話,推薦直接使用build目錄下的contract,已經生成好了相關的wast以及abi文件。
二. 部署eosio.token合約
這里我們使用token合約的相關權限操作進行多簽名的演示。同樣的,我們也創建一個賬戶eosio.token並用其部署eosio.token合約。創建一個SYS token,並發放給jack,bob和alice三人分別100個。然后我們調用命令:
liuwenbin@liuwenbin:~$ cleos push action eosio.token transfer '["jack","bob","50 SYS"]' -p jack@owner
executed transaction: 8166f8503a098eeab6642afe375167e18bf79d0d904015f5bba19524ce2d56d6 144 bytes 846 us
# eosio.token <= eosio.token::transfer {"from":"jack","to":"bob","quantity":"50 SYS","memo":""}
# jack <= eosio.token::transfer {"from":"jack","to":"bob","quantity":"50 SYS","memo":""}
# bob <= eosio.token::transfer {"from":"jack","to":"bob","quantity":"50 SYS","memo":""}
warning: transaction executed locally, but may not be confirmed by the network yet
liuwenbin@liuwenbin:~$ cleos get currency balance eosio.token alice
100 SYS
liuwenbin@liuwenbin:~$ cleos get currency balance eosio.token jack
50 SYS
liuwenbin@liuwenbin:~$ cleos get currency balance eosio.token bob
150 SYS
該命令執行成功,是由jack@owner權限許可的該命令操作。
三. 描述問題,引出多簽名
我們在上一小節中介紹到了,jack的owner權限是與bob和alice相關的。所以可能遇到這么一個情況,jack今天不在,而它又需要以上的操作怎么辦?那么是否可以通過上面對jack權限的設置,在jack不在的情況下通過bob和alice的操作完成本屬於jack權限許可的操作呢?是的,這就是多簽名的概念和意義。
四. 建立多簽名操作提案
首先,eos中關於多簽名的命令是multisig,這也是通過第一步中eosio.msig合約實現的。
jack不在,要想通過bob和alice兩個人來審批本屬於jack的操作,這就要比jack本人操作要麻煩一點,需要新建一個提案。
liuwenbin@liuwenbin:~$ cleos multisig propose nojack '[{"actor":"alice","permission":"owner"},{"actor":"bob","permission":"owner"}]' '[{"actor":"jack","permission":"owner"}]' eosio.token transfer '{"from":"jack","to":"bob","quantity":"5 SYS","memo":"test multisig"}' -p eosio.msig
executed transaction: a223945e79c92cd5ca5243d64dce02ecf9bb1420e6ca3b5e5ac3fdae3dd4e8c3 240 bytes 389 us
# eosio.msig <= eosio.msig::propose {"proposer":"eosio.msig","proposal_name":"nojack","requested":[{"actor":"alice","permission":"owner"...
warning: transaction executed locally, but may not be confirmed by the network yet
注意在提案中的定義操作要輸入完整的key-value對應參數關系。本例中,transfer后面的參數的定義內容與上面直接用jack是一致的,但格式是完整格式,上面jack單簽名時是使用的簡略版,可自行對比發現。
這個提案結尾需要一個賬戶授權,這個賬戶是不做限制的,只是為了日后操作這個提案本身,因此這里選擇一個不想關的用來部署eosio.msig合約的賬戶eosio.msig簽名授權該提案。
五. 查看提案
liuwenbin@liuwenbin:~$ cleos multisig review eosio.msig nojack
{
"proposal_name": "nojack",
"packed_transaction": "38d0295b000000000000000000000100a6823403ea3055000000572d3ccdcd0100000000000091790000000080ab26a72e00000000000091790000000000000e3d320000000000000000535953000000000d74657374206d756c746973696700",
"transaction": {
"expiration": "2018-06-20T03:55:36",
"ref_block_num": 0,
"ref_block_prefix": 0,
"max_net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [{
"account": "eosio.token",
"name": "transfer",
"authorization": [{
"actor": "jack",
"permission": "owner"
}
],
"data": {
"from": "jack",
"to": "bob",
"quantity": "50 SYS",
"memo": "test multisig"
},
"hex_data": "00000000000091790000000000000e3d320000000000000000535953000000000d74657374206d756c7469736967"
}
],
"transaction_extensions": []
}
}
重點查看該提案設定的action,包括該action需要的權限,操作名,參數等。
六. 查看提案審批情況
multisig合約定義了兩個數據表,其中一個approvals表就是記錄了提案的審批狀態。
liuwenbin@liuwenbin:~/.local/share/eosio/nodeos/config$ cleos get table eosio.msig eosio.msig approvals {
"rows": [{
"proposal_name": "nojack",
"requested_approvals": [{
"actor": "alice",
"permission": "owner"
},{
"actor": "bob",
"permission": "owner"
}
],
"provided_approvals": []
}],
"more": false
}
七. 審批提案
提案創建完成以后,就需要alice和bob這兩個賬戶分別進行審批,他們各自都需要使用自己的owner權限簽名審批這個動作。
liuwenbin@liuwenbin:~$ cleos multisig approve eosio.msig nojack '{"actor":"alice","permission":"owner"}' -p alice@owner
executed transaction: be473b9abcc361a6d4c00d726318d06cb98775bd9390f2ebe1e4e8d72afd5de2 128 bytes 269 us
# eosio.msig <= eosio.msig::approve {"proposer":"eosio.msig","proposal_name":"nojack","level":{"actor":"alice","permission":"owner"}}
warning: transaction executed locally, but may not be confirmed by the network yet
liuwenbin@liuwenbin:~$ cleos multisig approve eosio.msig nojack '{"actor":"bob","permission":"owner"}' -p bob@owner
executed transaction: 98c707b04ef43b17e25bfb5fb6700300f438ea3af08f5c5f44724bb7f66c9eb2 128 bytes 441 us
# eosio.msig <= eosio.msig::approve {"proposer":"eosio.msig","proposal_name":"nojack","level":{"actor":"bob","permission":"owner"}}
warning: transaction executed locally, but may not be confirmed by the network yet
八. 返回第6步
liuwenbin@liuwenbin:~/.local/share/eosio/nodeos/config$ cleos get table eosio.msig eosio.msig approvals {
"rows": [{
"proposal_name": "nojack",
"requested_approvals": [],
"provided_approvals": [{
"actor": "alice",
"permission": "owner"
},{
"actor": "bob",
"permission": "owner"
}
]
}],
"more": false
}
可以看到,原requested_approvals中的待審批項已經全部轉到了provided_approvals 已審批列表中。
這里我們可以通過unapprove命令將已審批項轉至未審批集合中去。unapprove的參數與以上approve完全相同。
九. 執行提案
以上兩個非jack賬戶的審批過程就是多簽名的內容,多簽名執行成功以后,可以開始執行提案。
liuwenbin@liuwenbin:~$ cleos multisig exec eosio.msig nojack -p eosio.msig
executed transaction: 8734cd573e33c0bbc9f13372f4950908312afdac0146bccae624dba53995bf4b 160 bytes 441 us
# eosio.msig <= eosio.msig::exec {"proposer":"eosio.msig","proposal_name":"nojack","executer":"eosio.msig"}
warning: transaction executed locally, but may not be confirmed by the network yet
解決問題:以上提案執行過程會出現執行失敗的情況,錯誤信息:
{"auth":{"actor":"jack","permission":"owner"},"provided_delay":0,"provided_permissions":[{"actor":"eosio.msig","permission":"eosio.code"}],"provided_keys":[],"delay_max_limit_ms":388800000}
解決方法是要部署system合約並利用system合約為多簽名合約的功能授權,授權成功以后再執行即可成功。
cleos push action eosio setpriv '["eosio.msig", 1]' -p eosio
1個小坑:是在部署system合約時報錯transaction took too long, 這種情況只需要在執行命令時加 -x 參數,默認值是30s,改為60讓時間延長一些即可執行成功。
TODO:馬上會根據當前eos最新版本v1.0.6發出一篇關於環境搭建的配置文章,以避免未來踩到與此類似的環境問題。
十. 驗證結果
執行成功以后,再次檢查各賬戶的SYS余額,能夠發現此提案的轉賬50個SYS的操作已經生效。
liuwenbin@liuwenbin:~$ cleos get currency balance eosio.token alice
100 SYS
liuwenbin@liuwenbin:~$ cleos get currency balance eosio.token jack
45 SYS
liuwenbin@liuwenbin:~$ cleos get currency balance eosio.token bob
155 SYS
自定義權限
上面提到了,除了owner和active權限之外,我們還可以自定義權限。仍在以上@Jack賬戶中進行補充,增加一個自定義權限,名為publish:
permission | account | weight | threshold |
---|---|---|---|
publish | 2 | ||
@Alice | 2 | ||
@Bob | 2 | ||
"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" | 1 |
weight:
權重,這是相對於上面threshold閾值的概念,就拿上面的屬於賬戶@Jack的publish權限來講,它包含三個子權限分別是:
- @Alice賬戶,權重為2
- @Bob賬戶,權重為2
- 一個keys,權重為1
而此時publish權限的閾值設定為2,也就是說,@Alice或@Bob任意一個賬戶均可以簽名授權@Jack賬戶的publish權限,因為這兩個賬戶的權重均為2,任意一個簽名都可保證夠到publish權限的閾值2。而如果只有權重為1的keys簽名,則必須再有以上任意一個賬戶來同時簽名,總權重達到了3,超過了publish閾值2才可以成功授權。
自定義權限部分的演示與上面的類似,通過set account permission可以設置有key,有賬戶,不同權重的十分復雜的權限內容。這里不再贅述。
總結
本文可以分為兩部分,第一部分是如何為一個賬戶設置復雜的權限體系,第二部分是如何利用賬戶權限進行多賬戶簽名。這部分內容十分靈活,依據這個流程,我們可以根據業務場景需要,調整設計。例如,多賬戶必須均簽名才能通過某一個操作,或者多賬戶只要其中一個或者多個簽名即可通過某一個操作。通過本文所述,均可實現。
參考資料
- EOS官方文檔
- 本地環境測試(基於EOS v1.0.5)