Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix epoch_conflict_confirm test #4779

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions nano/core_test/block_store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1630,11 +1630,8 @@ TEST (block_store, final_vote)
ASSERT_EQ (store->final_vote.count (transaction), 0);
store->final_vote.put (transaction, qualified_root, nano::block_hash (2));
ASSERT_EQ (store->final_vote.count (transaction), 1);
// Clearing with incorrect root shouldn't remove
store->final_vote.clear (transaction, qualified_root.previous ());
ASSERT_EQ (store->final_vote.count (transaction), 1);
// Clearing with correct root should remove
store->final_vote.clear (transaction, qualified_root.root ());
store->final_vote.del (transaction, qualified_root);
ASSERT_EQ (store->final_vote.count (transaction), 0);
}
}
Expand Down
4 changes: 2 additions & 2 deletions nano/core_test/ledger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5482,8 +5482,8 @@ TEST (ledger, migrate_lmdb_to_rocksdb)
ASSERT_FALSE (rocksdb_store.confirmation_height.get (rocksdb_transaction, nano::dev::genesis_key.pub, confirmation_height_info));
ASSERT_EQ (confirmation_height_info.height, 2);
ASSERT_EQ (confirmation_height_info.frontier, send->hash ());
ASSERT_EQ (rocksdb_store.final_vote.get (rocksdb_transaction, nano::root (send->previous ())).size (), 1);
ASSERT_EQ (rocksdb_store.final_vote.get (rocksdb_transaction, nano::root (send->previous ()))[0], nano::block_hash (2));
ASSERT_TRUE (rocksdb_store.final_vote.get (rocksdb_transaction, send->qualified_root ()).has_value ());
ASSERT_EQ (rocksdb_store.final_vote.get (rocksdb_transaction, send->qualified_root ()).value (), nano::block_hash (2));

// Retry migration while rocksdb folder is still present
auto error_on_retry = ledger.migrate_lmdb_to_rocksdb (path);
Expand Down
31 changes: 17 additions & 14 deletions nano/core_test/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2157,6 +2157,13 @@ TEST (node, epoch_conflict_confirm)
nano::keypair key;
nano::keypair epoch_signer (nano::dev::genesis_key);
nano::state_block_builder builder;

// Node 1 is the voting node
// Send sends to an account we control: send -> open -> change
// Send2 sends to an account with public key of the open block
// Epoch open qualified root: (open, 0) on account with the same public key as the hash of the open block
// Epoch open and change have the same root!

auto send = builder.make_block ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
Expand Down Expand Up @@ -2203,34 +2210,30 @@ TEST (node, epoch_conflict_confirm)
.work (*system.work.generate (open->hash ()))
.build ();

// Process initial blocks on node1
ASSERT_TRUE (nano::test::process (node1, { send, send2, open }));

// Confirm open block in node1 to allow generating votes
nano::test::confirm (node1.ledger, open);

// Process initial blocks on node0
ASSERT_TRUE (nano::test::process (node0, { send, send2, open }));
// Process initial blocks
ASSERT_TRUE (nano::test::process (node0, nano::test::clone ({ send, send2, open })));
ASSERT_TRUE (nano::test::process (node1, nano::test::clone ({ send, send2, open })));

// Process conflicting blocks on node 0 as blocks coming from live network
ASSERT_TRUE (nano::test::process_live (node0, { change, epoch_open }));
// Process conflicting blocks on nodes as blocks coming from live network
ASSERT_TRUE (nano::test::process_live (node0, nano::test::clone ({ change, epoch_open })));
ASSERT_TRUE (nano::test::process_live (node1, nano::test::clone ({ change, epoch_open })));

// Ensure blocks were propagated to both nodes
ASSERT_TIMELY (5s, nano::test::exists (node0, { change, epoch_open }));
ASSERT_TIMELY (5s, nano::test::exists (node1, { change, epoch_open }));

// Confirm initial blocks in node1 to allow generating votes later
ASSERT_TRUE (nano::test::start_elections (system, node1, { change, epoch_open, send2 }, true));
nano::test::confirm (node1, { change, epoch_open, send2 });
ASSERT_TIMELY (5s, nano::test::confirmed (node1, { change, epoch_open, send2 }));

// Start elections for node0 for conflicting change and epoch_open blocks (those two blocks have the same root)
// Start elections on node0 for conflicting change and epoch_open blocks (these two blocks have the same root)
ASSERT_TRUE (nano::test::activate (node0, { change, epoch_open }));
ASSERT_TIMELY (5s, nano::test::active (node0, { change, epoch_open }));

