From 6d378ff67b6767ed956ba7101b0d42fc177ef3ab Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Thu, 19 Oct 2017 16:07:11 -0700 Subject: [PATCH] Implement verify_signatures API call #1674 --- libraries/app/database_api.cpp | 60 +++++++++++++++++++ .../app/include/steemit/app/database_api.hpp | 23 +++++++ .../include/steemit/protocol/sign_state.hpp | 5 ++ .../include/steemit/protocol/transaction.hpp | 4 +- libraries/protocol/transaction.cpp | 29 ++++++--- 5 files changed, 109 insertions(+), 12 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index a07ddefc81..f518793798 100755 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -70,6 +70,7 @@ class database_api_impl : public std::enable_shared_from_this set get_potential_signatures( const signed_transaction& trx )const; bool verify_authority( const signed_transaction& trx )const; bool verify_account_authority( const string& name_or_id, const flat_set& signers )const; + verify_signatures_return verify_signatures( const verify_signatures_args& args )const; // signal handlers void on_applied_block( const chain::signed_block& b ); @@ -964,6 +965,65 @@ bool database_api_impl::verify_account_authority( const string& name, const flat return verify_authority( trx ); } +verify_signatures_return database_api_impl::verify_signatures( const verify_signatures_args& args )const +{ + // get_signature_keys can throw for dup sigs. Allow this to throw. + flat_set< public_key_type > sig_keys; + for( const auto& sig : args.signatures ) + { + STEEMIT_ASSERT( + sig_keys.insert( fc::ecc::public_key(sig,args.hash) ).second, + tx_duplicate_sig, + "Duplicate Signature detected" ); + } + + verify_signatures_return result; + result.valid = true; + + // verify authority throws on failure, catch and return false + try + { + steemit::protocol::verify_authority( + [&args]( flat_set< account_name_type >& required_active, + flat_set< account_name_type >& required_owner, + flat_set< account_name_type >& required_posting, + vector< authority >& ) + { + switch( args.auth_level ) + { + case authority::owner: + std::copy( args.accounts.begin(), args.accounts.end(), required_owner.end() ); + break; + case authority::active: + std::copy( args.accounts.begin(), args.accounts.end(), required_active.end() ); + break; + case authority::posting: + std::copy( args.accounts.begin(), args.accounts.end(), required_posting.end() ); + break; + case authority::key: + default: + FC_ASSERT( false, "verify_signatures only supports owner, active, and posting auths" ); + } + }, + sig_keys, + [this]( const string& name ) { return authority( _db.get< account_authority_object, by_account >( name ).owner ); }, + [this]( const string& name ) { return authority( _db.get< account_authority_object, by_account >( name ).active ); }, + [this]( const string& name ) { return authority( _db.get< account_authority_object, by_account >( name ).posting ); }, + STEEMIT_MAX_SIG_CHECK_DEPTH ); + } + catch( fc::exception& ) { result.valid = false; } + + return result; +} + +verify_signatures_return database_api::verify_signatures( const verify_signatures_args& args )const +{ + return my->_db.with_read_lock( [&]() + { + return my->verify_signatures( args ); + }); +} + vector database_api::get_conversion_requests( const string& account )const { return my->_db.with_read_lock( [&]() diff --git a/libraries/app/include/steemit/app/database_api.hpp b/libraries/app/include/steemit/app/database_api.hpp index e0475937b3..c4d64f43d3 100755 --- a/libraries/app/include/steemit/app/database_api.hpp +++ b/libraries/app/include/steemit/app/database_api.hpp @@ -98,6 +98,19 @@ struct discussion_query { optional parent_permlink; }; +struct verify_signatures_args +{ + fc::sha256 hash; + vector< signature_type > signatures; + vector< account_name_type > accounts; + authority::classification auth_level; +}; + +struct verify_signatures_return +{ + bool valid; +}; + /** * @brief The database_api class implements the RPC API for the chain database. * @@ -326,6 +339,12 @@ class database_api */ bool verify_account_authority( const string& name_or_id, const flat_set& signers )const; + /* + * This is a general purpose API that checks signatures against accounts for an arbitrary sha256 hash + * using the existing authority structures in Steem + */ + verify_signatures_return verify_signatures( const verify_signatures_args& args )const; + /** * if permlink is "" then it will return all votes for author */ @@ -447,6 +466,9 @@ FC_REFLECT( steemit::app::discussion_query, (tag)(filter_tags)(select_tags)(sele FC_REFLECT_ENUM( steemit::app::withdraw_route_type, (incoming)(outgoing)(all) ); +FC_REFLECT( steemit::app::verify_signatures_args, (hash)(signatures)(accounts)(auth_level) ); +FC_REFLECT( steemit::app::verify_signatures_return, (valid) ); + FC_API(steemit::app::database_api, // Subscriptions (set_block_applied_callback) @@ -519,6 +541,7 @@ FC_API(steemit::app::database_api, (get_potential_signatures) (verify_authority) (verify_account_authority) + (verify_signatures) // votes (get_active_votes) diff --git a/libraries/protocol/include/steemit/protocol/sign_state.hpp b/libraries/protocol/include/steemit/protocol/sign_state.hpp index 05e810d651..d03db0d6aa 100644 --- a/libraries/protocol/include/steemit/protocol/sign_state.hpp +++ b/libraries/protocol/include/steemit/protocol/sign_state.hpp @@ -6,6 +6,11 @@ namespace steemit { namespace protocol { typedef std::function authority_getter; +typedef std::function< void( flat_set< account_name_type >&, + flat_set< account_name_type >&, + flat_set< account_name_type >&, + vector< authority >& ) > + required_authority_getter; struct sign_state { diff --git a/libraries/protocol/include/steemit/protocol/transaction.hpp b/libraries/protocol/include/steemit/protocol/transaction.hpp index 3233906588..4a33ae6c6f 100644 --- a/libraries/protocol/include/steemit/protocol/transaction.hpp +++ b/libraries/protocol/include/steemit/protocol/transaction.hpp @@ -91,7 +91,7 @@ namespace steemit { namespace protocol { void clear() { operations.clear(); signatures.clear(); } }; - void verify_authority( const vector& ops, const flat_set& sigs, + void verify_authority( const required_authority_getter& ops, const flat_set& sigs, const authority_getter& get_active, const authority_getter& get_owner, const authority_getter& get_posting, @@ -101,7 +101,6 @@ namespace steemit { namespace protocol { const flat_set< account_name_type >& owner_aprovals = flat_set< account_name_type >(), const flat_set< account_name_type >& posting_approvals = flat_set< account_name_type >()); - struct annotated_signed_transaction : public signed_transaction { annotated_signed_transaction(){} annotated_signed_transaction( const signed_transaction& trx ) @@ -112,7 +111,6 @@ namespace steemit { namespace protocol { uint32_t transaction_num = 0; }; - /// @} transactions group } } // steemit::protocol diff --git a/libraries/protocol/transaction.cpp b/libraries/protocol/transaction.cpp index 7a24bf3937..61b40a1864 100644 --- a/libraries/protocol/transaction.cpp +++ b/libraries/protocol/transaction.cpp @@ -82,7 +82,7 @@ void transaction::get_required_authorities( flat_set< account_name_type >& activ operation_get_required_authorities( op, active, owner, posting, other ); } -void verify_authority( const vector& ops, const flat_set& sigs, +void verify_authority( const required_authority_getter& get_required_authorities, const flat_set& sigs, const authority_getter& get_active, const authority_getter& get_owner, const authority_getter& get_posting, @@ -98,8 +98,7 @@ void verify_authority( const vector& ops, const flat_set required_posting; vector< authority > other; - for( const auto& op : ops ) - operation_get_required_authorities( op, required_active, required_owner, required_posting, other ); + get_required_authorities( required_active, required_owner, required_posting, other ); /** * Transactions with operations required posting authority cannot be combined @@ -169,8 +168,7 @@ void verify_authority( const vector& ops, const flat_set signed_transaction::get_signature_keys( const chain_id_type& chain_id )const { try { @@ -186,8 +184,6 @@ flat_set signed_transaction::get_signature_keys( const chain_id return result; } FC_CAPTURE_AND_RETHROW() } - - set signed_transaction::get_required_signatures( const chain_id_type& chain_id, const flat_set& available_keys, @@ -262,7 +258,14 @@ set signed_transaction::minimize_required_signatures( result.erase( k ); try { - steemit::protocol::verify_authority( operations, result, get_active, get_owner, get_posting, max_recursion ); + steemit::protocol::verify_authority( [this]( flat_set< account_name_type >& required_active, + flat_set< account_name_type >& required_owner, + flat_set< account_name_type >& required_posting, + vector< authority >& other ) + { + for( const auto& op : operations ) + operation_get_required_authorities( op, required_active, required_owner, required_posting, other ); + }, result, get_active, get_owner, get_posting, max_recursion ); continue; // element stays erased if verify_authority is ok } catch( const tx_missing_owner_auth& e ) {} @@ -281,7 +284,15 @@ void signed_transaction::verify_authority( const authority_getter& get_posting, uint32_t max_recursion )const { try { - steemit::protocol::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_posting, max_recursion ); + steemit::protocol::verify_authority( [this]( flat_set< account_name_type >& required_active, + flat_set< account_name_type >& required_owner, + flat_set< account_name_type >& required_posting, + vector< authority >& other ) + { + for( const auto& op : operations ) + operation_get_required_authorities( op, required_active, required_owner, required_posting, other ); + }, + get_signature_keys( chain_id ), get_active, get_owner, get_posting, max_recursion ); } FC_CAPTURE_AND_RETHROW( (*this) ) } } } // steemit::protocol