mirror of
https://github.com/wownero/wownero.git
synced 2026-10-18 16:11:41 +02:00
+3









j-berman
jeffro256
Luke Parker
Lee *!* Clagett
SyntheticBird45
jeetrex17
UkoeHB
MrCyjaneK
tobtoht
selsta
Thomas
navidR
Boog900
a9145f9d38
Co-authored-by: jeffro256 <jeffro256@tutanota.com> Co-authored-by: Luke Parker <lukeparker5132@gmail.com> Co-authored-by: Lee *!* Clagett <code@leeclagett.com> Co-authored-by: SyntheticBird45 <someoneelse.is_on.github.rio7x@simplelogin.com> Co-authored-by: jeetrex17 <jeetrex@tutamail.com> Co-authored-by: UkoeHB <ukoehb@users.noreply.github.com> Co-authored-by: MrCyjaneK Co-authored-by: tobtoht Co-authored-by: selsta Co-authored-by: Thomas <thomas.giudici@proton.me> Co-authored-by: navidR Co-authored-by: Boog900 <boog900@tutanota.com>
570 lines
22 KiB
C++
570 lines
22 KiB
C++
// Copyright (c) 2014-2024, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "string_tools.h"
|
|
#include "blockchain_db/blockchain_db.h"
|
|
#include "blockchain_db/lmdb/db_lmdb.h"
|
|
#include "common/pruning.h"
|
|
#include "cryptonote_basic/cryptonote_format_utils.h"
|
|
|
|
using namespace cryptonote;
|
|
using epee::string_tools::pod_to_hex;
|
|
|
|
#define ASSERT_HASH_EQ(a,b) ASSERT_EQ(pod_to_hex(a), pod_to_hex(b))
|
|
|
|
namespace { // anonymous namespace
|
|
|
|
const std::vector<std::string> t_blocks =
|
|
{
|
|
"0100d5adc49a053b8818b2b6023cd2d532c6774e164a8fcacd603651cb3ea0cb7f9340b28ec016b4bc4ca301aa0101ff6e08acbb2702eab03067870349139bee7eab2ca2e030a6bb73d4f68ab6a3b6ca937214054cdac0843d028bbe23b57ea9bae53f12da93bb57bf8a2e40598d9fccd10c2921576e987d93cd80b4891302468738e391f07c4f2b356f7957160968e0bfef6e907c3cee2d8c23cbf04b089680c6868f01025a0f41f063e195a966051e3a29e17130a9ce97d48f55285b9bb04bdd55a09ae78088aca3cf0202d0f26169290450fe17e08974789c3458910b4db18361cdc564f8f2d0bdd2cf568090cad2c60e02d6f3483ec45505cc3be841046c7a12bf953ac973939bc7b727e54258e1881d4d80e08d84ddcb0102dae6dfb16d3e28aaaf43e00170b90606b36f35f38f8a3dceb5ee18199dd8f17c80c0caf384a30202385d7e57a4daba4cdd9e550a92dcc188838386e7581f13f09de796cbed4716a42101c052492a077abf41996b50c1b2e67fd7288bcd8c55cdc657b4e22d0804371f6901beb76a82ea17400cd6d7f595f70e1667d2018ed8f5a78d1ce07484222618c3cd"
|
|
, "0100f9adc49a057d3113f562eac36f14afa08c22ae20bbbf8cffa31a4466d24850732cb96f80e9762365ee01ab0101ff6f08cc953502be76deb845c431f2ed9a4862457654b914003693b8cd672abc935f0d97b16380c08db7010291819f2873e3efbae65ecd5a736f5e8a26318b591c21e39a03fb536520ac63ba80dac40902439a10fde02e39e48e0b31e57cc084a07eedbefb8cbea0143aedd0442b189caa80c6868f010227b84449de4cd7a48cbdce8974baf0b6646e03384e32055e705c243a86bef8a58088aca3cf0202fa7bd15e4e7e884307ab130bb9d50e33c5fcea6546042a26f948efd5952459ee8090cad2c60e028695583dbb8f8faab87e3ef3f88fa827db097bbf51761d91924f5c5b74c6631780e08d84ddcb010279d2f247b54690e3b491e488acff16014a825fd740c23988a25df7c4670c1f2580c0caf384a302022599dfa3f8788b66295051d85937816e1c320cdb347a0fba5219e3fe60c83b2421010576509c5672025d28fd5d3f38efce24e1f9aaf65dd3056b2504e6e2b7f19f7800"
|
|
};
|
|
|
|
const std::vector<size_t> t_sizes =
|
|
{
|
|
1122
|
|
, 347
|
|
};
|
|
|
|
const std::vector<difficulty_type> t_diffs =
|
|
{
|
|
4003674
|
|
, 4051757
|
|
};
|
|
|
|
const std::vector<uint64_t> t_coins =
|
|
{
|
|
1952630229575370
|
|
, 1970220553446486
|
|
};
|
|
|
|
const std::vector<std::vector<std::string>> t_transactions =
|
|
{
|
|
{
|
|
"0100010280e08d84ddcb0106010401110701f254220bb50d901a5523eaed438af5d43f8c6d0e54ba0632eb539884f6b7c02008c0a8a50402f9c7cf807ae74e56f4ec84db2bd93cfb02c2249b38e306f5b54b6e05d00d543b8095f52a02b6abb84e00f47f0a72e37b6b29392d906a38468404c57db3dbc5e8dd306a27a880d293ad0302cfc40a86723e7d459e90e45d47818dc0e81a1f451ace5137a4af8110a89a35ea80b4c4c321026b19c796338607d5a2c1ba240a167134142d72d1640ef07902da64fed0b10cfc8088aca3cf02021f6f655254fee84161118b32e7b6f8c31de5eb88aa00c29a8f57c0d1f95a24dd80d0b8e1981a023321af593163cea2ae37168ab926efd87f195756e3b723e886bdb7e618f751c480a094a58d1d0295ed2b08d1cf44482ae0060a5dcc4b7d810a85dea8c62e274f73862f3d59f8ed80a0e5b9c2910102dc50f2f28d7ceecd9a1147f7106c8d5b4e08b2ec77150f52dd7130ee4f5f50d42101d34f90ac861d0ee9fe3891656a234ea86a8a93bf51a237db65baa00d3f4aa196a9e1d89bc06b40e94ea9a26059efc7ba5b2de7ef7c139831ca62f3fe0bb252008f8c7ee810d3e1e06313edf2db362fc39431755779466b635f12f9f32e44470a3e85e08a28fcd90633efc94aa4ae39153dfaf661089d045521343a3d63e8da08d7916753c66aaebd4eefcfe8e58e5b3d266b752c9ca110749fa33fce7c44270386fcf2bed4f03dd5dadb2dc1fd4c505419f8217b9eaec07521f0d8963e104603c926745039cf38d31de6ed95ace8e8a451f5a36f818c151f517546d55ac0f500e54d07b30ea7452f2e93fa4f60bdb30d71a0a97f97eb121e662006780fbf69002228224a96bff37893d47ec3707b17383906c0cd7d9e7412b3e6c8ccf1419b093c06c26f96e3453b424713cdc5c9575f81cda4e157052df11f4c40809edf420f88a3dd1f7909bbf77c8b184a933389094a88e480e900bcdbf6d1824742ee520fc0032e7d892a2b099b8c6edfd1123ce58a34458ee20cad676a7f7cfd80a28f0cb0888af88838310db372986bdcf9bfcae2324480ca7360d22bff21fb569a530e"
|
|
}
|
|
, {
|
|
}
|
|
};
|
|
|
|
// if the return type (blobdata for now) of block_to_blob ever changes
|
|
// from std::string, this might break.
|
|
bool compare_blocks(const block& a, const block& b)
|
|
{
|
|
auto hash_a = pod_to_hex(get_block_hash(a));
|
|
auto hash_b = pod_to_hex(get_block_hash(b));
|
|
|
|
return hash_a == hash_b;
|
|
}
|
|
|
|
/*
|
|
void print_block(const block& blk, const std::string& prefix = "")
|
|
{
|
|
std::cerr << prefix << ": " << std::endl
|
|
<< "\thash - " << pod_to_hex(get_block_hash(blk)) << std::endl
|
|
<< "\tparent - " << pod_to_hex(blk.prev_id) << std::endl
|
|
<< "\ttimestamp - " << blk.timestamp << std::endl
|
|
;
|
|
}
|
|
|
|
// if the return type (blobdata for now) of tx_to_blob ever changes
|
|
// from std::string, this might break.
|
|
bool compare_txs(const transaction& a, const transaction& b)
|
|
{
|
|
auto ab = tx_to_blob(a);
|
|
auto bb = tx_to_blob(b);
|
|
|
|
return ab == bb;
|
|
}
|
|
*/
|
|
|
|
// convert hex string to string that has values based on that hex
|
|
// thankfully should automatically ignore null-terminator.
|
|
std::string h2b(const std::string& s)
|
|
{
|
|
bool upper = true;
|
|
std::string result;
|
|
unsigned char val = 0;
|
|
for (char c : s)
|
|
{
|
|
if (upper)
|
|
{
|
|
val = 0;
|
|
if (c <= 'f' && c >= 'a')
|
|
{
|
|
val = ((c - 'a') + 10) << 4;
|
|
}
|
|
else
|
|
{
|
|
val = (c - '0') << 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (c <= 'f' && c >= 'a')
|
|
{
|
|
val |= (c - 'a') + 10;
|
|
}
|
|
else
|
|
{
|
|
val |= c - '0';
|
|
}
|
|
result += (char)val;
|
|
}
|
|
upper = !upper;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void check_lmdb(int result, const char* message)
|
|
{
|
|
if (result)
|
|
throw std::runtime_error(std::string(message) + ": " + mdb_strerror(result));
|
|
}
|
|
|
|
class test_lmdb_env
|
|
{
|
|
public:
|
|
explicit test_lmdb_env(const std::string& path)
|
|
{
|
|
check_lmdb(mdb_env_create(&m_env), "Failed to create LMDB environment");
|
|
try
|
|
{
|
|
check_lmdb(mdb_env_set_maxdbs(m_env, 32), "Failed to set LMDB max databases");
|
|
check_lmdb(mdb_env_open(m_env, path.c_str(), 0, 0644), "Failed to open LMDB environment");
|
|
}
|
|
catch (...)
|
|
{
|
|
mdb_env_close(m_env);
|
|
m_env = nullptr;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
~test_lmdb_env()
|
|
{
|
|
mdb_env_close(m_env);
|
|
}
|
|
|
|
MDB_env* get() const { return m_env; }
|
|
|
|
private:
|
|
MDB_env* m_env = nullptr;
|
|
};
|
|
|
|
crypto::hash pruning_test_hash(uint32_t value)
|
|
{
|
|
crypto::hash hash{};
|
|
memcpy(hash.data + sizeof(hash.data) - sizeof(value), &value, sizeof(value));
|
|
return hash;
|
|
}
|
|
|
|
MDB_dbi open_test_table(MDB_txn* txn, const char* name)
|
|
{
|
|
MDB_dbi table;
|
|
check_lmdb(mdb_dbi_open(txn, name, 0, &table), "Failed to open LMDB table");
|
|
return table;
|
|
}
|
|
|
|
void create_interrupted_pruning_data(const std::string& path, size_t records, size_t missing_pruned_record)
|
|
{
|
|
test_lmdb_env env(path);
|
|
mdb_txn_safe txn;
|
|
check_lmdb(mdb_txn_begin(env.get(), nullptr, 0, txn), "Failed to begin LMDB transaction");
|
|
const MDB_dbi blocks = open_test_table(txn, "blocks");
|
|
const MDB_dbi txs_pruned = open_test_table(txn, "txs_pruned");
|
|
const MDB_dbi txs_prunable = open_test_table(txn, "txs_prunable");
|
|
const MDB_dbi tx_indices = open_test_table(txn, "tx_indices");
|
|
|
|
check_lmdb(mdb_set_compare(txn, blocks, BlockchainLMDB::compare_uint64), "Failed to set blocks comparator");
|
|
check_lmdb(mdb_set_compare(txn, txs_pruned, BlockchainLMDB::compare_uint64),
|
|
"Failed to set txs_pruned comparator");
|
|
check_lmdb(mdb_set_compare(txn, txs_prunable, BlockchainLMDB::compare_uint64),
|
|
"Failed to set txs_prunable comparator");
|
|
check_lmdb(mdb_set_dupsort(txn, tx_indices, BlockchainLMDB::compare_hash32),
|
|
"Failed to set tx_indices comparator");
|
|
|
|
const char block_blob = 0;
|
|
MDB_val block_value{sizeof(block_blob), const_cast<char*>(&block_blob)};
|
|
for (uint64_t height = 0; height <= CRYPTONOTE_PRUNING_TIP_BLOCKS; ++height)
|
|
{
|
|
MDB_val block_key{sizeof(height), &height};
|
|
check_lmdb(mdb_put(txn, blocks, &block_key, &block_value, MDB_APPEND), "Failed to add test block");
|
|
}
|
|
|
|
const char pruned_blob = 2;
|
|
const char prunable_blob = 1;
|
|
const uint64_t zero = 0;
|
|
MDB_val tx_indices_key{sizeof(zero), const_cast<uint64_t*>(&zero)};
|
|
for (uint64_t tx_id = 0; tx_id < records; ++tx_id)
|
|
{
|
|
MDB_val tx_key{sizeof(tx_id), &tx_id};
|
|
MDB_val prunable_value{sizeof(prunable_blob), const_cast<char*>(&prunable_blob)};
|
|
check_lmdb(mdb_put(txn, txs_prunable, &tx_key, &prunable_value, MDB_APPEND),
|
|
"Failed to add prunable test transaction");
|
|
|
|
if (tx_id != missing_pruned_record)
|
|
{
|
|
MDB_val pruned_value{sizeof(pruned_blob), const_cast<char*>(&pruned_blob)};
|
|
check_lmdb(mdb_put(txn, txs_pruned, &tx_key, &pruned_value, MDB_APPEND),
|
|
"Failed to add pruned test transaction");
|
|
}
|
|
|
|
txindex tx_index{};
|
|
tx_index.key = pruning_test_hash(tx_id + 1);
|
|
tx_index.data.tx_id = tx_id;
|
|
tx_index.data.block_id = 0;
|
|
MDB_val tx_index_value{sizeof(tx_index), &tx_index};
|
|
check_lmdb(mdb_put(txn, tx_indices, &tx_indices_key, &tx_index_value, MDB_APPENDDUP),
|
|
"Failed to add test transaction index");
|
|
}
|
|
|
|
txn.commit();
|
|
}
|
|
|
|
void prepare_pruning_resume(const std::string& path, uint64_t missing_pruned_record,
|
|
uint64_t matured_tip_record, uint64_t skipped_record)
|
|
{
|
|
test_lmdb_env env(path);
|
|
mdb_txn_safe txn;
|
|
check_lmdb(mdb_txn_begin(env.get(), nullptr, 0, txn), "Failed to begin LMDB transaction");
|
|
const MDB_dbi txs_pruned = open_test_table(txn, "txs_pruned");
|
|
const MDB_dbi txs_prunable = open_test_table(txn, "txs_prunable");
|
|
const MDB_dbi txs_prunable_tip = open_test_table(txn, "txs_prunable_tip");
|
|
check_lmdb(mdb_set_compare(txn, txs_pruned, BlockchainLMDB::compare_uint64),
|
|
"Failed to set txs_pruned comparator");
|
|
check_lmdb(mdb_set_compare(txn, txs_prunable, BlockchainLMDB::compare_uint64),
|
|
"Failed to set txs_prunable comparator");
|
|
check_lmdb(mdb_set_dupsort(txn, txs_prunable_tip, BlockchainLMDB::compare_uint64),
|
|
"Failed to set txs_prunable_tip comparator");
|
|
|
|
const char pruned_blob = 2;
|
|
MDB_val missing_key{sizeof(missing_pruned_record), &missing_pruned_record};
|
|
MDB_val pruned_value{sizeof(pruned_blob), const_cast<char*>(&pruned_blob)};
|
|
check_lmdb(mdb_put(txn, txs_pruned, &missing_key, &pruned_value, 0),
|
|
"Failed to repair pruned test transaction");
|
|
|
|
const char prunable_blob = 1;
|
|
MDB_val tip_key{sizeof(matured_tip_record), &matured_tip_record};
|
|
MDB_val prunable_value{sizeof(prunable_blob), const_cast<char*>(&prunable_blob)};
|
|
check_lmdb(mdb_put(txn, txs_prunable, &tip_key, &prunable_value, 0), "Failed to add matured tip transaction");
|
|
|
|
MDB_val skipped_key{sizeof(skipped_record), &skipped_record};
|
|
check_lmdb(mdb_put(txn, txs_prunable, &skipped_key, &prunable_value, 0), "Failed to add skipped transaction");
|
|
|
|
const uint64_t block_height = 0;
|
|
MDB_val tip_value{sizeof(block_height), const_cast<uint64_t*>(&block_height)};
|
|
check_lmdb(mdb_put(txn, txs_prunable_tip, &tip_key, &tip_value, 0), "Failed to add matured tip record");
|
|
txn.commit();
|
|
}
|
|
|
|
size_t test_table_entries(const std::string& path, const char* name)
|
|
{
|
|
test_lmdb_env env(path);
|
|
mdb_txn_safe txn;
|
|
check_lmdb(mdb_txn_begin(env.get(), nullptr, MDB_RDONLY, txn), "Failed to begin LMDB transaction");
|
|
const MDB_dbi table = open_test_table(txn, name);
|
|
MDB_stat stat;
|
|
check_lmdb(mdb_stat(txn, table, &stat), "Failed to read LMDB table stats");
|
|
return stat.ms_entries;
|
|
}
|
|
|
|
bool get_pruning_progress(const std::string& path, crypto::hash& progress)
|
|
{
|
|
test_lmdb_env env(path);
|
|
mdb_txn_safe txn;
|
|
check_lmdb(mdb_txn_begin(env.get(), nullptr, MDB_RDONLY, txn), "Failed to begin LMDB transaction");
|
|
const MDB_dbi properties = open_test_table(txn, "properties");
|
|
check_lmdb(mdb_set_compare(txn, properties, BlockchainLMDB::compare_string),
|
|
"Failed to set properties comparator");
|
|
const char key_data[] = "pruning_progress";
|
|
MDB_val key{sizeof(key_data), const_cast<char*>(key_data)};
|
|
MDB_val value;
|
|
const int result = mdb_get(txn, properties, &key, &value);
|
|
if (result == MDB_NOTFOUND)
|
|
return false;
|
|
check_lmdb(result, "Failed to read pruning progress");
|
|
if (value.mv_size != sizeof(progress))
|
|
throw std::runtime_error("Unexpected pruning progress size");
|
|
memcpy(&progress, value.mv_data, sizeof(progress));
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
class BlockchainDBTest : public testing::Test
|
|
{
|
|
protected:
|
|
BlockchainDBTest() : m_db(new T()), m_hardfork(*m_db, 1, 0)
|
|
{
|
|
for (auto& i : t_blocks)
|
|
{
|
|
block bl;
|
|
blobdata bd = h2b(i);
|
|
CHECK_AND_ASSERT_THROW_MES(parse_and_validate_block_from_blob(bd, bl), "Invalid block");
|
|
m_blocks.push_back(std::make_pair(bl, bd));
|
|
}
|
|
for (auto& i : t_transactions)
|
|
{
|
|
std::vector<std::pair<transaction, blobdata>> txs;
|
|
for (auto& j : i)
|
|
{
|
|
transaction tx;
|
|
blobdata bd = h2b(j);
|
|
CHECK_AND_ASSERT_THROW_MES(parse_and_validate_tx_from_blob(bd, tx), "Invalid transaction");
|
|
txs.push_back(std::make_pair(tx, bd));
|
|
}
|
|
m_txs.push_back(txs);
|
|
}
|
|
}
|
|
|
|
~BlockchainDBTest() {
|
|
delete m_db;
|
|
remove_files();
|
|
}
|
|
|
|
BlockchainDB* m_db;
|
|
HardFork m_hardfork;
|
|
std::string m_prefix;
|
|
std::vector<std::pair<block, blobdata>> m_blocks;
|
|
std::vector<std::vector<std::pair<transaction, blobdata>>> m_txs;
|
|
std::vector<std::string> m_filenames;
|
|
|
|
void init_hard_fork()
|
|
{
|
|
m_hardfork.init();
|
|
m_db->set_hard_fork(&m_hardfork);
|
|
}
|
|
|
|
void get_filenames()
|
|
{
|
|
m_filenames = m_db->get_filenames();
|
|
for (auto& f : m_filenames)
|
|
{
|
|
std::cerr << "File created by test: " << f << std::endl;
|
|
}
|
|
}
|
|
|
|
void remove_files()
|
|
{
|
|
// remove each file the db created, making sure it starts with fname.
|
|
for (auto& f : m_filenames)
|
|
{
|
|
if (boost::starts_with(f, m_prefix))
|
|
{
|
|
boost::filesystem::remove(f);
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "File created by test not to be removed (for safety): " << f << std::endl;
|
|
}
|
|
}
|
|
|
|
// remove directory if it still exists
|
|
boost::filesystem::remove_all(m_prefix);
|
|
}
|
|
|
|
void set_prefix(const std::string& prefix)
|
|
{
|
|
m_prefix = prefix;
|
|
}
|
|
};
|
|
|
|
using testing::Types;
|
|
|
|
typedef Types<BlockchainLMDB> implementations;
|
|
|
|
TYPED_TEST_SUITE(BlockchainDBTest, implementations);
|
|
|
|
TYPED_TEST(BlockchainDBTest, OpenAndClose)
|
|
{
|
|
boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
|
std::string dirPath = tempPath.string();
|
|
|
|
this->set_prefix(dirPath);
|
|
|
|
// make sure open does not throw
|
|
ASSERT_NO_THROW(this->m_db->open(dirPath));
|
|
this->get_filenames();
|
|
|
|
// make sure open when already open DOES throw
|
|
ASSERT_THROW(this->m_db->open(dirPath), DB_OPEN_FAILURE);
|
|
|
|
ASSERT_NO_THROW(this->m_db->close());
|
|
}
|
|
|
|
TYPED_TEST(BlockchainDBTest, AddBlock)
|
|
{
|
|
|
|
boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
|
std::string dirPath = tempPath.string();
|
|
|
|
this->set_prefix(dirPath);
|
|
|
|
// make sure open does not throw
|
|
ASSERT_NO_THROW(this->m_db->open(dirPath));
|
|
this->get_filenames();
|
|
this->init_hard_fork();
|
|
|
|
db_wtxn_guard guard(this->m_db);
|
|
|
|
// adding a block with no parent in the blockchain should throw.
|
|
// note: this shouldn't be possible, but is a good (and cheap) failsafe.
|
|
//
|
|
// TODO: need at least one more block to make this reasonable, as the
|
|
// BlockchainDB implementation should not check for parent if
|
|
// no blocks have been added yet (because genesis has no parent).
|
|
//ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE);
|
|
|
|
ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], {}));
|
|
ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1], {}));
|
|
|
|
block b;
|
|
ASSERT_TRUE(this->m_db->block_exists(get_block_hash(this->m_blocks[0].first)));
|
|
ASSERT_NO_THROW(b = this->m_db->get_block(get_block_hash(this->m_blocks[0].first)));
|
|
|
|
ASSERT_TRUE(compare_blocks(this->m_blocks[0].first, b));
|
|
|
|
ASSERT_NO_THROW(b = this->m_db->get_block_from_height(0));
|
|
|
|
ASSERT_TRUE(compare_blocks(this->m_blocks[0].first, b));
|
|
|
|
// assert that we can't add the same block twice
|
|
ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], {}), TX_EXISTS);
|
|
|
|
for (auto& h : this->m_blocks[0].first.tx_hashes)
|
|
{
|
|
transaction tx;
|
|
ASSERT_TRUE(this->m_db->tx_exists(h));
|
|
ASSERT_NO_THROW(tx = this->m_db->get_tx(h));
|
|
|
|
ASSERT_HASH_EQ(h, get_transaction_hash(tx));
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(BlockchainDBTest, RetrieveBlockData)
|
|
{
|
|
boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
|
std::string dirPath = tempPath.string();
|
|
|
|
this->set_prefix(dirPath);
|
|
|
|
// make sure open does not throw
|
|
ASSERT_NO_THROW(this->m_db->open(dirPath));
|
|
this->get_filenames();
|
|
this->init_hard_fork();
|
|
|
|
db_wtxn_guard guard(this->m_db);
|
|
|
|
ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0], {}));
|
|
|
|
ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0));
|
|
ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0));
|
|
ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0));
|
|
ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0));
|
|
|
|
ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1], {}));
|
|
ASSERT_EQ(t_diffs[1] - t_diffs[0], this->m_db->get_block_difficulty(1));
|
|
|
|
ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), this->m_db->get_block_hash_from_height(0));
|
|
|
|
std::vector<block> blks;
|
|
ASSERT_NO_THROW(blks = this->m_db->get_blocks_range(0, 1));
|
|
ASSERT_EQ(2, blks.size());
|
|
|
|
ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), get_block_hash(blks[0]));
|
|
ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1].first), get_block_hash(blks[1]));
|
|
|
|
std::vector<crypto::hash> hashes;
|
|
ASSERT_NO_THROW(hashes = this->m_db->get_hashes_range(0, 1));
|
|
ASSERT_EQ(2, hashes.size());
|
|
|
|
ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), hashes[0]);
|
|
ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1].first), hashes[1]);
|
|
}
|
|
|
|
TYPED_TEST(BlockchainDBTest, ResumeInterruptedPruning)
|
|
{
|
|
static constexpr size_t checkpoint_records = 4096;
|
|
static constexpr size_t records = checkpoint_records + 1;
|
|
static constexpr size_t missing_pruned_record = checkpoint_records;
|
|
static constexpr size_t matured_tip_record = 0;
|
|
static constexpr size_t skipped_record = 1;
|
|
|
|
const auto test_resume = [&](bool update)
|
|
{
|
|
SCOPED_TRACE(update ? "update" : "manual");
|
|
const boost::filesystem::path temp_path =
|
|
boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
|
const std::string dir_path = temp_path.string();
|
|
this->set_prefix(dir_path);
|
|
|
|
ASSERT_NO_THROW(this->m_db->open(dir_path));
|
|
this->get_filenames();
|
|
ASSERT_NO_THROW(this->m_db->close());
|
|
ASSERT_NO_THROW(create_interrupted_pruning_data(dir_path, records, missing_pruned_record));
|
|
|
|
const uint32_t pruning_seed = tools::make_pruning_seed(2, CRYPTONOTE_PRUNING_LOG_STRIPES);
|
|
ASSERT_NO_THROW(this->m_db->open(dir_path));
|
|
ASSERT_THROW(this->m_db->prune_blockchain(pruning_seed), DB_ERROR);
|
|
ASSERT_NO_THROW(this->m_db->close());
|
|
|
|
crypto::hash progress{};
|
|
ASSERT_EQ(1, test_table_entries(dir_path, "txs_prunable"));
|
|
ASSERT_TRUE(get_pruning_progress(dir_path, progress));
|
|
ASSERT_HASH_EQ(pruning_test_hash(checkpoint_records), progress);
|
|
|
|
ASSERT_NO_THROW(prepare_pruning_resume(dir_path, missing_pruned_record, matured_tip_record, skipped_record));
|
|
ASSERT_EQ(3, test_table_entries(dir_path, "txs_prunable"));
|
|
ASSERT_EQ(1, test_table_entries(dir_path, "txs_prunable_tip"));
|
|
ASSERT_NO_THROW(this->m_db->open(dir_path));
|
|
ASSERT_TRUE(update ? this->m_db->update_pruning() : this->m_db->prune_blockchain(pruning_seed));
|
|
ASSERT_NO_THROW(this->m_db->close());
|
|
|
|
ASSERT_EQ(1, test_table_entries(dir_path, "txs_prunable"));
|
|
ASSERT_EQ(0, test_table_entries(dir_path, "txs_prunable_tip"));
|
|
ASSERT_FALSE(get_pruning_progress(dir_path, progress));
|
|
|
|
boost::filesystem::remove_all(dir_path);
|
|
this->m_filenames.clear();
|
|
};
|
|
|
|
ASSERT_NO_FATAL_FAILURE(test_resume(true));
|
|
ASSERT_NO_FATAL_FAILURE(test_resume(false));
|
|
}
|
|
|
|
} // anonymous namespace
|