* Move `test::get_available_port` to `test::system` class * Use ephemeral ports in tests * Fix database directories for core tests * Fix rpc tests * Use `SO_REUSEADDR` * Fix `ipv6_bind_send_ipv4` * Use 0 to bind to any port by default * Fix `network` tests * Fix `socket` tests * Fix `ipc` tests * Fix remaining tests * Raise the file limit soft-cap when running tests in parallel. * Importing gtest-parallel as a submodule. --------- Co-authored-by: Colin LeMahieu <clemahieu@gmail.com>
2580 lines
99 KiB
C++
2580 lines
99 KiB
C++
#include <nano/crypto_lib/random_pool.hpp>
|
|
#include <nano/lib/lmdbconfig.hpp>
|
|
#include <nano/lib/logger_mt.hpp>
|
|
#include <nano/lib/stats.hpp>
|
|
#include <nano/lib/utility.hpp>
|
|
#include <nano/lib/work.hpp>
|
|
#include <nano/node/common.hpp>
|
|
#include <nano/node/lmdb/lmdb.hpp>
|
|
#include <nano/node/rocksdb/rocksdb.hpp>
|
|
#include <nano/secure/ledger.hpp>
|
|
#include <nano/secure/utility.hpp>
|
|
#include <nano/secure/versioning.hpp>
|
|
#include <nano/test_common/system.hpp>
|
|
#include <nano/test_common/testutil.hpp>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
namespace nano
|
|
{
|
|
namespace lmdb
|
|
{
|
|
void modify_account_info_to_v14 (nano::lmdb::store & store, nano::transaction const & transaction_a, nano::account const & account_a, uint64_t confirmation_height, nano::block_hash const & rep_block);
|
|
void modify_confirmation_height_to_v15 (nano::lmdb::store & store, nano::transaction const & transaction, nano::account const & account, uint64_t confirmation_height);
|
|
void write_sideband_v14 (nano::lmdb::store & store_a, nano::transaction & transaction_a, nano::block const & block_a, MDB_dbi db_a);
|
|
void write_sideband_v15 (nano::lmdb::store & store_a, nano::transaction & transaction_a, nano::block const & block_a);
|
|
void write_block_w_sideband_v18 (nano::lmdb::store & store_a, MDB_dbi database, nano::write_transaction & transaction_a, nano::block const & block_a);
|
|
}
|
|
}
|
|
|
|
TEST (block_store, construction)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
}
|
|
|
|
TEST (block_store, block_details)
|
|
{
|
|
nano::block_details details_send (nano::epoch::epoch_0, true, false, false);
|
|
ASSERT_TRUE (details_send.is_send);
|
|
ASSERT_FALSE (details_send.is_receive);
|
|
ASSERT_FALSE (details_send.is_epoch);
|
|
ASSERT_EQ (nano::epoch::epoch_0, details_send.epoch);
|
|
|
|
nano::block_details details_receive (nano::epoch::epoch_1, false, true, false);
|
|
ASSERT_FALSE (details_receive.is_send);
|
|
ASSERT_TRUE (details_receive.is_receive);
|
|
ASSERT_FALSE (details_receive.is_epoch);
|
|
ASSERT_EQ (nano::epoch::epoch_1, details_receive.epoch);
|
|
|
|
nano::block_details details_epoch (nano::epoch::epoch_2, false, false, true);
|
|
ASSERT_FALSE (details_epoch.is_send);
|
|
ASSERT_FALSE (details_epoch.is_receive);
|
|
ASSERT_TRUE (details_epoch.is_epoch);
|
|
ASSERT_EQ (nano::epoch::epoch_2, details_epoch.epoch);
|
|
|
|
nano::block_details details_none (nano::epoch::unspecified, false, false, false);
|
|
ASSERT_FALSE (details_none.is_send);
|
|
ASSERT_FALSE (details_none.is_receive);
|
|
ASSERT_FALSE (details_none.is_epoch);
|
|
ASSERT_EQ (nano::epoch::unspecified, details_none.epoch);
|
|
}
|
|
|
|
TEST (block_store, block_details_serialization)
|
|
{
|
|
nano::block_details details1;
|
|
details1.epoch = nano::epoch::epoch_2;
|
|
details1.is_epoch = false;
|
|
details1.is_receive = true;
|
|
details1.is_send = false;
|
|
std::vector<uint8_t> vector;
|
|
{
|
|
nano::vectorstream stream1 (vector);
|
|
details1.serialize (stream1);
|
|
}
|
|
nano::bufferstream stream2 (vector.data (), vector.size ());
|
|
nano::block_details details2;
|
|
ASSERT_FALSE (details2.deserialize (stream2));
|
|
ASSERT_EQ (details1, details2);
|
|
}
|
|
|
|
TEST (block_store, sideband_serialization)
|
|
{
|
|
nano::block_sideband sideband1;
|
|
sideband1.account = 1;
|
|
sideband1.balance = 2;
|
|
sideband1.height = 3;
|
|
sideband1.successor = 4;
|
|
sideband1.timestamp = 5;
|
|
std::vector<uint8_t> vector;
|
|
{
|
|
nano::vectorstream stream1 (vector);
|
|
sideband1.serialize (stream1, nano::block_type::receive);
|
|
}
|
|
nano::bufferstream stream2 (vector.data (), vector.size ());
|
|
nano::block_sideband sideband2;
|
|
ASSERT_FALSE (sideband2.deserialize (stream2, nano::block_type::receive));
|
|
ASSERT_EQ (sideband1.account, sideband2.account);
|
|
ASSERT_EQ (sideband1.balance, sideband2.balance);
|
|
ASSERT_EQ (sideband1.height, sideband2.height);
|
|
ASSERT_EQ (sideband1.successor, sideband2.successor);
|
|
ASSERT_EQ (sideband1.timestamp, sideband2.timestamp);
|
|
}
|
|
|
|
TEST (block_store, add_item)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::block_builder builder;
|
|
auto block = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block->sideband_set ({});
|
|
auto hash1 (block->hash ());
|
|
auto transaction (store->tx_begin_write ());
|
|
auto latest1 (store->block.get (transaction, hash1));
|
|
ASSERT_EQ (nullptr, latest1);
|
|
ASSERT_FALSE (store->block.exists (transaction, hash1));
|
|
store->block.put (transaction, hash1, *block);
|
|
auto latest2 (store->block.get (transaction, hash1));
|
|
ASSERT_NE (nullptr, latest2);
|
|
ASSERT_EQ (*block, *latest2);
|
|
ASSERT_TRUE (store->block.exists (transaction, hash1));
|
|
ASSERT_FALSE (store->block.exists (transaction, hash1.number () - 1));
|
|
store->block.del (transaction, hash1);
|
|
auto latest3 (store->block.get (transaction, hash1));
|
|
ASSERT_EQ (nullptr, latest3);
|
|
}
|
|
|
|
TEST (block_store, clear_successor)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::block_builder builder;
|
|
auto block1 = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block1->sideband_set ({});
|
|
auto transaction (store->tx_begin_write ());
|
|
store->block.put (transaction, block1->hash (), *block1);
|
|
auto block2 = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (2)
|
|
.account (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block2->sideband_set ({});
|
|
store->block.put (transaction, block2->hash (), *block2);
|
|
auto block2_store (store->block.get (transaction, block1->hash ()));
|
|
ASSERT_NE (nullptr, block2_store);
|
|
ASSERT_EQ (0, block2_store->sideband ().successor.number ());
|
|
auto modified_sideband = block2_store->sideband ();
|
|
modified_sideband.successor = block2->hash ();
|
|
block1->sideband_set (modified_sideband);
|
|
store->block.put (transaction, block1->hash (), *block1);
|
|
{
|
|
auto block1_store (store->block.get (transaction, block1->hash ()));
|
|
ASSERT_NE (nullptr, block1_store);
|
|
ASSERT_EQ (block2->hash (), block1_store->sideband ().successor);
|
|
}
|
|
store->block.successor_clear (transaction, block1->hash ());
|
|
{
|
|
auto block1_store (store->block.get (transaction, block1->hash ()));
|
|
ASSERT_NE (nullptr, block1_store);
|
|
ASSERT_EQ (0, block1_store->sideband ().successor.number ());
|
|
}
|
|
}
|
|
|
|
TEST (block_store, add_nonempty_block)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::keypair key1;
|
|
nano::block_builder builder;
|
|
auto block = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block->sideband_set ({});
|
|
auto hash1 (block->hash ());
|
|
block->signature = nano::sign_message (key1.prv, key1.pub, hash1);
|
|
auto transaction (store->tx_begin_write ());
|
|
auto latest1 (store->block.get (transaction, hash1));
|
|
ASSERT_EQ (nullptr, latest1);
|
|
store->block.put (transaction, hash1, *block);
|
|
auto latest2 (store->block.get (transaction, hash1));
|
|
ASSERT_NE (nullptr, latest2);
|
|
ASSERT_EQ (*block, *latest2);
|
|
}
|
|
|
|
TEST (block_store, add_two_items)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::keypair key1;
|
|
nano::block_builder builder;
|
|
auto block = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (1)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block->sideband_set ({});
|
|
auto hash1 (block->hash ());
|
|
block->signature = nano::sign_message (key1.prv, key1.pub, hash1);
|
|
auto transaction (store->tx_begin_write ());
|
|
auto latest1 (store->block.get (transaction, hash1));
|
|
ASSERT_EQ (nullptr, latest1);
|
|
auto block2 = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (3)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block2->sideband_set ({});
|
|
block2->hashables.account = 3;
|
|
auto hash2 (block2->hash ());
|
|
block2->signature = nano::sign_message (key1.prv, key1.pub, hash2);
|
|
auto latest2 (store->block.get (transaction, hash2));
|
|
ASSERT_EQ (nullptr, latest2);
|
|
store->block.put (transaction, hash1, *block);
|
|
store->block.put (transaction, hash2, *block2);
|
|
auto latest3 (store->block.get (transaction, hash1));
|
|
ASSERT_NE (nullptr, latest3);
|
|
ASSERT_EQ (*block, *latest3);
|
|
auto latest4 (store->block.get (transaction, hash2));
|
|
ASSERT_NE (nullptr, latest4);
|
|
ASSERT_EQ (*block2, *latest4);
|
|
ASSERT_FALSE (*latest3 == *latest4);
|
|
}
|
|
|
|
TEST (block_store, add_receive)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::keypair key1;
|
|
nano::keypair key2;
|
|
nano::block_builder builder;
|
|
auto block1 = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block1->sideband_set ({});
|
|
auto transaction (store->tx_begin_write ());
|
|
store->block.put (transaction, block1->hash (), *block1);
|
|
auto block = builder
|
|
.receive ()
|
|
.previous (block1->hash ())
|
|
.source (1)
|
|
.sign (nano::keypair ().prv, 2)
|
|
.work (3)
|
|
.build ();
|
|
block->sideband_set ({});
|
|
nano::block_hash hash1 (block->hash ());
|
|
auto latest1 (store->block.get (transaction, hash1));
|
|
ASSERT_EQ (nullptr, latest1);
|
|
store->block.put (transaction, hash1, *block);
|
|
auto latest2 (store->block.get (transaction, hash1));
|
|
ASSERT_NE (nullptr, latest2);
|
|
ASSERT_EQ (*block, *latest2);
|
|
}
|
|
|
|
TEST (block_store, add_pending)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::keypair key1;
|
|
nano::pending_key key2 (0, 0);
|
|
nano::pending_info pending1;
|
|
auto transaction (store->tx_begin_write ());
|
|
ASSERT_TRUE (store->pending.get (transaction, key2, pending1));
|
|
store->pending.put (transaction, key2, pending1);
|
|
nano::pending_info pending2;
|
|
ASSERT_FALSE (store->pending.get (transaction, key2, pending2));
|
|
ASSERT_EQ (pending1, pending2);
|
|
store->pending.del (transaction, key2);
|
|
ASSERT_TRUE (store->pending.get (transaction, key2, pending2));
|
|
}
|
|
|
|
TEST (block_store, pending_iterator)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
auto transaction (store->tx_begin_write ());
|
|
ASSERT_EQ (store->pending.end (), store->pending.begin (transaction));
|
|
store->pending.put (transaction, nano::pending_key (1, 2), { 2, 3, nano::epoch::epoch_1 });
|
|
auto current (store->pending.begin (transaction));
|
|
ASSERT_NE (store->pending.end (), current);
|
|
nano::pending_key key1 (current->first);
|
|
ASSERT_EQ (nano::account (1), key1.account);
|
|
ASSERT_EQ (nano::block_hash (2), key1.hash);
|
|
nano::pending_info pending (current->second);
|
|
ASSERT_EQ (nano::account (2), pending.source);
|
|
ASSERT_EQ (nano::amount (3), pending.amount);
|
|
ASSERT_EQ (nano::epoch::epoch_1, pending.epoch);
|
|
}
|
|
|
|
/**
|
|
* Regression test for Issue 1164
|
|
* This reconstructs the situation where a key is larger in pending than the account being iterated in pending_v1, leaving
|
|
* iteration order up to the value, causing undefined behavior.
|
|
* After the bugfix, the value is compared only if the keys are equal.
|
|
*/
|
|
TEST (block_store, pending_iterator_comparison)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::stats stats;
|
|
auto transaction (store->tx_begin_write ());
|
|
// Populate pending
|
|
store->pending.put (transaction, nano::pending_key (nano::account (3), nano::block_hash (1)), nano::pending_info (nano::account (10), nano::amount (1), nano::epoch::epoch_0));
|
|
store->pending.put (transaction, nano::pending_key (nano::account (3), nano::block_hash (4)), nano::pending_info (nano::account (10), nano::amount (0), nano::epoch::epoch_0));
|
|
// Populate pending_v1
|
|
store->pending.put (transaction, nano::pending_key (nano::account (2), nano::block_hash (2)), nano::pending_info (nano::account (10), nano::amount (2), nano::epoch::epoch_1));
|
|
store->pending.put (transaction, nano::pending_key (nano::account (2), nano::block_hash (3)), nano::pending_info (nano::account (10), nano::amount (3), nano::epoch::epoch_1));
|
|
|
|
// Iterate account 3 (pending)
|
|
{
|
|
size_t count = 0;
|
|
nano::account begin (3);
|
|
nano::account end (begin.number () + 1);
|
|
for (auto i (store->pending.begin (transaction, nano::pending_key (begin, 0))), n (store->pending.begin (transaction, nano::pending_key (end, 0))); i != n; ++i, ++count)
|
|
{
|
|
nano::pending_key key (i->first);
|
|
ASSERT_EQ (key.account, begin);
|
|
ASSERT_LT (count, 3);
|
|
}
|
|
ASSERT_EQ (count, 2);
|
|
}
|
|
|
|
// Iterate account 2 (pending_v1)
|
|
{
|
|
size_t count = 0;
|
|
nano::account begin (2);
|
|
nano::account end (begin.number () + 1);
|
|
for (auto i (store->pending.begin (transaction, nano::pending_key (begin, 0))), n (store->pending.begin (transaction, nano::pending_key (end, 0))); i != n; ++i, ++count)
|
|
{
|
|
nano::pending_key key (i->first);
|
|
ASSERT_EQ (key.account, begin);
|
|
ASSERT_LT (count, 3);
|
|
}
|
|
ASSERT_EQ (count, 2);
|
|
}
|
|
}
|
|
|
|
TEST (block_store, genesis)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::ledger_cache ledger_cache;
|
|
auto transaction (store->tx_begin_write ());
|
|
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
|
nano::account_info info;
|
|
ASSERT_FALSE (store->account.get (transaction, nano::dev::genesis->account (), info));
|
|
ASSERT_EQ (nano::dev::genesis->hash (), info.head);
|
|
auto block1 (store->block.get (transaction, info.head));
|
|
ASSERT_NE (nullptr, block1);
|
|
auto receive1 (dynamic_cast<nano::open_block *> (block1.get ()));
|
|
ASSERT_NE (nullptr, receive1);
|
|
ASSERT_LE (info.modified, nano::seconds_since_epoch ());
|
|
ASSERT_EQ (info.block_count, 1);
|
|
// Genesis block should be confirmed by default
|
|
nano::confirmation_height_info confirmation_height_info;
|
|
ASSERT_FALSE (store->confirmation_height.get (transaction, nano::dev::genesis->account (), confirmation_height_info));
|
|
ASSERT_EQ (confirmation_height_info.height, 1);
|
|
ASSERT_EQ (confirmation_height_info.frontier, nano::dev::genesis->hash ());
|
|
auto dev_pub_text (nano::dev::genesis_key.pub.to_string ());
|
|
auto dev_pub_account (nano::dev::genesis_key.pub.to_account ());
|
|
auto dev_prv_text (nano::dev::genesis_key.prv.to_string ());
|
|
ASSERT_EQ (nano::dev::genesis->account (), nano::dev::genesis_key.pub);
|
|
}
|
|
|
|
TEST (block_store, empty_accounts)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
auto transaction (store->tx_begin_read ());
|
|
auto begin (store->account.begin (transaction));
|
|
auto end (store->account.end ());
|
|
ASSERT_EQ (end, begin);
|
|
}
|
|
|
|
TEST (block_store, one_block)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::block_builder builder;
|
|
auto block1 = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block1->sideband_set ({});
|
|
auto transaction (store->tx_begin_write ());
|
|
store->block.put (transaction, block1->hash (), *block1);
|
|
ASSERT_TRUE (store->block.exists (transaction, block1->hash ()));
|
|
}
|
|
|
|
TEST (block_store, empty_bootstrap)
|
|
{
|
|
nano::test::system system{};
|
|
nano::logger_mt logger;
|
|
nano::unchecked_map unchecked{ system.stats, false };
|
|
size_t count = 0;
|
|
unchecked.for_each ([&count] (nano::unchecked_key const & key, nano::unchecked_info const & info) {
|
|
++count;
|
|
});
|
|
ASSERT_EQ (count, 0);
|
|
}
|
|
|
|
TEST (block_store, unchecked_begin_search)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::keypair key0;
|
|
nano::block_builder builder;
|
|
auto block1 = builder
|
|
.send ()
|
|
.previous (0)
|
|
.destination (1)
|
|
.balance (2)
|
|
.sign (key0.prv, key0.pub)
|
|
.work (3)
|
|
.build ();
|
|
auto block2 = builder
|
|
.send ()
|
|
.previous (5)
|
|
.destination (6)
|
|
.balance (7)
|
|
.sign (key0.prv, key0.pub)
|
|
.work (8)
|
|
.build ();
|
|
}
|
|
|
|
TEST (block_store, frontier_retrieval)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::account account1{};
|
|
nano::account_info info1 (0, 0, 0, 0, 0, 0, nano::epoch::epoch_0);
|
|
auto transaction (store->tx_begin_write ());
|
|
store->confirmation_height.put (transaction, account1, { 0, nano::block_hash (0) });
|
|
store->account.put (transaction, account1, info1);
|
|
nano::account_info info2;
|
|
store->account.get (transaction, account1, info2);
|
|
ASSERT_EQ (info1, info2);
|
|
}
|
|
|
|
TEST (block_store, one_account)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::account account{};
|
|
nano::block_hash hash (0);
|
|
auto transaction (store->tx_begin_write ());
|
|
store->confirmation_height.put (transaction, account, { 20, nano::block_hash (15) });
|
|
store->account.put (transaction, account, { hash, account, hash, 42, 100, 200, nano::epoch::epoch_0 });
|
|
auto begin (store->account.begin (transaction));
|
|
auto end (store->account.end ());
|
|
ASSERT_NE (end, begin);
|
|
ASSERT_EQ (account, nano::account (begin->first));
|
|
nano::account_info info (begin->second);
|
|
ASSERT_EQ (hash, info.head);
|
|
ASSERT_EQ (42, info.balance.number ());
|
|
ASSERT_EQ (100, info.modified);
|
|
ASSERT_EQ (200, info.block_count);
|
|
nano::confirmation_height_info confirmation_height_info;
|
|
ASSERT_FALSE (store->confirmation_height.get (transaction, account, confirmation_height_info));
|
|
ASSERT_EQ (20, confirmation_height_info.height);
|
|
ASSERT_EQ (nano::block_hash (15), confirmation_height_info.frontier);
|
|
++begin;
|
|
ASSERT_EQ (end, begin);
|
|
}
|
|
|
|
TEST (block_store, two_block)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::block_builder builder;
|
|
auto block1 = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (1)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block1->sideband_set ({});
|
|
block1->hashables.account = 1;
|
|
std::vector<nano::block_hash> hashes;
|
|
std::vector<nano::open_block> blocks;
|
|
hashes.push_back (block1->hash ());
|
|
blocks.push_back (*block1);
|
|
auto transaction (store->tx_begin_write ());
|
|
store->block.put (transaction, hashes[0], *block1);
|
|
auto block2 = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (2)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block2->sideband_set ({});
|
|
hashes.push_back (block2->hash ());
|
|
blocks.push_back (*block2);
|
|
store->block.put (transaction, hashes[1], *block2);
|
|
ASSERT_TRUE (store->block.exists (transaction, block1->hash ()));
|
|
ASSERT_TRUE (store->block.exists (transaction, block2->hash ()));
|
|
}
|
|
|
|
TEST (block_store, two_account)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::account account1 (1);
|
|
nano::block_hash hash1 (2);
|
|
nano::account account2 (3);
|
|
nano::block_hash hash2 (4);
|
|
auto transaction (store->tx_begin_write ());
|
|
store->confirmation_height.put (transaction, account1, { 20, nano::block_hash (10) });
|
|
store->account.put (transaction, account1, { hash1, account1, hash1, 42, 100, 300, nano::epoch::epoch_0 });
|
|
store->confirmation_height.put (transaction, account2, { 30, nano::block_hash (20) });
|
|
store->account.put (transaction, account2, { hash2, account2, hash2, 84, 200, 400, nano::epoch::epoch_0 });
|
|
auto begin (store->account.begin (transaction));
|
|
auto end (store->account.end ());
|
|
ASSERT_NE (end, begin);
|
|
ASSERT_EQ (account1, nano::account (begin->first));
|
|
nano::account_info info1 (begin->second);
|
|
ASSERT_EQ (hash1, info1.head);
|
|
ASSERT_EQ (42, info1.balance.number ());
|
|
ASSERT_EQ (100, info1.modified);
|
|
ASSERT_EQ (300, info1.block_count);
|
|
nano::confirmation_height_info confirmation_height_info;
|
|
ASSERT_FALSE (store->confirmation_height.get (transaction, account1, confirmation_height_info));
|
|
ASSERT_EQ (20, confirmation_height_info.height);
|
|
ASSERT_EQ (nano::block_hash (10), confirmation_height_info.frontier);
|
|
++begin;
|
|
ASSERT_NE (end, begin);
|
|
ASSERT_EQ (account2, nano::account (begin->first));
|
|
nano::account_info info2 (begin->second);
|
|
ASSERT_EQ (hash2, info2.head);
|
|
ASSERT_EQ (84, info2.balance.number ());
|
|
ASSERT_EQ (200, info2.modified);
|
|
ASSERT_EQ (400, info2.block_count);
|
|
ASSERT_FALSE (store->confirmation_height.get (transaction, account2, confirmation_height_info));
|
|
ASSERT_EQ (30, confirmation_height_info.height);
|
|
ASSERT_EQ (nano::block_hash (20), confirmation_height_info.frontier);
|
|
++begin;
|
|
ASSERT_EQ (end, begin);
|
|
}
|
|
|
|
TEST (block_store, latest_find)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::account account1 (1);
|
|
nano::block_hash hash1 (2);
|
|
nano::account account2 (3);
|
|
nano::block_hash hash2 (4);
|
|
auto transaction (store->tx_begin_write ());
|
|
store->confirmation_height.put (transaction, account1, { 0, nano::block_hash (0) });
|
|
store->account.put (transaction, account1, { hash1, account1, hash1, 100, 0, 300, nano::epoch::epoch_0 });
|
|
store->confirmation_height.put (transaction, account2, { 0, nano::block_hash (0) });
|
|
store->account.put (transaction, account2, { hash2, account2, hash2, 200, 0, 400, nano::epoch::epoch_0 });
|
|
auto first (store->account.begin (transaction));
|
|
auto second (store->account.begin (transaction));
|
|
++second;
|
|
auto find1 (store->account.begin (transaction, 1));
|
|
ASSERT_EQ (first, find1);
|
|
auto find2 (store->account.begin (transaction, 3));
|
|
ASSERT_EQ (second, find2);
|
|
auto find3 (store->account.begin (transaction, 2));
|
|
ASSERT_EQ (second, find3);
|
|
}
|
|
|
|
namespace nano
|
|
{
|
|
namespace lmdb
|
|
{
|
|
TEST (mdb_block_store, supported_version_upgrades)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
// Check that upgrading from an unsupported version is not supported
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::logger_mt logger;
|
|
{
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
nano::stats stats;
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
// Lower the database to the max version unsupported for upgrades
|
|
store.version.put (transaction, store.version_minimum - 1);
|
|
}
|
|
|
|
// Upgrade should fail
|
|
{
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
ASSERT_TRUE (store.init_error ());
|
|
}
|
|
|
|
auto path1 (nano::unique_path () / "data.ldb");
|
|
// Now try with the minimum version
|
|
{
|
|
nano::lmdb::store store (logger, path1, nano::dev::constants);
|
|
nano::stats stats;
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
// Lower the database version to the minimum version supported for upgrade.
|
|
store.version.put (transaction, store.version_minimum);
|
|
store.confirmation_height.del (transaction, nano::dev::genesis->account ());
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "accounts_v1", MDB_CREATE,
|
|
&store.account_store.accounts_v1_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "open", MDB_CREATE, &store.block_store.open_blocks_handle));
|
|
modify_account_info_to_v14 (store, transaction, nano::dev::genesis->account (), 1,
|
|
nano::dev::genesis->hash ());
|
|
write_block_w_sideband_v18 (store, store.block_store.open_blocks_handle, transaction, *nano::dev::genesis);
|
|
}
|
|
|
|
// Upgrade should work
|
|
{
|
|
nano::lmdb::store store (logger, path1, nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST (mdb_block_store, bad_path)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, boost::filesystem::path ("///"), nano::dev::constants);
|
|
ASSERT_TRUE (store.init_error ());
|
|
}
|
|
|
|
TEST (block_store, DISABLED_already_open) // File can be shared
|
|
{
|
|
auto path (nano::unique_path ());
|
|
boost::filesystem::create_directories (path.parent_path ());
|
|
nano::set_secure_perm_directory (path.parent_path ());
|
|
std::ofstream file;
|
|
file.open (path.string ().c_str ());
|
|
ASSERT_TRUE (file.is_open ());
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, path, nano::dev::constants);
|
|
ASSERT_TRUE (store->init_error ());
|
|
}
|
|
|
|
TEST (block_store, roots)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::block_builder builder;
|
|
auto send_block = builder
|
|
.send ()
|
|
.previous (0)
|
|
.destination (1)
|
|
.balance (2)
|
|
.sign (nano::keypair ().prv, 4)
|
|
.work (5)
|
|
.build ();
|
|
ASSERT_EQ (send_block->hashables.previous, send_block->root ().as_block_hash ());
|
|
auto change_block = builder
|
|
.change ()
|
|
.previous (0)
|
|
.representative (1)
|
|
.sign (nano::keypair ().prv, 3)
|
|
.work (4)
|
|
.build ();
|
|
ASSERT_EQ (change_block->hashables.previous, change_block->root ().as_block_hash ());
|
|
auto receive_block = builder
|
|
.receive ()
|
|
.previous (0)
|
|
.source (1)
|
|
.sign (nano::keypair ().prv, 3)
|
|
.work (4)
|
|
.build ();
|
|
ASSERT_EQ (receive_block->hashables.previous, receive_block->root ().as_block_hash ());
|
|
auto open_block = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (2)
|
|
.sign (nano::keypair ().prv, 4)
|
|
.work (5)
|
|
.build ();
|
|
ASSERT_EQ (open_block->hashables.account, open_block->root ().as_account ());
|
|
}
|
|
|
|
TEST (block_store, pending_exists)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::pending_key two (2, 0);
|
|
nano::pending_info pending;
|
|
auto transaction (store->tx_begin_write ());
|
|
store->pending.put (transaction, two, pending);
|
|
nano::pending_key one (1, 0);
|
|
ASSERT_FALSE (store->pending.exists (transaction, one));
|
|
}
|
|
|
|
TEST (block_store, latest_exists)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::account two (2);
|
|
nano::account_info info;
|
|
auto transaction (store->tx_begin_write ());
|
|
store->confirmation_height.put (transaction, two, { 0, nano::block_hash (0) });
|
|
store->account.put (transaction, two, info);
|
|
nano::account one (1);
|
|
ASSERT_FALSE (store->account.exists (transaction, one));
|
|
}
|
|
|
|
TEST (block_store, large_iteration)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
std::unordered_set<nano::account> accounts1;
|
|
for (auto i (0); i < 1000; ++i)
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
nano::account account;
|
|
nano::random_pool::generate_block (account.bytes.data (), account.bytes.size ());
|
|
accounts1.insert (account);
|
|
store->confirmation_height.put (transaction, account, { 0, nano::block_hash (0) });
|
|
store->account.put (transaction, account, nano::account_info ());
|
|
}
|
|
std::unordered_set<nano::account> accounts2;
|
|
nano::account previous{};
|
|
auto transaction (store->tx_begin_read ());
|
|
for (auto i (store->account.begin (transaction, 0)), n (store->account.end ()); i != n; ++i)
|
|
{
|
|
nano::account current (i->first);
|
|
ASSERT_GT (current.number (), previous.number ());
|
|
accounts2.insert (current);
|
|
previous = current;
|
|
}
|
|
ASSERT_EQ (accounts1, accounts2);
|
|
// Reverse iteration
|
|
std::unordered_set<nano::account> accounts3;
|
|
previous = std::numeric_limits<nano::uint256_t>::max ();
|
|
for (auto i (store->account.rbegin (transaction)), n (store->account.end ()); i != n; --i)
|
|
{
|
|
nano::account current (i->first);
|
|
ASSERT_LT (current.number (), previous.number ());
|
|
accounts3.insert (current);
|
|
previous = current;
|
|
}
|
|
ASSERT_EQ (accounts1, accounts3);
|
|
}
|
|
|
|
TEST (block_store, frontier)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
auto transaction (store->tx_begin_write ());
|
|
nano::block_hash hash (100);
|
|
nano::account account (200);
|
|
ASSERT_TRUE (store->frontier.get (transaction, hash).is_zero ());
|
|
store->frontier.put (transaction, hash, account);
|
|
ASSERT_EQ (account, store->frontier.get (transaction, hash));
|
|
store->frontier.del (transaction, hash);
|
|
ASSERT_TRUE (store->frontier.get (transaction, hash).is_zero ());
|
|
}
|
|
|
|
TEST (block_store, block_replace)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::block_builder builder;
|
|
auto send1 = builder
|
|
.send ()
|
|
.previous (0)
|
|
.destination (0)
|
|
.balance (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (1)
|
|
.build ();
|
|
send1->sideband_set ({});
|
|
auto send2 = builder
|
|
.send ()
|
|
.previous (0)
|
|
.destination (0)
|
|
.balance (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (2)
|
|
.build ();
|
|
send2->sideband_set ({});
|
|
auto transaction (store->tx_begin_write ());
|
|
store->block.put (transaction, 0, *send1);
|
|
store->block.put (transaction, 0, *send2);
|
|
auto block3 (store->block.get (transaction, 0));
|
|
ASSERT_NE (nullptr, block3);
|
|
ASSERT_EQ (2, block3->block_work ());
|
|
}
|
|
|
|
TEST (block_store, block_count)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
ASSERT_EQ (0, store->block.count (transaction));
|
|
nano::block_builder builder;
|
|
auto block = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block->sideband_set ({});
|
|
auto hash1 (block->hash ());
|
|
store->block.put (transaction, hash1, *block);
|
|
}
|
|
auto transaction (store->tx_begin_read ());
|
|
ASSERT_EQ (1, store->block.count (transaction));
|
|
}
|
|
|
|
TEST (block_store, account_count)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
ASSERT_EQ (0, store->account.count (transaction));
|
|
nano::account account (200);
|
|
store->confirmation_height.put (transaction, account, { 0, nano::block_hash (0) });
|
|
store->account.put (transaction, account, nano::account_info ());
|
|
}
|
|
auto transaction (store->tx_begin_read ());
|
|
ASSERT_EQ (1, store->account.count (transaction));
|
|
}
|
|
|
|
TEST (block_store, cemented_count_cache)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
auto transaction (store->tx_begin_write ());
|
|
nano::ledger_cache ledger_cache;
|
|
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
|
ASSERT_EQ (1, ledger_cache.cemented_count);
|
|
}
|
|
|
|
TEST (block_store, block_random)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
{
|
|
nano::ledger_cache ledger_cache;
|
|
auto transaction (store->tx_begin_write ());
|
|
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
|
}
|
|
auto transaction (store->tx_begin_read ());
|
|
auto block (store->block.random (transaction));
|
|
ASSERT_NE (nullptr, block);
|
|
ASSERT_EQ (*block, *nano::dev::genesis);
|
|
}
|
|
|
|
TEST (block_store, pruned_random)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::block_builder builder;
|
|
auto block = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (0)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block->sideband_set ({});
|
|
auto hash1 (block->hash ());
|
|
{
|
|
nano::ledger_cache ledger_cache;
|
|
auto transaction (store->tx_begin_write ());
|
|
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
|
store->pruned.put (transaction, hash1);
|
|
}
|
|
auto transaction (store->tx_begin_read ());
|
|
auto random_hash (store->pruned.random (transaction));
|
|
ASSERT_EQ (hash1, random_hash);
|
|
}
|
|
|
|
TEST (block_store, state_block)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_FALSE (store->init_error ());
|
|
nano::keypair key1;
|
|
nano::block_builder builder;
|
|
auto block1 = builder
|
|
.state ()
|
|
.account (1)
|
|
.previous (nano::dev::genesis->hash ())
|
|
.representative (3)
|
|
.balance (4)
|
|
.link (6)
|
|
.sign (key1.prv, key1.pub)
|
|
.work (7)
|
|
.build ();
|
|
|
|
block1->sideband_set ({});
|
|
{
|
|
nano::ledger_cache ledger_cache;
|
|
auto transaction (store->tx_begin_write ());
|
|
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
|
ASSERT_EQ (nano::block_type::state, block1->type ());
|
|
store->block.put (transaction, block1->hash (), *block1);
|
|
ASSERT_TRUE (store->block.exists (transaction, block1->hash ()));
|
|
auto block2 (store->block.get (transaction, block1->hash ()));
|
|
ASSERT_NE (nullptr, block2);
|
|
ASSERT_EQ (*block1, *block2);
|
|
}
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
auto count (store->block.count (transaction));
|
|
ASSERT_EQ (2, count);
|
|
store->block.del (transaction, block1->hash ());
|
|
ASSERT_FALSE (store->block.exists (transaction, block1->hash ()));
|
|
}
|
|
auto transaction (store->tx_begin_read ());
|
|
auto count2 (store->block.count (transaction));
|
|
ASSERT_EQ (1, count2);
|
|
}
|
|
|
|
TEST (mdb_block_store, sideband_height)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
nano::logger_mt logger;
|
|
nano::keypair key1;
|
|
nano::keypair key2;
|
|
nano::keypair key3;
|
|
nano::lmdb::store store (logger, nano::unique_path () / "data.ldb", nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
nano::stats stats;
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
nano::block_builder builder;
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
|
auto send = builder
|
|
.send ()
|
|
.previous (nano::dev::genesis->hash ())
|
|
.destination (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (nano::dev::genesis->hash ()))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
|
|
auto receive = builder
|
|
.receive ()
|
|
.previous (send->hash ())
|
|
.source (send->hash ())
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (send->hash ()))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive).code);
|
|
auto change = builder
|
|
.change ()
|
|
.previous (receive->hash ())
|
|
.representative (0)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (receive->hash ()))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change).code);
|
|
auto state_send1 = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (change->hash ())
|
|
.representative (0)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.link (key1.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (change->hash ()))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send1).code);
|
|
auto state_send2 = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (state_send1->hash ())
|
|
.representative (0)
|
|
.balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio)
|
|
.link (key2.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_send1->hash ()))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send2).code);
|
|
auto state_send3 = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (state_send2->hash ())
|
|
.representative (0)
|
|
.balance (nano::dev::constants.genesis_amount - 3 * nano::Gxrb_ratio)
|
|
.link (key3.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_send2->hash ()))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send3).code);
|
|
auto state_open = builder
|
|
.state ()
|
|
.account (key1.pub)
|
|
.previous (0)
|
|
.representative (0)
|
|
.balance (nano::Gxrb_ratio)
|
|
.link (state_send1->hash ())
|
|
.sign (key1.prv, key1.pub)
|
|
.work (*pool.generate (key1.pub))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_open).code);
|
|
auto epoch = builder
|
|
.state ()
|
|
.account (key1.pub)
|
|
.previous (state_open->hash ())
|
|
.representative (0)
|
|
.balance (nano::Gxrb_ratio)
|
|
.link (ledger.epoch_link (nano::epoch::epoch_1))
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_open->hash ()))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch).code);
|
|
ASSERT_EQ (nano::epoch::epoch_1, store.block.version (transaction, epoch->hash ()));
|
|
auto epoch_open = builder
|
|
.state ()
|
|
.account (key2.pub)
|
|
.previous (0)
|
|
.representative (0)
|
|
.balance (0)
|
|
.link (ledger.epoch_link (nano::epoch::epoch_1))
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (key2.pub))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch_open).code);
|
|
ASSERT_EQ (nano::epoch::epoch_1, store.block.version (transaction, epoch_open->hash ()));
|
|
auto state_receive = builder
|
|
.state ()
|
|
.account (key2.pub)
|
|
.previous (epoch_open->hash ())
|
|
.representative (0)
|
|
.balance (nano::Gxrb_ratio)
|
|
.link (state_send2->hash ())
|
|
.sign (key2.prv, key2.pub)
|
|
.work (*pool.generate (epoch_open->hash ()))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_receive).code);
|
|
auto open = builder
|
|
.open ()
|
|
.source (state_send3->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.account (key3.pub)
|
|
.sign (key3.prv, key3.pub)
|
|
.work (*pool.generate (key3.pub))
|
|
.build ();
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open).code);
|
|
auto block1 (store.block.get (transaction, nano::dev::genesis->hash ()));
|
|
ASSERT_EQ (block1->sideband ().height, 1);
|
|
auto block2 (store.block.get (transaction, send->hash ()));
|
|
ASSERT_EQ (block2->sideband ().height, 2);
|
|
auto block3 (store.block.get (transaction, receive->hash ()));
|
|
ASSERT_EQ (block3->sideband ().height, 3);
|
|
auto block4 (store.block.get (transaction, change->hash ()));
|
|
ASSERT_EQ (block4->sideband ().height, 4);
|
|
auto block5 (store.block.get (transaction, state_send1->hash ()));
|
|
ASSERT_EQ (block5->sideband ().height, 5);
|
|
auto block6 (store.block.get (transaction, state_send2->hash ()));
|
|
ASSERT_EQ (block6->sideband ().height, 6);
|
|
auto block7 (store.block.get (transaction, state_send3->hash ()));
|
|
ASSERT_EQ (block7->sideband ().height, 7);
|
|
auto block8 (store.block.get (transaction, state_open->hash ()));
|
|
ASSERT_EQ (block8->sideband ().height, 1);
|
|
auto block9 (store.block.get (transaction, epoch->hash ()));
|
|
ASSERT_EQ (block9->sideband ().height, 2);
|
|
auto block10 (store.block.get (transaction, epoch_open->hash ()));
|
|
ASSERT_EQ (block10->sideband ().height, 1);
|
|
auto block11 (store.block.get (transaction, state_receive->hash ()));
|
|
ASSERT_EQ (block11->sideband ().height, 2);
|
|
auto block12 (store.block.get (transaction, open->hash ()));
|
|
ASSERT_EQ (block12->sideband ().height, 1);
|
|
}
|
|
|
|
TEST (block_store, peers)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
|
|
nano::endpoint_key endpoint (boost::asio::ip::address_v6::any ().to_bytes (), 100);
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
|
|
// Confirm that the store is empty
|
|
ASSERT_FALSE (store->peer.exists (transaction, endpoint));
|
|
ASSERT_EQ (store->peer.count (transaction), 0);
|
|
|
|
// Add one
|
|
store->peer.put (transaction, endpoint);
|
|
ASSERT_TRUE (store->peer.exists (transaction, endpoint));
|
|
}
|
|
|
|
// Confirm that it can be found
|
|
{
|
|
auto transaction (store->tx_begin_read ());
|
|
ASSERT_EQ (store->peer.count (transaction), 1);
|
|
}
|
|
|
|
// Add another one and check that it (and the existing one) can be found
|
|
nano::endpoint_key endpoint1 (boost::asio::ip::address_v6::any ().to_bytes (), 101);
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
store->peer.put (transaction, endpoint1);
|
|
ASSERT_TRUE (store->peer.exists (transaction, endpoint1)); // Check new peer is here
|
|
ASSERT_TRUE (store->peer.exists (transaction, endpoint)); // Check first peer is still here
|
|
}
|
|
|
|
{
|
|
auto transaction (store->tx_begin_read ());
|
|
ASSERT_EQ (store->peer.count (transaction), 2);
|
|
}
|
|
|
|
// Delete the first one
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
store->peer.del (transaction, endpoint1);
|
|
ASSERT_FALSE (store->peer.exists (transaction, endpoint1)); // Confirm it no longer exists
|
|
ASSERT_TRUE (store->peer.exists (transaction, endpoint)); // Check first peer is still here
|
|
}
|
|
|
|
{
|
|
auto transaction (store->tx_begin_read ());
|
|
ASSERT_EQ (store->peer.count (transaction), 1);
|
|
}
|
|
|
|
// Delete original one
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
store->peer.del (transaction, endpoint);
|
|
ASSERT_FALSE (store->peer.exists (transaction, endpoint));
|
|
}
|
|
|
|
{
|
|
auto transaction (store->tx_begin_read ());
|
|
ASSERT_EQ (store->peer.count (transaction), 0);
|
|
}
|
|
}
|
|
|
|
TEST (block_store, endpoint_key_byte_order)
|
|
{
|
|
boost::asio::ip::address_v6 address (boost::asio::ip::make_address_v6 ("::ffff:127.0.0.1"));
|
|
uint16_t port = 100;
|
|
nano::endpoint_key endpoint_key (address.to_bytes (), port);
|
|
|
|
std::vector<uint8_t> bytes;
|
|
{
|
|
nano::vectorstream stream (bytes);
|
|
nano::write (stream, endpoint_key);
|
|
}
|
|
|
|
// This checks that the endpoint is serialized as expected, with a size
|
|
// of 18 bytes (16 for ipv6 address and 2 for port), both in network byte order.
|
|
ASSERT_EQ (bytes.size (), 18);
|
|
ASSERT_EQ (bytes[10], 0xff);
|
|
ASSERT_EQ (bytes[11], 0xff);
|
|
ASSERT_EQ (bytes[12], 127);
|
|
ASSERT_EQ (bytes[bytes.size () - 2], 0);
|
|
ASSERT_EQ (bytes.back (), 100);
|
|
|
|
// Deserialize the same stream bytes
|
|
nano::bufferstream stream1 (bytes.data (), bytes.size ());
|
|
nano::endpoint_key endpoint_key1;
|
|
nano::read (stream1, endpoint_key1);
|
|
|
|
// This should be in network bytes order
|
|
ASSERT_EQ (address.to_bytes (), endpoint_key1.address_bytes ());
|
|
|
|
// This should be in host byte order
|
|
ASSERT_EQ (port, endpoint_key1.port ());
|
|
}
|
|
|
|
TEST (block_store, online_weight)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_FALSE (store->init_error ());
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
ASSERT_EQ (0, store->online_weight.count (transaction));
|
|
ASSERT_EQ (store->online_weight.end (), store->online_weight.begin (transaction));
|
|
ASSERT_EQ (store->online_weight.end (), store->online_weight.rbegin (transaction));
|
|
store->online_weight.put (transaction, 1, 2);
|
|
store->online_weight.put (transaction, 3, 4);
|
|
}
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
ASSERT_EQ (2, store->online_weight.count (transaction));
|
|
auto item (store->online_weight.begin (transaction));
|
|
ASSERT_NE (store->online_weight.end (), item);
|
|
ASSERT_EQ (1, item->first);
|
|
ASSERT_EQ (2, item->second.number ());
|
|
auto item_last (store->online_weight.rbegin (transaction));
|
|
ASSERT_NE (store->online_weight.end (), item_last);
|
|
ASSERT_EQ (3, item_last->first);
|
|
ASSERT_EQ (4, item_last->second.number ());
|
|
store->online_weight.del (transaction, 1);
|
|
ASSERT_EQ (1, store->online_weight.count (transaction));
|
|
ASSERT_EQ (store->online_weight.begin (transaction), store->online_weight.rbegin (transaction));
|
|
store->online_weight.del (transaction, 3);
|
|
}
|
|
auto transaction (store->tx_begin_read ());
|
|
ASSERT_EQ (0, store->online_weight.count (transaction));
|
|
ASSERT_EQ (store->online_weight.end (), store->online_weight.begin (transaction));
|
|
ASSERT_EQ (store->online_weight.end (), store->online_weight.rbegin (transaction));
|
|
}
|
|
|
|
TEST (block_store, pruned_blocks)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
|
|
nano::keypair key1;
|
|
nano::block_builder builder;
|
|
auto block1 = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (key1.pub)
|
|
.sign (key1.prv, key1.pub)
|
|
.work (0)
|
|
.build ();
|
|
auto hash1 (block1->hash ());
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
|
|
// Confirm that the store is empty
|
|
ASSERT_FALSE (store->pruned.exists (transaction, hash1));
|
|
ASSERT_EQ (store->pruned.count (transaction), 0);
|
|
|
|
// Add one
|
|
store->pruned.put (transaction, hash1);
|
|
ASSERT_TRUE (store->pruned.exists (transaction, hash1));
|
|
}
|
|
|
|
// Confirm that it can be found
|
|
ASSERT_EQ (store->pruned.count (store->tx_begin_read ()), 1);
|
|
|
|
// Add another one and check that it (and the existing one) can be found
|
|
auto block2 = builder
|
|
.open ()
|
|
.source (1)
|
|
.representative (2)
|
|
.account (key1.pub)
|
|
.sign (key1.prv, key1.pub)
|
|
.work (0)
|
|
.build ();
|
|
block2->sideband_set ({});
|
|
auto hash2 (block2->hash ());
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
store->pruned.put (transaction, hash2);
|
|
ASSERT_TRUE (store->pruned.exists (transaction, hash2)); // Check new pruned hash is here
|
|
ASSERT_FALSE (store->block.exists (transaction, hash2));
|
|
ASSERT_TRUE (store->pruned.exists (transaction, hash1)); // Check first pruned hash is still here
|
|
ASSERT_FALSE (store->block.exists (transaction, hash1));
|
|
}
|
|
|
|
ASSERT_EQ (store->pruned.count (store->tx_begin_read ()), 2);
|
|
|
|
// Delete the first one
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
store->pruned.del (transaction, hash2);
|
|
ASSERT_FALSE (store->pruned.exists (transaction, hash2)); // Confirm it no longer exists
|
|
ASSERT_FALSE (store->block.exists (transaction, hash2)); // true for block_exists
|
|
store->block.put (transaction, hash2, *block2); // Add corresponding block
|
|
ASSERT_TRUE (store->block.exists (transaction, hash2));
|
|
ASSERT_TRUE (store->pruned.exists (transaction, hash1)); // Check first pruned hash is still here
|
|
ASSERT_FALSE (store->block.exists (transaction, hash1));
|
|
}
|
|
|
|
ASSERT_EQ (store->pruned.count (store->tx_begin_read ()), 1);
|
|
|
|
// Delete original one
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
store->pruned.del (transaction, hash1);
|
|
ASSERT_FALSE (store->pruned.exists (transaction, hash1));
|
|
}
|
|
|
|
ASSERT_EQ (store->pruned.count (store->tx_begin_read ()), 0);
|
|
}
|
|
|
|
namespace nano
|
|
{
|
|
namespace lmdb
|
|
{
|
|
TEST (mdb_block_store, upgrade_v14_v15)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
// Extract confirmation height to a separate database
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::block_builder builder;
|
|
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
|
auto send = builder
|
|
.send ()
|
|
.previous (nano::dev::genesis->hash ())
|
|
.destination (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (nano::dev::genesis->hash ()))
|
|
.build ();
|
|
auto epoch = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (send->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.link (nano::dev::network_params.ledger.epochs.link (nano::epoch::epoch_1))
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (send->hash ()))
|
|
.build ();
|
|
auto state_send = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (epoch->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
|
|
.link (nano::dev::genesis_key.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (epoch->hash ()))
|
|
.build ();
|
|
{
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
nano::stats stats;
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
auto account_info = ledger.account_info (transaction, nano::dev::genesis->account ());
|
|
ASSERT_TRUE (account_info);
|
|
nano::confirmation_height_info confirmation_height_info;
|
|
ASSERT_FALSE (store.confirmation_height.get (transaction, nano::dev::genesis->account (),
|
|
confirmation_height_info));
|
|
ASSERT_EQ (confirmation_height_info.height, 1);
|
|
ASSERT_EQ (confirmation_height_info.frontier, nano::dev::genesis->hash ());
|
|
// These databases get removed after an upgrade, so readd them
|
|
ASSERT_FALSE (
|
|
mdb_dbi_open (store.env.tx (transaction), "state_v1", MDB_CREATE, &store.block_store.state_blocks_v1_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "accounts_v1", MDB_CREATE,
|
|
&store.account_store.accounts_v1_handle));
|
|
ASSERT_FALSE (
|
|
mdb_dbi_open (store.env.tx (transaction), "pending_v1", MDB_CREATE, &store.pending_store.pending_v1_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "open", MDB_CREATE, &store.block_store.open_blocks_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "send", MDB_CREATE, &store.block_store.send_blocks_handle));
|
|
ASSERT_FALSE (
|
|
mdb_dbi_open (store.env.tx (transaction), "state_blocks", MDB_CREATE,
|
|
&store.block_store.state_blocks_handle));
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send).code);
|
|
// Lower the database to the previous version
|
|
store.version.put (transaction, 14);
|
|
store.confirmation_height.del (transaction, nano::dev::genesis->account ());
|
|
modify_account_info_to_v14 (store, transaction, nano::dev::genesis->account (),
|
|
confirmation_height_info.height, state_send->hash ());
|
|
|
|
store.pending.del (transaction, nano::pending_key (nano::dev::genesis->account (), state_send->hash ()));
|
|
|
|
write_sideband_v14 (store, transaction, *state_send, store.block_store.state_blocks_v1_handle);
|
|
write_sideband_v14 (store, transaction, *epoch, store.block_store.state_blocks_v1_handle);
|
|
write_block_w_sideband_v18 (store, store.block_store.open_blocks_handle, transaction, *nano::dev::genesis);
|
|
write_block_w_sideband_v18 (store, store.block_store.send_blocks_handle, transaction, *send);
|
|
|
|
// Remove from blocks table
|
|
store.block.del (transaction, state_send->hash ());
|
|
store.block.del (transaction, epoch->hash ());
|
|
|
|
// Turn pending into v14
|
|
ASSERT_FALSE (mdb_put (store.env.tx (transaction), store.pending_store.pending_v0_handle,
|
|
nano::mdb_val (nano::pending_key (nano::dev::genesis_key.pub, send->hash ())),
|
|
nano::mdb_val (
|
|
nano::pending_info_v14 (nano::dev::genesis->account (), nano::Gxrb_ratio,
|
|
nano::epoch::epoch_0)),
|
|
0));
|
|
ASSERT_FALSE (mdb_put (store.env.tx (transaction), store.pending_store.pending_v1_handle,
|
|
nano::mdb_val (nano::pending_key (nano::dev::genesis_key.pub, state_send->hash ())),
|
|
nano::mdb_val (
|
|
nano::pending_info_v14 (nano::dev::genesis->account (), nano::Gxrb_ratio,
|
|
nano::epoch::epoch_1)),
|
|
0));
|
|
|
|
// This should fail as sizes are no longer correct for account_info
|
|
nano::mdb_val value;
|
|
ASSERT_FALSE (mdb_get (store.env.tx (transaction), store.account_store.accounts_v1_handle,
|
|
nano::mdb_val (nano::dev::genesis->account ()), value));
|
|
nano::account_info info;
|
|
ASSERT_NE (value.size (), info.db_size ());
|
|
store.account.del (transaction, nano::dev::genesis->account ());
|
|
|
|
// Confirmation height for the account should be deleted
|
|
ASSERT_TRUE (mdb_get (store.env.tx (transaction), store.confirmation_height_store.confirmation_height_handle,
|
|
nano::mdb_val (nano::dev::genesis->account ()), value));
|
|
}
|
|
|
|
// Now do the upgrade
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
auto transaction (store.tx_begin_read ());
|
|
|
|
// Size of account_info should now equal that set in db
|
|
nano::mdb_val value;
|
|
ASSERT_FALSE (mdb_get (store.env.tx (transaction), store.account_store.accounts_handle,
|
|
nano::mdb_val (nano::dev::genesis->account ()), value));
|
|
nano::account_info info (value);
|
|
ASSERT_EQ (value.size (), info.db_size ());
|
|
|
|
// Confirmation height should exist
|
|
nano::confirmation_height_info confirmation_height_info;
|
|
ASSERT_FALSE (
|
|
store.confirmation_height.get (transaction, nano::dev::genesis->account (),
|
|
confirmation_height_info));
|
|
ASSERT_EQ (confirmation_height_info.height, 1);
|
|
ASSERT_EQ (confirmation_height_info.frontier, nano::dev::genesis->hash ());
|
|
|
|
// accounts_v1, state_blocks_v1 & pending_v1 tables should be deleted
|
|
auto error_get_accounts_v1 (mdb_get (store.env.tx (transaction), store.account_store.accounts_v1_handle,
|
|
nano::mdb_val (nano::dev::genesis->account ()), value));
|
|
ASSERT_NE (error_get_accounts_v1, MDB_SUCCESS);
|
|
auto error_get_pending_v1 (mdb_get (store.env.tx (transaction), store.pending_store.pending_v1_handle, nano::mdb_val (nano::pending_key (nano::dev::genesis_key.pub, state_send->hash ())), value));
|
|
ASSERT_NE (error_get_pending_v1, MDB_SUCCESS);
|
|
auto error_get_state_v1 (
|
|
mdb_get (store.env.tx (transaction), store.block_store.state_blocks_v1_handle, nano::mdb_val (state_send->hash ()),
|
|
value));
|
|
ASSERT_NE (error_get_state_v1, MDB_SUCCESS);
|
|
|
|
// Check that the epochs are set correctly for the sideband, accounts and pending entries
|
|
auto block = store.block.get (transaction, state_send->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
block = store.block.get (transaction, send->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_0);
|
|
ASSERT_EQ (info.epoch (), nano::epoch::epoch_1);
|
|
nano::pending_info pending_info;
|
|
store.pending.get (transaction, nano::pending_key (nano::dev::genesis_key.pub, send->hash ()), pending_info);
|
|
ASSERT_EQ (pending_info.epoch, nano::epoch::epoch_0);
|
|
store.pending.get (transaction, nano::pending_key (nano::dev::genesis_key.pub, state_send->hash ()),
|
|
pending_info);
|
|
ASSERT_EQ (pending_info.epoch, nano::epoch::epoch_1);
|
|
|
|
// Version should be correct
|
|
ASSERT_LT (14, store.version.get (transaction));
|
|
}
|
|
|
|
TEST (mdb_block_store, upgrade_v15_v16)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::mdb_val value;
|
|
{
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
nano::stats stats;
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
// The representation table should get removed after, so readd it so that we can later confirm this actually happens
|
|
auto txn = store.env.tx (transaction);
|
|
ASSERT_FALSE (
|
|
mdb_dbi_open (txn, "representation", MDB_CREATE, &store.account_store.representation_handle));
|
|
auto weight = ledger.cache.rep_weights.representation_get (nano::dev::genesis->account ());
|
|
ASSERT_EQ (MDB_SUCCESS, mdb_put (txn, store.account_store.representation_handle, nano::mdb_val (nano::dev::genesis->account ()), nano::mdb_val (nano::uint128_union (weight)), 0));
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "open", MDB_CREATE, &store.block_store.open_blocks_handle));
|
|
write_block_w_sideband_v18 (store, store.block_store.open_blocks_handle, transaction, *nano::dev::genesis);
|
|
// Lower the database to the previous version
|
|
store.version.put (transaction, 15);
|
|
// Confirm the rep weight exists in the database
|
|
ASSERT_EQ (MDB_SUCCESS, mdb_get (store.env.tx (transaction), store.account_store.representation_handle, nano::mdb_val (nano::dev::genesis->account ()), value));
|
|
store.confirmation_height.del (transaction, nano::dev::genesis->account ());
|
|
}
|
|
|
|
// Now do the upgrade
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
auto transaction (store.tx_begin_read ());
|
|
|
|
// The representation table should now be deleted
|
|
auto error_get_representation (mdb_get (store.env.tx (transaction), store.account_store.representation_handle,
|
|
nano::mdb_val (nano::dev::genesis->account ()), value));
|
|
ASSERT_NE (MDB_SUCCESS, error_get_representation);
|
|
ASSERT_EQ (store.account_store.representation_handle, 0);
|
|
|
|
// Version should be correct
|
|
ASSERT_LT (15, store.version.get (transaction));
|
|
}
|
|
|
|
TEST (mdb_block_store, upgrade_v16_v17)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
|
nano::block_builder builder;
|
|
auto block1 = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (nano::dev::genesis->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.link (nano::dev::genesis_key.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (nano::dev::genesis->hash ()))
|
|
.build ();
|
|
auto block2 = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (block1->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio - 1)
|
|
.link (nano::dev::genesis_key.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (block1->hash ()))
|
|
.build ();
|
|
auto block3 = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (block2->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio - 2)
|
|
.link (nano::dev::genesis_key.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (block2->hash ()))
|
|
.build ();
|
|
|
|
auto code = [&block1, &block2, &block3] (auto confirmation_height, nano::block_hash const & expected_cemented_frontier) {
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::mdb_val value;
|
|
{
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
nano::stats stats;
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block2).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block3).code);
|
|
modify_confirmation_height_to_v15 (store, transaction, nano::dev::genesis->account (), confirmation_height);
|
|
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "open", MDB_CREATE, &store.block_store.open_blocks_handle));
|
|
write_block_w_sideband_v18 (store, store.block_store.open_blocks_handle, transaction, *nano::dev::genesis);
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "state_blocks", MDB_CREATE, &store.block_store.state_blocks_handle));
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *block1);
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *block2);
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *block3);
|
|
|
|
// Lower the database to the previous version
|
|
store.version.put (transaction, 16);
|
|
}
|
|
|
|
// Now do the upgrade
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
auto transaction (store.tx_begin_read ());
|
|
|
|
nano::confirmation_height_info confirmation_height_info;
|
|
ASSERT_FALSE (store.confirmation_height.get (transaction, nano::dev::genesis->account (), confirmation_height_info));
|
|
ASSERT_EQ (confirmation_height_info.height, confirmation_height);
|
|
|
|
// Check confirmation height frontier is correct
|
|
ASSERT_EQ (confirmation_height_info.frontier, expected_cemented_frontier);
|
|
|
|
// Version should be correct
|
|
ASSERT_LT (16, store.version.get (transaction));
|
|
};
|
|
|
|
code (0, nano::block_hash (0));
|
|
code (1, nano::dev::genesis->hash ());
|
|
code (2, block1->hash ());
|
|
code (3, block2->hash ());
|
|
code (4, block3->hash ());
|
|
}
|
|
|
|
TEST (mdb_block_store, upgrade_v17_v18)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::block_builder builder;
|
|
nano::keypair key1;
|
|
nano::keypair key2;
|
|
nano::keypair key3;
|
|
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
|
auto send_zero = builder
|
|
.send ()
|
|
.previous (nano::dev::genesis->hash ())
|
|
.destination (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (nano::dev::genesis->hash ()))
|
|
.build ();
|
|
auto state_receive_zero = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (send_zero->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount)
|
|
.link (send_zero->hash ())
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (send_zero->hash ()))
|
|
.build ();
|
|
auto epoch = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (state_receive_zero->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount)
|
|
.link (nano::dev::network_params.ledger.epochs.link (nano::epoch::epoch_1))
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_receive_zero->hash ()))
|
|
.build ();
|
|
auto state_send = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (epoch->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.link (nano::dev::genesis_key.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (epoch->hash ()))
|
|
.build ();
|
|
auto state_receive = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (state_send->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount)
|
|
.link (state_send->hash ())
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_send->hash ()))
|
|
.build ();
|
|
auto state_change = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (state_receive->hash ())
|
|
.representative (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount)
|
|
.link (0)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_receive->hash ()))
|
|
.build ();
|
|
auto state_send_change = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (state_change->hash ())
|
|
.representative (key1.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.link (key1.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_change->hash ()))
|
|
.build ();
|
|
auto epoch_first = builder
|
|
.state ()
|
|
.account (key1.pub)
|
|
.previous (0)
|
|
.representative (0)
|
|
.balance (0)
|
|
.link (nano::dev::network_params.ledger.epochs.link (nano::epoch::epoch_2))
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (key1.pub))
|
|
.build ();
|
|
auto state_receive2 = builder
|
|
.state ()
|
|
.account (key1.pub)
|
|
.previous (epoch_first->hash ())
|
|
.representative (key1.pub)
|
|
.balance (nano::Gxrb_ratio)
|
|
.link (state_send_change->hash ())
|
|
.sign (key1.prv, key1.pub)
|
|
.work (*pool.generate (epoch_first->hash ()))
|
|
.build ();
|
|
auto state_send2 = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (state_send_change->hash ())
|
|
.representative (key1.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
|
|
.link (key2.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_send_change->hash ()))
|
|
.build ();
|
|
auto state_open = builder
|
|
.state ()
|
|
.account (key2.pub)
|
|
.previous (0)
|
|
.representative (key2.pub)
|
|
.balance (nano::Gxrb_ratio)
|
|
.link (state_send2->hash ())
|
|
.sign (key2.prv, key2.pub)
|
|
.work (*pool.generate (key2.pub))
|
|
.build ();
|
|
auto state_send_epoch_link = builder
|
|
.state ()
|
|
.account (key2.pub)
|
|
.previous (state_open->hash ())
|
|
.representative (key2.pub)
|
|
.balance (0)
|
|
.link (nano::dev::network_params.ledger.epochs.link (nano::epoch::epoch_2))
|
|
.sign (key2.prv, key2.pub)
|
|
.work (*pool.generate (state_open->hash ()))
|
|
.build ();
|
|
{
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
nano::stats stats;
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send_zero).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_receive_zero).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_receive).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_change).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send_change).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch_first).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_receive2).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send2).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_open).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send_epoch_link).code);
|
|
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "open", MDB_CREATE, &store.block_store.open_blocks_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "send", MDB_CREATE, &store.block_store.send_blocks_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "state_blocks", MDB_CREATE, &store.block_store.state_blocks_handle));
|
|
|
|
// Downgrade the store
|
|
store.version.put (transaction, 17);
|
|
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *state_receive);
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *epoch_first);
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *state_send2);
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *state_send_epoch_link);
|
|
write_block_w_sideband_v18 (store, store.block_store.open_blocks_handle, transaction, *nano::dev::genesis);
|
|
write_block_w_sideband_v18 (store, store.block_store.send_blocks_handle, transaction, *send_zero);
|
|
|
|
// Replace with the previous sideband version for state blocks
|
|
// The upgrade can resume after upgrading some blocks, test this by only downgrading some of them
|
|
write_sideband_v15 (store, transaction, *state_receive_zero);
|
|
write_sideband_v15 (store, transaction, *epoch);
|
|
write_sideband_v15 (store, transaction, *state_send);
|
|
write_sideband_v15 (store, transaction, *state_change);
|
|
write_sideband_v15 (store, transaction, *state_send_change);
|
|
write_sideband_v15 (store, transaction, *state_receive2);
|
|
write_sideband_v15 (store, transaction, *state_open);
|
|
|
|
store.block.del (transaction, state_receive_zero->hash ());
|
|
store.block.del (transaction, epoch->hash ());
|
|
store.block.del (transaction, state_send->hash ());
|
|
store.block.del (transaction, state_change->hash ());
|
|
store.block.del (transaction, state_send_change->hash ());
|
|
store.block.del (transaction, state_receive2->hash ());
|
|
store.block.del (transaction, state_open->hash ());
|
|
}
|
|
|
|
// Now do the upgrade
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
auto transaction (store.tx_begin_read ());
|
|
|
|
// Size of state block should equal that set in db (no change)
|
|
nano::mdb_val value;
|
|
ASSERT_FALSE (mdb_get (store.env.tx (transaction), store.block_store.blocks_handle, nano::mdb_val (state_send->hash ()), value));
|
|
ASSERT_EQ (value.size (), sizeof (nano::block_type) + nano::state_block::size + nano::block_sideband::size (nano::block_type::state));
|
|
|
|
// Check that sidebands are correctly populated
|
|
{
|
|
// Non-state unaffected
|
|
auto block = store.block.get (transaction, send_zero->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
// All defaults
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_0);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_FALSE (block->sideband ().details.is_send);
|
|
ASSERT_FALSE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// State receive from old zero send
|
|
auto block = store.block.get (transaction, state_receive_zero->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_0);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_FALSE (block->sideband ().details.is_send);
|
|
ASSERT_TRUE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// Epoch
|
|
auto block = store.block.get (transaction, epoch->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
ASSERT_TRUE (block->sideband ().details.is_epoch);
|
|
ASSERT_FALSE (block->sideband ().details.is_send);
|
|
ASSERT_FALSE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// State send
|
|
auto block = store.block.get (transaction, state_send->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_TRUE (block->sideband ().details.is_send);
|
|
ASSERT_FALSE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// State receive
|
|
auto block = store.block.get (transaction, state_receive->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_FALSE (block->sideband ().details.is_send);
|
|
ASSERT_TRUE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// State change
|
|
auto block = store.block.get (transaction, state_change->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_FALSE (block->sideband ().details.is_send);
|
|
ASSERT_FALSE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// State send + change
|
|
auto block = store.block.get (transaction, state_send_change->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_TRUE (block->sideband ().details.is_send);
|
|
ASSERT_FALSE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// Epoch on unopened account
|
|
auto block = store.block.get (transaction, epoch_first->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_2);
|
|
ASSERT_TRUE (block->sideband ().details.is_epoch);
|
|
ASSERT_FALSE (block->sideband ().details.is_send);
|
|
ASSERT_FALSE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// State open following epoch
|
|
auto block = store.block.get (transaction, state_receive2->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_2);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_FALSE (block->sideband ().details.is_send);
|
|
ASSERT_TRUE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// Another state send
|
|
auto block = store.block.get (transaction, state_send2->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_TRUE (block->sideband ().details.is_send);
|
|
ASSERT_FALSE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// State open
|
|
auto block = store.block.get (transaction, state_open->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_FALSE (block->sideband ().details.is_send);
|
|
ASSERT_TRUE (block->sideband ().details.is_receive);
|
|
}
|
|
{
|
|
// State send to an epoch link
|
|
auto block = store.block.get (transaction, state_send_epoch_link->hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
ASSERT_EQ (block->sideband ().details.epoch, nano::epoch::epoch_1);
|
|
ASSERT_FALSE (block->sideband ().details.is_epoch);
|
|
ASSERT_TRUE (block->sideband ().details.is_send);
|
|
ASSERT_FALSE (block->sideband ().details.is_receive);
|
|
}
|
|
// Version should be correct
|
|
ASSERT_LT (17, store.version.get (transaction));
|
|
}
|
|
|
|
TEST (mdb_block_store, upgrade_v18_v19)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::keypair key1;
|
|
nano::block_builder builder;
|
|
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
|
auto send = builder
|
|
.send ()
|
|
.previous (nano::dev::genesis->hash ())
|
|
.destination (nano::dev::genesis_key.pub)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (nano::dev::genesis->hash ()))
|
|
.build ();
|
|
auto receive = builder
|
|
.receive ()
|
|
.previous (send->hash ())
|
|
.source (send->hash ())
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (send->hash ()))
|
|
.build ();
|
|
auto change = builder
|
|
.change ()
|
|
.previous (receive->hash ())
|
|
.representative (0)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (receive->hash ()))
|
|
.build ();
|
|
auto state_epoch = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (change->hash ())
|
|
.representative (0)
|
|
.balance (nano::dev::constants.genesis_amount)
|
|
.link (nano::dev::network_params.ledger.epochs.link (nano::epoch::epoch_1))
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (change->hash ()))
|
|
.build ();
|
|
auto state_send = builder
|
|
.state ()
|
|
.account (nano::dev::genesis_key.pub)
|
|
.previous (state_epoch->hash ())
|
|
.representative (0)
|
|
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
|
.link (key1.pub)
|
|
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
|
.work (*pool.generate (state_epoch->hash ()))
|
|
.build ();
|
|
auto state_open = builder
|
|
.state ()
|
|
.account (key1.pub)
|
|
.previous (0)
|
|
.representative (0)
|
|
.balance (nano::Gxrb_ratio)
|
|
.link (state_send->hash ())
|
|
.sign (key1.prv, key1.pub)
|
|
.work (*pool.generate (key1.pub))
|
|
.build ();
|
|
{
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
nano::stats stats;
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_epoch).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_send).code);
|
|
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *state_open).code);
|
|
|
|
// These tables need to be re-opened and populated so that an upgrade can be done
|
|
auto txn = store.env.tx (transaction);
|
|
ASSERT_FALSE (mdb_dbi_open (txn, "open", MDB_CREATE, &store.block_store.open_blocks_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (txn, "receive", MDB_CREATE, &store.block_store.receive_blocks_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (txn, "send", MDB_CREATE, &store.block_store.send_blocks_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (txn, "change", MDB_CREATE, &store.block_store.change_blocks_handle));
|
|
ASSERT_FALSE (mdb_dbi_open (txn, "state_blocks", MDB_CREATE, &store.block_store.state_blocks_handle));
|
|
|
|
// Modify blocks back to the old tables
|
|
write_block_w_sideband_v18 (store, store.block_store.open_blocks_handle, transaction, *nano::dev::genesis);
|
|
write_block_w_sideband_v18 (store, store.block_store.send_blocks_handle, transaction, *send);
|
|
write_block_w_sideband_v18 (store, store.block_store.receive_blocks_handle, transaction, *receive);
|
|
write_block_w_sideband_v18 (store, store.block_store.change_blocks_handle, transaction, *change);
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *state_epoch);
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *state_send);
|
|
write_block_w_sideband_v18 (store, store.block_store.state_blocks_handle, transaction, *state_open);
|
|
|
|
store.version.put (transaction, 18);
|
|
}
|
|
|
|
// Now do the upgrade
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
auto transaction (store.tx_begin_read ());
|
|
|
|
// These tables should be deleted
|
|
ASSERT_EQ (store.block_store.send_blocks_handle, 0);
|
|
ASSERT_EQ (store.block_store.receive_blocks_handle, 0);
|
|
ASSERT_EQ (store.block_store.change_blocks_handle, 0);
|
|
ASSERT_EQ (store.block_store.open_blocks_handle, 0);
|
|
ASSERT_EQ (store.block_store.state_blocks_handle, 0);
|
|
|
|
// Confirm these blocks all exist after the upgrade
|
|
ASSERT_TRUE (store.block.get (transaction, send->hash ()));
|
|
ASSERT_TRUE (store.block.get (transaction, receive->hash ()));
|
|
ASSERT_TRUE (store.block.get (transaction, change->hash ()));
|
|
ASSERT_TRUE (store.block.get (transaction, nano::dev::genesis->hash ()));
|
|
auto state_epoch_disk (store.block.get (transaction, state_epoch->hash ()));
|
|
ASSERT_NE (nullptr, state_epoch_disk);
|
|
ASSERT_EQ (nano::epoch::epoch_1, state_epoch_disk->sideband ().details.epoch);
|
|
ASSERT_EQ (nano::epoch::epoch_0, state_epoch_disk->sideband ().source_epoch); // Not used for epoch state blocks
|
|
ASSERT_TRUE (store.block.get (transaction, state_send->hash ()));
|
|
auto state_send_disk (store.block.get (transaction, state_send->hash ()));
|
|
ASSERT_NE (nullptr, state_send_disk);
|
|
ASSERT_EQ (nano::epoch::epoch_1, state_send_disk->sideband ().details.epoch);
|
|
ASSERT_EQ (nano::epoch::epoch_0, state_send_disk->sideband ().source_epoch); // Not used for send state blocks
|
|
ASSERT_TRUE (store.block.get (transaction, state_open->hash ()));
|
|
auto state_open_disk (store.block.get (transaction, state_open->hash ()));
|
|
ASSERT_NE (nullptr, state_open_disk);
|
|
ASSERT_EQ (nano::epoch::epoch_1, state_open_disk->sideband ().details.epoch);
|
|
ASSERT_EQ (nano::epoch::epoch_1, state_open_disk->sideband ().source_epoch);
|
|
|
|
ASSERT_EQ (7, store.count (transaction, store.block_store.blocks_handle));
|
|
|
|
// Version should be correct
|
|
ASSERT_LT (18, store.version.get (transaction));
|
|
}
|
|
|
|
TEST (mdb_block_store, upgrade_v19_v20)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::logger_mt logger;
|
|
nano::stats stats;
|
|
{
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, nano::dev::constants);
|
|
// Delete pruned table
|
|
ASSERT_FALSE (mdb_drop (store.env.tx (transaction), store.pruned_store.pruned_handle, 1));
|
|
store.version.put (transaction, 19);
|
|
}
|
|
// Upgrading should create the table
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
ASSERT_NE (store.pruned_store.pruned_handle, 0);
|
|
|
|
// Version should be correct
|
|
auto transaction (store.tx_begin_read ());
|
|
ASSERT_LT (19, store.version.get (transaction));
|
|
}
|
|
|
|
TEST (mdb_block_store, upgrade_v20_v21)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::logger_mt logger;
|
|
nano::stats stats;
|
|
{
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
nano::ledger ledger (store, stats, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.initialize (transaction, ledger.cache, ledger.constants);
|
|
// Delete pruned table
|
|
ASSERT_FALSE (mdb_drop (store.env.tx (transaction), store.final_vote_store.final_votes_handle, 1));
|
|
store.version.put (transaction, 20);
|
|
}
|
|
// Upgrading should create the table
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
ASSERT_FALSE (store.init_error ());
|
|
ASSERT_NE (store.final_vote_store.final_votes_handle, 0);
|
|
|
|
// Version should be correct
|
|
auto transaction (store.tx_begin_read ());
|
|
ASSERT_LT (19, store.version.get (transaction));
|
|
}
|
|
|
|
TEST (mdb_block_store, upgrade_v21_v22)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
|
|
auto path (nano::unique_path () / "data.ldb");
|
|
nano::logger_mt logger;
|
|
nano::stats stats;
|
|
auto const check_correct_state = [&] () {
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
ASSERT_EQ (store.version.get (transaction), store.version_current);
|
|
MDB_dbi unchecked_handle{ 0 };
|
|
ASSERT_EQ (MDB_NOTFOUND, mdb_dbi_open (store.env.tx (transaction), "unchecked", 0, &unchecked_handle));
|
|
};
|
|
|
|
// Testing current version doesn't contain the unchecked table
|
|
check_correct_state ();
|
|
|
|
// Setting the database to its 21st version state
|
|
{
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.version.put (transaction, 21);
|
|
MDB_dbi unchecked_handle{ 0 };
|
|
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "unchecked", MDB_CREATE, &unchecked_handle));
|
|
ASSERT_EQ (store.version.get (transaction), 21);
|
|
}
|
|
|
|
// Testing the upgrade code worked
|
|
check_correct_state ();
|
|
}
|
|
}
|
|
|
|
namespace rocksdb
|
|
{
|
|
TEST (rocksdb_block_store, upgrade_v21_v22)
|
|
{
|
|
if (!nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in LMDB mode
|
|
GTEST_SKIP ();
|
|
}
|
|
|
|
auto const path = nano::unique_path () / "rocksdb";
|
|
nano::logger_mt logger;
|
|
nano::stats stats;
|
|
auto const check_correct_state = [&] () {
|
|
nano::rocksdb::store store (logger, path, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
ASSERT_EQ (store.version.get (transaction), store.version_current);
|
|
ASSERT_FALSE (store.column_family_exists ("unchecked"));
|
|
};
|
|
|
|
// Testing current version doesn't contain the unchecked table
|
|
check_correct_state ();
|
|
|
|
// Setting the database to its 21st version state
|
|
{
|
|
nano::rocksdb::store store (logger, path, nano::dev::constants);
|
|
|
|
// Create a column family for "unchecked"
|
|
::rocksdb::ColumnFamilyOptions new_cf_options;
|
|
::rocksdb::ColumnFamilyHandle * new_cf_handle;
|
|
::rocksdb::Status status = store.db->CreateColumnFamily (new_cf_options, "unchecked", &new_cf_handle);
|
|
store.handles.emplace_back (new_cf_handle);
|
|
|
|
// The new column family was created successfully, and 'new_cf_handle' now points to it.
|
|
ASSERT_TRUE (status.ok ());
|
|
|
|
// Rollback the database version number.
|
|
auto transaction (store.tx_begin_write ());
|
|
store.version.put (transaction, 21);
|
|
ASSERT_EQ (store.version.get (transaction), 21);
|
|
}
|
|
|
|
// Testing the upgrade code worked
|
|
check_correct_state ();
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST (mdb_block_store, upgrade_backup)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
auto dir (nano::unique_path ());
|
|
namespace fs = boost::filesystem;
|
|
fs::create_directory (dir);
|
|
auto path = dir / "data.ldb";
|
|
/** Returns 'dir' if backup file cannot be found */
|
|
auto get_backup_path = [&dir] () {
|
|
for (fs::directory_iterator itr (dir); itr != fs::directory_iterator (); ++itr)
|
|
{
|
|
if (itr->path ().filename ().string ().find ("data_backup_") != std::string::npos)
|
|
{
|
|
return itr->path ();
|
|
}
|
|
}
|
|
return dir;
|
|
};
|
|
|
|
{
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants);
|
|
auto transaction (store.tx_begin_write ());
|
|
store.version.put (transaction, 14);
|
|
}
|
|
ASSERT_EQ (get_backup_path ().string (), dir.string ());
|
|
|
|
// Now do the upgrade and confirm that backup is saved
|
|
nano::logger_mt logger;
|
|
nano::lmdb::store store (logger, path, nano::dev::constants, nano::txn_tracking_config{}, std::chrono::seconds (5), nano::lmdb_config{}, true);
|
|
ASSERT_FALSE (store.init_error ());
|
|
auto transaction (store.tx_begin_read ());
|
|
ASSERT_LT (14, store.version.get (transaction));
|
|
ASSERT_NE (get_backup_path ().string (), dir.string ());
|
|
}
|
|
|
|
// Test various confirmation height values as well as clearing them
|
|
TEST (block_store, confirmation_height)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode
|
|
GTEST_SKIP ();
|
|
}
|
|
auto path (nano::unique_path ());
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, path, nano::dev::constants);
|
|
|
|
nano::account account1{};
|
|
nano::account account2{ 1 };
|
|
nano::account account3{ 2 };
|
|
nano::block_hash cemented_frontier1 (3);
|
|
nano::block_hash cemented_frontier2 (4);
|
|
nano::block_hash cemented_frontier3 (5);
|
|
{
|
|
auto transaction (store->tx_begin_write ());
|
|
store->confirmation_height.put (transaction, account1, { 500, cemented_frontier1 });
|
|
store->confirmation_height.put (transaction, account2, { std::numeric_limits<uint64_t>::max (), cemented_frontier2 });
|
|
store->confirmation_height.put (transaction, account3, { 10, cemented_frontier3 });
|
|
|
|
nano::confirmation_height_info confirmation_height_info;
|
|
ASSERT_FALSE (store->confirmation_height.get (transaction, account1, confirmation_height_info));
|
|
ASSERT_EQ (confirmation_height_info.height, 500);
|
|
ASSERT_EQ (confirmation_height_info.frontier, cemented_frontier1);
|
|
ASSERT_FALSE (store->confirmation_height.get (transaction, account2, confirmation_height_info));
|
|
ASSERT_EQ (confirmation_height_info.height, std::numeric_limits<uint64_t>::max ());
|
|
ASSERT_EQ (confirmation_height_info.frontier, cemented_frontier2);
|
|
ASSERT_FALSE (store->confirmation_height.get (transaction, account3, confirmation_height_info));
|
|
ASSERT_EQ (confirmation_height_info.height, 10);
|
|
ASSERT_EQ (confirmation_height_info.frontier, cemented_frontier3);
|
|
|
|
// Check clearing of confirmation heights
|
|
store->confirmation_height.clear (transaction);
|
|
}
|
|
auto transaction (store->tx_begin_read ());
|
|
ASSERT_EQ (store->confirmation_height.count (transaction), 0);
|
|
nano::confirmation_height_info confirmation_height_info;
|
|
ASSERT_TRUE (store->confirmation_height.get (transaction, account1, confirmation_height_info));
|
|
ASSERT_TRUE (store->confirmation_height.get (transaction, account2, confirmation_height_info));
|
|
ASSERT_TRUE (store->confirmation_height.get (transaction, account3, confirmation_height_info));
|
|
}
|
|
|
|
// Test various confirmation height values as well as clearing them
|
|
TEST (block_store, final_vote)
|
|
{
|
|
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
// Don't test this in rocksdb mode as deletions cause inaccurate counts
|
|
GTEST_SKIP ();
|
|
}
|
|
auto path (nano::unique_path ());
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, path, nano::dev::constants);
|
|
|
|
{
|
|
auto qualified_root = nano::dev::genesis->qualified_root ();
|
|
auto transaction (store->tx_begin_write ());
|
|
store->final_vote.put (transaction, qualified_root, nano::block_hash (2));
|
|
ASSERT_EQ (store->final_vote.count (transaction), 1);
|
|
store->final_vote.clear (transaction);
|
|
ASSERT_EQ (store->final_vote.count (transaction), 0);
|
|
store->final_vote.put (transaction, qualified_root, nano::block_hash (2));
|
|
ASSERT_EQ (store->final_vote.count (transaction), 1);
|
|
// Clearing with incorrect root shouldn't remove
|
|
store->final_vote.clear (transaction, qualified_root.previous ());
|
|
ASSERT_EQ (store->final_vote.count (transaction), 1);
|
|
// Clearing with correct root should remove
|
|
store->final_vote.clear (transaction, qualified_root.root ());
|
|
ASSERT_EQ (store->final_vote.count (transaction), 0);
|
|
}
|
|
}
|
|
|
|
// Ledger versions are not forward compatible
|
|
TEST (block_store, incompatible_version)
|
|
{
|
|
auto path (nano::unique_path ());
|
|
nano::logger_mt logger;
|
|
{
|
|
auto store = nano::make_store (logger, path, nano::dev::constants);
|
|
ASSERT_FALSE (store->init_error ());
|
|
|
|
// Put version to an unreachable number so that it should always be incompatible
|
|
auto transaction (store->tx_begin_write ());
|
|
store->version.put (transaction, std::numeric_limits<int>::max ());
|
|
}
|
|
|
|
// Now try and read it, should give an error
|
|
{
|
|
auto store = nano::make_store (logger, path, nano::dev::constants, true);
|
|
ASSERT_TRUE (store->init_error ());
|
|
|
|
auto transaction = store->tx_begin_read ();
|
|
auto version_l = store->version.get (transaction);
|
|
ASSERT_EQ (version_l, std::numeric_limits<int>::max ());
|
|
}
|
|
}
|
|
|
|
TEST (block_store, reset_renew_existing_transaction)
|
|
{
|
|
nano::logger_mt logger;
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
|
|
nano::keypair key1;
|
|
nano::block_builder builder;
|
|
auto block = builder
|
|
.open ()
|
|
.source (0)
|
|
.representative (1)
|
|
.account (1)
|
|
.sign (nano::keypair ().prv, 0)
|
|
.work (0)
|
|
.build ();
|
|
block->sideband_set ({});
|
|
auto hash1 (block->hash ());
|
|
auto read_transaction = store->tx_begin_read ();
|
|
|
|
// Block shouldn't exist yet
|
|
auto block_non_existing (store->block.get (read_transaction, hash1));
|
|
ASSERT_EQ (nullptr, block_non_existing);
|
|
|
|
// Release resources for the transaction
|
|
read_transaction.reset ();
|
|
|
|
// Write the block
|
|
{
|
|
auto write_transaction (store->tx_begin_write ());
|
|
store->block.put (write_transaction, hash1, *block);
|
|
}
|
|
|
|
read_transaction.renew ();
|
|
|
|
// Block should exist now
|
|
auto block_existing (store->block.get (read_transaction, hash1));
|
|
ASSERT_NE (nullptr, block_existing);
|
|
}
|
|
|
|
TEST (block_store, rocksdb_force_test_env_variable)
|
|
{
|
|
nano::logger_mt logger;
|
|
|
|
// Set environment variable
|
|
constexpr auto env_var = "TEST_USE_ROCKSDB";
|
|
auto value = std::getenv (env_var);
|
|
|
|
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
|
|
|
auto mdb_cast = dynamic_cast<nano::lmdb::store *> (store.get ());
|
|
if (value && boost::lexical_cast<int> (value) == 1)
|
|
{
|
|
ASSERT_NE (boost::polymorphic_downcast<nano::rocksdb::store *> (store.get ()), nullptr);
|
|
}
|
|
else
|
|
{
|
|
ASSERT_NE (mdb_cast, nullptr);
|
|
}
|
|
}
|
|
|
|
namespace nano
|
|
{
|
|
// This thest ensures the tombstone_count is increased when there is a delete. The tombstone_count is part of a flush
|
|
// logic bound to the way RocksDB is used by the node.
|
|
TEST (rocksdb_block_store, tombstone_count)
|
|
{
|
|
if (!nano::rocksdb_config::using_rocksdb_in_tests ())
|
|
{
|
|
GTEST_SKIP ();
|
|
}
|
|
nano::test::system system;
|
|
nano::logger_mt logger;
|
|
auto store = std::make_unique<nano::rocksdb::store> (logger, nano::unique_path () / "rocksdb", nano::dev::constants);
|
|
ASSERT_TRUE (!store->init_error ());
|
|
nano::block_builder builder;
|
|
auto block = builder
|
|
.send ()
|
|
.previous (0)
|
|
.destination (1)
|
|
.balance (2)
|
|
.sign (nano::keypair ().prv, 4)
|
|
.work (5)
|
|
.build_shared ();
|
|
// Enqueues a block to be saved in the database
|
|
nano::account account{ 1 };
|
|
store->account.put (store->tx_begin_write (), account, nano::account_info{});
|
|
auto check_block_is_listed = [&] (nano::transaction const & transaction_a) {
|
|
return store->account.exists (transaction_a, account);
|
|
};
|
|
// Waits for the block to get saved
|
|
ASSERT_TIMELY (5s, check_block_is_listed (store->tx_begin_read ()));
|
|
ASSERT_EQ (store->tombstone_map.at (nano::tables::accounts).num_since_last_flush.load (), 0);
|
|
// Performs a delete operation and checks for the tombstone counter
|
|
store->account.del (store->tx_begin_write (), account);
|
|
ASSERT_TIMELY (5s, store->tombstone_map.at (nano::tables::accounts).num_since_last_flush.load () == 1);
|
|
}
|
|
}
|
|
|
|
namespace nano
|
|
{
|
|
namespace lmdb
|
|
{
|
|
void write_sideband_v14 (nano::lmdb::store & store_a, nano::transaction & transaction_a, nano::block const & block_a, MDB_dbi db_a)
|
|
{
|
|
auto block = store_a.block.get (transaction_a, block_a.hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
|
|
nano::block_sideband_v14 sideband_v14 (block->type (), block->sideband ().account, block->sideband ().successor, block->sideband ().balance, block->sideband ().timestamp, block->sideband ().height);
|
|
std::vector<uint8_t> data;
|
|
{
|
|
nano::vectorstream stream (data);
|
|
block_a.serialize (stream);
|
|
sideband_v14.serialize (stream);
|
|
}
|
|
|
|
MDB_val val{ data.size (), data.data () };
|
|
ASSERT_FALSE (mdb_put (store_a.env.tx (transaction_a), block->sideband ().details.epoch == nano::epoch::epoch_0 ? store_a.block_store.state_blocks_v0_handle : store_a.block_store.state_blocks_v1_handle, nano::mdb_val (block_a.hash ()), &val, 0));
|
|
}
|
|
|
|
void write_sideband_v15 (nano::lmdb::store & store_a, nano::transaction & transaction_a, nano::block const & block_a)
|
|
{
|
|
auto block = store_a.block.get (transaction_a, block_a.hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
|
|
ASSERT_LE (block->sideband ().details.epoch, nano::epoch::max);
|
|
// Simulated by writing 0 on every of the most significant bits, leaving out epoch only, as if pre-upgrade
|
|
nano::block_sideband_v18 sideband_v15 (block->sideband ().account, block->sideband ().successor, block->sideband ().balance, block->sideband ().timestamp, block->sideband ().height, block->sideband ().details.epoch, false, false, false);
|
|
std::vector<uint8_t> data;
|
|
{
|
|
nano::vectorstream stream (data);
|
|
block_a.serialize (stream);
|
|
sideband_v15.serialize (stream, block_a.type ());
|
|
}
|
|
|
|
MDB_val val{ data.size (), data.data () };
|
|
ASSERT_FALSE (mdb_put (store_a.env.tx (transaction_a), store_a.block_store.state_blocks_handle, nano::mdb_val (block_a.hash ()), &val, 0));
|
|
}
|
|
|
|
void write_block_w_sideband_v18 (nano::lmdb::store & store_a, MDB_dbi database, nano::write_transaction & transaction_a, nano::block const & block_a)
|
|
{
|
|
auto block = store_a.block.get (transaction_a, block_a.hash ());
|
|
ASSERT_NE (block, nullptr);
|
|
auto new_sideband (block->sideband ());
|
|
nano::block_sideband_v18 sideband_v18 (new_sideband.account, new_sideband.successor, new_sideband.balance, new_sideband.height, new_sideband.timestamp, new_sideband.details.epoch, new_sideband.details.is_send, new_sideband.details.is_receive, new_sideband.details.is_epoch);
|
|
|
|
std::vector<uint8_t> data;
|
|
{
|
|
nano::vectorstream stream (data);
|
|
block->serialize (stream);
|
|
sideband_v18.serialize (stream, block->type ());
|
|
}
|
|
|
|
MDB_val val{ data.size (), data.data () };
|
|
ASSERT_FALSE (mdb_put (store_a.env.tx (transaction_a), database, nano::mdb_val (block_a.hash ()), &val, 0));
|
|
store_a.del (transaction_a, nano::tables::blocks, nano::mdb_val (block_a.hash ()));
|
|
}
|
|
|
|
void modify_account_info_to_v14 (nano::lmdb::store & store, nano::transaction const & transaction, nano::account const & account, uint64_t confirmation_height, nano::block_hash const & rep_block)
|
|
{
|
|
nano::account_info info;
|
|
ASSERT_FALSE (store.account.get (transaction, account, info));
|
|
nano::account_info_v14 account_info_v14 (info.head, rep_block, info.open_block, info.balance, info.modified, info.block_count, confirmation_height, info.epoch ());
|
|
auto status (mdb_put (store.env.tx (transaction), info.epoch () == nano::epoch::epoch_0 ? store.account_store.accounts_v0_handle : store.account_store.accounts_v1_handle, nano::mdb_val (account), nano::mdb_val (account_info_v14), 0));
|
|
ASSERT_EQ (status, 0);
|
|
}
|
|
|
|
void modify_confirmation_height_to_v15 (nano::lmdb::store & store, nano::transaction const & transaction, nano::account const & account, uint64_t confirmation_height)
|
|
{
|
|
auto status (mdb_put (store.env.tx (transaction), store.confirmation_height_store.confirmation_height_handle, nano::mdb_val (account), nano::mdb_val (confirmation_height), 0));
|
|
ASSERT_EQ (status, 0);
|
|
}
|
|
}
|
|
}
|