EOS · 2018年9月18日 1

EOS账户权限与多重签名

简介

eosio的权限体系是一套适应性与复杂性都很高的,无论是在DAWN 4.0加入的eosio.code权限还是eosio.msig都可以帮协助我们实现一套相对完善的系统或DAPP;这里将重点介绍多重签名的实现。

账户与权限

经过之前的使用已经知道了账户是指EOS Account,名称只能是base32字符集,现版本在账户建立时默认两个权限OwnerActive;这里就产生了一些个问题:

我们转账或者其他操作的权限到底是属于私钥还是账户?

EOS Account是公开的仅当做一个地址、一个代号,作为权限与私钥拥有者的一个代称,无法作为token或者其他权限的持有者。(严格说私钥也不一定能拥有对应的权限)

我的EOS币等资产到底由谁支配?

既不是由某个EOS Account支配也不是由某个私钥支配。账户下的资产归属对应权限,谁能开启此权限,谁就能动用此资产。
就是说,你可能知道了某个账号的一个私钥,但是你却无法利用它获利,因为你可能值拥有了必要权限的一部分。

eosio是什么账户?

eosio是一个特权账户,它是在eos源码编译时就已经定义好的账号(可以修改);
eosio.systemeosio.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,那他有两条路:

  1. 场外沟通,找到userA这个人跟他讲,让他提交这个命令2
  2. 想办法获取userA@active权限的私钥。第二个办法那只能各自想动脑筋去了。

很明显方式一有个缺陷,必须要场外沟通,而且交易命令还是由userA提交,userB只能从结果上得知命令是否成功或者有没有问题等。那eosio.msig合约就提供了这样一个权限申请:

  1. 由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
  1. userA在线上对userB的test提案内容查询后,发现没有问题,将自己的active权限授予test提案表明可以执行;
$ cleos multisig review userB test -p userA
$ cleos multisig approve userB test '{"actor": "userA", "permission": "active"}' -p userA
  1. 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合约权限申请的功能,如何实现多重签名?

在文章的前半部有提到权限的组成是多样化的,可以多人多密钥共同管理权限,那也就是说,在提案审批的时候,由于权重问题就需要不同用户来进行审批,即多重签名

权限构建

接下来看一个实现多重签名的例子:

首先创建三个账户testaaaa1111testaaaa1112testaaaa1113

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已经被移除,并使用testaaaa1112testaaaa1113两个账号共同拥有此权限。


创建提案

以下内容均不使用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,名为proposalapprovals,分别记录了提案对应的交易与提案当前权限申请的状况。
可以在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账户权限与多重签名