mirror of
https://github.com/wownero/wownero-cpp.git
synced 2026-10-17 23:31:32 +02:00
fix query issue where full tx needed
refactor getTransfersAux(), getOutputsAux() query cycles supported
This commit is contained in:
+257
-217
@@ -82,6 +82,37 @@ namespace monero {
|
||||
|
||||
// ----------------------- INTERNAL PRIVATE HELPERS -----------------------
|
||||
|
||||
/**
|
||||
* Remove query criteria which require looking up other transfers/outputs to
|
||||
* fulfill query.
|
||||
*
|
||||
* @param query the query to decontextualize
|
||||
* @return a reference to the query for convenience
|
||||
*/
|
||||
shared_ptr<monero_tx_query> decontextualize(shared_ptr<monero_tx_query> query) {
|
||||
query->m_is_incoming = boost::none;
|
||||
query->m_is_outgoing = boost::none;
|
||||
query->m_transfer_query = boost::none;
|
||||
query->m_output_query = boost::none;
|
||||
return query;
|
||||
}
|
||||
|
||||
bool is_contextual(const monero_transfer_query& query) {
|
||||
if (query.m_tx_query == boost::none) return false;
|
||||
if (query.m_tx_query.get()->m_is_incoming != boost::none) return true; // requires context of all transfers
|
||||
if (query.m_tx_query.get()->m_is_outgoing != boost::none) return true;
|
||||
if (query.m_tx_query.get()->m_output_query != boost::none) return true; // requires context of outputs
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_contextual(const monero_output_query& query) {
|
||||
if (query.m_tx_query == boost::none) return false;
|
||||
if (query.m_tx_query.get()->m_is_incoming != boost::none) return true; // requires context of all transfers
|
||||
if (query.m_tx_query.get()->m_is_outgoing != boost::none) return true;
|
||||
if (query.m_tx_query.get()->m_transfer_query != boost::none) return true; // requires context of transfers
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bool_equals(bool val, const boost::optional<bool>& opt_val) {
|
||||
return opt_val == boost::none ? false : val == *opt_val;
|
||||
}
|
||||
@@ -1389,26 +1420,25 @@ namespace monero {
|
||||
vector<shared_ptr<monero_tx_wallet>> monero_wallet_core::get_txs(const monero_tx_query& query, vector<string>& missing_tx_hashes) const {
|
||||
MTRACE("get_txs(query)");
|
||||
|
||||
// copy and normalize tx query
|
||||
shared_ptr<monero_tx_query> query_ptr = make_shared<monero_tx_query>(query); // convert to shared pointer for copy
|
||||
shared_ptr<monero_tx_query> _query = query_ptr->copy(query_ptr, make_shared<monero_tx_query>()); // deep copy
|
||||
if (_query->m_transfer_query == boost::none) _query->m_transfer_query = make_shared<monero_transfer_query>();
|
||||
if (_query->m_output_query == boost::none) _query->m_output_query = make_shared<monero_output_query>();
|
||||
// copy query
|
||||
shared_ptr<monero_tx_query> query_sp = make_shared<monero_tx_query>(query); // convert to shared pointer
|
||||
shared_ptr<monero_tx_query> _query = query_sp->copy(query_sp, make_shared<monero_tx_query>()); // deep copy
|
||||
|
||||
// // log query
|
||||
// if (_query->m_block != boost::none) cout << "Tx query's rooted at [block]: " << _query->m_block.get()->serialize() << endl;
|
||||
// else cout << "Tx _query: " << _query->serialize() << endl;
|
||||
|
||||
// temporarily disable transfer and output queries in order to collect all tx information
|
||||
shared_ptr<monero_transfer_query> transfer_query = _query->m_transfer_query.get();
|
||||
shared_ptr<monero_output_query> output_query = _query->m_output_query.get();
|
||||
// temporarily disable transfer and output queries in order to collect all tx context
|
||||
boost::optional<shared_ptr<monero_transfer_query>> transfer_query = _query->m_transfer_query;
|
||||
boost::optional<shared_ptr<monero_output_query>> output_query = _query->m_output_query;
|
||||
_query->m_transfer_query = boost::none;
|
||||
_query->m_output_query = boost::none;
|
||||
|
||||
// fetch all transfers that meet tx query
|
||||
monero_transfer_query temp_transfer_query;
|
||||
temp_transfer_query.m_tx_query = make_shared<monero_tx_query>(*_query);
|
||||
vector<shared_ptr<monero_transfer>> transfers = get_transfers(temp_transfer_query);
|
||||
shared_ptr<monero_transfer_query> temp_transfer_query = make_shared<monero_transfer_query>();
|
||||
temp_transfer_query->m_tx_query = decontextualize(_query->copy(_query, make_shared<monero_tx_query>()));
|
||||
temp_transfer_query->m_tx_query.get()->m_transfer_query = temp_transfer_query;
|
||||
vector<shared_ptr<monero_transfer>> transfers = get_transfers_aux(*temp_transfer_query);
|
||||
|
||||
// collect unique txs from transfers while retaining order
|
||||
vector<shared_ptr<monero_tx_wallet>> txs = vector<shared_ptr<monero_tx_wallet>>();
|
||||
@@ -1428,12 +1458,11 @@ namespace monero {
|
||||
}
|
||||
|
||||
// fetch and merge outputs if requested
|
||||
monero_output_query temp_output_query;
|
||||
temp_output_query.m_tx_query = make_shared<monero_tx_query>(*_query);
|
||||
if ((_query->m_include_outputs != boost::none && *_query->m_include_outputs) || !output_query->is_default()) {
|
||||
|
||||
// fetch outputs
|
||||
vector<shared_ptr<monero_output_wallet>> outputs = get_outputs(temp_output_query);
|
||||
if ((_query->m_include_outputs != boost::none && *_query->m_include_outputs) || output_query != boost::none) {
|
||||
shared_ptr<monero_output_query> temp_output_query = make_shared<monero_output_query>();
|
||||
temp_output_query->m_tx_query = decontextualize(_query->copy(_query, make_shared<monero_tx_query>()));
|
||||
temp_output_query->m_tx_query.get()->m_output_query = temp_output_query;
|
||||
vector<shared_ptr<monero_output_wallet>> outputs = get_outputs_aux(*temp_output_query);
|
||||
|
||||
// merge output txs one time while retaining order
|
||||
unordered_set<shared_ptr<monero_tx_wallet>> output_txs;
|
||||
@@ -1485,227 +1514,43 @@ namespace monero {
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_transfer>> monero_wallet_core::get_transfers(const monero_transfer_query& query) const {
|
||||
MTRACE("monero_wallet_core::get_transfers(query)");
|
||||
|
||||
// // log query
|
||||
// if (query.m_tx_query != boost::none) {
|
||||
// if ((*query.m_tx_query)->m_block == boost::none) cout << "Transfer query's tx query rooted at [tx]:" << (*query.m_tx_query)->serialize() << endl;
|
||||
// else cout << "Transfer query's tx query rooted at [block]: " << (*(*query.m_tx_query)->m_block)->serialize() << endl;
|
||||
// }
|
||||
// } else cout << "Transfer query: " << query.serialize() << endl;
|
||||
|
||||
// copy and normalize query
|
||||
shared_ptr<monero_transfer_query> _query;
|
||||
if (query.m_tx_query == boost::none) {
|
||||
shared_ptr<monero_transfer_query> query_ptr = make_shared<monero_transfer_query>(query); // convert to shared pointer for copy // TODO: does this copy unecessarily? copy constructor is not defined
|
||||
_query = query_ptr->copy(query_ptr, make_shared<monero_transfer_query>());
|
||||
} else {
|
||||
shared_ptr<monero_tx_query> tx_query = query.m_tx_query.get()->copy(query.m_tx_query.get(), make_shared<monero_tx_query>());
|
||||
if (query.m_tx_query.get()->m_transfer_query != boost::none && query.m_tx_query.get()->m_transfer_query.get().get() == &query) {
|
||||
_query = tx_query->m_transfer_query.get();
|
||||
} else {
|
||||
if (query.m_tx_query.get()->m_transfer_query != boost::none) throw runtime_error("Transfer query's tx query must be a circular reference or null");
|
||||
shared_ptr<monero_transfer_query> query_ptr = make_shared<monero_transfer_query>(query); // convert query to shared pointer for copy
|
||||
_query = query_ptr->copy(query_ptr, make_shared<monero_transfer_query>());
|
||||
_query->m_tx_query = tx_query;
|
||||
}
|
||||
}
|
||||
if (_query->m_tx_query == boost::none) _query->m_tx_query = make_shared<monero_tx_query>();
|
||||
shared_ptr<monero_tx_query> tx_query = _query->m_tx_query.get();
|
||||
tx_query->m_transfer_query = boost::none; // break circular link for meets_criteria()
|
||||
// get transfers directly if query does not require tx context (e.g. other transfers, outputs)
|
||||
if (!is_contextual(query)) return get_transfers_aux(query);
|
||||
|
||||
// build parameters for m_w2->get_payments()
|
||||
uint64_t min_height = tx_query->m_min_height == boost::none ? 0 : *tx_query->m_min_height;
|
||||
uint64_t max_height = tx_query->m_max_height == boost::none ? CRYPTONOTE_MAX_BLOCK_NUMBER : min((uint64_t) CRYPTONOTE_MAX_BLOCK_NUMBER, *tx_query->m_max_height);
|
||||
if (min_height > 0) min_height--; // TODO monero core: wallet2::get_payments() m_min_height is exclusive, so manually offset to match intended range (issues 5751, #5598)
|
||||
boost::optional<uint32_t> account_index = boost::none;
|
||||
if (_query->m_account_index != boost::none) account_index = *_query->m_account_index;
|
||||
std::set<uint32_t> subaddress_indices;
|
||||
for (int i = 0; i < _query->m_subaddress_indices.size(); i++) {
|
||||
subaddress_indices.insert(_query->m_subaddress_indices[i]);
|
||||
}
|
||||
|
||||
// check if pool txs explicitly requested without daemon connection
|
||||
if (tx_query->m_in_tx_pool != boost::none && tx_query->m_in_tx_pool.get() && !is_connected()) {
|
||||
throw runtime_error("Cannot fetch pool transactions because wallet has no daemon connection");
|
||||
}
|
||||
|
||||
// translate from monero_tx_query to in, out, pending, pool, failed terminology used by monero-wallet-rpc
|
||||
bool can_be_confirmed = !bool_equals(false, tx_query->m_is_confirmed) && !bool_equals(true, tx_query->m_in_tx_pool) && !bool_equals(true, tx_query->m_is_failed) && !bool_equals(false, tx_query->m_is_relayed);
|
||||
bool can_be_in_tx_pool = is_connected() && !bool_equals(true, tx_query->m_is_confirmed) && !bool_equals(false, tx_query->m_in_tx_pool) && !bool_equals(true, tx_query->m_is_failed) && !bool_equals(false, tx_query->m_is_relayed) && tx_query->get_height() == boost::none && tx_query->m_min_height == boost::none;
|
||||
bool can_be_incoming = !bool_equals(false, _query->m_is_incoming) && !bool_equals(true, _query->is_outgoing()) && !bool_equals(true, _query->m_has_destinations);
|
||||
bool can_be_outgoing = !bool_equals(false, _query->is_outgoing()) && !bool_equals(true, _query->m_is_incoming);
|
||||
bool is_in = can_be_incoming && can_be_confirmed;
|
||||
bool is_out = can_be_outgoing && can_be_confirmed;
|
||||
bool is_pending = can_be_outgoing && can_be_in_tx_pool;
|
||||
bool is_pool = can_be_incoming && can_be_in_tx_pool;
|
||||
bool is_failed = !bool_equals(false, tx_query->m_is_failed) && !bool_equals(true, tx_query->m_is_confirmed) && !bool_equals(true, tx_query->m_in_tx_pool);
|
||||
|
||||
// cache unique txs and blocks
|
||||
uint64_t height = get_height();
|
||||
map<string, shared_ptr<monero_tx_wallet>> tx_map;
|
||||
map<uint64_t, shared_ptr<monero_block>> block_map;
|
||||
|
||||
// get confirmed incoming transfers
|
||||
if (is_in) {
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
|
||||
m_w2->get_payments(payments, min_height, max_height, account_index, subaddress_indices);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_incoming_transfer(*m_w2, height, i->first, i->second);
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
}
|
||||
|
||||
// get confirmed outgoing transfers
|
||||
if (is_out) {
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
|
||||
m_w2->get_payments_out(payments, min_height, max_height, account_index, subaddress_indices);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_outgoing_transfer(*m_w2, height, i->first, i->second);
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
}
|
||||
|
||||
// get unconfirmed or failed outgoing transfers
|
||||
if (is_pending || is_failed) {
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
|
||||
m_w2->get_unconfirmed_payments_out(upayments, account_index, subaddress_indices);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_outgoing_transfer_unconfirmed(*m_w2, i->first, i->second);
|
||||
if (tx_query->m_is_failed != boost::none && tx_query->m_is_failed.get() != tx->m_is_failed.get()) continue; // skip merging if tx excluded
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
}
|
||||
|
||||
// get unconfirmed incoming transfers
|
||||
if (is_pool) {
|
||||
|
||||
// update pool state TODO monero-core: this should be encapsulated in wallet when unconfirmed transfers queried
|
||||
std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
|
||||
m_w2->update_pool_state(process_txs);
|
||||
if (!process_txs.empty()) m_w2->process_pool_state(process_txs);
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
|
||||
m_w2->get_unconfirmed_payments(payments, account_index, subaddress_indices);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_incoming_transfer_unconfirmed(*m_w2, i->first, i->second);
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
}
|
||||
|
||||
// sort txs by block height
|
||||
vector<shared_ptr<monero_tx_wallet>> txs ;
|
||||
for (map<string, shared_ptr<monero_tx_wallet>>::const_iterator tx_iter = tx_map.begin(); tx_iter != tx_map.end(); tx_iter++) {
|
||||
txs.push_back(tx_iter->second);
|
||||
}
|
||||
sort(txs.begin(), txs.end(), tx_height_less_than);
|
||||
|
||||
// filter and return transfers
|
||||
// otherwise get txs with full models to fulfill query
|
||||
vector<shared_ptr<monero_transfer>> transfers;
|
||||
for (const shared_ptr<monero_tx_wallet>& tx : txs) {
|
||||
|
||||
// tx is not incoming/outgoing unless already set
|
||||
if (tx->m_is_incoming == boost::none) tx->m_is_incoming = false;
|
||||
if (tx->m_is_outgoing == boost::none) tx->m_is_outgoing = false;
|
||||
|
||||
// sort transfers
|
||||
sort(tx->m_incoming_transfers.begin(), tx->m_incoming_transfers.end(), incoming_transfer_before);
|
||||
|
||||
// collect outgoing transfer, erase if excluded TODO: js does not erase excluded data, port to js
|
||||
if (tx->m_outgoing_transfer != boost::none && _query->meets_criteria(tx->m_outgoing_transfer.get().get())) transfers.push_back(tx->m_outgoing_transfer.get());
|
||||
else tx->m_outgoing_transfer = boost::none;
|
||||
|
||||
// collect incoming transfers, erase if excluded
|
||||
vector<shared_ptr<monero_incoming_transfer>>::iterator iter = tx->m_incoming_transfers.begin();
|
||||
while (iter != tx->m_incoming_transfers.end()) {
|
||||
if (_query->meets_criteria((*iter).get())) {
|
||||
transfers.push_back(*iter);
|
||||
iter++;
|
||||
} else {
|
||||
iter = tx->m_incoming_transfers.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
// remove excluded txs from block
|
||||
if (tx->m_block != boost::none && tx->m_outgoing_transfer == boost::none && tx->m_incoming_transfers.empty()) {
|
||||
tx->m_block.get()->m_txs.erase(std::remove(tx->m_block.get()->m_txs.begin(), tx->m_block.get()->m_txs.end(), tx), tx->m_block.get()->m_txs.end()); // TODO, no way to use const_iterator?
|
||||
for (const shared_ptr<monero_tx_wallet>& tx : get_txs(*(query.m_tx_query.get()))) {
|
||||
for (const shared_ptr<monero_transfer>& transfer : tx->filter_transfers(query)) { // collect queried transfers, erase if excluded
|
||||
transfers.push_back(transfer);
|
||||
}
|
||||
}
|
||||
MTRACE("monero_wallet_core.cpp get_transfers() returning " << transfers.size() << " transfers");
|
||||
|
||||
return transfers;
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_output_wallet>> monero_wallet_core::get_outputs(const monero_output_query& query) const {
|
||||
MTRACE("monero_wallet_core::get_outputs(query)");
|
||||
|
||||
// // log query
|
||||
// MTRACE("Output query: " << query.serialize());
|
||||
// if (query.m_tx_query != boost::none) {
|
||||
// if ((*query.m_tx_query)->m_block == boost::none) MTRACE("Output query's tx query rooted at [tx]:" << (*query.m_tx_query)->serialize());
|
||||
// else MTRACE("Output query's tx query rooted at [block]: " << (*(*query.m_tx_query)->m_block)->serialize());
|
||||
// }
|
||||
// if ((*query.m_tx_query)->m_block == boost::none) cout << "Output query's tx query rooted at [tx]:" << (*query.m_tx_query)->serialize() << endl;
|
||||
// else cout << "Output query's tx query rooted at [block]: " << (*(*query.m_tx_query)->m_block)->serialize() << endl;
|
||||
// } else cout << "Output query: " << query.serialize() << endl;
|
||||
|
||||
// copy and normalize query
|
||||
shared_ptr<monero_output_query> _query;
|
||||
if (query.m_tx_query == boost::none) {
|
||||
shared_ptr<monero_output_query> query_ptr = make_shared<monero_output_query>(query); // convert to shared pointer for copy
|
||||
_query = query_ptr->copy(query_ptr, make_shared<monero_output_query>());
|
||||
} else {
|
||||
shared_ptr<monero_tx_query> tx_query = query.m_tx_query.get()->copy(query.m_tx_query.get(), make_shared<monero_tx_query>());
|
||||
if (query.m_tx_query.get()->m_output_query != boost::none && query.m_tx_query.get()->m_output_query.get().get() == &query) {
|
||||
_query = tx_query->m_output_query.get();
|
||||
} else {
|
||||
if (query.m_tx_query.get()->m_output_query != boost::none) throw runtime_error("Output query's tx query must be a circular reference or null");
|
||||
shared_ptr<monero_output_query> query_ptr = make_shared<monero_output_query>(query); // convert query to shared pointer for copy
|
||||
_query = query_ptr->copy(query_ptr, make_shared<monero_output_query>());
|
||||
_query->m_tx_query = tx_query;
|
||||
}
|
||||
}
|
||||
if (_query->m_tx_query == boost::none) _query->m_tx_query = make_shared<monero_tx_query>();
|
||||
shared_ptr<monero_tx_query> tx_query = _query->m_tx_query.get();
|
||||
tx_query->m_output_query = boost::none; // break circular link for meets_criteria()
|
||||
// get outputs directly if query does not require tx context (e.g. other outputs, transfers)
|
||||
if (!is_contextual(query)) return get_outputs_aux(query);
|
||||
|
||||
// get output data from wallet2
|
||||
tools::wallet2::transfer_container outputs_w2;
|
||||
m_w2->get_transfers(outputs_w2);
|
||||
|
||||
// cache unique txs and blocks
|
||||
map<string, shared_ptr<monero_tx_wallet>> tx_map;
|
||||
map<uint64_t, shared_ptr<monero_block>> block_map;
|
||||
for (const auto& output_w2 : outputs_w2) {
|
||||
// TODO: skip tx building if m_w2 output excluded by indices, etc
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_vout(*m_w2, output_w2);
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
|
||||
// sort txs by block height
|
||||
vector<shared_ptr<monero_tx_wallet>> txs ;
|
||||
for (map<string, shared_ptr<monero_tx_wallet>>::const_iterator tx_iter = tx_map.begin(); tx_iter != tx_map.end(); tx_iter++) {
|
||||
txs.push_back(tx_iter->second);
|
||||
}
|
||||
sort(txs.begin(), txs.end(), tx_height_less_than);
|
||||
|
||||
// filter and return outputs
|
||||
// otherwise get txs with full models to fulfill query
|
||||
vector<shared_ptr<monero_output_wallet>> outputs;
|
||||
for (const shared_ptr<monero_tx_wallet>& tx : txs) {
|
||||
|
||||
// sort outputs
|
||||
sort(tx->m_outputs.begin(), tx->m_outputs.end(), vout_before);
|
||||
|
||||
// collect queried outputs, remove excluded outputs
|
||||
vector<shared_ptr<monero_output>>::iterator voutIter = tx->m_outputs.begin();
|
||||
while (voutIter != tx->m_outputs.end()) {
|
||||
shared_ptr<monero_output_wallet> vout_wallet = static_pointer_cast<monero_output_wallet>(*voutIter);
|
||||
if (_query->meets_criteria(vout_wallet.get())) {
|
||||
outputs.push_back(vout_wallet);
|
||||
voutIter++;
|
||||
} else {
|
||||
voutIter = tx->m_outputs.erase(voutIter); // remove excluded outputs
|
||||
}
|
||||
for (const shared_ptr<monero_tx_wallet>& tx : get_txs(*(query.m_tx_query.get()))) {
|
||||
for (const shared_ptr<monero_output_wallet>& output : tx->filter_outputs_wallet(query)) { // collect queried outputs, erase if excluded
|
||||
outputs.push_back(output);
|
||||
}
|
||||
|
||||
// remove txs without outputs
|
||||
if (tx->m_outputs.empty() && tx->m_block != boost::none) tx->m_block.get()->m_txs.erase(std::remove(tx->m_block.get()->m_txs.begin(), tx->m_block.get()->m_txs.end(), tx), tx->m_block.get()->m_txs.end()); // TODO, no way to use const_iterator?
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
@@ -3203,6 +3048,201 @@ namespace monero {
|
||||
#endif
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_transfer>> monero_wallet_core::get_transfers_aux(const monero_transfer_query& query) const {
|
||||
MTRACE("monero_wallet_core::get_transfers(query)");
|
||||
|
||||
// // log query
|
||||
// if (query.m_tx_query != boost::none) {
|
||||
// if ((*query.m_tx_query)->m_block == boost::none) cout << "Transfer query's tx query rooted at [tx]:" << (*query.m_tx_query)->serialize() << endl;
|
||||
// else cout << "Transfer query's tx query rooted at [block]: " << (*(*query.m_tx_query)->m_block)->serialize() << endl;
|
||||
// } else cout << "Transfer query: " << query.serialize() << endl;
|
||||
|
||||
// copy and normalize query
|
||||
shared_ptr<monero_transfer_query> _query;
|
||||
if (query.m_tx_query == boost::none) {
|
||||
shared_ptr<monero_transfer_query> query_ptr = make_shared<monero_transfer_query>(query); // convert to shared pointer for copy // TODO: does this copy unecessarily? copy constructor is not defined
|
||||
_query = query_ptr->copy(query_ptr, make_shared<monero_transfer_query>());
|
||||
_query->m_tx_query = make_shared<monero_tx_query>();
|
||||
_query->m_tx_query.get()->m_transfer_query = _query;
|
||||
} else {
|
||||
shared_ptr<monero_tx_query> tx_query = query.m_tx_query.get()->copy(query.m_tx_query.get(), make_shared<monero_tx_query>());
|
||||
_query = tx_query->m_transfer_query.get();
|
||||
}
|
||||
shared_ptr<monero_tx_query> tx_query = _query->m_tx_query.get();
|
||||
|
||||
// build parameters for m_w2->get_payments()
|
||||
uint64_t min_height = tx_query->m_min_height == boost::none ? 0 : *tx_query->m_min_height;
|
||||
uint64_t max_height = tx_query->m_max_height == boost::none ? CRYPTONOTE_MAX_BLOCK_NUMBER : min((uint64_t) CRYPTONOTE_MAX_BLOCK_NUMBER, *tx_query->m_max_height);
|
||||
if (min_height > 0) min_height--; // TODO monero core: wallet2::get_payments() m_min_height is exclusive, so manually offset to match intended range (issues 5751, #5598)
|
||||
boost::optional<uint32_t> account_index = boost::none;
|
||||
if (_query->m_account_index != boost::none) account_index = *_query->m_account_index;
|
||||
std::set<uint32_t> subaddress_indices;
|
||||
for (int i = 0; i < _query->m_subaddress_indices.size(); i++) {
|
||||
subaddress_indices.insert(_query->m_subaddress_indices[i]);
|
||||
}
|
||||
|
||||
// check if pool txs explicitly requested without daemon connection
|
||||
if (tx_query->m_in_tx_pool != boost::none && tx_query->m_in_tx_pool.get() && !is_connected()) {
|
||||
throw runtime_error("Cannot fetch pool transactions because wallet has no daemon connection");
|
||||
}
|
||||
|
||||
// translate from monero_tx_query to in, out, pending, pool, failed terminology used by monero-wallet-rpc
|
||||
bool can_be_confirmed = !bool_equals(false, tx_query->m_is_confirmed) && !bool_equals(true, tx_query->m_in_tx_pool) && !bool_equals(true, tx_query->m_is_failed) && !bool_equals(false, tx_query->m_is_relayed);
|
||||
bool can_be_in_tx_pool = is_connected() && !bool_equals(true, tx_query->m_is_confirmed) && !bool_equals(false, tx_query->m_in_tx_pool) && !bool_equals(true, tx_query->m_is_failed) && !bool_equals(false, tx_query->m_is_relayed) && tx_query->get_height() == boost::none && tx_query->m_min_height == boost::none;
|
||||
bool can_be_incoming = !bool_equals(false, _query->m_is_incoming) && !bool_equals(true, _query->is_outgoing()) && !bool_equals(true, _query->m_has_destinations);
|
||||
bool can_be_outgoing = !bool_equals(false, _query->is_outgoing()) && !bool_equals(true, _query->m_is_incoming);
|
||||
bool is_in = can_be_incoming && can_be_confirmed;
|
||||
bool is_out = can_be_outgoing && can_be_confirmed;
|
||||
bool is_pending = can_be_outgoing && can_be_in_tx_pool;
|
||||
bool is_pool = can_be_incoming && can_be_in_tx_pool;
|
||||
bool is_failed = !bool_equals(false, tx_query->m_is_failed) && !bool_equals(true, tx_query->m_is_confirmed) && !bool_equals(true, tx_query->m_in_tx_pool);
|
||||
|
||||
// cache unique txs and blocks
|
||||
uint64_t height = get_height();
|
||||
map<string, shared_ptr<monero_tx_wallet>> tx_map;
|
||||
map<uint64_t, shared_ptr<monero_block>> block_map;
|
||||
|
||||
// get confirmed incoming transfers
|
||||
if (is_in) {
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
|
||||
m_w2->get_payments(payments, min_height, max_height, account_index, subaddress_indices);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_incoming_transfer(*m_w2, height, i->first, i->second);
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
}
|
||||
|
||||
// get confirmed outgoing transfers
|
||||
if (is_out) {
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
|
||||
m_w2->get_payments_out(payments, min_height, max_height, account_index, subaddress_indices);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_outgoing_transfer(*m_w2, height, i->first, i->second);
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
}
|
||||
|
||||
// get unconfirmed or failed outgoing transfers
|
||||
if (is_pending || is_failed) {
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
|
||||
m_w2->get_unconfirmed_payments_out(upayments, account_index, subaddress_indices);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_outgoing_transfer_unconfirmed(*m_w2, i->first, i->second);
|
||||
if (tx_query->m_is_failed != boost::none && tx_query->m_is_failed.get() != tx->m_is_failed.get()) continue; // skip merging if tx excluded
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
}
|
||||
|
||||
// get unconfirmed incoming transfers
|
||||
if (is_pool) {
|
||||
|
||||
// update pool state TODO monero-core: this should be encapsulated in wallet when unconfirmed transfers queried
|
||||
std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
|
||||
m_w2->update_pool_state(process_txs);
|
||||
if (!process_txs.empty()) m_w2->process_pool_state(process_txs);
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
|
||||
m_w2->get_unconfirmed_payments(payments, account_index, subaddress_indices);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_incoming_transfer_unconfirmed(*m_w2, i->first, i->second);
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
}
|
||||
|
||||
// sort txs by block height
|
||||
vector<shared_ptr<monero_tx_wallet>> txs ;
|
||||
for (map<string, shared_ptr<monero_tx_wallet>>::const_iterator tx_iter = tx_map.begin(); tx_iter != tx_map.end(); tx_iter++) {
|
||||
txs.push_back(tx_iter->second);
|
||||
}
|
||||
sort(txs.begin(), txs.end(), tx_height_less_than);
|
||||
|
||||
// filter and return transfers
|
||||
vector<shared_ptr<monero_transfer>> transfers;
|
||||
for (const shared_ptr<monero_tx_wallet>& tx : txs) {
|
||||
|
||||
// tx is not incoming/outgoing unless already set
|
||||
if (tx->m_is_incoming == boost::none) tx->m_is_incoming = false;
|
||||
if (tx->m_is_outgoing == boost::none) tx->m_is_outgoing = false;
|
||||
|
||||
// sort incoming transfers
|
||||
sort(tx->m_incoming_transfers.begin(), tx->m_incoming_transfers.end(), incoming_transfer_before);
|
||||
|
||||
// collect queried transfers, erase if excluded
|
||||
for (const shared_ptr<monero_transfer>& transfer : tx->filter_transfers(*_query)) transfers.push_back(transfer);
|
||||
|
||||
// remove excluded txs from block
|
||||
if (tx->m_block != boost::none && tx->m_outgoing_transfer == boost::none && tx->m_incoming_transfers.empty()) {
|
||||
tx->m_block.get()->m_txs.erase(std::remove(tx->m_block.get()->m_txs.begin(), tx->m_block.get()->m_txs.end(), tx), tx->m_block.get()->m_txs.end()); // TODO, no way to use const_iterator?
|
||||
}
|
||||
}
|
||||
MTRACE("monero_wallet_core.cpp get_transfers() returning " << transfers.size() << " transfers");
|
||||
|
||||
return transfers;
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_output_wallet>> monero_wallet_core::get_outputs_aux(const monero_output_query& query) const {
|
||||
MTRACE("monero_wallet_core::get_outputs_aux(query)");
|
||||
|
||||
// // log query
|
||||
// if (query.m_tx_query != boost::none) {
|
||||
// if ((*query.m_tx_query)->m_block == boost::none) cout << "Output query's tx query rooted at [tx]:" << (*query.m_tx_query)->serialize() << endl;
|
||||
// else cout << "Output query's tx query rooted at [block]: " << (*(*query.m_tx_query)->m_block)->serialize() << endl;
|
||||
// } else cout << "Output query: " << query.serialize() << endl;
|
||||
|
||||
// copy and normalize query
|
||||
shared_ptr<monero_output_query> _query;
|
||||
if (query.m_tx_query == boost::none) {
|
||||
shared_ptr<monero_output_query> query_ptr = make_shared<monero_output_query>(query); // convert to shared pointer for copy
|
||||
_query = query_ptr->copy(query_ptr, make_shared<monero_output_query>());
|
||||
} else {
|
||||
shared_ptr<monero_tx_query> tx_query = query.m_tx_query.get()->copy(query.m_tx_query.get(), make_shared<monero_tx_query>());
|
||||
if (query.m_tx_query.get()->m_output_query != boost::none && query.m_tx_query.get()->m_output_query.get().get() == &query) {
|
||||
_query = tx_query->m_output_query.get();
|
||||
} else {
|
||||
if (query.m_tx_query.get()->m_output_query != boost::none) throw runtime_error("Output query's tx query must be a circular reference or null");
|
||||
shared_ptr<monero_output_query> query_ptr = make_shared<monero_output_query>(query); // convert query to shared pointer for copy
|
||||
_query = query_ptr->copy(query_ptr, make_shared<monero_output_query>());
|
||||
_query->m_tx_query = tx_query;
|
||||
}
|
||||
}
|
||||
if (_query->m_tx_query == boost::none) _query->m_tx_query = make_shared<monero_tx_query>();
|
||||
|
||||
// get output data from wallet2
|
||||
tools::wallet2::transfer_container outputs_w2;
|
||||
m_w2->get_transfers(outputs_w2);
|
||||
|
||||
// cache unique txs and blocks
|
||||
map<string, shared_ptr<monero_tx_wallet>> tx_map;
|
||||
map<uint64_t, shared_ptr<monero_block>> block_map;
|
||||
for (const auto& output_w2 : outputs_w2) {
|
||||
// TODO: skip tx building if m_w2 output excluded by indices, etc
|
||||
shared_ptr<monero_tx_wallet> tx = build_tx_with_vout(*m_w2, output_w2);
|
||||
merge_tx(tx, tx_map, block_map, false);
|
||||
}
|
||||
|
||||
// sort txs by block height
|
||||
vector<shared_ptr<monero_tx_wallet>> txs ;
|
||||
for (map<string, shared_ptr<monero_tx_wallet>>::const_iterator tx_iter = tx_map.begin(); tx_iter != tx_map.end(); tx_iter++) {
|
||||
txs.push_back(tx_iter->second);
|
||||
}
|
||||
sort(txs.begin(), txs.end(), tx_height_less_than);
|
||||
|
||||
// filter and return outputs
|
||||
vector<shared_ptr<monero_output_wallet>> outputs;
|
||||
for (const shared_ptr<monero_tx_wallet>& tx : txs) {
|
||||
|
||||
// sort outputs
|
||||
sort(tx->m_outputs.begin(), tx->m_outputs.end(), vout_before);
|
||||
|
||||
// collect queried outputs, erase if excluded
|
||||
for (const shared_ptr<monero_output_wallet>& output : tx->filter_outputs_wallet(*_query)) outputs.push_back(output);
|
||||
|
||||
// remove txs without outputs
|
||||
if (tx->m_outputs.empty() && tx->m_block != boost::none) tx->m_block.get()->m_txs.erase(std::remove(tx->m_block.get()->m_txs.begin(), tx->m_block.get()->m_txs.end(), tx), tx->m_block.get()->m_txs.end()); // TODO, no way to use const_iterator?
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
// private helper to initialize subaddresses using transfer details
|
||||
vector<monero_subaddress> monero_wallet_core::get_subaddresses_aux(const uint32_t account_idx, const vector<uint32_t>& subaddress_indices, const vector<tools::wallet2::transfer_details>& transfers) const {
|
||||
vector<monero_subaddress> subaddresses;
|
||||
|
||||
@@ -292,6 +292,8 @@ namespace monero {
|
||||
|
||||
void init_common();
|
||||
vector<monero_subaddress> get_subaddresses_aux(uint32_t account_idx, const vector<uint32_t>& subaddress_indices, const vector<tools::wallet2::transfer_details>& transfers) const;
|
||||
vector<shared_ptr<monero_transfer>> get_transfers_aux(const monero_transfer_query& query) const;
|
||||
vector<shared_ptr<monero_output_wallet>> get_outputs_aux(const monero_output_query& query) const;
|
||||
vector<shared_ptr<monero_tx_wallet>> sweep_account(const monero_tx_config& config); // sweeps unlocked funds within an account; private helper to sweep_unlocked()
|
||||
|
||||
// blockchain sync management
|
||||
|
||||
@@ -85,6 +85,7 @@ namespace monero {
|
||||
shared_ptr<monero_tx_query> tx_query = make_shared<monero_tx_query>();
|
||||
monero_tx_query::from_property_tree(it2->second, tx_query);
|
||||
block->m_txs.push_back(tx_query);
|
||||
tx_query->m_block = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,9 +228,9 @@ namespace monero {
|
||||
if (!src->m_incoming_transfers.empty()) {
|
||||
tgt->m_incoming_transfers = vector<shared_ptr<monero_incoming_transfer>>();
|
||||
for (const shared_ptr<monero_incoming_transfer>& transfer : src->m_incoming_transfers) {
|
||||
shared_ptr<monero_incoming_transfer> transferCopy = transfer->copy(transfer, make_shared<monero_incoming_transfer>());
|
||||
transferCopy->m_tx = tgt;
|
||||
tgt->m_incoming_transfers.push_back(transferCopy);
|
||||
shared_ptr<monero_incoming_transfer> transfer_copy = transfer->copy(transfer, make_shared<monero_incoming_transfer>());
|
||||
transfer_copy->m_tx = tgt;
|
||||
tgt->m_incoming_transfers.push_back(transfer_copy);
|
||||
}
|
||||
}
|
||||
if (src->m_outgoing_transfer != boost::none) {
|
||||
@@ -288,6 +289,67 @@ namespace monero {
|
||||
m_extra_hex = gen_utils::reconcile(m_extra_hex, other->m_extra_hex);
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_transfer>> monero_tx_wallet::get_transfers() const {
|
||||
monero_transfer_query query;
|
||||
return get_transfers(query);
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_transfer>> monero_tx_wallet::get_transfers(const monero_transfer_query& query) const {
|
||||
vector<shared_ptr<monero_transfer>> transfers;
|
||||
if (m_outgoing_transfer != boost::none && query.meets_criteria(m_outgoing_transfer.get().get())) transfers.push_back(m_outgoing_transfer.get());
|
||||
for (const shared_ptr<monero_transfer>& transfer : m_incoming_transfers) if (query.meets_criteria(transfer.get())) transfers.push_back(transfer);
|
||||
return transfers;
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_transfer>> monero_tx_wallet::filter_transfers(const monero_transfer_query& query) {
|
||||
|
||||
// collect outgoing transfer, erase if excluded
|
||||
vector<shared_ptr<monero_transfer>> transfers;
|
||||
if (m_outgoing_transfer != boost::none && query.meets_criteria(m_outgoing_transfer.get().get())) transfers.push_back(m_outgoing_transfer.get());
|
||||
else m_outgoing_transfer = boost::none;
|
||||
|
||||
// collect incoming transfers, erase if excluded
|
||||
vector<shared_ptr<monero_incoming_transfer>>::iterator iter = m_incoming_transfers.begin();
|
||||
while (iter != m_incoming_transfers.end()) {
|
||||
if (query.meets_criteria((*iter).get())) {
|
||||
transfers.push_back(*iter);
|
||||
iter++;
|
||||
} else {
|
||||
iter = m_incoming_transfers.erase(iter);
|
||||
}
|
||||
}
|
||||
return transfers;
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_output_wallet>> monero_tx_wallet::get_outputs_wallet() const {
|
||||
monero_output_query query;
|
||||
return get_outputs_wallet(query);
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_output_wallet>> monero_tx_wallet::get_outputs_wallet(const monero_output_query& query) const {
|
||||
vector<shared_ptr<monero_output_wallet>> outputs;
|
||||
for (const shared_ptr<monero_output>& output : m_outputs) {
|
||||
shared_ptr<monero_output_wallet> output_wallet = dynamic_pointer_cast<monero_output_wallet>(output);
|
||||
if (query.meets_criteria(output_wallet.get())) outputs.push_back(output_wallet);
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
vector<shared_ptr<monero_output_wallet>> monero_tx_wallet::filter_outputs_wallet(const monero_output_query& query) {
|
||||
vector<shared_ptr<monero_output_wallet>> outputs;
|
||||
vector<shared_ptr<monero_output>>::iterator iter = m_outputs.begin();
|
||||
while (iter != m_outputs.end()) {
|
||||
shared_ptr<monero_output_wallet> output_wallet = dynamic_pointer_cast<monero_output_wallet>(*iter);
|
||||
if (query.meets_criteria(output_wallet.get())) {
|
||||
outputs.push_back(output_wallet);
|
||||
iter++;
|
||||
} else {
|
||||
iter = m_outputs.erase(iter);
|
||||
}
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
// -------------------------- MONERO TX QUERY -----------------------------
|
||||
|
||||
rapidjson::Value monero_tx_query::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
|
||||
@@ -336,10 +398,12 @@ namespace monero {
|
||||
else if (key == string("transferQuery")) {
|
||||
tx_query->m_transfer_query = make_shared<monero_transfer_query>();
|
||||
monero_transfer_query::from_property_tree(it->second, tx_query->m_transfer_query.get());
|
||||
tx_query->m_transfer_query.get()->m_tx_query = tx_query;
|
||||
}
|
||||
else if (key == string("outputQuery")) {
|
||||
tx_query->m_output_query = make_shared<monero_output_query>();
|
||||
monero_output_query::from_property_tree(it->second, tx_query->m_output_query.get());
|
||||
tx_query->m_output_query.get()->m_tx_query = tx_query;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,13 +450,19 @@ namespace monero {
|
||||
tgt->m_min_height = src->m_min_height;
|
||||
tgt->m_max_height = src->m_max_height;
|
||||
tgt->m_include_outputs = src->m_include_outputs;
|
||||
if (src->m_transfer_query != boost::none) tgt->m_transfer_query = src->m_transfer_query.get()->copy(src->m_transfer_query.get(), make_shared<monero_transfer_query>());
|
||||
if (src->m_output_query != boost::none) tgt->m_output_query = src->m_output_query.get()->copy(src->m_output_query.get(), make_shared<monero_output_query>());
|
||||
if (src->m_transfer_query != boost::none) {
|
||||
tgt->m_transfer_query = src->m_transfer_query.get()->copy(src->m_transfer_query.get(), make_shared<monero_transfer_query>());
|
||||
tgt->m_transfer_query.get()->m_tx_query = tgt;
|
||||
}
|
||||
if (src->m_output_query != boost::none) {
|
||||
tgt->m_output_query = src->m_output_query.get()->copy(src->m_output_query.get(), make_shared<monero_output_query>());
|
||||
tgt->m_output_query.get()->m_tx_query = tgt;
|
||||
}
|
||||
return tgt;
|
||||
};
|
||||
|
||||
bool monero_tx_query::meets_criteria(monero_tx_wallet* tx) const {
|
||||
if (tx == nullptr) return false;
|
||||
bool monero_tx_query::meets_criteria(monero_tx_wallet* tx, bool query_children) const {
|
||||
if (tx == nullptr) throw runtime_error("nullptr given to monero_tx_query::meets_criteria()");
|
||||
|
||||
// filter on tx
|
||||
if (m_hash != boost::none && m_hash != tx->m_hash) return false;
|
||||
@@ -404,35 +474,6 @@ namespace monero {
|
||||
if (m_is_miner_tx != boost::none && m_is_miner_tx != tx->m_is_miner_tx) return false;
|
||||
if (m_is_locked != boost::none && m_is_locked != tx->m_is_locked) return false;
|
||||
|
||||
// at least one transfer must meet transfer query if defined
|
||||
if (m_transfer_query != boost::none) {
|
||||
bool matchFound = false;
|
||||
if (tx->m_outgoing_transfer != boost::none && m_transfer_query.get()->meets_criteria(tx->m_outgoing_transfer.get().get())) matchFound = true;
|
||||
else if (!tx->m_incoming_transfers.empty()) {
|
||||
for (const shared_ptr<monero_incoming_transfer>& incoming_transfer : tx->m_incoming_transfers) {
|
||||
if (m_transfer_query.get()->meets_criteria(incoming_transfer.get())) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matchFound) return false;
|
||||
}
|
||||
|
||||
// at least one output must meet output query if defined
|
||||
if (m_output_query != boost::none && !m_output_query.get()->is_default()) {
|
||||
if (tx->m_outputs.empty()) return false;
|
||||
bool matchFound = false;
|
||||
for (const shared_ptr<monero_output>& output : tx->m_outputs) {
|
||||
shared_ptr<monero_output_wallet> vout_wallet = static_pointer_cast<monero_output_wallet>(output);
|
||||
if (m_output_query.get()->meets_criteria(vout_wallet.get())) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matchFound) return false;
|
||||
}
|
||||
|
||||
// filter on having a payment id
|
||||
if (m_has_payment_id != boost::none) {
|
||||
if (*m_has_payment_id && tx->m_payment_id == boost::none) return false;
|
||||
@@ -453,6 +494,38 @@ namespace monero {
|
||||
if (m_min_height != boost::none && (txHeight == boost::none || *txHeight < *m_min_height)) return false;
|
||||
if (m_max_height != boost::none && (txHeight == boost::none || *txHeight > *m_max_height)) return false;
|
||||
|
||||
// done if not querying transfers or outputs
|
||||
if (!query_children) return true;
|
||||
|
||||
// at least one transfer must meet transfer query if defined
|
||||
if (m_transfer_query != boost::none) {
|
||||
bool match_found = false;
|
||||
if (tx->m_outgoing_transfer != boost::none && m_transfer_query.get()->meets_criteria(tx->m_outgoing_transfer.get().get())) match_found = true;
|
||||
else if (!tx->m_incoming_transfers.empty()) {
|
||||
for (const shared_ptr<monero_incoming_transfer>& incoming_transfer : tx->m_incoming_transfers) {
|
||||
if (m_transfer_query.get()->meets_criteria(incoming_transfer.get(), false)) {
|
||||
match_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!match_found) return false;
|
||||
}
|
||||
|
||||
// at least one output must meet output query if defined
|
||||
if (m_output_query != boost::none) {
|
||||
if (tx->m_outputs.empty()) return false;
|
||||
bool match_found = false;
|
||||
for (const shared_ptr<monero_output>& output : tx->m_outputs) {
|
||||
shared_ptr<monero_output_wallet> vout_wallet = static_pointer_cast<monero_output_wallet>(output);
|
||||
if (m_output_query.get()->meets_criteria(vout_wallet.get(), false)) {
|
||||
match_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match_found) return false;
|
||||
}
|
||||
|
||||
// transaction meets query criteria
|
||||
return true;
|
||||
}
|
||||
@@ -678,7 +751,7 @@ namespace monero {
|
||||
m_destinations = gen_utils::reconcile(m_destinations, other->m_destinations);
|
||||
}
|
||||
|
||||
// ----------------------- MONERO TRANSFER REQUEST --------------------------
|
||||
// ----------------------- MONERO TRANSFER QUERY --------------------------
|
||||
|
||||
rapidjson::Value monero_transfer_query::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
|
||||
|
||||
@@ -746,9 +819,8 @@ namespace monero {
|
||||
// get / create transfer query
|
||||
shared_ptr<monero_transfer_query> transfer_query = tx_query->m_transfer_query == boost::none ? make_shared<monero_transfer_query>() : *tx_query->m_transfer_query;
|
||||
|
||||
// transfer query references tx query but not the other way around to avoid circular loop // TODO: could add check within meetsCriterias()
|
||||
// set transfer query's tx query
|
||||
transfer_query->m_tx_query = tx_query;
|
||||
tx_query->m_transfer_query = boost::none;
|
||||
|
||||
// return deserialized query
|
||||
return transfer_query;
|
||||
@@ -781,13 +853,12 @@ namespace monero {
|
||||
return tgt;
|
||||
};
|
||||
|
||||
bool monero_transfer_query::meets_criteria(monero_transfer* transfer) const {
|
||||
if (transfer == nullptr) throw runtime_error("transfer is null");
|
||||
if (m_tx_query != boost::none && (*m_tx_query)->m_transfer_query != boost::none) throw runtime_error("Transfer query's tx query cannot have a circular transfer query"); // TODO: could auto detect and handle this. port to java/js
|
||||
bool monero_transfer_query::meets_criteria(monero_transfer* transfer, bool query_parent) const {
|
||||
if (transfer == nullptr) throw runtime_error("nullptr given to monero_transfer_query::meets_criteria()");
|
||||
|
||||
// filter on common fields
|
||||
if (is_incoming() != boost::none && *is_incoming() != *transfer->is_incoming()) return false;
|
||||
if (is_outgoing() != boost::none && is_outgoing() != transfer->is_outgoing()) return false;
|
||||
if (is_outgoing() != boost::none && *is_outgoing() != *transfer->is_outgoing()) return false;
|
||||
if (m_amount != boost::none && *m_amount != *transfer->m_amount) return false;
|
||||
if (m_account_index != boost::none && *m_account_index != *transfer->m_account_index) return false;
|
||||
|
||||
@@ -849,7 +920,7 @@ namespace monero {
|
||||
if (inTransfer == nullptr && out_transfer == nullptr) throw runtime_error("Transfer must be monero_incoming_transfer or monero_outgoing_transfer");
|
||||
|
||||
// filter with tx query
|
||||
if (m_tx_query != boost::none && !(*m_tx_query)->meets_criteria(transfer->m_tx.get())) return false;
|
||||
if (query_parent && m_tx_query != boost::none && !(*m_tx_query)->meets_criteria(transfer->m_tx.get(), false)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -925,7 +996,7 @@ namespace monero {
|
||||
m_is_frozen = gen_utils::reconcile(m_is_frozen, other->m_is_frozen);
|
||||
}
|
||||
|
||||
// ------------------------ MONERO OUTPUT REQUEST ---------------------------
|
||||
// ------------------------ MONERO OUTPUT QUERY ---------------------------
|
||||
|
||||
rapidjson::Value monero_output_query::to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const {
|
||||
|
||||
@@ -976,20 +1047,13 @@ namespace monero {
|
||||
// get / create output query
|
||||
shared_ptr<monero_output_query> output_query = tx_query->m_output_query == boost::none ? make_shared<monero_output_query>() : *tx_query->m_output_query;
|
||||
|
||||
// output query references tx query but not the other way around to avoid circular loop // TODO: could add check within meetsCriterias(), TODO: move this temp hack to the caller
|
||||
// set output query's tx query
|
||||
output_query->m_tx_query = tx_query;
|
||||
tx_query->m_output_query = boost::none;
|
||||
|
||||
// return deserialized query
|
||||
return output_query;
|
||||
}
|
||||
|
||||
// initialize static empty output for is_default() check
|
||||
const unique_ptr<monero_output_wallet> monero_output_query::M_EMPTY_OUTPUT = unique_ptr<monero_output_wallet>(new monero_output_wallet());
|
||||
bool monero_output_query::is_default() const {
|
||||
return meets_criteria(monero_output_query::M_EMPTY_OUTPUT.get());
|
||||
}
|
||||
|
||||
shared_ptr<monero_output_query> monero_output_query::copy(const shared_ptr<monero_output>& src, const shared_ptr<monero_output>& tgt) const {
|
||||
return monero_output_query::copy(static_pointer_cast<monero_output_query>(src), static_pointer_cast<monero_output_query>(tgt));
|
||||
};
|
||||
@@ -1009,10 +1073,12 @@ namespace monero {
|
||||
if (!src->m_subaddress_indices.empty()) tgt->m_subaddress_indices = vector<uint32_t>(src->m_subaddress_indices);
|
||||
tgt->m_min_amount = src->m_min_amount;
|
||||
tgt->m_max_amount = src->m_max_amount;
|
||||
tgt->m_tx_query = src->m_tx_query;
|
||||
return tgt;
|
||||
};
|
||||
|
||||
bool monero_output_query::meets_criteria(monero_output_wallet* output) const {
|
||||
bool monero_output_query::meets_criteria(monero_output_wallet* output, bool query_parent) const {
|
||||
if (output == nullptr) throw runtime_error("nullptr given to monero_output_query::meets_criteria()");
|
||||
|
||||
// filter on output
|
||||
if (m_account_index != boost::none && (output->m_account_index == boost::none || *m_account_index != *output->m_account_index)) return false;
|
||||
@@ -1033,7 +1099,7 @@ namespace monero {
|
||||
if (m_max_amount != boost::none && (output->m_amount == boost::none || output->m_amount.get() > m_max_amount.get())) return false;
|
||||
|
||||
// filter with tx query
|
||||
if (m_tx_query != boost::none && !(*m_tx_query)->meets_criteria(static_pointer_cast<monero_tx_wallet>(output->m_tx).get())) return false;
|
||||
if (query_parent && m_tx_query != boost::none && !(*m_tx_query)->meets_criteria(static_pointer_cast<monero_tx_wallet>(output->m_tx).get(), false)) return false;
|
||||
|
||||
// output meets query
|
||||
return true;
|
||||
@@ -1128,7 +1194,7 @@ namespace monero {
|
||||
else if (priority_num == 1) config->m_priority = monero_tx_priority::UNIMPORTANT;
|
||||
else if (priority_num == 2) config->m_priority = monero_tx_priority::NORMAL;
|
||||
else if (priority_num == 3) config->m_priority = monero_tx_priority::ELEVATED;
|
||||
else throw new runtime_error("Invalid priority number: " + std::to_string(priority_num));
|
||||
else throw runtime_error("Invalid priority number: " + std::to_string(priority_num));
|
||||
}
|
||||
else if (key == string("ringSize")) config->m_ring_size = it->second.get_value<uint32_t>();
|
||||
else if (key == string("fee")) config->m_fee = it->second.get_value<uint64_t>();
|
||||
|
||||
@@ -125,6 +125,8 @@ namespace monero {
|
||||
|
||||
/**
|
||||
* Models a base transfer of funds to or from the wallet.
|
||||
*
|
||||
* TODO: m_is_incoming for api consistency
|
||||
*/
|
||||
struct monero_transfer : serializable_struct {
|
||||
shared_ptr<monero_tx_wallet> m_tx;
|
||||
@@ -195,7 +197,7 @@ namespace monero {
|
||||
shared_ptr<monero_transfer_query> copy(const shared_ptr<monero_transfer>& src, const shared_ptr<monero_transfer>& tgt) const;
|
||||
shared_ptr<monero_transfer_query> copy(const shared_ptr<monero_transfer_query>& src, const shared_ptr<monero_transfer_query>& tgt) const;
|
||||
boost::optional<bool> is_incoming() const;
|
||||
bool meets_criteria(monero_transfer* transfer) const;
|
||||
bool meets_criteria(monero_transfer* transfer, bool query_parent = true) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -231,14 +233,10 @@ namespace monero {
|
||||
rapidjson::Value to_rapidjson_val(rapidjson::Document::AllocatorType& allocator) const;
|
||||
static void from_property_tree(const boost::property_tree::ptree& node, const shared_ptr<monero_output_query>& output_query);
|
||||
static shared_ptr<monero_output_query> deserialize_from_block(const string& output_query_json);
|
||||
bool is_default() const;
|
||||
shared_ptr<monero_output_query> copy(const shared_ptr<monero_output>& src, const shared_ptr<monero_output>& tgt) const;
|
||||
shared_ptr<monero_output_query> copy(const shared_ptr<monero_output_wallet>& src, const shared_ptr<monero_output_wallet>& tgt) const; // TODO: necessary to override all super classes?
|
||||
shared_ptr<monero_output_query> copy(const shared_ptr<monero_output_query>& src, const shared_ptr<monero_output_query>& tgt) const;
|
||||
bool meets_criteria(monero_output_wallet* output) const;
|
||||
|
||||
private:
|
||||
static const unique_ptr<monero_output_wallet> M_EMPTY_OUTPUT;
|
||||
bool meets_criteria(monero_output_wallet* output, bool query_parent = true) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -265,6 +263,12 @@ namespace monero {
|
||||
shared_ptr<monero_tx_wallet> copy(const shared_ptr<monero_tx_wallet>& src, const shared_ptr<monero_tx_wallet>& tgt) const;
|
||||
void merge(const shared_ptr<monero_tx>& self, const shared_ptr<monero_tx>& other);
|
||||
void merge(const shared_ptr<monero_tx_wallet>& self, const shared_ptr<monero_tx_wallet>& other);
|
||||
vector<shared_ptr<monero_transfer>> get_transfers() const;
|
||||
vector<shared_ptr<monero_transfer>> get_transfers(const monero_transfer_query& query) const;
|
||||
vector<shared_ptr<monero_transfer>> filter_transfers(const monero_transfer_query& query);
|
||||
vector<shared_ptr<monero_output_wallet>> get_outputs_wallet() const;
|
||||
vector<shared_ptr<monero_output_wallet>> get_outputs_wallet(const monero_output_query& query) const;
|
||||
vector<shared_ptr<monero_output_wallet>> filter_outputs_wallet(const monero_output_query& query);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -291,7 +295,7 @@ namespace monero {
|
||||
shared_ptr<monero_tx_query> copy(const shared_ptr<monero_tx>& src, const shared_ptr<monero_tx>& tgt) const;
|
||||
shared_ptr<monero_tx_query> copy(const shared_ptr<monero_tx_wallet>& src, const shared_ptr<monero_tx_wallet>& tgt) const; // TODO: necessary to override all super classes?
|
||||
shared_ptr<monero_tx_query> copy(const shared_ptr<monero_tx_query>& src, const shared_ptr<monero_tx_query>& tgt) const;
|
||||
bool meets_criteria(monero_tx_wallet* tx) const;
|
||||
bool meets_criteria(monero_tx_wallet* tx, bool query_children = true) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,9 +55,10 @@ int main(int argc, const char* argv[]) {
|
||||
monero_tx_query tx_query;
|
||||
tx_query.m_hash = "314a0f1375db31cea4dac4e0a51514a6282b43792269b3660166d4d2b46437ca";
|
||||
shared_ptr<monero_tx_wallet> tx = wallet_restored->get_txs(tx_query)[0];
|
||||
for (const shared_ptr<monero_incoming_transfer> in_transfer : tx->m_incoming_transfers) {
|
||||
uint64_t in_amount = in_transfer->m_amount.get();
|
||||
int account_index = in_transfer->m_account_index.get();
|
||||
for (const shared_ptr<monero_transfer> transfer : tx->get_transfers()) {
|
||||
bool is_incoming = transfer->is_incoming().get();
|
||||
uint64_t in_amount = transfer->m_amount.get();
|
||||
int account_index = transfer->m_account_index.get();
|
||||
}
|
||||
|
||||
// query incoming transfers to account 1
|
||||
|
||||
Reference in New Issue
Block a user