mirror of
https://github.com/wownero/wownero-cpp.git
synced 2026-10-17 23:31:32 +02:00
support notifications of unlocked outputs
outputs merged by key images xor appended: pre-ringct indices not unique
This commit is contained in:
@@ -427,6 +427,7 @@ namespace monero {
|
||||
m_fee = gen_utils::reconcile(m_fee, other->m_fee, "tx fee");
|
||||
m_ring_size = gen_utils::reconcile(m_ring_size, other->m_ring_size, "tx m_ring_size");
|
||||
m_is_confirmed = gen_utils::reconcile(m_is_confirmed, other->m_is_confirmed);
|
||||
m_is_miner_tx = gen_utils::reconcile(m_is_miner_tx, other->m_is_miner_tx);
|
||||
m_relay = gen_utils::reconcile(m_relay, other->m_relay);
|
||||
m_is_relayed = gen_utils::reconcile(m_is_relayed, other->m_is_relayed);
|
||||
m_is_double_spend_seen = gen_utils::reconcile(m_is_double_spend_seen, other->m_is_double_spend_seen);
|
||||
@@ -483,60 +484,37 @@ namespace monero {
|
||||
throw std::runtime_error("Some outputs have an output index and some do not");
|
||||
}
|
||||
|
||||
// merge by output indices if present
|
||||
if (num_indices > 0) {
|
||||
for (const std::shared_ptr<monero_output>& merger : other->m_outputs) {
|
||||
// determine if key images present
|
||||
int numKeyImages = 0;
|
||||
for (const std::shared_ptr<monero_output> output : m_outputs) {
|
||||
if (output->m_key_image != boost::none) {
|
||||
if ((*output->m_key_image)->m_hex == boost::none) throw std::runtime_error("Key image hex cannot be null");
|
||||
numKeyImages++;
|
||||
}
|
||||
}
|
||||
for (const std::shared_ptr<monero_output>& output : other->m_outputs) {
|
||||
if (output->m_key_image != boost::none) {
|
||||
if ((*output->m_key_image)->m_hex == boost::none) throw std::runtime_error("Key image hex cannot be null");
|
||||
numKeyImages++;
|
||||
}
|
||||
}
|
||||
if (numKeyImages != 0 && m_outputs.size() + other->m_outputs.size() != numKeyImages) throw std::runtime_error("Some outputs have a key image and some do not");
|
||||
|
||||
// merge outputs by key image if present, otherwise append (cannot merge by global index because pre-RingCT output indices are not unique)
|
||||
for (const std::shared_ptr<monero_output>& merger : other->m_outputs) {
|
||||
if (numKeyImages > 0) {
|
||||
bool merged = false;
|
||||
merger->m_tx = self;
|
||||
for (const std::shared_ptr<monero_output>& mergee : this->m_outputs) {
|
||||
if (mergee->m_index.get() == merger->m_index.get()) {
|
||||
for (const std::shared_ptr<monero_output>& mergee : m_outputs) {
|
||||
if ((*mergee->m_key_image)->m_hex == (*merger->m_key_image)->m_hex) {
|
||||
mergee->merge(mergee, merger);
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!merged) this->m_outputs.push_back(merger);
|
||||
}
|
||||
} else {
|
||||
|
||||
// determine if key images present
|
||||
int numKeyImages = 0;
|
||||
for (const std::shared_ptr<monero_output> output : m_outputs) {
|
||||
if (output->m_key_image != boost::none) {
|
||||
if ((*output->m_key_image)->m_hex == boost::none) throw std::runtime_error("Key image hex cannot be null");
|
||||
numKeyImages++;
|
||||
}
|
||||
}
|
||||
for (const std::shared_ptr<monero_output>& output : other->m_outputs) {
|
||||
if (output->m_key_image != boost::none) {
|
||||
if ((*output->m_key_image)->m_hex == boost::none) throw std::runtime_error("Key image hex cannot be null");
|
||||
numKeyImages++;
|
||||
}
|
||||
}
|
||||
if (numKeyImages != 0 && m_outputs.size() + other->m_outputs.size() != numKeyImages) throw std::runtime_error("Some outputs have a key image and some do not");
|
||||
|
||||
// merge by key images
|
||||
if (numKeyImages > 0) {
|
||||
for (const std::shared_ptr<monero_output>& merger : other->m_outputs) {
|
||||
bool merged = false;
|
||||
merger->m_tx = self;
|
||||
for (const std::shared_ptr<monero_output>& mergee : m_outputs) {
|
||||
if ((*mergee->m_key_image)->m_hex == (*merger->m_key_image)->m_hex) {
|
||||
mergee->merge(mergee, merger);
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!merged) m_outputs.push_back(merger);
|
||||
}
|
||||
}
|
||||
|
||||
// merge by position
|
||||
else {
|
||||
if (m_outputs.size() != other->m_outputs.size()) throw std::runtime_error("Vout sizes are different");
|
||||
for (int i = 0; i < other->m_outputs.size(); i++) {
|
||||
m_outputs.at(i)->merge(m_outputs.at(i), other->m_outputs.at(i));
|
||||
}
|
||||
if (!merged) m_outputs.push_back(merger); // add new key image
|
||||
} else {
|
||||
m_outputs.push_back(merger); // no key images present, append
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -611,7 +589,11 @@ namespace monero {
|
||||
}
|
||||
|
||||
void monero_key_image::merge(const std::shared_ptr<monero_key_image>& self, const std::shared_ptr<monero_key_image>& other) {
|
||||
throw std::runtime_error("Not implemented");
|
||||
MTRACE("monero_key_image::merge(self, other)");
|
||||
if (this != self.get()) throw std::runtime_error("this != self");
|
||||
if (self == other) return;
|
||||
m_hex = gen_utils::reconcile(m_hex, other->m_hex);
|
||||
m_signature = gen_utils::reconcile(m_signature, other->m_signature);
|
||||
}
|
||||
|
||||
// ------------------------------ MONERO OUTPUT -----------------------------
|
||||
|
||||
@@ -451,11 +451,14 @@ namespace monero {
|
||||
// compare by height
|
||||
if (tx_height_less_than(ow1->m_tx, ow2->m_tx)) return true;
|
||||
|
||||
// compare by account index, subaddress index, and output
|
||||
// compare by account index, subaddress index, output index, then key image hex
|
||||
if (ow1->m_account_index.get() < ow2->m_account_index.get()) return true;
|
||||
else if (ow1->m_account_index.get() == ow2->m_account_index.get()) {
|
||||
if (ow1->m_account_index.get() == ow2->m_account_index.get()) {
|
||||
if (ow1->m_subaddress_index.get() < ow2->m_subaddress_index.get()) return true;
|
||||
if (ow1->m_subaddress_index.get() == ow2->m_subaddress_index.get() && ow1->m_index.get() < ow2->m_index.get()) return true;
|
||||
if (ow1->m_subaddress_index.get() == ow2->m_subaddress_index.get()) {
|
||||
if (ow1->m_index.get() < ow2->m_index.get()) return true;
|
||||
if (ow1->m_index.get() == ow2->m_index.get()) return ow1->m_key_image.get()->m_hex.get().compare(ow2->m_key_image.get()->m_hex.get()) < 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -689,6 +692,8 @@ namespace monero {
|
||||
wallet2_listener(monero_wallet_core& wallet, tools::wallet2& m_w2) : m_wallet(wallet), m_w2(m_w2) {
|
||||
this->m_sync_start_height = boost::none;
|
||||
this->m_sync_end_height = boost::none;
|
||||
m_prev_balance = wallet.get_balance();
|
||||
m_prev_unlocked_balance = wallet.get_unlocked_balance();
|
||||
}
|
||||
|
||||
~wallet2_listener() {
|
||||
@@ -709,6 +714,7 @@ namespace monero {
|
||||
void on_sync_end() {
|
||||
m_sync_start_height = boost::none;
|
||||
m_sync_end_height = boost::none;
|
||||
check_for_changed_funds();
|
||||
}
|
||||
|
||||
void on_new_block(uint64_t height, const cryptonote::block& cn_block) override {
|
||||
@@ -729,13 +735,9 @@ namespace monero {
|
||||
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
||||
listener->on_sync_progress(height, *m_sync_start_height, *m_sync_end_height, percent_done, message);
|
||||
}
|
||||
}
|
||||
|
||||
void on_balances_changed(uint64_t new_balance, uint64_t new_unlocked_balance) {
|
||||
if (m_wallet.get_listeners().empty()) return;
|
||||
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
||||
listener->on_balances_changed(new_balance, new_unlocked_balance);
|
||||
}
|
||||
// notify if balances or unlocked txs changed
|
||||
check_for_changed_funds();
|
||||
}
|
||||
|
||||
void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& cn_tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) override {
|
||||
@@ -744,6 +746,7 @@ namespace monero {
|
||||
// create library tx
|
||||
std::shared_ptr<monero_tx_wallet> tx = std::static_pointer_cast<monero_tx_wallet>(monero_utils::cn_tx_to_tx(cn_tx, true));
|
||||
tx->m_hash = epee::string_tools::pod_to_hex(txid);
|
||||
tx->m_is_locked = true;
|
||||
std::shared_ptr<monero_output_wallet> output = std::make_shared<monero_output_wallet>();
|
||||
tx->m_outputs.push_back(output);
|
||||
output->m_tx = tx;
|
||||
@@ -756,8 +759,8 @@ namespace monero {
|
||||
listener->on_output_received(*output);
|
||||
}
|
||||
|
||||
// notify if balances changed
|
||||
m_wallet.check_for_changed_balances();
|
||||
// notify if balances or unlocked txs changed
|
||||
check_for_changed_funds();
|
||||
|
||||
// free memory
|
||||
output.reset();
|
||||
@@ -775,6 +778,7 @@ namespace monero {
|
||||
tx->m_block = block;
|
||||
tx->m_hash = epee::string_tools::pod_to_hex(txid);
|
||||
tx->m_unlock_time = unlock_time;
|
||||
tx->m_is_locked = true;
|
||||
std::shared_ptr<monero_output_wallet> output = std::make_shared<monero_output_wallet>();
|
||||
tx->m_outputs.push_back(output);
|
||||
output->m_tx = tx;
|
||||
@@ -788,7 +792,7 @@ namespace monero {
|
||||
}
|
||||
|
||||
// notify if balances changed
|
||||
m_wallet.check_for_changed_balances();
|
||||
check_for_changed_funds();
|
||||
|
||||
// free memory
|
||||
monero_utils::free(block);
|
||||
@@ -820,7 +824,7 @@ namespace monero {
|
||||
}
|
||||
|
||||
// notify if balances changed
|
||||
m_wallet.check_for_changed_balances();
|
||||
check_for_changed_funds();
|
||||
|
||||
// free memory
|
||||
monero_utils::free(block);
|
||||
@@ -833,6 +837,73 @@ namespace monero {
|
||||
tools::wallet2& m_w2; // internal wallet implementation to listen to
|
||||
boost::optional<uint64_t> m_sync_start_height;
|
||||
boost::optional<uint64_t> m_sync_end_height;
|
||||
uint64_t m_prev_balance;
|
||||
uint64_t m_prev_unlocked_balance;
|
||||
std::vector<std::shared_ptr<monero_tx_wallet>> m_locked_txs;
|
||||
|
||||
void check_for_changed_funds() {
|
||||
if (m_wallet.get_listeners().empty()) return; // skip if no listeners
|
||||
if (m_prev_balance != m_wallet.get_balance() || m_prev_unlocked_balance != m_wallet.get_unlocked_balance()) {
|
||||
on_balances_changed(m_wallet.get_balance(), m_wallet.get_unlocked_balance());
|
||||
m_prev_balance = m_wallet.get_balance();
|
||||
m_prev_unlocked_balance = m_wallet.get_unlocked_balance();
|
||||
}
|
||||
if (m_wallet.is_synced()) check_for_changed_unlocked_txs(); // check for newly unlocked outputs if synced
|
||||
}
|
||||
|
||||
void on_balances_changed(uint64_t new_balance, uint64_t new_unlocked_balance) {
|
||||
if (m_wallet.get_listeners().empty()) return;
|
||||
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
||||
listener->on_balances_changed(new_balance, new_unlocked_balance);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this can probably be optimized using e.g. wallet2.get_num_rct_outputs() or wallet2.get_num_transfer_details(), or by retaining confirmed block height and only checking on or after unlock height, etc
|
||||
void check_for_changed_unlocked_txs() {
|
||||
if (m_wallet.get_listeners().empty()) return; // skip if no listeners
|
||||
|
||||
// get locked txs
|
||||
monero_tx_query query = monero_tx_query();
|
||||
query.m_is_locked = true;
|
||||
query.m_is_confirmed = true;
|
||||
std::vector<std::shared_ptr<monero_tx_wallet>> locked_txs = m_wallet.get_txs(query);
|
||||
|
||||
// collect hashes of txs no longer locked
|
||||
std::vector<std::string> tx_hashes_no_longer_locked;
|
||||
for (const std::shared_ptr<monero_tx_wallet>& prev_locked_tx : m_locked_txs) {
|
||||
bool found = false;
|
||||
for (const std::shared_ptr<monero_tx_wallet>& cur_locked_tx : locked_txs) {
|
||||
if (cur_locked_tx->m_hash.get() == prev_locked_tx->m_hash.get()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) tx_hashes_no_longer_locked.push_back(prev_locked_tx->m_hash.get());
|
||||
}
|
||||
|
||||
// fetch txs that are no longer locked
|
||||
std::vector<std::shared_ptr<monero_tx_wallet>> txs_no_longer_locked;
|
||||
if (!tx_hashes_no_longer_locked.empty()) {
|
||||
query.m_hashes = tx_hashes_no_longer_locked;
|
||||
query.m_is_locked = false;
|
||||
query.m_is_confirmed = true;
|
||||
query.m_include_outputs = true; // TODO: fetch outputs that are no longer locked directly?
|
||||
std::vector<std::string> missing_tx_hashes;
|
||||
txs_no_longer_locked = m_wallet.get_txs(query, missing_tx_hashes);
|
||||
}
|
||||
|
||||
// notify listeners of newly unlocked tx outputs
|
||||
for (const std::shared_ptr<monero_tx_wallet>& unlocked_tx : txs_no_longer_locked) {
|
||||
for (const std::shared_ptr<monero_output_wallet>& output : unlocked_tx->get_outputs_wallet()) {
|
||||
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
||||
listener->on_output_received(*output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// re-assign currently locked txs
|
||||
m_locked_txs = locked_txs;
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------- STATIC WALLET UTILS --------------------------
|
||||
@@ -3052,8 +3123,6 @@ namespace monero {
|
||||
MTRACE("monero_wallet_core.cpp init_common()");
|
||||
m_w2_listener = std::unique_ptr<wallet2_listener>(new wallet2_listener(*this, *m_w2));
|
||||
if (get_daemon_connection() == boost::none) m_is_connected = false;
|
||||
m_prev_balance = get_balance();
|
||||
m_prev_unlocked_balance = get_unlocked_balance();
|
||||
m_is_synced = false;
|
||||
m_rescan_on_sync = false;
|
||||
m_syncing_enabled = false;
|
||||
@@ -3071,14 +3140,6 @@ namespace monero {
|
||||
#endif
|
||||
}
|
||||
|
||||
void monero_wallet_core::check_for_changed_balances() {
|
||||
if (m_prev_balance != get_balance() || m_prev_unlocked_balance != get_unlocked_balance()) {
|
||||
m_prev_balance = get_balance();
|
||||
m_prev_unlocked_balance = get_unlocked_balance();
|
||||
m_w2_listener->on_balances_changed(m_prev_balance, m_prev_unlocked_balance);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<monero_transfer>> monero_wallet_core::get_transfers_aux(const monero_transfer_query& query) const {
|
||||
MTRACE("monero_wallet_core::get_transfers(query)");
|
||||
|
||||
@@ -3325,6 +3386,8 @@ namespace monero {
|
||||
while (m_syncing_enabled) {
|
||||
try {
|
||||
lock_and_sync();
|
||||
} catch (std::exception const& e) {
|
||||
std::cout << "monero_wallet_core failed to background synchronize: " << e.what() << std::endl;
|
||||
} catch (...) {
|
||||
std::cout << "monero_wallet_core failed to background synchronize" << std::endl;
|
||||
}
|
||||
@@ -3385,9 +3448,8 @@ namespace monero {
|
||||
// find and save rings
|
||||
m_w2->find_and_save_rings(false);
|
||||
|
||||
// notify listeners of sync end and check for updated balances
|
||||
// notify listeners of sync end and check for updated funds
|
||||
m_w2_listener->on_sync_end();
|
||||
check_for_changed_balances();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +288,6 @@ namespace monero {
|
||||
std::unique_ptr<wallet2_listener> m_w2_listener; // internal wallet implementation listener
|
||||
std::set<monero_wallet_listener*> m_listeners; // external wallet listeners
|
||||
|
||||
uint64_t m_prev_balance;
|
||||
uint64_t m_prev_unlocked_balance;
|
||||
void check_for_changed_balances();
|
||||
|
||||
void init_common();
|
||||
std::vector<monero_subaddress> get_subaddresses_aux(uint32_t account_idx, const std::vector<uint32_t>& subaddress_indices, const std::vector<tools::wallet2::transfer_details>& transfers) const;
|
||||
std::vector<std::shared_ptr<monero_transfer>> get_transfers_aux(const monero_transfer_query& query) const;
|
||||
|
||||
Reference in New Issue
Block a user