fix query issue where full tx needed

refactor getTransfersAux(), getOutputsAux()
query cycles supported
This commit is contained in:
woodser
2020-05-16 20:51:34 -04:00
parent f7daa09c5d
commit f7e46006e5
5 changed files with 396 additions and 283 deletions
+257 -217
View File
@@ -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;
+2
View File
@@ -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
+122 -56
View File
@@ -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>();
+11 -7
View File
@@ -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;
};
/**
+4 -3
View File
@@ -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