Skip to content

Commit 0d4448f

Browse files
author
ABW
committed
add top witness transaction privilege for flood condition
The mechanism makes sure top witness operations are still passing through, even if normal transactions would be delayed or even dropped.
1 parent 095804f commit 0d4448f

File tree

4 files changed

+48
-13
lines changed

4 files changed

+48
-13
lines changed

libraries/chain/database.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,17 @@ void database::_push_transaction(const std::shared_ptr<full_transaction_type>& f
743743

744744
auto temp_session = start_undo_session();
745745
_apply_transaction(full_transaction);
746-
_pending_tx.push_back(full_transaction);
747-
_pending_tx_size += full_transaction->get_transaction_size();
746+
if( full_transaction->check_privilege() && is_validating_one_tx() )
747+
{
748+
// reuse mechanism used for forks to make sure privileged transaction gets rewritten to the start
749+
// of pendling list and therefore won't be delayed indefinitely by the transaction flood
750+
_popped_tx.emplace_front( full_transaction );
751+
}
752+
else
753+
{
754+
_pending_tx.push_back( full_transaction );
755+
_pending_tx_size += full_transaction->get_transaction_size();
756+
}
748757

749758
// The transaction applied successfully. Merge its changes into the pending block session.
750759
temp_session.squash();

libraries/chain/include/hive/chain/full_transaction.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ struct full_transaction_type
8484

8585
mutable bool is_packed_in_legacy_format = false;
8686
mutable bool is_in_cache = false; // true if this is tracked in the global transaction cache; if so, we need to remove ourselves upon garbage collection
87+
mutable bool is_privileged = false; // true if transaction is supposed to be put in front of pending list
8788

8889
mutable std::atomic<bool> has_merkle_digest = { false };
8990
mutable std::atomic<bool> has_legacy_transaction_message_hash = { false };
@@ -124,10 +125,12 @@ struct full_transaction_type
124125
void precompute_validation(std::function<void(const hive::protocol::operation& op, bool post)> notify = std::function<void(const hive::protocol::operation&, bool)>()) const;
125126
void validate(std::function<void(const hive::protocol::operation& op, bool post)> notify = std::function<void(const hive::protocol::operation&, bool)>()) const;
126127
void set_rc_cost( int64_t cost ) const { rc_cost = cost; } // can only be called under main lock of write thread
128+
void set_privilege() const { is_privileged = true; } // marks transaction as high priority to be put in front of pending
127129

128130
const serialized_transaction_data& get_serialized_transaction() const;
129131
size_t get_transaction_size() const;
130132
int64_t get_rc_cost() const { return rc_cost; }
133+
bool check_privilege() const { return is_privileged; }
131134

132135
template <class DataStream>
133136
void dump_serialized_transaction(DataStream& datastream) const

libraries/chain/include/hive/chain/rc/rc_utility.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ class resource_credits
138138
const rc_block_info& get_block_info() const { return block_info; }
139139

140140
private:
141-
// consumes RC mana from payer account (or throws exception if not enough), supplements tx_info with RC mana
142-
void use_account_rcs( int64_t rc );
141+
// consumes RC mana from payer account (or throws exception if not enough), supplements tx_info with RC mana, returns true for high priority op
142+
bool use_account_rcs( int64_t rc );
143143

144144
// registers evaluator for custom rc operations
145145
void initialize_evaluators();

libraries/chain/rc/rc_utility.cpp

+32-9
Original file line numberDiff line numberDiff line change
@@ -457,14 +457,14 @@ void resource_credits::update_rc_for_custom_action( std::function<void()>&& call
457457
update_account_after_vest_change( account, now );
458458
}
459459

460-
void resource_credits::use_account_rcs( int64_t rc )
460+
bool resource_credits::use_account_rcs( int64_t rc )
461461
{
462462
const account_name_type& account_name = tx_info.payer;
463463
if( account_name == account_name_type() )
464464
{
465465
if( db.is_in_control() )
466466
HIVE_ASSERT( false, plugin_exception, "Tried to execute transaction with no resource user", );
467-
return;
467+
return false;
468468
}
469469

470470
const account_object& account = db.get_account( account_name );
@@ -476,10 +476,10 @@ void resource_credits::use_account_rcs( int64_t rc )
476476
tx_info.max = max_mana;
477477
tx_info.rc = account.rc_manabar.current_mana; // initialize before regen in case of exception
478478
mbparams.regen_time = HIVE_RC_REGEN_TIME;
479+
bool is_privileged = false;
479480

480481
try
481482
{
482-
483483
db.modify( account, [&]( account_object& acc )
484484
{
485485
acc.rc_manabar.regenerate_mana< true >( mbparams, dgpo.time.sec_since_epoch() );
@@ -490,11 +490,32 @@ void resource_credits::use_account_rcs( int64_t rc )
490490
uint64_t blocks_in_mempool = db._pending_tx_size / dgpo.maximum_block_size;
491491
if( blocks_in_mempool > flood_level )
492492
{
493-
uint128_t sc = ( blocks_in_mempool - flood_level ) * flood_surcharge;
494-
sc *= rc;
495-
sc /= HIVE_100_PERCENT;
496-
FC_ASSERT( sc <= static_cast< uint64_t >( std::numeric_limits<int64_t>::max() - rc ) );
497-
surcharge = static_cast< int64_t >( sc );
493+
// check if transaction might be privileged - top witness transaction containing only
494+
// one witness related operation is privileged because its execution might fix flood problem
495+
// and is at the very least important for chain to function properly
496+
if( tx_info.op.valid() )
497+
{
498+
auto op = tx_info.op.value();
499+
switch( op )
500+
{
501+
case operation::tag<feed_publish_operation>::value:
502+
case operation::tag<witness_update_operation>::value:
503+
case operation::tag<witness_set_properties_operation>::value:
504+
{
505+
const auto* witness = db.find_witness( tx_info.payer );
506+
is_privileged = ( witness != nullptr ) && ( witness->schedule == witness_object::elected );
507+
} break;
508+
}
509+
}
510+
511+
if( !is_privileged )
512+
{
513+
uint128_t sc = ( blocks_in_mempool - flood_level ) * flood_surcharge;
514+
sc *= rc;
515+
sc /= HIVE_100_PERCENT;
516+
FC_ASSERT( sc <= static_cast< uint64_t >( std::numeric_limits<int64_t>::max() - rc ) );
517+
surcharge = static_cast< int64_t >( sc );
518+
}
498519
}
499520
}
500521
bool has_mana = acc.rc_manabar.has_mana( rc + surcharge );
@@ -547,6 +568,7 @@ void resource_credits::use_account_rcs( int64_t rc )
547568
tx_info.rc = acc.rc_manabar.current_mana;
548569
} );
549570
} FC_CAPTURE_AND_RETHROW( (tx_info) )
571+
return is_privileged;
550572
}
551573

552574
bool resource_credits::has_expired_delegation( const account_object& account ) const
@@ -775,7 +797,8 @@ void resource_credits::finalize_transaction( const full_transaction_type& full_t
775797

776798
// note: since transaction can influence amount of RC the payer has, we can't check if the payer has
777799
// enough RC mana prior to actual execution of transaction
778-
use_account_rcs( total_cost );
800+
if( use_account_rcs( total_cost ) )
801+
full_tx.set_privilege();
779802

780803
if( db.is_validating_block() || db.is_replaying_block() )
781804
{

0 commit comments

Comments
 (0)