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.cpp186
-rw-r--r--src/wallet/api/wallet.h5
-rw-r--r--src/wallet/api/wallet_manager.cpp153
-rw-r--r--src/wallet/api/wallet_manager.h1
-rw-r--r--src/wallet/wallet2.cpp1022
-rw-r--r--src/wallet/wallet2.h140
-rw-r--r--src/wallet/wallet2_api.h20
-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.cpp452
-rw-r--r--src/wallet/wallet_rpc_server.h19
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h252
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h4
16 files changed, 1872 insertions, 491 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.cpp b/src/wallet/api/wallet.cpp
index 8e747d16b..fd0b65866 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1385,24 +1385,194 @@ std::string WalletImpl::getUserNote(const std::string &txid) const
return m_wallet->get_tx_note(htxid);
}
-std::string WalletImpl::getTxKey(const std::string &txid) const
+std::string WalletImpl::getTxKey(const std::string &txid_str) const
{
- cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(txid_str, txid))
{
- return "";
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return "";
}
- const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- if (m_wallet->get_tx_key(htxid, tx_key, additional_tx_keys))
+ if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
{
- return epee::string_tools::pod_to_hex(tx_key);
+ m_status = Status_Ok;
+ std::ostringstream oss;
+ oss << epee::string_tools::pod_to_hex(tx_key);
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
+ return oss.str();
}
else
{
- return "";
+ m_status = Status_Error;
+ m_errorString = tr("no tx keys found for this txid");
+ return "";
+ }
+}
+
+bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, const std::string &address_str, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return false;
+ }
+
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse tx key");
+ return false;
+ }
+ tx_key_str = tx_key_str.substr(64);
+ while (!tx_key_str.empty())
+ {
+ additional_tx_keys.resize(additional_tx_keys.size() + 1);
+ if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back()))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse tx key");
+ return false;
+ }
+ tx_key_str = tx_key_str.substr(64);
+ }
+
+ cryptonote::address_parse_info info;
+ if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse address");
+ return false;
+ }
+
+ try
+ {
+ m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, received, in_pool, confirmations);
+ m_status = Status_Ok;
+ return true;
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
+ }
+}
+
+std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const
+{
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return "";
+ }
+
+ cryptonote::address_parse_info info;
+ if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse address");
+ return "";
+ }
+
+ try
+ {
+ m_status = Status_Ok;
+ return m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, message);
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return "";
+ }
+}
+
+bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return false;
+ }
+
+ cryptonote::address_parse_info info;
+ if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse address");
+ return false;
+ }
+
+ try
+ {
+ good = m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, message, signature, received, in_pool, confirmations);
+ m_status = Status_Ok;
+ return true;
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
+ }
+}
+
+std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const {
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return "";
+ }
+
+ try
+ {
+ m_status = Status_Ok;
+ return m_wallet->get_spend_proof(txid, message);
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return "";
+ }
+}
+
+bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string &message, const std::string &signature, bool &good) const {
+ good = false;
+ crypto::hash txid;
+ if(!epee::string_tools::hex_to_pod(txid_str, txid))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse txid");
+ return false;
+ }
+
+ try
+ {
+ m_status = Status_Ok;
+ good = m_wallet->check_spend_proof(txid, message, signature);
+ return true;
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
}
}
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 051eda3ba..8ceabc843 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -137,6 +137,11 @@ public:
virtual bool setUserNote(const std::string &txid, const std::string &note);
virtual std::string getUserNote(const std::string &txid) const;
virtual std::string getTxKey(const std::string &txid) const;
+ virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const;
+ virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ virtual std::string getSpendProof(const std::string &txid, const std::string &message) const;
+ virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const;
virtual std::string signMessage(const std::string &message);
virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const;
virtual void startRefresh();
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index a64766c84..ee69ec028 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -189,155 +189,6 @@ bool WalletManagerImpl::connected(uint32_t *version) const
return true;
}
-bool WalletManagerImpl::checkPayment(const std::string &address_text, const std::string &txid_text, const std::string &txkey_text, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const
-{
- error = "";
- cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data) || txid_data.size() != sizeof(crypto::hash))
- {
- error = tr("failed to parse txid");
- return false;
- }
- crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
-
- if (txkey_text.size() < 64 || txkey_text.size() % 64)
- {
- error = tr("failed to parse tx key");
- return false;
- }
- crypto::secret_key tx_key;
- cryptonote::blobdata tx_key_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data) || tx_key_data.size() != sizeof(crypto::hash))
- {
- error = tr("failed to parse tx key");
- return false;
- }
- tx_key = *reinterpret_cast<const crypto::secret_key*>(tx_key_data.data());
-
- bool testnet = address_text[0] != '4';
- cryptonote::address_parse_info info;
- if(!cryptonote::get_account_address_from_str(info, testnet, address_text))
- {
- error = tr("failed to parse address");
- return false;
- }
-
- cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
- cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
- if (!connect_and_invoke(m_daemonAddress, "/gettransactions", req, res) ||
- (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
- {
- error = tr("failed to get transaction from daemon");
- return false;
- }
- cryptonote::blobdata tx_data;
- bool ok;
- if (res.txs.size() == 1)
- ok = epee::string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
- else
- ok = epee::string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
- if (!ok)
- {
- error = tr("failed to parse transaction from daemon");
- return false;
- }
- crypto::hash tx_hash, tx_prefix_hash;
- cryptonote::transaction tx;
- if (!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash))
- {
- error = tr("failed to validate transaction from daemon");
- return false;
- }
- if (tx_hash != txid)
- {
- error = tr("failed to get the right transaction from daemon");
- return false;
- }
-
- crypto::key_derivation derivation;
- if (!crypto::generate_key_derivation(info.address.m_view_public_key, tx_key, derivation))
- {
- error = tr("failed to generate key derivation from supplied parameters");
- return false;
- }
-
- received = 0;
- try {
- for (size_t n = 0; n < tx.vout.size(); ++n)
- {
- if (typeid(cryptonote::txout_to_key) != tx.vout[n].target.type())
- continue;
- const cryptonote::txout_to_key tx_out_to_key = boost::get<cryptonote::txout_to_key>(tx.vout[n].target);
- crypto::public_key pubkey;
- derive_public_key(derivation, n, info.address.m_spend_public_key, pubkey);
- if (pubkey == tx_out_to_key.key)
- {
- uint64_t amount;
- if (tx.version == 1)
- {
- amount = tx.vout[n].amount;
- }
- else
- {
- try
- {
- rct::key Ctmp;
- //rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
- crypto::key_derivation derivation;
- bool r = crypto::generate_key_derivation(info.address.m_view_public_key, tx_key, derivation);
- if (!r)
- {
- LOG_ERROR("Failed to generate key derivation to decode rct output " << n);
- amount = 0;
- }
- else
- {
- crypto::secret_key scalar1;
- crypto::derivation_to_scalar(derivation, n, scalar1);
- rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
- rct::key C = tx.rct_signatures.outPk[n].mask;
- rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
- if (rct::equalKeys(C, Ctmp))
- amount = rct::h2d(ecdh_info.amount);
- else
- amount = 0;
- }
- }
- catch (...) { amount = 0; }
- }
- received += amount;
- }
- }
- }
- catch(const std::exception &e)
- {
- LOG_ERROR("error: " << e.what());
- error = std::string(tr("error: ")) + e.what();
- return false;
- }
-
- if (received > 0)
- {
- LOG_PRINT_L1(get_account_address_as_str(testnet, info.is_subaddress, info.address) << " " << tr("received") << " " << cryptonote::print_money(received) << " " << tr("in txid") << " " << txid);
- }
- else
- {
- LOG_PRINT_L1(get_account_address_as_str(testnet, info.is_subaddress, info.address) << " " << tr("received nothing in txid") << " " << txid);
- }
- if (res.txs.front().in_pool)
- {
- height = 0;
- }
- else
- {
- height = res.txs.front().block_height;
- }
-
- return true;
-}
-
uint64_t WalletManagerImpl::blockchainHeight() const
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
@@ -434,12 +285,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/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 8455f0f16..5772eda05 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -55,7 +55,6 @@ public:
std::string errorString() const;
void setDaemonAddress(const std::string &address);
bool connected(uint32_t *version = NULL) const;
- bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const;
uint64_t blockchainHeight() const;
uint64_t blockchainTargetHeight() const;
uint64_t networkDifficulty() const;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index e9af93c05..c995c4333 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,19 @@ 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);
+ THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password"));
+
+ 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 +218,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 +247,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 +263,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 +280,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 +295,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 +311,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 +349,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 +369,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 +390,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 +429,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 +485,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 +508,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 +794,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 +832,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 +854,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 +889,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 +901,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 +1141,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 +1219,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 +1230,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 +1475,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 +1500,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 +1606,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 +1654,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 +1664,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 +1673,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 +1696,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 +1745,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 +3064,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 +3263,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 +3271,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 +3312,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 +3321,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 +3942,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 +4078,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 +4182,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 +4417,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 +4571,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)
{
@@ -5133,7 +5138,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;
@@ -5199,7 +5204,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);
}
@@ -5329,11 +5338,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;
@@ -5365,7 +5390,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;
@@ -5420,9 +5445,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// throw if attempting a transaction with no money
THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination);
- std::map<uint32_t, uint64_t> balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
+ std::map<uint32_t, uint64_t> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
+ std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account);
- if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked bakance
+ if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked balance
{
for (const auto& i : balance_per_subaddr)
subaddr_indices.insert(i.first);
@@ -5432,10 +5458,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through
uint64_t balance_subtotal = 0;
+ uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
+ {
balance_subtotal += balance_per_subaddr[index_minor];
+ unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor];
+ }
THROW_WALLET_EXCEPTION_IF(needed_money > balance_subtotal, error::not_enough_money,
balance_subtotal, needed_money, 0);
+ // first check overall balance is enough, then unlocked one, so we throw distinct exceptions
+ THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance_subtotal, error::not_enough_unlocked_money,
+ unlocked_balance_subtotal, needed_money, 0);
for (uint32_t i : subaddr_indices)
LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
@@ -5479,24 +5512,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
- // early out if we know we can't make it anyway
- // we could also check for being within FEE_PER_KB, but if the fee calculation
- // ever changes, this might be missed, so let this go through
- // first check overall balance is enough, then unlocked one, so we throw distinct exceptions
- THROW_WALLET_EXCEPTION_IF(needed_money > balance(subaddr_account), error::not_enough_money,
- unlocked_balance(subaddr_account), needed_money, 0);
- THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance(subaddr_account), error::not_enough_unlocked_money,
- unlocked_balance(subaddr_account), needed_money, 0);
-
// shuffle & sort output indices
{
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), g);
std::shuffle(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), g);
- auto sort_predicate = [&balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y)
+ auto sort_predicate = [&unlocked_balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y)
{
- return balance_per_subaddr[x.first] > balance_per_subaddr[y.first];
+ return unlocked_balance_per_subaddr[x.first] > unlocked_balance_per_subaddr[y.first];
};
std::sort(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), sort_predicate);
std::sort(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), sort_predicate);
@@ -5807,7 +5831,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);
@@ -5844,11 +5868,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;
@@ -6168,6 +6213,576 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
additional_tx_keys = j->second;
return true;
}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string &message)
+{
+ THROW_WALLET_EXCEPTION_IF(m_watch_only, error::wallet_internal_error,
+ "get_spend_proof requires spend secret key and is not available for a watch-only wallet");
+
+ // fetch tx from daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected 1");
+ cryptonote::blobdata bd;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd), error::wallet_internal_error, "failed to parse tx from hexstr");
+ cryptonote::transaction tx;
+ crypto::hash tx_hash, tx_prefix_hash;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch");
+
+ std::vector<std::vector<crypto::signature>> signatures;
+
+ // get signature prefix hash
+ std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
+ sig_prefix_data += message;
+ crypto::hash sig_prefix_hash;
+ crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
+
+ for(size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
+ if (in_key == nullptr)
+ continue;
+
+ // check if the key image belongs to us
+ const auto found = m_key_images.find(in_key->k_image);
+ if(found == m_key_images.end())
+ {
+ THROW_WALLET_EXCEPTION_IF(i > 0, error::wallet_internal_error, "subset of key images belong to us, very weird!");
+ THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "This tx wasn't generated by this wallet!");
+ }
+
+ // derive the real output keypair
+ const transfer_details& in_td = m_transfers[found->second];
+ const txout_to_key* const in_tx_out_pkey = boost::get<txout_to_key>(std::addressof(in_td.m_tx.vout[in_td.m_internal_output_index].target));
+ THROW_WALLET_EXCEPTION_IF(in_tx_out_pkey == nullptr, error::wallet_internal_error, "Output is not txout_to_key");
+ const crypto::public_key in_tx_pub_key = get_tx_pub_key_from_extra(in_td.m_tx, in_td.m_pk_index);
+ const std::vector<crypto::public_key> in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx);
+ keypair in_ephemeral;
+ crypto::key_image in_img;
+ THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img),
+ error::wallet_internal_error, "failed to generate key image");
+ THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch");
+
+ // get output pubkeys in the ring
+ const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
+ const size_t ring_size = in_key->key_offsets.size();
+ THROW_WALLET_EXCEPTION_IF(absolute_offsets.size() != ring_size, error::wallet_internal_error, "absolute offsets size is wrong");
+ COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
+ req.outputs.resize(ring_size);
+ for (size_t j = 0; j < ring_size; ++j)
+ {
+ req.outputs[j].amount = in_key->amount;
+ req.outputs[j].index = absolute_offsets[j];
+ }
+ COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.outs.size() != ring_size, error::wallet_internal_error,
+ "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
+ std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
+
+ // copy pubkey pointers
+ std::vector<const crypto::public_key *> p_output_keys;
+ for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
+ p_output_keys.push_back(&out.key);
+
+ // figure out real output index and secret key
+ size_t sec_index = -1;
+ for (size_t j = 0; j < ring_size; ++j)
+ {
+ if (res.outs[j].key == in_ephemeral.pub)
+ {
+ sec_index = j;
+ break;
+ }
+ }
+ THROW_WALLET_EXCEPTION_IF(sec_index >= ring_size, error::wallet_internal_error, "secret index not found");
+
+ // generate ring sig for this input
+ signatures.push_back(std::vector<crypto::signature>());
+ std::vector<crypto::signature>& sigs = signatures.back();
+ sigs.resize(in_key->key_offsets.size());
+ crypto::generate_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, in_ephemeral.sec, sec_index, sigs.data());
+ }
+
+ std::string sig_str = "SpendProofV1";
+ for (const std::vector<crypto::signature>& ring_sig : signatures)
+ for (const crypto::signature& sig : ring_sig)
+ sig_str += tools::base58::encode(std::string((const char *)&sig, sizeof(crypto::signature)));
+ return sig_str;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str)
+{
+ const std::string header = "SpendProofV1";
+ const size_t header_len = header.size();
+ THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
+ "Signature header check error");
+
+ // fetch tx from daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected 1");
+ cryptonote::blobdata bd;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd), error::wallet_internal_error, "failed to parse tx from hexstr");
+ cryptonote::transaction tx;
+ crypto::hash tx_hash, tx_prefix_hash;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch");
+
+ // check signature size
+ size_t num_sigs = 0;
+ for(size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
+ if (in_key != nullptr)
+ num_sigs += in_key->key_offsets.size();
+ }
+ std::vector<std::vector<crypto::signature>> signatures = { std::vector<crypto::signature>(1) };
+ const size_t sig_len = tools::base58::encode(std::string((const char *)&signatures[0][0], sizeof(crypto::signature))).size();
+ THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * sig_len,
+ error::wallet_internal_error, "incorrect signature size");
+
+ // decode base58
+ signatures.clear();
+ size_t offset = header_len;
+ for(size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
+ if (in_key == nullptr)
+ continue;
+ signatures.resize(signatures.size() + 1);
+ signatures.back().resize(in_key->key_offsets.size());
+ for (size_t j = 0; j < in_key->key_offsets.size(); ++j)
+ {
+ std::string sig_decoded;
+ THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, sig_len), sig_decoded), error::wallet_internal_error, "Signature decoding error");
+ THROW_WALLET_EXCEPTION_IF(sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error, "Signature decoding error");
+ memcpy(&signatures.back()[j], sig_decoded.data(), sizeof(crypto::signature));
+ offset += sig_len;
+ }
+ }
+
+ // get signature prefix hash
+ std::string sig_prefix_data((const char*)&txid, sizeof(crypto::hash));
+ sig_prefix_data += message;
+ crypto::hash sig_prefix_hash;
+ crypto::cn_fast_hash(sig_prefix_data.data(), sig_prefix_data.size(), sig_prefix_hash);
+
+ std::vector<std::vector<crypto::signature>>::const_iterator sig_iter = signatures.cbegin();
+ for(size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ const txin_to_key* const in_key = boost::get<txin_to_key>(std::addressof(tx.vin[i]));
+ if (in_key == nullptr)
+ continue;
+
+ // get output pubkeys in the ring
+ COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
+ const std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key->key_offsets);
+ req.outputs.resize(absolute_offsets.size());
+ for (size_t j = 0; j < absolute_offsets.size(); ++j)
+ {
+ req.outputs[j].amount = in_key->amount;
+ req.outputs[j].index = absolute_offsets[j];
+ }
+ COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "get_outs.bin");
+ THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
+ "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
+ std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
+
+ // copy pointers
+ std::vector<const crypto::public_key *> p_output_keys;
+ for (const COMMAND_RPC_GET_OUTPUTS_BIN::outkey &out : res.outs)
+ p_output_keys.push_back(&out.key);
+
+ // check this ring
+ if (!crypto::check_ring_signature(sig_prefix_hash, in_key->k_image, p_output_keys, sig_iter->data()))
+ return false;
+ ++sig_iter;
+ }
+ THROW_WALLET_EXCEPTION_IF(sig_iter != signatures.cend(), error::wallet_internal_error, "Signature iterator didn't reach the end");
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+
+void wallet2::check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ crypto::key_derivation derivation;
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation), error::wallet_internal_error,
+ "Failed to generate key derivation from supplied parameters");
+
+ std::vector<crypto::key_derivation> additional_derivations;
+ additional_derivations.resize(additional_tx_keys.size());
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, additional_tx_keys[i], additional_derivations[i]), error::wallet_internal_error,
+ "Failed to generate key derivation from supplied parameters");
+
+ check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
+}
+
+void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ m_daemon_rpc_mutex.lock();
+ bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ cryptonote::blobdata tx_data;
+ if (res.txs.size() == 1)
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
+ else
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+
+ crypto::hash tx_hash, tx_prefix_hash;
+ cryptonote::transaction tx;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error,
+ "Failed to validate transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error,
+ "Failed to get the right transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(!additional_derivations.empty() && additional_derivations.size() != tx.vout.size(), error::wallet_internal_error,
+ "The size of additional derivations is wrong");
+
+ received = 0;
+ for (size_t n = 0; n < tx.vout.size(); ++n)
+ {
+ const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target));
+ if (!out_key)
+ continue;
+
+ crypto::public_key derived_out_key;
+ derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
+ bool found = out_key->key == derived_out_key;
+ crypto::key_derivation found_derivation = derivation;
+ if (!found && !additional_derivations.empty())
+ {
+ derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
+ found = out_key->key == derived_out_key;
+ found_derivation = additional_derivations[n];
+ }
+
+ if (found)
+ {
+ uint64_t amount;
+ if (tx.version == 1 || tx.rct_signatures.type == rct::RCTTypeNull)
+ {
+ amount = tx.vout[n].amount;
+ }
+ else
+ {
+ crypto::secret_key scalar1;
+ crypto::derivation_to_scalar(found_derivation, n, scalar1);
+ rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
+ const rct::key C = tx.rct_signatures.outPk[n].mask;
+ rct::key Ctmp;
+ rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
+ if (rct::equalKeys(C, Ctmp))
+ amount = rct::h2d(ecdh_info.amount);
+ else
+ amount = 0;
+ }
+ received += amount;
+ }
+ }
+
+ in_pool = res.txs.front().in_pool;
+ confirmations = (uint64_t)-1;
+ if (!in_pool)
+ {
+ std::string err;
+ uint64_t bc_height = get_daemon_blockchain_height(err);
+ if (err.empty())
+ confirmations = bc_height - (res.txs.front().block_height + 1);
+ }
+}
+
+std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
+{
+ // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
+ const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
+
+ std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
+ prefix_data += message;
+ crypto::hash prefix_hash;
+ crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
+
+ std::vector<crypto::public_key> shared_secret;
+ std::vector<crypto::signature> sig;
+ std::string sig_str;
+ if (is_out)
+ {
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ THROW_WALLET_EXCEPTION_IF(!get_tx_key(txid, tx_key, additional_tx_keys), error::wallet_internal_error, "Tx secret key wasn't found in the wallet file.");
+
+ const size_t num_sigs = 1 + additional_tx_keys.size();
+ shared_secret.resize(num_sigs);
+ sig.resize(num_sigs);
+
+ shared_secret[0] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
+ crypto::public_key tx_pub_key;
+ if (is_subaddress)
+ {
+ tx_pub_key = rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key)));
+ crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]);
+ }
+ else
+ {
+ crypto::secret_key_to_public_key(tx_key, tx_pub_key);
+ crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
+ }
+ for (size_t i = 1; i < num_sigs; ++i)
+ {
+ shared_secret[i] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1])));
+ if (is_subaddress)
+ {
+ tx_pub_key = rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1])));
+ crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
+ }
+ else
+ {
+ crypto::secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key);
+ crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
+ }
+ }
+ sig_str = std::string("OutProofV1");
+ }
+ else
+ {
+ // fetch tx pubkey from the daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ m_daemon_rpc_mutex.lock();
+ bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ cryptonote::blobdata tx_data;
+ if (res.txs.size() == 1)
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
+ else
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+
+ crypto::hash tx_hash, tx_prefix_hash;
+ cryptonote::transaction tx;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error,
+ "Failed to validate transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+
+ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
+ THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
+
+ std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
+ const size_t num_sigs = 1 + additional_tx_pub_keys.size();
+ shared_secret.resize(num_sigs);
+ sig.resize(num_sigs);
+
+ const crypto::secret_key& a = m_account.get_keys().m_view_secret_key;
+ shared_secret[0] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(tx_pub_key), rct::sk2rct(a)));
+ if (is_subaddress)
+ {
+ crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]);
+ }
+ else
+ {
+ crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]);
+ }
+ for (size_t i = 1; i < num_sigs; ++i)
+ {
+ shared_secret[i] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a)));
+ if (is_subaddress)
+ {
+ crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]);
+ }
+ else
+ {
+ crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]);
+ }
+ }
+ sig_str = std::string("InProofV1");
+ }
+ const size_t num_sigs = shared_secret.size();
+
+ // check if this address actually received any funds
+ crypto::key_derivation derivation;
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
+ std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
+ for (size_t i = 1; i < num_sigs; ++i)
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+ check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
+ THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx."));
+
+ // concatenate all signature strings
+ for (size_t i = 0; i < num_sigs; ++i)
+ sig_str +=
+ tools::base58::encode(std::string((const char *)&shared_secret[i], sizeof(crypto::public_key))) +
+ tools::base58::encode(std::string((const char *)&sig[i], sizeof(crypto::signature)));
+ return sig_str;
+}
+
+bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations)
+{
+ const bool is_out = sig_str.substr(0, 3) == "Out";
+ const std::string header = is_out ? "OutProofV1" : "InProofV1";
+ const size_t header_len = header.size();
+ THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error,
+ "Signature header check error");
+
+ // decode base58
+ std::vector<crypto::public_key> shared_secret(1);
+ std::vector<crypto::signature> sig(1);
+ const size_t pk_len = tools::base58::encode(std::string((const char *)&shared_secret[0], sizeof(crypto::public_key))).size();
+ const size_t sig_len = tools::base58::encode(std::string((const char *)&sig[0], sizeof(crypto::signature))).size();
+ const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len);
+ THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * (pk_len + sig_len), error::wallet_internal_error,
+ "Wrong signature size");
+ shared_secret.resize(num_sigs);
+ sig.resize(num_sigs);
+ for (size_t i = 0; i < num_sigs; ++i)
+ {
+ std::string pk_decoded;
+ std::string sig_decoded;
+ const size_t offset = header_len + i * (pk_len + sig_len);
+ THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, pk_len), pk_decoded), error::wallet_internal_error,
+ "Signature decoding error");
+ THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset + pk_len, sig_len), sig_decoded), error::wallet_internal_error,
+ "Signature decoding error");
+ THROW_WALLET_EXCEPTION_IF(sizeof(crypto::public_key) != pk_decoded.size() || sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error,
+ "Signature decoding error");
+ memcpy(&shared_secret[i], pk_decoded.data(), sizeof(crypto::public_key));
+ memcpy(&sig[i], sig_decoded.data(), sizeof(crypto::signature));
+ }
+
+ // fetch tx pubkey from the daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ m_daemon_rpc_mutex.lock();
+ bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ cryptonote::blobdata tx_data;
+ if (res.txs.size() == 1)
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
+ else
+ ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+
+ crypto::hash tx_hash, tx_prefix_hash;
+ cryptonote::transaction tx;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error,
+ "Failed to validate transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+
+ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
+ THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found");
+
+ std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
+ THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.size() + 1 != num_sigs, error::wallet_internal_error, "Signature size mismatch with additional tx pubkeys");
+
+ std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
+ prefix_data += message;
+ crypto::hash prefix_hash;
+ crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
+
+ // check signature
+ std::vector<int> good_signature(num_sigs, 0);
+ if (is_out)
+ {
+ good_signature[0] = is_subaddress ?
+ crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
+ crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0]);
+
+ for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
+ {
+ good_signature[i + 1] = is_subaddress ?
+ crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
+ crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, boost::none, shared_secret[i + 1], sig[i + 1]);
+ }
+ }
+ else
+ {
+ good_signature[0] = is_subaddress ?
+ crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0]) :
+ crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0]);
+
+ for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
+ {
+ good_signature[i + 1] = is_subaddress ?
+ crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) :
+ crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], boost::none, shared_secret[i + 1], sig[i + 1]);
+ }
+ }
+
+ if (std::any_of(good_signature.begin(), good_signature.end(), [](int i) { return i > 0; }))
+ {
+ // obtain key derivation by multiplying scalar 1 to the shared secret
+ crypto::key_derivation derivation;
+ if (good_signature[0])
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[0], rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
+
+ std::vector<crypto::key_derivation> additional_derivations(num_sigs - 1);
+ for (size_t i = 1; i < num_sigs; ++i)
+ if (good_signature[i])
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation");
+
+ check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations);
+ return true;
+ }
+ return false;
+}
std::string wallet2::get_wallet_file() const
{
@@ -6450,16 +7065,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
@@ -6468,31 +7079,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..ce0c67fc3 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,13 +285,26 @@ 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;
std::vector<cryptonote::tx_destination_entry> dests; // original setup, does not include change
uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer
std::set<uint32_t> subaddr_indices; // set of address indices used as inputs in this transfer
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(sources)
+ FIELD(change_dts)
+ FIELD(splitted_dsts)
+ FIELD(selected_transfers)
+ FIELD(extra)
+ FIELD(unlock_time)
+ FIELD(use_rct)
+ FIELD(dests)
+ FIELD(subaddr_account)
+ FIELD(subaddr_indices)
+ END_SERIALIZE()
};
typedef std::vector<transfer_details> transfer_container;
@@ -303,13 +319,27 @@ 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;
std::vector<cryptonote::tx_destination_entry> dests;
tx_construction_data construction_data;
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(tx)
+ FIELD(dust)
+ FIELD(fee)
+ FIELD(dust_added_to_fee)
+ FIELD(change_dts)
+ FIELD(selected_transfers)
+ FIELD(key_images)
+ FIELD(tx_key)
+ FIELD(additional_tx_keys)
+ FIELD(dests)
+ FIELD(construction_data)
+ END_SERIALIZE()
};
// The term "Unsigned tx" is not really a tx since it's not signed yet.
@@ -501,10 +531,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 +551,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 +561,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 +616,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 +638,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 +659,9 @@ namespace tools
if(ver < 21)
return;
a & m_attributes;
+ if(ver < 22)
+ return;
+ a & m_unconfirmed_payments;
}
/*!
@@ -670,7 +711,13 @@ namespace tools
uint32_t get_confirm_backlog_threshold() const { return m_confirm_backlog_threshold; };
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
+ void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ void check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message);
+ bool check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations);
+ std::string get_spend_proof(const crypto::hash &txid, const std::string &message);
+ bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str);
/*!
* \brief GUI Address book get/store
*/
@@ -700,8 +747,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 +791,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 +805,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 +845,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 +856,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 +875,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 +894,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 +956,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 +1186,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 +1228,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 +1249,9 @@ namespace boost
}
a & x.subaddr_account;
a & x.subaddr_indices;
+ if (ver < 2)
+ return;
+ a & x.selected_transfers;
}
template <class Archive>
@@ -1194,7 +1262,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 +1279,9 @@ namespace boost
if (ver < 1)
return;
a & x.additional_tx_keys;
+ if (ver < 2)
+ return;
+ a & x.selected_transfers;
}
}
}
@@ -1291,7 +1371,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..b1f8369a3 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -700,6 +700,11 @@ struct Wallet
*/
virtual std::string getUserNote(const std::string &txid) const = 0;
virtual std::string getTxKey(const std::string &txid) const = 0;
+ virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
+ virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const = 0;
+ virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
+ virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0;
+ virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const = 0;
/*
* \brief signMessage - sign a message with the spend private key
@@ -819,19 +824,6 @@ struct WalletManager
*/
virtual std::vector<std::string> findWallets(const std::string &path) = 0;
- /*!
- * \brief checkPayment - checks a payment was made using a txkey
- * \param address - the address the payment was sent to
- * \param txid - the transaction id for that payment
- * \param txkey - the transaction's secret key
- * \param daemon_address - the address (host and port) to the daemon to request transaction data
- * \param received - if succesful, will hold the amount of monero received
- * \param height - if succesful, will hold the height of the transaction (0 if only in the pool)
- * \param error - if unsuccesful, will hold an error string with more information about the error
- * \return - true is succesful, false otherwise
- */
- virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
-
//! returns verbose error string regarding last error;
virtual std::string errorString() const = 0;
@@ -869,7 +861,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..fe7f51617 100755..100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -30,6 +30,7 @@
#include <boost/format.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/filesystem/operations.hpp>
+#include <boost/algorithm/string.hpp>
#include <cstdint>
#include "include_base_utils.h"
using namespace epee;
@@ -58,8 +59,19 @@ namespace
const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"};
const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false};
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
+ const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
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 +143,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 +239,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 +298,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 +311,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 +606,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.
@@ -624,6 +625,8 @@ namespace tools
if (req.get_tx_key)
{
res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
+ for (const crypto::secret_key& additional_tx_key : ptx_vector.back().additional_tx_keys)
+ res.tx_key += epee::string_tools::pod_to_hex(additional_tx_key);
}
res.fee = ptx_vector.back().fee;
@@ -633,6 +636,13 @@ namespace tools
tx_to_blob(ptx_vector.back().tx, blob);
res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, ptx_vector.back());
+ res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
+ }
return true;
}
catch (const std::exception& e)
@@ -665,7 +675,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");
@@ -680,12 +690,14 @@ namespace tools
}
// populate response with tx hashes
- for (auto & ptx : ptx_vector)
+ for (const auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
{
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
+ for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
+ res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key);
}
// Compute amount leaving wallet in tx. By convention dests does not include change outputs
ptx_amount = 0;
@@ -701,6 +713,13 @@ namespace tools
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
+ res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
+ }
}
return true;
@@ -731,7 +750,7 @@ namespace tools
m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
- for (auto & ptx : ptx_vector)
+ for (const auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
@@ -745,6 +764,13 @@ namespace tools
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
+ res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
+ }
}
return true;
@@ -782,14 +808,14 @@ 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)
m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
- for (auto & ptx : ptx_vector)
+ for (const auto & ptx : ptx_vector)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
@@ -802,6 +828,13 @@ namespace tools
tx_to_blob(ptx.tx, blob);
res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
}
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
+ res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
+ }
}
return true;
@@ -813,6 +846,149 @@ 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 = m_wallet->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);
+ }
+ if (req.get_tx_metadata)
+ {
+ std::ostringstream oss;
+ binary_archive<true> ar(oss);
+ ::serialization::serialize(ar, const_cast<tools::wallet2::pending_tx&>(ptx));
+ res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
+ }
+
+ 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_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.hex, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+
+ std::stringstream ss;
+ ss << blob;
+ binary_archive<false> ba(ss);
+
+ tools::wallet2::pending_tx ptx;
+ bool r = ::serialization::serialize(ba, ptx);
+ if (!r)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_TX_METADATA;
+ er.message = "Failed to parse tx metadata.";
+ return false;
+ }
+
+ try
+ {
+ m_wallet->commit_tx(ptx);
+ }
+ catch(const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
+ er.message = "Failed to commit tx.";
+ return false;
+ }
+
+ res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx));
+
+ 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 +1245,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);
}
}
@@ -1301,6 +1478,208 @@ namespace tools
res.value = m_wallet->get_attribute(req.key);
return true;
}
+ bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ if (!m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_TXKEY;
+ er.message = "No tx secret key is stored for this tx";
+ return false;
+ }
+
+ std::ostringstream oss;
+ oss << epee::string_tools::pod_to_hex(tx_key);
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
+ res.tx_key = oss.str();
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_check_tx_key(const wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ std::string tx_key_str = req.tx_key;
+ crypto::secret_key tx_key;
+ if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
+ er.message = "Tx key has invalid format";
+ return false;
+ }
+ tx_key_str = tx_key_str.substr(64);
+ std::vector<crypto::secret_key> additional_tx_keys;
+ while (!tx_key_str.empty())
+ {
+ additional_tx_keys.resize(additional_tx_keys.size() + 1);
+ if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back()))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
+ er.message = "Tx key has invalid format";
+ return false;
+ }
+ tx_key_str = tx_key_str.substr(64);
+ }
+
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_wallet->testnet(), req.address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+
+ try
+ {
+ m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, res.received, res.in_pool, res.confirmations);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_wallet->testnet(), req.address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+
+ try
+ {
+ res.signature = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, req.message);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_check_tx_proof(const wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_wallet->testnet(), req.address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+
+ try
+ {
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+ res.good = m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, req.message, req.signature, res.received, res.in_pool, res.confirmations);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_spend_proof(const wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ try
+ {
+ res.signature = m_wallet->get_spend_proof(txid, req.message);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_check_spend_proof(const wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(req.txid, txid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ try
+ {
+ res.good = m_wallet->check_spend_proof(txid, req.message, req.signature);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er)
{
@@ -1357,9 +1736,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 +1809,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 +2188,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, nullptr).first;
if (!wal)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1883,7 +2262,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, nullptr).first;
}
catch (const std::exception& e)
{
@@ -1975,13 +2354,14 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_wallet_file);
command_line::add_arg(desc_params, arg_from_json);
command_line::add_arg(desc_params, arg_wallet_dir);
-
+ command_line::add_arg(desc_params, arg_prompt_for_password);
const auto vm = wallet_args::main(
argc, 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
);
@@ -1996,6 +2376,8 @@ int main(int argc, char** argv) {
const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file);
const auto from_json = command_line::get_arg(*vm, arg_from_json);
const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir);
+ const auto prompt_for_password = command_line::get_arg(*vm, arg_prompt_for_password);
+ const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
if(!wallet_file.empty() && !from_json.empty())
{
@@ -2018,11 +2400,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_prompt).first;
}
else
{
- wal = tools::wallet2::make_from_json(*vm, from_json);
+ try
+ {
+ wal = tools::wallet2::make_from_json(*vm, from_json, password_prompt);
+ }
+ 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..9455c4769 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -79,6 +79,8 @@ 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("relay_tx", on_relay_tx, wallet_rpc::COMMAND_RPC_RELAY_TX)
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)
@@ -92,6 +94,12 @@ namespace tools
MAP_JON_RPC_WE("get_tx_notes", on_get_tx_notes, wallet_rpc::COMMAND_RPC_GET_TX_NOTES)
MAP_JON_RPC_WE("set_attribute", on_set_attribute, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE)
MAP_JON_RPC_WE("get_attribute", on_get_attribute, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE)
+ MAP_JON_RPC_WE("get_tx_key", on_get_tx_key, wallet_rpc::COMMAND_RPC_GET_TX_KEY)
+ MAP_JON_RPC_WE("check_tx_key", on_check_tx_key, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY)
+ MAP_JON_RPC_WE("get_tx_proof", on_get_tx_proof, wallet_rpc::COMMAND_RPC_GET_TX_PROOF)
+ MAP_JON_RPC_WE("check_tx_proof", on_check_tx_proof, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF)
+ MAP_JON_RPC_WE("get_spend_proof", on_get_spend_proof, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF)
+ MAP_JON_RPC_WE("check_spend_proof", on_check_spend_proof, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF)
MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS)
MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID)
MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN)
@@ -126,6 +134,8 @@ 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_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::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);
@@ -138,6 +148,12 @@ namespace tools
bool on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er);
bool on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er);
bool on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er);
+ bool on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er);
+ bool on_check_tx_key(const wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::response& res, epee::json_rpc::error& er);
+ bool on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er);
+ bool on_check_tx_proof(const wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::response& res, epee::json_rpc::error& er);
+ bool on_get_spend_proof(const wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::response& res, epee::json_rpc::error& er);
+ bool on_check_spend_proof(const wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::response& res, epee::json_rpc::error& er);
bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er);
bool on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er);
bool on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er);
@@ -163,9 +179,8 @@ 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);
wallet2 *m_wallet;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index ffc2e2d49..e084d9e6d 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -291,6 +291,7 @@ namespace wallet_rpc
bool get_tx_key;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -303,6 +304,7 @@ namespace wallet_rpc
KV_SERIALIZE(get_tx_key)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
@@ -313,6 +315,7 @@ namespace wallet_rpc
std::list<std::string> amount_keys;
uint64_t fee;
std::string tx_blob;
+ std::string tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
@@ -320,6 +323,7 @@ namespace wallet_rpc
KV_SERIALIZE(amount_keys)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
+ KV_SERIALIZE(tx_metadata)
END_KV_SERIALIZE_MAP()
};
};
@@ -338,6 +342,7 @@ namespace wallet_rpc
bool get_tx_keys;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -350,6 +355,7 @@ namespace wallet_rpc
KV_SERIALIZE(get_tx_keys)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
@@ -369,6 +375,7 @@ namespace wallet_rpc
std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
+ std::list<std::string> tx_metadata_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
@@ -376,6 +383,7 @@ namespace wallet_rpc
KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
+ KV_SERIALIZE(tx_metadata_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -387,11 +395,13 @@ namespace wallet_rpc
bool get_tx_keys;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(get_tx_keys)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
@@ -410,12 +420,14 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
+ std::list<std::string> tx_metadata_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
+ KV_SERIALIZE(tx_metadata_list)
END_KV_SERIALIZE_MAP()
};
};
@@ -435,6 +447,7 @@ namespace wallet_rpc
uint64_t below_amount;
bool do_not_relay;
bool get_tx_hex;
+ bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
@@ -448,6 +461,7 @@ namespace wallet_rpc
KV_SERIALIZE(below_amount)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
@@ -466,12 +480,88 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
+ std::list<std::string> tx_metadata_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
+ KV_SERIALIZE(tx_metadata_list)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ 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;
+ bool get_tx_metadata;
+
+ 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)
+ KV_SERIALIZE_OPT(get_tx_metadata, false)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string tx_hash;
+ std::string tx_key;
+ uint64_t fee;
+ std::string tx_blob;
+ std::string tx_metadata;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tx_hash)
+ KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(fee)
+ KV_SERIALIZE(tx_blob)
+ KV_SERIALIZE(tx_metadata)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_RELAY_TX
+ {
+ struct request
+ {
+ std::string hex;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(hex)
+ 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()
};
};
@@ -562,6 +652,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 +661,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 +672,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()
};
@@ -781,6 +875,114 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_GET_TX_KEY
+ {
+ struct request
+ {
+ std::string txid;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string tx_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tx_key)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_CHECK_TX_KEY
+ {
+ struct request
+ {
+ std::string txid;
+ std::string tx_key;
+ std::string address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(address)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(received)
+ KV_SERIALIZE(in_pool)
+ KV_SERIALIZE(confirmations)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_GET_TX_PROOF
+ {
+ struct request
+ {
+ std::string txid;
+ std::string address;
+ std::string message;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(message)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_CHECK_TX_PROOF
+ {
+ struct request
+ {
+ std::string txid;
+ std::string address;
+ std::string message;
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(message)
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ bool good;
+ uint64_t received;
+ bool in_pool;
+ uint64_t confirmations;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(good)
+ KV_SERIALIZE(received)
+ KV_SERIALIZE(in_pool)
+ KV_SERIALIZE(confirmations)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct transfer_entry
{
std::string txid;
@@ -794,6 +996,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,9 +1010,58 @@ namespace wallet_rpc
KV_SERIALIZE(type);
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index);
+ KV_SERIALIZE(double_spend_seen)
END_KV_SERIALIZE_MAP()
};
+ struct COMMAND_RPC_GET_SPEND_PROOF
+ {
+ struct request
+ {
+ std::string txid;
+ std::string message;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(message)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_CHECK_SPEND_PROOF
+ {
+ struct request
+ {
+ std::string txid;
+ std::string message;
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txid)
+ KV_SERIALIZE(message)
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ bool good;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(good)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_GET_TRANSFERS
{
struct request
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index e74e9110b..c3f3e20d1 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -54,3 +54,7 @@
#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS -21
#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD -22
#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR -23
+#define WALLET_RPC_ERROR_CODE_NO_TXKEY -24
+#define WALLET_RPC_ERROR_CODE_WRONG_KEY -25
+#define WALLET_RPC_ERROR_CODE_BAD_HEX -26
+#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA -27