diff --git a/README.md b/README.md index b333f8b..e1b34bf 100644 --- a/README.md +++ b/README.md @@ -54,23 +54,27 @@ uint64_t unlocked_account_balance = account.m_unlocked_balance.get(); // get boo // query a transaction by hash monero_tx_query tx_query; tx_query.m_hash = "314a0f1375db31cea4dac4e0a51514a6282b43792269b3660166d4d2b46437ca"; -shared_ptr tx = wallet_restored->get_txs(tx_query)[0]; +vector> txs = wallet_restored->get_txs(tx_query); +shared_ptr tx = txs[0]; for (const shared_ptr 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(); } +monero_utils::free(txs); // query incoming transfers to account 1 monero_transfer_query transfer_query; transfer_query.m_is_incoming = true; transfer_query.m_account_index = 1; vector> transfers = wallet_restored->get_transfers(transfer_query); +monero_utils::free(transfers); // query unspent outputs monero_output_query output_query; output_query.m_is_spent = false; vector> outputs = wallet_restored->get_outputs(output_query); +monero_utils::free(outputs); // create and sync a new wallet with a random seed phrase wallet_config = monero_wallet_config(); @@ -116,6 +120,7 @@ tx_config.m_amount = 50000; tx_config.m_relay = true; shared_ptr sent_tx = wallet_restored->create_tx(tx_config); bool in_pool = sent_tx->m_in_tx_pool.get(); // true +monero_utils::free(sent_tx); // mine with 7 threads to push the network along int num_threads = 7; @@ -146,10 +151,13 @@ tx_config.m_destinations = destinations; shared_ptr created_tx = wallet_restored->create_tx(tx_config); uint64_t fee = created_tx->m_fee.get(); // "Are you sure you want to send ...?" wallet_restored->relay_tx(*created_tx); // recipient receives notification within 5 seconds +monero_utils::free(created_tx); // save and close the wallets wallet_restored->close(true); wallet_random->close(true); +delete wallet_restored; +delete wallet_random; ``` ## Documentation diff --git a/src/utils/monero_utils.h b/src/utils/monero_utils.h index 1940841..2b13eb2 100644 --- a/src/utils/monero_utils.h +++ b/src/utils/monero_utils.h @@ -175,16 +175,70 @@ namespace monero_utils return ss.str(); } - /** - * Free memory of a block. - * - * @param block is the block to free - */ + // ----------------------------- GATHER BLOCKS ------------------------------ + + static std::vector> get_blocks_from_txs(std::vector> txs) { + std::shared_ptr unconfirmed_block = nullptr; // placeholder for unconfirmed txs + std::vector> blocks; + std::unordered_set> seen_block_ptrs; + for (const std::shared_ptr& tx : txs) { + if (tx->m_block == boost::none) { + if (unconfirmed_block == nullptr) unconfirmed_block = std::make_shared(); + tx->m_block = unconfirmed_block; + unconfirmed_block->m_txs.push_back(tx); + } + std::unordered_set>::const_iterator got = seen_block_ptrs.find(tx->m_block.get()); + if (got == seen_block_ptrs.end()) { + seen_block_ptrs.insert(tx->m_block.get()); + blocks.push_back(tx->m_block.get()); + } + } + return blocks; + } + + static std::vector> get_blocks_from_transfers(std::vector> transfers) { + std::shared_ptr unconfirmed_block = nullptr; // placeholder for unconfirmed txs in return json + std::vector> blocks; + std::unordered_set> seen_block_ptrs; + for (auto const& transfer : transfers) { + std::shared_ptr tx = transfer->m_tx; + if (tx->m_block == boost::none) { + if (unconfirmed_block == nullptr) unconfirmed_block = std::make_shared(); + tx->m_block = unconfirmed_block; + unconfirmed_block->m_txs.push_back(tx); + } + std::unordered_set>::const_iterator got = seen_block_ptrs.find(tx->m_block.get()); + if (got == seen_block_ptrs.end()) { + seen_block_ptrs.insert(tx->m_block.get()); + blocks.push_back(tx->m_block.get()); + } + } + return blocks; + } + + static std::vector> get_blocks_from_outputs(std::vector> outputs) { + std::vector> blocks; + std::unordered_set> seen_block_ptrs; + for (auto const& output : outputs) { + std::shared_ptr tx = std::static_pointer_cast(output->m_tx); + if (tx->m_block == boost::none) throw std::runtime_error("Need to handle unconfirmed output"); + std::unordered_set>::const_iterator got = seen_block_ptrs.find(*tx->m_block); + if (got == seen_block_ptrs.end()) { + seen_block_ptrs.insert(*tx->m_block); + blocks.push_back(*tx->m_block); + } + } + return blocks; + } + + // ------------------------------ FREE MEMORY ------------------------------- + static void free(std::shared_ptr block) { for (std::shared_ptr& tx : block->m_txs) { - tx->m_block.reset(); + tx->m_block->reset(); monero_tx_wallet* tx_wallet = dynamic_cast(tx.get()); if (tx_wallet != nullptr) { + if (tx_wallet->m_tx_set != boost::none) tx_wallet->m_tx_set->reset(); if (tx_wallet->m_outgoing_transfer != boost::none) tx_wallet->m_outgoing_transfer.get()->m_tx.reset(); for (std::shared_ptr transfer : tx_wallet->m_incoming_transfers) transfer->m_tx.reset(); for (std::shared_ptr output : tx_wallet->m_outputs) output->m_tx.reset(); @@ -193,17 +247,44 @@ namespace monero_utils input->m_tx.reset(); } } + monero_tx_query* tx_query = dynamic_cast(tx.get()); + if (tx_query != nullptr) { + if (tx_query->m_transfer_query != boost::none) { + tx_query->m_transfer_query.get()->m_tx_query->reset(); + tx_query->m_transfer_query.get().reset(); + } + if (tx_query->m_output_query != boost::none) { + tx_query->m_output_query.get()->m_tx_query->reset(); + tx_query->m_output_query.get().reset(); + } + } } block.reset(); } - /** - * Free memory of blocks. - * - * @param blocks are blocks to free - */ static void free(std::vector> blocks) { for (std::shared_ptr& block : blocks) monero_utils::free(block); } + + static void free(std::shared_ptr tx) { + if (tx->m_block == boost::none) { + std::shared_ptr block = std::make_shared(); + tx->m_block = block; + block->m_txs.push_back(tx); + } + monero_utils::free(tx->m_block.get()); + } + + static void free(std::vector> txs) { + return monero_utils::free(monero_utils::get_blocks_from_txs(txs)); + } + + static void free(std::vector> transfers) { + return monero_utils::free(monero_utils::get_blocks_from_transfers(transfers)); + } + + static void free(std::vector> outputs) { + return monero_utils::free(monero_utils::get_blocks_from_outputs(outputs)); + } } #endif /* monero_utils_h */ diff --git a/src/wallet/monero_wallet.h b/src/wallet/monero_wallet.h index 4d07165..65cb346 100644 --- a/src/wallet/monero_wallet.h +++ b/src/wallet/monero_wallet.h @@ -704,7 +704,7 @@ namespace monero { * Get all wallet transactions. Wallet transactions contain one or more * transfers that are either incoming or outgoing to the wallet. * - * @return all wallet transactions + * @return all wallet transactions (free memory using monero_utils::free) */ virtual std::vector> get_txs() const { throw std::runtime_error("get_txs() not supported"); @@ -720,7 +720,7 @@ namespace monero { * not defined. * * @param query filters query results (optional) - * @return wallet transactions per the query + * @return wallet transactions per the query (free memory using monero_utils::free) */ virtual std::vector> get_txs(const monero_tx_query& query) const { throw std::runtime_error("get_txs(query) not supported"); @@ -740,7 +740,7 @@ namespace monero { * defined. * * @param query filters query results (optional) - * @return wallet transfers per the query + * @return wallet transfers per the query (free memory using monero_utils::free) */ virtual std::vector> get_transfers(const monero_transfer_query& query) const { throw std::runtime_error("get_transfers() not supported"); @@ -756,7 +756,7 @@ namespace monero { * filtering is optional and no filtering is applied when not defined. * * @param query specifies query options (optional) - * @return wallet outputs per the query + * @return wallet outputs per the query (free memory using monero_utils::free) */ virtual std::vector> get_outputs(const monero_output_query& query) const { throw std::runtime_error("get_outputs() not supported"); @@ -834,7 +834,7 @@ namespace monero { * Create a transaction to transfer funds from this wallet. * * @param config configures the transaction to create - * @return the created transaction + * @return the created transaction (free memory using monero_utils::free) */ virtual std::shared_ptr create_tx(const monero_tx_config& config) { if (config.m_can_split != boost::none && config.m_can_split.get()) throw std::runtime_error("Cannot split transactions with create_tx(); use create_txs() instead"); @@ -847,7 +847,7 @@ namespace monero { * Create one or more transactions to transfer funds from this wallet. * * @param config configures the transactions to create - * @return the created transactions + * @return the created transactions (free memory using monero_utils::free) */ virtual std::vector> create_txs(const monero_tx_config& config) { throw std::runtime_error("create_txs() not supported"); @@ -857,7 +857,7 @@ namespace monero { * Sweep unlocked funds according to the given config. * * @param config is the sweep configuration - * @return the created transactions + * @return the created transactions (free memory using monero_utils::free) */ virtual std::vector> sweep_unlocked(const monero_tx_config& config) { throw std::runtime_error("sweep_unlocked() not supported"); @@ -867,7 +867,7 @@ namespace monero { * Sweep an output with a given key image. * * @param config configures the sweep transaction - * @return the created transaction + * @return the created transaction (free memory using monero_utils::free) */ virtual std::shared_ptr sweep_output(const monero_tx_config& config) { throw std::runtime_error("sweep_output() not supported"); @@ -877,7 +877,7 @@ namespace monero { * Sweep all unmixable dust outputs back to the wallet to make them easier to spend and mix. * * @param relay specifies if the resulting transaction should be relayed (default false) - * @return the created transactions + * @return the created transactions (free memory using monero_utils::free) */ virtual std::vector> sweep_dust(bool relay = false) { throw std::runtime_error("sweep_dust() not supported"); diff --git a/src/wallet/monero_wallet_full.cpp b/src/wallet/monero_wallet_full.cpp index e236dfd..5d2f953 100644 --- a/src/wallet/monero_wallet_full.cpp +++ b/src/wallet/monero_wallet_full.cpp @@ -819,8 +819,7 @@ namespace monero { m_prev_locked_tx_hashes.insert(tx->m_hash.get()); // free memory - output.reset(); - tx.reset(); + monero_utils::free(tx); } catch (std::exception& e) { std::cout << "Error processing unconfirmed output received: " << std::string(e.what()) << std::endl; } @@ -862,8 +861,6 @@ namespace monero { // free memory monero_utils::free(block); - output.reset(); - tx.reset(); } catch (std::exception& e) { std::cout << "Error processing confirmed output received: " << std::string(e.what()) << std::endl; } @@ -905,8 +902,6 @@ namespace monero { // free memory monero_utils::free(block); - output.reset(); - tx.reset(); } catch (std::exception& e) { std::cout << "Error processing confirmed output spent: " << std::string(e.what()) << std::endl; } @@ -998,6 +993,10 @@ namespace monero { for (const std::shared_ptr& locked_tx : locked_txs) { m_prev_locked_tx_hashes.insert(locked_tx->m_hash.get()); } + + // free memory + monero_utils::free(locked_txs); + monero_utils::free(txs_no_longer_locked); } void notify_outputs(const std::shared_ptr& tx) { @@ -1023,8 +1022,9 @@ namespace monero { block_notify->m_txs.push_back(tx_notify); } - // notify listeners + // notify listeners and free memory for (monero_wallet_listener* listener : m_wallet.get_listeners()) listener->on_output_spent(*input); + monero_utils::free(tx_notify); } // notify received outputs @@ -1741,6 +1741,7 @@ namespace monero { temp_transfer_query->m_tx_query = decontextualize(_query->copy(_query, std::make_shared())); temp_transfer_query->m_tx_query.get()->m_transfer_query = temp_transfer_query; std::vector> transfers = get_transfers_aux(*temp_transfer_query); + monero_utils::free(temp_transfer_query->m_tx_query.get()); // collect unique txs from transfers while retaining order std::vector> txs = std::vector>(); @@ -1765,6 +1766,7 @@ namespace monero { temp_output_query->m_tx_query = decontextualize(_query->copy(_query, std::make_shared())); temp_output_query->m_tx_query.get()->m_output_query = temp_output_query; std::vector> outputs = get_outputs_aux(*temp_output_query); + monero_utils::free(temp_output_query->m_tx_query.get()); // merge output txs one time while retaining order std::unordered_set> output_txs; @@ -1802,7 +1804,9 @@ namespace monero { for (const std::shared_ptr& tx : txs) { if (*tx->m_is_confirmed && tx->m_block == boost::none) { std::cout << "WARNING: Inconsistency detected building txs from multiple wallet2 calls, re-fetching" << std::endl; - return get_txs(*_query); + std::vector> txs = get_txs(*_query); + monero_utils::free(_query); + return txs; } } @@ -1815,6 +1819,8 @@ namespace monero { } } + // free query and return + monero_utils::free(_query); return txs; } @@ -3567,6 +3573,7 @@ namespace monero { // check if fetching pool txs contradicted by configuration if (tx_query->m_in_tx_pool != boost::none && tx_query->m_in_tx_pool.get() && !can_be_in_tx_pool) { + monero_utils::free(tx_query); throw std::runtime_error("Cannot fetch pool transactions because it contradicts configuration"); } @@ -3633,7 +3640,7 @@ namespace monero { } sort(txs.begin(), txs.end(), tx_height_less_than); - // filter and return transfers + // filter transfers std::vector> transfers; for (const std::shared_ptr& tx : txs) { @@ -3654,6 +3661,8 @@ namespace monero { } MTRACE("monero_wallet_full.cpp get_transfers() returning " << transfers.size() << " transfers"); + // free query and return transfers + monero_utils::free(tx_query); return transfers; } @@ -3683,6 +3692,7 @@ namespace monero { } } if (_query->m_tx_query == boost::none) _query->m_tx_query = std::make_shared(); + std::shared_ptr tx_query = _query->m_tx_query.get(); // get output data from wallet2 tools::wallet2::transfer_container outputs_w2; @@ -3717,6 +3727,9 @@ namespace monero { // 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? } + + // free query and return outputs + monero_utils::free(tx_query); return outputs; } diff --git a/test/sample_code.cpp b/test/sample_code.cpp index 8648bcf..f2013d4 100644 --- a/test/sample_code.cpp +++ b/test/sample_code.cpp @@ -2,6 +2,7 @@ #include #include "wallet2.h" #include "wallet/monero_wallet_full.h" +#include "utils/monero_utils.h" using namespace std; @@ -52,23 +53,27 @@ int main(int argc, const char* argv[]) { // query a transaction by hash monero_tx_query tx_query; tx_query.m_hash = "314a0f1375db31cea4dac4e0a51514a6282b43792269b3660166d4d2b46437ca"; - shared_ptr tx = wallet_restored->get_txs(tx_query)[0]; + vector> txs = wallet_restored->get_txs(tx_query); + shared_ptr tx = txs[0]; for (const shared_ptr 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(); } + monero_utils::free(txs); // query incoming transfers to account 1 monero_transfer_query transfer_query; transfer_query.m_is_incoming = true; transfer_query.m_account_index = 1; vector> transfers = wallet_restored->get_transfers(transfer_query); + monero_utils::free(transfers); // query unspent outputs monero_output_query output_query; output_query.m_is_spent = false; vector> outputs = wallet_restored->get_outputs(output_query); + monero_utils::free(outputs); // create and sync a new wallet with a random seed wallet_config = monero_wallet_config(); @@ -114,6 +119,7 @@ int main(int argc, const char* argv[]) { tx_config.m_relay = true; shared_ptr sent_tx = wallet_restored->create_tx(tx_config); bool in_pool = sent_tx->m_in_tx_pool.get(); // true + monero_utils::free(sent_tx); // mine with 7 threads to push the network along int num_threads = 7; @@ -144,6 +150,7 @@ int main(int argc, const char* argv[]) { shared_ptr created_tx = wallet_restored->create_tx(tx_config); uint64_t fee = created_tx->m_fee.get(); // "Are you sure you want to send ...?" wallet_restored->relay_tx(*created_tx); // recipient receives notification within 5 seconds + monero_utils::free(created_tx); // the recipient wallet will be notified if (FUNDS_RECEIVED) cout << "Sample code completed successfully" << endl; @@ -152,4 +159,6 @@ int main(int argc, const char* argv[]) { // save and close the wallets wallet_restored->close(true); wallet_random->close(true); + delete wallet_restored; + delete wallet_random; } diff --git a/test/scratchpad.cpp b/test/scratchpad.cpp index 03b6803..d172c46 100644 --- a/test/scratchpad.cpp +++ b/test/scratchpad.cpp @@ -2,6 +2,7 @@ #include #include "wallet2.h" #include "wallet/monero_wallet_full.h" +#include "utils/monero_utils.h" using namespace std; @@ -29,13 +30,26 @@ int main(int argc, const char* argv[]) { int network_type = 2; // load wallet - monero_wallet* wallet = monero_wallet_full::open_wallet("../../test_wallets/test_wallet_1", "supersecretpassword123", monero_network_type::STAGENET); - wallet->set_daemon_connection("http://localhost:38081", "", ""); + monero_wallet* wallet = monero_wallet_full::open_wallet("../../test_wallets/test_wallet_1", "supersecretpassword123", monero_network_type::TESTNET); + wallet->set_daemon_connection("http://localhost:28081", "", ""); - // fetch txs + // get txs vector> txs = wallet->get_txs(); - MINFO("Wallet has " << txs.size() << " txs. Printing some."); - for (int i = 0; i < txs.size() && i < 10; i++) { - MINFO(txs[i]->serialize()); - } + MINFO("Wallet has " << txs.size() << " txs"); + for (int i = 0; i < txs.size() && i < 10; i++) MINFO(txs[i]->serialize()); + monero_utils::free(txs); + + // get transfers + vector> transfers = wallet->get_transfers(monero_transfer_query()); + MINFO("Wallet has " << transfers.size() << " transfers"); + monero_utils::free(transfers); + + // get outputs + vector> outputs = wallet->get_outputs(monero_output_query()); + MINFO("Wallet has " << outputs.size() << " outputs"); + monero_utils::free(outputs); + + // close wallet and free pointer + wallet->close(true); + delete wallet; }