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 kept_by_block;
|
||||||
uint8_t relayed;
|
uint8_t relayed;
|
||||||
uint8_t do_not_relay;
|
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
|
#define DBF_SAFE 1
|
||||||
|
@ -3166,9 +3166,9 @@ bool Blockchain::flush_txes_from_pool(const std::list<crypto::hash> &txids)
|
|||||||
cryptonote::transaction tx;
|
cryptonote::transaction tx;
|
||||||
size_t blob_size;
|
size_t blob_size;
|
||||||
uint64_t fee;
|
uint64_t fee;
|
||||||
bool relayed, do_not_relay;
|
bool relayed, do_not_relay, double_spend_seen;
|
||||||
MINFO("Removing txid " << txid << " from the pool");
|
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");
|
MERROR("Failed to remove txid " << txid << " from the pool");
|
||||||
res = false;
|
res = false;
|
||||||
@ -3351,7 +3351,7 @@ leave:
|
|||||||
transaction tx;
|
transaction tx;
|
||||||
size_t blob_size = 0;
|
size_t blob_size = 0;
|
||||||
uint64_t fee = 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);
|
TIME_MEASURE_START(aa);
|
||||||
|
|
||||||
// XXX old code does not check whether tx exists
|
// XXX old code does not check whether tx exists
|
||||||
@ -3368,7 +3368,7 @@ leave:
|
|||||||
TIME_MEASURE_START(bb);
|
TIME_MEASURE_START(bb);
|
||||||
|
|
||||||
// get transaction with hash <tx_id> from tx_pool
|
// 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);
|
MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
|
||||||
bvc.m_verifivation_failed = true;
|
bvc.m_verifivation_failed = true;
|
||||||
@ -4381,12 +4381,12 @@ void Blockchain::load_compiled_in_block_hashes()
|
|||||||
|
|
||||||
size_t blob_size;
|
size_t blob_size;
|
||||||
uint64_t fee;
|
uint64_t fee;
|
||||||
bool relayed, do_not_relay;
|
bool relayed, do_not_relay, double_spend_seen;
|
||||||
transaction pool_tx;
|
transaction pool_tx;
|
||||||
for(const transaction &tx : txs)
|
for(const transaction &tx : txs)
|
||||||
{
|
{
|
||||||
crypto::hash tx_hash = get_transaction_hash(tx);
|
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))
|
if(have_tx_keyimges_as_spent(tx))
|
||||||
{
|
{
|
||||||
|
mark_double_spend(tx);
|
||||||
LOG_PRINT_L1("Transaction with id= "<< id << " used already spent key images");
|
LOG_PRINT_L1("Transaction with id= "<< id << " used already spent key images");
|
||||||
tvc.m_verifivation_failed = true;
|
tvc.m_verifivation_failed = true;
|
||||||
tvc.m_double_spend = true;
|
tvc.m_double_spend = true;
|
||||||
@ -228,6 +229,7 @@ namespace cryptonote
|
|||||||
meta.last_relayed_time = time(NULL);
|
meta.last_relayed_time = time(NULL);
|
||||||
meta.relayed = relayed;
|
meta.relayed = relayed;
|
||||||
meta.do_not_relay = do_not_relay;
|
meta.do_not_relay = do_not_relay;
|
||||||
|
meta.double_spend_seen = have_tx_keyimges_as_spent(tx);
|
||||||
memset(meta.padding, 0, sizeof(meta.padding));
|
memset(meta.padding, 0, sizeof(meta.padding));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -266,6 +268,7 @@ namespace cryptonote
|
|||||||
meta.last_relayed_time = time(NULL);
|
meta.last_relayed_time = time(NULL);
|
||||||
meta.relayed = relayed;
|
meta.relayed = relayed;
|
||||||
meta.do_not_relay = do_not_relay;
|
meta.do_not_relay = do_not_relay;
|
||||||
|
meta.double_spend_seen = false;
|
||||||
memset(meta.padding, 0, sizeof(meta.padding));
|
memset(meta.padding, 0, sizeof(meta.padding));
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -354,7 +357,7 @@ namespace cryptonote
|
|||||||
return true;
|
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_LOCAL(m_transactions_lock);
|
||||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||||
@ -377,6 +380,7 @@ namespace cryptonote
|
|||||||
fee = meta.fee;
|
fee = meta.fee;
|
||||||
relayed = meta.relayed;
|
relayed = meta.relayed;
|
||||||
do_not_relay = meta.do_not_relay;
|
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
|
// remove first, in case this throws, so key images aren't removed
|
||||||
m_blockchain.remove_txpool_tx(id);
|
m_blockchain.remove_txpool_tx(id);
|
||||||
@ -594,6 +598,8 @@ namespace cryptonote
|
|||||||
uint64_t age = now - meta.receive_time + (now == meta.receive_time);
|
uint64_t age = now - meta.receive_time + (now == meta.receive_time);
|
||||||
agebytes[age].txs++;
|
agebytes[age].txs++;
|
||||||
agebytes[age].bytes += meta.blob_size;
|
agebytes[age].bytes += meta.blob_size;
|
||||||
|
if (meta.double_spend_seen)
|
||||||
|
++stats.num_double_spends;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
stats.bytes_med = epee::misc_utils::median(sizes);
|
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){
|
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;
|
tx_info txi;
|
||||||
txi.id_hash = epee::string_tools::pod_to_hex(txid);
|
txi.id_hash = epee::string_tools::pod_to_hex(txid);
|
||||||
|
txi.tx_blob = *bd;
|
||||||
transaction tx;
|
transaction tx;
|
||||||
if (!parse_and_validate_tx_from_blob(*bd, tx))
|
if (!parse_and_validate_tx_from_blob(*bd, tx))
|
||||||
{
|
{
|
||||||
@ -668,6 +675,7 @@ namespace cryptonote
|
|||||||
txi.relayed = meta.relayed;
|
txi.relayed = meta.relayed;
|
||||||
txi.last_relayed_time = meta.last_relayed_time;
|
txi.last_relayed_time = meta.last_relayed_time;
|
||||||
txi.do_not_relay = meta.do_not_relay;
|
txi.do_not_relay = meta.do_not_relay;
|
||||||
|
txi.double_spend_seen = meta.double_spend_seen;
|
||||||
tx_infos.push_back(txi);
|
tx_infos.push_back(txi);
|
||||||
return true;
|
return true;
|
||||||
}, true);
|
}, true);
|
||||||
@ -712,6 +720,7 @@ namespace cryptonote
|
|||||||
txi.relayed = meta.relayed;
|
txi.relayed = meta.relayed;
|
||||||
txi.last_relayed_time = meta.last_relayed_time;
|
txi.last_relayed_time = meta.last_relayed_time;
|
||||||
txi.do_not_relay = meta.do_not_relay;
|
txi.do_not_relay = meta.do_not_relay;
|
||||||
|
txi.double_spend_seen = meta.double_spend_seen;
|
||||||
tx_infos.push_back(txi);
|
tx_infos.push_back(txi);
|
||||||
return true;
|
return true;
|
||||||
}, 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 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))
|
if(m_blockchain.have_tx_keyimges_as_spent(tx))
|
||||||
|
{
|
||||||
|
txd.double_spend_seen = true;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//transaction is ok.
|
//transaction is ok.
|
||||||
return true;
|
return true;
|
||||||
@ -871,6 +883,39 @@ namespace cryptonote
|
|||||||
return true;
|
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::string tx_memory_pool::print_pool(bool short_format) const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@ -890,6 +935,7 @@ namespace cryptonote
|
|||||||
ss << "blob_size: " << meta.blob_size << std::endl
|
ss << "blob_size: " << meta.blob_size << std::endl
|
||||||
<< "fee: " << print_money(meta.fee) << std::endl
|
<< "fee: " << print_money(meta.fee) << std::endl
|
||||||
<< "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << 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_height: " << meta.max_used_block_height << std::endl
|
||||||
<< "max_used_block_id: " << meta.max_used_block_id << std::endl
|
<< "max_used_block_id: " << meta.max_used_block_id << std::endl
|
||||||
<< "last_failed_height: " << meta.last_failed_height << std::endl
|
<< "last_failed_height: " << meta.last_failed_height << std::endl
|
||||||
|
@ -137,10 +137,11 @@ namespace cryptonote
|
|||||||
* @param fee the transaction fee
|
* @param fee the transaction fee
|
||||||
* @param relayed return-by-reference was transaction relayed to us by the network?
|
* @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 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
|
* @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
|
* @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
|
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 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 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:
|
private:
|
||||||
@ -478,6 +481,11 @@ namespace cryptonote
|
|||||||
*/
|
*/
|
||||||
bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const;
|
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
|
//TODO: confirm the below comments and investigate whether or not this
|
||||||
// is the desired behavior
|
// is the desired behavior
|
||||||
//! map key images to transactions which spent them
|
//! 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
|
<< "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
|
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
|
||||||
<< "kept_by_block: " << (tx_info.kept_by_block ? '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_height: " << tx_info.max_used_block_height << std::endl
|
||||||
<< "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl
|
<< "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl
|
||||||
<< "last_failed_height: " << tx_info.last_failed_height << std::endl
|
<< "last_failed_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
|
<< "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
|
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
|
||||||
<< "kept_by_block: " << (tx_info.kept_by_block ? '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_height: " << tx_info.max_used_block_height << std::endl
|
||||||
<< "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl
|
<< "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl
|
||||||
<< "last_failed_height: " << tx_info.last_failed_height << std::endl
|
<< "last_failed_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
|
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
|
<< "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())
|
if (n_transactions > 1 && res.pool_stats.histo.size())
|
||||||
{
|
{
|
||||||
|
@ -478,15 +478,17 @@ namespace cryptonote
|
|||||||
// try the pool for any missing txes
|
// try the pool for any missing txes
|
||||||
size_t found_in_pool = 0;
|
size_t found_in_pool = 0;
|
||||||
std::unordered_set<crypto::hash> pool_tx_hashes;
|
std::unordered_set<crypto::hash> pool_tx_hashes;
|
||||||
|
std::unordered_map<crypto::hash, bool> double_spend_seen;
|
||||||
if (!missed_txs.empty())
|
if (!missed_txs.empty())
|
||||||
{
|
{
|
||||||
std::list<transaction> pool_txs;
|
std::vector<tx_info> pool_tx_info;
|
||||||
bool r = m_core.get_pool_transactions(pool_txs);
|
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)
|
if(r)
|
||||||
{
|
{
|
||||||
// sort to match original request
|
// sort to match original request
|
||||||
std::list<transaction> sorted_txs;
|
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)
|
for (const crypto::hash &h: vh)
|
||||||
{
|
{
|
||||||
if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end())
|
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()));
|
sorted_txs.push_back(std::move(txs.front()));
|
||||||
txs.pop_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);
|
missed_txs.remove(h);
|
||||||
pool_tx_hashes.insert(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;
|
++found_in_pool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -530,11 +547,21 @@ namespace cryptonote
|
|||||||
if (e.in_pool)
|
if (e.in_pool)
|
||||||
{
|
{
|
||||||
e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max();
|
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
|
else
|
||||||
{
|
{
|
||||||
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
|
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.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
|
// 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_hex;
|
||||||
std::string as_json;
|
std::string as_json;
|
||||||
bool in_pool;
|
bool in_pool;
|
||||||
|
bool double_spend_seen;
|
||||||
uint64_t block_height;
|
uint64_t block_height;
|
||||||
uint64_t block_timestamp;
|
uint64_t block_timestamp;
|
||||||
std::vector<uint64_t> output_indices;
|
std::vector<uint64_t> output_indices;
|
||||||
@ -575,6 +576,7 @@ namespace cryptonote
|
|||||||
KV_SERIALIZE(as_hex)
|
KV_SERIALIZE(as_hex)
|
||||||
KV_SERIALIZE(as_json)
|
KV_SERIALIZE(as_json)
|
||||||
KV_SERIALIZE(in_pool)
|
KV_SERIALIZE(in_pool)
|
||||||
|
KV_SERIALIZE(double_spend_seen)
|
||||||
KV_SERIALIZE(block_height)
|
KV_SERIALIZE(block_height)
|
||||||
KV_SERIALIZE(block_timestamp)
|
KV_SERIALIZE(block_timestamp)
|
||||||
KV_SERIALIZE(output_indices)
|
KV_SERIALIZE(output_indices)
|
||||||
@ -1357,6 +1359,8 @@ namespace cryptonote
|
|||||||
bool relayed;
|
bool relayed;
|
||||||
uint64_t last_relayed_time;
|
uint64_t last_relayed_time;
|
||||||
bool do_not_relay;
|
bool do_not_relay;
|
||||||
|
bool double_spend_seen;
|
||||||
|
std::string tx_blob;
|
||||||
|
|
||||||
BEGIN_KV_SERIALIZE_MAP()
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
KV_SERIALIZE(id_hash)
|
KV_SERIALIZE(id_hash)
|
||||||
@ -1372,6 +1376,8 @@ namespace cryptonote
|
|||||||
KV_SERIALIZE(relayed)
|
KV_SERIALIZE(relayed)
|
||||||
KV_SERIALIZE(last_relayed_time)
|
KV_SERIALIZE(last_relayed_time)
|
||||||
KV_SERIALIZE(do_not_relay)
|
KV_SERIALIZE(do_not_relay)
|
||||||
|
KV_SERIALIZE(double_spend_seen)
|
||||||
|
KV_SERIALIZE(tx_blob)
|
||||||
END_KV_SERIALIZE_MAP()
|
END_KV_SERIALIZE_MAP()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1480,6 +1486,7 @@ namespace cryptonote
|
|||||||
uint32_t num_not_relayed;
|
uint32_t num_not_relayed;
|
||||||
uint64_t histo_98pc;
|
uint64_t histo_98pc;
|
||||||
std::vector<txpool_histo> histo;
|
std::vector<txpool_histo> histo;
|
||||||
|
uint32_t num_double_spends;
|
||||||
|
|
||||||
BEGIN_KV_SERIALIZE_MAP()
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
KV_SERIALIZE(bytes_total)
|
KV_SERIALIZE(bytes_total)
|
||||||
@ -1494,6 +1501,7 @@ namespace cryptonote
|
|||||||
KV_SERIALIZE(num_not_relayed)
|
KV_SERIALIZE(num_not_relayed)
|
||||||
KV_SERIALIZE(histo_98pc)
|
KV_SERIALIZE(histo_98pc)
|
||||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(histo)
|
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(histo)
|
||||||
|
KV_SERIALIZE(num_double_spends)
|
||||||
END_KV_SERIALIZE_MAP()
|
END_KV_SERIALIZE_MAP()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ namespace rpc
|
|||||||
uint64_t last_relayed_time;
|
uint64_t last_relayed_time;
|
||||||
bool relayed;
|
bool relayed;
|
||||||
bool do_not_relay;
|
bool do_not_relay;
|
||||||
|
bool double_spend_seen;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unordered_map<crypto::key_image, std::vector<crypto::hash> > key_images_with_tx_hashes;
|
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, last_relayed_time, tx.last_relayed_time);
|
||||||
INSERT_INTO_JSON_OBJECT(val, doc, relayed, tx.relayed);
|
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, 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.last_relayed_time, last_relayed_time);
|
||||||
GET_FROM_JSON_OBJECT(val, tx.relayed, relayed);
|
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.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)
|
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
|
try
|
||||||
{
|
{
|
||||||
m_wallet->update_pool_state();
|
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);
|
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) {
|
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;
|
const tools::wallet2::payment_details &pd = i->second.m_pd;
|
||||||
std::string payment_id = string_tools::pod_to_hex(i->first);
|
std::string payment_id = string_tools::pod_to_hex(i->first);
|
||||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||||
payment_id = payment_id.substr(0,16);
|
payment_id = payment_id.substr(0,16);
|
||||||
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
|
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)
|
catch (const std::exception& e)
|
||||||
@ -5439,10 +5442,10 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_wallet->update_pool_state();
|
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);
|
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) {
|
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;
|
const tools::wallet2::payment_details &pd = i->second.m_pd;
|
||||||
if (pd.m_tx_hash == txid)
|
if (pd.m_tx_hash == txid)
|
||||||
{
|
{
|
||||||
std::string payment_id = string_tools::pod_to_hex(i->first);
|
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() << "Payment ID: " << payment_id;
|
||||||
success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor;
|
success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor;
|
||||||
success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,10 +217,10 @@ void TransactionHistoryImpl::refresh()
|
|||||||
|
|
||||||
|
|
||||||
// unconfirmed payments (tx pool)
|
// 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);
|
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) {
|
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;
|
const tools::wallet2::payment_details &pd = i->second.m_pd;
|
||||||
std::string payment_id = string_tools::pod_to_hex(i->first);
|
std::string payment_id = string_tools::pod_to_hex(i->first);
|
||||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||||
payment_id = payment_id.substr(0,16);
|
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();
|
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
|
||||||
|
|
||||||
namespace tools
|
namespace tools
|
||||||
@ -793,7 +808,7 @@ void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote
|
|||||||
++num_vouts_received;
|
++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
|
// In this function, tx (probably) only contains the base information
|
||||||
// (that is, the prunable stuff may or may not be included)
|
// (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_timestamp = ts;
|
||||||
payment.m_subaddr_index = i.first;
|
payment.m_subaddr_index = i.first;
|
||||||
if (pool) {
|
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)
|
if (0 != m_callback)
|
||||||
m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index);
|
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)
|
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
|
||||||
{
|
{
|
||||||
TIME_MEASURE_START(miner_tx_handle_time);
|
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_FINISH(miner_tx_handle_time);
|
||||||
|
|
||||||
TIME_MEASURE_START(txs_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;
|
cryptonote::transaction tx;
|
||||||
bool r = parse_and_validate_tx_base_from_blob(txblob, tx);
|
bool r = parse_and_validate_tx_base_from_blob(txblob, tx);
|
||||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
|
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;
|
++idx;
|
||||||
}
|
}
|
||||||
TIME_MEASURE_FINISH(txs_handle_time);
|
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)
|
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
|
// 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())
|
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;
|
bool found = false;
|
||||||
for (const auto &it2: tx_hashes)
|
for (const auto &it2: tx_hashes)
|
||||||
{
|
{
|
||||||
@ -1626,23 +1641,27 @@ void wallet2::update_pool_state(bool refreshed)
|
|||||||
MDEBUG("update_pool_state done second loop");
|
MDEBUG("update_pool_state done second loop");
|
||||||
|
|
||||||
// gather txids of new pool txes to us
|
// 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)
|
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;
|
bool txid_found_in_up = false;
|
||||||
for (const auto &up: m_unconfirmed_payments)
|
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;
|
txid_found_in_up = true;
|
||||||
break;
|
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)
|
if (!txid_found_in_up)
|
||||||
{
|
{
|
||||||
LOG_PRINT_L1("Found new pool tx: " << txid);
|
LOG_PRINT_L1("Found new pool tx: " << txid);
|
||||||
@ -1670,7 +1689,7 @@ void wallet2::update_pool_state(bool refreshed)
|
|||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
// not one of those we sent ourselves
|
// not one of those we sent ourselves
|
||||||
txids.push_back(txid);
|
txids.push_back({txid, false});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1680,6 +1699,7 @@ void wallet2::update_pool_state(bool refreshed)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_PRINT_L1("Already saw that one, it's for us");
|
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::request req;
|
||||||
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
|
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
|
||||||
for (const auto &txid: txids)
|
for (const auto &p: txids)
|
||||||
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
|
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first));
|
||||||
MDEBUG("asking for " << txids.size() << " transactions");
|
MDEBUG("asking for " << txids.size() << " transactions");
|
||||||
req.decode_as_json = false;
|
req.decode_as_json = false;
|
||||||
m_daemon_rpc_mutex.lock();
|
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))
|
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())
|
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);
|
m_scanned_pool_txs[0].insert(tx_hash);
|
||||||
if (m_scanned_pool_txs[0].size() > 5000)
|
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) {
|
for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {
|
||||||
if ((!subaddr_account || *subaddr_account == i->second.m_subaddr_index.major) &&
|
if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) &&
|
||||||
(subaddr_indices.empty() || subaddr_indices.count(i->second.m_subaddr_index.minor) == 1))
|
(subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1))
|
||||||
unconfirmed_payments.push_back(*i);
|
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);
|
payments_txs.push_back(p.second.m_tx_hash);
|
||||||
std::vector<crypto::hash> unconfirmed_payments_txs;
|
std::vector<crypto::hash> unconfirmed_payments_txs;
|
||||||
for(const auto &up: m_unconfirmed_payments)
|
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
|
// for balance calculation
|
||||||
uint64_t wallet_total_sent = 0;
|
uint64_t wallet_total_sent = 0;
|
||||||
@ -5195,7 +5216,11 @@ void wallet2::light_wallet_get_address_txs()
|
|||||||
if (t.mempool) {
|
if (t.mempool) {
|
||||||
if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
|
if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
|
||||||
pool_txs.push_back(tx_hash);
|
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) {
|
if (0 != m_callback) {
|
||||||
m_callback->on_lw_unconfirmed_money_received(t.height, payment.m_tx_hash, payment.m_amount);
|
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;
|
bool m_incoming;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pool_payment_details
|
||||||
|
{
|
||||||
|
payment_details m_pd;
|
||||||
|
bool m_double_spend_seen;
|
||||||
|
};
|
||||||
|
|
||||||
struct unconfirmed_transfer_details
|
struct unconfirmed_transfer_details
|
||||||
{
|
{
|
||||||
cryptonote::transaction_prefix m_tx;
|
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,
|
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;
|
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_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; }
|
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
|
||||||
void rescan_spent();
|
void rescan_spent();
|
||||||
@ -585,7 +591,7 @@ namespace tools
|
|||||||
std::unordered_map<crypto::hash, payment_details> m;
|
std::unordered_map<crypto::hash, payment_details> m;
|
||||||
a & m;
|
a & m;
|
||||||
for (std::unordered_map<crypto::hash, payment_details>::const_iterator i = m.begin(); i != m.end(); ++i)
|
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)
|
if(ver < 14)
|
||||||
return;
|
return;
|
||||||
@ -607,7 +613,15 @@ namespace tools
|
|||||||
a & m_address_book;
|
a & m_address_book;
|
||||||
if(ver < 17)
|
if(ver < 17)
|
||||||
return;
|
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)
|
if(ver < 18)
|
||||||
return;
|
return;
|
||||||
a & m_scanned_pool_txs[0];
|
a & m_scanned_pool_txs[0];
|
||||||
@ -621,6 +635,9 @@ namespace tools
|
|||||||
if(ver < 21)
|
if(ver < 21)
|
||||||
return;
|
return;
|
||||||
a & m_attributes;
|
a & m_attributes;
|
||||||
|
if(ver < 22)
|
||||||
|
return;
|
||||||
|
a & m_unconfirmed_payments;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -797,7 +814,7 @@ namespace tools
|
|||||||
* \param password Password of wallet file
|
* \param password Password of wallet file
|
||||||
*/
|
*/
|
||||||
bool load_keys(const std::string& keys_file_name, const std::string& password);
|
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 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 detach_blockchain(uint64_t height);
|
||||||
void get_short_chain_history(std::list<crypto::hash>& ids) const;
|
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::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, unconfirmed_transfer_details> m_unconfirmed_txs;
|
||||||
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_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;
|
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
|
||||||
cryptonote::checkpoints m_checkpoints;
|
cryptonote::checkpoints m_checkpoints;
|
||||||
std::unordered_map<crypto::hash, std::vector<crypto::secret_key>> m_additional_tx_keys;
|
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;
|
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::transfer_details, 8)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2)
|
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::unconfirmed_transfer_details, 7)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 5)
|
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 5)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
|
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
|
||||||
@ -1138,6 +1156,13 @@ namespace boost
|
|||||||
a & x.m_subaddr_index;
|
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>
|
template <class Archive>
|
||||||
inline void serialize(Archive& a, tools::wallet2::address_book_row& x, const boost::serialization::version_type ver)
|
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 };
|
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.txid = string_tools::pod_to_hex(pd.m_tx_hash);
|
||||||
entry.payment_id = string_tools::pod_to_hex(payment_id);
|
entry.payment_id = string_tools::pod_to_hex(payment_id);
|
||||||
if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
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.unlock_time = pd.m_unlock_time;
|
||||||
entry.fee = 0; // TODO
|
entry.fee = 0; // TODO
|
||||||
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
|
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
|
||||||
|
entry.double_spend_seen = ppd.m_double_spend_seen;
|
||||||
entry.type = "pool";
|
entry.type = "pool";
|
||||||
entry.subaddr_index = pd.m_subaddr_index;
|
entry.subaddr_index = pd.m_subaddr_index;
|
||||||
}
|
}
|
||||||
@ -1357,9 +1359,9 @@ namespace tools
|
|||||||
{
|
{
|
||||||
m_wallet->update_pool_state();
|
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);
|
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());
|
res.pool.push_back(wallet_rpc::transfer_entry());
|
||||||
fill_transfer_entry(res.pool.back(), i->first, i->second);
|
fill_transfer_entry(res.pool.back(), i->first, i->second);
|
||||||
}
|
}
|
||||||
@ -1430,10 +1432,10 @@ namespace tools
|
|||||||
|
|
||||||
m_wallet->update_pool_state();
|
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);
|
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) {
|
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_tx_hash == txid)
|
if (i->second.m_pd.m_tx_hash == txid)
|
||||||
{
|
{
|
||||||
fill_transfer_entry(res.transfer, i->first, i->second);
|
fill_transfer_entry(res.transfer, i->first, i->second);
|
||||||
return true;
|
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 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::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 &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);
|
bool not_open(epee::json_rpc::error& er);
|
||||||
uint64_t adjust_mixin(uint64_t mixin);
|
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);
|
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;
|
std::string type;
|
||||||
uint64_t unlock_time;
|
uint64_t unlock_time;
|
||||||
cryptonote::subaddress_index subaddr_index;
|
cryptonote::subaddress_index subaddr_index;
|
||||||
|
bool double_spend_seen;
|
||||||
|
|
||||||
BEGIN_KV_SERIALIZE_MAP()
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
KV_SERIALIZE(txid);
|
KV_SERIALIZE(txid);
|
||||||
@ -807,6 +808,7 @@ namespace wallet_rpc
|
|||||||
KV_SERIALIZE(type);
|
KV_SERIALIZE(type);
|
||||||
KV_SERIALIZE(unlock_time)
|
KV_SERIALIZE(unlock_time)
|
||||||
KV_SERIALIZE(subaddr_index);
|
KV_SERIALIZE(subaddr_index);
|
||||||
|
KV_SERIALIZE(double_spend_seen)
|
||||||
END_KV_SERIALIZE_MAP()
|
END_KV_SERIALIZE_MAP()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user