patch memory leaks using analyzer

master
woodser 9 months ago
parent 93cd0f47cd
commit 7d97007519

@ -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<monero_tx_wallet> tx = wallet_restored->get_txs(tx_query)[0];
vector<shared_ptr<monero_tx_wallet>> txs = wallet_restored->get_txs(tx_query);
shared_ptr<monero_tx_wallet> tx = txs[0];
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();
}
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<shared_ptr<monero_transfer>> 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<shared_ptr<monero_output_wallet>> 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<monero_tx_wallet> 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<monero_tx_wallet> 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

@ -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<std::shared_ptr<monero_block>> get_blocks_from_txs(std::vector<std::shared_ptr<monero_tx_wallet>> txs) {
std::shared_ptr<monero_block> unconfirmed_block = nullptr; // placeholder for unconfirmed txs
std::vector<std::shared_ptr<monero_block>> blocks;
std::unordered_set<std::shared_ptr<monero_block>> seen_block_ptrs;
for (const std::shared_ptr<monero_tx_wallet>& tx : txs) {
if (tx->m_block == boost::none) {
if (unconfirmed_block == nullptr) unconfirmed_block = std::make_shared<monero_block>();
tx->m_block = unconfirmed_block;
unconfirmed_block->m_txs.push_back(tx);
}
std::unordered_set<std::shared_ptr<monero_block>>::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<std::shared_ptr<monero_block>> get_blocks_from_transfers(std::vector<std::shared_ptr<monero_transfer>> transfers) {
std::shared_ptr<monero_block> unconfirmed_block = nullptr; // placeholder for unconfirmed txs in return json
std::vector<std::shared_ptr<monero_block>> blocks;
std::unordered_set<std::shared_ptr<monero_block>> seen_block_ptrs;
for (auto const& transfer : transfers) {
std::shared_ptr<monero_tx_wallet> tx = transfer->m_tx;
if (tx->m_block == boost::none) {
if (unconfirmed_block == nullptr) unconfirmed_block = std::make_shared<monero_block>();
tx->m_block = unconfirmed_block;
unconfirmed_block->m_txs.push_back(tx);
}
std::unordered_set<std::shared_ptr<monero_block>>::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<std::shared_ptr<monero_block>> get_blocks_from_outputs(std::vector<std::shared_ptr<monero_output_wallet>> outputs) {
std::vector<std::shared_ptr<monero_block>> blocks;
std::unordered_set<std::shared_ptr<monero_block>> seen_block_ptrs;
for (auto const& output : outputs) {
std::shared_ptr<monero_tx_wallet> tx = std::static_pointer_cast<monero_tx_wallet>(output->m_tx);
if (tx->m_block == boost::none) throw std::runtime_error("Need to handle unconfirmed output");
std::unordered_set<std::shared_ptr<monero_block>>::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<monero_block> block) {
for (std::shared_ptr<monero_tx>& tx : block->m_txs) {
tx->m_block.reset();
tx->m_block->reset();
monero_tx_wallet* tx_wallet = dynamic_cast<monero_tx_wallet*>(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<monero_transfer> transfer : tx_wallet->m_incoming_transfers) transfer->m_tx.reset();
for (std::shared_ptr<monero_output> 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<monero_tx_query*>(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<std::shared_ptr<monero_block>> blocks) {
for (std::shared_ptr<monero_block>& block : blocks) monero_utils::free(block);
}
static void free(std::shared_ptr<monero_tx> tx) {
if (tx->m_block == boost::none) {
std::shared_ptr<monero_block> block = std::make_shared<monero_block>();
tx->m_block = block;
block->m_txs.push_back(tx);
}
monero_utils::free(tx->m_block.get());
}
static void free(std::vector<std::shared_ptr<monero_tx_wallet>> txs) {
return monero_utils::free(monero_utils::get_blocks_from_txs(txs));
}
static void free(std::vector<std::shared_ptr<monero_transfer>> transfers) {
return monero_utils::free(monero_utils::get_blocks_from_transfers(transfers));
}
static void free(std::vector<std::shared_ptr<monero_output_wallet>> outputs) {
return monero_utils::free(monero_utils::get_blocks_from_outputs(outputs));
}
}
#endif /* monero_utils_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<std::shared_ptr<monero_tx_wallet>> 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<std::shared_ptr<monero_tx_wallet>> 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<std::shared_ptr<monero_transfer>> 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<std::shared_ptr<monero_output_wallet>> 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<monero_tx_wallet> 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<std::shared_ptr<monero_tx_wallet>> 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<std::shared_ptr<monero_tx_wallet>> 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<monero_tx_wallet> 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<std::shared_ptr<monero_tx_wallet>> sweep_dust(bool relay = false) {
throw std::runtime_error("sweep_dust() not supported");

@ -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<monero_tx_wallet>& 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<monero_tx_wallet>& 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<monero_tx_query>()));
temp_transfer_query->m_tx_query.get()->m_transfer_query = temp_transfer_query;
std::vector<std::shared_ptr<monero_transfer>> 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<std::shared_ptr<monero_tx_wallet>> txs = std::vector<std::shared_ptr<monero_tx_wallet>>();
@ -1765,6 +1766,7 @@ namespace monero {
temp_output_query->m_tx_query = decontextualize(_query->copy(_query, std::make_shared<monero_tx_query>()));
temp_output_query->m_tx_query.get()->m_output_query = temp_output_query;
std::vector<std::shared_ptr<monero_output_wallet>> 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<std::shared_ptr<monero_tx_wallet>> output_txs;
@ -1802,7 +1804,9 @@ namespace monero {
for (const std::shared_ptr<monero_tx_wallet>& 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<std::shared_ptr<monero_tx_wallet>> 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<std::shared_ptr<monero_transfer>> transfers;
for (const std::shared_ptr<monero_tx_wallet>& 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<monero_tx_query>();
std::shared_ptr<monero_tx_query> 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;
}

@ -2,6 +2,7 @@
#include <iostream>
#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<monero_tx_wallet> tx = wallet_restored->get_txs(tx_query)[0];
vector<shared_ptr<monero_tx_wallet>> txs = wallet_restored->get_txs(tx_query);
shared_ptr<monero_tx_wallet> tx = txs[0];
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();
}
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<shared_ptr<monero_transfer>> 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<shared_ptr<monero_output_wallet>> 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<monero_tx_wallet> 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<monero_tx_wallet> 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;
}

@ -2,6 +2,7 @@
#include <iostream>
#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<shared_ptr<monero_tx_wallet>> 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<shared_ptr<monero_transfer>> transfers = wallet->get_transfers(monero_transfer_query());
MINFO("Wallet has " << transfers.size() << " transfers");
monero_utils::free(transfers);
// get outputs
vector<shared_ptr<monero_output_wallet>> 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;
}

Loading…
Cancel
Save