eosio.msig合约源码分析

eosio.msig

接口

  • propose()
  • propose(account_name proposer, name proposal_name, vector<permission_level> requested, transaction trx)
  • approve(account_name proposer, name proposal_name, permission_level level)
  • unapprove(account_name proposer, name proposal_name, permission_level level)
  • cancel(account_name proposer, name proposal_name, account_name canceler)
  • exec(account_name proposer, name proposal_name, account_name executer)

数据结构

struct proposal {
	name                       proposal_name;
	vector<char>               packed_transaction;

	auto primary_key()const { return proposal_name.value; }
};
typedef eosio::multi_index<N(proposal),proposal> proposals;

struct approvals_info {
	name                       proposal_name;
	vector<permission_level>   requested_approvals;
    vector<permission_level>   provided_approvals;

    auto primary_key()const { return proposal_name.value; }
};
typedef eosio::multi_index<N(approvals),approvals_info> approvals;

分析

propose

该接口会在合约接口实现中分析输入数据(而不是在系统的分发器),因为如果在交易数据很大,在系统分发器分析数据会消耗非常多的CPU。因为这样我们看到propose接口的函数声明是

propose()

如果使用系统的分发器,那么接口的函数声明应该是

propose(account_name proposer, name proposal_name, vector<permission_level> requested, transaction trx)

  1. propose首先会通过调用read_action_data读取传入的action数据
constexpr size_t max_stack_buffer_size = 512;
size_t size = action_data_size();
char* buffer = (char*)( max_stack_buffer_size < size ? malloc(size) : alloca(size) );
read_action_data( buffer, size );
  1. 然后通过数据流datastream<const char*>反序列化参数数据
account_name proposer;
name proposal_name;
vector<permission_level> requested;
transaction_header trx_header;

datastream<const char*> ds( buffer, size );
ds >> proposer >> proposal_name >> requested;

size_t trx_pos = ds.tellp();
ds >> trx_header;
  1. 接着,要求poster的授权,检测提交方案的合约数据的时限是否还有效和提案是否重复
require_auth( proposer );
eosio_assert( trx_header.expiration >= eosio::time_point_sec(now()), "transaction expired" );

proposals proptable( _self, proposer );
eosio_assert( proptable.find( proposal_name ) == proptable.end(), "proposal with the same name exists" );
  1. 校验交易是否存在requested中指定的授权。
bytes packed_requested = pack(requested);
auto res = ::check_transaction_authorization( buffer+trx_pos, size-trx_pos,
                                              (const char*)0, 0,
                                              packed_requested.data(), 
                                              packed_requested.size()
                                              );
eosio_assert( res > 0, "transaction authorization failed" );
  1. 存储提案交易数据到持久化数据表
proptable.emplace( proposer, [&]( auto& prop ) {
   prop.proposal_name       = proposal_name;
   prop.packed_transaction  = bytes( buffer+trx_pos, buffer+size );
});

approvals apptable(  _self, proposer );
apptable.emplace( proposer, [&]( auto& a ) {
   a.proposal_name       = proposal_name;
   a.requested_approvals = std::move(requested);
});

approve

  1. 校验提案是否存在
  2. 校验提供的授权是否是需要的授权
  3. 添加授权到提案持久化数据与去除需要的对应授权。
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 );
});

unapprove

approve的逆操作。

cancel

任何人都可以取消提案,但是只有提案的提出者可以在提案有效期内中途取消提案。

// ...
if( canceler != proposer ) {
   eosio_assert( unpack<transaction_header>( prop.packed_transaction ).expiration < eosio::time_point_sec(now()), "cannot cancel until expiration" );
}
// ...
proptable.erase(prop);
apptable.erase(apps);

exec

任何人都可以执行已经通过的提案。
提案只要被任何一个人执行,就会清除唯一的持久化记录。
提案是存在时效性的,过期的提案不可以被执行。
提案作为延时交易(deferred transaction)被执行,无论提案执行成功与否,持久化的提案记录都会被删除。

require_auth( executer );
// ...
eosio_assert( trx_header.expiration >= eosio::time_point_sec(now()), "transaction expired" );
// ...
send_deferred( (uint128_t(proposer) << 64) | proposal_name, executer, prop.packed_transaction.data(), prop.packed_transaction.size() );

proptable.erase(prop);
apptable.erase(apps);

猜你喜欢

转载自blog.csdn.net/bnbjin/article/details/83928973