美文网首页
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