Compare commits

...

5 commits

Author SHA1 Message Date
sierra19XX
df7f9c3783 compilation fix 2020-09-08 11:51:00 +00:00
sierra19XX
4c7e951d6b Merge remote-tracking branch 'origin/develop' into feature/bts-210 2020-09-08 11:22:18 +00:00
Nathan Hourt
406cc54299
Ref #381: Fix bad merge
During merge conflict resolution, I accidentally broke custom
authorities. This fixes it.
2020-09-03 17:16:06 -05:00
Nathan Hourt
5d48052da3
Ref #381: Fixes
Build and logic fixes for Pull Request #381
2020-09-02 14:18:44 -05:00
Nathan Hourt
133b41b65a
Resolve #210: [HF] Check authorities on custom_operation
The required_auths field on custom_operation was being ignored during authority checking. This commit causes it to be checked correctly, and adds a unit test verifying as much.
2020-09-02 13:14:11 -05:00
14 changed files with 164 additions and 87 deletions

View file

@ -789,12 +789,14 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
if( !(skip & (skip_transaction_signatures | skip_authority_check) ) ) if( !(skip & (skip_transaction_signatures | skip_authority_check) ) )
{ {
auto get_active = [&]( account_id_type id ) { return &id(*this).active; }; auto get_active = [this]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; }; auto get_owner = [this]( account_id_type id ) { return &id(*this).owner; };
auto get_custom = [&]( account_id_type id, const operation& op ) { auto get_custom = [this]( account_id_type id, const operation& op ) {
return get_account_custom_authorities(id, 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 ); trx.verify_authority( chain_id, get_active, get_owner, get_custom,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(head_block_time()),
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 //Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is

View file

@ -40,6 +40,7 @@
#include <graphene/chain/vesting_balance_object.hpp> #include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/transaction_object.hpp> #include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/impacted.hpp> #include <graphene/chain/impacted.hpp>
#include <graphene/chain/hardfork.hpp>
using namespace fc; using namespace fc;
@ -49,8 +50,13 @@ using namespace graphene::chain;
struct get_impacted_account_visitor struct get_impacted_account_visitor
{ {
flat_set<account_id_type>& _impacted; flat_set<account_id_type>& _impacted;
get_impacted_account_visitor( flat_set<account_id_type>& impact ):_impacted(impact) {} bool _ignore_custom_op_reqd_auths;
typedef void result_type;
get_impacted_account_visitor( flat_set<account_id_type>& impact, bool ignore_custom_operation_required_auths )
: _impacted( impact ), _ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths )
{}
using result_type = void;
void operator()( const transfer_operation& op ) void operator()( const transfer_operation& op )
{ {
@ -136,7 +142,7 @@ struct get_impacted_account_visitor
{ {
vector<authority> other; vector<authority> other;
for( const auto& proposed_op : op.proposed_ops ) for( const auto& proposed_op : op.proposed_ops )
operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other ); operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other, _ignore_custom_op_reqd_auths );
for( auto& o : other ) for( auto& o : other )
add_authority_accounts( _impacted, o ); add_authority_accounts( _impacted, o );
} }
@ -346,20 +352,17 @@ struct get_impacted_account_visitor
} }
}; };
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result ) void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
{ get_impacted_account_visitor vtor = get_impacted_account_visitor( result, ignore_custom_operation_required_auths );
get_impacted_account_visitor vtor = get_impacted_account_visitor( result );
op.visit( vtor ); op.visit( vtor );
} }
void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result ) void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
{
for( const auto& op : tx.operations ) for( const auto& op : tx.operations )
operation_get_impacted_accounts( op, result ); operation_get_impacted_accounts( op, result, ignore_custom_operation_required_auths );
} }
void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts ) void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts, bool ignore_custom_operation_required_auths ) {
{
if( obj->id.space() == protocol_ids ) if( obj->id.space() == protocol_ids )
{ {
switch( (object_type)obj->id.type() ) switch( (object_type)obj->id.type() )
@ -406,12 +409,14 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
} case proposal_object_type:{ } case proposal_object_type:{
const auto& aobj = dynamic_cast<const proposal_object*>(obj); const auto& aobj = dynamic_cast<const proposal_object*>(obj);
assert( aobj != nullptr ); assert( aobj != nullptr );
transaction_get_impacted_accounts( aobj->proposed_transaction, accounts ); transaction_get_impacted_accounts( aobj->proposed_transaction, accounts,
ignore_custom_operation_required_auths);
break; break;
} case operation_history_object_type:{ } case operation_history_object_type:{
const auto& aobj = dynamic_cast<const operation_history_object*>(obj); const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
assert( aobj != nullptr ); assert( aobj != nullptr );
operation_get_impacted_accounts( aobj->op, accounts ); operation_get_impacted_accounts( aobj->op, accounts,
ignore_custom_operation_required_auths);
break; break;
} case withdraw_permission_object_type:{ } case withdraw_permission_object_type:{
const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj); const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);
@ -462,7 +467,8 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
} case impl_transaction_object_type:{ } case impl_transaction_object_type:{
const auto& aobj = dynamic_cast<const transaction_object*>(obj); const auto& aobj = dynamic_cast<const transaction_object*>(obj);
assert( aobj != nullptr ); assert( aobj != nullptr );
transaction_get_impacted_accounts( aobj->trx, accounts ); transaction_get_impacted_accounts( aobj->trx, accounts,
ignore_custom_operation_required_auths);
break; break;
} case impl_blinded_balance_object_type:{ } case impl_blinded_balance_object_type:{
const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj); const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);
@ -507,6 +513,7 @@ void database::notify_changed_objects()
if( _undo_db.enabled() ) if( _undo_db.enabled() )
{ {
const auto& head_undo = _undo_db.head(); const auto& head_undo = _undo_db.head();
auto chain_time = head_block_time();
// New // New
if( !new_objects.empty() ) if( !new_objects.empty() )
@ -518,7 +525,8 @@ void database::notify_changed_objects()
new_ids.push_back(item); new_ids.push_back(item);
auto obj = find_object(item); auto obj = find_object(item);
if(obj != nullptr) if(obj != nullptr)
get_relevant_accounts(obj, new_accounts_impacted); get_relevant_accounts(obj, new_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
} }
GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted) GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted)
@ -532,7 +540,8 @@ void database::notify_changed_objects()
for( const auto& item : head_undo.old_values ) for( const auto& item : head_undo.old_values )
{ {
changed_ids.push_back(item.first); changed_ids.push_back(item.first);
get_relevant_accounts(item.second.get(), changed_accounts_impacted); get_relevant_accounts(item.second.get(), changed_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
} }
GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted) GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted)
@ -549,7 +558,8 @@ void database::notify_changed_objects()
removed_ids.emplace_back( item.first ); removed_ids.emplace_back( item.first );
auto obj = item.second.get(); auto obj = item.second.get();
removed.emplace_back( obj ); removed.emplace_back( obj );
get_relevant_accounts(obj, removed_accounts_impacted); get_relevant_accounts(obj, removed_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
} }
GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted) GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted)

View file

@ -0,0 +1,6 @@
// #210 Check authorities on custom_operation
#ifndef HARDFORK_CORE_210_TIME
#define HARDFORK_CORE_210_TIME (fc::time_point_sec(1893456000)) // Jan 1 00:00:00 2030 (Not yet scheduled)
// Bugfix: pre-HF 210, custom_operation's required_auths field was ignored.
#define MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) (chain_time <= HARDFORK_CORE_210_TIME)
#endif

View file

@ -30,13 +30,12 @@
namespace graphene { namespace chain { namespace graphene { namespace chain {
void operation_get_impacted_accounts( void operation_get_impacted_accounts( const graphene::chain::operation& op,
const graphene::chain::operation& op, fc::flat_set<graphene::chain::account_id_type>& result,
fc::flat_set<graphene::chain::account_id_type>& result ); bool ignore_custom_operation_required_auths );
void transaction_get_impacted_accounts( void transaction_get_impacted_accounts( const graphene::chain::transaction& tx,
const graphene::chain::transaction& tx, fc::flat_set<graphene::chain::account_id_type>& result,
fc::flat_set<graphene::chain::account_id_type>& result bool ignore_custom_operation_required_auths );
);
} } // graphene::app } } // graphene::app

View file

@ -50,6 +50,9 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return payer; } account_id_type fee_payer()const { return payer; }
void validate()const; void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const; share_type calculate_fee(const fee_parameters_type& k)const;
void get_required_active_authorities( flat_set<account_id_type>& auths )const {
auths.insert( required_auths.begin(), required_auths.end() );
}
}; };
} } // namespace graphene::chain } } // namespace graphene::chain

