mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-14 07:03:29 +01:00
my/openmonero API functions
This commit is contained in:
parent
288d3c75c3
commit
f44d156cbd
@ -4625,6 +4625,447 @@ 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;
|
||||||
|
cryptonote::COMMAND_RPC_LOGIN::request request;
|
||||||
|
cryptonote::COMMAND_RPC_LOGIN::response response;
|
||||||
|
request.address = get_account().get_public_address_str(m_testnet);
|
||||||
|
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
|
||||||
|
// Always create account if it doesnt exist.
|
||||||
|
request.create_account = true;
|
||||||
|
m_daemon_rpc_mutex.lock();
|
||||||
|
bool connected = epee::net_utils::invoke_http_json("/login", request, response, m_http_client, 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 successfull login.
|
||||||
|
// m_transfers.clear();
|
||||||
|
// m_payments.clear();
|
||||||
|
// m_unconfirmed_payments.clear();
|
||||||
|
}
|
||||||
|
return m_light_wallet_connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wallet2::light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
|
||||||
|
{
|
||||||
|
MDEBUG("Light wallet import wallet request");
|
||||||
|
cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq;
|
||||||
|
oreq.address = get_account().get_public_address_str(m_testnet);
|
||||||
|
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
|
||||||
|
m_daemon_rpc_mutex.lock();
|
||||||
|
bool r = epee::net_utils::invoke_http_json("/import_wallet_request", oreq, response, m_http_client, 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");
|
||||||
|
|
||||||
|
cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq;
|
||||||
|
cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::response ores;
|
||||||
|
|
||||||
|
oreq.amount = "0";
|
||||||
|
oreq.address = get_account().get_public_address_str(m_testnet);
|
||||||
|
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 = epee::net_utils::invoke_http_json("/get_unspent_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_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(boost::value_initialized<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;
|
||||||
|
td.m_amount = o.amount;
|
||||||
|
td.m_pk_index = 0;
|
||||||
|
td.m_internal_output_index = o.index;
|
||||||
|
td.m_spent = spent;
|
||||||
|
|
||||||
|
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(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
|
||||||
|
{
|
||||||
|
MTRACE(__FUNCTION__);
|
||||||
|
|
||||||
|
cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::request request;
|
||||||
|
|
||||||
|
request.address = get_account().get_public_address_str(m_testnet);
|
||||||
|
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
|
||||||
|
m_daemon_rpc_mutex.lock();
|
||||||
|
bool r = epee::net_utils::invoke_http_json("/get_address_info", request, response, m_http_client, 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");
|
||||||
|
|
||||||
|
cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::request ireq;
|
||||||
|
cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::response ires;
|
||||||
|
|
||||||
|
ireq.address = get_account().get_public_address_str(m_testnet);
|
||||||
|
ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
|
||||||
|
m_daemon_rpc_mutex.lock();
|
||||||
|
bool r = epee::net_utils::invoke_http_json("/get_address_txs", ireq, ires, m_http_client, 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_tx_hash);
|
||||||
|
|
||||||
|
// for balance calculation
|
||||||
|
uint64_t wallet_total_sent = 0;
|
||||||
|
uint64_t wallet_total_unlocked_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_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_block_height = t.height;
|
||||||
|
payment.m_unlock_time = t.unlock_time;
|
||||||
|
payment.m_timestamp = t.timestamp;
|
||||||
|
|
||||||
|
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);
|
||||||
|
m_unconfirmed_payments.emplace(tx_hash, payment);
|
||||||
|
if (0 != m_callback) {
|
||||||
|
cryptonote::transaction dummy_tx;
|
||||||
|
m_callback->on_unconfirmed_money_received(t.height, payment.m_tx_hash, dummy_tx, 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) {
|
||||||
|
cryptonote::transaction dummy_tx;
|
||||||
|
m_callback->on_money_received(t.height, payment.m_tx_hash, dummy_tx, 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_money_spent(t.height, tx_hash, dummy_tx, amount_sent, dummy_tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Calculate wallet balance
|
||||||
|
m_light_wallet_balance = ires.total_received-wallet_total_sent;
|
||||||
|
// MyMonero doesnt 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;
|
||||||
|
generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_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
|
||||||
|
std::map<uint64_t, crypto::key_image> index_keyimage_map;
|
||||||
|
std::unordered_map<crypto::public_key, std::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;
|
||||||
|
cryptonote::generate_key_image_helper(get_account().get_keys(), tx_public_key, out_index, in_ephemeral, 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
|
||||||
|
@ -230,6 +230,13 @@ namespace tools
|
|||||||
cryptonote::subaddress_index m_subaddr_index;
|
cryptonote::subaddress_index m_subaddr_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct address_tx : payment_details
|
||||||
|
{
|
||||||
|
bool m_coinbase;
|
||||||
|
bool m_mempool;
|
||||||
|
bool m_incoming;
|
||||||
|
};
|
||||||
|
|
||||||
struct unconfirmed_transfer_details
|
struct unconfirmed_transfer_details
|
||||||
{
|
{
|
||||||
cryptonote::transaction_prefix m_tx;
|
cryptonote::transaction_prefix m_tx;
|
||||||
@ -423,6 +430,15 @@ namespace tools
|
|||||||
*/
|
*/
|
||||||
bool is_deterministic() const;
|
bool is_deterministic() const;
|
||||||
bool get_seed(std::string& electrum_words, const std::string &passphrase = std::string()) const;
|
bool get_seed(std::string& electrum_words, const std::string &passphrase = std::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
|
||||||
*/
|
*/
|
||||||
@ -715,6 +731,24 @@ namespace tools
|
|||||||
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
|
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
|
||||||
uint64_t get_per_kb_fee();
|
uint64_t get_per_kb_fee();
|
||||||
|
|
||||||
|
// 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(cryptonote::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(cryptonote::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::list<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);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
* \brief Stores wallet information to wallet file.
|
* \brief Stores wallet information to wallet file.
|
||||||
|
Loading…
Reference in New Issue
Block a user