目录
简介
eosio的权限体系是一套适应性与复杂性都很高的,无论是在DAWN 4.0加入的eosio.code
权限还是eosio.msig
都可以帮协助我们实现一套相对完善的系统或DAPP;这里将重点介绍多重签名的实现。
账户与权限
经过之前的使用已经知道了账户是指EOS Account,名称只能是base32字符集,现版本在账户建立时默认两个权限Owner、Active;这里就产生了一些个问题:
我们转账或者其他操作的权限到底是属于私钥还是账户?
EOS Account是公开的仅当做一个地址、一个代号,作为权限与私钥拥有者的一个代称,无法作为token或者其他权限的持有者。(严格说私钥也不一定能拥有对应的权限)
我的EOS币等资产到底由谁支配?
既不是由某个EOS Account支配也不是由某个私钥支配。账户下的资产归属对应权限,谁能开启此权限,谁就能动用此资产。
就是说,你可能知道了某个账号的一个私钥,但是你却无法利用它获利,因为你可能值拥有了必要权限的一部分。
eosio是什么账户?
eosio是一个特权账户,它是在eos源码编译时就已经定义好的账号(可以修改);
eosio.system
与eosio.bios
智能合约必须使用特权账号部署。
EOS权限体系
先解释几个关键词:
- perm_name 权限名称
- parent 父级权限名称
- weight 对应key权重
- threshold 阈值;比如owner权限阈值为1,owner内的一个key权重为1,那此key就可以触发使用owner权限,小于阈值则不行。
使用eosjs查询一个账户可以得到以下内容:
(http://127.0.0.1:8888/v1/chain/get_account)
....
....
"permissions": [
{
"perm_name": "active",
"parent": "owner",
"required_auth": {
"threshold": 1,
"keys": [{
"key": "EOS56eDHRpSPMHZfjUBxWeoVXCHMWmAQ3YaQLaZNeSmRyFLw45Eem",
"weight": 1
}],
"accounts": [{
"permission": {
"actor": "",
"permission": ""
},
"weight": 0
}],
"waits": [{
"wait_sec": 0,
"weight": 0
}]
}
},
{
"perm_name": "owner",
"parent": "",
"required_auth": {
"threshold": 1,
"keys": [{
"key": "EOS56eDHRpSPMHZfjUBxWeoVXCHMWmAQ3YaQLaZNeSmRyFLw45Eem",
"weight": 1
}],
"accounts": [{
"permission": {
"actor": "",
"permission": ""
},
"weight": 0
}],
"waits": [{
"wait_sec": 0,
"weight": 0
}]
}
}
],
....
....
请看下表,在表中owner权限的阈值是2,但是下属的两个密钥权重都只有1,那如果要使用此权限owner权限则必须使用EOSkey1111和EOSkey2222,相加权重才会够阈值下限。
active权限也是两个密钥,其中EOSkey3333权重是2,就是说它可以单独使用active权限,但是相对的EOSkey4444则不行,必须得到EOSkey3333的同意;此外active是从属于owner的,所以能使用owner权限则一定能使用active。
PARENT-PERM | PERMISSION | ACCOUNT/KEY | WEIGHT | THRESHOLD |
---|---|---|---|---|
– | owner | – | – | 2 |
– | ├ | EOSkey1111 | 1 | – |
– | └ | EOSkey2222 | 1 | – |
owner | active | – | – | 2 |
– | ├ | EOSkey3333 | 2 | – |
– | └ | EOSkey4444 | 1 | – |
在之前的我们执行转账调用合约都使用的是@active权限,因为我们active只有一个密钥,故它拥有了转账的权利。
再看下表,把key替换为其他的账号,比如下面这是user1
的权限信息,那这个user1
能独立做什么呢?什么都做不了,user1
不具有操作owner与active所需要的权重,此账号如果要使用某种权限必然经过Account2222
同意,没有它的同意,user1
本身无法动用账户内所有的资产;但是Account2222
可以独立使用user1
的所有权限;简单说就是user1
下的代币资产等等是完全归属于Account2222
的;说到这里也就能明白eos体系内权限设计的重要性,它完全确定了资产、权利的归属,可以设计多Account、多key、多权限、多层级的复杂权限体系,借助这套体系可以玩出很多东西来。
PARENT-PERM | PERMISSION | ACCOUNT/KEY | WEIGHT | THRESHOLD |
---|---|---|---|---|
– | owner | – | – | 2 |
– | ├ | EOSkey1111 | 1 | – |
– | └ | @Account2222 | 2 | – |
owner | active | – | – | 2 |
– | ├ | @Account2222 | 2 | – |
– | └ | EOSkey4444 | 1 | – |
这就是上边说到的账户下的资产归属对应权限,谁能开启此权限,谁就能动用此资产。
多重签名(Multiple Signatures)
利用上文所讲的eos账户权限机制我们可以实现多方签名共同操作同一个合约。这里就要使用到eosio.msig
合约,此合约由官方提供,它负责我们多重签名的主要工作。
eosio.msig
eosio.msig
中提出了一个proposal
的概念,就暂时叫做提案吧;它本身并不是一个为了多重签名而生的东西,个人认为此合约它是为了提供权限申请的功能,举个例子:
创建token QQB指定所属用户userA,表明userA用户有权利给任何人派发QQB,然后issue发行货币给userB:
# 命令1
$ cleos push action eosio.token create '[ "userA", "10000.0000 QQB"]' -p eosio.token@active
# 命令2
$ cleos push action eosio.token issue '[ "userB", "10.0000 QQB", "memo" ]' -p userA@active
注意看命令2使用的权限为userA@active
,就是说常规模式下你没有userA@active
此权限的私钥就不可能给别人派发QQB token。换个角度分析,如果userB需要QQB,那他有两条路:
- 场外沟通,找到userA这个人跟他讲,让他提交这个命令2;
- 想办法获取
userA@active
权限的私钥。第二个办法那只能各自想动脑筋去了。
很明显方式一有个缺陷,必须要场外沟通,而且交易命令还是由userA提交,userB只能从结果上得知命令是否成功或者有没有问题等。那eosio.msig
合约就提供了这样一个权限申请:
- 由userB发起一比msig中的提案,内容为:想给userB发10个QQB,但是没有userA的权限,特发此提案;
$ cleos multisig propose test '[{"actor": "userA", "permission": "active"}]' '[{"actor": "userA", "permission": "active"}]' eosio.token issue '{"to": "userB", "quantity": "10.0000 QQB", "memo": ""}' -p userB
- userA在线上对userB的test提案内容查询后,发现没有问题,将自己的active权限授予test提案表明可以执行;
$ cleos multisig review userB test -p userA
$ cleos multisig approve userB test '{"actor": "userA", "permission": "active"}' -p userA
- test提案得到了应有的权限后,它就可以被创始人userB直接执行,至此userB也就得到了他想要的10QQB。
$ cleos multisig exec userB test -p userB
此外eosio.msig
合约还提供拒绝提案、取消提案等操作。
另外使用此合约需要给部署此合约的账号授予特权:
$ cleos push action eosio setpriv '["eosio.msig", 1]' -p eosio
通过msig我们可以向他人申请本不属于我们的权限,而且是在eos体系线上执行的,无须借助场外的手段请求他人的权限。
那么有了eosio.msig
合约权限申请的功能,如何实现多重签名?
在文章的前半部有提到权限的组成是多样化的,可以多人多密钥共同管理权限,那也就是说,在提案审批的时候,由于权重问题就需要不同用户来进行审批,即多重签名。
权限构建
接下来看一个实现多重签名的例子:
首先创建三个账户testaaaa1111
、testaaaa1112
、testaaaa1113
。
将testaaaa1111
构建有如下权限的账户:
PARENT-PERM | PERMISSION | ACCOUNT/KEY | WEIGHT | THRESHOLD |
---|---|---|---|---|
– | owner | – | – | 1 |
– | ├ | EOSkeySDG..... | 1 | – |
owner | active | – | – | 2 |
– | ├ | @testaaaa1112 | 1 | – |
– | └ | @testaaaa1113 | 1 | – |
为了方便我们只使用修改active
权限对owner不做修改,这里使用了eosjs
实现:
exports.updateAccountPermission = function () {
return eos.transaction(tr => {
tr.updateauth({
"account": "testaaaa1111",
"permission": "active",
"parent": "owner",
"auth": {
"threshold": 2,
"keys": [],
"accounts": [{
"permission": {
"actor": "testaaaa1112",
"permission": "active"
},
"weight": 1
}, {
"permission": {
"actor": "testaaaa1113",
"permission": "active"
},
"weight": 1
}],
"waits": []
}
});
});
};
调用http://127.0.0.1:8888/v1/chain/get_account
可以查询到:
...
...
"permissions": [{
"perm_name": "active",
"parent": "owner",
"required_auth": {
"threshold": 2,
"keys": [],
"accounts": [
{
"permission": {
"actor": "testaaaa1112",
"permission": "active"
},
"weight": 1
},
{
"permission": {
"actor": "testaaaa1113",
"permission": "active"
},
"weight": 1
}],
"waits": []
}
},
{
"perm_name": "owner",
"parent": "",
"required_auth": {
"threshold": 1,
"keys": [{
"key": "EOS8PRzZoJN19VUnDxn2jPqtidCWvkqKQ4izyL7RDZkAmaDUCQCfm",
"weight": 1
}],
"accounts": [],
"waits": []
}
}
],
...
...
可以看出来testaaaa1111@actve
权限的key已经被移除,并使用testaaaa1112
、testaaaa1113
两个账号共同拥有此权限。
创建提案
以下内容均不使用owner
权限。
此时使用testaaaa1111
转账等操作就会发现除了使用owner
权限的key签名之外,连签名交易的key都不知道用什么。假设testaaaa1112
想让testaaaa1111
转出token,接下来使用eosio.msig
合约的功能,调用合约中发起提案的propose
方法:
exports.propose = function () {
return eos.transaction({
actions: [{
account: 'eosio.msig',
name: 'propose',
authorization: [{
actor: 'testaaaa1112',
permission: 'active'
}],
data: {
// 提案创建人,可以是其他无关账号
proposer: 'testaaaa1112',
// 提案名称
proposal_name: 'firstmsig11',
// 当前提案申请的权限
requested: [{
"actor": "testaaaa1112",
"permission": "active"
}, {
"actor": "testaaaa1113",
"permission": "active"
}],
// 提案所包含的待执行交易内容
trx: {
// 等同于交易截至时间
"expiration": "2018-09-28T10:56:12",
"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": "testaaaa1111",
"permission": "active"
}],
"data": {
"from": "testaaaa1111",
"to": "testaaaa1112",
"quantity": "1.2000 SYS",
"memo": ""
}
}],
"transaction_extensions": []
}
}
}]
});
};
eosio.msig
合约使用了两个Multi-Index DB Table,名为proposal
和approvals
,分别记录了提案对应的交易与提案当前权限申请的状况。
可以在http://127.0.0.1:8888/v1/chain/get_table_rows
curl --request POST \
--url http://127.0.0.1:8888/v1/chain/get_table_rows \
--data '{"code":"eosio.msig","scope":"testaaaa1112","table":"proposal","json":"true"}'
查询结果分别如下(这里查询的结果使用eosjs的查询结果可能有些许差异):
{
"rows": [
{
"proposal_name": "firstmsig11",
"requested_approvals": [
{
"actor": "testaaaa1112",
"permission": "active"
},
{
"actor": "testaaaa1113",
"permission": "active"
}
],
"provided_approvals": [
]
}
],
"more": false
}
#-----------------
{
"rows": [
{
"proposal_name": "firstmsig11",
"packed_transaction": "cc08ae5b000000000000000000000100a6823403ea3055000000572d3ccdcd01104208c61893b1ca00000000a8ed323221104208c61893b1ca204208c61893b1cae02e00000000000004535953000000000000"
}
],
"more": false
}
这里可以看到请求权限字段requested_approvals
中有两个权限,已经提供权限provided_approvals
没有值。
审批提案
接下来使用testaaaa1113
账号进行一次审批提案:
exports.approve = function () {
return eos.transaction({
actions: [{
account: 'eosio.msig',
name: 'approve',
authorization: [{
actor: 'testaaaa1113',
permission: 'active'
}],
data: {
// 提案创建人
proposer: 'testaaaa1112',
// 提案名称
proposal_name: 'firstmsig11',
// 提供的权限
level: {
"actor": "testaaaa1113",
"permission": "active"
},
}
}]
});
};
使用http://127.0.0.1:8888/v1/chain/get_table_rows
再次查询firstmsig11
的进度:
{
"rows": [
{
"proposal_name": "firstmsig11",
"requested_approvals": [
{
"actor": "testaaaa1112",
"permission": "active"
}
],
"provided_approvals": [
{
"actor": "testaaaa1113",
"permission": "active"
}
]
}
],
"more": false
}
基本上符合预期的结果,不过此requested_approvals
字段表达的意思应该是:当前提案尚未取得的权限。
同样的使用刚才审批的操作将testaaaa1112@active
也赋予给此提案。
执行提案
当两个必要权限都申请通过后,就可以执行此提案包含的交易内容,执行前记得看以下账户余额,我这里目前是testaaaa1111 199.0000 SYS; testaaaa1112 1.0000 SYS
。
exports.exec = function () {
return eos.transaction({
actions: [{
account: 'eosio.msig',
name: 'exec',
authorization: [{
actor: 'testaaaa1112',
permission: 'active'
}],
data: {
// 提案创建人
proposer: 'testaaaa1112',
// 提案名称
proposal_name: 'firstmsig11',
// 执行人
executer: "testaaaa1112"
}
}]
});
};
当提案成功执行后会从Multi-Index DB Table中删除对应的记录。
至此多重签名就算是完成了。
.
.
.
.
.
.
【本文章出自NM1024.com,转载请注明作者出处。】
>>转载请注明原文链接地址:EOS账户权限与多重签名
1 条回复
[…] 此合约我在本地也尝试的使用非eosio账户部署,但是部署成功后不生效,得出的结论就是eosio.bios和eosio.system都只能部署到eosio特权账号上,而且两个合约只能生效一个。 […]