View file

@ -169,7 +169,8 @@ namespace graphene { namespace chain {
void operation_get_required_authorities( const operation& op, void operation_get_required_authorities( const operation& op,
flat_set<account_id_type>& active, flat_set<account_id_type>& active,
flat_set<account_id_type>& owner, flat_set<account_id_type>& owner,
vector<authority>& other ); vector<authority>& other,
bool ignore_custom_operation_required_auths );
void operation_validate( const operation& op ); void operation_validate( const operation& op );

View file

@ -112,7 +112,10 @@ namespace graphene { namespace chain {
return results; return results;
} }
void get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const; void get_required_authorities( flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other,
bool ignore_custom_operation_required_auths )const;
}; };
/** /**
@ -142,6 +145,7 @@ namespace graphene { namespace chain {
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom, const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_authorities,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
)const; )const;
@ -150,6 +154,7 @@ namespace graphene { namespace chain {
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom, const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
/** /**
@ -158,13 +163,13 @@ namespace graphene { namespace chain {
* some cases where get_required_signatures() returns a * some cases where get_required_signatures() returns a
* non-minimal set. * non-minimal set.
*/ */
set<public_key_type> minimize_required_signatures( set<public_key_type> minimize_required_signatures(
const chain_id_type& chain_id, const chain_id_type& chain_id,
const flat_set<public_key_type>& available_keys, 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_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom, const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
) const; ) const;
@ -198,10 +203,11 @@ namespace graphene { namespace chain {
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom, const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
bool allow_committe = false, bool allow_committee = false,
const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(), const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(),
const flat_set<account_id_type>& owner_approvals = flat_set<account_id_type>()); const flat_set<account_id_type>& owner_approvals = flat_set<account_id_type>() );
/** /**
* @brief captures the result of evaluating the operations contained in the transaction * @brief captures the result of evaluating the operations contained in the transaction

View file

@ -204,19 +204,20 @@ struct proposal_operation_hardfork_visitor
} }
}; };
void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) void_result proposal_create_evaluator::do_evaluate( const proposal_create_operation& o )
{ try { { try {
const database& d = db(); const database& d = db();
auto block_time = d.head_block_time();
proposal_operation_hardfork_visitor vtor( d.head_block_time() ); proposal_operation_hardfork_visitor vtor( block_time );
vtor( o ); vtor( o );
const auto& global_parameters = d.get_global_properties().parameters; const auto& global_parameters = d.get_global_properties().parameters;
FC_ASSERT( o.expiration_time > d.head_block_time(), "Proposal has already expired on creation." ); FC_ASSERT( o.expiration_time > block_time, "Proposal has already expired on creation." );
FC_ASSERT( o.expiration_time <= d.head_block_time() + global_parameters.maximum_proposal_lifetime, FC_ASSERT( o.expiration_time <= block_time + global_parameters.maximum_proposal_lifetime,
"Proposal expiration time is too far in the future."); "Proposal expiration time is too far in the future.");
FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - d.head_block_time()), FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - block_time),
"Proposal review period must be less than its overall lifetime." ); "Proposal review period must be less than its overall lifetime." );
{ {
@ -225,7 +226,8 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
vector<authority> other; vector<authority> other;
for( auto& op : o.proposed_ops ) for( auto& op : o.proposed_ops )
{ {
operation_get_required_authorities(op.op, auths, auths, other); operation_get_required_authorities( op.op, auths, auths, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(block_time) );
} }
FC_ASSERT( other.size() == 0 ); // TODO: what about other??? FC_ASSERT( other.size() == 0 ); // TODO: what about other???
@ -248,18 +250,19 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
} }
} }
for( const op_wrapper& op : o.proposed_ops ) for (const op_wrapper& op : o.proposed_ops)
_proposed_trx.operations.push_back(op.op); _proposed_trx.operations.push_back(op.op);
_proposed_trx.validate(); _proposed_trx.validate();
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
object_id_type proposal_create_evaluator::do_apply(const proposal_create_operation& o) object_id_type proposal_create_evaluator::do_apply( const proposal_create_operation& o )
{ try { { try {
database& d = db(); database& d = db();
auto chain_time = d.head_block_time();
const proposal_object& proposal = d.create<proposal_object>([&](proposal_object& proposal) { const proposal_object& proposal = d.create<proposal_object>( [&o, this, chain_time](proposal_object& proposal) {
_proposed_trx.expiration = o.expiration_time; _proposed_trx.expiration = o.expiration_time;
proposal.proposed_transaction = _proposed_trx; proposal.proposed_transaction = _proposed_trx;
proposal.proposer = o.fee_paying_account; proposal.proposer = o.fee_paying_account;
@ -273,7 +276,8 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
// TODO: consider caching values from evaluate? // TODO: consider caching values from evaluate?
for( auto& op : _proposed_trx.operations ) for( auto& op : _proposed_trx.operations )
operation_get_required_authorities(op, required_active, proposal.required_owner_approvals, other); operation_get_required_authorities( op, required_active, proposal.required_owner_approvals, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) );
//All accounts which must provide both owner and active authority should be omitted from the active authority set; //All accounts which must provide both owner and active authority should be omitted from the active authority set;
//owner authority approval implies active authority approval. //owner authority approval implies active authority approval.
@ -285,7 +289,7 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
return proposal.id; return proposal.id;
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }
void_result proposal_update_evaluator::do_evaluate(const proposal_update_operation& o) void_result proposal_update_evaluator::do_evaluate( const proposal_update_operation& o )
{ try { { try {
database& d = db(); database& d = db();

View file

@ -24,12 +24,13 @@
#include <graphene/chain/database.hpp> #include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp> #include <graphene/chain/account_object.hpp>
#include <graphene/chain/proposal_object.hpp> #include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
bool proposal_object::is_authorized_to_execute(database& db) const bool proposal_object::is_authorized_to_execute( database& db ) const
{ {
transaction_evaluation_state dry_run_eval(&db); transaction_evaluation_state dry_run_eval( &db );
try { try {
verify_authority( proposed_transaction.operations, verify_authority( proposed_transaction.operations,
@ -38,6 +39,7 @@ bool proposal_object::is_authorized_to_execute(database& db) const
[&]( account_id_type id ){ return &id(db).owner; }, [&]( account_id_type id ){ return &id(db).owner; },
[&]( account_id_type id, const operation& op ){ [&]( account_id_type id, const operation& op ){
return db.get_account_custom_authorities(id, op); }, return db.get_account_custom_authorities(id, op); },
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ),
db.get_global_properties().parameters.max_authority_depth, db.get_global_properties().parameters.max_authority_depth,
true, /* allow committee */ true, /* allow committee */
available_active_approvals, available_active_approvals,

