aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/epee/include/net/levin_base.h1
-rw-r--r--contrib/epee/include/net/levin_client_async.h12
-rw-r--r--contrib/epee/include/net/levin_protocol_handler.h2
-rw-r--r--contrib/epee/include/net/levin_protocol_handler_async.h16
-rw-r--r--contrib/epee/tests/src/net/test_net.h2
-rw-r--r--external/db_drivers/liblmdb/mdb.c10
-rw-r--r--src/blockchain_db/blockchain_db.h7
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp45
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h4
-rw-r--r--src/common/util.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp20
-rw-r--r--src/cryptonote_core/blockchain.h4
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp16
-rw-r--r--src/cryptonote_core/cryptonote_core.h14
-rw-r--r--src/cryptonote_core/tx_pool.cpp110
-rw-r--r--src/cryptonote_core/tx_pool.h32
-rw-r--r--src/daemon/rpc_command_executor.cpp10
-rw-r--r--src/p2p/net_node.inl2
-rwxr-xr-xsrc/rpc/core_rpc_server.cpp55
-rw-r--r--src/rpc/core_rpc_server.h8
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h8
-rw-r--r--src/rpc/message_data_structs.h1
-rw-r--r--src/serialization/json_object.cpp2
-rw-r--r--src/simplewallet/simplewallet.cpp19
-rw-r--r--src/wallet/api/transaction_history.cpp6
-rw-r--r--src/wallet/wallet2.cpp71
-rw-r--r--src/wallet/wallet2.h39
-rwxr-xr-xsrc/wallet/wallet_rpc_server.cpp14
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h2
-rw-r--r--tests/net_load_tests/clt.cpp9
-rw-r--r--tests/net_load_tests/net_load_tests.h5
-rw-r--r--tests/net_load_tests/srv.cpp4
-rw-r--r--tests/unit_tests/epee_levin_protocol_handler_async.cpp8
-rw-r--r--tests/unit_tests/hardfork.cpp4
-rw-r--r--utils/gpg_keys/stoffu.asc62
36 files changed, 474 insertions, 155 deletions
diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h
index d630bff19..7d060f5ef 100644
--- a/contrib/epee/include/net/levin_base.h
+++ b/contrib/epee/include/net/levin_base.h
@@ -87,6 +87,7 @@ namespace levin
virtual void on_connection_new(t_connection_context& context){};
virtual void on_connection_close(t_connection_context& context){};
+ virtual ~levin_commands_handler(){}
};
#define LEVIN_OK 0
diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h
index 337d345c4..6c8f9bcb3 100644
--- a/contrib/epee/include/net/levin_client_async.h
+++ b/contrib/epee/include/net/levin_client_async.h
@@ -52,6 +52,7 @@ namespace levin
class levin_client_async
{
levin_commands_handler* m_pcommands_handler;
+ void (*commands_handler_destroy)(levin_commands_handler*);
volatile uint32_t m_is_stop;
volatile uint32_t m_threads_count;
::critical_section m_send_lock;
@@ -85,9 +86,9 @@ namespace levin
::critical_section m_connection_lock;
net_utils::blocked_mode_client m_transport;
public:
- levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
+ levin_client_async():m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
{}
- levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
+ levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), commands_handler_destroy(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
{}
~levin_client_async()
{
@@ -97,11 +98,16 @@ namespace levin
while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count))
::Sleep(100);
+
+ set_handler(NULL);
}
- void set_handler(levin_commands_handler* phandler)
+ void set_handler(levin_commands_handler* phandler, void (*destroy)(levin_commands_handler*) = NULL)
{
+ if (commands_handler_destroy && m_pcommands_handler)
+ (*commands_handler_destroy)(m_pcommands_handler);
m_pcommands_handler = phandler;
+ m_pcommands_handler_destroy = destroy;
}
bool connect(uint32_t ip, uint32_t port, uint32_t timeout)
diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h
index fbc9727e2..b3a75bedc 100644
--- a/contrib/epee/include/net/levin_protocol_handler.h
+++ b/contrib/epee/include/net/levin_protocol_handler.h
@@ -43,6 +43,8 @@ namespace levin
struct protocl_handler_config
{
levin_commands_handler<t_connection_context>* m_pcommands_handler;
+ void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*);
+ ~protocl_handler_config() { if (m_pcommands_handler && m_pcommands_handler_destroy) (*m_pcommands_handler_destroy)(m_pcommands_handler); }
};
template<class t_connection_context = net_utils::connection_context_base>
diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h
index 779f4e78f..7ad6d198b 100644
--- a/contrib/epee/include/net/levin_protocol_handler_async.h
+++ b/contrib/epee/include/net/levin_protocol_handler_async.h
@@ -72,9 +72,11 @@ class async_protocol_handler_config
friend class async_protocol_handler<t_connection_context>;
+ levin_commands_handler<t_connection_context>* m_pcommands_handler;
+ void (*m_pcommands_handler_destroy)(levin_commands_handler<t_connection_context>*);
+
public:
typedef t_connection_context connection_context;
- levin_commands_handler<t_connection_context>* m_pcommands_handler;
uint64_t m_max_packet_size;
uint64_t m_invoke_timeout;
@@ -91,8 +93,9 @@ public:
template<class callback_t>
bool for_connection(const boost::uuids::uuid &connection_id, callback_t cb);
size_t get_connections_count();
+ void set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*) = NULL);
- async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE)
+ async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE)
{}
void del_out_connections(size_t count);
};
@@ -832,6 +835,15 @@ size_t async_protocol_handler_config<t_connection_context>::get_connections_coun
}
//------------------------------------------------------------------------------------------
template<class t_connection_context>
+void async_protocol_handler_config<t_connection_context>::set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*))
+{
+ if (m_pcommands_handler && m_pcommands_handler_destroy)
+ (*m_pcommands_handler_destroy)(m_pcommands_handler);
+ m_pcommands_handler = handler;
+ m_pcommands_handler_destroy = destroy;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
int async_protocol_handler_config<t_connection_context>::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id)
{
async_protocol_handler<t_connection_context>* aph;
diff --git a/contrib/epee/tests/src/net/test_net.h b/contrib/epee/tests/src/net/test_net.h
index 5b21036bb..2e1b1e5fd 100644
--- a/contrib/epee/tests/src/net/test_net.h
+++ b/contrib/epee/tests/src/net/test_net.h
@@ -155,7 +155,7 @@ namespace tests
bool init(const std::string& bind_port = "", const std::string& bind_ip = "0.0.0.0")
{
- m_net_server.get_config_object().m_pcommands_handler = this;
+ m_net_server.get_config_object().set_handler(this);
m_net_server.get_config_object().m_invoke_timeout = 1000;
LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port);
return m_net_server.init_server(bind_port, bind_ip);
diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c
index b3de9702f..87b244ce7 100644
--- a/external/db_drivers/liblmdb/mdb.c
+++ b/external/db_drivers/liblmdb/mdb.c
@@ -304,7 +304,8 @@ union semun {
# else
# define MDB_USE_ROBUST 1
/* glibc < 2.12 only provided _np API */
-# if defined(__GLIBC__) && GLIBC_VER < 0x02000c
+# if (defined(__GLIBC__) && GLIBC_VER < 0x02000c) || \
+ (defined(PTHREAD_MUTEX_ROBUST_NP) && !defined(PTHREAD_MUTEX_ROBUST))
# define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP
# define pthread_mutexattr_setrobust(attr, flag) pthread_mutexattr_setrobust_np(attr, flag)
# define pthread_mutex_consistent(mutex) pthread_mutex_consistent_np(mutex)
@@ -4977,6 +4978,13 @@ mdb_env_setup_locks(MDB_env *env, char *lpath, int mode, int *excl)
#else /* MDB_USE_POSIX_MUTEX: */
pthread_mutexattr_t mattr;
+ /* Solaris needs this before initing a robust mutex. Otherwise
+ * it may skip the init and return EBUSY "seems someone already
+ * inited" or EINVAL "it was inited differently".
+ */
+ memset(env->me_txns->mti_rmutex, 0, sizeof(*env->me_txns->mti_rmutex));
+ memset(env->me_txns->mti_wmutex, 0, sizeof(*env->me_txns->mti_wmutex));
+
if ((rc = pthread_mutexattr_init(&mattr))
|| (rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED))
#ifdef MDB_ROBUST_SUPPORTED
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 838385e8a..79676b808 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -147,8 +147,9 @@ struct txpool_tx_meta_t
uint8_t kept_by_block;
uint8_t relayed;
uint8_t do_not_relay;
+ uint8_t double_spend_seen: 1;
- uint8_t padding[77]; // till 192 bytes
+ uint8_t padding[76]; // till 192 bytes
};
#define DBF_SAFE 1
@@ -1314,7 +1315,7 @@ public:
/**
* @brief get the number of transactions in the txpool
*/
- virtual uint64_t get_txpool_tx_count() const = 0;
+ virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const = 0;
/**
* @brief check whether a txid is in the txpool
@@ -1369,7 +1370,7 @@ public:
*
* @return false if the function returns false for any transaction, otherwise true
*/
- virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false) const = 0;
+ virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const = 0;
/**
* @brief runs a function over all key images stored
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 5bd02bcf7..6ebb35639 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1522,21 +1522,49 @@ void BlockchainLMDB::update_txpool_tx(const crypto::hash &txid, const txpool_tx_
}
}
-uint64_t BlockchainLMDB::get_txpool_tx_count() const
+uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
- TXN_PREFIX_RDONLY();
int result;
+ uint64_t num_entries = 0;
- MDB_stat db_stats;
- if ((result = mdb_stat(m_txn, m_txpool_meta, &db_stats)))
- throw0(DB_ERROR(lmdb_error("Failed to query m_txpool_meta: ", result).c_str()));
+ TXN_PREFIX_RDONLY();
+
+ if (include_unrelayed_txes)
+ {
+ // No filtering, we can get the number of tx the "fast" way
+ MDB_stat db_stats;
+ if ((result = mdb_stat(m_txn, m_txpool_meta, &db_stats)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_txpool_meta: ", result).c_str()));
+ num_entries = db_stats.ms_entries;
+ }
+ else
+ {
+ // Filter unrelayed tx out of the result, so we need to loop over transactions and check their meta data
+ RCURSOR(txpool_meta);
+ RCURSOR(txpool_blob);
+ MDB_val k;
+ MDB_val v;
+ MDB_cursor_op op = MDB_FIRST;
+ while (1)
+ {
+ result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, op);
+ op = MDB_NEXT;
+ if (result == MDB_NOTFOUND)
+ break;
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str()));
+ const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data;
+ if (!meta.do_not_relay)
+ ++num_entries;
+ }
+ }
TXN_POSTFIX_RDONLY();
- return db_stats.ms_entries;
+ return num_entries;
}
bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid) const
@@ -1633,7 +1661,7 @@ cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid
return bd;
}
-bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob) const
+bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1657,6 +1685,9 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&,
throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str()));
const crypto::hash txid = *(const crypto::hash*)k.mv_data;
const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data;
+ if (!include_unrelayed_txes && meta.do_not_relay)
+ // Skipping that tx
+ continue;
const cryptonote::blobdata *passed_bd = NULL;
cryptonote::blobdata bd;
if (include_blob)
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 98571a7f8..fce8f29ed 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -243,13 +243,13 @@ public:
virtual void add_txpool_tx(const transaction &tx, const txpool_tx_meta_t& meta);
virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& meta);
- virtual uint64_t get_txpool_tx_count() const;
+ virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const;
virtual bool txpool_has_tx(const crypto::hash &txid) const;
virtual void remove_txpool_tx(const crypto::hash& txid);
virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const;
virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const;
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const;
- virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false) const;
+ virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const;
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
virtual bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const;
diff --git a/src/common/util.h b/src/common/util.h
index 2e4d6e917..1aac026c1 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -148,9 +148,10 @@ namespace tools
}
return r;
#else
- /* Only blocks SIGINT and SIGTERM */
+ /* Only blocks SIGINT, SIGTERM and SIGPIPE */
signal(SIGINT, posix_handler);
signal(SIGTERM, posix_handler);
+ signal(SIGPIPE, SIG_IGN);
m_handler = t;
return true;
#endif
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 9d89c6280..4e1ab8a48 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -3166,9 +3166,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 +3351,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 +3368,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 +4211,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 +4231,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 +4381,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..3f2930fb0 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -947,11 +947,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 61f844612..3f56ffac7 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1182,21 +1182,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 +1210,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 7340e1024..a3d47280a 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -420,11 +420,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 +435,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 +458,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/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 9071c330c..6bfcfe529 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -187,6 +187,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 +229,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 +268,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 +357,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 +380,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 +436,7 @@ namespace cryptonote
remove.insert(txid);
}
return true;
- });
+ }, false);
if (!remove.empty())
{
@@ -494,7 +498,7 @@ namespace cryptonote
}
}
return true;
- });
+ }, false);
return true;
}
//---------------------------------------------------------------------------------
@@ -521,14 +525,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 +546,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 +567,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 +598,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 +648,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 +671,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 +690,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 +740,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 +872,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 +903,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 +955,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 +1110,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
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 2c11dcb31..83b04bff5 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -814,7 +814,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() {
}
else
{
- if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK)
+ if (!m_rpc_server->on_get_transaction_pool(req, res, false) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
@@ -840,6 +840,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() {
<< "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
<< "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl
+ << "double_spend_seen: " << (tx_info.double_spend_seen ? '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
@@ -898,7 +899,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
}
else
{
- if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK)
+ if (!m_rpc_server->on_get_transaction_pool(req, res, false) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
@@ -922,6 +923,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
<< "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
<< "kept_by_block: " << (tx_info.kept_by_block ? 'T' : 'F') << std::endl
+ << "double_spend_seen: " << (tx_info.double_spend_seen ? '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
@@ -954,7 +956,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
else
{
memset(&res.pool_stats, 0, sizeof(res.pool_stats));
- if (!m_rpc_server->on_get_transaction_pool_stats(req, res) || res.status != CORE_RPC_STATUS_OK)
+ if (!m_rpc_server->on_get_transaction_pool_stats(req, res, false) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
@@ -984,7 +986,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
tools::msg_writer() << n_transactions << " tx(es), " << res.pool_stats.bytes_total << " bytes total (min " << res.pool_stats.bytes_min << ", max " << res.pool_stats.bytes_max << ", avg " << avg_bytes << ", median " << res.pool_stats.bytes_med << ")" << std::endl
<< "fees " << cryptonote::print_money(res.pool_stats.fee_total) << " (avg " << cryptonote::print_money(n_transactions ? res.pool_stats.fee_total / n_transactions : 0) << " per tx" << ", " << cryptonote::print_money(res.pool_stats.bytes_total ? res.pool_stats.fee_total / res.pool_stats.bytes_total : 0) << " per byte)" << std::endl
- << res.pool_stats.num_not_relayed << " not relayed, " << res.pool_stats.num_failing << " failing, " << res.pool_stats.num_10m << " older than 10 minutes (oldest " << (res.pool_stats.oldest == 0 ? "-" : get_human_time_ago(res.pool_stats.oldest, now)) << "), " << backlog_message;
+ << res.pool_stats.num_double_spends << " double spends, " << res.pool_stats.num_not_relayed << " not relayed, " << res.pool_stats.num_failing << " failing, " << res.pool_stats.num_10m << " older than 10 minutes (oldest " << (res.pool_stats.oldest == 0 ? "-" : get_human_time_ago(res.pool_stats.oldest, now)) << "), " << backlog_message;
if (n_transactions > 1 && res.pool_stats.histo.size())
{
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 6162d649b..ee3ff160a 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -560,7 +560,7 @@ namespace nodetool
//configure self
m_net_server.set_threads_prefix("P2P");
- m_net_server.get_config_object().m_pcommands_handler = this;
+ m_net_server.get_config_object().set_handler(this);
m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT;
m_net_server.set_connection_filter(this);
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index b3ce30d0c..9094ec732 100755
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -478,15 +478,17 @@ namespace cryptonote
// try the pool for any missing txes
size_t found_in_pool = 0;
std::unordered_set<crypto::hash> pool_tx_hashes;
+ std::unordered_map<crypto::hash, bool> double_spend_seen;
if (!missed_txs.empty())
{
- std::list<transaction> pool_txs;
- bool r = m_core.get_pool_transactions(pool_txs);
+ std::vector<tx_info> pool_tx_info;
+ std::vector<spent_key_image_info> pool_key_image_info;
+ bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info);
if(r)
{
// sort to match original request
std::list<transaction> sorted_txs;
- std::list<cryptonote::transaction>::const_iterator i;
+ std::vector<tx_info>::const_iterator i;
for (const crypto::hash &h: vh)
{
if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end())
@@ -500,11 +502,26 @@ namespace cryptonote
sorted_txs.push_back(std::move(txs.front()));
txs.pop_front();
}
- else if ((i = std::find_if(pool_txs.begin(), pool_txs.end(), [h](cryptonote::transaction &tx) { return h == cryptonote::get_transaction_hash(tx); })) != pool_txs.end())
+ else if ((i = std::find_if(pool_tx_info.begin(), pool_tx_info.end(), [h](const tx_info &txi) { return epee::string_tools::pod_to_hex(h) == txi.id_hash; })) != pool_tx_info.end())
{
- sorted_txs.push_back(*i);
+ cryptonote::transaction tx;
+ if (!cryptonote::parse_and_validate_tx_from_blob(i->tx_blob, tx))
+ {
+ res.status = "Failed to parse and validate tx from blob";
+ return true;
+ }
+ sorted_txs.push_back(tx);
missed_txs.remove(h);
pool_tx_hashes.insert(h);
+ const std::string hash_string = epee::string_tools::pod_to_hex(h);
+ for (const auto &ti: pool_tx_info)
+ {
+ if (ti.id_hash == hash_string)
+ {
+ double_spend_seen.insert(std::make_pair(h, ti.double_spend_seen));
+ break;
+ }
+ }
++found_in_pool;
}
}
@@ -530,11 +547,21 @@ namespace cryptonote
if (e.in_pool)
{
e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max();
+ if (double_spend_seen.find(tx_hash) != double_spend_seen.end())
+ {
+ e.double_spend_seen = double_spend_seen[tx_hash];
+ }
+ else
+ {
+ MERROR("Failed to determine double spend status for " << tx_hash);
+ e.double_spend_seen = false;
+ }
}
else
{
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height);
+ e.double_spend_seen = false;
}
// fill up old style responses too, in case an old wallet asks
@@ -564,7 +591,7 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res)
+ bool core_rpc_server::on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, bool request_has_rpc_origin)
{
CHECK_CORE_BUSY();
std::vector<crypto::key_image> key_images;
@@ -596,7 +623,7 @@ namespace cryptonote
// check the pool too
std::vector<cryptonote::tx_info> txs;
std::vector<cryptonote::spent_key_image_info> ki;
- r = m_core.get_pool_transactions_and_spent_keys_info(txs, ki);
+ r = m_core.get_pool_transactions_and_spent_keys_info(txs, ki, !request_has_rpc_origin || !m_restricted);
if(!r)
{
res.status = "Failed";
@@ -843,26 +870,26 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res)
+ bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, bool request_has_rpc_origin)
{
CHECK_CORE_BUSY();
- m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images);
+ m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted);
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res)
+ bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin)
{
CHECK_CORE_BUSY();
- m_core.get_pool_transaction_hashes(res.tx_hashes);
+ m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !m_restricted);
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res)
+ bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, bool request_has_rpc_origin)
{
CHECK_CORE_BUSY();
- m_core.get_pool_transaction_stats(res.pool_stats);
+ m_core.get_pool_transaction_stats(res.pool_stats, !request_has_rpc_origin || !m_restricted);
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -1825,7 +1852,7 @@ namespace cryptonote
const command_line::arg_descriptor<bool> core_rpc_server::arg_restricted_rpc = {
"restricted-rpc"
- , "Restrict RPC to view only commands"
+ , "Restrict RPC to view only commands and do not return privacy sensitive data in RPC calls"
, false
};
} // namespace cryptonote
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 73a308a72..79405a9d3 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -137,7 +137,7 @@ namespace cryptonote
bool on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res);
bool on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res);
bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res);
- bool on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res);
+ bool on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, bool request_has_rpc_origin = true);
bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res);
bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res);
bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res);
@@ -153,9 +153,9 @@ namespace cryptonote
bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res);
bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res);
bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res);
- bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res);
- bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res);
- bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res);
+ bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, bool request_has_rpc_origin = true);
+ bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin = true);
+ bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, bool request_has_rpc_origin = true);
bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res);
bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res);
bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index ee2a79eb4..57799bb81 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -566,6 +566,7 @@ namespace cryptonote
std::string as_hex;
std::string as_json;
bool in_pool;
+ bool double_spend_seen;
uint64_t block_height;
uint64_t block_timestamp;
std::vector<uint64_t> output_indices;
@@ -575,6 +576,7 @@ namespace cryptonote
KV_SERIALIZE(as_hex)
KV_SERIALIZE(as_json)
KV_SERIALIZE(in_pool)
+ KV_SERIALIZE(double_spend_seen)
KV_SERIALIZE(block_height)
KV_SERIALIZE(block_timestamp)
KV_SERIALIZE(output_indices)
@@ -1357,6 +1359,8 @@ namespace cryptonote
bool relayed;
uint64_t last_relayed_time;
bool do_not_relay;
+ bool double_spend_seen;
+ std::string tx_blob;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(id_hash)
@@ -1372,6 +1376,8 @@ namespace cryptonote
KV_SERIALIZE(relayed)
KV_SERIALIZE(last_relayed_time)
KV_SERIALIZE(do_not_relay)
+ KV_SERIALIZE(double_spend_seen)
+ KV_SERIALIZE(tx_blob)
END_KV_SERIALIZE_MAP()
};
@@ -1480,6 +1486,7 @@ namespace cryptonote
uint32_t num_not_relayed;
uint64_t histo_98pc;
std::vector<txpool_histo> histo;
+ uint32_t num_double_spends;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(bytes_total)
@@ -1494,6 +1501,7 @@ namespace cryptonote
KV_SERIALIZE(num_not_relayed)
KV_SERIALIZE(histo_98pc)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(histo)
+ KV_SERIALIZE(num_double_spends)
END_KV_SERIALIZE_MAP()
};
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
index 00f1e0caa..581048eaf 100644
--- a/src/rpc/message_data_structs.h
+++ b/src/rpc/message_data_structs.h
@@ -95,6 +95,7 @@ namespace rpc
uint64_t last_relayed_time;
bool relayed;
bool do_not_relay;
+ bool double_spend_seen;
};
typedef std::unordered_map<crypto::key_image, std::vector<crypto::hash> > key_images_with_tx_hashes;
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index a40821d19..6e6e51528 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -755,6 +755,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx
INSERT_INTO_JSON_OBJECT(val, doc, last_relayed_time, tx.last_relayed_time);
INSERT_INTO_JSON_OBJECT(val, doc, relayed, tx.relayed);
INSERT_INTO_JSON_OBJECT(val, doc, do_not_relay, tx.do_not_relay);
+ INSERT_INTO_JSON_OBJECT(val, doc, double_spend_seen, tx.double_spend_seen);
}
@@ -777,6 +778,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx)
GET_FROM_JSON_OBJECT(val, tx.last_relayed_time, last_relayed_time);
GET_FROM_JSON_OBJECT(val, tx.relayed, relayed);
GET_FROM_JSON_OBJECT(val, tx.do_not_relay, do_not_relay);
+ GET_FROM_JSON_OBJECT(val, tx.double_spend_seen, double_spend_seen);
}
void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val)
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 03b280acf..7ab2e5cda 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -4437,15 +4437,18 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
try
{
m_wallet->update_pool_state();
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
- const tools::wallet2::payment_details &pd = i->second;
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
+ const tools::wallet2::payment_details &pd = i->second.m_pd;
std::string payment_id = string_tools::pod_to_hex(i->first);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
- message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str();
+ std::string double_spend_note;
+ if (i->second.m_double_spend_seen)
+ double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] ");
+ message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str();
}
}
catch (const std::exception& e)
@@ -5461,10 +5464,10 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
try
{
m_wallet->update_pool_state();
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
m_wallet->get_unconfirmed_payments(pool_payments);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
- const tools::wallet2::payment_details &pd = i->second;
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
+ const tools::wallet2::payment_details &pd = i->second.m_pd;
if (pd.m_tx_hash == txid)
{
std::string payment_id = string_tools::pod_to_hex(i->first);
@@ -5477,6 +5480,8 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
success_msg_writer() << "Payment ID: " << payment_id;
success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor;
success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
+ if (i->second.m_double_spend_seen)
+ success_msg_writer() << tr("Double spend seen on the network: this transaction may or may not end up being mined");
return true;
}
}
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index 59eca3dd7..8a8243047 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -217,10 +217,10 @@ void TransactionHistoryImpl::refresh()
// unconfirmed payments (tx pool)
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> upayments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> upayments;
m_wallet->m_wallet->get_unconfirmed_payments(upayments);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
- const tools::wallet2::payment_details &pd = i->second;
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
+ const tools::wallet2::payment_details &pd = i->second.m_pd;
std::string payment_id = string_tools::pod_to_hex(i->first);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 54ec73692..4d9de3bc5 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -453,6 +453,21 @@ std::string strjoin(const std::vector<size_t> &V, const char *sep)
return ss.str();
}
+static void emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container,
+ const crypto::hash &key, const tools::wallet2::pool_payment_details &pd)
+{
+ auto range = container.equal_range(key);
+ for (auto i = range.first; i != range.second; ++i)
+ {
+ if (i->second.m_pd.m_tx_hash == pd.m_pd.m_tx_hash)
+ {
+ i->second = pd;
+ return;
+ }
+ }
+ container.emplace(key, pd);
+}
+
} //namespace
namespace tools
@@ -802,7 +817,7 @@ void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote
++num_vouts_received;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
+void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen)
{
// In this function, tx (probably) only contains the base information
// (that is, the prunable stuff may or may not be included)
@@ -1172,7 +1187,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
payment.m_timestamp = ts;
payment.m_subaddr_index = i.first;
if (pool) {
- m_unconfirmed_payments.emplace(payment_id, payment);
+ emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen});
if (0 != m_callback)
m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index);
}
@@ -1250,7 +1265,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
{
TIME_MEASURE_START(miner_tx_handle_time);
- process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false);
+ process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false, false);
TIME_MEASURE_FINISH(miner_tx_handle_time);
TIME_MEASURE_START(txs_handle_time);
@@ -1261,7 +1276,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
cryptonote::transaction tx;
bool r = parse_and_validate_tx_base_from_blob(txblob, tx);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
- process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false);
+ process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false, false);
++idx;
}
TIME_MEASURE_FINISH(txs_handle_time);
@@ -1529,10 +1544,10 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes)
{
// remove pool txes to us that aren't in the pool anymore
- std::unordered_multimap<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
+ std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin();
while (uit != m_unconfirmed_payments.end())
{
- const crypto::hash &txid = uit->second.m_tx_hash;
+ const crypto::hash &txid = uit->second.m_pd.m_tx_hash;
bool found = false;
for (const auto &it2: tx_hashes)
{
@@ -1635,23 +1650,27 @@ void wallet2::update_pool_state(bool refreshed)
MDEBUG("update_pool_state done second loop");
// gather txids of new pool txes to us
- std::vector<crypto::hash> txids;
+ std::vector<std::pair<crypto::hash, bool>> txids;
for (const auto &txid: res.tx_hashes)
{
- if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
- {
- LOG_PRINT_L2("Already seen " << txid << ", skipped");
- continue;
- }
bool txid_found_in_up = false;
for (const auto &up: m_unconfirmed_payments)
{
- if (up.second.m_tx_hash == txid)
+ if (up.second.m_pd.m_tx_hash == txid)
{
txid_found_in_up = true;
break;
}
}
+ if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
+ {
+ // if it's for us, we want to keep track of whether we saw a double spend, so don't bail out
+ if (!txid_found_in_up)
+ {
+ LOG_PRINT_L2("Already seen " << txid << ", and not for us, skipped");
+ continue;
+ }
+ }
if (!txid_found_in_up)
{
LOG_PRINT_L1("Found new pool tx: " << txid);
@@ -1679,7 +1698,7 @@ void wallet2::update_pool_state(bool refreshed)
if (!found)
{
// not one of those we sent ourselves
- txids.push_back(txid);
+ txids.push_back({txid, false});
}
else
{
@@ -1689,6 +1708,7 @@ void wallet2::update_pool_state(bool refreshed)
else
{
LOG_PRINT_L1("Already saw that one, it's for us");
+ txids.push_back({txid, true});
}
}
@@ -1697,8 +1717,8 @@ void wallet2::update_pool_state(bool refreshed)
{
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
- for (const auto &txid: txids)
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ for (const auto &p: txids)
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first));
MDEBUG("asking for " << txids.size() << " transactions");
req.decode_as_json = false;
m_daemon_rpc_mutex.lock();
@@ -1720,10 +1740,11 @@ void wallet2::update_pool_state(bool refreshed)
{
if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash))
{
- const std::vector<crypto::hash>::const_iterator i = std::find(txids.begin(), txids.end(), tx_hash);
+ const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(),
+ [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
if (i != txids.end())
{
- process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
+ process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen);
m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
{
@@ -3082,11 +3103,11 @@ void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wall
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
+void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
{
for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {
- if ((!subaddr_account || *subaddr_account == i->second.m_subaddr_index.major) &&
- (subaddr_indices.empty() || subaddr_indices.count(i->second.m_subaddr_index.minor) == 1))
+ if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) &&
+ (subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1))
unconfirmed_payments.push_back(*i);
}
}
@@ -5138,7 +5159,7 @@ void wallet2::light_wallet_get_address_txs()
payments_txs.push_back(p.second.m_tx_hash);
std::vector<crypto::hash> unconfirmed_payments_txs;
for(const auto &up: m_unconfirmed_payments)
- unconfirmed_payments_txs.push_back(up.second.m_tx_hash);
+ unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash);
// for balance calculation
uint64_t wallet_total_sent = 0;
@@ -5204,7 +5225,11 @@ void wallet2::light_wallet_get_address_txs()
if (t.mempool) {
if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
pool_txs.push_back(tx_hash);
- m_unconfirmed_payments.emplace(tx_hash, payment);
+ // assume false as we don't get that info from the light wallet server
+ crypto::hash payment_id;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::hex_to_pod(t.payment_id, payment_id),
+ error::wallet_internal_error, "Failed to parse payment id");
+ emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false});
if (0 != m_callback) {
m_callback->on_lw_unconfirmed_money_received(t.height, payment.m_tx_hash, payment.m_amount);
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index f1e12a700..ba2fc567d 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -244,6 +244,12 @@ namespace tools
bool m_incoming;
};
+ struct pool_payment_details
+ {
+ payment_details m_pd;
+ bool m_double_spend_seen;
+ };
+
struct unconfirmed_transfer_details
{
cryptonote::transaction_prefix m_tx;
@@ -530,7 +536,7 @@ namespace tools
void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
- void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
+ void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
void rescan_spent();
@@ -585,7 +591,7 @@ namespace tools
std::unordered_map<crypto::hash, payment_details> m;
a & m;
for (std::unordered_map<crypto::hash, payment_details>::const_iterator i = m.begin(); i != m.end(); ++i)
- m_unconfirmed_payments.insert(*i);
+ m_unconfirmed_payments.insert(std::make_pair(i->first, pool_payment_details{i->second, false}));
}
if(ver < 14)
return;
@@ -607,7 +613,15 @@ namespace tools
a & m_address_book;
if(ver < 17)
return;
- a & m_unconfirmed_payments;
+ if (ver < 21)
+ {
+ // we're loading an old version, where m_unconfirmed_payments payload was payment_details
+ std::unordered_map<crypto::hash, payment_details> m;
+ a & m;
+ for (const auto &i: m)
+ m_unconfirmed_payments.insert(std::make_pair(i.first, pool_payment_details{i.second, false}));
+ return;
+ }
if(ver < 18)
return;
a & m_scanned_pool_txs[0];
@@ -621,6 +635,9 @@ namespace tools
if(ver < 21)
return;
a & m_attributes;
+ if(ver < 22)
+ return;
+ a & m_unconfirmed_payments;
}
/*!
@@ -797,7 +814,7 @@ namespace tools
* \param password Password of wallet file
*/
bool load_keys(const std::string& keys_file_name, const std::string& password);
- void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool);
+ void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen);
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices);
void detach_blockchain(uint64_t height);
void get_short_chain_history(std::list<crypto::hash>& ids) const;
@@ -846,7 +863,7 @@ namespace tools
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;
- std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments;
+ std::unordered_multimap<crypto::hash, pool_payment_details> m_unconfirmed_payments;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
cryptonote::checkpoints m_checkpoints;
std::unordered_map<crypto::hash, std::vector<crypto::secret_key>> m_additional_tx_keys;
@@ -908,9 +925,10 @@ namespace tools
std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> > m_key_image_cache;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 21)
+BOOST_CLASS_VERSION(tools::wallet2, 22)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 8)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2)
+BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 7)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 5)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
@@ -1137,7 +1155,14 @@ namespace boost
}
a & x.m_subaddr_index;
}
-
+
+ template <class Archive>
+ inline void serialize(Archive& a, tools::wallet2::pool_payment_details& x, const boost::serialization::version_type ver)
+ {
+ a & x.m_pd;
+ a & x.m_double_spend_seen;
+ }
+
template <class Archive>
inline void serialize(Archive& a, tools::wallet2::address_book_row& x, const boost::serialization::version_type ver)
{
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 9e6a97bdc..5dbf30419 100755
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -299,8 +299,9 @@ namespace tools
entry.subaddr_index = { pd.m_subaddr_account, 0 };
}
//------------------------------------------------------------------------------------------------------------------------------
- void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
+ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd)
{
+ const tools::wallet2::payment_details &pd = ppd.m_pd;
entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
entry.payment_id = string_tools::pod_to_hex(payment_id);
if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
@@ -311,6 +312,7 @@ namespace tools
entry.unlock_time = pd.m_unlock_time;
entry.fee = 0; // TODO
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
+ entry.double_spend_seen = ppd.m_double_spend_seen;
entry.type = "pool";
entry.subaddr_index = pd.m_subaddr_index;
}
@@ -1357,9 +1359,9 @@ namespace tools
{
m_wallet->update_pool_state();
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments, req.account_index, req.subaddr_indices);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.pool.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.pool.back(), i->first, i->second);
}
@@ -1430,10 +1432,10 @@ namespace tools
m_wallet->update_pool_state();
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
m_wallet->get_unconfirmed_payments(pool_payments);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
- if (i->second.m_tx_hash == txid)
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
+ if (i->second.m_pd.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index b38726cb7..a2677ef1b 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -163,7 +163,7 @@ namespace tools
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd);
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd);
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd);
- void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd);
+ void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &pd);
bool not_open(epee::json_rpc::error& er);
uint64_t adjust_mixin(uint64_t mixin);
void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index ffc2e2d49..06f2456c3 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -794,6 +794,7 @@ namespace wallet_rpc
std::string type;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
+ bool double_spend_seen;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txid);
@@ -807,6 +808,7 @@ namespace wallet_rpc
KV_SERIALIZE(type);
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index);
+ KV_SERIALIZE(double_spend_seen)
END_KV_SERIALIZE_MAP()
};
diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp
index 376d7ee53..c930a5b57 100644
--- a/tests/net_load_tests/clt.cpp
+++ b/tests/net_load_tests/clt.cpp
@@ -193,7 +193,7 @@ namespace
{
m_thread_count = (std::max)(min_thread_count, boost::thread::hardware_concurrency() / 2);
- m_tcp_server.get_config_object().m_pcommands_handler = &m_commands_handler;
+ m_tcp_server.get_config_object().set_handler(&m_commands_handler);
m_tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT;
ASSERT_TRUE(m_tcp_server.init_server(clt_port, "127.0.0.1"));
@@ -238,9 +238,10 @@ namespace
static void TearDownTestCase()
{
// Stop server
- test_levin_commands_handler commands_handler;
- test_tcp_server tcp_server(epee::net_utils::e_connection_type_NET);
- tcp_server.get_config_object().m_pcommands_handler = &commands_handler;
+ test_levin_commands_handler *commands_handler_ptr = new test_levin_commands_handler();
+ test_levin_commands_handler &commands_handler = *commands_handler_ptr;
+ test_tcp_server tcp_server(epee::net_utils::e_connection_type_RPC);
+ tcp_server.get_config_object().set_handler(commands_handler_ptr, [](epee::levin::levin_commands_handler<test_connection_context> *handler)->void { delete handler; });
tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT;
if (!tcp_server.init_server(clt_port, "127.0.0.1")) return;
diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h
index f74282683..ce9d8b6fe 100644
--- a/tests/net_load_tests/net_load_tests.h
+++ b/tests/net_load_tests/net_load_tests.h
@@ -151,6 +151,11 @@ namespace net_load_tests
bool handle_new_connection(const boost::uuids::uuid& connection_id, bool ignore_close_fails = false)
{
size_t idx = m_next_opened_conn_idx.fetch_add(1, std::memory_order_relaxed);
+ if (idx >= m_connections.size())
+ {
+ LOG_PRINT_L0("ERROR: connections overflow");
+ exit(1);
+ }
m_connections[idx] = connection_id;
size_t prev_connection_count = m_opened_connection_count.fetch_add(1, std::memory_order_relaxed);
diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp
index e6dee1639..f0b0889ea 100644
--- a/tests/net_load_tests/srv.cpp
+++ b/tests/net_load_tests/srv.cpp
@@ -224,8 +224,8 @@ int main(int argc, char** argv)
if (!tcp_server.init_server(srv_port, "127.0.0.1"))
return 1;
- srv_levin_commands_handler commands_handler(tcp_server);
- tcp_server.get_config_object().m_pcommands_handler = &commands_handler;
+ srv_levin_commands_handler *commands_handler = new srv_levin_commands_handler(tcp_server);
+ tcp_server.get_config_object().set_handler(commands_handler, [](epee::levin::levin_commands_handler<test_connection_context> *handler) { delete handler; });
tcp_server.get_config_object().m_invoke_timeout = 10000;
//tcp_server.get_config_object().m_max_packet_size = max_packet_size;
diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp
index d2aa31555..c749c2531 100644
--- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp
+++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp
@@ -187,9 +187,11 @@ namespace
typedef std::unique_ptr<test_connection> test_connection_ptr;
- async_protocol_handler_test()
+ async_protocol_handler_test():
+ m_pcommands_handler(new test_levin_commands_handler()),
+ m_commands_handler(*m_pcommands_handler)
{
- m_handler_config.m_pcommands_handler = &m_commands_handler;
+ m_handler_config.set_handler(m_pcommands_handler, [](epee::levin::levin_commands_handler<test_levin_connection_context> *handler) { delete handler; });
m_handler_config.m_invoke_timeout = invoke_timeout;
m_handler_config.m_max_packet_size = max_packet_size;
}
@@ -212,7 +214,7 @@ namespace
protected:
boost::asio::io_service m_io_service;
test_levin_protocol_handler_config m_handler_config;
- test_levin_commands_handler m_commands_handler;
+ test_levin_commands_handler *m_pcommands_handler, &m_commands_handler;
};
class positive_test_connection_to_levin_protocol_handler_calls : public async_protocol_handler_test
diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp
index 2b0904224..c235f49fd 100644
--- a/tests/unit_tests/hardfork.cpp
+++ b/tests/unit_tests/hardfork.cpp
@@ -115,13 +115,13 @@ public:
virtual void add_txpool_tx(const transaction &tx, const txpool_tx_meta_t& details) {}
virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& details) {}
- virtual uint64_t get_txpool_tx_count() const { return 0; }
+ virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; }
virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; }
virtual void remove_txpool_tx(const crypto::hash& txid) {}
virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const { return txpool_tx_meta_t(); }
virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; }
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; }
- virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false) const { return false; }
+ virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; }
virtual void add_block( const block& blk
, const size_t& block_size
diff --git a/utils/gpg_keys/stoffu.asc b/utils/gpg_keys/stoffu.asc
new file mode 100644
index 000000000..5067ccf90
--- /dev/null
+++ b/utils/gpg_keys/stoffu.asc
@@ -0,0 +1,62 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+hnbUrKukLHjFgd8=
+=GKYE
+-----END PGP PUBLIC KEY BLOCK-----