aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/wallet.cpp8
-rw-r--r--src/wallet/ringdb.cpp4
-rw-r--r--src/wallet/wallet2.cpp49
-rw-r--r--src/wallet/wallet_rpc_server.cpp168
-rw-r--r--src/wallet/wallet_rpc_server.h3
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h63
6 files changed, 280 insertions, 15 deletions
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index de1bfdae1..1b4370c36 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -776,7 +776,7 @@ bool WalletImpl::setPassword(const std::string &password)
{
clearStatus();
try {
- m_wallet->rewrite(m_wallet->get_wallet_file(), password);
+ m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password);
m_password = password;
} catch (const std::exception &e) {
setStatusError(e.what());
@@ -2161,7 +2161,7 @@ bool WalletImpl::blackballOutputs(const std::vector<std::string> &outputs, bool
bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add);
if (!ret)
{
- setStatusError(tr("Failed to set blackballed outputs"));
+ setStatusError(tr("Failed to mark outputs as spent"));
return false;
}
return true;
@@ -2183,7 +2183,7 @@ bool WalletImpl::blackballOutput(const std::string &amount, const std::string &o
bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset));
if (!ret)
{
- setStatusError(tr("Failed to blackball output"));
+ setStatusError(tr("Failed to mark output as spent"));
return false;
}
return true;
@@ -2205,7 +2205,7 @@ bool WalletImpl::unblackballOutput(const std::string &amount, const std::string
bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset));
if (!ret)
{
- setStatusError(tr("Failed to unblackball output"));
+ setStatusError(tr("Failed to mark output as unspent"));
return false;
}
return true;
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index e5995e7fb..f562d6c06 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -412,13 +412,13 @@ bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &
switch (op)
{
case BLACKBALL_BLACKBALL:
- MDEBUG("Blackballing output " << output.first << "/" << output.second);
+ MDEBUG("Marking output " << output.first << "/" << output.second << " as spent");
dbr = mdb_cursor_put(cursor, &key, &data, MDB_APPENDDUP);
if (dbr == MDB_KEYEXIST)
dbr = 0;
break;
case BLACKBALL_UNBLACKBALL:
- MDEBUG("Unblackballing output " << output.first << "/" << output.second);
+ MDEBUG("Marking output " << output.first << "/" << output.second << " as unspent");
dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
if (dbr == 0)
dbr = mdb_cursor_del(cursor, 0);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index f87edf506..c5618375e 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -37,6 +37,8 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/join.hpp>
+#include <boost/range/adaptor/transformed.hpp>
#include "include_base_utils.h"
using namespace epee;
@@ -121,6 +123,8 @@ using namespace cryptonote;
#define FIRST_REFRESH_GRANULARITY 1024
+#define GAMMA_PICK_HALF_WINDOW 5
+
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
@@ -3611,6 +3615,7 @@ bool wallet2::query_device(hw::device::device_type& device_type, const std::stri
if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ device_type = hw::device::device_type::SOFTWARE;
// The contents should be JSON if the wallet follows the new format.
if (json.Parse(account_data.c_str()).HasParseError())
{
@@ -6792,10 +6797,29 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
error::get_output_distribution, "Decreasing offsets in rct distribution: " +
std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " +
std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1]));
- uint64_t n_rct = rct_offsets[block_offset + 1] - rct_offsets[block_offset];
+ uint64_t first_block_offset = block_offset, last_block_offset = block_offset;
+ for (size_t half_window = 0; half_window < GAMMA_PICK_HALF_WINDOW; ++half_window)
+ {
+ // end when we have a non empty block
+ uint64_t cum0 = first_block_offset > 0 ? rct_offsets[first_block_offset] - rct_offsets[first_block_offset - 1] : rct_offsets[0];
+ if (cum0 > 1)
+ break;
+ uint64_t cum1 = last_block_offset > 0 ? rct_offsets[last_block_offset] - rct_offsets[last_block_offset - 1] : rct_offsets[0];
+ if (cum1 > 1)
+ break;
+ if (first_block_offset == 0 && last_block_offset >= rct_offsets.size() - 2)
+ break;
+ // expand up to bounds
+ if (first_block_offset > 0)
+ --first_block_offset;
+ if (last_block_offset < rct_offsets.size() - 1)
+ ++last_block_offset;
+ }
+ const uint64_t n_rct = rct_offsets[last_block_offset] - (first_block_offset == 0 ? 0 : rct_offsets[first_block_offset - 1]);
if (n_rct == 0)
return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0;
- return rct_offsets[block_offset] + crypto::rand<uint64_t>() % n_rct;
+ MDEBUG("Picking 1/" << n_rct << " in " << (last_block_offset - first_block_offset + 1) << " blocks centered around " << block_offset);
+ return rct_offsets[first_block_offset] + crypto::rand<uint64_t>() % n_rct;
};
size_t num_selected_transfers = 0;
@@ -6965,6 +6989,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_money(amount));
}
+ std::unordered_map<const char*, std::set<uint64_t>> picks;
+
// while we still need more mixins
uint64_t num_usable_outs = num_outs;
bool allow_blackballed = false;
@@ -6979,7 +7005,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// outputs, we still need to reach the minimum ring size)
if (allow_blackballed)
break;
- MINFO("Not enough non blackballed outputs, we'll allow blackballed ones");
+ MINFO("Not enough output not marked as spent, we'll allow outputs marked as spent");
allow_blackballed = true;
num_usable_outs = num_outs;
}
@@ -7063,11 +7089,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
seen_indices.emplace(i);
- LOG_PRINT_L2("picking " << i << " as " << type);
+ picks[type].insert(i);
req.outputs.push_back({amount, i});
++num_found;
}
+ for (const auto &pick: picks)
+ MDEBUG("picking " << pick.first << " outputs: " <<
+ boost::join(pick.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
+
// if we had enough unusable outputs, we might fall off here and still
// have too few outputs, so we stuff with one to keep counts good, and
// we'll error out later
@@ -7083,8 +7113,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
[](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; });
}
- for (auto i: req.outputs)
- LOG_PRINT_L1("asking for output " << i.index << " for " << print_money(i.amount));
+ if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY))
+ {
+ std::map<uint64_t, std::set<uint64_t>> outs;
+ for (const auto &i: req.outputs)
+ outs[i.amount].insert(i.index);
+ for (const auto &o: outs)
+ MDEBUG("asking for outputs with amount " << print_money(o.first) << ": " <<
+ boost::join(o.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
+ }
// get the keys for those
m_daemon_rpc_mutex.lock();
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index e0b631aaf..496c30807 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -994,6 +994,171 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ if (m_wallet->key_on_device())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "command not supported by HW wallet";
+ return false;
+ }
+ if(m_wallet->watch_only())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
+ er.message = "command not supported by watch-only wallet";
+ return false;
+ }
+
+ tools::wallet2::unsigned_tx_set exported_txs;
+ try
+ {
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+ if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "cannot load unsigned_txset";
+ return false;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "failed to parse unsigned transfers: " + std::string(e.what());
+ return false;
+ }
+
+ std::vector<tools::wallet2::pending_tx> ptx;
+ try
+ {
+ // gather info to ask the user
+ std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
+ int first_known_non_zero_change_index = -1;
+ for (size_t n = 0; n < exported_txs.txes.size(); ++n)
+ {
+ const tools::wallet2::tx_construction_data &cd = exported_txs.txes[n];
+ res.desc.push_back({0, 0, std::numeric_limits<uint32_t>::max(), 0, {}, "", 0, "", 0, 0, ""});
+ wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back();
+
+ std::vector<cryptonote::tx_extra_field> tx_extra_fields;
+ bool has_encrypted_payment_id = false;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
+ if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
+ {
+ cryptonote::tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ crypto::hash payment_id;
+ if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id8);
+ has_encrypted_payment_id = true;
+ }
+ else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id);
+ }
+ }
+ }
+
+ for (size_t s = 0; s < cd.sources.size(); ++s)
+ {
+ desc.amount_in += cd.sources[s].amount;
+ size_t ring_size = cd.sources[s].outputs.size();
+ if (ring_size < desc.ring_size)
+ desc.ring_size = ring_size;
+ }
+ for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
+ {
+ const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
+ std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
+ if (has_encrypted_payment_id && !entry.is_subaddress)
+ address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
+ auto i = dests.find(entry.addr);
+ if (i == dests.end())
+ dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
+ else
+ i->second.second += entry.amount;
+ desc.amount_out += entry.amount;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ auto it = dests.find(cd.change_dts.addr);
+ if (it == dests.end())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Claimed change does not go to a paid address";
+ return false;
+ }
+ if (it->second.second < cd.change_dts.amount)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Claimed change is larger than payment to the change address";
+ return false;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ if (first_known_non_zero_change_index == -1)
+ first_known_non_zero_change_index = n;
+ const tools::wallet2::tx_construction_data &cdn = exported_txs.txes[first_known_non_zero_change_index];
+ if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr)))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Change goes to more than one address";
+ return false;
+ }
+ }
+ desc.change_amount += cd.change_dts.amount;
+ it->second.second -= cd.change_dts.amount;
+ if (it->second.second == 0)
+ dests.erase(cd.change_dts.addr);
+ }
+
+ size_t n_dummy_outputs = 0;
+ for (auto i = dests.begin(); i != dests.end(); )
+ {
+ if (i->second.second > 0)
+ {
+ desc.recipients.push_back({i->second.first, i->second.second});
+ }
+ else
+ ++desc.dummy_outputs;
+ ++i;
+ }
+
+ if (desc.change_amount > 0)
+ {
+ const tools::wallet2::tx_construction_data &cd0 = exported_txs.txes[0];
+ desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
+ }
+
+ desc.fee = desc.amount_in - desc.amount_out;
+ desc.unlock_time = cd.unlock_time;
+ desc.extra = epee::to_hex::string({cd.extra.data(), cd.extra.size()});
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "failed to parse unsigned transfers";
+ return false;
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -2822,8 +2987,7 @@ namespace tools
{
try
{
- m_wallet->rewrite(m_wallet->get_wallet_file(), req.new_password);
- m_wallet->store();
+ m_wallet->change_password(m_wallet->get_wallet_file(), req.old_password, req.new_password);
LOG_PRINT_L0("Wallet password changed.");
}
catch (const std::exception& e)
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index ab896aa3b..887723ed5 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -35,6 +35,7 @@
#include <string>
#include "common/util.h"
#include "net/http_server_impl_base.h"
+#include "math_helper.h"
#include "wallet_rpc_server_commands_defs.h"
#include "wallet2.h"
@@ -86,6 +87,7 @@ namespace tools
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER)
+ MAP_JON_RPC_WE("describe_transfer", on_describe_transfer, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER)
MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER)
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
MAP_JON_RPC_WE("sweep_unmixable", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
@@ -166,6 +168,7 @@ namespace tools
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er);
+ bool on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 2377b69e3..c65c7e9b5 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 4
+#define WALLET_RPC_VERSION_MINOR 5
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -531,6 +531,67 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_DESCRIBE_TRANSFER
+ {
+ struct recipient
+ {
+ std::string address;
+ uint64_t amount;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(amount)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct transfer_description
+ {
+ uint64_t amount_in;
+ uint64_t amount_out;
+ uint32_t ring_size;
+ uint64_t unlock_time;
+ std::list<recipient> recipients;
+ std::string payment_id;
+ uint64_t change_amount;
+ std::string change_address;
+ uint64_t fee;
+ uint32_t dummy_outputs;
+ std::string extra;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount_in)
+ KV_SERIALIZE(amount_out)
+ KV_SERIALIZE(ring_size)
+ KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(recipients)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(change_amount)
+ KV_SERIALIZE(change_address)
+ KV_SERIALIZE(fee)
+ KV_SERIALIZE(dummy_outputs)
+ KV_SERIALIZE(extra)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct request
+ {
+ std::string unsigned_txset;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(unsigned_txset)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::list<transfer_description> desc;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(desc)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_SIGN_TRANSFER
{
struct request