bitshares研究系列【交易签名】

in #bitshares6 years ago

通过以前的一些文章,我们知道Bitshares中operation都需要签名,也了解到签名一般都使用公私钥,看起来Bitshares签名有不少代码,到底都做了些什么呢?

对公私钥有兴趣的请google非对称加密、RSA、公私钥等

需要的签名公钥

在operation函数最后,一般都会调用

      return sign_transaction( tx, broadcast );

看看sign_transaction()函数:

   signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false)
   {
      set<public_key_type> pks = _remote_db->get_potential_signatures( tx );
      flat_set<public_key_type> owned_keys;
      owned_keys.reserve( pks.size() );
      std::copy_if( pks.begin(), pks.end(), std::inserter(owned_keys, owned_keys.end()),
                    [this](const public_key_type& pk){ return _keys.find(pk) != _keys.end(); } );
      set<public_key_type> approving_key_set = _remote_db->get_required_signatures( tx, owned_keys );

      auto dyn_props = get_dynamic_global_properties();
      tx.set_reference_block( dyn_props.head_block_id );

      // first, some bookkeeping, expire old items from _recently_generated_transactions
      // since transactions include the head block id, we just need the index for keeping transactions unique
      // when there are multiple transactions in the same block.  choose a time period that should be at
      // least one block long, even in the worst case.  2 minutes ought to be plenty.
      fc::time_point_sec oldest_transaction_ids_to_track(dyn_props.time - fc::minutes(2));
      auto oldest_transaction_record_iter = _recently_generated_transactions.get<timestamp_index>().lower_bound(oldest_transaction_ids_to_track);
      auto begin_iter = _recently_generated_transactions.get<timestamp_index>().begin();
      _recently_generated_transactions.get<timestamp_index>().erase(begin_iter, oldest_transaction_record_iter);

      uint32_t expiration_time_offset = 0;
      for (;;)
      {
         tx.set_expiration( dyn_props.time + fc::seconds(30 + expiration_time_offset) );
         tx.signatures.clear();

         for( const public_key_type& key : approving_key_set )
            tx.sign( get_private_key(key), _chain_id );

         graphene::chain::transaction_id_type this_transaction_id = tx.id();
         auto iter = _recently_generated_transactions.find(this_transaction_id);
         if (iter == _recently_generated_transactions.end())
         {
            // we haven't generated this transaction before, the usual case
            recently_generated_transaction_record this_transaction_record;
            this_transaction_record.generation_time = dyn_props.time;
            this_transaction_record.transaction_id = this_transaction_id;
            _recently_generated_transactions.insert(this_transaction_record);
            break;
         }

         // else we've generated a dupe, increment expiration time and re-sign it
         ++expiration_time_offset;
      }
      ...
      return tx;
   }

看起来是通过 get_potential_signatures() 函数先取到可能的公钥,然后再通过 get_required_signatures() 取到需要签名的公钥,再填充交易块信息等签名后发送。

没明白怎么通过交易取到可能的签名公钥,继续看下这个函数:

set<public_key_type> database_api_impl::get_potential_signatures( const signed_transaction& trx )const
{
   set<public_key_type> result;
   trx.get_required_signatures(
      _db.get_chain_id(),
      flat_set<public_key_type>(),
      [&]( account_id_type id )
      {
         const auto& auth = id(_db).active;
         for( const auto& k : auth.get_keys() )
            result.insert(k);
         return &auth;
      },
      [&]( account_id_type id )
      {
         const auto& auth = id(_db).owner;
         for( const auto& k : auth.get_keys() )
            result.insert(k);
         return &auth;
      },
      _db.get_global_properties().parameters.max_authority_depth
   );

   // Insert keys in required "other" authories
   flat_set<account_id_type> required_active;
   flat_set<account_id_type> required_owner;
   vector<authority> other;
   trx.get_required_authorities( required_active, required_owner, other );
   for( const auto& auth : other )
      for( const auto& key : auth.get_keys() )
         result.insert( key );

   return result;
}

函数内部又调用交易的 get_required_signatures() 函数:

