aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2015-04-08 23:08:23 +0200
committerRiccardo Spagni <ric@spagni.net>2015-04-08 23:08:27 +0200
commitc41d14b2aa3fc883d45299add1cbb8ebbe6c9ed8 (patch)
treef18f76d7e78b50a52c5d796de35e54b72afa3a9a /src/cryptonote_core
parentMerge pull request #257 (diff)
parentMerge BlockchainDB into upstream (diff)
downloadmonero-c41d14b2aa3fc883d45299add1cbb8ebbe6c9ed8.tar.xz
Merge pull request #256
7d8acd1 explicitly disable AES on ARM (Riccardo Spagni) 4222b37 add arm6 target and flags (Riccardo Spagni) 5675325 new makefile targets for static builds (Riccardo Spagni) 9519526 Only compile BerkeleyDB as an option in non-static (Thomas Winget) 7b14d4a Steps toward multiple dbs available -- working (Thomas Winget) dbdcf11 blockchain_converter: Add support for resume from last block (warptangent) 4880803 blockchain_import: lengthen string for line clear (warptangent) 7476d2e blockchain_export: show progress during export (warptangent) 4bedd68 Update Blockchain::get_db() to return reference instead of pointer (warptangent) 0386e99 Add README for blockchain converter, importer, and exporter utilities (warptangent) ead7fad BerkeleyDB implementation of BlockchainDB seems to be working! (Thomas Winget) ffadb65 blockchain_export: Add compile-time support for BlockchainDB (warptangent) f6cbfb6 Add blockchain_export utility (warptangent) 275cbd4 Add support for database open with flags (warptangent) cb862cb Add mdb_flags variable to LMDB database open (warptangent) 260cc56 Add blockchain_import utility (warptangent) ca75b47 Blockchain: add get_db() accessor, needed for blockchain_import (warptangent) a3dd9d1 blockchain_converter: Add support for batch transactions (warptangent) acb5d29 Update and relocate comment that applies class wide (warptangent) bd9da6d Merges #33 (Thomas Winget) 6e9e8ab Move db_drivers/ to external/ (Thomas Winget) 56a1bf5 fixed BUILD_64 option (Riccardo Spagni) c97a685 fixed arch_width option (Riccardo Spagni) 3a3c07c fixed msys2 / mingw folders based on architecture, added license to unbound CMakeList as that is not part of standard Unbound (Riccardo Spagni) 3b3da86 moved 32-bit/64-bit detection into main CMakeLists (Riccardo Spagni) a0cbf73 removed findlmdb.cmake, no longer required (Riccardo Spagni) 0e8bbdb 32-bit LMDB, removed check for in-system LMDB as it is consensus-critical (Riccardo Spagni) 11e8157 Revert "Moved db_drivers/ into external/ for consistency" (Thomas Winget) daf2a8f Revert "Build fixed, goofed up some CMake" (Thomas Winget) e146027 BlockchainBDB passes unit tests (Thomas Winget) 43477b7 BerkeleyDB Blockchain building, not working yet (Thomas Winget) cade0da CMake wiring, minor cleanup, minor test addition (Thomas Winget) 1bc8939 BerkeleyDB BlockchainDB impl copy/paste/modify (Thomas Winget) 5112dc3 Try to not pollute cryptonote namespace (Thomas Winget) edef0bb Initial commit of BDB BlockchainDB implementation (Thomas Winget) eee3ee7 BlockchainDB implementations have names now (Thomas Winget) 5d2a2b7 Fixed includes in BlockchainDB unit tests (Thomas Winget) 8b82f3c Build fixed, goofed up some CMake (Thomas Winget) b213356 Moved db_drivers/ into external/ for consistency (Thomas Winget) 5eab480 Moved BlockchainDB into its own src/ subfolder (Thomas Winget) 0ad0784 Changed log level of debug message -- too spammy (Thomas Winget) 6485dac BlockchainLMDB: Add profiling to tx_exists() (warptangent) 83fb6d8 BlockchainLMDB: Add batch transaction support to tx_exists() (warptangent) 8529c0e BlockchainDB, BlockchainLMDB: Add profiling for DB commits (warptangent) 7a66b8b BlockchainDB: Add virtual function declarations for batch transactions (warptangent) b7a2d84 BlockchainLMDB: Add check for open database to two functions (warptangent) 58ecc58 BlockchainLMDB: Add support for batch transactions (warptangent) 8909d7d Improve block and tx processing efficiency by less repeat hashing (warptangent) 3676ac5 Add profiling to block and tx processing (warptangent) ce71abd Move LMDB storage to subfolder (warptangent) 42f8fe5 Fix formatting (warptangent) aa82f78 Fix log statement (warptangent) 26873db Remove unused variable (warptangent) 4b90fd3 Add log statement (warptangent) 2531aa3 Add and extend log statements (warptangent) 59305d3 Blockchain: match original function declaration from blockchain_storage (warptangent) cd972bd Update year and formatting in license (warptangent) b88ab64 Fix Blockchain::get_tail_id() to set parameter to last block number instead of height (warptangent) 963bc09 Revert "Bounds error, should fix #27" (warptangent) 6f1c4b4 Bounds error, should fix #27 (Thomas Winget) 84fe5fb Add compile-time support for both db implementations: in-memory and LMDB (warptangent) 8bd1983 Blockchain: reflect log updates from blockchain_storage (warptangent) 7f9b070 Blockchain: reflect log and assert updates from blockchain_storage (warptangent) 70342ec Blockchain: reflect log level of blockchain_storage (warptangent) c8d27fb Blockchain: reflect assert behavior of blockchain_storage for get_tx_outputs_gindexs() (warptangent) d00ee78 Update recently added log statement to fix possible null dereference (warptangent) acd4c36 Should fix std::min issues related to size_t (Thomas Winget) 3488359 Add in-source lmdb to build process (Thomas Winget) 4e45fae Initial import of lmdb source (Thomas Winget) 800d9b9 Remove code previously made unused and marked unused (warptangent) 0840c2f Fix height assertion in Blockchain::handle_alternative_block() (warptangent) 63051be Fix comparison between main and alternate chain's cumulative difficulty. (warptangent) 909ea81 Remove a have_block() check so alternate block can be processed (warptangent) 4d0a94b Complete implementation of transaction removal (warptangent) 1701c26 Use block index when obtaining block's difficulty for log statement (warptangent) 4eba21f Fix transfers to support mixins (warptangent) d045dfa Fix transfers (without mixins) (warptangent) 429a740 throw inline functions need to keep exception type (Thomas Winget) 14555ee Fixes segfault in Blockchain::handle_alternative_block (Thomas Winget) 5086ca1 add BlockchainDB tests to new cmake (Thomas Winget) c5c100c Obtain tx hash and tx output index from amount and output offset (warptangent) ad8200a db_lmdb: fix global index calculation off by 1 (moneromooo-monero) 57b80c5 db_lmdb: remove redundant checks (moneromooo-monero) 1362846 blockchain_converter: add --testnet for converting testnet blockchain (moneromooo-monero) c50cd95 Fixes a bug with getting output metadata from BlockchainDB (Thomas Winget) c3fa07b update comments to reflect changed code (Thomas Winget) 59d2b0e db_lmdb: do not give the group database write permissions (moneromooo-monero) 4c2a452 db_lmdb: catch attempt to remove block from an empty blockchain (moneromooo-monero) 3a3459d db_lmdb: factor all the log+throw code paths (moneromooo-monero) 3fcb8da db_lmdb: factor the MDB_val setup code (moneromooo-monero) 609cf7f blockchain_converter: a bit more user friendly output (moneromooo-monero) 2b9f737 blockchain_converter: only call data path function once (moneromooo-monero) 1860658 blockchain: do not append "testnet" to the data directory (moneromooo-monero) 1c578ad db_lmdb: remove block timestamp too when removing a block (moneromooo-monero) c93a186 db_lmdb: do not cast const away (moneromooo-monero) 198368b blockchain: fix wallet syncing from scratch (moneromooo-monero) 1d23db2 db_lmdb: do not keep a dangling pointer to stack objects (moneromooo-monero) 29b5876 db_lmdb: make cursor internal members private (moneromooo-monero) a3157d7 blockchain_storage: refactor genesis block creation (moneromooo-monero) 8e41b1e blockchain_storage: add consts where appropriate (moneromooo-monero) 98bdadc blockchain_converter: delete blockchain on succesful exit (moneromooo-monero) 256162f checkpoints: add consts where appropriate (moneromooo-monero) b7270ab blockchain: add consts where appropriate (moneromooo-monero) 23f3cb4 blockchain_db: add consts where appropriate (moneromooo-monero) 10fd6ca blockchain_db: factor some exception code (moneromooo-monero) 11129b9 blockchain_converter: use the actual blockchain location (moneromooo-monero) 0886183 build: add liblmdb to the cmake autodetection system (moneromooo-monero) 215e63b extraneous semicolon in Blockchain::complete_timestamps_vector (Thomas Winget) 8e1b7e2 raised maximum mapsize for lmdb to ~16GB (Thomas Winget) 6c8b8ac more blockchain height-related fixes, syncing other nodes code this time (Thomas Winget) 9455e0c ~ didn't work, need hard path. debug print. (Thomas Winget) 4af0918 very, VERY primitive blockchain converter (Thomas Winget) 26a7db3 add new checkpointing behavior to Blockchain class (Thomas Winget) 767aac2 Remove unused dependency (Thomas Winget) 006e106 Store output pubkeys separately, bug fixes (Thomas Winget) ab7951d Minor bugfixes, redundancy removal (Thomas Winget) 71b18d7 moar bug fixes, removed debug prints (Thomas Winget) 0915913 BlockchainLMDB seems to be working*! (Thomas Winget) 1a546e3 some bug fixes, but still needs work (Thomas Winget) 006afe2 Minor bug fixes and debug prints (Thomas Winget) 90f402e minor fixes to Blockchain.cpp (Thomas Winget) 74a1a89 Integrate BlockchainDB into cryptonote_core (Thomas Winget) d8c570b All LMDB BlockchainDB implemented, not tested (Thomas Winget) e47e343 LMDB blockchain: remove outputs and spent keys (Thomas Winget) a0af217 Adding block data to LMDB BlockchainDB coded (Thomas Winget) db00ce0 Parts of LMDB impl of BlockchainDB done and working (Thomas Winget) 1240cf8 BlockchainDB unit tests, lmdb linker flag (Thomas Winget) b98b964 Initial commit of lmdb BlockchainDB impl (Thomas Winget) bc44bc1 Initial commit of BlockchainDB tests, other misc (Thomas Winget) 90d6f8b Adding libglim as an external library (Thomas Winget) 07733f9 update new blockchain to build with new changes (Thomas Winget) 1ffbeb2 stupid past me, fixing typos and shit... (Thomas Winget) 67515b8 missing typedef (Thomas Winget) aba548c import of BlockchainDB files (Thomas Winget)
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/CMakeLists.txt3
-rw-r--r--src/cryptonote_core/blockchain.cpp2342
-rw-r--r--src/cryptonote_core/blockchain.h229
-rw-r--r--src/cryptonote_core/blockchain_storage.cpp150
-rw-r--r--src/cryptonote_core/blockchain_storage.h121
-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.cpp60
-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
12 files changed, 2817 insertions, 149 deletions
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 9eed11874..e144a93a2 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -29,6 +29,7 @@
set(cryptonote_core_sources
account.cpp
blockchain_storage.cpp
+ blockchain.cpp
checkpoints.cpp
checkpoints_create.cpp
cryptonote_basic_impl.cpp
@@ -45,6 +46,7 @@ set(cryptonote_core_private_headers
account_boost_serialization.h
blockchain_storage.h
blockchain_storage_boost_serialization.h
+ blockchain.h
checkpoints.h
checkpoints_create.h
connection_context.h
@@ -71,6 +73,7 @@ target_link_libraries(cryptonote_core
common
crypto
otshell_utils
+ blockchain_db
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
new file mode 100644
index 000000000..0261a5614
--- /dev/null
+++ b/src/cryptonote_core/blockchain.cpp
@@ -0,0 +1,2342 @@
+// 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 "blockchain_db/blockchain_db.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)
+
+//------------------------------------------------------------------
+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_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(BlockchainDB* db, const bool testnet)
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ if (db == nullptr)
+ {
+ LOG_ERROR("Attempted to init Blockchain with null DB");
+ return false;
+ }
+ if (!db->is_open())
+ {
+ LOG_ERROR("Attempted to init Blockchain with unopened DB");
+ return false;
+ }
+
+ m_db = db;
+
+ // 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() - 1;
+ 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(const 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..bc13901d2
--- /dev/null
+++ b/src/cryptonote_core/blockchain.h
@@ -0,0 +1,229 @@
+// 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/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(BlockchainDB* db, const 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(const checkpoints& points, bool enforce);
+ void set_enforce_dns_checkpoints(bool enforce);
+ bool update_checkpoints(const std::string& file_path, bool check_dns);
+
+ BlockchainDB& get_db()
+ {
+ return *m_db;
+ }
+
+ 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;
+
+
+ checkpoints m_checkpoints;
+ std::atomic<bool> m_is_in_checkpoint_zone;
+ std::atomic<bool> m_is_blockchain_storing;
+ 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_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp
index d59fc7a58..ee4263687 100644
--- a/src/cryptonote_core/blockchain_storage.cpp
+++ b/src/cryptonote_core/blockchain_storage.cpp
@@ -58,19 +58,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);
@@ -80,7 +80,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();
@@ -117,24 +117,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 {
@@ -163,7 +153,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>();
@@ -177,7 +167,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;
}
//------------------------------------------------------------------
@@ -235,7 +225,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;
}
//------------------------------------------------------------------
@@ -311,7 +301,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");
}
@@ -337,14 +327,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;
@@ -355,7 +345,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;
@@ -385,7 +375,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())
@@ -394,7 +384,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
@@ -414,20 +404,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;
@@ -539,7 +529,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;
@@ -584,7 +574,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");
@@ -607,7 +597,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;
@@ -634,7 +624,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());
@@ -646,7 +636,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())
@@ -654,12 +644,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;
@@ -680,16 +670,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;
}
@@ -778,7 +768,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)
@@ -943,7 +933,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())
@@ -959,7 +949,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())
@@ -1003,7 +993,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);
@@ -1014,21 +1004,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
@@ -1041,7 +1031,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())
@@ -1050,7 +1040,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;
@@ -1058,7 +1048,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)
@@ -1071,7 +1061,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);
@@ -1106,7 +1096,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);
@@ -1153,7 +1143,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()");
@@ -1163,7 +1153,7 @@ uint64_t blockchain_storage::block_difficulty(size_t i)
return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty;
}
//------------------------------------------------------------------
-double blockchain_storage::get_avg_block_size( size_t count)
+double blockchain_storage::get_avg_block_size( size_t count) const
{
if (count > get_current_blockchain_height()) return 500;
@@ -1241,7 +1231,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))
@@ -1254,7 +1244,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) {
@@ -1293,7 +1283,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))
@@ -1331,13 +1321,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);
@@ -1428,7 +1418,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);
@@ -1443,7 +1433,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);
@@ -1453,7 +1443,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)
{
@@ -1464,13 +1454,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)
@@ -1502,7 +1492,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)
{
@@ -1523,15 +1513,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)
{
@@ -1573,13 +1563,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)
{
@@ -1595,7 +1585,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;
@@ -1696,7 +1686,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);
@@ -1708,7 +1698,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);
@@ -1721,7 +1711,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;
@@ -1785,7 +1775,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;
}
@@ -1808,7 +1798,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))
{
@@ -1829,7 +1819,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 505ed4574..50a62a428 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,56 +88,56 @@ 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);
- double get_avg_block_size( size_t count);
+ 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;
+ double get_avg_block_size( size_t count) 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);
@@ -157,7 +157,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);
@@ -167,7 +167,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);
@@ -181,10 +181,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;
@@ -194,8 +199,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
@@ -230,26 +235,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, size_t blob_size);
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);
};
@@ -317,7 +322,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);
@@ -327,7 +332,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)
{
@@ -336,7 +341,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 9be5eca9b..4754be43c 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -44,6 +44,11 @@ using namespace epee;
#include <csignal>
#include "daemon/command_line_args.h"
#include "cryptonote_core/checkpoints_create.h"
+#include "blockchain_db/blockchain_db.h"
+#include "blockchain_db/lmdb/db_lmdb.h"
+#ifndef STATICLIB
+#include "blockchain_db/berkeleydb/db_bdb.h"
+#endif
DISABLE_VS_WARNINGS(4355)
@@ -53,7 +58,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),
@@ -195,7 +204,50 @@ namespace cryptonote
r = m_mempool.init(m_config_folder);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
+#if BLOCKCHAIN_DB == DB_LMDB
+ std::string db_type = command_line::get_arg(vm, daemon_args::arg_db_type);
+
+ BlockchainDB* db = nullptr;
+ if (db_type == "lmdb")
+ {
+ db = new BlockchainLMDB();
+ }
+ else if (db_type == "berkeley")
+ {
+#ifndef STATICLIB
+ db = new BlockchainBDB();
+#else
+ LOG_ERROR("BlockchainBDB not supported on STATIC builds");
+ return false;
+#endif
+ }
+ else
+ {
+ LOG_ERROR("Attempted to use non-existant database type");
+ return false;
+ }
+
+ boost::filesystem::path folder(m_config_folder);
+
+ folder /= db->get_db_name();
+
+ LOG_PRINT_L0("Loading blockchain from folder " << folder.c_str() << " ...");
+
+ const std::string filename = folder.string();
+ try
+ {
+ db->open(filename);
+ }
+ catch (const DB_ERROR& e)
+ {
+ LOG_PRINT_L0("Error opening database: " << e.what());
+ return false;
+ }
+
+ r = m_blockchain_storage.init(db, m_testnet);
+#else
r = m_blockchain_storage.init(m_config_folder, m_testnet);
+#endif
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
// load json & DNS checkpoints, and verify them
@@ -363,9 +415,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;
}
@@ -650,7 +702,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 bb53ecb81..973f01272 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"
@@ -118,7 +122,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();
@@ -159,7 +167,11 @@ namespace cryptonote
uint64_t m_test_drop_download_height = 0;
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
};
}