diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index df6458f3..2aee274d 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1934,6 +1934,8 @@ bool database_api_impl::verify_authority( const signed_transaction& trx )const trx.verify_authority( _db.get_chain_id(), [this]( account_id_type id ){ return &id(_db).active; }, [this]( account_id_type id ){ return &id(_db).owner; }, + [this]( account_id_type id, const operation& op ) { + return _db.get_account_custom_authorities(id, op); }, _db.get_global_properties().parameters.max_authority_depth ); return true; } diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index b45d922b..1d555fa5 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -790,7 +790,10 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx { 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 ); + auto get_custom = [&]( account_id_type id, const operation& op ) { + return get_account_custom_authorities(id, op); + }; + trx.verify_authority( chain_id, get_active, get_owner, get_custom, get_global_properties().parameters.max_authority_depth ); } //Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index a6f7af19..294aaa5e 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include @@ -159,4 +161,24 @@ const witness_schedule_object& database::get_witness_schedule_object()const return *_p_witness_schedule_obj; } +vector database::get_account_custom_authorities(account_id_type account, const operation& op)const +{ + const auto& pindex = get_index_type().indices().get(); + const auto& cindex = get_index_type().indices().get(); + auto prange = pindex.equal_range(boost::make_tuple(account)); + time_point_sec now = head_block_time(); + vector custom_auths; + for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second)) + { + auto crange = cindex.equal_range(boost::make_tuple(pobj.id, op.which())); + for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second)) + { + if(now >= cobj.valid_from && now < cobj.valid_to) + { + custom_auths.push_back(pobj.auth); + } + } + } + return custom_auths; +} } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index e697b797..2b921b1d 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -294,6 +294,7 @@ namespace graphene { namespace chain { uint32_t last_non_undoable_block_num() const; + vector get_account_custom_authorities(account_id_type account, const operation& op)const; //////////////////// db_init.cpp //////////////////// void initialize_evaluators(); diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 2a9909a5..7408a29c 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -148,6 +148,7 @@ namespace graphene { namespace chain { const chain_id_type& chain_id, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; /** @@ -162,6 +163,7 @@ namespace graphene { namespace chain { const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH ) const; @@ -194,6 +196,7 @@ namespace graphene { namespace chain { void verify_authority( const vector& ops, const flat_set& sigs, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH, bool allow_committe = false, const flat_set& active_aprovals = flat_set(), diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 88d985ff..efa07a46 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -132,6 +132,30 @@ struct proposal_operation_hardfork_visitor FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" ); } + void operator()(const custom_permission_create_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_create_operation not allowed yet!" ); + } + + void operator()(const custom_permission_update_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_update_operation not allowed yet!" ); + } + + void operator()(const custom_permission_delete_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_permission_delete_operation not allowed yet!" ); + } + + void operator()(const custom_account_authority_create_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_create_operation not allowed yet!" ); + } + + void operator()(const custom_account_authority_update_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_update_operation not allowed yet!" ); + } + + void operator()(const custom_account_authority_delete_operation &v) const { + FC_ASSERT( block_time >= HARDFORK_RBAC_TIME, "custom_account_authority_delete_operation not allowed yet!" ); + } + // loop and self visit in proposals void operator()(const proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) diff --git a/libraries/chain/proposal_object.cpp b/libraries/chain/proposal_object.cpp index 1d5a8706..a2f6d1ae 100644 --- a/libraries/chain/proposal_object.cpp +++ b/libraries/chain/proposal_object.cpp @@ -36,6 +36,8 @@ bool proposal_object::is_authorized_to_execute(database& db) const available_key_approvals, [&]( account_id_type id ){ return &id(db).active; }, [&]( account_id_type id ){ return &id(db).owner; }, + [&]( account_id_type id, const operation& op ){ + return db.get_account_custom_authorities(id, op); }, db.get_global_properties().parameters.max_authority_depth, true, /* allow committee */ available_active_approvals, diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 093e7833..7f931aba 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -248,6 +248,7 @@ struct sign_state void verify_authority( const vector& ops, const flat_set& sigs, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion_depth, bool allow_committe, const flat_set& active_aprovals, @@ -257,13 +258,6 @@ void verify_authority( const vector& ops, const flat_set required_owner; vector 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 ) @@ -271,6 +265,37 @@ void verify_authority( const vector& ops, const flat_set operation_required_active; + operation_get_required_authorities( op, operation_required_active, required_owner, other ); + + auto itr = operation_required_active.begin(); + while ( itr != operation_required_active.end() ) { + if ( approved_by_custom_authority( *itr, op ) ) + itr = operation_required_active.erase( itr ); + else + ++itr; + } + + required_active.insert( operation_required_active.begin(), operation_required_active.end() ); + } + + if( !allow_committe ) + GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(), + invalid_committee_approval, "Committee account may only propose transactions" ); + + for( const auto& auth : other ) { GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) ); @@ -359,6 +384,7 @@ set signed_transaction::minimize_required_signatures( const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion ) const { @@ -370,7 +396,7 @@ set signed_transaction::minimize_required_signatures( result.erase( k ); try { - graphene::chain::verify_authority( operations, result, get_active, get_owner, max_recursion ); + graphene::chain::verify_authority( operations, result, get_active, get_owner, get_custom, max_recursion ); continue; // element stays erased if verify_authority is ok } catch( const tx_missing_owner_auth& e ) {} @@ -385,9 +411,10 @@ void signed_transaction::verify_authority( const chain_id_type& chain_id, const std::function& get_active, const std::function& get_owner, + const std::function(account_id_type, const operation&)>& get_custom, uint32_t max_recursion )const { try { - graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, max_recursion ); + graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_custom, max_recursion ); } FC_CAPTURE_AND_RETHROW( (*this) ) } } } // graphene::chain diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 2afd12a6..92c62ba6 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1303,6 +1303,14 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) return &(aid(db).owner); } ; + auto get_custom = [&]( + account_id_type id, + const operation& op + ) -> vector + { + return db.get_account_custom_authorities(id, op); + } ; + auto chk = [&]( const signed_transaction& tx, flat_set available_keys, @@ -1322,7 +1330,7 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); + set result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom ); //wdump( (result_set)(ref_set) ); return result_set == ref_set; } ; @@ -1341,9 +1349,9 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) BOOST_CHECK( chk( tx, { alice_public_key, bob_public_key }, { alice_public_key, bob_public_key } ) ); BOOST_CHECK( chk_min( tx, { alice_public_key, bob_public_key }, { alice_public_key } ) ); - GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), fc::exception ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom ), fc::exception ); sign( tx, alice_private_key ); - tx.verify_authority( db.get_chain_id(), get_active, get_owner ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom ); } catch(fc::exception& e) {