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 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. diff --git a/nano/secure/pending_info.hpp b/nano/secure/pending_info.hpp index 0136da020d..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,10 +28,20 @@ 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) + { + const int epoch = nano::normalized_epoch (info.epoch); + os << "Source: " << info.source << ", Amount: " << info.amount.to_string_dec () << " Epoch: " << epoch; + 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: @@ -40,9 +51,16 @@ 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) + { + 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 98fdc98b86..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,20 +301,33 @@ 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_receivable_entries (const nano::store::component & store) { - 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 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"; + 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; @@ -321,11 +335,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..89155614d1 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,13 +419,18 @@ 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 entries in the pending table. Intended to be used to debug unit tests. */ - void print_all_account_info (nano::node & node); + void print_all_receivable_entries (const nano::store::component & store); /** - * \brief Debugging function to print all blocks in a node. 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_blocks (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. + */ + void print_all_blocks (const nano::store::component & store); } } \ No newline at end of file