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

[Pruning] Wallet receive action & search pending for pruned blocks #2992

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
46 changes: 46 additions & 0 deletions nano/core_test/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,52 @@ TEST (node, search_pending_confirmed)
ASSERT_TIMELY (10s, node->balance (key2.pub) == 2 * node->config.receive_minimum.number ());
}

TEST (node, search_pending_pruned)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto node1 = system.add_node (node_config);
nano::node_flags node_flags;
node_flags.enable_pruning = true;
nano::node_config config (nano::get_available_port (), system.logging);
config.enable_voting = false; // Remove after allowing pruned voting
auto node2 = system.add_node (config, node_flags);
nano::keypair key2;
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
auto send1 (system.wallet (0)->send_action (nano::dev_genesis_key.pub, key2.pub, node2->config.receive_minimum.number ()));
ASSERT_NE (nullptr, send1);
auto send2 (system.wallet (0)->send_action (nano::dev_genesis_key.pub, key2.pub, node2->config.receive_minimum.number ()));
ASSERT_NE (nullptr, send2);

// Confirmation
ASSERT_TIMELY (10s, node1->active.empty () && node2->active.empty ());
ASSERT_TIMELY (5s, node1->ledger.block_confirmed (node1->store.tx_begin_read (), send2->hash ()));
ASSERT_TIMELY (5s, node2->ledger.cache.cemented_count == 3);
system.wallet (0)->store.erase (node1->wallets.tx_begin_write (), nano::dev_genesis_key.pub);

// Pruning
{
auto transaction (node2->store.tx_begin_write ());
ASSERT_EQ (1, node2->ledger.pruning_action (transaction, send1->hash (), 1));
}
ASSERT_EQ (1, node2->ledger.cache.pruned_count);
ASSERT_TRUE (node2->ledger.block_or_pruned_exists (send1->hash ()));
ASSERT_FALSE (node2->ledger.block_exists (send1->hash ()));

// Receive pruned block
system.wallet (1)->insert_adhoc (key2.prv);
ASSERT_FALSE (system.wallet (1)->search_pending ());
{
nano::lock_guard<std::mutex> guard (node2->active.mutex);
auto existing1 (node2->active.blocks.find (send1->hash ()));
ASSERT_EQ (node2->active.blocks.end (), existing1);
auto existing2 (node2->active.blocks.find (send2->hash ()));
ASSERT_EQ (node2->active.blocks.end (), existing2);
}
ASSERT_TIMELY (10s, node2->balance (key2.pub) == 2 * node2->config.receive_minimum.number ());
}

TEST (node, unlock_search)
{
nano::system system (1);
Expand Down
49 changes: 45 additions & 4 deletions nano/core_test/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,7 @@ TEST (wallet, epoch_2_validation)
ASSERT_EQ (nano::epoch::epoch_2, send->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, send->sideband ().source_epoch); // Not used for send state blocks

auto receive = wallet.receive_action (*send, nano::dev_genesis_key.pub, amount, 1);
auto receive = wallet.receive_action (send->hash (), nano::dev_genesis_key.pub, amount, send->link ().as_account (), 1);
ASSERT_NE (nullptr, receive);
if (receive->difficulty () < node.network_params.network.publish_thresholds.base)
{
Expand Down Expand Up @@ -1355,7 +1355,7 @@ TEST (wallet, epoch_2_receive_propagation)
auto amount = node.config.receive_minimum.number ();
auto send1 = wallet.send_action (nano::dev_genesis_key.pub, key.pub, amount, 1);
ASSERT_NE (nullptr, send1);
ASSERT_NE (nullptr, wallet.receive_action (*send1, nano::dev_genesis_key.pub, amount, 1));
ASSERT_NE (nullptr, wallet.receive_action (send1->hash (), nano::dev_genesis_key.pub, amount, send1->link ().as_account (), 1));

// Upgrade the genesis account to epoch 2
auto epoch2 = system.upgrade_genesis_epoch (node, nano::epoch::epoch_2);
Expand All @@ -1370,7 +1370,7 @@ TEST (wallet, epoch_2_receive_propagation)
nano::lock_guard<std::mutex> guard (node.active.mutex);
node.active.trended_active_multiplier = 1.0;
}
auto receive2 = wallet.receive_action (*send2, key.pub, amount, 1);
auto receive2 = wallet.receive_action (send2->hash (), key.pub, amount, send2->link ().as_account (), 1);
ASSERT_NE (nullptr, receive2);
if (receive2->difficulty () < node.network_params.network.publish_thresholds.base)
{
Expand Down Expand Up @@ -1420,7 +1420,7 @@ TEST (wallet, epoch_2_receive_unopened)
nano::lock_guard<std::mutex> guard (node.active.mutex);
node.active.trended_active_multiplier = 1.0;
}
auto receive1 = wallet.receive_action (*send1, key.pub, amount, 1);
auto receive1 = wallet.receive_action (send1->hash (), key.pub, amount, send1->link ().as_account (), 1);
ASSERT_NE (nullptr, receive1);
if (receive1->difficulty () < node.network_params.network.publish_thresholds.base)
{
Expand Down Expand Up @@ -1503,3 +1503,44 @@ TEST (wallet, search_pending)
ASSERT_EQ (receive->sideband ().height, 3);
ASSERT_EQ (send->hash (), receive->link ().as_block_hash ());
}

TEST (wallet, receive_pruned)
{
nano::system system;
nano::node_flags node_flags;
node_flags.disable_request_loop = true;
auto & node1 (*system.add_node (node_flags));
node_flags.enable_pruning = true;
nano::node_config config (nano::get_available_port (), system.logging);
config.enable_voting = false; // Remove after allowing pruned voting
auto & node2 (*system.add_node (config, node_flags));

auto & wallet1 (*system.wallet (0));
auto & wallet2 (*system.wallet (1));

nano::keypair key;
nano::state_block_builder builder;

// Send
wallet1.insert_adhoc (nano::dev_genesis_key.prv, false);
auto amount = node2.config.receive_minimum.number ();
auto send1 = wallet1.send_action (nano::dev_genesis_key.pub, key.pub, amount, 1);
auto send2 = wallet1.send_action (nano::dev_genesis_key.pub, key.pub, 1, 1);

// Pruning
ASSERT_TIMELY (5s, node2.ledger.cache.cemented_count == 3);
{
auto transaction (node2.store.tx_begin_write ());
ASSERT_EQ (1, node2.ledger.pruning_action (transaction, send1->hash (), 2));
}
ASSERT_EQ (1, node2.ledger.cache.pruned_count);
ASSERT_TRUE (node2.ledger.block_or_pruned_exists (send1->hash ()));
ASSERT_FALSE (node2.ledger.block_exists (send1->hash ()));

wallet2.insert_adhoc (key.prv, false);

auto open1 = wallet2.receive_action (send1->hash (), key.pub, amount, send1->link ().as_account (), 1);
ASSERT_NE (nullptr, open1);
ASSERT_EQ (amount, node2.ledger.balance (node2.store.tx_begin_read (), open1->hash ()));
ASSERT_TIMELY (5s, node2.ledger.cache.cemented_count == 4);
}
11 changes: 11 additions & 0 deletions nano/lib/blocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ nano::block_hash const & nano::block::source () const
return source;
}

nano::account const & nano::block::destination () const
{
static nano::account destination{ 0 };
return destination;
}

nano::link const & nano::block::link () const
{
static nano::link link{ 0 };
Expand Down Expand Up @@ -442,6 +448,11 @@ nano::block_hash const & nano::send_block::previous () const
return hashables.previous;
}

nano::account const & nano::send_block::destination () const
{
return hashables.destination;
}

nano::root const & nano::send_block::root () const
{
return hashables.previous;
Expand Down
3 changes: 3 additions & 0 deletions nano/lib/blocks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class block
virtual nano::block_hash const & previous () const = 0;
// Source block for open/receive blocks, zero otherwise.
virtual nano::block_hash const & source () const;
// Destination account for send blocks, zero otherwise.
virtual nano::account const & destination () const;
// Previous block or account number for open blocks
virtual nano::root const & root () const = 0;
// Qualified root value based on previous() and root()
Expand Down Expand Up @@ -153,6 +155,7 @@ class send_block : public nano::block
uint64_t block_work () const override;
void block_work_set (uint64_t) override;
nano::block_hash const & previous () const override;
nano::account const & destination () const override;
nano::root const & root () const override;
nano::amount const & balance () const override;
void serialize (nano::stream &) const override;
Expand Down
3 changes: 2 additions & 1 deletion nano/node/active_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::b
{
add_recently_cemented (election->status);
lk.unlock ();
node.receive_confirmed (node.wallets.tx_begin_read (), transaction, block_a, hash);
auto destination (block_a->link ().is_zero () ? block_a->destination () : block_a->link ().as_account ());
node.receive_confirmed (node.wallets.tx_begin_read (), transaction, hash, destination);
nano::account account (0);
nano::uint128_t amount (0);
bool is_state_send (false);
Expand Down
5 changes: 2 additions & 3 deletions nano/node/json_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3225,8 +3225,7 @@ void nano::json_handler::receive ()
if (!ec)
{
auto block_transaction (node.store.tx_begin_read ());
auto block (node.store.block_get (block_transaction, hash));
if (block != nullptr)
if (node.ledger.block_or_pruned_exists (block_transaction, hash))
{
nano::pending_info pending_info;
if (!node.store.pending_get (block_transaction, nano::pending_key (account, hash), pending_info))
Expand Down Expand Up @@ -3268,7 +3267,7 @@ void nano::json_handler::receive ()
bool generate_work (work == 0); // Disable work generation if "work" option is provided
auto response_a (response);
wallet->receive_async (
std::move (block), representative, node.network_params.ledger.genesis_amount, [response_a](std::shared_ptr<nano::block> block_a) {
hash, representative, node.network_params.ledger.genesis_amount, account, [response_a](std::shared_ptr<nano::block> block_a) {
if (block_a != nullptr)
{
boost::property_tree::ptree response_l;
Expand Down
92 changes: 28 additions & 64 deletions nano/node/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1179,80 +1179,36 @@ void nano::node::ongoing_online_weight_calculation ()
ongoing_online_weight_calculation_queue ();
}

namespace
void nano::node::receive_confirmed (nano::transaction const & wallet_transaction_a, nano::transaction const & block_transaction_a, nano::block_hash const & hash_a, nano::account const & destination_a)
{
class confirmed_visitor : public nano::block_visitor
{
public:
confirmed_visitor (nano::transaction const & wallet_transaction_a, nano::transaction const & block_transaction_a, nano::node & node_a, std::shared_ptr<nano::block> const & block_a, nano::block_hash const & hash_a) :
wallet_transaction (wallet_transaction_a),
block_transaction (block_transaction_a),
node (node_a),
block (block_a),
hash (hash_a)
{
}
virtual ~confirmed_visitor () = default;
void scan_receivable (nano::account const & account_a)
for (auto const & [id /*unused*/, wallet] : wallets.get_wallets ())
{
for (auto const & [id /*unused*/, wallet] : node.wallets.get_wallets ())
(void)id;
if (wallet->store.exists (wallet_transaction_a, destination_a))
{
if (wallet->store.exists (wallet_transaction, account_a))
nano::account representative;
nano::pending_info pending;
representative = wallet->store.representative (wallet_transaction_a);
auto error (store.pending_get (block_transaction_a, nano::pending_key (destination_a, hash_a), pending));
if (!error)
{
auto amount (pending.amount.number ());
wallet->receive_async (hash_a, representative, amount, destination_a, [](std::shared_ptr<nano::block>) {});
}
else
{
nano::account representative;
nano::pending_info pending;
representative = wallet->store.representative (wallet_transaction);
auto error (node.store.pending_get (block_transaction, nano::pending_key (account_a, hash), pending));
if (!error)
if (!store.block_or_pruned_exists (block_transaction_a, hash_a))
{
auto node_l (node.shared ());
auto amount (pending.amount.number ());
wallet->receive_async (block, representative, amount, [](std::shared_ptr<nano::block>) {});
logger.try_log (boost::str (boost::format ("Confirmed block is missing: %1%") % hash_a.to_string ()));
debug_assert (false && "Confirmed block is missing");
}
else
{
if (!node.store.block_exists (block_transaction, hash))
{
node.logger.try_log (boost::str (boost::format ("Confirmed block is missing: %1%") % hash.to_string ()));
debug_assert (false && "Confirmed block is missing");
}
else
{
node.logger.try_log (boost::str (boost::format ("Block %1% has already been received") % hash.to_string ()));
}
logger.try_log (boost::str (boost::format ("Block %1% has already been received") % hash_a.to_string ()));
}
}
}
}
void state_block (nano::state_block const & block_a) override
{
scan_receivable (block_a.hashables.link.as_account ());
}
void send_block (nano::send_block const & block_a) override
{
scan_receivable (block_a.hashables.destination);
}
void receive_block (nano::receive_block const &) override
{
}
void open_block (nano::open_block const &) override
{
}
void change_block (nano::change_block const &) override
{
}
nano::transaction const & wallet_transaction;
nano::transaction const & block_transaction;
nano::node & node;
std::shared_ptr<nano::block> block;
nano::block_hash const & hash;
};
}

void nano::node::receive_confirmed (nano::transaction const & wallet_transaction_a, nano::transaction const & block_transaction_a, std::shared_ptr<nano::block> const & block_a, nano::block_hash const & hash_a)
{
confirmed_visitor visitor (wallet_transaction_a, block_transaction_a, *this, block_a, hash_a);
block_a->visit (visitor);
}

void nano::node::process_confirmed_data (nano::transaction const & transaction_a, std::shared_ptr<nano::block> block_a, nano::block_hash const & hash_a, nano::account & account_a, nano::uint128_t & amount_a, bool & is_state_send_a, nano::account & pending_account_a)
Expand All @@ -1265,11 +1221,19 @@ void nano::node::process_confirmed_data (nano::transaction const & transaction_a
}
// Faster amount calculation
auto previous (block_a->previous ());
auto previous_balance (ledger.balance (transaction_a, previous));
bool error (false);
auto previous_balance (ledger.balance_safe (transaction_a, previous, error));
auto block_balance (store.block_balance_calculated (block_a));
if (hash_a != ledger.network_params.ledger.genesis_account)
{
amount_a = block_balance > previous_balance ? block_balance - previous_balance : previous_balance - block_balance;
if (!error)
{
amount_a = block_balance > previous_balance ? block_balance - previous_balance : previous_balance - block_balance;
}
else
{
amount_a = 0;
}
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion nano/node/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class node final : public std::enable_shared_from_this<nano::node>
void stop ();
std::shared_ptr<nano::node> shared ();
int store_version ();
void receive_confirmed (nano::transaction const & wallet_transaction_a, nano::transaction const & block_transaction_a, std::shared_ptr<nano::block> const &, nano::block_hash const &);
void receive_confirmed (nano::transaction const & wallet_transaction_a, nano::transaction const & block_transaction_a, nano::block_hash const & hash_a, nano::account const & destination_a);
void process_confirmed_data (nano::transaction const &, std::shared_ptr<nano::block>, nano::block_hash const &, nano::account &, nano::uint128_t &, bool &, nano::account &);
void process_confirmed (nano::election_status const &, uint64_t = 0);
void process_active (std::shared_ptr<nano::block>);
Expand Down
Loading