Merge pull request #8760

1698cb1 remove defunct light wallet code (tobtoht)
This commit is contained in:
luigi1111 2023-04-26 14:02:25 -04:00
commit a2e8d1d427
No known key found for this signature in database
GPG Key ID: F4ACA0183641E010
6 changed files with 21 additions and 1185 deletions

View File

@ -209,38 +209,6 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
// TODO; // TODO;
} }
// Light wallet callbacks
virtual void on_lw_new_block(uint64_t height)
{
if (m_listener) {
m_listener->newBlock(height);
}
}
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount)
{
if (m_listener) {
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
m_listener->moneyReceived(tx_hash, amount);
}
}
virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount)
{
if (m_listener) {
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
m_listener->unconfirmedMoneyReceived(tx_hash, amount);
}
}
virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount)
{
if (m_listener) {
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
m_listener->moneySpent(tx_hash, amount);
}
}
virtual void on_device_button_request(uint64_t code) virtual void on_device_button_request(uint64_t code)
{ {
if (m_listener) { if (m_listener) {
@ -950,42 +918,11 @@ string WalletImpl::keysFilename() const
bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl, bool lightWallet, const std::string &proxy_address) bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl, bool lightWallet, const std::string &proxy_address)
{ {
clearStatus(); clearStatus();
m_wallet->set_light_wallet(lightWallet);
if(daemon_username != "") if(daemon_username != "")
m_daemon_login.emplace(daemon_username, daemon_password); m_daemon_login.emplace(daemon_username, daemon_password);
return doInit(daemon_address, proxy_address, upper_transaction_size_limit, use_ssl); return doInit(daemon_address, proxy_address, upper_transaction_size_limit, use_ssl);
} }
bool WalletImpl::lightWalletLogin(bool &isNewWallet) const
{
return m_wallet->light_wallet_login(isNewWallet);
}
bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status)
{
try
{
tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response response;
if(!m_wallet->light_wallet_import_wallet_request(response)){
setStatusError(tr("Failed to send import wallet request"));
return false;
}
fee = response.import_fee;
payment_id = response.payment_id;
new_request = response.new_request;
request_fulfilled = response.request_fulfilled;
payment_address = response.payment_address;
status = response.status;
}
catch (const std::exception &e)
{
LOG_ERROR("Error sending import wallet request: " << e.what());
setStatusError(e.what());
return false;
}
return true;
}
void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height)
{ {
m_wallet->set_refresh_from_block_height(refresh_from_block_height); m_wallet->set_refresh_from_block_height(refresh_from_block_height);
@ -1018,9 +955,6 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const
uint64_t WalletImpl::blockChainHeight() const uint64_t WalletImpl::blockChainHeight() const
{ {
if(m_wallet->light_wallet()) {
return m_wallet->get_light_wallet_scanned_block_height();
}
return m_wallet->get_blockchain_current_height(); return m_wallet->get_blockchain_current_height();
} }
uint64_t WalletImpl::approximateBlockChainHeight() const uint64_t WalletImpl::approximateBlockChainHeight() const
@ -1035,9 +969,6 @@ uint64_t WalletImpl::estimateBlockChainHeight() const
uint64_t WalletImpl::daemonBlockChainHeight() const uint64_t WalletImpl::daemonBlockChainHeight() const
{ {
if(m_wallet->light_wallet()) {
return m_wallet->get_light_wallet_scanned_block_height();
}
if (!m_is_connected) if (!m_is_connected)
return 0; return 0;
std::string err; std::string err;
@ -1054,9 +985,6 @@ uint64_t WalletImpl::daemonBlockChainHeight() const
uint64_t WalletImpl::daemonBlockChainTargetHeight() const uint64_t WalletImpl::daemonBlockChainTargetHeight() const
{ {
if(m_wallet->light_wallet()) {
return m_wallet->get_light_wallet_blockchain_height();
}
if (!m_is_connected) if (!m_is_connected)
return 0; return 0;
std::string err; std::string err;
@ -2177,13 +2105,12 @@ Wallet::ConnectionStatus WalletImpl::connected() const
m_is_connected = m_wallet->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS, &wallet_is_outdated, &daemon_is_outdated); m_is_connected = m_wallet->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS, &wallet_is_outdated, &daemon_is_outdated);
if (!m_is_connected) if (!m_is_connected)
{ {
if (!m_wallet->light_wallet() && (wallet_is_outdated || daemon_is_outdated)) if (wallet_is_outdated || daemon_is_outdated)
return Wallet::ConnectionStatus_WrongVersion; return Wallet::ConnectionStatus_WrongVersion;
else else
return Wallet::ConnectionStatus_Disconnected; return Wallet::ConnectionStatus_Disconnected;
} }
// Version check is not implemented in light wallets nodes/wallets if ((version >> 16) != CORE_RPC_VERSION_MAJOR)
if (!m_wallet->light_wallet() && (version >> 16) != CORE_RPC_VERSION_MAJOR)
return Wallet::ConnectionStatus_WrongVersion; return Wallet::ConnectionStatus_WrongVersion;
return Wallet::ConnectionStatus_Connected; return Wallet::ConnectionStatus_Connected;
} }
@ -2277,7 +2204,7 @@ void WalletImpl::doRefresh()
LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<<rescan); LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<<rescan);
// Syncing daemon and refreshing wallet simultaneously is very resource intensive. // Syncing daemon and refreshing wallet simultaneously is very resource intensive.
// Disable refresh if wallet is disconnected or daemon isn't synced. // Disable refresh if wallet is disconnected or daemon isn't synced.
if (m_wallet->light_wallet() || daemonSynced()) { if (daemonSynced()) {
if(rescan) if(rescan)
m_wallet->rescan_blockchain(false); m_wallet->rescan_blockchain(false);
m_wallet->refresh(trustedDaemon()); m_wallet->refresh(trustedDaemon());
@ -2368,7 +2295,6 @@ bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_a
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
// If daemon isn't synced a calculated block height will be used instead // If daemon isn't synced a calculated block height will be used instead
//TODO: Handle light wallet scenario where block height = 0.
if (isNewWallet() && daemonSynced()) { if (isNewWallet() && daemonSynced()) {
LOG_PRINT_L2(__FUNCTION__ << ":New Wallet - fast refresh until " << daemonBlockChainHeight()); LOG_PRINT_L2(__FUNCTION__ << ":New Wallet - fast refresh until " << daemonBlockChainHeight());
m_wallet->set_refresh_from_block_height(daemonBlockChainHeight()); m_wallet->set_refresh_from_block_height(daemonBlockChainHeight());

View File

@ -208,8 +208,6 @@ public:
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) override; virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) override;
virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const override; virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const override;
virtual std::string getDefaultDataDir() const override; virtual std::string getDefaultDataDir() const override;
virtual bool lightWalletLogin(bool &isNewWallet) const override;
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) override;
virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) override; virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) override;
virtual bool blackballOutput(const std::string &amount, const std::string &offset) override; virtual bool blackballOutput(const std::string &amount, const std::string &offset) override;
virtual bool unblackballOutput(const std::string &amount, const std::string &offset) override; virtual bool unblackballOutput(const std::string &amount, const std::string &offset) override;

