aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt63
-rw-r--r--src/wallet/api/transaction_history.cpp6
-rw-r--r--src/wallet/api/wallet_manager.cpp4
-rw-r--r--src/wallet/wallet2.cpp425
-rw-r--r--src/wallet/wallet2.h107
-rw-r--r--src/wallet/wallet2_api.h2
-rw-r--r--src/wallet/wallet_args.cpp33
-rw-r--r--src/wallet/wallet_args.h1
-rw-r--r--src/wallet/wallet_errors.h6
-rw-r--r--[-rwxr-xr-x]src/wallet/wallet_rpc_server.cpp158
-rw-r--r--src/wallet/wallet_rpc_server.h4
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h49
12 files changed, 556 insertions, 302 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 24399790c..e5c79a447 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -79,7 +79,6 @@ target_link_libraries(wallet
common
cryptonote_core
mnemonics
- p2p
${Boost_CHRONO_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
@@ -89,42 +88,40 @@ target_link_libraries(wallet
PRIVATE
${EXTRA_LIBRARIES})
-if (NOT BUILD_GUI_DEPS)
- set(wallet_rpc_sources
- wallet_rpc_server.cpp)
+set(wallet_rpc_sources
+ wallet_rpc_server.cpp)
- set(wallet_rpc_headers)
+set(wallet_rpc_headers)
- set(wallet_rpc_private_headers
- wallet_rpc_server.h)
+set(wallet_rpc_private_headers
+ wallet_rpc_server.h)
- monero_private_headers(wallet_rpc_server
- ${wallet_rpc_private_headers})
- monero_add_executable(wallet_rpc_server
- ${wallet_rpc_sources}
- ${wallet_rpc_headers}
- ${wallet_rpc_private_headers})
+monero_private_headers(wallet_rpc_server
+ ${wallet_rpc_private_headers})
+monero_add_executable(wallet_rpc_server
+ ${wallet_rpc_sources}
+ ${wallet_rpc_headers}
+ ${wallet_rpc_private_headers})
- target_link_libraries(wallet_rpc_server
- PRIVATE
- wallet
- epee
- rpc
- cryptonote_core
- cncrypto
- common
- version
- ${Boost_CHRONO_LIBRARY}
- ${Boost_PROGRAM_OPTIONS_LIBRARY}
- ${Boost_FILESYSTEM_LIBRARY}
- ${Boost_THREAD_LIBRARY}
- ${CMAKE_THREAD_LIBS_INIT}
- ${EXTRA_LIBRARIES})
- set_property(TARGET wallet_rpc_server
- PROPERTY
- OUTPUT_NAME "monero-wallet-rpc")
- install(TARGETS wallet_rpc_server DESTINATION bin)
-endif()
+target_link_libraries(wallet_rpc_server
+ PRIVATE
+ wallet
+ epee
+ rpc
+ cryptonote_core
+ cncrypto
+ common
+ version
+ ${Boost_CHRONO_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${EXTRA_LIBRARIES})
+set_property(TARGET wallet_rpc_server
+ PROPERTY
+ OUTPUT_NAME "monero-wallet-rpc")
+install(TARGETS wallet_rpc_server DESTINATION bin)
# build and install libwallet_merged only if we building for GUI
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index 59eca3dd7..8a8243047 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -217,10 +217,10 @@ void TransactionHistoryImpl::refresh()
// unconfirmed payments (tx pool)
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> upayments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> upayments;
m_wallet->m_wallet->get_unconfirmed_payments(upayments);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
- const tools::wallet2::payment_details &pd = i->second;
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
+ const tools::wallet2::payment_details &pd = i->second.m_pd;
std::string payment_id = string_tools::pod_to_hex(i->first);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index a64766c84..2326f54d3 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -434,12 +434,14 @@ std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool
return addresses.front();
}
-std::tuple<bool, std::string, std::string, std::string, std::string> WalletManager::checkUpdates(const std::string &software, const std::string &subdir)
+std::tuple<bool, std::string, std::string, std::string, std::string> WalletManager::checkUpdates(const std::string &software, std::string subdir)
{
#ifdef BUILD_TAG
static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG);
#else
static const char buildtag[] = "source";
+ // Override the subdir string when built from source
+ subdir = "source";
#endif
std::string version, hash;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 647301133..00b096b88 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -59,7 +59,6 @@ using namespace epee;
#include "rapidjson/stringbuffer.h"
#include "common/json_util.h"
#include "common/base58.h"
-#include "common/scoped_message_writer.h"
#include "ringct/rctSigs.h"
extern "C"
@@ -67,6 +66,8 @@ extern "C"
#include "crypto/keccak.h"
#include "crypto/crypto-ops.h"
}
+using namespace std;
+using namespace crypto;
using namespace cryptonote;
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -135,7 +136,7 @@ uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, ui
return calculate_fee(fee_per_kb, blob.size(), fee_multiplier);
}
-std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts)
+std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool restricted = command_line::get_arg(vm, opts.restricted);
@@ -144,17 +145,16 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
- if (!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port)
- {
- tools::fail_msg_writer() << tools::wallet2::tr("can't specify daemon host or port more than once");
- return nullptr;
- }
+ THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
+ tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
boost::optional<epee::net_utils::http::login> login{};
if (command_line::has_arg(vm, opts.daemon_login))
{
auto parsed = tools::login::parse(
- command_line::get_arg(vm, opts.daemon_login), false, "Daemon client password"
+ command_line::get_arg(vm, opts.daemon_login), false, [password_prompter](bool verify) {
+ return password_prompter("Daemon client password", verify);
+ }
);
if (!parsed)
return nullptr;
@@ -178,12 +178,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
return wallet;
}
-boost::optional<tools::password_container> get_password(const boost::program_options::variables_map& vm, const options& opts, const bool verify)
+boost::optional<tools::password_container> get_password(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char*, bool)> &password_prompter, const bool verify)
{
if (command_line::has_arg(vm, opts.password) && command_line::has_arg(vm, opts.password_file))
{
- tools::fail_msg_writer() << tools::wallet2::tr("can't specify more than one of --password and --password-file");
- return boost::none;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("can't specify more than one of --password and --password-file"));
}
if (command_line::has_arg(vm, opts.password))
@@ -196,21 +195,17 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
std::string password;
bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, opts.password_file),
password);
- if (!r)
- {
- tools::fail_msg_writer() << tools::wallet2::tr("the password file specified could not be read");
- return boost::none;
- }
+ THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, tools::wallet2::tr("the password file specified could not be read"));
// Remove line breaks the user might have inserted
boost::trim_right_if(password, boost::is_any_of("\r\n"));
return {tools::password_container{std::move(password)}};
}
- return tools::wallet2::password_prompt(verify);
+ return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify);
}
-std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts)
+std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
@@ -221,22 +216,20 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
const auto do_generate = [&]() -> bool {
std::string buf;
if (!epee::file_io_utils::load_file_to_string(json_file, buf)) {
- tools::fail_msg_writer() << tools::wallet2::tr("Failed to load file ") << json_file;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("Failed to load file ")) + json_file);
return false;
}
rapidjson::Document json;
if (json.Parse(buf.c_str()).HasParseError()) {
- tools::fail_msg_writer() << tools::wallet2::tr("Failed to parse JSON");
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Failed to parse JSON"));
return false;
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true, 0);
const int current_version = 1;
- if (field_version > current_version) {
- tools::fail_msg_writer() << boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version;
- return false;
- }
+ THROW_WALLET_EXCEPTION_IF(field_version > current_version, tools::error::wallet_internal_error,
+ ((boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version)).str());
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string());
@@ -252,14 +245,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
cryptonote::blobdata viewkey_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
{
- tools::fail_msg_writer() << tools::wallet2::tr("failed to parse view key secret key");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse view key secret key"));
}
viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
crypto::public_key pkey;
if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
- tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
}
}
@@ -270,14 +261,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
cryptonote::blobdata spendkey_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
{
- tools::fail_msg_writer() << tools::wallet2::tr("failed to parse spend key secret key");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse spend key secret key"));
}
spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
crypto::public_key pkey;
if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
- tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
}
}
@@ -289,8 +278,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
{
if (!crypto::ElectrumWords::words_to_bytes(field_seed, recovery_key, old_language))
{
- tools::fail_msg_writer() << tools::wallet2::tr("Electrum-style word list failed verification");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Electrum-style word list failed verification"));
}
restore_deterministic_wallet = true;
@@ -305,15 +293,13 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string());
// compatibility checks
- if (!field_seed_found && !field_viewkey_found)
+ if (!field_seed_found && !field_viewkey_found && !field_spendkey_found)
{
- tools::fail_msg_writer() << tools::wallet2::tr("At least one of Electrum-style word list and private view key must be specified");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("At least one of Electrum-style word list and private view key and private spend key must be specified"));
}
if (field_seed_found && (field_viewkey_found || field_spendkey_found))
{
- tools::fail_msg_writer() << tools::wallet2::tr("Both Electrum-style word list and private key(s) specified");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Both Electrum-style word list and private key(s) specified"));
}
// if an address was given, we check keys against it, and deduce the spend
@@ -323,43 +309,36 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, testnet, field_address))
{
- tools::fail_msg_writer() << tools::wallet2::tr("invalid address");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("invalid address"));
}
if (field_viewkey_found)
{
crypto::public_key pkey;
if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
- tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
}
if (info.address.m_view_public_key != pkey) {
- tools::fail_msg_writer() << tools::wallet2::tr("view key does not match standard address");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view key does not match standard address"));
}
}
if (field_spendkey_found)
{
crypto::public_key pkey;
if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
- tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
}
if (info.address.m_spend_public_key != pkey) {
- tools::fail_msg_writer() << tools::wallet2::tr("spend key does not match standard address");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend key does not match standard address"));
}
}
}
const bool deprecated_wallet = restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) ||
crypto::ElectrumWords::get_is_old_style_seed(field_seed));
- if (deprecated_wallet) {
- tools::fail_msg_writer() << tools::wallet2::tr("Cannot create deprecated wallets from JSON");
- return false;
- }
+ THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error,
+ tools::wallet2::tr("Cannot create deprecated wallets from JSON"));
- wallet.reset(make_basic(vm, opts).release());
+ wallet.reset(make_basic(vm, opts, password_prompter).release());
wallet->set_refresh_from_block_height(field_scan_from_height);
try
@@ -368,12 +347,15 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
{
wallet->generate(field_filename, field_password, recovery_key, recover, false);
}
+ else if (field_viewkey.empty() && !field_spendkey.empty())
+ {
+ wallet->generate(field_filename, field_password, spendkey, recover, false);
+ }
else
{
cryptonote::account_public_address address;
if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) {
- tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
}
if (field_spendkey.empty())
@@ -385,18 +367,20 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, testnet, field_address))
{
- tools::fail_msg_writer() << tools::wallet2::tr("failed to parse address: ") << field_address;
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to parse address: ")) + field_address);
}
address.m_spend_public_key = info.address.m_spend_public_key;
}
+ else
+ {
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Address must be specified in order to create watch-only wallet"));
+ }
wallet->generate(field_filename, field_password, address, viewkey);
}
else
{
if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) {
- tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key");
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
}
wallet->generate(field_filename, field_password, address, spendkey, viewkey);
}
@@ -404,8 +388,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
}
catch (const std::exception& e)
{
- tools::fail_msg_writer() << tools::wallet2::tr("failed to generate new wallet: ") << e.what();
- return false;
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to generate new wallet: ")) + e.what());
}
return true;
};
@@ -444,6 +427,34 @@ std::string strjoin(const std::vector<size_t> &V, const char *sep)
return ss.str();
}
+static void emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container,
+ const crypto::hash &key, const tools::wallet2::pool_payment_details &pd)
+{
+ auto range = container.equal_range(key);
+ for (auto i = range.first; i != range.second; ++i)
+ {
+ if (i->second.m_pd.m_tx_hash == pd.m_pd.m_tx_hash)
+ {
+ i->second = pd;
+ return;
+ }
+ }
+ container.emplace(key, pd);
+}
+
+void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_t N)
+{
+ std::list<crypto::hash>::iterator right;
+ // drop early N off, skipping the genesis block
+ if (short_chain_history.size() > N) {
+ right = short_chain_history.end();
+ std::advance(right,-1);
+ std::list<crypto::hash>::iterator left = right;
+ std::advance(left, -N);
+ short_chain_history.erase(left, right);
+ }
+}
+
} //namespace
namespace tools
@@ -472,34 +483,22 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.restricted);
}
-boost::optional<password_container> wallet2::password_prompt(const bool new_password)
-{
- auto pwd_container = tools::password_container::prompt(
- new_password, (new_password ? tr("Enter new wallet password") : tr("Wallet password"))
- );
- if (!pwd_container)
- {
- tools::fail_msg_writer() << tr("failed to read wallet password");
- }
- return pwd_container;
-}
-
-std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file)
+std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- return generate_from_json(json_file, vm, opts);
+ return generate_from_json(json_file, vm, opts, password_prompter);
}
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
- const boost::program_options::variables_map& vm, const std::string& wallet_file)
+ const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- auto pwd = get_password(vm, opts, false);
+ auto pwd = get_password(vm, opts, password_prompter, false);
if (!pwd)
{
return {nullptr, password_container{}};
}
- auto wallet = make_basic(vm, opts);
+ auto wallet = make_basic(vm, opts, password_prompter);
if (wallet)
{
wallet->load(wallet_file, pwd->password());
@@ -507,21 +506,21 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
return {std::move(wallet), std::move(*pwd)};
}
-std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm)
+std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- auto pwd = get_password(vm, opts, true);
+ auto pwd = get_password(vm, opts, password_prompter, true);
if (!pwd)
{
return {nullptr, password_container{}};
}
- return {make_basic(vm, opts), std::move(*pwd)};
+ return {make_basic(vm, opts, password_prompter), std::move(*pwd)};
}
-std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm)
+std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const options opts{};
- return make_basic(vm, opts);
+ return make_basic(vm, opts, password_prompter);
}
//----------------------------------------------------------------------------------------------------
@@ -793,7 +792,7 @@ void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote
++num_vouts_received;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
+void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen)
{
// In this function, tx (probably) only contains the base information
// (that is, the prunable stuff may or may not be included)
@@ -831,7 +830,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
- bool r = true;
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
const cryptonote::account_keys& keys = m_account.get_keys();
@@ -854,46 +852,33 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{
check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, tx_scan_info[0]);
- if (tx_scan_info[0].error)
- {
- r = false;
- }
- else
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
+
+ // this assumes that the miner tx pays a single address
+ if (tx_scan_info[0].received)
{
- // this assumes that the miner tx pays a single address
- if (tx_scan_info[0].received)
+ // process the other outs from that tx
+ // the first one was already checked
+ for (size_t i = 1; i < tx.vout.size(); ++i)
{
- scan_output(keys, tx, tx_pub_key, 0, tx_scan_info[0], num_vouts_received, tx_money_got_in_outs, outs);
-
- // process the other outs from that tx
- // the first one was already checked
- for (size_t i = 1; i < tx.vout.size(); ++i)
- {
- tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
- std::ref(tx_scan_info[i])));
- }
- waiter.wait();
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
+ std::ref(tx_scan_info[i])));
+ }
+ waiter.wait();
- for (size_t i = 1; i < tx.vout.size(); ++i)
+ // then scan all outputs from 0
+ for (size_t i = 0; i < tx.vout.size(); ++i)
+ {
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
+ if (tx_scan_info[i].received)
{
- if (tx_scan_info[i].error)
- {
- r = false;
- break;
- }
- if (tx_scan_info[i].received)
- {
- scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
- }
+ scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
}
else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1)
{
- tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
-
for (size_t i = 0; i < tx.vout.size(); ++i)
{
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
@@ -902,11 +887,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
waiter.wait();
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- if (tx_scan_info[i].error)
- {
- r = false;
- break;
- }
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
@@ -918,18 +899,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
for (size_t i = 0; i < tx.vout.size(); ++i)
{
check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, tx_scan_info[i]);
- if (tx_scan_info[i].error)
- {
- r = false;
- break;
- }
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
- THROW_WALLET_EXCEPTION_IF(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if(!outs.empty() && num_vouts_received > 0)
{
@@ -1163,7 +1139,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
payment.m_timestamp = ts;
payment.m_subaddr_index = i.first;
if (pool) {
- m_unconfirmed_payments.emplace(payment_id, payment);
+ emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen});
if (0 != m_callback)
m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index);
}
@@ -1241,7 +1217,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
{
TIME_MEASURE_START(miner_tx_handle_time);
- process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false);
+ process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false, false);
TIME_MEASURE_FINISH(miner_tx_handle_time);
TIME_MEASURE_START(txs_handle_time);
@@ -1252,7 +1228,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
cryptonote::transaction tx;
bool r = parse_and_validate_tx_base_from_blob(txblob, tx);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
- process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false);
+ process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false, false);
++idx;
}
TIME_MEASURE_FINISH(txs_handle_time);
@@ -1497,6 +1473,8 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
try
{
+ drop_from_short_history(short_chain_history, 3);
+
// prepend the last 3 blocks, should be enough to guard against a block or two's reorg
cryptonote::block bl;
std::list<cryptonote::block_complete_entry>::const_reverse_iterator i = prev_blocks.rbegin();
@@ -1520,10 +1498,10 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes)
{
// remove pool txes to us that aren't in the pool anymore
- std::unordered_multimap<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
+ std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin();
while (uit != m_unconfirmed_payments.end())
{
- const crypto::hash &txid = uit->second.m_tx_hash;
+ const crypto::hash &txid = uit->second.m_pd.m_tx_hash;
bool found = false;
for (const auto &it2: tx_hashes)
{
@@ -1626,23 +1604,27 @@ void wallet2::update_pool_state(bool refreshed)
MDEBUG("update_pool_state done second loop");
// gather txids of new pool txes to us
- std::vector<crypto::hash> txids;
+ std::vector<std::pair<crypto::hash, bool>> txids;
for (const auto &txid: res.tx_hashes)
{
- if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
- {
- LOG_PRINT_L2("Already seen " << txid << ", skipped");
- continue;
- }
bool txid_found_in_up = false;
for (const auto &up: m_unconfirmed_payments)
{
- if (up.second.m_tx_hash == txid)
+ if (up.second.m_pd.m_tx_hash == txid)
{
txid_found_in_up = true;
break;
}
}
+ if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
+ {
+ // if it's for us, we want to keep track of whether we saw a double spend, so don't bail out
+ if (!txid_found_in_up)
+ {
+ LOG_PRINT_L2("Already seen " << txid << ", and not for us, skipped");
+ continue;
+ }
+ }
if (!txid_found_in_up)
{
LOG_PRINT_L1("Found new pool tx: " << txid);
@@ -1670,7 +1652,7 @@ void wallet2::update_pool_state(bool refreshed)
if (!found)
{
// not one of those we sent ourselves
- txids.push_back(txid);
+ txids.push_back({txid, false});
}
else
{
@@ -1680,6 +1662,7 @@ void wallet2::update_pool_state(bool refreshed)
else
{
LOG_PRINT_L1("Already saw that one, it's for us");
+ txids.push_back({txid, true});
}
}
@@ -1688,8 +1671,8 @@ void wallet2::update_pool_state(bool refreshed)
{
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
- for (const auto &txid: txids)
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ for (const auto &p: txids)
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first));
MDEBUG("asking for " << txids.size() << " transactions");
req.decode_as_json = false;
m_daemon_rpc_mutex.lock();
@@ -1711,10 +1694,11 @@ void wallet2::update_pool_state(bool refreshed)
{
if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash))
{
- const std::vector<crypto::hash>::const_iterator i = std::find(txids.begin(), txids.end(), tx_hash);
+ const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(),
+ [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
if (i != txids.end())
{
- process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
+ process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen);
m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
{
@@ -1759,24 +1743,29 @@ void wallet2::update_pool_state(bool refreshed)
void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history)
{
std::list<crypto::hash> hashes;
- size_t current_index = m_blockchain.size();
+ const uint64_t checkpoint_height = m_checkpoints.get_max_height();
+ if (stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height)
+ {
+ // we will drop all these, so don't bother getting them
+ uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size();
+ while (missing_blocks-- > 0)
+ m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
+ m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
+ m_local_bc_height = m_blockchain.size();
+ short_chain_history.clear();
+ get_short_chain_history(short_chain_history);
+ }
+
+ size_t current_index = m_blockchain.size();
while(m_run.load(std::memory_order_relaxed) && current_index < stop_height)
{
pull_hashes(0, blocks_start_height, short_chain_history, hashes);
if (hashes.size() <= 3)
return;
if (hashes.size() + current_index < stop_height) {
- std::list<crypto::hash>::iterator right;
- // drop early 3 off, skipping the genesis block
- if (short_chain_history.size() > 3) {
- right = short_chain_history.end();
- std::advance(right,-1);
- std::list<crypto::hash>::iterator left = right;
- std::advance(left, -3);
- short_chain_history.erase(left, right);
- }
- right = hashes.end();
+ drop_from_short_history(short_chain_history, 3);
+ std::list<crypto::hash>::iterator right = hashes.end();
// prepend 3 more
for (int i = 0; i<3; i++) {
right--;
@@ -3073,11 +3062,11 @@ void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wall
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
+void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
{
for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {
- if ((!subaddr_account || *subaddr_account == i->second.m_subaddr_index.major) &&
- (subaddr_indices.empty() || subaddr_indices.count(i->second.m_subaddr_index.minor) == 1))
+ if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) &&
+ (subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1))
unconfirmed_payments.push_back(*i);
}
}
@@ -3272,7 +3261,7 @@ float wallet2::get_output_relatedness(const transfer_details &td0, const transfe
return 0.0f;
}
//----------------------------------------------------------------------------------------------------
-size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_indices, const std::list<size_t>& selected_transfers, bool smallest) const
+size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_indices, const std::vector<size_t>& selected_transfers, bool smallest) const
{
std::vector<size_t> candidates;
float best_relatedness = 1.0f;
@@ -3280,7 +3269,7 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve
{
const transfer_details &candidate = transfers[unused_indices[n]];
float relatedness = 0.0f;
- for (std::list<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i)
+ for (std::vector<size_t>::const_iterator i = selected_transfers.begin(); i != selected_transfers.end(); ++i)
{
float r = get_output_relatedness(candidate, transfers[*i]);
if (r > relatedness)
@@ -3321,7 +3310,7 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve
return pop_index (unused_indices, candidates[idx]);
}
//----------------------------------------------------------------------------------------------------
-size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::list<size_t>& selected_transfers, bool smallest) const
+size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::vector<size_t>& selected_transfers, bool smallest) const
{
return pop_best_value_from(m_transfers, unused_indices, selected_transfers, smallest);
}
@@ -3330,9 +3319,10 @@ size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::l
// returns:
// direct return: amount of money found
// modified reference: selected_transfers, a list of iterators/indices of input sources
-uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<size_t>& selected_transfers, bool trusted_daemon)
+uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon)
{
uint64_t found_money = 0;
+ selected_transfers.reserve(unused_transfers_indices.size());
while (found_money < needed_money && !unused_transfers_indices.empty())
{
size_t idx = pop_best_value(unused_transfers_indices, selected_transfers);
@@ -3950,6 +3940,19 @@ int wallet2::get_fee_algorithm()
return 1;
return 0;
}
+//------------------------------------------------------------------------------------------------------------------------------
+uint64_t wallet2::adjust_mixin(uint64_t mixin)
+{
+ if (mixin < 4 && use_fork_rules(6, 10)) {
+ MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5");
+ mixin = 4;
+ }
+ else if (mixin < 2 && use_fork_rules(2, 10)) {
+ MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3");
+ mixin = 2;
+ }
+ return mixin;
+}
//----------------------------------------------------------------------------------------------------
// separated the call(s) to wallet2::transfer into their own function
//
@@ -4073,7 +4076,7 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
return true;
}
-void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count) {
+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");
@@ -4177,7 +4180,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
}
}
-void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count)
+void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count)
{
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
outs.clear();
@@ -4412,7 +4415,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
template<typename T>
-void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count,
+void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
{
@@ -4566,7 +4569,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
LOG_PRINT_L2("transfer_selected done");
}
-void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count,
+void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx)
{
@@ -5129,7 +5132,7 @@ void wallet2::light_wallet_get_address_txs()
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);
+ unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash);
// for balance calculation
uint64_t wallet_total_sent = 0;
@@ -5195,7 +5198,11 @@ void wallet2::light_wallet_get_address_txs()
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);
+ // 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);
}
@@ -5325,11 +5332,27 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image,
crypto::key_image calculated_key_image;
cryptonote::keypair in_ephemeral;
- // Subaddresses aren't supported in mymonero/openmonero yet. Using empty values.
- const std::vector<crypto::public_key> additional_tx_pub_keys;
- const crypto::public_key pkey = crypto::null_pkey;
-
- cryptonote::generate_key_image_helper(get_account().get_keys(), m_subaddresses, pkey, tx_public_key, additional_tx_pub_keys, out_index, in_ephemeral, calculated_key_image);
+ // 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;
@@ -5361,7 +5384,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
uint64_t needed_money;
uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
struct TX {
- std::list<size_t> selected_transfers;
+ std::vector<size_t> selected_transfers;
std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx;
pending_tx ptx;
@@ -5802,7 +5825,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
std::vector<size_t> unused_dust_indices;
const bool use_rct = use_fork_rules(4, 0);
- THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unclocked balance in the entire wallet");
+ THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
std::map<uint32_t, uint64_t> balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
@@ -5839,11 +5862,32 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
}
+std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon)
+{
+ std::vector<size_t> unused_transfers_indices;
+ std::vector<size_t> unused_dust_indices;
+ const bool use_rct = use_fork_rules(4, 0);
+ // find output with the given key image
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details& td = m_transfers[i];
+ if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
+ {
+ if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
+ unused_transfers_indices.push_back(i);
+ else
+ unused_dust_indices.push_back(i);
+ break;
+ }
+ }
+ return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
+}
+
std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon)
{
uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
struct TX {
- std::list<size_t> selected_transfers;
+ std::vector<size_t> selected_transfers;
std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx;
pending_tx ptx;
@@ -6445,16 +6489,12 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
std::string data;
bool r = epee::file_io_utils::load_file_to_string(filename, data);
- if (!r)
- {
- fail_msg_writer() << tr("failed to read file ") << filename;
- return 0;
- }
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename);
+
const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC);
if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen))
{
- fail_msg_writer() << "Bad key image export file magic in " << filename;
- return 0;
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename);
}
try
@@ -6463,31 +6503,22 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
}
catch (const std::exception &e)
{
- fail_msg_writer() << "Failed to decrypt " << filename << ": " << e.what();
- return 0;
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what());
}
const size_t headerlen = 2 * sizeof(crypto::public_key);
- if (data.size() < headerlen)
- {
- fail_msg_writer() << "Bad data size from file " << filename;
- return 0;
- }
+ THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename);
const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
{
- fail_msg_writer() << "Key images from " << filename << " are for a different account";
- return 0;
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account");
}
const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature);
- if ((data.size() - headerlen) % record_size)
- {
- fail_msg_writer() << "Bad data size from file " << filename;
- return 0;
- }
+ THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size,
+ error::wallet_internal_error, std::string("Bad data size from file ") + filename);
size_t nki = (data.size() - headerlen) / record_size;
std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index f1e12a700..6e01d4e28 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -147,7 +147,7 @@ namespace tools
};
private:
- wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false),m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
+ wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshDefault), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false),m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
public:
static const char* tr(const char* str);
@@ -155,25 +155,22 @@ namespace tools
static bool has_testnet_option(const boost::program_options::variables_map& vm);
static void init_options(boost::program_options::options_description& desc_params);
- //! \return Password retrieved from prompt. Logs error on failure.
- static boost::optional<password_container> password_prompt(const bool new_password);
-
//! Uses stdin and stdout. Returns a wallet2 if no errors.
- static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file);
+ static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
static std::pair<std::unique_ptr<wallet2>, password_container>
- make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file);
+ make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
- static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm);
+ static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
//! Just parses variables.
- static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm);
+ static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
static bool verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only);
- wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), 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) {}
+ wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshDefault), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), 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) {}
struct tx_scan_info_t
{
@@ -244,6 +241,12 @@ namespace tools
bool m_incoming;
};
+ struct pool_payment_details
+ {
+ payment_details m_pd;
+ bool m_double_spend_seen;
+ };
+
struct unconfirmed_transfer_details
{
cryptonote::transaction_prefix m_tx;
@@ -282,7 +285,7 @@ namespace tools
std::vector<cryptonote::tx_source_entry> sources;
cryptonote::tx_destination_entry change_dts;
std::vector<cryptonote::tx_destination_entry> splitted_dsts; // split, includes change
- std::list<size_t> selected_transfers;
+ std::vector<size_t> selected_transfers;
std::vector<uint8_t> extra;
uint64_t unlock_time;
bool use_rct;
@@ -303,7 +306,7 @@ namespace tools
uint64_t dust, fee;
bool dust_added_to_fee;
cryptonote::tx_destination_entry change_dts;
- std::list<size_t> selected_transfers;
+ std::vector<size_t> selected_transfers;
std::string key_images;
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
@@ -501,10 +504,10 @@ namespace tools
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, bool trusted_daemon);
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outputs_count, const std::vector<size_t> &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon);
template<typename T>
- void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count,
+ void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
- void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<size_t>& selected_transfers, size_t fake_outputs_count,
+ void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
@@ -521,6 +524,7 @@ namespace tools
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon);
+ std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000);
@@ -530,7 +534,7 @@ namespace tools
void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
uint64_t min_height, uint64_t max_height = (uint64_t)-1, 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::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_local_bc_height; }
void rescan_spent();
@@ -585,7 +589,7 @@ namespace tools
std::unordered_map<crypto::hash, payment_details> m;
a & m;
for (std::unordered_map<crypto::hash, payment_details>::const_iterator i = m.begin(); i != m.end(); ++i)
- m_unconfirmed_payments.insert(*i);
+ m_unconfirmed_payments.insert(std::make_pair(i->first, pool_payment_details{i->second, false}));
}
if(ver < 14)
return;
@@ -607,7 +611,14 @@ namespace tools
a & m_address_book;
if(ver < 17)
return;
- a & m_unconfirmed_payments;
+ if (ver < 22)
+ {
+ // we're loading an old version, where m_unconfirmed_payments payload was payment_details
+ std::unordered_multimap<crypto::hash, payment_details> m;
+ a & m;
+ for (const auto &i: m)
+ m_unconfirmed_payments.insert(std::make_pair(i.first, pool_payment_details{i.second, false}));
+ }
if(ver < 18)
return;
a & m_scanned_pool_txs[0];
@@ -621,6 +632,9 @@ namespace tools
if(ver < 21)
return;
a & m_attributes;
+ if(ver < 22)
+ return;
+ a & m_unconfirmed_payments;
}
/*!
@@ -700,8 +714,8 @@ namespace tools
std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon);
std::vector<size_t> select_available_mixable_outputs(bool trusted_daemon);
- size_t pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_dust_indices, const std::list<size_t>& selected_transfers, bool smallest = false) const;
- size_t pop_best_value(std::vector<size_t> &unused_dust_indices, const std::list<size_t>& selected_transfers, bool smallest = false) const;
+ size_t pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_dust_indices, const std::vector<size_t>& selected_transfers, bool smallest = false) const;
+ size_t pop_best_value(std::vector<size_t> &unused_dust_indices, const std::vector<size_t>& selected_transfers, bool smallest = false) const;
void set_tx_note(const crypto::hash &txid, const std::string &note);
std::string get_tx_note(const crypto::hash &txid) const;
@@ -744,6 +758,7 @@ namespace tools
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
uint64_t get_per_kb_fee();
+ uint64_t adjust_mixin(uint64_t mixin);
// Light wallet specific functions
// fetch unspent outs from lw node and store in m_transfers
@@ -757,7 +772,7 @@ namespace tools
// 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);
+ 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
@@ -797,7 +812,7 @@ namespace tools
* \param password Password of wallet file
*/
bool load_keys(const std::string& keys_file_name, const std::string& password);
- void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool);
+ void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen);
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices);
void detach_blockchain(uint64_t height);
void get_short_chain_history(std::list<crypto::hash>& ids) const;
@@ -808,7 +823,7 @@ namespace tools
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history);
void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, bool &error);
void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added);
- uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<size_t>& selected_transfers, bool trusted_daemon);
+ uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon);
bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
@@ -827,7 +842,7 @@ namespace tools
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
void set_spent(size_t idx, uint64_t height);
void set_unspent(size_t idx);
- void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count);
+ void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
@@ -846,7 +861,7 @@ namespace tools
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;
- std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments;
+ std::unordered_multimap<crypto::hash, pool_payment_details> m_unconfirmed_payments;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
cryptonote::checkpoints m_checkpoints;
std::unordered_map<crypto::hash, std::vector<crypto::secret_key>> m_additional_tx_keys;
@@ -908,16 +923,17 @@ namespace tools
std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> > m_key_image_cache;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 21)
+BOOST_CLASS_VERSION(tools::wallet2, 22)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 8)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2)
+BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 7)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 5)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0)
-BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 1)
-BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 1)
+BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 2)
+BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 2)
namespace boost
{
@@ -1137,7 +1153,14 @@ namespace boost
}
a & x.m_subaddr_index;
}
-
+
+ template <class Archive>
+ inline void serialize(Archive& a, tools::wallet2::pool_payment_details& x, const boost::serialization::version_type ver)
+ {
+ a & x.m_pd;
+ a & x.m_double_spend_seen;
+ }
+
template <class Archive>
inline void serialize(Archive& a, tools::wallet2::address_book_row& x, const boost::serialization::version_type ver)
{
@@ -1172,7 +1195,16 @@ namespace boost
a & x.sources;
a & x.change_dts;
a & x.splitted_dsts;
- a & x.selected_transfers;
+ if (ver < 2)
+ {
+ // load list to vector
+ std::list<size_t> selected_transfers;
+ a & selected_transfers;
+ x.selected_transfers.clear();
+ x.selected_transfers.reserve(selected_transfers.size());
+ for (size_t t: selected_transfers)
+ x.selected_transfers.push_back(t);
+ }
a & x.extra;
a & x.unlock_time;
a & x.use_rct;
@@ -1184,6 +1216,9 @@ namespace boost
}
a & x.subaddr_account;
a & x.subaddr_indices;
+ if (ver < 2)
+ return;
+ a & x.selected_transfers;
}
template <class Archive>
@@ -1194,7 +1229,16 @@ namespace boost
a & x.fee;
a & x.dust_added_to_fee;
a & x.change_dts;
- a & x.selected_transfers;
+ if (ver < 2)
+ {
+ // load list to vector
+ std::list<size_t> selected_transfers;
+ a & selected_transfers;
+ x.selected_transfers.clear();
+ x.selected_transfers.reserve(selected_transfers.size());
+ for (size_t t: selected_transfers)
+ x.selected_transfers.push_back(t);
+ }
a & x.key_images;
a & x.tx_key;
a & x.dests;
@@ -1202,6 +1246,9 @@ namespace boost
if (ver < 1)
return;
a & x.additional_tx_keys;
+ if (ver < 2)
+ return;
+ a & x.selected_transfers;
}
}
}
@@ -1291,7 +1338,7 @@ namespace tools
// randomly select inputs for transaction
// throw if requested send amount is greater than (unlocked) amount available to send
- std::list<size_t> selected_transfers;
+ std::vector<size_t> selected_transfers;
uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers, trusted_daemon);
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee);
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index a8c150ca7..432c820cb 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -869,7 +869,7 @@ struct WalletManager
virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0;
//! checks for an update and returns version, hash and url
- static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir);
+ static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, std::string subdir);
};
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index df01ec238..cc6bb1de2 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -30,7 +30,6 @@
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include "common/i18n.h"
-#include "common/scoped_message_writer.h"
#include "common/util.h"
#include "misc_log_ex.h"
#include "string_tools.h"
@@ -50,6 +49,20 @@
#define DEFAULT_MAX_CONCURRENCY 0
#endif
+namespace
+{
+ class Print
+ {
+ public:
+ Print(const std::function<void(const std::string&, bool)> &p, bool em = false): print(p), emphasis(em) {}
+ ~Print() { print(ss.str(), emphasis); }
+ template<typename T> std::ostream &operator<<(const T &t) { ss << t; return ss; }
+ private:
+ const std::function<void(const std::string&, bool)> &print;
+ std::stringstream ss;
+ bool emphasis;
+ };
+}
namespace wallet_args
{
@@ -73,6 +86,7 @@ namespace wallet_args
const char* const usage,
boost::program_options::options_description desc_params,
const boost::program_options::positional_options_description& positional_options,
+ const std::function<void(const std::string&, bool)> &print,
const char *default_log_name,
bool log_to_console)
@@ -118,16 +132,16 @@ namespace wallet_args
if (command_line::get_arg(vm, command_line::arg_help))
{
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
- tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
+ Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
+ Print(print) << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
"daemon to work correctly.") << ENDL;
- tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage;
- tools::msg_writer() << desc_all;
+ Print(print) << wallet_args::tr("Usage:") << ENDL << " " << usage;
+ Print(print) << desc_all;
return false;
}
else if (command_line::get_arg(vm, command_line::arg_version))
{
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+ Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
return false;
}
@@ -142,7 +156,7 @@ namespace wallet_args
}
else
{
- tools::fail_msg_writer() << wallet_args::tr("Can't find config file ") << config;
+ MERROR(wallet_args::tr("Can't find config file ") << config);
return false;
}
}
@@ -167,14 +181,15 @@ namespace wallet_args
if(command_line::has_arg(vm, arg_max_concurrency))
tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));
- tools::scoped_message_writer(epee::console_color_white, true) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+ Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
if (!command_line::is_arg_defaulted(vm, arg_log_level))
MINFO("Setting log level = " << command_line::get_arg(vm, arg_log_level));
else
MINFO("Setting log levels = " << getenv("MONERO_LOGS"));
MINFO(wallet_args::tr("Logging to: ") << log_path);
- tools::scoped_message_writer(epee::console_color_white, true) << boost::format(wallet_args::tr("Logging to %s")) % log_path;
+
+ Print(print) << boost::format(wallet_args::tr("Logging to %s")) % log_path;
return {std::move(vm)};
}
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index cf23ffded..8974098ad 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -50,5 +50,6 @@ namespace wallet_args
const char* const usage,
boost::program_options::options_description desc_params,
const boost::program_options::positional_options_description& positional_options,
+ const std::function<void(const std::string&, bool)> &print,
const char *default_log_name, bool log_to_console = false);
}
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 9d66f125e..41eb77451 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -765,6 +765,12 @@ namespace tools
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
+#define THROW_WALLET_EXCEPTION(err_type, ...) \
+ do { \
+ LOG_ERROR("THROW EXCEPTION: " << #err_type); \
+ tools::error::throw_wallet_ex<err_type>(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ## __VA_ARGS__); \
+ } while(0)
+
#define THROW_WALLET_EXCEPTION_IF(cond, err_type, ...) \
if (cond) \
{ \
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 9e6a97bdc..09ed205c6 100755..100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -60,6 +60,16 @@ namespace
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
constexpr const char default_rpc_username[] = "monero";
+
+ boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
+ {
+ auto pwd_container = tools::password_container::prompt(verify, prompt);
+ if (!pwd_container)
+ {
+ MERROR("failed to read wallet password");
+ }
+ return pwd_container;
+ }
}
namespace tools
@@ -131,7 +141,7 @@ namespace tools
walvars = m_wallet;
else
{
- tmpwal = tools::wallet2::make_dummy(*m_vm);
+ tmpwal = tools::wallet2::make_dummy(*m_vm, password_prompter);
walvars = tmpwal.get();
}
boost::optional<epee::net_utils::http::login> http_login{};
@@ -227,19 +237,6 @@ namespace tools
return false;
}
//------------------------------------------------------------------------------------------------------------------------------
- uint64_t wallet_rpc_server::adjust_mixin(uint64_t mixin)
- {
- if (mixin < 4 && m_wallet->use_fork_rules(6, 10)) {
- MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5");
- mixin = 4;
- }
- else if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) {
- MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3");
- mixin = 2;
- }
- return mixin;
- }
- //------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
{
entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
@@ -299,8 +296,9 @@ namespace tools
entry.subaddr_index = { pd.m_subaddr_account, 0 };
}
//------------------------------------------------------------------------------------------------------------------------------
- void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
+ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd)
{
+ const tools::wallet2::payment_details &pd = ppd.m_pd;
entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
entry.payment_id = string_tools::pod_to_hex(payment_id);
if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
@@ -311,6 +309,7 @@ namespace tools
entry.unlock_time = pd.m_unlock_time;
entry.fee = 0; // TODO
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
+ entry.double_spend_seen = ppd.m_double_spend_seen;
entry.type = "pool";
entry.subaddr_index = pd.m_subaddr_index;
}
@@ -605,7 +604,7 @@ namespace tools
try
{
- uint64_t mixin = adjust_mixin(req.mixin);
+ uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
// reject proposed transactions if there are more than one. see on_transfer_split below.
@@ -665,7 +664,7 @@ namespace tools
try
{
- uint64_t mixin = adjust_mixin(req.mixin);
+ uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
uint64_t ptx_amount;
std::vector<wallet2::pending_tx> ptx_vector;
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
@@ -782,7 +781,7 @@ namespace tools
try
{
- uint64_t mixin = adjust_mixin(req.mixin);
+ uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
if (!req.do_not_relay)
@@ -813,6 +812,101 @@ namespace tools
}
return true;
}
+//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er)
+ {
+ std::vector<cryptonote::tx_destination_entry> dsts;
+ std::vector<uint8_t> extra;
+
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
+ // validate the transfer requested and populate dsts & extra
+ std::list<wallet_rpc::transfer_destination> destination;
+ destination.push_back(wallet_rpc::transfer_destination());
+ destination.back().amount = 0;
+ destination.back().address = req.address;
+ if (!validate_transfer(destination, req.payment_id, dsts, extra, er))
+ {
+ return false;
+ }
+
+ crypto::key_image ki;
+ if (!epee::string_tools::hex_to_pod(req.key_image, ki))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
+ er.message = "failed to parse key image";
+ return false;
+ }
+
+ try
+ {
+ uint64_t mixin = adjust_mixin(req.mixin);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
+
+ if (ptx_vector.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "No outputs found";
+ return false;
+ }
+ if (ptx_vector.size() > 1)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Multiple transactions are created, which is not supposed to happen";
+ return false;
+ }
+ if (ptx_vector[0].selected_transfers.size() > 1)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "The transaction uses multiple inputs, which is not supposed to happen";
+ return false;
+ }
+
+ if (!req.do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
+
+ // populate response with tx hashes
+ const wallet2::pending_tx &ptx = ptx_vector[0];
+ res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx));
+ if (req.get_tx_key)
+ {
+ res.tx_key = epee::string_tools::pod_to_hex(ptx.tx_key);
+ }
+ if (req.get_tx_hex)
+ {
+ cryptonote::blobdata blob;
+ tx_to_blob(ptx.tx, blob);
+ res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
+ }
+
+ return true;
+ }
+ catch (const tools::error::daemon_busy& e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
+ er.message = e.what();
+ return false;
+ }
+ catch (const std::exception& e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ catch (...)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
+ return false;
+ }
+ return true;
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er)
{
@@ -1069,6 +1163,7 @@ namespace tools
rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.tx_size = txBlob.size();
rpc_transfers.subaddr_index = td.m_subaddr_index.minor;
+ rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
res.transfers.push_back(rpc_transfers);
}
}
@@ -1357,9 +1452,9 @@ namespace tools
{
m_wallet->update_pool_state();
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments, req.account_index, req.subaddr_indices);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.pool.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.pool.back(), i->first, i->second);
}
@@ -1430,10 +1525,10 @@ namespace tools
m_wallet->update_pool_state();
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
m_wallet->get_unconfirmed_payments(pool_payments);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
- if (i->second.m_tx_hash == txid)
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
+ if (i->second.m_pd.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
@@ -1809,7 +1904,7 @@ namespace tools
command_line::add_arg(desc, arg_password);
po::store(po::parse_command_line(argc, argv, desc), vm2);
}
- std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2).first;
+ std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, password_prompter).first;
if (!wal)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1883,7 +1978,7 @@ namespace tools
}
std::unique_ptr<tools::wallet2> wal = nullptr;
try {
- wal = tools::wallet2::make_from_file(vm2, wallet_file).first;
+ wal = tools::wallet2::make_from_file(vm2, wallet_file, password_prompter).first;
}
catch (const std::exception& e)
{
@@ -1982,6 +2077,7 @@ int main(int argc, char** argv) {
"monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
desc_params,
po::positional_options_description(),
+ [](const std::string &s, bool emphasis){ epee::set_console_color(emphasis ? epee::console_color_white : epee::console_color_default, true); std::cout << s << std::endl; if (emphasis) epee::reset_console_color(); },
"monero-wallet-rpc.log",
true
);
@@ -2018,11 +2114,19 @@ int main(int argc, char** argv) {
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
if(!wallet_file.empty())
{
- wal = tools::wallet2::make_from_file(*vm, wallet_file).first;
+ wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompter).first;
}
else
{
- wal = tools::wallet2::make_from_json(*vm, from_json);
+ try
+ {
+ wal = tools::wallet2::make_from_json(*vm, from_json, password_prompter);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Error creating wallet: " << e.what());
+ return 1;
+ }
}
if (!wal)
{
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index b38726cb7..2a51ddf46 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -79,6 +79,7 @@ namespace tools
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
MAP_JON_RPC_WE("sweep_all", on_sweep_all, wallet_rpc::COMMAND_RPC_SWEEP_ALL)
+ MAP_JON_RPC_WE("sweep_single", on_sweep_single, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE)
MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE)
MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS)
MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS)
@@ -126,6 +127,7 @@ namespace tools
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_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);
+ bool on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er);
bool on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er);
@@ -163,7 +165,7 @@ namespace tools
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd);
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd);
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd);
- void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd);
+ void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &pd);
bool not_open(epee::json_rpc::error& er);
uint64_t adjust_mixin(uint64_t mixin);
void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index ffc2e2d49..9bcc5138a 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -476,6 +476,49 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_SWEEP_SINGLE
+ {
+ struct request
+ {
+ std::string address;
+ uint32_t priority;
+ uint64_t mixin;
+ uint64_t unlock_time;
+ std::string payment_id;
+ bool get_tx_key;
+ std::string key_image;
+ bool do_not_relay;
+ bool get_tx_hex;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(priority)
+ KV_SERIALIZE(mixin)
+ KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(get_tx_key)
+ KV_SERIALIZE(key_image)
+ KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(get_tx_hex, false)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string tx_hash;
+ std::string tx_key;
+ uint64_t fee;
+ std::string tx_blob;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tx_hash)
+ KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(fee)
+ KV_SERIALIZE(tx_blob)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_STORE
{
struct request
@@ -562,6 +605,7 @@ namespace wallet_rpc
std::string tx_hash;
uint64_t tx_size;
uint32_t subaddr_index;
+ std::string key_image;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
@@ -570,6 +614,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_size)
KV_SERIALIZE(subaddr_index)
+ KV_SERIALIZE(key_image)
END_KV_SERIALIZE_MAP()
};
@@ -580,11 +625,13 @@ namespace wallet_rpc
std::string transfer_type;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
+ bool verbose;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(transfer_type)
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
+ KV_SERIALIZE(verbose)
END_KV_SERIALIZE_MAP()
};
@@ -794,6 +841,7 @@ namespace wallet_rpc
std::string type;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
+ bool double_spend_seen;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txid);
@@ -807,6 +855,7 @@ namespace wallet_rpc
KV_SERIALIZE(type);
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index);
+ KV_SERIALIZE(double_spend_seen)
END_KV_SERIALIZE_MAP()
};