diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 78d034e7a..a5517e011 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -658,6 +658,11 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::get_pool_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos) const + { + return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos); + } + //----------------------------------------------------------------------------------------------- bool core::get_short_chain_history(std::list& ids) { return m_blockchain_storage.get_short_chain_history(ids); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 51584dbcf..ff187b4ce 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -108,6 +108,7 @@ namespace cryptonote void set_enforce_dns_checkpoints(bool enforce_dns); bool get_pool_transactions(std::list& txs); + bool get_pool_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos) const; size_t get_pool_transactions_count(); size_t get_blockchain_total_transactions(); //bool get_outs(uint64_t amount, std::list& pkeys); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 03ced2c2e..5543ac64f 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -257,6 +257,7 @@ namespace cryptonote (tx_age > CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME && it->second.kept_by_block) ) { LOG_PRINT_L1("Tx " << it->first << " removed from tx pool due to outdated, age: " << tx_age ); + remove_transaction_keyimages(it->second.tx); m_transactions.erase(it++); }else ++it; @@ -276,6 +277,40 @@ namespace cryptonote BOOST_FOREACH(const auto& tx_vt, m_transactions) txs.push_back(tx_vt.second.tx); } + //------------------------------------------------------------------ + bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + for (const auto& tx_vt : m_transactions) + { + tx_info txi; + const tx_details& txd = tx_vt.second; + txi.id_hash = epee::string_tools::pod_to_hex(tx_vt.first); + txi.tx_json = obj_to_json_str(*const_cast(&txd.tx)); + txi.blob_size = txd.blob_size; + txi.fee = txd.fee; + txi.kept_by_block = txd.kept_by_block; + txi.max_used_block_height = txd.max_used_block_height; + txi.max_used_block_id_hash = epee::string_tools::pod_to_hex(txd.max_used_block_id); + txi.last_failed_height = txd.last_failed_height; + txi.last_failed_id_hash = epee::string_tools::pod_to_hex(txd.last_failed_id); + txi.receive_time = txd.receive_time; + tx_infos.push_back(txi); + } + + for (const key_images_container::value_type& kee : m_spent_key_images) { + const crypto::key_image& k_image = kee.first; + const std::unordered_set& kei_image_set = kee.second; + spent_key_image_info ki; + ki.id_hash = epee::string_tools::pod_to_hex(k_image); + for (const crypto::hash& tx_id_hash : kei_image_set) + { + ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash)); + } + key_image_infos.push_back(ki); + } + return true; + } //--------------------------------------------------------------------------------- bool tx_memory_pool::get_transaction(const crypto::hash& id, transaction& tx) const { diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index b867a1a7d..77f2e3483 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -43,6 +43,7 @@ #include "cryptonote_basic_impl.h" #include "verification_context.h" #include "crypto/hash.h" +#include "rpc/core_rpc_server_commands_defs.h" namespace cryptonote { @@ -81,6 +82,7 @@ namespace cryptonote bool deinit(); bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee); void get_transactions(std::list& txs) const; + bool get_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos) const; bool get_transaction(const crypto::hash& h, transaction& tx) const; size_t get_transactions_count() const; std::string print_pool(bool short_format) const; diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 5d73c7e8c..8ef91600c 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -523,20 +523,58 @@ bool t_rpc_command_executor::print_transaction_pool_long() { } } - if (res.transactions.empty()) + if (res.transactions.empty() && res.spent_key_images.empty()) { tools::msg_writer() << "Pool is empty" << std::endl; } - for (auto & tx_info : res.transactions) + if (! res.transactions.empty()) { - tools::msg_writer() << "id: " << tx_info.id_hash << std::endl - << "blob_size: " << tx_info.blob_size << std::endl - << "fee: " << tx_info.fee << std::endl - << "kept_by_block: " << tx_info.kept_by_block << std::endl - << "max_used_block_height: " << tx_info.max_used_block_height << std::endl - << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl - << "last_failed_height: " << tx_info.last_failed_height << std::endl - << "last_failed_id: " << tx_info.last_failed_id_hash << std::endl; + tools::msg_writer() << "Transactions: "; + for (auto & tx_info : res.transactions) + { + tools::msg_writer() << "id: " << tx_info.id_hash << std::endl + << tx_info.tx_json << std::endl + << "blob_size: " << tx_info.blob_size << std::endl + << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl + << "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl + << "max_used_block_height: " << tx_info.max_used_block_height << std::endl + << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl + << "last_failed_height: " << tx_info.last_failed_height << std::endl + << "last_failed_id: " << tx_info.last_failed_id_hash << std::endl; + } + if (res.spent_key_images.empty()) + { + tools::msg_writer() << "WARNING: Inconsistent pool state - no spent key images"; + } + } + if (! res.spent_key_images.empty()) + { + tools::msg_writer() << ""; // one newline + tools::msg_writer() << "Spent key images: "; + for (const cryptonote::spent_key_image_info& kinfo : res.spent_key_images) + { + tools::msg_writer() << "key image: " << kinfo.id_hash; + if (kinfo.txs_hashes.size() == 1) + { + tools::msg_writer() << " tx: " << kinfo.txs_hashes[0]; + } + else if (kinfo.txs_hashes.size() == 0) + { + tools::msg_writer() << " WARNING: spent key image has no txs associated"; + } + else + { + tools::msg_writer() << " NOTE: key image for multiple txs: " << kinfo.txs_hashes.size(); + for (const std::string& tx_id : kinfo.txs_hashes) + { + tools::msg_writer() << " tx: " << tx_id; + } + } + } + if (res.transactions.empty()) + { + tools::msg_writer() << "WARNING: Inconsistent pool state - no transactions"; + } } return true; @@ -571,10 +609,9 @@ bool t_rpc_command_executor::print_transaction_pool_short() { for (auto & tx_info : res.transactions) { tools::msg_writer() << "id: " << tx_info.id_hash << std::endl - << tx_info.tx_json << std::endl << "blob_size: " << tx_info.blob_size << std::endl - << "fee: " << tx_info.fee << std::endl - << "kept_by_block: " << tx_info.kept_by_block << std::endl + << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl + << "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl << "max_used_block_height: " << tx_info.max_used_block_height << std::endl << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl << "last_failed_height: " << tx_info.last_failed_height << std::endl diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 8eeac489d..561161950 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -401,10 +401,8 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res) { - /* CHECK_CORE_BUSY(); - res.transactions = m_core.transaction_pool_info(); - */ + m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index e54dec2c9..b6a2edd0b 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -619,7 +619,18 @@ namespace cryptonote KV_SERIALIZE(receive_time) END_KV_SERIALIZE_MAP() }; - + + struct spent_key_image_info + { + std::string id_hash; + std::vector txs_hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id_hash) + KV_SERIALIZE(txs_hashes) + END_KV_SERIALIZE_MAP() + }; + struct COMMAND_RPC_GET_TRANSACTION_POOL { struct request @@ -632,10 +643,12 @@ namespace cryptonote { std::string status; std::vector transactions; + std::vector spent_key_images; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(transactions) + KV_SERIALIZE(spent_key_images) END_KV_SERIALIZE_MAP() }; };