View File

@ -541,7 +541,7 @@ struct Wallet
* \param upper_transaction_size_limit * \param upper_transaction_size_limit
* \param daemon_username * \param daemon_username
* \param daemon_password * \param daemon_password
* \param lightWallet - start wallet in light mode, connect to a openmonero compatible server. * \param lightWallet - deprecated
* \param proxy_address - set proxy address, empty string to disable * \param proxy_address - set proxy address, empty string to disable
* \return - true on success * \return - true on success
*/ */
@ -1065,12 +1065,6 @@ struct Wallet
//! secondary key reuse mitigation //! secondary key reuse mitigation
virtual void keyReuseMitigation2(bool mitigation) = 0; virtual void keyReuseMitigation2(bool mitigation) = 0;
//! Light wallet authenticate and login
virtual bool lightWalletLogin(bool &isNewWallet) const = 0;
//! Initiates a light wallet import wallet request
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0;
//! locks/unlocks the keys file; returns true on success //! locks/unlocks the keys file; returns true on success
virtual bool lockKeysFile() = 0; virtual bool lockKeysFile() = 0;
virtual bool unlockKeysFile() = 0; virtual bool unlockKeysFile() = 0;

View File

@ -1198,12 +1198,6 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_account_public_address{crypto::null_pkey, crypto::null_pkey}, m_account_public_address{crypto::null_pkey, crypto::null_pkey},
m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR), m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR), m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
m_light_wallet(false),
m_light_wallet_scanned_block_height(0),
m_light_wallet_blockchain_height(0),
m_light_wallet_connected(false),
m_light_wallet_balance(0),
m_light_wallet_unlocked_balance(0),
m_original_keys_available(false), m_original_keys_available(false),
m_message_store(http_client_factory->create()), m_message_store(http_client_factory->create()),
m_key_device_type(hw::device::device_type::SOFTWARE), m_key_device_type(hw::device::device_type::SOFTWARE),
@ -3605,38 +3599,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
return; return;
} }
if(m_light_wallet) {
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
// This call is not really needed for other purposes and can be removed if mymonero changes their backend.
tools::COMMAND_RPC_GET_ADDRESS_INFO::response res;
// Get basic info
if(light_wallet_get_address_info(res)) {
// Last stored block height
uint64_t prev_height = m_light_wallet_blockchain_height;
// Update lw heights
m_light_wallet_scanned_block_height = res.scanned_block_height;
m_light_wallet_blockchain_height = res.blockchain_height;
// If new height - call new_block callback
if(m_light_wallet_blockchain_height != prev_height)
{
MDEBUG("new block since last time!");
m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1);
}
m_light_wallet_connected = true;
MDEBUG("lw scanned block height: " << m_light_wallet_scanned_block_height);
MDEBUG("lw blockchain height: " << m_light_wallet_blockchain_height);
MDEBUG(m_light_wallet_blockchain_height-m_light_wallet_scanned_block_height << " blocks behind");
// TODO: add wallet created block info
light_wallet_get_address_txs();
} else
m_light_wallet_connected = false;
// Lighwallet refresh done
return;
}
received_money = false; received_money = false;
blocks_fetched = 0; blocks_fetched = 0;
uint64_t added_blocks = 0; uint64_t added_blocks = 0;
@ -5576,16 +5538,6 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout, b
return false; return false;
} }
// TODO: Add light wallet version check.
if(m_light_wallet) {
m_rpc_version = 0;
if (version)
*version = 0;
if (ssl)
*ssl = m_light_wallet_connected; // light wallet is always SSL
return m_light_wallet_connected;
}
{ {
boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
if(!m_http_client->is_connected(ssl)) if(!m_http_client->is_connected(ssl))
@ -6111,8 +6063,6 @@ boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data(const epe
uint64_t wallet2::balance(uint32_t index_major, bool strict) const uint64_t wallet2::balance(uint32_t index_major, bool strict) const
{ {
uint64_t amount = 0; uint64_t amount = 0;
if(m_light_wallet)
return m_light_wallet_balance;
for (const auto& i : balance_per_subaddress(index_major, strict)) for (const auto& i : balance_per_subaddress(index_major, strict))
amount += i.second; amount += i.second;
return amount; return amount;
@ -6125,8 +6075,6 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t *
*blocks_to_unlock = 0; *blocks_to_unlock = 0;
if (time_to_unlock) if (time_to_unlock)
*time_to_unlock = 0; *time_to_unlock = 0;
if(m_light_wallet)
return m_light_wallet_unlocked_balance;
for (const auto& i : unlocked_balance_per_subaddress(index_major, strict)) for (const auto& i : unlocked_balance_per_subaddress(index_major, strict))
{ {
amount += i.second.first; amount += i.second.first;
@ -6661,43 +6609,25 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
void wallet2::commit_tx(pending_tx& ptx) void wallet2::commit_tx(pending_tx& ptx)
{ {
using namespace cryptonote; using namespace cryptonote;
if(m_light_wallet) // Normal submit
COMMAND_RPC_SEND_RAW_TX::request req;
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
req.do_not_relay = false;
req.do_sanity_checks = true;
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
{ {
cryptonote::COMMAND_RPC_SUBMIT_RAW_TX::request oreq; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
cryptonote::COMMAND_RPC_SUBMIT_RAW_TX::response ores; bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, *m_http_client, rpc_timeout);
oreq.address = get_account().get_public_address_str(m_nettype); THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
{
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, *m_http_client, rpc_timeout, "POST");
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx");
// MyMonero and OpenMonero use different status strings
THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error);
}
} }
else
// sanity checks
for (size_t idx: ptx.selected_transfers)
{ {
// Normal submit THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
COMMAND_RPC_SEND_RAW_TX::request req; "Bad output index in selected transfers: " + boost::lexical_cast<std::string>(idx));
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
req.do_not_relay = false;
req.do_sanity_checks = true;
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
{
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
}
// sanity checks
for (size_t idx: ptx.selected_transfers)
{
THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error,
"Bad output index in selected transfers: " + boost::lexical_cast<std::string>(idx));
}
} }
crypto::hash txid; crypto::hash txid;
@ -7670,13 +7600,6 @@ uint64_t wallet2::get_dynamic_base_fee_estimate()
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_base_fee() uint64_t wallet2::get_base_fee()
{ {
if(m_light_wallet)
{
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
return m_light_wallet_per_kb_fee / 1024;
else
return m_light_wallet_per_kb_fee;
}
bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -30 * 1); bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -30 * 1);
if (!use_dyn_fee) if (!use_dyn_fee)
return FEE_PER_KB; return FEE_PER_KB;
@ -7720,10 +7643,6 @@ uint64_t wallet2::get_base_fee(uint32_t priority)
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_fee_quantization_mask() uint64_t wallet2::get_fee_quantization_mask()
{ {
if(m_light_wallet)
{
return 1; // TODO
}
bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0); bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
if (!use_per_byte_fee) if (!use_per_byte_fee)
return 1; return 1;
@ -8198,113 +8117,6 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
return true; return true;
} }
void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count) {
MDEBUG("LIGHTWALLET - Getting random outs");
tools::COMMAND_RPC_GET_RANDOM_OUTS::request oreq;
tools::COMMAND_RPC_GET_RANDOM_OUTS::response ores;
size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
// Amounts to ask for
// MyMonero api handle amounts and fees as strings
for(size_t idx: selected_transfers) {
const uint64_t ask_amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount();
std::ostringstream amount_ss;
amount_ss << ask_amount;
oreq.amounts.push_back(amount_ss.str());
}
oreq.count = light_wallet_requested_outputs_count;
{
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, *m_http_client, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs");
THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
}
// Check if we got enough outputs for each amount
for(auto& out: ores.amount_outs) {
THROW_WALLET_EXCEPTION_IF(out.outputs.size() < light_wallet_requested_outputs_count , error::wallet_internal_error, "Not enough outputs for amount: " + boost::lexical_cast<std::string>(out.amount));
MDEBUG(out.outputs.size() << " outputs for amount "+ boost::lexical_cast<std::string>(out.amount) + " received from light wallet node");
}
MDEBUG("selected transfers size: " << selected_transfers.size());
std::unordered_set<crypto::public_key> valid_public_keys_cache;
for(size_t idx: selected_transfers)
{
// Create new index
outs.push_back(std::vector<get_outs_entry>());
outs.back().reserve(fake_outputs_count + 1);
// add real output first
const transfer_details &td = m_transfers[idx];
const uint64_t amount = td.is_rct() ? 0 : td.amount();
outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), rct::commit(td.amount(), td.m_mask)));
MDEBUG("added real output " << string_tools::pod_to_hex(td.get_public_key()));
// Even if the lightwallet server returns random outputs, we pick them randomly.
std::vector<size_t> order;
order.resize(light_wallet_requested_outputs_count);
for (size_t n = 0; n < order.size(); ++n)
order[n] = n;
std::shuffle(order.begin(), order.end(), crypto::random_device{});
LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs with amounts " << print_money(td.is_rct() ? 0 : td.amount()));
MDEBUG("OUTS SIZE: " << outs.back().size());
for (size_t o = 0; o < light_wallet_requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
{
// Random pick
size_t i = order[o];
// Find which random output key to use
bool found_amount = false;
size_t amount_key;
for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key)
{
if(boost::lexical_cast<uint64_t>(ores.amount_outs[amount_key].amount) == amount) {
found_amount = true;
break;
}
}
THROW_WALLET_EXCEPTION_IF(!found_amount , error::wallet_internal_error, "Outputs for amount " + boost::lexical_cast<std::string>(ores.amount_outs[amount_key].amount) + " not found" );
LOG_PRINT_L2("Index " << i << "/" << light_wallet_requested_outputs_count << ": idx " << ores.amount_outs[amount_key].outputs[i].global_index << " (real " << td.m_global_output_index << "), unlocked " << "(always in light)" << ", key " << ores.amount_outs[0].outputs[i].public_key);
// Convert light wallet string data to proper data structures
crypto::public_key tx_public_key;
rct::key mask = AUTO_VAL_INIT(mask); // decrypted mask - not used here
rct::key rct_commit = AUTO_VAL_INIT(rct_commit);
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ores.amount_outs[amount_key].outputs[i].public_key), error::wallet_internal_error, "Invalid public_key");
string_tools::hex_to_pod(ores.amount_outs[amount_key].outputs[i].public_key, tx_public_key);
const uint64_t global_index = ores.amount_outs[amount_key].outputs[i].global_index;
if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false))
rct_commit = rct::zeroCommit(td.amount());
if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true, valid_public_keys_cache)) {
MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key);
MDEBUG("index " << global_index);
}
}
THROW_WALLET_EXCEPTION_IF(outs.back().size() < fake_outputs_count + 1 , error::wallet_internal_error, "Not enough fake outputs found" );
// Real output is the first. Shuffle outputs
MTRACE(outs.back().size() << " outputs added. Sorting outputs by index:");
std::sort(outs.back().begin(), outs.back().end(), [](const get_outs_entry &a, const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); });
// Print output order
for(auto added_out: outs.back())
MTRACE(std::get<0>(added_out));
}
}
std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs) std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs)
{ {
std::set<uint64_t> unique; std::set<uint64_t> unique;
@ -8355,11 +8167,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
outs.clear(); outs.clear();
if(m_light_wallet && fake_outputs_count > 0) {
light_wallet_get_outs(outs, selected_transfers, fake_outputs_count);
return;
}
if (fake_outputs_count > 0) if (fake_outputs_count > 0)
{ {
uint64_t segregation_fork_height = get_segregation_fork_height(); uint64_t segregation_fork_height = get_segregation_fork_height();
@ -9619,476 +9426,6 @@ static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &tr
return count; return count;
} }
bool wallet2::light_wallet_login(bool &new_address)
{
MDEBUG("Light wallet login request");
m_light_wallet_connected = false;
tools::COMMAND_RPC_LOGIN::request request;
tools::COMMAND_RPC_LOGIN::response response;
request.address = get_account().get_public_address_str(m_nettype);
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
// Always create account if it doesn't exist.
request.create_account = true;
m_daemon_rpc_mutex.lock();
bool connected = invoke_http_json("/login", request, response, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
// MyMonero doesn't send any status message. OpenMonero does.
m_light_wallet_connected = connected && (response.status.empty() || response.status == "success");
new_address = response.new_address;
MDEBUG("Status: " << response.status);
MDEBUG("Reason: " << response.reason);
MDEBUG("New wallet: " << response.new_address);
if(m_light_wallet_connected)
{
// Clear old data on successful login.
// m_transfers.clear();
// m_payments.clear();
// m_unconfirmed_payments.clear();
}
return m_light_wallet_connected;
}
bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
{
MDEBUG("Light wallet import wallet request");
tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq;
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
bool r = invoke_http_json("/import_wallet_request", oreq, response, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request");
return true;
}
void wallet2::light_wallet_get_unspent_outs()
{
MDEBUG("Getting unspent outs");
tools::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq;
tools::COMMAND_RPC_GET_UNSPENT_OUTS::response ores;
oreq.amount = "0";
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
// openMonero specific
oreq.dust_threshold = boost::lexical_cast<std::string>(::config::DEFAULT_DUST_THRESHOLD);
// below are required by openMonero api - but are not used.
oreq.mixin = 0;
oreq.use_dust = true;
m_daemon_rpc_mutex.lock();
bool r = invoke_http_json("/get_unspent_outs", oreq, ores, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_unspent_outs");
THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason);
m_light_wallet_per_kb_fee = ores.per_kb_fee;
std::unordered_map<crypto::hash,bool> transfers_txs;
for(const auto &t: m_transfers)
transfers_txs.emplace(t.m_txid,t.m_spent);
MDEBUG("FOUND " << ores.outputs.size() <<" outputs");
// return if no outputs found
if(ores.outputs.empty())
return;
// Clear old outputs
m_transfers.clear();
for (const auto &o: ores.outputs) {
bool spent = false;
bool add_transfer = true;
crypto::key_image unspent_key_image;
crypto::public_key tx_public_key = AUTO_VAL_INIT(tx_public_key);
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
string_tools::hex_to_pod(o.tx_pub_key, tx_public_key);
for (const std::string &ski: o.spend_key_images) {
spent = false;
// Check if key image is ours
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ski), error::wallet_internal_error, "Invalid key image");
string_tools::hex_to_pod(ski, unspent_key_image);
if(light_wallet_key_image_is_ours(unspent_key_image, tx_public_key, o.index)){
MTRACE("Output " << o.public_key << " is spent. Key image: " << ski);
spent = true;
break;
} {
MTRACE("Unspent output found. " << o.public_key);
}
}
// Check if tx already exists in m_transfers.
crypto::hash txid;
crypto::public_key tx_pub_key;
crypto::public_key public_key;
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_hash), error::wallet_internal_error, "Invalid tx_hash field");
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.public_key), error::wallet_internal_error, "Invalid public_key field");
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
string_tools::hex_to_pod(o.tx_hash, txid);
string_tools::hex_to_pod(o.public_key, public_key);
string_tools::hex_to_pod(o.tx_pub_key, tx_pub_key);
for(auto &t: m_transfers){
if(t.get_public_key() == public_key) {
t.m_spent = spent;
add_transfer = false;
break;
}
}
if(!add_transfer)
continue;
m_transfers.push_back(transfer_details{});
transfer_details& td = m_transfers.back();
td.m_block_height = o.height;
td.m_global_output_index = o.global_index;
td.m_txid = txid;
// Add to extra
add_tx_pub_key_to_extra(td.m_tx, tx_pub_key);
td.m_key_image = unspent_key_image;
td.m_key_image_known = !m_watch_only && !m_multisig;
td.m_key_image_request = false;
td.m_key_image_partial = m_multisig;
td.m_amount = o.amount;
td.m_pk_index = 0;
td.m_internal_output_index = o.index;
td.m_spent = spent;
td.m_frozen = false;
tx_out txout;
txout.target = txout_to_key(public_key);
txout.amount = td.m_amount;
td.m_tx.vout.resize(td.m_internal_output_index + 1);
td.m_tx.vout[td.m_internal_output_index] = txout;
// Add unlock time and coinbase bool got from get_address_txs api call
std::unordered_map<crypto::hash,address_tx>::const_iterator found = m_light_wallet_address_txs.find(txid);
THROW_WALLET_EXCEPTION_IF(found == m_light_wallet_address_txs.end(), error::wallet_internal_error, "Lightwallet: tx not found in m_light_wallet_address_txs");
bool miner_tx = found->second.m_coinbase;
td.m_tx.unlock_time = found->second.m_unlock_time;
if (!o.rct.empty())
{
// Coinbase tx's
if(miner_tx)
{
td.m_mask = rct::identity();
}
else
{
// rct txs
// decrypt rct mask, calculate commit hash and compare against blockchain commit hash
rct::key rct_commit;
light_wallet_parse_rct_str(o.rct, tx_pub_key, td.m_internal_output_index, td.m_mask, rct_commit, true);
bool valid_commit = (rct_commit == rct::commit(td.amount(), td.m_mask));
if(!valid_commit)
{
MDEBUG("output index: " << o.global_index);
MDEBUG("mask: " + string_tools::pod_to_hex(td.m_mask));
MDEBUG("calculated commit: " + string_tools::pod_to_hex(rct::commit(td.amount(), td.m_mask)));
MDEBUG("expected commit: " + string_tools::pod_to_hex(rct_commit));
MDEBUG("amount: " << td.amount());
}
THROW_WALLET_EXCEPTION_IF(!valid_commit, error::wallet_internal_error, "Lightwallet: rct commit hash mismatch!");
}
td.m_rct = true;
}
else
{
td.m_mask = rct::identity();
td.m_rct = false;
}
if(!spent)
set_unspent(m_transfers.size()-1);
m_key_images[td.m_key_image] = m_transfers.size()-1;
m_pub_keys[td.get_public_key()] = m_transfers.size()-1;
}
}
bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
{
MTRACE(__FUNCTION__);
tools::COMMAND_RPC_GET_ADDRESS_INFO::request request;
request.address = get_account().get_public_address_str(m_nettype);
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
bool r = invoke_http_json("/get_address_info", request, response, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_info");
// TODO: Validate result
return true;
}
void wallet2::light_wallet_get_address_txs()
{
MDEBUG("Refreshing light wallet");
tools::COMMAND_RPC_GET_ADDRESS_TXS::request ireq;
tools::COMMAND_RPC_GET_ADDRESS_TXS::response ires;
ireq.address = get_account().get_public_address_str(m_nettype);
ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
bool r = invoke_http_json("/get_address_txs", ireq, ires, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_txs");
//OpenMonero sends status=success, Mymonero doesn't.
THROW_WALLET_EXCEPTION_IF((!ires.status.empty() && ires.status != "success"), error::no_connection_to_daemon, "get_address_txs");
// Abort if no transactions
if(ires.transactions.empty())
return;
// Create searchable vectors
std::vector<crypto::hash> payments_txs;
for(const auto &p: m_payments)
payments_txs.push_back(p.second.m_tx_hash);
std::vector<crypto::hash> unconfirmed_payments_txs;
for(const auto &up: m_unconfirmed_payments)
unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash);
// for balance calculation
uint64_t wallet_total_sent = 0;
// txs in pool
std::vector<crypto::hash> pool_txs;
for (const auto &t: ires.transactions) {
const uint64_t total_received = t.total_received;
uint64_t total_sent = t.total_sent;
// Check key images - subtract fake outputs from total_sent
for(const auto &so: t.spent_outputs)
{
crypto::public_key tx_public_key;
crypto::key_image key_image;
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field");
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.key_image), error::wallet_internal_error, "Invalid key_image field");
string_tools::hex_to_pod(so.tx_pub_key, tx_public_key);
string_tools::hex_to_pod(so.key_image, key_image);
if(!light_wallet_key_image_is_ours(key_image, tx_public_key, so.out_index)) {
THROW_WALLET_EXCEPTION_IF(so.amount > t.total_sent, error::wallet_internal_error, "Lightwallet: total sent is negative!");
total_sent -= so.amount;
}
}
// Do not add tx if empty.
if(total_sent == 0 && total_received == 0)
continue;
crypto::hash payment_id = null_hash;
crypto::hash tx_hash;
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.payment_id), error::wallet_internal_error, "Invalid payment_id field");
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.hash), error::wallet_internal_error, "Invalid hash field");
string_tools::hex_to_pod(t.payment_id, payment_id);
string_tools::hex_to_pod(t.hash, tx_hash);
// lightwallet specific info
bool incoming = (total_received > total_sent);
address_tx address_tx;
address_tx.m_tx_hash = tx_hash;
address_tx.m_incoming = incoming;
address_tx.m_amount = incoming ? total_received - total_sent : total_sent - total_received;
address_tx.m_fee = 0; // TODO
address_tx.m_block_height = t.height;
address_tx.m_unlock_time = t.unlock_time;
address_tx.m_timestamp = t.timestamp;
address_tx.m_coinbase = t.coinbase;
address_tx.m_mempool = t.mempool;
m_light_wallet_address_txs.emplace(tx_hash,address_tx);
// populate data needed for history (m_payments, m_unconfirmed_payments, m_confirmed_txs)
// INCOMING transfers
if(total_received > total_sent) {
payment_details payment;
payment.m_tx_hash = tx_hash;
payment.m_amount = total_received - total_sent;
payment.m_fee = 0; // TODO
payment.m_block_height = t.height;
payment.m_unlock_time = t.unlock_time;
payment.m_timestamp = t.timestamp;
payment.m_coinbase = t.coinbase;
if (t.mempool) {
if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
pool_txs.push_back(tx_hash);
// assume false as we don't get that info from the light wallet server
crypto::hash payment_id;
THROW_WALLET_EXCEPTION_IF(!epee::string_tools::hex_to_pod(t.payment_id, payment_id),
error::wallet_internal_error, "Failed to parse payment id");
emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false});
if (0 != m_callback) {
m_callback->on_lw_unconfirmed_money_received(t.height, payment.m_tx_hash, payment.m_amount);
}
}
} else {
if (std::find(payments_txs.begin(), payments_txs.end(), tx_hash) == payments_txs.end()) {
m_payments.emplace(tx_hash, payment);
if (0 != m_callback) {
m_callback->on_lw_money_received(t.height, payment.m_tx_hash, payment.m_amount);
}
}
}
// Outgoing transfers
} else {
uint64_t amount_sent = total_sent - total_received;
cryptonote::transaction dummy_tx; // not used by light wallet
// increase wallet total sent
wallet_total_sent += total_sent;
if (t.mempool)
{
// Handled by add_unconfirmed_tx in commit_tx
// If sent from another wallet instance we need to add it
if(m_unconfirmed_txs.find(tx_hash) == m_unconfirmed_txs.end())
{
unconfirmed_transfer_details utd;
utd.m_amount_in = amount_sent;
utd.m_amount_out = amount_sent;
utd.m_change = 0;
utd.m_payment_id = payment_id;
utd.m_timestamp = t.timestamp;
utd.m_state = wallet2::unconfirmed_transfer_details::pending;
m_unconfirmed_txs.emplace(tx_hash,utd);
}
}
else
{
// Only add if new
auto confirmed_tx = m_confirmed_txs.find(tx_hash);
if(confirmed_tx == m_confirmed_txs.end()) {
// tx is added to m_unconfirmed_txs - move to confirmed
if(m_unconfirmed_txs.find(tx_hash) != m_unconfirmed_txs.end())
{
process_unconfirmed(tx_hash, dummy_tx, t.height);
}
// Tx sent by another wallet instance
else
{
confirmed_transfer_details ctd;
ctd.m_amount_in = amount_sent;
ctd.m_amount_out = amount_sent;
ctd.m_change = 0;
ctd.m_payment_id = payment_id;
ctd.m_block_height = t.height;
ctd.m_timestamp = t.timestamp;
m_confirmed_txs.emplace(tx_hash,ctd);
}
if (0 != m_callback)
{
m_callback->on_lw_money_spent(t.height, tx_hash, amount_sent);
}
}
// If not new - check the amount and update if necessary.
// when sending a tx to same wallet the receiving amount has to be credited
else
{
if(confirmed_tx->second.m_amount_in != amount_sent || confirmed_tx->second.m_amount_out != amount_sent)
{
MDEBUG("Adjusting amount sent/received for tx: <" + t.hash + ">. Is tx sent to own wallet? " << print_money(amount_sent) << " != " << print_money(confirmed_tx->second.m_amount_in));
confirmed_tx->second.m_amount_in = amount_sent;
confirmed_tx->second.m_amount_out = amount_sent;
confirmed_tx->second.m_change = 0;
}
}
}
}
}
// TODO: purge old unconfirmed_txs
remove_obsolete_pool_txs(pool_txs, false);
// Calculate wallet balance
m_light_wallet_balance = ires.total_received-wallet_total_sent;
// MyMonero doesn't send unlocked balance
if(ires.total_received_unlocked > 0)
m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent;
else
m_light_wallet_unlocked_balance = m_light_wallet_balance;
}
bool wallet2::light_wallet_parse_rct_str(const std::string& rct_string, const crypto::public_key& tx_pub_key, uint64_t internal_output_index, rct::key& decrypted_mask, rct::key& rct_commit, bool decrypt) const
{
// rct string is empty if output is non RCT
if (rct_string.empty())
return false;
// rct_string is a string with length 64+64+64 (<rct commit> + <encrypted mask> + <rct amount>)
rct::key encrypted_mask;
std::string rct_commit_str = rct_string.substr(0,64);
std::string encrypted_mask_str = rct_string.substr(64,64);
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, rct_commit_str), error::wallet_internal_error, "Invalid rct commit hash: " + rct_commit_str);
THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, encrypted_mask_str), error::wallet_internal_error, "Invalid rct mask: " + encrypted_mask_str);
string_tools::hex_to_pod(rct_commit_str, rct_commit);
string_tools::hex_to_pod(encrypted_mask_str, encrypted_mask);
if (decrypt) {
// Decrypt the mask
crypto::key_derivation derivation;
bool r = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
crypto::secret_key scalar;
crypto::derivation_to_scalar(derivation, internal_output_index, scalar);
sc_sub(decrypted_mask.bytes,encrypted_mask.bytes,rct::hash_to_scalar(rct::sk2rct(scalar)).bytes);
}
return true;
}
bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index)
{
// Lookup key image from cache
serializable_map<uint64_t, crypto::key_image> index_keyimage_map;
serializable_unordered_map<crypto::public_key, serializable_map<uint64_t, crypto::key_image> >::const_iterator found_pub_key = m_key_image_cache.find(tx_public_key);
if(found_pub_key != m_key_image_cache.end()) {
// pub key found. key image for index cached?
index_keyimage_map = found_pub_key->second;
std::map<uint64_t,crypto::key_image>::const_iterator index_found = index_keyimage_map.find(out_index);
if(index_found != index_keyimage_map.end())
return key_image == index_found->second;
}
// Not in cache - calculate key image
crypto::key_image calculated_key_image;
cryptonote::keypair in_ephemeral;
// Subaddresses aren't supported in mymonero/openmonero yet. Roll out the original scheme:
// compute D = a*R
// compute P = Hs(D || i)*G + B
// compute x = Hs(D || i) + b (and check if P==x*G)
// compute I = x*Hp(P)
const account_keys& ack = get_account().get_keys();
crypto::key_derivation derivation;
bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
r = crypto::derive_public_key(derivation, out_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub);
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key (" << derivation << ", " << out_index << ", " << ack.m_account_address.m_spend_public_key << ")");
crypto::derive_secret_key(derivation, out_index, ack.m_spend_secret_key, in_ephemeral.sec);
crypto::public_key out_pkey_test;
r = crypto::secret_key_to_public_key(in_ephemeral.sec, out_pkey_test);
CHECK_AND_ASSERT_MES(r, false, "failed to secret_key_to_public_key(" << in_ephemeral.sec << ")");
CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_pkey_test, false, "derived secret key doesn't match derived public key");
crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, calculated_key_image);
index_keyimage_map.emplace(out_index, calculated_key_image);
m_key_image_cache.emplace(tx_public_key, index_keyimage_map);
return key_image == calculated_key_image;
}
// Another implementation of transaction creation that is hopefully better // Another implementation of transaction creation that is hopefully better
// While there is anything left to pay, it goes through random outputs and tries // While there is anything left to pay, it goes through random outputs and tries
// to fill the next destination/amount. If it fully fills it, it will use the // to fill the next destination/amount. If it fully fills it, it will use the
@ -10113,10 +9450,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
auto original_dsts = dsts; auto original_dsts = dsts;
if(m_light_wallet) {
// Populate m_transfers
light_wallet_get_unspent_outs();
}
std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_transfers_indices_per_subaddr; std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_transfers_indices_per_subaddr;
std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_dust_indices_per_subaddr; std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_dust_indices_per_subaddr;
uint64_t needed_money; uint64_t needed_money;
@ -11179,9 +10512,6 @@ void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height)
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks)
{ {
// TODO: How to get fork rule info from light wallet node?
if(m_light_wallet)
return true;
uint64_t height, earliest_height; uint64_t height, earliest_height;
boost::optional<std::string> result = m_node_rpc_proxy.get_height(height); boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get height"); THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get height");

View File

@ -70,7 +70,6 @@
#include "common/password.h" #include "common/password.h"
#include "node_rpc_proxy.h" #include "node_rpc_proxy.h"
#include "message_store.h" #include "message_store.h"
#include "wallet_light_rpc.h"
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@ -143,11 +142,6 @@ private:
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {} virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason) { return boost::none; } virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason) { return boost::none; }
// Light wallet callbacks
virtual void on_lw_new_block(uint64_t height) {}
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
// Device callbacks // Device callbacks
virtual void on_device_button_request(uint64_t code) {} virtual void on_device_button_request(uint64_t code) {}
virtual void on_device_button_pressed() {} virtual void on_device_button_pressed() {}
@ -981,14 +975,6 @@ private:
bool is_deterministic() const; bool is_deterministic() const;
bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
/*!
* \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned.
*/
bool light_wallet() const { return m_light_wallet; }
void set_light_wallet(bool light_wallet) { m_light_wallet = light_wallet; }
uint64_t get_light_wallet_scanned_block_height() const { return m_light_wallet_scanned_block_height; }
uint64_t get_light_wallet_blockchain_height() const { return m_light_wallet_blockchain_height; }
/*! /*!
* \brief Gets the seed language * \brief Gets the seed language
*/ */
@ -1104,7 +1090,7 @@ private:
void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); } uint64_t get_blockchain_current_height() const { return m_blockchain.size(); }
void rescan_spent(); void rescan_spent();
void rescan_blockchain(bool hard, bool refresh = true, bool keep_key_images = false); void rescan_blockchain(bool hard, bool refresh = true, bool keep_key_images = false);
bool is_transfer_unlocked(const transfer_details& td); bool is_transfer_unlocked(const transfer_details& td);
@ -1537,23 +1523,6 @@ private:
std::pair<size_t, uint64_t> estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size); std::pair<size_t, uint64_t> estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size);
// Light wallet specific functions
// fetch unspent outs from lw node and store in m_transfers
void light_wallet_get_unspent_outs();
// fetch txs and store in m_payments
void light_wallet_get_address_txs();
// get_address_info
bool light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response);
// Login. new_address is true if address hasn't been used on lw node before.
bool light_wallet_login(bool &new_address);
// Send an import request to lw node. returns info about import fee, address and payment_id
bool light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response);
// get random outputs from light wallet server
void light_wallet_get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
// Parse rct string
bool light_wallet_parse_rct_str(const std::string& rct_string, const crypto::public_key& tx_pub_key, uint64_t internal_output_index, rct::key& decrypted_mask, rct::key& rct_commit, bool decrypt) const;
// check if key image is ours
bool light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index);
/* /*
* "attributes" are a mechanism to store an arbitrary number of string values * "attributes" are a mechanism to store an arbitrary number of string values
@ -1861,20 +1830,6 @@ private:
// Aux transaction data from device // Aux transaction data from device
serializable_unordered_map<crypto::hash, std::string> m_tx_device; serializable_unordered_map<crypto::hash, std::string> m_tx_device;
// Light wallet
bool m_light_wallet; /* sends view key to daemon for scanning */
uint64_t m_light_wallet_scanned_block_height;
uint64_t m_light_wallet_blockchain_height;
uint64_t m_light_wallet_per_kb_fee = FEE_PER_KB;
bool m_light_wallet_connected;
uint64_t m_light_wallet_balance;
uint64_t m_light_wallet_unlocked_balance;
// Light wallet info needed to populate m_payment requires 2 separate api calls (get_address_txs and get_unspent_outs)
// We save the info from the first call in m_light_wallet_address_txs for easier lookup.
std::unordered_map<crypto::hash, address_tx> m_light_wallet_address_txs;
// store calculated key image for faster lookup
serializable_unordered_map<crypto::public_key, serializable_map<uint64_t, crypto::key_image> > m_key_image_cache;
std::string m_ring_database; std::string m_ring_database;
bool m_ring_history_saved; bool m_ring_history_saved;
std::unique_ptr<ringdb> m_ringdb; std::unique_ptr<ringdb> m_ringdb;

