aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt3
-rw-r--r--src/wallet/api/CMakeLists.txt2
-rw-r--r--src/wallet/api/address_book.cpp2
-rw-r--r--src/wallet/api/address_book.h2
-rw-r--r--src/wallet/api/pending_transaction.cpp19
-rw-r--r--src/wallet/api/pending_transaction.h4
-rw-r--r--src/wallet/api/subaddress.cpp2
-rw-r--r--src/wallet/api/subaddress.h2
-rw-r--r--src/wallet/api/subaddress_account.cpp2
-rw-r--r--src/wallet/api/subaddress_account.h2
-rw-r--r--src/wallet/api/transaction_history.cpp2
-rw-r--r--src/wallet/api/transaction_history.h2
-rw-r--r--src/wallet/api/transaction_info.cpp2
-rw-r--r--src/wallet/api/transaction_info.h2
-rw-r--r--src/wallet/api/unsigned_transaction.cpp2
-rw-r--r--src/wallet/api/unsigned_transaction.h2
-rw-r--r--src/wallet/api/utils.cpp2
-rw-r--r--src/wallet/api/wallet.cpp90
-rw-r--r--src/wallet/api/wallet.h6
-rw-r--r--src/wallet/api/wallet2_api.h48
-rw-r--r--src/wallet/api/wallet_manager.cpp2
-rw-r--r--src/wallet/api/wallet_manager.h2
-rw-r--r--src/wallet/node_rpc_proxy.cpp2
-rw-r--r--src/wallet/node_rpc_proxy.h2
-rw-r--r--src/wallet/wallet2.cpp792
-rw-r--r--src/wallet/wallet2.h75
-rw-r--r--src/wallet/wallet_args.cpp2
-rw-r--r--src/wallet/wallet_args.h2
-rw-r--r--src/wallet/wallet_errors.h25
-rw-r--r--src/wallet/wallet_light_rpc.h320
-rw-r--r--src/wallet/wallet_rpc_server.cpp283
-rw-r--r--src/wallet/wallet_rpc_server.h8
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h61
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h2
34 files changed, 1480 insertions, 296 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 2991f75c5..def23aff0 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2018, The Monero Project
+# Copyright (c) 2014-2019, The Monero Project
#
# All rights reserved.
#
@@ -63,6 +63,7 @@ target_link_libraries(wallet
cryptonote_core
mnemonics
device_trezor
+ net
${LMDB_LIBRARY}
${Boost_CHRONO_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt
index d6f2bf6b7..3376ec70e 100644
--- a/src/wallet/api/CMakeLists.txt
+++ b/src/wallet/api/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2018, The Monero Project
+# Copyright (c) 2014-2019, The Monero Project
#
# All rights reserved.
#
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp
index 7ef011e06..7be78bba7 100644
--- a/src/wallet/api/address_book.cpp
+++ b/src/wallet/api/address_book.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h
index f4ca68efd..92e6eaa17 100644
--- a/src/wallet/api/address_book.h
+++ b/src/wallet/api/address_book.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp
index 913e3156f..52510164a 100644
--- a/src/wallet/api/pending_transaction.cpp
+++ b/src/wallet/api/pending_transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -109,6 +109,23 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
}
m_wallet.pauseRefresh();
+
+ const bool tx_cold_signed = m_wallet.m_wallet->get_account().get_device().has_tx_cold_sign();
+ if (tx_cold_signed){
+ std::unordered_set<size_t> selected_transfers;
+ for(const tools::wallet2::pending_tx & ptx : m_pending_tx){
+ for(size_t s : ptx.selected_transfers){
+ selected_transfers.insert(s);
+ }
+ }
+
+ m_wallet.m_wallet->cold_tx_aux_import(m_pending_tx, m_tx_device_aux);
+ bool r = m_wallet.m_wallet->import_key_images(m_key_images, 0, selected_transfers);
+ if (!r){
+ throw runtime_error("Cold sign transaction submit failed - key image sync fail");
+ }
+ }
+
while (!m_pending_tx.empty()) {
auto & ptx = m_pending_tx.back();
m_wallet.m_wallet->commit_tx(ptx);
diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h
index 50b9f07ef..92801d77d 100644
--- a/src/wallet/api/pending_transaction.h
+++ b/src/wallet/api/pending_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -67,6 +67,8 @@ private:
std::string m_errorString;
std::vector<tools::wallet2::pending_tx> m_pending_tx;
std::unordered_set<crypto::public_key> m_signers;
+ std::vector<std::string> m_tx_device_aux;
+ std::vector<crypto::key_image> m_key_images;
};
diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp
index 61dbbf4b0..8a1d34864 100644
--- a/src/wallet/api/subaddress.cpp
+++ b/src/wallet/api/subaddress.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018, The Monero Project
+// Copyright (c) 2017-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h
index f3db7d97b..87585ec16 100644
--- a/src/wallet/api/subaddress.h
+++ b/src/wallet/api/subaddress.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018, The Monero Project
+// Copyright (c) 2017-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/subaddress_account.cpp b/src/wallet/api/subaddress_account.cpp
index 4765465c3..9bc9d1d91 100644
--- a/src/wallet/api/subaddress_account.cpp
+++ b/src/wallet/api/subaddress_account.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018, The Monero Project
+// Copyright (c) 2017-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/subaddress_account.h b/src/wallet/api/subaddress_account.h
index b052182f8..358e446d4 100644
--- a/src/wallet/api/subaddress_account.h
+++ b/src/wallet/api/subaddress_account.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018, The Monero Project
+// Copyright (c) 2017-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index ba46a6904..f4ad8b1f6 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h
index 7bdce97e2..67fe1989d 100644
--- a/src/wallet/api/transaction_history.h
+++ b/src/wallet/api/transaction_history.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp
index cc3209609..21573c6f6 100644
--- a/src/wallet/api/transaction_info.cpp
+++ b/src/wallet/api/transaction_info.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h
index 37e0445d9..d5c8f31cf 100644
--- a/src/wallet/api/transaction_info.h
+++ b/src/wallet/api/transaction_info.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index 29910a3b6..c2c04cbc3 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h
index 8a3330014..f1af80fa1 100644
--- a/src/wallet/api/unsigned_transaction.h
+++ b/src/wallet/api/unsigned_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp
index 86fe56564..24252868a 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 44cd67657..82986ba2d 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -242,6 +242,42 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
+ virtual void on_device_button_request(uint64_t code)
+ {
+ if (m_listener) {
+ m_listener->onDeviceButtonRequest(code);
+ }
+ }
+
+ virtual boost::optional<epee::wipeable_string> on_device_pin_request()
+ {
+ if (m_listener) {
+ auto pin = m_listener->onDevicePinRequest();
+ if (pin){
+ return boost::make_optional(epee::wipeable_string((*pin).data(), (*pin).size()));
+ }
+ }
+ return boost::none;
+ }
+
+ virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device)
+ {
+ if (m_listener) {
+ auto passphrase = m_listener->onDevicePassphraseRequest(on_device);
+ if (!on_device && passphrase) {
+ return boost::make_optional(epee::wipeable_string((*passphrase).data(), (*passphrase).size()));
+ }
+ }
+ return boost::none;
+ }
+
+ virtual void on_device_progress(const hw::device_progress & event)
+ {
+ if (m_listener) {
+ m_listener->onDeviceProgress(DeviceProgress(event.progress(), event.indeterminate()));
+ }
+ }
+
WalletListener * m_listener;
WalletImpl * m_wallet;
};
@@ -785,6 +821,28 @@ bool WalletImpl::setPassword(const std::string &password)
return status() == Status_Ok;
}
+bool WalletImpl::setDevicePin(const std::string &pin)
+{
+ clearStatus();
+ try {
+ m_wallet->get_account().get_device().set_pin(epee::wipeable_string(pin.data(), pin.size()));
+ } catch (const std::exception &e) {
+ setStatusError(e.what());
+ }
+ return status() == Status_Ok;
+}
+
+bool WalletImpl::setDevicePassphrase(const std::string &passphrase)
+{
+ clearStatus();
+ try {
+ m_wallet->get_account().get_device().set_passphrase(epee::wipeable_string(passphrase.data(), passphrase.size()));
+ } catch (const std::exception &e) {
+ setStatusError(e.what());
+ }
+ return status() == Status_Ok;
+}
+
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const
{
return m_wallet->get_subaddress_as_str({accountIndex, addressIndex});
@@ -1428,8 +1486,12 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
extra, subaddr_account, subaddr_indices);
}
+ pendingTxPostProcess(transaction);
+
if (multisig().isMultisig) {
- transaction->m_signers = m_wallet->make_multisig_tx_set(transaction->m_pending_tx).m_signers;
+ auto tx_set = m_wallet->make_multisig_tx_set(transaction->m_pending_tx);
+ transaction->m_pending_tx = tx_set.m_ptx;
+ transaction->m_signers = tx_set.m_signers;
}
} catch (const tools::error::daemon_busy&) {
// TODO: make it translatable with "tr"?
@@ -1511,6 +1573,7 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
do {
try {
transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions();
+ pendingTxPostProcess(transaction);
} catch (const tools::error::daemon_busy&) {
// TODO: make it translatable with "tr"?
@@ -2093,10 +2156,24 @@ bool WalletImpl::isNewWallet() const
return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly();
}
+void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending)
+{
+ // If the device being used is HW device with cold signing protocol, cold sign then.
+ if (!m_wallet->get_account().get_device().has_tx_cold_sign()){
+ return;
+ }
+
+ tools::wallet2::signed_tx_set exported_txs;
+ std::vector<cryptonote::address_parse_info> dsts_info;
+
+ m_wallet->cold_sign_tx(pending->m_pending_tx, exported_txs, dsts_info, pending->m_tx_device_aux);
+ pending->m_key_images = exported_txs.key_images;
+ pending->m_pending_tx = exported_txs.ptx;
+}
+
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
{
- // claim RPC so there's no in-memory encryption for now
- if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
+ if (!m_wallet->init(daemon_address, m_daemon_login, tcp::endpoint{}, upper_transaction_size_limit))
return false;
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
@@ -2325,6 +2402,11 @@ bool WalletImpl::isKeysFileLocked()
{
return m_wallet->is_keys_file_locked();
}
+
+uint64_t WalletImpl::coldKeyImageSync(uint64_t &spent, uint64_t &unspent)
+{
+ return m_wallet->cold_key_image_sync(spent, unspent);
+}
} // namespace
namespace Bitmonero = Monero;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 55240d64f..9e07b6e19 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -89,6 +89,8 @@ public:
std::string errorString() const override;
void statusWithErrorString(int& status, std::string& errorString) const override;
bool setPassword(const std::string &password) override;
+ bool setDevicePin(const std::string &password) override;
+ bool setDevicePassphrase(const std::string &password) override;
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
std::string integratedAddress(const std::string &payment_id) const override;
std::string secretViewKey() const override;
@@ -198,6 +200,7 @@ public:
virtual bool lockKeysFile() override;
virtual bool unlockKeysFile() override;
virtual bool isKeysFileLocked() override;
+ virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) override;
private:
void clearStatus() const;
@@ -209,6 +212,7 @@ private:
bool daemonSynced() const;
void stopRefresh();
bool isNewWallet() const;
+ void pendingTxPostProcess(PendingTransactionImpl * pending);
bool doInit(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, bool ssl = false);
private:
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 5c301974f..ee1d6ae79 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -324,6 +324,19 @@ struct MultisigState {
uint32_t total;
};
+
+struct DeviceProgress {
+ DeviceProgress(): m_progress(0), m_indeterminate(false) {}
+ DeviceProgress(double progress, bool indeterminate=false): m_progress(progress), m_indeterminate(indeterminate) {}
+
+ virtual double progress() const { return m_progress; }
+ virtual bool indeterminate() const { return m_indeterminate; }
+
+protected:
+ double m_progress;
+ bool m_indeterminate;
+};
+
struct WalletListener
{
virtual ~WalletListener() = 0;
@@ -364,6 +377,31 @@ struct WalletListener
* @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously
*/
virtual void refreshed() = 0;
+
+ /**
+ * @brief called by device if the action is required
+ */
+ virtual void onDeviceButtonRequest(uint64_t code) {}
+
+ /**
+ * @brief called by device when PIN is needed
+ */
+ virtual optional<std::string> onDevicePinRequest() {
+ throw std::runtime_error("Not supported");
+ }
+
+ /**
+ * @brief called by device when passphrase entry is needed
+ */
+ virtual optional<std::string> onDevicePassphraseRequest(bool on_device) {
+ if (!on_device) throw std::runtime_error("Not supported");
+ return optional<std::string>();
+ }
+
+ /**
+ * @brief Signalizes device operation progress
+ */
+ virtual void onDeviceProgress(const DeviceProgress & event) {};
};
@@ -375,7 +413,8 @@ struct Wallet
{
enum Device {
Device_Software = 0,
- Device_Ledger = 1
+ Device_Ledger = 1,
+ Device_Trezor = 2
};
enum Status {
@@ -401,6 +440,8 @@ struct Wallet
//! returns both error and error string atomically. suggested to use in instead of status() and errorString()
virtual void statusWithErrorString(int& status, std::string& errorString) const = 0;
virtual bool setPassword(const std::string &password) = 0;
+ virtual bool setDevicePin(const std::string &password) { return false; };
+ virtual bool setDevicePassphrase(const std::string &password) { return false; };
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
std::string mainAddress() const { return address(0, 0); }
virtual std::string path() const = 0;
@@ -947,6 +988,9 @@ struct Wallet
* \return Device they are on
*/
virtual Device getDeviceType() const = 0;
+
+ //! cold-device protocol key image sync
+ virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0;
};
/**
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 89fe01c0d..f584e88ac 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index b3c0d6c00..0c83d794f 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 605531e59..f5f3c0e1b 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018, The Monero Project
+// Copyright (c) 2017-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h
index 65f13eaaa..3630aec08 100644
--- a/src/wallet/node_rpc_proxy.h
+++ b/src/wallet/node_rpc_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018, The Monero Project
+// Copyright (c) 2017-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 11be8fd6e..439873932 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -38,6 +38,7 @@
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/join.hpp>
+#include <boost/asio/ip/address.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include "include_base_utils.h"
using namespace epee;
@@ -75,6 +76,7 @@ using namespace epee;
#include "ringdb.h"
#include "device/device_cold.hpp"
#include "device_trezor/device_trezor.hpp"
+#include "net/socks_connect.h"
extern "C"
{
@@ -231,6 +233,7 @@ namespace
struct options {
const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
+ const command_line::arg_descriptor<std::string> proxy = {"proxy", tools::wallet2::tr("[<ip>:]<port> socks proxy to use for daemon connections"), {}, true};
const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true};
@@ -303,6 +306,8 @@ std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_siz
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
+ namespace ip = boost::asio::ip;
+
const bool testnet = command_line::get_arg(vm, opts.testnet);
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
@@ -352,6 +357,44 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
+ boost::asio::ip::tcp::endpoint proxy{};
+ if (command_line::has_arg(vm, opts.proxy))
+ {
+ namespace ip = boost::asio::ip;
+ const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
+
+ // onion and i2p addresses contain information about the server cert
+ // which both authenticates and encrypts
+ const bool unencrypted_proxy =
+ !real_daemon.ends_with(".onion") && !real_daemon.ends_with(".i2p") &&
+ daemon_ssl_allowed_certificates.empty() && daemon_ssl_allowed_fingerprints.empty();
+ THROW_WALLET_EXCEPTION_IF(
+ unencrypted_proxy,
+ tools::error::wallet_internal_error,
+ std::string{"Use of --"} + opts.proxy.name + " requires --" + opts.daemon_ssl_allowed_certificates.name + " or --" + opts.daemon_ssl_allowed_fingerprints.name + " or use of a .onion/.i2p domain"
+ );
+
+ const auto proxy_address = command_line::get_arg(vm, opts.proxy);
+
+ boost::string_ref proxy_port{proxy_address};
+ boost::string_ref proxy_host = proxy_port.substr(0, proxy_port.rfind(":"));
+ if (proxy_port.size() == proxy_host.size())
+ proxy_host = "127.0.0.1";
+ else
+ proxy_port = proxy_port.substr(proxy_host.size() + 1);
+
+ uint16_t port_value = 0;
+ THROW_WALLET_EXCEPTION_IF(
+ !epee::string_tools::get_xtype_from_string(port_value, std::string{proxy_port}),
+ tools::error::wallet_internal_error,
+ std::string{"Invalid port specified for --"} + opts.proxy.name
+ );
+
+ boost::system::error_code error{};
+ proxy = ip::tcp::endpoint{ip::address::from_string(std::string{proxy_host}, error), port_value};
+ THROW_WALLET_EXCEPTION_IF(bool(error), tools::error::wallet_internal_error, std::string{"Invalid IP address specified for --"} + opts.proxy.name);
+ }
+
boost::optional<bool> trusted_daemon;
if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon))
trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon);
@@ -388,8 +431,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
- wallet->init(std::move(daemon_address), std::move(login), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert);
-
+ wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert);
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
wallet->get_message_store().set_options(vm);
@@ -470,7 +512,7 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string());
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0);
- const bool recover = field_scan_from_height_found;
+ const bool recover = true;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string());
@@ -580,6 +622,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
wallet->set_refresh_from_block_height(field_scan_from_height);
wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
+ if (!old_language.empty())
+ wallet->set_seed_language(old_language);
try
{
@@ -793,9 +837,8 @@ uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx,
return calculate_fee(base_fee, blob_size, fee_multiplier);
}
-crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
+bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
{
- crypto::hash8 payment_id8 = null_hash8;
std::vector<tx_extra_field> tx_extra_fields;
parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
cryptonote::tx_extra_nonce extra_nonce;
@@ -806,19 +849,19 @@ crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::de
if (ptx.dests.empty())
{
MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
- return crypto::null_hash8;
+ return false;
}
- hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key);
+ return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key);
}
}
- return payment_id8;
+ return false;
}
tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
{
tools::wallet2::tx_construction_data construction_data = ptx.construction_data;
- crypto::hash8 payment_id = get_short_payment_id(ptx,hwdev);
- if (payment_id != null_hash8)
+ crypto::hash8 payment_id = null_hash8;
+ if (get_short_payment_id(payment_id, ptx, hwdev))
{
// Remove encrypted
remove_field_from_tx_extra(construction_data.extra, typeid(cryptonote::tx_extra_nonce));
@@ -929,22 +972,30 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
}
}
-void wallet_device_callback::on_button_request()
+void wallet_device_callback::on_button_request(uint64_t code)
+{
+ if (wallet)
+ wallet->on_device_button_request(code);
+}
+
+boost::optional<epee::wipeable_string> wallet_device_callback::on_pin_request()
{
if (wallet)
- wallet->on_button_request();
+ return wallet->on_device_pin_request();
+ return boost::none;
}
-void wallet_device_callback::on_pin_request(epee::wipeable_string & pin)
+boost::optional<epee::wipeable_string> wallet_device_callback::on_passphrase_request(bool on_device)
{
if (wallet)
- wallet->on_pin_request(pin);
+ return wallet->on_device_passphrase_request(on_device);
+ return boost::none;
}
-void wallet_device_callback::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
+void wallet_device_callback::on_progress(const hw::device_progress& event)
{
if (wallet)
- wallet->on_passphrase_request(on_device, passphrase);
+ wallet->on_device_progress(event);
}
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
@@ -1039,6 +1090,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
const options opts{};
command_line::add_arg(desc_params, opts.daemon_address);
command_line::add_arg(desc_params, opts.daemon_host);
+ command_line::add_arg(desc_params, opts.proxy);
command_line::add_arg(desc_params, opts.trusted_daemon);
command_line::add_arg(desc_params, opts.untrusted_daemon);
command_line::add_arg(desc_params, opts.password);
@@ -1102,7 +1154,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert)
+bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert)
{
m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected())
@@ -1112,6 +1164,10 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
m_trusted_daemon = trusted_daemon;
+ if (proxy != boost::asio::ip::tcp::endpoint{})
+ m_http_client.set_connector(net::socks::connector{std::move(proxy)});
+
+ // When switching from light wallet to full wallet, we need to reset the height we got from lw node.
return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert);
}
//----------------------------------------------------------------------------------------------------
@@ -2399,7 +2455,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
" (height " + std::to_string(start_height) + "), local block id at this height: " +
string_tools::pod_to_hex(m_blockchain[current_index]));
- detach_blockchain(current_index);
+ detach_blockchain(current_index, output_tracker_cache);
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
}
else
@@ -2843,7 +2899,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
// This call is not really needed for other purposes and can be removed if mymonero changes their backend.
- cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response res;
+ tools::COMMAND_RPC_GET_ADDRESS_INFO::response res;
// Get basic info
if(light_wallet_get_address_info(res)) {
@@ -2884,6 +2940,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
std::vector<parsed_block> parsed_blocks;
bool refreshed = false;
std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> output_tracker_cache;
+ hw::device &hwdev = m_account.get_device();
// pull the first set of blocks
get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY);
@@ -2990,7 +3047,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// if we've got at least 10 blocks to refresh, assume we're starting
// a long refresh, and setup a tracking output cache if we need to
- if (m_track_uses && !output_tracker_cache && next_blocks.size() >= 10)
+ if (m_track_uses && (!output_tracker_cache || output_tracker_cache->empty()) && next_blocks.size() >= 10)
output_tracker_cache = create_output_tracker_cache();
// switch to the new blocks from the daemon
@@ -3040,6 +3097,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
LOG_PRINT_L1("Failed to check pending transactions");
}
+ hwdev.computing_key_images(false);
m_first_refresh_done = true;
LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all()) << ", unlocked: " << print_money(unlocked_balance_all()));
@@ -3130,7 +3188,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
return true;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::detach_blockchain(uint64_t height)
+void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
{
LOG_PRINT_L0("Detaching blockchain on height " << height);
@@ -3152,6 +3210,15 @@ void wallet2::detach_blockchain(uint64_t height)
}
}
+ for (transfer_details &td: m_transfers)
+ {
+ while (!td.m_uses.empty() && td.m_uses.back().first >= height)
+ td.m_uses.pop_back();
+ }
+
+ if (output_tracker_cache)
+ output_tracker_cache->clear();
+
auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;});
size_t i_start = it - m_transfers.begin();
@@ -3223,6 +3290,26 @@ bool wallet2::clear()
m_device_last_key_image_sync = 0;
return true;
}
+//----------------------------------------------------------------------------------------------------
+void wallet2::clear_soft(bool keep_key_images)
+{
+ m_blockchain.clear();
+ m_transfers.clear();
+ if (!keep_key_images)
+ m_key_images.clear();
+ m_pub_keys.clear();
+ m_unconfirmed_txs.clear();
+ m_payments.clear();
+ m_confirmed_txs.clear();
+ m_unconfirmed_payments.clear();
+ m_scanned_pool_txs[0].clear();
+ m_scanned_pool_txs[1].clear();
+
+ cryptonote::block b;
+ generate_genesis(b);
+ m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
+}
/*!
* \brief Stores wallet information to wallet file.
@@ -3461,7 +3548,8 @@ void wallet2::change_password(const std::string &filename, const epee::wipeable_
decrypt_keys(original_password);
setup_keys(new_password);
rewrite(filename, new_password);
- store();
+ if (!filename.empty())
+ store();
}
//----------------------------------------------------------------------------------------------------
/*!
@@ -5091,7 +5179,14 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
MERROR("Failed to save rings, will try again next time");
}
- m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
+ try
+ {
+ m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to initialize MMS, it will be unusable");
+ }
}
//----------------------------------------------------------------------------------------------------
void wallet2::trim_hashchain()
@@ -5143,7 +5238,8 @@ std::string wallet2::path() const
//----------------------------------------------------------------------------------------------------
void wallet2::store()
{
- store_to("", epee::wipeable_string());
+ if (!m_wallet_file.empty())
+ store_to("", epee::wipeable_string());
}
//----------------------------------------------------------------------------------------------------
void wallet2::store_to(const std::string &path, const epee::wipeable_string &password)
@@ -5469,8 +5565,12 @@ void wallet2::rescan_spent()
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::rescan_blockchain(bool hard, bool refresh)
+void wallet2::rescan_blockchain(bool hard, bool refresh, bool keep_key_images)
{
+ CHECK_AND_ASSERT_THROW_MES(!hard || !keep_key_images, "Cannot preserve key images on hard rescan");
+ const size_t transfers_cnt = m_transfers.size();
+ crypto::hash transfers_hash{};
+
if(hard)
{
clear();
@@ -5478,25 +5578,16 @@ void wallet2::rescan_blockchain(bool hard, bool refresh)
}
else
{
- m_blockchain.clear();
- m_transfers.clear();
- m_key_images.clear();
- m_pub_keys.clear();
- m_unconfirmed_txs.clear();
- m_payments.clear();
- m_confirmed_txs.clear();
- m_unconfirmed_payments.clear();
- m_scanned_pool_txs[0].clear();
- m_scanned_pool_txs[1].clear();
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
+ if (keep_key_images && refresh)
+ hash_m_transfers((int64_t) transfers_cnt, transfers_hash);
+ clear_soft(keep_key_images);
}
if (refresh)
this->refresh(false);
+
+ if (refresh && keep_key_images)
+ finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_unlocked(const transfer_details& td) const
@@ -5981,12 +6072,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
- rct::RCTConfig rct_config = { rct::RangeProofBorromean, 0 };
- if (sd.use_bulletproofs)
- {
- rct_config.range_proof_type = rct::RangeProofPaddedBulletproof;
- rct_config.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1;
- }
+ rct::RCTConfig rct_config = sd.rct_config;
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout;
@@ -6335,17 +6421,17 @@ bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const
return epee::file_io_utils::save_string_to_file(filename, ciphertext);
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
+bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const
{
const size_t magiclen = strlen(MULTISIG_UNSIGNED_TX_PREFIX);
- if (strncmp(s.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen))
+ if (strncmp(multisig_tx_st.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen))
{
LOG_PRINT_L0("Bad magic from multisig tx data");
return false;
}
try
{
- s = decrypt_with_view_secret_key(std::string(s, magiclen));
+ multisig_tx_st = decrypt_with_view_secret_key(std::string(multisig_tx_st, magiclen));
}
catch (const std::exception &e)
{
@@ -6354,7 +6440,7 @@ bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported
}
try
{
- std::istringstream iss(s);
+ std::istringstream iss(multisig_tx_st);
boost::archive::portable_binary_iarchive ar(iss);
ar >> exported_txs;
}
@@ -6376,6 +6462,17 @@ bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported
CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(), false, "Mismatched sources/vin sizes");
}
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func)
+{
+ if(!parse_multisig_tx_from_str(s, exported_txs))
+ {
+ LOG_PRINT_L0("Failed to parse multisig transaction from string");
+ return false;
+ }
+
LOG_PRINT_L1("Loaded multisig tx unsigned data from binary: " << exported_txs.m_ptx.size() << " transactions");
for (auto &ptx: exported_txs.m_ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx));
@@ -6452,12 +6549,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
cryptonote::transaction tx;
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
auto sources = sd.sources;
- rct::RCTConfig rct_config = { rct::RangeProofBorromean, 0 };
- if (sd.use_bulletproofs)
- {
- rct_config.range_proof_type = rct::RangeProofPaddedBulletproof;
- rct_config.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1;
- }
+ rct::RCTConfig rct_config = sd.rct_config;
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, false);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
@@ -7025,6 +7117,17 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
return false;
+ // check the keys are valid
+ if (!rct::isInMainSubgroup(rct::pk2rct(output_public_key)))
+ {
+ MWARNING("Key " << output_public_key << " at index " << global_index << " is not in the main subgroup");
+ return false;
+ }
+ if (!rct::isInMainSubgroup(mask))
+ {
+ MWARNING("Commitment " << mask << " at index " << global_index << " is not in the main subgroup");
+ return false;
+ }
// if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
// return false;
outs.back().push_back(item);
@@ -7907,7 +8010,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
- ptx.construction_data.use_bulletproofs = false;
+ ptx.construction_data.rct_config = { rct::RangeProofBorromean, 0 };
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -8093,6 +8196,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
else
{
change_dts.addr = get_subaddress({subaddr_account, 0});
+ change_dts.is_subaddress = subaddr_account != 0;
splitted_dsts.push_back(change_dts);
}
@@ -8189,7 +8293,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.construction_data.extra = tx.extra;
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = true;
- ptx.construction_data.use_bulletproofs = !tx.rct_signatures.p.bulletproofs.empty();
+ ptx.construction_data.rct_config = { tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof, use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1};
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -8317,8 +8421,8 @@ bool wallet2::light_wallet_login(bool &new_address)
{
MDEBUG("Light wallet login request");
m_light_wallet_connected = false;
- cryptonote::COMMAND_RPC_LOGIN::request request;
- cryptonote::COMMAND_RPC_LOGIN::response response;
+ tools::COMMAND_RPC_LOGIN::request request;
+ tools::COMMAND_RPC_LOGIN::response response;
request.address = get_account().get_public_address_str(m_nettype);
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
// Always create account if it doesn't exist.
@@ -8342,10 +8446,10 @@ bool wallet2::light_wallet_login(bool &new_address)
return m_light_wallet_connected;
}
-bool wallet2::light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
+bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
{
MDEBUG("Light wallet import wallet request");
- cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq;
+ tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq;
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
@@ -8361,8 +8465,8 @@ void wallet2::light_wallet_get_unspent_outs()
{
MDEBUG("Getting unspent outs");
- cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq;
- cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::response ores;
+ tools::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq;
+ tools::COMMAND_RPC_GET_UNSPENT_OUTS::response ores;
oreq.amount = "0";
oreq.address = get_account().get_public_address_str(m_nettype);
@@ -8510,11 +8614,11 @@ void wallet2::light_wallet_get_unspent_outs()
}
}
-bool wallet2::light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
+bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
{
MTRACE(__FUNCTION__);
- cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::request request;
+ tools::COMMAND_RPC_GET_ADDRESS_INFO::request request;
request.address = get_account().get_public_address_str(m_nettype);
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
@@ -8530,8 +8634,8 @@ void wallet2::light_wallet_get_address_txs()
{
MDEBUG("Refreshing light wallet");
- cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::request ireq;
- cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::response ires;
+ tools::COMMAND_RPC_GET_ADDRESS_TXS::request ireq;
+ tools::COMMAND_RPC_GET_ADDRESS_TXS::response ires;
ireq.address = get_account().get_public_address_str(m_nettype);
ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
@@ -8804,6 +8908,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
boost::unique_lock<hw::device> hwdev_lock (hwdev);
hw::reset_mode rst(hwdev);
+ auto original_dsts = dsts;
+
if(m_light_wallet) {
// Populate m_transfers
light_wallet_get_unspent_outs();
@@ -9149,6 +9255,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
+ THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
}
}
@@ -9323,10 +9430,77 @@ skip_tx:
ptx_vector.push_back(tx.ptx);
}
+ THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, original_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
+
// if we made it this far, we're OK to actually send the transactions
return ptx_vector;
}
+bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, std::vector<cryptonote::tx_destination_entry> dsts) const
+{
+ MDEBUG("sanity_check: " << ptx_vector.size() << " txes, " << dsts.size() << " destinations");
+
+ hw::device &hwdev = m_account.get_device();
+
+ THROW_WALLET_EXCEPTION_IF(ptx_vector.empty(), error::wallet_internal_error, "No transactions");
+
+ // check every party in there does receive at least the required amount
+ std::unordered_map<account_public_address, std::pair<uint64_t, bool>> required;
+ for (const auto &d: dsts)
+ {
+ required[d.addr].first += d.amount;
+ required[d.addr].second = d.is_subaddress;
+ }
+
+ // add change
+ uint64_t change = 0;
+ for (const auto &ptx: ptx_vector)
+ {
+ for (size_t idx: ptx.selected_transfers)
+ change += m_transfers[idx].amount();
+ change -= ptx.fee;
+ }
+ for (const auto &r: required)
+ change -= r.second.first;
+ MDEBUG("Adding " << cryptonote::print_money(change) << " expected change");
+
+ for (const pending_tx &ptx: ptx_vector)
+ THROW_WALLET_EXCEPTION_IF(ptx.change_dts.addr != ptx_vector[0].change_dts.addr, error::wallet_internal_error,
+ "Change goes to several different addresses");
+ const auto it = m_subaddresses.find(ptx_vector[0].change_dts.addr.m_spend_public_key);
+ THROW_WALLET_EXCEPTION_IF(it == m_subaddresses.end(), error::wallet_internal_error, "Change address is not ours");
+
+ required[ptx_vector[0].change_dts.addr].first += change;
+ required[ptx_vector[0].change_dts.addr].second = ptx_vector[0].change_dts.is_subaddress;
+
+ for (const auto &r: required)
+ {
+ const account_public_address &address = r.first;
+ const crypto::public_key &view_pkey = address.m_view_public_key;
+
+ uint64_t total_received = 0;
+ for (const auto &ptx: ptx_vector)
+ {
+ uint64_t received = 0;
+ try
+ {
+ std::string proof = get_tx_proof(ptx.tx, ptx.tx_key, ptx.additional_tx_keys, address, r.second.second, "automatic-sanity-check");
+ check_tx_proof(ptx.tx, address, r.second.second, "automatic-sanity-check", proof, received);
+ }
+ catch (const std::exception &e) { received = 0; }
+ total_received += received;
+ }
+
+ std::stringstream ss;
+ ss << "Total received by " << cryptonote::get_account_address_as_str(m_nettype, r.second.second, address) << ": "
+ << cryptonote::print_money(total_received) << ", expected " << cryptonote::print_money(r.second.first);
+ MDEBUG(ss.str());
+ THROW_WALLET_EXCEPTION_IF(total_received < r.second.first, error::wallet_internal_error, ss.str());
+ }
+
+ return true;
+}
+
std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, 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)
{
std::vector<size_t> unused_transfers_indices;
@@ -9609,6 +9783,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
ptx_vector.push_back(tx.ptx);
}
+ uint64_t a = 0;
+ for (size_t idx: unused_transfers_indices) a += m_transfers[idx].amount();
+ for (size_t idx: unused_dust_indices) a += m_transfers[idx].amount();
+ std::vector<cryptonote::tx_destination_entry> synthetic_dsts(1, cryptonote::tx_destination_entry("", a, address, is_subaddress));
+ THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, synthetic_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
+
// if we made it this far, we're OK to actually send the transactions
return ptx_vector;
}
@@ -9644,6 +9824,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
hw::wallet_shim wallet_shim;
setup_shim(&wallet_shim, this);
aux_data.tx_recipients = dsts_info;
+ aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1;
dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
tx_device_aux = aux_data.tx_device_aux;
@@ -9688,7 +9869,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
throw_on_rpc_response_error(result, "get_hard_fork_info");
- bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
+ bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
if (close_enough)
LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
else
@@ -9860,7 +10041,7 @@ void wallet2::discard_unmixable_outputs()
}
}
-bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const
+bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const
{
additional_tx_keys.clear();
const std::unordered_map<crypto::hash, crypto::secret_key>::const_iterator i = m_tx_keys.find(txid);
@@ -9873,6 +10054,82 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
return true;
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys)
+{
+ bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys);
+ if (r)
+ {
+ return true;
+ }
+
+ auto & hwdev = get_account().get_device();
+
+ // So far only Cold protocol devices are supported.
+ if (hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
+ {
+ return false;
+ }
+
+ const auto tx_data_it = m_tx_device.find(txid);
+ if (tx_data_it == m_tx_device.end())
+ {
+ MDEBUG("Aux data not found for txid: " << txid);
+ return false;
+ }
+
+ auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
+ CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
+ if (!dev_cold->is_get_tx_key_supported())
+ {
+ MDEBUG("get_tx_key not supported by the device");
+ return false;
+ }
+
+ hw::device_cold::tx_key_data_t tx_key_data;
+ dev_cold->load_tx_key_data(tx_key_data, tx_data_it->second);
+
+ // Load missing tx prefix hash
+ if (tx_key_data.tx_prefix_hash.empty())
+ {
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ req.prune = true;
+ 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::transaction tx;
+ crypto::hash tx_hash{};
+ cryptonote::blobdata tx_data;
+ crypto::hash tx_prefix_hash{};
+ 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");
+ 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");
+
+ tx_key_data.tx_prefix_hash = std::string(tx_prefix_hash.data, 32);
+ }
+
+ std::vector<crypto::secret_key> tx_keys;
+ dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key);
+ if (tx_keys.empty())
+ {
+ return false;
+ }
+
+ tx_key = tx_keys[0];
+ tx_keys.erase(tx_keys.begin());
+ additional_tx_keys = tx_keys;
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys)
{
// fetch tx from daemon and check if secret keys agree with corresponding public keys
@@ -10164,41 +10421,8 @@ void wallet2::check_tx_key(const crypto::hash &txid, const crypto::secret_key &t
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)
+void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received) const
{
- COMMAND_RPC_GET_TRANSACTIONS::request req;
- COMMAND_RPC_GET_TRANSACTIONS::response res;
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
- req.decode_as_json = false;
- req.prune = true;
- 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::transaction tx;
- crypto::hash tx_hash;
- if (res.txs.size() == 1)
- {
- ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
- THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- }
- else
- {
- cryptonote::blobdata tx_data;
- 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");
- THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx),
- error::wallet_internal_error, "Failed to validate transaction from daemon");
- tx_hash = cryptonote::get_transaction_hash(tx);
- }
-
- 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;
hw::device &hwdev = m_account.get_device();
for (size_t n = 0; n < tx.vout.size(); ++n)
@@ -10246,6 +10470,44 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
received += amount;
}
}
+}
+
+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));
+ req.decode_as_json = false;
+ req.prune = true;
+ 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::transaction tx;
+ crypto::hash tx_hash;
+ if (res.txs.size() == 1)
+ {
+ ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+ }
+ else
+ {
+ cryptonote::blobdata tx_data;
+ 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");
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx),
+ error::wallet_internal_error, "Failed to validate transaction from daemon");
+ tx_hash = cryptonote::get_transaction_hash(tx);
+ }
+
+ 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");
+
+ check_tx_key_helper(tx, derivation, additional_derivations, address, received);
in_pool = res.txs.front().in_pool;
confirmations = (uint64_t)-1;
@@ -10260,9 +10522,55 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
{
+ // 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));
+ req.decode_as_json = false;
+ req.prune = true;
+ 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::transaction tx;
+ crypto::hash tx_hash;
+ if (res.txs.size() == 1)
+ {
+ ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+ }
+ else
+ {
+ cryptonote::blobdata tx_data;
+ 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");
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx),
+ error::wallet_internal_error, "Failed to validate transaction from daemon");
+ tx_hash = cryptonote::get_transaction_hash(tx);
+ }
+
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+
+ // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
+ crypto::secret_key tx_key = crypto::null_skey;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
+ if (is_out)
+ {
+ 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.");
+ }
+
+ return get_tx_proof(tx, tx_key, additional_tx_keys, address, is_subaddress, message);
+}
+
+std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) const
+{
// 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;
+ const crypto::hash txid = cryptonote::get_transaction_hash(tx);
std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
prefix_data += message;
crypto::hash prefix_hash;
@@ -10273,10 +10581,6 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
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);
@@ -10311,37 +10615,6 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
}
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));
- req.decode_as_json = false;
- req.prune = true;
- 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::transaction tx;
- crypto::hash tx_hash;
- if (res.txs.size() == 1)
- {
- ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
- THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- }
- else
- {
- cryptonote::blobdata tx_data;
- 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");
- THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx),
- error::wallet_internal_error, "Failed to validate transaction from daemon");
- tx_hash = cryptonote::get_transaction_hash(tx);
- }
-
- 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");
@@ -10383,9 +10656,7 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
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);
+ check_tx_key_helper(tx, derivation, additional_derivations, address, received);
THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx."));
// concatenate all signature strings
@@ -10398,37 +10669,6 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
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;
@@ -10460,12 +10700,62 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+ if (!check_tx_proof(tx, address, is_subaddress, message, sig_str, received))
+ return false;
+
+ 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);
+ }
+
+ return true;
+}
+
+bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received) const
+{
+ 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));
+ }
+
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");
+ const crypto::hash txid = cryptonote::get_transaction_hash(tx);
std::string prefix_data((const char*)&txid, sizeof(crypto::hash));
prefix_data += message;
crypto::hash prefix_hash;
@@ -10512,7 +10802,7 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
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);
+ check_tx_key_helper(tx, derivation, additional_derivations, address, received);
return true;
}
return false;
@@ -11014,15 +11304,6 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
size_t pk_index = 0;
hw::device &hwdev = m_account.get_device();
- const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
- std::vector<crypto::key_derivation> additional_derivations;
- for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
- {
- additional_derivations.push_back({});
- bool r = hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
- THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
- }
-
while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
const crypto::public_key tx_pub_key = pub_key_field.pub_key;
crypto::key_derivation derivation;
@@ -11032,16 +11313,15 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
for (size_t i = 0; i < td.m_tx.vout.size(); ++i)
{
tx_scan_info_t tx_scan_info;
- check_acc_out_precomp(td.m_tx.vout[i], derivation, additional_derivations, i, tx_scan_info);
+ check_acc_out_precomp(td.m_tx.vout[i], derivation, {}, i, tx_scan_info);
if (!tx_scan_info.error && tx_scan_info.received)
return tx_pub_key;
}
}
- // we found no key yielding an output
- THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error,
- "Public key yielding at least one output wasn't found in the transaction extra");
- return crypto::null_pkey;
+ // we found no key yielding an output, but it might be in the additional
+ // tx pub keys only, which we do not need to check, so return the first one
+ return tx_pub_key;
}
bool wallet2::export_key_images(const std::string &filename) const
@@ -11283,6 +11563,17 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
}
PERF_TIMER_STOP(import_key_images_C);
+ // accumulate outputs before the updated data
+ for(size_t i = 0; i < offset; ++i)
+ {
+ const transfer_details &td = m_transfers[i];
+ uint64_t amount = td.amount();
+ if (td.m_spent)
+ spent += amount;
+ else
+ unspent += amount;
+ }
+
PERF_TIMER_START(import_key_images_D);
for(size_t i = 0; i < signed_key_images.size(); ++i)
{
@@ -11392,6 +11683,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
if (it != m_key_images.end())
{
+ THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, std::string("Key images cache contains illegal transfer offset: ") + std::to_string(it->second) + std::string(" m_transfers.size() = ") + std::to_string(m_transfers.size()));
const transfer_details& td = m_transfers[it->second];
uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
if (amount > 0)
@@ -11445,32 +11737,52 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
PERF_TIMER_STOP(import_key_images_G);
}
- return m_transfers[signed_key_images.size() - 1].m_block_height;
+ // this can be 0 if we do not know the height
+ return m_transfers[signed_key_images.size() + offset - 1].m_block_height;
}
-bool wallet2::import_key_images(std::vector<crypto::key_image> key_images)
+bool wallet2::import_key_images(std::vector<crypto::key_image> key_images, size_t offset, boost::optional<std::unordered_set<size_t>> selected_transfers)
{
- if (key_images.size() > m_transfers.size())
+ if (key_images.size() + offset > m_transfers.size())
{
LOG_PRINT_L1("More key images returned that we know outputs for");
return false;
}
- for (size_t i = 0; i < key_images.size(); ++i)
+ for (size_t ki_idx = 0; ki_idx < key_images.size(); ++ki_idx)
{
- transfer_details &td = m_transfers[i];
- if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[i])
- LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one");
- td.m_key_image = key_images[i];
- m_key_images[m_transfers[i].m_key_image] = i;
+ const size_t transfer_idx = ki_idx + offset;
+ if (selected_transfers && selected_transfers.get().find(transfer_idx) == selected_transfers.get().end())
+ continue;
+
+ transfer_details &td = m_transfers[transfer_idx];
+ if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[ki_idx])
+ LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << ki_idx << ": trusting imported one");
+ td.m_key_image = key_images[ki_idx];
+ m_key_images[td.m_key_image] = transfer_idx;
td.m_key_image_known = true;
td.m_key_image_request = false;
td.m_key_image_partial = false;
- m_pub_keys[m_transfers[i].get_public_key()] = i;
+ m_pub_keys[td.get_public_key()] = transfer_idx;
}
return true;
}
+bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool only_selected_transfers)
+{
+ std::unordered_set<size_t> selected_transfers;
+ if (only_selected_transfers)
+ {
+ for (const pending_tx & ptx : signed_tx.ptx)
+ {
+ for (const size_t s: ptx.selected_transfers)
+ selected_transfers.insert(s);
+ }
+ }
+
+ return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none);
+}
+
wallet2::payment_container wallet2::export_payments() const
{
payment_container payments;
@@ -12451,22 +12763,30 @@ wallet_device_callback * wallet2::get_device_callback()
}
return m_device_callback.get();
}//----------------------------------------------------------------------------------------------------
-void wallet2::on_button_request()
+void wallet2::on_device_button_request(uint64_t code)
{
- if (0 != m_callback)
- m_callback->on_button_request();
+ if (nullptr != m_callback)
+ m_callback->on_device_button_request(code);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::on_pin_request(epee::wipeable_string & pin)
+boost::optional<epee::wipeable_string> wallet2::on_device_pin_request()
{
- if (0 != m_callback)
- m_callback->on_pin_request(pin);
+ if (nullptr != m_callback)
+ return m_callback->on_device_pin_request();
+ return boost::none;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
+boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(bool on_device)
{
- if (0 != m_callback)
- m_callback->on_passphrase_request(on_device, passphrase);
+ if (nullptr != m_callback)
+ return m_callback->on_device_passphrase_request(on_device);
+ return boost::none;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::on_device_progress(const hw::device_progress& event)
+{
+ if (nullptr != m_callback)
+ m_callback->on_device_progress(event);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_rpc_status(const std::string &s) const
@@ -12490,5 +12810,61 @@ void wallet2::throw_on_rpc_response_error(const boost::optional<std::string> &st
THROW_WALLET_EXCEPTION_IF(*status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, method);
THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, m_trusted_daemon ? *status : "daemon error");
}
+//----------------------------------------------------------------------------------------------------
+void wallet2::hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const
+{
+ KECCAK_CTX state;
+ keccak_init(&state);
+ keccak_update(&state, (const uint8_t *) transfer.m_txid.data, sizeof(transfer.m_txid.data));
+ keccak_update(&state, (const uint8_t *) transfer.m_internal_output_index, sizeof(transfer.m_internal_output_index));
+ keccak_update(&state, (const uint8_t *) transfer.m_global_output_index, sizeof(transfer.m_global_output_index));
+ keccak_update(&state, (const uint8_t *) transfer.m_amount, sizeof(transfer.m_amount));
+ keccak_finish(&state, (uint8_t *) hash.data);
+}
+//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const
+{
+ CHECK_AND_ASSERT_THROW_MES(transfer_height > (int64_t)m_transfers.size(), "Hash height is greater than number of transfers");
+
+ KECCAK_CTX state;
+ crypto::hash tmp_hash{};
+ uint64_t current_height = 0;
+
+ keccak_init(&state);
+ for(const transfer_details & transfer : m_transfers){
+ if (transfer_height >= 0 && current_height >= (uint64_t)transfer_height){
+ break;
+ }
+
+ hash_m_transfer(transfer, tmp_hash);
+ keccak_update(&state, (const uint8_t *) transfer.m_block_height, sizeof(transfer.m_block_height));
+ keccak_update(&state, (const uint8_t *) tmp_hash.data, sizeof(tmp_hash.data));
+ current_height += 1;
+ }
+
+ keccak_finish(&state, (uint8_t *) hash.data);
+ return current_height;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash)
+{
+ // Compute hash of m_transfers, if differs there had to be BC reorg.
+ crypto::hash new_transfers_hash{};
+ hash_m_transfers((int64_t) transfer_height, new_transfers_hash);
+ if (new_transfers_hash != hash)
+ {
+ // Soft-Reset to avoid inconsistency in case of BC reorg.
+ clear_soft(false); // keep_key_images works only with soft reset.
+ THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Transfers changed during rescan, soft or hard rescan is needed");
+ }
+
+ // Restore key images in m_transfers from m_key_images
+ for(auto it = m_key_images.begin(); it != m_key_images.end(); it++)
+ {
+ THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, "Key images cache contains illegal transfer offset");
+ m_transfers[it->second].m_key_image = it->first;
+ m_transfers[it->second].m_key_image_known = true;
+ }
+}
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index b510a8f99..28ebd6704 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -62,6 +62,7 @@
#include "common/password.h"
#include "node_rpc_proxy.h"
#include "message_store.h"
+#include "wallet_light_rpc.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@@ -103,9 +104,10 @@ namespace tools
virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
// Device callbacks
- virtual void on_button_request() {}
- virtual void on_pin_request(epee::wipeable_string & pin) {}
- virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {}
+ virtual void on_device_button_request(uint64_t code) {}
+ virtual boost::optional<epee::wipeable_string> on_device_pin_request() { return boost::none; }
+ virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device) { return boost::none; }
+ virtual void on_device_progress(const hw::device_progress& event) {};
// Common callbacks
virtual void on_pool_tx_removed(const crypto::hash &txid) {}
virtual ~i_wallet2_callback() {}
@@ -115,9 +117,10 @@ namespace tools
{
public:
wallet_device_callback(wallet2 * wallet): wallet(wallet) {};
- void on_button_request() override;
- void on_pin_request(epee::wipeable_string & pin) override;
- void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) override;
+ void on_button_request(uint64_t code=0) override;
+ boost::optional<epee::wipeable_string> on_pin_request() override;
+ boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) override;
+ void on_progress(const hw::device_progress& event) override;
private:
wallet2 * wallet;
};
@@ -372,7 +375,7 @@ namespace tools
std::vector<uint8_t> extra;
uint64_t unlock_time;
bool use_rct;
- bool use_bulletproofs;
+ rct::RCTConfig rct_config;
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
@@ -385,7 +388,7 @@ namespace tools
FIELD(extra)
FIELD(unlock_time)
FIELD(use_rct)
- FIELD(use_bulletproofs)
+ FIELD(rct_config)
FIELD(dests)
FIELD(subaddr_account)
FIELD(subaddr_indices)
@@ -678,7 +681,9 @@ namespace tools
bool deinit();
bool init(std::string daemon_address = "http://localhost:8080",
- boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0,
+ boost::optional<epee::net_utils::http::login> daemon_login = boost::none,
+ boost::asio::ip::tcp::endpoint proxy = {},
+ uint64_t upper_transaction_weight_limit = 0,
bool trusted_daemon = true,
epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
const std::pair<std::string, std::string> &private_key_and_certificate_path = {},
@@ -797,9 +802,11 @@ namespace tools
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, 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);
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 outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, 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 sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, std::vector<cryptonote::tx_destination_entry> dsts) const;
void cold_tx_aux_import(const std::vector<pending_tx>& ptx, const std::vector<std::string>& tx_device_aux);
void cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux);
uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent);
+ bool parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const;
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func);
@@ -818,7 +825,7 @@ namespace tools
uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); }
void rescan_spent();
- void rescan_blockchain(bool hard, bool refresh = true);
+ void rescan_blockchain(bool hard, bool refresh = true, bool keep_key_images = false);
bool is_transfer_unlocked(const transfer_details& td) const;
bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const;
@@ -1004,12 +1011,16 @@ namespace tools
const std::string & device_derivation_path() const { return m_device_derivation_path; }
void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; }
- bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
+ bool get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
+ bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys);
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);
+ void check_tx_key_helper(const cryptonote::transaction &tx, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received) const;
std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message);
+ std::string get_tx_proof(const cryptonote::transaction &tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) const;
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);
+ bool check_tx_proof(const cryptonote::transaction &tx, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received) const;
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);
@@ -1127,7 +1138,8 @@ namespace tools
std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> export_key_images(bool all = false) const;
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
- bool import_key_images(std::vector<crypto::key_image> key_images);
+ bool import_key_images(std::vector<crypto::key_image> key_images, size_t offset=0, boost::optional<std::unordered_set<size_t>> selected_transfers=boost::none);
+ bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
void update_pool_state(bool refreshed = false);
@@ -1167,11 +1179,11 @@ namespace tools
// fetch txs and store in m_payments
void light_wallet_get_address_txs();
// get_address_info
- bool light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response);
+ bool light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response);
// Login. new_address is true if address hasn't been used on lw node before.
bool light_wallet_login(bool &new_address);
// Send an import request to lw node. returns info about import fee, address and payment_id
- bool light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response);
+ bool light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response);
// get random outputs from light wallet server
void light_wallet_get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
// Parse rct string
@@ -1248,6 +1260,9 @@ namespace tools
void set_tx_notify(const std::shared_ptr<tools::Notify> &notify) { m_tx_notify = notify; }
bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const;
+ void hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const;
+ uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const;
+ void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash);
private:
/*!
@@ -1266,9 +1281,10 @@ namespace tools
bool load_keys(const std::string& keys_file_name, const epee::wipeable_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, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
- void detach_blockchain(uint64_t height);
+ void detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
bool clear();
+ void clear_soft(bool keep_key_images=false);
void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices);
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes);
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false);
@@ -1333,9 +1349,10 @@ namespace tools
void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
wallet_device_callback * get_device_callback();
- void on_button_request();
- void on_pin_request(epee::wipeable_string & pin);
- void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase);
+ void on_device_button_request(uint64_t code);
+ boost::optional<epee::wipeable_string> on_device_pin_request();
+ boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device);
+ void on_device_progress(const hw::device_progress& event);
std::string get_rpc_status(const std::string &s) const;
void throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) const;
@@ -1477,7 +1494,7 @@ BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1)
-BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 3)
+BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4)
BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3)
BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 0)
@@ -1846,11 +1863,27 @@ namespace boost
a & x.subaddr_account;
a & x.subaddr_indices;
if (ver < 2)
+ {
+ if (!typename Archive::is_saving())
+ x.rct_config = { rct::RangeProofBorromean, 0 };
return;
+ }
a & x.selected_transfers;
if (ver < 3)
+ {
+ if (!typename Archive::is_saving())
+ x.rct_config = { rct::RangeProofBorromean, 0 };
return;
- a & x.use_bulletproofs;
+ }
+ if (ver < 4)
+ {
+ bool use_bulletproofs = x.rct_config.range_proof_type != rct::RangeProofBorromean;
+ a & use_bulletproofs;
+ if (!typename Archive::is_saving())
+ x.rct_config = { use_bulletproofs ? rct::RangeProofBulletproof : rct::RangeProofBorromean, 0 };
+ return;
+ }
+ a & x.rct_config;
}
template <class Archive>
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index b9d0a6a75..a4bb342ca 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index a1f251144..c861dca11 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 35862bda1..6ebaaa395 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -699,26 +699,43 @@ namespace tools
explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_weight_limit)
: transfer_error(std::move(loc), "transaction is too big")
, m_tx(tx)
+ , m_tx_valid(true)
+ , m_tx_weight(cryptonote::get_transaction_weight(tx))
, m_tx_weight_limit(tx_weight_limit)
{
}
+ explicit tx_too_big(std::string&& loc, uint64_t tx_weight, uint64_t tx_weight_limit)
+ : transfer_error(std::move(loc), "transaction would be too big")
+ , m_tx_valid(false)
+ , m_tx_weight(tx_weight)
+ , m_tx_weight_limit(tx_weight_limit)
+ {
+ }
+
+ bool tx_valid() const { return m_tx_valid; }
const cryptonote::transaction& tx() const { return m_tx; }
+ uint64_t tx_weight() const { return m_tx_weight; }
uint64_t tx_weight_limit() const { return m_tx_weight_limit; }
std::string to_string() const
{
std::ostringstream ss;
- cryptonote::transaction tx = m_tx;
ss << transfer_error::to_string() <<
", tx_weight_limit = " << m_tx_weight_limit <<
- ", tx weight = " << get_transaction_weight(m_tx) <<
- ", tx:\n" << cryptonote::obj_to_json_str(tx);
+ ", tx weight = " << m_tx_weight;
+ if (m_tx_valid)
+ {
+ cryptonote::transaction tx = m_tx;
+ ss << ", tx:\n" << cryptonote::obj_to_json_str(tx);
+ }
return ss.str();
}
private:
cryptonote::transaction m_tx;
+ bool m_tx_valid;
+ uint64_t m_tx_weight;
uint64_t m_tx_weight_limit;
};
//----------------------------------------------------------------------------------------------------
diff --git a/src/wallet/wallet_light_rpc.h b/src/wallet/wallet_light_rpc.h
new file mode 100644
index 000000000..1d35cec33
--- /dev/null
+++ b/src/wallet/wallet_light_rpc.h
@@ -0,0 +1,320 @@
+// Copyright (c) 2014-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "crypto/hash.h"
+
+namespace tools
+{
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_ADDRESS_TXS
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct spent_output {
+ uint64_t amount;
+ std::string key_image;
+ std::string tx_pub_key;
+ uint64_t out_index;
+ uint32_t mixin;
+
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(key_image)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(out_index)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct transaction
+ {
+ uint64_t id;
+ std::string hash;
+ uint64_t timestamp;
+ uint64_t total_received;
+ uint64_t total_sent;
+ uint64_t unlock_time;
+ uint64_t height;
+ std::list<spent_output> spent_outputs;
+ std::string payment_id;
+ bool coinbase;
+ bool mempool;
+ uint32_t mixin;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(id)
+ KV_SERIALIZE(hash)
+ KV_SERIALIZE(timestamp)
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_sent)
+ KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(height)
+ KV_SERIALIZE(spent_outputs)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(coinbase)
+ KV_SERIALIZE(mempool)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+ struct response_t
+ {
+ //std::list<std::string> txs_as_json;
+ uint64_t total_received;
+ uint64_t total_received_unlocked = 0; // OpenMonero only
+ uint64_t scanned_height;
+ std::vector<transaction> transactions;
+ uint64_t blockchain_height;
+ uint64_t scanned_block_height;
+ std::string status;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_received_unlocked)
+ KV_SERIALIZE(scanned_height)
+ KV_SERIALIZE(transactions)
+ KV_SERIALIZE(blockchain_height)
+ KV_SERIALIZE(scanned_block_height)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_ADDRESS_INFO
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct spent_output
+ {
+ uint64_t amount;
+ std::string key_image;
+ std::string tx_pub_key;
+ uint64_t out_index;
+ uint32_t mixin;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(key_image)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(out_index)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response_t
+ {
+ uint64_t locked_funds;
+ uint64_t total_received;
+ uint64_t total_sent;
+ uint64_t scanned_height;
+ uint64_t scanned_block_height;
+ uint64_t start_height;
+ uint64_t transaction_height;
+ uint64_t blockchain_height;
+ std::list<spent_output> spent_outputs;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(locked_funds)
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_sent)
+ KV_SERIALIZE(scanned_height)
+ KV_SERIALIZE(scanned_block_height)
+ KV_SERIALIZE(start_height)
+ KV_SERIALIZE(transaction_height)
+ KV_SERIALIZE(blockchain_height)
+ KV_SERIALIZE(spent_outputs)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_UNSPENT_OUTS
+ {
+ struct request_t
+ {
+ std::string amount;
+ std::string address;
+ std::string view_key;
+ // OpenMonero specific
+ uint64_t mixin;
+ bool use_dust;
+ std::string dust_threshold;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ KV_SERIALIZE(mixin)
+ KV_SERIALIZE(use_dust)
+ KV_SERIALIZE(dust_threshold)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+
+ struct output {
+ uint64_t amount;
+ std::string public_key;
+ uint64_t index;
+ uint64_t global_index;
+ std::string rct;
+ std::string tx_hash;
+ std::string tx_pub_key;
+ std::string tx_prefix_hash;
+ std::vector<std::string> spend_key_images;
+ uint64_t timestamp;
+ uint64_t height;
+
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(public_key)
+ KV_SERIALIZE(index)
+ KV_SERIALIZE(global_index)
+ KV_SERIALIZE(rct)
+ KV_SERIALIZE(tx_hash)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(tx_prefix_hash)
+ KV_SERIALIZE(spend_key_images)
+ KV_SERIALIZE(timestamp)
+ KV_SERIALIZE(height)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response_t
+ {
+ uint64_t amount;
+ std::list<output> outputs;
+ uint64_t per_kb_fee;
+ std::string status;
+ std::string reason;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(outputs)
+ KV_SERIALIZE(per_kb_fee)
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(reason)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+ //-----------------------------------------------
+ struct COMMAND_RPC_LOGIN
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string view_key;
+ bool create_account;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ KV_SERIALIZE(create_account)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+ std::string reason;
+ bool new_address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(reason)
+ KV_SERIALIZE(new_address)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+ //-----------------------------------------------
+ struct COMMAND_RPC_IMPORT_WALLET_REQUEST
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string payment_id;
+ uint64_t import_fee;
+ bool new_request;
+ bool request_fulfilled;
+ std::string payment_address;
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(import_fee)
+ KV_SERIALIZE(new_request)
+ KV_SERIALIZE(request_fulfilled)
+ KV_SERIALIZE(payment_address)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+ //-----------------------------------------------
+}
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 30def234a..18b2de33f 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -56,6 +56,8 @@ using namespace epee;
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
+#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds
+
namespace
{
const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
@@ -124,13 +126,18 @@ namespace tools
{
m_stop = false;
m_net_server.add_idle_handler([this](){
+ if (m_auto_refresh_period == 0) // disabled
+ return true;
+ if (boost::posix_time::microsec_clock::universal_time() < m_last_auto_refresh_time + boost::posix_time::seconds(m_auto_refresh_period))
+ return true;
try {
if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon());
} catch (const std::exception& ex) {
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
}
+ m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time();
return true;
- }, 20000);
+ }, 1000);
m_net_server.add_idle_handler([this](){
if (m_stop.load(std::memory_order_relaxed))
{
@@ -263,6 +270,9 @@ namespace tools
std::vector<std::vector<uint8_t>> allowed_fingerprints{ rpc_ssl_allowed_fingerprints.size() };
std::transform(rpc_ssl_allowed_fingerprints.begin(), rpc_ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector);
+ m_auto_refresh_period = DEFAULT_AUTO_REFRESH_PERIOD;
+ m_last_auto_refresh_time = boost::posix_time::min_date_time;
+
m_net_server.set_threads_prefix("RPC");
auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
@@ -319,6 +329,8 @@ namespace tools
entry.type = "out";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
+ for (uint32_t i: pd.m_subaddr_indices)
+ entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
}
@@ -339,6 +351,8 @@ namespace tools
entry.note = m_wallet->get_tx_note(txid);
entry.type = is_failed ? "failed" : "pending";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
+ for (uint32_t i: pd.m_subaddr_indices)
+ entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
}
@@ -1074,29 +1088,59 @@ namespace tools
er.message = "command not supported by watch-only wallet";
return false;
}
-
- tools::wallet2::unsigned_tx_set exported_txs;
- try
+ if(req.unsigned_txset.empty() && req.multisig_txset.empty())
{
- cryptonote::blobdata blob;
- if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
- {
- er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
- er.message = "Failed to parse hex.";
- return false;
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "no txset provided";
+ return false;
+ }
+
+ std::vector <wallet2::tx_construction_data> tx_constructions;
+ if (!req.unsigned_txset.empty()) {
+ try {
+ tools::wallet2::unsigned_tx_set exported_txs;
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+ if (!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs)) {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "cannot load unsigned_txset";
+ return false;
+ }
+ tx_constructions = exported_txs.txes;
}
- if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs))
- {
+ catch (const std::exception &e) {
er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
- er.message = "cannot load unsigned_txset";
+ er.message = "failed to parse unsigned transfers: " + std::string(e.what());
+ return false;
+ }
+ } else if (!req.multisig_txset.empty()) {
+ try {
+ tools::wallet2::multisig_tx_set exported_txs;
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.multisig_txset, blob)) {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+ if (!m_wallet->parse_multisig_tx_from_str(blob, exported_txs)) {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA;
+ er.message = "cannot load multisig_txset";
+ return false;
+ }
+
+ for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n) {
+ tx_constructions.push_back(exported_txs.m_ptx[n].construction_data);
+ }
+ }
+ catch (const std::exception &e) {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA;
+ er.message = "failed to parse multisig transfers: " + std::string(e.what());
return false;
}
- }
- catch (const std::exception &e)
- {
- er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
- er.message = "failed to parse unsigned transfers: " + std::string(e.what());
- return false;
}
std::vector<tools::wallet2::pending_tx> ptx;
@@ -1105,9 +1149,9 @@ namespace tools
// gather info to ask the user
std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
int first_known_non_zero_change_index = -1;
- for (size_t n = 0; n < exported_txs.txes.size(); ++n)
+ for (size_t n = 0; n < tx_constructions.size(); ++n)
{
- const tools::wallet2::tx_construction_data &cd = exported_txs.txes[n];
+ const tools::wallet2::tx_construction_data &cd = tx_constructions[n];
res.desc.push_back({0, 0, std::numeric_limits<uint32_t>::max(), 0, {}, "", 0, "", 0, 0, ""});
wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back();
@@ -1171,7 +1215,7 @@ namespace tools
{
if (first_known_non_zero_change_index == -1)
first_known_non_zero_change_index = n;
- const tools::wallet2::tx_construction_data &cdn = exported_txs.txes[first_known_non_zero_change_index];
+ const tools::wallet2::tx_construction_data &cdn = tx_constructions[first_known_non_zero_change_index];
if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr)))
{
er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
@@ -1199,7 +1243,7 @@ namespace tools
if (desc.change_amount > 0)
{
- const tools::wallet2::tx_construction_data &cd0 = exported_txs.txes[0];
+ const tools::wallet2::tx_construction_data &cd0 = tx_constructions[0];
desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
}
@@ -2800,6 +2844,28 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ try
+ {
+ m_auto_refresh_period = req.enable ? req.period ? req.period : DEFAULT_AUTO_REFRESH_PERIOD : 0;
+ MINFO("Auto refresh now " << (m_auto_refresh_period ? std::to_string(m_auto_refresh_period) + " seconds" : std::string("disabled")));
+ return true;
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
@@ -2902,7 +2968,7 @@ namespace tools
er.message = "Invalid filename";
return false;
}
- std::string wallet_file = m_wallet_dir + "/" + req.filename;
+ std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
{
std::vector<std::string> languages;
crypto::ElectrumWords::get_language_list(languages);
@@ -3177,7 +3243,7 @@ namespace tools
}
}
//------------------------------------------------------------------------------------------------------------------------------
- bool wallet_rpc_server::on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request &req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response &res, epee::json_rpc::error &er, const connection_context *ctx)
+ bool wallet_rpc_server::on_generate_from_keys(const wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::request &req, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::response &res, epee::json_rpc::error &er, const connection_context *ctx)
{
if (m_wallet_dir.empty())
{
@@ -3187,12 +3253,169 @@ namespace tools
}
// early check for mandatory fields
- if (req.filename.empty())
+ if (req.viewkey.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "field 'viewkey' is mandatory. Please provide a view key you want to restore from.";
+ return false;
+ }
+ if (req.address.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "field 'address' is mandatory. Please provide a public address.";
+ return false;
+ }
+
+ namespace po = boost::program_options;
+ po::variables_map vm2;
+ const char *ptr = strchr(req.filename.c_str(), '/');
+ #ifdef _WIN32
+ if (!ptr)
+ ptr = strchr(req.filename.c_str(), '\\');
+ if (!ptr)
+ ptr = strchr(req.filename.c_str(), ':');
+ #endif
+ if (ptr)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Invalid filename";
+ return false;
+ }
+ std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
+ // check if wallet file already exists
+ if (!wallet_file.empty())
+ {
+ try
+ {
+ boost::system::error_code ignored_ec;
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(wallet_file, ignored_ec), error::file_exists, wallet_file);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Wallet already exists.";
+ return false;
+ }
+ }
+
+ {
+ po::options_description desc("dummy");
+ const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
+ const char *argv[4];
+ int argc = 3;
+ argv[0] = "wallet-rpc";
+ argv[1] = "--password";
+ argv[2] = req.password.c_str();
+ argv[3] = NULL;
+ vm2 = *m_vm;
+ command_line::add_arg(desc, arg_password);
+ po::store(po::parse_command_line(argc, argv, desc), vm2);
+ }
+
+ auto rc = tools::wallet2::make_new(vm2, true, nullptr);
+ std::unique_ptr<wallet2> wal;
+ wal = std::move(rc.first);
+ if (!wal)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to create wallet";
+ return false;
+ }
+
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, wal->nettype(), req.address))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to.";
+ er.message = "Failed to parse public address";
+ return false;
+ }
+
+ epee::wipeable_string password = rc.second.password();
+ epee::wipeable_string viewkey_string = req.viewkey;
+ crypto::secret_key viewkey;
+ if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to parse view key secret key";
+ return false;
+ }
+
+ try
+ {
+ if (!req.spendkey.empty())
+ {
+ epee::wipeable_string spendkey_string = req.spendkey;
+ crypto::secret_key spendkey;
+ if (!spendkey_string.hex_to_pod(unwrap(unwrap(spendkey))))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to parse spend key secret key";
+ return false;
+ }
+ wal->generate(wallet_file, std::move(rc.second).password(), info.address, spendkey, viewkey, false);
+ res.info = "Wallet has been generated successfully.";
+ }
+ else
+ {
+ wal->generate(wallet_file, std::move(rc.second).password(), info.address, viewkey, false);
+ res.info = "Watch-only wallet has been generated successfully.";
+ }
+ MINFO("Wallet has been generated.\n");
+ }
+ catch (const std::exception &e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
return false;
}
+
+ if (!wal)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to generate wallet";
+ return false;
+ }
+
+ // set blockheight if given
+ try
+ {
+ wal->set_refresh_from_block_height(req.restore_height);
+ wal->rewrite(wallet_file, password);
+ }
+ catch (const std::exception &e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+
+ if (m_wallet)
+ {
+ try
+ {
+ if (!wallet_file.empty())
+ m_wallet->store();
+ }
+ catch (const std::exception &e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ delete m_wallet;
+ }
+ m_wallet = wal.release();
+ res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request &req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response &res, epee::json_rpc::error &er, const connection_context *ctx)
+ {
+ if (m_wallet_dir.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR;
+ er.message = "No wallet dir configured";
+ return false;
+ }
+
+ // early check for mandatory fields
if (req.seed.empty())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -3215,7 +3438,7 @@ namespace tools
er.message = "Invalid filename";
return false;
}
- std::string wallet_file = m_wallet_dir + "/" + req.filename;
+ std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
// check if wallet file already exists
if (!wallet_file.empty())
{
@@ -3331,7 +3554,7 @@ namespace tools
er.message = "Failed to encode seed";
return false;
}
- res.seed = electrum_words.data();
+ res.seed = std::string(electrum_words.data(), electrum_words.size());
if (!wal)
{
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 8157344c2..2b52275b8 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -129,6 +129,7 @@ namespace tools
MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH)
+ MAP_JON_RPC_WE("auto_refresh", on_auto_refresh, wallet_rpc::COMMAND_RPC_AUTO_REFRESH)
MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT)
MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING)
MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING)
@@ -137,6 +138,7 @@ namespace tools
MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET)
MAP_JON_RPC_WE("close_wallet", on_close_wallet, wallet_rpc::COMMAND_RPC_CLOSE_WALLET)
MAP_JON_RPC_WE("change_wallet_password", on_change_wallet_password, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD)
+ MAP_JON_RPC_WE("generate_from_keys", on_generate_from_keys, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS)
MAP_JON_RPC_WE("restore_deterministic_wallet", on_restore_deterministic_wallet, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET)
MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG)
MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG)
@@ -209,6 +211,7 @@ namespace tools
bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+ bool on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
@@ -217,6 +220,7 @@ namespace tools
bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+ bool on_generate_from_keys(const wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::request& req, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request& req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
@@ -254,5 +258,7 @@ namespace tools
std::atomic<bool> m_stop;
bool m_restricted;
const boost::program_options::variables_map *m_vm;
+ uint32_t m_auto_refresh_period;
+ boost::posix_time::ptime m_last_auto_refresh_time;
};
}
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 546e572bc..df4370949 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@@ -610,9 +610,11 @@ namespace wallet_rpc
struct request_t
{
std::string unsigned_txset;
+ std::string multisig_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(unsigned_txset)
+ KV_SERIALIZE(multisig_txset)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -1357,6 +1359,7 @@ namespace wallet_rpc
std::string type;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
+ std::vector<cryptonote::subaddress_index> subaddr_indices;
std::string address;
bool double_spend_seen;
uint64_t confirmations;
@@ -1374,6 +1377,7 @@ namespace wallet_rpc
KV_SERIALIZE(type);
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index);
+ KV_SERIALIZE(subaddr_indices);
KV_SERIALIZE(address);
KV_SERIALIZE(double_spend_seen)
KV_SERIALIZE_OPT(confirmations, (uint64_t)0)
@@ -1928,6 +1932,28 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_AUTO_REFRESH
+ {
+ struct request_t
+ {
+ bool enable;
+ uint32_t period; // seconds
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(enable, true)
+ KV_SERIALIZE_OPT(period, (uint32_t)0)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_START_MINING
{
struct request_t
@@ -2074,6 +2100,39 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_GENERATE_FROM_KEYS
+ {
+ struct request
+ {
+ uint64_t restore_height;
+ std::string filename;
+ std::string address;
+ std::string spendkey;
+ std::string viewkey;
+ std::string password;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
+ KV_SERIALIZE(filename)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(spendkey)
+ KV_SERIALIZE(viewkey)
+ KV_SERIALIZE(password)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string address;
+ std::string info;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(info)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET
{
struct request_t
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index 9b3a2847d..440a58a47 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//