From e8d2b45d67e277d3979a40ace33425a84366def6 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 10 Jul 2015 15:56:44 -0400 Subject: [PATCH] improve authority checking performance, adding automatic transaction signing helpers --- .../graphene/chain/protocol/transaction.hpp | 20 ++- libraries/chain/protocol/transaction.cpp | 170 +++++++++++++++++- .../chain/transaction_evaluation_state.cpp | 36 ++-- 3 files changed, 211 insertions(+), 15 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 85080ce6..4575742e 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -114,7 +114,7 @@ namespace graphene { namespace chain { op.visit( std::forward( visitor ) ); } - void get_required_authorities( flat_set& active, flat_set& owner, vector& other ); + void get_required_authorities( flat_set& active, flat_set& owner, vector& other )const; protected: // Intentionally unreflected: does not go on wire @@ -129,7 +129,23 @@ namespace graphene { namespace chain { signed_transaction( const transaction& trx = transaction() ) : transaction(trx){} - void sign( const private_key_type& key ); + /** signs and appends to signatures */ + const signature_type& sign( const private_key_type& key ); + + /** returns signature but does not append */ + signature_type sign( const private_key_type& key )const; + + /** + * Given a set of private keys sign this transaction with a minimial subset of required keys. + * + * @pram get_auth - used to fetch the active authority required for an account referenced by another authority + */ + void sign( const vector& keys, + const std::function& get_active, + const std::function& get_owner ); + + bool verify( const std::function& get_active, + const std::function& get_owner )const; vector signatures; diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 421b04ce..5dc32798 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -63,16 +63,27 @@ graphene::chain::transaction_id_type graphene::chain::transaction::id() const return result; } -void graphene::chain::signed_transaction::sign(const private_key_type& key) +const signature_type& graphene::chain::signed_transaction::sign(const private_key_type& key) { if( relative_expiration != 0 ) { // Relative expiration is set, meaning we must include the block ID in the signature - assert(block_id_cache.valid()); + FC_ASSERT(block_id_cache.valid()); signatures.push_back(key.sign_compact(digest(*block_id_cache))); } else { signatures.push_back(key.sign_compact(digest())); } + return signatures.back(); +} +signature_type graphene::chain::signed_transaction::sign(const private_key_type& key)const +{ + if( relative_expiration != 0 ) + { + // Relative expiration is set, meaning we must include the block ID in the signature + FC_ASSERT(block_id_cache.valid()); + return key.sign_compact(digest(*block_id_cache)); + } + return key.sign_compact(digest()); } void transaction::set_expiration( fc::time_point_sec expiration_time ) @@ -90,10 +101,163 @@ void transaction::set_expiration( const block_id_type& reference_block, unsigned relative_expiration = lifetime_intervals; block_id_cache = reference_block; } -void transaction::get_required_authorities( flat_set& active, flat_set& owner, vector& other ) +void transaction::get_required_authorities( flat_set& active, flat_set& owner, vector& other )const { for( const auto& op : operations ) operation_get_required_authorities( op, active, owner, other ); } +struct sign_state +{ + /** returns true if we have a signature for this key or can + * produce a signature for this key, else returns false. + */ + bool signed_by( const public_key_type& k ) + { + auto itr = signatures.find(k); + if( itr == signatures.end() ) + { + auto pk = keys.find(k); + if( pk != keys.end() ) + { + signatures[k] = trx.sign(pk->second); + checked_sigs.insert(k); + return true; + } + return false; + } + checked_sigs.insert(k); + return true; + } + + /** + * Checks to see if we have signatures of the active authorites of + * the accounts specified in authority or the keys specified. + */ + bool check_authority( const authority* au, int depth = 0 ) + { + if( au == nullptr ) return false; + const authority& auth = *au; + + uint32_t total_weight = 0; + for( const auto& k : auth.key_auths ) + if( signed_by( k.first ) ) + { + total_weight += k.second; + if( total_weight >= auth.weight_threshold ) + return true; + } + + for( const auto& a : auth.account_auths ) + { + if( approved_by.find(a.first) == approved_by.end() ) + { + if( depth == GRAPHENE_MAX_SIG_CHECK_DEPTH ) + return false; + if( check_authority( get_active( a.first ), depth+1 ) ) + { + approved_by.insert( a.first ); + total_weight += a.second; + if( total_weight >= auth.weight_threshold ) + return true; + } + } + else + { + total_weight += a.second; + if( total_weight >= auth.weight_threshold ) + return true; + } + } + return total_weight >= auth.weight_threshold; + } + + bool remove_unused_signatures() + { + vector remove_sigs; + for( const auto& sig : signatures ) + if( checked_sigs.find(sig.first) == checked_sigs.end() ) + remove_sigs.push_back( sig.first ); + for( auto& sig : remove_sigs ) + signatures.erase(sig); + return remove_sigs.size() != 0; + } + + sign_state( const signed_transaction& t, const std::function& a, + const vector& kys = vector() ) + :trx(t),get_active(a) + { + auto d = trx.digest(); + for( const auto& sig : trx.signatures ) + signatures[ fc::ecc::public_key( sig, d ) ] = sig; + + for( const auto& key : kys ) + keys[key.get_public_key()] = key; + } + + const signed_transaction& trx; + const std::function& get_active; + + flat_map keys; + flat_map signatures; + + set checked_sigs; + flat_set approved_by; + +}; + +/** + * Given a set of private keys sign this transaction with a minimial subset of required keys. + */ +void signed_transaction::sign( const vector& keys, + const std::function& get_active, + const std::function& get_owner ) +{ + flat_set required_active; + flat_set required_owner; + vector other; + get_required_authorities( required_active, required_owner, other ); + + sign_state s(*this,get_active,keys); + + for( const auto& auth : other ) + s.check_authority(&auth); + for( auto id : required_active ) + s.check_authority(get_active(id)); + for( auto id : required_owner ) + s.check_authority(get_owner(id)); + + s.remove_unused_signatures(); + + signatures.clear(); + for( const auto& sig : s.signatures ) + signatures.push_back(sig.second); +} + +bool signed_transaction::verify( const std::function& get_active, + const std::function& get_owner )const +{ + flat_set required_active; + flat_set required_owner; + vector other; + get_required_authorities( required_active, required_owner, other ); + + sign_state s(*this,get_active); + + for( const auto& auth : other ) + if( !s.check_authority(&auth) ) + return false; + + // fetch all of the top level authorities + for( auto id : required_active ) + if( !s.check_authority(get_active(id)) ) + return false; + for( auto id : required_owner ) + if( !s.check_authority(get_owner(id)) ) + return false; + if( s.remove_unused_signatures() ) + return false; + return true; +} + } } // graphene::chain diff --git a/libraries/chain/transaction_evaluation_state.cpp b/libraries/chain/transaction_evaluation_state.cpp index 7e8a6eed..0d79d39d 100644 --- a/libraries/chain/transaction_evaluation_state.cpp +++ b/libraries/chain/transaction_evaluation_state.cpp @@ -60,10 +60,34 @@ namespace graphene { namespace chain { return true; uint32_t total_weight = 0; + + for( const auto& key : au.key_auths ) + { + if( signed_by( key.first ) ) + { + total_weight += key.second; + if( total_weight >= au.weight_threshold ) + return true; + } + } + for( const auto& key : au.address_auths ) + { + if( signed_by( key.first ) ) + { + total_weight += key.second; + if( total_weight >= au.weight_threshold ) + return true; + } + } + for( const auto& auth : au.account_auths ) { if( approved_by.find( std::make_pair(auth.first,auth_class) ) != approved_by.end() ) + { total_weight += auth.second; + if( total_weight >= au.weight_threshold ) + return true; + } else { if( depth == GRAPHENE_MAX_SIG_CHECK_DEPTH ) @@ -76,19 +100,11 @@ namespace graphene { namespace chain { { approved_by.insert( std::make_pair(acnt.id,auth_class) ); total_weight += auth.second; + if( total_weight >= au.weight_threshold ) + return true; } } } - for( const auto& key : au.key_auths ) - { - if( signed_by( key.first ) ) - total_weight += key.second; - } - for( const auto& key : au.address_auths ) - { - if( signed_by( key.first ) ) - total_weight += key.second; - } return total_weight >= au.weight_threshold; } FC_CAPTURE_AND_RETHROW( (au)(auth_class)(depth) ) }