// Make node1 a representative
// Make node1 a representative so it can vote for both blocks
system.wallet (1)->insert_adhoc (nano::dev::genesis_key.prv);

// Ensure the elections for conflicting blocks have completed
// Ensure the elections for conflicting blocks have started
ASSERT_TIMELY (5s, nano::test::active (node0, { change, epoch_open }));

// Ensure both conflicting blocks were successfully processed and confirmed
Expand Down
27 changes: 26 additions & 1 deletion nano/lib/blocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ nano::block_hash nano::block::full_hash () const

nano::block_sideband const & nano::block::sideband () const
{
debug_assert (sideband_m.is_initialized ());
release_assert (sideband_m.is_initialized ());
return *sideband_m;
}

Expand Down Expand Up @@ -569,6 +569,11 @@ nano::send_block::send_block (bool & error_a, boost::property_tree::ptree const
}
}

std::shared_ptr<nano::block> nano::send_block::clone () const
{
return std::make_shared<nano::send_block> (*this);
}

bool nano::send_block::operator== (nano::block const & other_a) const
{
return blocks_equal (*this, other_a);
Expand Down Expand Up @@ -758,6 +763,11 @@ nano::open_block::open_block (bool & error_a, boost::property_tree::ptree const
}
}

std::shared_ptr<nano::block> nano::open_block::clone () const
{
return std::make_shared<nano::open_block> (*this);
}

void nano::open_block::generate_hash (blake2b_state & hash_a) const
{
hashables.hash (hash_a);
Expand Down Expand Up @@ -1029,6 +1039,11 @@ nano::change_block::change_block (bool & error_a, boost::property_tree::ptree co
}
}

std::shared_ptr<nano::block> nano::change_block::clone () const
{
return std::make_shared<nano::change_block> (*this);
}

void nano::change_block::generate_hash (blake2b_state & hash_a) const
{
hashables.hash (hash_a);
Expand Down Expand Up @@ -1326,6 +1341,11 @@ nano::state_block::state_block (bool & error_a, boost::property_tree::ptree cons
}
}

std::shared_ptr<nano::block> nano::state_block::clone () const
{
return std::make_shared<nano::state_block> (*this);
}

void nano::state_block::generate_hash (blake2b_state & hash_a) const
{
nano::uint256_union preamble (static_cast<uint64_t> (nano::block_type::state));
Expand Down Expand Up @@ -1792,6 +1812,11 @@ nano::receive_block::receive_block (bool & error_a, boost::property_tree::ptree
}
}

std::shared_ptr<nano::block> nano::receive_block::clone () const
{
return std::make_shared<nano::receive_block> (*this);
}

