Fire callback & add to history after confirmation height is set (#2233)
* Fire callback & add to history only after confirmation height is set * Clear dependent election blocks after a block is confirmed and this is already pending in conf height processor * Formatting * Fix failed merge * Serg review comments * Formatting
This commit is contained in:
parent
c293678173
commit
ceff5a98fd
11 changed files with 1372 additions and 1034 deletions
|
@ -4,6 +4,7 @@ add_executable (core_test
|
|||
active_transactions.cpp
|
||||
block.cpp
|
||||
block_store.cpp
|
||||
confirmation_height.cpp
|
||||
conflicts.cpp
|
||||
difficulty.cpp
|
||||
distributed_work.cpp
|
||||
|
|
1184
nano/core_test/confirmation_height.cpp
Normal file
1184
nano/core_test/confirmation_height.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1473,779 +1473,6 @@ TEST (bootstrap, tcp_node_id_handshake)
|
|||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void add_callback_stats (nano::node & node)
|
||||
{
|
||||
node.observers.blocks.add ([& stats = node.stats](nano::election_status const & status_a, nano::account const &, nano::amount const &, bool) {
|
||||
stats.inc (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST (confirmation_height, single)
|
||||
{
|
||||
auto amount (std::numeric_limits<nano::uint128_t>::max ());
|
||||
nano::system system (24000, 2);
|
||||
nano::keypair key1;
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
nano::block_hash latest1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
|
||||
system.wallet (1)->insert_adhoc (key1.prv);
|
||||
auto send1 (std::make_shared<nano::send_block> (latest1, key1.pub, amount - system.nodes[0]->config.receive_minimum.number (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest1)));
|
||||
|
||||
// Check confirmation heights before, should be uninitialized (1 for genesis).
|
||||
uint64_t confirmation_height;
|
||||
for (auto & node : system.nodes)
|
||||
{
|
||||
add_callback_stats (*node);
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (1, confirmation_height);
|
||||
}
|
||||
|
||||
for (auto & node : system.nodes)
|
||||
{
|
||||
node->process_active (send1);
|
||||
node->block_processor.flush ();
|
||||
|
||||
system.deadline_set (10s);
|
||||
while (true)
|
||||
{
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
if (node->ledger.block_confirmed (transaction, send1->hash ()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (2, confirmation_height);
|
||||
|
||||
// Rollbacks should fail as these blocks have been cemented
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, latest1));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, send1->hash ()));
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
}
|
||||
}
|
||||
|
||||
TEST (confirmation_height, multiple_accounts)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24001, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
system.add_node (node_config);
|
||||
node_config.peering_port = 24002;
|
||||
system.add_node (node_config);
|
||||
nano::keypair key1;
|
||||
nano::keypair key2;
|
||||
nano::keypair key3;
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
nano::block_hash latest1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
|
||||
system.wallet (1)->insert_adhoc (key1.prv);
|
||||
system.wallet (0)->insert_adhoc (key2.prv);
|
||||
system.wallet (1)->insert_adhoc (key3.prv);
|
||||
|
||||
// Send to all accounts
|
||||
nano::send_block send1 (latest1, key1.pub, system.nodes.front ()->config.online_weight_minimum.number () + 300, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest1));
|
||||
nano::send_block send2 (send1.hash (), key2.pub, system.nodes.front ()->config.online_weight_minimum.number () + 200, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send1.hash ()));
|
||||
nano::send_block send3 (send2.hash (), key3.pub, system.nodes.front ()->config.online_weight_minimum.number () + 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send2.hash ()));
|
||||
|
||||
// Open all accounts
|
||||
nano::open_block open1 (send1.hash (), nano::genesis_account, key1.pub, key1.prv, key1.pub, system.work.generate (key1.pub));
|
||||
nano::open_block open2 (send2.hash (), nano::genesis_account, key2.pub, key2.prv, key2.pub, system.work.generate (key2.pub));
|
||||
nano::open_block open3 (send3.hash (), nano::genesis_account, key3.pub, key3.prv, key3.pub, system.work.generate (key3.pub));
|
||||
|
||||
// Send and recieve various blocks to these accounts
|
||||
nano::send_block send4 (open1.hash (), key2.pub, 50, key1.prv, key1.pub, system.work.generate (open1.hash ()));
|
||||
nano::send_block send5 (send4.hash (), key2.pub, 10, key1.prv, key1.pub, system.work.generate (send4.hash ()));
|
||||
|
||||
nano::receive_block receive1 (open2.hash (), send4.hash (), key2.prv, key2.pub, system.work.generate (open2.hash ()));
|
||||
nano::send_block send6 (receive1.hash (), key3.pub, 10, key2.prv, key2.pub, system.work.generate (receive1.hash ()));
|
||||
nano::receive_block receive2 (send6.hash (), send5.hash (), key2.prv, key2.pub, system.work.generate (send6.hash ()));
|
||||
|
||||
for (auto & node : system.nodes)
|
||||
{
|
||||
add_callback_stats (*node);
|
||||
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send3).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open3).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send4).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send5).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, receive1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send6).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, receive2).code);
|
||||
|
||||
// Check confirmation heights of all the accounts are uninitialized (0),
|
||||
// as we have any just added them to the ledger and not processed any live transactions yet.
|
||||
uint64_t confirmation_height;
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (1, confirmation_height);
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key1.pub, confirmation_height));
|
||||
ASSERT_EQ (0, confirmation_height);
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key2.pub, confirmation_height));
|
||||
ASSERT_EQ (0, confirmation_height);
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key3.pub, confirmation_height));
|
||||
ASSERT_EQ (0, confirmation_height);
|
||||
}
|
||||
|
||||
// The nodes process a live receive which propagates across to all accounts
|
||||
auto receive3 = std::make_shared<nano::receive_block> (open3.hash (), send6.hash (), key3.prv, key3.pub, system.work.generate (open3.hash ()));
|
||||
|
||||
for (auto & node : system.nodes)
|
||||
{
|
||||
node->process_active (receive3);
|
||||
node->block_processor.flush ();
|
||||
|
||||
system.deadline_set (10s);
|
||||
while (true)
|
||||
{
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
if (node->ledger.block_confirmed (transaction, receive3->hash ()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
nano::account_info account_info;
|
||||
uint64_t confirmation_height;
|
||||
auto & store = node->store;
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
ASSERT_FALSE (store.account_get (transaction, nano::test_genesis_key.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (4, confirmation_height);
|
||||
ASSERT_EQ (4, account_info.block_count);
|
||||
ASSERT_FALSE (store.account_get (transaction, key1.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key1.pub, confirmation_height));
|
||||
ASSERT_EQ (2, confirmation_height);
|
||||
ASSERT_EQ (3, account_info.block_count);
|
||||
ASSERT_FALSE (store.account_get (transaction, key2.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key2.pub, confirmation_height));
|
||||
ASSERT_EQ (3, confirmation_height);
|
||||
ASSERT_EQ (4, account_info.block_count);
|
||||
ASSERT_FALSE (store.account_get (transaction, key3.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key3.pub, confirmation_height));
|
||||
ASSERT_EQ (2, confirmation_height);
|
||||
ASSERT_EQ (2, account_info.block_count);
|
||||
|
||||
// The accounts for key1 and key2 have 1 more block in the chain than is confirmed.
|
||||
// So this can be rolled back, but the one before that cannot. Check that this is the case
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_FALSE (node->ledger.rollback (transaction, node->latest (key2.pub)));
|
||||
ASSERT_FALSE (node->ledger.rollback (transaction, node->latest (key1.pub)));
|
||||
}
|
||||
{
|
||||
// These rollbacks should fail
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, node->latest (key1.pub)));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, node->latest (key2.pub)));
|
||||
|
||||
// Confirm the other latest can't be rolled back either
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, node->latest (key3.pub)));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, node->latest (nano::test_genesis_key.pub)));
|
||||
|
||||
// Attempt some others which have been cemented
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, open1.hash ()));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, send2.hash ()));
|
||||
}
|
||||
ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (10, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
}
|
||||
}
|
||||
|
||||
TEST (confirmation_height, gap_bootstrap)
|
||||
{
|
||||
nano::system system (24000, 1);
|
||||
auto & node1 (*system.nodes[0]);
|
||||
nano::genesis genesis;
|
||||
nano::keypair destination;
|
||||
auto send1 (std::make_shared<nano::state_block> (nano::genesis_account, genesis.hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
|
||||
node1.work_generate_blocking (*send1);
|
||||
auto send2 (std::make_shared<nano::state_block> (nano::genesis_account, send1->hash (), nano::genesis_account, nano::genesis_amount - 2 * nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
|
||||
node1.work_generate_blocking (*send2);
|
||||
auto send3 (std::make_shared<nano::state_block> (nano::genesis_account, send2->hash (), nano::genesis_account, nano::genesis_amount - 3 * nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
|
||||
node1.work_generate_blocking (*send3);
|
||||
auto open1 (std::make_shared<nano::open_block> (send1->hash (), destination.pub, destination.pub, destination.prv, destination.pub, 0));
|
||||
node1.work_generate_blocking (*open1);
|
||||
|
||||
// Receive
|
||||
auto receive1 (std::make_shared<nano::receive_block> (open1->hash (), send2->hash (), destination.prv, destination.pub, 0));
|
||||
node1.work_generate_blocking (*receive1);
|
||||
auto receive2 (std::make_shared<nano::receive_block> (receive1->hash (), send3->hash (), destination.prv, destination.pub, 0));
|
||||
node1.work_generate_blocking (*receive2);
|
||||
|
||||
node1.block_processor.add (send1);
|
||||
node1.block_processor.add (send2);
|
||||
node1.block_processor.add (send3);
|
||||
node1.block_processor.add (receive1);
|
||||
node1.block_processor.flush ();
|
||||
|
||||
add_callback_stats (node1);
|
||||
|
||||
// Receive 2 comes in on the live network, however the chain has not been finished so it gets added to unchecked
|
||||
node1.process_active (receive2);
|
||||
node1.block_processor.flush ();
|
||||
|
||||
// Confirmation heights should not be updated
|
||||
{
|
||||
auto transaction (node1.store.tx_begin_read ());
|
||||
auto unchecked_count (node1.store.unchecked_count (transaction));
|
||||
ASSERT_EQ (unchecked_count, 2);
|
||||
|
||||
uint64_t confirmation_height;
|
||||
ASSERT_FALSE (node1.store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (1, confirmation_height);
|
||||
}
|
||||
|
||||
// Now complete the chain where the block comes in on the bootstrap network.
|
||||
node1.block_processor.add (open1);
|
||||
node1.block_processor.flush ();
|
||||
|
||||
// Confirmation height should be unchanged and unchecked should now be 0
|
||||
{
|
||||
auto transaction (node1.store.tx_begin_read ());
|
||||
auto unchecked_count (node1.store.unchecked_count (transaction));
|
||||
ASSERT_EQ (unchecked_count, 0);
|
||||
|
||||
uint64_t confirmation_height;
|
||||
ASSERT_FALSE (node1.store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (1, confirmation_height);
|
||||
ASSERT_FALSE (node1.store.confirmation_height_get (transaction, destination.pub, confirmation_height));
|
||||
ASSERT_EQ (0, confirmation_height);
|
||||
}
|
||||
ASSERT_EQ (0, node1.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (0, node1.stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
}
|
||||
|
||||
TEST (confirmation_height, gap_live)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24001, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
system.add_node (node_config);
|
||||
node_config.peering_port = 24002;
|
||||
system.add_node (node_config);
|
||||
nano::keypair destination;
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
system.wallet (1)->insert_adhoc (destination.prv);
|
||||
|
||||
nano::genesis genesis;
|
||||
auto send1 (std::make_shared<nano::state_block> (nano::genesis_account, genesis.hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
|
||||
system.nodes[0]->work_generate_blocking (*send1);
|
||||
auto send2 (std::make_shared<nano::state_block> (nano::genesis_account, send1->hash (), nano::genesis_account, nano::genesis_amount - 2 * nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
|
||||
system.nodes[0]->work_generate_blocking (*send2);
|
||||
auto send3 (std::make_shared<nano::state_block> (nano::genesis_account, send2->hash (), nano::genesis_account, nano::genesis_amount - 3 * nano::Gxrb_ratio, destination.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
|
||||
system.nodes[0]->work_generate_blocking (*send3);
|
||||
|
||||
auto open1 (std::make_shared<nano::open_block> (send1->hash (), destination.pub, destination.pub, destination.prv, destination.pub, 0));
|
||||
system.nodes[0]->work_generate_blocking (*open1);
|
||||
auto receive1 (std::make_shared<nano::receive_block> (open1->hash (), send2->hash (), destination.prv, destination.pub, 0));
|
||||
system.nodes[0]->work_generate_blocking (*receive1);
|
||||
auto receive2 (std::make_shared<nano::receive_block> (receive1->hash (), send3->hash (), destination.prv, destination.pub, 0));
|
||||
system.nodes[0]->work_generate_blocking (*receive2);
|
||||
|
||||
for (auto & node : system.nodes)
|
||||
{
|
||||
node->block_processor.add (send1);
|
||||
node->block_processor.add (send2);
|
||||
node->block_processor.add (send3);
|
||||
node->block_processor.add (receive1);
|
||||
node->block_processor.flush ();
|
||||
|
||||
add_callback_stats (*node);
|
||||
|
||||
// Receive 2 comes in on the live network, however the chain has not been finished so it gets added to unchecked
|
||||
node->process_active (receive2);
|
||||
node->block_processor.flush ();
|
||||
|
||||
// Confirmation heights should not be updated
|
||||
{
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
uint64_t confirmation_height;
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (1, confirmation_height);
|
||||
}
|
||||
|
||||
// Now complete the chain where the block comes in on the live network
|
||||
node->process_active (open1);
|
||||
node->block_processor.flush ();
|
||||
|
||||
system.deadline_set (10s);
|
||||
while (true)
|
||||
{
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
if (node->ledger.block_confirmed (transaction, receive2->hash ()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
// This should confirm the open block and the source of the receive blocks
|
||||
auto transaction (node->store.tx_begin_read ());
|
||||
auto unchecked_count (node->store.unchecked_count (transaction));
|
||||
ASSERT_EQ (unchecked_count, 0);
|
||||
|
||||
uint64_t confirmation_height;
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (4, confirmation_height);
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, destination.pub, confirmation_height));
|
||||
ASSERT_EQ (3, confirmation_height);
|
||||
|
||||
ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (6, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
}
|
||||
}
|
||||
|
||||
TEST (confirmation_height, send_receive_between_2_accounts)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config);
|
||||
nano::keypair key1;
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
nano::block_hash latest (node->latest (nano::test_genesis_key.pub));
|
||||
system.wallet (0)->insert_adhoc (key1.prv);
|
||||
|
||||
nano::send_block send1 (latest, key1.pub, node->config.online_weight_minimum.number () + 2, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest));
|
||||
nano::open_block open1 (send1.hash (), nano::genesis_account, key1.pub, key1.prv, key1.pub, system.work.generate (key1.pub));
|
||||
|
||||
nano::send_block send2 (open1.hash (), nano::genesis_account, 1000, key1.prv, key1.pub, system.work.generate (open1.hash ()));
|
||||
nano::send_block send3 (send2.hash (), nano::genesis_account, 900, key1.prv, key1.pub, system.work.generate (send2.hash ()));
|
||||
nano::send_block send4 (send3.hash (), nano::genesis_account, 500, key1.prv, key1.pub, system.work.generate (send3.hash ()));
|
||||
|
||||
nano::receive_block receive1 (send1.hash (), send2.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send1.hash ()));
|
||||
nano::receive_block receive2 (receive1.hash (), send3.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (receive1.hash ()));
|
||||
nano::receive_block receive3 (receive2.hash (), send4.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (receive2.hash ()));
|
||||
|
||||
nano::send_block send5 (receive3.hash (), key1.pub, node->config.online_weight_minimum.number () + 1, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (receive3.hash ()));
|
||||
auto receive4 = std::make_shared<nano::receive_block> (send4.hash (), send5.hash (), key1.prv, key1.pub, system.work.generate (send4.hash ()));
|
||||
// Unpocketed send
|
||||
nano::keypair key2;
|
||||
nano::send_block send6 (send5.hash (), key2.pub, node->config.online_weight_minimum.number (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send5.hash ()));
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open1).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, receive1).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send3).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send4).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, receive2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, receive3).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send5).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send6).code);
|
||||
}
|
||||
|
||||
add_callback_stats (*node);
|
||||
|
||||
node->process_active (receive4);
|
||||
node->block_processor.flush ();
|
||||
|
||||
system.deadline_set (10s);
|
||||
while (true)
|
||||
{
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
if (node->ledger.block_confirmed (transaction, receive4->hash ()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
auto transaction (node->store.tx_begin_read ());
|
||||
|
||||
nano::account_info account_info;
|
||||
uint64_t confirmation_height;
|
||||
ASSERT_FALSE (node->store.account_get (transaction, nano::test_genesis_key.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (6, confirmation_height);
|
||||
ASSERT_EQ (7, account_info.block_count);
|
||||
|
||||
ASSERT_FALSE (node->store.account_get (transaction, key1.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key1.pub, confirmation_height));
|
||||
ASSERT_EQ (5, confirmation_height);
|
||||
ASSERT_EQ (5, account_info.block_count);
|
||||
|
||||
ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (10, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
ASSERT_EQ (11, node->ledger.cemented_count);
|
||||
}
|
||||
|
||||
TEST (confirmation_height, send_receive_self)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config);
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
nano::block_hash latest (node->latest (nano::test_genesis_key.pub));
|
||||
|
||||
nano::send_block send1 (latest, nano::test_genesis_key.pub, nano::genesis_amount - 2, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest));
|
||||
nano::receive_block receive1 (send1.hash (), send1.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send1.hash ()));
|
||||
nano::send_block send2 (receive1.hash (), nano::test_genesis_key.pub, nano::genesis_amount - 2, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (receive1.hash ()));
|
||||
nano::send_block send3 (send2.hash (), nano::test_genesis_key.pub, nano::genesis_amount - 3, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send2.hash ()));
|
||||
|
||||
nano::receive_block receive2 (send3.hash (), send2.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send3.hash ()));
|
||||
auto receive3 = std::make_shared<nano::receive_block> (receive2.hash (), send3.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (receive2.hash ()));
|
||||
|
||||
// Send to another account to prevent automatic receiving on the genesis account
|
||||
nano::keypair key1;
|
||||
nano::send_block send4 (receive3->hash (), key1.pub, node->config.online_weight_minimum.number (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (receive3->hash ()));
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, receive1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send3).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, receive2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *receive3).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send4).code);
|
||||
}
|
||||
|
||||
add_callback_stats (*node);
|
||||
|
||||
node->block_confirm (receive3);
|
||||
|
||||
system.deadline_set (10s);
|
||||
while (true)
|
||||
{
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
if (node->ledger.block_confirmed (transaction, receive3->hash ()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
auto transaction (node->store.tx_begin_read ());
|
||||
nano::account_info account_info;
|
||||
ASSERT_FALSE (node->store.account_get (transaction, nano::test_genesis_key.pub, account_info));
|
||||
uint64_t confirmation_height;
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (7, confirmation_height);
|
||||
ASSERT_EQ (8, account_info.block_count);
|
||||
ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (6, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
ASSERT_EQ (confirmation_height, node->ledger.cemented_count);
|
||||
}
|
||||
|
||||
TEST (confirmation_height, all_block_types)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config);
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
nano::block_hash latest (node->latest (nano::test_genesis_key.pub));
|
||||
nano::keypair key1;
|
||||
nano::keypair key2;
|
||||
auto & store = node->store;
|
||||
nano::send_block send (latest, key1.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest));
|
||||
nano::send_block send1 (send.hash (), key2.pub, nano::genesis_amount - nano::Gxrb_ratio * 2, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send.hash ()));
|
||||
|
||||
nano::open_block open (send.hash (), nano::test_genesis_key.pub, key1.pub, key1.prv, key1.pub, system.work.generate (key1.pub));
|
||||
nano::state_block state_open (key2.pub, 0, 0, nano::Gxrb_ratio, send1.hash (), key2.prv, key2.pub, system.work.generate (key2.pub));
|
||||
|
||||
nano::send_block send2 (open.hash (), key2.pub, 0, key1.prv, key1.pub, system.work.generate (open.hash ()));
|
||||
nano::state_block state_receive (key2.pub, state_open.hash (), 0, nano::Gxrb_ratio * 2, send2.hash (), key2.prv, key2.pub, system.work.generate (state_open.hash ()));
|
||||
|
||||
nano::state_block state_send (key2.pub, state_receive.hash (), 0, nano::Gxrb_ratio, key1.pub, key2.prv, key2.pub, system.work.generate (state_receive.hash ()));
|
||||
nano::receive_block receive (send2.hash (), state_send.hash (), key1.prv, key1.pub, system.work.generate (send2.hash ()));
|
||||
|
||||
nano::change_block change (receive.hash (), key2.pub, key1.prv, key1.pub, system.work.generate (receive.hash ()));
|
||||
|
||||
nano::state_block state_change (key2.pub, state_send.hash (), nano::test_genesis_key.pub, nano::Gxrb_ratio, 0, key2.prv, key2.pub, system.work.generate (state_send.hash ()));
|
||||
|
||||
nano::state_block epoch (key2.pub, state_change.hash (), nano::test_genesis_key.pub, nano::Gxrb_ratio, node->ledger.link (nano::epoch::epoch_1), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (state_change.hash ()));
|
||||
|
||||
nano::state_block epoch1 (key1.pub, change.hash (), key2.pub, nano::Gxrb_ratio, node->ledger.link (nano::epoch::epoch_1), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (change.hash ()));
|
||||
nano::state_block state_send1 (key1.pub, epoch1.hash (), 0, nano::Gxrb_ratio - 1, key2.pub, key1.prv, key1.pub, system.work.generate (epoch1.hash ()));
|
||||
nano::state_block state_receive2 (key2.pub, epoch.hash (), 0, nano::Gxrb_ratio + 1, state_send1.hash (), key2.prv, key2.pub, system.work.generate (epoch.hash ()));
|
||||
|
||||
auto state_send2 = std::make_shared<nano::state_block> (key2.pub, state_receive2.hash (), 0, nano::Gxrb_ratio, key1.pub, key2.prv, key2.pub, system.work.generate (state_receive2.hash ()));
|
||||
nano::state_block state_send3 (key2.pub, state_send2->hash (), 0, nano::Gxrb_ratio - 1, key1.pub, key2.prv, key2.pub, system.work.generate (state_send2->hash ()));
|
||||
|
||||
nano::state_block state_send4 (key1.pub, state_send1.hash (), 0, nano::Gxrb_ratio - 2, nano::test_genesis_key.pub, key1.prv, key1.pub, system.work.generate (state_send1.hash ()));
|
||||
nano::state_block state_receive3 (nano::genesis_account, send1.hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio * 2 + 1, state_send4.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send1.hash ()));
|
||||
|
||||
{
|
||||
auto transaction (store.tx_begin_write ());
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_open).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_receive).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_send).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, receive).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, change).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_change).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, epoch).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, epoch1).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_send1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_receive2).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *state_send2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_send3).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_send4).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, state_receive3).code);
|
||||
}
|
||||
|
||||
add_callback_stats (*node);
|
||||
node->block_confirm (state_send2);
|
||||
|
||||
system.deadline_set (10s);
|
||||
while (true)
|
||||
{
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
if (node->ledger.block_confirmed (transaction, state_send2->hash ()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
auto transaction (node->store.tx_begin_read ());
|
||||
nano::account_info account_info;
|
||||
uint64_t confirmation_height;
|
||||
ASSERT_FALSE (node->store.account_get (transaction, nano::test_genesis_key.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, nano::test_genesis_key.pub, confirmation_height));
|
||||
ASSERT_EQ (3, confirmation_height);
|
||||
ASSERT_LE (4, account_info.block_count);
|
||||
|
||||
ASSERT_FALSE (node->store.account_get (transaction, key1.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key1.pub, confirmation_height));
|
||||
ASSERT_EQ (6, confirmation_height);
|
||||
ASSERT_LE (7, account_info.block_count);
|
||||
|
||||
ASSERT_FALSE (node->store.account_get (transaction, key2.pub, account_info));
|
||||
ASSERT_FALSE (node->store.confirmation_height_get (transaction, key2.pub, confirmation_height));
|
||||
ASSERT_EQ (7, confirmation_height);
|
||||
ASSERT_LE (8, account_info.block_count);
|
||||
|
||||
ASSERT_EQ (15, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (15, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
ASSERT_EQ (16, node->ledger.cemented_count);
|
||||
}
|
||||
|
||||
/* Bulk of the this test was taken from the node.fork_flip test */
|
||||
TEST (confirmation_height, conflict_rollback_cemented)
|
||||
{
|
||||
boost::iostreams::stream_buffer<nano::stringstream_mt_sink> sb;
|
||||
sb.open (nano::stringstream_mt_sink{});
|
||||
nano::boost_log_cerr_redirect redirect_cerr (&sb);
|
||||
nano::system system (24000, 2);
|
||||
auto & node1 (*system.nodes[0]);
|
||||
auto & node2 (*system.nodes[1]);
|
||||
ASSERT_EQ (1, node1.network.size ());
|
||||
nano::keypair key1;
|
||||
nano::genesis genesis;
|
||||
auto send1 (std::make_shared<nano::send_block> (genesis.hash (), key1.pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (genesis.hash ())));
|
||||
nano::publish publish1 (send1);
|
||||
nano::keypair key2;
|
||||
auto send2 (std::make_shared<nano::send_block> (genesis.hash (), key2.pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (genesis.hash ())));
|
||||
nano::publish publish2 (send2);
|
||||
auto channel1 (node1.network.udp_channels.create (node1.network.endpoint ()));
|
||||
node1.network.process_message (publish1, channel1);
|
||||
node1.block_processor.flush ();
|
||||
auto channel2 (node2.network.udp_channels.create (node1.network.endpoint ()));
|
||||
node2.network.process_message (publish2, channel2);
|
||||
node2.block_processor.flush ();
|
||||
ASSERT_EQ (1, node1.active.size ());
|
||||
ASSERT_EQ (1, node2.active.size ());
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
node1.network.process_message (publish2, channel1);
|
||||
node1.block_processor.flush ();
|
||||
node2.network.process_message (publish1, channel2);
|
||||
node2.block_processor.flush ();
|
||||
nano::unique_lock<std::mutex> lock (node2.active.mutex);
|
||||
auto conflict (node2.active.roots.find (nano::qualified_root (genesis.hash (), genesis.hash ())));
|
||||
ASSERT_NE (node2.active.roots.end (), conflict);
|
||||
auto votes1 (conflict->election);
|
||||
ASSERT_NE (nullptr, votes1);
|
||||
ASSERT_EQ (1, votes1->last_votes.size ());
|
||||
lock.unlock ();
|
||||
// Force blocks to be cemented on both nodes
|
||||
{
|
||||
auto transaction (system.nodes[0]->store.tx_begin_write ());
|
||||
ASSERT_TRUE (node1.store.block_exists (transaction, publish1.block->hash ()));
|
||||
node1.store.confirmation_height_put (transaction, nano::genesis_account, 2);
|
||||
}
|
||||
{
|
||||
auto transaction (system.nodes[1]->store.tx_begin_write ());
|
||||
ASSERT_TRUE (node2.store.block_exists (transaction, publish2.block->hash ()));
|
||||
node2.store.confirmation_height_put (transaction, nano::genesis_account, 2);
|
||||
}
|
||||
|
||||
auto rollback_log_entry = boost::str (boost::format ("Failed to roll back %1%") % send2->hash ().to_string ());
|
||||
system.deadline_set (20s);
|
||||
auto done (false);
|
||||
while (!done)
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
done = (sb.component ()->str ().find (rollback_log_entry) != std::string::npos);
|
||||
}
|
||||
auto transaction1 (system.nodes[0]->store.tx_begin_read ());
|
||||
auto transaction2 (system.nodes[1]->store.tx_begin_read ());
|
||||
lock.lock ();
|
||||
auto winner (*votes1->tally ().begin ());
|
||||
ASSERT_EQ (*publish1.block, *winner.second);
|
||||
ASSERT_EQ (nano::genesis_amount - 100, winner.first);
|
||||
ASSERT_TRUE (node1.store.block_exists (transaction1, publish1.block->hash ()));
|
||||
ASSERT_TRUE (node2.store.block_exists (transaction2, publish2.block->hash ()));
|
||||
ASSERT_FALSE (node2.store.block_exists (transaction2, publish1.block->hash ()));
|
||||
}
|
||||
|
||||
TEST (confirmation_height, observers)
|
||||
{
|
||||
auto amount (std::numeric_limits<nano::uint128_t>::max ());
|
||||
nano::system system (24000, 1);
|
||||
auto node1 (system.nodes[0]);
|
||||
nano::keypair key1;
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
nano::block_hash latest1 (node1->latest (nano::test_genesis_key.pub));
|
||||
auto send1 (std::make_shared<nano::send_block> (latest1, key1.pub, amount - node1->config.receive_minimum.number (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest1)));
|
||||
|
||||
add_callback_stats (*node1);
|
||||
|
||||
node1->process_active (send1);
|
||||
node1->block_processor.flush ();
|
||||
bool confirmed (false);
|
||||
system.deadline_set (10s);
|
||||
while (!confirmed)
|
||||
{
|
||||
auto transaction = node1->store.tx_begin_read ();
|
||||
confirmed = node1->ledger.block_confirmed (transaction, send1->hash ());
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
ASSERT_EQ (1, node1->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (1, node1->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
}
|
||||
|
||||
// This tests when a read has been done and the block no longer exists by the time a write is done
|
||||
TEST (confirmation_height, modified_chain)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config);
|
||||
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
nano::block_hash latest (node->latest (nano::test_genesis_key.pub));
|
||||
|
||||
nano::keypair key1;
|
||||
auto & store = node->store;
|
||||
auto send = std::make_shared<nano::send_block> (latest, key1.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest));
|
||||
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send).code);
|
||||
}
|
||||
|
||||
{
|
||||
// The write guard prevents the confirmation height processor doing any writes
|
||||
auto write_guard = node->write_database_queue.wait (nano::writer::process_batch);
|
||||
node->confirmation_height_processor.add (send->hash ());
|
||||
system.deadline_set (10s);
|
||||
while (!node->write_database_queue.contains (nano::writer::confirmation_height))
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
store.block_del (store.tx_begin_write (), send->hash ());
|
||||
}
|
||||
|
||||
system.deadline_set (10s);
|
||||
while (node->write_database_queue.contains (nano::writer::confirmation_height))
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::invalid_block, nano::stat::dir::in));
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
TEST (confirmation_height, pending_observer_callbacks)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config);
|
||||
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
nano::block_hash latest (node->latest (nano::test_genesis_key.pub));
|
||||
|
||||
nano::keypair key1;
|
||||
nano::send_block send (latest, key1.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest));
|
||||
auto send1 = std::make_shared<nano::send_block> (send.hash (), key1.pub, nano::genesis_amount - nano::Gxrb_ratio * 2, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send.hash ()));
|
||||
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send1).code);
|
||||
}
|
||||
|
||||
add_callback_stats (*node);
|
||||
|
||||
node->confirmation_height_processor.add (send1->hash ());
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (node->pending_confirmation_height.size () == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Can have timing issues.
|
||||
node->confirmation_height_processor.add (send.hash ());
|
||||
{
|
||||
nano::unique_lock<std::mutex> lk (node->pending_confirmation_height.mutex);
|
||||
while (!node->pending_confirmation_height.current_hash.is_zero ())
|
||||
{
|
||||
lk.unlock ();
|
||||
std::this_thread::yield ();
|
||||
lk.lock ();
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm the callback is not called under this circumstance
|
||||
ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (0, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out));
|
||||
ASSERT_EQ (3, node->ledger.cemented_count);
|
||||
}
|
||||
}
|
||||
|
||||
TEST (bootstrap, tcp_listener_timeout_empty)
|
||||
{
|
||||
nano::system system (24000, 1);
|
||||
|
|
|
@ -367,8 +367,10 @@ TEST (node, search_pending_multiple)
|
|||
|
||||
TEST (node, search_pending_confirmed)
|
||||
{
|
||||
nano::system system (24000, 1);
|
||||
auto node (system.nodes[0]);
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config);
|
||||
nano::keypair key2;
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
auto send1 (system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, node->config.receive_minimum.number ()));
|
||||
|
@ -1679,6 +1681,7 @@ TEST (node, broadcast_elected)
|
|||
node2->process_active (fork1);
|
||||
//std::cerr << "fork0: " << fork_hash.to_string () << std::endl;
|
||||
//std::cerr << "fork1: " << fork1.hash ().to_string () << std::endl;
|
||||
system.deadline_set (10s);
|
||||
while (!node0->ledger.block_exists (fork0->hash ()) || !node1->ledger.block_exists (fork0->hash ()))
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
|
@ -3145,203 +3148,6 @@ TEST (node, bidirectional_tcp)
|
|||
}
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
TEST (confirmation_height, prioritize_frontiers)
|
||||
{
|
||||
nano::system system;
|
||||
// Prevent frontiers being confirmed as it will affect the priorization checking
|
||||
nano::node_config node_config (24001, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config);
|
||||
|
||||
nano::keypair key1;
|
||||
nano::keypair key2;
|
||||
nano::keypair key3;
|
||||
nano::keypair key4;
|
||||
nano::block_hash latest1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
|
||||
|
||||
// Send different numbers of blocks all accounts
|
||||
nano::send_block send1 (latest1, key1.pub, node->config.online_weight_minimum.number () + 10000, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest1));
|
||||
nano::send_block send2 (send1.hash (), key1.pub, node->config.online_weight_minimum.number () + 8500, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send1.hash ()));
|
||||
nano::send_block send3 (send2.hash (), key1.pub, node->config.online_weight_minimum.number () + 8000, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send2.hash ()));
|
||||
nano::send_block send4 (send3.hash (), key2.pub, node->config.online_weight_minimum.number () + 7500, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send3.hash ()));
|
||||
nano::send_block send5 (send4.hash (), key3.pub, node->config.online_weight_minimum.number () + 6500, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send4.hash ()));
|
||||
nano::send_block send6 (send5.hash (), key4.pub, node->config.online_weight_minimum.number () + 6000, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send5.hash ()));
|
||||
|
||||
// Open all accounts and add other sends to get different uncemented counts (as well as some which are the same)
|
||||
nano::open_block open1 (send1.hash (), nano::genesis_account, key1.pub, key1.prv, key1.pub, system.work.generate (key1.pub));
|
||||
nano::send_block send7 (open1.hash (), nano::test_genesis_key.pub, 500, key1.prv, key1.pub, system.work.generate (open1.hash ()));
|
||||
|
||||
nano::open_block open2 (send4.hash (), nano::genesis_account, key2.pub, key2.prv, key2.pub, system.work.generate (key2.pub));
|
||||
|
||||
nano::open_block open3 (send5.hash (), nano::genesis_account, key3.pub, key3.prv, key3.pub, system.work.generate (key3.pub));
|
||||
nano::send_block send8 (open3.hash (), nano::test_genesis_key.pub, 500, key3.prv, key3.pub, system.work.generate (open3.hash ()));
|
||||
nano::send_block send9 (send8.hash (), nano::test_genesis_key.pub, 200, key3.prv, key3.pub, system.work.generate (send8.hash ()));
|
||||
|
||||
nano::open_block open4 (send6.hash (), nano::genesis_account, key4.pub, key4.prv, key4.pub, system.work.generate (key4.pub));
|
||||
nano::send_block send10 (open4.hash (), nano::test_genesis_key.pub, 500, key4.prv, key4.pub, system.work.generate (open4.hash ()));
|
||||
nano::send_block send11 (send10.hash (), nano::test_genesis_key.pub, 200, key4.prv, key4.pub, system.work.generate (send10.hash ()));
|
||||
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send2).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send3).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send4).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send5).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send6).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send7).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open2).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open3).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send8).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send9).code);
|
||||
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open4).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send10).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send11).code);
|
||||
}
|
||||
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
constexpr auto num_accounts = 5;
|
||||
// clang-format off
|
||||
auto priority_orders_match = [](auto const & cementable_frontiers, auto const & desired_order) {
|
||||
return std::equal (desired_order.begin (), desired_order.end (), cementable_frontiers.template get<1> ().begin (), cementable_frontiers.template get<1> ().end (), [](nano::account const & account, nano::cementable_account const & cementable_account) {
|
||||
return (account == cementable_account.account);
|
||||
});
|
||||
};
|
||||
// clang-format on
|
||||
{
|
||||
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
|
||||
ASSERT_EQ (node->active.priority_cementable_frontiers_size (), num_accounts);
|
||||
// Check the order of accounts is as expected (greatest number of uncemented blocks at the front). key3 and key4 have the same value, the order is unspecified so check both
|
||||
std::array<nano::account, num_accounts> desired_order_1{ nano::genesis_account, key3.pub, key4.pub, key1.pub, key2.pub };
|
||||
std::array<nano::account, num_accounts> desired_order_2{ nano::genesis_account, key4.pub, key3.pub, key1.pub, key2.pub };
|
||||
ASSERT_TRUE (priority_orders_match (node->active.priority_cementable_frontiers, desired_order_1) || priority_orders_match (node->active.priority_cementable_frontiers, desired_order_2));
|
||||
}
|
||||
|
||||
{
|
||||
// Add some to the local node wallets and check ordering of both containers
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
system.wallet (0)->insert_adhoc (key1.prv);
|
||||
system.wallet (0)->insert_adhoc (key2.prv);
|
||||
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
|
||||
ASSERT_EQ (node->active.priority_cementable_frontiers_size (), num_accounts - 3);
|
||||
ASSERT_EQ (node->active.priority_wallet_cementable_frontiers_size (), num_accounts - 2);
|
||||
std::array<nano::account, 3> local_desired_order{ nano::genesis_account, key1.pub, key2.pub };
|
||||
ASSERT_TRUE (priority_orders_match (node->active.priority_wallet_cementable_frontiers, local_desired_order));
|
||||
std::array<nano::account, 2> desired_order_1{ key3.pub, key4.pub };
|
||||
std::array<nano::account, 2> desired_order_2{ key4.pub, key3.pub };
|
||||
ASSERT_TRUE (priority_orders_match (node->active.priority_cementable_frontiers, desired_order_1) || priority_orders_match (node->active.priority_cementable_frontiers, desired_order_2));
|
||||
}
|
||||
|
||||
{
|
||||
// Add the remainder of accounts to node wallets and check size/ordering is correct
|
||||
system.wallet (0)->insert_adhoc (key3.prv);
|
||||
system.wallet (0)->insert_adhoc (key4.prv);
|
||||
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
|
||||
ASSERT_EQ (node->active.priority_cementable_frontiers_size (), 0);
|
||||
ASSERT_EQ (node->active.priority_wallet_cementable_frontiers_size (), num_accounts);
|
||||
std::array<nano::account, num_accounts> desired_order_1{ nano::genesis_account, key3.pub, key4.pub, key1.pub, key2.pub };
|
||||
std::array<nano::account, num_accounts> desired_order_2{ nano::genesis_account, key4.pub, key3.pub, key1.pub, key2.pub };
|
||||
ASSERT_TRUE (priority_orders_match (node->active.priority_wallet_cementable_frontiers, desired_order_1) || priority_orders_match (node->active.priority_wallet_cementable_frontiers, desired_order_2));
|
||||
}
|
||||
|
||||
// Check that accounts which already exist have their order modified when the uncemented count changes.
|
||||
nano::send_block send12 (send9.hash (), nano::test_genesis_key.pub, 100, key3.prv, key3.pub, system.work.generate (send9.hash ()));
|
||||
nano::send_block send13 (send12.hash (), nano::test_genesis_key.pub, 90, key3.prv, key3.pub, system.work.generate (send12.hash ()));
|
||||
nano::send_block send14 (send13.hash (), nano::test_genesis_key.pub, 80, key3.prv, key3.pub, system.work.generate (send13.hash ()));
|
||||
nano::send_block send15 (send14.hash (), nano::test_genesis_key.pub, 70, key3.prv, key3.pub, system.work.generate (send14.hash ()));
|
||||
nano::send_block send16 (send15.hash (), nano::test_genesis_key.pub, 60, key3.prv, key3.pub, system.work.generate (send15.hash ()));
|
||||
nano::send_block send17 (send16.hash (), nano::test_genesis_key.pub, 50, key3.prv, key3.pub, system.work.generate (send16.hash ()));
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send12).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send13).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send14).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send15).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send16).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send17).code);
|
||||
}
|
||||
transaction.refresh ();
|
||||
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
|
||||
ASSERT_TRUE (priority_orders_match (node->active.priority_wallet_cementable_frontiers, std::array<nano::account, num_accounts>{ key3.pub, nano::genesis_account, key4.pub, key1.pub, key2.pub }));
|
||||
node->active.confirm_frontiers (transaction);
|
||||
|
||||
// Check that the active transactions roots contains the frontiers
|
||||
system.deadline_set (std::chrono::seconds (10));
|
||||
while (node->active.size () != num_accounts)
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
||||
std::array<nano::qualified_root, num_accounts> frontiers{ send17.qualified_root (), send6.qualified_root (), send7.qualified_root (), open2.qualified_root (), send11.qualified_root () };
|
||||
for (auto & frontier : frontiers)
|
||||
{
|
||||
ASSERT_NE (node->active.roots.find (frontier), node->active.roots.end ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST (confirmation_height, frontiers_confirmation_mode)
|
||||
{
|
||||
nano::genesis genesis;
|
||||
nano::keypair key;
|
||||
// Always mode
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::always;
|
||||
auto node = system.add_node (node_config);
|
||||
nano::state_block send (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *node->work_generate_blocking (genesis.hash ()));
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send).code);
|
||||
}
|
||||
system.deadline_set (5s);
|
||||
while (node->active.size () != 1)
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
}
|
||||
// Auto mode
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::automatic;
|
||||
auto node = system.add_node (node_config);
|
||||
nano::state_block send (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *node->work_generate_blocking (genesis.hash ()));
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send).code);
|
||||
}
|
||||
system.deadline_set (5s);
|
||||
while (node->active.size () != 1)
|
||||
{
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
}
|
||||
// Disabled mode
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (24000, system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config);
|
||||
nano::state_block send (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *node->work_generate_blocking (genesis.hash ()));
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send).code);
|
||||
}
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
std::this_thread::sleep_for (std::chrono::seconds (1));
|
||||
ASSERT_EQ (0, node->active.size ());
|
||||
}
|
||||
}
|
||||
|
||||
TEST (active_difficulty, recalculate_work)
|
||||
{
|
||||
nano::system system;
|
||||
|
|
|
@ -101,6 +101,51 @@ void nano::active_transactions::confirm_frontiers (nano::transaction const & tra
|
|||
next_frontier_check = steady_clock::now () + (agressive_factor / test_network_factor);
|
||||
}
|
||||
}
|
||||
void nano::active_transactions::post_confirmation_height_set (nano::transaction const & transaction_a, std::shared_ptr<nano::block> block_a, nano::block_sideband const & sideband_a, nano::election_status_type election_status_type_a)
|
||||
{
|
||||
if (election_status_type_a == nano::election_status_type::inactive_confirmation_height)
|
||||
{
|
||||
nano::account account (0);
|
||||
nano::uint128_t amount (0);
|
||||
bool is_state_send (false);
|
||||
nano::account pending_account (0);
|
||||
node.process_confirmed_data (transaction_a, block_a, block_a->hash (), sideband_a, account, amount, is_state_send, pending_account);
|
||||
node.observers.blocks.notify (nano::election_status{ block_a, 0, std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values<std::chrono::milliseconds>::zero (), nano::election_status_type::inactive_confirmation_height }, account, amount, is_state_send);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto hash (block_a->hash ());
|
||||
nano::lock_guard<std::mutex> lock (mutex);
|
||||
auto existing (pending_conf_height.find (hash));
|
||||
if (existing != pending_conf_height.end ())
|
||||
{
|
||||
auto election = existing->second;
|
||||
if (election->confirmed && !election->stopped && election->status.winner->hash () == hash)
|
||||
{
|
||||
add_confirmed (existing->second->status, block_a->qualified_root ());
|
||||
|
||||
node.receive_confirmed (transaction_a, block_a, hash);
|
||||
nano::account account (0);
|
||||
nano::uint128_t amount (0);
|
||||
bool is_state_send (false);
|
||||
nano::account pending_account (0);
|
||||
node.process_confirmed_data (transaction_a, block_a, hash, sideband_a, account, amount, is_state_send, pending_account);
|
||||
election->status.type = election_status_type_a;
|
||||
node.observers.blocks.notify (election->status, account, amount, is_state_send);
|
||||
if (amount > 0)
|
||||
{
|
||||
node.observers.account_balance.notify (account, false);
|
||||
if (!pending_account.is_zero ())
|
||||
{
|
||||
node.observers.account_balance.notify (pending_account, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pending_conf_height.erase (hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> & lock_a)
|
||||
{
|
||||
|
@ -128,11 +173,10 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
|
|||
auto election_l (i->election);
|
||||
if ((election_l->confirmed || election_l->stopped) && election_l->confirmation_request_count >= minimum_confirmation_request_count - 1)
|
||||
{
|
||||
if (election_l->confirmed)
|
||||
if (election_l->stopped)
|
||||
{
|
||||
add_confirmed (election_l->status, root);
|
||||
inactive.insert (root);
|
||||
}
|
||||
inactive.insert (root);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -877,7 +921,14 @@ bool nano::active_transactions::publish (std::shared_ptr<nano::block> block_a)
|
|||
return result;
|
||||
}
|
||||
|
||||
void nano::active_transactions::confirm_block (nano::transaction const & transaction_a, std::shared_ptr<nano::block> block_a, nano::block_sideband const & sideband_a)
|
||||
void nano::active_transactions::clear_block (nano::block_hash const & hash_a)
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (mutex);
|
||||
pending_conf_height.erase (hash_a);
|
||||
}
|
||||
|
||||
// Returns the type of election status requiring callbacks calling later
|
||||
boost::optional<nano::election_status_type> nano::active_transactions::confirm_block (nano::transaction const & transaction_a, std::shared_ptr<nano::block> block_a)
|
||||
{
|
||||
auto hash (block_a->hash ());
|
||||
nano::unique_lock<std::mutex> lock (mutex);
|
||||
|
@ -887,17 +938,16 @@ void nano::active_transactions::confirm_block (nano::transaction const & transac
|
|||
if (!existing->second->confirmed && !existing->second->stopped && existing->second->status.winner->hash () == hash)
|
||||
{
|
||||
existing->second->confirm_once (nano::election_status_type::active_confirmation_height);
|
||||
return nano::election_status_type::active_confirmation_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
return boost::optional<nano::election_status_type>{};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.unlock ();
|
||||
nano::account account (0);
|
||||
nano::uint128_t amount (0);
|
||||
bool is_state_send (false);
|
||||
nano::account pending_account (0);
|
||||
node.process_confirmed_data (transaction_a, block_a, hash, sideband_a, account, amount, is_state_send, pending_account);
|
||||
node.observers.blocks.notify (nano::election_status{ block_a, 0, std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values<std::chrono::milliseconds>::zero (), nano::election_status_type::inactive_confirmation_height }, account, amount, is_state_send);
|
||||
return nano::election_status_type::inactive_confirmation_height;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -985,17 +1035,20 @@ std::unique_ptr<seq_con_info_component> collect_seq_con_info (active_transaction
|
|||
size_t roots_count = 0;
|
||||
size_t blocks_count = 0;
|
||||
size_t confirmed_count = 0;
|
||||
size_t pending_conf_height_count = 0;
|
||||
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (active_transactions.mutex);
|
||||
roots_count = active_transactions.roots.size ();
|
||||
blocks_count = active_transactions.blocks.size ();
|
||||
confirmed_count = active_transactions.confirmed.size ();
|
||||
pending_conf_height_count = active_transactions.pending_conf_height.size ();
|
||||
}
|
||||
|
||||
auto composite = std::make_unique<seq_con_info_composite> (name);
|
||||
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "roots", roots_count, sizeof (decltype (active_transactions.roots)::value_type) }));
|
||||
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "blocks", blocks_count, sizeof (decltype (active_transactions.blocks)::value_type) }));
|
||||
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "pending_conf_height", pending_conf_height_count, sizeof (decltype (active_transactions.pending_conf_height)::value_type) }));
|
||||
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "confirmed", confirmed_count, sizeof (decltype (active_transactions.confirmed)::value_type) }));
|
||||
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "priority_wallet_cementable_frontiers_count", active_transactions.priority_wallet_cementable_frontiers_size (), sizeof (nano::cementable_account) }));
|
||||
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "priority_cementable_frontiers_count", active_transactions.priority_cementable_frontiers_size (), sizeof (nano::cementable_account) }));
|
||||
|
|
|
@ -106,7 +106,8 @@ public:
|
|||
size_t size ();
|
||||
void stop ();
|
||||
bool publish (std::shared_ptr<nano::block> block_a);
|
||||
void confirm_block (nano::transaction const &, std::shared_ptr<nano::block>, nano::block_sideband const &);
|
||||
boost::optional<nano::election_status_type> confirm_block (nano::transaction const &, std::shared_ptr<nano::block>);
|
||||
void post_confirmation_height_set (nano::transaction const & transaction_a, std::shared_ptr<nano::block> block_a, nano::block_sideband const & sideband_a, nano::election_status_type election_status_type_a);
|
||||
boost::multi_index_container<
|
||||
nano::conflict_info,
|
||||
boost::multi_index::indexed_by<
|
||||
|
@ -136,6 +137,8 @@ public:
|
|||
size_t priority_wallet_cementable_frontiers_size ();
|
||||
boost::circular_buffer<double> difficulty_trend ();
|
||||
size_t inactive_votes_cache_size ();
|
||||
std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> pending_conf_height;
|
||||
void clear_block (nano::block_hash const & hash_a);
|
||||
|
||||
private:
|
||||
// Call action with confirmed block, may be different than what we started with
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/confirmation_height_processor.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/write_database_queue.hpp>
|
||||
#include <nano/secure/blockstore.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
|
@ -48,7 +49,7 @@ void nano::confirmation_height_processor::run ()
|
|||
nano::unique_lock<std::mutex> lk (pending_confirmations.mutex);
|
||||
while (!stopped)
|
||||
{
|
||||
if (!pending_confirmations.pending.empty ())
|
||||
if (!paused && !pending_confirmations.pending.empty ())
|
||||
{
|
||||
pending_confirmations.current_hash = *pending_confirmations.pending.begin ();
|
||||
pending_confirmations.pending.erase (pending_confirmations.current_hash);
|
||||
|
@ -83,6 +84,17 @@ void nano::confirmation_height_processor::run ()
|
|||
}
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::pause ()
|
||||
{
|
||||
paused = true;
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::unpause ()
|
||||
{
|
||||
paused = false;
|
||||
condition.notify_one ();
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::add (nano::block_hash const & hash_a)
|
||||
{
|
||||
{
|
||||
|
@ -106,6 +118,7 @@ void nano::confirmation_height_processor::add_confirmation_height (nano::block_h
|
|||
release_assert (receive_source_pairs.empty ());
|
||||
|
||||
auto read_transaction (ledger.store.tx_begin_read ());
|
||||
auto last_iteration = false;
|
||||
// Traverse account chain and all sources for receive blocks iteratively
|
||||
do
|
||||
{
|
||||
|
@ -123,6 +136,7 @@ void nano::confirmation_height_processor::add_confirmation_height (nano::block_h
|
|||
{
|
||||
current = hash_a;
|
||||
receive_details = boost::none;
|
||||
last_iteration = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +159,25 @@ void nano::confirmation_height_processor::add_confirmation_height (nano::block_h
|
|||
}
|
||||
}
|
||||
|
||||
if (!last_iteration && current == hash_a && confirmation_height >= block_height)
|
||||
{
|
||||
auto it = std::find_if (pending_writes.begin (), pending_writes.end (), [&hash_a](auto & conf_height_details) {
|
||||
auto it = std::find_if (conf_height_details.block_callbacks_required.begin (), conf_height_details.block_callbacks_required.end (), [&hash_a](auto & callback_data) {
|
||||
return callback_data.block->hash () == hash_a;
|
||||
});
|
||||
return (it != conf_height_details.block_callbacks_required.end ());
|
||||
});
|
||||
|
||||
if (it == pending_writes.end ())
|
||||
{
|
||||
// This is a block which has been added to the processor but already has its confirmation height set (or about to be set)
|
||||
// Just need to perform active cleanup, no callbacks are needed.
|
||||
active.clear_block (hash_a);
|
||||
}
|
||||
}
|
||||
|
||||
auto count_before_receive = receive_source_pairs.size ();
|
||||
std::vector<callback_data> block_callbacks_required;
|
||||
if (block_height > iterated_height)
|
||||
{
|
||||
if ((block_height - iterated_height) > 20000)
|
||||
|
@ -153,7 +185,7 @@ void nano::confirmation_height_processor::add_confirmation_height (nano::block_h
|
|||
logger.always_log ("Iterating over a large account chain for setting confirmation height. The top block: ", current.to_string ());
|
||||
}
|
||||
|
||||
collect_unconfirmed_receive_and_sources_for_account (block_height, iterated_height, current, account, read_transaction);
|
||||
collect_unconfirmed_receive_and_sources_for_account (block_height, iterated_height, current, account, read_transaction, block_callbacks_required);
|
||||
}
|
||||
|
||||
// Exit early when the processor has been stopped, otherwise this function may take a
|
||||
|
@ -187,7 +219,7 @@ void nano::confirmation_height_processor::add_confirmation_height (nano::block_h
|
|||
confirmed_iterated_pairs.emplace (account, confirmed_iterated_pair{ block_height, block_height });
|
||||
}
|
||||
|
||||
pending_writes.emplace_back (account, current, block_height, block_height - confirmation_height);
|
||||
pending_writes.emplace_back (account, current, block_height, block_height - confirmation_height, block_callbacks_required);
|
||||
}
|
||||
|
||||
if (receive_details)
|
||||
|
@ -298,6 +330,11 @@ bool nano::confirmation_height_processor::write_pending (std::deque<conf_height_
|
|||
return true;
|
||||
}
|
||||
|
||||
for (auto & callback_data : pending.block_callbacks_required)
|
||||
{
|
||||
active.post_confirmation_height_set (transaction, callback_data.block, callback_data.sideband, callback_data.election_status_type);
|
||||
}
|
||||
|
||||
ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in, pending.height - confirmation_height);
|
||||
assert (pending.num_blocks_confirmed == pending.height - confirmation_height);
|
||||
confirmation_height = pending.height;
|
||||
|
@ -319,7 +356,7 @@ bool nano::confirmation_height_processor::write_pending (std::deque<conf_height_
|
|||
return false;
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::collect_unconfirmed_receive_and_sources_for_account (uint64_t block_height_a, uint64_t confirmation_height_a, nano::block_hash const & hash_a, nano::account const & account_a, nano::read_transaction const & transaction_a)
|
||||
void nano::confirmation_height_processor::collect_unconfirmed_receive_and_sources_for_account (uint64_t block_height_a, uint64_t confirmation_height_a, nano::block_hash const & hash_a, nano::account const & account_a, nano::read_transaction const & transaction_a, std::vector<callback_data> & block_callbacks_required)
|
||||
{
|
||||
auto hash (hash_a);
|
||||
auto num_to_confirm = block_height_a - confirmation_height_a;
|
||||
|
@ -335,8 +372,18 @@ void nano::confirmation_height_processor::collect_unconfirmed_receive_and_source
|
|||
{
|
||||
if (!pending_confirmations.is_processing_block (hash))
|
||||
{
|
||||
active.confirm_block (transaction_a, block, sideband);
|
||||
auto election_status_type = active.confirm_block (transaction_a, block);
|
||||
if (election_status_type.is_initialized ())
|
||||
{
|
||||
block_callbacks_required.emplace_back (block, sideband, *election_status_type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This block is the original which is having its confirmation height set on
|
||||
block_callbacks_required.emplace_back (block, sideband, nano::election_status_type::active_confirmed_quorum);
|
||||
}
|
||||
|
||||
auto source (block->source ());
|
||||
if (source.is_zero ())
|
||||
{
|
||||
|
@ -350,9 +397,15 @@ void nano::confirmation_height_processor::collect_unconfirmed_receive_and_source
|
|||
if (next_height != height_not_set)
|
||||
{
|
||||
receive_source_pairs.back ().receive_details.num_blocks_confirmed = next_height - block_height;
|
||||
|
||||
auto & receive_callbacks_required = receive_source_pairs.back ().receive_details.block_callbacks_required;
|
||||
|
||||
// Don't include the last one as that belongs to the next recieve
|
||||
std::copy (block_callbacks_required.begin (), block_callbacks_required.end () - 1, std::back_inserter (receive_callbacks_required));
|
||||
block_callbacks_required = { block_callbacks_required.back () };
|
||||
}
|
||||
|
||||
receive_source_pairs.emplace_back (conf_height_details{ account_a, hash, block_height, height_not_set }, source);
|
||||
receive_source_pairs.emplace_back (conf_height_details{ account_a, hash, block_height, height_not_set, {} }, source);
|
||||
++receive_source_pairs_size;
|
||||
next_height = block_height;
|
||||
}
|
||||
|
@ -374,16 +427,18 @@ void nano::confirmation_height_processor::collect_unconfirmed_receive_and_source
|
|||
{
|
||||
auto & last_receive_details = receive_source_pairs.back ().receive_details;
|
||||
last_receive_details.num_blocks_confirmed = last_receive_details.height - confirmation_height_a;
|
||||
last_receive_details.block_callbacks_required = block_callbacks_required;
|
||||
}
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
confirmation_height_processor::conf_height_details::conf_height_details (nano::account const & account_a, nano::block_hash const & hash_a, uint64_t height_a, uint64_t num_blocks_confirmed_a) :
|
||||
confirmation_height_processor::conf_height_details::conf_height_details (nano::account const & account_a, nano::block_hash const & hash_a, uint64_t height_a, uint64_t num_blocks_confirmed_a, std::vector<callback_data> const & block_callbacks_required_a) :
|
||||
account (account_a),
|
||||
hash (hash_a),
|
||||
height (height_a),
|
||||
num_blocks_confirmed (num_blocks_confirmed_a)
|
||||
num_blocks_confirmed (num_blocks_confirmed_a),
|
||||
block_callbacks_required (block_callbacks_required_a)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -398,6 +453,13 @@ confirmed_height (confirmed_height_a), iterated_height (iterated_height_a)
|
|||
{
|
||||
}
|
||||
|
||||
confirmation_height_processor::callback_data::callback_data (std::shared_ptr<nano::block> const & block_a, nano::block_sideband const & sideband_a, nano::election_status_type election_status_type_a) :
|
||||
block (block_a),
|
||||
sideband (sideband_a),
|
||||
election_status_type (election_status_type_a)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<seq_con_info_component> collect_seq_con_info (confirmation_height_processor & confirmation_height_processor_a, const std::string & name_a)
|
||||
{
|
||||
size_t receive_source_pairs_count = confirmation_height_processor_a.receive_source_pairs_size;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/secure/blockstore.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
|
||||
#include <condition_variable>
|
||||
|
@ -30,6 +32,8 @@ private:
|
|||
nano::block_hash current_hash{ 0 };
|
||||
friend class confirmation_height_processor;
|
||||
friend class confirmation_height_pending_observer_callbacks_Test;
|
||||
friend class confirmation_height_dependent_election_Test;
|
||||
friend class confirmation_height_dependent_election_after_already_cemented_Test;
|
||||
};
|
||||
|
||||
std::unique_ptr<seq_con_info_component> collect_seq_con_info (pending_confirmation_height &, const std::string &);
|
||||
|
@ -41,6 +45,8 @@ public:
|
|||
~confirmation_height_processor ();
|
||||
void add (nano::block_hash const &);
|
||||
void stop ();
|
||||
void pause ();
|
||||
void unpause ();
|
||||
|
||||
/** The maximum amount of accounts to iterate over while writing */
|
||||
static uint64_t constexpr batch_write_size = 2048;
|
||||
|
@ -49,15 +55,25 @@ public:
|
|||
static uint64_t constexpr batch_read_size = 4096;
|
||||
|
||||
private:
|
||||
class callback_data final
|
||||
{
|
||||
public:
|
||||
callback_data (std::shared_ptr<nano::block> const &, nano::block_sideband const &, nano::election_status_type);
|
||||
std::shared_ptr<nano::block> block;
|
||||
nano::block_sideband sideband;
|
||||
nano::election_status_type election_status_type;
|
||||
};
|
||||
|
||||
class conf_height_details final
|
||||
{
|
||||
public:
|
||||
conf_height_details (nano::account const &, nano::block_hash const &, uint64_t, uint64_t);
|
||||
conf_height_details (nano::account const &, nano::block_hash const &, uint64_t, uint64_t, std::vector<callback_data> const &);
|
||||
|
||||
nano::account account;
|
||||
nano::block_hash hash;
|
||||
uint64_t height;
|
||||
uint64_t num_blocks_confirmed;
|
||||
std::vector<callback_data> block_callbacks_required;
|
||||
};
|
||||
|
||||
class receive_source_pair final
|
||||
|
@ -80,6 +96,7 @@ private:
|
|||
nano::condition_variable condition;
|
||||
nano::pending_confirmation_height & pending_confirmations;
|
||||
std::atomic<bool> stopped{ false };
|
||||
std::atomic<bool> paused{ false };
|
||||
nano::ledger & ledger;
|
||||
nano::active_transactions & active;
|
||||
nano::logger_mt & logger;
|
||||
|
@ -97,7 +114,7 @@ private:
|
|||
|
||||
void run ();
|
||||
void add_confirmation_height (nano::block_hash const &);
|
||||
void collect_unconfirmed_receive_and_sources_for_account (uint64_t, uint64_t, nano::block_hash const &, nano::account const &, nano::read_transaction const &);
|
||||
void collect_unconfirmed_receive_and_sources_for_account (uint64_t, uint64_t, nano::block_hash const &, nano::account const &, nano::read_transaction const &, std::vector<callback_data> &);
|
||||
bool write_pending (std::deque<conf_height_details> &);
|
||||
|
||||
friend std::unique_ptr<seq_con_info_component> collect_seq_con_info (confirmation_height_processor &, const std::string &);
|
||||
|
|
|
@ -51,7 +51,7 @@ void nano::election::confirm_once (nano::election_status_type type_a)
|
|||
--node.active.long_unconfirmed_size;
|
||||
}
|
||||
auto root (status.winner->qualified_root ());
|
||||
node.active.add_confirmed (status, root);
|
||||
node.active.pending_conf_height.emplace (status.winner->hash (), shared_from_this ());
|
||||
clear_blocks ();
|
||||
clear_dependent ();
|
||||
node.active.roots.erase (root);
|
||||
|
|
|
@ -687,6 +687,7 @@ void nano::node::stop ()
|
|||
if (!stopped.exchange (true))
|
||||
{
|
||||
logger.always_log ("Node stopping");
|
||||
write_database_queue.stop ();
|
||||
block_processor.stop ();
|
||||
if (block_processor_thread.joinable ())
|
||||
{
|
||||
|
@ -706,7 +707,6 @@ void nano::node::stop ()
|
|||
checker.stop ();
|
||||
wallets.stop ();
|
||||
stats.stop ();
|
||||
write_database_queue.stop ();
|
||||
worker.stop ();
|
||||
// work pool is not stopped on purpose due to testing setup
|
||||
}
|
||||
|
@ -1181,41 +1181,27 @@ void nano::node::process_confirmed_data (nano::transaction const & transaction_a
|
|||
|
||||
void nano::node::process_confirmed (nano::election_status const & status_a, uint8_t iteration)
|
||||
{
|
||||
auto block_a (status_a.winner);
|
||||
auto hash (block_a->hash ());
|
||||
nano::block_sideband sideband;
|
||||
auto transaction (store.tx_begin_read ());
|
||||
if (store.block_get (transaction, hash, &sideband) != nullptr)
|
||||
if (status_a.type == nano::election_status_type::active_confirmed_quorum)
|
||||
{
|
||||
confirmation_height_processor.add (hash);
|
||||
|
||||
receive_confirmed (transaction, block_a, hash);
|
||||
nano::account account (0);
|
||||
nano::uint128_t amount (0);
|
||||
bool is_state_send (false);
|
||||
nano::account pending_account (0);
|
||||
process_confirmed_data (transaction, block_a, hash, sideband, account, amount, is_state_send, pending_account);
|
||||
observers.blocks.notify (status_a, account, amount, is_state_send);
|
||||
if (amount > 0)
|
||||
auto block_a (status_a.winner);
|
||||
auto hash (block_a->hash ());
|
||||
auto transaction (store.tx_begin_read ());
|
||||
if (store.block_get (transaction, hash) != nullptr)
|
||||
{
|
||||
observers.account_balance.notify (account, false);
|
||||
if (!pending_account.is_zero ())
|
||||
{
|
||||
observers.account_balance.notify (pending_account, true);
|
||||
}
|
||||
confirmation_height_processor.add (hash);
|
||||
}
|
||||
// Limit to 0.5 * 20 = 10 seconds (more than max block_processor::process_batch finish time)
|
||||
else if (iteration < 20)
|
||||
{
|
||||
iteration++;
|
||||
std::weak_ptr<nano::node> node_w (shared ());
|
||||
alarm.add (std::chrono::steady_clock::now () + network_params.node.process_confirmed_interval, [node_w, status_a, iteration]() {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->process_confirmed (status_a, iteration);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Limit to 0.5 * 20 = 10 seconds (more than max block_processor::process_batch finish time)
|
||||
else if (iteration < 20)
|
||||
{
|
||||
iteration++;
|
||||
std::weak_ptr<nano::node> node_w (shared ());
|
||||
alarm.add (std::chrono::steady_clock::now () + network_params.node.process_confirmed_interval, [node_w, status_a, iteration]() {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->process_confirmed (status_a, iteration);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1379,12 +1365,10 @@ std::unique_ptr<nano::block_store> nano::make_store (nano::logger_mt & logger, b
|
|||
#if NANO_ROCKSDB
|
||||
/** To use RocksDB in tests make sure the node is built with the cmake variable -DNANO_ROCKSDB=ON and the environment variable TEST_USE_ROCKSDB=1 is set */
|
||||
static nano::network_constants network_constants;
|
||||
if (auto use_rocksdb_str = std::getenv ("TEST_USE_ROCKSDB") && network_constants.is_test_network ())
|
||||
auto use_rocksdb_str = std::getenv ("TEST_USE_ROCKSDB");
|
||||
if (use_rocksdb_str && (boost::lexical_cast<int> (use_rocksdb_str) == 1) && network_constants.is_test_network ())
|
||||
{
|
||||
if (boost::lexical_cast<int> (use_rocksdb_str) == 1)
|
||||
{
|
||||
return make_rocksdb ();
|
||||
}
|
||||
return make_rocksdb ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ namespace nano
|
|||
enum class writer
|
||||
{
|
||||
confirmation_height,
|
||||
process_batch
|
||||
process_batch,
|
||||
testing // Used in tests to emulate a write lock
|
||||
};
|
||||
|
||||
class write_guard final
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue