Skip to content

Commit

Permalink
add top witness transaction privilege for flood condition
Browse files Browse the repository at this point in the history
The mechanism makes sure top witness operations are still passing through, even if normal transactions would be delayed or even dropped.
  • Loading branch information
ABW committed Oct 21, 2024
1 parent 095804f commit 0d4448f
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 13 deletions.
13 changes: 11 additions & 2 deletions libraries/chain/database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,8 +743,17 @@ void database::_push_transaction(const std::shared_ptr<full_transaction_type>& f

auto temp_session = start_undo_session();
_apply_transaction(full_transaction);
_pending_tx.push_back(full_transaction);
_pending_tx_size += full_transaction->get_transaction_size();
if( full_transaction->check_privilege() && is_validating_one_tx() )
{
// reuse mechanism used for forks to make sure privileged transaction gets rewritten to the start
// of pendling list and therefore won't be delayed indefinitely by the transaction flood
_popped_tx.emplace_front( full_transaction );
}
else
{
_pending_tx.push_back( full_transaction );
_pending_tx_size += full_transaction->get_transaction_size();
}

// The transaction applied successfully. Merge its changes into the pending block session.
temp_session.squash();
Expand Down
3 changes: 3 additions & 0 deletions libraries/chain/include/hive/chain/full_transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ struct full_transaction_type

mutable bool is_packed_in_legacy_format = false;
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
mutable bool is_privileged = false; // true if transaction is supposed to be put in front of pending list

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

const serialized_transaction_data& get_serialized_transaction() const;
size_t get_transaction_size() const;
int64_t get_rc_cost() const { return rc_cost; }
bool check_privilege() const { return is_privileged; }

template <class DataStream>
void dump_serialized_transaction(DataStream& datastream) const
Expand Down
4 changes: 2 additions & 2 deletions libraries/chain/include/hive/chain/rc/rc_utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ class resource_credits
const rc_block_info& get_block_info() const { return block_info; }

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

// registers evaluator for custom rc operations
void initialize_evaluators();
Expand Down
41 changes: 32 additions & 9 deletions libraries/chain/rc/rc_utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,14 +457,14 @@ void resource_credits::update_rc_for_custom_action( std::function<void()>&& call
update_account_after_vest_change( account, now );
}

void resource_credits::use_account_rcs( int64_t rc )
bool resource_credits::use_account_rcs( int64_t rc )
{
const account_name_type& account_name = tx_info.payer;
if( account_name == account_name_type() )
{
if( db.is_in_control() )
HIVE_ASSERT( false, plugin_exception, "Tried to execute transaction with no resource user", );
return;
return false;
}

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

try
{

db.modify( account, [&]( account_object& acc )
{
acc.rc_manabar.regenerate_mana< true >( mbparams, dgpo.time.sec_since_epoch() );
Expand All @@ -490,11 +490,32 @@ void resource_credits::use_account_rcs( int64_t rc )
uint64_t blocks_in_mempool = db._pending_tx_size / dgpo.maximum_block_size;
if( blocks_in_mempool > flood_level )
{
uint128_t sc = ( blocks_in_mempool - flood_level ) * flood_surcharge;
sc *= rc;
sc /= HIVE_100_PERCENT;
FC_ASSERT( sc <= static_cast< uint64_t >( std::numeric_limits<int64_t>::max() - rc ) );
surcharge = static_cast< int64_t >( sc );
// check if transaction might be privileged - top witness transaction containing only
// one witness related operation is privileged because its execution might fix flood problem
// and is at the very least important for chain to function properly
if( tx_info.op.valid() )
{
auto op = tx_info.op.value();
switch( op )
{
case operation::tag<feed_publish_operation>::value:
case operation::tag<witness_update_operation>::value:
case operation::tag<witness_set_properties_operation>::value:
{
const auto* witness = db.find_witness( tx_info.payer );
is_privileged = ( witness != nullptr ) && ( witness->schedule == witness_object::elected );
} break;
}
}

if( !is_privileged )
{
uint128_t sc = ( blocks_in_mempool - flood_level ) * flood_surcharge;
sc *= rc;
sc /= HIVE_100_PERCENT;
FC_ASSERT( sc <= static_cast< uint64_t >( std::numeric_limits<int64_t>::max() - rc ) );
surcharge = static_cast< int64_t >( sc );
}
}
}
bool has_mana = acc.rc_manabar.has_mana( rc + surcharge );
Expand Down Expand Up @@ -547,6 +568,7 @@ void resource_credits::use_account_rcs( int64_t rc )
tx_info.rc = acc.rc_manabar.current_mana;
} );
} FC_CAPTURE_AND_RETHROW( (tx_info) )
return is_privileged;
}

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

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

if( db.is_validating_block() || db.is_replaying_block() )
{
Expand Down

0 comments on commit 0d4448f

Please sign in to comment.