track double spending in the txpool
Transactions in the txpool are marked when another transaction is seen double spending one or more of its inputs. This is then exposed wherever appropriate. Note that being marked with this "double spend seen" flag does NOT mean this transaction IS a double spend and will never be mined: it just means that the network has seen at least another transaction spending at least one of the same inputs, so care should be taken to wait for a few confirmations before acting upon that transaction (ie, mostly of use for merchants wanting to accept unconfirmed transactions).
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user