免责声明:金色财经所有资讯仅代表作者个人观点,不构成任何投资理财建议。请确保访问网址为(jinse.cn) 举报

    EOS合约开发之合约权限短暂移交的实现

    本文来源:天算Delphy技术团队/johnathon

    EOS合约开发之合约权限短暂移交的实现

    说起智能合约,大家一般都会想到其不可篡改的特性。合约一旦部署便永不可更改,是以太坊智能合约的一大优点。正因为这个特点,大家才可以放心地使用,而不用担心合约开发者跑路。

    在许多合约里,这个特性非常重要。然而,对另一些应用来说,尽管可以让用户用得放心,代码不可修改也为开发带来了很大的麻烦。在以太坊中,我们可以通过将合约模块化,并更新主合约中记录的子合约地址来实现代码更新。

    在EOS中,情况便发生了180°大转变。默认情况下,合约账户可以随时修改代码而不受任何约束。换而言之,即便用户在这一刻验证过合约源码无误,下一秒,合约便可以修改代码从而实现任何操作。

    这对于涉及到钱的应用来说是个大忌。由于代码可以变更,用户不放心把钱转入该合约,从而使得用户使用的门槛提升不少。但是,另一方面,如果把active权限设定为合约本身,使代码绝对不可更改,如果代码出现了漏洞,又或者在功能上需要更新时,迁移用户至新的合约又十分麻烦。

    综上所述,每个合约里都应该有一个权限短暂移交的机制。这个机制能保证合约在一定的时间内绝对不可被更改,而且合约的所有者可以随时延长这个时间。只有当这个时间结束时,合约所有者才可以重新掌握合约的代码更改权限。

    举个例子,一个游戏的合约里应该有一个机制,使得游戏开始前,所有者可以移交权限。而只有在一轮游戏结束后,所有者才可以进行代码更新。这个机制可以大大降低用户参与的信任门槛,而又不会失去合约更新的能力。

    按照这个思路,代码上可以有很多方法实现,这里介绍一个简单实现。完整的源码已经在GitHub上发布:

    GitHub - xJonathanLEI/eosyield

    合约接口

    首先,按照上述的思路,我们至少需要以下几个接口:

    setowner:

    合约本身以及现有owner都可以调用这个接口来设置合约所有者。

    yieldcontrol:

    只有owner本身可以调用,重设合约的owner和active权限。

    extend:

    延长移交时限,只有owner可以调用。除非代码在短期内需要更新,合约所有者应该经常调用这个接口来保证接下来的一段时间内,合约不可被篡改。

    regain:

    移交时限结束后,合约owner可以通过这个接口,重新掌握合约的owner和active权限。

    数据表

    接下来设计一个表,用来存储权限移交相关的数据。在EOS智能合约中,一般会使用multi_index类来承载数据。然而,在我们这个合约中,由于只有一条记录,使用singleton会更加方便。

    singleton实际上是对multi_index类的一个简单封装。它将表名本身作为主键存储了一条记录,背后用的实际上还是multi_index。详情可以查阅EOS合约库源码:

    eosiolib/singleton.hpp

    我们需要在数据表中存储当前的owner以及移交期限,定义为以下字段:

    struct yield_info{     account_name owner;     time_point_sec expiration;};

    其中,time_point_sec是一个对uint32_t的封装,代表从Epoch到当前时间的秒数,精度为1秒,向下取整。

    expiration字段为0时表示权限未被移交,否则其代表移交结束时间的时间戳。

    接口实现

    首先在合约中加入yield变量表示数据表记录:

    class eosyield : public contract{     eosyield(account_name self) : contract(self), yield(self, self) {}

        typedef singleton<N(yieldinfo), yield_info> tbl_yield;     tbl_yield yield;}

    setowner:

    接口接受一个类型为account_name的参数,代表新的owner账户名:

     void setowner(account_name new_owner);

    合约本身以及现有的owner都可以设定新的owner。即便是合约权限已经移交,owner也可以被重设:

    yield_info new_info{     new_owner,     time_point_sec(0)};yield.set(new_info, _self);

    yieldcontrol:

    只有owner可以调用该接口,调用时需要提供移交期限的秒数:

    void yieldcontrol(uint32_t yield_seconds);

    检查合约现有状态以及参数后,将合约的owner以及active权限都设置为eosio.code本身:

    // 定义账户权限authority owner_active = authority{     .threshold = 1,     .keys = {},     .accounts = {         permission_level_weight{             .permission = {_self, N(eosio.code)},             .weight = 1}},     .waits = {}};// 变更owner权限action(permission_level(_self, N(owner)), N(eosio), N(updateauth), updateauth_args{_self, N(active), N(owner), owner_active}).send();// 变更active权限action(permission_level(_self, N(owner)), N(eosio), N(updateauth), updateauth_args{_self, N(owner), 0, owner_active}).send();

    extend:

    该接口接受一个新的参数,代表新的移交期限秒数。设定的新移交期限必须比现有的移交期限更晚:

    void extend(uint32_t new_yield_seconds);

    regain:

    只有owner可以调用该接口,接口不接受任何参数。成功调用后,合约的权限会被重设为合约本身的eosio.code权限以及owner的active权限:

    void regain();

    值得注意的是,EOS中对于权限中的accounts字段有排序要求。由于我们正在设定超过一个账户的权限,我们必须先对这两个权限进行排序,否则有可能会报错,导致永远无法重获账户权限。

    首先定义两个权限:

    // 定义合约eosio.code权限permission_level_weight contract_permission{     .permission = {_self, N(eosio.code)},     .weight = 1};// 定义owner的active权限permission_level_weight owner_permission{     .permission = {info.owner, N(active)},     .weight = 1};

    然后对两个权限进行排序:

    vector<permission_level_weight> accounts;if (std::tie(contract_permission.permission.actor, contract_permission.permission.permission) < std::tie(owner_permission.permission.actor, owner_permission.permission.permission))     accounts = {contract_permission, owner_permission};else     accounts = {owner_permission, contract_permission};authority owner_active = authority{     .threshold = 1,     .keys = {},     .accounts = accounts,     .waits = {}};

    最后再和yieldcontrol接口中一样进行权限变更。

    合约测试

    按照GitHub中指引的步骤进行部署后,过程中查询账户权限,可以看到其变化。

    调用yieldcontrol之后:

    [     {         "perm_name": "active",         "parent": "owner",         "required_auth": {             "threshold": 1,             "keys": [],             "accounts": [                 {                     "permission": {                         "actor": "yield",                         "permission": "eosio.code"                     },                     "weight": 1                 }             ],             "waits": []         }     },     {         "perm_name": "owner",         "parent": "",         "required_auth": {             "threshold": 1,             "keys": [],             "accounts": [                 {                     "permission": {                         "actor": "yield",                         "permission": "eosio.code"                     },                     "weight": 1                 }             ],             "waits": []         }     }]

    调用regain之后:

    [     {         "perm_name": "active",         "parent": "owner",         "required_auth": {             "threshold": 1,             "keys": [],             "accounts": [                 {                     "permission": {                         "actor": "jonathan",                         "permission": "active"                     },                     "weight": 1                 },                 {                     "permission": {                         "actor": "yield",                         "permission": "eosio.code"                     },                     "weight": 1                 }             ],             "waits": []         }     },     {         "perm_name": "owner",         "parent": "",         "required_auth": {             "threshold": 1,             "keys": [],             "accounts": [                 {                     "permission": {                         "actor": "jonathan",                         "permission": "active"                     },                     "weight": 1                 },                 {                     "permission": {                         "actor": "yield",                         "permission": "eosio.code"                     },                     "weight": 1                 }             ],             "waits": []         }     }]

    可见,在regain之前,没有任何人拥有对合约的修改权限。

    以上便是整个合约的实现思路,完整的源码以及部署指引请参见GitHub页面。合约权限短暂移交对开发者和用户都有很多好处,希望可以看到类似的权限架构在更多的合约中实现。 

    jinse.cn 0
    好文章,需要你的鼓励
    jinse.cn 0
    好文章,需要你的鼓励
    参与评论
    0/140
    提交评论
    文章作者: / 责任编辑:

    声明:本文由入驻金色财经的作者撰写,观点仅代表作者本人,绝不代表金色财经赞同其观点或证实其描述。

    提示:投资有风险,入市须谨慎。本资讯不作为投资理财建议。

    金色财经 > 天算Delphy > EOS合约开发之合约权限短暂移交的实现
    • 寻求报道
    • 金色财经中国版App下载
      金色财经APP
      iOS & Android
    • 加入社群
      Telegram
    • 意见反馈
    • 返回顶部
    • 返回底部