diff options
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 31 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 9 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 120 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 73 | ||||
-rw-r--r-- | src/cryptonote_core/i_core_events.h | 44 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 209 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.h | 53 |
7 files changed, 334 insertions, 205 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d22158dfc..7357e44d0 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) @@ -5098,7 +5103,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..c68fa22e3 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); 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..815b03335 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,9 +257,10 @@ 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(); } @@ -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,9 +303,10 @@ 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(); } @@ -315,7 +317,7 @@ namespace cryptonote } 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: |