From 89dbbd9a36770f7a724e11aff7e3719376833fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 20 May 2025 13:31:16 +0200 Subject: [PATCH] Merge pull request #4907 from pwojcikdev/election-sideband-tests Election sideband handling tests --- nano/core_test/active_elections.cpp | 96 +++++++++++++++++++++++++++++ nano/lib/block_sideband.hpp | 19 ++++-- 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/nano/core_test/active_elections.cpp b/nano/core_test/active_elections.cpp index 24f0dfb1d..8838c546c 100644 --- a/nano/core_test/active_elections.cpp +++ b/nano/core_test/active_elections.cpp @@ -164,6 +164,102 @@ TEST (active_elections, confirm_frontier) ASSERT_GT (election2->confirmation_request_count, 0u); } +// Tests that confirming the other side of the initial fork works +TEST (active_elections, confirm_fork) +{ + nano::test::system system; + // One node is enough because we can cast the deciding vote locally. + auto & node = *system.add_node (); + + nano::keypair dest; + nano::state_block_builder builder{}; + + // Base state: genesis → fork_* (two alternatives) + auto fork1 = builder.make_block () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 100) // –100 RAW + .link (dest.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + + auto fork2 = builder.make_block () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 200) // –200 RAW + .link (dest.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + + // Inject both forks live; they’ll merge into a single election + node.process_active (fork1); + + // Wait until the election object exists + std::shared_ptr election; + ASSERT_TIMELY (5s, (election = node.active.election (fork1->qualified_root ())) != nullptr); + + // Cast the decisive final vote for fork2 + auto vote_fork2 = nano::test::make_final_vote (nano::dev::genesis_key, { fork2 }); + node.vote_processor.vote (vote_fork2, + std::make_shared (node, node)); + + node.process_active (fork2); + + // The election should confirm and choose fork2 as the winner + ASSERT_TIMELY (5s, election->confirmed ()); + ASSERT_EQ (fork2->hash (), election->status.winner->hash ()); + + // Ledger view: fork2 cemented, fork1 not + ASSERT_TIMELY (3s, node.block_confirmed (fork2->hash ())); + ASSERT_FALSE (node.block_confirmed (fork1->hash ())); +} + +// Tests that confirming a block that was initially in the fork cache works +TEST (active_elections, confirm_fork_cache) +{ + nano::test::system system; + // One node is enough because we can cast the deciding vote locally. + auto & node = *system.add_node (); + + nano::keypair dest; + nano::state_block_builder builder{}; + + auto fork1 = builder.make_block () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 100) // –100 RAW + .link (dest.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + + // Inject fork1 into the fork cache, this block is without sideband + node.fork_cache.put (fork1->clone ()); + + // Inject both forks live; they’ll merge into a single election + node.process_active (fork1->clone ()); + + // Wait until the election object exists + std::shared_ptr election; + ASSERT_TIMELY (5s, (election = node.active.election (fork1->qualified_root ())) != nullptr); + + // Cast the decisive final vote + auto vote_fork2 = nano::test::make_final_vote (nano::dev::genesis_key, { fork1 }); + node.vote_processor.vote (vote_fork2, + std::make_shared (node, node)); + + // The election should confirm + ASSERT_TIMELY (5s, election->confirmed ()); + ASSERT_EQ (fork1->hash (), election->status.winner->hash ()); + + ASSERT_TIMELY (3s, node.block_confirmed (fork1->hash ())); +} + // TODO: Adjust for new behaviour of bounded buckets TEST (active_elections, DISABLED_keep_local) { diff --git a/nano/lib/block_sideband.hpp b/nano/lib/block_sideband.hpp index 09dcc8a96..4e034d549 100644 --- a/nano/lib/block_sideband.hpp +++ b/nano/lib/block_sideband.hpp @@ -17,14 +17,17 @@ class block_details public: block_details () = default; - block_details (nano::epoch const epoch_a, bool const is_send_a, bool const is_receive_a, bool const is_epoch_a); + block_details (nano::epoch epoch, bool is_send, bool is_receive, bool is_epoch); + static constexpr size_t size () { return 1; } - bool operator== (block_details const & other_a) const; + bool operator== (block_details const &) const; void serialize (nano::stream &) const; bool deserialize (nano::stream &); + +public: nano::epoch epoch{ nano::epoch::epoch_0 }; bool is_send{ false }; bool is_receive{ false }; @@ -38,17 +41,21 @@ public: // Logging void operator() (nano::object_stream &) const; }; -std::string state_subtype (nano::block_details const); +std::string state_subtype (nano::block_details); class block_sideband final { public: block_sideband () = default; - block_sideband (nano::account const &, nano::block_hash const &, nano::amount const &, uint64_t const, nano::seconds_t const local_timestamp, nano::block_details const &, nano::epoch const source_epoch_a); - block_sideband (nano::account const &, nano::block_hash const &, nano::amount const &, uint64_t const, nano::seconds_t const local_timestamp, nano::epoch const epoch_a, bool const is_send, bool const is_receive, bool const is_epoch, nano::epoch const source_epoch_a); + block_sideband (nano::account const & account, nano::block_hash const & successor, nano::amount const & balance, uint64_t height, nano::seconds_t timestamp, nano::block_details const & details, nano::epoch source_epoch); + block_sideband (nano::account const & account, nano::block_hash const & successor, nano::amount const & balance, uint64_t height, nano::seconds_t timestamp, nano::epoch epoch, bool is_send, bool is_receive, bool is_epoch, nano::epoch source_epoch); + void serialize (nano::stream &, nano::block_type) const; bool deserialize (nano::stream &, nano::block_type); + static size_t size (nano::block_type); + +public: nano::block_hash successor{ 0 }; nano::account account{}; nano::amount balance{ 0 }; @@ -60,4 +67,4 @@ public: public: // Logging void operator() (nano::object_stream &) const; }; -} // namespace nano +}