diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 97796ce17e..00f3f9a1e7 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -6,6 +6,12 @@ #include +namespace +{ +void modify_account_info_to_v13 (nano::mdb_store & store, nano::transaction const & transaction_a, nano::account const & account_a); +void modify_genesis_account_info_to_v5 (nano::mdb_store & store, nano::transaction const & transaction_a); +} + TEST (block_store, construction) { nano::logging logging; @@ -474,7 +480,7 @@ TEST (block_store, frontier_retrieval) nano::mdb_store store (init, logging, nano::unique_path ()); ASSERT_TRUE (!init); nano::account account1 (0); - nano::account_info info1 (0, 0, 0, 0, 0, 0, nano::epoch::epoch_0); + nano::account_info info1 (0, 0, 0, 0, 0, 0, 0, nano::epoch::epoch_0); auto transaction (store.tx_begin (true)); store.account_put (transaction, account1, info1); nano::account_info info2; @@ -491,7 +497,7 @@ TEST (block_store, one_account) nano::account account (0); nano::block_hash hash (0); auto transaction (store.tx_begin (true)); - store.account_put (transaction, account, { hash, account, hash, 42, 100, 200, nano::epoch::epoch_0 }); + store.account_put (transaction, account, { hash, account, hash, 42, 100, 200, 20, nano::epoch::epoch_0 }); auto begin (store.latest_begin (transaction)); auto end (store.latest_end ()); ASSERT_NE (end, begin); @@ -501,6 +507,7 @@ TEST (block_store, one_account) ASSERT_EQ (42, info.balance.number ()); ASSERT_EQ (100, info.modified); ASSERT_EQ (200, info.block_count); + ASSERT_EQ (20, info.confirmation_height); ++begin; ASSERT_EQ (end, begin); } @@ -535,14 +542,13 @@ TEST (block_store, two_account) bool init (false); nano::mdb_store store (init, logging, nano::unique_path ()); ASSERT_TRUE (!init); - store.stop (); nano::account account1 (1); nano::block_hash hash1 (2); nano::account account2 (3); nano::block_hash hash2 (4); auto transaction (store.tx_begin (true)); - store.account_put (transaction, account1, { hash1, account1, hash1, 42, 100, 300, nano::epoch::epoch_0 }); - store.account_put (transaction, account2, { hash2, account2, hash2, 84, 200, 400, nano::epoch::epoch_0 }); + store.account_put (transaction, account1, { hash1, account1, hash1, 42, 100, 300, 20, nano::epoch::epoch_0 }); + store.account_put (transaction, account2, { hash2, account2, hash2, 84, 200, 400, 30, nano::epoch::epoch_0 }); auto begin (store.latest_begin (transaction)); auto end (store.latest_end ()); ASSERT_NE (end, begin); @@ -552,6 +558,7 @@ TEST (block_store, two_account) ASSERT_EQ (42, info1.balance.number ()); ASSERT_EQ (100, info1.modified); ASSERT_EQ (300, info1.block_count); + ASSERT_EQ (20, info1.confirmation_height); ++begin; ASSERT_NE (end, begin); ASSERT_EQ (account2, nano::account (begin->first)); @@ -560,6 +567,7 @@ TEST (block_store, two_account) ASSERT_EQ (84, info2.balance.number ()); ASSERT_EQ (200, info2.modified); ASSERT_EQ (400, info2.block_count); + ASSERT_EQ (30, info2.confirmation_height); ++begin; ASSERT_EQ (end, begin); } @@ -570,14 +578,13 @@ TEST (block_store, latest_find) bool init (false); nano::mdb_store store (init, logging, nano::unique_path ()); ASSERT_TRUE (!init); - store.stop (); nano::account account1 (1); nano::block_hash hash1 (2); nano::account account2 (3); nano::block_hash hash2 (4); auto transaction (store.tx_begin (true)); - store.account_put (transaction, account1, { hash1, account1, hash1, 100, 0, 300, nano::epoch::epoch_0 }); - store.account_put (transaction, account2, { hash2, account2, hash2, 200, 0, 400, nano::epoch::epoch_0 }); + store.account_put (transaction, account1, { hash1, account1, hash1, 100, 0, 300, 0, nano::epoch::epoch_0 }); + store.account_put (transaction, account2, { hash2, account2, hash2, 200, 0, 400, 0, nano::epoch::epoch_0 }); auto first (store.latest_begin (transaction)); auto second (store.latest_begin (transaction)); ++second; @@ -786,7 +793,6 @@ TEST (block_store, upgrade_v2_v3) bool init (false); nano::mdb_store store (init, logging, path); ASSERT_TRUE (!init); - store.stop (); auto transaction (store.tx_begin (true)); nano::genesis genesis; auto hash (genesis.hash ()); @@ -837,7 +843,6 @@ TEST (block_store, upgrade_v3_v4) bool init (false); nano::mdb_store store (init, logging, path); ASSERT_FALSE (init); - store.stop (); auto transaction (store.tx_begin (true)); store.version_put (transaction, 3); nano::pending_info_v3 info (key1.pub, 100, key2.pub); @@ -871,7 +876,6 @@ TEST (block_store, upgrade_v4_v5) bool init (false); nano::mdb_store store (init, logging, path); ASSERT_FALSE (init); - store.stop (); auto transaction (store.tx_begin (true)); nano::genesis genesis; nano::stat stats; @@ -879,7 +883,7 @@ TEST (block_store, upgrade_v4_v5) store.initialize (transaction, genesis); store.version_put (transaction, 4); nano::account_info info; - store.account_get (transaction, nano::test_genesis_key.pub, info); + ASSERT_FALSE (store.account_get (transaction, nano::test_genesis_key.pub, info)); nano::keypair key0; nano::send_block block0 (info.head, key0.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, block0).code); @@ -888,11 +892,7 @@ TEST (block_store, upgrade_v4_v5) genesis_hash = info.head; store.block_successor_clear (transaction, info.head); ASSERT_TRUE (store.block_successor (transaction, genesis_hash).is_zero ()); - nano::account_info info2; - store.account_get (transaction, nano::test_genesis_key.pub, info2); - nano::account_info_v5 info_old (info2.head, info2.rep_block, info2.open_block, info2.balance, info2.modified); - auto status (mdb_put (store.env.tx (transaction), store.accounts_v0, nano::mdb_val (nano::test_genesis_key.pub), info_old.val (), 0)); - assert (status == 0); + modify_genesis_account_info_to_v5 (store, transaction); } nano::logging logging; bool init (false); @@ -924,16 +924,11 @@ TEST (block_store, upgrade_v5_v6) bool init (false); nano::mdb_store store (init, logging, path); ASSERT_FALSE (init); - store.stop (); auto transaction (store.tx_begin (true)); nano::genesis genesis; store.initialize (transaction, genesis); store.version_put (transaction, 5); - nano::account_info info; - store.account_get (transaction, nano::test_genesis_key.pub, info); - nano::account_info_v5 info_old (info.head, info.rep_block, info.open_block, info.balance, info.modified); - auto status (mdb_put (store.env.tx (transaction), store.accounts_v0, nano::mdb_val (nano::test_genesis_key.pub), info_old.val (), 0)); - assert (status == 0); + modify_genesis_account_info_to_v5 (store, transaction); } nano::logging logging; bool init (false); @@ -953,11 +948,11 @@ TEST (block_store, upgrade_v6_v7) bool init (false); nano::mdb_store store (init, logging, path); ASSERT_FALSE (init); - store.stop (); auto transaction (store.tx_begin (true)); nano::genesis genesis; store.initialize (transaction, genesis); store.version_put (transaction, 6); + modify_account_info_to_v13 (store, transaction, nano::genesis_account); auto send1 (std::make_shared (0, 0, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); store.unchecked_put (transaction, send1->hash (), send1); store.flush (transaction); @@ -1024,7 +1019,6 @@ TEST (block_store, upgrade_v7_v8) nano::logging logging; bool init (false); nano::mdb_store store (init, logging, path); - store.stop (); auto transaction (store.tx_begin (true)); ASSERT_EQ (0, mdb_drop (store.env.tx (transaction), store.unchecked, 1)); ASSERT_EQ (0, mdb_dbi_open (store.env.tx (transaction), "unchecked", MDB_CREATE, &store.unchecked)); @@ -1097,7 +1091,6 @@ TEST (block_store, upgrade_v8_v9) nano::logging logging; bool init (false); nano::mdb_store store (init, logging, path); - store.stop (); auto transaction (store.tx_begin (true)); ASSERT_EQ (0, mdb_drop (store.env.tx (transaction), store.vote, 1)); ASSERT_EQ (0, mdb_dbi_open (store.env.tx (transaction), "sequence", MDB_CREATE, &store.vote)); @@ -1174,10 +1167,10 @@ TEST (block_store, upgrade_sideband_genesis) nano::logging logging; nano::mdb_store store (error, logging, path); ASSERT_FALSE (error); - store.stop (); auto transaction (store.tx_begin (true)); store.version_put (transaction, 11); store.initialize (transaction, genesis); + modify_account_info_to_v13 (store, transaction, nano::genesis_account); nano::block_sideband sideband; auto genesis_block (store.block_get (transaction, genesis.hash (), &sideband)); ASSERT_NE (nullptr, genesis_block); @@ -1190,17 +1183,8 @@ TEST (block_store, upgrade_sideband_genesis) nano::logging logging; nano::mdb_store store (error, logging, path); ASSERT_FALSE (error); - auto done (false); - auto iterations (0); - while (!done) - { - std::this_thread::sleep_for (std::chrono::milliseconds (10)); - auto transaction (store.tx_begin (false)); - done = store.full_sideband (transaction); - ASSERT_LT (iterations, 200); - ++iterations; - } auto transaction (store.tx_begin_read ()); + ASSERT_TRUE (store.full_sideband (transaction)); nano::block_sideband sideband; auto genesis_block (store.block_get (transaction, genesis.hash (), &sideband)); ASSERT_NE (nullptr, genesis_block); @@ -1217,7 +1201,6 @@ TEST (block_store, upgrade_sideband_two_blocks) nano::logging logging; nano::mdb_store store (error, logging, path); ASSERT_FALSE (error); - store.stop (); nano::stat stat; nano::ledger ledger (store, stat); auto transaction (store.tx_begin (true)); @@ -1228,21 +1211,13 @@ TEST (block_store, upgrade_sideband_two_blocks) ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, block).code); write_legacy_sideband (store, transaction, *genesis.open, hash2, store.open_blocks); write_legacy_sideband (store, transaction, block, 0, store.state_blocks_v0); + modify_account_info_to_v13 (store, transaction, nano::genesis_account); } nano::logging logging; nano::mdb_store store (error, logging, path); ASSERT_FALSE (error); - auto done (false); - auto iterations (0); - while (!done) - { - std::this_thread::sleep_for (std::chrono::milliseconds (10)); - auto transaction (store.tx_begin (false)); - done = store.full_sideband (transaction); - ASSERT_LT (iterations, 200); - ++iterations; - } auto transaction (store.tx_begin_read ()); + ASSERT_TRUE (store.full_sideband (transaction)); nano::block_sideband sideband; auto genesis_block (store.block_get (transaction, genesis.hash (), &sideband)); ASSERT_NE (nullptr, genesis_block); @@ -1265,7 +1240,6 @@ TEST (block_store, upgrade_sideband_two_accounts) nano::logging logging; nano::mdb_store store (error, logging, path); ASSERT_FALSE (error); - store.stop (); nano::stat stat; nano::ledger ledger (store, stat); auto transaction (store.tx_begin (true)); @@ -1280,21 +1254,14 @@ TEST (block_store, upgrade_sideband_two_accounts) write_legacy_sideband (store, transaction, *genesis.open, hash2, store.open_blocks); write_legacy_sideband (store, transaction, block1, 0, store.state_blocks_v0); write_legacy_sideband (store, transaction, block2, 0, store.state_blocks_v0); + modify_account_info_to_v13 (store, transaction, nano::genesis_account); + modify_account_info_to_v13 (store, transaction, block2.account ()); } nano::logging logging; nano::mdb_store store (error, logging, path); ASSERT_FALSE (error); - auto done (false); - auto iterations (0); - while (!done) - { - std::this_thread::sleep_for (std::chrono::milliseconds (10)); - auto transaction (store.tx_begin (false)); - done = store.full_sideband (transaction); - ASSERT_LT (iterations, 200); - ++iterations; - } auto transaction (store.tx_begin_read ()); + ASSERT_TRUE (store.full_sideband (transaction)); nano::block_sideband sideband; auto genesis_block (store.block_get (transaction, genesis.hash (), &sideband)); ASSERT_NE (nullptr, genesis_block); @@ -1316,7 +1283,6 @@ TEST (block_store, insert_after_legacy) nano::genesis genesis; nano::mdb_store store (error, logging, nano::unique_path ()); ASSERT_FALSE (error); - store.stop (); nano::stat stat; nano::ledger ledger (store, stat); auto transaction (store.tx_begin (true)); @@ -1327,31 +1293,6 @@ TEST (block_store, insert_after_legacy) ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, block).code); } -TEST (block_store, upgrade_sideband_rollback_old) -{ - nano::logging logging; - bool error (false); - nano::genesis genesis; - nano::mdb_store store (error, logging, nano::unique_path ()); - ASSERT_FALSE (error); - store.stop (); - nano::stat stat; - nano::ledger ledger (store, stat); - auto transaction (store.tx_begin (true)); - store.version_put (transaction, 11); - store.initialize (transaction, genesis); - nano::send_block block1 (genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); - ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, block1).code); - nano::send_block block2 (block1.hash (), nano::test_genesis_key.pub, nano::genesis_amount - 2 * nano::Gxrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); - ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, block2).code); - write_legacy_sideband (store, transaction, *genesis.open, block1.hash (), store.open_blocks); - write_legacy_sideband (store, transaction, block1, block2.hash (), store.send_blocks); - write_legacy_sideband (store, transaction, block2, 0, store.send_blocks); - ASSERT_TRUE (store.block_exists (transaction, block2.hash ())); - ledger.rollback (transaction, block2.hash ()); - ASSERT_FALSE (store.block_exists (transaction, block2.hash ())); -} - // Account for an open block should be retrievable TEST (block_store, legacy_account_computed) { @@ -1359,7 +1300,6 @@ TEST (block_store, legacy_account_computed) bool init (false); nano::mdb_store store (init, logging, nano::unique_path ()); ASSERT_TRUE (!init); - store.stop (); nano::stat stats; nano::ledger ledger (store, stats); nano::genesis genesis; @@ -1380,7 +1320,6 @@ TEST (block_store, upgrade_sideband_epoch) nano::logging logging; nano::mdb_store store (error, logging, path); ASSERT_FALSE (error); - store.stop (); nano::stat stat; nano::ledger ledger (store, stat, 42, nano::test_genesis_key.pub); auto transaction (store.tx_begin (true)); @@ -1392,23 +1331,15 @@ TEST (block_store, upgrade_sideband_epoch) ASSERT_EQ (nano::epoch::epoch_1, store.block_version (transaction, hash2)); write_legacy_sideband (store, transaction, *genesis.open, hash2, store.open_blocks); write_legacy_sideband (store, transaction, block1, 0, store.state_blocks_v1); + modify_account_info_to_v13 (store, transaction, nano::genesis_account); } nano::logging logging; nano::mdb_store store (error, logging, path); nano::stat stat; nano::ledger ledger (store, stat, 42, nano::test_genesis_key.pub); ASSERT_FALSE (error); - auto done (false); - auto iterations (0); - while (!done) - { - std::this_thread::sleep_for (std::chrono::milliseconds (10)); - auto transaction (store.tx_begin (false)); - done = store.full_sideband (transaction); - ASSERT_LT (iterations, 200); - ++iterations; - } auto transaction (store.tx_begin_write ()); + ASSERT_TRUE (store.full_sideband (transaction)); ASSERT_EQ (nano::epoch::epoch_1, store.block_version (transaction, hash2)); nano::block_sideband sideband; auto block1 (store.block_get (transaction, hash2, &sideband)); @@ -1429,7 +1360,6 @@ TEST (block_store, sideband_height) nano::keypair key3; nano::mdb_store store (error, logging, nano::unique_path ()); ASSERT_FALSE (error); - store.stop (); nano::stat stat; nano::ledger ledger (store, stat); ledger.epoch_signer = epoch_key.pub; @@ -1587,3 +1517,149 @@ TEST (block_store, online_weight) ASSERT_EQ (0, store.online_weight_count (transaction)); ASSERT_EQ (store.online_weight_end (), store.online_weight_begin (transaction)); } + +// Adding confirmation height to accounts +TEST (block_store, upgrade_v13_v14) +{ + auto path (nano::unique_path ()); + { + nano::logging logging; + nano::genesis genesis; + auto error (false); + nano::mdb_store store (error, logging, path); + auto transaction (store.tx_begin (true)); + store.initialize (transaction, genesis); + nano::account_info account_info; + ASSERT_FALSE (store.account_get (transaction, nano::genesis_account, account_info)); + ASSERT_EQ (account_info.confirmation_height, 0); + store.version_put (transaction, 13); + modify_account_info_to_v13 (store, transaction, nano::genesis_account); + + // This should fail as sizes are no longer correct for account_info + nano::account_info account_info1; + ASSERT_TRUE (store.account_get (transaction, nano::genesis_account, account_info1)); + } + + // Now do the upgrade and confirm that confirmation height is 0 and version is updated as expected + nano::logging logging; + auto error (false); + nano::mdb_store store (error, logging, path); + ASSERT_FALSE (error); + auto transaction (store.tx_begin (true)); + + // This should now work and have confirmation height of 0 + nano::account_info account_info; + ASSERT_FALSE (store.account_get (transaction, nano::genesis_account, account_info)); + ASSERT_EQ (account_info.confirmation_height, 0); + ASSERT_LT (13, store.version_get (transaction)); +} + +// Test various confirmation height values as well as clearing them +TEST (block_store, confirmation_height) +{ + auto path (nano::unique_path ()); + nano::logging logging; + auto error (false); + nano::mdb_store store (error, logging, path); + auto transaction (store.tx_begin (true)); + + nano::account account1 (0); + nano::account_info info1 (0, 0, 0, 0, 0, 0, 500, nano::epoch::epoch_0); + store.account_put (transaction, account1, info1); + + nano::account account2 (1); + nano::account_info info2 (0, 0, 0, 0, 0, 0, std::numeric_limits::max (), nano::epoch::epoch_0); + store.account_put (transaction, account2, info2); + + nano::account account3 (2); + nano::account_info info3 (0, 0, 0, 0, 0, 0, 10, nano::epoch::epoch_0); + store.account_put (transaction, account3, info3); + + nano::account_info stored_account_info; + ASSERT_FALSE (store.account_get (transaction, account1, stored_account_info)); + ASSERT_EQ (stored_account_info.confirmation_height, 500); + + ASSERT_FALSE (store.account_get (transaction, account2, stored_account_info)); + ASSERT_EQ (stored_account_info.confirmation_height, std::numeric_limits::max ()); + + ASSERT_FALSE (store.account_get (transaction, account3, stored_account_info)); + ASSERT_EQ (stored_account_info.confirmation_height, 10); + + // Check cleaning of confirmation heights + store.confirmation_height_clear (transaction); + ASSERT_EQ (store.account_count (transaction), 3); + + ASSERT_FALSE (store.account_get (transaction, account1, stored_account_info)); + ASSERT_EQ (stored_account_info.confirmation_height, 0); + + ASSERT_FALSE (store.account_get (transaction, account2, stored_account_info)); + ASSERT_EQ (stored_account_info.confirmation_height, 0); + + ASSERT_FALSE (store.account_get (transaction, account3, stored_account_info)); + ASSERT_EQ (stored_account_info.confirmation_height, 0); +} + +// Upgrade many accounts to add a confirmation height of 0 +TEST (block_store, upgrade_confirmation_height_many) +{ + auto error (false); + nano::genesis genesis; + auto total_num_accounts = 1000; // Includes the genesis account + + auto path (nano::unique_path ()); + { + nano::logging logging; + nano::mdb_store store (error, logging, path); + ASSERT_FALSE (error); + auto transaction (store.tx_begin (true)); + store.version_put (transaction, 13); + store.initialize (transaction, genesis); + modify_account_info_to_v13 (store, transaction, nano::genesis_account); + + // Add many accounts + for (auto i = 0; i < total_num_accounts - 1; ++i) + { + nano::account account (i); + nano::open_block open (1, 2, 3, nullptr); + nano::account_info_v13 account_info_v13 (open.hash (), open.hash (), open.hash (), 3, 4, 1, nano::epoch::epoch_1); + auto status (mdb_put (store.env.tx (transaction), store.accounts_v1, nano::mdb_val (account), nano::mdb_val (account_info_v13), 0)); + ASSERT_EQ (status, 0); + } + + ASSERT_EQ (store.account_count (transaction), total_num_accounts); + } + + // Loop over them all and confirm all have a confirmation height of 0 + nano::logging logging; + nano::mdb_store store (error, logging, path); + auto transaction (store.tx_begin (false)); + ASSERT_EQ (store.account_count (transaction), total_num_accounts); + + for (auto i (store.latest_begin (transaction)), n (store.latest_end ()); i != n; ++i) + { + nano::account_info current (i->second); + ASSERT_EQ (current.confirmation_height, 0); + } +} + +namespace +{ +// These functions take the latest account_info and create a legacy one so that upgrade tests can be emulated more easily. +void modify_account_info_to_v13 (nano::mdb_store & store, nano::transaction const & transaction_a, nano::account const & account) +{ + nano::account_info info; + ASSERT_FALSE (store.account_get (transaction_a, account, info)); + nano::account_info_v13 account_info_v13 (info.head, info.rep_block, info.open_block, info.balance, info.modified, info.block_count, info.epoch); + auto status (mdb_put (store.env.tx (transaction_a), store.get_account_db (info.epoch), nano::mdb_val (account), nano::mdb_val (account_info_v13), 0)); + assert (status == 0); +} + +void modify_genesis_account_info_to_v5 (nano::mdb_store & store, nano::transaction const & transaction_a) +{ + nano::account_info info; + store.account_get (transaction_a, nano::test_genesis_key.pub, info); + nano::account_info_v5 info_old (info.head, info.rep_block, info.open_block, info.balance, info.modified); + auto status (mdb_put (store.env.tx (transaction_a), store.accounts_v0, nano::mdb_val (nano::test_genesis_key.pub), info_old.val (), 0)); + assert (status == 0); +} +} diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 9280142d1b..8c905dd047 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -132,7 +132,7 @@ TEST (ledger, process_send) auto latest5 (dynamic_cast (latest4.get ())); ASSERT_NE (nullptr, latest5); ASSERT_EQ (open, *latest5); - ledger.rollback (transaction, hash2); + ASSERT_FALSE (ledger.rollback (transaction, hash2)); ASSERT_TRUE (store.frontier_get (transaction, hash2).is_zero ()); nano::account_info info5; ASSERT_TRUE (ledger.store.account_get (transaction, key2.pub, info5)); @@ -148,7 +148,7 @@ TEST (ledger, process_send) nano::account_info info6; ASSERT_FALSE (ledger.store.account_get (transaction, nano::test_genesis_key.pub, info6)); ASSERT_EQ (hash1, info6.head); - ledger.rollback (transaction, info6.head); + ASSERT_FALSE (ledger.rollback (transaction, info6.head)); ASSERT_EQ (nano::genesis_amount, ledger.weight (transaction, nano::test_genesis_key.pub)); ASSERT_EQ (nano::test_genesis_key.pub, store.frontier_get (transaction, info1.head)); ASSERT_TRUE (store.frontier_get (transaction, hash1).is_zero ()); @@ -205,7 +205,7 @@ TEST (ledger, process_receive) ASSERT_EQ (0, ledger.account_pending (transaction, key2.pub)); ASSERT_EQ (nano::genesis_amount - 25, ledger.account_balance (transaction, key2.pub)); ASSERT_EQ (nano::genesis_amount - 25, ledger.weight (transaction, key3.pub)); - ledger.rollback (transaction, hash4); + ASSERT_FALSE (ledger.rollback (transaction, hash4)); ASSERT_TRUE (store.block_successor (transaction, hash2).is_zero ()); ASSERT_EQ (key2.pub, store.frontier_get (transaction, hash2)); ASSERT_TRUE (store.frontier_get (transaction, hash4).is_zero ()); @@ -247,7 +247,7 @@ TEST (ledger, rollback_receiver) ASSERT_EQ (50, ledger.weight (transaction, nano::test_genesis_key.pub)); ASSERT_EQ (0, ledger.weight (transaction, key2.pub)); ASSERT_EQ (nano::genesis_amount - 50, ledger.weight (transaction, key3.pub)); - ledger.rollback (transaction, hash1); + ASSERT_FALSE (ledger.rollback (transaction, hash1)); ASSERT_EQ (nano::genesis_amount, ledger.account_balance (transaction, nano::test_genesis_key.pub)); ASSERT_EQ (0, ledger.account_balance (transaction, key2.pub)); ASSERT_EQ (nano::genesis_amount, ledger.weight (transaction, nano::test_genesis_key.pub)); @@ -291,13 +291,13 @@ TEST (ledger, rollback_representation) nano::account_info info1; ASSERT_FALSE (store.account_get (transaction, key2.pub, info1)); ASSERT_EQ (open.hash (), info1.rep_block); - ledger.rollback (transaction, receive1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, receive1.hash ())); nano::account_info info2; ASSERT_FALSE (store.account_get (transaction, key2.pub, info2)); ASSERT_EQ (open.hash (), info2.rep_block); ASSERT_EQ (0, ledger.weight (transaction, key2.pub)); ASSERT_EQ (nano::genesis_amount - 50, ledger.weight (transaction, key4.pub)); - ledger.rollback (transaction, open.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, open.hash ())); ASSERT_EQ (1, ledger.weight (transaction, key3.pub)); ASSERT_EQ (0, ledger.weight (transaction, key4.pub)); ledger.rollback (transaction, send1.hash ()); @@ -305,7 +305,7 @@ TEST (ledger, rollback_representation) nano::account_info info3; ASSERT_FALSE (store.account_get (transaction, nano::test_genesis_key.pub, info3)); ASSERT_EQ (change2.hash (), info3.rep_block); - ledger.rollback (transaction, change2.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, change2.hash ())); nano::account_info info4; ASSERT_FALSE (store.account_get (transaction, nano::test_genesis_key.pub, info4)); ASSERT_EQ (change1.hash (), info4.rep_block); @@ -328,7 +328,7 @@ TEST (ledger, receive_rollback) ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send).code); nano::receive_block receive (send.hash (), send.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive).code); - ledger.rollback (transaction, receive.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, receive.hash ())); } TEST (ledger, process_duplicate) @@ -413,7 +413,7 @@ TEST (ledger, representative_change) nano::account_info info2; ASSERT_FALSE (store.account_get (transaction, nano::test_genesis_key.pub, info2)); ASSERT_EQ (block.hash (), info2.head); - ledger.rollback (transaction, info2.head); + ASSERT_FALSE (ledger.rollback (transaction, info2.head)); ASSERT_EQ (nano::test_genesis_key.pub, store.frontier_get (transaction, info1.head)); ASSERT_TRUE (store.frontier_get (transaction, block.hash ()).is_zero ()); nano::account_info info3; @@ -685,7 +685,7 @@ TEST (ledger, double_open) ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, open2).code); } -TEST (ledegr, double_receive) +TEST (ledger, double_receive) { nano::logging logging; bool init (false); @@ -1509,23 +1509,23 @@ TEST (ledger, send_open_receive_rollback) ASSERT_EQ (100, ledger.weight (transaction, key2.pub)); ASSERT_EQ (0, ledger.weight (transaction, nano::test_genesis_key.pub)); ASSERT_EQ (nano::genesis_amount - 100, ledger.weight (transaction, key3.pub)); - ledger.rollback (transaction, receive.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, receive.hash ())); ASSERT_EQ (50, ledger.weight (transaction, key2.pub)); ASSERT_EQ (0, ledger.weight (transaction, nano::test_genesis_key.pub)); ASSERT_EQ (nano::genesis_amount - 100, ledger.weight (transaction, key3.pub)); - ledger.rollback (transaction, open.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, open.hash ())); ASSERT_EQ (0, ledger.weight (transaction, key2.pub)); ASSERT_EQ (0, ledger.weight (transaction, nano::test_genesis_key.pub)); ASSERT_EQ (nano::genesis_amount - 100, ledger.weight (transaction, key3.pub)); - ledger.rollback (transaction, change1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, change1.hash ())); ASSERT_EQ (0, ledger.weight (transaction, key2.pub)); ASSERT_EQ (0, ledger.weight (transaction, key3.pub)); ASSERT_EQ (nano::genesis_amount - 100, ledger.weight (transaction, nano::test_genesis_key.pub)); - ledger.rollback (transaction, send2.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, send2.hash ())); ASSERT_EQ (0, ledger.weight (transaction, key2.pub)); ASSERT_EQ (0, ledger.weight (transaction, key3.pub)); ASSERT_EQ (nano::genesis_amount - 50, ledger.weight (transaction, nano::test_genesis_key.pub)); - ledger.rollback (transaction, send1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, send1.hash ())); ASSERT_EQ (0, ledger.weight (transaction, key2.pub)); ASSERT_EQ (0, ledger.weight (transaction, key3.pub)); ASSERT_EQ (nano::genesis_amount - 0, ledger.weight (transaction, nano::test_genesis_key.pub)); @@ -2101,7 +2101,7 @@ TEST (ledger, state_rollback_send) ASSERT_FALSE (store.pending_get (transaction, nano::pending_key (nano::genesis_account, send1.hash ()), info)); ASSERT_EQ (nano::genesis_account, info.source); ASSERT_EQ (nano::Gxrb_ratio, info.amount.number ()); - ledger.rollback (transaction, send1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, send1.hash ())); ASSERT_FALSE (store.block_exists (transaction, send1.hash ())); ASSERT_EQ (nano::genesis_amount, ledger.account_balance (transaction, nano::genesis_account)); ASSERT_EQ (nano::genesis_amount, ledger.weight (transaction, nano::genesis_account)); @@ -2125,7 +2125,7 @@ TEST (ledger, state_rollback_receive) nano::state_block receive1 (nano::genesis_account, send1.hash (), nano::genesis_account, nano::genesis_amount, send1.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive1).code); ASSERT_FALSE (store.pending_exists (transaction, nano::pending_key (nano::genesis_account, receive1.hash ()))); - ledger.rollback (transaction, receive1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, receive1.hash ())); nano::pending_info info; ASSERT_FALSE (store.pending_get (transaction, nano::pending_key (nano::genesis_account, send1.hash ()), info)); ASSERT_EQ (nano::genesis_account, info.source); @@ -2152,7 +2152,7 @@ TEST (ledger, state_rollback_received_send) nano::state_block receive1 (key.pub, 0, key.pub, nano::Gxrb_ratio, send1.hash (), key.prv, key.pub, 0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive1).code); ASSERT_FALSE (store.pending_exists (transaction, nano::pending_key (nano::genesis_account, receive1.hash ()))); - ledger.rollback (transaction, send1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, send1.hash ())); ASSERT_FALSE (store.pending_exists (transaction, nano::pending_key (nano::genesis_account, send1.hash ()))); ASSERT_FALSE (store.block_exists (transaction, send1.hash ())); ASSERT_FALSE (store.block_exists (transaction, receive1.hash ())); @@ -2176,7 +2176,7 @@ TEST (ledger, state_rep_change_rollback) nano::keypair rep; nano::state_block change1 (nano::genesis_account, genesis.hash (), rep.pub, nano::genesis_amount, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, change1).code); - ledger.rollback (transaction, change1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, change1.hash ())); ASSERT_FALSE (store.block_exists (transaction, change1.hash ())); ASSERT_EQ (nano::genesis_amount, ledger.account_balance (transaction, nano::genesis_account)); ASSERT_EQ (nano::genesis_amount, ledger.weight (transaction, nano::genesis_account)); @@ -2199,7 +2199,7 @@ TEST (ledger, state_open_rollback) ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send1).code); nano::state_block open1 (destination.pub, 0, nano::genesis_account, nano::Gxrb_ratio, send1.hash (), destination.prv, destination.pub, 0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, open1).code); - ledger.rollback (transaction, open1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, open1.hash ())); ASSERT_FALSE (store.block_exists (transaction, open1.hash ())); ASSERT_EQ (0, ledger.account_balance (transaction, destination.pub)); ASSERT_EQ (nano::genesis_amount - nano::Gxrb_ratio, ledger.weight (transaction, nano::genesis_account)); @@ -2223,7 +2223,7 @@ TEST (ledger, state_send_change_rollback) nano::keypair rep; nano::state_block send1 (nano::genesis_account, genesis.hash (), rep.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::genesis_account, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send1).code); - ledger.rollback (transaction, send1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, send1.hash ())); ASSERT_FALSE (store.block_exists (transaction, send1.hash ())); ASSERT_EQ (nano::genesis_amount, ledger.account_balance (transaction, nano::genesis_account)); ASSERT_EQ (nano::genesis_amount, ledger.weight (transaction, nano::genesis_account)); @@ -2246,7 +2246,7 @@ TEST (ledger, state_receive_change_rollback) nano::keypair rep; nano::state_block receive1 (nano::genesis_account, send1.hash (), rep.pub, nano::genesis_amount, send1.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive1).code); - ledger.rollback (transaction, receive1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, receive1.hash ())); ASSERT_FALSE (store.block_exists (transaction, receive1.hash ())); ASSERT_EQ (nano::genesis_amount - nano::Gxrb_ratio, ledger.account_balance (transaction, nano::genesis_account)); ASSERT_EQ (nano::genesis_amount - nano::Gxrb_ratio, ledger.weight (transaction, nano::genesis_account)); @@ -2273,7 +2273,7 @@ TEST (ledger, epoch_blocks_general) nano::account_info genesis_info; ASSERT_FALSE (ledger.store.account_get (transaction, nano::genesis_account, genesis_info)); ASSERT_EQ (genesis_info.epoch, nano::epoch::epoch_1); - ledger.rollback (transaction, epoch1.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, epoch1.hash ())); ASSERT_FALSE (ledger.store.account_get (transaction, nano::genesis_account, genesis_info)); ASSERT_EQ (genesis_info.epoch, nano::epoch::epoch_0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, epoch1).code); @@ -2328,7 +2328,7 @@ TEST (ledger, epoch_blocks_receive_upgrade) nano::account_info destination_info; ASSERT_FALSE (ledger.store.account_get (transaction, destination.pub, destination_info)); ASSERT_EQ (destination_info.epoch, nano::epoch::epoch_1); - ledger.rollback (transaction, receive2.hash ()); + ASSERT_FALSE (ledger.rollback (transaction, receive2.hash ())); ASSERT_FALSE (ledger.store.account_get (transaction, destination.pub, destination_info)); ASSERT_EQ (destination_info.epoch, nano::epoch::epoch_0); ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive2).code); @@ -2587,3 +2587,29 @@ TEST (ledger, unchecked_receive) ASSERT_EQ (unchecked_count, 0); } } + +TEST (ledger, confirmation_height_not_updated) +{ + bool error (false); + nano::logging logging; + nano::mdb_store store (error, logging, nano::unique_path ()); + ASSERT_TRUE (!error); + nano::stat stats; + nano::ledger ledger (store, stats); + auto transaction (store.tx_begin (true)); + nano::genesis genesis; + store.initialize (transaction, genesis); + nano::account_info account_info; + ASSERT_FALSE (store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + nano::keypair key; + nano::send_block send1 (account_info.head, key.pub, 50, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); + ASSERT_EQ (0, account_info.confirmation_height); + ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send1).code); + ASSERT_FALSE (store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + nano::open_block open1 (send1.hash (), nano::genesis_account, key.pub, key.prv, key.pub, 0); + ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, open1).code); + nano::account_info account_info1; + ASSERT_FALSE (store.account_get (transaction, key.pub, account_info1)); + ASSERT_EQ (0, account_info1.confirmation_height); +} diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 1578582084..addd699e70 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -1423,3 +1423,310 @@ TEST (bootstrap, keepalive) ASSERT_NO_ERROR (system.poll ()); } } + +TEST (confirmation_height, single) +{ + auto amount (std::numeric_limits::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 block1 (std::make_shared (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 (0) + nano::account_info account_info; + { + auto transaction = system.nodes[0]->store.tx_begin_read (); + ASSERT_FALSE (system.nodes[0]->store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + + auto transaction1 = system.nodes[1]->store.tx_begin_read (); + ASSERT_FALSE (system.nodes[1]->store.account_get (transaction1, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + } + + system.nodes[0]->process_active (block1); + system.nodes[0]->block_processor.flush (); + system.nodes[1]->process_active (block1); + system.nodes[1]->block_processor.flush (); + + system.deadline_set (10s); + while (system.nodes[0]->balance (key1.pub) != system.nodes[0]->config.receive_minimum.number ()) + { + ASSERT_NO_ERROR (system.poll ()); + } + + // Check confirmation heights after + { + auto transaction = system.nodes[0]->store.tx_begin_write (); + ASSERT_FALSE (system.nodes[0]->store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (2, account_info.confirmation_height); + + auto transaction1 = system.nodes[1]->store.tx_begin_read (); + ASSERT_FALSE (system.nodes[1]->store.account_get (transaction1, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (2, account_info.confirmation_height); + + // Rollback should fail as this transaction has been cemented + ASSERT_TRUE (system.nodes[0]->ledger.rollback (transaction, block1->hash ())); + } +} + +TEST (confirmation_height, multiple) +{ + auto amount (std::numeric_limits::max ()); + nano::system system (24000, 2); + 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, 300, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); + nano::send_block send2 (send1.hash (), key2.pub, 1, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); + nano::send_block send3 (send2.hash (), key3.pub, 1, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); + + // Open all accounts + nano::open_block open1 (send1.hash (), nano::genesis_account, key1.pub, key1.prv, key1.pub, 0); + nano::open_block open2 (send2.hash (), nano::genesis_account, key2.pub, key2.prv, key2.pub, 0); + nano::open_block open3 (send3.hash (), nano::genesis_account, key3.pub, key3.prv, key3.pub, 0); + + // Send and recieve various blocks to these accounts + nano::send_block send4 (open1.hash (), key2.pub, 50, key1.prv, key1.pub, 0); + nano::send_block send5 (send4.hash (), key2.pub, 10, key1.prv, key1.pub, 0); + + nano::receive_block receive1 (open2.hash (), send4.hash (), key2.prv, key2.pub, 0); + nano::send_block send6 (receive1.hash (), key3.pub, 10, key2.prv, key2.pub, 0); + nano::receive_block receive2 (send6.hash (), send5.hash (), key2.prv, key2.pub, 0); + + for (auto & node : system.nodes) + { + 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. + nano::account_info account_info; + ASSERT_FALSE (node->store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + ASSERT_FALSE (node->store.account_get (transaction, key1.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + ASSERT_FALSE (node->store.account_get (transaction, key2.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + ASSERT_FALSE (node->store.account_get (transaction, key3.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + } + + // The nodes process a live receive which propagates across to all accounts + auto receive3 = std::make_shared (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; + 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_EQ (4, account_info.confirmation_height); + ASSERT_EQ (4, account_info.block_count); + ASSERT_FALSE (store.account_get (transaction, key1.pub, account_info)); + ASSERT_EQ (2, account_info.confirmation_height); + ASSERT_EQ (3, account_info.block_count); + ASSERT_FALSE (store.account_get (transaction, key2.pub, account_info)); + ASSERT_EQ (3, account_info.confirmation_height); + ASSERT_EQ (4, account_info.block_count); + ASSERT_FALSE (store.account_get (transaction, key3.pub, account_info)); + ASSERT_EQ (2, account_info.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 ())); + } + } +} + +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::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::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::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 (send1->hash (), destination.pub, destination.pub, destination.prv, destination.pub, 0)); + node1.work_generate_blocking (*open1); + + // Receive + auto receive1 (std::make_shared (open1->hash (), send2->hash (), destination.prv, destination.pub, 0)); + node1.work_generate_blocking (*receive1); + auto receive2 (std::make_shared (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 (); + + // 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 ()); + auto unchecked_count (node1.store.unchecked_count (transaction)); + ASSERT_EQ (unchecked_count, 2); + + nano::account_info account_info; + ASSERT_FALSE (node1.store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (0, account_info.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 still be 0 and unchecked should now be 0 + { + auto transaction (node1.store.tx_begin ()); + auto unchecked_count (node1.store.unchecked_count (transaction)); + ASSERT_EQ (unchecked_count, 0); + + nano::account_info account_info; + ASSERT_FALSE (node1.store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + ASSERT_FALSE (node1.store.account_get (transaction, destination.pub, account_info)); + ASSERT_EQ (0, account_info.confirmation_height); + } +} + +TEST (confirmation_height, gap_live) +{ + nano::system system (24000, 2); + nano::keypair destination; + 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 (destination.prv); + + nano::genesis genesis; + auto send1 (std::make_shared (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)); + auto send2 (std::make_shared (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)); + auto send3 (std::make_shared (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)); + + auto open1 (std::make_shared (send1->hash (), destination.pub, destination.pub, destination.prv, destination.pub, 0)); + auto receive1 (std::make_shared (open1->hash (), send2->hash (), destination.prv, destination.pub, 0)); + auto receive2 (std::make_shared (receive1->hash (), send3->hash (), destination.prv, destination.pub, 0)); + + for (auto & node : system.nodes) + { + node->work_generate_blocking (*send1); + node->work_generate_blocking (*send2); + node->work_generate_blocking (*send3); + node->work_generate_blocking (*open1); + node->work_generate_blocking (*receive1); + node->work_generate_blocking (*receive2); + + node->block_processor.add (send1); + node->block_processor.add (send2); + node->block_processor.add (send3); + node->block_processor.add (receive1); + node->block_processor.flush (); + + // 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 (); + nano::account_info account_info; + ASSERT_FALSE (node->store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (0, account_info.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, open1->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 ()); + auto unchecked_count (node->store.unchecked_count (transaction)); + ASSERT_EQ (unchecked_count, 0); + + nano::account_info account_info; + ASSERT_FALSE (node->store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_EQ (4, account_info.block_count); + ASSERT_EQ (2, account_info.confirmation_height); + ASSERT_FALSE (node->store.account_get (transaction, destination.pub, account_info)); + ASSERT_EQ (1, account_info.confirmation_height); + ASSERT_EQ (3, account_info.block_count); + } + } +} diff --git a/nano/core_test/rpc.cpp b/nano/core_test/rpc.cpp index cee4142e01..6a3b13740f 100644 --- a/nano/core_test/rpc.cpp +++ b/nano/core_test/rpc.cpp @@ -971,7 +971,7 @@ TEST (rpc, frontier) { nano::keypair key; source[key.pub] = key.prv.data; - system.nodes[0]->store.account_put (transaction, key.pub, nano::account_info (key.prv.data, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); + system.nodes[0]->store.account_put (transaction, key.pub, nano::account_info (key.prv.data, 0, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } nano::keypair key; @@ -1012,7 +1012,7 @@ TEST (rpc, frontier_limited) { nano::keypair key; source[key.pub] = key.prv.data; - system.nodes[0]->store.account_put (transaction, key.pub, nano::account_info (key.prv.data, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); + system.nodes[0]->store.account_put (transaction, key.pub, nano::account_info (key.prv.data, 0, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } nano::keypair key; @@ -1043,7 +1043,7 @@ TEST (rpc, frontier_startpoint) { nano::keypair key; source[key.pub] = key.prv.data; - system.nodes[0]->store.account_put (transaction, key.pub, nano::account_info (key.prv.data, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); + system.nodes[0]->store.account_put (transaction, key.pub, nano::account_info (key.prv.data, 0, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } nano::keypair key; @@ -3357,6 +3357,14 @@ TEST (rpc, account_info) system.nodes[0]->process (send); auto time (nano::seconds_since_epoch ()); + { + auto transaction = system.nodes[0]->store.tx_begin_write (); + nano::account_info account_info; + ASSERT_FALSE (system.nodes[0]->store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + account_info.confirmation_height = 1; + system.nodes[0]->store.account_put (transaction, nano::test_genesis_key.pub, account_info); + } + nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); rpc.start (); boost::property_tree::ptree request; @@ -3381,6 +3389,8 @@ TEST (rpc, account_info) ASSERT_LT (std::abs ((long)time - stol (modified_timestamp)), 5); std::string block_count (response.json.get ("block_count")); ASSERT_EQ ("2", block_count); + std::string confirmation_height (response.json.get ("confirmation_height")); + ASSERT_EQ ("1", confirmation_height); ASSERT_EQ (0, response.json.get ("account_version")); boost::optional weight (response.json.get_optional ("weight")); ASSERT_FALSE (weight.is_initialized ()); @@ -4774,3 +4784,86 @@ TEST (rpc, memory_stats) ASSERT_EQ (response.json.get_child ("node").get_child ("vote_uniquer").get_child ("votes").get ("count"), "1"); } + +TEST (rpc, block_confirmed) +{ + nano::system system (24000, 1); + auto node = system.nodes.front (); + nano::rpc rpc (system.io_ctx, *node, nano::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "block_confirmed"); + request.put ("hash", "bad_hash1337"); + test_response response (request, rpc, system.io_ctx); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + ASSERT_EQ ("Invalid block hash", response.json.get ("error")); + + request.put ("hash", "0"); + test_response response1 (request, rpc, system.io_ctx); + while (response1.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response1.status); + ASSERT_EQ ("Block not found", response1.json.get ("error")); + + system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); + nano::keypair key; + system.wallet (0)->insert_adhoc (key.prv); + + // Open an account directly in the ledger + { + auto transaction = node->store.tx_begin_write (); + nano::block_hash latest (node->latest (nano::test_genesis_key.pub)); + nano::send_block send1 (latest, key.pub, 300, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); + ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send1).code); + + nano::open_block open1 (send1.hash (), nano::genesis_account, key.pub, key.prv, key.pub, 0); + ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open1).code); + } + + // This should not be confirmed + nano::block_hash latest (node->latest (nano::test_genesis_key.pub)); + request.put ("hash", latest.to_string ()); + test_response response2 (request, rpc, system.io_ctx); + while (response2.status == 0) + { + system.poll (); + } + + ASSERT_EQ (200, response2.status); + ASSERT_FALSE (response2.json.get ("confirmed")); + + // Create and process a new send block + auto send = std::make_shared (latest, key.pub, 10, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (latest)); + node->process_active (send); + node->block_processor.flush (); + + // Wait until it has been confirmed by the network + system.deadline_set (10s); + while (true) + { + auto transaction = node->store.tx_begin_read (); + if (node->ledger.block_confirmed (transaction, send->hash ())) + { + break; + } + + ASSERT_NO_ERROR (system.poll ()); + } + + // Requesting confirmation for this should now succeed + request.put ("hash", send->hash ().to_string ()); + test_response response3 (request, rpc, system.io_ctx); + while (response3.status == 0) + { + system.poll (); + } + + ASSERT_EQ (200, response3.status); + ASSERT_TRUE (response3.json.get ("confirmed")); +} diff --git a/nano/core_test/versioning.cpp b/nano/core_test/versioning.cpp index eda329e222..0d36721ddf 100644 --- a/nano/core_test/versioning.cpp +++ b/nano/core_test/versioning.cpp @@ -14,7 +14,6 @@ TEST (versioning, account_info_v1) auto error (false); nano::mdb_store store (error, logging, file); ASSERT_FALSE (error); - store.stop (); auto transaction (store.tx_begin (true)); nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0); store.block_put (transaction, open.hash (), open, sideband); @@ -22,18 +21,92 @@ TEST (versioning, account_info_v1) ASSERT_EQ (0, status); store.version_put (transaction, 1); } + + nano::logging logging; + auto error (false); + nano::mdb_store store (error, logging, file); + ASSERT_FALSE (error); + auto transaction (store.tx_begin ()); + nano::account_info v_latest; + ASSERT_FALSE (store.account_get (transaction, account, v_latest)); + ASSERT_EQ (open.hash (), v_latest.open_block); + ASSERT_EQ (v1.balance, v_latest.balance); + ASSERT_EQ (v1.head, v_latest.head); + ASSERT_EQ (v1.modified, v_latest.modified); + ASSERT_EQ (v1.rep_block, v_latest.rep_block); + ASSERT_EQ (1, v_latest.block_count); + ASSERT_EQ (0, v_latest.confirmation_height); + ASSERT_EQ (nano::epoch::epoch_0, v_latest.epoch); +} + +TEST (versioning, account_info_v5) +{ + auto file (nano::unique_path ()); + nano::account account (1); + nano::open_block open (1, 2, 3, nullptr); + nano::account_info_v5 v5 (open.hash (), open.hash (), open.hash (), 3, 4); { nano::logging logging; auto error (false); nano::mdb_store store (error, logging, file); ASSERT_FALSE (error); - auto transaction (store.tx_begin ()); - nano::account_info v2; - ASSERT_FALSE (store.account_get (transaction, account, v2)); - ASSERT_EQ (open.hash (), v2.open_block); - ASSERT_EQ (v1.balance, v2.balance); - ASSERT_EQ (v1.head, v2.head); - ASSERT_EQ (v1.modified, v2.modified); - ASSERT_EQ (v1.rep_block, v2.rep_block); + auto transaction (store.tx_begin (true)); + nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0); + store.block_put (transaction, open.hash (), open, sideband); + auto status (mdb_put (store.env.tx (transaction), store.accounts_v0, nano::mdb_val (account), v5.val (), 0)); + ASSERT_EQ (0, status); + store.version_put (transaction, 5); } + + nano::logging logging; + auto error (false); + nano::mdb_store store (error, logging, file); + ASSERT_FALSE (error); + auto transaction (store.tx_begin ()); + nano::account_info v_latest; + ASSERT_FALSE (store.account_get (transaction, account, v_latest)); + ASSERT_EQ (v5.open_block, v_latest.open_block); + ASSERT_EQ (v5.balance, v_latest.balance); + ASSERT_EQ (v5.head, v_latest.head); + ASSERT_EQ (v5.modified, v_latest.modified); + ASSERT_EQ (v5.rep_block, v_latest.rep_block); + ASSERT_EQ (1, v_latest.block_count); + ASSERT_EQ (0, v_latest.confirmation_height); + ASSERT_EQ (nano::epoch::epoch_0, v_latest.epoch); +} + +TEST (versioning, account_info_v13) +{ + auto file (nano::unique_path ()); + nano::account account (1); + nano::open_block open (1, 2, 3, nullptr); + nano::account_info_v13 v13 (open.hash (), open.hash (), open.hash (), 3, 4, 10, nano::epoch::epoch_0); + { + nano::logging logging; + auto error (false); + nano::mdb_store store (error, logging, file); + ASSERT_FALSE (error); + auto transaction (store.tx_begin (true)); + nano::block_sideband sideband (nano::block_type::open, 0, 0, 0, 0, 0); + store.block_put (transaction, open.hash (), open, sideband); + auto status (mdb_put (store.env.tx (transaction), store.accounts_v0, nano::mdb_val (account), nano::mdb_val (v13), 0)); + ASSERT_EQ (0, status); + store.version_put (transaction, 13); + } + + nano::logging logging; + auto error (false); + nano::mdb_store store (error, logging, file); + ASSERT_FALSE (error); + auto transaction (store.tx_begin ()); + nano::account_info v_latest; + ASSERT_FALSE (store.account_get (transaction, account, v_latest)); + ASSERT_EQ (v13.open_block, v_latest.open_block); + ASSERT_EQ (v13.balance, v_latest.balance); + ASSERT_EQ (v13.head, v_latest.head); + ASSERT_EQ (v13.modified, v_latest.modified); + ASSERT_EQ (v13.rep_block, v_latest.rep_block); + ASSERT_EQ (v13.block_count, v_latest.block_count); + ASSERT_EQ (0, v_latest.confirmation_height); + ASSERT_EQ (v13.epoch, v_latest.epoch); } diff --git a/nano/core_test/wallets.cpp b/nano/core_test/wallets.cpp index 4a00dbefb4..60d7701a67 100644 --- a/nano/core_test/wallets.cpp +++ b/nano/core_test/wallets.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -92,6 +93,12 @@ TEST (wallets, upgrade) MDB_txn * tx_destination (*boost::polymorphic_downcast (transaction_destination.impl.get ())); node1->wallets.move_table (id.pub.to_string (), tx_source, tx_destination); node1->store.version_put (transaction_destination, 11); + + nano::account_info info; + ASSERT_FALSE (mdb_store.account_get (transaction_destination, nano::genesis_account, info)); + nano::account_info_v13 account_info_v13 (info.head, info.rep_block, info.open_block, info.balance, info.modified, info.block_count, info.epoch); + auto status (mdb_put (mdb_store.env.tx (transaction_destination), mdb_store.get_account_db (info.epoch), nano::mdb_val (nano::test_genesis_key.pub), nano::mdb_val (account_info_v13), 0)); + assert (status == 0); } nano::node_init init1; auto node1 (std::make_shared (init1, system.io_ctx, 24001, path, system.alarm, system.logging, system.work)); diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 39a3f33efe..2efa0efed0 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -295,8 +295,14 @@ void nano::block_processor::process_batch (std::unique_lock & lock_a // Replace our block with the winner and roll back any dependent blocks node.logger.always_log (boost::str (boost::format ("Rolling back %1% and replacing with %2%") % successor->hash ().to_string () % hash.to_string ())); std::vector rollback_list; - node.ledger.rollback (transaction, successor->hash (), rollback_list); - node.logger.always_log (boost::str (boost::format ("%1% blocks rolled back") % rollback_list.size ())); + if (node.ledger.rollback (transaction, successor->hash (), rollback_list)) + { + node.logger.always_log (boost::str (boost::format ("Failed to roll back %1% because it or a successor was confirmed") % successor->hash ().to_string ())); + } + else + { + node.logger.always_log (boost::str (boost::format ("%1% blocks rolled back") % rollback_list.size ())); + } lock_a.lock (); // Prevent rolled back blocks second insertion auto inserted (rolled_back.insert (nano::rolled_hash{ std::chrono::steady_clock::now (), successor->hash () })); diff --git a/nano/node/cli.cpp b/nano/node/cli.cpp index e662558c59..b66b2e6900 100644 --- a/nano/node/cli.cpp +++ b/nano/node/cli.cpp @@ -36,6 +36,7 @@ void nano::add_node_options (boost::program_options::options_description & descr ("online_weight_clear", "Clear online weight history records") ("peer_clear", "Clear online peers database dump") ("unchecked_clear", "Clear unchecked blocks") + ("confirmation_height_clear", "Clear confirmation height") ("diagnostics", "Run internal diagnostics") ("key_create", "Generates a adhoc random keypair and prints it to stdout") ("key_expand", "Derive public key and account number from ") @@ -246,6 +247,12 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map auto transaction (node.node->store.tx_begin_write ()); node.node->store.peer_clear (transaction); } + if (vm.count ("confirmation_height_clear")) + { + auto transaction (node.node->store.tx_begin_write ()); + node.node->store.confirmation_height_clear (transaction); + } + success = node.node->copy_with_compaction (snapshot_path); } if (success) @@ -272,7 +279,7 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map inactive_node node (data_path); auto transaction (node.node->store.tx_begin_write ()); node.node->store.unchecked_clear (transaction); - std::cerr << "Unchecked blocks deleted" << std::endl; + std::cout << "Unchecked blocks deleted" << std::endl; } else if (vm.count ("delete_node_id")) { @@ -280,7 +287,7 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map inactive_node node (data_path); auto transaction (node.node->store.tx_begin_write ()); node.node->store.delete_node_id (transaction); - std::cerr << "Deleted Node ID" << std::endl; + std::cout << "Deleted Node ID" << std::endl; } else if (vm.count ("clear_send_ids")) { @@ -288,7 +295,7 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map inactive_node node (data_path); auto transaction (node.node->wallets.tx_begin_write ()); node.node->wallets.clear_send_ids (transaction); - std::cerr << "Send IDs deleted" << std::endl; + std::cout << "Send IDs deleted" << std::endl; } else if (vm.count ("online_weight_clear")) { @@ -296,7 +303,7 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map inactive_node node (data_path); auto transaction (node.node->store.tx_begin_write ()); node.node->store.online_weight_clear (transaction); - std::cerr << "Onine weight records are removed" << std::endl; + std::cout << "Onine weight records are removed" << std::endl; } else if (vm.count ("peer_clear")) { @@ -304,7 +311,45 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map inactive_node node (data_path); auto transaction (node.node->store.tx_begin_write ()); node.node->store.peer_clear (transaction); - std::cerr << "Database peers are removed" << std::endl; + std::cout << "Database peers are removed" << std::endl; + } + else if (vm.count ("confirmation_height_clear")) + { + boost::filesystem::path data_path = vm.count ("data_path") ? boost::filesystem::path (vm["data_path"].as ()) : nano::working_path (); + inactive_node node (data_path); + auto account_it = vm.find ("account"); + if (account_it != vm.cend ()) + { + auto account_str = account_it->second.as (); + nano::account account; + if (!account.decode_account (account_str)) + { + nano::account_info account_info; + auto transaction (node.node->store.tx_begin_read ()); + if (!node.node->store.account_get (transaction, account, account_info)) + { + auto transaction (node.node->store.tx_begin_write ()); + node.node->store.confirmation_height_clear (transaction, account, account_info); + std::cout << "Confirmation height of account " << account_str << " is set to 0" << std::endl; + } + else + { + std::cerr << "Could not find account" << std::endl; + ec = nano::error_cli::generic; + } + } + else + { + std::cerr << "Invalid account id\n"; + ec = nano::error_cli::invalid_arguments; + } + } + else + { + auto transaction (node.node->store.tx_begin_write ()); + node.node->store.confirmation_height_clear (transaction); + std::cout << "Confirmation heights of all accounts are set to 0" << std::endl; + } } else if (vm.count ("diagnostics")) { @@ -342,7 +387,8 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map } else { - std::cout << "Error initializing OpenCL" << std::endl; + std::cerr << "Error initializing OpenCL" << std::endl; + ec = nano::error_cli::generic; } } else if (vm.count ("key_create")) diff --git a/nano/node/common.cpp b/nano/node/common.cpp index f819dff72f..78397ccab2 100644 --- a/nano/node/common.cpp +++ b/nano/node/common.cpp @@ -577,7 +577,7 @@ bool nano::confirm_req::deserialize (nano::stream & stream_a, nano::block_unique result = block == nullptr; } } - catch (const std::runtime_error & error) + catch (const std::runtime_error &) { result = true; } diff --git a/nano/node/lmdb.cpp b/nano/node/lmdb.cpp index 898da8e910..b134397f2d 100644 --- a/nano/node/lmdb.cpp +++ b/nano/node/lmdb.cpp @@ -101,6 +101,11 @@ mdb_val (val_a.db_size (), const_cast (&val_a)) { } +nano::mdb_val::mdb_val (nano::account_info_v13 const & val_a) : +mdb_val (val_a.db_size (), const_cast (&val_a)) +{ +} + nano::mdb_val::mdb_val (nano::pending_info const & val_a) : mdb_val (sizeof (val_a.source) + sizeof (val_a.amount), const_cast (&val_a)) { @@ -171,6 +176,15 @@ nano::mdb_val::operator nano::account_info () const return result; } +nano::mdb_val::operator nano::account_info_v13 () const +{ + nano::account_info_v13 result; + result.epoch = epoch; + assert (value.mv_size == result.db_size ()); + std::copy (reinterpret_cast (value.mv_data), reinterpret_cast (value.mv_data) + result.db_size (), reinterpret_cast (&result)); + return result; +} + nano::mdb_val::operator nano::block_info () const { nano::block_info result; @@ -748,7 +762,6 @@ nano::mdb_store::mdb_store (bool & error_a, nano::logging & logging_a, boost::fi logging (logging_a), env (error_a, path_a, lmdb_max_dbs) { - auto slow_upgrade (false); if (!error_a) { auto transaction (tx_begin_write ()); @@ -775,34 +788,13 @@ env (error_a, path_a, lmdb_max_dbs) } if (!error_a) { - do_upgrades (transaction, slow_upgrade); + do_upgrades (transaction, batch_size); if (drop_unchecked) { unchecked_clear (transaction); } } } - if (slow_upgrade) - { - upgrades = std::thread ([this, batch_size]() { - nano::thread_role::set (nano::thread_role::name::slow_db_upgrade); - do_slow_upgrades (batch_size); - }); - } -} - -nano::mdb_store::~mdb_store () -{ - stop (); -} - -void nano::mdb_store::stop () -{ - stopped = true; - if (upgrades.joinable ()) - { - upgrades.join (); - } } nano::transaction nano::mdb_store::tx_begin_write () @@ -820,6 +812,10 @@ nano::transaction nano::mdb_store::tx_begin (bool write_a) return env.tx_begin (write_a); } +/** + * This is only used with testing. If using a different store version than the latest then you may need + * to modify some of the objects in the store to be appropriate for the version before an upgrade. + */ void nano::mdb_store::initialize (nano::transaction const & transaction_a, nano::genesis const & genesis_a) { auto hash_l (genesis_a.hash ()); @@ -827,7 +823,7 @@ void nano::mdb_store::initialize (nano::transaction const & transaction_a, nano: assert (latest_v1_begin (transaction_a) == latest_v1_end ()); nano::block_sideband sideband (nano::block_type::open, nano::genesis_account, 0, nano::genesis_amount, 1, nano::seconds_since_epoch ()); block_put (transaction_a, hash_l, *genesis_a.open, sideband); - account_put (transaction_a, genesis_account, { hash_l, genesis_a.open->hash (), genesis_a.open->hash (), std::numeric_limits::max (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0 }); + account_put (transaction_a, genesis_account, { hash_l, genesis_a.open->hash (), genesis_a.open->hash (), std::numeric_limits::max (), nano::seconds_since_epoch (), 1, 0, nano::epoch::epoch_0 }); representation_put (transaction_a, genesis_account, std::numeric_limits::max ()); frontier_put (transaction_a, hash_l, genesis_account); } @@ -851,7 +847,7 @@ void nano::mdb_store::version_put (nano::transaction const & transaction_a, int } } -int nano::mdb_store::version_get (nano::transaction const & transaction_a) +int nano::mdb_store::version_get (nano::transaction const & transaction_a) const { nano::uint256_union version_key (1); nano::mdb_val data; @@ -866,7 +862,7 @@ int nano::mdb_store::version_get (nano::transaction const & transaction_a) return result; } -nano::raw_key nano::mdb_store::get_node_id (nano::transaction const & transaction_a) +nano::raw_key nano::mdb_store::get_node_id (nano::transaction const & transaction_a) const { nano::uint256_union node_id_mdb_key (3); nano::raw_key node_id; @@ -941,7 +937,7 @@ nano::store_iterator nano::mdb_store::peers_ return result; } -void nano::mdb_store::do_upgrades (nano::transaction const & transaction_a, bool & slow_upgrade) +void nano::mdb_store::do_upgrades (nano::transaction const & transaction_a, size_t batch_size) { switch (version_get (transaction_a)) { @@ -966,13 +962,12 @@ void nano::mdb_store::do_upgrades (nano::transaction const & transaction_a, bool case 10: upgrade_v10_to_v11 (transaction_a); case 11: - // Signal the start of sideband upgrade upgrade_v11_to_v12 (transaction_a); - // [[fallthrough]]; case 12: - slow_upgrade = true; - break; + upgrade_v12_to_v13 (transaction_a, batch_size); case 13: + upgrade_v13_to_v14 (transaction_a); + case 14: break; default: assert (false); @@ -1089,7 +1084,7 @@ void nano::mdb_store::upgrade_v4_to_v5 (nano::transaction const & transaction_a) void nano::mdb_store::upgrade_v5_to_v6 (nano::transaction const & transaction_a) { version_put (transaction_a, 6); - std::deque> headers; + std::deque> headers; for (auto i (nano::store_iterator (std::make_unique> (transaction_a, accounts_v0))), n (nano::store_iterator (nullptr)); i != n; ++i) { nano::account account (i->first); @@ -1103,12 +1098,12 @@ void nano::mdb_store::upgrade_v5_to_v6 (nano::transaction const & transaction_a) assert (block != nullptr); hash = block->previous (); } - nano::account_info info (info_old.head, info_old.rep_block, info_old.open_block, info_old.balance, info_old.modified, block_count, nano::epoch::epoch_0); - headers.push_back (std::make_pair (account, info)); + headers.emplace_back (account, nano::account_info_v13{ info_old.head, info_old.rep_block, info_old.open_block, info_old.balance, info_old.modified, block_count, nano::epoch::epoch_0 }); } for (auto i (headers.begin ()), n (headers.end ()); i != n; ++i) { - account_put (transaction_a, i->first, i->second); + auto status (mdb_put (env.tx (transaction_a), accounts_v0, nano::mdb_val (i->first), nano::mdb_val (i->second), 0)); + release_assert (status == 0); } } @@ -1164,39 +1159,6 @@ void nano::mdb_store::upgrade_v10_to_v11 (nano::transaction const & transaction_ mdb_drop (env.tx (transaction_a), unsynced, 1); } -void nano::mdb_store::do_slow_upgrades (size_t const batch_size) -{ - int version; - { - nano::transaction transaction (tx_begin_read ()); - version = version_get (transaction); - } - switch (version) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - case 10: - case 11: - break; - case 12: - upgrade_v12_to_v13 (batch_size); - break; - case 13: - break; - default: - assert (false); - break; - } -} - void nano::mdb_store::upgrade_v11_to_v12 (nano::transaction const & transaction_a) { version_put (transaction_a, 12); @@ -1207,19 +1169,19 @@ void nano::mdb_store::upgrade_v11_to_v12 (nano::transaction const & transaction_ mdb_drop (env.tx (transaction_a), checksum, 1); } -void nano::mdb_store::upgrade_v12_to_v13 (size_t const batch_size) +void nano::mdb_store::upgrade_v12_to_v13 (nano::transaction const & transaction_a, size_t const batch_size) { size_t cost (0); nano::account account (0); - auto transaction (tx_begin_write ()); auto const & not_an_account (nano::not_an_account ()); - while (!stopped && account != not_an_account) + while (account != not_an_account) { nano::account first (0); - nano::account_info second; + nano::account_info_v13 second; { - auto current (latest_begin (transaction, account)); - if (current != latest_end ()) + nano::store_iterator current (std::make_unique> (transaction_a, accounts_v0, accounts_v1, nano::mdb_val (account))); + nano::store_iterator end (nullptr); + if (current != end) { first = current->first; second = current->second; @@ -1230,12 +1192,12 @@ void nano::mdb_store::upgrade_v12_to_v13 (size_t const batch_size) auto hash (second.open_block); uint64_t height (1); nano::block_sideband sideband; - while (!stopped && !hash.is_zero ()) + while (!hash.is_zero ()) { if (cost >= batch_size) { logging.logger.always_log (boost::str (boost::format ("Upgrading sideband information for account %1%... height %2%") % first.to_account ().substr (0, 24) % std::to_string (height))); - auto tx (boost::polymorphic_downcast (transaction.impl.get ())); + auto tx (boost::polymorphic_downcast (transaction_a.impl.get ())); auto status0 (mdb_txn_commit (*tx)); release_assert (status0 == MDB_SUCCESS); std::this_thread::yield (); @@ -1243,12 +1205,12 @@ void nano::mdb_store::upgrade_v12_to_v13 (size_t const batch_size) release_assert (status1 == MDB_SUCCESS); cost = 0; } - auto block (block_get (transaction, hash, &sideband)); + auto block (block_get (transaction_a, hash, &sideband)); assert (block != nullptr); if (sideband.height == 0) { sideband.height = height; - block_put (transaction, hash, *block, sideband, block_version (transaction, hash)); + block_put (transaction_a, hash, *block, sideband, block_version (transaction_a, hash)); cost += 16; } else @@ -1268,8 +1230,32 @@ void nano::mdb_store::upgrade_v12_to_v13 (size_t const batch_size) if (account == not_an_account) { logging.logger.always_log (boost::str (boost::format ("Completed sideband upgrade"))); - version_put (transaction, 13); + version_put (transaction_a, 13); + } +} + +void nano::mdb_store::upgrade_v13_to_v14 (nano::transaction const & transaction_a) +{ + // Upgrade all accounts to have a confirmation of 0 + version_put (transaction_a, 14); + nano::store_iterator i (std::make_unique> (transaction_a, accounts_v0, accounts_v1)); + nano::store_iterator n (nullptr); + constexpr uint64_t zeroed_confirmation_height (0); + + std::vector> account_infos; + account_infos.reserve (account_count (transaction_a)); + for (; i != n; ++i) + { + nano::account_info_v13 account_info_v13 (i->second); + account_infos.emplace_back (i->first, nano::account_info{ account_info_v13.head, account_info_v13.rep_block, account_info_v13.open_block, account_info_v13.balance, account_info_v13.modified, account_info_v13.block_count, zeroed_confirmation_height, account_info_v13.epoch }); } + + for (auto const & account_info : account_infos) + { + account_put (transaction_a, account_info.first, account_info.second); + } + + logging.logger.always_log (boost::str (boost::format ("Completed confirmation height upgrade"))); } void nano::mdb_store::clear (MDB_dbi db_a) @@ -1305,7 +1291,7 @@ nano::uint128_t nano::mdb_store::block_balance (nano::transaction const & transa return result; } -nano::uint128_t nano::mdb_store::block_balance_computed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) +nano::uint128_t nano::mdb_store::block_balance_computed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const { assert (!full_sideband (transaction_a)); summation_visitor visitor (transaction_a, *this); @@ -1396,7 +1382,7 @@ void nano::mdb_store::block_put (nano::transaction const & transaction_a, nano:: assert (block_a.previous ().is_zero () || block_successor (transaction_a, block_a.previous ()) == hash_a); } -boost::optional nano::mdb_store::block_raw_get_by_type (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a) +boost::optional nano::mdb_store::block_raw_get_by_type (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a) const { nano::mdb_val value; auto status (MDB_NOTFOUND); @@ -1448,7 +1434,7 @@ boost::optional nano::mdb_store::block_raw_get_by_type (nano::transacti return result; } -MDB_val nano::mdb_store::block_raw_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a) +MDB_val nano::mdb_store::block_raw_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_type & type_a) const { nano::mdb_val result; // Table lookups are ordered by match probability @@ -1532,17 +1518,17 @@ std::shared_ptr nano::mdb_store::block_random (nano::transaction co return result; } -bool nano::mdb_store::full_sideband (nano::transaction const & transaction_a) +bool nano::mdb_store::full_sideband (nano::transaction const & transaction_a) const { return version_get (transaction_a) > 12; } -bool nano::mdb_store::entry_has_sideband (MDB_val entry_a, nano::block_type type_a) +bool nano::mdb_store::entry_has_sideband (MDB_val entry_a, nano::block_type type_a) const { return entry_a.mv_size == nano::block::size (type_a) + nano::block_sideband::size (type_a); } -size_t nano::mdb_store::block_successor_offset (nano::transaction const & transaction_a, MDB_val entry_a, nano::block_type type_a) +size_t nano::mdb_store::block_successor_offset (nano::transaction const & transaction_a, MDB_val entry_a, nano::block_type type_a) const { size_t result; if (full_sideband (transaction_a) || entry_has_sideband (entry_a, type_a)) @@ -1558,7 +1544,7 @@ size_t nano::mdb_store::block_successor_offset (nano::transaction const & transa return result; } -nano::block_hash nano::mdb_store::block_successor (nano::transaction const & transaction_a, nano::block_hash const & hash_a) +nano::block_hash nano::mdb_store::block_successor (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const { nano::block_type type; auto value (block_raw_get (transaction_a, hash_a, type)); @@ -1588,7 +1574,16 @@ void nano::mdb_store::block_successor_clear (nano::transaction const & transacti block_raw_put (transaction_a, block_database (type, version), hash_a, nano::mdb_val (data.size (), data.data ())); } -std::shared_ptr nano::mdb_store::block_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_sideband * sideband_a) +// Converts a block hash to a block height +uint64_t nano::mdb_store::block_account_height (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const +{ + nano::block_sideband sideband; + auto block = block_get (transaction_a, hash_a, &sideband); + assert (block != nullptr); + return sideband.height; +} + +std::shared_ptr nano::mdb_store::block_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_sideband * sideband_a) const { nano::block_type type; auto value (block_raw_get (transaction_a, hash_a, type)); @@ -1760,7 +1755,7 @@ bool nano::mdb_store::source_exists (nano::transaction const & transaction_a, na return block_exists (transaction_a, nano::block_type::state, source_a) || block_exists (transaction_a, nano::block_type::send, source_a); } -nano::account nano::mdb_store::block_account (nano::transaction const & transaction_a, nano::block_hash const & hash_a) +nano::account nano::mdb_store::block_account (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const { nano::block_sideband sideband; auto block (block_get (transaction_a, hash_a, &sideband)); @@ -1774,7 +1769,7 @@ nano::account nano::mdb_store::block_account (nano::transaction const & transact } // Return account containing hash -nano::account nano::mdb_store::block_account_computed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) +nano::account nano::mdb_store::block_account_computed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const { assert (!full_sideband (transaction_a)); nano::account result (0); @@ -1861,7 +1856,7 @@ bool nano::mdb_store::account_get (nano::transaction const & transaction_a, nano { nano::bufferstream stream (reinterpret_cast (value.data ()), value.size ()); info_a.epoch = epoch; - info_a.deserialize (stream); + result = info_a.deserialize (stream); } return result; } @@ -1872,7 +1867,7 @@ void nano::mdb_store::frontier_put (nano::transaction const & transaction_a, nan release_assert (status == 0); } -nano::account nano::mdb_store::frontier_get (nano::transaction const & transaction_a, nano::block_hash const & block_a) +nano::account nano::mdb_store::frontier_get (nano::transaction const & transaction_a, nano::block_hash const & block_a) const { nano::mdb_val value; auto status (mdb_get (env.tx (transaction_a), frontiers, nano::mdb_val (block_a), value)); @@ -1903,10 +1898,10 @@ size_t nano::mdb_store::account_count (nano::transaction const & transaction_a) return result; } -void nano::mdb_store::account_put (nano::transaction const & transaction_a, nano::account const & account_a, nano::account_info const & info_a) +MDB_dbi nano::mdb_store::get_account_db (nano::epoch epoch_a) const { MDB_dbi db; - switch (info_a.epoch) + switch (epoch_a) { case nano::epoch::invalid: case nano::epoch::unspecified: @@ -1918,14 +1913,13 @@ void nano::mdb_store::account_put (nano::transaction const & transaction_a, nano db = accounts_v1; break; } - auto status (mdb_put (env.tx (transaction_a), db, nano::mdb_val (account_a), nano::mdb_val (info_a), 0)); - release_assert (status == 0); + return db; } -void nano::mdb_store::pending_put (nano::transaction const & transaction_a, nano::pending_key const & key_a, nano::pending_info const & pending_a) +MDB_dbi nano::mdb_store::get_pending_db (nano::epoch epoch_a) const { MDB_dbi db; - switch (pending_a.epoch) + switch (epoch_a) { case nano::epoch::invalid: case nano::epoch::unspecified: @@ -1937,7 +1931,36 @@ void nano::mdb_store::pending_put (nano::transaction const & transaction_a, nano db = pending_v1; break; } - auto status (mdb_put (env.tx (transaction_a), db, nano::mdb_val (key_a), nano::mdb_val (pending_a), 0)); + return db; +} + +void nano::mdb_store::account_put (nano::transaction const & transaction_a, nano::account const & account_a, nano::account_info const & info_a) +{ + auto status (mdb_put (env.tx (transaction_a), get_account_db (info_a.epoch), nano::mdb_val (account_a), nano::mdb_val (info_a), 0)); + release_assert (status == 0); +} + +void nano::mdb_store::confirmation_height_clear (nano::transaction const & transaction_a, nano::account const & account, nano::account_info const & account_info) +{ + nano::account_info info_copy (account_info); + if (info_copy.confirmation_height > 0) + { + info_copy.confirmation_height = 0; + account_put (transaction_a, account, info_copy); + } +} + +void nano::mdb_store::confirmation_height_clear (nano::transaction const & transaction_a) +{ + for (auto i (latest_begin (transaction_a)), n (latest_end ()); i != n; ++i) + { + confirmation_height_clear (transaction_a, i->first, i->second); + } +} + +void nano::mdb_store::pending_put (nano::transaction const & transaction_a, nano::pending_key const & key_a, nano::pending_info const & pending_a) +{ + auto status (mdb_put (env.tx (transaction_a), get_pending_db (pending_a.epoch), nano::mdb_val (key_a), nano::mdb_val (pending_a), 0)); release_assert (status == 0); } @@ -1986,7 +2009,7 @@ bool nano::mdb_store::pending_get (nano::transaction const & transaction_a, nano { nano::bufferstream stream (reinterpret_cast (value.data ()), value.size ()); pending_a.epoch = epoch; - pending_a.deserialize (stream); + result = pending_a.deserialize (stream); } return result; } @@ -2045,7 +2068,7 @@ nano::store_iterator nano::mdb_store::pen return result; } -bool nano::mdb_store::block_info_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_info & block_info_a) +bool nano::mdb_store::block_info_get (nano::transaction const & transaction_a, nano::block_hash const & hash_a, nano::block_info & block_info_a) const { assert (!full_sideband (transaction_a)); nano::mdb_val value; @@ -2133,12 +2156,6 @@ std::vector nano::mdb_store::unchecked_get (nano::transact return result; } -bool nano::mdb_store::unchecked_exists (nano::transaction const & transaction_a, nano::unchecked_key const & key_a) -{ - auto iterator (unchecked_begin (transaction_a, key_a)); - return iterator != unchecked_end () && nano::unchecked_key (iterator->first) == key_a; -} - void nano::mdb_store::unchecked_del (nano::transaction const & transaction_a, nano::unchecked_key const & key_a) { auto status (mdb_del (env.tx (transaction_a), unchecked, nano::mdb_val (key_a), nullptr)); diff --git a/nano/node/lmdb.hpp b/nano/node/lmdb.hpp index ee0bb89444..7ef49c945b 100644 --- a/nano/node/lmdb.hpp +++ b/nano/node/lmdb.hpp @@ -14,6 +14,7 @@ namespace nano { class mdb_env; +class account_info_v13; class mdb_txn : public transaction_impl { public: @@ -48,6 +49,7 @@ class mdb_val public: mdb_val (nano::epoch = nano::epoch::unspecified); mdb_val (nano::account_info const &); + mdb_val (nano::account_info_v13 const &); mdb_val (nano::block_info const &); mdb_val (MDB_val const &, nano::epoch = nano::epoch::unspecified); mdb_val (nano::pending_info const &); @@ -63,6 +65,7 @@ class mdb_val void * data () const; size_t size () const; explicit operator nano::account_info () const; + explicit operator nano::account_info_v13 () const; explicit operator nano::block_info () const; explicit operator nano::pending_info () const; explicit operator nano::pending_key () const; @@ -151,18 +154,15 @@ class mdb_store : public block_store public: mdb_store (bool &, nano::logging &, boost::filesystem::path const &, int lmdb_max_dbs = 128, bool drop_unchecked = false, size_t batch_size = 512); - ~mdb_store (); - nano::transaction tx_begin_write () override; nano::transaction tx_begin_read () override; nano::transaction tx_begin (bool write = false) override; void initialize (nano::transaction const &, nano::genesis const &) override; void block_put (nano::transaction const &, nano::block_hash const &, nano::block const &, nano::block_sideband const &, nano::epoch version = nano::epoch::epoch_0) override; - size_t block_successor_offset (nano::transaction const &, MDB_val, nano::block_type); - nano::block_hash block_successor (nano::transaction const &, nano::block_hash const &) override; + nano::block_hash block_successor (nano::transaction const &, nano::block_hash const &) const override; void block_successor_clear (nano::transaction const &, nano::block_hash const &) override; - std::shared_ptr block_get (nano::transaction const &, nano::block_hash const &, nano::block_sideband * = nullptr) override; + std::shared_ptr block_get (nano::transaction const &, nano::block_hash const &, nano::block_sideband * = nullptr) const override; std::shared_ptr block_random (nano::transaction const &) override; void block_del (nano::transaction const &, nano::block_hash const &) override; bool block_exists (nano::transaction const &, nano::block_hash const &) override; @@ -170,10 +170,10 @@ class mdb_store : public block_store nano::block_counts block_count (nano::transaction const &) override; bool root_exists (nano::transaction const &, nano::uint256_union const &) override; bool source_exists (nano::transaction const &, nano::block_hash const &) override; - nano::account block_account (nano::transaction const &, nano::block_hash const &) override; + nano::account block_account (nano::transaction const &, nano::block_hash const &) const override; void frontier_put (nano::transaction const &, nano::block_hash const &, nano::account const &) override; - nano::account frontier_get (nano::transaction const &, nano::block_hash const &) override; + nano::account frontier_get (nano::transaction const &, nano::block_hash const &) const override; void frontier_del (nano::transaction const &, nano::block_hash const &) override; void account_put (nano::transaction const &, nano::account const &, nano::account_info const &) override; @@ -181,6 +181,8 @@ class mdb_store : public block_store void account_del (nano::transaction const &, nano::account const &) override; bool account_exists (nano::transaction const &, nano::account const &) override; size_t account_count (nano::transaction const &) override; + void confirmation_height_clear (nano::transaction const &, nano::account const & account, nano::account_info const & account_info) override; + void confirmation_height_clear (nano::transaction const &) override; nano::store_iterator latest_v0_begin (nano::transaction const &, nano::account const &) override; nano::store_iterator latest_v0_begin (nano::transaction const &) override; nano::store_iterator latest_v0_end () override; @@ -205,7 +207,7 @@ class mdb_store : public block_store nano::store_iterator pending_begin (nano::transaction const &) override; nano::store_iterator pending_end () override; - bool block_info_get (nano::transaction const &, nano::block_hash const &, nano::block_info &) override; + bool block_info_get (nano::transaction const &, nano::block_hash const &, nano::block_info &) const override; nano::uint128_t block_balance (nano::transaction const &, nano::block_hash const &) override; nano::epoch block_version (nano::transaction const &, nano::block_hash const &) override; @@ -219,7 +221,6 @@ class mdb_store : public block_store void unchecked_put (nano::transaction const &, nano::unchecked_key const &, nano::unchecked_info const &) override; void unchecked_put (nano::transaction const &, nano::block_hash const &, std::shared_ptr const &) override; std::vector unchecked_get (nano::transaction const &, nano::block_hash const &) override; - bool unchecked_exists (nano::transaction const &, nano::unchecked_key const &) override; void unchecked_del (nano::transaction const &, nano::unchecked_key const &) override; nano::store_iterator unchecked_begin (nano::transaction const &) override; nano::store_iterator unchecked_begin (nano::transaction const &, nano::unchecked_key const &) override; @@ -251,25 +252,10 @@ class mdb_store : public block_store std::unordered_map> vote_cache_l2; void version_put (nano::transaction const &, int) override; - int version_get (nano::transaction const &) override; - void do_upgrades (nano::transaction const &, bool &); - void upgrade_v1_to_v2 (nano::transaction const &); - void upgrade_v2_to_v3 (nano::transaction const &); - void upgrade_v3_to_v4 (nano::transaction const &); - void upgrade_v4_to_v5 (nano::transaction const &); - void upgrade_v5_to_v6 (nano::transaction const &); - void upgrade_v6_to_v7 (nano::transaction const &); - void upgrade_v7_to_v8 (nano::transaction const &); - void upgrade_v8_to_v9 (nano::transaction const &); - void upgrade_v9_to_v10 (nano::transaction const &); - void upgrade_v10_to_v11 (nano::transaction const &); - void upgrade_v11_to_v12 (nano::transaction const &); - void do_slow_upgrades (size_t const); - void upgrade_v12_to_v13 (size_t const); - bool full_sideband (nano::transaction const &); + int version_get (nano::transaction const &) const override; // Requires a write transaction - nano::raw_key get_node_id (nano::transaction const &) override; + nano::raw_key get_node_id (nano::transaction const &) const override; /** Deletes the node ID from the store */ void delete_node_id (nano::transaction const &) override; @@ -283,7 +269,11 @@ class mdb_store : public block_store nano::store_iterator peers_begin (nano::transaction const & transaction_a) override; nano::store_iterator peers_end () override; - void stop (); + uint64_t block_account_height (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const override; + + bool full_sideband (nano::transaction const &) const; + MDB_dbi get_account_db (nano::epoch epoch_a) const; + size_t block_successor_offset (nano::transaction const &, MDB_val, nano::block_type) const; nano::logging & logging; @@ -398,18 +388,31 @@ class mdb_store : public block_store MDB_dbi peers{ 0 }; private: - bool entry_has_sideband (MDB_val, nano::block_type); - nano::account block_account_computed (nano::transaction const &, nano::block_hash const &); - nano::uint128_t block_balance_computed (nano::transaction const &, nano::block_hash const &); + bool entry_has_sideband (MDB_val, nano::block_type) const; + nano::account block_account_computed (nano::transaction const &, nano::block_hash const &) const; + nano::uint128_t block_balance_computed (nano::transaction const &, nano::block_hash const &) const; MDB_dbi block_database (nano::block_type, nano::epoch); template std::shared_ptr block_random (nano::transaction const &, MDB_dbi); - MDB_val block_raw_get (nano::transaction const &, nano::block_hash const &, nano::block_type &); - boost::optional block_raw_get_by_type (nano::transaction const &, nano::block_hash const &, nano::block_type &); + MDB_val block_raw_get (nano::transaction const &, nano::block_hash const &, nano::block_type &) const; + boost::optional block_raw_get_by_type (nano::transaction const &, nano::block_hash const &, nano::block_type &) const; void block_raw_put (nano::transaction const &, MDB_dbi, nano::block_hash const &, MDB_val); void clear (MDB_dbi); - std::atomic stopped{ false }; - std::thread upgrades; + void do_upgrades (nano::transaction const &, size_t); + void upgrade_v1_to_v2 (nano::transaction const &); + void upgrade_v2_to_v3 (nano::transaction const &); + void upgrade_v3_to_v4 (nano::transaction const &); + void upgrade_v4_to_v5 (nano::transaction const &); + void upgrade_v5_to_v6 (nano::transaction const &); + void upgrade_v6_to_v7 (nano::transaction const &); + void upgrade_v7_to_v8 (nano::transaction const &); + void upgrade_v8_to_v9 (nano::transaction const &); + void upgrade_v9_to_v10 (nano::transaction const &); + void upgrade_v10_to_v11 (nano::transaction const &); + void upgrade_v11_to_v12 (nano::transaction const &); + void upgrade_v12_to_v13 (nano::transaction const &, size_t); + void upgrade_v13_to_v14 (nano::transaction const &); + MDB_dbi get_pending_db (nano::epoch epoch_a) const; }; class wallet_value { diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 761836b449..657c3e7971 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -2669,6 +2669,11 @@ void nano::node::process_confirmed (std::shared_ptr block_a, uint8_ auto hash (block_a->hash ()); if (ledger.block_exists (block_a->type (), hash)) { + { + auto transaction (store.tx_begin_write ()); + add_confirmation_heights (transaction, hash); + } + auto transaction (store.tx_begin_read ()); confirmed_visitor visitor (transaction, *this, block_a, hash); block_a->visit (visitor); @@ -3737,6 +3742,86 @@ std::unique_ptr collect_seq_con_info (active_transaction return composite; } } + +/** + * For all the blocks below this height which have been implicitly confirmed check if they + * are open/receive blocks, and if so follow the source blocks and iteratively repeat to genesis. + */ +void nano::node::add_confirmation_heights (nano::transaction const & transaction, nano::block_hash const & hash) +{ + std::stack> open_receive_blocks; + auto current = hash; + + nano::genesis genesis; + do + { + if (!open_receive_blocks.empty ()) + { + current = open_receive_blocks.top (); + open_receive_blocks.pop (); + + auto block (store.block_get (transaction, current)); + if (block != nullptr) + { + nano::block_hash source_hash = block->source (); + auto source_block (store.block_get (transaction, source_hash)); + if (source_block != nullptr) + { + current = source_block->hash (); + } + } + } + + auto hash (current); + auto block_height (store.block_account_height (transaction, hash)); + assert (block_height >= 0); + nano::account_info account_info; + nano::account account (ledger.account (transaction, hash)); + release_assert (!store.account_get (transaction, account, account_info)); + auto confirmation_height = account_info.confirmation_height; + if (block_height > confirmation_height) + { + account_info.confirmation_height = block_height; + store.account_put (transaction, account, account_info); + + // Get the difference and check if any of these are recieve blocks + auto num_confirmed_blocks = block_height - confirmation_height; + + // Start from the most recent one and work our way through + for (uint64_t i = 0; i < num_confirmed_blocks && !current.is_zero (); ++i) + { + auto block (store.block_get (transaction, current)); + if (block != nullptr) + { + // First check legacy receive/open + if (block->type () == nano::block_type::receive || (block->type () == nano::block_type::open && current != genesis.hash ())) + { + open_receive_blocks.push (block->source ()); + } + else + { + // Then check state blocks + auto state = std::dynamic_pointer_cast (block); + if (state != nullptr) + { + nano::block_hash previous (state->hashables.previous); + if (!previous.is_zero ()) + { + if (state->hashables.balance > ledger.balance (transaction, previous)) + { + open_receive_blocks.push (state->hashables.link); + } + } + } + } + + current = block->previous (); + } + } + } + } while (!open_receive_blocks.empty ()); +} + int nano::node::store_version () { auto transaction (store.tx_begin_read ()); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index b7e42d2995..39ce838510 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -529,6 +529,9 @@ class node : public std::enable_shared_from_this static std::chrono::seconds constexpr peer_interval = search_pending_interval; static std::chrono::hours constexpr unchecked_cleanup_interval = std::chrono::hours (1); static std::chrono::milliseconds constexpr process_confirmed_interval = nano::is_test_network ? std::chrono::milliseconds (50) : std::chrono::milliseconds (500); + +private: + void add_confirmation_heights (nano::transaction const & transaction, nano::block_hash const & hash); }; std::unique_ptr collect_seq_con_info (node & node, const std::string & name); diff --git a/nano/node/rpc.cpp b/nano/node/rpc.cpp index eb5aa0b94b..0c23b300d0 100644 --- a/nano/node/rpc.cpp +++ b/nano/node/rpc.cpp @@ -491,6 +491,7 @@ void nano::rpc_handler::account_info () response_l.put ("modified_timestamp", std::to_string (info.modified)); response_l.put ("block_count", std::to_string (info.block_count)); response_l.put ("account_version", info.epoch == nano::epoch::epoch_1 ? "1" : "0"); + response_l.put ("confirmation_height", std::to_string (info.confirmation_height)); if (representative) { auto block (node.store.block_get (transaction, info.rep_block)); @@ -1070,6 +1071,25 @@ void nano::rpc_handler::block_account () response_errors (); } +void nano::rpc_handler::block_confirmed () +{ + auto hash (hash_impl ()); + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + if (node.store.block_exists (transaction, hash)) + { + auto confirmed (node.ledger.block_confirmed (transaction, hash)); + response_l.put ("confirmed", confirmed); + } + else + { + ec = nano::error_blocks::not_found; + } + } + response_errors (); +} + void nano::rpc_handler::block_count () { auto transaction (node.store.tx_begin_read ()); @@ -4655,6 +4675,7 @@ rpc_handler_no_arg_func_map create_rpc_handler_no_arg_func_map () no_arg_funcs.emplace ("blocks", &nano::rpc_handler::blocks); no_arg_funcs.emplace ("blocks_info", &nano::rpc_handler::blocks_info); no_arg_funcs.emplace ("block_account", &nano::rpc_handler::block_account); + no_arg_funcs.emplace ("block_confirmed", &nano::rpc_handler::block_confirmed); no_arg_funcs.emplace ("block_count", &nano::rpc_handler::block_count); no_arg_funcs.emplace ("block_count_type", &nano::rpc_handler::block_count_type); no_arg_funcs.emplace ("block_create", &nano::rpc_handler::block_create); diff --git a/nano/node/rpc.hpp b/nano/node/rpc.hpp index ebd4e2c2ee..5fa1643cba 100644 --- a/nano/node/rpc.hpp +++ b/nano/node/rpc.hpp @@ -113,6 +113,7 @@ class rpc_handler : public std::enable_shared_from_this void blocks (); void blocks_info (); void block_account (); + void block_confirmed (); void block_count (); void block_count_type (); void block_create (); diff --git a/nano/node/testing.cpp b/nano/node/testing.cpp index 0f7b6226c6..ce33ca453f 100644 --- a/nano/node/testing.cpp +++ b/nano/node/testing.cpp @@ -178,7 +178,8 @@ void nano::system::generate_rollback (nano::node & node_a, std::vector block_get (nano::transaction const &, nano::block_hash const &, nano::block_sideband * = nullptr) = 0; + virtual std::shared_ptr block_get (nano::transaction const &, nano::block_hash const &, nano::block_sideband * = nullptr) const = 0; virtual std::shared_ptr block_random (nano::transaction const &) = 0; virtual void block_del (nano::transaction const &, nano::block_hash const &) = 0; virtual bool block_exists (nano::transaction const &, nano::block_hash const &) = 0; @@ -219,10 +219,10 @@ class block_store virtual nano::block_counts block_count (nano::transaction const &) = 0; virtual bool root_exists (nano::transaction const &, nano::uint256_union const &) = 0; virtual bool source_exists (nano::transaction const &, nano::block_hash const &) = 0; - virtual nano::account block_account (nano::transaction const &, nano::block_hash const &) = 0; + virtual nano::account block_account (nano::transaction const &, nano::block_hash const &) const = 0; virtual void frontier_put (nano::transaction const &, nano::block_hash const &, nano::account const &) = 0; - virtual nano::account frontier_get (nano::transaction const &, nano::block_hash const &) = 0; + virtual nano::account frontier_get (nano::transaction const &, nano::block_hash const &) const = 0; virtual void frontier_del (nano::transaction const &, nano::block_hash const &) = 0; virtual void account_put (nano::transaction const &, nano::account const &, nano::account_info const &) = 0; @@ -230,6 +230,8 @@ class block_store virtual void account_del (nano::transaction const &, nano::account const &) = 0; virtual bool account_exists (nano::transaction const &, nano::account const &) = 0; virtual size_t account_count (nano::transaction const &) = 0; + virtual void confirmation_height_clear (nano::transaction const &, nano::account const & account, nano::account_info const & account_info) = 0; + virtual void confirmation_height_clear (nano::transaction const &) = 0; virtual nano::store_iterator latest_v0_begin (nano::transaction const &, nano::account const &) = 0; virtual nano::store_iterator latest_v0_begin (nano::transaction const &) = 0; virtual nano::store_iterator latest_v0_end () = 0; @@ -254,7 +256,7 @@ class block_store virtual nano::store_iterator pending_begin (nano::transaction const &) = 0; virtual nano::store_iterator pending_end () = 0; - virtual bool block_info_get (nano::transaction const &, nano::block_hash const &, nano::block_info &) = 0; + virtual bool block_info_get (nano::transaction const &, nano::block_hash const &, nano::block_info &) const = 0; virtual nano::uint128_t block_balance (nano::transaction const &, nano::block_hash const &) = 0; virtual nano::epoch block_version (nano::transaction const &, nano::block_hash const &) = 0; @@ -268,7 +270,6 @@ class block_store virtual void unchecked_put (nano::transaction const &, nano::unchecked_key const &, nano::unchecked_info const &) = 0; virtual void unchecked_put (nano::transaction const &, nano::block_hash const &, std::shared_ptr const &) = 0; virtual std::vector unchecked_get (nano::transaction const &, nano::block_hash const &) = 0; - virtual bool unchecked_exists (nano::transaction const &, nano::unchecked_key const &) = 0; virtual void unchecked_del (nano::transaction const &, nano::unchecked_key const &) = 0; virtual nano::store_iterator unchecked_begin (nano::transaction const &) = 0; virtual nano::store_iterator unchecked_begin (nano::transaction const &, nano::unchecked_key const &) = 0; @@ -296,7 +297,7 @@ class block_store virtual void online_weight_clear (nano::transaction const &) = 0; virtual void version_put (nano::transaction const &, int) = 0; - virtual int version_get (nano::transaction const &) = 0; + virtual int version_get (nano::transaction const &) const = 0; virtual void peer_put (nano::transaction const & transaction_a, nano::endpoint_key const & endpoint_a) = 0; virtual void peer_del (nano::transaction const & transaction_a, nano::endpoint_key const & endpoint_a) = 0; @@ -306,8 +307,10 @@ class block_store virtual nano::store_iterator peers_begin (nano::transaction const & transaction_a) = 0; virtual nano::store_iterator peers_end () = 0; + virtual uint64_t block_account_height (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const = 0; + // Requires a write transaction - virtual nano::raw_key get_node_id (nano::transaction const &) = 0; + virtual nano::raw_key get_node_id (nano::transaction const &) const = 0; /** Deletes the node ID from the store */ virtual void delete_node_id (nano::transaction const &) = 0; diff --git a/nano/secure/common.cpp b/nano/secure/common.cpp index ab4b091d46..f345dc9b7c 100644 --- a/nano/secure/common.cpp +++ b/nano/secure/common.cpp @@ -150,13 +150,14 @@ void nano::serialize_block (nano::stream & stream_a, nano::block const & block_a block_a.serialize (stream_a); } -nano::account_info::account_info (nano::block_hash const & head_a, nano::block_hash const & rep_block_a, nano::block_hash const & open_block_a, nano::amount const & balance_a, uint64_t modified_a, uint64_t block_count_a, nano::epoch epoch_a) : +nano::account_info::account_info (nano::block_hash const & head_a, nano::block_hash const & rep_block_a, nano::block_hash const & open_block_a, nano::amount const & balance_a, uint64_t modified_a, uint64_t block_count_a, uint64_t confirmation_height_a, nano::epoch epoch_a) : head (head_a), rep_block (rep_block_a), open_block (open_block_a), balance (balance_a), modified (modified_a), block_count (block_count_a), +confirmation_height (confirmation_height_a), epoch (epoch_a) { } @@ -172,6 +173,7 @@ bool nano::account_info::deserialize (nano::stream & stream_a) nano::read (stream_a, balance.bytes); nano::read (stream_a, modified); nano::read (stream_a, block_count); + nano::read (stream_a, confirmation_height); } catch (std::runtime_error const &) { @@ -183,7 +185,7 @@ bool nano::account_info::deserialize (nano::stream & stream_a) bool nano::account_info::operator== (nano::account_info const & other_a) const { - return head == other_a.head && rep_block == other_a.rep_block && open_block == other_a.open_block && balance == other_a.balance && modified == other_a.modified && block_count == other_a.block_count && epoch == other_a.epoch; + return head == other_a.head && rep_block == other_a.rep_block && open_block == other_a.open_block && balance == other_a.balance && modified == other_a.modified && block_count == other_a.block_count && confirmation_height == other_a.confirmation_height && epoch == other_a.epoch; } bool nano::account_info::operator!= (nano::account_info const & other_a) const @@ -199,7 +201,8 @@ size_t nano::account_info::db_size () const assert (reinterpret_cast (&open_block) + sizeof (open_block) == reinterpret_cast (&balance)); assert (reinterpret_cast (&balance) + sizeof (balance) == reinterpret_cast (&modified)); assert (reinterpret_cast (&modified) + sizeof (modified) == reinterpret_cast (&block_count)); - return sizeof (head) + sizeof (rep_block) + sizeof (open_block) + sizeof (balance) + sizeof (modified) + sizeof (block_count); + assert (reinterpret_cast (&block_count) + sizeof (block_count) == reinterpret_cast (&confirmation_height)); + return sizeof (head) + sizeof (rep_block) + sizeof (open_block) + sizeof (balance) + sizeof (modified) + sizeof (block_count) + sizeof (confirmation_height); } size_t nano::block_counts::sum () const diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index df33359a91..357a99d97f 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -79,7 +79,7 @@ class account_info { public: account_info () = default; - account_info (nano::block_hash const &, nano::block_hash const &, nano::block_hash const &, nano::amount const &, uint64_t, uint64_t, epoch); + account_info (nano::block_hash const &, nano::block_hash const &, nano::block_hash const &, nano::amount const &, uint64_t, uint64_t, uint64_t, epoch); bool deserialize (nano::stream &); bool operator== (nano::account_info const &) const; bool operator!= (nano::account_info const &) const; @@ -91,6 +91,7 @@ class account_info /** Seconds since posix epoch */ uint64_t modified{ 0 }; uint64_t block_count{ 0 }; + uint64_t confirmation_height{ 0 }; nano::epoch epoch{ nano::epoch::epoch_0 }; }; diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 123f907657..a0e04ac5f2 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -23,21 +23,24 @@ class rollback_visitor : public nano::block_visitor auto hash (block_a.hash ()); nano::pending_info pending; nano::pending_key key (block_a.hashables.destination, hash); - while (ledger.store.pending_get (transaction, key, pending)) + while (!error && ledger.store.pending_get (transaction, key, pending)) { - ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.destination), list); + error = ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.destination), list); + } + if (!error) + { + nano::account_info info; + auto error (ledger.store.account_get (transaction, pending.source, info)); + assert (!error); + ledger.store.pending_del (transaction, key); + ledger.store.representation_add (transaction, ledger.representative (transaction, hash), pending.amount.number ()); + ledger.change_latest (transaction, pending.source, block_a.hashables.previous, info.rep_block, ledger.balance (transaction, block_a.hashables.previous), info.block_count - 1); + ledger.store.block_del (transaction, hash); + ledger.store.frontier_del (transaction, hash); + ledger.store.frontier_put (transaction, block_a.hashables.previous, pending.source); + ledger.store.block_successor_clear (transaction, block_a.hashables.previous); + ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::send); } - nano::account_info info; - auto error (ledger.store.account_get (transaction, pending.source, info)); - assert (!error); - ledger.store.pending_del (transaction, key); - ledger.store.representation_add (transaction, ledger.representative (transaction, hash), pending.amount.number ()); - ledger.change_latest (transaction, pending.source, block_a.hashables.previous, info.rep_block, ledger.balance (transaction, block_a.hashables.previous), info.block_count - 1); - ledger.store.block_del (transaction, hash); - ledger.store.frontier_del (transaction, hash); - ledger.store.frontier_put (transaction, block_a.hashables.previous, pending.source); - ledger.store.block_successor_clear (transaction, block_a.hashables.previous); - ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::send); } void receive_block (nano::receive_block const & block_a) override { @@ -113,9 +116,9 @@ class rollback_visitor : public nano::block_visitor if (is_send) { nano::pending_key key (block_a.hashables.link, hash); - while (!ledger.store.pending_exists (transaction, key)) + while (!error && !ledger.store.pending_exists (transaction, key)) { - ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.link), list); + error = ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.link), list); } ledger.store.pending_del (transaction, key); ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::send); @@ -150,6 +153,7 @@ class rollback_visitor : public nano::block_visitor nano::transaction const & transaction; nano::ledger & ledger; std::vector & list; + bool error{ false }; }; class ledger_processor : public nano::block_visitor @@ -666,7 +670,7 @@ epoch_signer (epoch_signer_a) } // Balance for account containing hash -nano::uint128_t nano::ledger::balance (nano::transaction const & transaction_a, nano::block_hash const & hash_a) +nano::uint128_t nano::ledger::balance (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const { return hash_a.is_zero () ? 0 : store.block_balance (transaction_a, hash_a); } @@ -753,7 +757,7 @@ std::string nano::ledger::block_text (nano::block_hash const & hash_a) return result; } -bool nano::ledger::is_send (nano::transaction const & transaction_a, nano::state_block const & block_a) +bool nano::ledger::is_send (nano::transaction const & transaction_a, nano::state_block const & block_a) const { bool result (false); nano::block_hash previous (block_a.hashables.previous); @@ -825,31 +829,42 @@ nano::uint128_t nano::ledger::weight (nano::transaction const & transaction_a, n return store.representation_get (transaction_a, account_a); } -// Rollback blocks until `block_a' doesn't exist -void nano::ledger::rollback (nano::transaction const & transaction_a, nano::block_hash const & block_a, std::vector & list_a) +// Rollback blocks until `block_a' doesn't exist or it tries to penetrate the confirmation height +bool nano::ledger::rollback (nano::transaction const & transaction_a, nano::block_hash const & block_a, std::vector & list_a) { assert (store.block_exists (transaction_a, block_a)); auto account_l (account (transaction_a, block_a)); + auto block_account_height (store.block_account_height (transaction_a, block_a)); rollback_visitor rollback (transaction_a, *this, list_a); - nano::account_info info; - while (store.block_exists (transaction_a, block_a)) + nano::account_info account_info; + auto error (false); + while (!error && store.block_exists (transaction_a, block_a)) { - auto latest_error (store.account_get (transaction_a, account_l, info)); + auto latest_error (store.account_get (transaction_a, account_l, account_info)); assert (!latest_error); - auto block (store.block_get (transaction_a, info.head)); - list_a.push_back (info.head); - block->visit (rollback); + if (block_account_height > account_info.confirmation_height) + { + auto block (store.block_get (transaction_a, account_info.head)); + list_a.push_back (account_info.head); + block->visit (rollback); + error = rollback.error; + } + else + { + error = true; + } } + return error; } -void nano::ledger::rollback (nano::transaction const & transaction_a, nano::block_hash const & block_a) +bool nano::ledger::rollback (nano::transaction const & transaction_a, nano::block_hash const & block_a) { std::vector rollback_list; - rollback (transaction_a, block_a, rollback_list); + return rollback (transaction_a, block_a, rollback_list); } // Return account containing hash -nano::account nano::ledger::account (nano::transaction const & transaction_a, nano::block_hash const & hash_a) +nano::account nano::ledger::account (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const { return store.block_account (transaction_a, hash_a); } @@ -1031,6 +1046,20 @@ std::shared_ptr nano::ledger::forked_block (nano::transaction const return result; } +bool nano::ledger::block_confirmed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const +{ + auto confirmed (false); + auto block_height (store.block_account_height (transaction_a, hash_a)); + if (block_height) // 0 indicates that the block doesn't exist + { + auto account_l (account (transaction_a, hash_a)); + nano::account_info account_info; + release_assert (!store.account_get (transaction_a, account_l, account_info)); + confirmed = (account_info.confirmation_height >= block_height); + } + return confirmed; +} + namespace nano { std::unique_ptr collect_seq_con_info (ledger & ledger, const std::string & name) diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 1fca677d22..61b0b30994 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -18,14 +18,15 @@ class ledger { public: ledger (nano::block_store &, nano::stat &, nano::uint256_union const & = 1, nano::account const & = 0); - nano::account account (nano::transaction const &, nano::block_hash const &); + nano::account account (nano::transaction const &, nano::block_hash const &) const; nano::uint128_t amount (nano::transaction const &, nano::block_hash const &); - nano::uint128_t balance (nano::transaction const &, nano::block_hash const &); + nano::uint128_t balance (nano::transaction const &, nano::block_hash const &) const; nano::uint128_t account_balance (nano::transaction const &, nano::account const &); nano::uint128_t account_pending (nano::transaction const &, nano::account const &); nano::uint128_t weight (nano::transaction const &, nano::account const &); std::shared_ptr successor (nano::transaction const &, nano::uint512_union const &); std::shared_ptr forked_block (nano::transaction const &, nano::block const &); + bool block_confirmed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const; nano::block_hash latest (nano::transaction const &, nano::account const &); nano::block_hash latest_root (nano::transaction const &, nano::account const &); nano::block_hash representative (nano::transaction const &, nano::block_hash const &); @@ -34,12 +35,12 @@ class ledger bool block_exists (nano::block_type, nano::block_hash const &); std::string block_text (char const *); std::string block_text (nano::block_hash const &); - bool is_send (nano::transaction const &, nano::state_block const &); + bool is_send (nano::transaction const &, nano::state_block const &) const; nano::block_hash block_destination (nano::transaction const &, nano::block const &); nano::block_hash block_source (nano::transaction const &, nano::block const &); nano::process_return process (nano::transaction const &, nano::block const &, nano::signature_verification = nano::signature_verification::unknown); - void rollback (nano::transaction const &, nano::block_hash const &, std::vector &); - void rollback (nano::transaction const &, nano::block_hash const &); + bool rollback (nano::transaction const &, nano::block_hash const &, std::vector &); + bool rollback (nano::transaction const &, nano::block_hash const &); void change_latest (nano::transaction const &, nano::account const &, nano::block_hash const &, nano::account const &, nano::uint128_union const &, uint64_t, bool = false, nano::epoch = nano::epoch::epoch_0); void dump_account_chain (nano::account const &); bool could_fit (nano::transaction const &, nano::block const &); diff --git a/nano/secure/versioning.cpp b/nano/secure/versioning.cpp index 5c58094e24..5c2a3059d8 100644 --- a/nano/secure/versioning.cpp +++ b/nano/secure/versioning.cpp @@ -59,3 +59,25 @@ nano::mdb_val nano::account_info_v5::val () const { return nano::mdb_val (sizeof (*this), const_cast (this)); } + +nano::account_info_v13::account_info_v13 (nano::block_hash const & head_a, nano::block_hash const & rep_block_a, nano::block_hash const & open_block_a, nano::amount const & balance_a, uint64_t modified_a, uint64_t block_count_a, nano::epoch epoch_a) : +head (head_a), +rep_block (rep_block_a), +open_block (open_block_a), +balance (balance_a), +modified (modified_a), +block_count (block_count_a), +epoch (epoch_a) +{ +} + +size_t nano::account_info_v13::db_size () const +{ + assert (reinterpret_cast (this) == reinterpret_cast (&head)); + assert (reinterpret_cast (&head) + sizeof (head) == reinterpret_cast (&rep_block)); + assert (reinterpret_cast (&rep_block) + sizeof (rep_block) == reinterpret_cast (&open_block)); + assert (reinterpret_cast (&open_block) + sizeof (open_block) == reinterpret_cast (&balance)); + assert (reinterpret_cast (&balance) + sizeof (balance) == reinterpret_cast (&modified)); + assert (reinterpret_cast (&modified) + sizeof (modified) == reinterpret_cast (&block_count)); + return sizeof (head) + sizeof (rep_block) + sizeof (open_block) + sizeof (balance) + sizeof (modified) + sizeof (block_count); +} diff --git a/nano/secure/versioning.hpp b/nano/secure/versioning.hpp index 2d0b87c43c..7fcf30de2b 100644 --- a/nano/secure/versioning.hpp +++ b/nano/secure/versioning.hpp @@ -29,7 +29,6 @@ class pending_info_v3 nano::amount amount{ 0 }; nano::account destination{ 0 }; }; -// Latest information about an account class account_info_v5 { public: @@ -43,4 +42,18 @@ class account_info_v5 nano::amount balance{ 0 }; uint64_t modified{ 0 }; }; +class account_info_v13 +{ +public: + account_info_v13 () = default; + account_info_v13 (nano::block_hash const &, nano::block_hash const &, nano::block_hash const &, nano::amount const &, uint64_t, uint64_t block_count, nano::epoch epoch_a); + size_t db_size () const; + nano::block_hash head{ 0 }; + nano::block_hash rep_block{ 0 }; + nano::block_hash open_block{ 0 }; + nano::amount balance{ 0 }; + uint64_t modified{ 0 }; + uint64_t block_count{ 0 }; + nano::epoch epoch{ nano::epoch::epoch_0 }; +}; }