mirror of
https://github.com/monero-project/monero.git
synced 2024-10-01 11:49:47 -04:00
Merge pull request #2509
ccf53a56
track double spending in the txpool (moneromooo-monero)
This commit is contained in:
commit
3fff292dc1
@ -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
|
||||
|
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
@ -594,6 +598,8 @@ 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;
|
||||
});
|
||||
stats.bytes_med = epee::misc_utils::median(sizes);
|
||||
@ -649,6 +655,7 @@ namespace cryptonote
|
||||
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){
|
||||
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))
|
||||
{
|
||||
@ -668,6 +675,7 @@ 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);
|
||||
@ -712,6 +720,7 @@ 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);
|
||||
@ -843,7 +852,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 +883,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 +935,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
|
||||
|
@ -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
|
||||
@ -391,6 +392,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 +481,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
|
||||
|
@ -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
|
||||
@ -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
|
||||
@ -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())
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -4415,15 +4415,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)
|
||||
@ -5439,10 +5442,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);
|
||||
@ -5455,6 +5458,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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -444,6 +444,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
|
||||
@ -793,7 +808,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)
|
||||
@ -1163,7 +1178,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);
|
||||
}
|
||||
@ -1241,7 +1256,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);
|
||||
@ -1252,7 +1267,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);
|
||||
@ -1520,10 +1535,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)
|
||||
{
|
||||
@ -1626,23 +1641,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);
|
||||
@ -1670,7 +1689,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
|
||||
{
|
||||
@ -1680,6 +1699,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});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1688,8 +1708,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();
|
||||
@ -1711,10 +1731,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)
|
||||
{
|
||||
@ -3073,11 +3094,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);
|
||||
}
|
||||
}
|
||||
@ -5129,7 +5150,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;
|
||||
@ -5195,7 +5216,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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user