void nano::receive_block::generate_hash (blake2b_state & hash_a) const
{
hashables.hash (hash_a);
Expand Down
8 changes: 7 additions & 1 deletion nano/lib/blocks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace nano
class block
{
public:
virtual ~block () = default;
// Return a digest of the hashables in this block.
nano::block_hash const & hash () const;
// Return a digest of hashables and non-hashables in this block.
Expand All @@ -46,10 +47,10 @@ class block
virtual nano::block_type type () const = 0;
virtual nano::signature const & block_signature () const = 0;
virtual void signature_set (nano::signature const &) = 0;
virtual ~block () = default;
virtual bool valid_predecessor (nano::block const &) const = 0;
static size_t size (nano::block_type);
virtual nano::work_version work_version () const;
virtual std::shared_ptr<nano::block> clone () const = 0;
// If there are any changes to the hashables, call this to update the cached hash
void refresh ();
bool is_send () const noexcept;
Expand Down Expand Up @@ -138,6 +139,7 @@ class send_block : public nano::block
bool operator== (nano::block const &) const override;
bool operator== (nano::send_block const &) const;
bool valid_predecessor (nano::block const &) const override;
std::shared_ptr<nano::block> clone () const override;
send_hashables hashables;
nano::signature signature;
uint64_t work;
Expand Down Expand Up @@ -192,6 +194,7 @@ class receive_block : public nano::block
bool operator== (nano::block const &) const override;
bool operator== (nano::receive_block const &) const;
bool valid_predecessor (nano::block const &) const override;
std::shared_ptr<nano::block> clone () const override;
receive_hashables hashables;
nano::signature signature;
uint64_t work;
Expand Down Expand Up @@ -247,6 +250,7 @@ class open_block : public nano::block
bool operator== (nano::block const &) const override;
bool operator== (nano::open_block const &) const;
bool valid_predecessor (nano::block const &) const override;
std::shared_ptr<nano::block> clone () const override;
nano::open_hashables hashables;
nano::signature signature;
uint64_t work;
Expand Down Expand Up @@ -302,6 +306,7 @@ class change_block : public nano::block
bool operator== (nano::block const &) const override;
bool operator== (nano::change_block const &) const;
bool valid_predecessor (nano::block const &) const override;
std::shared_ptr<nano::block> clone () const override;
nano::change_hashables hashables;
nano::signature signature;
uint64_t work;
Expand Down Expand Up @@ -368,6 +373,7 @@ class state_block : public nano::block
bool operator== (nano::block const &) const override;
bool operator== (nano::state_block const &) const;
bool valid_predecessor (nano::block const &) const override;
std::shared_ptr<nano::block> clone () const override;
nano::state_hashables hashables;
nano::signature signature;
uint64_t work;
Expand Down
4 changes: 2 additions & 2 deletions nano/node/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -646,10 +646,10 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map
{
auto root_str = root_it->second.as<std::string> ();
auto transaction (node.node->store.tx_begin_write ());
nano::root root;
nano::qualified_root root;
if (!root.decode_hex (root_str))
{
node.node->store.final_vote.clear (transaction, root);
node.node->store.final_vote.del (transaction, root);
std::cout << "Successfully cleared final votes" << std::endl;
}
else
Expand Down
64 changes: 21 additions & 43 deletions nano/node/request_aggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <nano/node/wallet.hpp>
#include <nano/secure/ledger.hpp>
#include <nano/secure/ledger_set_any.hpp>
#include <nano/secure/ledger_set_confirmed.hpp>
#include <nano/store/component.hpp>

nano::request_aggregator::request_aggregator (request_aggregator_config const & config_a, nano::node & node_a, nano::stats & stats_a, nano::vote_generator & generator_a, nano::vote_generator & final_generator_a, nano::local_vote_history & history_a, nano::ledger & ledger_a, nano::wallets & wallets_a, nano::vote_router & vote_router_a) :
Expand Down Expand Up @@ -208,68 +209,45 @@ void nano::request_aggregator::erase_duplicates (std::vector<std::pair<nano::blo
requests_a.end ());
}

// This filters candidates for vote generation, the final decision and necessary checks are also performed by the vote generator
auto nano::request_aggregator::aggregate (nano::secure::transaction const & transaction, request_type const & requests_a, std::shared_ptr<nano::transport::channel> const & channel_a) const -> aggregate_result
{
std::vector<std::shared_ptr<nano::block>> to_generate;
std::vector<std::shared_ptr<nano::block>> to_generate_final;
for (auto const & [hash, root] : requests_a)
{
bool generate_final_vote (false);
std::shared_ptr<nano::block> block;
// Ledger by hash
std::shared_ptr<nano::block> block = ledger.any.block_get (transaction, hash);

// 2. Final votes
auto final_vote_hashes (ledger.store.final_vote.get (transaction, root));
if (!final_vote_hashes.empty ())
// Ledger by root
if (!block && !root.is_zero ())
{
generate_final_vote = true;
block = ledger.any.block_get (transaction, final_vote_hashes[0]);
// Allow same root vote
if (block != nullptr && final_vote_hashes.size () > 1)
// Search for block root
if (auto successor = ledger.any.block_successor (transaction, root.as_block_hash ()))
{
// WTF? This shouldn't be done like this
to_generate_final.push_back (block);
block = ledger.any.block_get (transaction, final_vote_hashes[1]);
debug_assert (final_vote_hashes.size () == 2);
block = ledger.any.block_get (transaction, successor.value ());
release_assert (block);
}
}

// 4. Ledger by hash
if (block == nullptr)
{
block = ledger.any.block_get (transaction, hash);
// Confirmation status. Generate final votes for confirmed
if (block != nullptr)
auto should_generate_final_vote = [&] (auto const & block) {
release_assert (block);

// Check if final vote is set for this block
if (auto final_hash = ledger.store.final_vote.get (transaction, block->qualified_root ()))
{
nano::confirmation_height_info confirmation_height_info;
ledger.store.confirmation_height.get (transaction, block->account (), confirmation_height_info);
generate_final_vote = (confirmation_height_info.height >= block->sideband ().height);
return final_hash == block->hash ();
}
}

// 5. Ledger by root
if (block == nullptr && !root.is_zero ())
{
// Search for block root
auto successor = ledger.any.block_successor (transaction, root.as_block_hash ());
if (successor)
// If the final vote is not set, generate vote if the block is confirmed
else
{
auto successor_block = ledger.any.block_get (transaction, successor.value ());
release_assert (successor_block != nullptr);
block = std::move (successor_block);

// Confirmation status. Generate final votes for confirmed successor
if (block != nullptr)
{
nano::confirmation_height_info confirmation_height_info;
ledger.store.confirmation_height.get (transaction, block->account (), confirmation_height_info);
generate_final_vote = (confirmation_height_info.height >= block->sideband ().height);
}
return ledger.confirmed.block_exists (transaction, block->hash ());
}
}
};

if (block)
{
if (generate_final_vote)
if (should_generate_final_vote (block))
{
to_generate_final.push_back (block);
stats.inc (nano::stat::type::requests, nano::stat::detail::requests_final);
Expand Down
5 changes: 2 additions & 3 deletions nano/store/final_vote.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ class final_vote

public:
virtual bool put (store::write_transaction const & transaction_a, nano::qualified_root const & root_a, nano::block_hash const & hash_a) = 0;
virtual std::vector<nano::block_hash> get (store::transaction const & transaction_a, nano::root const & root_a) = 0;
virtual void del (store::write_transaction const & transaction_a, nano::root const & root_a) = 0;
virtual std::optional<nano::block_hash> get (store::transaction const & transaction_a, nano::qualified_root const & qualified_root_a) = 0;
virtual void del (store::write_transaction const & transaction_a, nano::qualified_root const & root_a) = 0;
virtual size_t count (store::transaction const & transaction_a) const = 0;
virtual void clear (store::write_transaction const &, nano::root const &) = 0;
virtual void clear (store::write_transaction const &) = 0;
virtual iterator begin (store::transaction const & transaction_a, nano::qualified_root const & root_a) const = 0;
virtual iterator begin (store::transaction const & transaction_a) const = 0;
Expand Down
33 changes: 10 additions & 23 deletions nano/store/lmdb/final_vote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,29 @@ bool nano::store::lmdb::final_vote::put (store::write_transaction const & transa
return result;
}

std::vector<nano::block_hash> nano::store::lmdb::final_vote::get (store::transaction const & transaction, nano::root const & root_a)
std::optional<nano::block_hash> nano::store::lmdb::final_vote::get (store::transaction const & transaction, nano::qualified_root const & qualified_root_a)
{
std::vector<nano::block_hash> result;
nano::qualified_root key_start{ root_a.raw, 0 };
for (auto i = begin (transaction, key_start), n = end (transaction); i != n && nano::qualified_root{ i->first }.root () == root_a; ++i)
nano::store::lmdb::db_val result;
auto status = store.get (transaction, tables::final_votes, qualified_root_a, result);
std::optional<nano::block_hash> final_vote_hash;
if (store.success (status))
{
result.push_back (i->second);
final_vote_hash = static_cast<nano::block_hash> (result);
}
return result;
return final_vote_hash;
}

void nano::store::lmdb::final_vote::del (store::write_transaction const & transaction, nano::root const & root)
void nano::store::lmdb::final_vote::del (store::write_transaction const & transaction, nano::qualified_root const & root)
{
std::vector<nano::qualified_root> final_vote_qualified_roots;
for (auto i = begin (transaction, nano::qualified_root{ root.raw, 0 }), n = end (transaction); i != n && nano::qualified_root{ i->first }.root () == root; ++i)
{
final_vote_qualified_roots.push_back (i->first);
}

for (auto & final_vote_qualified_root : final_vote_qualified_roots)
{
auto status = store.del (transaction, tables::final_votes, final_vote_qualified_root);
store.release_assert_success (status);
}
auto status = store.del (transaction, tables::final_votes, root);
store.release_assert_success (status);
}

size_t nano::store::lmdb::final_vote::count (store::transaction const & transaction_a) const
{
return store.count (transaction_a, tables::final_votes);
}

void nano::store::lmdb::final_vote::clear (store::write_transaction const & transaction_a, nano::root const & root_a)
{
del (transaction_a, root_a);
}

void nano::store::lmdb::final_vote::clear (store::write_transaction const & transaction_a)
{
store.drop (transaction_a, nano::tables::final_votes);
Expand Down
Loading
Loading