From ce4ea05407d932e089880709823d2e539a8eebe0 Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Fri, 12 Apr 2024 23:29:24 +0900 Subject: [PATCH 1/6] Make nano::ledger::account_balance() const --- nano/secure/ledger.cpp | 2 +- nano/secure/ledger.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 85313fa625..bc8ad825fb 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -794,7 +794,7 @@ bool nano::ledger::block_exists (store::transaction const & transaction, nano::b } // Balance for an account by account number -nano::uint128_t nano::ledger::account_balance (store::transaction const & transaction_a, nano::account const & account_a, bool only_confirmed_a) +nano::uint128_t nano::ledger::account_balance (store::transaction const & transaction_a, nano::account const & account_a, bool only_confirmed_a) const { nano::uint128_t result (0); if (only_confirmed_a) diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index a3e7f127a3..fc35e49dcf 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -43,7 +43,7 @@ class ledger final std::optional balance (store::transaction const &, nano::block_hash const &) const; std::shared_ptr block (store::transaction const & transaction, nano::block_hash const & hash) const; bool block_exists (store::transaction const & transaction, nano::block_hash const & hash) const; - nano::uint128_t account_balance (store::transaction const &, nano::account const &, bool = false); + nano::uint128_t account_balance (store::transaction const &, nano::account const &, bool = false) const; nano::uint128_t account_receivable (store::transaction const &, nano::account const &, bool = false); /** * Returns the cached vote weight for the given representative. From 2095d20fc68e2f140a15d708902f9fab5e4486c7 Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Fri, 12 Apr 2024 23:35:09 +0900 Subject: [PATCH 2/6] Refactor print_all_blocks() to take a store rather than a whole node. --- nano/test_common/testutil.cpp | 8 ++++---- nano/test_common/testutil.hpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index 98fdc98b86..bfd4646924 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -321,11 +321,11 @@ void nano::test::print_all_account_info (nano::node & node) } } -void nano::test::print_all_blocks (nano::node & node) +void nano::test::print_all_blocks (const nano::store::component & store) { - auto tx = node.store.tx_begin_read (); - auto i = node.store.block.begin (tx); - auto end = node.store.block.end (); + auto tx = store.tx_begin_read (); + auto i = store.block.begin (tx); + auto end = store.block.end (); std::cout << "Listing all blocks" << std::endl; for (; i != end; ++i) { diff --git a/nano/test_common/testutil.hpp b/nano/test_common/testutil.hpp index e55f53535b..e171f444af 100644 --- a/nano/test_common/testutil.hpp +++ b/nano/test_common/testutil.hpp @@ -423,8 +423,8 @@ namespace test void print_all_account_info (nano::node & node); /** - * \brief Debugging function to print all blocks in a node. Intented to be used to debug unit tests. + * \brief Debugging function to print all blocks in a node. Intended to be used to debug unit tests. */ - void print_all_blocks (nano::node & node); + void print_all_blocks (const nano::store::component & store); } } \ No newline at end of file From c98892ebf26f4c64000c3d6f95c3eacfcff7cefe Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Fri, 12 Apr 2024 23:39:17 +0900 Subject: [PATCH 3/6] Refactor print_all_account_info() to take a ledger rather than a node ref --- nano/test_common/testutil.cpp | 13 +++++++------ nano/test_common/testutil.hpp | 5 +++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index bfd4646924..3e198a0994 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -300,20 +300,21 @@ uint64_t nano::test::account_height (nano::node const & node, nano::account cons return height_info.height; } -void nano::test::print_all_account_info (nano::node & node) +void nano::test::print_all_account_info (const nano::ledger & ledger) { - auto const tx = node.ledger.store.tx_begin_read (); - auto const end = node.ledger.store.account.end (); - for (auto i = node.ledger.store.account.begin (tx); i != end; ++i) + std::cout << "Printing all account info:\n"; + auto const tx = ledger.store.tx_begin_read (); + auto const end = ledger.store.account.end (); + for (auto i = ledger.store.account.begin (tx); i != end; ++i) { nano::account acc = i->first; nano::account_info acc_info = i->second; nano::confirmation_height_info height_info; std::cout << "Account: " << acc.to_account () << std::endl; std::cout << " Unconfirmed Balance: " << acc_info.balance.to_string_dec () << std::endl; - std::cout << " Confirmed Balance: " << node.ledger.account_balance (tx, acc, true) << std::endl; + std::cout << " Confirmed Balance: " << ledger.account_balance (tx, acc, true) << std::endl; std::cout << " Block Count: " << acc_info.block_count << std::endl; - if (!node.ledger.store.confirmation_height.get (tx, acc, height_info)) + if (!ledger.store.confirmation_height.get (tx, acc, height_info)) { std::cout << " Conf. Height: " << height_info.height << std::endl; std::cout << " Conf. Frontier: " << height_info.frontier.to_string () << std::endl; diff --git a/nano/test_common/testutil.hpp b/nano/test_common/testutil.hpp index e171f444af..6414e5263d 100644 --- a/nano/test_common/testutil.hpp +++ b/nano/test_common/testutil.hpp @@ -169,6 +169,7 @@ class network_params; class vote; class block; class election; +class ledger; extern nano::uint128_t const & genesis_amount; @@ -418,9 +419,9 @@ namespace test uint64_t account_height (nano::node const & node, nano::account const & acc); /** - * \brief Debugging function to print all accounts in a ledger. Intented to be used to debug unit tests. + * \brief Debugging function to print all accounts in a ledger. Intended to be used to debug unit tests. */ - void print_all_account_info (nano::node & node); + void print_all_account_info (const nano::ledger & ledger); /** * \brief Debugging function to print all blocks in a node. Intended to be used to debug unit tests. From 7c106bc74aeb6e82f4ac11937c46f7a977869226 Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Fri, 12 Apr 2024 23:45:57 +0900 Subject: [PATCH 4/6] Introduce nano::test::print_all_receivable_entries() This prints all entries in the pending table. Intended to be used for learning and unit test debugging. Also intoduce ostream operator << for pending_key and pending_info. --- nano/secure/pending_info.hpp | 13 +++++++++++++ nano/test_common/testutil.cpp | 13 +++++++++++++ nano/test_common/testutil.hpp | 5 +++++ 3 files changed, 31 insertions(+) diff --git a/nano/secure/pending_info.hpp b/nano/secure/pending_info.hpp index 0136da020d..b5ee8bef1e 100644 --- a/nano/secure/pending_info.hpp +++ b/nano/secure/pending_info.hpp @@ -30,6 +30,13 @@ class pending_info final nano::account source{}; nano::amount amount{ 0 }; nano::epoch epoch{ nano::epoch::epoch_0 }; + + friend std::ostream & operator<< (std::ostream & os, const nano::pending_info & info) + { + const int epoch = nano::normalized_epoch (info.epoch); + os << "Source: " << info.source << ", Amount: " << info.amount.to_string_dec () << " Epoch: " << epoch; + return os; + } }; class pending_key final { @@ -42,6 +49,12 @@ class pending_key final nano::account const & key () const; nano::account account{}; nano::block_hash hash{ 0 }; + + friend std::ostream & operator<< (std::ostream & os, const nano::pending_key & key) + { + os << "Account: " << key.account << ", Hash: " << key.hash; + return os; + } }; // This class iterates receivable enttries for an account class receivable_iterator diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index 3e198a0994..81708eac2c 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -300,6 +301,18 @@ uint64_t nano::test::account_height (nano::node const & node, nano::account cons return height_info.height; } +void nano::test::print_all_receivable_entries (const nano::store::component & store) +{ + std::cout << "Printing all receivable entries:\n"; + auto const tx = store.tx_begin_read (); + auto const end = store.pending.end (); + for (auto i = store.pending.begin (tx); i != end; ++i) + { + std::cout << "Key: " << i->first << std::endl; + std::cout << "Info: " << i->second << std::endl; + } +} + void nano::test::print_all_account_info (const nano::ledger & ledger) { std::cout << "Printing all account info:\n"; diff --git a/nano/test_common/testutil.hpp b/nano/test_common/testutil.hpp index 6414e5263d..89155614d1 100644 --- a/nano/test_common/testutil.hpp +++ b/nano/test_common/testutil.hpp @@ -418,6 +418,11 @@ namespace test */ uint64_t account_height (nano::node const & node, nano::account const & acc); + /** + * \brief Debugging function to print all entries in the pending table. Intended to be used to debug unit tests. + */ + void print_all_receivable_entries (const nano::store::component & store); + /** * \brief Debugging function to print all accounts in a ledger. Intended to be used to debug unit tests. */ From 92b4455c588fb722887e8fdbefde269d6e6e7cb0 Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Sat, 13 Apr 2024 02:09:27 +0900 Subject: [PATCH 5/6] Test case for pending table query at different epochs --- nano/core_test/CMakeLists.txt | 1 + nano/core_test/receivable.cpp | 99 +++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 nano/core_test/receivable.cpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 793c7c2617..d78b56c25f 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -39,6 +39,7 @@ add_executable( processing_queue.cpp processor_service.cpp rep_crawler.cpp + receivable.cpp peer_container.cpp rep_weight_store.cpp scheduler_buckets.cpp diff --git a/nano/core_test/receivable.cpp b/nano/core_test/receivable.cpp new file mode 100644 index 0000000000..aef5534fdf --- /dev/null +++ b/nano/core_test/receivable.cpp @@ -0,0 +1,99 @@ +#include +#include + +#include + +using namespace std::chrono_literals; + +// this test sends 3 send blocks in 3 different epochs and checks that +// the pending table records the epochs correctly for each send +TEST (receivable, pending_table_query_epochs) +{ + nano::test::system system{ 1 }; + auto & node = *system.nodes[0]; + nano::keypair key2; + nano::block_builder builder; + + // epoch 0 send + auto send0 = builder + .send () + .previous (nano::dev::genesis->hash ()) + .destination (key2.pub) + .balance (nano::dev::constants.genesis_amount - 1) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + nano::test::process (node, { send0 }); + ASSERT_TIMELY (5s, nano::test::exists (node, { send0 })); + + auto epoch1 = system.upgrade_genesis_epoch (node, nano::epoch::epoch_1); + ASSERT_TRUE (epoch1); + ASSERT_TIMELY (5s, nano::test::exists (node, { epoch1 })); + + // epoch 1 send + auto send1 = builder + .state () + .account (nano::dev::genesis_key.pub) + .representative (nano::dev::genesis_key.pub) + .previous (epoch1->hash ()) + .link (key2.pub) + .balance (nano::dev::constants.genesis_amount - 11) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (epoch1->hash ())) + .build (); + ASSERT_TRUE (nano::test::process (node, { send1 })); + ASSERT_TIMELY (5s, nano::test::exists (node, { send1 })); + + auto epoch2 = system.upgrade_genesis_epoch (node, nano::epoch::epoch_2); + ASSERT_TRUE (epoch2); + ASSERT_TIMELY (5s, nano::test::exists (node, { epoch2 })); + + // epoch 2 send + auto send2 = builder + .state () + .account (nano::dev::genesis_key.pub) + .representative (nano::dev::genesis_key.pub) + .previous (epoch2->hash ()) + .link (key2.pub) + .balance (nano::dev::constants.genesis_amount - 111) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (epoch2->hash ())) + .build (); + nano::test::process (node, { send2 }); + ASSERT_TIMELY (5s, nano::test::exists (node, { send2 })); + + auto tx = node.store.tx_begin_read (); + + // check epoch 0 send + { + nano::pending_key key{ key2.pub, send0->hash () }; + auto opt_info = node.store.pending.get (tx, key); + ASSERT_TRUE (opt_info.has_value ()); + auto info = opt_info.value (); + ASSERT_EQ (info.source, nano::dev::genesis_key.pub); + ASSERT_EQ (info.amount, 1); + ASSERT_EQ (info.epoch, nano::epoch::epoch_0); + } + + // check epoch 1 send + { + nano::pending_key key{ key2.pub, send1->hash () }; + auto opt_info = node.store.pending.get (tx, key); + ASSERT_TRUE (opt_info.has_value ()); + auto info = opt_info.value (); + ASSERT_EQ (info.source, nano::dev::genesis_key.pub); + ASSERT_EQ (info.amount, 10); + ASSERT_EQ (info.epoch, nano::epoch::epoch_1); + } + + // check epoch 2 send + { + nano::pending_key key{ key2.pub, send2->hash () }; + auto opt_info = node.store.pending.get (tx, key); + ASSERT_TRUE (opt_info.has_value ()); + auto info = opt_info.value (); + ASSERT_EQ (info.source, nano::dev::genesis_key.pub); + ASSERT_EQ (info.amount, 100); + ASSERT_EQ (info.epoch, nano::epoch::epoch_2); + } +} \ No newline at end of file From ded54b2f7bd32fd16ebc1dee3b94b2c8f1cc06e0 Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Sat, 13 Apr 2024 02:16:54 +0900 Subject: [PATCH 6/6] Some comments documenting pending_key and pending_info classes --- nano/secure/pending_info.hpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/nano/secure/pending_info.hpp b/nano/secure/pending_info.hpp index b5ee8bef1e..748e3a63a2 100644 --- a/nano/secure/pending_info.hpp +++ b/nano/secure/pending_info.hpp @@ -18,6 +18,7 @@ namespace nano { /** * Information on an uncollected send + * This class captures the data stored in a pending table entry */ class pending_info final { @@ -27,9 +28,9 @@ class pending_info final size_t db_size () const; bool deserialize (nano::stream &); bool operator== (nano::pending_info const &) const; - nano::account source{}; - nano::amount amount{ 0 }; - nano::epoch epoch{ nano::epoch::epoch_0 }; + nano::account source{}; // the account sending the funds + nano::amount amount{ 0 }; // amount receivable in this transaction + nano::epoch epoch{ nano::epoch::epoch_0 }; // epoch of sending block, this info is stored here to make it possible to prune the send block friend std::ostream & operator<< (std::ostream & os, const nano::pending_info & info) { @@ -38,6 +39,9 @@ class pending_info final return os; } }; + +// This class represents the data written into the pending (receivable) database table key +// the receiving account and hash of the send block identify a pending db table entry class pending_key final { public: @@ -47,8 +51,8 @@ class pending_key final bool operator== (nano::pending_key const &) const; bool operator< (nano::pending_key const &) const; nano::account const & key () const; - nano::account account{}; - nano::block_hash hash{ 0 }; + nano::account account{}; // receiving account + nano::block_hash hash{ 0 }; // hash of the send block friend std::ostream & operator<< (std::ostream & os, const nano::pending_key & key) { @@ -56,6 +60,7 @@ class pending_key final return os; } }; + // This class iterates receivable enttries for an account class receivable_iterator {