【問鏈-EOS公開課】第十四課 EOS從單簽名到多簽名
一、基本知識
賬戶:是儲存在區塊鏈上的人們可識別的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時,需要通過預設的配置,單獨簽名授權一個本機許可權。
建立賬戶
cleos create account [OPTIONS] creator name OwnerKey [ActiveKey]
(在eos正式版本中,activeKey已不強制,變為可選)在建立賬戶時,需要指定其owner和active兩個許可權的key,這裡要使用公鑰。所以,
cleos create account eosio alice EOS5JUBZXokmgHR7yHFgxoZdQZyfvu2oCHiPBeGUE3fQyZ9MHdmi8 返回結果如下: executed transaction: ac51aea40af91e283b7e99b431e36b4556bc8487d73c807118e30dd3324c4136 200 bytes 149 us # eosio <= eosio::newaccount {"creator":"eosio","name":"alice","owner":{"threshold":1,"keys":[{"key":"EOS5JUBZXokmgHR7yHFgxoZdQZy...
這個版本只需要指定一個owner許可權的key即可建立賬戶。active許可權是小於owner的,所以顯得不那麼重要。
三、多簽名賬戶
上面單簽名的內容很簡單,之所以介紹它,是為了與本節多簽名賬戶進行比較學習。
多簽名賬戶,一個賬戶假設為Jack的owner和active許可權分別被授權為其他兩個賬戶Alice和Bob的許可。
@Jack
首先,我們建立至少三對key:
cleos wallet keys
[
"EOS5JUBZXokmgHR7yHFgxoZdQZyfvu2oCHiPBeGUE3fQyZ9MHdmi8",
"EOS6FMtV7gnbBLQ2CbD7J3YAKA8hY6u6jZ2KGtcy628z8QQ8MvXBz",
"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"EOS8H7CydzSJ2y5xXdnM5JBKZ2N1A8mE83Lg751G7q3gQqUzrmzwt",
"EOS8YMWk6vxawJEDML7mYXA3mGUap7L68L3GroGUpDhpbuadwHXom",
"EOS8fURVqUA6YHeKEHD6fLMvqj6NYEcFTqj6bNApCz24SVzFxKqev"
]
然後,分別使用不同的public key建立三個賬戶:jack, alice和bob,建立成功以後,我們先來檢視當前jack賬戶的permission內容:
cleos get account alice
permissions:
owner 1: 1 EOS5JUBZXokmgHR7yHFgxoZdQZyfvu2oCHiPBeGUE3fQyZ9MHdmi8
active 1: 1 EOS5JUBZXokmgHR7yHFgxoZdQZyfvu2oCHiPBeGUE3fQyZ9MHdmi8
memory:
quota: unlimited used: 2.66 KiB
接著,按照上面我們設計的表格內容,我們先對jack的owner許可權進行修改:
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 [email protected]
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,否則會預設指向自己,報錯。
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 [email protected]
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賬戶的許可權內容:
cleos get account jack
permissions:
owner 2: 1 [email protected], 1 [email protected],
active 1: 1 [email protected], 1 [email protected]
四、多簽名過程實戰
1.部署eosio.msig合約
首先要注意的是,EOS中對多簽名的支援是通過eosio.msig智慧合約來支援的,因此秉承eos"專人專責"的設計規範,我們先建立一個賬戶eosio.msig,並用該賬戶部署eosio.msig合約。
cleos create account eosio eosio.msig EOS8H7CydzSJ2y5xXdnM5JBKZ2N1A8mE83Lg751G7q3gQqUzrmzwt
如果不是自己新增或者對原合約有修改的話,推薦直接使用build目錄下的contract,已經生成好了相關的wast以及abi檔案。
2.部署eosio.token合約
這裡我們使用token合約的相關許可權操作進行多簽名的演示。同樣的,我們也建立一個賬戶eosio.token並用其部署eosio.token合約。
cleos create account eosio eosio.token EOS8YMWk6vxawJEDML7mYXA3mGUap7L68L3GroGUpDhpbuadwHXom
executed transaction: c922c4767f23d4526261a7ca0b6554e5c02b5b4aea95b83510eecd81a9696ce3 200 bytes 165 us
# eosio <= eosio::newaccount "0000000000ea305500a6823403ea305501000000010003e110a468d88b85b1257ee1d6949836c68a84a4c694c5cbd4297a6...
cleos set contract eosio.token /home/kris/eos/build/contracts/eosio.token -p eosio.token
Reading WAST/WASM from /home/kris/eos/build/contracts/eosio.token/eosio.token.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 127adf7ee9b404ccd8d7fa5645f33591cadabfb9dbb5feb619ce0c19130730ce 8104 bytes 886 us
# eosio <= eosio::setcode "00a6823403ea305500009594010061736d01000000017e1560037f7e7f0060057f7e7e7f7f0060047f7e7f7f00600000600...
# eosio <= eosio::setabi "00a6823403ea3055b4030e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d6505087472616...
3.建立一個SYS token,併發放給jack,bob和alice三人分別100個。
cleos push action eosio.token create '["eosio","100000000.0000 SYS"]' -p eosio.token
executed transaction: 260b8183e4d4e0d5c227971473791c8aecebf647856dd84c2eca8ae5365498df 120 bytes 257 us
# eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"100000000.0000 SYS"}
轉賬之前先建立發行代幣使用者bank
cleos create account eosio bank EOS8fURVqUA6YHeKEHD6fLMvqj6NYEcFTqj6bNApCz24SVzFxKqev
executed transaction: 8f98a0681cd1f2d787ef55583e93e7d7e250b4de7e24ea859de709e06be6b3ea 200 bytes 179 us
# eosio <= eosio::newaccount "0000000000ea3055000000000000a73901000000010003f13ad86a535895877f448556649f9548631da55a1891a37f12e9e...
cleos push action eosio.token issue '["bank","100000000.0000 SOS","issue token"]' -p eosio
executed transaction: 8f04ae4a2bdd8442a8339f488c6cdf51175dabe9fc6e911c7c61dd8482390b31 128 bytes 810 us
# eosio.token <= eosio.token::issue {"to":"bank","quantity":"100000000.0000 SYS","memo":"issue token"}
# eosio.token <= eosio.token::transfer {"from":"eosio","to":"bank","quantity":"100000000.0000 SOS","memo":"issue token"}
# eosio <= eosio.token::transfer {"from":"eosio","to":"bank","quantity":"100000000.0000 SOS","memo":"issue token"}
# bank <= eosio.token::transfer {"from":"eosio","to":"bank","quantity":"100000000.0000 SOS","memo":"issue token"}
cleos push action eosio.token transfer '["bank","jack","100.0000 SYS","transfer from bank to jack"]' -p bank
cleos push action eosio.token transfer '["bank","alice","100.0000 SYS","transfer from bank to alice"]' -p bank
cleos push action eosio.token transfer '["bank","bob","100.0000 SYS","transfer from bank to bob"]' -p bank
4.然後我們呼叫命令,從jack轉50SYS給bob:
cleos push action eosio.token transfer '["jack","bob","50 SYS"]' -p [email protected]
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":""}
5.查詢餘額
cleos get currency balance eosio.token alice
100 SYS
$ cleos get currency balance eosio.token jack
50 SYS
$ cleos get currency balance eosio.token bob
150 SYS
該命令執行成功,是由[email protected]許可權許可的該命令操作。到這裡並沒有結束,jack的owner許可權是與bob和alice相關的。所以可能遇到這麼一個情況,jack今天不在,而它又需要以上的操作怎麼辦?那麼是否可以通過上面對jack許可權的設定,在jack不在的情況下通過bob和alice的操作完成本屬於jack許可權許可的操作呢?是的,這就是多簽名的概念和意義。
6. 建立多簽名操作提案
首先,eos中關於多簽名的命令是multisig,這也是通過第一步中eosio.msig合約實現的。
jack不在,要想通過bob和alice兩個人來審批本屬於jack的操作,這就要比jack本人操作要麻煩一點,需要新建一個提案。
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簽名授權該提案。
7. 檢視提案
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需要的許可權,操作名,引數等。
8.檢視提案審批情況
multisig合約定義了兩個資料表,其中一個approvals表就是記錄了提案的審批狀態。
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
}
9.審批提案
提案建立完成以後,就需要alice和bob這兩個賬戶分別進行審批,他們各自都需要使用自己的owner許可權簽名審批這個動作。
cleos multisig approve eosio.msig nojack '{"actor":"alice","permission":"owner"}' -p [email protected]
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
cleos multisig approve eosio.msig nojack '{"actor":"bob","permission":"owner"}' -p [email protected]
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
10.重新檢視提案情況
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完全相同。
11.執行提案
以上兩個非jack賬戶的審批過程就是多簽名的內容,多簽名執行成功以後,可以開始執行提案。
$ 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讓時間延長一些即可執行成功。
12. 驗證結果
執行成功以後,再次檢查各賬戶的SYS餘額,能夠發現此提案的轉賬50個SYS的操作已經生效。
~$ cleos get currency balance eosio.token alice
100 SYS
~$ cleos get currency balance eosio.token jack
45 SYS
~$ cleos get currency balance eosio.token bob
155 SYS
五、自定義許可權
上面提到了,除了owner和active許可權之外,我們還可以自定義許可權。仍在以上@Jack賬戶中進行補充,增加一個自定義許可權,名為publish:
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,有賬戶,不同權重的十分複雜的許可權內容。這裡不再贅述。
本文參考轉載來自【http://www.cnblogs.com/Evsward/p/msig.html?utm_source=debugrun&utm_medium=referral】