|
|
|
@@ -381,7 +381,7 @@ namespace monero {
|
|
|
|
|
output->m_subaddress_index = td.m_subaddr_index.minor;
|
|
|
|
|
output->m_is_spent = td.m_spent;
|
|
|
|
|
output->m_is_frozen = td.m_frozen;
|
|
|
|
|
//output->m_stealth_public_key = epee::string_tools::pod_to_hex(td.get_public_key()); // TODO (monero-wallet-rpc): provide this field
|
|
|
|
|
output->m_stealth_public_key = epee::string_tools::pod_to_hex(td.get_public_key());
|
|
|
|
|
if (td.m_key_image_known) {
|
|
|
|
|
output->m_key_image = std::make_shared<monero_key_image>();
|
|
|
|
|
output->m_key_image.get()->m_hex = epee::string_tools::pod_to_hex(td.m_key_image);
|
|
|
|
@@ -720,6 +720,7 @@ namespace monero {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void update_listening() {
|
|
|
|
|
boost::lock_guard<boost::mutex> guarg(m_listener_mutex);
|
|
|
|
|
|
|
|
|
|
// if starting to listen, cache locked txs for later comparison
|
|
|
|
|
if (!m_wallet.get_listeners().empty() && m_w2.callback() == nullptr) check_for_changed_unlocked_txs();
|
|
|
|
@@ -785,72 +786,80 @@ namespace monero {
|
|
|
|
|
// queue notification processing off main thread
|
|
|
|
|
tools::threadpool::waiter waiter(*m_notification_pool);
|
|
|
|
|
m_notification_pool->submit(&waiter, [this, height, txid, cn_tx, amount, subaddr_index]() {
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
// 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_confirmed = false;
|
|
|
|
|
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;
|
|
|
|
|
output->m_amount = amount;
|
|
|
|
|
output->m_account_index = subaddr_index.major;
|
|
|
|
|
output->m_subaddress_index = subaddr_index.minor;
|
|
|
|
|
// 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_confirmed = false;
|
|
|
|
|
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;
|
|
|
|
|
output->m_amount = amount;
|
|
|
|
|
output->m_account_index = subaddr_index.major;
|
|
|
|
|
output->m_subaddress_index = subaddr_index.minor;
|
|
|
|
|
|
|
|
|
|
// notify listeners of output
|
|
|
|
|
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
|
|
|
|
listener->on_output_received(*output);
|
|
|
|
|
// notify listeners of output
|
|
|
|
|
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
|
|
|
|
listener->on_output_received(*output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// notify if balances changed
|
|
|
|
|
check_for_changed_balances();
|
|
|
|
|
|
|
|
|
|
// watch for unlock
|
|
|
|
|
m_prev_locked_tx_hashes.insert(tx->m_hash.get());
|
|
|
|
|
|
|
|
|
|
// free memory
|
|
|
|
|
output.reset();
|
|
|
|
|
tx.reset();
|
|
|
|
|
} catch (std::exception& e) {
|
|
|
|
|
std::cout << "Error processing unconfirmed output received: " << std::string(e.what()) << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// notify if balances changed
|
|
|
|
|
check_for_changed_balances();
|
|
|
|
|
|
|
|
|
|
// watch for unlock
|
|
|
|
|
m_prev_locked_tx_hashes.insert(tx->m_hash.get());
|
|
|
|
|
|
|
|
|
|
// free memory
|
|
|
|
|
output.reset();
|
|
|
|
|
tx.reset();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& cn_tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_height) override {
|
|
|
|
|
void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& cn_tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_height) override {
|
|
|
|
|
if (m_wallet.get_listeners().empty()) return;
|
|
|
|
|
|
|
|
|
|
// queue notification processing off main thread
|
|
|
|
|
tools::threadpool::waiter waiter(*m_notification_pool);
|
|
|
|
|
m_notification_pool->submit(&waiter, [this, height, txid, cn_tx, amount, subaddr_index, is_change, unlock_height]() {
|
|
|
|
|
m_notification_pool->submit(&waiter, [this, height, txid, cn_tx, amount, burnt, subaddr_index, is_change, unlock_height]() {
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
// create native library tx
|
|
|
|
|
std::shared_ptr<monero_block> block = std::make_shared<monero_block>();
|
|
|
|
|
block->m_height = height;
|
|
|
|
|
std::shared_ptr<monero_tx_wallet> tx = std::static_pointer_cast<monero_tx_wallet>(monero_utils::cn_tx_to_tx(cn_tx, true));
|
|
|
|
|
block->m_txs.push_back(tx);
|
|
|
|
|
tx->m_block = block;
|
|
|
|
|
tx->m_hash = epee::string_tools::pod_to_hex(txid);
|
|
|
|
|
tx->m_is_confirmed = true;
|
|
|
|
|
tx->m_is_locked = true;
|
|
|
|
|
tx->m_unlock_height = unlock_height;
|
|
|
|
|
std::shared_ptr<monero_output_wallet> output = std::make_shared<monero_output_wallet>();
|
|
|
|
|
tx->m_outputs.push_back(output);
|
|
|
|
|
output->m_tx = tx;
|
|
|
|
|
output->m_amount = amount;
|
|
|
|
|
output->m_account_index = subaddr_index.major;
|
|
|
|
|
output->m_subaddress_index = subaddr_index.minor;
|
|
|
|
|
// create native library tx
|
|
|
|
|
std::shared_ptr<monero_block> block = std::make_shared<monero_block>();
|
|
|
|
|
block->m_height = height;
|
|
|
|
|
std::shared_ptr<monero_tx_wallet> tx = std::static_pointer_cast<monero_tx_wallet>(monero_utils::cn_tx_to_tx(cn_tx, true));
|
|
|
|
|
block->m_txs.push_back(tx);
|
|
|
|
|
tx->m_block = block;
|
|
|
|
|
tx->m_hash = epee::string_tools::pod_to_hex(txid);
|
|
|
|
|
tx->m_is_confirmed = true;
|
|
|
|
|
tx->m_is_locked = true;
|
|
|
|
|
tx->m_unlock_height = unlock_height;
|
|
|
|
|
std::shared_ptr<monero_output_wallet> output = std::make_shared<monero_output_wallet>();
|
|
|
|
|
tx->m_outputs.push_back(output);
|
|
|
|
|
output->m_tx = tx;
|
|
|
|
|
output->m_amount = amount - burnt;
|
|
|
|
|
output->m_account_index = subaddr_index.major;
|
|
|
|
|
output->m_subaddress_index = subaddr_index.minor;
|
|
|
|
|
|
|
|
|
|
// notify listeners of output
|
|
|
|
|
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
|
|
|
|
listener->on_output_received(*output);
|
|
|
|
|
// notify listeners of output
|
|
|
|
|
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
|
|
|
|
listener->on_output_received(*output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// watch for unlock
|
|
|
|
|
m_prev_locked_tx_hashes.insert(tx->m_hash.get());
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// watch for unlock
|
|
|
|
|
m_prev_locked_tx_hashes.insert(tx->m_hash.get());
|
|
|
|
|
|
|
|
|
|
// free memory
|
|
|
|
|
monero_utils::free(block);
|
|
|
|
|
output.reset();
|
|
|
|
|
tx.reset();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -861,35 +870,39 @@ namespace monero {
|
|
|
|
|
// queue notification processing off main thread
|
|
|
|
|
tools::threadpool::waiter waiter(*m_notification_pool);
|
|
|
|
|
m_notification_pool->submit(&waiter, [this, height, txid, cn_tx_in, amount, cn_tx_out, subaddr_index]() {
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
// create native library tx
|
|
|
|
|
std::shared_ptr<monero_block> block = std::make_shared<monero_block>();
|
|
|
|
|
block->m_height = height;
|
|
|
|
|
std::shared_ptr<monero_tx_wallet> tx = std::static_pointer_cast<monero_tx_wallet>(monero_utils::cn_tx_to_tx(cn_tx_in, true));
|
|
|
|
|
block->m_txs.push_back(tx);
|
|
|
|
|
tx->m_block = block;
|
|
|
|
|
tx->m_hash = epee::string_tools::pod_to_hex(txid);
|
|
|
|
|
tx->m_is_confirmed = true;
|
|
|
|
|
tx->m_is_locked = true;
|
|
|
|
|
std::shared_ptr<monero_output_wallet> output = std::make_shared<monero_output_wallet>();
|
|
|
|
|
tx->m_inputs.push_back(output);
|
|
|
|
|
output->m_tx = tx;
|
|
|
|
|
output->m_amount = amount;
|
|
|
|
|
output->m_account_index = subaddr_index.major;
|
|
|
|
|
output->m_subaddress_index = subaddr_index.minor;
|
|
|
|
|
// create native library tx
|
|
|
|
|
std::shared_ptr<monero_block> block = std::make_shared<monero_block>();
|
|
|
|
|
block->m_height = height;
|
|
|
|
|
std::shared_ptr<monero_tx_wallet> tx = std::static_pointer_cast<monero_tx_wallet>(monero_utils::cn_tx_to_tx(cn_tx_in, true));
|
|
|
|
|
block->m_txs.push_back(tx);
|
|
|
|
|
tx->m_block = block;
|
|
|
|
|
tx->m_hash = epee::string_tools::pod_to_hex(txid);
|
|
|
|
|
tx->m_is_confirmed = true;
|
|
|
|
|
tx->m_is_locked = true;
|
|
|
|
|
std::shared_ptr<monero_output_wallet> output = std::make_shared<monero_output_wallet>();
|
|
|
|
|
tx->m_inputs.push_back(output);
|
|
|
|
|
output->m_tx = tx;
|
|
|
|
|
output->m_amount = amount;
|
|
|
|
|
output->m_account_index = subaddr_index.major;
|
|
|
|
|
output->m_subaddress_index = subaddr_index.minor;
|
|
|
|
|
|
|
|
|
|
// notify listeners of output
|
|
|
|
|
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
|
|
|
|
listener->on_output_spent(*output);
|
|
|
|
|
// notify listeners of output
|
|
|
|
|
for (monero_wallet_listener* listener : m_wallet.get_listeners()) {
|
|
|
|
|
listener->on_output_spent(*output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// watch for unlock
|
|
|
|
|
m_prev_locked_tx_hashes.insert(tx->m_hash.get());
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// watch for unlock
|
|
|
|
|
m_prev_locked_tx_hashes.insert(tx->m_hash.get());
|
|
|
|
|
|
|
|
|
|
// free memory
|
|
|
|
|
monero_utils::free(block);
|
|
|
|
|
output.reset();
|
|
|
|
|
tx.reset();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -917,6 +930,7 @@ 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;
|
|
|
|
|
boost::mutex m_listener_mutex;
|
|
|
|
|
uint64_t m_prev_balance;
|
|
|
|
|
uint64_t m_prev_unlocked_balance;
|
|
|
|
|
std::set<std::string> m_prev_locked_tx_hashes;
|
|
|
|
@@ -1326,7 +1340,7 @@ namespace monero {
|
|
|
|
|
|
|
|
|
|
// validate standard address
|
|
|
|
|
cryptonote::address_parse_info info;
|
|
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_w2->nettype(), standard_address)) throw std::runtime_error("Invalid address: " + standard_address);
|
|
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_w2->nettype(), standard_address)) throw std::runtime_error("Invalid address");
|
|
|
|
|
if (info.is_subaddress) throw std::runtime_error("Subaddress shouldn't be used");
|
|
|
|
|
if (info.has_payment_id) throw std::runtime_error("Already integrated address");
|
|
|
|
|
if (payment_id.empty()) throw std::runtime_error("Payment ID shouldn't be left unspecified");
|
|
|
|
@@ -1341,7 +1355,7 @@ namespace monero {
|
|
|
|
|
|
|
|
|
|
// validate integrated address
|
|
|
|
|
cryptonote::address_parse_info info;
|
|
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_w2->nettype(), integrated_address)) throw std::runtime_error("Invalid integrated address: " + integrated_address);
|
|
|
|
|
if (!cryptonote::get_account_address_from_str(info, m_w2->nettype(), integrated_address)) throw std::runtime_error("Invalid address");
|
|
|
|
|
if (!info.has_payment_id) throw std::runtime_error("Address is not an integrated address");
|
|
|
|
|
|
|
|
|
|
// initialize and return result
|
|
|
|
@@ -1942,7 +1956,6 @@ namespace monero {
|
|
|
|
|
if (tx->m_is_relayed.get()) tx->m_last_relayed_timestamp = static_cast<uint64_t>(time(NULL)); // set last relayed timestamp to current time iff relayed // TODO monero-project: this should be encapsulated in wallet2
|
|
|
|
|
out_transfer->m_account_index = config.m_account_index;
|
|
|
|
|
if (config.m_subaddress_indices.size() == 1) out_transfer->m_subaddress_indices.push_back(config.m_subaddress_indices[0]); // subaddress index is known iff 1 requested // TODO: get all known subaddress indices here
|
|
|
|
|
out_transfer->m_destinations = config.get_normalized_destinations();
|
|
|
|
|
|
|
|
|
|
// iterate to next element
|
|
|
|
|
tx_keys_iter++;
|
|
|
|
@@ -1954,6 +1967,9 @@ namespace monero {
|
|
|
|
|
input_key_images_list_iter++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// copy destinations if single tx
|
|
|
|
|
if (txs.size() == 1) txs[0]->m_outgoing_transfer.get()->m_destinations = config.get_normalized_destinations();
|
|
|
|
|
|
|
|
|
|
// build tx set
|
|
|
|
|
std::shared_ptr<monero_tx_set> tx_set = std::make_shared<monero_tx_set>();
|
|
|
|
|
tx_set->m_txs = txs;
|
|
|
|
@@ -2424,8 +2440,7 @@ namespace monero {
|
|
|
|
|
bool loaded = false;
|
|
|
|
|
tools::wallet2::pending_tx ptx;
|
|
|
|
|
try {
|
|
|
|
|
std::istringstream iss(blob);
|
|
|
|
|
binary_archive<false> ar(iss);
|
|
|
|
|
binary_archive<false> ar{epee::strspan<std::uint8_t>(blob)};
|
|
|
|
|
if (::serialization::serialize(ar, ptx)) loaded = true;
|
|
|
|
|
} catch (...) {}
|
|
|
|
|
if (!loaded) {
|
|
|
|
@@ -3014,8 +3029,8 @@ namespace monero {
|
|
|
|
|
if (!m_w2->delete_address_book_row(index)) throw std::runtime_error("Failed to delete address book entry");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string monero_wallet_full::create_payment_uri(const monero_tx_config& config) const {
|
|
|
|
|
MTRACE("create_payment_uri()");
|
|
|
|
|
std::string monero_wallet_full::get_payment_uri(const monero_tx_config& config) const {
|
|
|
|
|
MTRACE("get_payment_uri()");
|
|
|
|
|
|
|
|
|
|
// validate config
|
|
|
|
|
std::vector<std::shared_ptr<monero_destination>> destinations = config.get_normalized_destinations();
|
|
|
|
@@ -3158,17 +3173,15 @@ namespace monero {
|
|
|
|
|
std::string monero_wallet_full::prepare_multisig() {
|
|
|
|
|
if (m_w2->multisig()) throw std::runtime_error("This wallet is already multisig");
|
|
|
|
|
if (m_w2->watch_only()) throw std::runtime_error("This wallet is view-only and cannot be made multisig");
|
|
|
|
|
m_w2->enable_multisig(true);
|
|
|
|
|
return m_w2->get_multisig_first_kex_msg();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
monero_multisig_init_result monero_wallet_full::make_multisig(const std::vector<std::string>& multisig_hexes, int threshold, const std::string& password) {
|
|
|
|
|
std::string monero_wallet_full::make_multisig(const std::vector<std::string>& multisig_hexes, int threshold, const std::string& password) {
|
|
|
|
|
if (m_w2->multisig()) throw std::runtime_error("This wallet is already multisig");
|
|
|
|
|
if (m_w2->watch_only()) throw std::runtime_error("This wallet is view-only and cannot be made multisig");
|
|
|
|
|
boost::lock_guard<boost::mutex> guarg(m_sync_mutex); // do not refresh while making multisig
|
|
|
|
|
monero_multisig_init_result result;
|
|
|
|
|
result.m_multisig_hex = m_w2->make_multisig(epee::wipeable_string(password), multisig_hexes, threshold);
|
|
|
|
|
result.m_address = m_w2->get_account().get_public_address_str(m_w2->nettype());
|
|
|
|
|
return result;
|
|
|
|
|
return m_w2->make_multisig(epee::wipeable_string(password), multisig_hexes, threshold);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
monero_multisig_init_result monero_wallet_full::exchange_multisig_keys(const std::vector<std::string>& multisig_hexes, const std::string& password) {
|
|
|
|
@@ -3178,7 +3191,7 @@ namespace monero {
|
|
|
|
|
uint32_t threshold, total;
|
|
|
|
|
if (!m_w2->multisig(&ready, &threshold, &total)) throw std::runtime_error("This wallet is not multisig");
|
|
|
|
|
if (ready) throw std::runtime_error("This wallet is multisig, and already finalized");
|
|
|
|
|
if (multisig_hexes.size() < 1 || multisig_hexes.size() > total) throw std::runtime_error("Needs multisig info from more participants");
|
|
|
|
|
if (multisig_hexes.size() + 1 < total) throw std::runtime_error("Needs multisig info from more participants");
|
|
|
|
|
|
|
|
|
|
// do not refresh while exchanging multisig keys
|
|
|
|
|
boost::lock_guard<boost::mutex> guarg(m_sync_mutex);
|
|
|
|
@@ -3186,10 +3199,11 @@ namespace monero {
|
|
|
|
|
// import peer multisig keys and get multisig hex to be shared next round
|
|
|
|
|
std::string multisig_hex = m_w2->exchange_multisig_keys(epee::wipeable_string(password), multisig_hexes);
|
|
|
|
|
|
|
|
|
|
// build and return the exchange result
|
|
|
|
|
// build and return exchange result
|
|
|
|
|
monero_multisig_init_result result;
|
|
|
|
|
if (!multisig_hex.empty()) result.m_multisig_hex = multisig_hex;
|
|
|
|
|
else result.m_address = m_w2->get_account().get_public_address_str(m_w2->nettype()); // only return address on completion
|
|
|
|
|
m_w2->multisig(&ready);
|
|
|
|
|
result.m_multisig_hex = multisig_hex;
|
|
|
|
|
if (ready) result.m_address = m_w2->get_account().get_public_address_str(m_w2->nettype());
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|