diff options
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 22 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 14 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 135 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 19 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.cpp | 27 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_tx_utils.h | 14 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 112 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.h | 32 |
8 files changed, 272 insertions, 103 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 9d89c6280..3d586a704 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -61,6 +61,8 @@ #define FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE (100*1024*1024) // 100 MB +using namespace crypto; + //#include "serialization/json_archive.h" /* TODO: @@ -3166,9 +3168,9 @@ bool Blockchain::flush_txes_from_pool(const std::list<crypto::hash> &txids) cryptonote::transaction tx; size_t blob_size; uint64_t fee; - bool relayed, do_not_relay; + bool relayed, do_not_relay, double_spend_seen; MINFO("Removing txid " << txid << " from the pool"); - if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed, do_not_relay)) + if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) { MERROR("Failed to remove txid " << txid << " from the pool"); res = false; @@ -3351,7 +3353,7 @@ leave: transaction tx; size_t blob_size = 0; uint64_t fee = 0; - bool relayed = false, do_not_relay = false; + bool relayed = false, do_not_relay = false, double_spend_seen = false; TIME_MEASURE_START(aa); // XXX old code does not check whether tx exists @@ -3368,7 +3370,7 @@ leave: TIME_MEASURE_START(bb); // get transaction with hash <tx_id> from tx_pool - if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee, relayed, do_not_relay)) + if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) { MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id); bvc.m_verifivation_failed = true; @@ -4211,9 +4213,9 @@ void Blockchain::remove_txpool_tx(const crypto::hash &txid) m_db->remove_txpool_tx(txid); } -uint64_t Blockchain::get_txpool_tx_count() const +uint64_t Blockchain::get_txpool_tx_count(bool include_unrelayed_txes) const { - return m_db->get_txpool_tx_count(); + return m_db->get_txpool_tx_count(include_unrelayed_txes); } txpool_tx_meta_t Blockchain::get_txpool_tx_meta(const crypto::hash& txid) const @@ -4231,9 +4233,9 @@ cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid) co return m_db->get_txpool_tx_blob(txid); } -bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob) 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, bool include_unrelayed_txes) const { - return m_db->for_all_txpool_txes(f, include_blob); + return m_db->for_all_txpool_txes(f, include_blob, include_unrelayed_txes); } void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, blockchain_db_sync_mode sync_mode, bool fast_sync) @@ -4381,12 +4383,12 @@ void Blockchain::load_compiled_in_block_hashes() size_t blob_size; uint64_t fee; - bool relayed, do_not_relay; + bool relayed, do_not_relay, double_spend_seen; transaction pool_tx; for(const transaction &tx : txs) { crypto::hash tx_hash = get_transaction_hash(tx); - m_tx_pool.take_tx(tx_hash, pool_tx, blob_size, fee, relayed, do_not_relay); + m_tx_pool.take_tx(tx_hash, pool_tx, blob_size, fee, relayed, do_not_relay, double_spend_seen); } } } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index f64bd35e3..e0936da8f 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -635,16 +635,6 @@ namespace cryptonote uint64_t get_current_cumulative_blocksize_limit() const; /** - * @brief checks if the blockchain is currently being stored - * - * Note: this should be meaningless in cases where Blockchain is not - * directly managing saving the blockchain to disk. - * - * @return true if Blockchain is having the chain stored currently, else false - */ - bool is_storing_blockchain()const{return false;} - - /** * @brief gets the difficulty of the block with a given height * * @param i the height @@ -947,11 +937,11 @@ namespace cryptonote void add_txpool_tx(transaction &tx, 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() const; + uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const; txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) 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) 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 is_within_compiled_block_hash_area(uint64_t height) const; bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 69e15c0b4..5c181208f 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -38,6 +38,7 @@ using namespace epee; #include "common/updates.h" #include "common/download.h" #include "common/threadpool.h" +#include "common/command_line.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" @@ -61,6 +62,69 @@ DISABLE_VS_WARNINGS(4355) namespace cryptonote { + const command_line::arg_descriptor<std::string> arg_data_dir = { + "data-dir" + , "Specify data directory" + }; + const command_line::arg_descriptor<std::string> arg_testnet_data_dir = { + "testnet-data-dir" + , "Specify testnet data directory" + }; + const command_line::arg_descriptor<bool, false> arg_testnet_on = { + "testnet" + , "Run on testnet. The wallet must be launched with --testnet flag." + , false + }; + + static const command_line::arg_descriptor<bool> arg_test_drop_download = { + "test-drop-download" + , "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)" + }; + static const command_line::arg_descriptor<uint64_t> arg_test_drop_download_height = { + "test-drop-download-height" + , "Like test-drop-download but disards only after around certain height" + , 0 + }; + static const command_line::arg_descriptor<int> arg_test_dbg_lock_sleep = { + "test-dbg-lock-sleep" + , "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests." + , 0 + }; + static const command_line::arg_descriptor<bool> arg_dns_checkpoints = { + "enforce-dns-checkpointing" + , "checkpoints from DNS server will be enforced" + , false + }; + static const command_line::arg_descriptor<uint64_t> arg_fast_block_sync = { + "fast-block-sync" + , "Sync up most of the way by using embedded, known block hashes." + , 1 + }; + static const command_line::arg_descriptor<uint64_t> arg_prep_blocks_threads = { + "prep-blocks-threads" + , "Max number of threads to use when preparing block hashes in groups." + , 4 + }; + static const command_line::arg_descriptor<uint64_t> arg_show_time_stats = { + "show-time-stats" + , "Show time-stats when processing blocks/txs and disk synchronization." + , 0 + }; + static const command_line::arg_descriptor<size_t> arg_block_sync_size = { + "block-sync-size" + , "How many blocks to sync at once during chain synchronization (0 = adaptive)." + , 0 + }; + static const command_line::arg_descriptor<std::string> arg_check_updates = { + "check-updates" + , "Check for new versions of monero: [disabled|notify|download|update]" + , "notify" + }; + static const command_line::arg_descriptor<bool> arg_fluffy_blocks = { + "fluffy-blocks" + , "Relay blocks as fluffy blocks where possible (automatic on testnet)" + , false + }; //----------------------------------------------------------------------------------------------- core::core(i_cryptonote_protocol* pprotocol): @@ -148,20 +212,21 @@ namespace cryptonote //----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& desc) { - command_line::add_arg(desc, command_line::arg_data_dir, tools::get_default_data_dir()); - command_line::add_arg(desc, command_line::arg_testnet_data_dir, (boost::filesystem::path(tools::get_default_data_dir()) / "testnet").string()); + command_line::add_arg(desc, arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(desc, arg_testnet_data_dir, (boost::filesystem::path(tools::get_default_data_dir()) / "testnet").string()); - command_line::add_arg(desc, command_line::arg_test_drop_download); - command_line::add_arg(desc, command_line::arg_test_drop_download_height); + command_line::add_arg(desc, arg_test_drop_download); + command_line::add_arg(desc, arg_test_drop_download_height); - command_line::add_arg(desc, command_line::arg_testnet_on); - command_line::add_arg(desc, command_line::arg_dns_checkpoints); - command_line::add_arg(desc, command_line::arg_prep_blocks_threads); - command_line::add_arg(desc, command_line::arg_fast_block_sync); - command_line::add_arg(desc, command_line::arg_show_time_stats); - command_line::add_arg(desc, command_line::arg_block_sync_size); - command_line::add_arg(desc, command_line::arg_check_updates); - command_line::add_arg(desc, command_line::arg_fluffy_blocks); + command_line::add_arg(desc, arg_testnet_on); + command_line::add_arg(desc, arg_dns_checkpoints); + command_line::add_arg(desc, arg_prep_blocks_threads); + command_line::add_arg(desc, arg_fast_block_sync); + command_line::add_arg(desc, arg_show_time_stats); + command_line::add_arg(desc, arg_block_sync_size); + command_line::add_arg(desc, arg_check_updates); + command_line::add_arg(desc, arg_fluffy_blocks); + command_line::add_arg(desc, arg_test_dbg_lock_sleep); // we now also need some of net_node's options (p2p bind arg, for separate data dir) command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false); @@ -173,9 +238,9 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_command_line(const boost::program_options::variables_map& vm) { - m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + m_testnet = command_line::get_arg(vm, arg_testnet_on); - auto data_dir_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + auto data_dir_arg = m_testnet ? arg_testnet_data_dir : arg_data_dir; m_config_folder = command_line::get_arg(vm, data_dir_arg); auto data_dir = boost::filesystem::path(m_config_folder); @@ -196,13 +261,15 @@ namespace cryptonote } - set_enforce_dns_checkpoints(command_line::get_arg(vm, command_line::arg_dns_checkpoints)); - test_drop_download_height(command_line::get_arg(vm, command_line::arg_test_drop_download_height)); - m_fluffy_blocks_enabled = m_testnet || get_arg(vm, command_line::arg_fluffy_blocks); + set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints)); + test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height)); + m_fluffy_blocks_enabled = m_testnet || get_arg(vm, arg_fluffy_blocks); - if (command_line::get_arg(vm, command_line::arg_test_drop_download) == true) + if (command_line::get_arg(vm, arg_test_drop_download) == true) test_drop_download(); + epee::debug::g_test_dbg_lock_sleep() = command_line::get_arg(vm, arg_test_dbg_lock_sleep); + return true; } //----------------------------------------------------------------------------------------------- @@ -268,7 +335,7 @@ namespace cryptonote m_fakechain = test_options != NULL; bool r = handle_command_line(vm); - bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + bool testnet = command_line::get_arg(vm, arg_testnet_on); auto p2p_bind_arg = testnet ? nodetool::arg_testnet_p2p_bind_port : nodetool::arg_p2p_bind_port; std::string m_port = command_line::get_arg(vm, p2p_bind_arg); std::string m_config_folder_mempool = m_config_folder; @@ -281,9 +348,9 @@ namespace cryptonote std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode); bool db_salvage = command_line::get_arg(vm, cryptonote::arg_db_salvage) != 0; - bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0; - uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads); - std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates); + bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0; + uint64_t blocks_threads = command_line::get_arg(vm, arg_prep_blocks_threads); + std::string check_updates_string = command_line::get_arg(vm, arg_check_updates); boost::filesystem::path folder(m_config_folder); if (m_fakechain) @@ -409,11 +476,11 @@ namespace cryptonote // transactions in the pool that do not conform to the current fork m_mempool.validate(m_blockchain_storage.get_current_hard_fork_version()); - bool show_time_stats = command_line::get_arg(vm, command_line::arg_show_time_stats) != 0; + bool show_time_stats = command_line::get_arg(vm, arg_show_time_stats) != 0; m_blockchain_storage.set_show_time_stats(show_time_stats); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); - block_sync_size = command_line::get_arg(vm, command_line::arg_block_sync_size); + block_sync_size = command_line::get_arg(vm, arg_block_sync_size); MGINFO("Loading checkpoints"); @@ -498,8 +565,8 @@ namespace cryptonote return false; } - tx_hash = null_hash; - tx_prefixt_hash = null_hash; + tx_hash = crypto::null_hash; + tx_prefixt_hash = crypto::null_hash; if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { @@ -1182,21 +1249,21 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transactions(std::list<transaction>& txs) const + bool core::get_pool_transactions(std::list<transaction>& txs, bool include_sensitive_data) const { - m_mempool.get_transactions(txs); + m_mempool.get_transactions(txs, include_sensitive_data); return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const + bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive_data) const { - m_mempool.get_transaction_hashes(txs); + m_mempool.get_transaction_hashes(txs, include_sensitive_data); return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transaction_stats(struct txpool_stats& stats) const + bool core::get_pool_transaction_stats(struct txpool_stats& stats, bool include_sensitive_data) const { - m_mempool.get_transaction_stats(stats); + m_mempool.get_transaction_stats(stats, include_sensitive_data); return true; } //----------------------------------------------------------------------------------------------- @@ -1210,9 +1277,9 @@ namespace cryptonote return m_mempool.have_tx(id); } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const + 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 { - return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos); + return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos, include_sensitive_data); } //----------------------------------------------------------------------------------------------- bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 4837febdf..905e67f6d 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -41,6 +41,7 @@ #include "storages/portable_storage_template_helper.h" #include "common/download.h" #include "common/threadpool.h" +#include "common/command_line.h" #include "tx_pool.h" #include "blockchain.h" #include "cryptonote_basic/miner.h" @@ -58,6 +59,10 @@ namespace cryptonote const std::pair<uint8_t, uint64_t> *hard_forks; }; + extern const command_line::arg_descriptor<std::string> arg_data_dir; + extern const command_line::arg_descriptor<std::string> arg_testnet_data_dir; + extern const command_line::arg_descriptor<bool, false> arg_testnet_on; + /************************************************************************/ /* */ /************************************************************************/ @@ -420,11 +425,12 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_transactions + * @param include_unrelayed_txes include unrelayed txes in result * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transactions(std::list<transaction>& txs) const; - + bool get_pool_transactions(std::list<transaction>& txs, bool include_unrelayed_txes = true) const; + /** * @copydoc tx_memory_pool::get_txpool_backlog * @@ -434,17 +440,19 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_transactions + * @param include_unrelayed_txes include unrelayed txes in result * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const; + bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const; /** * @copydoc tx_memory_pool::get_transactions + * @param include_unrelayed_txes include unrelayed txes in result * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transaction_stats(struct txpool_stats& stats) const; + bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const; /** * @copydoc tx_memory_pool::get_transaction @@ -455,10 +463,11 @@ namespace cryptonote /** * @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info + * @param include_unrelayed_txes include unrelayed txes in result * * @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) 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_unrelayed_txes = true) const; /** * @copydoc tx_memory_pool::get_pool_for_rpc diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 96f6ee872..feefc1592 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -40,6 +40,8 @@ using namespace epee; #include "crypto/hash.h" #include "ringct/rctSigs.h" +using namespace crypto; + namespace cryptonote { //--------------------------------------------------------------- @@ -160,12 +162,6 @@ namespace cryptonote //--------------------------------------------------------------- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct) { - if (destinations.empty()) - { - LOG_ERROR("The destinations must be non-empty"); - return false; - } - std::vector<rct::key> amount_keys; tx.set_null(); amount_keys.clear(); @@ -174,9 +170,8 @@ namespace cryptonote tx.unlock_time = unlock_time; tx.extra = extra; - keypair txkey = keypair::generate(); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); - add_tx_pub_key_to_extra(tx, txkey.pub); + keypair txkey; + txkey.sec = rct::rct2sk(rct::skGen()); tx_key = txkey.sec; // if we have a stealth payment id, find it and encrypt it with the tx key now @@ -323,9 +318,13 @@ namespace cryptonote if (num_stdaddresses == 0 && num_subaddresses == 1) { txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(txkey.sec))); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); - add_tx_pub_key_to_extra(tx, txkey.pub); } + else + { + txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(txkey.sec))); + } + remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); + add_tx_pub_key_to_extra(tx, txkey.pub); std::vector<crypto::public_key> additional_tx_public_keys; additional_tx_keys.clear(); @@ -348,9 +347,11 @@ namespace cryptonote keypair additional_txkey; if (need_additional_txkeys) { - additional_txkey = keypair::generate(); + additional_txkey.sec = rct::rct2sk(rct::skGen()); if (dst_entr.is_subaddress) additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec))); + else + additional_txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(additional_txkey.sec))); } bool r; @@ -393,7 +394,6 @@ namespace cryptonote } remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys)); - add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys); LOG_PRINT_L2("tx pubkey: " << txkey.pub); if (need_additional_txkeys) @@ -401,6 +401,7 @@ namespace cryptonote LOG_PRINT_L2("additional tx pubkeys: "); for (size_t i = 0; i < additional_tx_public_keys.size(); ++i) LOG_PRINT_L2(additional_tx_public_keys[i]); + add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys); } //check money diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 9a3b2484d..8d9a1e332 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -53,6 +53,20 @@ namespace cryptonote rct::key mask; //ringct amount mask void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); } + + BEGIN_SERIALIZE_OBJECT() + FIELD(outputs) + FIELD(real_output) + FIELD(real_out_tx_key) + FIELD(real_out_additional_tx_keys) + FIELD(real_output_in_tx_index) + FIELD(amount) + FIELD(rct) + FIELD(mask) + + if (real_output >= outputs.size()) + return false; + END_SERIALIZE() }; struct tx_destination_entry diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 9071c330c..e6f217463 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -51,6 +51,8 @@ DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated +using namespace crypto; + namespace cryptonote { namespace @@ -187,6 +189,7 @@ namespace cryptonote { if(have_tx_keyimges_as_spent(tx)) { + mark_double_spend(tx); LOG_PRINT_L1("Transaction with id= "<< id << " used already spent key images"); tvc.m_verifivation_failed = true; tvc.m_double_spend = true; @@ -228,6 +231,7 @@ namespace cryptonote meta.last_relayed_time = time(NULL); meta.relayed = relayed; meta.do_not_relay = do_not_relay; + meta.double_spend_seen = have_tx_keyimges_as_spent(tx); memset(meta.padding, 0, sizeof(meta.padding)); try { @@ -266,6 +270,7 @@ namespace cryptonote meta.last_relayed_time = time(NULL); meta.relayed = relayed; meta.do_not_relay = do_not_relay; + meta.double_spend_seen = false; memset(meta.padding, 0, sizeof(meta.padding)); try @@ -354,7 +359,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay) + bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -377,6 +382,7 @@ namespace cryptonote fee = meta.fee; relayed = meta.relayed; do_not_relay = meta.do_not_relay; + double_spend_seen = meta.double_spend_seen; // remove first, in case this throws, so key images aren't removed m_blockchain.remove_txpool_tx(id); @@ -432,7 +438,7 @@ namespace cryptonote remove.insert(txid); } return true; - }); + }, false); if (!remove.empty()) { @@ -494,7 +500,7 @@ namespace cryptonote } } return true; - }); + }, false); return true; } //--------------------------------------------------------------------------------- @@ -521,14 +527,14 @@ namespace cryptonote } } //--------------------------------------------------------------------------------- - size_t tx_memory_pool::get_transactions_count() const + size_t tx_memory_pool::get_transactions_count(bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - return m_blockchain.get_txpool_tx_count(); + return m_blockchain.get_txpool_tx_count(include_unrelayed_txes); } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_transactions(std::list<transaction>& txs) const + void tx_memory_pool::get_transactions(std::list<transaction>& txs, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -542,20 +548,20 @@ namespace cryptonote } txs.push_back(tx); return true; - }, true); + }, true, include_unrelayed_txes); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs) const + void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); 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); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog) const + void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -563,16 +569,16 @@ namespace cryptonote 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.blob_size, meta.fee, meta.receive_time - now}); return true; - }); + }, false, include_unrelayed_txes); } //------------------------------------------------------------------ - void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats) const + void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); std::map<uint64_t, txpool_histo> agebytes; - stats.txs_total = m_blockchain.get_txpool_tx_count(); + stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes); std::vector<uint32_t> sizes; sizes.reserve(stats.txs_total); m_blockchain.for_all_txpool_txes([&stats, &sizes, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ @@ -594,8 +600,10 @@ namespace cryptonote uint64_t age = now - meta.receive_time + (now == meta.receive_time); agebytes[age].txs++; agebytes[age].bytes += meta.blob_size; + if (meta.double_spend_seen) + ++stats.num_double_spends; return true; - }); + }, false, include_unrelayed_txes); stats.bytes_med = epee::misc_utils::median(sizes); if (stats.txs_total > 1) { @@ -642,13 +650,14 @@ namespace cryptonote } //------------------------------------------------------------------ //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const + bool tx_memory_pool::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) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ + 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); + txi.tx_blob = *bd; transaction tx; if (!parse_and_validate_tx_from_blob(*bd, tx)) { @@ -664,14 +673,18 @@ namespace cryptonote txi.max_used_block_id_hash = epee::string_tools::pod_to_hex(meta.max_used_block_id); txi.last_failed_height = meta.last_failed_height; txi.last_failed_id_hash = epee::string_tools::pod_to_hex(meta.last_failed_id); - txi.receive_time = meta.receive_time; + // In restricted mode we do not include this data: + txi.receive_time = include_sensitive_data ? meta.receive_time : 0; txi.relayed = meta.relayed; - txi.last_relayed_time = meta.last_relayed_time; + // In restricted mode we do not include this data: + txi.last_relayed_time = include_sensitive_data ? meta.last_relayed_time : 0; txi.do_not_relay = meta.do_not_relay; + txi.double_spend_seen = meta.double_spend_seen; tx_infos.push_back(txi); return true; - }, true); + }, true, include_sensitive_data); + txpool_tx_meta_t meta; for (const key_images_container::value_type& kee : m_spent_key_images) { const crypto::key_image& k_image = kee.first; const std::unordered_set<crypto::hash>& kei_image_set = kee.second; @@ -679,9 +692,26 @@ 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 + { + meta = m_blockchain.get_txpool_tx_meta(tx_id_hash); + 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)); } - key_image_infos.push_back(ki); + // 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); } return true; } @@ -712,9 +742,10 @@ namespace cryptonote txi.relayed = meta.relayed; txi.last_relayed_time = meta.last_relayed_time; txi.do_not_relay = meta.do_not_relay; + txi.double_spend_seen = meta.double_spend_seen; tx_infos.push_back(txi); return true; - }, true); + }, true, false); for (const key_images_container::value_type& kee : m_spent_key_images) { std::vector<crypto::hash> tx_hashes; @@ -843,7 +874,10 @@ namespace cryptonote } //if we here, transaction seems valid, but, anyway, check for key_images collisions with blockchain, just to be sure if(m_blockchain.have_tx_keyimges_as_spent(tx)) + { + txd.double_spend_seen = true; return false; + } //transaction is ok. return true; @@ -871,6 +905,39 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- + void tx_memory_pool::mark_double_spend(const transaction &tx) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + CRITICAL_REGION_LOCAL1(m_blockchain); + LockedTXN lock(m_blockchain); + for(size_t i = 0; i!= tx.vin.size(); i++) + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, void()); + const key_images_container::const_iterator it = m_spent_key_images.find(itk.k_image); + if (it != m_spent_key_images.end()) + { + for (const crypto::hash &txid: it->second) + { + txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(txid); + if (!meta.double_spend_seen) + { + MDEBUG("Marking " << txid << " as double spending " << itk.k_image); + meta.double_spend_seen = true; + try + { + m_blockchain.update_txpool_tx(txid, meta); + } + catch (const std::exception &e) + { + MERROR("Failed to update tx meta: " << e.what()); + // continue, not fatal + } + } + } + } + } + } + //--------------------------------------------------------------------------------- std::string tx_memory_pool::print_pool(bool short_format) const { std::stringstream ss; @@ -890,6 +957,7 @@ namespace cryptonote ss << "blob_size: " << meta.blob_size << std::endl << "fee: " << print_money(meta.fee) << std::endl << "kept_by_block: " << (meta.kept_by_block ? '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 @@ -1044,7 +1112,7 @@ namespace cryptonote remove.insert(txid); } return true; - }); + }, false); size_t n_removed = 0; if (!remove.empty()) diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 3e4ccb338..d657c6554 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -137,10 +137,11 @@ namespace cryptonote * @param fee the transaction fee * @param relayed return-by-reference was transaction relayed to us by the network? * @param do_not_relay return-by-reference is transaction not to be relayed to the network? + * @param double_spend_seen return-by-reference was a double spend seen for that transaction? * * @return true unless the transaction cannot be found in the pool */ - bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay); + bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen); /** * @brief checks if the pool has a transaction with the given hash @@ -233,29 +234,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 + * */ - void get_transactions(std::list<transaction>& txs) const; + void get_transactions(std::list<transaction>& txs, bool include_unrelayed_txes = true) 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 + * */ - void get_transaction_hashes(std::vector<crypto::hash>& txs) const; + void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const; /** * @brief get (size, 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 + * */ - void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog) const; + void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes = true) 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 + * */ - void get_transaction_stats(struct txpool_stats& stats) const; + void get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const; /** * @brief get information about all transactions and key images in the pool @@ -264,10 +273,11 @@ 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 * * @return true */ - bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) 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 = true) const; /** * @brief get information about all transactions and key images in the pool @@ -308,6 +318,7 @@ namespace cryptonote * nonzero fee * hasn't been relayed too recently * isn't old enough that relaying it is considered harmful + * Note a transaction can be "relayable" even if do_not_relay is true * * @param txs return-by-reference the transactions and their hashes * @@ -327,7 +338,7 @@ namespace cryptonote * * @return the number of transactions in the pool */ - size_t get_transactions_count() const; + size_t get_transactions_count(bool include_unrelayed_txes = true) const; /** * @brief get a string containing human-readable pool information @@ -391,6 +402,8 @@ namespace cryptonote time_t last_relayed_time; //!< the last time the transaction was relayed to the network bool relayed; //!< whether or not the transaction has been relayed to the network bool do_not_relay; //!< to avoid relay this transaction to the network + + bool double_spend_seen; //!< true iff another tx was seen double spending this one }; private: @@ -478,6 +491,11 @@ namespace cryptonote */ bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const; + /** + * @brief mark all transactions double spending the one passed + */ + void mark_double_spend(const transaction &tx); + //TODO: confirm the below comments and investigate whether or not this // is the desired behavior //! map key images to transactions which spent them |