set<public_key_type> signed_transaction::get_required_signatures(
   const chain_id_type& chain_id,
   const flat_set<public_key_type>& available_keys,
   const std::function<const authority*(account_id_type)>& get_active,
   const std::function<const authority*(account_id_type)>& get_owner,
   uint32_t max_recursion_depth )const
{
   flat_set<account_id_type> required_active;
   flat_set<account_id_type> required_owner;
   vector<authority> other;
   get_required_authorities( required_active, required_owner, other );

   flat_set<public_key_type> signature_keys = get_signature_keys( chain_id );
   sign_state s( signature_keys, get_active, available_keys );
   s.max_recursion = max_recursion_depth;

   for( const auto& auth : other )
      s.check_authority(&auth);
   for( auto& owner : required_owner )
      s.check_authority( get_owner( owner ) );
   for( auto& active : required_active )
      s.check_authority( active ) || s.check_authority( get_owner( active ) );

   s.remove_unused_signatures();

   set<public_key_type> result;

   for( auto& provided_sig : s.provided_signatures )
      if( available_keys.find( provided_sig.first ) != available_keys.end()
            && signature_keys.find( provided_sig.first ) == signature_keys.end() )
         result.insert( provided_sig.first );

   return result;
}

get_required_signatures函数有5个参数:链id、公钥set、get_active函数、get_owner函数、最大深度。

sign_state 用来存储签名状态,构造函数传入3个参数:已签名key、get_active函数、有效keys。这个函数后面一部分都是使用sign_state对象处理逻辑。

check_authority()函数可以判断出是否已有足够的key签名。

又会调用到 get_required_authorities() 函数,而此函数内部通过调用 operation_get_required_authorities() 得到需要的认证key。

void transaction::get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const
{
   for( const auto& op : operations )
      operation_get_required_authorities( op, active, owner, other );
   for( const auto& account : owner )
      active.erase( account );
}

void operation_get_required_authorities( const operation& op, 
                                         flat_set<account_id_type>& active,
                                         flat_set<account_id_type>& owner,
                                         vector<authority>&  other )
{
   op.visit( operation_get_required_auth( active, owner, other ) );
}

当前交易所带的operations必须大于0个,怎么通过 operation_get_required_authorities() 取得需要的认证key?

operation_get_required_auth 是一个如下的类定义:

struct operation_get_required_auth
{
   typedef void result_type;

   flat_set<account_id_type>& active;
   flat_set<account_id_type>& owner;
   vector<authority>&         other;


   operation_get_required_auth( flat_set<account_id_type>& a,
     flat_set<account_id_type>& own,
     vector<authority>&  oth ):active(a),owner(own),other(oth){}

   template<typename T>
   void operator()( const T& v )const 
   { 
      active.insert( v.fee_payer() );
      v.get_required_active_authorities( active ); 
      v.get_required_owner_authorities( owner ); 
      v.get_required_authorities( other );
   }
};

此处的v就是一个operation,active key会插入每个operation的fee_payer,这个fee_payer()函数是每个operation单独定义的。

从代码中看get_required_xxx也只有account_create_operation、account_update_operation、proposal_update_operation等少数几个operation有单独的处理,而缺省的operation并没有什么操作。那就是一般情况下,active key中会有操作的付费帐号。

operation特别指定

get_required_active_authorities

get_required_owner_authorities

account_update_operation,更新owner key时需要owner权限:

      void get_required_owner_authorities( flat_set<account_id_type>& a )const
      { if( is_owner_update() ) a.insert( account ); }

      void get_required_active_authorities( flat_set<account_id_type>& a )const
      { if( !is_owner_update() ) a.insert( account ); }      

proposal_update_operation,需要提议审批人的权限:

void proposal_update_operation::get_required_active_authorities( flat_set<account_id_type>& a )const
{
   for( const auto& i : active_approvals_to_add )    a.insert(i);
   for( const auto& i : active_approvals_to_remove ) a.insert(i);
}

void proposal_update_operation::get_required_owner_authorities( flat_set<account_id_type>& a )const
{
   for( const auto& i : owner_approvals_to_add )    a.insert(i);
   for( const auto& i : owner_approvals_to_remove ) a.insert(i);
}

get_required_authorities

get_required_authorities在 get_required_authorities、transfer_from_blind_operation、blind_transfer_operation、proposal_update_operation有单独的处理,来看下proposal_update_operation这块认证权限的指定:

void proposal_update_operation::get_required_authorities( vector<authority>& o )const
{
   authority auth;
   for( const auto& k : key_approvals_to_add )
      auth.key_auths[k] = 1;
   for( const auto& k : key_approvals_to_remove )
      auth.key_auths[k] = 1;
   auth.weight_threshold = auth.key_auths.size();

   o.emplace_back( std::move(auth) );
}

上面代码实现了提议的多重签名功能,weight_threshold指定了需要auth.key_auths指定的所有人同意提议。

签名

交易签名比较简单,用私钥生成签名后加到signatures就完事了,如下:

const signature_type& graphene::chain::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id)
{
   digest_type h = sig_digest( chain_id );
   signatures.push_back(key.sign_compact(h));
   return signatures.back();
}

elliptic_impl_priv.cpp

    compact_signature private_key::sign_compact( const fc::sha256& digest, bool require_canonical )const
    {
        FC_ASSERT( my->_key != empty_priv );
        compact_signature result;
        int recid;
        unsigned int counter = 0;
        do
        {
            FC_ASSERT( secp256k1_ecdsa_sign_compact( detail::_get_context(), (unsigned char*) digest.data(), (unsigned char*) result.begin() + 1, (unsigned char*) my->_key.data(), extended_nonce_function, &counter, &recid ));
        } while( require_canonical && !public_key::is_canonical( result ) );
        result.begin()[0] = 27 + 4 + recid;
        return result;
    }

验证签名

void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs, 
                       const std::function<const authority*(account_id_type)>& get_active,
                       const std::function<const authority*(account_id_type)>& get_owner,
                       uint32_t max_recursion_depth,
                       bool  allow_committe,
                       const flat_set<account_id_type>& active_aprovals,
                       const flat_set<account_id_type>& owner_approvals )
{ try {
   flat_set<account_id_type> required_active;
   flat_set<account_id_type> required_owner;
   vector<authority> other;

   for( const auto& op : ops )
      operation_get_required_authorities( op, required_active, required_owner, other );

   if( !allow_committe )
      GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
                       invalid_committee_approval, "Committee account may only propose transactions" );

   sign_state s(sigs,get_active);
   s.max_recursion = max_recursion_depth;
   for( auto& id : active_aprovals )
      s.approved_by.insert( id );
   for( auto& id : owner_approvals )
      s.approved_by.insert( id );

   for( const auto& auth : other )
   {
      GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) );
   }

   // fetch all of the top level authorities
   for( auto id : required_active )
   {
      GRAPHENE_ASSERT( s.check_authority(id) || 
                       s.check_authority(get_owner(id)), 
                       tx_missing_active_auth, "Missing Active Authority ${id}", ("id",id)("auth",*get_active(id))("owner",*get_owner(id)) );
   }

   for( auto id : required_owner )
   {
      GRAPHENE_ASSERT( owner_approvals.find(id) != owner_approvals.end() ||
                       s.check_authority(get_owner(id)), 
                       tx_missing_owner_auth, "Missing Owner Authority ${id}", ("id",id)("auth",*get_owner(id)) );
   }

   GRAPHENE_ASSERT(
      !s.remove_unused_signatures(),
      tx_irrelevant_sig,
      "Unnecessary signature(s) detected"
      );
} FC_CAPTURE_AND_RETHROW( (ops)(sigs) ) }

verify_authority函数中调用的其它函数在上面已经分析过了,就是判断需要哪些密钥签名和已经有哪些有效的签名。

这个函数在处理每笔交易时调用,如下:

processed_transaction database::_apply_transaction(const signed_transaction& trx)
{
   ...
   if( !(skip & (skip_transaction_signatures | skip_authority_check) ) )
   {
      auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
      auto get_owner  = [&]( account_id_type id ) { return &id(*this).owner;  };
      trx.verify_authority( chain_id, get_active, get_owner, get_global_properties().parameters.max_authority_depth );
   }

结论

交易签名首先会需要operation指定的支付人的签名,对一些特殊的operation可能还需要其他帐户签名,如提议等。

简单过了一下权限签名相关的代码,下次再碰到“Missing Authority”提示时就容易了解和解决问题了。


感谢您阅读 @chaimyu 的帖子,期待您能留言交流!

Sort:  

你今天过的开心吗?客官渴不渴,有没有去 @laodr 老道茶馆喝口热茶啊?假如我的留言打扰到你,请回复“取消”。

Congratulations @chaimyu! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:
SteemitBoard World Cup Contest - Colombia vs England


Participate in the SteemitBoard World Cup Contest!
Collect World Cup badges and win free SBD
Support the Gold Sponsors of the contest: @good-karma and @lukestokes


Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

Coin Marketplace

STEEM 0.32
TRX 0.11
JST 0.034
BTC 66791.24
ETH 3239.69
USDT 1.00
SBD 4.22