View file

@ -53,25 +53,38 @@ struct operation_validator
struct operation_get_required_auth struct operation_get_required_auth
{ {
typedef void result_type; using result_type = void;
flat_set<account_id_type>& active; flat_set<account_id_type>& active;
flat_set<account_id_type>& owner; flat_set<account_id_type>& owner;
vector<authority>& other; vector<authority>& other;
bool ignore_custom_op_reqd_auths;
operation_get_required_auth( flat_set<account_id_type>& a, operation_get_required_auth( flat_set<account_id_type>& a,
flat_set<account_id_type>& own, flat_set<account_id_type>& own,
vector<authority>& oth ):active(a),owner(own),other(oth){} vector<authority>& oth,
bool ignore_custom_operation_required_auths )
: active( a ), owner( own ), other( oth ),
ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths )
{}
template<typename T> template<typename T>
void operator()( const T& v )const void operator()( const T& v ) const {
{
active.insert( v.fee_payer() ); active.insert( v.fee_payer() );
v.get_required_active_authorities( active ); v.get_required_active_authorities( active );
v.get_required_owner_authorities( owner ); v.get_required_owner_authorities( owner );
v.get_required_authorities( other ); v.get_required_authorities( other );
} }
void operator()( const custom_operation& op ) const {
active.insert( op.fee_payer() );
if( !ignore_custom_op_reqd_auths ) {
op.get_required_active_authorities( active );
op.get_required_owner_authorities( owner );
op.get_required_authorities( other );
}
}
}; };
void operation_validate( const operation& op ) void operation_validate( const operation& op )
@ -82,9 +95,10 @@ void operation_validate( const operation& op )
void operation_get_required_authorities( const operation& op, void operation_get_required_authorities( const operation& op,
flat_set<account_id_type>& active, flat_set<account_id_type>& active,
flat_set<account_id_type>& owner, flat_set<account_id_type>& owner,
vector<authority>& other ) vector<authority>& other,
bool ignore_custom_operation_required_auths )
{ {
op.visit( operation_get_required_auth( active, owner, other ) ); op.visit( operation_get_required_auth( active, owner, other, ignore_custom_operation_required_auths ) );
} }
} } // namespace graphene::chain } } // namespace graphene::chain

