diff options
Diffstat (limited to 'src')
34 files changed, 450 insertions, 429 deletions
diff --git a/src/cryptonote_basic/cryptonote_stat_info.h b/src/blockchain_db/locked_txn.h index 4cc1bc764..d14631ddb 100644 --- a/src/cryptonote_basic/cryptonote_stat_info.h +++ b/src/blockchain_db/locked_txn.h @@ -1,21 +1,21 @@ // Copyright (c) 2014-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 @@ -25,30 +25,29 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +// // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once -#include "serialization/keyvalue_serialization.h" - namespace cryptonote { - struct core_stat_info_t - { - uint64_t tx_pool_size; - uint64_t blockchain_height; - uint64_t mining_speed; - uint64_t alternative_blocks; - std::string top_block_id_str; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_pool_size) - KV_SERIALIZE(blockchain_height) - KV_SERIALIZE(mining_speed) - KV_SERIALIZE(alternative_blocks) - KV_SERIALIZE(top_block_id_str) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<core_stat_info_t> core_stat_info; + // This class is meant to create a batch when none currently exists. + // If a batch exists, it can't be from another thread, since we can + // only be called with the txpool lock taken, and it is held during + // the whole prepare/handle/cleanup incoming block sequence. + class LockedTXN { + public: + LockedTXN(BlockchainDB &db): m_db(db), m_batch(false), m_active(false) { + m_batch = m_db.batch_start(); + m_active = true; + } + void commit() { try { if (m_batch && m_active) { m_db.batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } } + void abort() { try { if (m_batch && m_active) { m_db.batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } } + ~LockedTXN() { abort(); } + private: + BlockchainDB &m_db; + bool m_batch; + bool m_active; + }; } diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 18204eeee..753bf238c 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -71,6 +71,7 @@ void threadpool::recycle() { } void threadpool::create(unsigned int max_threads) { + const boost::unique_lock<boost::mutex> lock(mutex); boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); max = max_threads ? max_threads : tools::get_max_concurrency(); diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c index 969bf8a0f..606c67336 100644 --- a/src/crypto/rx-slow-hash.c +++ b/src/crypto/rx-slow-hash.c @@ -284,6 +284,10 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch CTHR_MUTEX_LOCK(rx_dataset_mutex); if (rx_dataset != NULL && rx_dataset_height != seedheight) rx_initdata(cache, miners, seedheight); + else if (rx_dataset == NULL) { + /* this is a no-op if the cache hasn't changed */ + randomx_vm_set_cache(rx_vm, rx_sp->rs_cache); + } CTHR_MUTEX_UNLOCK(rx_dataset_mutex); } else { /* this is a no-op if the cache hasn't changed */ diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index 5bb56e083..59040d8a2 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -54,7 +54,6 @@ set(cryptonote_basic_private_headers cryptonote_basic_impl.h cryptonote_boost_serialization.h cryptonote_format_utils.h - cryptonote_stat_info.h difficulty.h hardfork.h miner.h diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h index f5f663464..4d49b692c 100644 --- a/src/cryptonote_basic/verification_context.h +++ b/src/cryptonote_basic/verification_context.h @@ -47,7 +47,6 @@ namespace cryptonote bool m_too_big; bool m_overspend; bool m_fee_too_low; - bool m_not_rct; bool m_too_few_outputs; }; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 134b630f7..4598baad7 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -144,8 +144,6 @@ #define RPC_IP_FAILS_BEFORE_BLOCK 3 -#define ALLOW_DEBUG_COMMANDS - #define CRYPTONOTE_NAME "bitmonero" #define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" #define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb" @@ -193,7 +191,6 @@ namespace config uint8_t const FEE_CALCULATION_MAX_RETRIES = 10; uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)2000000000); // 2 * pow(10, 9) uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8) - std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000"; uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18; uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index a8683a531..5578f519e 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3423,7 +3423,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const if (version >= HF_VERSION_PER_BYTE_FEE) { const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; - uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version); + uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? std::min<uint64_t>(median, m_long_term_effective_median_block_weight) : median, version); MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee"); needed_fee = tx_weight * fee_per_byte; // quantize fee up to 8 decimals @@ -3481,7 +3481,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0; uint64_t base_reward; - if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) + if (!get_block_reward(m_current_block_cumul_weight_limit / 2, 1, already_generated_coins, base_reward, version)) { MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound"); base_reward = BLOCK_REWARD_OVERESTIMATE; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 8b1613b4b..212616af8 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1056,17 +1056,6 @@ namespace cryptonote return handle_incoming_txs({std::addressof(tx_blob), 1}, {std::addressof(tvc), 1}, tx_relay, relayed); } //----------------------------------------------------------------------------------------------- - bool core::get_stat_info(core_stat_info& st_inf) const - { - st_inf.mining_speed = m_miner.get_speed(); - st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); - st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - st_inf.tx_pool_size = m_mempool.get_transactions_count(); - st_inf.top_block_id_str = epee::string_tools::pod_to_hex(m_blockchain_storage.get_tail_id()); - return true; - } - - //----------------------------------------------------------------------------------------------- bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) const { if(!tx.vin.size()) @@ -1946,6 +1935,12 @@ namespace cryptonote { m_blockchain_storage.flush_invalid_blocks(); } + //----------------------------------------------------------------------------------------------- + bool core::get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) + { + return m_mempool.get_complement(hashes, txes); + } + //----------------------------------------------------------------------------------------------- bool core::update_blockchain_pruning() { return m_blockchain_storage.update_blockchain_pruning(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 5dec890e8..79a846de1 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -45,7 +45,6 @@ #include "blockchain.h" #include "cryptonote_basic/miner.h" #include "cryptonote_basic/connection_context.h" -#include "cryptonote_basic/cryptonote_stat_info.h" #include "warnings.h" #include "crypto/hash.h" #include "span.h" @@ -556,15 +555,6 @@ namespace cryptonote bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const; /** - * @brief gets some stats about the daemon - * - * @param st_inf return-by-reference container for the stats requested - * - * @return true - */ - bool get_stat_info(core_stat_info& st_inf) const; - - /** * @copydoc Blockchain::get_tx_outputs_gindexs * * @note see Blockchain::get_tx_outputs_gindexs @@ -864,6 +854,15 @@ namespace cryptonote */ void flush_invalid_blocks(); + /** + * @brief returns the set of transactions in the txpool which are not in the argument + * + * @param hashes hashes of transactions to exclude from the result + * + * @return true iff success, false otherwise + */ + bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes); + private: /** diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 1bc475879..53a6ce579 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -38,6 +38,7 @@ #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_config.h" #include "blockchain.h" +#include "blockchain_db/locked_txn.h" #include "blockchain_db/blockchain_db.h" #include "common/boost_serialization_helper.h" #include "int-util.h" @@ -88,25 +89,6 @@ namespace cryptonote else return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } - - // This class is meant to create a batch when none currently exists. - // If a batch exists, it can't be from another thread, since we can - // only be called with the txpool lock taken, and it is held during - // the whole prepare/handle/cleanup incoming block sequence. - class LockedTXN { - public: - LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false), m_active(false) { - m_batch = m_blockchain.get_db().batch_start(); - m_active = true; - } - void commit() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } } - void abort() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } } - ~LockedTXN() { abort(); } - private: - Blockchain &m_blockchain; - bool m_batch; - bool m_active; - }; } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- @@ -256,7 +238,7 @@ namespace cryptonote if (kept_by_block) m_parsed_tx_cache.insert(std::make_pair(id, tx)); CRITICAL_REGION_LOCAL1(m_blockchain); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); if (!insert_key_images(tx, id, tx_relay)) return false; @@ -301,7 +283,7 @@ namespace cryptonote if (kept_by_block) m_parsed_tx_cache.insert(std::make_pair(id, tx)); CRITICAL_REGION_LOCAL1(m_blockchain); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); m_blockchain.remove_txpool_tx(id); if (!insert_key_images(tx, id, tx_relay)) return false; @@ -362,7 +344,7 @@ namespace cryptonote if (bytes == 0) bytes = m_txpool_max_weight; CRITICAL_REGION_LOCAL1(m_blockchain); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); bool changed = false; // this will never remove the first one, but we don't care @@ -494,7 +476,7 @@ namespace cryptonote try { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(id, meta)) { @@ -549,7 +531,7 @@ namespace cryptonote try { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(txid, meta)) { @@ -594,6 +576,39 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + CRITICAL_REGION_LOCAL1(m_blockchain); + + m_blockchain.for_all_txpool_txes([this, &hashes, &txes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { + const auto relay_method = meta.get_relay_method(); + if (relay_method != relay_method::block && relay_method != relay_method::fluff) + return true; + const auto i = std::find(hashes.begin(), hashes.end(), txid); + if (i == hashes.end()) + { + cryptonote::blobdata bd; + try + { + if (!m_blockchain.get_txpool_tx_blob(txid, bd, cryptonote::relay_category::broadcasted)) + { + MERROR("Failed to get blob for txpool transaction " << txid); + return true; + } + txes.emplace_back(std::move(bd)); + } + catch (const std::exception &e) + { + MERROR("Failed to get blob for txpool transaction " << txid << ": " << e.what()); + return true; + } + } + return true; + }, false); + return true; + } + //--------------------------------------------------------------------------------- void tx_memory_pool::on_idle() { m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();}); @@ -638,7 +653,7 @@ namespace cryptonote if (!remove.empty()) { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for (const std::pair<crypto::hash, uint64_t> &entry: remove) { const crypto::hash &txid = entry.first; @@ -709,7 +724,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const time_t now = time(NULL); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for (const auto& hash : hashes) { try @@ -1186,7 +1201,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); bool changed = false; - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for(size_t i = 0; i!= tx.vin.size(); i++) { CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, void()); @@ -1268,7 +1283,11 @@ namespace cryptonote fee = 0; //baseline empty block - get_block_reward(median_weight, total_weight, already_generated_coins, best_coinbase, version); + if (!get_block_reward(median_weight, total_weight, already_generated_coins, best_coinbase, version)) + { + MERROR("Failed to get block reward for empty block"); + return false; + } size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; @@ -1278,7 +1297,7 @@ namespace cryptonote LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); auto sorted_it = m_txs_by_fee_and_receive_time.begin(); for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it) @@ -1415,7 +1434,7 @@ namespace cryptonote size_t n_removed = 0; if (!remove.empty()) { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for (const crypto::hash &txid: remove) { try @@ -1495,7 +1514,7 @@ namespace cryptonote } if (!remove.empty()) { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for (const auto &txid: remove) { try diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index f716440ad..ca0e50415 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -441,6 +441,11 @@ namespace cryptonote */ bool get_transaction_info(const crypto::hash &txid, tx_details &td) const; + /** + * @brief get transactions not in the passed set + */ + bool get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const; + private: /** diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp index 03cbb5c26..e99982def 100644 --- a/src/cryptonote_core/tx_sanity_check.cpp +++ b/src/cryptonote_core/tx_sanity_check.cpp @@ -28,7 +28,7 @@ #include <stdint.h> #include <vector> -#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "blockchain.h" #include "tx_sanity_check.h" @@ -39,7 +39,7 @@ namespace cryptonote { -bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob) +bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available) { cryptonote::transaction tx; @@ -70,14 +70,18 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob n_indices += in_to_key.key_offsets.size(); } + return tx_sanity_check(rct_indices, n_indices, rct_outs_available); +} + +bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available) +{ if (n_indices <= 10) { MDEBUG("n_indices is only " << n_indices << ", not checking"); return true; } - uint64_t n_available = blockchain.get_num_mature_outputs(0); - if (n_available < 10000) + if (rct_outs_available < 10000) return true; if (rct_indices.size() < n_indices * 8 / 10) @@ -88,9 +92,9 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end()); uint64_t median = epee::misc_utils::median(offsets); - if (median < n_available * 6 / 10) + if (median < rct_outs_available * 6 / 10) { - MERROR("median offset index is too low (median is " << median << " out of total " << n_available << "offsets). Transactions should contain a higher fraction of recent outputs."); + MERROR("median offset index is too low (median is " << median << " out of total " << rct_outs_available << "offsets). Transactions should contain a higher fraction of recent outputs."); return false; } diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h index c12d1b0b1..4a469462f 100644 --- a/src/cryptonote_core/tx_sanity_check.h +++ b/src/cryptonote_core/tx_sanity_check.h @@ -26,11 +26,11 @@ // 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 <set> #include "cryptonote_basic/blobdatatype.h" namespace cryptonote { - class Blockchain; - - bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob); + bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available); + bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available); } diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index ee7e69eb7..f809bff74 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -353,5 +353,23 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init<request_t> request; }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_GET_TXPOOL_COMPLEMENT + { + const static int ID = BC_COMMANDS_POOL_BASE + 10; + + struct request_t + { + std::vector<crypto::hash> hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(hashes) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index ddbd45a61..2664716a8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -45,7 +45,6 @@ #include "block_queue.h" #include "common/perf_timer.h" #include "cryptonote_basic/connection_context.h" -#include "cryptonote_basic/cryptonote_stat_info.h" #include <boost/circular_buffer.hpp> PUSH_WARNINGS @@ -77,7 +76,6 @@ namespace cryptonote { public: typedef cryptonote_connection_context connection_context; - typedef core_stat_info stat_info; typedef t_cryptonote_protocol_handler<t_core> cryptonote_protocol_handler; typedef CORE_SYNC_DATA payload_type; @@ -92,6 +90,7 @@ namespace cryptonote HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) HANDLE_NOTIFY_T2(NOTIFY_NEW_FLUFFY_BLOCK, &cryptonote_protocol_handler::handle_notify_new_fluffy_block) HANDLE_NOTIFY_T2(NOTIFY_REQUEST_FLUFFY_MISSING_TX, &cryptonote_protocol_handler::handle_request_fluffy_missing_tx) + HANDLE_NOTIFY_T2(NOTIFY_GET_TXPOOL_COMPLEMENT, &cryptonote_protocol_handler::handle_notify_get_txpool_complement) END_INVOKE_MAP2() bool on_idle(); @@ -102,7 +101,6 @@ namespace cryptonote bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); bool get_payload_sync_data(blobdata& data); bool get_payload_sync_data(CORE_SYNC_DATA& hshd); - bool get_stat_info(core_stat_info& stat_inf); bool on_callback(cryptonote_connection_context& context); t_core& get_core(){return m_core;} bool is_synchronized(){return m_synchronized;} @@ -127,6 +125,7 @@ namespace cryptonote int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); int handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context); int handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context); + int handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context); //----------------- i_bc_protocol_layout --------------------------------------- virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); @@ -147,6 +146,7 @@ namespace cryptonote int try_add_next_blocks(cryptonote_connection_context &context); void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe); void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const; + bool request_txpool_complement(cryptonote_connection_context &context); t_core& m_core; @@ -156,6 +156,7 @@ namespace cryptonote std::atomic<bool> m_synchronized; std::atomic<bool> m_stopping; std::atomic<bool> m_no_sync; + std::atomic<bool> m_ask_for_txpool_complement; boost::mutex m_sync_lock; block_queue m_block_queue; epee::math_helper::once_a_time_seconds<30> m_idle_peer_kicker; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index d11f198aa..3aacce421 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -83,6 +83,7 @@ namespace cryptonote m_p2p(p_net_layout), m_syncronized_connections_count(0), m_synchronized(offline), + m_ask_for_txpool_complement(true), m_stopping(false), m_no_sync(false) @@ -154,12 +155,6 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> - bool t_cryptonote_protocol_handler<t_core>::get_stat_info(core_stat_info& stat_inf) - { - return m_core.get_stat_info(stat_inf); - } - //------------------------------------------------------------------------------------------------------------------------ - template<class t_core> void t_cryptonote_protocol_handler<t_core>::log_connections() { std::stringstream ss; @@ -344,7 +339,7 @@ namespace cryptonote if(m_core.have_block(hshd.top_id)) { context.m_state = cryptonote_connection_context::state_normal; - if(is_inital && target == m_core.get_current_blockchain_height()) + if(is_inital && hshd.current_height >= target && target == m_core.get_current_blockchain_height()) on_connection_synchronized(); return true; } @@ -885,6 +880,34 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> + int t_cryptonote_protocol_handler<t_core>::handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context) + { + MLOG_P2P_MESSAGE("Received NOTIFY_GET_TXPOOL_COMPLEMENT (" << arg.hashes.size() << " txes)"); + + std::vector<std::pair<cryptonote::blobdata, block>> local_blocks; + std::vector<cryptonote::blobdata> local_txs; + + std::vector<cryptonote::blobdata> txes; + if (!m_core.get_txpool_complement(arg.hashes, txes)) + { + LOG_ERROR_CCONTEXT("failed to get txpool complement"); + return 1; + } + + NOTIFY_NEW_TRANSACTIONS::request new_txes; + new_txes.txs = std::move(txes); + + MLOG_P2P_MESSAGE + ( + "-->>NOTIFY_NEW_TRANSACTIONS: " + << ", txs.size()=" << new_txes.txs.size() + ); + + post_notify<NOTIFY_NEW_TRANSACTIONS>(new_txes, context); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)"); @@ -2201,6 +2224,27 @@ skip: } m_core.safesyncmode(true); m_p2p->clear_used_stripe_peers(); + + // ask for txpool complement from any suitable node if we did not yet + val_expected = true; + if (m_ask_for_txpool_complement.compare_exchange_strong(val_expected, false)) + { + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + { + if(context.m_state < cryptonote_connection_context::state_synchronizing) + { + MDEBUG(context << "not ready, ignoring"); + return true; + } + if (!request_txpool_complement(context)) + { + MERROR(context << "Failed to request txpool complement"); + return true; + } + return false; + }); + } + return true; } //------------------------------------------------------------------------------------------------------------------------ @@ -2354,6 +2398,21 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::request_txpool_complement(cryptonote_connection_context &context) + { + NOTIFY_GET_TXPOOL_COMPLEMENT::request r = {}; + if (!m_core.get_pool_transaction_hashes(r.hashes, false)) + { + MERROR("Failed to get txpool hashes"); + return false; + } + MLOG_P2P_MESSAGE("-->>NOTIFY_GET_TXPOOL_COMPLEMENT: hashes.size()=" << r.hashes.size() ); + post_notify<NOTIFY_GET_TXPOOL_COMPLEMENT>(r, context); + MLOG_PEER_STATE("requesting txpool complement"); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> std::string t_cryptonote_protocol_handler<t_core>::get_peers_overview() const { std::stringstream ss; @@ -2463,7 +2522,10 @@ skip: MINFO("Target height decreasing from " << previous_target << " to " << target); m_core.set_target_blockchain_height(target); if (target == 0 && context.m_state > cryptonote_connection_context::state_before_handshake && !m_stopping) + { MCWARNING("global", "monerod is now disconnected from the network"); + m_ask_for_txpool_complement = true; + } } m_block_queue.flush_spans(context.m_connection_id, false); diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp index cba829d3f..f4cc75fee 100644 --- a/src/net/i2p_address.cpp +++ b/src/net/i2p_address.cpp @@ -171,7 +171,8 @@ namespace net bool i2p_address::less(const i2p_address& rhs) const noexcept { - return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port(); + int res = std::strcmp(host_str(), rhs.host_str()); + return res < 0 || (res == 0 && port() < rhs.port()); } bool i2p_address::is_same_host(const i2p_address& rhs) const noexcept diff --git a/src/net/tor_address.cpp b/src/net/tor_address.cpp index 904a9a0fc..4414861e7 100644 --- a/src/net/tor_address.cpp +++ b/src/net/tor_address.cpp @@ -173,7 +173,8 @@ namespace net bool tor_address::less(const tor_address& rhs) const noexcept { - return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port(); + int res = std::strcmp(host_str(), rhs.host_str()); + return res < 0 || (res == 0 && port() < rhs.port()); } bool tor_address::is_same_host(const tor_address& rhs) const noexcept diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 9b9ffbe2a..31d8aad3f 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -301,8 +301,6 @@ namespace nodetool bool islimitup=false; bool islimitdown=false; - typedef COMMAND_REQUEST_STAT_INFO_T<typename t_payload_net_handler::stat_info> COMMAND_REQUEST_STAT_INFO; - CHAIN_LEVIN_INVOKE_MAP2(p2p_connection_context); //move levin_commands_handler interface invoke(...) callbacks into invoke map CHAIN_LEVIN_NOTIFY_MAP2(p2p_connection_context); //move levin_commands_handler interface notify(...) callbacks into nothing @@ -313,11 +311,6 @@ namespace nodetool HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake) HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync) HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping) -#ifdef ALLOW_DEBUG_COMMANDS - HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) - HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) - HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) -#endif HANDLE_INVOKE_T2(COMMAND_REQUEST_SUPPORT_FLAGS, &node_server::handle_get_support_flags) CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) END_INVOKE_MAP2() @@ -328,17 +321,11 @@ namespace nodetool int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context); int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context); int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context); -#ifdef ALLOW_DEBUG_COMMANDS - int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context); - int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context); - int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context); -#endif int handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context); bool init_config(); bool make_default_peer_id(); bool make_default_config(); bool store_config(); - bool check_trust(const proof_of_trust& tr, epee::net_utils::zone zone_type); //----------------- levin_commands_handler ------------------------------------------------------------- @@ -393,7 +380,7 @@ namespace nodetool bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb); bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f); bool make_expected_connections_count(network_zone& zone, PeerType peer_type, size_t expected_connections); - void cache_connect_fail_info(const epee::net_utils::network_address& addr); + void record_addr_failed(const epee::net_utils::network_address& addr); bool is_addr_recently_failed(const epee::net_utils::network_address& addr); bool is_priority_node(const epee::net_utils::network_address& na); std::set<std::string> get_seed_nodes(cryptonote::network_type nettype) const; @@ -414,7 +401,6 @@ namespace nodetool bool set_rate_limit(const boost::program_options::variables_map& vm, int64_t limit); bool has_too_many_connections(const epee::net_utils::network_address &address); - uint64_t get_connections_count(); size_t get_incoming_connections_count(); size_t get_incoming_connections_count(network_zone&); size_t get_outgoing_connections_count(); @@ -478,9 +464,6 @@ namespace nodetool epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval; epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval; -#ifdef ALLOW_DEBUG_COMMANDS - uint64_t m_last_stat_request_time; -#endif std::list<epee::net_utils::network_address> m_priority_peers; std::vector<epee::net_utils::network_address> m_exclusive_peers; std::vector<epee::net_utils::network_address> m_seed_nodes; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 6a02d31b5..dcd16e5c1 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -813,7 +813,6 @@ namespace nodetool //only in case if we really sure that we have external visible ip m_have_address = true; - m_last_stat_request_time = 0; //configure self @@ -940,15 +939,6 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - uint64_t node_server<t_payload_net_handler>::get_connections_count() - { - std::uint64_t count = 0; - for (auto& zone : m_network_zones) - count += zone.second.m_net_server.get_config_object().get_connections_count(); - return count; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::deinit() { kill(); @@ -1023,15 +1013,18 @@ namespace nodetool epee::simple_event ev; std::atomic<bool> hsh_result(false); + bool timeout = false; bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_HANDSHAKE::response>(context_, COMMAND_HANDSHAKE::ID, arg, zone.m_net_server.get_config_object(), - [this, &pi, &ev, &hsh_result, &just_take_peerlist, &context_](int code, const typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) + [this, &pi, &ev, &hsh_result, &just_take_peerlist, &context_, &timeout](int code, const typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) { epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ev.raise();}); if(code < 0) { LOG_WARNING_CC(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")"); + if (code == LEVIN_ERROR_CONNECTION_TIMEDOUT || code == LEVIN_ERROR_CONNECTION_DESTROYED) + timeout = true; return; } @@ -1089,7 +1082,8 @@ namespace nodetool if(!hsh_result) { LOG_WARNING_CC(context_, "COMMAND_HANDSHAKE Failed"); - m_network_zones.at(context_.m_remote_address.get_zone()).m_net_server.get_config_object().close(context_.m_connection_id); + if (!timeout) + zone.m_net_server.get_config_object().close(context_.m_connection_id); } else if (!just_take_peerlist) { @@ -1262,7 +1256,7 @@ namespace nodetool bool is_priority = is_priority_node(na); LOG_PRINT_CC_PRIORITY_NODE(is_priority, bool(con), "Connect failed to " << na.str() /*<< ", try " << try_count*/); - //m_peerlist.set_peer_unreachable(pe); + record_addr_failed(na); return false; } @@ -1276,7 +1270,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); + record_addr_failed(na); return false; } @@ -1327,6 +1321,7 @@ namespace nodetool bool is_priority = is_priority_node(na); LOG_PRINT_CC_PRIORITY_NODE(is_priority, p2p_connection_context{}, "Connect failed to " << na.str()); + record_addr_failed(na); return false; } @@ -1338,7 +1333,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); + record_addr_failed(na); return false; } @@ -1353,6 +1348,13 @@ namespace nodetool //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::record_addr_failed(const epee::net_utils::network_address& addr) + { + CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock); + m_conn_fails_cache[addr.host_str()] = time(NULL); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::is_addr_recently_failed(const epee::net_utils::network_address& addr) { CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock); @@ -1434,10 +1436,10 @@ namespace nodetool std::deque<size_t> filtered; const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max(); - size_t idx = 0, skipped = 0; for (int step = 0; step < 2; ++step) { bool skip_duplicate_class_B = step == 0; + size_t idx = 0, skipped = 0; zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ if (filtered.size() >= limit) return false; @@ -1543,6 +1545,7 @@ namespace nodetool return true; size_t try_count = 0; + bool is_connected_to_at_least_one_seed_node = false; size_t current_index = crypto::rand_idx(m_seed_nodes.size()); const net_server& server = m_network_zones.at(epee::net_utils::zone::public_).m_net_server; while(true) @@ -1550,21 +1553,25 @@ namespace nodetool if(server.is_stop_signal_sent()) return false; - if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) + peerlist_entry pe_seed{}; + pe_seed.adr = m_seed_nodes[current_index]; + if (is_peer_used(pe_seed)) + is_connected_to_at_least_one_seed_node = true; + else if (try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) break; if(++try_count > m_seed_nodes.size()) { if (!m_fallback_seed_nodes_added) { MWARNING("Failed to connect to any of seed peers, trying fallback seeds"); - current_index = m_seed_nodes.size(); + current_index = m_seed_nodes.size() - 1; for (const auto &peer: get_seed_nodes(m_nettype)) { MDEBUG("Fallback seed node: " << peer); append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); } m_fallback_seed_nodes_added = true; - if (current_index == m_seed_nodes.size()) + if (current_index == m_seed_nodes.size() - 1) { MWARNING("No fallback seeds, continuing without seeds"); break; @@ -1573,7 +1580,8 @@ namespace nodetool } else { - MWARNING("Failed to connect to any of seed peers, continuing without seeds"); + if (!is_connected_to_at_least_one_seed_node) + MWARNING("Failed to connect to any of seed peers, continuing without seeds"); break; } } @@ -1912,7 +1920,7 @@ namespace nodetool LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size()); LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_)); - return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_); + return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { return !is_addr_recently_failed(pe.adr); }); } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -1929,91 +1937,6 @@ namespace nodetool return true; } //----------------------------------------------------------------------------------- -#ifdef ALLOW_DEBUG_COMMANDS - template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::check_trust(const proof_of_trust& tr, const epee::net_utils::zone zone_type) - { - uint64_t local_time = time(NULL); - uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time; - if(time_delata > 24*60*60 ) - { - MWARNING("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time); - return false; - } - if(m_last_stat_request_time >= tr.time ) - { - MWARNING("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time); - return false; - } - - const network_zone& zone = m_network_zones.at(zone_type); - if(zone.m_config.m_peer_id != tr.peer_id) - { - MWARNING("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << peerid_to_string(zone.m_config.m_peer_id) << ")"); - return false; - } - crypto::public_key pk = AUTO_VAL_INIT(pk); - epee::string_tools::hex_to_pod(::config::P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY, pk); - crypto::hash h = get_proof_of_trust_hash(tr); - if(!crypto::check_signature(h, pk, tr.sign)) - { - MWARNING("check_trust failed: sign check failed"); - return false; - } - //update last request time - m_last_stat_request_time = tr.time; - return true; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> - int node_server<t_payload_net_handler>::handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context) - { - if(!check_trust(arg.tr, context.m_remote_address.get_zone())) - { - drop_connection(context); - return 1; - } - rsp.connections_count = get_connections_count(); - rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); - rsp.version = MONERO_VERSION_FULL; - rsp.os_version = tools::get_os_version_string(); - m_payload_handler.get_stat_info(rsp.payload_info); - return 1; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> - int node_server<t_payload_net_handler>::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context) - { - if(!check_trust(arg.tr, context.m_remote_address.get_zone())) - { - drop_connection(context); - return 1; - } - m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - connection_entry ce; - ce.adr = cntxt.m_remote_address; - ce.id = cntxt.peer_id; - ce.is_income = cntxt.m_is_income; - rsp.connections_list.push_back(ce); - return true; - }); - - network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone()); - zone.m_peerlist.get_peerlist(rsp.local_peerlist_gray, rsp.local_peerlist_white); - rsp.my_id = zone.m_config.m_peer_id; - rsp.local_time = time(NULL); - return 1; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> - int node_server<t_payload_net_handler>::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context) - { - rsp.my_id = m_network_zones.at(context.m_remote_address.get_zone()).m_config.m_peer_id; - return 1; - } -#endif - //----------------------------------------------------------------------------------- template<class t_payload_net_handler> int node_server<t_payload_net_handler>::handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context) { diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 8225ad2fa..300181bbb 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -43,6 +43,7 @@ #include <boost/range/adaptor/reversed.hpp> +#include "crypto/crypto.h" #include "cryptonote_config.h" #include "net/enums.h" #include "net/local_ip.h" @@ -101,7 +102,7 @@ namespace nodetool bool init(peerlist_types&& peers, bool allow_local_ip); size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();} size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();} - bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs); + bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs, const std::function<bool(const peerlist_entry&)> &f = NULL); bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white); void get_peerlist(peerlist_types& peers); @@ -112,7 +113,6 @@ namespace nodetool bool append_with_peer_gray(const peerlist_entry& pr); bool append_with_peer_anchor(const anchor_peerlist_entry& ple); bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash); - bool set_peer_unreachable(const peerlist_entry& pr); bool is_host_allowed(const epee::net_utils::network_address &address); bool get_random_gray_peer(peerlist_entry& pe); bool remove_from_peer_gray(const peerlist_entry& pe); @@ -213,12 +213,13 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs) + bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs, const std::function<bool(const peerlist_entry&)> &f) { CRITICAL_REGION_LOCAL(m_peerlist_lock); for(const peerlist_entry& be: outer_bs) { - append_with_peer_gray(be); + if (!f || f(be)) + append_with_peer_gray(be); } // delete extra elements trim_gray_peerlist(); diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index efc8e52fd..609661871 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -40,9 +40,6 @@ #include "string_tools.h" #include "time_helper.h" #include "cryptonote_config.h" -#ifdef ALLOW_DEBUG_COMMANDS -#include "crypto/crypto.h" -#endif namespace nodetool { @@ -286,117 +283,6 @@ namespace nodetool }; -#ifdef ALLOW_DEBUG_COMMANDS - //These commands are considered as insecure, and made in debug purposes for a limited lifetime. - //Anyone who feel unsafe with this commands can disable the ALLOW_GET_STAT_COMMAND macro. - - struct proof_of_trust - { - peerid_type peer_id; - uint64_t time; - crypto::signature sign; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(peer_id) - KV_SERIALIZE(time) - KV_SERIALIZE_VAL_POD_AS_BLOB(sign) - END_KV_SERIALIZE_MAP() - }; - - - template<class payload_stat_info> - struct COMMAND_REQUEST_STAT_INFO_T - { - const static int ID = P2P_COMMANDS_POOL_BASE + 4; - - struct request_t - { - proof_of_trust tr; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tr) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string version; - std::string os_version; - uint64_t connections_count; - uint64_t incoming_connections_count; - payload_stat_info payload_info; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(version) - KV_SERIALIZE(os_version) - KV_SERIALIZE(connections_count) - KV_SERIALIZE(incoming_connections_count) - KV_SERIALIZE(payload_info) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - - /************************************************************************/ - /* */ - /************************************************************************/ - struct COMMAND_REQUEST_NETWORK_STATE - { - const static int ID = P2P_COMMANDS_POOL_BASE + 5; - - struct request_t - { - proof_of_trust tr; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tr) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::vector<peerlist_entry> local_peerlist_white; - std::vector<peerlist_entry> local_peerlist_gray; - std::vector<connection_entry> connections_list; - peerid_type my_id; - uint64_t local_time; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_white) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_gray) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(connections_list) - KV_SERIALIZE(my_id) - KV_SERIALIZE(local_time) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - struct COMMAND_REQUEST_PEER_ID - { - const static int ID = P2P_COMMANDS_POOL_BASE + 6; - - struct request_t - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - peerid_type my_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(my_id) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - /************************************************************************/ /* */ /************************************************************************/ @@ -421,16 +307,4 @@ namespace nodetool }; typedef epee::misc_utils::struct_init<response_t> response; }; - -#endif - - - inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) - { - std::string s; - s.append(reinterpret_cast<const char*>(&pot.peer_id), sizeof(pot.peer_id)); - s.append(reinterpret_cast<const char*>(&pot.time), sizeof(pot.time)); - return crypto::cn_fast_hash(s.data(), s.size()); - } - } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 5b79310c6..d33dbd16a 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -153,6 +153,7 @@ namespace cryptonote command_line::add_arg(desc, arg_rpc_payment_address); command_line::add_arg(desc, arg_rpc_payment_difficulty); command_line::add_arg(desc, arg_rpc_payment_credits); + command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback); } //------------------------------------------------------------------------------------------------------------------------------ core_rpc_server::core_rpc_server( @@ -163,6 +164,7 @@ namespace cryptonote , m_p2p(p2p) , m_was_bootstrap_ever_used(false) , disable_rpc_ban(false) + , m_rpc_payment_allow_free_loopback(false) {} //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password) @@ -280,6 +282,7 @@ namespace cryptonote MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given"); return false; } + m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback); m_rpc_payment.reset(new rpc_payment(info.address, diff, credits)); m_rpc_payment->load(command_line::get_arg(vm, cryptonote::arg_data_dir)); m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff)); @@ -353,7 +356,7 @@ namespace cryptonote #define CHECK_PAYMENT_BASE(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P > 0 && !check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0) #define CHECK_PAYMENT(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, false) #define CHECK_PAYMENT_SAME_TS(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, true) -#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P == 0) P = 1; if(!check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0) +#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx || (m_rpc_payment_allow_free_loopback && ctx->m_remote_address.is_loopback())) break; uint64_t P = (uint64_t)payment; if (P == 0) P = 1; if(!check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0) //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::check_core_ready() { @@ -788,6 +791,9 @@ namespace cryptonote CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false); + const bool restricted = m_restricted && ctx; + const bool request_has_rpc_origin = ctx != NULL; + std::vector<crypto::hash> vh; for(const auto& tx_hex_str: req.txs_hashes) { @@ -822,7 +828,7 @@ namespace cryptonote { std::vector<tx_info> pool_tx_info; std::vector<spent_key_image_info> pool_key_image_info; - bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info); + bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info, !request_has_rpc_origin || !restricted); if(r) { // sort to match original request @@ -1100,7 +1106,7 @@ namespace cryptonote return true; } - if (req.do_sanity_checks && !cryptonote::tx_sanity_check(m_core.get_blockchain_storage(), tx_blob)) + if (req.do_sanity_checks && !cryptonote::tx_sanity_check(tx_blob, m_core.get_blockchain_storage().get_num_mature_outputs(0))) { res.status = "Failed"; res.reason = "Sanity check failed"; @@ -1128,8 +1134,6 @@ namespace cryptonote add_reason(reason, "overspend"); if ((res.fee_too_low = tvc.m_fee_too_low)) add_reason(reason, "fee too low"); - if ((res.not_rct = tvc.m_not_rct)) - add_reason(reason, "tx is not ringct"); if ((res.too_few_outputs = tvc.m_too_few_outputs)) add_reason(reason, "too few outputs"); const std::string punctuation = reason.empty() ? "" : ": "; @@ -3255,4 +3259,10 @@ namespace cryptonote , "Restrict RPC to clients sending micropayment, yields that many credits per payment" , DEFAULT_PAYMENT_CREDITS_PER_HASH }; + + const command_line::arg_descriptor<bool> core_rpc_server::arg_rpc_payment_allow_free_loopback = { + "rpc-payment-allow-free-loopback" + , "Allow free access from the loopback address (ie, the local host)" + , false + }; } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 3b8e9c20a..d82ab6af4 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -75,6 +75,7 @@ namespace cryptonote static const command_line::arg_descriptor<std::string> arg_rpc_payment_address; static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty; static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits; + static const command_line::arg_descriptor<bool> arg_rpc_payment_allow_free_loopback; typedef epee::net_utils::connection_context_base connection_context; @@ -287,6 +288,7 @@ private: std::map<std::string, uint64_t> m_host_fails_score; std::unique_ptr<rpc_payment> m_rpc_payment; bool disable_rpc_ban; + bool m_rpc_payment_allow_free_loopback; }; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index dbb1d4472..a3c187c24 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -588,7 +588,6 @@ namespace cryptonote bool too_big; bool overspend; bool fee_too_low; - bool not_rct; bool too_few_outputs; bool sanity_check_failed; @@ -603,7 +602,6 @@ namespace cryptonote KV_SERIALIZE(too_big) KV_SERIALIZE(overspend) KV_SERIALIZE(fee_too_low) - KV_SERIALIZE(not_rct) KV_SERIALIZE(too_few_outputs) KV_SERIALIZE(sanity_check_failed) END_KV_SERIALIZE_MAP() diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 125688ba5..7292176b4 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -410,11 +410,6 @@ namespace rpc if (!res.error_details.empty()) res.error_details += " and "; res.error_details += "fee too low"; } - if (tvc.m_not_rct) - { - if (!res.error_details.empty()) res.error_details += " and "; - res.error_details += "tx is not ringct"; - } if (tvc.m_too_few_outputs) { if (!res.error_details.empty()) res.error_details += " and "; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 19e3656c2..9569863b3 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1691,7 +1691,7 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args) rings.push_back({key_image, ring}); else if (!m_wallet->get_rings(txid, rings)) { - fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0"); + fail_msg_writer() << tr("Key image either not spent, or spent with ring size 1"); return true; } @@ -6064,11 +6064,14 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std:: return std::make_pair(ostr.str(), ring_str); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr) +bool simple_wallet::process_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr, bool verbose) { uint32_t version; if (!try_connect_to_daemon(false, &version)) + { + fail_msg_writer() << tr("failed to connect to daemon"); return false; + } // available for RPC version 1.4 or higher if (version < MAKE_CORE_RPC_VERSION(1, 4)) return true; @@ -6084,7 +6087,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending { const cryptonote::transaction& tx = ptx_vector[n].tx; const tools::wallet2::tx_construction_data& construction_data = ptx_vector[n].construction_data; - ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx); + if (verbose) + ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx); // for each input std::vector<uint64_t> spent_key_height(tx.vin.size()); std::vector<crypto::hash> spent_key_txid (tx.vin.size()); @@ -6105,7 +6109,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending } const cryptonote::tx_source_entry& source = *sptr; - ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount); + if (verbose) + ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount); // convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets); // get block heights from which those ring member keys originated @@ -6135,7 +6140,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending return false; } } - ostr << tr("\nOriginating block heights: "); + if (verbose) + ostr << tr("\nOriginating block heights: "); spent_key_height[i] = res.outs[source.real_output].height; spent_key_txid [i] = res.outs[source.real_output].txid; std::vector<uint64_t> heights(absolute_offsets.size(), 0); @@ -6144,7 +6150,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending heights[j] = res.outs[j].height; } std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, source.real_output); - ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n"); + if (verbose) + ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n"); } // warn if rings contain keys originating from the same tx or temporally very close block heights bool are_keys_from_same_tx = false; @@ -6163,7 +6170,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending ostr << tr("\nWarning: Some input keys being spent are from ") << (are_keys_from_same_tx ? tr("the same transaction") : tr("blocks that are temporally very close")) - << tr(", which can break the anonymity of ring signature. Make sure this is intentional!"); + << tr(", which can break the anonymity of ring signatures. Make sure this is intentional!"); } ostr << ENDL; } @@ -6604,11 +6611,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri float days = locked_blocks / 720.0f; prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days; } - if (m_wallet->print_ring_members()) - { - if (!print_ring_members(ptx_vector, prompt)) - return false; - } + if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) + return false; bool default_ring_size = true; for (const auto &ptx: ptx_vector) { @@ -7064,7 +7068,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st if (subaddr_indices.size() > 1) prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n"); } - if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt)) + if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) return true; if (ptx_vector.size() > 1) { prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) % @@ -7308,7 +7312,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) uint64_t total_fee = ptx_vector[0].fee; uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount(); std::ostringstream prompt; - if (!print_ring_members(ptx_vector, prompt)) + if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) return true; prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) % print_money(total_sent) % diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index c0416ecbb..4ba2793e0 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -270,7 +270,7 @@ namespace cryptonote bool accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message = std::string()); bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs); bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs); - bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr); + bool process_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr, bool verbose); std::string get_prompt() const; bool print_seed(bool encrypted); void key_images_sync_intern(); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 6200c7a1f..33047f703 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1671,6 +1671,26 @@ void WalletImpl::disposeTransaction(PendingTransaction *t) delete t; } +uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const +{ + const size_t pubkey_size = 33; + const size_t encrypted_paymentid_size = 11; + const size_t extra_size = pubkey_size + encrypted_paymentid_size; + + return m_wallet->estimate_fee( + m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0), + m_wallet->use_fork_rules(4, 0), + 1, + m_wallet->get_min_ring_size() - 1, + destinations.size() + 1, + extra_size, + m_wallet->use_fork_rules(8, 0), + m_wallet->get_base_fee(), + m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))), + m_wallet->get_fee_quantization_mask()); +} + TransactionHistory *WalletImpl::history() { return m_history.get(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 331bf4b38..e46a62340 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -166,6 +166,8 @@ public: bool importKeyImages(const std::string &filename) override; virtual void disposeTransaction(PendingTransaction * t) override; + virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const override; virtual TransactionHistory * history() override; virtual AddressBook * addressBook() override; virtual Subaddress * subaddress() override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index e543a115b..3945e55c9 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -879,6 +879,14 @@ struct Wallet */ virtual void disposeTransaction(PendingTransaction * t) = 0; + /*! + * \brief Estimates transaction fee. + * \param destinations Vector consisting of <address, amount> pairs. + * \return Estimated fee. + */ + virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const = 0; + /*! * \brief exportKeyImages - exports key images to file * \param filename diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index b7efdd75c..5e88ea788 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -39,6 +39,8 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb" +#define V1TAG ((uint64_t)798237759845202) + static const char zerokey[8] = {0}; static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey }; @@ -63,15 +65,16 @@ static int compare_uint64(const MDB_val *a, const MDB_val *b) return va < vb ? -1 : va > vb; } -static std::string compress_ring(const std::vector<uint64_t> &ring) +static std::string compress_ring(const std::vector<uint64_t> &ring, uint64_t tag) { std::string s; + s += tools::get_varint_data(tag); for (uint64_t out: ring) s += tools::get_varint_data(out); return s; } -static std::vector<uint64_t> decompress_ring(const std::string &s) +static std::vector<uint64_t> decompress_ring(const std::string &s, uint64_t tag) { std::vector<uint64_t> ring; int read = 0; @@ -81,6 +84,13 @@ static std::vector<uint64_t> decompress_ring(const std::string &s) std::string tmp(i, s.cend()); read = tools::read_varint(tmp.begin(), tmp.end(), out); THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring"); + if (tag) + { + if (tag != out) + return {}; + tag = 0; + continue; + } ring.push_back(out); } return ring; @@ -93,25 +103,27 @@ std::string get_rings_filename(boost::filesystem::path filename) return filename.string(); } -static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key) +static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { static const char salt[] = "ringdsb"; - uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt)]; + uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt) + sizeof(field)]; memcpy(buffer, &key_image, sizeof(key_image)); memcpy(buffer + sizeof(key_image), &key, sizeof(key)); memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt)); + memcpy(buffer + sizeof(key_image) + sizeof(key) + sizeof(salt), &field, sizeof(field)); crypto::hash hash; - crypto::cn_fast_hash(buffer, sizeof(buffer), hash.data); + // if field is 0, backward compat mode: hash without the field + crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data); static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes"); crypto::chacha_iv iv; memcpy(&iv, &hash, CHACHA_IV_SIZE); return iv; } -static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - const crypto::chacha_iv iv = make_iv(key_image, key); + const crypto::chacha_iv iv = make_iv(key_image, key, field); std::string ciphertext; ciphertext.resize(plaintext.size() + sizeof(iv)); crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); @@ -119,14 +131,14 @@ static std::string encrypt(const std::string &plaintext, const crypto::key_image return ciphertext; } -static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key); + return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key, field); } -static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - const crypto::chacha_iv iv = make_iv(key_image, key); + const crypto::chacha_iv iv = make_iv(key_image, key, field); std::string plaintext; THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text"); plaintext.resize(ciphertext.size() - sizeof(iv)); @@ -137,11 +149,11 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector<uint64_t> &relative_ring, const crypto::chacha_key &chacha_key) { MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); - std::string compressed_ring = compress_ring(relative_ring); - std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key); + std::string compressed_ring = compress_ring(relative_ring, V1TAG); + std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1); data.mv_size = data_ciphertext.size(); data.mv_data = (void*)data_ciphertext.c_str(); int dbr = mdb_put(txn, dbi, &key, &data, 0); @@ -297,7 +309,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto for (const crypto::key_image &key_image: key_images) { MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); @@ -349,7 +361,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im tx_active = true; MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); dbr = mdb_get(txn, dbi_rings, &key, &data); @@ -358,8 +370,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return false; THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size"); - std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key); - outs = decompress_ring(data_plaintext); + bool try_v0 = false; + std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1); + try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; } + catch(...) { try_v0 = true; } + if (try_v0) + { + data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 0); + outs = decompress_ring(data_plaintext, 0); + } MDEBUG("Found ring for key image " << key_image << ":"); MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); outs = cryptonote::relative_output_offsets_to_absolute(outs); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7ee32a436..def6ac3ed 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -44,6 +44,7 @@ using namespace epee; #include "cryptonote_config.h" +#include "cryptonote_core/tx_sanity_check.h" #include "wallet_rpc_helpers.h" #include "wallet2.h" #include "cryptonote_basic/cryptonote_format_utils.h" @@ -149,6 +150,9 @@ static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1"; static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1"; +boost::mutex tools::wallet2::default_daemon_address_lock; +std::string tools::wallet2::default_daemon_address = ""; + namespace { std::string get_default_ringdb_path() @@ -236,8 +240,6 @@ namespace add_reason(reason, "overspend"); if (res.fee_too_low) add_reason(reason, "fee too low"); - if (res.not_rct) - add_reason(reason, "tx is not ringct"); if (res.sanity_check_failed) add_reason(reason, "tx sanity check failed"); if (res.not_relayed) @@ -412,6 +414,15 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl daemon_port = get_config(nettype).RPC_DEFAULT_PORT; } + // if no daemon settings are given and we have a previous one, reuse that one + if (command_line::is_arg_defaulted(vm, opts.daemon_host) && command_line::is_arg_defaulted(vm, opts.daemon_port) && command_line::is_arg_defaulted(vm, opts.daemon_address)) + { + // not a bug: taking a const ref to a temporary in this way is actually ok in a recent C++ standard + const std::string &def = tools::wallet2::get_default_daemon_address(); + if (!def.empty()) + daemon_address = def; + } + if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); @@ -591,6 +602,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f } viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); crypto::public_key pkey; + if (viewkey == crypto::null_skey) + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view secret key may not be all zeroes")); if (!crypto::secret_key_to_public_key(viewkey, pkey)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key")); } @@ -607,6 +620,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f } spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); crypto::public_key pkey; + if (spendkey == crypto::null_skey) + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend secret key may not be all zeroes")); if (!crypto::secret_key_to_public_key(spendkey, pkey)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key")); } @@ -879,20 +894,6 @@ uint8_t get_bulletproof_fork() return 8; } -uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) -{ - if (use_per_byte_fee) - { - const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); - return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask); - } - else - { - const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); - return calculate_fee(base_fee, estimated_tx_size, fee_multiplier); - } -} - uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) { if (use_per_byte_fee) @@ -1313,8 +1314,15 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u m_node_rpc_proxy.invalidate(); } - MINFO("setting daemon to " << get_daemon_address()); - return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options)); + const std::string address = get_daemon_address(); + MINFO("setting daemon to " << address); + bool ret = m_http_client.set_server(address, get_daemon_login(), std::move(ssl_options)); + if (ret) + { + CRITICAL_REGION_LOCAL(default_daemon_address_lock); + default_daemon_address = address; + } + return ret; } //---------------------------------------------------------------------------------------------------- bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) @@ -2934,7 +2942,6 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, pit->second.m_state = wallet2::unconfirmed_transfer_details::failed; // the inputs aren't spent anymore, since the tx failed - remove_rings(pit->second.m_tx); for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini) { if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key)) @@ -4088,9 +4095,18 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_always_confirm_transfers = field_always_confirm_transfers; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true); m_print_ring_members = field_print_ring_members; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true); - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true); - m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0)); + if (json.HasMember("store_tx_info")) + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, true, true); + m_store_tx_info = field_store_tx_info; + } + else if (json.HasMember("store_tx_keys")) // backward compatibility + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, true, true); + m_store_tx_info = field_store_tx_keys; + } + else + m_store_tx_info = true; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0); m_default_mixin = field_default_mixin; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0); @@ -7134,6 +7150,20 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto return sign_multisig_tx_to_file(exported_txs, filename, txids); } //---------------------------------------------------------------------------------------------------- +uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const +{ + if (use_per_byte_fee) + { + const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); + return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask); + } + else + { + const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); + return calculate_fee(base_fee, estimated_tx_size, fee_multiplier); + } +} + uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) { static const struct @@ -7775,8 +7805,50 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ } } +std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs) +{ + std::set<uint64_t> unique; + size_t total = 0; + + for (const auto &it : outs) + { + for (const auto &out : it) + { + const uint64_t global_index = std::get<0>(out); + unique.insert(global_index); + } + total += it.size(); + } + + return std::make_pair(std::move(unique), total); +} + void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count) { + std::vector<uint64_t> rct_offsets; + for (size_t attempts = 3; attempts > 0; --attempts) + { + get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets); + + const auto unique = outs_unique(outs); + if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back())) + { + return; + } + + std::vector<crypto::key_image> key_images; + key_images.reserve(selected_transfers.size()); + std::for_each(selected_transfers.begin(), selected_transfers.end(), [this, &key_images](size_t index) { + key_images.push_back(m_transfers[index].m_key_image); + }); + unset_ring(key_images); + } + + THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed")); +} + +void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets) +{ LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); @@ -7797,7 +7869,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // if we have at least one rct out, get the distribution, or fall back to the previous system uint64_t rct_start_height; - std::vector<uint64_t> rct_offsets; bool has_rct = false; uint64_t max_rct_index = 0; for (size_t idx: selected_transfers) @@ -7806,7 +7877,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> has_rct = true; max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index); } - const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets); + const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets)); if (has_rct_distribution) { // check we're clear enough of rct start, to avoid corner cases below diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 8ecc4d8fa..1dac8acac 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1246,6 +1246,7 @@ private: std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels); std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees); + uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const; uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1); uint64_t get_base_fee(); uint64_t get_fee_quantization_mask(); @@ -1389,6 +1390,8 @@ private: uint64_t credits() const { return m_rpc_payment_state.credits; } void credit_report(uint64_t &expected_spent, uint64_t &discrepancy) const { expected_spent = m_rpc_payment_state.expected_spent; discrepancy = m_rpc_payment_state.discrepancy; } + static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } + private: /*! * \brief Stores wallet information to wallet file. @@ -1440,6 +1443,7 @@ private: bool is_spent(const transfer_details &td, bool strict = true) const; bool is_spent(size_t idx, bool strict = true) const; void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); + void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets); bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; @@ -1628,6 +1632,9 @@ private: std::unique_ptr<wallet_device_callback> m_device_callback; ExportFormat m_export_format; + + static boost::mutex default_daemon_address_lock; + static std::string default_daemon_address; }; } BOOST_CLASS_VERSION(tools::wallet2, 29) |