View File

@ -1,367 +0,0 @@
// Copyright (c) 2014-2023, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 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
#pragma once
#include "cryptonote_basic/cryptonote_basic.h"
#include "crypto/hash.h"
namespace tools
{
//-----------------------------------------------
struct COMMAND_RPC_GET_ADDRESS_TXS
{
struct request_t
{
std::string address;
std::string view_key;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(view_key)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct spent_output {
uint64_t amount;
std::string key_image;
std::string tx_pub_key;
uint64_t out_index;
uint32_t mixin;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(key_image)
KV_SERIALIZE(tx_pub_key)
KV_SERIALIZE(out_index)
KV_SERIALIZE(mixin)
END_KV_SERIALIZE_MAP()
};
struct transaction
{
uint64_t id;
std::string hash;
uint64_t timestamp;
uint64_t total_received;
uint64_t total_sent;
uint64_t unlock_time;
uint64_t height;
std::list<spent_output> spent_outputs;
std::string payment_id;
bool coinbase;
bool mempool;
uint32_t mixin;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(id)
KV_SERIALIZE(hash)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(total_received)
KV_SERIALIZE(total_sent)
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(height)
KV_SERIALIZE(spent_outputs)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(coinbase)
KV_SERIALIZE(mempool)
KV_SERIALIZE(mixin)
END_KV_SERIALIZE_MAP()
};
struct response_t
{
//std::list<std::string> txs_as_json;
uint64_t total_received;
uint64_t total_received_unlocked = 0; // OpenMonero only
uint64_t scanned_height;
std::vector<transaction> transactions;
uint64_t blockchain_height;
uint64_t scanned_block_height;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(total_received)
KV_SERIALIZE(total_received_unlocked)
KV_SERIALIZE(scanned_height)
KV_SERIALIZE(transactions)
KV_SERIALIZE(blockchain_height)
KV_SERIALIZE(scanned_block_height)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_GET_ADDRESS_INFO
{
struct request_t
{
std::string address;
std::string view_key;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(view_key)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct spent_output
{
uint64_t amount;
std::string key_image;
std::string tx_pub_key;
uint64_t out_index;
uint32_t mixin;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(key_image)
KV_SERIALIZE(tx_pub_key)
KV_SERIALIZE(out_index)
KV_SERIALIZE(mixin)
END_KV_SERIALIZE_MAP()
};
struct response_t
{
uint64_t locked_funds;
uint64_t total_received;
uint64_t total_sent;
uint64_t scanned_height;
uint64_t scanned_block_height;
uint64_t start_height;
uint64_t transaction_height;
uint64_t blockchain_height;
std::list<spent_output> spent_outputs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(locked_funds)
KV_SERIALIZE(total_received)
KV_SERIALIZE(total_sent)
KV_SERIALIZE(scanned_height)
KV_SERIALIZE(scanned_block_height)
KV_SERIALIZE(start_height)
KV_SERIALIZE(transaction_height)
KV_SERIALIZE(blockchain_height)
KV_SERIALIZE(spent_outputs)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_GET_UNSPENT_OUTS
{
struct request_t
{
std::string amount;
std::string address;
std::string view_key;
// OpenMonero specific
uint64_t mixin;
bool use_dust;
std::string dust_threshold;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(address)
KV_SERIALIZE(view_key)
KV_SERIALIZE(mixin)
KV_SERIALIZE(use_dust)
KV_SERIALIZE(dust_threshold)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct output {
uint64_t amount;
std::string public_key;
uint64_t index;
uint64_t global_index;
std::string rct;
std::string tx_hash;
std::string tx_pub_key;
std::string tx_prefix_hash;
std::vector<std::string> spend_key_images;
uint64_t timestamp;
uint64_t height;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(public_key)
KV_SERIALIZE(index)
KV_SERIALIZE(global_index)
KV_SERIALIZE(rct)
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_pub_key)
KV_SERIALIZE(tx_prefix_hash)
KV_SERIALIZE(spend_key_images)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(height)
END_KV_SERIALIZE_MAP()
};
struct response_t
{
uint64_t amount;
std::list<output> outputs;
uint64_t per_kb_fee;
std::string status;
std::string reason;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(outputs)
KV_SERIALIZE(per_kb_fee)
KV_SERIALIZE(status)
KV_SERIALIZE(reason)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_LOGIN
{
struct request_t
{
std::string address;
std::string view_key;
bool create_account;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(view_key)
KV_SERIALIZE(create_account)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::string status;
std::string reason;
bool new_address;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(reason)
KV_SERIALIZE(new_address)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_IMPORT_WALLET_REQUEST
{
struct request_t
{
std::string address;
std::string view_key;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(view_key)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::string payment_id;
uint64_t import_fee;
bool new_request;
bool request_fulfilled;
std::string payment_address;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(payment_id)
KV_SERIALIZE(import_fee)
KV_SERIALIZE(new_request)
KV_SERIALIZE(request_fulfilled)
KV_SERIALIZE(payment_address)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
struct COMMAND_RPC_GET_RANDOM_OUTS
{
struct request_t
{
std::vector<std::string> amounts;
uint32_t count;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amounts)
KV_SERIALIZE(count)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct output {
std::string public_key;
uint64_t global_index;
std::string rct; // 64+64+64 characters long (<rct commit> + <encrypted mask> + <rct amount>)
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(public_key)
KV_SERIALIZE(global_index)
KV_SERIALIZE(rct)
END_KV_SERIALIZE_MAP()
};
struct amount_out {
uint64_t amount;
std::vector<output> outputs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(outputs)
END_KV_SERIALIZE_MAP()
};
struct response_t
{
std::vector<amount_out> amount_outs;
std::string Error;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount_outs)
KV_SERIALIZE(Error)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
}