diff options
Diffstat (limited to 'src')
45 files changed, 1045 insertions, 356 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da6d76d97..8a21763c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -133,7 +133,7 @@ if(NOT IOS) add_subdirectory(blockchain_utilities) endif() -if(CMAKE_BUILD_TYPE STREQUAL Debug) +if(BUILD_DEBUG_UTILITIES) add_subdirectory(debug_utilities) endif() diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index b6b8c6c3e..bb4de3ce6 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -129,6 +129,15 @@ struct tx_data_t }; #pragma pack(pop) +struct alt_block_data_t +{ + uint64_t height; + uint64_t cumulative_weight; + uint64_t cumulative_difficulty_low; + uint64_t cumulative_difficulty_high; + uint64_t already_generated_coins; +}; + /** * @brief a struct containing txpool per transaction metadata */ @@ -1543,8 +1552,45 @@ public: * * @param: sz the block size */ - virtual void add_max_block_size(uint64_t sz) = 0; + + /** + * @brief add a new alternative block + * + * @param: blkid the block hash + * @param: data: the metadata for the block + * @param: blob: the block's blob + */ + virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) = 0; + + /** + * @brief get an alternative block by hash + * + * @param: blkid the block hash + * @param: data: the metadata for the block + * @param: blob: the block's blob + * + * @return true if the block was found in the alternative blocks list, false otherwise + */ + virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) = 0; + + /** + * @brief remove an alternative block + * + * @param: blkid the block hash + */ + virtual void remove_alt_block(const crypto::hash &blkid) = 0; + + /** + * @brief get the number of alternative blocks stored + */ + virtual uint64_t get_alt_block_count() = 0; + + /** + * @brief drop all alternative blocks + */ + virtual void drop_alt_blocks() = 0; + /** * @brief runs a function over all txpool transactions * @@ -1634,6 +1680,23 @@ public: virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const = 0; virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const = 0; + /** + * @brief runs a function over all alternative blocks stored + * + * The subclass should run the passed function for each alt block it has + * stored, passing (blkid, data, blob) as its parameters. + * + * If any call to the function returns false, the subclass should return + * false. Otherwise, the subclass returns true. + * + * The subclass should throw DB_ERROR if any of the expected values are + * not found. Current implementations simply return false. + * + * @param std::function f the function to run + * + * @return false if the function returns false for any output, otherwise true + */ + virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const = 0; // diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index d0fd787db..78db37b5a 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -194,6 +194,8 @@ namespace * txpool_meta txn hash txn metadata * txpool_blob txn hash txn blob * + * alt_blocks block hash {block data, block blob} + * * Note: where the data items are of uniform size, DUPFIXED tables have * been used to save space. In most of these cases, a dummy "zerokval" * key is used when accessing the table; the Key listed above will be @@ -221,6 +223,8 @@ const char* const LMDB_SPENT_KEYS = "spent_keys"; const char* const LMDB_TXPOOL_META = "txpool_meta"; const char* const LMDB_TXPOOL_BLOB = "txpool_blob"; +const char* const LMDB_ALT_BLOCKS = "alt_blocks"; + const char* const LMDB_HF_STARTING_HEIGHTS = "hf_starting_heights"; const char* const LMDB_HF_VERSIONS = "hf_versions"; @@ -707,7 +711,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin ++num_blocks_used; } if (my_rtxn) block_rtxn_stop(); - avg_block_size = total_block_size / num_blocks_used; + avg_block_size = total_block_size / (num_blocks_used ? num_blocks_used : 1); MDEBUG("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size); } estim: @@ -1400,6 +1404,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) lmdb_db_open(txn, LMDB_TXPOOL_META, MDB_CREATE, m_txpool_meta, "Failed to open db handle for m_txpool_meta"); lmdb_db_open(txn, LMDB_TXPOOL_BLOB, MDB_CREATE, m_txpool_blob, "Failed to open db handle for m_txpool_blob"); + lmdb_db_open(txn, LMDB_ALT_BLOCKS, MDB_CREATE, m_alt_blocks, "Failed to open db handle for m_alt_blocks"); + // this subdb is dropped on sight, so it may not be present when we open the DB. // Since we use MDB_CREATE, we'll get an exception if we open read-only and it does not exist. // So we don't open for read-only, and also not drop below. It is not used elsewhere. @@ -1423,6 +1429,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) mdb_set_compare(txn, m_txpool_meta, compare_hash32); mdb_set_compare(txn, m_txpool_blob, compare_hash32); + mdb_set_compare(txn, m_alt_blocks, compare_hash32); mdb_set_compare(txn, m_properties, compare_string); if (!(mdb_flags & MDB_RDONLY)) @@ -2290,6 +2297,50 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, return ret; } +bool BlockchainLMDB::for_all_alt_blocks(std::function<bool(const crypto::hash&, const alt_block_data_t&, const cryptonote::blobdata*)> f, bool include_blob) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(alt_blocks); + + MDB_val k; + MDB_val v; + bool ret = true; + + MDB_cursor_op op = MDB_FIRST; + while (1) + { + int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, op); + op = MDB_NEXT; + if (result == MDB_NOTFOUND) + break; + if (result) + throw0(DB_ERROR(lmdb_error("Failed to enumerate alt blocks: ", result).c_str())); + const crypto::hash &blkid = *(const crypto::hash*)k.mv_data; + if (v.mv_size < sizeof(alt_block_data_t)) + throw0(DB_ERROR("alt_blocks record is too small")); + const alt_block_data_t *data = (const alt_block_data_t*)v.mv_data; + const cryptonote::blobdata *passed_bd = NULL; + cryptonote::blobdata bd; + if (include_blob) + { + bd.assign(reinterpret_cast<const char*>(v.mv_data) + sizeof(alt_block_data_t), v.mv_size - sizeof(alt_block_data_t)); + passed_bd = &bd; + } + + if (!f(blkid, *data, passed_bd)) { + ret = false; + break; + } + } + + TXN_POSTFIX_RDONLY(); + + return ret; +} + bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -4111,6 +4162,110 @@ uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const return ret; } +void BlockchainLMDB::add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + + CURSOR(alt_blocks) + + MDB_val k = {sizeof(blkid), (void *)&blkid}; + const size_t val_size = sizeof(alt_block_data_t) + blob.size(); + std::unique_ptr<char[]> val(new char[val_size]); + memcpy(val.get(), &data, sizeof(alt_block_data_t)); + memcpy(val.get() + sizeof(alt_block_data_t), blob.data(), blob.size()); + MDB_val v = {val_size, (void *)val.get()}; + if (auto result = mdb_cursor_put(m_cur_alt_blocks, &k, &v, MDB_NODUPDATA)) { + if (result == MDB_KEYEXIST) + throw1(DB_ERROR("Attempting to add alternate block that's already in the db")); + else + throw1(DB_ERROR(lmdb_error("Error adding alternate block to db transaction: ", result).c_str())); + } +} + +bool BlockchainLMDB::get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) +{ + LOG_PRINT_L3("BlockchainLMDB:: " << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(alt_blocks); + + MDB_val_set(k, blkid); + MDB_val v; + int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, MDB_SET); + if (result == MDB_NOTFOUND) + return false; + + if (result) + throw0(DB_ERROR(lmdb_error("Error attempting to retrieve alternate block " + epee::string_tools::pod_to_hex(blkid) + " from the db: ", result).c_str())); + if (v.mv_size < sizeof(alt_block_data_t)) + throw0(DB_ERROR("Record size is less than expected")); + + const alt_block_data_t *ptr = (const alt_block_data_t*)v.mv_data; + if (data) + *data = *ptr; + if (blob) + blob->assign((const char*)(ptr + 1), v.mv_size - sizeof(alt_block_data_t)); + + TXN_POSTFIX_RDONLY(); + return true; +} + +void BlockchainLMDB::remove_alt_block(const crypto::hash &blkid) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + + CURSOR(alt_blocks) + + MDB_val k = {sizeof(blkid), (void *)&blkid}; + MDB_val v; + int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, MDB_SET); + if (result) + throw0(DB_ERROR(lmdb_error("Error locating alternate block " + epee::string_tools::pod_to_hex(blkid) + " in the db: ", result).c_str())); + result = mdb_cursor_del(m_cur_alt_blocks, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Error deleting alternate block " + epee::string_tools::pod_to_hex(blkid) + " from the db: ", result).c_str())); +} + +uint64_t BlockchainLMDB::get_alt_block_count() +{ + LOG_PRINT_L3("BlockchainLMDB:: " << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(alt_blocks); + + MDB_stat db_stats; + int result = mdb_stat(m_txn, m_alt_blocks, &db_stats); + uint64_t count = 0; + if (result != MDB_NOTFOUND) + { + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_alt_blocks: ", result).c_str())); + count = db_stats.ms_entries; + } + TXN_POSTFIX_RDONLY(); + return count; +} + +void BlockchainLMDB::drop_alt_blocks() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX(0); + + auto result = mdb_drop(*txn_ptr, m_alt_blocks, 0); + if (result) + throw1(DB_ERROR(lmdb_error("Error dropping alternative blocks: ", result).c_str())); + + TXN_POSTFIX_SUCCESS(); +} + bool BlockchainLMDB::is_read_only() const { unsigned int flags; diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 4b46f081e..61a551476 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -67,6 +67,8 @@ typedef struct mdb_txn_cursors MDB_cursor *m_txc_txpool_meta; MDB_cursor *m_txc_txpool_blob; + MDB_cursor *m_txc_alt_blocks; + MDB_cursor *m_txc_hf_versions; MDB_cursor *m_txc_properties; @@ -87,6 +89,7 @@ typedef struct mdb_txn_cursors #define m_cur_spent_keys m_cursors->m_txc_spent_keys #define m_cur_txpool_meta m_cursors->m_txc_txpool_meta #define m_cur_txpool_blob m_cursors->m_txc_txpool_blob +#define m_cur_alt_blocks m_cursors->m_txc_alt_blocks #define m_cur_hf_versions m_cursors->m_txc_hf_versions #define m_cur_properties m_cursors->m_txc_properties @@ -108,6 +111,7 @@ typedef struct mdb_rflags bool m_rf_spent_keys; bool m_rf_txpool_meta; bool m_rf_txpool_blob; + bool m_rf_alt_blocks; bool m_rf_hf_versions; bool m_rf_properties; } mdb_rflags; @@ -288,6 +292,12 @@ public: virtual bool update_pruning(); virtual bool check_pruning(); + virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob); + virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob); + virtual void remove_alt_block(const crypto::hash &blkid); + virtual uint64_t get_alt_block_count(); + virtual void drop_alt_blocks(); + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const; virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const; @@ -295,6 +305,7 @@ public: virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const; virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const; virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const; + virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const; virtual uint64_t add_block( const std::pair<block, blobdata>& blk , size_t block_weight @@ -452,6 +463,8 @@ private: MDB_dbi m_txpool_meta; MDB_dbi m_txpool_blob; + MDB_dbi m_alt_blocks; + MDB_dbi m_hf_starting_heights; MDB_dbi m_hf_versions; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 34e635899..ac19fae25 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -156,6 +156,13 @@ public: virtual uint64_t get_max_block_size() override { return 100000000; } virtual void add_max_block_size(uint64_t sz) override { } + + virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) override {} + virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) override { return false; } + virtual void remove_alt_block(const crypto::hash &blkid) override {} + virtual uint64_t get_alt_block_count() override { return 0; } + virtual void drop_alt_blocks() override {} + virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const override { return true; } }; } diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 6ff184041..f824d93a6 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -637,6 +637,7 @@ static void inc_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t total v.mv_size = 2 * sizeof(uint64_t); v.mv_data = (void*)data; dbr = mdb_cursor_put(cur, &k, &v, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to write record for per amount outputs: " + std::string(mdb_strerror(dbr))); mdb_cursor_close(cur); } diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index fa1243c1f..85566efca 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -177,7 +177,7 @@ int main(int argc, char* argv[]) } r = core_storage->init(db, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET); - if (core_storage->get_blockchain_pruning_seed()) + if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat) { LOG_PRINT_L0("Blockchain is pruned, cannot export"); return 1; diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat Binary files differindex b975af6b4..a7d309753 100644 --- a/src/blocks/checkpoints.dat +++ b/src/blocks/checkpoints.dat diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 7f36c9dc3..13835c823 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -897,7 +897,6 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int // locals to avoid constant TLS dereferencing uint8_t *local_hp_state = hp_state; - v4_random_math_JIT_func local_hp_jitfunc = hp_jitfunc; /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ if (prehashed) { diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0dcc25d8d..31e2ac136 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -731,9 +731,9 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; - m_alternative_chains.clear(); invalidate_block_template_cache(); m_db->reset(); + m_db->drop_alt_blocks(); m_hardfork->init(); db_wtxn_guard wtxn_guard(m_db); @@ -858,10 +858,15 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph // 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) + alt_block_data_t data; + cryptonote::blobdata blob; + if (m_db->get_alt_block(h, &data, &blob)) { - blk = it_alt->second.bl; + if (!cryptonote::parse_and_validate_block_from_blob(blob, blk)) + { + MERROR("Found block " << h << " in alt chain, but failed to parse it"); + throw std::runtime_error("Found block in alt chain, but failed to parse it"); + } if (orphan) *orphan = true; return true; @@ -1019,7 +1024,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, //------------------------------------------------------------------ // 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::const_iterator>& alt_chain, bool discard_disconnected_chain) +bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool discard_disconnected_chain) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1030,7 +1035,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: 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)) + if (!m_db->block_exists(alt_chain.front().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; @@ -1039,7 +1044,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: // 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) + while (m_db->top_block_hash() != alt_chain.front().bl.prev_id) { block b = pop_block_from_blockchain(); disconnected_chain.push_front(b); @@ -1050,11 +1055,11 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: //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; + const auto &bei = *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); + bool r = handle_block_to_main_chain(bei.bl, bvc); // if adding block to main chain failed, rollback to previous state and // return false @@ -1070,14 +1075,18 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: // 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)); - MERROR("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl)); - m_alternative_chains.erase(*alt_ch_iter++); + const crypto::hash blkid = cryptonote::get_block_hash(bei.bl); + add_block_as_invalid(bei, blkid); + MERROR("The block was inserted as invalid while connecting new alternative chain, block_id: " << blkid); + m_db->remove_alt_block(blkid); + alt_ch_iter++; for(auto alt_ch_to_orph_iter = alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); ) { - add_block_as_invalid((*alt_ch_to_orph_iter)->second, (*alt_ch_to_orph_iter)->first); - m_alternative_chains.erase(*alt_ch_to_orph_iter++); + const auto &bei = *alt_ch_to_orph_iter++; + const crypto::hash blkid = cryptonote::get_block_hash(bei.bl); + add_block_as_invalid(bei, blkid); + m_db->remove_alt_block(blkid); } return false; } @@ -1102,9 +1111,9 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: } //removing alt_chain entries from alternative chains container - for (auto ch_ent: alt_chain) + for (const auto &bei: alt_chain) { - m_alternative_chains.erase(ch_ent); + m_db->remove_alt_block(cryptonote::get_block_hash(bei.bl)); } m_hardfork->reorganize_from_chain_height(split_height); @@ -1120,7 +1129,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: //------------------------------------------------------------------ // 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::const_iterator>& alt_chain, block_extended_info& bei) const +difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<block_extended_info>& alt_chain, block_extended_info& bei) const { if (m_fixed_difficulty) { @@ -1138,7 +1147,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: 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_stop_offset = alt_chain.size() ? alt_chain.front().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; @@ -1156,10 +1165,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: // 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) + for (const auto &bei : alt_chain) { - timestamps.push_back(it->second.bl.timestamp); - cumulative_difficulties.push_back(it->second.cumulative_difficulty); + timestamps.push_back(bei.bl.timestamp); + cumulative_difficulties.push_back(bei.cumulative_difficulty); } } // if the alt chain is long enough for the difficulty calc, grab difficulties @@ -1171,10 +1180,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: size_t count = 0; size_t max_i = timestamps.size()-1; // get difficulties and timestamps from most recent blocks in alt chain - for(auto it: boost::adaptors::reverse(alt_chain)) + for (const auto bei: boost::adaptors::reverse(alt_chain)) { - timestamps[max_i - count] = it->second.bl.timestamp; - cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty; + timestamps[max_i - count] = bei.bl.timestamp; + cumulative_difficulties[max_i - count] = bei.cumulative_difficulty; count++; if(count >= DIFFICULTY_BLOCKS_COUNT) break; @@ -1393,16 +1402,17 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, //build alternative subchain, front -> mainchain, back -> alternative head //block is not related with head of main chain //first of all - look in alternative chains container - auto it_prev = m_alternative_chains.find(*from_block); + alt_block_data_t prev_data; + bool parent_in_alt = m_db->get_alt_block(*from_block, &prev_data, NULL); bool parent_in_main = m_db->block_exists(*from_block); - if(it_prev == m_alternative_chains.end() && !parent_in_main) + if (!parent_in_alt && !parent_in_main) { MERROR("Unknown from block"); return false; } //we have new block in alternative chain - std::list<blocks_ext_by_hash::const_iterator> alt_chain; + std::list<block_extended_info> alt_chain; block_verification_context bvc = boost::value_initialized<block_verification_context>(); std::vector<uint64_t> timestamps; if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc)) @@ -1417,7 +1427,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, } else { - height = alt_chain.back()->second.height + 1; + height = alt_chain.back().height + 1; } b.major_version = m_hardfork->get_ideal_version(height); b.minor_version = m_hardfork->get_ideal_version(); @@ -1432,14 +1442,14 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, } else { - median_weight = it_prev->second.block_cumulative_weight - it_prev->second.block_cumulative_weight / 20; - already_generated_coins = alt_chain.back()->second.already_generated_coins; + median_weight = prev_data.cumulative_weight - prev_data.cumulative_weight / 20; + already_generated_coins = alt_chain.back().already_generated_coins; } // 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(*from_block) + 1; + bei.height = alt_chain.size() ? prev_data.height + 1 : m_db->get_block_height(*from_block) + 1; diffic = get_next_difficulty_for_alternative_chain(alt_chain, bei); } @@ -1618,16 +1628,25 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect return true; } //------------------------------------------------------------------ -bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const +bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const { //build alternative subchain, front -> mainchain, back -> alternative head - blocks_ext_by_hash::const_iterator alt_it = m_alternative_chains.find(prev_id); + cryptonote::alt_block_data_t data; + cryptonote::blobdata blob; + bool found = m_db->get_alt_block(prev_id, &data, &blob); timestamps.clear(); - while(alt_it != m_alternative_chains.end()) + while(found) { - 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); + block_extended_info bei; + CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_block_from_blob(blob, bei.bl), false, "Failed to parse alt block"); + bei.height = data.height; + bei.block_cumulative_weight = data.cumulative_weight; + bei.cumulative_difficulty = data.cumulative_difficulty_high; + bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + data.cumulative_difficulty_low; + bei.already_generated_coins = data.already_generated_coins; + timestamps.push_back(bei.bl.timestamp); + alt_chain.push_front(std::move(bei)); + found = m_db->get_alt_block(bei.bl.prev_id, &data, &blob); } // if block to be added connects to known blocks that aren't part of the @@ -1635,20 +1654,20 @@ bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_e if(!alt_chain.empty()) { // 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"); + CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front().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)) + if (!m_db->block_exists(alt_chain.front().bl.prev_id)) { MERROR("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); + auto h = m_db->get_block_hash_from_height(alt_chain.front().height - 1); + CHECK_AND_ASSERT_MES(h == alt_chain.front().bl.prev_id, false, "alternative chain has wrong connection to main chain"); + complete_timestamps_vector(m_db->get_block_height(alt_chain.front().bl.prev_id), timestamps); } // if block not associated with known alternate chain else @@ -1703,12 +1722,13 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id //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); + alt_block_data_t prev_data; + bool parent_in_alt = m_db->get_alt_block(b.prev_id, &prev_data, NULL); bool parent_in_main = m_db->block_exists(b.prev_id); - if(it_prev != m_alternative_chains.end() || parent_in_main) + if (parent_in_alt || parent_in_main) { //we have new block in alternative chain - std::list<blocks_ext_by_hash::const_iterator> alt_chain; + std::list<block_extended_info> alt_chain; std::vector<uint64_t> timestamps; if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc)) return false; @@ -1716,10 +1736,10 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id // FIXME: consider moving away from block_extended_info at some point block_extended_info bei = boost::value_initialized<block_extended_info>(); bei.bl = b; - const uint64_t prev_height = alt_chain.size() ? it_prev->second.height : m_db->get_block_height(b.prev_id); + const uint64_t prev_height = alt_chain.size() ? prev_data.height : m_db->get_block_height(b.prev_id); bei.height = prev_height + 1; uint64_t block_reward = get_outs_money_amount(b.miner_tx); - bei.already_generated_coins = block_reward + (alt_chain.size() ? it_prev->second.already_generated_coins : m_db->get_block_already_generated_coins(prev_height)); + bei.already_generated_coins = block_reward + (alt_chain.size() ? prev_data.already_generated_coins : m_db->get_block_already_generated_coins(prev_height)); // verify that the block's timestamp is within the acceptable range // (not earlier than the median of the last X blocks) @@ -1763,7 +1783,8 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id 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; + bei.cumulative_difficulty = prev_data.cumulative_difficulty_high; + bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + prev_data.cumulative_difficulty_low; } else { @@ -1774,15 +1795,21 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id // 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); + CHECK_AND_ASSERT_MES(!m_db->get_alt_block(id, NULL, NULL), false, "insertion of new alternative block returned as it already exists"); + cryptonote::alt_block_data_t data; + data.height = bei.height; + data.cumulative_weight = bei.block_cumulative_weight; + data.cumulative_difficulty_low = (bei.cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>(); + data.cumulative_difficulty_high = ((bei.cumulative_difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>(); + data.already_generated_coins = bei.already_generated_coins; + m_db->add_alt_block(id, data, cryptonote::block_to_blob(bei.bl)); + alt_chain.push_back(bei); // FIXME: is it even possible for a checkpoint to show up not on the main chain? if(is_a_checkpoint) { //do reorganize! - MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height); + MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height); bool r = switch_to_alternative_blockchain(alt_chain, true); @@ -1794,7 +1821,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id else if(main_chain_cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain { //do reorganize! - MGINFO_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); + MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().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); bool r = switch_to_alternative_blockchain(alt_chain, false); if (r) @@ -1814,7 +1841,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id //block orphaned bvc.m_marked_as_orphaned = true; MERROR_VER("Block recognized as orphaned and rejected, id = " << id << ", height " << block_height - << ", parent in alt " << (it_prev != m_alternative_chains.end()) << ", parent in main " << parent_in_main + << ", parent in alt " << parent_in_alt << ", parent in main " << parent_in_main << " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << get_current_blockchain_height() << ")"); } @@ -1921,11 +1948,20 @@ bool Blockchain::get_alternative_blocks(std::vector<block>& blocks) const LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - blocks.reserve(m_alternative_chains.size()); - for (const auto& alt_bl: m_alternative_chains) - { - blocks.push_back(alt_bl.second.bl); - } + blocks.reserve(m_db->get_alt_block_count()); + m_db->for_all_alt_blocks([&blocks](const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata *blob) { + if (!blob) + { + MERROR("No blob, but blobs were requested"); + return false; + } + cryptonote::block bl; + if (cryptonote::parse_and_validate_block_from_blob(*blob, bl)) + blocks.push_back(std::move(bl)); + else + MERROR("Failed to parse block from blob"); + return true; + }, true); return true; } //------------------------------------------------------------------ @@ -1933,7 +1969,7 @@ size_t Blockchain::get_alternative_blocks_count() const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_alternative_chains.size(); + return m_db->get_alt_block_count(); } //------------------------------------------------------------------ // This function adds the output specified by <amount, i> to the result_outs container @@ -2037,7 +2073,6 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, if (to_height > 0 && to_height < from_height) return false; - const uint64_t real_start_height = start_height; if (from_height > start_height) start_height = from_height; @@ -2051,7 +2086,7 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, { std::vector<uint64_t> heights; heights.reserve(to_height + 1 - start_height); - uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; + const uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; for (uint64_t h = real_start_height; h <= to_height; ++h) heights.push_back(h); distribution = m_db->get_block_cumulative_rct_outputs(heights); @@ -2421,9 +2456,9 @@ bool Blockchain::have_block(const crypto::hash& id) const return true; } - if(m_alternative_chains.count(id)) + if(m_db->get_alt_block(id, NULL, NULL)) { - LOG_PRINT_L2("block " << id << " found in m_alternative_chains"); + LOG_PRINT_L2("block " << id << " found in alternative chains"); return true; } @@ -3252,7 +3287,6 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const { const uint8_t version = get_current_hard_fork_version(); - const uint64_t blockchain_height = m_db->height(); uint64_t median = 0; uint64_t already_generated_coins = 0; @@ -3960,14 +3994,12 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti const uint64_t db_height = m_db->height(); const uint8_t hf_version = get_current_hard_fork_version(); uint64_t full_reward_zone = get_min_block_weight(hf_version); - uint64_t long_term_block_weight; if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) { std::vector<uint64_t> weights; get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); m_current_block_cumul_weight_median = epee::misc_utils::median(weights); - long_term_block_weight = weights.back(); } else { @@ -3989,7 +4021,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); uint64_t short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5; - long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint); + uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint); if (db_height == 1) { @@ -4812,11 +4844,35 @@ std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> { std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains; - for (const auto &i: m_alternative_chains) + blocks_ext_by_hash alt_blocks; + alt_blocks.reserve(m_db->get_alt_block_count()); + m_db->for_all_alt_blocks([&alt_blocks](const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata *blob) { + if (!blob) + { + MERROR("No blob, but blobs were requested"); + return false; + } + cryptonote::block bl; + block_extended_info bei; + if (cryptonote::parse_and_validate_block_from_blob(*blob, bei.bl)) + { + bei.height = data.height; + bei.block_cumulative_weight = data.cumulative_weight; + bei.cumulative_difficulty = data.cumulative_difficulty_high; + bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + data.cumulative_difficulty_low; + bei.already_generated_coins = data.already_generated_coins; + alt_blocks.insert(std::make_pair(cryptonote::get_block_hash(bei.bl), std::move(bei))); + } + else + MERROR("Failed to parse block from blob"); + return true; + }, true); + + for (const auto &i: alt_blocks) { - const crypto::hash &top = i.first; + const crypto::hash top = cryptonote::get_block_hash(i.second.bl); bool found = false; - for (const auto &j: m_alternative_chains) + for (const auto &j: alt_blocks) { if (j.second.bl.prev_id == top) { @@ -4830,7 +4886,7 @@ std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> auto h = i.second.bl.prev_id; chain.push_back(top); blocks_ext_by_hash::const_iterator prev; - while ((prev = m_alternative_chains.find(h)) != m_alternative_chains.end()) + while ((prev = alt_blocks.find(h)) != alt_blocks.end()) { chain.push_back(h); h = prev->second.bl.prev_id; @@ -4847,7 +4903,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "cfca50ea0c87718ac92a14654c60d7ee8f6453e2765b329b40d10da4ed85a4f2"; +static const char expected_block_hashes_hash[] = "7dafb40b414a0e59bfced6682ef519f0b416bc914dd3d622b72e0dd1a47117c2"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 32ed96b5b..b1c23b704 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -92,24 +92,13 @@ namespace cryptonote { public: /** - * @brief Now-defunct (TODO: remove) struct from in-memory blockchain - */ - 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; - }; - - /** * @brief container for passing a block and metadata about it on the blockchain */ struct block_extended_info { block bl; //!< the block uint64_t height; //!< the height of the block in the blockchain - size_t block_cumulative_weight; //!< the weight of the block + uint64_t block_cumulative_weight; //!< the weight of the block difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block uint64_t already_generated_coins; //!< the total coins minted after that block }; @@ -1011,20 +1000,12 @@ namespace cryptonote #endif // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage - 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; @@ -1033,7 +1014,6 @@ namespace cryptonote mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock // main chain - transactions_container m_transactions; size_t m_current_block_cumul_weight_limit; size_t m_current_block_cumul_weight_median; @@ -1074,9 +1054,6 @@ namespace cryptonote boost::thread_group m_async_pool; std::unique_ptr<boost::asio::io_service::work> m_async_work_idle; - // 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 @@ -1186,7 +1163,7 @@ namespace cryptonote * * @return false if the reorganization fails, otherwise true */ - bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain); + bool switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool discard_disconnected_chain); /** * @brief removes the most recent block from the blockchain @@ -1249,7 +1226,7 @@ namespace cryptonote * * @return true on success, false otherwise */ - bool build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const; + bool build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const; /** * @brief gets the difficulty requirement for a new block on an alternate chain @@ -1259,7 +1236,7 @@ namespace cryptonote * * @return the difficulty requirement */ - difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const; + difficulty_type get_next_difficulty_for_alternative_chain(const std::list<block_extended_info>& alt_chain, block_extended_info& bei) const; /** * @brief sanity checks a miner transaction before validating an entire block diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 13426230e..2ebd99904 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -208,13 +208,17 @@ namespace cryptonote "is acted upon." , "" }; + static const command_line::arg_descriptor<bool> arg_keep_alt_blocks = { + "keep-alt-blocks" + , "Keep alternative blocks on restart" + , false + }; //----------------------------------------------------------------------------------------------- core::core(i_cryptonote_protocol* pprotocol): m_mempool(m_blockchain_storage), m_blockchain_storage(m_mempool), m_miner(this), - m_miner_address(boost::value_initialized<account_public_address>()), m_starter_message_showed(false), m_target_blockchain_height(0), m_checkpoints_path(""), @@ -325,6 +329,7 @@ namespace cryptonote command_line::add_arg(desc, arg_prune_blockchain); command_line::add_arg(desc, arg_reorg_notify); command_line::add_arg(desc, arg_block_rate_notify); + command_line::add_arg(desc, arg_keep_alt_blocks); miner::init_options(desc); BlockchainDB::init_options(desc); @@ -447,6 +452,7 @@ namespace cryptonote m_nettype = FAKECHAIN; } bool r = handle_command_line(vm); + CHECK_AND_ASSERT_MES(r, false, "Failed to handle command line"); std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode); @@ -456,6 +462,7 @@ namespace cryptonote std::string check_updates_string = command_line::get_arg(vm, arg_check_updates); size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight); bool prune_blockchain = command_line::get_arg(vm, arg_prune_blockchain); + bool keep_alt_blocks = command_line::get_arg(vm, arg_keep_alt_blocks); boost::filesystem::path folder(m_config_folder); if (m_nettype == FAKECHAIN) @@ -634,6 +641,7 @@ namespace cryptonote }; const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); + CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); r = m_mempool.init(max_txpool_weight); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); @@ -671,6 +679,9 @@ namespace cryptonote r = m_miner.init(vm, m_nettype); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance"); + if (!keep_alt_blocks && !m_blockchain_storage.get_db().is_read_only()) + m_blockchain_storage.get_db().drop_alt_blocks(); + if (prune_blockchain) { // display a message if the blockchain is not pruned yet diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 2fcf26a17..badbaf936 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1014,7 +1014,6 @@ namespace cryptonote //m_miner and m_miner_addres are probably temporary here miner m_miner; //!< miner instance - account_public_address m_miner_address; //!< address to mine to (for miner instance) std::string m_config_folder; //!< folder to look in for configs and other files diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 0ca717344..940d35581 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -341,6 +341,11 @@ namespace cryptonote if(m_core.have_block(hshd.top_id)) { + if (target > hshd.current_height) + { + MINFO(context << "peer is not ahead of us and we're syncing, disconnecting"); + return false; + } context.m_state = cryptonote_connection_context::state_normal; if(is_inital && target == m_core.get_current_blockchain_height()) on_connection_synchronized(); diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 0b452800e..778d7b4d8 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -494,11 +494,14 @@ bool t_command_parser_executor::set_limit_down(const std::vector<std::string>& a bool t_command_parser_executor::out_peers(const std::vector<std::string>& args) { - if (args.empty()) return false; - - unsigned int limit; + bool set = false; + uint32_t limit = 0; try { - limit = std::stoi(args[0]); + if (!args.empty()) + { + limit = std::stoi(args[0]); + set = true; + } } catch(const std::exception& ex) { @@ -506,16 +509,19 @@ bool t_command_parser_executor::out_peers(const std::vector<std::string>& args) return false; } - return m_executor.out_peers(limit); + return m_executor.out_peers(set, limit); } bool t_command_parser_executor::in_peers(const std::vector<std::string>& args) { - if (args.empty()) return false; - - unsigned int limit; + bool set = false; + uint32_t limit = 0; try { - limit = std::stoi(args[0]); + if (!args.empty()) + { + limit = std::stoi(args[0]); + set = true; + } } catch(const std::exception& ex) { @@ -523,19 +529,7 @@ bool t_command_parser_executor::in_peers(const std::vector<std::string>& args) return false; } - return m_executor.in_peers(limit); -} - -bool t_command_parser_executor::start_save_graph(const std::vector<std::string>& args) -{ - if (!args.empty()) return false; - return m_executor.start_save_graph(); -} - -bool t_command_parser_executor::stop_save_graph(const std::vector<std::string>& args) -{ - if (!args.empty()) return false; - return m_executor.stop_save_graph(); + return m_executor.in_peers(set, limit); } bool t_command_parser_executor::hard_fork_info(const std::vector<std::string>& args) @@ -596,6 +590,13 @@ bool t_command_parser_executor::unban(const std::vector<std::string>& args) return m_executor.unban(ip); } +bool t_command_parser_executor::banned(const std::vector<std::string>& args) +{ + if (args.size() != 1) return false; + std::string address = args[0]; + return m_executor.banned(address); +} + bool t_command_parser_executor::flush_txpool(const std::vector<std::string>& args) { if (args.size() > 1) return false; diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 2efd78ec0..d39bc1c9b 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -115,10 +115,6 @@ public: bool in_peers(const std::vector<std::string>& args); - bool start_save_graph(const std::vector<std::string>& args); - - bool stop_save_graph(const std::vector<std::string>& args); - bool hard_fork_info(const std::vector<std::string>& args); bool show_bans(const std::vector<std::string>& args); @@ -127,6 +123,8 @@ public: bool unban(const std::vector<std::string>& args); + bool banned(const std::vector<std::string>& args); + bool flush_txpool(const std::vector<std::string>& args); bool output_histogram(const std::vector<std::string>& args); diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index f665eec9c..aecdda52c 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -215,16 +215,6 @@ t_command_server::t_command_server( , "Set the <max_number> of in peers." ); m_command_lookup.set_handler( - "start_save_graph" - , std::bind(&t_command_parser_executor::start_save_graph, &m_parser, p::_1) - , "Start saving data for dr monero." - ); - m_command_lookup.set_handler( - "stop_save_graph" - , std::bind(&t_command_parser_executor::stop_save_graph, &m_parser, p::_1) - , "Stop saving data for dr monero." - ); - m_command_lookup.set_handler( "hard_fork_info" , std::bind(&t_command_parser_executor::hard_fork_info, &m_parser, p::_1) , "Print the hard fork voting information." @@ -243,10 +233,16 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "unban" , std::bind(&t_command_parser_executor::unban, &m_parser, p::_1) - , "unban <IP>" + , "unban <address>" , "Unban a given <IP>." ); m_command_lookup.set_handler( + "banned" + , std::bind(&t_command_parser_executor::banned, &m_parser, p::_1) + , "banned <address>" + , "Check whether an <address> is banned." + ); + m_command_lookup.set_handler( "flush_txpool" , std::bind(&t_command_parser_executor::flush_txpool, &m_parser, p::_1) , "flush_txpool [<txid>]" diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index cca0f75f9..f3b9ea6ec 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -54,7 +54,7 @@ namespace { std::string id_str; std::string port_str; - std::string elapsed = epee::misc_utils::get_time_interval_string(now - last_seen); + std::string elapsed = peer.last_seen == 0 ? "never" : epee::misc_utils::get_time_interval_string(now - last_seen); std::string ip_str = epee::string_tools::get_ip_string_from_int32(peer.ip); std::stringstream peer_id_str; peer_id_str << std::hex << std::setw(16) << peer.id; @@ -338,6 +338,7 @@ bool t_rpc_command_executor::show_difficulty() { tools::success_msg_writer() << "BH: " << res.height << ", TH: " << res.top_block_hash << ", DIFF: " << res.difficulty + << ", CUM_DIFF: " << res.cumulative_difficulty << ", HR: " << res.difficulty / res.target << " H/s"; return true; @@ -1466,13 +1467,14 @@ bool t_rpc_command_executor::get_limit_down() return true; } -bool t_rpc_command_executor::out_peers(uint64_t limit) +bool t_rpc_command_executor::out_peers(bool set, uint32_t limit) { cryptonote::COMMAND_RPC_OUT_PEERS::request req; cryptonote::COMMAND_RPC_OUT_PEERS::response res; epee::json_rpc::error error_resp; + req.set = set; req.out_peers = limit; std::string fail_message = "Unsuccessful"; @@ -1493,18 +1495,20 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) } } - tools::msg_writer() << "Max number of out peers set to " << limit << std::endl; + const std::string s = res.out_peers == (uint32_t)-1 ? "unlimited" : std::to_string(res.out_peers); + tools::msg_writer() << "Max number of out peers set to " << s << std::endl; return true; } -bool t_rpc_command_executor::in_peers(uint64_t limit) +bool t_rpc_command_executor::in_peers(bool set, uint32_t limit) { cryptonote::COMMAND_RPC_IN_PEERS::request req; cryptonote::COMMAND_RPC_IN_PEERS::response res; epee::json_rpc::error error_resp; + req.set = set; req.in_peers = limit; std::string fail_message = "Unsuccessful"; @@ -1525,64 +1529,12 @@ bool t_rpc_command_executor::in_peers(uint64_t limit) } } - tools::msg_writer() << "Max number of in peers set to " << limit << std::endl; + const std::string s = res.in_peers == (uint32_t)-1 ? "unlimited" : std::to_string(res.in_peers); + tools::msg_writer() << "Max number of in peers set to " << s << std::endl; return true; } -bool t_rpc_command_executor::start_save_graph() -{ - cryptonote::COMMAND_RPC_START_SAVE_GRAPH::request req; - cryptonote::COMMAND_RPC_START_SAVE_GRAPH::response res; - std::string fail_message = "Unsuccessful"; - - if (m_is_rpc) - { - if (!m_rpc_client->rpc_request(req, res, "/start_save_graph", fail_message.c_str())) - { - return true; - } - } - - else - { - if (!m_rpc_server->on_start_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK) - { - tools::fail_msg_writer() << make_error(fail_message, res.status); - return true; - } - } - - tools::success_msg_writer() << "Saving graph is now on"; - return true; -} - -bool t_rpc_command_executor::stop_save_graph() -{ - cryptonote::COMMAND_RPC_STOP_SAVE_GRAPH::request req; - cryptonote::COMMAND_RPC_STOP_SAVE_GRAPH::response res; - std::string fail_message = "Unsuccessful"; - - if (m_is_rpc) - { - if (!m_rpc_client->rpc_request(req, res, "/stop_save_graph", fail_message.c_str())) - { - return true; - } - } - - else - { - if (!m_rpc_server->on_stop_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK) - { - tools::fail_msg_writer() << make_error(fail_message, res.status); - return true; - } - } - tools::success_msg_writer() << "Saving graph is now off"; - return true; -} - bool t_rpc_command_executor::hard_fork_info(uint8_t version) { cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req; @@ -1641,14 +1593,14 @@ bool t_rpc_command_executor::print_bans() for (auto i = res.bans.begin(); i != res.bans.end(); ++i) { - tools::msg_writer() << epee::string_tools::get_ip_string_from_int32(i->ip) << " banned for " << i->seconds << " seconds"; + tools::msg_writer() << i->host << " banned for " << i->seconds << " seconds"; } return true; } -bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) +bool t_rpc_command_executor::ban(const std::string &address, time_t seconds) { cryptonote::COMMAND_RPC_SETBANS::request req; cryptonote::COMMAND_RPC_SETBANS::response res; @@ -1656,11 +1608,8 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) epee::json_rpc::error error_resp; cryptonote::COMMAND_RPC_SETBANS::ban ban; - if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip)) - { - tools::fail_msg_writer() << "Invalid IP"; - return true; - } + ban.host = address; + ban.ip = 0; ban.ban = true; ban.seconds = seconds; req.bans.push_back(ban); @@ -1684,7 +1633,7 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) return true; } -bool t_rpc_command_executor::unban(const std::string &ip) +bool t_rpc_command_executor::unban(const std::string &address) { cryptonote::COMMAND_RPC_SETBANS::request req; cryptonote::COMMAND_RPC_SETBANS::response res; @@ -1692,11 +1641,8 @@ bool t_rpc_command_executor::unban(const std::string &ip) epee::json_rpc::error error_resp; cryptonote::COMMAND_RPC_SETBANS::ban ban; - if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip)) - { - tools::fail_msg_writer() << "Invalid IP"; - return true; - } + ban.host = address; + ban.ip = 0; ban.ban = false; ban.seconds = 0; req.bans.push_back(ban); @@ -1720,6 +1666,39 @@ bool t_rpc_command_executor::unban(const std::string &ip) return true; } +bool t_rpc_command_executor::banned(const std::string &address) +{ + cryptonote::COMMAND_RPC_BANNED::request req; + cryptonote::COMMAND_RPC_BANNED::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + req.address = address; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "banned", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_banned(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + if (res.banned) + tools::msg_writer() << address << " is banned for " << res.seconds << " seconds"; + else + tools::msg_writer() << address << " is not banned"; + + return true; +} + bool t_rpc_command_executor::flush_txpool(const std::string &txid) { cryptonote::COMMAND_RPC_FLUSH_TRANSACTION_POOL::request req; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index df2894d09..4622609ae 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -125,21 +125,19 @@ public: bool set_limit(int64_t limit_down, int64_t limit_up); - bool out_peers(uint64_t limit); + bool out_peers(bool set, uint32_t limit); - bool in_peers(uint64_t limit); + bool in_peers(bool set, uint32_t limit); - bool start_save_graph(); - - bool stop_save_graph(); - bool hard_fork_info(uint8_t version); bool print_bans(); - bool ban(const std::string &ip, time_t seconds); + bool ban(const std::string &address, time_t seconds); - bool unban(const std::string &ip); + bool unban(const std::string &address); + + bool banned(const std::string &address); bool flush_txpool(const std::string &txid); diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt index 7bc2c324f..03c2b3e20 100644 --- a/src/debug_utilities/CMakeLists.txt +++ b/src/debug_utilities/CMakeLists.txt @@ -69,3 +69,25 @@ set_property(TARGET object_sizes PROPERTY OUTPUT_NAME "monero-utils-object-sizes") + +set(dns_checks_sources + dns_checks.cpp + ) + +monero_add_executable(dns_checks + ${dns_checks_sources} + ${dns_checks_private_headers}) + +target_link_libraries(dns_checks + LINK_PRIVATE + common + epee + version + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) + +set_property(TARGET dns_checks + PROPERTY + OUTPUT_NAME "monero-utils-dns-checks") + diff --git a/src/debug_utilities/dns_checks.cpp b/src/debug_utilities/dns_checks.cpp new file mode 100644 index 000000000..3c9daa769 --- /dev/null +++ b/src/debug_utilities/dns_checks.cpp @@ -0,0 +1,149 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string> +#include <vector> +#include <map> +#include <algorithm> +#include <boost/program_options.hpp> +#include "misc_log_ex.h" +#include "common/util.h" +#include "common/command_line.h" +#include "common/dns_utils.h" +#include "version.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "debugtools.dnschecks" + +namespace po = boost::program_options; + +enum lookup_t { LOOKUP_A, LOOKUP_TXT }; + +static std::vector<std::string> lookup(lookup_t type, const char *hostname) +{ + bool dnssec_available = false, dnssec_valid = false; + std::vector<std::string> res; + switch (type) + { + case LOOKUP_A: res = tools::DNSResolver::instance().get_ipv4(hostname, dnssec_available, dnssec_valid); break; + case LOOKUP_TXT: res = tools::DNSResolver::instance().get_txt_record(hostname, dnssec_available, dnssec_valid); break; + default: MERROR("Invalid lookup type: " << (int)type); return {}; + } + if (!dnssec_available) + { + MWARNING("No DNSSEC for " << hostname); + return {}; + } + if (!dnssec_valid) + { + MWARNING("Invalid DNSSEC check for " << hostname); + return {}; + } + MINFO(res.size() << " valid signed result(s) for " << hostname); + return res; +} + +static void lookup(lookup_t type, const std::vector<std::string> hostnames) +{ + std::vector<std::vector<std::string>> results; + for (const std::string &hostname: hostnames) + { + auto res = lookup(type, hostname.c_str()); + if (!res.empty()) + { + std::sort(res.begin(), res.end()); + results.push_back(res); + } + } + std::map<std::vector<std::string>, size_t> counter; + for (const auto &e: results) + counter[e]++; + size_t count = 0; + for (const auto &e: counter) + count = std::max(count, e.second); + if (results.size() > 1) + { + if (count < results.size()) + MERROR("Only " << count << "/" << results.size() << " records match"); + else + MINFO(count << "/" << results.size() << " records match"); + } +} + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + + tools::on_startup(); + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + + command_line::add_arg(desc_cmd_only, command_line::arg_help); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + po::store(po::parse_command_line(argc, argv, desc_options), vm); + po::notify(vm); + return true; + }); + if (! r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << desc_options << std::endl; + return 1; + } + + mlog_configure("", true); + mlog_set_categories("+" MONERO_DEFAULT_LOG_CATEGORY ":INFO"); + + lookup(LOOKUP_A, {"seeds.moneroseeds.se", "seeds.moneroseeds.ae.org", "seeds.moneroseeds.ch", "seeds.moneroseeds.li"}); + + lookup(LOOKUP_TXT, {"updates.moneropulse.org", "updates.moneropulse.net", "updates.moneropulse.co", "updates.moneropulse.se"}); + + lookup(LOOKUP_TXT, {"checkpoints.moneropulse.org", "checkpoints.moneropulse.net", "checkpoints.moneropulse.co", "checkpoints.moneropulse.se"}); + + // those are in the code, but don't seem to actually exist +#if 0 + lookup(LOOKUP_TXT, {"testpoints.moneropulse.org", "testpoints.moneropulse.net", "testpoints.moneropulse.co", "testpoints.moneropulse.se"); + + lookup(LOOKUP_TXT, {"stagenetpoints.moneropulse.org", "stagenetpoints.moneropulse.net", "stagenetpoints.moneropulse.co", "stagenetpoints.moneropulse.se"}); +#endif + + lookup(LOOKUP_TXT, {"segheights.moneropulse.org", "segheights.moneropulse.net", "segheights.moneropulse.co", "segheights.moneropulse.se"}); + + return 0; + CATCH_ENTRY_L0("main", 1); +} diff --git a/src/net/error.h b/src/net/error.h index c8338f7e2..7c852dd20 100644 --- a/src/net/error.h +++ b/src/net/error.h @@ -42,7 +42,8 @@ namespace net invalid_i2p_address, invalid_port, //!< Outside of 0-65535 range invalid_tor_address,//!< Invalid base32 or length - unsupported_address //!< Type not supported by `get_network_address` + unsupported_address,//!< Type not supported by `get_network_address` + invalid_mask, //!< Outside of 0-32 range }; //! \return `std::error_category` for `net` namespace. diff --git a/src/net/parse.cpp b/src/net/parse.cpp index eaaadb67e..d93d7d352 100644 --- a/src/net/parse.cpp +++ b/src/net/parse.cpp @@ -58,4 +58,27 @@ namespace net return {epee::net_utils::ipv4_network_address{ip, port}}; return make_error_code(net::error::unsupported_address); } + + expect<epee::net_utils::ipv4_network_subnet> + get_ipv4_subnet_address(const boost::string_ref address, bool allow_implicit_32) + { + uint32_t mask = 32; + const boost::string_ref::size_type slash = address.find_first_of('/'); + if (slash != boost::string_ref::npos) + { + if (!epee::string_tools::get_xtype_from_string(mask, std::string{address.substr(slash + 1)})) + return make_error_code(net::error::invalid_mask); + if (mask > 32) + return make_error_code(net::error::invalid_mask); + } + else if (!allow_implicit_32) + return make_error_code(net::error::invalid_mask); + + std::uint32_t ip = 0; + boost::string_ref S(address.data(), slash != boost::string_ref::npos ? slash : address.size()); + if (!epee::string_tools::get_ip_int32_from_string(ip, std::string(S))) + return make_error_code(net::error::invalid_host); + + return {epee::net_utils::ipv4_network_subnet{ip, (uint8_t)mask}}; + } } diff --git a/src/net/parse.h b/src/net/parse.h index 5804c4128..9f0d66ea6 100644 --- a/src/net/parse.h +++ b/src/net/parse.h @@ -50,5 +50,18 @@ namespace net */ expect<epee::net_utils::network_address> get_network_address(boost::string_ref address, std::uint16_t default_port); + + /*! + Identifies an IPv4 subnet in CIDR notatioa and returns it as a generic + `network_address`. If the type is unsupported, it might be a hostname, + and `error() == net::error::kUnsupportedAddress` is returned. + + \param address An ipv4 address. + \param allow_implicit_32 whether to accept "raw" IPv4 addresses, with CIDR notation + + \return A tor or IPv4 address, else error. + */ + expect<epee::net_utils::ipv4_network_subnet> + get_ipv4_subnet_address(boost::string_ref address, bool allow_implicit_32 = false); } diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index fcbcce58c..b55003d75 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -143,8 +143,6 @@ namespace nodetool const command_line::arg_descriptor<int64_t> arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN}; const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; - const command_line::arg_descriptor<bool> arg_save_graph = {"save-graph", "Save data for dr monero", false}; - boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm) { namespace ip = boost::asio::ip; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 42bb3b061..5d9e19d5d 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -216,7 +216,6 @@ namespace nodetool m_hide_my_port(false), m_no_igd(false), m_offline(false), - m_save_graph(false), is_closing(false), m_network_id() {} @@ -245,10 +244,16 @@ namespace nodetool size_t get_zone_count() const { return m_network_zones.size(); } void change_max_out_public_peers(size_t count); + uint32_t get_max_out_public_peers() const; void change_max_in_public_peers(size_t count); + uint32_t get_max_in_public_peers() const; virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME); virtual bool unblock_host(const epee::net_utils::network_address &address); - virtual std::map<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } + virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME); + virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet); + virtual bool is_host_blocked(const epee::net_utils::network_address &address, time_t *seconds) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return !is_remote_host_allowed(address, seconds); } + virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } + virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_subnets; } virtual void add_used_stripe_peer(const typename t_payload_net_handler::connection_context &context); virtual void remove_used_stripe_peer(const typename t_payload_net_handler::connection_context &context); @@ -319,7 +324,7 @@ namespace nodetool virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f); virtual bool add_host_fail(const epee::net_utils::network_address &address); //----------------- i_connection_filter -------------------------------------------------------- - virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address); + virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL); //----------------------------------------------------------------------------------------------- bool parse_peer_from_string(epee::net_utils::network_address& pe, const std::string& node_addr, uint16_t default_port = 0); bool handle_command_line( @@ -396,12 +401,6 @@ namespace nodetool public: - void set_save_graph(bool save_graph) - { - m_save_graph = save_graph; - epee::net_utils::connection_basic::set_save_graph(save_graph); - } - void set_rpc_port(uint16_t rpc_port) { m_rpc_port = rpc_port; @@ -419,7 +418,6 @@ namespace nodetool bool m_hide_my_port; bool m_no_igd; bool m_offline; - std::atomic<bool> m_save_graph; std::atomic<bool> is_closing; std::unique_ptr<boost::thread> mPeersLoggerThread; //critical_section m_connections_lock; @@ -461,8 +459,9 @@ namespace nodetool std::map<epee::net_utils::network_address, time_t> m_conn_fails_cache; epee::critical_section m_conn_fails_cache_lock; - epee::critical_section m_blocked_hosts_lock; - std::map<std::string, time_t> m_blocked_hosts; + epee::critical_section m_blocked_hosts_lock; // for both hosts and subnets + std::map<epee::net_utils::network_address, time_t> m_blocked_hosts; + std::map<epee::net_utils::ipv4_network_subnet, time_t> m_blocked_subnets; epee::critical_section m_host_fails_score_lock; std::map<std::string, uint64_t> m_host_fails_score; @@ -500,8 +499,6 @@ namespace nodetool extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up; extern const command_line::arg_descriptor<int64_t> arg_limit_rate_down; extern const command_line::arg_descriptor<int64_t> arg_limit_rate; - - extern const command_line::arg_descriptor<bool> arg_save_graph; } POP_WARNINGS diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index a5800df5e..ba7326286 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -111,7 +111,6 @@ namespace nodetool command_line::add_arg(desc, arg_limit_rate_up); command_line::add_arg(desc, arg_limit_rate_down); command_line::add_arg(desc, arg_limit_rate); - command_line::add_arg(desc, arg_save_graph); } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -155,19 +154,55 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address) + bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); - auto it = m_blocked_hosts.find(address.host_str()); - if(it == m_blocked_hosts.end()) - return true; - if(time(nullptr) >= it->second) + + const time_t now = time(nullptr); + + // look in the hosts list + auto it = m_blocked_hosts.find(address); + if (it != m_blocked_hosts.end()) { - m_blocked_hosts.erase(it); - MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked."); - return true; + if (now >= it->second) + { + m_blocked_hosts.erase(it); + MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked."); + it = m_blocked_hosts.end(); + } + else + { + if (t) + *t = it->second - now; + return false; + } } - return false; + + // manually loop in subnets + if (address.get_type_id() == epee::net_utils::address_type::ipv4) + { + auto ipv4_address = address.template as<epee::net_utils::ipv4_network_address>(); + std::map<epee::net_utils::ipv4_network_subnet, time_t>::iterator it; + for (it = m_blocked_subnets.begin(); it != m_blocked_subnets.end(); ) + { + if (now >= it->second) + { + it = m_blocked_subnets.erase(it); + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << it->first.host_str() << " unblocked."); + continue; + } + if (it->first.matches(ipv4_address)) + { + if (t) + *t = it->second - now; + return false; + } + ++it; + } + } + + // not found in hosts or subnets, allowed + return true; } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -184,7 +219,7 @@ namespace nodetool limit = std::numeric_limits<time_t>::max(); else limit = now + seconds; - m_blocked_hosts[addr.host_str()] = limit; + m_blocked_hosts[addr] = limit; // drop any connection to that address. This should only have to look into // the zone related to the connection, but really make sure everything is @@ -214,7 +249,7 @@ namespace nodetool bool node_server<t_payload_net_handler>::unblock_host(const epee::net_utils::network_address &address) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); - auto i = m_blocked_hosts.find(address.host_str()); + auto i = m_blocked_hosts.find(address); if (i == m_blocked_hosts.end()) return false; m_blocked_hosts.erase(i); @@ -223,6 +258,58 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds) + { + const time_t now = time(nullptr); + + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + time_t limit; + if (now > std::numeric_limits<time_t>::max() - seconds) + limit = std::numeric_limits<time_t>::max(); + else + limit = now + seconds; + m_blocked_subnets[subnet] = limit; + + // drop any connection to that subnet. This should only have to look into + // the zone related to the connection, but really make sure everything is + // swept ... + std::vector<boost::uuids::uuid> conns; + for(auto& zone : m_network_zones) + { + zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if (cntxt.m_remote_address.get_type_id() != epee::net_utils::ipv4_network_address::get_type_id()) + return true; + auto ipv4_address = cntxt.m_remote_address.template as<epee::net_utils::ipv4_network_address>(); + if (subnet.matches(ipv4_address)) + { + conns.push_back(cntxt.m_connection_id); + } + return true; + }); + for (const auto &c: conns) + zone.second.m_net_server.get_config_object().close(c); + + conns.clear(); + } + + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " blocked."); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet) + { + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + auto i = m_blocked_subnets.find(subnet); + if (i == m_blocked_subnets.end()) + return false; + m_blocked_subnets.erase(i); + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " unblocked."); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::add_host_fail(const epee::net_utils::network_address &address) { if(!address.is_blockable()) @@ -292,11 +379,6 @@ namespace nodetool } } - if(command_line::has_arg(vm, arg_save_graph)) - { - set_save_graph(true); - } - if (command_line::has_arg(vm,arg_p2p_add_exclusive_node)) { if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) @@ -944,7 +1026,10 @@ namespace nodetool } if(!context.m_is_income) m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port); - m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false); + if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false)) + { + m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id ); + } }); if(!r) @@ -1090,6 +1175,7 @@ namespace nodetool LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str() /*<< ", try " << try_count*/); + zone.m_net_server.get_config_object().close(con->m_connection_id); return false; } @@ -1149,7 +1235,7 @@ namespace nodetool bool is_priority = is_priority_node(na); LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str()); - + zone.m_net_server.get_config_object().close(con->m_connection_id); return false; } @@ -2251,6 +2337,15 @@ namespace nodetool } template<class t_payload_net_handler> + uint32_t node_server<t_payload_net_handler>::get_max_out_public_peers() const + { + const auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); + if (public_zone == m_network_zones.end()) + return 0; + return public_zone->second.m_config.m_net_config.max_out_connection_count; + } + + template<class t_payload_net_handler> void node_server<t_payload_net_handler>::change_max_in_public_peers(size_t count) { auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); @@ -2264,6 +2359,15 @@ namespace nodetool } template<class t_payload_net_handler> + uint32_t node_server<t_payload_net_handler>::get_max_in_public_peers() const + { + const auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); + if (public_zone == m_network_zones.end()) + return 0; + return public_zone->second.m_config.m_net_config.max_in_connection_count; + } + + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::set_tos_flag(const boost::program_options::variables_map& vm, int flag) { if(flag==-1){ diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 26451b333..34d151f5f 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -56,7 +56,8 @@ namespace nodetool virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0; virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds = 0)=0; virtual bool unblock_host(const epee::net_utils::network_address &address)=0; - virtual std::map<std::string, time_t> get_blocked_hosts()=0; + virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()=0; + virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()=0; virtual bool add_host_fail(const epee::net_utils::network_address &address)=0; virtual void add_used_stripe_peer(const t_connection_context &context)=0; virtual void remove_used_stripe_peer(const t_connection_context &context)=0; @@ -112,9 +113,13 @@ namespace nodetool { return true; } - virtual std::map<std::string, time_t> get_blocked_hosts() + virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { - return std::map<std::string, time_t>(); + return std::map<epee::net_utils::network_address, time_t>(); + } + virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() + { + return std::map<epee::net_utils::ipv4_network_subnet, time_t>(); } virtual bool add_host_fail(const epee::net_utils::network_address &address) { diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 6f77fed34..f69b4a12c 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -447,7 +447,6 @@ rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<str { CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small"); MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000)); - bool HiGi = cache != NULL; STEP = STEP ? STEP : 192; MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index f01e683cb..2c4e5fc3b 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -190,7 +190,6 @@ namespace rct { int byte, i, j; for (j = 0; j < 8; j++) { byte = 0; - i = 8 * j; for (i = 7; i > -1; i--) { byte = byte * 2 + amountb2[8 * j + i]; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b6836e636..f41ba6c37 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1476,7 +1476,7 @@ namespace cryptonote ok = ok && getheight_res.status == CORE_RPC_STATUS_OK; m_should_use_bootstrap_daemon = ok && top_height + 10 < getheight_res.height; - MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << getheight_res.height << ")"); + MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << (ok ? getheight_res.height : 0) << ")"); } if (!m_should_use_bootstrap_daemon) return false; @@ -1772,20 +1772,60 @@ namespace cryptonote PERF_TIMER(on_get_bans); auto now = time(nullptr); - std::map<std::string, time_t> blocked_hosts = m_p2p.get_blocked_hosts(); - for (std::map<std::string, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i) + std::map<epee::net_utils::network_address, time_t> blocked_hosts = m_p2p.get_blocked_hosts(); + for (std::map<epee::net_utils::network_address, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i) { if (i->second > now) { COMMAND_RPC_GETBANS::ban b; - b.host = i->first; + b.host = i->first.host_str(); b.ip = 0; uint32_t ip; - if (epee::string_tools::get_ip_int32_from_string(ip, i->first)) + if (epee::string_tools::get_ip_int32_from_string(ip, b.host)) b.ip = ip; b.seconds = i->second - now; res.bans.push_back(b); } } + std::map<epee::net_utils::ipv4_network_subnet, time_t> blocked_subnets = m_p2p.get_blocked_subnets(); + for (std::map<epee::net_utils::ipv4_network_subnet, time_t>::const_iterator i = blocked_subnets.begin(); i != blocked_subnets.end(); ++i) + { + if (i->second > now) { + COMMAND_RPC_GETBANS::ban b; + b.host = i->first.host_str(); + b.ip = 0; + b.seconds = i->second - now; + res.bans.push_back(b); + } + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) + { + PERF_TIMER(on_banned); + + auto na_parsed = net::get_network_address(req.address, 0); + if (!na_parsed) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Unsupported host type"; + return false; + } + epee::net_utils::network_address na = std::move(*na_parsed); + + time_t seconds; + if (m_p2p.is_host_blocked(na, &seconds)) + { + res.banned = true; + res.seconds = seconds; + } + else + { + res.banned = false; + res.seconds = 0; + } res.status = CORE_RPC_STATUS_OK; return true; @@ -1798,13 +1838,29 @@ namespace cryptonote for (auto i = req.bans.begin(); i != req.bans.end(); ++i) { epee::net_utils::network_address na; + + // try subnet first + if (!i->host.empty()) + { + auto ns_parsed = net::get_ipv4_subnet_address(i->host); + if (ns_parsed) + { + if (i->ban) + m_p2p.block_subnet(*ns_parsed, i->seconds); + else + m_p2p.unblock_subnet(*ns_parsed); + continue; + } + } + + // then host if (!i->host.empty()) { auto na_parsed = net::get_network_address(i->host, 0); if (!na_parsed) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; - error_resp.message = "Unsupported host type"; + error_resp.message = "Unsupported host/subnet type"; return false; } na = std::move(*na_parsed); @@ -2039,7 +2095,9 @@ namespace cryptonote bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx) { PERF_TIMER(on_out_peers); - m_p2p.change_max_out_public_peers(req.out_peers); + if (req.set) + m_p2p.change_max_out_public_peers(req.out_peers); + res.out_peers = m_p2p.get_max_out_public_peers(); res.status = CORE_RPC_STATUS_OK; return true; } @@ -2047,27 +2105,13 @@ namespace cryptonote bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx) { PERF_TIMER(on_in_peers); - m_p2p.change_max_in_public_peers(req.in_peers); + if (req.set) + m_p2p.change_max_in_public_peers(req.in_peers); + res.in_peers = m_p2p.get_max_in_public_peers(); res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx) - { - PERF_TIMER(on_start_save_graph); - m_p2p.set_save_graph(true); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx) - { - PERF_TIMER(on_stop_save_graph); - m_p2p.set_save_graph(false); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx) { PERF_TIMER(on_update); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index e4683bbe2..7c9bb6dc4 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -123,8 +123,6 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted) MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted) - MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) - MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted) MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION) @@ -154,6 +152,7 @@ namespace cryptonote MAP_JON_RPC_WE("hard_fork_info", on_hard_fork_info, COMMAND_RPC_HARD_FORK_INFO) MAP_JON_RPC_WE_IF("set_bans", on_set_bans, COMMAND_RPC_SETBANS, !m_restricted) MAP_JON_RPC_WE_IF("get_bans", on_get_bans, COMMAND_RPC_GETBANS, !m_restricted) + MAP_JON_RPC_WE_IF("banned", on_banned, COMMAND_RPC_BANNED, !m_restricted) MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted) MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM) MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION) @@ -198,8 +197,6 @@ namespace cryptonote bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx = NULL); bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx = NULL); bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx = NULL); - bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx = NULL); - bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx = NULL); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx = NULL); bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx = NULL); bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx = NULL); @@ -220,6 +217,7 @@ namespace cryptonote bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); + bool on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index cfe4bbf23..53c975392 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -84,7 +84,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 6 +#define CORE_RPC_VERSION_MINOR 7 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -1682,8 +1682,10 @@ namespace cryptonote { struct request_t { - uint64_t out_peers; + bool set; + uint32_t out_peers; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(set, true) KV_SERIALIZE(out_peers) END_KV_SERIALIZE_MAP() }; @@ -1691,9 +1693,11 @@ namespace cryptonote struct response_t { + uint32_t out_peers; std::string status; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(out_peers) KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; @@ -1704,8 +1708,10 @@ namespace cryptonote { struct request_t { - uint64_t in_peers; + bool set; + uint32_t in_peers; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(set, true) KV_SERIALIZE(in_peers) END_KV_SERIALIZE_MAP() }; @@ -1713,55 +1719,17 @@ namespace cryptonote struct response_t { + uint32_t in_peers; std::string status; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(in_peers) KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; }; - struct COMMAND_RPC_START_SAVE_GRAPH - { - struct request_t - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - struct COMMAND_RPC_STOP_SAVE_GRAPH - { - struct request_t - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - struct COMMAND_RPC_HARD_FORK_INFO { struct request_t @@ -1876,6 +1844,33 @@ namespace cryptonote typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_BANNED + { + struct request_t + { + std::string address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + std::string status; + bool banned; + uint32_t seconds; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(banned) + KV_SERIALIZE(seconds) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + struct COMMAND_RPC_FLUSH_TRANSACTION_POOL { struct request_t diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 02a099811..e8b203d9d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -92,6 +92,8 @@ typedef cryptonote::simple_wallet sw; #define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol +#define OLD_AGE_WARN_THRESHOLD (30 * 86400 / DIFFICULTY_TARGET_V2) // 30 days + #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ @@ -5608,6 +5610,43 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector) +{ + // count the number of old outputs + std::string err; + uint64_t bc_height = get_daemon_blockchain_height(err); + if (!err.empty()) + return true; + + int max_n_old = 0; + for (const auto &ptx: ptx_vector) + { + int n_old = 0; + for (const auto i: ptx.selected_transfers) + { + const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); + uint64_t age = bc_height - td.m_block_height; + if (age > OLD_AGE_WARN_THRESHOLD) + ++n_old; + } + max_n_old = std::max(max_n_old, n_old); + } + if (max_n_old > 1) + { + std::stringstream prompt; + prompt << tr("Transaction spends more than one very old output. Privacy would be better if they were sent separately."); + prompt << ENDL << tr("Spend them now anyway?"); + std::string accepted = input_line(prompt.str(), true); + if (std::cin.eof()) + return false; + if (!command_line::is_yes(accepted)) + { + return false; + } + } + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms) { // "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]" @@ -5909,6 +5948,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } } + if (!prompt_if_old(ptx_vector)) + { + fail_msg_writer() << tr("transaction cancelled."); + return false; + } + // if more than one tx necessary, prompt user to confirm if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1) { @@ -6092,7 +6137,8 @@ bool simple_wallet::locked_transfer(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_) { - return sweep_main(0, true, args_); + sweep_main(0, true, args_); + return true; } //---------------------------------------------------------------------------------------------------- @@ -6412,6 +6458,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st return true; } + if (!prompt_if_old(ptx_vector)) + { + fail_msg_writer() << tr("transaction cancelled."); + return false; + } + // give user total and fee, and prompt to confirm uint64_t total_fee = 0, total_sent = 0; for (size_t n = 0; n < ptx_vector.size(); ++n) @@ -6758,7 +6810,8 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { - return sweep_main(0, false, args_); + sweep_main(0, false, args_); + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_below(const std::vector<std::string> &args_) @@ -6774,7 +6827,8 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_) fail_msg_writer() << tr("invalid amount threshold"); return true; } - return sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end())); + sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end())); + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector<std::string> &args_) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 33b18612c..4bf7fa334 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -261,6 +261,7 @@ namespace cryptonote void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const; bool freeze_thaw(const std::vector<std::string>& args, bool freeze); + bool prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector); struct transfer_view { diff --git a/src/version.cpp.in b/src/version.cpp.in index 8aaa41b19..28ce38df7 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.14.1.0" +#define DEF_MONERO_VERSION "0.14.1.2" #define DEF_MONERO_RELEASE_NAME "Boron Butterfly" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 1711db482..5902eef4b 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1697,6 +1697,17 @@ void WalletImpl::setDefaultMixin(uint32_t arg) m_wallet->default_mixin(arg); } +bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val) +{ + m_wallet->set_attribute(key, val); + return true; +} + +std::string WalletImpl::getCacheAttribute(const std::string &key) const +{ + return m_wallet->get_attribute(key); +} + bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) { cryptonote::blobdata txid_data; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 9e07b6e19..5dfd997a1 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -168,6 +168,10 @@ public: virtual void setListener(WalletListener * l) override; virtual uint32_t defaultMixin() const override; virtual void setDefaultMixin(uint32_t arg) override; + + virtual bool setCacheAttribute(const std::string &key, const std::string &val) override; + virtual std::string getCacheAttribute(const std::string &key) const override; + virtual bool setUserNote(const std::string &txid, const std::string ¬e) override; virtual std::string getUserNote(const std::string &txid) const override; virtual std::string getTxKey(const std::string &txid) const override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 0af3b1867..f2e99be21 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -891,6 +891,19 @@ struct Wallet virtual void setDefaultMixin(uint32_t arg) = 0; /*! + * \brief setCacheAttribute - attach an arbitrary string to a wallet cache attribute + * \param key - the key + * \param val - the value + * \return true if successful, false otherwise + */ + virtual bool setCacheAttribute(const std::string &key, const std::string &val) = 0; + /*! + * \brief getCacheAttribute - return an arbitrary string attached to a wallet cache attribute + * \param key - the key + * \return the attached string, or empty string if there is none + */ + virtual std::string getCacheAttribute(const std::string &key) const = 0; + /*! * \brief setUserNote - attach an arbitrary string note to a txid * \param txid - the transaction id to attach the note to * \param note - the note diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index ef2ed2015..d589dcc75 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -227,9 +227,6 @@ std::string WalletManagerImpl::errorString() const void WalletManagerImpl::setDaemonAddress(const std::string &address) { - m_daemonAddress = address; - if(m_http_client.is_connected()) - m_http_client.disconnect(); m_http_client.set_server(address, boost::none); } diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 235f96e17..537fc5ba6 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -95,7 +95,6 @@ public: private: WalletManagerImpl() {} friend struct WalletManagerFactory; - std::string m_daemonAddress; epee::net_utils::http::http_simple_client m_http_client; std::string m_errorString; }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 8f3f30da1..308727657 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6931,7 +6931,7 @@ uint64_t wallet2::get_base_fee() const else return m_light_wallet_per_kb_fee; } - bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1); + bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -30 * 1); if (!use_dyn_fee) return FEE_PER_KB; @@ -6962,7 +6962,7 @@ int wallet2::get_fee_algorithm() const return 3; if (use_fork_rules(5, 0)) return 2; - if (use_fork_rules(3, -720 * 14)) + if (use_fork_rules(3, -30 * 14)) return 1; return 0; } @@ -7726,7 +7726,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> uint64_t num_found = 0; // if we have a known ring, use it - bool existing_ring_found = false; if (td.m_key_image_known && !td.m_key_image_partial) { std::vector<uint64_t> ring; @@ -7738,7 +7737,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> std::to_string(ring.size()) + ", it cannot be spent now with ring size " + std::to_string(fake_outputs_count + 1) + " as it is smaller: use a higher ring size"); bool own_found = false; - existing_ring_found = true; for (const auto &out: ring) { MINFO("Ring has output " << out); @@ -7984,7 +7982,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask)); // then pick outs from an existing ring, if any - bool existing_ring_found = false; if (td.m_key_image_known && !td.m_key_image_partial) { std::vector<uint64_t> ring; @@ -11387,12 +11384,13 @@ void wallet2::set_attribute(const std::string &key, const std::string &value) m_attributes[key] = value; } -std::string wallet2::get_attribute(const std::string &key) const +bool wallet2::get_attribute(const std::string &key, std::string &value) const { std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(key); if (i == m_attributes.end()) - return std::string(); - return i->second; + return false; + value = i->second; + return true; } void wallet2::set_description(const std::string &description) @@ -11402,7 +11400,10 @@ void wallet2::set_description(const std::string &description) std::string wallet2::get_description() const { - return get_attribute(ATTRIBUTE_DESCRIPTION); + std::string s; + if (get_attribute(ATTRIBUTE_DESCRIPTION, s)) + return s; + return ""; } const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags() diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 921c150cb..30589885e 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1247,7 +1247,7 @@ private: */ const char* const ATTRIBUTE_DESCRIPTION = "wallet2.description"; void set_attribute(const std::string &key, const std::string &value); - std::string get_attribute(const std::string &key) const; + bool get_attribute(const std::string &key, std::string &value) const; crypto::public_key get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const; crypto::public_key get_multisig_signer_public_key() const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 0e2a34470..9d3605d11 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1811,14 +1811,12 @@ namespace tools wallet2::transfer_container transfers; m_wallet->get_transfers(transfers); - bool transfers_found = false; for (const auto& td : transfers) { if (!filter || available != td.m_spent) { if (req.account_index != td.m_subaddr_index.major || (!req.subaddr_indices.empty() && req.subaddr_indices.count(td.m_subaddr_index.minor) == 0)) continue; - transfers_found = true; wallet_rpc::transfer_details rpc_transfers; rpc_transfers.amount = td.amount(); rpc_transfers.spent = td.m_spent; @@ -2104,7 +2102,12 @@ namespace tools return false; } - res.value = m_wallet->get_attribute(req.key); + if (!m_wallet->get_attribute(req.key, res.value)) + { + er.code = WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND; + er.message = "Attribute not found."; + return false; + } return true; } bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx) diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 011d146d4..9434fbc3e 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -75,3 +75,4 @@ #define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42 #define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC -43 #define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL -44 +#define WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND -45 |