aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/blockchain_converter/CMakeLists.txt53
-rw-r--r--src/blockchain_converter/blockchain_converter.cpp115
-rw-r--r--src/cryptonote_core/BlockchainDB_impl/db_lmdb.cpp1585
-rw-r--r--src/cryptonote_core/BlockchainDB_impl/db_lmdb.h269
-rw-r--r--src/cryptonote_core/CMakeLists.txt7
-rw-r--r--src/cryptonote_core/blockchain.cpp2361
-rw-r--r--src/cryptonote_core/blockchain.h226
-rw-r--r--src/cryptonote_core/blockchain_db.cpp122
-rw-r--r--src/cryptonote_core/blockchain_db.h467
-rw-r--r--src/cryptonote_core/blockchain_storage.cpp148
-rw-r--r--src/cryptonote_core/blockchain_storage.h119
-rw-r--r--src/cryptonote_core/checkpoints.cpp12
-rw-r--r--src/cryptonote_core/checkpoints.h6
-rw-r--r--src/cryptonote_core/cryptonote_basic.h2
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp12
-rw-r--r--src/cryptonote_core/cryptonote_core.h12
-rw-r--r--src/cryptonote_core/tx_pool.cpp13
-rw-r--r--src/cryptonote_core/tx_pool.h16
19 files changed, 5400 insertions, 147 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a80dfe378..7c4492b01 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -97,3 +97,5 @@ add_subdirectory(connectivity_tool)
add_subdirectory(miner)
add_subdirectory(simplewallet)
add_subdirectory(daemon)
+
+add_subdirectory(blockchain_converter)
diff --git a/src/blockchain_converter/CMakeLists.txt b/src/blockchain_converter/CMakeLists.txt
new file mode 100644
index 000000000..fa2c7bafc
--- /dev/null
+++ b/src/blockchain_converter/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (c) 2014-2015, 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.
+
+set(blockchain_converter_sources
+ blockchain_converter.cpp
+ )
+
+set(blockchain_converter_private_headers)
+
+bitmonero_private_headers(blockchain_converter
+ ${blockchain_converter_private_headers})
+
+if (BLOCKCHAIN_DB STREQUAL DB_LMDB)
+bitmonero_add_executable(blockchain_converter
+ ${blockchain_converter_sources}
+ ${blockchain_converter_private_headers})
+
+target_link_libraries(blockchain_converter
+ LINK_PRIVATE
+ cryptonote_core
+ ${CMAKE_THREAD_LIBS_INIT})
+
+add_dependencies(blockchain_converter
+ version)
+set_property(TARGET blockchain_converter
+ PROPERTY
+ OUTPUT_NAME "blockchain_converter")
+endif ()
diff --git a/src/blockchain_converter/blockchain_converter.cpp b/src/blockchain_converter/blockchain_converter.cpp
new file mode 100644
index 000000000..57822700f
--- /dev/null
+++ b/src/blockchain_converter/blockchain_converter.cpp
@@ -0,0 +1,115 @@
+// Copyright (c) 2014, 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 "include_base_utils.h"
+#include "common/util.h"
+#include "warnings.h"
+#include "crypto/crypto.h"
+#include "cryptonote_config.h"
+#include "cryptonote_core/cryptonote_format_utils.h"
+#include "misc_language.h"
+#include "cryptonote_core/blockchain_storage.h"
+#include "cryptonote_core/blockchain_db.h"
+#include "cryptonote_core/blockchain.h"
+#include "cryptonote_core/BlockchainDB_impl/db_lmdb.h"
+#include "cryptonote_core/tx_pool.h"
+#include <iostream>
+
+using namespace cryptonote;
+
+struct fake_core
+{
+ tx_memory_pool m_pool;
+ Blockchain dummy;
+
+ blockchain_storage m_storage;
+
+
+ fake_core(const boost::filesystem::path &path) : m_pool(dummy), dummy(m_pool), m_storage(&m_pool)
+ {
+ m_pool.init(path.string());
+ m_storage.init(path.string(), false);
+ }
+};
+
+int main(int argc, char* argv[])
+{
+ std::string dir = tools::get_default_data_dir();
+ boost::filesystem::path default_data_path {dir};
+ if (argc >= 2 && !strcmp(argv[1], "--testnet")) {
+ default_data_path /= "testnet";
+ }
+
+ fake_core c(default_data_path);
+
+ BlockchainDB *blockchain;
+
+ blockchain = new BlockchainLMDB();
+
+ blockchain->open(default_data_path.string());
+
+ for (uint64_t height, i = 0; i < (height = c.m_storage.get_current_blockchain_height()); ++i)
+ {
+ if (i % 10 == 0)
+ {
+ std::cout << "\r \r" << "block " << i << "/" << height
+ << " (" << (i+1)*100/height<< "%)" << std::flush;
+ }
+ block b = c.m_storage.get_block(i);
+ size_t bsize = c.m_storage.get_block_size(i);
+ difficulty_type bdiff = c.m_storage.get_block_cumulative_difficulty(i);
+ uint64_t bcoins = c.m_storage.get_block_coins_generated(i);
+ std::vector<transaction> txs;
+ std::vector<crypto::hash> missed;
+
+ c.m_storage.get_transactions(b.tx_hashes, txs, missed);
+ if (missed.size())
+ {
+ std::cout << std::endl;
+ std::cerr << "Missed transaction(s) for block at height " << i << ", exiting" << std::endl;
+ delete blockchain;
+ return 1;
+ }
+
+ try
+ {
+ blockchain->add_block(b, bsize, bdiff, bcoins, txs);
+ }
+ catch (const std::exception& e)
+ {
+ std::cout << std::endl;
+ std::cerr << "Error adding block to new blockchain: " << e.what() << std::endl;
+ delete blockchain;
+ return 2;
+ }
+ }
+
+ std::cout << std::endl;
+ delete blockchain;
+ return 0;
+}
diff --git a/src/cryptonote_core/BlockchainDB_impl/db_lmdb.cpp b/src/cryptonote_core/BlockchainDB_impl/db_lmdb.cpp
new file mode 100644
index 000000000..4fa16b23e
--- /dev/null
+++ b/src/cryptonote_core/BlockchainDB_impl/db_lmdb.cpp
@@ -0,0 +1,1585 @@
+// Copyright (c) 2014, 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 "db_lmdb.h"
+
+#include <boost/filesystem.hpp>
+#include <memory> // std::unique_ptr
+#include <cstring> // memcpy
+
+#include "cryptonote_core/cryptonote_format_utils.h"
+#include "crypto/crypto.h"
+
+using epee::string_tools::pod_to_hex;
+
+namespace
+{
+
+template <typename T>
+inline void throw0(const T &e)
+{
+ LOG_PRINT_L0(e.what());
+ throw e;
+}
+
+template <typename T>
+inline void throw1(const T &e)
+{
+ LOG_PRINT_L1(e.what());
+ throw e;
+}
+
+// cursor needs to be closed when it goes out of scope,
+// this helps if the function using it throws
+struct lmdb_cur
+{
+ lmdb_cur(MDB_txn* txn, MDB_dbi dbi)
+ {
+ if (mdb_cursor_open(txn, dbi, &m_cur))
+ throw0(cryptonote::DB_ERROR("Error opening db cursor"));
+ done = false;
+ }
+
+ ~lmdb_cur() { close(); }
+
+ operator MDB_cursor*() { return m_cur; }
+ operator MDB_cursor**() { return &m_cur; }
+
+ void close()
+ {
+ if (!done)
+ {
+ mdb_cursor_close(m_cur);
+ done = true;
+ }
+ }
+
+private:
+ MDB_cursor* m_cur;
+ bool done;
+};
+
+template<typename T>
+struct MDB_val_copy: public MDB_val
+{
+ MDB_val_copy(const T &t): t_copy(t)
+ {
+ mv_size = sizeof (T);
+ mv_data = &t_copy;
+ }
+private:
+ T t_copy;
+};
+
+template<>
+struct MDB_val_copy<cryptonote::blobdata>: public MDB_val
+{
+ MDB_val_copy(const cryptonote::blobdata &bd): data(new char[bd.size()])
+ {
+ memcpy(data.get(), bd.data(), bd.size());
+ mv_size = bd.size();
+ mv_data = data.get();
+ }
+private:
+ std::unique_ptr<char[]> data;
+};
+
+auto compare_uint64 = [](const MDB_val *a, const MDB_val *b) {
+ const uint64_t va = *(const uint64_t*)a->mv_data;
+ const uint64_t vb = *(const uint64_t*)b->mv_data;
+ if (va < vb) return -1;
+ else if (va == vb) return 0;
+ else return 1;
+};
+
+const char* const LMDB_BLOCKS = "blocks";
+const char* const LMDB_BLOCK_TIMESTAMPS = "block_timestamps";
+const char* const LMDB_BLOCK_HEIGHTS = "block_heights";
+const char* const LMDB_BLOCK_HASHES = "block_hashes";
+const char* const LMDB_BLOCK_SIZES = "block_sizes";
+const char* const LMDB_BLOCK_DIFFS = "block_diffs";
+const char* const LMDB_BLOCK_COINS = "block_coins";
+
+const char* const LMDB_TXS = "txs";
+const char* const LMDB_TX_UNLOCKS = "tx_unlocks";
+const char* const LMDB_TX_HEIGHTS = "tx_heights";
+const char* const LMDB_TX_OUTPUTS = "tx_outputs";
+
+const char* const LMDB_OUTPUT_TXS = "output_txs";
+const char* const LMDB_OUTPUT_INDICES = "output_indices";
+const char* const LMDB_OUTPUT_AMOUNTS = "output_amounts";
+const char* const LMDB_OUTPUT_KEYS = "output_keys";
+const char* const LMDB_OUTPUTS = "outputs";
+const char* const LMDB_OUTPUT_GINDICES = "output_gindices";
+const char* const LMDB_SPENT_KEYS = "spent_keys";
+
+inline void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi, const std::string& error_string)
+{
+ if (mdb_dbi_open(txn, name, flags, &dbi))
+ throw0(cryptonote::DB_OPEN_FAILURE(error_string.c_str()));
+}
+
+} // anonymous namespace
+
+namespace cryptonote
+{
+
+void BlockchainLMDB::add_block( const block& blk
+ , const size_t& block_size
+ , const difficulty_type& cumulative_difficulty
+ , const uint64_t& coins_generated
+ )
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ MDB_val_copy<crypto::hash> val_h(get_block_hash(blk));
+ MDB_val unused;
+ if (mdb_get(*m_write_txn, m_block_heights, &val_h, &unused) == 0)
+ throw1(BLOCK_EXISTS("Attempting to add block that's already in the db"));
+
+ if (m_height > 0)
+ {
+ MDB_val_copy<crypto::hash> parent_key(blk.prev_id);
+ MDB_val parent_h;
+ if (mdb_get(*m_write_txn, m_block_heights, &parent_key, &parent_h))
+ throw0(DB_ERROR("Failed to get top block hash to check for new block's parent"));
+
+ uint64_t parent_height = *(const uint64_t *)parent_h.mv_data;
+ if (parent_height != m_height - 1)
+ throw0(BLOCK_PARENT_DNE("Top block is not new block's parent"));
+ }
+
+ MDB_val_copy<uint64_t> key(m_height);
+
+ MDB_val_copy<blobdata> blob(block_to_blob(blk));
+ if (mdb_put(*m_write_txn, m_blocks, &key, &blob, 0))
+ throw0(DB_ERROR("Failed to add block blob to db transaction"));
+
+ MDB_val_copy<size_t> sz(block_size);
+ if (mdb_put(*m_write_txn, m_block_sizes, &key, &sz, 0))
+ throw0(DB_ERROR("Failed to add block size to db transaction"));
+
+ MDB_val_copy<uint64_t> ts(blk.timestamp);
+ if (mdb_put(*m_write_txn, m_block_timestamps, &key, &ts, 0))
+ throw0(DB_ERROR("Failed to add block timestamp to db transaction"));
+
+ MDB_val_copy<difficulty_type> diff(cumulative_difficulty);
+ if (mdb_put(*m_write_txn, m_block_diffs, &key, &diff, 0))
+ throw0(DB_ERROR("Failed to add block cumulative difficulty to db transaction"));
+
+ MDB_val_copy<uint64_t> coinsgen(coins_generated);
+ if (mdb_put(*m_write_txn, m_block_coins, &key, &coinsgen, 0))
+ throw0(DB_ERROR("Failed to add block total generated coins to db transaction"));
+
+ if (mdb_put(*m_write_txn, m_block_heights, &val_h, &key, 0))
+ throw0(DB_ERROR("Failed to add block height by hash to db transaction"));
+
+ if (mdb_put(*m_write_txn, m_block_hashes, &key, &val_h, 0))
+ throw0(DB_ERROR("Failed to add block hash to db transaction"));
+
+}
+
+void BlockchainLMDB::remove_block()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ if (m_height == 0)
+ throw0(BLOCK_DNE ("Attempting to remove block from an empty blockchain"));
+
+ MDB_val_copy<uint64_t> k(m_height - 1);
+ MDB_val h;
+ if (mdb_get(*m_write_txn, m_block_hashes, &k, &h))
+ throw1(BLOCK_DNE("Attempting to remove block that's not in the db"));
+
+ if (mdb_del(*m_write_txn, m_blocks, &k, NULL))
+ throw1(DB_ERROR("Failed to add removal of block to db transaction"));
+
+ if (mdb_del(*m_write_txn, m_block_sizes, &k, NULL))
+ throw1(DB_ERROR("Failed to add removal of block size to db transaction"));
+
+ if (mdb_del(*m_write_txn, m_block_diffs, &k, NULL))
+ throw1(DB_ERROR("Failed to add removal of block cumulative difficulty to db transaction"));
+
+ if (mdb_del(*m_write_txn, m_block_coins, &k, NULL))
+ throw1(DB_ERROR("Failed to add removal of block total generated coins to db transaction"));
+
+ if (mdb_del(*m_write_txn, m_block_timestamps, &k, NULL))
+ throw1(DB_ERROR("Failed to add removal of block timestamp to db transaction"));
+
+ if (mdb_del(*m_write_txn, m_block_heights, &h, NULL))
+ throw1(DB_ERROR("Failed to add removal of block height by hash to db transaction"));
+
+ if (mdb_del(*m_write_txn, m_block_hashes, &k, NULL))
+ throw1(DB_ERROR("Failed to add removal of block hash to db transaction"));
+}
+
+void BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const transaction& tx)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ MDB_val_copy<crypto::hash> val_h(get_transaction_hash(tx));
+ MDB_val unused;
+ if (mdb_get(*m_write_txn, m_txs, &val_h, &unused) == 0)
+ throw1(TX_EXISTS("Attempting to add transaction that's already in the db"));
+
+ MDB_val_copy<blobdata> blob(tx_to_blob(tx));
+ if (mdb_put(*m_write_txn, m_txs, &val_h, &blob, 0))
+ throw0(DB_ERROR("Failed to add tx blob to db transaction"));
+
+ MDB_val_copy<uint64_t> height(m_height);
+ if (mdb_put(*m_write_txn, m_tx_heights, &val_h, &height, 0))
+ throw0(DB_ERROR("Failed to add tx block height to db transaction"));
+
+ MDB_val_copy<uint64_t> unlock_time(tx.unlock_time);
+ if (mdb_put(*m_write_txn, m_tx_unlocks, &val_h, &unlock_time, 0))
+ throw0(DB_ERROR("Failed to add tx unlock time to db transaction"));
+}
+
+void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ MDB_val_copy<crypto::hash> val_h(tx_hash);
+ MDB_val unused;
+ if (mdb_get(*m_write_txn, m_txs, &val_h, &unused))
+ throw1(TX_DNE("Attempting to remove transaction that isn't in the db"));
+
+ if (mdb_del(*m_write_txn, m_txs, &val_h, NULL))
+ throw1(DB_ERROR("Failed to add removal of tx to db transaction"));
+ if (mdb_del(*m_write_txn, m_tx_unlocks, &val_h, NULL))
+ throw1(DB_ERROR("Failed to add removal of tx unlock time to db transaction"));
+ if (mdb_del(*m_write_txn, m_tx_heights, &val_h, NULL))
+ throw1(DB_ERROR("Failed to add removal of tx block height to db transaction"));
+
+ remove_tx_outputs(tx_hash, tx);
+
+ if (mdb_del(*m_write_txn, m_tx_outputs, &val_h, NULL))
+ throw1(DB_ERROR("Failed to add removal of tx outputs to db transaction"));
+
+}
+
+void BlockchainLMDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ MDB_val_copy<uint64_t> k(m_num_outputs);
+ MDB_val_copy<crypto::hash> v(tx_hash);
+
+ if (mdb_put(*m_write_txn, m_output_txs, &k, &v, 0))
+ throw0(DB_ERROR("Failed to add output tx hash to db transaction"));
+ if (mdb_put(*m_write_txn, m_tx_outputs, &v, &k, 0))
+ throw0(DB_ERROR("Failed to add tx output index to db transaction"));
+
+ MDB_val_copy<uint64_t> val_local_index(local_index);
+ if (mdb_put(*m_write_txn, m_output_indices, &k, &val_local_index, 0))
+ throw0(DB_ERROR("Failed to add tx output index to db transaction"));
+
+ MDB_val_copy<uint64_t> val_amount(tx_output.amount);
+ if (auto result = mdb_put(*m_write_txn, m_output_amounts, &val_amount, &k, 0))
+ throw0(DB_ERROR(std::string("Failed to add output amount to db transaction").append(mdb_strerror(result)).c_str()));
+
+ if (tx_output.target.type() == typeid(txout_to_key))
+ {
+ MDB_val_copy<crypto::public_key> val_pubkey(boost::get<txout_to_key>(tx_output.target).key);
+ if (mdb_put(*m_write_txn, m_output_keys, &k, &val_pubkey, 0))
+ throw0(DB_ERROR("Failed to add output pubkey to db transaction"));
+ }
+
+
+/****** Uncomment if ever outputs actually need to be stored in this manner
+ *
+ blobdata b = output_to_blob(tx_output);
+
+ v.mv_size = b.size();
+ v.mv_data = &b;
+ if (mdb_put(*m_write_txn, m_outputs, &k, &v, 0))
+ throw0(DB_ERROR("Failed to add output to db transaction"));
+ if (mdb_put(*m_write_txn, m_output_gindices, &v, &k, 0))
+ throw0(DB_ERROR("Failed to add output global index to db transaction"));
+************************************************************************/
+
+ m_num_outputs++;
+}
+
+void BlockchainLMDB::remove_tx_outputs(const crypto::hash& tx_hash, const transaction& tx)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+
+ lmdb_cur cur(*m_write_txn, m_tx_outputs);
+
+ MDB_val_copy<crypto::hash> k(tx_hash);
+ MDB_val v;
+
+ auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ {
+ LOG_ERROR("Attempting to remove a tx's outputs, but none found. Continuing, but...be wary, because that's weird.");
+ }
+ else if (result)
+ {
+ throw0(DB_ERROR("DB error attempting to get an output"));
+ }
+ else
+ {
+ size_t num_elems = 0;
+ mdb_cursor_count(cur, &num_elems);
+
+ mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+
+ for (uint64_t i = 0; i < num_elems; ++i)
+ {
+ const tx_out tx_output = tx.vout[i];
+ remove_output(*(const uint64_t*)v.mv_data, tx_output.amount);
+ if (i < num_elems - 1)
+ {
+ mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ }
+ }
+ }
+
+ cur.close();
+}
+
+// TODO: probably remove this function
+void BlockchainLMDB::remove_output(const tx_out& tx_output)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " (unused version - does nothing)");
+ return;
+}
+
+void BlockchainLMDB::remove_output(const uint64_t& out_index, const uint64_t amount)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ MDB_val_copy<uint64_t> k(out_index);
+ MDB_val v;
+
+/****** Uncomment if ever outputs actually need to be stored in this manner
+ blobdata b;
+ t_serializable_object_to_blob(tx_output, b);
+ k.mv_size = b.size();
+ k.mv_data = &b;
+
+ if (mdb_get(*m_write_txn, m_output_gindices, &k, &v))
+ throw1(OUTPUT_DNE("Attempting to remove output that does not exist"));
+
+ uint64_t gindex = *(uint64_t*)v.mv_data;
+
+ auto result = mdb_del(*m_write_txn, m_output_gindices, &k, NULL);
+ if (result != 0 && result != MDB_NOTFOUND)
+ throw1(DB_ERROR("Error adding removal of output global index to db transaction"));
+
+ result = mdb_del(*m_write_txn, m_outputs, &v, NULL);
+ if (result != 0 && result != MDB_NOTFOUND)
+ throw1(DB_ERROR("Error adding removal of output to db transaction"));
+*********************************************************************/
+
+ auto result = mdb_del(*m_write_txn, m_output_indices, &k, NULL);
+ if (result == MDB_NOTFOUND)
+ {
+ LOG_PRINT_L0("Unexpected: global output index not found in m_output_indices");
+ }
+ else if (result)
+ {
+ throw1(DB_ERROR("Error adding removal of output tx index to db transaction"));
+ }
+
+ result = mdb_del(*m_write_txn, m_output_txs, &k, NULL);
+ // if (result != 0 && result != MDB_NOTFOUND)
+ // throw1(DB_ERROR("Error adding removal of output tx hash to db transaction"));
+ if (result == MDB_NOTFOUND)
+ {
+ LOG_PRINT_L0("Unexpected: global output index not found in m_output_txs");
+ }
+ else if (result)
+ {
+ throw1(DB_ERROR("Error adding removal of output tx hash to db transaction"));
+ }
+
+ result = mdb_del(*m_write_txn, m_output_keys, &k, NULL);
+ if (result == MDB_NOTFOUND)
+ {
+ LOG_PRINT_L0("Unexpected: global output index not found in m_output_keys");
+ }
+ else if (result)
+ throw1(DB_ERROR("Error adding removal of output pubkey to db transaction"));
+
+ remove_amount_output_index(amount, out_index);
+
+ m_num_outputs--;
+}
+
+void BlockchainLMDB::remove_amount_output_index(const uint64_t amount, const uint64_t global_output_index)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ lmdb_cur cur(*m_write_txn, m_output_amounts);
+
+ MDB_val_copy<uint64_t> k(amount);
+ MDB_val v;
+
+ auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
+ else if (result)
+ throw0(DB_ERROR("DB error attempting to get an output"));
+
+ size_t num_elems = 0;
+ mdb_cursor_count(cur, &num_elems);
+
+ mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+
+ uint64_t amount_output_index = 0;
+ uint64_t goi = 0;
+ bool found_index = false;
+ for (uint64_t i = 0; i < num_elems; ++i)
+ {
+ mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+ goi = *(const uint64_t *)v.mv_data;
+ if (goi == global_output_index)
+ {
+ amount_output_index = i;
+ found_index = true;
+ break;
+ }
+ mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ }
+ if (found_index)
+ {
+ // found the amount output index
+ // now delete it
+ result = mdb_cursor_del(cur, 0);
+ if (result)
+ throw0(DB_ERROR(std::string("Error deleting amount output index ").append(boost::lexical_cast<std::string>(amount_output_index)).c_str()));
+ }
+ else
+ {
+ // not found
+ cur.close();
+ throw1(OUTPUT_DNE("Failed to find amount output index"));
+ }
+ cur.close();
+}
+
+void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ MDB_val_copy<crypto::key_image> val_key(k_image);
+ MDB_val unused;
+ if (mdb_get(*m_write_txn, m_spent_keys, &val_key, &unused) == 0)
+ throw1(KEY_IMAGE_EXISTS("Attempting to add spent key image that's already in the db"));
+
+ char anything = '\0';
+ unused.mv_size = sizeof(char);
+ unused.mv_data = &anything;
+ if (mdb_put(*m_write_txn, m_spent_keys, &val_key, &unused, 0))
+ throw1(DB_ERROR("Error adding spent key image to db transaction"));
+}
+
+void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ MDB_val_copy<crypto::key_image> k(k_image);
+ auto result = mdb_del(*m_write_txn, m_spent_keys, &k, NULL);
+ if (result != 0 && result != MDB_NOTFOUND)
+ throw1(DB_ERROR("Error adding removal of key image to db transaction"));
+}
+
+blobdata BlockchainLMDB::output_to_blob(const tx_out& output)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ blobdata b;
+ if (!t_serializable_object_to_blob(output, b))
+ throw1(DB_ERROR("Error serializing output to blob"));
+ return b;
+}
+
+tx_out BlockchainLMDB::output_from_blob(const blobdata& blob) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ std::stringstream ss;
+ ss << blob;
+ binary_archive<false> ba(ss);
+ tx_out o;
+
+ if (!(::serialization::serialize(ba, o)))
+ throw1(DB_ERROR("Error deserializing tx output blob"));
+
+ return o;
+}
+
+uint64_t BlockchainLMDB::get_output_global_index(const uint64_t& amount, const uint64_t& index) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ lmdb_cur cur(txn, m_output_amounts);
+
+ MDB_val_copy<uint64_t> k(amount);
+ MDB_val v;
+
+ auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
+ else if (result)
+ throw0(DB_ERROR("DB error attempting to get an output"));
+
+ size_t num_elems = 0;
+ mdb_cursor_count(cur, &num_elems);
+ if (num_elems <= index)
+ throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found"));
+
+ mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+
+ for (uint64_t i = 0; i < index; ++i)
+ {
+ mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ }
+
+ mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+
+ uint64_t glob_index = *(const uint64_t*)v.mv_data;
+
+ cur.close();
+
+ txn.commit();
+
+ return glob_index;
+}
+
+void BlockchainLMDB::check_open() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ if (!m_open)
+ throw0(DB_ERROR("DB operation attempted on a not-open DB instance"));
+}
+
+BlockchainLMDB::~BlockchainLMDB()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+}
+
+BlockchainLMDB::BlockchainLMDB()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ // initialize folder to something "safe" just in case
+ // someone accidentally misuses this class...
+ m_folder = "thishsouldnotexistbecauseitisgibberish";
+ m_open = false;
+ m_height = 0;
+}
+
+void BlockchainLMDB::open(const std::string& filename)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+
+ if (m_open)
+ throw0(DB_OPEN_FAILURE("Attempted to open db, but it's already open"));
+
+ boost::filesystem::path direc(filename);
+ if (boost::filesystem::exists(direc))
+ {
+ if (!boost::filesystem::is_directory(direc))
+ throw0(DB_OPEN_FAILURE("LMDB needs a directory path, but a file was passed"));
+ }
+ else
+ {
+ if (!boost::filesystem::create_directory(direc))
+ throw0(DB_OPEN_FAILURE(std::string("Failed to create directory ").append(filename).c_str()));
+ }
+
+ m_folder = filename;
+
+ // set up lmdb environment
+ if (mdb_env_create(&m_env))
+ throw0(DB_ERROR("Failed to create lmdb environment"));
+ if (mdb_env_set_maxdbs(m_env, 20))
+ throw0(DB_ERROR("Failed to set max number of dbs"));
+
+ size_t mapsize = 1LL << 34;
+ if (auto result = mdb_env_set_mapsize(m_env, mapsize))
+ throw0(DB_ERROR(std::string("Failed to set max memory map size: ").append(mdb_strerror(result)).c_str()));
+ if (auto result = mdb_env_open(m_env, filename.c_str(), 0, 0644))
+ throw0(DB_ERROR(std::string("Failed to open lmdb environment: ").append(mdb_strerror(result)).c_str()));
+
+ // get a read/write MDB_txn
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, 0, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ // open necessary databases, and set properties as needed
+ // uses macros to avoid having to change things too many places
+ lmdb_db_open(txn, LMDB_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_blocks, "Failed to open db handle for m_blocks");
+
+ lmdb_db_open(txn, LMDB_BLOCK_TIMESTAMPS, MDB_INTEGERKEY | MDB_CREATE, m_block_timestamps, "Failed to open db handle for m_block_timestamps");
+ lmdb_db_open(txn, LMDB_BLOCK_HEIGHTS, MDB_CREATE, m_block_heights, "Failed to open db handle for m_block_heights");
+ lmdb_db_open(txn, LMDB_BLOCK_HASHES, MDB_INTEGERKEY | MDB_CREATE, m_block_hashes, "Failed to open db handle for m_block_hashes");
+ lmdb_db_open(txn, LMDB_BLOCK_SIZES, MDB_INTEGERKEY | MDB_CREATE, m_block_sizes, "Failed to open db handle for m_block_sizes");
+ lmdb_db_open(txn, LMDB_BLOCK_DIFFS, MDB_INTEGERKEY | MDB_CREATE, m_block_diffs, "Failed to open db handle for m_block_diffs");
+ lmdb_db_open(txn, LMDB_BLOCK_COINS, MDB_INTEGERKEY | MDB_CREATE, m_block_coins, "Failed to open db handle for m_block_coins");
+
+ lmdb_db_open(txn, LMDB_TXS, MDB_CREATE, m_txs, "Failed to open db handle for m_txs");
+ lmdb_db_open(txn, LMDB_TX_UNLOCKS, MDB_CREATE, m_tx_unlocks, "Failed to open db handle for m_tx_unlocks");
+ lmdb_db_open(txn, LMDB_TX_HEIGHTS, MDB_CREATE, m_tx_heights, "Failed to open db handle for m_tx_heights");
+ lmdb_db_open(txn, LMDB_TX_OUTPUTS, MDB_DUPSORT | MDB_CREATE, m_tx_outputs, "Failed to open db handle for m_tx_outputs");
+
+ lmdb_db_open(txn, LMDB_OUTPUT_TXS, MDB_INTEGERKEY | MDB_CREATE, m_output_txs, "Failed to open db handle for m_output_txs");
+ lmdb_db_open(txn, LMDB_OUTPUT_INDICES, MDB_INTEGERKEY | MDB_CREATE, m_output_indices, "Failed to open db handle for m_output_indices");
+ lmdb_db_open(txn, LMDB_OUTPUT_AMOUNTS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_CREATE, m_output_amounts, "Failed to open db handle for m_output_amounts");
+ lmdb_db_open(txn, LMDB_OUTPUT_KEYS, MDB_INTEGERKEY | MDB_CREATE, m_output_keys, "Failed to open db handle for m_output_keys");
+
+/*************** not used, but kept for posterity
+ lmdb_db_open(txn, LMDB_OUTPUTS, MDB_INTEGERKEY | MDB_CREATE, m_outputs, "Failed to open db handle for m_outputs");
+ lmdb_db_open(txn, LMDB_OUTPUT_GINDICES, MDB_CREATE, m_output_gindices, "Failed to open db handle for m_output_gindices");
+*************************************************/
+
+ lmdb_db_open(txn, LMDB_SPENT_KEYS, MDB_CREATE, m_spent_keys, "Failed to open db handle for m_outputs");
+
+ mdb_set_dupsort(txn, m_output_amounts, compare_uint64);
+ mdb_set_dupsort(txn, m_tx_outputs, compare_uint64);
+
+ // get and keep current height
+ MDB_stat db_stats;
+ if (mdb_stat(txn, m_blocks, &db_stats))
+ throw0(DB_ERROR("Failed to query m_blocks"));
+ LOG_PRINT_L2("Setting m_height to: " << db_stats.ms_entries);
+ m_height = db_stats.ms_entries;
+
+ // get and keep current number of outputs
+ if (mdb_stat(txn, m_output_indices, &db_stats))
+ throw0(DB_ERROR("Failed to query m_output_indices"));
+ m_num_outputs = db_stats.ms_entries;
+
+ // commit the transaction
+ txn.commit();
+
+ m_open = true;
+ // from here, init should be finished
+}
+
+// unused for now, create will happen on open if doesn't exist
+void BlockchainLMDB::create(const std::string& filename)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ throw DB_CREATE_FAILURE("create() is not implemented for this BlockchainDB, open() will create files if needed.");
+}
+
+void BlockchainLMDB::close()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ // FIXME: not yet thread safe!!! Use with care.
+ mdb_env_close(m_env);
+}
+
+void BlockchainLMDB::sync()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ // LMDB documentation leads me to believe this is unnecessary
+}
+
+void BlockchainLMDB::reset()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ // TODO: this
+}
+
+std::vector<std::string> BlockchainLMDB::get_filenames() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ std::vector<std::string> filenames;
+
+ boost::filesystem::path datafile(m_folder);
+ datafile /= "data.mdb";
+ boost::filesystem::path lockfile(m_folder);
+ lockfile /= "lock.mdb";
+
+ filenames.push_back(datafile.string());
+ filenames.push_back(lockfile.string());
+
+ return filenames;
+}
+
+// TODO: this?
+bool BlockchainLMDB::lock()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ return false;
+}
+
+// TODO: this?
+void BlockchainLMDB::unlock()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+}
+
+
+bool BlockchainLMDB::block_exists(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<crypto::hash> key(h);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_block_heights, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ txn.commit();
+ LOG_PRINT_L1("Block with hash " << epee::string_tools::pod_to_hex(h) << "not found in db");
+ return false;
+ }
+ else if (get_result)
+ throw0(DB_ERROR("DB error attempting to fetch block index from hash"));
+
+ txn.commit();
+ return true;
+}
+
+block BlockchainLMDB::get_block(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ return get_block_from_height(get_block_height(h));
+}
+
+uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<crypto::hash> key(h);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_block_heights, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ throw1(BLOCK_DNE("Attempted to retrieve non-existent block height"));
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve a block height from the db"));
+
+ txn.commit();
+ return *(const uint64_t*)result.mv_data;
+}
+
+block_header BlockchainLMDB::get_block_header(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ // block_header object is automatically cast from block object
+ return get_block(h);
+}
+
+block BlockchainLMDB::get_block_from_height(const uint64_t& height) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> key(height);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_blocks, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ throw0(DB_ERROR(std::string("Attempt to get block from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block not in db").c_str()));
+ }
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve a block from the db"));
+
+ txn.commit();
+
+ blobdata bd;
+ bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size);
+
+ block b;
+ if (!parse_and_validate_block_from_blob(bd, b))
+ throw0(DB_ERROR("Failed to parse block from blob retrieved from the db"));
+
+ return b;
+}
+
+uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> key(height);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_block_timestamps, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ throw0(DB_ERROR(std::string("Attempt to get timestamp from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- timestamp not in db").c_str()));
+ }
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve a timestamp from the db"));
+
+ txn.commit();
+ return *(const uint64_t*)result.mv_data;
+}
+
+uint64_t BlockchainLMDB::get_top_block_timestamp() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ // if no blocks, return 0
+ if (m_height == 0)
+ {
+ return 0;
+ }
+
+ return get_block_timestamp(m_height - 1);
+}
+
+size_t BlockchainLMDB::get_block_size(const uint64_t& height) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> key(height);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_block_sizes, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ throw0(DB_ERROR(std::string("Attempt to get block size from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block size not in db").c_str()));
+ }
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve a block size from the db"));
+
+ txn.commit();
+ return *(const size_t*)result.mv_data;
+}
+
+difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t& height) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> key(height);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_block_diffs, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ throw0(DB_ERROR(std::string("Attempt to get cumulative difficulty from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- difficulty not in db").c_str()));
+ }
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve a cumulative difficulty from the db"));
+
+ txn.commit();
+ return *(difficulty_type*)result.mv_data;
+}
+
+difficulty_type BlockchainLMDB::get_block_difficulty(const uint64_t& height) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ difficulty_type diff1 = 0;
+ difficulty_type diff2 = 0;
+
+ diff1 = get_block_cumulative_difficulty(height);
+ if (height != 0)
+ {
+ diff2 = get_block_cumulative_difficulty(height - 1);
+ }
+
+ return diff1 - diff2;
+}
+
+uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& height) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> key(height);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_block_coins, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ throw0(DB_ERROR(std::string("Attempt to get generated coins from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block size not in db").c_str()));
+ }
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve a total generated coins from the db"));
+
+ txn.commit();
+ return *(const uint64_t*)result.mv_data;
+}
+
+crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> key(height);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_block_hashes, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ throw0(BLOCK_DNE(std::string("Attempt to get hash from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- hash not in db").c_str()));
+ }
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve a block hash from the db"));
+
+ txn.commit();
+ return *(crypto::hash*)result.mv_data;
+}
+
+std::vector<block> BlockchainLMDB::get_blocks_range(const uint64_t& h1, const uint64_t& h2) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ std::vector<block> v;
+
+ for (uint64_t height = h1; height <= h2; ++height)
+ {
+ v.push_back(get_block_from_height(height));
+ }
+
+ return v;
+}
+
+std::vector<crypto::hash> BlockchainLMDB::get_hashes_range(const uint64_t& h1, const uint64_t& h2) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ std::vector<crypto::hash> v;
+
+ for (uint64_t height = h1; height <= h2; ++height)
+ {
+ v.push_back(get_block_hash_from_height(height));
+ }
+
+ return v;
+}
+
+crypto::hash BlockchainLMDB::top_block_hash() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ if (m_height != 0)
+ {
+ return get_block_hash_from_height(m_height - 1);
+ }
+
+ return null_hash;
+}
+
+block BlockchainLMDB::get_top_block() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ if (m_height != 0)
+ {
+ return get_block_from_height(m_height - 1);
+ }
+
+ block b;
+ return b;
+}
+
+uint64_t BlockchainLMDB::height() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ return m_height;
+}
+
+
+bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<crypto::hash> key(h);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_txs, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ txn.commit();
+ LOG_PRINT_L1("transaction with hash " << epee::string_tools::pod_to_hex(h) << "not found in db");
+ return false;
+ }
+ else if (get_result)
+ throw0(DB_ERROR("DB error attempting to fetch transaction from hash"));
+
+ return true;
+}
+
+uint64_t BlockchainLMDB::get_tx_unlock_time(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<crypto::hash> key(h);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_tx_unlocks, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ throw1(TX_DNE(std::string("tx unlock time with hash ").append(epee::string_tools::pod_to_hex(h)).append("not found in db").c_str()));
+ else if (get_result)
+ throw0(DB_ERROR("DB error attempting to fetch tx unlock time from hash"));
+
+ return *(const uint64_t*)result.mv_data;
+}
+
+transaction BlockchainLMDB::get_tx(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<crypto::hash> key(h);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_txs, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append("not found in db").c_str()));
+ else if (get_result)
+ throw0(DB_ERROR("DB error attempting to fetch tx from hash"));
+
+ blobdata bd;
+ bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size);
+
+ transaction tx;
+ if (!parse_and_validate_tx_from_blob(bd, tx))
+ throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
+
+ return tx;
+}
+
+uint64_t BlockchainLMDB::get_tx_count() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_stat db_stats;
+ if (mdb_stat(txn, m_txs, &db_stats))
+ throw0(DB_ERROR("Failed to query m_txs"));
+
+ txn.commit();
+
+ return db_stats.ms_entries;
+}
+
+std::vector<transaction> BlockchainLMDB::get_tx_list(const std::vector<crypto::hash>& hlist) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ std::vector<transaction> v;
+
+ for (auto& h : hlist)
+ {
+ v.push_back(get_tx(h));
+ }
+
+ return v;
+}
+
+uint64_t BlockchainLMDB::get_tx_block_height(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<crypto::hash> key(h);
+ MDB_val result;
+ auto get_result = mdb_get(txn, m_tx_heights, &key, &result);
+ if (get_result == MDB_NOTFOUND)
+ {
+ throw1(TX_DNE(std::string("tx height with hash ").append(epee::string_tools::pod_to_hex(h)).append("not found in db").c_str()));
+ }
+ else if (get_result)
+ throw0(DB_ERROR("DB error attempting to fetch tx height from hash"));
+
+ return *(const uint64_t*)result.mv_data;
+}
+
+//FIXME: make sure the random method used here is appropriate
+uint64_t BlockchainLMDB::get_random_output(const uint64_t& amount) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ uint64_t num_outputs = get_num_outputs(amount);
+ if (num_outputs == 0)
+ throw1(OUTPUT_DNE("Attempting to get a random output for an amount, but none exist"));
+
+ return crypto::rand<uint64_t>() % num_outputs;
+}
+
+uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ lmdb_cur cur(txn, m_output_amounts);
+
+ MDB_val_copy<uint64_t> k(amount);
+ MDB_val v;
+ auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ {
+ return 0;
+ }
+ else if (result)
+ throw0(DB_ERROR("DB error attempting to get number of outputs of an amount"));
+
+ size_t num_elems = 0;
+ mdb_cursor_count(cur, &num_elems);
+
+ txn.commit();
+
+ return num_elems;
+}
+
+crypto::public_key BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ uint64_t glob_index = get_output_global_index(amount, index);
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> k(glob_index);
+ MDB_val v;
+ auto get_result = mdb_get(txn, m_output_keys, &k, &v);
+ if (get_result == MDB_NOTFOUND)
+ throw0(DB_ERROR("Attempting to get output pubkey by global index, but key does not exist"));
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve an output pubkey from the db"));
+
+ return *(crypto::public_key*)v.mv_data;
+}
+
+tx_out BlockchainLMDB::get_output(const crypto::hash& h, const uint64_t& index) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ lmdb_cur cur(txn, m_tx_outputs);
+
+ MDB_val_copy<crypto::hash> k(h);
+ MDB_val v;
+ auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ throw1(OUTPUT_DNE("Attempting to get an output by tx hash and tx index, but output not found"));
+ else if (result)
+ throw0(DB_ERROR("DB error attempting to get an output"));
+
+ size_t num_elems = 0;
+ mdb_cursor_count(cur, &num_elems);
+ if (num_elems <= index)
+ throw1(OUTPUT_DNE("Attempting to get an output by tx hash and tx index, but output not found"));
+
+ mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+
+ for (uint64_t i = 0; i < index; ++i)
+ {
+ mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ }
+
+ mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+
+ blobdata b;
+ b = *(blobdata*)v.mv_data;
+
+ cur.close();
+ txn.commit();
+
+ return output_from_blob(b);
+}
+
+// As this is not used, its return is now a blank output.
+// This will save on space in the db.
+tx_out BlockchainLMDB::get_output(const uint64_t& index) const
+{
+ return tx_out();
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> k(index);
+ MDB_val v;
+ auto get_result = mdb_get(txn, m_outputs, &k, &v);
+ if (get_result == MDB_NOTFOUND)
+ {
+ throw OUTPUT_DNE("Attempting to get output by global index, but output does not exist");
+ }
+ else if (get_result)
+ throw0(DB_ERROR("Error attempting to retrieve an output from the db"));
+
+ blobdata b = *(blobdata*)v.mv_data;
+
+ return output_from_blob(b);
+}
+
+tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t& index) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<uint64_t> k(index);
+ MDB_val v;
+
+ auto get_result = mdb_get(txn, m_output_txs, &k, &v);
+ if (get_result == MDB_NOTFOUND)
+ throw1(OUTPUT_DNE("output with given index not in db"));
+ else if (get_result)
+ throw0(DB_ERROR("DB error attempting to fetch output tx hash"));
+
+ crypto::hash tx_hash = *(crypto::hash*)v.mv_data;
+
+ get_result = mdb_get(txn, m_output_indices, &k, &v);
+ if (get_result == MDB_NOTFOUND)
+ throw1(OUTPUT_DNE("output with given index not in db"));
+ else if (get_result)
+ throw0(DB_ERROR("DB error attempting to fetch output tx index"));
+
+ return tx_out_index(tx_hash, *(const uint64_t *)v.mv_data);
+}
+
+tx_out_index BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ lmdb_cur cur(txn, m_output_amounts);
+
+ MDB_val_copy<uint64_t> k(amount);
+ MDB_val v;
+
+ auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
+ else if (result)
+ throw0(DB_ERROR("DB error attempting to get an output"));
+
+ size_t num_elems = 0;
+ mdb_cursor_count(cur, &num_elems);
+ if (num_elems <= index)
+ throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found"));
+
+ mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+
+ for (uint64_t i = 0; i < index; ++i)
+ {
+ mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ }
+
+ mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+
+ uint64_t glob_index = *(const uint64_t*)v.mv_data;
+
+ cur.close();
+
+ txn.commit();
+
+ return get_output_tx_and_index_from_global(glob_index);
+}
+
+std::vector<uint64_t> BlockchainLMDB::get_tx_output_indices(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ std::vector<uint64_t> index_vec;
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ lmdb_cur cur(txn, m_tx_outputs);
+
+ MDB_val_copy<crypto::hash> k(h);
+ MDB_val v;
+ auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ throw1(OUTPUT_DNE("Attempting to get an output by tx hash and tx index, but output not found"));
+ else if (result)
+ throw0(DB_ERROR("DB error attempting to get an output"));
+
+ size_t num_elems = 0;
+ mdb_cursor_count(cur, &num_elems);
+
+ mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+
+ for (uint64_t i = 0; i < num_elems; ++i)
+ {
+ mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+ index_vec.push_back(*(const uint64_t *)v.mv_data);
+ mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ }
+
+ cur.close();
+ txn.commit();
+
+ return index_vec;
+}
+
+std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const crypto::hash& h) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ std::vector<uint64_t> index_vec;
+ std::vector<uint64_t> index_vec2;
+
+ // get the transaction's global output indices first
+ index_vec = get_tx_output_indices(h);
+ // these are next used to obtain the amount output indices
+
+ transaction tx = get_tx(h);
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ uint64_t i = 0;
+ uint64_t global_index;
+ BOOST_FOREACH(const auto& vout, tx.vout)
+ {
+ uint64_t amount = vout.amount;
+
+ global_index = index_vec[i];
+
+ lmdb_cur cur(txn, m_output_amounts);
+
+ MDB_val_copy<uint64_t> k(amount);
+ MDB_val v;
+
+ auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
+ else if (result)
+ throw0(DB_ERROR("DB error attempting to get an output"));
+
+ size_t num_elems = 0;
+ mdb_cursor_count(cur, &num_elems);
+
+ mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+
+ uint64_t amount_output_index = 0;
+ uint64_t output_index = 0;
+ bool found_index = false;
+ for (uint64_t j = 0; j < num_elems; ++j)
+ {
+ mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+ output_index = *(const uint64_t *)v.mv_data;
+ if (output_index == global_index)
+ {
+ amount_output_index = j;
+ found_index = true;
+ break;
+ }
+ mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ }
+ if (found_index)
+ {
+ index_vec2.push_back(amount_output_index);
+ }
+ else
+ {
+ // not found
+ cur.close();
+ txn.commit();
+ throw1(OUTPUT_DNE("specified output not found in db"));
+ }
+
+ cur.close();
+ ++i;
+ }
+
+ txn.commit();
+
+ return index_vec2;
+}
+
+
+
+bool BlockchainLMDB::has_key_image(const crypto::key_image& img) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+
+ MDB_val_copy<crypto::key_image> val_key(img);
+ MDB_val unused;
+ if (mdb_get(txn, m_spent_keys, &val_key, &unused) == 0)
+ {
+ txn.commit();
+ return true;
+ }
+
+ txn.commit();
+ return false;
+}
+
+uint64_t BlockchainLMDB::add_block( const block& blk
+ , const size_t& block_size
+ , const difficulty_type& cumulative_difficulty
+ , const uint64_t& coins_generated
+ , const std::vector<transaction>& txs
+ )
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, 0, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+ m_write_txn = &txn;
+
+ uint64_t num_outputs = m_num_outputs;
+ try
+ {
+ BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs);
+ m_write_txn = NULL;
+
+ txn.commit();
+ }
+ catch (...)
+ {
+ m_num_outputs = num_outputs;
+ m_write_txn = NULL;
+ throw;
+ }
+
+ return ++m_height;
+}
+
+void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs)
+{
+ txn_safe txn;
+ if (mdb_txn_begin(m_env, NULL, 0, txn))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+ m_write_txn = &txn;
+
+ uint64_t num_outputs = m_num_outputs;
+ try
+ {
+ BlockchainDB::pop_block(blk, txs);
+ m_write_txn = NULL;
+
+ txn.commit();
+ }
+ catch (...)
+ {
+ m_num_outputs = num_outputs;
+ m_write_txn = NULL;
+ throw;
+ }
+
+ --m_height;
+}
+
+} // namespace cryptonote
diff --git a/src/cryptonote_core/BlockchainDB_impl/db_lmdb.h b/src/cryptonote_core/BlockchainDB_impl/db_lmdb.h
new file mode 100644
index 000000000..2513aa48a
--- /dev/null
+++ b/src/cryptonote_core/BlockchainDB_impl/db_lmdb.h
@@ -0,0 +1,269 @@
+// Copyright (c) 2014, 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 "cryptonote_core/blockchain_db.h"
+#include "cryptonote_protocol/blobdatatype.h" // for type blobdata
+
+#include <lmdb.h>
+
+namespace cryptonote
+{
+
+struct txn_safe
+{
+ txn_safe() : m_txn(NULL) { }
+ ~txn_safe()
+ {
+ if(m_txn != NULL)
+ {
+ mdb_txn_abort(m_txn);
+ }
+ }
+
+ void commit(std::string message = "")
+ {
+ if (message.size() == 0)
+ {
+ message = "Failed to commit a transaction to the db";
+ }
+
+ if (mdb_txn_commit(m_txn))
+ {
+ m_txn = NULL;
+ LOG_PRINT_L0(message);
+ throw DB_ERROR(message.c_str());
+ }
+ m_txn = NULL;
+ }
+
+ operator MDB_txn*()
+ {
+ return m_txn;
+ }
+
+ operator MDB_txn**()
+ {
+ return &m_txn;
+ }
+
+ MDB_txn* m_txn;
+};
+
+
+class BlockchainLMDB : public BlockchainDB
+{
+public:
+ BlockchainLMDB();
+ ~BlockchainLMDB();
+
+ virtual void open(const std::string& filename);
+
+ virtual void create(const std::string& filename);
+
+ virtual void close();
+
+ virtual void sync();
+
+ virtual void reset();
+
+ virtual std::vector<std::string> get_filenames() const;
+
+ virtual bool lock();
+
+ virtual void unlock();
+
+ virtual bool block_exists(const crypto::hash& h) const;
+
+ virtual block get_block(const crypto::hash& h) const;
+
+ virtual uint64_t get_block_height(const crypto::hash& h) const;
+
+ virtual block_header get_block_header(const crypto::hash& h) const;
+
+ virtual block get_block_from_height(const uint64_t& height) const;
+
+ virtual uint64_t get_block_timestamp(const uint64_t& height) const;
+
+ virtual uint64_t get_top_block_timestamp() const;
+
+ virtual size_t get_block_size(const uint64_t& height) const;
+
+ virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const;
+
+ virtual difficulty_type get_block_difficulty(const uint64_t& height) const;
+
+ virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const;
+
+ virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const;
+
+ virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const;
+
+ virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const;
+
+ virtual crypto::hash top_block_hash() const;
+
+ virtual block get_top_block() const;
+
+ virtual uint64_t height() const;
+
+ virtual bool tx_exists(const crypto::hash& h) const;
+
+ virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const;
+
+ virtual transaction get_tx(const crypto::hash& h) const;
+
+ virtual uint64_t get_tx_count() const;
+
+ virtual std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const;
+
+ virtual uint64_t get_tx_block_height(const crypto::hash& h) const;
+
+ virtual uint64_t get_random_output(const uint64_t& amount) const;
+
+ virtual uint64_t get_num_outputs(const uint64_t& amount) const;
+
+ virtual crypto::public_key get_output_key(const uint64_t& amount, const uint64_t& index) const;
+
+ virtual tx_out get_output(const crypto::hash& h, const uint64_t& index) const;
+
+ /**
+ * @brief get an output from its global index
+ *
+ * @param index global index of the output desired
+ *
+ * @return the output associated with the index.
+ * Will throw OUTPUT_DNE if not output has that global index.
+ * Will throw DB_ERROR if there is a non-specific LMDB error in fetching
+ */
+ tx_out get_output(const uint64_t& index) const;
+
+ virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const;
+
+ virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const;
+
+ virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const;
+ virtual std::vector<uint64_t> get_tx_amount_output_indices(const crypto::hash& h) const;
+
+ virtual bool has_key_image(const crypto::key_image& img) const;
+
+ virtual uint64_t add_block( const block& blk
+ , const size_t& block_size
+ , const difficulty_type& cumulative_difficulty
+ , const uint64_t& coins_generated
+ , const std::vector<transaction>& txs
+ );
+
+ virtual void pop_block(block& blk, std::vector<transaction>& txs);
+
+private:
+ virtual void add_block( const block& blk
+ , const size_t& block_size
+ , const difficulty_type& cumulative_difficulty
+ , const uint64_t& coins_generated
+ );
+
+ virtual void remove_block();
+
+ virtual void add_transaction_data(const crypto::hash& blk_hash, const transaction& tx);
+
+ virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx);
+
+ virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index);
+
+ virtual void remove_output(const tx_out& tx_output);
+
+ void remove_tx_outputs(const crypto::hash& tx_hash, const transaction& tx);
+
+ void remove_output(const uint64_t& out_index, const uint64_t amount);
+ void remove_amount_output_index(const uint64_t amount, const uint64_t global_output_index);
+
+ virtual void add_spent_key(const crypto::key_image& k_image);
+
+ virtual void remove_spent_key(const crypto::key_image& k_image);
+
+ /**
+ * @brief convert a tx output to a blob for storage
+ *
+ * @param output the output to convert
+ *
+ * @return the resultant blob
+ */
+ blobdata output_to_blob(const tx_out& output);
+
+ /**
+ * @brief convert a tx output blob to a tx output
+ *
+ * @param blob the blob to convert
+ *
+ * @return the resultant tx output
+ */
+ tx_out output_from_blob(const blobdata& blob) const;
+
+ /**
+ * @brief get the global index of the index-th output of the given amount
+ *
+ * @param amount the output amount
+ * @param index the index into the set of outputs of that amount
+ *
+ * @return the global index of the desired output
+ */
+ uint64_t get_output_global_index(const uint64_t& amount, const uint64_t& index) const;
+
+ void check_open() const;
+
+ MDB_env* m_env;
+
+ MDB_dbi m_blocks;
+ MDB_dbi m_block_heights;
+ MDB_dbi m_block_hashes;
+ MDB_dbi m_block_timestamps;
+ MDB_dbi m_block_sizes;
+ MDB_dbi m_block_diffs;
+ MDB_dbi m_block_coins;
+
+ MDB_dbi m_txs;
+ MDB_dbi m_tx_unlocks;
+ MDB_dbi m_tx_heights;
+ MDB_dbi m_tx_outputs;
+
+ MDB_dbi m_output_txs;
+ MDB_dbi m_output_indices;
+ MDB_dbi m_output_gindices;
+ MDB_dbi m_output_amounts;
+ MDB_dbi m_output_keys;
+ MDB_dbi m_outputs;
+
+ MDB_dbi m_spent_keys;
+
+ bool m_open;
+ uint64_t m_height;
+ uint64_t m_num_outputs;
+ std::string m_folder;
+ txn_safe* m_write_txn;
+};
+
+} // namespace cryptonote
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 3abf93f3c..fc9cc629b 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -29,6 +29,9 @@
set(cryptonote_core_sources
account.cpp
blockchain_storage.cpp
+ blockchain.cpp
+ blockchain_db.cpp
+ BlockchainDB_impl/db_lmdb.cpp
checkpoints.cpp
checkpoints_create.cpp
cryptonote_basic_impl.cpp
@@ -45,6 +48,9 @@ set(cryptonote_core_private_headers
account_boost_serialization.h
blockchain_storage.h
blockchain_storage_boost_serialization.h
+ blockchain.h
+ blockchain_db.h
+ BlockchainDB_impl/db_lmdb.h
checkpoints.h
checkpoints_create.h
connection_context.h
@@ -77,4 +83,5 @@ target_link_libraries(cryptonote_core
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
+ ${LMDB_LIBRARY}
${EXTRA_LIBRARIES})
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
new file mode 100644
index 000000000..b671fa71a
--- /dev/null
+++ b/src/cryptonote_core/blockchain.cpp
@@ -0,0 +1,2361 @@
+// Copyright (c) 2014, 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
+
+#include <algorithm>
+#include <cstdio>
+#include <boost/archive/binary_oarchive.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/filesystem.hpp>
+
+#include "include_base_utils.h"
+#include "cryptonote_basic_impl.h"
+#include "tx_pool.h"
+#include "blockchain.h"
+#include "cryptonote_core/blockchain_db.h"
+#include "cryptonote_core/BlockchainDB_impl/db_lmdb.h"
+#include "cryptonote_format_utils.h"
+#include "cryptonote_boost_serialization.h"
+#include "cryptonote_config.h"
+#include "miner.h"
+#include "misc_language.h"
+#include "profile_tools.h"
+#include "file_io_utils.h"
+#include "common/boost_serialization_helper.h"
+#include "warnings.h"
+#include "crypto/hash.h"
+#include "cryptonote_core/checkpoints_create.h"
+//#include "serialization/json_archive.h"
+
+/* TODO:
+ * Clean up code:
+ * Possibly change how outputs are referred to/indexed in blockchain and wallets
+ *
+ */
+
+using namespace cryptonote;
+using epee::string_tools::pod_to_hex;
+
+DISABLE_VS_WARNINGS(4267)
+
+//------------------------------------------------------------------
+// TODO: initialize m_db with a concrete implementation of BlockchainDB
+Blockchain::Blockchain(tx_memory_pool& tx_pool):m_db(), m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false), m_testnet(false), m_enforce_dns_checkpoints(false)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+}
+//------------------------------------------------------------------
+//TODO: is this still needed? I don't think so - tewinget
+template<class archive_t>
+void Blockchain::serialize(archive_t & ar, const unsigned int version)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ if(version < 11)
+ return;
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ ar & m_blocks;
+ ar & m_blocks_index;
+ ar & m_transactions;
+ ar & m_spent_keys;
+ ar & m_alternative_chains;
+ ar & m_outputs;
+ ar & m_invalid_blocks;
+ ar & m_current_block_cumul_sz_limit;
+ /*serialization bug workaround*/
+ if(version > 11)
+ {
+ uint64_t total_check_count = m_db->height() + m_blocks_index.size() + m_transactions.size() + m_spent_keys.size() + m_alternative_chains.size() + m_outputs.size() + m_invalid_blocks.size() + m_current_block_cumul_sz_limit;
+ if(archive_t::is_saving::value)
+ {
+ ar & total_check_count;
+ }else
+ {
+ uint64_t total_check_count_loaded = 0;
+ ar & total_check_count_loaded;
+ if(total_check_count != total_check_count_loaded)
+ {
+ LOG_ERROR("Blockchain storage data corruption detected. total_count loaded from file = " << total_check_count_loaded << ", expected = " << total_check_count);
+
+ LOG_PRINT_L0("Blockchain storage:" << std::endl <<
+ "m_blocks: " << m_db->height() << std::endl <<
+ "m_blocks_index: " << m_blocks_index.size() << std::endl <<
+ "m_transactions: " << m_transactions.size() << std::endl <<
+ "m_spent_keys: " << m_spent_keys.size() << std::endl <<
+ "m_alternative_chains: " << m_alternative_chains.size() << std::endl <<
+ "m_outputs: " << m_outputs.size() << std::endl <<
+ "m_invalid_blocks: " << m_invalid_blocks.size() << std::endl <<
+ "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit);
+
+ throw std::runtime_error("Blockchain data corruption");
+ }
+ }
+ }
+
+
+ LOG_PRINT_L3("Blockchain storage:" << std::endl <<
+ "m_blocks: " << m_db->height() << std::endl <<
+ "m_blocks_index: " << m_blocks_index.size() << std::endl <<
+ "m_transactions: " << m_transactions.size() << std::endl <<
+ "m_spent_keys: " << m_spent_keys.size() << std::endl <<
+ "m_alternative_chains: " << m_alternative_chains.size() << std::endl <<
+ "m_outputs: " << m_outputs.size() << std::endl <<
+ "m_invalid_blocks: " << m_invalid_blocks.size() << std::endl <<
+ "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit);
+}
+//------------------------------------------------------------------
+bool Blockchain::have_tx(const crypto::hash &id) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ return m_db->tx_exists(id);
+}
+//------------------------------------------------------------------
+bool Blockchain::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ return m_db->has_key_image(key_im);
+}
+//------------------------------------------------------------------
+// This function makes sure that each "input" in an input (mixins) exists
+// and collects the public key for each from the transaction it was included in
+// via the visitor passed to it.
+template<class visitor_t>
+bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // verify that the input has key offsets (that it exists properly, really)
+ if(!tx_in_to_key.key_offsets.size())
+ return false;
+
+ // cryptonote_format_utils uses relative offsets for indexing to the global
+ // outputs list. that is to say that absolute offset #2 is absolute offset
+ // #1 plus relative offset #2.
+ // TODO: Investigate if this is necessary / why this is done.
+ std::vector<uint64_t> absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets);
+
+
+ //std::vector<std::pair<crypto::hash, size_t> >& amount_outs_vec = it->second;
+ size_t count = 0;
+ for (const uint64_t& i : absolute_offsets)
+ {
+ try
+ {
+ // get tx hash and output index for output
+ auto output_index = m_db->get_output_tx_and_index(tx_in_to_key.amount, i);
+
+ // get tx that output is from
+ auto tx = m_db->get_tx(output_index.first);
+
+ // make sure output index is within range for the given transaction
+ if (output_index.second >= tx.vout.size())
+ {
+ LOG_PRINT_L0("Output does not exist. tx = " << output_index.first << ", index = " << output_index.second);
+ return false;
+ }
+
+ // call to the passed boost visitor to grab the public key for the output
+ if(!vis.handle_output(tx, tx.vout[output_index.second]))
+ {
+ LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i);
+ return false;
+ }
+
+ // if on last output and pmax_related_block_height not null pointer
+ if(++count == absolute_offsets.size() && pmax_related_block_height)
+ {
+ // set *pmax_related_block_height to tx block height for this output
+ auto h = m_db->get_tx_block_height(output_index.first);
+ if(*pmax_related_block_height < h)
+ {
+ *pmax_related_block_height = h;
+ }
+ }
+
+ }
+ catch (const OUTPUT_DNE& e)
+ {
+ LOG_PRINT_L0("Output does not exist: " << e.what());
+ return false;
+ }
+ catch (const TX_DNE& e)
+ {
+ LOG_PRINT_L0("Transaction does not exist: " << e.what());
+ return false;
+ }
+
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
+uint64_t Blockchain::get_current_blockchain_height() const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ return m_db->height();
+}
+//------------------------------------------------------------------
+//FIXME: possibly move this into the constructor, to avoid accidentally
+// dereferencing a null BlockchainDB pointer
+bool Blockchain::init(const std::string& config_folder, bool testnet)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ m_db = new BlockchainLMDB();
+
+ m_config_folder = config_folder;
+ m_testnet = testnet;
+
+ boost::filesystem::path folder(m_config_folder);
+
+ LOG_PRINT_L0("Loading blockchain...");
+
+ //FIXME: update filename for BlockchainDB
+ const std::string filename = folder.string();
+ try
+ {
+ m_db->open(filename);
+ }
+ catch (const DB_OPEN_FAILURE& e)
+ {
+ LOG_PRINT_L0("No blockchain file found, attempting to create one.");
+ try
+ {
+ m_db->create(filename);
+ }
+ catch (const DB_CREATE_FAILURE& db_create_error)
+ {
+ LOG_PRINT_L0("Unable to create BlockchainDB! This is not good...");
+ //TODO: make sure whatever calls this handles the return value properly
+ return false;
+ }
+ }
+
+ // if the blockchain is new, add the genesis block
+ // this feels kinda kludgy to do it this way, but can be looked at later.
+ // TODO: add function to create and store genesis block,
+ // taking testnet into account
+ if(!m_db->height())
+ {
+ LOG_PRINT_L0("Blockchain not loaded, generating genesis block.");
+ block bl = boost::value_initialized<block>();
+ block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ if (testnet)
+ {
+ generate_genesis_block(bl, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
+ }
+ else
+ {
+ generate_genesis_block(bl, config::GENESIS_TX, config::GENESIS_NONCE);
+ }
+ add_new_block(bl, bvc);
+ CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain");
+ }
+ // TODO: if blockchain load successful, verify blockchain against both
+ // hard-coded and runtime-loaded (and enforced) checkpoints.
+ else
+ {
+ }
+
+ // check how far behind we are
+ uint64_t top_block_timestamp = m_db->get_top_block_timestamp();
+ uint64_t timestamp_diff = time(NULL) - top_block_timestamp;
+
+ // genesis block has no timestamp, could probably change it to have timestamp of 1341378000...
+ if(!top_block_timestamp)
+ timestamp_diff = time(NULL) - 1341378000;
+ LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0);
+
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::store_blockchain()
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ // TODO: make sure if this throws that it is not simply ignored higher
+ // up the call stack
+ try
+ {
+ m_db->sync();
+ }
+ catch (const std::exception& e)
+ {
+ LOG_PRINT_L0(std::string("Error syncing blockchain db: ") + e.what() + "-- shutting down now to prevent issues!");
+ throw;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L0("There was an issue storing the blockchain, shutting down now to prevent issues!");
+ throw;
+ }
+ LOG_PRINT_L0("Blockchain stored OK.");
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::deinit()
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ // as this should be called if handling a SIGSEGV, need to check
+ // if m_db is a NULL pointer (and thus may have caused the illegal
+ // memory operation), otherwise we may cause a loop.
+ if (m_db == NULL)
+ {
+ throw new DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!");
+ }
+
+ try
+ {
+ m_db->close();
+ }
+ catch (const std::exception& e)
+ {
+ LOG_PRINT_L0(std::string("Error closing blockchain db: ") + e.what());
+ }
+ catch (...)
+ {
+ LOG_PRINT_L0("There was an issue closing/storing the blockchain, shutting down now to prevent issues!");
+ }
+
+ delete m_db;
+ return true;
+}
+//------------------------------------------------------------------
+// This function tells BlockchainDB to remove the top block from the
+// blockchain and then returns all transactions (except the miner tx, of course)
+// from it to the tx_pool
+block Blockchain::pop_block_from_blockchain()
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ block popped_block;
+ std::vector<transaction> popped_txs;
+
+ try
+ {
+ m_db->pop_block(popped_block, popped_txs);
+ }
+ // anything that could cause this to throw is likely catastrophic,
+ // so we re-throw
+ catch (const std::exception& e)
+ {
+ LOG_ERROR("Error popping block from blockchain: " << e.what());
+ throw;
+ }
+ catch (...)
+ {
+ LOG_ERROR("Error popping block from blockchain, throwing!");
+ throw;
+ }
+
+ // return transactions from popped block to the tx_pool
+ for (transaction& tx : popped_txs)
+ {
+ if (!is_coinbase(tx))
+ {
+ cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
+ bool r = m_tx_pool.add_tx(tx, tvc, true);
+ if (!r)
+ {
+ LOG_ERROR("Error returning transaction to tx_pool");
+ }
+ }
+ }
+
+ return popped_block;
+}
+//------------------------------------------------------------------
+bool Blockchain::reset_and_set_genesis_block(const block& b)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ m_transactions.clear();
+ m_spent_keys.clear();
+ m_blocks.clear();
+ m_blocks_index.clear();
+ m_alternative_chains.clear();
+ m_outputs.clear();
+ m_db->reset();
+
+ block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ add_new_block(b, bvc);
+ return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
+}
+//------------------------------------------------------------------
+//TODO: move to BlockchainDB subclass
+bool Blockchain::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ struct purge_transaction_visitor: public boost::static_visitor<bool>
+ {
+ key_images_container& m_spent_keys;
+ bool m_strict_check;
+ purge_transaction_visitor(key_images_container& spent_keys, bool strict_check):m_spent_keys(spent_keys), m_strict_check(strict_check){}
+
+ bool operator()(const txin_to_key& inp) const
+ {
+ //const crypto::key_image& ki = inp.k_image;
+ auto r = m_spent_keys.find(inp.k_image);
+ if(r != m_spent_keys.end())
+ {
+ m_spent_keys.erase(r);
+ }else
+ {
+ CHECK_AND_ASSERT_MES(!m_strict_check, false, "purge_block_data_from_blockchain: key image in transaction not found");
+ }
+ return true;
+ }
+ bool operator()(const txin_gen& inp) const
+ {
+ return true;
+ }
+ bool operator()(const txin_to_script& tx) const
+ {
+ return false;
+ }
+
+ bool operator()(const txin_to_scripthash& tx) const
+ {
+ return false;
+ }
+ };
+
+ BOOST_FOREACH(const txin_v& in, tx.vin)
+ {
+ bool r = boost::apply_visitor(purge_transaction_visitor(m_spent_keys, strict_check), in);
+ CHECK_AND_ASSERT_MES(!strict_check || r, false, "failed to process purge_transaction_visitor");
+ }
+ return true;
+}
+//------------------------------------------------------------------
+crypto::hash Blockchain::get_tail_id(uint64_t& height) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ height = m_db->height();
+ return get_tail_id();
+}
+//------------------------------------------------------------------
+crypto::hash Blockchain::get_tail_id() const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ return m_db->top_block_hash();
+}
+//------------------------------------------------------------------
+/*TODO: this function was...poorly written. As such, I'm not entirely
+ * certain on what it was supposed to be doing. Need to look into this,
+ * but it doesn't seem terribly important just yet.
+ *
+ * puts into list <ids> a list of hashes representing certain blocks
+ * from the blockchain in reverse chronological order
+ *
+ * the blocks chosen, at the time of this writing, are:
+ * the most recent 11
+ * powers of 2 less recent from there, so 13, 17, 25, etc...
+ *
+ */
+bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ uint64_t i = 0;
+ uint64_t current_multiplier = 1;
+ uint64_t sz = m_db->height();
+
+ if(!sz)
+ return true;
+
+ bool genesis_included = false;
+ uint64_t current_back_offset = 1;
+ while(current_back_offset < sz)
+ {
+ ids.push_back(m_db->get_block_hash_from_height(sz - current_back_offset));
+
+ if(sz-current_back_offset == 0)
+ {
+ genesis_included = true;
+ }
+ if(i < 10)
+ {
+ ++current_back_offset;
+ }
+ else
+ {
+ current_multiplier *= 2;
+ current_back_offset += current_multiplier;
+ }
+ ++i;
+ }
+
+ if (!genesis_included)
+ {
+ ids.push_back(m_db->get_block_hash_from_height(0));
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
+crypto::hash Blockchain::get_block_id_by_height(uint64_t height) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ try
+ {
+ return m_db->get_block_hash_from_height(height);
+ }
+ catch (const BLOCK_DNE& e)
+ {
+ }
+ catch (const std::exception& e)
+ {
+ LOG_PRINT_L0(std::string("Something went wrong fetching block hash by height: ") + e.what());
+ throw;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L0(std::string("Something went wrong fetching block hash by height"));
+ throw;
+ }
+ return null_hash;
+}
+//------------------------------------------------------------------
+bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // try to find block in main chain
+ try
+ {
+ blk = m_db->get_block(h);
+ return true;
+ }
+ // try to find block in alternative chain
+ catch (const BLOCK_DNE& e)
+ {
+ blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h);
+ if (m_alternative_chains.end() != it_alt) {
+ blk = it_alt->second.bl;
+ return true;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ LOG_PRINT_L0(std::string("Something went wrong fetching block by hash: ") + e.what());
+ throw;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L0(std::string("Something went wrong fetching block hash by hash"));
+ throw;
+ }
+
+ return false;
+}
+//------------------------------------------------------------------
+//FIXME: this function does not seem to be called from anywhere, but
+// if it ever is, should probably change std::list for std::vector
+void Blockchain::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ for (auto& a : m_db->get_hashes_range(0, m_db->height() - 1))
+ {
+ main.push_back(a);
+ }
+
+ BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_alternative_chains)
+ alt.push_back(v.first);
+
+ BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_invalid_blocks)
+ invalid.push_back(v.first);
+}
+//------------------------------------------------------------------
+// This function aggregates the cumulative difficulties and timestamps of the
+// last DIFFICULTY_BLOCKS_COUNT blocks and passes them to next_difficulty,
+// returning the result of that call. Ignores the genesis block, and can use
+// less blocks than desired if there aren't enough.
+difficulty_type Blockchain::get_difficulty_for_next_block() const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ std::vector<uint64_t> timestamps;
+ std::vector<difficulty_type> cumulative_difficulties;
+ auto h = m_db->height();
+
+ size_t offset = h - std::min<size_t>(h, static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
+
+ if (offset == 0)
+ {
+ ++offset;
+ }
+
+ for(; offset < h; offset++)
+ {
+ timestamps.push_back(m_db->get_block_timestamp(offset));
+ cumulative_difficulties.push_back(m_db->get_block_cumulative_difficulty(offset));
+ }
+ return next_difficulty(timestamps, cumulative_difficulties);
+}
+//------------------------------------------------------------------
+// This function removes blocks from the blockchain until it gets to the
+// position where the blockchain switch started and then re-adds the blocks
+// that had been removed.
+bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, uint64_t rollback_height)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // remove blocks from blockchain until we get back to where we should be.
+ while (m_db->height() != rollback_height)
+ {
+ pop_block_from_blockchain();
+ }
+
+ //return back original chain
+ for (auto& bl : original_chain)
+ {
+ block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ bool r = handle_block_to_main_chain(bl, bvc);
+ CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC! failed to add (again) block while chain switching during the rollback!");
+ }
+
+ LOG_PRINT_L1("Rollback to height " << rollback_height << " was successful.");
+ if (original_chain.size())
+ {
+ LOG_PRINT_L1("Restoration to previous blockchain successful as well.");
+ }
+ return true;
+}
+//------------------------------------------------------------------
+// This function attempts to switch to an alternate chain, returning
+// boolean based on success therein.
+bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // if empty alt chain passed (not sure how that could happen), return false
+ CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed");
+
+ // verify that main chain has front of alt chain's parent block
+ if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
+ {
+ LOG_ERROR("Attempting to move to an alternate chain, but it doesn't appear to connect to the main chain!");
+ return false;
+ }
+
+ // pop blocks from the blockchain until the top block is the parent
+ // of the front block of the alt chain.
+ std::list<block> disconnected_chain;
+ while (m_db->top_block_hash() != alt_chain.front()->second.bl.prev_id)
+ {
+ block b = pop_block_from_blockchain();
+ disconnected_chain.push_front(b);
+ }
+
+ auto split_height = m_db->height();
+
+ //connecting new alternative chain
+ for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++)
+ {
+ auto ch_ent = *alt_ch_iter;
+ block_verification_context bvc = boost::value_initialized<block_verification_context>();
+
+ // add block to main chain
+ bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc);
+
+ // if adding block to main chain failed, rollback to previous state and
+ // return false
+ if(!r || !bvc.m_added_to_main_chain)
+ {
+ LOG_PRINT_L1("Failed to switch to alternative blockchain");
+
+ // rollback_blockchain_switching should be moved to two different
+ // functions: rollback and apply_chain, but for now we pretend it is
+ // just the latter (because the rollback was done above).
+ rollback_blockchain_switching(disconnected_chain, m_db->height());
+
+ // FIXME: Why do we keep invalid blocks around? Possibly in case we hear
+ // about them again so we can immediately dismiss them, but needs some
+ // looking into.
+ add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl));
+ LOG_PRINT_L1("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl));
+ m_alternative_chains.erase(ch_ent);
+
+ for(auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++)
+ {
+ add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first);
+ m_alternative_chains.erase(*alt_ch_to_orph_iter);
+ }
+ return false;
+ }
+ }
+
+ // if we're to keep the disconnected blocks, add them as alternates
+ if(!discard_disconnected_chain)
+ {
+ //pushing old chain as alternative chain
+ for (auto& old_ch_ent : disconnected_chain)
+ {
+ block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc);
+ if(!r)
+ {
+ LOG_PRINT_L1("Failed to push ex-main chain blocks to alternative chain ");
+ // previously this would fail the blockchain switching, but I don't
+ // think this is bad enough to warrant that.
+ }
+ }
+ }
+
+ //removing alt_chain entries from alternative chain
+ BOOST_FOREACH(auto ch_ent, alt_chain)
+ {
+ m_alternative_chains.erase(ch_ent);
+ }
+
+ LOG_PRINT_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height(), LOG_LEVEL_0);
+ return true;
+}
+//------------------------------------------------------------------
+// This function calculates the difficulty target for the block being added to
+// an alternate chain.
+difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ std::vector<uint64_t> timestamps;
+ std::vector<difficulty_type> cumulative_difficulties;
+
+ // if the alt chain isn't long enough to calculate the difficulty target
+ // based on its blocks alone, need to get more blocks from the main chain
+ if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT)
+ {
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // Figure out start and stop offsets for main chain blocks
+ size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height;
+ size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size());
+ main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
+ size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
+
+ if(!main_chain_start_offset)
+ ++main_chain_start_offset; //skip genesis block
+
+ // get difficulties and timestamps from relevant main chain blocks
+ for(; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset)
+ {
+ timestamps.push_back(m_db->get_block_timestamp(main_chain_start_offset));
+ cumulative_difficulties.push_back(m_db->get_block_cumulative_difficulty(main_chain_start_offset));
+ }
+
+ // make sure we haven't accidentally grabbed too many blocks...maybe don't need this check?
+ CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false,
+ "Internal error, alt_chain.size()[" << alt_chain.size()
+ << "] + vtimestampsec.size()[" << timestamps.size()
+ << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT
+ );
+
+ for (auto it : alt_chain)
+ {
+ timestamps.push_back(it->second.bl.timestamp);
+ cumulative_difficulties.push_back(it->second.cumulative_difficulty);
+ }
+ }
+ // if the alt chain is long enough for the difficulty calc, grab difficulties
+ // and timestamps from it alone
+ else
+ {
+ timestamps.resize(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
+ cumulative_difficulties.resize(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
+ size_t count = 0;
+ size_t max_i = timestamps.size()-1;
+ // get difficulties and timestamps from most recent blocks in alt chain
+ BOOST_REVERSE_FOREACH(auto it, alt_chain)
+ {
+ timestamps[max_i - count] = it->second.bl.timestamp;
+ cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
+ count++;
+ if(count >= DIFFICULTY_BLOCKS_COUNT)
+ break;
+ }
+ }
+
+ // calculate the difficulty target for the block and return it
+ return next_difficulty(timestamps, cumulative_difficulties);
+}
+//------------------------------------------------------------------
+// This function does a sanity check on basic things that all miner
+// transactions have in common, such as:
+// one input, of type txin_gen, with height set to the block's height
+// correct miner tx unlock time
+// a non-overflowing tx amount (dubious necessity on this check)
+bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs");
+ CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
+ if(boost::get<txin_gen>(b.miner_tx.vin[0]).height != height)
+ {
+ LOG_PRINT_RED_L1("The miner transaction in block has invalid height: " << boost::get<txin_gen>(b.miner_tx.vin[0]).height << ", expected: " << height);
+ return false;
+ }
+ CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW,
+ false,
+ "coinbase transaction transaction has the wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
+
+
+ //check outs overflow
+ //NOTE: not entirely sure this is necessary, given that this function is
+ // designed simply to make sure the total amount for a transaction
+ // does not overflow a uint64_t, and this transaction *is* a uint64_t...
+ if(!check_outs_overflow(b.miner_tx))
+ {
+ LOG_PRINT_RED_L1("miner transaction has money overflow in block " << get_block_hash(b));
+ return false;
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
+// This function validates the miner transaction reward
+bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ //validate reward
+ uint64_t money_in_use = 0;
+ BOOST_FOREACH(auto& o, b.miner_tx.vout)
+ money_in_use += o.amount;
+
+ std::vector<size_t> last_blocks_sizes;
+ get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
+ if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward)) {
+ LOG_PRINT_L1("block size " << cumulative_block_size << " is bigger than allowed for this blockchain");
+ return false;
+ }
+ if(base_reward + fee < money_in_use)
+ {
+ LOG_PRINT_L1("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")");
+ return false;
+ }
+ if(base_reward + fee != money_in_use)
+ {
+ LOG_PRINT_L1("coinbase transaction doesn't use full amount of block reward: spent: "
+ << money_in_use << ", block reward " << base_reward + fee << "(" << base_reward << "+" << fee << ")");
+ return false;
+ }
+ return true;
+}
+//------------------------------------------------------------------
+// get the block sizes of the last <count> blocks, starting at <from_height>
+// and return by reference <sz>.
+void Blockchain::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ auto h = m_db->height();
+
+ // this function is meaningless for an empty blockchain...granted it should never be empty
+ if(h == 0)
+ return;
+
+ // add size of last <count> blocks to vector <sz> (or less, if blockchain size < count)
+ size_t start_offset = h - std::min<size_t>(h, count);
+ for(size_t i = start_offset; i < h; i++)
+ {
+ sz.push_back(m_db->get_block_size(i));
+ }
+}
+//------------------------------------------------------------------
+uint64_t Blockchain::get_current_cumulative_blocksize_limit() const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ return m_current_block_cumul_sz_limit;
+}
+//------------------------------------------------------------------
+//TODO: This function only needed minor modification to work with BlockchainDB,
+// and *works*. As such, to reduce the number of things that might break
+// in moving to BlockchainDB, this function will remain otherwise
+// unchanged for the time being.
+//
+// This function makes a new block for a miner to mine the hash for
+//
+// FIXME: this codebase references #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
+// in a lot of places. That flag is not referenced in any of the code
+// nor any of the makefiles, howeve. Need to look into whether or not it's
+// necessary at all.
+bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ size_t median_size;
+ uint64_t already_generated_coins;
+
+ CRITICAL_REGION_BEGIN(m_blockchain_lock);
+ b.major_version = CURRENT_BLOCK_MAJOR_VERSION;
+ b.minor_version = CURRENT_BLOCK_MINOR_VERSION;
+ b.prev_id = get_tail_id();
+ b.timestamp = time(NULL);
+
+ height = m_db->height();
+ diffic = get_difficulty_for_next_block();
+ CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead.");
+
+ median_size = m_current_block_cumul_sz_limit / 2;
+ already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
+
+ CRITICAL_REGION_END();
+
+ size_t txs_size;
+ uint64_t fee;
+ if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) {
+ return false;
+ }
+#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
+ size_t real_txs_size = 0;
+ uint64_t real_fee = 0;
+ CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock);
+ BOOST_FOREACH(crypto::hash &cur_hash, b.tx_hashes) {
+ auto cur_res = m_tx_pool.m_transactions.find(cur_hash);
+ if (cur_res == m_tx_pool.m_transactions.end()) {
+ LOG_ERROR("Creating block template: error: transaction not found");
+ continue;
+ }
+ tx_memory_pool::tx_details &cur_tx = cur_res->second;
+ real_txs_size += cur_tx.blob_size;
+ real_fee += cur_tx.fee;
+ if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) {
+ LOG_ERROR("Creating block template: error: invalid transaction size");
+ }
+ uint64_t inputs_amount;
+ if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) {
+ LOG_ERROR("Creating block template: error: cannot get inputs amount");
+ } else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) {
+ LOG_ERROR("Creating block template: error: invalid fee");
+ }
+ }
+ if (txs_size != real_txs_size) {
+ LOG_ERROR("Creating block template: error: wrongly calculated transaction size");
+ }
+ if (fee != real_fee) {
+ LOG_ERROR("Creating block template: error: wrongly calculated fee");
+ }
+ CRITICAL_REGION_END();
+ LOG_PRINT_L1("Creating block template: height " << height <<
+ ", median size " << median_size <<
+ ", already generated coins " << already_generated_coins <<
+ ", transaction size " << txs_size <<
+ ", fee " << fee);
+#endif
+
+ /*
+ two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know
+ block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
+ */
+ //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
+ bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
+ size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
+#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
+ LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) <<
+ ", cumulative size " << cumulative_size);
+#endif
+ for (size_t try_count = 0; try_count != 10; ++try_count) {
+ r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
+
+ CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
+ size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
+ if (coinbase_blob_size > cumulative_size - txs_size) {
+ cumulative_size = txs_size + coinbase_blob_size;
+#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
+ LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
+ ", cumulative size " << cumulative_size << " is greater then before");
+#endif
+ continue;
+ }
+
+ if (coinbase_blob_size < cumulative_size - txs_size) {
+ size_t delta = cumulative_size - txs_size - coinbase_blob_size;
+#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
+ LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
+ ", cumulative size " << txs_size + coinbase_blob_size <<
+ " is less then before, adding " << delta << " zero bytes");
+#endif
+ b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0);
+ //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len.
+ if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) {
+ CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx));
+ b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1);
+ if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) {
+ //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size
+ LOG_PRINT_RED("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2);
+ cumulative_size += delta - 1;
+ continue;
+ }
+ LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1);
+ }
+ }
+ CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx));
+#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
+ LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
+ ", cumulative size " << cumulative_size << " is now good");
+#endif
+ return true;
+ }
+ LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
+ return false;
+}
+//------------------------------------------------------------------
+// for an alternate chain, get the timestamps from the main chain to complete
+// the needed number of timestamps for the BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW.
+bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+
+ if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
+ return true;
+
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size();
+ CHECK_AND_ASSERT_MES(start_top_height < m_db->height(), false, "internal error: passed start_height not < " << " m_db->height() -- " << start_top_height << " >= " << m_db->height());
+ size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0;
+ while (start_top_height != stop_offset)
+ {
+ timestamps.push_back(m_db->get_block_timestamp(start_top_height));
+ --start_top_height;
+ }
+ return true;
+}
+//------------------------------------------------------------------
+// If a block is to be added and its parent block is not the current
+// main chain top block, then we need to see if we know about its parent block.
+// If its parent block is part of a known forked chain, then we need to see
+// if that chain is long enough to become the main chain and re-org accordingly
+// if so. If not, we need to hang on to the block in case it becomes part of
+// a long forked chain eventually.
+bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ uint64_t block_height = get_block_height(b);
+ if(0 == block_height)
+ {
+ LOG_PRINT_L1("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative), but miner tx says height is 0.");
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+ // this basically says if the blockchain is smaller than the first
+ // checkpoint then alternate blocks are allowed. Alternatively, if the
+ // last checkpoint *before* the end of the current chain is also before
+ // the block to be added, then this is fine.
+ if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height))
+ {
+ LOG_PRINT_RED_L1("Block with id: " << id
+ << std::endl << " can't be accepted for alternative chain, block height: " << block_height
+ << std::endl << " blockchain height: " << get_current_blockchain_height());
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+
+ //block is not related with head of main chain
+ //first of all - look in alternative chains container
+ auto it_prev = m_alternative_chains.find(b.prev_id);
+ bool parent_in_main = m_db->block_exists(b.prev_id);
+ if(it_prev != m_alternative_chains.end() || parent_in_main)
+ {
+ //we have new block in alternative chain
+
+ //build alternative subchain, front -> mainchain, back -> alternative head
+ blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find()
+ std::list<blocks_ext_by_hash::iterator> alt_chain;
+ std::vector<uint64_t> timestamps;
+ while(alt_it != m_alternative_chains.end())
+ {
+ alt_chain.push_front(alt_it);
+ timestamps.push_back(alt_it->second.bl.timestamp);
+ alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
+ }
+
+ // if block to be added connects to known blocks that aren't part of the
+ // main chain -- that is, if we're adding on to an alternate chain
+ if(alt_chain.size())
+ {
+ // make sure alt chain doesn't somehow start past the end of the main chain
+ CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
+
+ // make sure that the blockchain contains the block that should connect
+ // this alternate chain with it.
+ if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
+ {
+ LOG_PRINT_L1("alternate chain does not appear to connect to main chain...");
+ return false;
+ }
+
+ // make sure block connects correctly to the main chain
+ auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
+ CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
+ complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
+ }
+ // if block not associated with known alternate chain
+ else
+ {
+ // if block parent is not part of main chain or an alternate chain,
+ // we ignore it
+ CHECK_AND_ASSERT_MES(parent_in_main, false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()");
+
+ complete_timestamps_vector(m_db->get_block_height(b.prev_id), timestamps);
+ }
+
+ // verify that the block's timestamp is within the acceptable range
+ // (not earlier than the median of the last X blocks)
+ if(!check_block_timestamp(timestamps, b))
+ {
+ LOG_PRINT_RED_L1("Block with id: " << id
+ << std::endl << " for alternative chain, has invalid timestamp: " << b.timestamp);
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+
+ // FIXME: consider moving away from block_extended_info at some point
+ block_extended_info bei = boost::value_initialized<block_extended_info>();
+ bei.bl = b;
+ bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(b.prev_id) + 1;
+
+ bool is_a_checkpoint;
+ if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint))
+ {
+ LOG_ERROR("CHECKPOINT VALIDATION FAILED");
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+
+ // Check the block's hash against the difficulty target for its alt chain
+ m_is_in_checkpoint_zone = false;
+ difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei);
+ CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!");
+ crypto::hash proof_of_work = null_hash;
+ get_block_longhash(bei.bl, proof_of_work, bei.height);
+ if(!check_hash(proof_of_work, current_diff))
+ {
+ LOG_PRINT_RED_L1("Block with id: " << id
+ << std::endl << " for alternative chain, does not have enough proof of work: " << proof_of_work
+ << std::endl << " expected difficulty: " << current_diff);
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+
+ if(!prevalidate_miner_transaction(b, bei.height))
+ {
+ LOG_PRINT_RED_L1("Block with id: " << epee::string_tools::pod_to_hex(id)
+ << " (as alternative) has incorrect miner transaction.");
+ bvc.m_verifivation_failed = true;
+ return false;
+
+ }
+
+ // FIXME:
+ // this brings up an interesting point: consider allowing to get block
+ // difficulty both by height OR by hash, not just height.
+ difficulty_type main_chain_cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+ if (alt_chain.size())
+ {
+ bei.cumulative_difficulty = it_prev->second.cumulative_difficulty;
+ }
+ else
+ {
+ // passed-in block's previous block's cumulative difficulty, found on the main chain
+ bei.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->get_block_height(b.prev_id));
+ }
+ bei.cumulative_difficulty += current_diff;
+
+ // add block to alternate blocks storage,
+ // as well as the current "alt chain" container
+ auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei));
+ CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist");
+ alt_chain.push_back(i_res.first);
+
+ // FIXME: is it even possible for a checkpoint to show up not on the main chain?
+ if(is_a_checkpoint)
+ {
+ //do reorganize!
+ LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_db->height() - 1 <<
+ ", checkpoint is found in alternative chain on height " << bei.height, LOG_LEVEL_0);
+
+ bool r = switch_to_alternative_blockchain(alt_chain, true);
+
+ bvc.m_added_to_main_chain = r;
+ bvc.m_verifivation_failed = !r;
+
+ return r;
+ }
+ else if(main_chain_cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain
+ {
+ //do reorganize!
+ LOG_PRINT_GREEN("###### REORGANIZE on height: "
+ << alt_chain.front()->second.height << " of " << m_db->height() - 1
+ << " with cum_difficulty " << m_db->get_block_cumulative_difficulty(m_db->height() - 1)
+ << std::endl << " alternative blockchain size: " << alt_chain.size()
+ << " with cum_difficulty " << bei.cumulative_difficulty, LOG_LEVEL_0
+ );
+
+ bool r = switch_to_alternative_blockchain(alt_chain, false);
+ if(r) bvc.m_added_to_main_chain = true;
+ else bvc.m_verifivation_failed = true;
+ return r;
+ }
+ else
+ {
+ LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height
+ << std::endl << "id:\t" << id
+ << std::endl << "PoW:\t" << proof_of_work
+ << std::endl << "difficulty:\t" << current_diff, LOG_LEVEL_0);
+ return true;
+ }
+ }
+ else
+ {
+ //block orphaned
+ bvc.m_marked_as_orphaned = true;
+ LOG_PRINT_RED_L1("Block recognized as orphaned and rejected, id = " << id);
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ if(start_offset > m_db->height())
+ return false;
+
+ if (!get_blocks(start_offset, count, blocks))
+ {
+ return false;
+ }
+
+ for(const block& blk : blocks)
+ {
+ std::list<crypto::hash> missed_ids;
+ get_transactions(blk.tx_hashes, txs, missed_ids);
+ CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain");
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ if(start_offset > m_db->height())
+ return false;
+
+ for(size_t i = start_offset; i < start_offset + count && i <= m_db->height();i++)
+ {
+ blocks.push_back(m_db->get_block_from_height(i));
+ }
+ return true;
+}
+//------------------------------------------------------------------
+//TODO: This function *looks* like it won't need to be rewritten
+// to use BlockchainDB, as it calls other functions that were,
+// but it warrants some looking into later.
+bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ rsp.current_blockchain_height = get_current_blockchain_height();
+ std::list<block> blocks;
+ get_blocks(arg.blocks, blocks, rsp.missed_ids);
+
+ BOOST_FOREACH(const auto& bl, blocks)
+ {
+ std::list<crypto::hash> missed_tx_id;
+ std::list<transaction> txs;
+ get_transactions(bl.tx_hashes, txs, rsp.missed_ids);
+ CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: has missed missed_tx_id.size()=" << missed_tx_id.size()
+ << std::endl << "for block id = " << get_block_hash(bl));
+ rsp.blocks.push_back(block_complete_entry());
+ block_complete_entry& e = rsp.blocks.back();
+ //pack block
+ e.block = t_serializable_object_to_blob(bl);
+ //pack transactions
+ BOOST_FOREACH(transaction& tx, txs)
+ e.txs.push_back(t_serializable_object_to_blob(tx));
+
+ }
+ //get another transactions, if need
+ std::list<transaction> txs;
+ get_transactions(arg.txs, txs, rsp.missed_ids);
+ //pack aside transactions
+ BOOST_FOREACH(const auto& tx, txs)
+ rsp.txs.push_back(t_serializable_object_to_blob(tx));
+
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::get_alternative_blocks(std::list<block>& blocks) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ BOOST_FOREACH(const auto& alt_bl, m_alternative_chains)
+ {
+ blocks.push_back(alt_bl.second.bl);
+ }
+ return true;
+}
+//------------------------------------------------------------------
+size_t Blockchain::get_alternative_blocks_count() const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ return m_alternative_chains.size();
+}
+//------------------------------------------------------------------
+// This function adds the output specified by <amount, i> to the result_outs container
+// unlocked and other such checks should be done by here.
+void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
+ oen.global_amount_index = i;
+ oen.out_key = m_db->get_output_key(amount, i);
+}
+//------------------------------------------------------------------
+// This function takes an RPC request for mixins and creates an RPC response
+// with the requested mixins.
+// TODO: figure out why this returns boolean / if we should be returning false
+// in some cases
+bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ srand(static_cast<unsigned int>(time(NULL)));
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // for each amount that we need to get mixins for, get <n> random outputs
+ // from BlockchainDB where <n> is req.outs_count (number of mixins).
+ for (uint64_t amount : req.amounts)
+ {
+ // create outs_for_amount struct and populate amount field
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
+ result_outs.amount = amount;
+
+ std::unordered_set<uint64_t> seen_indices;
+
+ // if there aren't enough outputs to mix with (or just enough),
+ // use all of them. Eventually this should become impossible.
+ if (m_db->get_num_outputs(amount) <= req.outs_count)
+ {
+ for (uint64_t i = 0; i < m_db->get_num_outputs(amount); i++)
+ {
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+
+ // if tx is unlocked, add output to result_outs
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ add_out_to_get_random_outs(result_outs, amount, i);
+ }
+
+ }
+ }
+ else
+ {
+ // while we still need more mixins
+ auto num_outs = m_db->get_num_outputs(amount);
+ while (result_outs.outs.size() < req.outs_count)
+ {
+ // if we've gone through every possible output, we've gotten all we can
+ if (seen_indices.size() == num_outs)
+ {
+ break;
+ }
+
+ // get a random output index from the DB. If we've already seen it,
+ // return to the top of the loop and try again, otherwise add it to the
+ // list of output indices we've seen.
+ uint64_t i = m_db->get_random_output(amount);
+ if (seen_indices.count(i))
+ {
+ continue;
+ }
+ seen_indices.emplace(i);
+
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+
+ // if the output's transaction is unlocked, add the output's index to
+ // our list.
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ add_out_to_get_random_outs(result_outs, amount, i);
+ }
+ }
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------
+// This function takes a list of block hashes from another node
+// on the network to find where the split point is between us and them.
+// This is used to see what to send another node that needs to sync.
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // make sure the request includes at least the genesis block, otherwise
+ // how can we expect to sync from the client that the block list came from?
+ if(!qblock_ids.size() /*|| !req.m_total_height*/)
+ {
+ LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection");
+ return false;
+ }
+
+ // make sure that the last block in the request's block list matches
+ // the genesis block
+ auto gen_hash = m_db->get_block_hash_from_height(0);
+ if(qblock_ids.back() != gen_hash)
+ {
+ LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << std::endl << "id: "
+ << qblock_ids.back() << ", " << std::endl << "expected: " << gen_hash
+ << "," << std::endl << " dropping connection");
+ return false;
+ }
+
+ // Find the first block the foreign chain has that we also have.
+ // Assume qblock_ids is in reverse-chronological order.
+ auto bl_it = qblock_ids.begin();
+ uint64_t split_height = 0;
+ for(; bl_it != qblock_ids.end(); bl_it++)
+ {
+ try
+ {
+ split_height = m_db->get_block_height(*bl_it);
+ break;
+ }
+ catch (const BLOCK_DNE& e)
+ {
+ continue;
+ }
+ catch (const std::exception& e)
+ {
+ LOG_PRINT_L1("Non-critical error trying to find block by hash in BlockchainDB, hash: " << *bl_it);
+ return false;
+ }
+ }
+
+ // this should be impossible, as we checked that we share the genesis block,
+ // but just in case...
+ if(bl_it == qblock_ids.end())
+ {
+ LOG_PRINT_L1("Internal error handling connection, can't find split point");
+ return false;
+ }
+
+ // if split_height remains 0, we didn't have any but the genesis block in common
+ // which is only fine if the blocks just have the genesis block
+ if(split_height == 0 && qblock_ids.size() > 1)
+ {
+ LOG_ERROR("Ours and foreign blockchain have only genesis block in common... o.O");
+ return false;
+ }
+
+ //we start to put block ids INCLUDING last known id, just to make other side be sure
+ starter_offset = split_height;
+ return true;
+}
+//------------------------------------------------------------------
+uint64_t Blockchain::block_difficulty(uint64_t i) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ try
+ {
+ return m_db->get_block_difficulty(i);
+ }
+ catch (const BLOCK_DNE& e)
+ {
+ LOG_PRINT_L0("Attempted to get block difficulty for height above blockchain height");
+ }
+ return 0;
+}
+//------------------------------------------------------------------
+template<class t_ids_container, class t_blocks_container, class t_missed_container>
+bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ for (const auto& block_hash : block_ids)
+ {
+ try
+ {
+ blocks.push_back(m_db->get_block(block_hash));
+ }
+ catch (const BLOCK_DNE& e)
+ {
+ missed_bs.push_back(block_hash);
+ }
+ catch (const std::exception& e)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------
+template<class t_ids_container, class t_tx_container, class t_missed_container>
+bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ for (const auto& tx_hash : txs_ids)
+ {
+ try
+ {
+ txs.push_back(m_db->get_tx(tx_hash));
+ }
+ catch (const TX_DNE& e)
+ {
+ missed_txs.push_back(tx_hash);
+ }
+ //FIXME: is this the correct way to handle this?
+ catch (const std::exception& e)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------
+void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ std::stringstream ss;
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ auto h = m_db->height();
+ if(start_index > h)
+ {
+ LOG_PRINT_L1("Wrong starter index set: " << start_index << ", expected max index " << h);
+ return;
+ }
+
+ for(size_t i = start_index; i <= h && i != end_index; i++)
+ {
+ ss << "height " << i
+ << ", timestamp " << m_db->get_block_timestamp(i)
+ << ", cumul_dif " << m_db->get_block_cumulative_difficulty(i)
+ << ", size " << m_db->get_block_size(i)
+ << "\nid\t\t" << m_db->get_block_hash_from_height(i)
+ << "\ndifficulty\t\t" << m_db->get_block_difficulty(i)
+ << ", nonce " << m_db->get_block_from_height(i).nonce
+ << ", tx_count " << m_db->get_block_from_height(i).tx_hashes.size()
+ << std::endl;
+ }
+ LOG_PRINT_L1("Current blockchain:" << std::endl << ss.str());
+ LOG_PRINT_L0("Blockchain printed with log level 1");
+}
+//------------------------------------------------------------------
+void Blockchain::print_blockchain_index()
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ std::stringstream ss;
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ auto height = m_db->height();
+ if (height != 0)
+ {
+ for(uint64_t i = 0; i <= height; i++)
+ {
+ ss << "height: " << i << ", hash: " << m_db->get_block_hash_from_height(i);
+ }
+ }
+
+ LOG_PRINT_L0("Current blockchain index:" << std::endl
+ << ss.str()
+ );
+}
+//------------------------------------------------------------------
+//TODO: remove this function and references to it
+void Blockchain::print_blockchain_outs(const std::string& file)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ return;
+}
+//------------------------------------------------------------------
+// Find the split point between us and foreign blockchain and return
+// (by reference) the most recent common block hash along with up to
+// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // if we can't find the split point, return false
+ if(!find_blockchain_supplement(qblock_ids, resp.start_height))
+ {
+ return false;
+ }
+
+ resp.total_height = get_current_blockchain_height();
+ size_t count = 0;
+ for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
+ {
+ resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i));
+ }
+ return true;
+}
+//------------------------------------------------------------------
+//FIXME: change argument to std::vector, low priority
+// find split point between ours and foreign blockchain (or start at
+// blockchain height <req_start_block>), and return up to max_count FULL
+// blocks by reference.
+bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // if a specific start height has been requested
+ if(req_start_block > 0)
+ {
+ // if requested height is higher than our chain, return false -- we can't help
+ if (req_start_block >= m_db->height())
+ {
+ return false;
+ }
+ start_height = req_start_block;
+ }
+ else
+ {
+ if(!find_blockchain_supplement(qblock_ids, start_height))
+ {
+ return false;
+ }
+ }
+
+ total_height = get_current_blockchain_height();
+ size_t count = 0;
+ for(size_t i = start_height; i < total_height && count < max_count; i++, count++)
+ {
+ blocks.resize(blocks.size()+1);
+ blocks.back().first = m_db->get_block_from_height(i);
+ std::list<crypto::hash> mis;
+ get_transactions(blocks.back().first.tx_hashes, blocks.back().second, mis);
+ CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
+ }
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::add_block_as_invalid(const block& bl, const crypto::hash& h)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ block_extended_info bei = AUTO_VAL_INIT(bei);
+ bei.bl = bl;
+ return add_block_as_invalid(bei, h);
+}
+//------------------------------------------------------------------
+bool Blockchain::add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ auto i_res = m_invalid_blocks.insert(std::map<crypto::hash, block_extended_info>::value_type(h, bei));
+ CHECK_AND_ASSERT_MES(i_res.second, false, "at insertion invalid by tx returned status existed");
+ LOG_PRINT_L1("BLOCK ADDED AS INVALID: " << h << std::endl << ", prev_id=" << bei.bl.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size());
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::have_block(const crypto::hash& id) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ if(m_db->block_exists(id))
+ {
+ LOG_PRINT_L3("block exists in main chain");
+ return true;
+ }
+
+ if(m_alternative_chains.count(id))
+ {
+ LOG_PRINT_L3("block found in m_alternative_chains");
+ return true;
+ }
+
+ if(m_invalid_blocks.count(id))
+ {
+ LOG_PRINT_L3("block found in m_invalid_blocks");
+ return true;
+ }
+
+ return false;
+}
+//------------------------------------------------------------------
+bool Blockchain::handle_block_to_main_chain(const block& bl, block_verification_context& bvc)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ crypto::hash id = get_block_hash(bl);
+ return handle_block_to_main_chain(bl, id, bvc);
+}
+//------------------------------------------------------------------
+size_t Blockchain::get_total_transactions() const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ return m_db->get_tx_count();
+}
+//------------------------------------------------------------------
+// This function checks each input in the transaction <tx> to make sure it
+// has not been used already, and adds its key to the container <keys_this_block>.
+//
+// This container should be managed by the code that validates blocks so we don't
+// have to store the used keys in a given block in the permanent storage only to
+// remove them later if the block fails validation.
+bool Blockchain::check_for_double_spend(const transaction& tx, key_images_container& keys_this_block) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ struct add_transaction_input_visitor: public boost::static_visitor<bool>
+ {
+ key_images_container& m_spent_keys;
+ BlockchainDB* m_db;
+ add_transaction_input_visitor(key_images_container& spent_keys, BlockchainDB* db):m_spent_keys(spent_keys), m_db(db)
+ {}
+ bool operator()(const txin_to_key& in) const
+ {
+ const crypto::key_image& ki = in.k_image;
+
+ // attempt to insert the newly-spent key into the container of
+ // keys spent this block. If this fails, the key was spent already
+ // in this block, return false to flag that a double spend was detected.
+ //
+ // if the insert into the block-wide spent keys container succeeds,
+ // check the blockchain-wide spent keys container and make sure the
+ // key wasn't used in another block already.
+ auto r = m_spent_keys.insert(ki);
+ if(!r.second || m_db->has_key_image(ki))
+ {
+ //double spend detected
+ return false;
+ }
+
+ // if no double-spend detected, return true
+ return true;
+ }
+
+ bool operator()(const txin_gen& tx) const{return true;}
+ bool operator()(const txin_to_script& tx) const{return false;}
+ bool operator()(const txin_to_scripthash& tx) const{return false;}
+ };
+
+ for (const txin_v& in : tx.vin)
+ {
+ if(!boost::apply_visitor(add_transaction_input_visitor(keys_this_block, m_db), in))
+ {
+ LOG_ERROR("Double spend detected!");
+ return false;
+ }
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ if (!m_db->tx_exists(tx_id))
+ {
+ LOG_PRINT_RED_L1("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id);
+ return false;
+ }
+
+ // get amount output indexes, currently referred to in parts as "output global indices", but they are actually specific to amounts
+ indexs = m_db->get_tx_amount_output_indices(tx_id);
+ CHECK_AND_ASSERT_MES(indexs.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty");
+
+ return true;
+}
+//------------------------------------------------------------------
+// This function overloads its sister function with
+// an extra value (hash of highest block that holds an output used as input)
+// as a return-by-reference.
+bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ bool res = check_tx_inputs(tx, &max_used_block_height);
+ if(!res) return false;
+ CHECK_AND_ASSERT_MES(max_used_block_height < m_db->height(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_db->height());
+ max_used_block_id = m_db->get_block_hash_from_height(max_used_block_height);
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ BOOST_FOREACH(const txin_v& in, tx.vin)
+ {
+ CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true);
+ if(have_tx_keyimg_as_spent(in_to_key.k_image))
+ return true;
+ }
+ return false;
+}
+//------------------------------------------------------------------
+// This function validates transaction inputs and their keys. Previously
+// it also performed double spend checking, but that has been moved to its
+// own function.
+bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ size_t sig_index = 0;
+ if(pmax_used_block_height)
+ *pmax_used_block_height = 0;
+
+ crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
+
+ for (const auto& txin : tx.vin)
+ {
+ // make sure output being spent is of type txin_to_key, rather than
+ // e.g. txin_gen, which is only used for miner transactions
+ CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at Blockchain::check_tx_inputs");
+ const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
+
+ // make sure tx output has key offset(s) (is signed to be used)
+ CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx));
+
+ // basically, make sure number of inputs == number of signatures
+ CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
+
+ // make sure that output being spent matches up correctly with the
+ // signature spending it.
+ if(!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height))
+ {
+ LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
+ if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
+ {
+ LOG_PRINT_L1(" *pmax_used_block_height: " << *pmax_used_block_height);
+ }
+ return false;
+ }
+
+ sig_index++;
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
+// This function checks to see if a tx is unlocked. unlock_time is either
+// a block index or a unix time.
+bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
+ {
+ //interpret as block index
+ if(get_current_blockchain_height() + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
+ return true;
+ else
+ return false;
+ }else
+ {
+ //interpret as time
+ uint64_t current_time = static_cast<uint64_t>(time(NULL));
+ if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time)
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+//------------------------------------------------------------------
+// This function locates all outputs associated with a given input (mixins)
+// and validates that they exist and are usable. It also checks the ring
+// signature for each input.
+bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ struct outputs_visitor
+ {
+ std::vector<const crypto::public_key *>& m_p_output_keys;
+ std::vector<crypto::public_key >& m_output_keys;
+ const Blockchain& m_bch;
+ outputs_visitor(std::vector<crypto::public_key >& output_keys, std::vector<const crypto::public_key *>& p_output_keys, const Blockchain& bch) : m_output_keys(output_keys), m_p_output_keys(p_output_keys), m_bch(bch)
+ {}
+ bool handle_output(const transaction& tx, const tx_out& out)
+ {
+ //check tx unlock time
+ if(!m_bch.is_tx_spendtime_unlocked(tx.unlock_time))
+ {
+ LOG_PRINT_L1("One of outputs for one of inputs has wrong tx.unlock_time = " << tx.unlock_time);
+ return false;
+ }
+
+ if(out.target.type() != typeid(txout_to_key))
+ {
+ LOG_PRINT_L1("Output has wrong type id, which=" << out.target.which());
+ return false;
+ }
+
+ m_output_keys.push_back(boost::get<txout_to_key>(out.target).key);
+ return true;
+ }
+ };
+
+ //check ring signature
+ std::vector<crypto::public_key> output_keys;
+ std::vector<const crypto::public_key *> p_output_keys;
+ outputs_visitor vi(output_keys, p_output_keys, *this);
+ if(!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height))
+ {
+ LOG_PRINT_L1("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size());
+ return false;
+ }
+
+ for (auto& k : output_keys)
+ {
+ p_output_keys.push_back(&k);
+ }
+
+ if(txin.key_offsets.size() != output_keys.size())
+ {
+ LOG_PRINT_L1("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size());
+ return false;
+ }
+ CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
+ if(m_is_in_checkpoint_zone)
+ return true;
+ return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, p_output_keys, sig.data());
+}
+//------------------------------------------------------------------
+//TODO: Is this intended to do something else? Need to look into the todo there.
+uint64_t Blockchain::get_adjusted_time() const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ //TODO: add collecting median time
+ return time(NULL);
+}
+//------------------------------------------------------------------
+//TODO: revisit, has changed a bit on upstream
+bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ uint64_t median_ts = epee::misc_utils::median(timestamps);
+
+ if(b.timestamp < median_ts)
+ {
+ LOG_PRINT_L1("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts);
+ return false;
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
+// This function grabs the timestamps from the most recent <n> blocks,
+// where n = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW. If there are not those many
+// blocks in the blockchain, the timestap is assumed to be valid. If there
+// are, this function returns:
+// true if the block's timestamp is not less than the timestamp of the
+// median of the selected blocks
+// false otherwise
+bool Blockchain::check_block_timestamp(const block& b) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
+ {
+ LOG_PRINT_L1("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours");
+ return false;
+ }
+
+ // if not enough blocks, no proper median yet, return true
+ if(m_db->height() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW + 1)
+ {
+ return true;
+ }
+
+ std::vector<uint64_t> timestamps;
+ auto h = m_db->height();
+
+ // need most recent 60 blocks, get index of first of those
+ // using +1 because BlockchainDB::height() returns the index of the top block,
+ // not the size of the blockchain (0-indexed)
+ size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - 1;
+ for(;offset < h; ++offset)
+ {
+ timestamps.push_back(m_db->get_block_timestamp(offset));
+ }
+
+ return check_block_timestamp(timestamps, b);
+}
+//------------------------------------------------------------------
+// Needs to validate the block and acquire each transaction from the
+// transaction mem_pool, then pass the block and transactions to
+// m_db->add_block()
+bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+
+ TIME_MEASURE_START(block_processing_time);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ if(bl.prev_id != get_tail_id())
+ {
+ LOG_PRINT_L1("Block with id: " << id << std::endl
+ << "has wrong prev_id: " << bl.prev_id << std::endl
+ << "expected: " << get_tail_id());
+ return false;
+ }
+
+ // make sure block timestamp is not less than the median timestamp
+ // of a set number of the most recent blocks.
+ if(!check_block_timestamp(bl))
+ {
+ LOG_PRINT_L1("Block with id: " << id << std::endl
+ << "has invalid timestamp: " << bl.timestamp);
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+
+ //check proof of work
+ TIME_MEASURE_START(target_calculating_time);
+
+ // get the target difficulty for the block.
+ // the calculation can overflow, among other failure cases,
+ // so we need to check the return type.
+ // FIXME: get_difficulty_for_next_block can also assert, look into
+ // changing this to throwing exceptions instead so we can clean up.
+ difficulty_type current_diffic = get_difficulty_for_next_block();
+ CHECK_AND_ASSERT_MES(current_diffic, false, "!!!!!!!!! difficulty overhead !!!!!!!!!");
+
+ TIME_MEASURE_FINISH(target_calculating_time);
+
+ TIME_MEASURE_START(longhash_calculating_time);
+
+ crypto::hash proof_of_work = null_hash;
+
+ // Formerly the code below contained an if loop with the following condition
+ // !m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())
+ // however, this caused the daemon to not bother checking PoW for blocks
+ // before checkpoints, which is very dangerous behaviour. We moved the PoW
+ // validation out of the next chunk of code to make sure that we correctly
+ // check PoW now.
+ // FIXME: height parameter is not used...should it be used or should it not
+ // be a parameter?
+ proof_of_work = get_block_longhash(bl, m_db->height());
+
+ // validate proof_of_work versus difficulty target
+ if(!check_hash(proof_of_work, current_diffic))
+ {
+ LOG_PRINT_L1("Block with id: " << id << std::endl
+ << "does not have enough proof of work: " << proof_of_work << std::endl
+ << "unexpected difficulty: " << current_diffic );
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+
+ // If we're at a checkpoint, ensure that our hardcoded checkpoint hash
+ // is correct.
+ if(m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height()))
+ {
+ if(!m_checkpoints.check_block(get_current_blockchain_height(), id))
+ {
+ LOG_ERROR("CHECKPOINT VALIDATION FAILED");
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+ }
+
+ TIME_MEASURE_FINISH(longhash_calculating_time);
+
+ // sanity check basic miner tx properties
+ if(!prevalidate_miner_transaction(bl, m_db->height()))
+ {
+ LOG_PRINT_L1("Block with id: " << id
+ << " failed to pass prevalidation");
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+
+ size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx);
+ size_t cumulative_block_size = coinbase_blob_size;
+
+ std::vector<transaction> txs;
+ key_images_container keys;
+
+ uint64_t fee_summary = 0;
+
+ // Iterate over the block's transaction hashes, grabbing each
+ // from the tx_pool and validating them. Each is then added
+ // to txs. Keys spent in each are added to <keys> by the double spend check.
+ for (const crypto::hash& tx_id : bl.tx_hashes)
+ {
+ transaction tx;
+ size_t blob_size = 0;
+ uint64_t fee = 0;
+
+ if (m_db->tx_exists(tx_id))
+ {
+ LOG_PRINT_L1("Block with id: " << id << " attempting to add transaction already in blockchain with id: " << tx_id);
+ bvc.m_verifivation_failed = true;
+ break;
+ }
+
+ // get transaction with hash <tx_id> from tx_pool
+ if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee))
+ {
+ LOG_PRINT_L1("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
+ bvc.m_verifivation_failed = true;
+ break;
+ }
+
+ // add the transaction to the temp list of transactions, so we can either
+ // store the list of transactions all at once or return the ones we've
+ // taken from the tx_pool back to it if the block fails verification.
+ txs.push_back(tx);
+
+ // validate that transaction inputs and the keys spending them are correct.
+ if(!check_tx_inputs(tx))
+ {
+ LOG_PRINT_L1("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs.");
+
+ //TODO: why is this done? make sure that keeping invalid blocks makes sense.
+ add_block_as_invalid(bl, id);
+ LOG_PRINT_L1("Block with id " << id << " added as invalid becouse of wrong inputs in transactions");
+ bvc.m_verifivation_failed = true;
+ break;
+ }
+
+ if (!check_for_double_spend(tx, keys))
+ {
+ LOG_PRINT_L0("Double spend detected in transaction (id: " << tx_id);
+ bvc.m_verifivation_failed = true;
+ break;
+ }
+
+ fee_summary += fee;
+ cumulative_block_size += blob_size;
+ }
+
+ uint64_t base_reward = 0;
+ uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
+ if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins))
+ {
+ LOG_PRINT_L1("Block with id: " << id
+ << " has incorrect miner transaction");
+ bvc.m_verifivation_failed = true;
+ }
+
+
+ block_extended_info bei = boost::value_initialized<block_extended_info>();
+ size_t block_size;
+ difficulty_type cumulative_difficulty;
+
+ // populate various metadata about the block to be stored alongside it.
+ block_size = cumulative_block_size;
+ cumulative_difficulty = current_diffic;
+ already_generated_coins = already_generated_coins + base_reward;
+ if(m_db->height())
+ cumulative_difficulty += m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+
+ update_next_cumulative_size_limit();
+
+ TIME_MEASURE_FINISH(block_processing_time);
+
+ uint64_t new_height = 0;
+ bool add_success = true;
+ if (!bvc.m_verifivation_failed)
+ {
+ try
+ {
+ new_height = m_db->add_block(bl, block_size, cumulative_difficulty, already_generated_coins, txs);
+ }
+ catch (const std::exception& e)
+ {
+ //TODO: figure out the best way to deal with this failure
+ LOG_ERROR("Error adding block with hash: " << id << " to blockchain, what = " << e.what());
+ add_success = false;
+ }
+ }
+
+ // if we failed for any reason to verify the block, return taken
+ // transactions to the tx_pool.
+ if (bvc.m_verifivation_failed || !add_success)
+ {
+ // return taken transactions to transaction pool
+ for (auto& tx : txs)
+ {
+ cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
+ if (!m_tx_pool.add_tx(tx, tvc, true))
+ {
+ LOG_PRINT_L0("Failed to return taken transaction with hash: " << get_transaction_hash(tx) << " to tx_pool");
+ }
+ }
+ return false;
+ }
+
+ LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id
+ << std::endl << "PoW:\t" << proof_of_work
+ << std::endl << "HEIGHT " << new_height << ", difficulty:\t" << current_diffic
+ << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary)
+ << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size
+ << ", " << block_processing_time << "("<< target_calculating_time << "/" << longhash_calculating_time << ")ms");
+
+ bvc.m_added_to_main_chain = true;
+
+ // appears to be a NOP *and* is called elsewhere. wat?
+ m_tx_pool.on_blockchain_inc(new_height, id);
+
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::update_next_cumulative_size_limit()
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ std::vector<size_t> sz;
+ get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
+
+ uint64_t median = epee::misc_utils::median(sz);
+ if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE)
+ median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE;
+
+ m_current_block_cumul_sz_limit = median*2;
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::add_new_block(const block& bl_, block_verification_context& bvc)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ //copy block here to let modify block.target
+ block bl = bl_;
+ crypto::hash id = get_block_hash(bl);
+ CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
+ CRITICAL_REGION_LOCAL1(m_blockchain_lock);
+ if(have_block(id))
+ {
+ LOG_PRINT_L3("block with id = " << id << " already exists");
+ bvc.m_already_exists = true;
+ return false;
+ }
+
+ //check that block refers to chain tail
+ if(!(bl.prev_id == get_tail_id()))
+ {
+ //chain switching or wrong block
+ bvc.m_added_to_main_chain = false;
+ return handle_alternative_block(bl, id, bvc);
+ //never relay alternative blocks
+ }
+
+ return handle_block_to_main_chain(bl, id, bvc);
+}
+//------------------------------------------------------------------
+void Blockchain::check_against_checkpoints(checkpoints& points, bool enforce)
+{
+ const auto& pts = points.get_points();
+
+ for (const auto& pt : pts)
+ {
+ // if the checkpoint is for a block we don't have yet, move on
+ if (pt.first >= m_db->height())
+ {
+ continue;
+ }
+
+ if (!points.check_block(pt.first, m_db->get_block_hash_from_height(pt.first)))
+ {
+ // if asked to enforce checkpoints, roll back to a couple of blocks before the checkpoint
+ if (enforce)
+ {
+ LOG_ERROR("Local blockchain failed to pass a checkpoint, rolling back!");
+ std::list<block> empty;
+ rollback_blockchain_switching(empty, pt.first - 2);
+ }
+ else
+ {
+ LOG_ERROR("WARNING: local blockchain failed to pass a MoneroPulse checkpoint, and you could be on a fork. You should either sync up from scratch, OR download a fresh blockchain bootstrap, OR enable checkpoint enforcing with the --enforce-dns-checkpointing command-line option");
+ }
+ }
+ }
+}
+//------------------------------------------------------------------
+// returns false if any of the checkpoints loading returns false.
+// That should happen only if a checkpoint is added that conflicts
+// with an existing checkpoint.
+bool Blockchain::update_checkpoints(const std::string& file_path, bool check_dns)
+{
+ if (!cryptonote::load_checkpoints_from_json(m_checkpoints, file_path))
+ {
+ return false;
+ }
+
+ // if we're checking both dns and json, load checkpoints from dns.
+ // if we're not hard-enforcing dns checkpoints, handle accordingly
+ if (m_enforce_dns_checkpoints && check_dns)
+ {
+ if (!cryptonote::load_checkpoints_from_dns(m_checkpoints))
+ {
+ return false;
+ }
+ }
+ else if (check_dns)
+ {
+ checkpoints dns_points;
+ cryptonote::load_checkpoints_from_dns(dns_points);
+ if (m_checkpoints.check_for_conflicts(dns_points))
+ {
+ check_against_checkpoints(dns_points, false);
+ }
+ else
+ {
+ LOG_PRINT_L0("One or more checkpoints fetched from DNS conflicted with existing checkpoints!");
+ }
+ }
+
+ check_against_checkpoints(m_checkpoints, true);
+
+ return true;
+}
+//------------------------------------------------------------------
+void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints)
+{
+ m_enforce_dns_checkpoints = enforce_checkpoints;
+}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
new file mode 100644
index 000000000..6ab963ac7
--- /dev/null
+++ b/src/cryptonote_core/blockchain.h
@@ -0,0 +1,226 @@
+// Copyright (c) 2014, 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 <boost/serialization/serialization.hpp>
+#include <boost/serialization/version.hpp>
+#include <boost/serialization/list.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/global_fun.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/foreach.hpp>
+#include <atomic>
+
+#include "syncobj.h"
+#include "string_tools.h"
+#include "cryptonote_basic.h"
+#include "common/util.h"
+#include "cryptonote_protocol/cryptonote_protocol_defs.h"
+#include "rpc/core_rpc_server_commands_defs.h"
+#include "difficulty.h"
+#include "cryptonote_core/cryptonote_format_utils.h"
+#include "verification_context.h"
+#include "crypto/hash.h"
+#include "checkpoints.h"
+#include "blockchain_db.h"
+
+namespace cryptonote
+{
+ class tx_memory_pool;
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class Blockchain
+ {
+ public:
+ struct transaction_chain_entry
+ {
+ transaction tx;
+ uint64_t m_keeper_block_height;
+ size_t m_blob_size;
+ std::vector<uint64_t> m_global_output_indexes;
+ };
+
+ struct block_extended_info
+ {
+ block bl;
+ uint64_t height;
+ size_t block_cumulative_size;
+ difficulty_type cumulative_difficulty;
+ uint64_t already_generated_coins;
+ };
+
+ Blockchain(tx_memory_pool& tx_pool);
+
+ bool init(const std::string& config_folder, bool testnet = false);
+ bool deinit();
+
+ void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; }
+
+ //bool push_new_block();
+ bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
+ bool get_alternative_blocks(std::list<block>& blocks) const;
+ size_t get_alternative_blocks_count() const;
+ crypto::hash get_block_id_by_height(uint64_t height) const;
+ bool get_block_by_hash(const crypto::hash &h, block &blk) const;
+ void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const;
+
+ template<class archive_t>
+ void serialize(archive_t & ar, const unsigned int version);
+
+ bool have_tx(const crypto::hash &id) const;
+ bool have_tx_keyimges_as_spent(const transaction &tx) const;
+ bool have_tx_keyimg_as_spent(const crypto::key_image &key_im) const;
+
+ template<class visitor_t>
+ bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL) const;
+
+ uint64_t get_current_blockchain_height() const;
+ crypto::hash get_tail_id() const;
+ crypto::hash get_tail_id(uint64_t& height) const;
+ difficulty_type get_difficulty_for_next_block() const;
+ bool add_new_block(const block& bl_, block_verification_context& bvc);
+ bool reset_and_set_genesis_block(const block& b);
+ bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const;
+ bool have_block(const crypto::hash& id) const;
+ size_t get_total_transactions() const;
+ bool get_short_chain_history(std::list<crypto::hash>& ids) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const;
+ bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
+ bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
+ bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
+ bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const;
+ bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const;
+ bool store_blockchain();
+ bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height = NULL) const;
+ bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL) const;
+ bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id) const;
+ uint64_t get_current_cumulative_blocksize_limit() const;
+ bool is_storing_blockchain()const{return m_is_blockchain_storing;}
+ uint64_t block_difficulty(uint64_t i) const;
+
+ template<class t_ids_container, class t_blocks_container, class t_missed_container>
+ bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const;
+
+ template<class t_ids_container, class t_tx_container, class t_missed_container>
+ bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const;
+
+ //debug functions
+ void print_blockchain(uint64_t start_index, uint64_t end_index);
+ void print_blockchain_index();
+ void print_blockchain_outs(const std::string& file);
+
+ void check_against_checkpoints(checkpoints& points, bool enforce);
+ void set_enforce_dns_checkpoints(bool enforce);
+ bool update_checkpoints(const std::string& file_path, bool check_dns);
+
+ private:
+ typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
+ typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container;
+ typedef std::unordered_set<crypto::key_image> key_images_container;
+ typedef std::vector<block_extended_info> blocks_container;
+ typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash;
+ typedef std::unordered_map<crypto::hash, block> blocks_by_hash;
+ typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction
+
+ BlockchainDB* m_db;
+
+ tx_memory_pool& m_tx_pool;
+ mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
+
+ // main chain
+ blocks_container m_blocks; // height -> block_extended_info
+ blocks_by_id_index m_blocks_index; // crypto::hash -> height
+ transactions_container m_transactions;
+ key_images_container m_spent_keys;
+ size_t m_current_block_cumul_sz_limit;
+
+
+ // all alternative chains
+ blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info
+
+ // some invalid blocks
+ blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info
+ outputs_container m_outputs;
+
+
+ std::string m_config_folder;
+ checkpoints m_checkpoints;
+ std::atomic<bool> m_is_in_checkpoint_zone;
+ std::atomic<bool> m_is_blockchain_storing;
+ bool m_testnet;
+ bool m_enforce_dns_checkpoints;
+
+ bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain);
+ block pop_block_from_blockchain();
+ bool purge_transaction_from_blockchain(const crypto::hash& tx_id);
+ bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check);
+
+ bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc);
+ bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc);
+ bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
+ difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const;
+ bool prevalidate_miner_transaction(const block& b, uint64_t height);
+ bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins);
+ bool validate_transaction(const block& b, uint64_t height, const transaction& tx);
+ bool rollback_blockchain_switching(std::list<block>& original_chain, uint64_t rollback_height);
+ bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height);
+ bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes);
+ bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id);
+ void get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const;
+ void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
+ bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
+ bool add_block_as_invalid(const block& bl, const crypto::hash& h);
+ bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h);
+ bool check_block_timestamp(const block& b) const;
+ bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b) const;
+ uint64_t get_adjusted_time() const;
+ bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
+ bool update_next_cumulative_size_limit();
+
+ bool check_for_double_spend(const transaction& tx, key_images_container& keys_this_block) const;
+ };
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+
+ #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12
+
+ //------------------------------------------------------------------
+
+} // namespace cryptonote
+
+BOOST_CLASS_VERSION(cryptonote::Blockchain, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER)
diff --git a/src/cryptonote_core/blockchain_db.cpp b/src/cryptonote_core/blockchain_db.cpp
new file mode 100644
index 000000000..0ee10bd4d
--- /dev/null
+++ b/src/cryptonote_core/blockchain_db.cpp
@@ -0,0 +1,122 @@
+// Copyright (c) 2014, 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 "cryptonote_core/blockchain_db.h"
+#include "cryptonote_format_utils.h"
+
+using epee::string_tools::pod_to_hex;
+
+namespace cryptonote
+{
+
+void BlockchainDB::pop_block()
+{
+ block blk;
+ std::vector<transaction> txs;
+ pop_block(blk, txs);
+}
+
+void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx)
+{
+ crypto::hash tx_hash = get_transaction_hash(tx);
+
+ add_transaction_data(blk_hash, tx);
+
+ // iterate tx.vout using indices instead of C++11 foreach syntax because
+ // we need the index
+ if (tx.vout.size() != 0) // it may be technically possible for a tx to have no outputs
+ {
+ for (uint64_t i = 0; i < tx.vout.size(); ++i)
+ {
+ add_output(tx_hash, tx.vout[i], i);
+ }
+
+ for (const txin_v& tx_input : tx.vin)
+ {
+ if (tx_input.type() == typeid(txin_to_key))
+ {
+ add_spent_key(boost::get<txin_to_key>(tx_input).k_image);
+ }
+ }
+ }
+}
+
+uint64_t BlockchainDB::add_block( const block& blk
+ , const size_t& block_size
+ , const difficulty_type& cumulative_difficulty
+ , const uint64_t& coins_generated
+ , const std::vector<transaction>& txs
+ )
+{
+ crypto::hash blk_hash = get_block_hash(blk);
+
+ // call out to subclass implementation to add the block & metadata
+ add_block(blk, block_size, cumulative_difficulty, coins_generated);
+
+ // call out to add the transactions
+
+ add_transaction(blk_hash, blk.miner_tx);
+ for (const transaction& tx : txs)
+ {
+ add_transaction(blk_hash, tx);
+ }
+
+ return height();
+}
+
+void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
+{
+ blk = get_top_block();
+
+ remove_block();
+
+ remove_transaction(get_transaction_hash(blk.miner_tx));
+ for (const auto& h : blk.tx_hashes)
+ {
+ txs.push_back(get_tx(h));
+ remove_transaction(h);
+ }
+}
+
+void BlockchainDB::remove_transaction(const crypto::hash& tx_hash)
+{
+ transaction tx = get_tx(tx_hash);
+
+ for (const txin_v& tx_input : tx.vin)
+ {
+ if (tx_input.type() == typeid(txin_to_key))
+ {
+ remove_spent_key(boost::get<txin_to_key>(tx_input).k_image);
+ }
+ }
+
+ // need tx as tx.vout has the tx outputs, and the output amounts are needed
+ remove_transaction_data(tx_hash, tx);
+}
+
+} // namespace cryptonote
diff --git a/src/cryptonote_core/blockchain_db.h b/src/cryptonote_core/blockchain_db.h
new file mode 100644
index 000000000..db56c7c07
--- /dev/null
+++ b/src/cryptonote_core/blockchain_db.h
@@ -0,0 +1,467 @@
+// Copyright (c) 2014, 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.
+#ifndef BLOCKCHAIN_DB_H
+#define BLOCKCHAIN_DB_H
+
+#include <list>
+#include <string>
+#include <exception>
+#include "crypto/hash.h"
+#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_core/difficulty.h"
+
+/* DB Driver Interface
+ *
+ * The DB interface is a store for the canonical block chain.
+ * It serves as a persistent storage for the blockchain.
+ *
+ * For the sake of efficiency, the reference implementation will also
+ * store some blockchain data outside of the blocks, such as spent
+ * transfer key images, unspent transaction outputs, etc.
+ *
+ * Transactions are duplicated so that we don't have to fetch a whole block
+ * in order to fetch a transaction from that block. If this is deemed
+ * unnecessary later, this can change.
+ *
+ * Spent key images are duplicated outside of the blocks so it is quick
+ * to verify an output hasn't already been spent
+ *
+ * Unspent transaction outputs are duplicated to quickly gather random
+ * outputs to use for mixins
+ *
+ * IMPORTANT:
+ * A concrete implementation of this interface should populate these
+ * duplicated members! It is possible to have a partial implementation
+ * of this interface call to private members of the interface to be added
+ * later that will then populate as needed.
+ *
+ * General:
+ * open()
+ * close()
+ * sync()
+ * reset()
+ *
+ * Lock and unlock provided for reorg externally, and for block
+ * additions internally, this way threaded reads are completely fine
+ * unless the blockchain is changing.
+ * bool lock()
+ * unlock()
+ *
+ * vector<str> get_filenames()
+ *
+ * Blocks:
+ * bool block_exists(hash)
+ * height add_block(block, block_size, cumulative_difficulty, coins_generated, transactions)
+ * block get_block(hash)
+ * height get_block_height(hash)
+ * header get_block_header(hash)
+ * block get_block_from_height(height)
+ * size_t get_block_size(height)
+ * difficulty get_block_cumulative_difficulty(height)
+ * uint64_t get_block_already_generated_coins(height)
+ * uint64_t get_block_timestamp(height)
+ * uint64_t get_top_block_timestamp()
+ * hash get_block_hash_from_height(height)
+ * blocks get_blocks_range(height1, height2)
+ * hashes get_hashes_range(height1, height2)
+ * hash top_block_hash()
+ * block get_top_block()
+ * height height()
+ * void pop_block(block&, tx_list&)
+ *
+ * Transactions:
+ * bool tx_exists(hash)
+ * uint64_t get_tx_unlock_time(hash)
+ * tx get_tx(hash)
+ * uint64_t get_tx_count()
+ * tx_list get_tx_list(hash_list)
+ * height get_tx_block_height(hash)
+ *
+ * Outputs:
+ * index get_random_output(amount)
+ * uint64_t get_num_outputs(amount)
+ * pub_key get_output_key(amount, index)
+ * tx_out get_output(tx_hash, index)
+ * hash,index get_output_tx_and_index_from_global(index)
+ * hash,index get_output_tx_and_index(amount, index)
+ * vec<uint64> get_tx_output_indices(tx_hash)
+ *
+ *
+ * Spent Output Key Images:
+ * bool has_key_image(key_image)
+ *
+ * Exceptions:
+ * DB_ERROR -- generic
+ * DB_OPEN_FAILURE
+ * DB_CREATE_FAILURE
+ * DB_SYNC_FAILURE
+ * BLOCK_DNE
+ * BLOCK_PARENT_DNE
+ * BLOCK_EXISTS
+ * BLOCK_INVALID -- considering making this multiple errors
+ * TX_DNE
+ * TX_EXISTS
+ * OUTPUT_DNE
+ * OUTPUT_EXISTS
+ * KEY_IMAGE_EXISTS
+ */
+
+namespace cryptonote
+{
+
+// typedef for convenience
+typedef std::pair<crypto::hash, uint64_t> tx_out_index;
+
+/***********************************
+ * Exception Definitions
+ ***********************************/
+class DB_EXCEPTION : public std::exception
+{
+ private:
+ std::string m;
+
+ protected:
+ DB_EXCEPTION(const char *s) : m(s) { }
+
+ public:
+ virtual ~DB_EXCEPTION() { }
+
+ const char* what() const throw()
+ {
+ return m.c_str();
+ }
+};
+
+class DB_ERROR : public DB_EXCEPTION
+{
+ public:
+ DB_ERROR() : DB_EXCEPTION("Generic DB Error") { }
+ DB_ERROR(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class DB_OPEN_FAILURE : public DB_EXCEPTION
+{
+ public:
+ DB_OPEN_FAILURE() : DB_EXCEPTION("Failed to open the db") { }
+ DB_OPEN_FAILURE(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class DB_CREATE_FAILURE : public DB_EXCEPTION
+{
+ public:
+ DB_CREATE_FAILURE() : DB_EXCEPTION("Failed to create the db") { }
+ DB_CREATE_FAILURE(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class DB_SYNC_FAILURE : public DB_EXCEPTION
+{
+ public:
+ DB_SYNC_FAILURE() : DB_EXCEPTION("Failed to sync the db") { }
+ DB_SYNC_FAILURE(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class BLOCK_DNE : public DB_EXCEPTION
+{
+ public:
+ BLOCK_DNE() : DB_EXCEPTION("The block requested does not exist") { }
+ BLOCK_DNE(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class BLOCK_PARENT_DNE : public DB_EXCEPTION
+{
+ public:
+ BLOCK_PARENT_DNE() : DB_EXCEPTION("The parent of the block does not exist") { }
+ BLOCK_PARENT_DNE(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class BLOCK_EXISTS : public DB_EXCEPTION
+{
+ public:
+ BLOCK_EXISTS() : DB_EXCEPTION("The block to be added already exists!") { }
+ BLOCK_EXISTS(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class BLOCK_INVALID : public DB_EXCEPTION
+{
+ public:
+ BLOCK_INVALID() : DB_EXCEPTION("The block to be added did not pass validation!") { }
+ BLOCK_INVALID(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class TX_DNE : public DB_EXCEPTION
+{
+ public:
+ TX_DNE() : DB_EXCEPTION("The transaction requested does not exist") { }
+ TX_DNE(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class TX_EXISTS : public DB_EXCEPTION
+{
+ public:
+ TX_EXISTS() : DB_EXCEPTION("The transaction to be added already exists!") { }
+ TX_EXISTS(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class OUTPUT_DNE : public DB_EXCEPTION
+{
+ public:
+ OUTPUT_DNE() : DB_EXCEPTION("The output requested does not exist!") { }
+ OUTPUT_DNE(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class OUTPUT_EXISTS : public DB_EXCEPTION
+{
+ public:
+ OUTPUT_EXISTS() : DB_EXCEPTION("The output to be added already exists!") { }
+ OUTPUT_EXISTS(const char* s) : DB_EXCEPTION(s) { }
+};
+
+class KEY_IMAGE_EXISTS : public DB_EXCEPTION
+{
+ public:
+ KEY_IMAGE_EXISTS() : DB_EXCEPTION("The spent key image to be added already exists!") { }
+ KEY_IMAGE_EXISTS(const char* s) : DB_EXCEPTION(s) { }
+};
+
+/***********************************
+ * End of Exception Definitions
+ ***********************************/
+
+
+class BlockchainDB
+{
+private:
+ /*********************************************************************
+ * private virtual members
+ *********************************************************************/
+
+ // tells the subclass to add the block and metadata to storage
+ virtual void add_block( const block& blk
+ , const size_t& block_size
+ , const difficulty_type& cumulative_difficulty
+ , const uint64_t& coins_generated
+ ) = 0;
+
+ // tells the subclass to remove data about the top block
+ virtual void remove_block() = 0;
+
+ // tells the subclass to store the transaction and its metadata
+ virtual void add_transaction_data(const crypto::hash& blk_hash, const transaction& tx) = 0;
+
+ // tells the subclass to remove data about a transaction
+ virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) = 0;
+
+ // tells the subclass to store an output
+ virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index) = 0;
+
+ // tells the subclass to remove an output
+ virtual void remove_output(const tx_out& tx_output) = 0;
+
+ // tells the subclass to store a spent key
+ virtual void add_spent_key(const crypto::key_image& k_image) = 0;
+
+ // tells the subclass to remove a spent key
+ virtual void remove_spent_key(const crypto::key_image& k_image) = 0;
+
+
+ /*********************************************************************
+ * private concrete members
+ *********************************************************************/
+ // private version of pop_block, for undoing if an add_block goes tits up
+ void pop_block();
+
+ // helper function for add_transactions, to add each individual tx
+ void add_transaction(const crypto::hash& blk_hash, const transaction& tx);
+
+ // helper function to remove transaction from blockchain
+ void remove_transaction(const crypto::hash& tx_hash);
+
+
+public:
+
+ // virtual dtor
+ virtual ~BlockchainDB() { };
+
+
+ // open the db at location <filename>, or create it if there isn't one.
+ virtual void open(const std::string& filename) = 0;
+
+ // make sure implementation has a create function as well
+ virtual void create(const std::string& filename) = 0;
+
+ // close and sync the db
+ virtual void close() = 0;
+
+ // sync the db
+ virtual void sync() = 0;
+
+ // reset the db -- USE WITH CARE
+ virtual void reset() = 0;
+
+ // get all files used by this db (if any)
+ virtual std::vector<std::string> get_filenames() const = 0;
+
+
+ // FIXME: these are just for functionality mocking, need to implement
+ // RAII-friendly and multi-read one-write friendly locking mechanism
+ //
+ // acquire db lock
+ virtual bool lock() = 0;
+
+ // release db lock
+ virtual void unlock() = 0;
+
+
+ // adds a block with the given metadata to the top of the blockchain, returns the new height
+ // NOTE: subclass implementations of this (or the functions it calls) need
+ // to handle undoing any partially-added blocks in the event of a failure.
+ virtual uint64_t add_block( const block& blk
+ , const size_t& block_size
+ , const difficulty_type& cumulative_difficulty
+ , const uint64_t& coins_generated
+ , const std::vector<transaction>& txs
+ );
+
+ // return true if a block with hash <h> exists in the blockchain
+ virtual bool block_exists(const crypto::hash& h) const = 0;
+
+ // return block with hash <h>
+ virtual block get_block(const crypto::hash& h) const = 0;
+
+ // return the height of the block with hash <h> on the blockchain,
+ // throw if it doesn't exist
+ virtual uint64_t get_block_height(const crypto::hash& h) const = 0;
+
+ // return header for block with hash <h>
+ virtual block_header get_block_header(const crypto::hash& h) const = 0;
+
+ // return block at height <height>
+ virtual block get_block_from_height(const uint64_t& height) const = 0;
+
+ // return timestamp of block at height <height>
+ virtual uint64_t get_block_timestamp(const uint64_t& height) const = 0;
+
+ // return timestamp of most recent block
+ virtual uint64_t get_top_block_timestamp() const = 0;
+
+ // return block size of block at height <height>
+ virtual size_t get_block_size(const uint64_t& height) const = 0;
+
+ // return cumulative difficulty up to and including block at height <height>
+ virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const = 0;
+
+ // return difficulty of block at height <height>
+ virtual difficulty_type get_block_difficulty(const uint64_t& height) const = 0;
+
+ // return number of coins generated up to and including block at height <height>
+ virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const = 0;
+
+ // return hash of block at height <height>
+ virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const = 0;
+
+ // return vector of blocks in range <h1,h2> of height (inclusively)
+ virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const = 0;
+
+ // return vector of block hashes in range <h1, h2> of height (inclusively)
+ virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const = 0;
+
+ // return the hash of the top block on the chain
+ virtual crypto::hash top_block_hash() const = 0;
+
+ // return the block at the top of the blockchain
+ virtual block get_top_block() const = 0;
+
+ // return the height of the chain
+ virtual uint64_t height() const = 0;
+
+ // pops the top block off the blockchain.
+ // Returns by reference the popped block and its associated transactions
+ //
+ // IMPORTANT:
+ // When a block is popped, the transactions associated with it need to be
+ // removed, as well as outputs and spent key images associated with
+ // those transactions.
+ virtual void pop_block(block& blk, std::vector<transaction>& txs);
+
+
+ // return true if a transaction with hash <h> exists
+ virtual bool tx_exists(const crypto::hash& h) const = 0;
+
+ // return unlock time of tx with hash <h>
+ virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const = 0;
+
+ // return tx with hash <h>
+ // throw if no such tx exists
+ virtual transaction get_tx(const crypto::hash& h) const = 0;
+
+ // returns the total number of transactions in all blocks
+ virtual uint64_t get_tx_count() const = 0;
+
+ // return list of tx with hashes <hlist>.
+ // TODO: decide if a missing hash means return empty list
+ // or just skip that hash
+ virtual std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const = 0;
+
+ // returns height of block that contains transaction with hash <h>
+ virtual uint64_t get_tx_block_height(const crypto::hash& h) const = 0;
+
+ // return global output index of a random output of amount <amount>
+ virtual uint64_t get_random_output(const uint64_t& amount) const = 0;
+
+ // returns the total number of outputs of amount <amount>
+ virtual uint64_t get_num_outputs(const uint64_t& amount) const = 0;
+
+ // return public key for output with global output amount <amount> and index <index>
+ virtual crypto::public_key get_output_key(const uint64_t& amount, const uint64_t& index) const = 0;
+
+ // returns the output indexed by <index> in the transaction with hash <h>
+ virtual tx_out get_output(const crypto::hash& h, const uint64_t& index) const = 0;
+
+ // returns the tx hash associated with an output, referenced by global output index
+ virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const = 0;
+
+ // returns the transaction-local reference for the output with <amount> at <index>
+ // return type is pair of tx hash and index
+ virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const = 0;
+
+ // return a vector of indices corresponding to the global output index for
+ // each output in the transaction with hash <h>
+ virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const = 0;
+ // return a vector of indices corresponding to the amount output index for
+ // each output in the transaction with hash <h>
+ virtual std::vector<uint64_t> get_tx_amount_output_indices(const crypto::hash& h) const = 0;
+
+ // returns true if key image <img> is present in spent key images storage
+ virtual bool has_key_image(const crypto::key_image& img) const = 0;
+
+}; // class BlockchainDB
+
+
+} // namespace cryptonote
+
+#endif // BLOCKCHAIN_DB_H
diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp
index e2b6f2326..564342444 100644
--- a/src/cryptonote_core/blockchain_storage.cpp
+++ b/src/cryptonote_core/blockchain_storage.cpp
@@ -55,19 +55,19 @@ using namespace cryptonote;
DISABLE_VS_WARNINGS(4267)
//------------------------------------------------------------------
-bool blockchain_storage::have_tx(const crypto::hash &id)
+bool blockchain_storage::have_tx(const crypto::hash &id) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
return m_transactions.find(id) != m_transactions.end();
}
//------------------------------------------------------------------
-bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im)
+bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
return m_spent_keys.find(key_im) != m_spent_keys.end();
}
//------------------------------------------------------------------
-transaction *blockchain_storage::get_tx(const crypto::hash &id)
+const transaction *blockchain_storage::get_tx(const crypto::hash &id) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
auto it = m_transactions.find(id);
@@ -77,7 +77,7 @@ transaction *blockchain_storage::get_tx(const crypto::hash &id)
return &it->second.tx;
}
//------------------------------------------------------------------
-uint64_t blockchain_storage::get_current_blockchain_height()
+uint64_t blockchain_storage::get_current_blockchain_height() const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
return m_blocks.size();
@@ -113,24 +113,14 @@ bool blockchain_storage::init(const std::string& config_folder, bool testnet)
else
{
LOG_PRINT_L0("Can't load blockchain storage from file, generating genesis block.");
- block bl = boost::value_initialized<block>();
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- if (testnet)
- {
- generate_genesis_block(bl, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
- }
- else
- {
- generate_genesis_block(bl, config::GENESIS_TX, config::GENESIS_NONCE);
- }
- add_new_block(bl, bvc);
- CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && bvc.m_added_to_main_chain, false, "Failed to add genesis block to blockchain");
+ if (!store_genesis_block(testnet, true))
+ return false;
}
if(!m_blocks.size())
{
LOG_PRINT_L0("Blockchain not loaded, generating genesis block.");
- if (!store_genesis_block(testnet)) {
+ if (!store_genesis_block(testnet, false)) {
return false;
}
} else {
@@ -159,7 +149,7 @@ bool blockchain_storage::init(const std::string& config_folder, bool testnet)
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::store_genesis_block(bool testnet) {
+bool blockchain_storage::store_genesis_block(bool testnet, bool check_added) {
block bl = ::boost::value_initialized<block>();
block_verification_context bvc = boost::value_initialized<block_verification_context>();
@@ -173,7 +163,7 @@ bool blockchain_storage::store_genesis_block(bool testnet) {
}
add_new_block(bl, bvc);
- CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain");
+ CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && (bvc.m_added_to_main_chain || !check_added), false, "Failed to add genesis block to blockchain");
return true;
}
//------------------------------------------------------------------
@@ -231,7 +221,7 @@ bool blockchain_storage::pop_block_from_blockchain()
m_blocks_index.erase(bl_ind);
//pop block from core
m_blocks.pop_back();
- m_tx_pool.on_blockchain_dec(m_blocks.size()-1, get_tail_id());
+ m_tx_pool->on_blockchain_dec(m_blocks.size()-1, get_tail_id());
return true;
}
//------------------------------------------------------------------
@@ -307,7 +297,7 @@ bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& t
if(!is_coinbase(tx))
{
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool r = m_tx_pool.add_tx(tx, tvc, true);
+ bool r = m_tx_pool->add_tx(tx, tvc, true);
CHECK_AND_ASSERT_MES(r, false, "purge_block_data_from_blockchain: failed to add transaction to transaction pool");
}
@@ -333,14 +323,14 @@ bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_
return res;
}
//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_tail_id(uint64_t& height)
+crypto::hash blockchain_storage::get_tail_id(uint64_t& height) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
height = get_current_blockchain_height()-1;
return get_tail_id();
}
//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_tail_id()
+crypto::hash blockchain_storage::get_tail_id() const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
crypto::hash id = null_hash;
@@ -351,7 +341,7 @@ crypto::hash blockchain_storage::get_tail_id()
return id;
}
//------------------------------------------------------------------
-bool blockchain_storage::get_short_chain_history(std::list<crypto::hash>& ids)
+bool blockchain_storage::get_short_chain_history(std::list<crypto::hash>& ids) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
size_t i = 0;
@@ -381,7 +371,7 @@ bool blockchain_storage::get_short_chain_history(std::list<crypto::hash>& ids)
return true;
}
//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height)
+crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(height >= m_blocks.size())
@@ -390,7 +380,7 @@ crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height)
return get_block_hash(m_blocks[height].bl);
}
//------------------------------------------------------------------
-bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) {
+bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) const {
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// try to find block in main chain
@@ -410,20 +400,20 @@ bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) {
return false;
}
//------------------------------------------------------------------
-void blockchain_storage::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) {
+void blockchain_storage::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const {
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- BOOST_FOREACH(blocks_by_id_index::value_type &v, m_blocks_index)
+ BOOST_FOREACH(const blocks_by_id_index::value_type &v, m_blocks_index)
main.push_back(v.first);
- BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_alternative_chains)
+ BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_alternative_chains)
alt.push_back(v.first);
- BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_invalid_blocks)
+ BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_invalid_blocks)
invalid.push_back(v.first);
}
//------------------------------------------------------------------
-difficulty_type blockchain_storage::get_difficulty_for_next_block()
+difficulty_type blockchain_storage::get_difficulty_for_next_block() const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
std::vector<uint64_t> timestamps;
@@ -535,7 +525,7 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list<blocks_ext_b
return true;
}
//------------------------------------------------------------------
-difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei)
+difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const
{
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> commulative_difficulties;
@@ -580,7 +570,7 @@ difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(co
return next_difficulty(timestamps, commulative_difficulties);
}
//------------------------------------------------------------------
-bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height)
+bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) const
{
CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs");
CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
@@ -603,7 +593,7 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins)
+bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) const
{
//validate reward
uint64_t money_in_use = 0;
@@ -630,7 +620,7 @@ bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumul
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count)
+bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size());
@@ -642,7 +632,7 @@ bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vect
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count)
+bool blockchain_storage::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(!m_blocks.size())
@@ -650,12 +640,12 @@ bool blockchain_storage::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t
return get_backward_blocks_sizes(m_blocks.size() -1, sz, count);
}
//------------------------------------------------------------------
-uint64_t blockchain_storage::get_current_comulative_blocksize_limit()
+uint64_t blockchain_storage::get_current_cumulative_blocksize_limit() const
{
return m_current_block_cumul_sz_limit;
}
//------------------------------------------------------------------
-bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce)
+bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) const
{
size_t median_size;
uint64_t already_generated_coins;
@@ -676,16 +666,16 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad
size_t txs_size;
uint64_t fee;
- if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) {
+ if (!m_tx_pool->fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) {
return false;
}
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0;
uint64_t real_fee = 0;
- CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock);
+ CRITICAL_REGION_BEGIN(m_tx_pool->m_transactions_lock);
BOOST_FOREACH(crypto::hash &cur_hash, b.tx_hashes) {
- auto cur_res = m_tx_pool.m_transactions.find(cur_hash);
- if (cur_res == m_tx_pool.m_transactions.end()) {
+ auto cur_res = m_tx_pool->m_transactions.find(cur_hash);
+ if (cur_res == m_tx_pool->m_transactions.end()) {
LOG_ERROR("Creating block template: error: transaction not found");
continue;
}
@@ -774,7 +764,7 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad
return false;
}
//------------------------------------------------------------------
-bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps)
+bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) const
{
if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
@@ -939,7 +929,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs)
+bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(start_offset >= m_blocks.size())
@@ -955,7 +945,7 @@ bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::li
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks)
+bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(start_offset >= m_blocks.size())
@@ -999,7 +989,7 @@ bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request&
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::get_alternative_blocks(std::list<block>& blocks)
+bool blockchain_storage::get_alternative_blocks(std::list<block>& blocks) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1010,21 +1000,21 @@ bool blockchain_storage::get_alternative_blocks(std::list<block>& blocks)
return true;
}
//------------------------------------------------------------------
-size_t blockchain_storage::get_alternative_blocks_count()
+size_t blockchain_storage::get_alternative_blocks_count() const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
return m_alternative_chains.size();
}
//------------------------------------------------------------------
-bool blockchain_storage::add_out_to_get_random_outs(std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i)
+bool blockchain_storage::add_out_to_get_random_outs(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- transactions_container::iterator tx_it = m_transactions.find(amount_outs[i].first);
+ transactions_container::const_iterator tx_it = m_transactions.find(amount_outs[i].first);
CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "internal error: transaction with id " << amount_outs[i].first << ENDL <<
", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index");
CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index="
<< amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first);
- transaction& tx = tx_it->second.tx;
+ const transaction& tx = tx_it->second.tx;
CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type");
//check if transaction is unlocked
@@ -1037,7 +1027,7 @@ bool blockchain_storage::add_out_to_get_random_outs(std::vector<std::pair<crypto
return true;
}
//------------------------------------------------------------------
-size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs)
+size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(!amount_outs.size())
@@ -1046,7 +1036,7 @@ size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair
do
{
--i;
- transactions_container::iterator it = m_transactions.find(amount_outs[i].first);
+ transactions_container::const_iterator it = m_transactions.find(amount_outs[i].first);
CHECK_AND_ASSERT_MES(it != m_transactions.end(), 0, "internal error: failed to find transaction from outputs index with tx_id=" << amount_outs[i].first);
if(it->second.m_keeper_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW <= get_current_blockchain_height() )
return i+1;
@@ -1054,7 +1044,7 @@ size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair
return 0;
}
//------------------------------------------------------------------
-bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)
+bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
BOOST_FOREACH(uint64_t amount, req.amounts)
@@ -1067,7 +1057,7 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO
LOG_PRINT_L1("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist");
continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist
}
- std::vector<std::pair<crypto::hash, size_t> >& amount_outs = it->second;
+ const std::vector<std::pair<crypto::hash, size_t> >& amount_outs = it->second;
//it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split
//lets find upper bound of not fresh outs
size_t up_index_limit = find_end_of_allowed_index(amount_outs);
@@ -1096,7 +1086,7 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset)
+bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1143,7 +1133,7 @@ bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash
return true;
}
//------------------------------------------------------------------
-uint64_t blockchain_storage::block_difficulty(size_t i)
+uint64_t blockchain_storage::block_difficulty(size_t i) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
CHECK_AND_ASSERT_MES(i < m_blocks.size(), false, "wrong block index i = " << i << " at blockchain_storage::block_difficulty()");
@@ -1206,7 +1196,7 @@ void blockchain_storage::print_blockchain_outs(const std::string& file)
}
}
//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp)
+bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(!find_blockchain_supplement(qblock_ids, resp.start_height))
@@ -1219,7 +1209,7 @@ bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count)
+bool blockchain_storage::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(req_start_block > 0) {
@@ -1258,7 +1248,7 @@ bool blockchain_storage::add_block_as_invalid(const block_extended_info& bei, co
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::have_block(const crypto::hash& id)
+bool blockchain_storage::have_block(const crypto::hash& id) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(m_blocks_index.count(id))
@@ -1296,13 +1286,13 @@ bool blockchain_storage::push_transaction_to_global_outs_index(const transaction
return true;
}
//------------------------------------------------------------------
-size_t blockchain_storage::get_total_transactions()
+size_t blockchain_storage::get_total_transactions() const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
return m_transactions.size();
}
//------------------------------------------------------------------
-bool blockchain_storage::get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys)
+bool blockchain_storage::get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
auto it = m_outputs.find(amount);
@@ -1392,7 +1382,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs)
+bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
auto it = m_transactions.find(tx_id);
@@ -1407,7 +1397,7 @@ bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id)
+bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
bool res = check_tx_inputs(tx, &max_used_block_height);
@@ -1417,7 +1407,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_us
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx)
+bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const
{
BOOST_FOREACH(const txin_v& in, tx.vin)
{
@@ -1428,13 +1418,13 @@ bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx)
return false;
}
//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height)
+bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) const
{
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height);
}
//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height)
+bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) const
{
size_t sig_index = 0;
if(pmax_used_block_height)
@@ -1466,7 +1456,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha
return true;
}
//------------------------------------------------------------------
-bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time)
+bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) const
{
if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
{
@@ -1487,15 +1477,15 @@ bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time)
return false;
}
//------------------------------------------------------------------
-bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height)
+bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
struct outputs_visitor
{
std::vector<const crypto::public_key *>& m_results_collector;
- blockchain_storage& m_bch;
- outputs_visitor(std::vector<const crypto::public_key *>& results_collector, blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch)
+ const blockchain_storage& m_bch;
+ outputs_visitor(std::vector<const crypto::public_key *>& results_collector, const blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch)
{}
bool handle_output(const transaction& tx, const tx_out& out)
{
@@ -1537,13 +1527,13 @@ bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::h
return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data());
}
//------------------------------------------------------------------
-uint64_t blockchain_storage::get_adjusted_time()
+uint64_t blockchain_storage::get_adjusted_time() const
{
//TODO: add collecting median time
return time(NULL);
}
//------------------------------------------------------------------
-bool blockchain_storage::check_block_timestamp_main(const block& b)
+bool blockchain_storage::check_block_timestamp_main(const block& b) const
{
if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
{
@@ -1559,7 +1549,7 @@ bool blockchain_storage::check_block_timestamp_main(const block& b)
return check_block_timestamp(std::move(timestamps), b);
}
//------------------------------------------------------------------
-bool blockchain_storage::check_block_timestamp(std::vector<uint64_t> timestamps, const block& b)
+bool blockchain_storage::check_block_timestamp(std::vector<uint64_t> timestamps, const block& b) const
{
if(timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
return true;
@@ -1658,7 +1648,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
transaction tx;
size_t blob_size = 0;
uint64_t fee = 0;
- if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee))
+ if(!m_tx_pool->take_tx(tx_id, tx, blob_size, fee))
{
LOG_PRINT_L1("Block with id: " << id << "has at least one unknown transaction with id: " << tx_id);
purge_block_data_from_blockchain(bl, tx_processed_count);
@@ -1670,7 +1660,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
{
LOG_PRINT_L1("Block with id: " << id << "has at least one transaction (id: " << tx_id << ") with wrong inputs.");
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool add_res = m_tx_pool.add_tx(tx, tvc, true);
+ bool add_res = m_tx_pool->add_tx(tx, tvc, true);
CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
purge_block_data_from_blockchain(bl, tx_processed_count);
add_block_as_invalid(bl, id);
@@ -1683,7 +1673,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
{
LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage");
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool add_res = m_tx_pool.add_tx(tx, tvc, true);
+ bool add_res = m_tx_pool->add_tx(tx, tvc, true);
CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
purge_block_data_from_blockchain(bl, tx_processed_count);
bvc.m_verifivation_failed = true;
@@ -1738,7 +1728,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
/*if(!m_orphanes_reorganize_in_work)
review_orphaned_blocks_with_new_block_id(id, true);*/
- m_tx_pool.on_blockchain_inc(bei.height, id);
+ m_tx_pool->on_blockchain_inc(bei.height, id);
//LOG_PRINT_L0("BLOCK: " << ENDL << "" << dump_obj_as_json(bei.bl));
return true;
}
@@ -1761,7 +1751,7 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont
//copy block here to let modify block.target
block bl = bl_;
crypto::hash id = get_block_hash(bl);
- CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
+ CRITICAL_REGION_LOCAL(*m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
if(have_block(id))
{
@@ -1782,7 +1772,7 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont
return handle_block_to_main_chain(bl, id, bvc);
}
//------------------------------------------------------------------
-void blockchain_storage::check_against_checkpoints(checkpoints& points, bool enforce)
+void blockchain_storage::check_against_checkpoints(const checkpoints& points, bool enforce)
{
const auto& pts = points.get_points();
diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h
index 1bfdf7bd0..4846177ef 100644
--- a/src/cryptonote_core/blockchain_storage.h
+++ b/src/cryptonote_core/blockchain_storage.h
@@ -78,7 +78,7 @@ namespace cryptonote
uint64_t already_generated_coins;
};
- blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false)
+ blockchain_storage(tx_memory_pool* tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false)
{};
bool init() { return init(tools::get_default_data_dir(), true); }
@@ -88,55 +88,55 @@ namespace cryptonote
void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; }
//bool push_new_block();
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs);
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks);
- bool get_alternative_blocks(std::list<block>& blocks);
- size_t get_alternative_blocks_count();
- crypto::hash get_block_id_by_height(uint64_t height);
- bool get_block_by_hash(const crypto::hash &h, block &blk);
- void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid);
+ bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
+ bool get_alternative_blocks(std::list<block>& blocks) const;
+ size_t get_alternative_blocks_count() const;
+ crypto::hash get_block_id_by_height(uint64_t height) const;
+ bool get_block_by_hash(const crypto::hash &h, block &blk) const;
+ void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const;
template<class archive_t>
void serialize(archive_t & ar, const unsigned int version);
- bool have_tx(const crypto::hash &id);
- bool have_tx_keyimges_as_spent(const transaction &tx);
- bool have_tx_keyimg_as_spent(const crypto::key_image &key_im);
- transaction *get_tx(const crypto::hash &id);
+ bool have_tx(const crypto::hash &id) const;
+ bool have_tx_keyimges_as_spent(const transaction &tx) const;
+ bool have_tx_keyimg_as_spent(const crypto::key_image &key_im) const;
+ const transaction *get_tx(const crypto::hash &id) const;
template<class visitor_t>
- bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL);
+ bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL) const;
- uint64_t get_current_blockchain_height();
- crypto::hash get_tail_id();
- crypto::hash get_tail_id(uint64_t& height);
- difficulty_type get_difficulty_for_next_block();
+ uint64_t get_current_blockchain_height() const;
+ crypto::hash get_tail_id() const;
+ crypto::hash get_tail_id(uint64_t& height) const;
+ difficulty_type get_difficulty_for_next_block() const;
bool add_new_block(const block& bl_, block_verification_context& bvc);
bool reset_and_set_genesis_block(const block& b);
- bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce);
- bool have_block(const crypto::hash& id);
- size_t get_total_transactions();
- bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys);
- bool get_short_chain_history(std::list<crypto::hash>& ids);
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp);
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset);
- bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count);
+ bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const;
+ bool have_block(const crypto::hash& id) const;
+ size_t get_total_transactions() const;
+ bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys) const;
+ bool get_short_chain_history(std::list<crypto::hash>& ids) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const;
+ bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
- bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
- bool get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count);
- bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs);
+ bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const;
+ bool get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count) const;
+ bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const;
bool store_blockchain();
- bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height = NULL);
- bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL);
- bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL);
- bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id);
- uint64_t get_current_comulative_blocksize_limit();
- bool is_storing_blockchain(){return m_is_blockchain_storing;}
- uint64_t block_difficulty(size_t i);
+ bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height = NULL) const;
+ bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL) const;
+ bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL) const;
+ bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id) const;
+ uint64_t get_current_cumulative_blocksize_limit() const;
+ bool is_storing_blockchain()const{return m_is_blockchain_storing;}
+ uint64_t block_difficulty(size_t i) const;
template<class t_ids_container, class t_blocks_container, class t_missed_container>
- bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs)
+ bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -156,7 +156,7 @@ namespace cryptonote
}
template<class t_ids_container, class t_tx_container, class t_missed_container>
- bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs)
+ bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -166,7 +166,7 @@ namespace cryptonote
if(it == m_transactions.end())
{
transaction tx;
- if(!m_tx_pool.get_transaction(tx_id, tx))
+ if(!m_tx_pool->get_transaction(tx_id, tx))
missed_txs.push_back(tx_id);
else
txs.push_back(tx);
@@ -180,10 +180,15 @@ namespace cryptonote
void print_blockchain(uint64_t start_index, uint64_t end_index);
void print_blockchain_index();
void print_blockchain_outs(const std::string& file);
- void check_against_checkpoints(checkpoints& points, bool enforce);
+ void check_against_checkpoints(const checkpoints& points, bool enforce);
bool update_checkpoints(const std::string& file_path, bool check_dns);
void set_enforce_dns_checkpoints(bool enforce_checkpoints);
+ block get_block(uint64_t height) const { return m_blocks[height].bl; }
+ size_t get_block_size(uint64_t height) const { return m_blocks[height].block_cumulative_size; }
+ difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return m_blocks[height].cumulative_difficulty; }
+ uint64_t get_block_coins_generated(uint64_t height) const { return m_blocks[height].already_generated_coins; }
+
private:
typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container;
@@ -193,8 +198,8 @@ namespace cryptonote
typedef std::unordered_map<crypto::hash, block> blocks_by_hash;
typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction
- tx_memory_pool& m_tx_pool;
- epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
+ tx_memory_pool* m_tx_pool;
+ mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
// main chain
blocks_container m_blocks; // height -> block_extended_info
@@ -228,26 +233,26 @@ namespace cryptonote
bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc);
bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc);
bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
- difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei);
- bool prevalidate_miner_transaction(const block& b, uint64_t height);
- bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins);
- bool validate_transaction(const block& b, uint64_t height, const transaction& tx);
+ difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const;
+ bool prevalidate_miner_transaction(const block& b, uint64_t height) const;
+ bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) const;
+ bool validate_transaction(const block& b, uint64_t height, const transaction& tx) const;
bool rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height);
bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height);
bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes);
bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id);
- bool get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count);
- bool add_out_to_get_random_outs(std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i);
- bool is_tx_spendtime_unlocked(uint64_t unlock_time);
+ bool get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const;
+ bool add_out_to_get_random_outs(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
+ bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
bool add_block_as_invalid(const block& bl, const crypto::hash& h);
bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h);
- size_t find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs);
- bool check_block_timestamp_main(const block& b);
- bool check_block_timestamp(std::vector<uint64_t> timestamps, const block& b);
- uint64_t get_adjusted_time();
- bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
+ size_t find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs) const;
+ bool check_block_timestamp_main(const block& b) const;
+ bool check_block_timestamp(std::vector<uint64_t> timestamps, const block& b) const;
+ uint64_t get_adjusted_time() const;
+ bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps) const;
bool update_next_comulative_size_limit();
- bool store_genesis_block(bool testnet);
+ bool store_genesis_block(bool testnet, bool check_added = false);
};
@@ -315,7 +320,7 @@ namespace cryptonote
//------------------------------------------------------------------
template<class visitor_t>
- bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height)
+ bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
auto it = m_outputs.find(tx_in_to_key.amount);
@@ -325,7 +330,7 @@ namespace cryptonote
std::vector<uint64_t> absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets);
- std::vector<std::pair<crypto::hash, size_t> >& amount_outs_vec = it->second;
+ const std::vector<std::pair<crypto::hash, size_t> >& amount_outs_vec = it->second;
size_t count = 0;
BOOST_FOREACH(uint64_t i, absolute_offsets)
{
@@ -334,7 +339,7 @@ namespace cryptonote
LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1);
return false;
}
- transactions_container::iterator tx_it = m_transactions.find(amount_outs_vec[i].first);
+ transactions_container::const_iterator tx_it = m_transactions.find(amount_outs_vec[i].first);
CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first));
CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx_it->second.tx.vout.size(), false,
"Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx_it->second.tx.vout.size());
diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp
index 73668ab36..e4223afb5 100644
--- a/src/cryptonote_core/checkpoints.cpp
+++ b/src/cryptonote_core/checkpoints.cpp
@@ -84,6 +84,10 @@ namespace cryptonote
return check_block(height, h, ignored);
}
//---------------------------------------------------------------------------
+ // this basically says if the blockchain is smaller than the first
+ // checkpoint then alternate blocks are allowed. Alternatively, if the
+ // last checkpoint *before* the end of the current chain is also before
+ // the block to be added, then this is fine.
bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const
{
if (0 == block_height)
@@ -99,7 +103,7 @@ namespace cryptonote
return checkpoint_height < block_height;
}
//---------------------------------------------------------------------------
- uint64_t checkpoints::get_max_height()
+ uint64_t checkpoints::get_max_height() const
{
std::map< uint64_t, crypto::hash >::const_iterator highest =
std::max_element( m_points.begin(), m_points.end(),
@@ -108,18 +112,18 @@ namespace cryptonote
return highest->first;
}
//---------------------------------------------------------------------------
- const std::map<uint64_t, crypto::hash>& checkpoints::get_points()
+ const std::map<uint64_t, crypto::hash>& checkpoints::get_points() const
{
return m_points;
}
- bool checkpoints::check_for_conflicts(checkpoints& other)
+ bool checkpoints::check_for_conflicts(const checkpoints& other) const
{
for (auto& pt : other.get_points())
{
if (m_points.count(pt.first))
{
- CHECK_AND_ASSERT_MES(pt.second == m_points[pt.first], false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
+ CHECK_AND_ASSERT_MES(pt.second == m_points.at(pt.first), false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
}
}
return true;
diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h
index a39ce1964..55d765b71 100644
--- a/src/cryptonote_core/checkpoints.h
+++ b/src/cryptonote_core/checkpoints.h
@@ -45,9 +45,9 @@ namespace cryptonote
bool check_block(uint64_t height, const crypto::hash& h) const;
bool check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const;
bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const;
- uint64_t get_max_height();
- const std::map<uint64_t, crypto::hash>& get_points();
- bool check_for_conflicts(checkpoints& other);
+ uint64_t get_max_height() const;
+ const std::map<uint64_t, crypto::hash>& get_points() const;
+ bool check_for_conflicts(const checkpoints& other) const;
private:
std::map<uint64_t, crypto::hash> m_points;
};
diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h
index f50a19f9e..2be76c0de 100644
--- a/src/cryptonote_core/cryptonote_basic.h
+++ b/src/cryptonote_core/cryptonote_basic.h
@@ -50,6 +50,8 @@
#include "misc_language.h"
#include "tx_extra.h"
+#define DB_MEMORY 1
+#define DB_LMDB 2
namespace cryptonote
{
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index b8b5dc008..3a6b84b74 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -51,7 +51,11 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
core::core(i_cryptonote_protocol* pprotocol):
m_mempool(m_blockchain_storage),
+#if BLOCKCHAIN_DB == DB_LMDB
m_blockchain_storage(m_mempool),
+#else
+ m_blockchain_storage(&m_mempool),
+#endif
m_miner(this),
m_miner_address(boost::value_initialized<account_public_address>()),
m_starter_message_showed(false),
@@ -290,9 +294,9 @@ namespace cryptonote
return false;
}
- if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE)
+ if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE)
{
- LOG_PRINT_RED_L1("tx is too large " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
+ LOG_PRINT_RED_L1("tx is too large " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
return false;
}
@@ -577,7 +581,11 @@ namespace cryptonote
m_starter_message_showed = true;
}
+#if BLOCKCHAIN_DB == DB_LMDB
+ m_store_blockchain_interval.do_call(boost::bind(&Blockchain::store_blockchain, &m_blockchain_storage));
+#else
m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage));
+#endif
m_miner.on_idle();
m_mempool.on_idle();
return true;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 748f2b665..bf4d7d49f 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -39,7 +39,11 @@
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
#include "tx_pool.h"
+#if BLOCKCHAIN_DB == DB_LMDB
+#include "blockchain.h"
+#else
#include "blockchain_storage.h"
+#endif
#include "miner.h"
#include "connection_context.h"
#include "cryptonote_core/cryptonote_stat_info.h"
@@ -112,7 +116,11 @@ namespace cryptonote
bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
void pause_mine();
void resume_mine();
+#if BLOCKCHAIN_DB == DB_LMDB
+ Blockchain& get_blockchain_storage(){return m_blockchain_storage;}
+#else
blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;}
+#endif
//debug functions
void print_blockchain(uint64_t start_index, uint64_t end_index);
void print_blockchain_index();
@@ -149,7 +157,11 @@ namespace cryptonote
tx_memory_pool m_mempool;
+#if BLOCKCHAIN_DB == DB_LMDB
+ Blockchain m_blockchain_storage;
+#else
blockchain_storage m_blockchain_storage;
+#endif
i_cryptonote_protocol* m_pprotocol;
epee::critical_section m_incoming_tx_lock;
//m_miner and m_miner_addres are probably temporary here
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 691d16492..03ced2c2e 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -37,7 +37,11 @@
#include "cryptonote_format_utils.h"
#include "cryptonote_boost_serialization.h"
#include "cryptonote_config.h"
+#if BLOCKCHAIN_DB == DB_LMDB
+#include "blockchain.h"
+#else
#include "blockchain_storage.h"
+#endif
#include "common/boost_serialization_helper.h"
#include "common/int-util.h"
#include "misc_language.h"
@@ -52,12 +56,19 @@ namespace cryptonote
{
size_t const TRANSACTION_SIZE_LIMIT = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
}
-
//---------------------------------------------------------------------------------
+#if BLOCKCHAIN_DB == DB_LMDB
+ //---------------------------------------------------------------------------------
+ tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs)
+ {
+
+ }
+#else
tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs)
{
}
+#endif
//---------------------------------------------------------------------------------
bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block)
{
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 9c1c2b1aa..b867a1a7d 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -44,10 +44,13 @@
#include "verification_context.h"
#include "crypto/hash.h"
-
namespace cryptonote
{
+#if BLOCKCHAIN_DB == DB_LMDB
+ class Blockchain;
+#else
class blockchain_storage;
+#endif
/************************************************************************/
/* */
/************************************************************************/
@@ -55,7 +58,11 @@ namespace cryptonote
class tx_memory_pool: boost::noncopyable
{
public:
+#if BLOCKCHAIN_DB == DB_LMDB
+ tx_memory_pool(Blockchain& bchs);
+#else
tx_memory_pool(blockchain_storage& bchs);
+#endif
bool add_tx(const transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block);
bool add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block);
//gets tx and remove it from pool
@@ -127,7 +134,11 @@ namespace cryptonote
//transactions_container m_alternative_transactions;
std::string m_config_folder;
+#if BLOCKCHAIN_DB == DB_LMDB
+ Blockchain& m_blockchain;
+#else
blockchain_storage& m_blockchain;
+#endif
/************************************************************************/
/* */
/************************************************************************/
@@ -170,9 +181,12 @@ namespace cryptonote
uint64_t operator()(const txin_to_scripthash& tx) const {return 0;}
};
+#if BLOCKCHAIN_DB == DB_LMDB
+#else
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
friend class blockchain_storage;
#endif
+#endif
};
}