EOS智能合约开发系列(11): 多重签名

前面几篇文章中,我们学习了自定义许可权限,以及如何抵御彩虹攻击。其实,自定义许可权限还有一个非常重要的能力,多重签名。本文就来介绍一下多重签名的用法。

知识回顾

回忆一下我们前两篇文章中提到的bob和carl,我们为他们各创建了一个钱包,分别是bobwalletcarlwallet,他们各自的key就存储在里面。
后来,我们把testertrans许可授权给了bob和carl,也就是说,bob和carl可以使用tester的trans许可权限对交易进行签名。
注意,这里不是tester账户所有的交易都可以用trans许可权限签名的。
所谓交易,就是action消息的事务。转账交易实际上就是,向eosio.token合约发送一个transfer的action消息;eosio.token合约对transfer action进行处理,就完成了转账,我们之前专门介绍过eosio.token合约的源码,相信你应该对这一机制非常了解。
回到之前的话题,什么样的交易才能用trans许可进行签名呢?答案是,那些与trans 许可所关联的action交易,才可以用trans许可权限签名。

这里说的“用trans许可权限签名”又是什么意思呢?在这里例子中,就是用bob和carl的私钥进行签名了。如果用通用的语言解释,就是用trans许可所关联的key、或者所关联的账户的key,对交易进行签名。

我们之前也看到了,一个许可权限可以关联多个key,也可以关联多个账户,每个key和账户可以设置不同的权重。

上一篇中,我们给owner权限关联了两个key,每个key的权重都是1,threshold设置为2,这样,只有同时拥有这两个key的人才能够使用owner许可;从而增加了黑客彩虹攻击的难度,即便黑客破解了其中一个key,也无法使用owner许可。

多重签名的场景

本文将解决这样的一个场景问题:tester把自己账户的trans许可权限给了bob和carl,把他们各自的weight都设置为2,threshold 也是2,从而bob和carl中任何一个人都可以转出tester的资产。不过呢,tester名下资产比较多,他担心这样不安全,所以他想把trans许可改成,只有bob和carl同时同意签名转账交易,交易才能成功。

我们先看下之前设置的trans许可情况:

cleos get account tester
permissions:
     owner     2:    1 EOS7ufpQne6oXmanxvdoadmPkmfCT9rmtncML1tLRb7emgMrHgMBL, 1 EOS8UTyktNUn4afScvYqMJ9JumGA27X7qmwgYpscwDoEHeFVYjDgb
        active     1:    1 EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr
           trans     2:    2 bob@active, 2 carl@owner,
           vote     4:    3 EOS7LVbDSU3hMewm8F2sxXUNDFPCUmqNvpYJe4TCg58GJ9UW8bo3M, 1 EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr

可以看到我们设置的trans许可授权给了bob@activecarl@owner,他们的权重(weight)都是2,阀值(threshold)也是2 。

现在我们把它改为:授权给bob@activecarl@active,并且他们的权重都是1,threshold是2。这样只有他们俩的权重加在一起才等于threshold,也就是说,只有他们两个都签名了,trans 许可才能生效。

修改方法,和之前的类似:

cleos set account permission tester trans '{"threshold": 2, "accounts":[
  {
    "permission":
      {"actor":"bob","permission":"active"},
      "weight":1
  },
  {"permission":
      {"actor":"carl","permission":"active"},
      "weight":1}
]}' active -p tester@active

执行成功后,我们确认一下tester的trans许可:

~ cleos get account tester
permissions:
     owner     2:    1 EOS7ufpQne6oXmanxvdoadmPkmfCT9rmtncML1tLRb7emgMrHgMBL, 1 EOS8UTyktNUn4afScvYqMJ9JumGA27X7qmwgYpscwDoEHeFVYjDgb
        active     1:    1 EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr
           trans     2:    1 bob@active, 1 carl@active,
           vote     4:    3 EOS7LVbDSU3hMewm8F2sxXUNDFPCUmqNvpYJe4TCg58GJ9UW8bo3M, 1 EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr

可以看到和预想的一样。

试验一下

