diff options
Diffstat (limited to 'src')
45 files changed, 1028 insertions, 446 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f332af3d3..d45363e24 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -113,12 +113,10 @@ add_subdirectory(lmdb) add_subdirectory(multisig) add_subdirectory(net) add_subdirectory(hardforks) -if(NOT IOS) - add_subdirectory(blockchain_db) -endif() +add_subdirectory(blockchain_db) add_subdirectory(mnemonics) +add_subdirectory(rpc) if(NOT IOS) - add_subdirectory(rpc) add_subdirectory(serialization) endif() add_subdirectory(wallet) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 63ac38a88..5fec22d8d 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -44,6 +44,71 @@ using epee::string_tools::pod_to_hex; namespace cryptonote { +bool matches_category(relay_method method, relay_category category) noexcept +{ + switch (category) + { + default: + return false; + case relay_category::all: + return true; + case relay_category::relayable: + if (method == relay_method::none) + return false; + return true; + case relay_category::broadcasted: + case relay_category::legacy: + break; + } + // check for "broadcasted" or "legacy" methods: + switch (method) + { + default: + case relay_method::local: + return false; + case relay_method::block: + case relay_method::flood: + return true; + case relay_method::none: + break; + } + return category == relay_category::legacy; +} + +void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept +{ + kept_by_block = 0; + do_not_relay = 0; + is_local = 0; + + switch (method) + { + case relay_method::none: + do_not_relay = 1; + break; + case relay_method::local: + is_local = 1; + break; + default: + case relay_method::flood: + break; + case relay_method::block: + kept_by_block = 1; + break; + } +} + +relay_method txpool_tx_meta_t::get_relay_method() const noexcept +{ + if (kept_by_block) + return relay_method::block; + if (do_not_relay) + return relay_method::none; + if (is_local) + return relay_method::local; + return relay_method::flood; +} + const command_line::arg_descriptor<std::string> arg_db_sync_mode = { "db-sync-mode" , "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[<nblocks_per_sync>[blocks]|<nbytes_per_sync>[bytes]]." @@ -924,4 +989,23 @@ void BlockchainDB::fixup() batch_stop(); } +bool BlockchainDB::txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category) +{ + try + { + txpool_tx_meta_t meta{}; + if (!get_txpool_tx_meta(tx_hash, meta)) + { + MERROR("Failed to get tx meta from txpool"); + return false; + } + return meta.matches(category); + } + catch (const std::exception &e) + { + MERROR("Failed to get tx meta from txpool: " << e.what()); + } + return false; +} + } // namespace cryptonote diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index d1e4919be..b2c5d6cb4 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -39,6 +39,7 @@ #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/difficulty.h" #include "cryptonote_basic/hardfork.h" +#include "cryptonote_protocol/enums.h" /** \file * Cryptonote Blockchain Database Interface @@ -105,6 +106,16 @@ typedef std::pair<crypto::hash, uint64_t> tx_out_index; extern const command_line::arg_descriptor<std::string> arg_db_sync_mode; extern const command_line::arg_descriptor<bool, false> arg_db_salvage; +enum class relay_category : uint8_t +{ + broadcasted = 0,//!< Public txes received via block/flooding/fluff + relayable, //!< Every tx not marked `relay_method::none` + legacy, //!< `relay_category::broadcasted` + `relay_method::none` for rpc relay requests or historical reasons + all //!< Everything in the db +}; + +bool matches_category(relay_method method, relay_category category) noexcept; + #pragma pack(push, 1) /** @@ -156,11 +167,22 @@ struct txpool_tx_meta_t uint8_t do_not_relay; uint8_t double_spend_seen: 1; uint8_t pruned: 1; - uint8_t bf_padding: 6; + uint8_t is_local: 1; + uint8_t bf_padding: 5; uint8_t padding[76]; // till 192 bytes + + void set_relay_method(relay_method method) noexcept; + relay_method get_relay_method() const noexcept; + + //! See `relay_category` description + bool matches(const relay_category category) const noexcept + { + return matches_category(get_relay_method(), category); + } }; + #define DBF_SAFE 1 #define DBF_FAST 2 #define DBF_FASTEST 4 @@ -1465,12 +1487,12 @@ public: /** * @brief get the number of transactions in the txpool */ - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const = 0; + virtual uint64_t get_txpool_tx_count(relay_category tx_category = relay_category::broadcasted) const = 0; /** - * @brief check whether a txid is in the txpool + * @brief check whether a txid is in the txpool and meets tx_category requirements */ - virtual bool txpool_has_tx(const crypto::hash &txid) const = 0; + virtual bool txpool_has_tx(const crypto::hash &txid, relay_category tx_category) const = 0; /** * @brief remove a txpool transaction @@ -1494,10 +1516,11 @@ public: * * @param txid the transaction id of the transation to lookup * @param bd the blob to return + * @param tx_category for filtering out hidden/private txes * - * @return true if the txid was in the txpool, false otherwise + * @return True iff `txid` is in the pool and meets `tx_category` requirements */ - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const = 0; + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const = 0; /** * @brief get a txpool transaction's blob @@ -1506,7 +1529,17 @@ public: * * @return the blob for that transaction */ - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const = 0; + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const = 0; + + /** + * @brief Check if `tx_hash` relay status is in `category`. + * + * @param tx_hash hash of the transaction to lookup + * @param category relay status category to test against + * + * @return True if `tx_hash` latest relay status is in `category`. + */ + bool txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category); /** * @brief prune output data for the given amount @@ -1604,7 +1637,7 @@ public: * * @return false if the function returns false for any transaction, otherwise true */ - virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const = 0; + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, relay_category category = relay_category::broadcasted) const = 0; /** * @brief runs a function over all key images stored diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index f978ef307..e8667adcf 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1771,7 +1771,7 @@ void BlockchainLMDB::update_txpool_tx(const crypto::hash &txid, const txpool_tx_ } } -uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const +uint64_t BlockchainLMDB::get_txpool_tx_count(relay_category category) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1781,7 +1781,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const TXN_PREFIX_RDONLY(); - if (include_unrelayed_txes) + if (category == relay_category::all) { // No filtering, we can get the number of tx the "fast" way MDB_stat db_stats; @@ -1807,7 +1807,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const if (result) throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str())); const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data; - if (!meta.do_not_relay) + if (meta.matches(category)) ++num_entries; } } @@ -1816,7 +1816,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const return num_entries; } -bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid) const +bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid, relay_category tx_category) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1825,11 +1825,21 @@ bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid) const RCURSOR(txpool_meta) MDB_val k = {sizeof(txid), (void *)&txid}; - auto result = mdb_cursor_get(m_cur_txpool_meta, &k, NULL, MDB_SET); + MDB_val v; + auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET); if (result != 0 && result != MDB_NOTFOUND) throw1(DB_ERROR(lmdb_error("Error finding txpool tx meta: ", result).c_str())); + if (result == MDB_NOTFOUND) + return false; + + bool found = true; + if (tx_category != relay_category::all) + { + const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data; + found = meta.matches(tx_category); + } TXN_POSTFIX_RDONLY(); - return result != MDB_NOTFOUND; + return found; } void BlockchainLMDB::remove_txpool_tx(const crypto::hash& txid) @@ -1883,7 +1893,7 @@ bool BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta return true; } -bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const +bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1893,6 +1903,21 @@ bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::bl MDB_val k = {sizeof(txid), (void *)&txid}; MDB_val v; + + // if filtering, make sure those requirements are met before copying blob + if (tx_category != relay_category::all) + { + auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET); + if (result == MDB_NOTFOUND) + return false; + if (result != 0) + throw1(DB_ERROR(lmdb_error("Error finding txpool tx meta: ", result).c_str())); + + const txpool_tx_meta_t& meta = *(const txpool_tx_meta_t*)v.mv_data; + if (!meta.matches(tx_category)) + return false; + } + auto result = mdb_cursor_get(m_cur_txpool_blob, &k, &v, MDB_SET); if (result == MDB_NOTFOUND) return false; @@ -1904,10 +1929,10 @@ bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::bl return true; } -cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid) const +cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const { cryptonote::blobdata bd; - if (!get_txpool_tx_blob(txid, bd)) + if (!get_txpool_tx_blob(txid, bd, tx_category)) throw1(DB_ERROR("Tx not found in txpool: ")); return bd; } @@ -2245,7 +2270,7 @@ bool BlockchainLMDB::check_pruning() return prune_worker(prune_mode_check, 0); } -bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const +bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, relay_category category) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -2269,8 +2294,7 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str())); const crypto::hash txid = *(const crypto::hash*)k.mv_data; const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data; - if (!include_unrelayed_txes && meta.do_not_relay) - // Skipping that tx + if (!meta.matches(category)) continue; const cryptonote::blobdata *passed_bd = NULL; cryptonote::blobdata bd; diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 61a551476..e56711e8f 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -281,12 +281,12 @@ public: virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t& meta); virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& meta); - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const; - virtual bool txpool_has_tx(const crypto::hash &txid) const; + virtual uint64_t get_txpool_tx_count(relay_category category = relay_category::broadcasted) const; + virtual bool txpool_has_tx(const crypto::hash &txid, relay_category tx_category) const; virtual void remove_txpool_tx(const crypto::hash& txid); virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const; - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const; - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const; + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata& bd, relay_category tx_category) const; + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const; virtual uint32_t get_blockchain_pruning_seed() const; virtual bool prune_blockchain(uint32_t pruning_seed = 0); virtual bool update_pruning(); @@ -298,7 +298,7 @@ public: 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_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, relay_category category = relay_category::broadcasted) const; virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const; virtual bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index ac19fae25..a5847dec6 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -126,14 +126,14 @@ public: virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) override {} virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) override {} - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const override { return 0; } - virtual bool txpool_has_tx(const crypto::hash &txid) const override { return false; } + virtual uint64_t get_txpool_tx_count(relay_category tx_relay = relay_category::broadcasted) const override { return 0; } + virtual bool txpool_has_tx(const crypto::hash &txid, relay_category tx_category) const override { return false; } virtual void remove_txpool_tx(const crypto::hash& txid) override {} virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const override { return false; } - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const override { return false; } + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const override { return false; } virtual uint64_t get_database_size() const override { return 0; } - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const override { return ""; } - virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const override { return false; } + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const override { return ""; } + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, relay_category category = relay_category::broadcasted) const override { return false; } virtual void add_block( const cryptonote::block& blk , size_t block_weight diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 5d039d7f4..852e9cf4f 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -174,7 +174,7 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block for(auto& tx_blob: block_entry.txs) { tx_verification_context tvc = AUTO_VAL_INIT(tvc); - core.handle_incoming_tx(tx_blob, tvc, true, true, false); + core.handle_incoming_tx(tx_blob, tvc, relay_method::block, true); if(tvc.m_verifivation_failed) { MERROR("transaction verification failed, tx_id = " diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 2748c798c..18204eeee 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -37,16 +37,14 @@ static __thread bool is_leaf = false; namespace tools { threadpool::threadpool(unsigned int max_threads) : running(true), active(0) { - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - max = max_threads ? max_threads : tools::get_max_concurrency(); - size_t i = max ? max - 1 : 0; - while(i--) { - threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false))); - } + create(max_threads); } threadpool::~threadpool() { + destroy(); +} + +void threadpool::destroy() { try { const boost::unique_lock<boost::mutex> lock(mutex); @@ -64,6 +62,23 @@ threadpool::~threadpool() { try { threads[i].join(); } catch (...) { /* ignore */ } } + threads.clear(); +} + +void threadpool::recycle() { + destroy(); + create(max); +} + +void threadpool::create(unsigned int max_threads) { + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + max = max_threads ? max_threads : tools::get_max_concurrency(); + size_t i = max ? max - 1 : 0; + running = true; + while(i--) { + threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false))); + } } void threadpool::submit(waiter *obj, std::function<void()> f, bool leaf) { @@ -145,7 +160,7 @@ void threadpool::run(bool flush) { if (!running) break; active++; - e = queue.front(); + e = std::move(queue.front()); queue.pop_front(); lock.unlock(); ++depth; diff --git a/src/common/threadpool.h b/src/common/threadpool.h index 5e490ee7d..a49d0e14f 100644 --- a/src/common/threadpool.h +++ b/src/common/threadpool.h @@ -69,12 +69,17 @@ public: // task to finish. void submit(waiter *waiter, std::function<void()> f, bool leaf = false); + // destroy and recreate threads + void recycle(); + unsigned int get_max_concurrency() const; ~threadpool(); private: threadpool(unsigned int max_threads = 0); + void destroy(); + void create(unsigned int max_threads); typedef struct entry { waiter *wo; std::function<void()> f; diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index 1e305b3a6..bb2c5fb40 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -40,6 +40,7 @@ #include <string.h> #include <stdio.h> #include <stdint.h> +#include <memwipe.h> #include "blake256.h" #define U8TO32(p) \ @@ -277,7 +278,7 @@ void hmac_blake256_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { } blake256_update(&S->outer, pad, 512); - memset(keyhash, 0, 32); + memwipe(keyhash, sizeof(keyhash)); } // keylen = number of bytes @@ -307,7 +308,7 @@ void hmac_blake224_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { } blake224_update(&S->outer, pad, 512); - memset(keyhash, 0, 32); + memwipe(keyhash, sizeof(keyhash)); } // datalen = number of bits @@ -327,7 +328,7 @@ void hmac_blake256_final(hmac_state *S, uint8_t *digest) { blake256_final(&S->inner, ihash); blake256_update(&S->outer, ihash, 256); blake256_final(&S->outer, digest); - memset(ihash, 0, 32); + memwipe(ihash, sizeof(ihash)); } void hmac_blake224_final(hmac_state *S, uint8_t *digest) { @@ -335,7 +336,7 @@ void hmac_blake224_final(hmac_state *S, uint8_t *digest) { blake224_final(&S->inner, ihash); blake224_update(&S->outer, ihash, 224); blake224_final(&S->outer, digest); - memset(ihash, 0, 32); + memwipe(ihash, sizeof(ihash)); } // keylen = number of bytes; inlen = number of bytes diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d22158dfc..5070cc169 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -616,7 +616,7 @@ block Blockchain::pop_block_from_blockchain() // that might not be always true. Unlikely though, and always relaying // these again might cause a spike of traffic as many nodes re-relay // all the transactions in a popped block when a reorg happens. - bool r = m_tx_pool.add_tx(tx, tvc, true, true, false, version); + bool r = m_tx_pool.add_tx(tx, tvc, relay_method::block, true, version); if (!r) { LOG_ERROR("Error returning transaction to tx_pool"); @@ -1765,7 +1765,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id { cryptonote::tx_memory_pool::tx_details td; cryptonote::blobdata blob; - if (m_tx_pool.have_tx(txid)) + if (m_tx_pool.have_tx(txid, relay_category::legacy)) { if (m_tx_pool.get_transaction_info(txid, td)) { @@ -3641,7 +3641,7 @@ void Blockchain::return_tx_to_pool(std::vector<std::pair<transaction, blobdata>> // all the transactions in a popped block when a reorg happens. const size_t weight = get_transaction_weight(tx.first, tx.second.size()); const crypto::hash tx_hash = get_transaction_hash(tx.first); - if (!m_tx_pool.add_tx(tx.first, tx_hash, tx.second, weight, tvc, true, true, false, version)) + if (!m_tx_pool.add_tx(tx.first, tx_hash, tx.second, weight, tvc, relay_method::block, true, version)) { MERROR("Failed to return taken transaction with hash: " << get_transaction_hash(tx.first) << " to tx_pool"); } @@ -3661,7 +3661,7 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids) uint64_t fee; bool relayed, do_not_relay, double_spend_seen, pruned; MINFO("Removing txid " << txid << " from the pool"); - if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned)) + if(!m_tx_pool.have_tx(txid, relay_category::all) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned)) { MERROR("Failed to remove txid " << txid << " from the pool"); res = false; @@ -4895,9 +4895,9 @@ void Blockchain::remove_txpool_tx(const crypto::hash &txid) m_db->remove_txpool_tx(txid); } -uint64_t Blockchain::get_txpool_tx_count(bool include_unrelayed_txes) const +uint64_t Blockchain::get_txpool_tx_count(bool include_sensitive) const { - return m_db->get_txpool_tx_count(include_unrelayed_txes); + return m_db->get_txpool_tx_count(include_sensitive ? relay_category::all : relay_category::broadcasted); } bool Blockchain::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const @@ -4905,19 +4905,24 @@ bool Blockchain::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t & return m_db->get_txpool_tx_meta(txid, meta); } -bool Blockchain::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const +bool Blockchain::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const { - return m_db->get_txpool_tx_blob(txid, bd); + return m_db->get_txpool_tx_blob(txid, bd, tx_category); } -cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid) const +cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const { - return m_db->get_txpool_tx_blob(txid); + return m_db->get_txpool_tx_blob(txid, tx_category); } -bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const +bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, relay_category tx_category) const { - return m_db->for_all_txpool_txes(f, include_blob, include_unrelayed_txes); + return m_db->for_all_txpool_txes(f, include_blob, tx_category); +} + +bool Blockchain::txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category) +{ + return m_db->txpool_tx_matches_category(tx_hash, category); } void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold, blockchain_db_sync_mode sync_mode, bool fast_sync) @@ -5074,7 +5079,12 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get return; } const size_t size_needed = 4 + nblocks * (sizeof(crypto::hash) * 2); - if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed) + if(checkpoints.size() != size_needed) + { + MERROR("Failed to load hashes - unexpected data size"); + return; + } + else if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP) { p += sizeof(uint32_t); m_blocks_hash_of_hashes.reserve(nblocks); @@ -5098,7 +5108,7 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get CRITICAL_REGION_LOCAL(m_tx_pool); std::vector<transaction> txs; - m_tx_pool.get_transactions(txs); + m_tx_pool.get_transactions(txs, true); size_t tx_weight; uint64_t fee; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 6467031c2..0aecdcb57 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -964,11 +964,12 @@ namespace cryptonote void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta); void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta); void remove_txpool_tx(const crypto::hash &txid); - uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const; + uint64_t get_txpool_tx_count(bool include_sensitive = false) const; bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const; - bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const; - cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const; - bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const; + bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const; + cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const; + bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, relay_category tx_category = relay_category::broadcasted) const; + bool txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category); bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); } uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights); @@ -1042,7 +1043,7 @@ namespace cryptonote std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, std::vector<output_data_t>>> m_scan_table; std::unordered_map<crypto::hash, crypto::hash> m_blocks_longhash_table; - // SHA-3 hashes for each block and for fast pow checking + // Keccak hashes for each block and for fast pow checking std::vector<std::pair<crypto::hash, crypto::hash>> m_blocks_hash_of_hashes; std::vector<std::pair<crypto::hash, uint64_t>> m_blocks_hash_check; std::vector<crypto::hash> m_blocks_txs_check; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 02620996e..cf23a652c 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include <boost/algorithm/string.hpp> +#include <boost/uuid/nil_generator.hpp> #include "string_tools.h" using namespace epee; @@ -753,7 +754,7 @@ namespace cryptonote return false; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash) { tvc = {}; @@ -817,7 +818,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash) { if(!check_tx_syntax(tx)) { @@ -946,23 +947,29 @@ namespace cryptonote return ret; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_txs(const std::vector<tx_blob_entry>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_txs(const epee::span<const tx_blob_entry> tx_blobs, epee::span<tx_verification_context> tvc, relay_method tx_relay, bool relayed) { TRY_ENTRY(); - CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + + if (tx_blobs.size() != tvc.size()) + { + MERROR("tx_blobs and tx_verification_context spans must have equal size"); + return false; + } struct result { bool res; cryptonote::transaction tx; crypto::hash hash; }; std::vector<result> results(tx_blobs.size()); - tvc.resize(tx_blobs.size()); + CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - std::vector<tx_blob_entry>::const_iterator it = tx_blobs.begin(); + epee::span<tx_blob_entry>::const_iterator it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { tpool.submit(&waiter, [&, i, it] { try { - results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, keeped_by_block, relayed, do_not_relay); + results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash); } catch (const std::exception &e) { @@ -978,7 +985,7 @@ namespace cryptonote for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { if (!results[i].res) continue; - if(m_mempool.have_tx(results[i].hash)) + if(m_mempool.have_tx(results[i].hash, relay_category::legacy)) { LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool"); already_have[i] = true; @@ -993,7 +1000,7 @@ namespace cryptonote tpool.submit(&waiter, [&, i, it] { try { - results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, keeped_by_block, relayed, do_not_relay); + results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash); } catch (const std::exception &e) { @@ -1014,7 +1021,7 @@ namespace cryptonote tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res}); } if (!tx_info.empty()) - handle_incoming_tx_accumulated_batch(tx_info, keeped_by_block); + handle_incoming_tx_accumulated_batch(tx_info, tx_relay == relay_method::block); bool ok = true; it = tx_blobs.begin(); @@ -1024,13 +1031,14 @@ namespace cryptonote ok = false; continue; } - if (keeped_by_block) + if (tx_relay == relay_method::block) get_blockchain_storage().on_new_tx_from_block(results[i].tx); if (already_have[i]) continue; const uint64_t weight = results[i].tx.pruned ? get_pruned_transaction_weight(results[i].tx) : get_transaction_weight(results[i].tx, it->blob.size()); - ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i].blob, weight, tvc[i], keeped_by_block, relayed, do_not_relay); + ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i].blob, weight, tvc[i], tx_relay, relayed); + if(tvc[i].m_verifivation_failed) {MERROR_VER("Transaction verification failed: " << results[i].hash);} else if(tvc[i].m_verifivation_impossible) @@ -1044,19 +1052,9 @@ namespace cryptonote CATCH_ENTRY_L0("core::handle_incoming_txs()", false); } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) - { - std::vector<tx_blob_entry> tx_blobs; - tx_blobs.push_back(tx_blob); - std::vector<tx_verification_context> tvcv(1); - bool r = handle_incoming_txs(tx_blobs, tvcv, keeped_by_block, relayed, do_not_relay); - tvc = tvcv[0]; - return r; - } - //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, relay_method tx_relay, bool relayed) { - return handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, keeped_by_block, relayed, do_not_relay); + 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 @@ -1246,13 +1244,13 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::add_new_tx(transaction& tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed) { crypto::hash tx_hash = get_transaction_hash(tx); blobdata bl; t_serializable_object_to_blob(tx, bl); size_t tx_weight = get_transaction_weight(tx, bl.size()); - return add_new_tx(tx, tx_hash, bl, tx_weight, tvc, keeped_by_block, relayed, do_not_relay); + return add_new_tx(tx, tx_hash, bl, tx_weight, tvc, tx_relay, relayed); } //----------------------------------------------------------------------------------------------- size_t core::get_blockchain_total_transactions() const @@ -1260,9 +1258,9 @@ namespace cryptonote return m_blockchain_storage.get_total_transactions(); } //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed) { - if(m_mempool.have_tx(tx_hash)) + if(m_mempool.have_tx(tx_hash, relay_category::legacy)) { LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); return true; @@ -1275,40 +1273,62 @@ namespace cryptonote } uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); - return m_mempool.add_tx(tx, tx_hash, blob, tx_weight, tvc, keeped_by_block, relayed, do_not_relay, version); + return m_mempool.add_tx(tx, tx_hash, blob, tx_weight, tvc, tx_relay, relayed, version); } //----------------------------------------------------------------------------------------------- bool core::relay_txpool_transactions() { // we attempt to relay txes that should be relayed, but were not - std::vector<std::pair<crypto::hash, cryptonote::blobdata>> txs; + std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>> txs; if (m_mempool.get_relayable_transactions(txs) && !txs.empty()) { - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); - tx_verification_context tvc = AUTO_VAL_INIT(tvc); - NOTIFY_NEW_TRANSACTIONS::request r; - for (auto it = txs.begin(); it != txs.end(); ++it) + NOTIFY_NEW_TRANSACTIONS::request public_req{}; + NOTIFY_NEW_TRANSACTIONS::request private_req{}; + for (auto& tx : txs) { - r.txs.push_back(it->second); + switch (std::get<2>(tx)) + { + default: + case relay_method::none: + break; + case relay_method::local: + private_req.txs.push_back(std::move(std::get<1>(tx))); + break; + case relay_method::block: + case relay_method::flood: + public_req.txs.push_back(std::move(std::get<1>(tx))); + break; + } } - get_protocol()->relay_transactions(r, fake_context); - m_mempool.set_relayed(txs); + + /* All txes are sent on randomized timers per connection in + `src/cryptonote_protocol/levin_notify.cpp.` They are either sent with + "white noise" delays or via diffusion (Dandelion++ fluff). So + re-relaying public and private _should_ be acceptable here. */ + const boost::uuids::uuid source = boost::uuids::nil_uuid(); + if (!public_req.txs.empty()) + get_protocol()->relay_transactions(public_req, source, epee::net_utils::zone::public_); + if (!private_req.txs.empty()) + get_protocol()->relay_transactions(private_req, source, epee::net_utils::zone::invalid); } return true; } //----------------------------------------------------------------------------------------------- - void core::on_transaction_relayed(const cryptonote::blobdata& tx_blob) + void core::on_transactions_relayed(const epee::span<const cryptonote::blobdata> tx_blobs, const relay_method tx_relay) { - std::vector<std::pair<crypto::hash, cryptonote::blobdata>> txs; - cryptonote::transaction tx; - crypto::hash tx_hash; - if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash)) + std::vector<crypto::hash> tx_hashes{}; + tx_hashes.resize(tx_blobs.size()); + + cryptonote::transaction tx{}; + for (std::size_t i = 0; i < tx_blobs.size(); ++i) { - LOG_ERROR("Failed to parse relayed transaction"); - return; + if (!parse_and_validate_tx_from_blob(tx_blobs[i], tx, tx_hashes[i])) + { + LOG_ERROR("Failed to parse relayed transaction"); + return; + } } - txs.push_back(std::make_pair(tx_hash, std::move(tx_blob))); - m_mempool.set_relayed(txs); + m_mempool.set_relayed(epee::to_span(tx_hashes), tx_relay); } //----------------------------------------------------------------------------------------------- bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) @@ -1369,7 +1389,7 @@ namespace cryptonote for (const auto &tx_hash: b.tx_hashes) { cryptonote::blobdata txblob; - CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, txblob), "Transaction not found in pool"); + CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, txblob, relay_category::all), "Transaction not found in pool"); bce.txs.push_back({txblob, crypto::null_hash}); } return bce; @@ -1573,14 +1593,14 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transaction(const crypto::hash &id, cryptonote::blobdata& tx) const + bool core::get_pool_transaction(const crypto::hash &id, cryptonote::blobdata& tx, relay_category tx_category) const { - return m_mempool.get_transaction(id, tx); + return m_mempool.get_transaction(id, tx, tx_category); } //----------------------------------------------------------------------------------------------- bool core::pool_has_tx(const crypto::hash &id) const { - return m_mempool.have_tx(id); + return m_mempool.have_tx(id, relay_category::legacy); } //----------------------------------------------------------------------------------------------- bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index f69ac3509..4b67984ab 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -35,7 +35,9 @@ #include <boost/program_options/options_description.hpp> #include <boost/program_options/variables_map.hpp> +#include "cryptonote_core/i_core_events.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" +#include "cryptonote_protocol/enums.h" #include "storages/portable_storage_template_helper.h" #include "common/download.h" #include "common/command_line.h" @@ -46,6 +48,7 @@ #include "cryptonote_basic/cryptonote_stat_info.h" #include "warnings.h" #include "crypto/hash.h" +#include "span.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) @@ -77,7 +80,7 @@ namespace cryptonote * limited to, communication among the Blockchain, the transaction pool, * any miners, and the network. */ - class core: public i_miner_handler + class core final: public i_miner_handler, public i_core_events { public: @@ -115,14 +118,12 @@ namespace cryptonote * * @param tx_blob the tx to handle * @param tvc metadata about the transaction's validity - * @param keeped_by_block if the transaction has been in a block + * @param tx_relay how the transaction was received * @param relayed whether or not the transaction was relayed to us - * @param do_not_relay whether to prevent the transaction from being relayed * * @return true if the transaction was accepted, false otherwise */ - bool handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, relay_method tx_relay, bool relayed); /** * @brief handles a list of incoming transactions @@ -130,15 +131,35 @@ namespace cryptonote * Parses incoming transactions and, if nothing is obviously wrong, * passes them along to the transaction pool * + * @pre `tx_blobs.size() == tvc.size()` + * * @param tx_blobs the txs to handle * @param tvc metadata about the transactions' validity - * @param keeped_by_block if the transactions have been in a block + * @param tx_relay how the transaction was received. * @param relayed whether or not the transactions were relayed to us - * @param do_not_relay whether to prevent the transactions from being relayed * * @return true if the transactions were accepted, false otherwise */ - bool handle_incoming_txs(const std::vector<tx_blob_entry>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_txs(epee::span<const tx_blob_entry> tx_blobs, epee::span<tx_verification_context> tvc, relay_method tx_relay, bool relayed); + + /** + * @brief handles a list of incoming transactions + * + * Parses incoming transactions and, if nothing is obviously wrong, + * passes them along to the transaction pool + * + * @param tx_blobs the txs to handle + * @param tvc metadata about the transactions' validity + * @param tx_relay how the transaction was received. + * @param relayed whether or not the transactions were relayed to us + * + * @return true if the transactions were accepted, false otherwise + */ + bool handle_incoming_txs(const std::vector<tx_blob_entry>& tx_blobs, std::vector<tx_verification_context>& tvc, relay_method tx_relay, bool relayed) + { + tvc.resize(tx_blobs.size()); + return handle_incoming_txs(epee::to_span(tx_blobs), epee::to_mut_span(tvc), tx_relay, relayed); + } /** * @brief handles an incoming block @@ -212,9 +233,10 @@ namespace cryptonote virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); /** - * @brief called when a transaction is relayed + * @brief called when a transaction is relayed. + * @note Should only be invoked from `levin_notify`. */ - virtual void on_transaction_relayed(const cryptonote::blobdata& tx); + virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, relay_method tx_relay) final; /** @@ -440,11 +462,11 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_transactions - * @param include_unrelayed_txes include unrelayed txes in result + * @param include_sensitive_txes include private transactions * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes = true) const; + bool get_pool_transactions(std::vector<transaction>& txs, bool include_sensitive_txes = false) const; /** * @copydoc tx_memory_pool::get_txpool_backlog @@ -455,34 +477,34 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_transactions - * @param include_unrelayed_txes include unrelayed txes in result + * @param include_sensitive_txes include private transactions * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const; + bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive_txes = false) const; /** * @copydoc tx_memory_pool::get_transactions - * @param include_unrelayed_txes include unrelayed txes in result + * @param include_sensitive_txes include private transactions * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const; + bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_sensitive_txes = false) const; /** * @copydoc tx_memory_pool::get_transaction * * @note see tx_memory_pool::get_transaction */ - bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx) const; + bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx, relay_category tx_category) const; /** * @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info - * @param include_unrelayed_txes include unrelayed txes in result + * @param include_sensitive_txes include private transactions * * @note see tx_memory_pool::get_pool_transactions_and_spent_keys_info */ - bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_unrelayed_txes = true) const; + bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_txes = false) const; /** * @copydoc tx_memory_pool::get_pool_for_rpc @@ -852,11 +874,11 @@ namespace cryptonote * @param tx_hash the transaction's hash * @param blob the transaction as a blob * @param tx_weight the weight of the transaction + * @param tx_relay how the transaction was received * @param relayed whether or not the transaction was relayed to us - * @param do_not_relay whether to prevent the transaction from being relayed * */ - bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed); /** * @brief add a new transaction to the transaction pool @@ -865,15 +887,14 @@ namespace cryptonote * * @param tx the transaction to add * @param tvc return-by-reference metadata about the transaction's verification process - * @param keeped_by_block whether or not the transaction has been in a block + * @param tx_relay how the transaction was received * @param relayed whether or not the transaction was relayed to us - * @param do_not_relay whether to prevent the transaction from being relayed * * @return true if the transaction is already in the transaction pool, * is already in a block on the Blockchain, or is successfully added * to the transaction pool */ - bool add_new_tx(transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool add_new_tx(transaction& tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed); /** * @copydoc Blockchain::add_new_block @@ -929,8 +950,8 @@ namespace cryptonote bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const; void set_semantics_failed(const crypto::hash &tx_hash); - bool handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_tx_post(const tx_blob_entry &tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash); + bool handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash); struct tx_verification_batch_info { const cryptonote::transaction *tx; crypto::hash tx_hash; tx_verification_context &tvc; bool &result; }; bool handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block); diff --git a/src/cryptonote_core/i_core_events.h b/src/cryptonote_core/i_core_events.h new file mode 100644 index 000000000..f49fbdf07 --- /dev/null +++ b/src/cryptonote_core/i_core_events.h @@ -0,0 +1,44 @@ +// 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. + +#pragma once + +#include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_protocol/enums.h" +#include "span.h" + +namespace cryptonote +{ + struct i_core_events + { + virtual ~i_core_events() noexcept + {} + + virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, relay_method tx_relay) = 0; + }; +} diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 392e611e9..1bc475879 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -115,8 +115,10 @@ namespace cryptonote } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version) { + const bool kept_by_block = (tx_relay == relay_method::block); + // this should already be called with that lock, but let's make it explicit for clarity CRITICAL_REGION_LOCAL(m_transactions_lock); @@ -227,7 +229,7 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; - cryptonote::txpool_tx_meta_t meta; + cryptonote::txpool_tx_meta_t meta{}; bool ch_inp_res = check_tx_inputs([&tx]()->cryptonote::transaction&{ return tx; }, id, max_used_block_height, max_used_block_id, tvc, kept_by_block); if(!ch_inp_res) { @@ -241,11 +243,10 @@ namespace cryptonote meta.max_used_block_height = 0; meta.last_failed_height = 0; meta.last_failed_id = null_hash; - meta.kept_by_block = kept_by_block; meta.receive_time = receive_time; meta.last_relayed_time = time(NULL); meta.relayed = relayed; - meta.do_not_relay = do_not_relay; + meta.set_relay_method(tx_relay); meta.double_spend_seen = have_tx_keyimges_as_spent(tx); meta.pruned = tx.pruned; meta.bf_padding = 0; @@ -256,15 +257,16 @@ namespace cryptonote m_parsed_tx_cache.insert(std::make_pair(id, tx)); CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); - m_blockchain.add_txpool_tx(id, blob, meta); - if (!insert_key_images(tx, id, kept_by_block)) + if (!insert_key_images(tx, id, tx_relay)) return false; + + m_blockchain.add_txpool_tx(id, blob, meta); m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id); lock.commit(); } catch (const std::exception &e) { - MERROR("transaction already exists at inserting in memory pool: " << e.what()); + MERROR("Error adding transaction to txpool: " << e.what()); return false; } tvc.m_verifivation_impossible = true; @@ -280,7 +282,6 @@ namespace cryptonote { //update transactions container meta.weight = tx_weight; - meta.kept_by_block = kept_by_block; meta.fee = fee; meta.max_used_block_id = max_used_block_id; meta.max_used_block_height = max_used_block_height; @@ -289,7 +290,7 @@ namespace cryptonote meta.receive_time = receive_time; meta.last_relayed_time = time(NULL); meta.relayed = relayed; - meta.do_not_relay = do_not_relay; + meta.set_relay_method(tx_relay); meta.double_spend_seen = false; meta.pruned = tx.pruned; meta.bf_padding = 0; @@ -302,20 +303,21 @@ namespace cryptonote CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); m_blockchain.remove_txpool_tx(id); - m_blockchain.add_txpool_tx(id, blob, meta); - if (!insert_key_images(tx, id, kept_by_block)) + if (!insert_key_images(tx, id, tx_relay)) return false; + + m_blockchain.add_txpool_tx(id, blob, meta); m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id); lock.commit(); } catch (const std::exception &e) { - MERROR("internal error: transaction already exists at inserting in memory pool: " << e.what()); + MERROR("internal error: error adding transaction to txpool: " << e.what()); return false; } tvc.m_added_to_pool = true; - if(meta.fee > 0 && !do_not_relay) + if(meta.fee > 0 && tx_relay != relay_method::none) tvc.m_should_be_relayed = true; } @@ -331,7 +333,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version) { crypto::hash h = null_hash; size_t blob_size = 0; @@ -339,7 +341,7 @@ namespace cryptonote t_serializable_object_to_blob(tx, bl); if (bl.size() == 0 || !get_transaction_hash(tx, h)) return false; - return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, keeped_by_block, relayed, do_not_relay, version); + return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, tx_relay, relayed, version); } //--------------------------------------------------------------------------------- size_t tx_memory_pool::get_txpool_weight() const @@ -375,7 +377,7 @@ namespace cryptonote txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(txid, meta)) { - MERROR("Failed to find tx in txpool"); + MERROR("Failed to find tx_meta in txpool"); return; } // don't prune the kept_by_block ones, they're likely added because we're adding a block with those @@ -384,7 +386,7 @@ namespace cryptonote --it; continue; } - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all); cryptonote::transaction_prefix tx; if (!parse_and_validate_tx_prefix_from_blob(txblob, tx)) { @@ -413,17 +415,38 @@ namespace cryptonote MINFO("Pool weight after pruning is larger than limit: " << m_txpool_weight << "/" << bytes); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::insert_key_images(const transaction_prefix &tx, const crypto::hash &id, bool kept_by_block) + bool tx_memory_pool::insert_key_images(const transaction_prefix &tx, const crypto::hash &id, relay_method tx_relay) { for(const auto& in: tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false); std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image]; - CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: kept_by_block=" << kept_by_block - << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL - << "tx_id=" << id ); - auto ins_res = kei_image_set.insert(id); - CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set"); + + /* If any existing key-image in the set is publicly visible AND this is + not forcibly "kept_by_block", then fail (duplicate key image). If all + existing key images are supposed to be hidden, we silently allow so + that the node doesn't leak knowledge of a local/stem tx. */ + bool visible = false; + if (tx_relay != relay_method::block) + { + for (const crypto::hash& other_id : kei_image_set) + visible |= m_blockchain.txpool_tx_matches_category(other_id, relay_category::legacy); + } + + CHECK_AND_ASSERT_MES(!visible, false, "internal error: tx_relay=" << unsigned(tx_relay) + << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL + << "tx_id=" << id); + + /* If adding a tx (hash) that already exists, fail only if the tx has + been publicly "broadcast" previously. This way, when a private tx is + received for the first time from a remote node, "this" node will + respond as-if it were seen for the first time. LMDB does the + "hard-check" on key-images, so the effect is overwriting the existing + tx_pool metadata and "first seen" time. */ + const bool new_or_previously_private = + kei_image_set.insert(id).second || + !m_blockchain.txpool_tx_matches_category(id, relay_category::legacy); + CHECK_AND_ASSERT_MES(new_or_previously_private, false, "internal error: try to insert duplicate iterator in key_image set"); } ++m_cookie; return true; @@ -475,10 +498,10 @@ namespace cryptonote txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(id, meta)) { - MERROR("Failed to find tx in txpool"); + MERROR("Failed to find tx_meta in txpool"); return false; } - txblob = m_blockchain.get_txpool_tx_blob(id); + txblob = m_blockchain.get_txpool_tx_blob(id, relay_category::all); auto ci = m_parsed_tx_cache.find(id); if (ci != m_parsed_tx_cache.end()) { @@ -533,7 +556,7 @@ namespace cryptonote MERROR("Failed to find tx in txpool"); return false; } - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all); auto ci = m_parsed_tx_cache.find(txid); if (ci != m_parsed_tx_cache.end()) { @@ -611,7 +634,7 @@ namespace cryptonote remove.push_back(std::make_pair(txid, meta.weight)); } return true; - }, false); + }, false, relay_category::all); if (!remove.empty()) { @@ -621,7 +644,7 @@ namespace cryptonote const crypto::hash &txid = entry.first; try { - cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid, relay_category::all); cryptonote::transaction_prefix tx; if (!parse_and_validate_tx_prefix_from_blob(bd, tx)) { @@ -649,7 +672,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::get_relayable_transactions(std::vector<std::pair<crypto::hash, cryptonote::blobdata>> &txs) const + bool tx_memory_pool::get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>> &txs) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -667,8 +690,7 @@ namespace cryptonote { try { - cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid); - txs.push_back(std::make_pair(txid, bd)); + txs.emplace_back(txid, m_blockchain.get_txpool_tx_blob(txid, relay_category::all), meta.get_relay_method()); } catch (const std::exception &e) { @@ -678,26 +700,27 @@ namespace cryptonote } } return true; - }, false); + }, false, relay_category::relayable); return true; } //--------------------------------------------------------------------------------- - void tx_memory_pool::set_relayed(const std::vector<std::pair<crypto::hash, cryptonote::blobdata>> &txs) + void tx_memory_pool::set_relayed(const epee::span<const crypto::hash> hashes, const relay_method method) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const time_t now = time(NULL); LockedTXN lock(m_blockchain); - for (auto it = txs.begin(); it != txs.end(); ++it) + for (const auto& hash : hashes) { try { txpool_tx_meta_t meta; - if (m_blockchain.get_txpool_tx_meta(it->first, meta)) + if (m_blockchain.get_txpool_tx_meta(hash, meta)) { meta.relayed = true; meta.last_relayed_time = now; - m_blockchain.update_txpool_tx(it->first, meta); + meta.set_relay_method(method); + m_blockchain.update_txpool_tx(hash, meta); } } catch (const std::exception &e) @@ -709,18 +732,19 @@ namespace cryptonote lock.commit(); } //--------------------------------------------------------------------------------- - size_t tx_memory_pool::get_transactions_count(bool include_unrelayed_txes) const + size_t tx_memory_pool::get_transactions_count(bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - return m_blockchain.get_txpool_tx_count(include_unrelayed_txes); + return m_blockchain.get_txpool_tx_count(include_sensitive); } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes) const + void tx_memory_pool::get_transactions(std::vector<transaction>& txs, bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); + const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; + txs.reserve(m_blockchain.get_txpool_tx_count(include_sensitive)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ transaction tx; if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx))) @@ -732,39 +756,42 @@ namespace cryptonote tx.set_hash(txid); txs.push_back(std::move(tx)); return true; - }, true, include_unrelayed_txes); + }, true, category); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes) const + void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); + const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; + txs.reserve(m_blockchain.get_txpool_tx_count(include_sensitive)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ txs.push_back(txid); return true; - }, false, include_unrelayed_txes); + }, false, category); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes) const + void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); - backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); + const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; + backlog.reserve(m_blockchain.get_txpool_tx_count(include_sensitive)); m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ backlog.push_back({meta.weight, meta.fee, meta.receive_time - now}); return true; - }, false, include_unrelayed_txes); + }, false, category); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes) const + void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_sensitive) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); + const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted; std::map<uint64_t, txpool_histo> agebytes; - stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes); + stats.txs_total = m_blockchain.get_txpool_tx_count(include_sensitive); std::vector<uint32_t> weights; weights.reserve(stats.txs_total); m_blockchain.for_all_txpool_txes([&stats, &weights, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ @@ -789,7 +816,8 @@ namespace cryptonote if (meta.double_spend_seen) ++stats.num_double_spends; return true; - }, false, include_unrelayed_txes); + }, false, category); + stats.bytes_med = epee::misc_utils::median(weights); if (stats.txs_total > 1) { @@ -847,8 +875,10 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - tx_infos.reserve(m_blockchain.get_txpool_tx_count()); - key_image_infos.reserve(m_blockchain.get_txpool_tx_count()); + const relay_category category = include_sensitive_data ? relay_category::all : relay_category::broadcasted; + const size_t count = m_blockchain.get_txpool_tx_count(include_sensitive_data); + tx_infos.reserve(count); + key_image_infos.reserve(count); m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ tx_info txi; txi.id_hash = epee::string_tools::pod_to_hex(txid); @@ -879,7 +909,7 @@ namespace cryptonote txi.double_spend_seen = meta.double_spend_seen; tx_infos.push_back(std::move(txi)); return true; - }, true, include_sensitive_data); + }, true, category); txpool_tx_meta_t meta; for (const key_images_container::value_type& kee : m_spent_key_images) { @@ -889,30 +919,13 @@ namespace cryptonote ki.id_hash = epee::string_tools::pod_to_hex(k_image); for (const crypto::hash& tx_id_hash : kei_image_set) { - if (!include_sensitive_data) - { - try - { - if (!m_blockchain.get_txpool_tx_meta(tx_id_hash, meta)) - { - MERROR("Failed to get tx meta from txpool"); - return false; - } - if (!meta.relayed) - // Do not include that transaction if in restricted mode and it's not relayed - continue; - } - catch (const std::exception &e) - { - MERROR("Failed to get tx meta from txpool: " << e.what()); - return false; - } - } - ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash)); + if (m_blockchain.txpool_tx_matches_category(tx_id_hash, category)) + ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash)); } + // Only return key images for which we have at least one tx that we can show for them if (!ki.txs_hashes.empty()) - key_image_infos.push_back(ki); + key_image_infos.push_back(std::move(ki)); } return true; } @@ -948,18 +961,19 @@ namespace cryptonote txi.double_spend_seen = meta.double_spend_seen; tx_infos.push_back(txi); return true; - }, true, false); + }, true, relay_category::broadcasted); for (const key_images_container::value_type& kee : m_spent_key_images) { std::vector<crypto::hash> tx_hashes; const std::unordered_set<crypto::hash>& kei_image_set = kee.second; for (const crypto::hash& tx_id_hash : kei_image_set) { - tx_hashes.push_back(tx_id_hash); + if (m_blockchain.txpool_tx_matches_category(tx_id_hash, relay_category::broadcasted)) + tx_hashes.push_back(tx_id_hash); } - const crypto::key_image& k_image = kee.first; - key_image_infos[k_image] = std::move(tx_hashes); + if (!tx_hashes.empty()) + key_image_infos[kee.first] = std::move(tx_hashes); } return true; } @@ -973,19 +987,26 @@ namespace cryptonote for (const auto& image : key_images) { - spent.push_back(m_spent_key_images.find(image) == m_spent_key_images.end() ? false : true); + bool is_spent = false; + const auto found = m_spent_key_images.find(image); + if (found != m_spent_key_images.end()) + { + for (const crypto::hash& tx_hash : found->second) + is_spent |= m_blockchain.txpool_tx_matches_category(tx_hash, relay_category::broadcasted); + } + spent.push_back(is_spent); } return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob) const + bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob, relay_category tx_category) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); try { - return m_blockchain.get_txpool_tx_blob(id, txblob); + return m_blockchain.get_txpool_tx_blob(id, txblob, tx_category); } catch (const std::exception &e) { @@ -1009,11 +1030,11 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx(const crypto::hash &id) const + bool tx_memory_pool::have_tx(const crypto::hash &id, relay_category tx_category) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - return m_blockchain.get_db().txpool_has_tx(id); + return m_blockchain.get_db().txpool_has_tx(id, tx_category); } //--------------------------------------------------------------------------------- bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) const @@ -1032,7 +1053,14 @@ namespace cryptonote bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) const { CRITICAL_REGION_LOCAL(m_transactions_lock); - return m_spent_key_images.end() != m_spent_key_images.find(key_im); + bool spent = false; + const auto found = m_spent_key_images.find(key_im); + if (found != m_spent_key_images.end()) + { + for (const crypto::hash& tx_hash : found->second) + spent |= m_blockchain.txpool_tx_matches_category(tx_hash, relay_category::broadcasted); + } + return spent; } //--------------------------------------------------------------------------------- void tx_memory_pool::lock() const @@ -1217,13 +1245,14 @@ namespace cryptonote << "weight: " << meta.weight << std::endl << "fee: " << print_money(meta.fee) << std::endl << "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl + << "is_local" << (meta.is_local ? 'T' : 'F') << std::endl << "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl << "max_used_block_height: " << meta.max_used_block_height << std::endl << "max_used_block_id: " << meta.max_used_block_id << std::endl << "last_failed_height: " << meta.last_failed_height << std::endl << "last_failed_id: " << meta.last_failed_id << std::endl; return true; - }, !short_format); + }, !short_format, relay_category::all); return ss.str(); } @@ -1255,7 +1284,7 @@ namespace cryptonote for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it) { txpool_tx_meta_t meta; - if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta)) + if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta) && !meta.matches(relay_category::legacy)) { MERROR(" failed to find tx meta"); continue; @@ -1304,7 +1333,9 @@ namespace cryptonote } } - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second); + // "local" and "stem" txes are filtered above + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second, relay_category::all); + cryptonote::transaction tx; // Skip transactions that are not ready to be @@ -1379,7 +1410,7 @@ namespace cryptonote remove.insert(txid); } return true; - }, false); + }, false, relay_category::all); size_t n_removed = 0; if (!remove.empty()) @@ -1389,7 +1420,7 @@ namespace cryptonote { try { - cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); + cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all); cryptonote::transaction tx; if (!parse_and_validate_tx_from_blob(txblob, tx)) // remove pruned ones on startup, they're meant to be temporary { @@ -1450,7 +1481,7 @@ namespace cryptonote remove.push_back(txid); return true; } - if (!insert_key_images(tx, txid, meta.kept_by_block)) + if (!insert_key_images(tx, txid, meta.get_relay_method())) { MFATAL("Failed to insert key images from txpool tx"); return false; @@ -1458,7 +1489,7 @@ namespace cryptonote m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.weight, meta.receive_time), txid); m_txpool_weight += meta.weight; return true; - }, true); + }, true, relay_category::all); if (!r) return false; } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index dec7e3cd9..f716440ad 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -32,17 +32,20 @@ #include "include_base_utils.h" #include <set> +#include <tuple> #include <unordered_map> #include <unordered_set> #include <queue> #include <boost/serialization/version.hpp> #include <boost/utility.hpp> +#include "span.h" #include "string_tools.h" #include "syncobj.h" #include "math_helper.h" #include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/verification_context.h" +#include "cryptonote_protocol/enums.h" #include "blockchain_db/blockchain_db.h" #include "crypto/hash.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -105,9 +108,10 @@ namespace cryptonote * @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t) * * @param id the transaction's hash + * @tx_relay how the transaction was received * @param tx_weight the transaction's weight */ - bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); + bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version); /** * @brief add a transaction to the transaction pool @@ -119,14 +123,13 @@ namespace cryptonote * * @param tx the transaction to be added * @param tvc return-by-reference status about the transaction verification - * @param kept_by_block has this transaction been in a block? + * @tx_relay how the transaction was received * @param relayed was this transaction from the network or a local client? - * @param do_not_relay to avoid relaying the transaction to the network * @param version the version used to create the transaction * * @return true if the transaction passes validations, otherwise false */ - bool add_tx(transaction &tx, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); + bool add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version); /** * @brief takes a transaction with the given hash from the pool @@ -149,10 +152,11 @@ namespace cryptonote * @brief checks if the pool has a transaction with the given hash * * @param id the hash to look for + * @param tx_category a filter for txes * - * @return true if the transaction is in the pool, otherwise false + * @return true if the transaction is in the pool and meets tx_category requirements */ - bool have_tx(const crypto::hash &id) const; + bool have_tx(const crypto::hash &id, relay_category tx_category) const; /** * @brief action to take when notified of a block added to the blockchain @@ -236,37 +240,37 @@ namespace cryptonote * @brief get a list of all transactions in the pool * * @param txs return-by-reference the list of transactions - * @param include_unrelayed_txes include unrelayed txes in the result + * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes * */ - void get_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes = true) const; + void get_transactions(std::vector<transaction>& txs, bool include_sensitive = false) const; /** * @brief get a list of all transaction hashes in the pool * * @param txs return-by-reference the list of transactions - * @param include_unrelayed_txes include unrelayed txes in the result + * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes * */ - void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const; + void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive = false) const; /** * @brief get (weight, fee, receive time) for all transaction in the pool * * @param txs return-by-reference that data - * @param include_unrelayed_txes include unrelayed txes in the result + * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes * */ - void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes = true) const; + void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive = false) const; /** * @brief get a summary statistics of all transaction hashes in the pool * * @param stats return-by-reference the pool statistics - * @param include_unrelayed_txes include unrelayed txes in the result + * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes * */ - void get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const; + void get_transaction_stats(struct txpool_stats& stats, bool include_sensitive = false) const; /** * @brief get information about all transactions and key images in the pool @@ -275,11 +279,12 @@ namespace cryptonote * * @param tx_infos return-by-reference the transactions' information * @param key_image_infos return-by-reference the spent key images' information - * @param include_sensitive_data include unrelayed txes and fields that are sensitive to the node privacy + * @param include_sensitive_data return stempool, anonymity-pool, and unrelayed + * txes and fields that are sensitive to the node privacy * * @return true */ - bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data = true) const; + bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data = false) const; /** * @brief get information about all transactions and key images in the pool @@ -308,10 +313,11 @@ namespace cryptonote * * @param h the hash of the transaction to get * @param tx return-by-reference the transaction blob requested + * @param tx_relay last relay method us * * @return true if the transaction is found, otherwise false */ - bool get_transaction(const crypto::hash& h, cryptonote::blobdata& txblob) const; + bool get_transaction(const crypto::hash& h, cryptonote::blobdata& txblob, relay_category tx_category) const; /** * @brief get a list of all relayable transactions and their hashes @@ -326,21 +332,22 @@ namespace cryptonote * * @return true */ - bool get_relayable_transactions(std::vector<std::pair<crypto::hash, cryptonote::blobdata>>& txs) const; + bool get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>>& txs) const; /** * @brief tell the pool that certain transactions were just relayed * - * @param txs the list of transactions (and their hashes) + * @param hashes list of tx hashes that are about to be relayed + * @param tx_relay update how the tx left this node */ - void set_relayed(const std::vector<std::pair<crypto::hash, cryptonote::blobdata>>& txs); + void set_relayed(epee::span<const crypto::hash> hashes, relay_method tx_relay); /** * @brief get the total number of transactions in the pool * * @return the number of transactions in the pool */ - size_t get_transactions_count(bool include_unrelayed_txes = true) const; + size_t get_transactions_count(bool include_sensitive = false) const; /** * @brief get a string containing human-readable pool information @@ -441,7 +448,7 @@ namespace cryptonote * * @return true on success, false on error */ - bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, bool kept_by_block); + bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, relay_method tx_relay); /** * @brief remove old transactions from the pool @@ -544,7 +551,7 @@ namespace cryptonote * transaction on the assumption that the original will not be in a * block again. */ - typedef std::unordered_map<crypto::key_image, std::unordered_set<crypto::hash> > key_images_container; + typedef std::unordered_map<crypto::key_image, std::unordered_set<crypto::hash>> key_images_container; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) public: diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 3b456e324..ddbd45a61 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -130,7 +130,7 @@ namespace cryptonote //----------------- i_bc_protocol_layout --------------------------------------- virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context); + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone); //---------------------------------------------------------------------------------- //bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, cryptonote_connection_context& context); bool should_drop_connection(cryptonote_connection_context& context, uint32_t next_stripe); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 74ceeb41d..e20934a25 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -455,7 +455,7 @@ namespace cryptonote for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++) { cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx(*tx_blob_it, tvc, true, true, false); + m_core.handle_incoming_tx(*tx_blob_it, tvc, relay_method::block, true); if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); @@ -619,7 +619,7 @@ namespace cryptonote { MDEBUG("Incoming tx " << tx_hash << " not in pool, adding"); cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx(tx_blob, tvc, true, true, false) || tvc.m_verifivation_failed) + if(!m_core.handle_incoming_tx(tx_blob, tvc, relay_method::block, true) || tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); drop_connection(context, false, false); @@ -667,13 +667,13 @@ namespace cryptonote drop_connection(context, false, false); m_core.resume_mine(); return 1; - } - + } + size_t tx_idx = 0; for(auto& tx_hash: new_block.tx_hashes) { cryptonote::blobdata txblob; - if(m_core.get_pool_transaction(tx_hash, txblob)) + if(m_core.get_pool_transaction(tx_hash, txblob, relay_category::broadcasted)) { have_tx.push_back({txblob, crypto::null_hash}); } @@ -702,7 +702,7 @@ namespace cryptonote need_tx_indices.push_back(tx_idx); } } - + ++tx_idx; } @@ -909,8 +909,8 @@ namespace cryptonote newtxs.reserve(arg.txs.size()); for (size_t i = 0; i < arg.txs.size(); ++i) { - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, false, true, false); + cryptonote::tx_verification_context tvc{}; + m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::flood, true); if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection"); @@ -925,7 +925,7 @@ namespace cryptonote if(arg.txs.size()) { //TODO: add announce usage here - relay_transactions(arg, context); + relay_transactions(arg, context.m_connection_id, context.m_remote_address.get_zone()); } return 1; @@ -1316,7 +1316,7 @@ namespace cryptonote TIME_MEASURE_START(transactions_process_time); num_txs += block_entry.txs.size(); std::vector<tx_verification_context> tvc; - m_core.handle_incoming_txs(block_entry.txs, tvc, true, true, false); + m_core.handle_incoming_txs(block_entry.txs, tvc, relay_method::block, true); if (tvc.size() != block_entry.txs.size()) { LOG_ERROR_CCONTEXT("Internal error: tvc.size() != block_entry.txs.size()"); @@ -2344,14 +2344,14 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> - bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone) { - for(auto& tx_blob : arg.txs) - m_core.on_transaction_relayed(tx_blob); - - // no check for success, so tell core they're relayed unconditionally - m_p2p->send_txs(std::move(arg.txs), exclude_context.m_remote_address.get_zone(), exclude_context.m_connection_id, m_core.pad_transactions()); - return true; + /* Push all outgoing transactions to this function. The behavior needs to + identify how the transaction is going to be relayed, and then update the + local mempool before doing the relay. The code was already updating the + DB twice on received transactions - it is difficult to workaround this + due to the internal design. */ + return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core, m_core.pad_transactions()) != epee::net_utils::zone::invalid; } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index a67178c52..978a9ebf3 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -41,7 +41,7 @@ namespace cryptonote struct i_cryptonote_protocol { virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context)=0; - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)=0; + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone)=0; //virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0; }; @@ -54,7 +54,7 @@ namespace cryptonote { return false; } - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone) { return false; } diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h new file mode 100644 index 000000000..ad4eedf4c --- /dev/null +++ b/src/cryptonote_protocol/enums.h @@ -0,0 +1,43 @@ +// 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. + +#pragma once + +#include <cstdint> + +namespace cryptonote +{ + //! Methods tracking how a tx was received and relayed + enum class relay_method : std::uint8_t + { + none = 0, //!< Received via RPC with `do_not_relay` set + local, //!< Received via RPC; trying to send over i2p/tor, etc. + block, //!< Received in block, takes precedence over others + flood //!< Received/sent over public networks + }; +} diff --git a/src/cryptonote_protocol/fwd.h b/src/cryptonote_protocol/fwd.h new file mode 100644 index 000000000..616b48be3 --- /dev/null +++ b/src/cryptonote_protocol/fwd.h @@ -0,0 +1,37 @@ +// 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. + +#pragma once + +namespace cryptonote +{ + class core; + struct cryptonote_connection_context; + struct i_core_events; +} + diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h index 484243af5..8bc9b72fa 100644 --- a/src/cryptonote_protocol/levin_notify.h +++ b/src/cryptonote_protocol/levin_notify.h @@ -35,6 +35,7 @@ #include "byte_slice.h" #include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_protocol/fwd.h" #include "net/enums.h" #include "span.h" @@ -53,11 +54,6 @@ namespace nodetool namespace cryptonote { - struct cryptonote_connection_context; -} - -namespace cryptonote -{ namespace levin { namespace detail diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index b827221f6..99a460c78 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -28,7 +28,6 @@ #include "common/dns_utils.h" #include "common/command_line.h" -#include "version.h" #include "daemon/command_parser_executor.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -803,8 +802,7 @@ bool t_command_parser_executor::rpc_payments(const std::vector<std::string>& arg bool t_command_parser_executor::version(const std::vector<std::string>& args) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; - return true; + return m_executor.version(); } bool t_command_parser_executor::prune_blockchain(const std::vector<std::string>& args) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ed614a89b..ef9fed177 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -38,6 +38,7 @@ #include "cryptonote_basic/difficulty.h" #include "cryptonote_basic/hardfork.h" #include "rpc/rpc_payment_signature.h" +#include "rpc/rpc_version_str.h" #include <boost/format.hpp> #include <ctime> #include <string> @@ -380,7 +381,7 @@ static void get_metric_prefix(cryptonote::difficulty_type hr, double& hr_d, char prefix = 0; return; } - static const char metric_prefixes[4] = { 'k', 'M', 'G', 'T' }; + static const char metric_prefixes[] = { 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; for (size_t i = 0; i < sizeof(metric_prefixes); ++i) { if (hr < 1000000) @@ -2442,4 +2443,39 @@ bool t_rpc_command_executor::rpc_payments() return true; } +bool t_rpc_command_executor::version() +{ + cryptonote::COMMAND_RPC_GET_INFO::request req; + cryptonote::COMMAND_RPC_GET_INFO::response res; + + const char *fail_message = "Problem fetching info"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/getinfo", fail_message)) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_info(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + if (res.version.empty() || !cryptonote::rpc::is_version_string_valid(res.version)) + { + tools::fail_msg_writer() << "The daemon software version is not available."; + } + else + { + tools::success_msg_writer() << res.version; + } + + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index e8b12cb9b..af55f0e22 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -163,6 +163,8 @@ public: bool print_net_stats(); + bool version(); + bool set_bootstrap_daemon( const std::string &address, const std::string &username, diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp index 5af4e1a4a..16758215d 100644 --- a/src/daemonizer/posix_fork.cpp +++ b/src/daemonizer/posix_fork.cpp @@ -127,13 +127,18 @@ void fork(const std::string & pidfile) { quit("Unable to open output file: " + output); } +#else + if (open("/dev/null", O_WRONLY) < 0) + { + quit("Unable to open /dev/null"); + } +#endif // Also send standard error to the same log file. if (dup(1) < 0) { quit("Unable to dup output descriptor"); } -#endif } } // namespace posix diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 8d861ce29..ee70c2806 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -43,6 +43,7 @@ #include <vector> #include "cryptonote_config.h" +#include "cryptonote_protocol/fwd.h" #include "cryptonote_protocol/levin_notify.h" #include "warnings.h" #include "net/abstract_tcp_server2.h" @@ -336,7 +337,7 @@ namespace nodetool virtual void callback(p2p_connection_context& context); //----------------- i_p2p_endpoint ------------------------------------------------------------- virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections); - virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs); + virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs); virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context); virtual bool drop_connection(const epee::net_utils::connection_context_base& context); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index f8094bfa8..82abee71b 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1882,6 +1882,7 @@ namespace nodetool --i; continue; } + local_peerlist[i].last_seen = 0; #ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED be.pruning_seed = tools::make_pruning_seed(1 + (be.adr.as<epee::net_utils::ipv4_network_address>().ip()) % (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES), CRYPTONOTE_PRUNING_LOG_STRIPES); @@ -2053,13 +2054,18 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - epee::net_utils::zone node_server<t_payload_net_handler>::send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs) + epee::net_utils::zone node_server<t_payload_net_handler>::send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs) { namespace enet = epee::net_utils; - const auto send = [&txs, &source, pad_txs] (std::pair<const enet::zone, network_zone>& network) + const auto send = [&txs, &source, &core, pad_txs] (std::pair<const enet::zone, network_zone>& network) { - if (network.second.m_notifier.send_txs(std::move(txs), source, (pad_txs || network.first != enet::zone::public_))) + const bool is_public = (network.first == enet::zone::public_); + const cryptonote::relay_method tx_relay = is_public ? + cryptonote::relay_method::flood : cryptonote::relay_method::local; + + core.on_transactions_relayed(epee::to_span(txs), tx_relay); + if (network.second.m_notifier.send_txs(std::move(txs), source, (pad_txs || !is_public))) return network.first; return enet::zone::invalid; }; diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 752873666..aa1b83b83 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -34,6 +34,8 @@ #include <utility> #include <vector> #include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_protocol/enums.h" +#include "cryptonote_protocol/fwd.h" #include "net/enums.h" #include "net/net_utils_base.h" #include "p2p_protocol_defs.h" @@ -48,7 +50,7 @@ namespace nodetool struct i_p2p_endpoint { virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)=0; - virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs)=0; + virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs)=0; virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; @@ -73,7 +75,7 @@ namespace nodetool { return false; } - virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs) + virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs) { return epee::net_utils::zone::invalid; } diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index ff6fee95c..80ecc5593 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Adapted from Java code by Sarang Noether +// Paper references are to https://eprint.iacr.org/2017/1066 (revision 1 July 2018) #include <stdlib.h> #include <boost/thread/mutex.hpp> @@ -521,6 +522,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) } PERF_TIMER_STOP_BP(PROVE_v); + // PAPER LINES 41-42 PERF_TIMER_START_BP(PROVE_aLaR); for (size_t j = 0; j < M; ++j) { @@ -566,14 +568,14 @@ try_again: rct::key hash_cache = rct::hash_to_scalar(V); PERF_TIMER_START_BP(PROVE_step1); - // PAPER LINES 38-39 + // PAPER LINES 43-44 rct::key alpha = rct::skGen(); rct::key ve = vector_exponent(aL8, aR8); rct::key A; sc_mul(tmp.bytes, alpha.bytes, INV_EIGHT.bytes); rct::addKeys(A, ve, rct::scalarmultBase(tmp)); - // PAPER LINES 40-42 + // PAPER LINES 45-47 rct::keyV sL = rct::skvGen(MN), sR = rct::skvGen(MN); rct::key rho = rct::skGen(); ve = vector_exponent(sL, sR); @@ -581,7 +583,7 @@ try_again: rct::addKeys(S, ve, rct::scalarmultBase(rho)); S = rct::scalarmultKey(S, INV_EIGHT); - // PAPER LINES 43-45 + // PAPER LINES 48-50 rct::key y = hash_cache_mash(hash_cache, A, S); if (y == rct::zero()) { @@ -598,24 +600,20 @@ try_again: } // Polynomial construction by coefficients + // PAPER LINES 70-71 rct::keyV l0 = vector_subtract(aL, z); const rct::keyV &l1 = sL; - // This computes the ugly sum/concatenation from PAPER LINE 65 rct::keyV zero_twos(MN); const rct::keyV zpow = vector_powers(z, M+2); - for (size_t i = 0; i < MN; ++i) + for (size_t j = 0; j < M; ++j) { - zero_twos[i] = rct::zero(); - for (size_t j = 1; j <= M; ++j) - { - if (i >= (j-1)*N && i < j*N) + for (size_t i = 0; i < N; ++i) { - CHECK_AND_ASSERT_THROW_MES(1+j < zpow.size(), "invalid zpow index"); - CHECK_AND_ASSERT_THROW_MES(i-(j-1)*N < twoN.size(), "invalid twoN index"); - sc_muladd(zero_twos[i].bytes, zpow[1+j].bytes, twoN[i-(j-1)*N].bytes, zero_twos[i].bytes); + CHECK_AND_ASSERT_THROW_MES(j+2 < zpow.size(), "invalid zpow index"); + CHECK_AND_ASSERT_THROW_MES(i < twoN.size(), "invalid twoN index"); + sc_mul(zero_twos[j*N+i].bytes,zpow[j+2].bytes,twoN[i].bytes); } - } } rct::keyV r0 = vector_add(aR, z); @@ -624,7 +622,7 @@ try_again: r0 = vector_add(r0, zero_twos); rct::keyV r1 = hadamard(yMN, sR); - // Polynomial construction before PAPER LINE 46 + // Polynomial construction before PAPER LINE 51 rct::key t1_1 = inner_product(l0, r1); rct::key t1_2 = inner_product(l1, r0); rct::key t1; @@ -634,7 +632,7 @@ try_again: PERF_TIMER_STOP_BP(PROVE_step1); PERF_TIMER_START_BP(PROVE_step2); - // PAPER LINES 47-48 + // PAPER LINES 52-53 rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); rct::key T1, T2; @@ -648,7 +646,7 @@ try_again: ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes); ge_p3_tobytes(T2.bytes, &p3); - // PAPER LINES 49-51 + // PAPER LINES 54-56 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); if (x == rct::zero()) { @@ -657,7 +655,7 @@ try_again: goto try_again; } - // PAPER LINES 52-53 + // PAPER LINES 61-63 rct::key taux; sc_mul(taux.bytes, tau1.bytes, x.bytes); rct::key xsq; @@ -671,7 +669,7 @@ try_again: rct::key mu; sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes); - // PAPER LINES 54-57 + // PAPER LINES 58-60 rct::keyV l = l0; l = vector_add(l, vector_scalar(l1, x)); rct::keyV r = r0; @@ -690,7 +688,7 @@ try_again: CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed"); #endif - // PAPER LINES 32-33 + // PAPER LINE 6 rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t); if (x_ip == rct::zero()) { @@ -725,20 +723,19 @@ try_again: PERF_TIMER_STOP_BP(PROVE_step3); PERF_TIMER_START_BP(PROVE_step4); - // PAPER LINE 13 const rct::keyV *scale = &yinvpow; while (nprime > 1) { - // PAPER LINE 15 + // PAPER LINE 20 nprime /= 2; - // PAPER LINES 16-17 + // PAPER LINES 21-22 PERF_TIMER_START_BP(PROVE_inner_product); rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); PERF_TIMER_STOP_BP(PROVE_inner_product); - // PAPER LINES 18-19 + // PAPER LINES 23-24 PERF_TIMER_START_BP(PROVE_LR); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, scale, &ge_p3_H, &tmp); @@ -746,7 +743,7 @@ try_again: R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, scale, &ge_p3_H, &tmp); PERF_TIMER_STOP_BP(PROVE_LR); - // PAPER LINES 21-22 + // PAPER LINES 25-27 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); if (w[round] == rct::zero()) { @@ -755,7 +752,7 @@ try_again: goto try_again; } - // PAPER LINES 24-25 + // PAPER LINES 29-30 const rct::key winv = invert(w[round]); if (nprime > 1) { @@ -765,7 +762,7 @@ try_again: PERF_TIMER_STOP_BP(PROVE_hadamard2); } - // PAPER LINES 28-29 + // PAPER LINES 33-34 PERF_TIMER_START_BP(PROVE_prime); aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); @@ -776,7 +773,6 @@ try_again: } PERF_TIMER_STOP_BP(PROVE_step4); - // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) return Bulletproof(std::move(V), A, S, T1, T2, taux, mu, std::move(L), std::move(R), aprime[0], bprime[0], t); } @@ -810,7 +806,10 @@ struct proof_data_t size_t logM, inv_offset; }; -/* Given a range proof, determine if it is valid */ +/* Given a range proof, determine if it is valid + * This uses the method in PAPER LINES 95-105, + * weighted across multiple proofs in a batch + */ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) { init_exponents(); @@ -871,7 +870,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); PERF_TIMER_START_BP(VERIFY_line_21_22); - // PAPER LINES 21-22 // The inner product challenges are computed per round pd.w.resize(rounds); for (size_t i = 0; i < rounds; ++i) @@ -929,7 +927,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) rct::key proof8_A = rct::scalarmult8(proof.A); PERF_TIMER_START_BP(VERIFY_line_61); - // PAPER LINE 61 sc_mulsub(m_y0.bytes, proof.taux.bytes, weight_y.bytes, m_y0.bytes); const rct::keyV zpow = vector_powers(pd.z, M+3); @@ -962,7 +959,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) PERF_TIMER_STOP_BP(VERIFY_line_61rl_new); PERF_TIMER_START_BP(VERIFY_line_62); - // PAPER LINE 62 multiexp_data.emplace_back(weight_z, proof8_A); sc_mul(tmp.bytes, pd.x.bytes, weight_z.bytes); multiexp_data.emplace_back(tmp, proof8_S); @@ -973,7 +969,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); PERF_TIMER_START_BP(VERIFY_line_24_25); - // Basically PAPER LINES 24-25 // Compute the curvepoints from G[i] and H[i] rct::key yinvpow = rct::identity(); rct::key ypow = rct::identity(); @@ -1010,7 +1005,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) sc_mul(g_scalar.bytes, g_scalar.bytes, w_cache[i].bytes); sc_mul(h_scalar.bytes, h_scalar.bytes, w_cache[(~i) & (MN-1)].bytes); - // Adjust the scalars using the exponents from PAPER LINE 62 sc_add(g_scalar.bytes, g_scalar.bytes, pd.z.bytes); CHECK_AND_ASSERT_MES(2+i/N < zpow.size(), false, "invalid zpow index"); CHECK_AND_ASSERT_MES(i%N < twoN.size(), false, "invalid twoN index"); @@ -1043,7 +1037,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs) PERF_TIMER_STOP_BP(VERIFY_line_24_25); - // PAPER LINE 26 PERF_TIMER_START_BP(VERIFY_line_26_new); sc_muladd(z1.bytes, proof.mu.bytes, weight_z.bytes, z1.bytes); for (size_t i = 0; i < rounds; ++i) diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index 2c4e5fc3b..1763542db 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -31,6 +31,7 @@ #include "misc_log_ex.h" #include "cryptonote_config.h" #include "rctTypes.h" +#include "int-util.h" using namespace crypto; using namespace std; @@ -118,40 +119,22 @@ namespace rct { //uint long long to 32 byte key void d2h(key & amounth, const xmr_amount in) { sc_0(amounth.bytes); - xmr_amount val = in; - int i = 0; - while (val != 0) { - amounth[i] = (unsigned char)(val & 0xFF); - i++; - val /= (xmr_amount)256; - } + memcpy_swap64le(amounth.bytes, &in, 1); } //uint long long to 32 byte key key d2h(const xmr_amount in) { key amounth; - sc_0(amounth.bytes); - xmr_amount val = in; - int i = 0; - while (val != 0) { - amounth[i] = (unsigned char)(val & 0xFF); - i++; - val /= (xmr_amount)256; - } + d2h(amounth, in); return amounth; } //uint long long to int[64] void d2b(bits amountb, xmr_amount val) { int i = 0; - while (val != 0) { - amountb[i] = val & 1; - i++; - val >>= 1; - } while (i < 64) { - amountb[i] = 0; - i++; + amountb[i++] = val & 1; + val >>= 1; } } @@ -172,16 +155,11 @@ namespace rct { int val = 0, i = 0, j = 0; for (j = 0; j < 8; j++) { val = (unsigned char)test.bytes[j]; - i = 8 * j; - while (val != 0) { - amountb2[i] = val & 1; - i++; + i = 0; + while (i < 8) { + amountb2[j*8+i++] = val & 1; val >>= 1; } - while (i < 8 * (j + 1)) { - amountb2[i] = 0; - i++; - } } } diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index ebb1e767f..65d88b57e 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -37,6 +37,7 @@ set(rpc_sources bootstrap_daemon.cpp core_rpc_server.cpp rpc_payment.cpp + rpc_version_str.cpp instanciations) set(daemon_messages_sources @@ -54,6 +55,7 @@ set(rpc_base_headers rpc_handler.h) set(rpc_headers + rpc_version_str.h rpc_handler.h) set(daemon_rpc_server_headers) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 9117b5b3a..dc93e7023 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include <boost/preprocessor/stringize.hpp> +#include <boost/uuid/nil_generator.hpp> #include "include_base_utils.h" #include "string_tools.h" using namespace epee; @@ -1095,9 +1096,8 @@ namespace cryptonote } res.sanity_check_failed = false; - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); - tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, false, false, req.do_not_relay) || tvc.m_verifivation_failed) + tx_verification_context tvc{}; + if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, (req.do_not_relay ? relay_method::none : relay_method::local), false) || tvc.m_verifivation_failed) { res.status = "Failed"; std::string reason = ""; @@ -1142,7 +1142,7 @@ namespace cryptonote NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(tx_blob); - m_core.get_protocol()->relay_transactions(r, fake_context); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes res.status = CORE_RPC_STATUS_OK; return true; @@ -2370,7 +2370,7 @@ namespace cryptonote if (req.txids.empty()) { std::vector<transaction> pool_txs; - bool r = m_core.get_pool_transactions(pool_txs); + bool r = m_core.get_pool_transactions(pool_txs, true); if (!r) { res.status = "Failed to get txpool contents"; @@ -2747,13 +2747,11 @@ namespace cryptonote crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); cryptonote::blobdata txblob; - bool r = m_core.get_pool_transaction(txid, txblob); - if (r) + if (!m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) { - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(txblob); - m_core.get_protocol()->relay_transactions(r, fake_context); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes } else diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index d7e081af3..24800ff20 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -28,6 +28,7 @@ #include "daemon_handler.h" +#include <boost/uuid/nil_generator.hpp> // likely included by daemon_handler.h's includes, // but including here for clarity #include "cryptonote_core/cryptonote_core.h" @@ -288,10 +289,9 @@ namespace rpc return; } - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, false, false, !relay) || tvc.m_verifivation_failed) + if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, (relay ? relay_method::local : relay_method::none), false) || tvc.m_verifivation_failed) { if (tvc.m_verifivation_failed) { @@ -311,42 +311,42 @@ namespace rpc if (tvc.m_double_spend) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "double spend"; + res.error_details += "double spend"; } if (tvc.m_invalid_input) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "invalid input"; + res.error_details += "invalid input"; } if (tvc.m_invalid_output) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "invalid output"; + res.error_details += "invalid output"; } if (tvc.m_too_big) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "too big"; + res.error_details += "too big"; } if (tvc.m_overspend) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "overspend"; + res.error_details += "overspend"; } if (tvc.m_fee_too_low) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "fee too low"; + 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"; + res.error_details += "tx is not ringct"; } if (tvc.m_too_few_outputs) { if (!res.error_details.empty()) res.error_details += " and "; - res.error_details = "too few outputs"; + res.error_details += "too few outputs"; } if (res.error_details.empty()) { @@ -368,7 +368,7 @@ namespace rpc NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(tx_blob); - m_core.get_protocol()->relay_transactions(r, fake_context); + m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes res.status = Message::STATUS_OK; diff --git a/src/rpc/rpc_version_str.cpp b/src/rpc/rpc_version_str.cpp new file mode 100644 index 000000000..c60cf4891 --- /dev/null +++ b/src/rpc/rpc_version_str.cpp @@ -0,0 +1,55 @@ +// 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 "rpc_version_str.h" +#include "version.h" +#include <regex> + +namespace cryptonote +{ + +namespace rpc +{ + +// Expected format of Monero software version string: +// 1) Four numbers, one to two digits each, separated by periods +// 2) Optionally, one of the following suffixes: +// a) -release +// b) -<hash> where <hash> is exactly nine lowercase hex digits + +bool is_version_string_valid(const std::string& str) +{ + return std::regex_match(str, std::regex( + "^\\d{1,2}(\\.\\d{1,2}){3}(-(release|[0-9a-f]{9}))?$", + std::regex_constants::nosubs + )); +} + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/rpc_version_str.h b/src/rpc/rpc_version_str.h new file mode 100644 index 000000000..930c807d2 --- /dev/null +++ b/src/rpc/rpc_version_str.h @@ -0,0 +1,43 @@ +// 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. +// +#pragma once + +#include <string> + +namespace cryptonote +{ + +namespace rpc +{ + +bool is_version_string_valid(const std::string& str); + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea8f6f2f5..87bbf62d3 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -912,16 +912,6 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) bool simple_wallet::payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - - crypto::hash payment_id; - if (args.size() > 0) - { - PRINT_USAGE(USAGE_PAYMENT_ID); - return true; - } - payment_id = crypto::rand<crypto::hash>(); - success_msg_writer() << tr("Random payment ID: ") << payment_id; - return true; } bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std::vector<std::string>()*/) @@ -4356,7 +4346,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (std::cin.eof() || !command_line::is_yes(confirm)) CHECK_AND_ASSERT_MES(false, false, tr("account creation aborted")); - m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height()); + m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height() > 0 ? m_wallet->estimate_blockchain_height() - 1 : 0); m_wallet->explicit_refresh_from_block_height(true); m_restore_height = m_wallet->get_refresh_from_block_height(); } @@ -6293,13 +6283,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri if (tools::wallet2::parse_long_payment_id(payment_id_str, payment_id)) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - - std::string extra_nonce; - set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - local_args.pop_back(); - payment_id_seen = true; - message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead"); } if(!r) { @@ -6408,8 +6391,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri else if (tools::wallet2::parse_payment_id(payment_id_uri, payment_id)) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead"); } else { @@ -6945,11 +6926,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st if(r) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - - std::string extra_nonce; - set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - payment_id_seen = true; } if(!r && local_args.size() == 3) @@ -7191,7 +7167,6 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) if (tools::wallet2::parse_long_payment_id(local_args.back(), payment_id)) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); } else { @@ -9421,7 +9396,6 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v if (tools::wallet2::parse_long_payment_id(args[3], payment_id)) { LONG_PAYMENT_ID_SUPPORT_CHECK(); - description_start += 2; } else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id)) { diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 3be1a6f6b..a0a166a93 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -77,42 +77,43 @@ target_link_libraries(wallet PRIVATE ${EXTRA_LIBRARIES}) -set(wallet_rpc_sources - wallet_rpc_server.cpp) +if(NOT IOS) + set(wallet_rpc_sources + wallet_rpc_server.cpp) -set(wallet_rpc_headers) + set(wallet_rpc_headers) -set(wallet_rpc_private_headers - wallet_rpc_server.h) + set(wallet_rpc_private_headers + wallet_rpc_server.h) -monero_private_headers(wallet_rpc_server - ${wallet_rpc_private_headers}) -monero_add_executable(wallet_rpc_server - ${wallet_rpc_sources} - ${wallet_rpc_headers} - ${wallet_rpc_private_headers}) - -target_link_libraries(wallet_rpc_server - PRIVATE - wallet - rpc_base - cryptonote_core - cncrypto - common - version - daemonizer - ${EPEE_READLINE} - ${Boost_CHRONO_LIBRARY} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_THREAD_LIBRARY} - ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) -set_property(TARGET wallet_rpc_server - PROPERTY - OUTPUT_NAME "monero-wallet-rpc") -install(TARGETS wallet_rpc_server DESTINATION bin) + monero_private_headers(wallet_rpc_server + ${wallet_rpc_private_headers}) + monero_add_executable(wallet_rpc_server + ${wallet_rpc_sources} + ${wallet_rpc_headers} + ${wallet_rpc_private_headers}) + target_link_libraries(wallet_rpc_server + PRIVATE + wallet + rpc_base + cryptonote_core + cncrypto + common + version + daemonizer + ${EPEE_READLINE} + ${Boost_CHRONO_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + set_property(TARGET wallet_rpc_server + PROPERTY + OUTPUT_NAME "monero-wallet-rpc") + install(TARGETS wallet_rpc_server DESTINATION bin) +endif() # build and install libwallet_merged only if we building for GUI if (BUILD_GUI_DEPS) diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 15ea26044..005b0bafa 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -36,7 +36,7 @@ do { \ CHECK_AND_ASSERT_MES(error.code == 0, error.message, error.message); \ handle_payment_changes(res, std::integral_constant<bool, HasCredits<decltype(res)>::Has>()); \ - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); \ + CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); \ /* empty string -> not connection */ \ CHECK_AND_ASSERT_MES(!res.status.empty(), res.status, "No connection to daemon"); \ CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, res.status, "Daemon busy"); \ diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e60c6b7e1..4d2ec5103 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1038,10 +1038,15 @@ uint64_t gamma_picker::pick() return first_rct + crypto::rand_idx(n_rct); }; +boost::mutex wallet_keys_unlocker::lockers_lock; +unsigned int wallet_keys_unlocker::lockers = 0; wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password): w(w), locked(password != boost::none) { + boost::lock_guard<boost::mutex> lock(lockers_lock); + if (lockers++ > 0) + locked = false; if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) { locked = false; @@ -1056,6 +1061,9 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee:: w(w), locked(locked) { + boost::lock_guard<boost::mutex> lock(lockers_lock); + if (lockers++ > 0) + locked = false; if (!locked) return; w.generate_chacha_key_from_password(password, key); @@ -1064,9 +1072,19 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee:: wallet_keys_unlocker::~wallet_keys_unlocker() { - if (!locked) - return; - try { w.encrypt_keys(key); } + try + { + boost::lock_guard<boost::mutex> lock(lockers_lock); + if (lockers == 0) + { + MERROR("There are no lockers in wallet_keys_unlocker dtor"); + return; + } + --lockers; + if (!locked) + return; + w.encrypt_keys(key); + } catch (...) { MERROR("Failed to re-encrypt wallet keys"); @@ -13601,4 +13619,22 @@ std::vector<cryptonote::public_node> wallet2::get_public_nodes(bool white_only) std::copy(res.gray.begin(), res.gray.end(), std::back_inserter(nodes)); return nodes; } +//---------------------------------------------------------------------------------------------------- +std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size) +{ + THROW_WALLET_EXCEPTION_IF(n_inputs <= 0, tools::error::wallet_internal_error, "Invalid n_inputs"); + THROW_WALLET_EXCEPTION_IF(n_outputs < 0, tools::error::wallet_internal_error, "Invalid n_outputs"); + THROW_WALLET_EXCEPTION_IF(ring_size < 0, tools::error::wallet_internal_error, "Invalid ring size"); + + if (ring_size == 0) + ring_size = get_min_ring_size(); + if (n_outputs == 1) + n_outputs = 2; // extra dummy output + + const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); + size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof); + uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof); + return std::make_pair(size, weight); +} +//---------------------------------------------------------------------------------------------------- } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c86315f7c..8f840a42d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -122,6 +122,8 @@ private: wallet2 &w; bool locked; crypto::chacha_key key; + static boost::mutex lockers_lock; + static unsigned int lockers; }; class i_wallet2_callback @@ -1252,6 +1254,8 @@ private: bool is_unattended() const { return m_unattended; } + std::pair<size_t, uint64_t> estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size); + bool get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie); bool daemon_requires_payment(); bool make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..eb89a96e9 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -550,9 +550,29 @@ namespace tools if (!m_wallet) return not_open(er); try { - m_wallet->add_subaddress(req.account_index, req.label); - res.address_index = m_wallet->get_num_subaddresses(req.account_index) - 1; - res.address = m_wallet->get_subaddress_as_str({req.account_index, res.address_index}); + if (req.count < 1 || req.count > 64) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Count must be between 1 and 64."; + return false; + } + + std::vector<std::string> addresses; + std::vector<uint32_t> address_indices; + + addresses.reserve(req.count); + address_indices.reserve(req.count); + + for (uint32_t i = 0; i < req.count; i++) { + m_wallet->add_subaddress(req.account_index, req.label); + uint32_t new_address_index = m_wallet->get_num_subaddresses(req.account_index) - 1; + address_indices.push_back(new_address_index); + addresses.push_back(m_wallet->get_subaddress_as_str({req.account_index, new_address_index})); + } + + res.address = addresses[0]; + res.address_index = address_indices[0]; + res.addresses = addresses; + res.address_indices = address_indices; } catch (const std::exception& e) { @@ -4291,6 +4311,25 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); + try + { + size_t extra_size = 34 /* pubkey */ + 10 /* encrypted payment id */; // typical makeup + const std::pair<size_t, uint64_t> sw = m_wallet->estimate_tx_size_and_weight(req.rct, req.n_inputs, req.ring_size, req.n_outputs, extra_size); + res.size = sw.first; + res.weight = sw.second; + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to determine size and weight"; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx) { res.version = WALLET_RPC_VERSION; diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b2b5e7116..41f6879ef 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -154,6 +154,7 @@ namespace tools MAP_JON_RPC_WE("set_daemon", on_set_daemon, wallet_rpc::COMMAND_RPC_SET_DAEMON) MAP_JON_RPC_WE("set_log_level", on_set_log_level, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL) MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) + MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) END_JSON_RPC_MAP() END_URI_MAP2() @@ -240,6 +241,7 @@ namespace tools bool on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); //json rpc v2 diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..8d601b050 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 16 +#define WALLET_RPC_VERSION_MINOR 17 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -182,11 +182,13 @@ namespace wallet_rpc { struct request_t { - uint32_t account_index; + uint32_t account_index; + uint32_t count; std::string label; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(account_index) + KV_SERIALIZE_OPT(count, 1U) KV_SERIALIZE(label) END_KV_SERIALIZE_MAP() }; @@ -194,12 +196,16 @@ namespace wallet_rpc struct response_t { - std::string address; - uint32_t address_index; + std::string address; + uint32_t address_index; + std::vector<std::string> addresses; + std::vector<uint32_t> address_indices; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) KV_SERIALIZE(address_index) + KV_SERIALIZE(addresses) + KV_SERIALIZE(address_indices) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -2580,5 +2586,36 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT + { + struct request_t + { + uint32_t n_inputs; + uint32_t n_outputs; + uint32_t ring_size; + bool rct; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(n_inputs) + KV_SERIALIZE(n_outputs) + KV_SERIALIZE_OPT(ring_size, 0u) + KV_SERIALIZE_OPT(rct, true) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + uint64_t size; + uint64_t weight; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(size) + KV_SERIALIZE(weight) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + } } |