Add gap pending epoch open blocks to unchecked (#3108)
* Add gap pending epoch open blocks to unchecked to address found by @Srayman full bootstrap slowdown * Expanding test to show that these epochs are picked up when a pending entry comes in. * Using block_processor.flush () instead of ASSERT_TIMELY. * More checks in ledger.epoch_open_pending test * Do not check last epoch blocks * Explain settings Co-authored-by: clemahieu <clemahieu@gmail.com>
This commit is contained in:
parent
a314b1ac5e
commit
900e89e18f
10 changed files with 88 additions and 16 deletions
|
|
@ -2631,13 +2631,44 @@ TEST (ledger, successor_epoch)
|
|||
|
||||
TEST (ledger, epoch_open_pending)
|
||||
{
|
||||
nano::block_builder builder;
|
||||
nano::system system (1);
|
||||
auto & node1 (*system.nodes[0]);
|
||||
nano::work_pool pool (std::numeric_limits<unsigned>::max ());
|
||||
nano::keypair key1;
|
||||
nano::state_block epoch_open (key1.pub, 0, 0, 0, node1.ledger.epoch_link (nano::epoch::epoch_1), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *pool.generate (key1.pub));
|
||||
auto transaction (node1.store.tx_begin_write ());
|
||||
ASSERT_EQ (nano::process_result::block_position, node1.ledger.process (transaction, epoch_open).code);
|
||||
auto epoch_open = builder.state ()
|
||||
.account (key1.pub)
|
||||
.previous (0)
|
||||
.representative (0)
|
||||
.balance (0)
|
||||
.link (node1.ledger.epoch_link (nano::epoch::epoch_1))
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*pool.generate (key1.pub))
|
||||
.build_shared ();
|
||||
auto process_result (node1.ledger.process (node1.store.tx_begin_write (), *epoch_open));
|
||||
ASSERT_EQ (nano::process_result::gap_epoch_open_pending, process_result.code);
|
||||
ASSERT_EQ (nano::signature_verification::valid_epoch, process_result.verified);
|
||||
node1.block_processor.add (epoch_open);
|
||||
node1.block_processor.flush ();
|
||||
ASSERT_FALSE (node1.ledger.block_exists (epoch_open->hash ()));
|
||||
// Open block should be inserted into unchecked
|
||||
auto blocks (node1.store.unchecked_get (node1.store.tx_begin_read (), nano::hash_or_account (epoch_open->account ()).hash));
|
||||
ASSERT_EQ (blocks.size (), 1);
|
||||
ASSERT_EQ (blocks[0].block->full_hash (), epoch_open->full_hash ());
|
||||
ASSERT_EQ (blocks[0].verified, nano::signature_verification::valid_epoch);
|
||||
// New block to process epoch open
|
||||
auto send1 = builder.state ()
|
||||
.account (nano::genesis_account)
|
||||
.previous (nano::genesis_hash)
|
||||
.representative (nano::genesis_account)
|
||||
.balance (nano::genesis_amount - 100)
|
||||
.link (key1.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*pool.generate (nano::genesis_hash))
|
||||
.build_shared ();
|
||||
node1.block_processor.add (send1);
|
||||
node1.block_processor.flush ();
|
||||
ASSERT_TRUE (node1.ledger.block_exists (epoch_open->hash ()));
|
||||
}
|
||||
|
||||
TEST (ledger, block_hash_account_conflict)
|
||||
|
|
|
|||
|
|
@ -248,6 +248,8 @@ std::string nano::error_process_messages::message (int ev) const
|
|||
return "Gap previous block";
|
||||
case nano::error_process::gap_source:
|
||||
return "Gap source block";
|
||||
case nano::error_process::gap_epoch_open_pending:
|
||||
return "Gap pending for open epoch block";
|
||||
case nano::error_process::opened_burn_account:
|
||||
return "Burning account";
|
||||
case nano::error_process::balance_mismatch:
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ enum class error_process
|
|||
unreceivable, // Source block doesn't exist or has already been received
|
||||
gap_previous, // Block marked as previous is unknown
|
||||
gap_source, // Block marked as source is unknown
|
||||
gap_epoch_open_pending, // Block marked as pending blocks required for epoch open block are unknown
|
||||
opened_burn_account, // The impossible happened, someone found the private key associated with the public key '0'.
|
||||
balance_mismatch, // Balance and amount delta don't match
|
||||
block_position, // This block cannot follow the previous block
|
||||
|
|
|
|||
|
|
@ -389,6 +389,15 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction
|
|||
events_a.events.emplace_back ([this, hash, block = info_a.block, result, watch_work_a, origin_a](nano::transaction const & post_event_transaction_a) { process_live (post_event_transaction_a, hash, block, result, watch_work_a, origin_a); });
|
||||
}
|
||||
queue_unchecked (transaction_a, hash);
|
||||
/* For send blocks check epoch open unchecked (gap pending).
|
||||
For state blocks check only send subtype and only if block epoch is not last epoch.
|
||||
If epoch is last, then pending entry shouldn't trigger same epoch open block for destination account. */
|
||||
if (block->type () == nano::block_type::send || (block->type () == nano::block_type::state && block->sideband ().details.is_send && std::underlying_type_t<nano::epoch> (block->sideband ().details.epoch) < std::underlying_type_t<nano::epoch> (nano::epoch::max)))
|
||||
{
|
||||
/* block->destination () for legacy send blocks
|
||||
block->link () for state blocks (send subtype) */
|
||||
queue_unchecked (transaction_a, block->destination ().is_zero () ? block->link () : block->destination ());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nano::process_result::gap_previous:
|
||||
|
|
@ -431,6 +440,24 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction
|
|||
node.stats.inc (nano::stat::type::ledger, nano::stat::detail::gap_source);
|
||||
break;
|
||||
}
|
||||
case nano::process_result::gap_epoch_open_pending:
|
||||
{
|
||||
if (node.config.logging.ledger_logging ())
|
||||
{
|
||||
node.logger.try_log (boost::str (boost::format ("Gap pending entries for epoch open: %1%") % hash.to_string ()));
|
||||
}
|
||||
info_a.verified = result.verified;
|
||||
if (info_a.modified == 0)
|
||||
{
|
||||
info_a.modified = nano::seconds_since_epoch ();
|
||||
}
|
||||
|
||||
nano::unchecked_key unchecked_key (block->account (), hash); // Specific unchecked key starting with epoch open block account public key
|
||||
node.store.unchecked_put (transaction_a, unchecked_key, info_a);
|
||||
|
||||
node.stats.inc (nano::stat::type::ledger, nano::stat::detail::gap_source);
|
||||
break;
|
||||
}
|
||||
case nano::process_result::old:
|
||||
{
|
||||
if (node.config.logging.ledger_duplicate_logging ())
|
||||
|
|
@ -537,18 +564,18 @@ void nano::block_processor::process_old (nano::transaction const & transaction_a
|
|||
}
|
||||
}
|
||||
|
||||
void nano::block_processor::queue_unchecked (nano::write_transaction const & transaction_a, nano::block_hash const & hash_a)
|
||||
void nano::block_processor::queue_unchecked (nano::write_transaction const & transaction_a, nano::hash_or_account const & hash_or_account_a)
|
||||
{
|
||||
auto unchecked_blocks (node.store.unchecked_get (transaction_a, hash_a));
|
||||
auto unchecked_blocks (node.store.unchecked_get (transaction_a, hash_or_account_a.hash));
|
||||
for (auto & info : unchecked_blocks)
|
||||
{
|
||||
if (!node.flags.disable_block_processor_unchecked_deletion)
|
||||
{
|
||||
node.store.unchecked_del (transaction_a, nano::unchecked_key (hash_a, info.block->hash ()));
|
||||
node.store.unchecked_del (transaction_a, nano::unchecked_key (hash_or_account_a, info.block->hash ()));
|
||||
}
|
||||
add (info, true);
|
||||
}
|
||||
node.gap_cache.erase (hash_a);
|
||||
node.gap_cache.erase (hash_or_account_a.hash);
|
||||
}
|
||||
|
||||
void nano::block_processor::requeue_invalid (nano::block_hash const & hash_a, nano::unchecked_info const & info_a)
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public:
|
|||
static std::chrono::milliseconds constexpr confirmation_request_delay{ 1500 };
|
||||
|
||||
private:
|
||||
void queue_unchecked (nano::write_transaction const &, nano::block_hash const &);
|
||||
void queue_unchecked (nano::write_transaction const &, nano::hash_or_account const &);
|
||||
void process_batch (nano::unique_lock<nano::mutex> &);
|
||||
void process_live (nano::transaction const &, nano::block_hash const &, std::shared_ptr<nano::block> const &, nano::process_return const &, const bool = false, nano::block_origin const = nano::block_origin::remote);
|
||||
void process_old (nano::transaction const &, std::shared_ptr<nano::block> const &, nano::block_origin const);
|
||||
|
|
|
|||
|
|
@ -3107,6 +3107,11 @@ void nano::json_handler::process ()
|
|||
rpc_l->ec = nano::error_process::block_position;
|
||||
break;
|
||||
}
|
||||
case nano::process_result::gap_epoch_open_pending:
|
||||
{
|
||||
rpc_l->ec = nano::error_process::gap_epoch_open_pending;
|
||||
break;
|
||||
}
|
||||
case nano::process_result::fork:
|
||||
{
|
||||
const bool force = rpc_l->request.get<bool> ("force", false);
|
||||
|
|
|
|||
|
|
@ -806,10 +806,8 @@ public:
|
|||
{
|
||||
parallel_traversal<nano::uint512_t> (
|
||||
[&action_a, this](nano::uint512_t const & start, nano::uint512_t const & end, bool const is_last) {
|
||||
nano::uint512_union union_start (start);
|
||||
nano::uint512_union union_end (end);
|
||||
nano::unchecked_key key_start (union_start.uint256s[0].number (), union_start.uint256s[1].number ());
|
||||
nano::unchecked_key key_end (union_end.uint256s[0].number (), union_end.uint256s[1].number ());
|
||||
nano::unchecked_key key_start (start);
|
||||
nano::unchecked_key key_end (end);
|
||||
auto transaction (this->tx_begin_read ());
|
||||
action_a (transaction, this->unchecked_begin (transaction, key_start), !is_last ? this->unchecked_begin (transaction, key_end) : this->unchecked_end ());
|
||||
});
|
||||
|
|
|
|||
|
|
@ -822,12 +822,18 @@ nano::wallet_id nano::random_wallet_id ()
|
|||
return wallet_id;
|
||||
}
|
||||
|
||||
nano::unchecked_key::unchecked_key (nano::block_hash const & previous_a, nano::block_hash const & hash_a) :
|
||||
previous (previous_a),
|
||||
nano::unchecked_key::unchecked_key (nano::hash_or_account const & previous_a, nano::block_hash const & hash_a) :
|
||||
previous (previous_a.hash),
|
||||
hash (hash_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::unchecked_key::unchecked_key (nano::uint512_union const & union_a) :
|
||||
previous (union_a.uint256s[0].number ()),
|
||||
hash (union_a.uint256s[1].number ())
|
||||
{
|
||||
}
|
||||
|
||||
bool nano::unchecked_key::deserialize (nano::stream & stream_a)
|
||||
{
|
||||
auto error (false);
|
||||
|
|
|
|||
|
|
@ -171,7 +171,8 @@ class unchecked_key final
|
|||
{
|
||||
public:
|
||||
unchecked_key () = default;
|
||||
unchecked_key (nano::block_hash const &, nano::block_hash const &);
|
||||
unchecked_key (nano::hash_or_account const &, nano::block_hash const &);
|
||||
unchecked_key (nano::uint512_union const &);
|
||||
bool deserialize (nano::stream &);
|
||||
bool operator== (nano::unchecked_key const &) const;
|
||||
nano::block_hash const & key () const;
|
||||
|
|
@ -312,6 +313,7 @@ enum class process_result
|
|||
unreceivable, // Source block doesn't exist, has already been received, or requires an account upgrade (epoch blocks)
|
||||
gap_previous, // Block marked as previous is unknown
|
||||
gap_source, // Block marked as source is unknown
|
||||
gap_epoch_open_pending, // Block marked as pending blocks required for epoch open block are unknown
|
||||
opened_burn_account, // The impossible happened, someone found the private key associated with the public key '0'.
|
||||
balance_mismatch, // Balance and amount delta don't match
|
||||
representative_mismatch, // Representative is changed when it is not allowed
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a)
|
|||
if (result.code == nano::process_result::progress)
|
||||
{
|
||||
bool pending_exists = ledger.store.pending_any (transaction, block_a.hashables.account);
|
||||
result.code = pending_exists ? nano::process_result::progress : nano::process_result::block_position;
|
||||
result.code = pending_exists ? nano::process_result::progress : nano::process_result::gap_epoch_open_pending;
|
||||
}
|
||||
}
|
||||
if (result.code == nano::process_result::progress)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue