diff options
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 52 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 14 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 139 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 40 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.cpp | 4 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.h | 1 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.h | 1 |
7 files changed, 207 insertions, 44 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 2420adc53..d1f5f0dcd 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -118,6 +118,8 @@ static const struct { { 3, 800500, 0, 1472415034 }, { 4, 801219, 0, 1472415035 }, { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 + + { 6, 971400, 0, 1501709789 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; @@ -279,7 +281,8 @@ uint64_t Blockchain::get_current_blockchain_height() const bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::test_options *test_options) { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + CRITICAL_REGION_LOCAL(m_tx_pool); + CRITICAL_REGION_LOCAL1(m_blockchain_lock); bool fakechain = test_options != NULL; @@ -2254,8 +2257,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh TIME_MEASURE_FINISH(a); if(m_show_time_stats) { - size_t mixin = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() - 1 : 0; - MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << mixin << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a); + size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0; + MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a); } return true; } @@ -2266,8 +2269,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh TIME_MEASURE_FINISH(a); if(m_show_time_stats) { - size_t mixin = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() - 1 : 0; - MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << mixin << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx)); + size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0; + MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx)); } if (!res) return false; @@ -2459,13 +2462,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { if (n_unmixable == 0) { - MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low mixin (" << mixin << "), and no unmixable inputs"); + MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and no unmixable inputs"); tvc.m_low_mixin = true; return false; } if (n_mixable > 1) { - MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low mixin (" << mixin << "), and more than one mixable input with unmixable inputs"); + MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and more than one mixable input with unmixable inputs"); tvc.m_low_mixin = true; return false; } @@ -3097,6 +3100,8 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& CRITICAL_REGION_LOCAL(m_blockchain_lock); TIME_MEASURE_START(t1); + static bool seen_future_version = false; + m_db->block_txn_start(true); if(bl.prev_id != get_tail_id()) { @@ -3106,6 +3111,18 @@ leave: return false; } + // warn users if they're running an old version + if (!seen_future_version && bl.major_version > m_hardfork->get_ideal_version()) + { + seen_future_version = true; + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "**********************************************************************"); + MCLOG_RED(level, "global", "A block was seen on the network with a version higher than the last"); + MCLOG_RED(level, "global", "known one. This may be an old version of the daemon, and a software"); + MCLOG_RED(level, "global", "update may be required to sync further. Try running: update check"); + MCLOG_RED(level, "global", "**********************************************************************"); + } + // this is a cheap test if (!m_hardfork->check(bl)) { @@ -3541,19 +3558,17 @@ void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints) } //------------------------------------------------------------------ -void Blockchain::block_longhash_worker(const uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const +void Blockchain::block_longhash_worker(uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const { TIME_MEASURE_START(t); slow_hash_allocate_state(); - //FIXME: height should be changing here, as get_block_longhash expects - // the height of the block passed to it for (const auto & block : blocks) { if (m_cancel) - return; + break; crypto::hash id = get_block_hash(block); - crypto::hash pow = get_block_longhash(block, height); + crypto::hash pow = get_block_longhash(block, height++); map.emplace(id, pow); } @@ -3745,9 +3760,11 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e if (!blocks_exist) { m_blocks_longhash_table.clear(); + uint64_t thread_height = height; for (uint64_t i = 0; i < threads; i++) { - thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i])))); + thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])))); + thread_height += blocks[i].size(); } for (size_t j = 0; j < thread_list.size(); j++) @@ -4154,6 +4171,15 @@ void Blockchain::load_compiled_in_block_hashes() } #endif +bool Blockchain::is_within_compiled_block_hash_area(uint64_t height) const +{ +#if defined(PER_BLOCK_CHECKPOINT) + return height < m_blocks_hash_check.size(); +#else + return false; +#endif +} + void Blockchain::lock() { m_blockchain_lock.lock(); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 4f2e4f0d3..aa61dc034 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -746,6 +746,15 @@ namespace cryptonote uint8_t get_ideal_hard_fork_version(uint64_t height) const { return m_hardfork->get_ideal_version(height); } /** + * @brief returns the actual hardfork version for a given block height + * + * @param height the height for which to check version info + * + * @return the version + */ + uint8_t get_hard_fork_version(uint64_t height) const { return m_hardfork->get(height); } + + /** * @brief get information about hardfork voting for a version * * @param version the version in question @@ -846,7 +855,7 @@ namespace cryptonote * @param blocks the blocks to be hashed * @param map return-by-reference the hashes for each block */ - void block_longhash_worker(const uint64_t height, const std::vector<block> &blocks, + void block_longhash_worker(uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const; /** @@ -865,6 +874,9 @@ namespace cryptonote cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const; bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false) const; + bool is_within_compiled_block_hash_area(uint64_t height) const; + bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); } + void lock(); void unlock(); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 4cfa52441..13e5badd1 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -37,6 +37,7 @@ using namespace epee; #include "common/util.h" #include "common/updates.h" #include "common/download.h" +#include "common/task_region.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" @@ -76,6 +77,8 @@ namespace cryptonote m_checkpoints_path(""), m_last_dns_checkpoints_update(0), m_last_json_checkpoints_update(0), + m_disable_dns_checkpoints(false), + m_threadpool(tools::thread_group::optimal()), m_update_download(0) { m_checkpoints_updating.clear(); @@ -106,7 +109,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::update_checkpoints() { - if (m_testnet || m_fakechain) return true; + if (m_testnet || m_fakechain || m_disable_dns_checkpoints) return true; if (m_checkpoints_updating.test_and_set()) return true; @@ -414,6 +417,8 @@ namespace cryptonote if (block_sync_size == 0) block_sync_size = BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; + MGINFO("Loading checkpoints"); + // load json & DNS checkpoints, and verify them // with respect to what blocks we already have CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); @@ -483,11 +488,9 @@ namespace cryptonote return false; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay) { tvc = boost::value_initialized<tx_verification_context>(); - //want to process all transactions sequentially - CRITICAL_REGION_LOCAL(m_incoming_tx_lock); if(tx_blob.size() > get_max_tx_size()) { @@ -497,9 +500,8 @@ namespace cryptonote return false; } - crypto::hash tx_hash = null_hash; - crypto::hash tx_prefixt_hash = null_hash; - transaction tx; + tx_hash = null_hash; + tx_prefixt_hash = null_hash; if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { @@ -509,15 +511,18 @@ namespace cryptonote } //std::cout << "!"<< tx.vin.size() << std::endl; + bad_semantics_txes_lock.lock(); for (int idx = 0; idx < 2; ++idx) { if (bad_semantics_txes[idx].find(tx_hash) != bad_semantics_txes[idx].end()) { + bad_semantics_txes_lock.unlock(); LOG_PRINT_L1("Transaction already seen with bad semantics, rejected"); tvc.m_verifivation_failed = true; return false; } } + bad_semantics_txes_lock.unlock(); uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); const size_t max_tx_version = version == 1 ? 1 : 2; @@ -528,18 +533,11 @@ namespace cryptonote return false; } - if(m_mempool.have_tx(tx_hash)) - { - LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); - return true; - } - - if(m_blockchain_storage.have_tx(tx_hash)) - { - LOG_PRINT_L2("tx " << tx_hash << " already have transaction in blockchain"); - return true; - } - + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay) + { if(!check_tx_syntax(tx)) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"); @@ -564,27 +562,92 @@ namespace cryptonote rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key); } - if(!check_tx_semantic(tx, keeped_by_block)) + if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area()) + { + MTRACE("Skipping semantics check for tx kept by block in embedded hash area"); + } + else if(!check_tx_semantic(tx, keeped_by_block)) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); tvc.m_verifivation_failed = true; + bad_semantics_txes_lock.lock(); bad_semantics_txes[0].insert(tx_hash); if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE) { std::swap(bad_semantics_txes[0], bad_semantics_txes[1]); bad_semantics_txes[0].clear(); } + bad_semantics_txes_lock.unlock(); return false; } - bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block, relayed, do_not_relay); - if(tvc.m_verifivation_failed) - {MERROR_VER("Transaction verification failed: " << tx_hash);} - else if(tvc.m_verifivation_impossible) - {MERROR_VER("Transaction verification impossible: " << tx_hash);} + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_txs(const std::list<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + { + struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; bool in_txpool; bool in_blockchain; }; + std::vector<result> results(tx_blobs.size()); + + tvc.resize(tx_blobs.size()); + tools::task_region(m_threadpool, [&] (tools::task_region_handle& region) { + std::list<blobdata>::const_iterator it = tx_blobs.begin(); + for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { + region.run([&, i, it] { + results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay); + }); + } + }); + tools::task_region(m_threadpool, [&] (tools::task_region_handle& region) { + std::list<blobdata>::const_iterator it = tx_blobs.begin(); + for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { + if (!results[i].res) + continue; + if(m_mempool.have_tx(results[i].hash)) + { + LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool"); + } + else if(m_blockchain_storage.have_tx(results[i].hash)) + { + LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain"); + } + else + { + region.run([&, i, it] { + results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay); + }); + } + } + }); + + bool ok = true; + std::list<blobdata>::const_iterator it = tx_blobs.begin(); + for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { + if (!results[i].res) + { + ok = false; + continue; + } - if(tvc.m_added_to_pool) - MDEBUG("tx added: " << tx_hash); + ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, it->size(), tvc[i], keeped_by_block, relayed, do_not_relay); + if(tvc[i].m_verifivation_failed) + {MERROR_VER("Transaction verification failed: " << results[i].hash);} + else if(tvc[i].m_verifivation_impossible) + {MERROR_VER("Transaction verification impossible: " << results[i].hash);} + + if(tvc[i].m_added_to_pool) + MDEBUG("tx added: " << results[i].hash); + } + return ok; + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + { + std::list<cryptonote::blobdata> tx_blobs; + tx_blobs.push_back(tx_blob); + std::vector<tx_verification_context> tvcv(1); + bool r = handle_incoming_txs(tx_blobs, tvcv, keeped_by_block, relayed, do_not_relay); + tvc = tvcv[0]; return r; } //----------------------------------------------------------------------------------------------- @@ -660,6 +723,12 @@ namespace cryptonote return false; } + if (!check_tx_inputs_ring_members_diff(tx)) + { + MERROR_VER("tx uses duplicate ring members"); + return false; + } + if (!check_tx_inputs_keyimages_domain(tx)) { MERROR_VER("tx uses key image not in the valid domain"); @@ -752,6 +821,22 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_tx_inputs_ring_members_diff(const transaction& tx) const + { + const uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); + if (version >= 6) + { + for(const auto& in: tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + for (size_t n = 1; n < tokey_in.key_offsets.size(); ++n) + if (tokey_in.key_offsets[n] == 0) + return false; + } + } + return true; + } + //----------------------------------------------------------------------------------------------- bool core::check_tx_inputs_keyimages_domain(const transaction& tx) const { std::unordered_set<crypto::key_image> ki; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index e5fbf7f91..171c3cb98 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -40,6 +40,7 @@ #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "storages/portable_storage_template_helper.h" #include "common/download.h" +#include "common/thread_group.h" #include "tx_pool.h" #include "blockchain.h" #include "cryptonote_basic/miner.h" @@ -115,6 +116,22 @@ namespace cryptonote bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); /** + * @brief handles a list of incoming transactions + * + * Parses incoming transactions and, if nothing is obviously wrong, + * passes them along to the transaction pool + * + * @param tx_blobs the txs to handle + * @param tvc metadata about the transactions' validity + * @param keeped_by_block if the transactions have been in a block + * @param relayed whether or not the transactions were relayed to us + * @param do_not_relay whether to prevent the transactions from being relayed + * + * @return true if the transactions made it to the transaction pool, otherwise false + */ + bool handle_incoming_txs(const std::list<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + + /** * @brief handles an incoming block * * periodic update to checkpoints is triggered here @@ -390,6 +407,13 @@ namespace cryptonote void set_enforce_dns_checkpoints(bool enforce_dns); /** + * @brief set whether or not to enable or disable DNS checkpoints + * + * @param disble whether to disable DNS checkpoints + */ + void disable_dns_checkpoints(bool disable = true) { m_disable_dns_checkpoints = disable; } + + /** * @copydoc tx_memory_pool::have_tx * * @note see tx_memory_pool::have_tx @@ -753,6 +777,9 @@ namespace cryptonote */ bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const; + bool handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay); + /** * @copydoc miner::on_block_chain_update * @@ -781,6 +808,15 @@ namespace cryptonote bool check_tx_inputs_keyimages_diff(const transaction& tx) const; /** + * @brief verify that each ring uses distinct members + * + * @param tx the transaction to check + * + * @return false if any ring uses duplicate members, true otherwise + */ + bool check_tx_inputs_ring_members_diff(const transaction& tx) const; + + /** * @brief verify that each input key image in a transaction is in * the valid domain * @@ -853,12 +889,16 @@ namespace cryptonote time_t m_last_json_checkpoints_update; //!< time when json checkpoints were last updated std::atomic_flag m_checkpoints_updating; //!< set if checkpoints are currently updating to avoid multiple threads attempting to update at once + bool m_disable_dns_checkpoints; size_t block_sync_size; time_t start_time; std::unordered_set<crypto::hash> bad_semantics_txes[2]; + boost::mutex bad_semantics_txes_lock; + + tools::thread_group m_threadpool; enum { UPDATES_DISABLED, diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 26d5fb767..94f069827 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -265,7 +265,7 @@ namespace cryptonote // "Shuffle" outs std::vector<tx_destination_entry> shuffled_dsts(destinations); - std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); + std::random_shuffle(shuffled_dsts.begin(), shuffled_dsts.end(), [](unsigned int i) { return crypto::rand<unsigned int>() % i; }); uint64_t summary_outs_money = 0; //fill outputs @@ -371,7 +371,7 @@ namespace cryptonote // enforce same mixin for all outputs for (size_t i = 1; i < sources.size(); ++i) { if (n_total_outs != sources[i].outputs.size()) { - LOG_ERROR("Non-simple ringct transaction has varying mixin"); + LOG_ERROR("Non-simple ringct transaction has varying ring size"); return false; } } diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 933070e1e..7aa7c280d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -32,6 +32,7 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include <boost/serialization/vector.hpp> #include <boost/serialization/utility.hpp> +#include "ringct/rctOps.h" namespace cryptonote { diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 1858ccdd8..47a41d070 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -493,7 +493,6 @@ private: */ std::unordered_set<crypto::hash> m_timed_out_transactions; - std::string m_config_folder; //!< the folder to save state to Blockchain& m_blockchain; //!< reference to the Blockchain object }; } |