Skip to content

Commit

Permalink
Database scan fix (nanocurrency#4742)
Browse files Browse the repository at this point in the history
* Tests

* Fix
  • Loading branch information
pwojcikdev authored Oct 3, 2024
1 parent 2a155b7 commit 1f1c8ab
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 1 deletion.
218 changes: 218 additions & 0 deletions nano/core_test/bootstrap_ascending.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
#include <nano/lib/logging.hpp>
#include <nano/lib/stats.hpp>
#include <nano/lib/tomlconfig.hpp>
#include <nano/node/bootstrap_ascending/database_scan.hpp>
#include <nano/node/bootstrap_ascending/service.hpp>
#include <nano/node/make_store.hpp>
#include <nano/secure/ledger.hpp>
#include <nano/secure/ledger_set_any.hpp>
#include <nano/test_common/ledger_context.hpp>
#include <nano/test_common/system.hpp>
#include <nano/test_common/testutil.hpp>

Expand Down Expand Up @@ -266,3 +268,219 @@ TEST (bootstrap_ascending, trace_base)
// std::cerr << "node1: " << node1.network.endpoint () << std::endl;
ASSERT_TIMELY (10s, node1.block (receive1->hash ()) != nullptr);
}

TEST (bootstrap_ascending, pending_database_scanner)
{
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };

// Prepare pending sends from genesis
// 1 account with 1 pending
// 1 account with 21 pendings
// 2 accounts with 1 pending each
std::deque<std::shared_ptr<nano::block>> blocks;
nano::keypair key1, key2, key3, key4;
{
nano::state_block_builder builder;

auto source = nano::dev::genesis_key;
auto latest = nano::dev::genesis->hash ();
auto balance = nano::dev::genesis->balance ().number ();

// 1 account with 1 pending
{
auto send = builder.make_block ()
.account (source.pub)
.previous (latest)
.representative (source.pub)
.link (key1.pub)
.balance (balance - 1)
.sign (source.prv, source.pub)
.work (*pool.generate (latest))
.build ();
latest = send->hash ();
balance = send->balance_field ().value ().number ();
blocks.push_back (send);
}
// 1 account with 21 pendings
for (int i = 0; i < 21; ++i)
{
auto send = builder.make_block ()
.account (source.pub)
.previous (latest)
.representative (source.pub)
.link (key2.pub)
.balance (balance - 1)
.sign (source.prv, source.pub)
.work (*pool.generate (latest))
.build ();
latest = send->hash ();
balance = send->balance_field ().value ().number ();
blocks.push_back (send);
}
// 2 accounts with 1 pending each
{
auto send = builder.make_block ()
.account (source.pub)
.previous (latest)
.representative (source.pub)
.link (key3.pub)
.balance (balance - 1)
.sign (source.prv, source.pub)
.work (*pool.generate (latest))
.build ();
latest = send->hash ();
balance = send->balance_field ().value ().number ();
blocks.push_back (send);
}
{
auto send = builder.make_block ()
.account (source.pub)
.previous (latest)
.representative (source.pub)
.link (key4.pub)
.balance (balance - 1)
.sign (source.prv, source.pub)
.work (*pool.generate (latest))
.build ();
latest = send->hash ();
balance = send->balance_field ().value ().number ();
blocks.push_back (send);
}
}

nano::test::ledger_context ctx{ std::move (blocks) };

// Single batch
{
nano::bootstrap_ascending::pending_database_iterator scanner{ ctx.ledger () };
auto transaction = ctx.store ().tx_begin_read ();
auto accounts = scanner.next_batch (transaction, 256);

// Check that account set contains all keys
ASSERT_EQ (accounts.size (), 4);
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key1.pub) != accounts.end ());
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key2.pub) != accounts.end ());
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key3.pub) != accounts.end ());
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key4.pub) != accounts.end ());