View file

@ -95,10 +95,13 @@ void transaction::set_reference_block( const block_id_type& reference_block )
ref_block_prefix = reference_block._hash[1]; ref_block_prefix = reference_block._hash[1];
} }
void transaction::get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const void transaction::get_required_authorities( flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other,
bool ignore_custom_operation_required_auths )const
{ {
for( const auto& op : operations ) for ( const auto& op : operations )
operation_get_required_authorities( op, active, owner, other ); operation_get_required_authorities( op, active, owner, other, ignore_custom_operation_required_auths );
} }
@ -249,8 +252,9 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom, const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion_depth, uint32_t max_recursion_depth,
bool allow_committe, bool allow_committee,
const flat_set<account_id_type>& active_aprovals, const flat_set<account_id_type>& active_aprovals,
const flat_set<account_id_type>& owner_approvals ) const flat_set<account_id_type>& owner_approvals )
{ try { { try {
@ -276,7 +280,8 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
for( const auto& op : ops ) { for( const auto& op : ops ) {
flat_set<account_id_type> operation_required_active; flat_set<account_id_type> operation_required_active;
operation_get_required_authorities( op, operation_required_active, required_owner, other ); operation_get_required_authorities( op, operation_required_active, required_owner, other,
ignore_custom_operation_required_auths );
auto itr = operation_required_active.begin(); auto itr = operation_required_active.begin();
while ( itr != operation_required_active.end() ) { while ( itr != operation_required_active.end() ) {
@ -289,7 +294,7 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
required_active.insert( operation_required_active.begin(), operation_required_active.end() ); required_active.insert( operation_required_active.begin(), operation_required_active.end() );
} }
if( !allow_committe ) if( !allow_committee )
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(), GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
invalid_committee_approval, "Committee account may only propose transactions" ); invalid_committee_approval, "Committee account may only propose transactions" );
@ -349,6 +354,7 @@ set<public_key_type> signed_transaction::get_required_signatures(
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom, const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_authorities,
uint32_t max_recursion_depth )const uint32_t max_recursion_depth )const
{ {
flat_set<account_id_type> required_active; flat_set<account_id_type> required_active;
@ -370,7 +376,7 @@ set<public_key_type> signed_transaction::get_required_signatures(
for( const auto& op : operations ) { for( const auto& op : operations ) {
flat_set<account_id_type> operation_required_active; flat_set<account_id_type> operation_required_active;
operation_get_required_authorities( op, operation_required_active, required_owner, other ); operation_get_required_authorities( op, operation_required_active, required_owner, other, ignore_custom_operation_required_authorities );
auto itr = operation_required_active.begin(); auto itr = operation_required_active.begin();
while ( itr != operation_required_active.end() ) { while ( itr != operation_required_active.end() ) {
@ -383,7 +389,7 @@ set<public_key_type> signed_transaction::get_required_signatures(
required_active.insert( operation_required_active.begin(), operation_required_active.end() ); required_active.insert( operation_required_active.begin(), operation_required_active.end() );
} }
for( const auto& auth : other ) for (const auto& auth : other)
s.check_authority(&auth); s.check_authority(&auth);
for( auto& owner : required_owner ) for( auto& owner : required_owner )
s.check_authority( get_owner( owner ) ); s.check_authority( get_owner( owner ) );
@ -407,10 +413,12 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom, const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion uint32_t max_recursion
) const ) const
{ {
set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner, get_custom, max_recursion ); set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner, get_custom,
ignore_custom_operation_required_auths, max_recursion );
flat_set< public_key_type > result( s.begin(), s.end() ); flat_set< public_key_type > result( s.begin(), s.end() );
for( const public_key_type& k : s ) for( const public_key_type& k : s )
@ -418,7 +426,8 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
result.erase( k ); result.erase( k );
try try
{ {
graphene::chain::verify_authority( operations, result, get_active, get_owner, get_custom, max_recursion ); graphene::chain::verify_authority( operations, result, get_active, get_owner, get_custom,
ignore_custom_operation_required_auths, max_recursion );
continue; // element stays erased if verify_authority is ok continue; // element stays erased if verify_authority is ok
} }
catch( const tx_missing_owner_auth& e ) {} catch( const tx_missing_owner_auth& e ) {}
@ -434,6 +443,7 @@ void signed_transaction::verify_authority(
const std::function<const authority*(account_id_type)>& get_active, const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner, const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom, const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion )const uint32_t max_recursion )const
{ try { { try {
graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_custom, max_recursion ); graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_custom, max_recursion );

View file

@ -33,6 +33,7 @@
#include <graphene/chain/evaluator.hpp> #include <graphene/chain/evaluator.hpp>
#include <graphene/chain/operation_history_object.hpp> #include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp> #include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/chain/hardfork.hpp>
#include <fc/smart_ref_impl.hpp> #include <fc/smart_ref_impl.hpp>
#include <fc/thread/thread.hpp> #include <fc/thread/thread.hpp>
@ -123,12 +124,15 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
// get the set of accounts this operation applies to // get the set of accounts this operation applies to
flat_set<account_id_type> impacted; flat_set<account_id_type> impacted;
vector<authority> other; vector<authority> other;
operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here // fee payer is added here
operation_get_required_authorities( op.op, impacted, impacted, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
if( op.op.which() == operation::tag< account_create_operation >::value ) if( op.op.which() == operation::tag< account_create_operation >::value )
impacted.insert( op.result.get<object_id_type>() ); impacted.insert( op.result.get<object_id_type>() );
else else
graphene::chain::operation_get_impacted_accounts( op.op, impacted ); graphene::chain::operation_get_impacted_accounts( op.op, impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(db.head_block_time()) );
if( op.op.which() == operation::tag< lottery_end_operation >::value ) if( op.op.which() == operation::tag< lottery_end_operation >::value )
{ {
auto lop = op.op.get< lottery_end_operation >(); auto lop = op.op.get< lottery_end_operation >();

View file

@ -25,7 +25,7 @@
#include <graphene/elasticsearch/elasticsearch_plugin.hpp> #include <graphene/elasticsearch/elasticsearch_plugin.hpp>
#include <graphene/chain/impacted.hpp> #include <graphene/chain/impacted.hpp>
#include <graphene/chain/account_evaluator.hpp> #include <graphene/chain/account_evaluator.hpp>
#include <fc/smart_ref_impl.hpp> #include <graphene/chain/hardfork.hpp>
#include <curl/curl.h> #include <curl/curl.h>
namespace graphene { namespace elasticsearch { namespace graphene { namespace elasticsearch {
@ -157,12 +157,15 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b
// get the set of accounts this operation applies to // get the set of accounts this operation applies to
flat_set<account_id_type> impacted; flat_set<account_id_type> impacted;
vector<authority> other; vector<authority> other;
operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here // fee_payer is added here
operation_get_required_authorities( op.op, impacted, impacted, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
if( op.op.which() == operation::tag< account_create_operation >::value ) if( op.op.which() == operation::tag< account_create_operation >::value )
impacted.insert( op.result.get<object_id_type>() ); impacted.insert( op.result.get<object_id_type>() );
else else
graphene::chain::operation_get_impacted_accounts( op.op, impacted ); operation_get_impacted_accounts( op.op, impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
for( auto& a : other ) for( auto& a : other )
for( auto& item : a.account_auths ) for( auto& item : a.account_auths )

View file

@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account )
{ {
vector<authority> other; vector<authority> other;
flat_set<account_id_type> active_set, owner_set; flat_set<account_id_type> active_set, owner_set;
operation_get_required_authorities(op,active_set,owner_set,other); operation_get_required_authorities(op, active_set, owner_set, other, false);
BOOST_CHECK_EQUAL(active_set.size(), 1lu); BOOST_CHECK_EQUAL(active_set.size(), 1lu);
BOOST_CHECK_EQUAL(owner_set.size(), 0lu); BOOST_CHECK_EQUAL(owner_set.size(), 0lu);
BOOST_CHECK_EQUAL(other.size(), 0lu); BOOST_CHECK_EQUAL(other.size(), 0lu);
@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE( proposed_single_account )
active_set.clear(); active_set.clear();
other.clear(); other.clear();
operation_get_required_authorities(op.proposed_ops.front().op,active_set,owner_set,other); operation_get_required_authorities(op.proposed_ops.front().op, active_set, owner_set, other, false);
BOOST_CHECK_EQUAL(active_set.size(), 1lu); BOOST_CHECK_EQUAL(active_set.size(), 1lu);
BOOST_CHECK_EQUAL(owner_set.size(), 0lu); BOOST_CHECK_EQUAL(owner_set.size(), 0lu);
BOOST_CHECK_EQUAL(other.size(), 0lu); BOOST_CHECK_EQUAL(other.size(), 0lu);
@ -1055,7 +1055,7 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture )
flat_set<account_id_type> active_set, owner_set; flat_set<account_id_type> active_set, owner_set;
vector<authority> others; vector<authority> others;
trx.get_required_authorities( active_set, owner_set, others ); trx.get_required_authorities(active_set, owner_set, others, false);
PUSH_TX( db, trx, skip ); PUSH_TX( db, trx, skip );
@ -1204,9 +1204,12 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture )
) -> bool ) -> bool
{ {
//wdump( (tx)(available_keys) ); //wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom ); set<public_key_type> result_set = tx.get_required_signatures(db.get_chain_id(), available_keys,
//wdump( (result_set)(ref_set) ); get_active, get_owner, get_custom, false);
return result_set == ref_set; set<public_key_type> result_set2 = tx.get_required_signatures(db.get_chain_id(), available_keys,
get_active, get_owner, get_custom, true);
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ; } ;
set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) ); set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) );
@ -1326,9 +1329,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
) -> bool ) -> bool
{ {
//wdump( (tx)(available_keys) ); //wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom ); set<public_key_type> result_set = tx.get_required_signatures(db.get_chain_id(), available_keys,
//wdump( (result_set)(ref_set) ); get_active, get_owner, get_custom, false);
return result_set == ref_set; set<public_key_type> result_set2 = tx.get_required_signatures(db.get_chain_id(), available_keys,
get_active, get_owner, get_custom, true);
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ; } ;
auto chk_min = [&]( auto chk_min = [&](
@ -1338,9 +1344,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
) -> bool ) -> bool
{ {
//wdump( (tx)(available_keys) ); //wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, get_custom ); set<public_key_type> result_set = tx.minimize_required_signatures(db.get_chain_id(), available_keys,
//wdump( (result_set)(ref_set) ); get_active, get_owner, get_custom, false);
return result_set == ref_set; set<public_key_type> result_set2 = tx.minimize_required_signatures(db.get_chain_id(), available_keys,
get_active, get_owner, get_custom, true);
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ; } ;
set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) ); set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) );
@ -1357,9 +1366,13 @@ 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( 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 } ) ); 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, get_custom ), fc::exception ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, false ),
fc::exception );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, true ),
fc::exception );
sign( tx, alice_private_key ); sign( tx, alice_private_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, false );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, get_custom, true );
} }
catch(fc::exception& e) catch(fc::exception& e)
{ {