diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f3105114e..e80e3f66c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1805,6 +1805,7 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, case STAGENET: start_height = stagenet_hard_forks[3].height; break; case TESTNET: start_height = testnet_hard_forks[3].height; break; case MAINNET: start_height = mainnet_hard_forks[3].height; break; + case FAKECHAIN: start_height = 0; break; default: return false; } } @@ -1823,18 +1824,21 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t db_height = m_db->height(); if (db_height == 0) return false; - if (to_height == 0) - to_height = db_height - 1; if (start_height >= db_height || to_height >= db_height) return false; if (amount == 0) { std::vector heights; heights.reserve(to_height + 1 - start_height); - for (uint64_t h = start_height; h <= to_height; ++h) + uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; + for (uint64_t h = real_start_height; h <= to_height; ++h) heights.push_back(h); distribution = m_db->get_block_cumulative_rct_outputs(heights); - base = 0; + if (start_height > 0) + { + base = distribution[0]; + distribution.erase(distribution.begin()); + } return true; } else diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c2e71bef8..df9eee781 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2125,7 +2125,7 @@ namespace cryptonote const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); for (uint64_t amount: req.amounts) { - auto data = rpc::RpcHandler::get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative); + auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative); if (!data) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 8822bd378..64a5cc858 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -751,7 +751,7 @@ namespace rpc const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); for (std::uint64_t amount : req.amounts) { - auto data = get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative); + auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative); if (!data) { res.distributions.clear(); diff --git a/src/rpc/rpc_handler.cpp b/src/rpc/rpc_handler.cpp index d4beb1928..63664bf8b 100644 --- a/src/rpc/rpc_handler.cpp +++ b/src/rpc/rpc_handler.cpp @@ -26,7 +26,7 @@ namespace rpc } boost::optional - RpcHandler::get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative) + RpcHandler::get_output_distribution(const std::function&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative) { static struct D { @@ -43,7 +43,7 @@ namespace rpc std::vector distribution; std::uint64_t start_height, base; - if (!src.get_output_distribution(amount, from_height, to_height, start_height, distribution, base)) + if (!f(amount, from_height, to_height, start_height, distribution, base)) return boost::none; if (to_height > 0 && to_height >= from_height) diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h index 3cccef78a..e0d520408 100644 --- a/src/rpc/rpc_handler.h +++ b/src/rpc/rpc_handler.h @@ -56,7 +56,7 @@ class RpcHandler virtual std::string handle(const std::string& request) = 0; static boost::optional - get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative); + get_output_distribution(const std::function&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative); }; diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 7687e3c52..a46f11b5f 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -62,6 +62,7 @@ set(unit_tests_sources multiexp.cpp multisig.cpp notify.cpp + output_distribution.cpp parse_amount.cpp random.cpp serialization.cpp diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index fc488bb14..ec8d1d202 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -34,101 +34,19 @@ #include "blockchain_db/blockchain_db.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/hardfork.h" +#include "testdb.h" using namespace cryptonote; #define BLOCKS_PER_YEAR 525960 #define SECONDS_PER_YEAR 31557600 +namespace +{ -class TestDB: public BlockchainDB { +class TestDB: public BaseTestDB { public: - TestDB() {}; - virtual void open(const std::string& filename, const int db_flags = 0) { } - virtual void close() {} - virtual void sync() {} - virtual void safesyncmode(const bool onoff) {} - virtual void reset() {} - virtual std::vector get_filenames() const { return std::vector(); } - virtual bool remove_data_file(const std::string& folder) const { return true; } - virtual std::string get_db_name() const { return std::string(); } - virtual bool lock() { return true; } - virtual void unlock() { } - virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } - virtual void batch_stop() {} - virtual void set_batch_transactions(bool) {} - virtual void block_txn_start(bool readonly=false) {} - virtual void block_txn_stop() {} - virtual void block_txn_abort() {} - virtual void drop_hard_fork_info() {} - virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } - virtual blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } - virtual blobdata get_block_blob(const crypto::hash& h) const { return blobdata(); } - virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; } - virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } - virtual block_header get_block_header(const crypto::hash& h) const { return block_header(); } - virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } - virtual std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const { return {}; } - virtual uint64_t get_top_block_timestamp() const { return 0; } - virtual size_t get_block_weight(const uint64_t& height) const { return 128; } - virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } - virtual difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } - virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } - virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } - virtual std::vector get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } - virtual std::vector get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } - virtual crypto::hash top_block_hash() const { return crypto::hash(); } - virtual block get_top_block() const { return block(); } virtual uint64_t height() const { return blocks.size(); } - virtual bool tx_exists(const crypto::hash& h) const { return false; } - virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; } - virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } - virtual transaction get_tx(const crypto::hash& h) const { return transaction(); } - virtual bool get_tx(const crypto::hash& h, transaction &tx) const { return false; } - virtual uint64_t get_tx_count() const { return 0; } - virtual std::vector get_tx_list(const std::vector& hlist) const { return std::vector(); } - virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; } - virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } - virtual uint64_t get_indexing_base() const { return 0; } - virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return output_data_t(); } - virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); } - virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return tx_out_index(); } - virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices) const {} - virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs, bool allow_partial = false) {} - virtual bool can_thread_bulk_indices() const { return false; } - virtual std::vector get_tx_output_indices(const crypto::hash& h) const { return std::vector(); } - virtual std::vector get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector(); } - virtual bool has_key_image(const crypto::key_image& img) const { return false; } - virtual void remove_block() { blocks.pop_back(); } - virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;} - virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) {} - virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;} - virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector& amount_output_indices) {} - virtual void add_spent_key(const crypto::key_image& k_image) {} - virtual void remove_spent_key(const crypto::key_image& k_image) {} - - virtual bool for_all_key_images(std::function) const { return true; } - virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function) const { return true; } - virtual bool for_all_transactions(std::function, bool pruned) const { return true; } - virtual bool for_all_outputs(std::function f) const { return true; } - virtual bool for_all_outputs(uint64_t amount, const std::function &f) const { return true; } - virtual bool is_read_only() const { return false; } - virtual std::map> get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map>(); } - virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const { return false; } - - virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t& details) {} - virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& details) {} - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } - virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } - virtual void remove_txpool_tx(const crypto::hash& txid) {} - virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const { return false; } - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } - virtual uint64_t get_database_size() const { return 0; } - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } - virtual bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } - virtual void add_block( const block& blk , size_t block_weight , const difficulty_type& cumulative_difficulty @@ -138,6 +56,7 @@ public: ) { blocks.push_back(blk); } + virtual void remove_block() { blocks.pop_back(); } virtual block get_block_from_height(const uint64_t& height) const { return blocks.at(height); } @@ -149,13 +68,14 @@ public: virtual uint8_t get_hard_fork_version(uint64_t height) const { return versions.at(height); } - virtual void check_hard_fork_info() {} private: std::vector blocks; std::deque versions; }; +} + static cryptonote::block mkblock(uint8_t version, uint8_t vote) { cryptonote::block b; diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp new file mode 100644 index 000000000..649752ac7 --- /dev/null +++ b/tests/unit_tests/output_distribution.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2018, 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 "gtest/gtest.h" +#include "misc_log_ex.h" +#include "rpc/rpc_handler.h" +#include "blockchain_db/blockchain_db.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/blockchain.h" +#include "testdb.h" + +static const uint64_t test_distribution[32] = { + 0, 0, 0, 0, 0, 1, 5, 1, 4, 0, 0, 1, 0, 1, 2, 3, 1, 0, 2, 0, 1, 3, 8, 1, 3, 5, 7, 1, 5, 0, 2, 3 +}; +static const size_t test_distribution_size = sizeof(test_distribution) / sizeof(test_distribution[0]); + +namespace +{ + +class TestDB: public BaseTestDB +{ +public: + TestDB(size_t bc_height = test_distribution_size): blockchain_height(bc_height) { m_open = true; } + virtual uint64_t height() const override { return blockchain_height; } + + std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const override + { + std::vector d; + for (uint64_t h: heights) + { + uint64_t c = 0; + for (uint64_t i = 0; i <= h; ++i) + c += test_distribution[i]; + d.push_back(c); + } + return d; + } + + uint64_t blockchain_height; +}; + +} + +bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector &distribution, uint64_t &base) +{ + std::unique_ptr bc; + cryptonote::tx_memory_pool txpool(*bc); + bc.reset(new cryptonote::Blockchain(txpool)); + struct get_test_options { + const std::pair hard_forks[2]; + const cryptonote::test_options test_options = { + hard_forks + }; + get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)}{} + } opts; + cryptonote::Blockchain *blockchain = bc.get(); + bool r = blockchain->init(new TestDB(test_distribution_size), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); + return r && bc->get_output_distribution(amount, from, to, start_height, distribution, base); +} + +TEST(output_distribution, extend) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 29, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 2); + ASSERT_EQ(res->distribution, std::vector({5, 0})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 29, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 2); + ASSERT_EQ(res->distribution, std::vector({55, 55})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 30, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 3); + ASSERT_EQ(res->distribution, std::vector({5, 0, 2})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 30, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 3); + ASSERT_EQ(res->distribution, std::vector({55, 55, 57})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 31, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 4); + ASSERT_EQ(res->distribution, std::vector({5, 0, 2, 3})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 31, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 4); + ASSERT_EQ(res->distribution, std::vector({55, 55, 57, 60})); +} + +TEST(output_distribution, one) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 0, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 1); + ASSERT_EQ(res->distribution.back(), 0); +} + +TEST(output_distribution, full_cumulative) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 31, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 32); + ASSERT_EQ(res->distribution.back(), 60); +} + +TEST(output_distribution, full_noncumulative) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 31, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 32); + for (size_t i = 0; i < 32; ++i) + ASSERT_EQ(res->distribution[i], test_distribution[i]); +} + +TEST(output_distribution, part_cumulative) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 4, 8, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 5); + ASSERT_EQ(res->distribution, std::vector({0, 1, 6, 7, 11})); +} + +TEST(output_distribution, part_noncumulative) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 4, 8, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 5); + ASSERT_EQ(res->distribution, std::vector({0, 1, 5, 1, 4})); +} diff --git a/tests/unit_tests/testdb.h b/tests/unit_tests/testdb.h new file mode 100644 index 000000000..b6962cc41 --- /dev/null +++ b/tests/unit_tests/testdb.h @@ -0,0 +1,146 @@ +// Copyright (c) 2014-2018, 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. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include +#include +#include "gtest/gtest.h" + +#include "blockchain_db/blockchain_db.h" + +class BaseTestDB: public cryptonote::BlockchainDB { +public: + BaseTestDB() {} + virtual void open(const std::string& filename, const int db_flags = 0) { } + virtual void close() {} + virtual void sync() {} + virtual void safesyncmode(const bool onoff) {} + virtual void reset() {} + virtual std::vector get_filenames() const { return std::vector(); } + virtual bool remove_data_file(const std::string& folder) const { return true; } + virtual std::string get_db_name() const { return std::string(); } + virtual bool lock() { return true; } + virtual void unlock() { } + virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } + virtual void batch_stop() {} + virtual void set_batch_transactions(bool) {} + virtual void block_txn_start(bool readonly=false) {} + virtual void block_txn_stop() {} + virtual void block_txn_abort() {} + virtual void drop_hard_fork_info() {} + virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } + virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } + virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const { return cryptonote::blobdata(); } + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; } + virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } + virtual cryptonote::block_header get_block_header(const crypto::hash& h) const { return cryptonote::block_header(); } + virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } + virtual std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const { return {}; } + virtual uint64_t get_top_block_timestamp() const { return 0; } + virtual size_t get_block_weight(const uint64_t& height) const { return 128; } + virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } + virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } + virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } + virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } + virtual std::vector get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } + virtual std::vector get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } + virtual crypto::hash top_block_hash() const { return crypto::hash(); } + virtual cryptonote::block get_top_block() const { return cryptonote::block(); } + virtual uint64_t height() const { return 1; } + virtual bool tx_exists(const crypto::hash& h) const { return false; } + virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; } + virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } + virtual cryptonote::transaction get_tx(const crypto::hash& h) const { return cryptonote::transaction(); } + virtual bool get_tx(const crypto::hash& h, cryptonote::transaction &tx) const { return false; } + virtual uint64_t get_tx_count() const { return 0; } + virtual std::vector get_tx_list(const std::vector& hlist) const { return std::vector(); } + virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; } + virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } + virtual uint64_t get_indexing_base() const { return 0; } + virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return cryptonote::output_data_t(); } + virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); } + virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); } + virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices) const {} + virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs, bool allow_partial = false) {} + virtual bool can_thread_bulk_indices() const { return false; } + virtual std::vector get_tx_output_indices(const crypto::hash& h) const { return std::vector(); } + virtual std::vector get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector(); } + virtual bool has_key_image(const crypto::key_image& img) const { return false; } + virtual void remove_block() { } + virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const cryptonote::transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;} + virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) {} + virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;} + virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector& amount_output_indices) {} + virtual void add_spent_key(const crypto::key_image& k_image) {} + virtual void remove_spent_key(const crypto::key_image& k_image) {} + + virtual bool for_all_key_images(std::function) const { return true; } + virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function) const { return true; } + virtual bool for_all_transactions(std::function, bool pruned) const { return true; } + virtual bool for_all_outputs(std::function f) const { return true; } + virtual bool for_all_outputs(uint64_t amount, const std::function &f) const { return true; } + virtual bool is_read_only() const { return false; } + virtual std::map> get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map>(); } + virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const { return false; } + + virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) {} + virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) {} + virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } + virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } + virtual void remove_txpool_tx(const crypto::hash& txid) {} + virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const { return false; } + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } + virtual uint64_t get_database_size() const { return 0; } + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } + virtual bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) { } + virtual cryptonote::block get_block_from_height(const uint64_t& height) const { return cryptonote::block(); } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) {} + virtual uint8_t get_hard_fork_version(uint64_t height) const { return 0; } + virtual void check_hard_fork_info() {} + + virtual uint32_t get_blockchain_pruning_seed() const { return 0; } + virtual bool prune_blockchain(uint32_t pruning_seed = 0) { return true; } + virtual bool update_pruning() { return true; } + virtual bool check_pruning() { return true; } +}; +