美文网首页
EOS智能合约开发系列(13): 多签合约代码分析(二)

EOS智能合约开发系列(13): 多签合约代码分析(二)

作者: 鹏飞_3870 | 来源:发表于2018-08-25 07:20 被阅读0次

    简介: 昨天我们分析了eosio.msig合约的头文件,以及propose方法,今天继续分析其他方法。

    代码在eos源码目录的contracts/eosio.msig/文件夹下面。

    multisig::approve方法:

    对比一下之前文章中,我们用carlbob举例子所使用的命令:

    cleos multisig approve bob payuser '{"actor":"bob", "permission":"active"}' -p bob@active
    

    源码:

    void multisig::approve( account_name proposer, name proposal_name, permission_level level ) {
       require_auth( level );
    
       approvals apptable(  _self, proposer );
       auto& apps = apptable.get( proposal_name, "proposal not found" );
    
       auto itr = std::find( apps.requested_approvals.begin(), apps.requested_approvals.end(), level );
       eosio_assert( itr != apps.requested_approvals.end(), "approval is not on the list of requested approvals" );
    
       apptable.modify( apps, proposer, [&]( auto& a ) {
          a.provided_approvals.push_back( level );
          a.requested_approvals.erase( itr );
       });
    }
    

    approve方法,因为参数比较简单,没有像上一篇文章中分析的propose方法那样,在方法内部自己解析参数。

    在上面命令的情况下,可以看出,这里传入的proposer就是bob, proposal_namepayuserlevel就是{"actor":"bob", "permission":"active"}

    方法内部,第一条语句:

    require_auth( level );
    

    意思是,检查当前触发此action的账户是否有level所指定的权限,如果没有,则中止并回滚当前事务。

       approvals apptable(  _self, proposer );
       auto& apps = apptable.get( proposal_name, "proposal not found" );
    

    这里创建了一个table,然后利用get方法,从table中取出提案。你可能会疑惑:“为什么刚创建的table就能有数据?”

    其实table相当于给了我们一组操作持久化数据的接口,底层是数据库支持的。我们这里的table不是数据的集合本身,可以被认为是一组操作数据库的接口;这里说的创建table,其实就是初始化这样一组接口。_self, proposer作为初始化参数,告诉这组接口,我们后续的操作是针对于哪个存储表的。

    我们之前没有对表的操作接口做详细讲解。这里我们看到了get 接口,它有两个参数,第一个参数是主键,第二个参数是找对到对应的记录时,打印出来的错误信息。如果找不到,当前执行的事务中止并回滚。如果成功,则返回这条记录。还记得approvals表记录的类型吗?

            struct approvals_info {
                name                       proposal_name;
                vector<permission_level>   requested_approvals;
                vector<permission_level>   provided_approvals;
    
                auto primary_key()const { return proposal_name.value; }
             };
    

    继续看下面的代码:

    auto itr = std::find( apps.requested_approvals.begin(), apps.requested_approvals.end(), level );
    eosio_assert( itr != apps.requested_approvals.end(), "approval is not on the list of requested approvals" );
    apptable.modify( apps, proposer, [&]( auto& a ) {
          a.provided_approvals.push_back( level );
          a.requested_approvals.erase( itr );
       });
    

    从本提案的请求的签署权限列表中,查找与当前签署的level权限一致的权限,并把它从请求签署权限列表(requested_approvals)中删除,然后加入到已签署权限列表(provided_approvals)中。
    这里面用到了C++标准模板库(STL)中的vector数据结构和std::find方法,这里就不多介绍。本系列只介绍EOS相关的知识。如果对C++方面还不熟,请随时查阅C++的相关书籍或者网上搜索相关资料学习。

    multisig::unapprove方法

    把下面的源码,对比一下这个命令:

    cleos multisig unapprove bob payuser '{"actor":"carl", "permission":"active"}' -p bob@active
    

    源码:

    void multisig::unapprove( account_name proposer, name proposal_name, permission_level level ) {
       require_auth( level );
    
       approvals apptable(  _self, proposer );
       auto& apps = apptable.get( proposal_name, "proposal not found" );
       auto itr = std::find( apps.provided_approvals.begin(), apps.provided_approvals.end(), level );
       eosio_assert( itr != apps.provided_approvals.end(), "no approval previously granted" );
    
       apptable.modify( apps, proposer, [&]( auto& a ) {
          a.requested_approvals.push_back(level);
          a.provided_approvals.erase(itr);
       });
    }
    

    对比一下approve源码,你是不是发现非常相似,不同的地方在于,这个unapprove 是把level权限从已签署列表(provided_approvals)中删除,然后添加到请求签署列表(requested_approvals)中。

    multisig::cancel

    这个方法也比较简单,分别从proposalsapprovalstable中删除相关的提案信息即可。具体参看源码:

    
    void multisig::cancel( account_name proposer, name proposal_name, account_name canceler ) {
       require_auth( canceler );
    
       proposals proptable( _self, proposer );
       auto& prop = proptable.get( proposal_name, "proposal not found" );
    
       if( canceler != proposer ) {
          eosio_assert( unpack<transaction_header>( prop.packed_transaction ).expiration < eosio::time_point_sec(now()), "cannot cancel until expiration" );
       }
    
       approvals apptable(  _self, proposer );
       auto& apps = apptable.get( proposal_name, "proposal not found" );
    
       proptable.erase(prop);
       apptable.erase(apps);
    }
    
    

    需要注意的地方只有一点:

    中间有一条语句判断,如果取消的人与proposer不是同一个人,则中止此事务,也就是不做取消操作

    multisig::exec

    看下exec的命令形式:

    cleos multisig exec bob payuser -p user@active
    

    源码:

    
    void multisig::exec( account_name proposer, name proposal_name, account_name executer ) {
       require_auth( executer );
    
       proposals proptable( _self, proposer );
       auto& prop = proptable.get( proposal_name, "proposal not found" );
    
       approvals apptable(  _self, proposer );
       auto& apps = apptable.get( proposal_name, "proposal not found" );
    
       transaction_header trx_header;
       datastream<const char*> ds( prop.packed_transaction.data(), prop.packed_transaction.size() );
       ds >> trx_header;
       eosio_assert( trx_header.expiration >= eosio::time_point_sec(now()), "transaction expired" );
    
       bytes packed_provided_approvals = pack(apps.provided_approvals);
       auto res = ::check_transaction_authorization( prop.packed_transaction.data(), prop.packed_transaction.size(),
                                                     (const char*)0, 0,
                                                     packed_provided_approvals.data(), packed_provided_approvals.size()
                                                   );
       eosio_assert( res > 0, "transaction authorization failed" );
    
       send_deferred( (uint128_t(proposer) << 64) | proposal_name, executer, prop.packed_transaction.data(), prop.packed_transaction.size() );
    
       proptable.erase(prop);
       apptable.erase(apps);
    }
    

    有如下几点需要注意一下:

    1. 只检查了当前的action的权限中否有当前的executer,但是没有限制executer的身份。也就是说,任何一个人,只要用了自己的签名就可以发起exec的action。
    2. 对已签署许可是否满足所需许可的检查,是在::check_transaction_authorization里面的检查的,这个方法已经不止一次用到。
    3. 提案的交易事务,是以deferred action来执行的,我们以后把它翻译成延迟的action或者异步的action吧。关于inline actiondeferred action的区别,我们还没有介绍,以后找个专题来介绍吧。

    今天就到这里吧,以后想详细解释下require_auth,以及inline action 和 deferred action


    简介:不羁,一名程序员;专研EOS技术,玩转EOS智能合约开发。
    微信公众号:know_it_well
    知识星球地址:https://t.zsxq.com/QvbuzFM

    相关文章

      网友评论

          本文标题:EOS智能合约开发系列(13): 多签合约代码分析(二)

          本文链接:https://www.haomeiwen.com/subject/etciiftx.html