我们试试看,如果只用bob签名,看能否使用trans许可转账。
我们锁住其他的钱包,只解锁bobwallet,然后用trans 许可向eosio.token发送 transfer action。

~ cleos wallet lock_all
Locked All Wallets
~ cleos wallet unlock -n bobwallet
password: Unlocked: bobwallet
~ cleos push action eosio.token transfer \
        '[ "tester", "user", "0.5000 SYS", "some money" ]' -p tester@trans
Error 3090003: Provided keys, permissions, and delays do not satisfy declared authorizations
Ensure that you have the related private keys inside your wallet and your wallet is unlocked.

你看到了,提示权限不足。如果我们打开carlwallet 再试试呢?

~ cleos wallet unlock -n carlwallet
password: Unlocked: carlwallet

~ cleos push action eosio.token transfer \
        '[ "tester", "user", "0.5000 SYS", "some money" ]' -p tester@trans
executed transaction: ad643a390d6439c285d39c1587902e8de2f3a25de02d551da0fac3c051b3d8ca  152 bytes  3623 us
#   eosio.token <= eosio.token::transfer        {"from":"tester","to":"user","quantity":"0.5000 SYS","memo":"some money"}
#        tester <= eosio.token::transfer        {"from":"tester","to":"user","quantity":"0.5000 SYS","memo":"some money"}
#          user <= eosio.token::transfer        {"from":"tester","to":"user","quantity":"0.5000 SYS","memo":"some money"}

成功了,对吧。但这不符合我们的场景,我们的场景是说,不存在一个人同时有bob和carl的钱包,我们不能是bob和carl的合体,对吧。

那我们怎么实现bob和carl同时对这个交易签名呢?这就是多重签名(multisig)的作用了。

部署多重签名合约

在开始之前,我们需要在我们自己的测试节点上部署eosio.msig合约(在主网和jungle测试网络上,已经部署好了,所以,如果你使用主网或者jungle测试网络,可以直接使用这个合约)。

部署合约的方法也非常简单,和我们之前部署eosio.token类似,本着一账户一个合约的原则,我们先创建eosio.msig账户,然后再部署eosio.msig合约。

创建eosio.msig账号:

~ cleos create account eosio eosio.msig  \
        EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr \
        EOS83sN8bfKGk3jTBezN41UN7LfXSVFa1w3YQcGApE67J26t3HLcr

部署eosio.msig合约,我们在编译完成eos源码之后,合约目标文件会自动生成,在eos源码目录的build/contracts/eosio.msig下面。

~ cd ~/eos/build/contracts
~ cleos set contract eosio.msig ./eosio.msig -p eosio.msig@active
Reading WAST/WASM from ./eosio.msig/eosio.msig.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: d121e31ae3e0152b6431f4de1eb724d2c836f21b7c669ead1ea31d18a4345326  8864 bytes  19510 us
#         eosio <= eosio::setcode               {"account":"eosio.msig","vmtype":0,"vmversion":0,"code":"0061736d010000000198011760017f0060047f7e7e7...
#         eosio <= eosio::setabi                {"account":"eosio.msig","abi":"0e656f73696f3a3a6162692f312e30030c6163636f756e745f6e616d65046e616d650...

部署成功!

多重签名

我们想模拟的情况是,bob和carl是独立的个体,他们互不知道对方的key。而我们还要实现他们两个对同一个交易进行签名,这怎么办呢?

多重签名给了我们一个流程:

  1. 首先有一个转账发起人,假设是bob吧,它提出一个transfer交易的提案:
➜  ~ cleos wallet lock_all
Locked All Wallets
➜  ~ cleos wallet unlock -n bobwallet
password: Unlocked: bobwallet
➜  ~ cleos multisig propose payuser '[{"actor": "bob", "permission": "active"},{"actor": "carl", "permission": "active"}]' '[{"actor": "tester", "permission": "trans"}]' eosio.token transfer '{"from":"tester", "to":"user", "quantity":"0.0005 SYS", "memo":"pay user some money"}' -p bob@active

executed transaction: 507346a3a782f0fa82cbb5b7e3a93d8a86caac0b2d273dd9b29cc6795a431a7e  248 bytes  2442 us
#    eosio.msig <= eosio.msig::propose          {"proposer":"bob","proposal_name":"payuser","requested":[{"actor":"bob","permission":"active"},{"act...

我们先把所有的钱包都锁住,只打开bobwallet,这样可以完全模拟发起人是bob的情形。目前为止,bob发起提案成功。

发起提案的命令格式是这样的:

cleos multisig propose <proposal-name> < requested_permissions> <trx_permissions> <contract> <action> <data> -p accountname@permission

proposal-name: 就是提案的名字了, 它与发起人账户一起唯一标识这个提案,本例中,我们的提案名是payuser
requested_permission: 代表,要执行本提案中的交易所需要的各个参与方的权限
trx_permissions: 代表本提案中的交易在什么许可下执行。本例中,该许可,是tester账户的trans许可。
contract 与 action: 本提案中的交易执行的是哪个合约的哪个action
data: 是对应的action所需要的参数
-p accountname@permission: 是发起当前提案的许可权限

本例中,bob是发起人,同时该提案也需要bob的active权限;其实发起人也可以不是所发起提案的权限的参与方,也就是说,任何一个人都可以发起任何形式的提案,这种情况我就不再这里演示了。

  1. bob把提案的名字告诉carl,carl可以审查一下这个提案的内容:
~ cleos multisig review bob payuser
{
  "proposal_name": "payuser",
  "packed_transaction": "c9b17f5b000000000000000000000100a6823403ea3055000000572d3ccdcd01000000005c95b1ca00000000003ccdcd34000000005c95b1ca00000000007015d60500000000000000045359530000000013706179207573657220736f6d65206d6f6e657900",
  "transaction": {
    "expiration": "2018-08-24T07:20:41",
    "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": "tester",
            "permission": "trans"
          }
        ],
        "data": {
          "from": "tester",
          "to": "user",
          "quantity": "0.0005 SYS",
          "memo": "pay user some money"
        },
        "hex_data": "000000005c95b1ca00000000007015d60500000000000000045359530000000013706179207573657220736f6d65206d6f6e6579"
      }
    ],
    "transaction_extensions": []
  }
}

这个命令的格式是:

cleos multisig review <proposer> <proposal-name>

proposer: 提案发起人,本例中是bob
proposal-name: 提案的名字,本例中是payuser

你发现了,这个命令不需要权限,也就是说,任何人只要知道发起人和提案名,就可以查看该提案的内容。

carl从这个提案中,看到要从tester账户中转出0.0005 SYS 到 user账户,carl觉得该提案没问题,就同意了,于是他做了下面这个操作。

签署提案

为了模拟carl签署提案的过程,我们先锁住所有的钱包,只打开carlwallet:

~ cleos wallet lock_all
Locked All Wallets
~ cleos wallet unlock -n carlwallet
password: Unlocked: carlwallet
~ cleos multisig approve bob payuser '{"actor":"carl", "permission":"active"}' -p carl@active
executed transaction: 4440f20256399c8f11f5365e2d514d7f05ef0bca25914973266ca7ea75a3e813  128 bytes  3972 us
#    eosio.msig <= eosio.msig::approve          {"proposer":"bob","proposal_name":"payuser","level":{"actor":"carl","permission":"active"}}

可以看到我们签署成功。这个命令的格式是:

cleos multisig approve <proposer> <proposal-name> <provided-permission> -p accountname@permission

proposer与proposal-name: 提案发起人,和提案的名字
provided-permission:本次签署所提供的权限
-p accountname@permission: 代表本次签署交易以什么许可执行,这个许可必须有权力提供provided-permission的权限

bob签署提案,并执行

bob用同样的方法签署:

~ cleos wallet lock_all
Locked All Wallets
~ cleos wallet unlock -n bobwallet
password: Unlocked: bobwallet