ASSERT_EQ (scanner.completed, 1);
}
// Multi batch
{
nano::bootstrap_ascending::pending_database_iterator scanner{ ctx.ledger () };
auto transaction = ctx.store ().tx_begin_read ();

// Request accounts in multiple batches
auto accounts1 = scanner.next_batch (transaction, 2);
auto accounts2 = scanner.next_batch (transaction, 1);
auto accounts3 = scanner.next_batch (transaction, 1);

ASSERT_EQ (accounts1.size (), 2);
ASSERT_EQ (accounts2.size (), 1);
ASSERT_EQ (accounts3.size (), 1);

std::deque<nano::account> accounts;
accounts.insert (accounts.end (), accounts1.begin (), accounts1.end ());
accounts.insert (accounts.end (), accounts2.begin (), accounts2.end ());
accounts.insert (accounts.end (), accounts3.begin (), accounts3.end ());

// Check that account set contains all keys
ASSERT_EQ (accounts.size (), 4);
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key1.pub) != accounts.end ());
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key2.pub) != accounts.end ());
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key3.pub) != accounts.end ());
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key4.pub) != accounts.end ());

ASSERT_EQ (scanner.completed, 1);
}
}

TEST (bootstrap_ascending, account_database_scanner)
{
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };

size_t const count = 4;

// Prepare some accounts
std::deque<std::shared_ptr<nano::block>> blocks;
std::deque<nano::keypair> keys;
{
nano::state_block_builder builder;

auto source = nano::dev::genesis_key;
auto latest = nano::dev::genesis->hash ();
auto balance = nano::dev::genesis->balance ().number ();

for (int i = 0; i < count; ++i)
{
nano::keypair key;
auto send = builder.make_block ()
.account (source.pub)
.previous (latest)
.representative (source.pub)
.link (key.pub)
.balance (balance - 1)
.sign (source.prv, source.pub)
.work (*pool.generate (latest))
.build ();
auto open = builder.make_block ()
.account (key.pub)
.previous (0)
.representative (key.pub)
.link (send->hash ())
.balance (1)
.sign (key.prv, key.pub)
.work (*pool.generate (key.pub))
.build ();
latest = send->hash ();
balance = send->balance_field ().value ().number ();
blocks.push_back (send);
blocks.push_back (open);
keys.push_back (key);
}
}

nano::test::ledger_context ctx{ std::move (blocks) };

// Single batch
{
nano::bootstrap_ascending::account_database_iterator scanner{ ctx.ledger () };
auto transaction = ctx.store ().tx_begin_read ();
auto accounts = scanner.next_batch (transaction, 256);

// Check that account set contains all keys
ASSERT_EQ (accounts.size (), keys.size () + 1); // +1 for genesis
for (auto const & key : keys)
{
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key.pub) != accounts.end ());
}
ASSERT_EQ (scanner.completed, 1);
}
// Multi batch
{
nano::bootstrap_ascending::account_database_iterator scanner{ ctx.ledger () };
auto transaction = ctx.store ().tx_begin_read ();

// Request accounts in multiple batches
auto accounts1 = scanner.next_batch (transaction, 2);
auto accounts2 = scanner.next_batch (transaction, 2);
auto accounts3 = scanner.next_batch (transaction, 1);

ASSERT_EQ (accounts1.size (), 2);
ASSERT_EQ (accounts2.size (), 2);
ASSERT_EQ (accounts3.size (), 1);

std::deque<nano::account> accounts;
accounts.insert (accounts.end (), accounts1.begin (), accounts1.end ());
accounts.insert (accounts.end (), accounts2.begin (), accounts2.end ());
accounts.insert (accounts.end (), accounts3.begin (), accounts3.end ());

// Check that account set contains all keys
ASSERT_EQ (accounts.size (), keys.size () + 1); // +1 for genesis
for (auto const & key : keys)
{
ASSERT_TRUE (std::find (accounts.begin (), accounts.end (), key.pub) != accounts.end ());
}
ASSERT_EQ (scanner.completed, 1);
}
}
2 changes: 1 addition & 1 deletion nano/node/bootstrap_ascending/database_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ std::deque<nano::account> nano::bootstrap_ascending::pending_database_iterator::
}

// If we didn't advance to the next account, perform a fresh lookup
if (it != end && it->first.account != starting_account)
if (it != end && it->first.account == starting_account)
{
it = ledger.store.pending.begin (transaction, { starting_account.number () + 1, 0 });
}
Expand Down

0 comments on commit 1f1c8ab

Please sign in to comment.