/** * Copyright (c) 2017-2019 woodser * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Parts of this file are originally copyright (c) 2014-2019, The Monero Project * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * All rights reserved. * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers */ #include "monero_wallet.h" #include "utils/monero_utils.h" #include #include using namespace std; /** * Public library interface. */ namespace monero { // ----------------------- UNDECLARED PRIVATE HELPERS ----------------------- void merge_incoming_transfer(vector>& transfers, const shared_ptr& transfer) { for (const shared_ptr& aTransfer : transfers) { if (aTransfer->account_index.get() == transfer->account_index.get() && aTransfer->subaddress_index.get() == transfer->subaddress_index.get()) { aTransfer->merge(aTransfer, transfer); return; } } transfers.push_back(transfer); } // ---------------------------- MONERO ACCOUNT ------------------------------ boost::property_tree::ptree monero_account::to_property_tree() const { boost::property_tree::ptree node; if (index != boost::none) node.put("index", *index); if (primary_address != boost::none) node.put("primaryAddress", *primary_address); if (balance != boost::none) node.put("balance", *balance); if (unlocked_balance != boost::none) node.put("unlockedBalance", *unlocked_balance); if (!subaddresses.empty()) node.add_child("subaddresses", monero_utils::to_property_tree(subaddresses)); return node; } // -------------------------- MONERO SUBADDRESS ----------------------------- boost::property_tree::ptree monero_subaddress::to_property_tree() const { boost::property_tree::ptree node; if (account_index != boost::none) node.put("accountIndex", *account_index); if (index != boost::none) node.put("index", *index); if (address != boost::none) node.put("address", *address); if (label != boost::none) node.put("label", *label); if (balance != boost::none) node.put("balance", *balance); if (unlocked_balance != boost::none) node.put("unlockedBalance", *unlocked_balance); if (num_unspent_outputs != boost::none) node.put("numUnspentOutputs", *num_unspent_outputs); if (is_used != boost::none) node.put("isUsed", *is_used); if (num_blocks_to_unlock != boost::none) node.put("numBlocksToUnlock", *num_blocks_to_unlock); return node; } // --------------------------- MONERO TX WALLET ----------------------------- shared_ptr monero_tx_wallet::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_tx_wallet::copy(const shared_ptr& src, const shared_ptr& tgt)"); return monero_tx_wallet::copy(static_pointer_cast(src), static_pointer_cast(tgt)); }; shared_ptr monero_tx_wallet::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_tx_wallet::copy(const shared_ptr& src, const shared_ptr& tgt)"); if (this != src.get()) throw runtime_error("this != src"); // copy base class monero_tx::copy(static_pointer_cast(src), static_pointer_cast(tgt)); // copy wallet extensions if (!src->incoming_transfers.empty()) { tgt->incoming_transfers = vector>(); for (const shared_ptr& transfer : src->incoming_transfers) { shared_ptr transferCopy = transfer->copy(transfer, make_shared()); transferCopy->tx = tgt; tgt->incoming_transfers.push_back(transferCopy); } } if (src->outgoing_transfer != boost::none) { shared_ptr transferCopy = src->outgoing_transfer.get()->copy(src->outgoing_transfer.get(), make_shared()); transferCopy->tx = tgt; tgt->outgoing_transfer = transferCopy; } tgt->note = src->note; return tgt; }; boost::property_tree::ptree monero_tx_wallet::to_property_tree() const { boost::property_tree::ptree node = monero_tx::to_property_tree(); if (!incoming_transfers.empty()) node.add_child("incomingTransfers", monero_utils::to_property_tree(incoming_transfers)); if (outgoing_transfer != boost::none) node.add_child("outgoingTransfer", (*outgoing_transfer)->to_property_tree()); if (note != boost::none) node.put("note", *note); return node; } bool monero_tx_wallet::getIsOutgoing() const { return outgoing_transfer != boost::none; } bool monero_tx_wallet::getIsIncoming() const { return !incoming_transfers.empty(); } void monero_tx_wallet::merge(const shared_ptr& self, const shared_ptr& other) { merge(static_pointer_cast(self), static_pointer_cast(other)); } void monero_tx_wallet::merge(const shared_ptr& self, const shared_ptr& other) { if (this != self.get()) throw runtime_error("this != self"); if (self == other) return; // merge base classes monero_tx::merge(self, other); // merge wallet extensions note = monero_utils::reconcile(note, other->note); // merge incoming transfers if (!other->incoming_transfers.empty()) { for (const shared_ptr& transfer : other->incoming_transfers) { // NOTE: not using reference so shared_ptr is not deleted when tx is dereferenced transfer->tx = self; merge_incoming_transfer(self->incoming_transfers, transfer); } } // merge outgoing transfer if (other->outgoing_transfer != boost::none) { other->outgoing_transfer.get()->tx = self; if (self->outgoing_transfer == boost::none) self->outgoing_transfer = other->outgoing_transfer; else self->outgoing_transfer.get()->merge(self->outgoing_transfer.get(), other->outgoing_transfer.get()); } } // -------------------------- MONERO TX REQUEST ----------------------------- shared_ptr monero_tx_request::copy(const shared_ptr& src, const shared_ptr& tgt) const { return copy(static_pointer_cast(src), static_pointer_cast(tgt)); }; shared_ptr monero_tx_request::copy(const shared_ptr& src, const shared_ptr& tgt) const { return copy(static_pointer_cast(src), static_pointer_cast(tgt)); }; shared_ptr monero_tx_request::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_tx_request::copy(const shared_ptr& src, const shared_ptr& tgt)"); if (this != src.get()) throw runtime_error("this != src"); // copy base class monero_tx_wallet::copy(static_pointer_cast(src), static_pointer_cast(tgt)); // copy request extensions tgt->is_outgoing = src->is_outgoing; tgt->is_incoming = src->is_incoming; if (!src->tx_ids.empty()) tgt->tx_ids = vector(src->tx_ids); tgt-> has_payment_id = src->has_payment_id; if (!src->payment_ids.empty()) tgt->payment_ids = vector(src->payment_ids); tgt->height = src->height; tgt->min_height = src->min_height; tgt->max_height = src->max_height; tgt->include_outputs = src->include_outputs; if (src->transfer_request != boost::none) tgt->transfer_request = src->transfer_request.get()->copy(src->transfer_request.get(), make_shared()); if (src->output_request != boost::none) tgt->output_request = src->output_request.get()->copy(src->output_request.get(), make_shared()); return tgt; }; boost::property_tree::ptree monero_tx_request::to_property_tree() const { boost::property_tree::ptree node = monero_tx_wallet::to_property_tree(); if (is_outgoing != boost::none) node.put("isOutgoing", *is_outgoing); if (is_incoming != boost::none) node.put("isIncoming", *is_incoming); if (!tx_ids.empty()) node.add_child("txIds", monero_utils::to_property_tree(tx_ids)); if (has_payment_id != boost::none) node.put("hasPaymentId", *has_payment_id); if (!payment_ids.empty()) node.add_child("paymentIds", monero_utils::to_property_tree(payment_ids)); if (height != boost::none) node.put("height", *height); if (min_height != boost::none) node.put("minHeight", *min_height); if (max_height != boost::none) node.put("maxHeight", *max_height); if (include_outputs != boost::none) node.put("includeOutputs", *include_outputs); if (transfer_request != boost::none) node.add_child("transferRequest", (*transfer_request)->to_property_tree()); return node; } bool monero_tx_request::meets_criteria(monero_tx_wallet* tx) const { if (tx == nullptr) return false; // filter on tx if (id != boost::none && id != tx->id) return false; if (payment_id != boost::none && payment_id != tx->payment_id) return false; if (is_confirmed != boost::none && is_confirmed != tx->is_confirmed) return false; if (in_tx_pool != boost::none && in_tx_pool != tx->in_tx_pool) return false; if (do_not_relay != boost::none && do_not_relay != tx->do_not_relay) return false; if (is_failed != boost::none && is_failed != tx->is_failed) return false; if (is_miner_tx != boost::none && is_miner_tx != tx->is_miner_tx) return false; // at least one transfer must meet transfer request if defined if (transfer_request != boost::none) { bool matchFound = false; if (tx->outgoing_transfer != boost::none && (*transfer_request)->meets_criteria((*tx->outgoing_transfer).get())) matchFound = true; else if (!tx->incoming_transfers.empty()) { for (const shared_ptr& incomingTransfer : tx->incoming_transfers) { if ((*transfer_request)->meets_criteria(incomingTransfer.get())) { matchFound = true; break; } } } if (!matchFound) return false; } // filter on having a payment id if (has_payment_id != boost::none) { if (*has_payment_id && tx->payment_id == boost::none) return false; if (!*has_payment_id && tx->payment_id != boost::none) return false; } // filter on incoming if (is_incoming != boost::none && is_incoming != tx->getIsIncoming()) return false; // filter on outgoing if (is_outgoing != boost::none && is_outgoing != tx->getIsOutgoing()) return false; // filter on remaining fields boost::optional txHeight = tx->get_height(); if (!tx_ids.empty() && find(tx_ids.begin(), tx_ids.end(), *tx->id) == tx_ids.end()) return false; if (!payment_ids.empty() && (tx->payment_id == boost::none || find(payment_ids.begin(), payment_ids.end(), *tx->payment_id) == payment_ids.end())) return false; if (height != boost::none && (txHeight == boost::none || *txHeight != *height)) return false; if (min_height != boost::none && (txHeight == boost::none || *txHeight < *min_height)) return false; if (max_height != boost::none && (txHeight == boost::none || *txHeight > *max_height)) return false; // transaction meets request criteria return true; } // -------------------------- MONERO DESTINATION ---------------------------- shared_ptr monero_destination::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_destination::copy(const shared_ptr& src, const shared_ptr& tgt)"); if (this != src.get()) throw runtime_error("this != src"); tgt->address = src->address; tgt->amount = src->amount; return tgt; }; boost::property_tree::ptree monero_destination::to_property_tree() const { boost::property_tree::ptree node; if (address != boost::none) node.put("address", *address); if (amount != boost::none) node.put("amount", *amount); return node; } // ---------------------------- MONERO TRANSFER ----------------------------- shared_ptr monero_transfer::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_transfer::copy(const shared_ptr& src, const shared_ptr& tgt)"); tgt->tx = src->tx; // reference parent tx by default tgt->amount = src->amount; tgt->account_index = src->account_index; tgt->num_suggested_confirmations = src->num_suggested_confirmations; return tgt; } boost::property_tree::ptree monero_transfer::to_property_tree() const { boost::property_tree::ptree node; if (amount != boost::none) node.put("amount", *amount); if (account_index != boost::none) node.put("accountIndex", *account_index); if (num_suggested_confirmations != boost::none) node.put("numSuggestedConfirmations", *num_suggested_confirmations); return node; } void monero_transfer::merge(const shared_ptr& self, const shared_ptr& other) { if (this != self.get()) throw runtime_error("this != self"); if (self == other) return; // merge txs if they're different which comes back to merging transfers if (tx != other->tx) { tx->merge(tx, other->tx); return; } // otherwise merge transfer fields account_index = monero_utils::reconcile(account_index, other->account_index, "acountIndex"); // TODO monero core: failed tx in pool (after testUpdateLockedDifferentAccounts()) causes non-originating saved wallets to return duplicate incoming transfers but one has amount/num_suggested_confirmations of 0 if (amount != boost::none && other->amount != boost::none && *amount != *other->amount && (*amount == 0 || *other->amount == 0)) { account_index = monero_utils::reconcile(account_index, other->account_index, boost::none, boost::none, true, "acountIndex"); num_suggested_confirmations = monero_utils::reconcile(num_suggested_confirmations, other->num_suggested_confirmations, boost::none, boost::none, true, "num_suggested_confirmations"); MWARNING("WARNING: failed tx in pool causes non-originating wallets to return duplicate incoming transfers but with one amount/num_suggested_confirmations of 0"); } else { amount = monero_utils::reconcile(amount, other->amount, "transfer amount"); num_suggested_confirmations = monero_utils::reconcile(num_suggested_confirmations, other->num_suggested_confirmations, boost::none, boost::none, false, "num_suggested_confirmations"); } } // ----------------------- MONERO INCOMING TRANSFER ------------------------- shared_ptr monero_incoming_transfer::copy(const shared_ptr& src, const shared_ptr& tgt) const { return copy(static_pointer_cast(src), static_pointer_cast(tgt)); } shared_ptr monero_incoming_transfer::copy(const shared_ptr& src, const shared_ptr& tgt) const { throw runtime_error("monero_incoming_transfer::copy(inTransfer) not implemented"); }; boost::optional monero_incoming_transfer::getIsIncoming() const { return true; } boost::property_tree::ptree monero_incoming_transfer::to_property_tree() const { boost::property_tree::ptree node = monero_transfer::to_property_tree(); if (subaddress_index != boost::none) node.put("subaddressIndex", *subaddress_index); if (address != boost::none) node.put("address", *address); return node; } void monero_incoming_transfer::merge(const shared_ptr& self, const shared_ptr& other) { merge(static_pointer_cast(self), static_pointer_cast(other)); } void monero_incoming_transfer::merge(const shared_ptr& self, const shared_ptr& other) { if (self == other) return; monero_transfer::merge(self, other); subaddress_index = monero_utils::reconcile(subaddress_index, other->subaddress_index, "incoming transfer subaddress_index"); address = monero_utils::reconcile(address, other->address); } // ----------------------- MONERO OUTGOING TRANSFER ------------------------- shared_ptr monero_outgoing_transfer::copy(const shared_ptr& src, const shared_ptr& tgt) const { return copy(static_pointer_cast(src), static_pointer_cast(tgt)); }; shared_ptr monero_outgoing_transfer::copy(const shared_ptr& src, const shared_ptr& tgt) const { throw runtime_error("monero_outgoing_transfer::copy(out_transfer) not implemented"); }; boost::optional monero_outgoing_transfer::getIsIncoming() const { return false; } boost::property_tree::ptree monero_outgoing_transfer::to_property_tree() const { boost::property_tree::ptree node = monero_transfer::to_property_tree(); if (!subaddress_indices.empty()) node.add_child("subaddressIndices", monero_utils::to_property_tree(subaddress_indices)); if (!addresses.empty()) node.add_child("addresses", monero_utils::to_property_tree(addresses)); if (!destinations.empty()) node.add_child("destinations", monero_utils::to_property_tree(destinations)); return node; } void monero_outgoing_transfer::merge(const shared_ptr& self, const shared_ptr& other) { merge(static_pointer_cast(self), static_pointer_cast(other)); } void monero_outgoing_transfer::merge(const shared_ptr& self, const shared_ptr& other) { if (self == other) return; monero_transfer::merge(self, other); subaddress_indices = monero_utils::reconcile(subaddress_indices, other->subaddress_indices); addresses = monero_utils::reconcile(addresses, other->addresses); destinations = monero_utils::reconcile(destinations, other->destinations); } // ----------------------- MONERO TRANSFER REQUEST -------------------------- shared_ptr monero_transfer_request::copy(const shared_ptr& src, const shared_ptr& tgt) const { return copy(static_pointer_cast(src), static_pointer_cast(tgt)); }; shared_ptr monero_transfer_request::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_transfer_request::copy(const shared_ptr& src, const shared_ptr& tgt)"); if (this != src.get()) throw runtime_error("this != src"); // copy base class monero_transfer::copy(static_pointer_cast(src), static_pointer_cast(tgt)); // copy extensions tgt->is_incoming = src->is_incoming; tgt->address = src->address; if (!src->addresses.empty()) tgt->addresses = vector(src->addresses); tgt->subaddress_index = src->subaddress_index; if (!src->subaddress_indices.empty()) tgt->subaddress_indices = vector(src->subaddress_indices); if (!src->destinations.empty()) { for (const shared_ptr& destination : src->destinations) { tgt->destinations.push_back(destination->copy(destination, make_shared())); } } tgt->has_destinations = src->has_destinations; tgt->tx_request = src->tx_request; return tgt; }; boost::optional monero_transfer_request::getIsIncoming() const { return is_incoming; } boost::property_tree::ptree monero_transfer_request::to_property_tree() const { boost::property_tree::ptree node = monero_transfer::to_property_tree(); if (getIsIncoming() != boost::none) node.put("isIncoming", *getIsIncoming()); if (address != boost::none) node.put("address", *address); if (subaddress_index != boost::none) node.put("subaddressIndex", *subaddress_index); if (has_destinations != boost::none) node.put("hasDestinations", *has_destinations); if (!subaddress_indices.empty()) node.add_child("subaddressIndices", monero_utils::to_property_tree(subaddress_indices)); if (!addresses.empty()) node.add_child("addresses", monero_utils::to_property_tree(addresses)); if (!destinations.empty()) node.add_child("destinations", monero_utils::to_property_tree(destinations)); return node; } bool monero_transfer_request::meets_criteria(monero_transfer* transfer) const { if (transfer == nullptr) throw runtime_error("transfer is null"); if (tx_request != boost::none && (*tx_request)->transfer_request != boost::none) throw runtime_error("Transfer request's tx request cannot have a circular transfer request"); // TODO: could auto detect and handle this. port to java/js // filter on common fields if (getIsIncoming() != boost::none && *getIsIncoming() != *transfer->getIsIncoming()) return false; if (getIsOutgoing() != boost::none && getIsOutgoing() != transfer->getIsOutgoing()) return false; if (amount != boost::none && *amount != *transfer->amount) return false; if (account_index != boost::none && *account_index != *transfer->account_index) return false; // filter on incoming fields monero_incoming_transfer* inTransfer = dynamic_cast(transfer); if (inTransfer != nullptr) { if (has_destinations != boost::none) return false; if (address != boost::none && *address != *inTransfer->address) return false; if (!addresses.empty() && find(addresses.begin(), addresses.end(), *inTransfer->address) == addresses.end()) return false; if (subaddress_index != boost::none && *subaddress_index != *inTransfer->subaddress_index) return false; if (!subaddress_indices.empty() && find(subaddress_indices.begin(), subaddress_indices.end(), *inTransfer->subaddress_index) == subaddress_indices.end()) return false; } // filter on outgoing fields monero_outgoing_transfer* out_transfer = dynamic_cast(transfer); if (out_transfer != nullptr) { // filter on addresses if (address != boost::none && (out_transfer->addresses.empty() || find(out_transfer->addresses.begin(), out_transfer->addresses.end(), *address) == out_transfer->addresses.end())) return false; // TODO: will filter all transfers if they don't contain addresses if (!addresses.empty()) { bool intersects = false; for (const string& addressReq : addresses) { for (const string& address : out_transfer->addresses) { if (addressReq == address) { intersects = true; break; } } } if (!intersects) return false; // must have overlapping addresses } // filter on subaddress indices if (subaddress_index != boost::none && (out_transfer->subaddress_indices.empty() || find(out_transfer->subaddress_indices.begin(), out_transfer->subaddress_indices.end(), *subaddress_index) == out_transfer->subaddress_indices.end())) return false; // TODO: will filter all transfers if they don't contain subaddress indices if (!subaddress_indices.empty()) { bool intersects = false; for (const uint32_t& subaddressIndexReq : subaddress_indices) { for (const uint32_t& subaddress_index : out_transfer->subaddress_indices) { if (subaddressIndexReq == subaddress_index) { intersects = true; break; } } } if (!intersects) return false; // must have overlapping subaddress indices } // filter on having destinations if (has_destinations != boost::none) { if (*has_destinations && out_transfer->destinations.empty()) return false; if (!*has_destinations && !out_transfer->destinations.empty()) return false; } // filter on destinations TODO: start with test for this // if (this.getDestionations() != null && this.getDestionations() != transfer.getDestionations()) return false; } // validate type if (inTransfer == nullptr && out_transfer == nullptr) throw runtime_error("Transfer must be monero_incoming_transfer or monero_outgoing_transfer"); // filter with tx request if (tx_request != boost::none && !(*tx_request)->meets_criteria(transfer->tx.get())) return false; return true; } // ------------------------- MONERO OUTPUT WALLET --------------------------- shared_ptr monero_output_wallet::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_output_wallet::copy(output)"); return monero_output_wallet::copy(static_pointer_cast(src), static_pointer_cast(tgt)); }; shared_ptr monero_output_wallet::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_output_wallet::copy(output_wallet)"); if (this != src.get()) throw runtime_error("this != src"); // copy base class monero_output::copy(static_pointer_cast(src), static_pointer_cast(tgt)); // copy extensions tgt->account_index = src->account_index; tgt->subaddress_index = src->subaddress_index; tgt->is_spent = src->is_spent; tgt->is_unlocked = src->is_unlocked; tgt->is_frozen = src->is_frozen; return tgt; }; boost::property_tree::ptree monero_output_wallet::to_property_tree() const { boost::property_tree::ptree node = monero_output::to_property_tree(); if (account_index != boost::none) node.put("accountIndex", *account_index); if (subaddress_index != boost::none) node.put("subaddressIndex", *subaddress_index); if (is_spent != boost::none) node.put("isSpent", *is_spent); if (is_unlocked != boost::none) node.put("isUnlocked", *is_unlocked); if (is_frozen != boost::none) node.put("isFrozen", *is_frozen); return node; } void monero_output_wallet::merge(const shared_ptr& self, const shared_ptr& other) { merge(static_pointer_cast(self), static_pointer_cast(other)); } void monero_output_wallet::merge(const shared_ptr& self, const shared_ptr& other) { throw runtime_error("monero_output_wallet::merge(self, other) not implemented"); } // ------------------------ MONERO OUTPUT REQUEST --------------------------- shared_ptr monero_output_request::copy(const shared_ptr& src, const shared_ptr& tgt) const { return monero_output_request::copy(static_pointer_cast(src), static_pointer_cast(tgt)); }; shared_ptr monero_output_request::copy(const shared_ptr& src, const shared_ptr& tgt) const { return monero_output_request::copy(static_pointer_cast(src), static_pointer_cast(tgt)); }; shared_ptr monero_output_request::copy(const shared_ptr& src, const shared_ptr& tgt) const { MTRACE("monero_output_request::copy(output_request)"); if (this != src.get()) throw runtime_error("this != src"); // copy base class monero_output_wallet::copy(static_pointer_cast(src), static_pointer_cast(tgt)); // copy extensions if (!src->subaddress_indices.empty()) tgt->subaddress_indices = vector(src->subaddress_indices); return tgt; }; boost::property_tree::ptree monero_output_request::to_property_tree() const { boost::property_tree::ptree node = monero_output_wallet::to_property_tree(); if (!subaddress_indices.empty()) node.add_child("subaddressIndices", monero_utils::to_property_tree(subaddress_indices)); return node; } bool monero_output_request::meets_criteria(monero_output_wallet* output) const { // filter on output if (account_index != boost::none && *account_index != *output->account_index) return false; if (subaddress_index != boost::none && *subaddress_index != *output->subaddress_index) return false; if (amount != boost::none && *amount != *output->amount) return false; if (is_spent != boost::none && *is_spent != *output->is_spent) return false; if (is_unlocked != boost::none && *is_unlocked != *output->is_unlocked) return false; // filter on output key image if (key_image != boost::none) { if (output->key_image == boost::none) return false; if ((*key_image)->hex != boost::none && ((*output->key_image)->hex == boost::none || *(*key_image)->hex != *(*output->key_image)->hex)) return false; if ((*key_image)->signature != boost::none && ((*output->key_image)->signature == boost::none || *(*key_image)->signature != *(*output->key_image)->signature)) return false; } // filter on extensions if (!subaddress_indices.empty() && find(subaddress_indices.begin(), subaddress_indices.end(), *output->subaddress_index) == subaddress_indices.end()) return false; // filter with tx request if (tx_request != boost::none && !(*tx_request)->meets_criteria(static_pointer_cast(output->tx).get())) return false; // output meets request return true; } // ------------------------- MONERO SEND REQUEST ---------------------------- boost::property_tree::ptree monero_send_request::to_property_tree() const { boost::property_tree::ptree node; if (!destinations.empty()) node.add_child("destinations", monero_utils::to_property_tree(destinations)); if (payment_id != boost::none) node.put("paymentId", *payment_id); if (priority != boost::none) node.put("priority", *priority); if (mixin != boost::none) node.put("mixin", *mixin); if (ring_size != boost::none) node.put("ringSize", *ring_size); if (account_index != boost::none) node.put("accountIndex", *account_index); if (!subaddress_indices.empty()) node.add_child("subaddressIndices", monero_utils::to_property_tree(subaddress_indices)); if (unlock_time != boost::none) node.put("unlockTime", *unlock_time); if (can_split != boost::none) node.put("canSplit", *can_split); if (do_not_relay != boost::none) node.put("doNotRelay", *do_not_relay); if (note != boost::none) node.put("note", *note); if (recipient_name != boost::none) node.put("recipientName", *recipient_name); if (below_amount != boost::none) node.put("belowAmount", *below_amount); if (sweep_each_subaddress != boost::none) node.put("sweepEachSubaddress", *sweep_each_subaddress); if (key_image != boost::none) node.put("keyImage", *key_image); return node; } // ---------------------- MONERO INTEGRATED ADDRESS ------------------------- boost::property_tree::ptree monero_integrated_address::to_property_tree() const { boost::property_tree::ptree node; node.put("standardAddress", standard_address); node.put("paymentId", payment_id); node.put("integratedAddress", integrated_address); return node; } // -------------------- MONERO KEY IMAGE IMPORT RESULT ---------------------- boost::property_tree::ptree monero_key_image_import_result::to_property_tree() const { boost::property_tree::ptree node; if (height != boost::none) node.put("height", *height); if (spent_amount != boost::none) node.put("spentAmount", *spent_amount); if (unspent_amount != boost::none) node.put("unspentAmount", *unspent_amount); return node; } // ----------------------------- MONERO CHECK ------------------------------- boost::property_tree::ptree monero_check::to_property_tree() const { boost::property_tree::ptree node; node.put("is_good", is_good); return node; } // --------------------------- MONERO CHECK TX ------------------------------ boost::property_tree::ptree monero_check_tx::to_property_tree() const { boost::property_tree::ptree node = monero_check::to_property_tree();; if (in_tx_pool != boost::none) node.put("inTxPool", *in_tx_pool); if (num_confirmations != boost::none) node.put("numConfirmations", *num_confirmations); if (received_amount != boost::none) node.put("receivedAmount", *received_amount); return node; } // ------------------------ MONERO CHECK RESERVE ---------------------------- boost::property_tree::ptree monero_check_reserve::to_property_tree() const { boost::property_tree::ptree node = monero_check::to_property_tree(); if (total_amount != boost::none) node.put("totalAmount", *total_amount); if (unconfirmed_spent_amount != boost::none) node.put("unconfirmedSpentAmount", *unconfirmed_spent_amount); return node; } }