~ cleos multisig approve bob payuser '{"actor":"bob", "permission":"active"}' -p bob@active
executed transaction: b850d7c3a6674e03112f40f8c91c79cd9cd496c8a9900782fb4e4ff007d06fac  128 bytes  5158 us
#    eosio.msig <= eosio.msig::approve          {"proposer":"bob","proposal_name":"payuser","level":{"actor":"bob","permission":"active"}}

我们也是在只有bobwallet解锁的情况下签署的。下面我以bob的身份来执行这个多签合约:

 ~ cleos multisig exec bob payuser -p bob@active
Error 3090003: Provided keys, permissions, and delays do not satisfy declared authorizations
Ensure that you have the related private keys inside your wallet and your wallet is unlocked.

这个命令的比较简单,就不多解释。你应该发现,我这里执行失败了,我查下nodeos的log,发现了这样的内容:

3090003 unsatisfied_authorization: Provided keys, permissions, and delays do not satisfy declared authorizations
transaction declares authority '{"actor":"tester","permission":"trans"}', but does not have signatures for it under a provided delay of 0 ms, provided permissions [{"actor":"eosio.msig","permission":"eosio.code"}], and provided keys []
    {"auth":{"actor":"tester","permission":"trans"},"provided_delay":0,"provided_permissions":[{"actor":"eosio.msig","permission":"eosio.code"}],"provided_keys":[],"delay_max_limit_ms":3888000000}

这是啥意思呢?意思就是说multisig没有权限执行tester@trans许可,这是自然的。如果随便部署一个合约都能执行别的账户的许可,这就是天大的安全问题。所以我们需要给这个multisig一个系统权限。注意,我们现在用的自测的nodeos节点,部署的时候没有给它系统权限,主网和测试网络上的eosio.multisig合约都已经拥有了系统权限。

我们执行这样一个命令就好了:

cleos push action eosio setpriv '["eosio.msig", 1]' -p eosio

这个命令的意思是说,给eosio合约发个setpriv action,参数是 '["eosio.msig", 1]' ,以后我们分析系统合约的时候,再详细看下它的代码实现。

好,现在我们再去执行这个多重签名提案:

~  cleos multisig exec bob payuser -p bob@active
executed transaction: 689fa2757f35c1f1a6d6fcb600fda3eda2a3abf11d7f2bf8fdf2565942e0ab57  160 bytes  6062 us
#    eosio.msig <= eosio.msig::exec             {"proposer":"bob","proposal_name":"payuser","executer":"bob"}

这次很顺利就成功了。

注意:

  1. 本例中,我们是以bob发起的提案,bob本身也是该提案的签署方,其实这个提案谁发起都无所谓,与是不是该提案的签署方没有任何关系。
  2. 本例中,过程是这样的:bob发起提案,carl签署,然后bob再签署,然后bob执行。实际上,bob和carl的签署顺序对结果没有任何影响,而且不一定非要bob来执行,carl也可以执行,甚至其他的人,一个不相干的人,只要提案被签署通过,任何人都可以执行。
  3. 本例中,bob@activecarl@active的权重都是1,而threshold是2,如果提案仅有他们中的一人签署,那么该提案是无法执行的;我这里没有演示这种情况,你可以自行试验一下。

多重签名的其他命令

多重签名还有两个命令:

  1. 撤销签署
cleos multisig unapprove bob payuser '{"actor":"carl", "permission":"active"}' -p carl@active

这个命令与签署命令的形式类似。撤销签署,只有在提案执行前才有意义。如果已经执行,就撤销不掉。
另外,只有签署人自己才能撤销自己的签署。

  1. 取消提案
cleos multisig cancel bob payuser -p bob@active

这个命令也比较简单,不过有两点需要注意:

  1. 只有提案发起人本人可以撤销提案
  2. 在提案发起后到提案执行前这段时间里,发起人都可以撤销提案;也就是说,即便提案已经被签署了,只要还没有执行,就可以被撤销。

好了,今天挺多内容,就到这里。
简介:不羁,一名程序员;专研EOS技术,玩转EOS智能合约开发。
微信公众号:know_it_well
知识星球地址:https://t.zsxq.com/QvbuzFM

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容