aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt2
-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.cpp83
-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.cpp366
-rw-r--r--src/wallet/wallet2.h38
-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_rpc_server.cpp245
-rw-r--r--src/wallet/wallet_rpc_server.h4
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h37
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h2
33 files changed, 778 insertions, 141 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 2991f75c5..efd61cb5a 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.
#
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 3202f2755..d7226b656 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,6 +1486,8 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
extra, subaddr_account, subaddr_indices);
}
+ pendingTxPostProcess(transaction);
+
if (multisig().isMultisig) {
auto tx_set = m_wallet->make_multisig_tx_set(transaction->m_pending_tx);
transaction->m_pending_tx = tx_set.m_ptx;
@@ -1513,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"?
@@ -2095,6 +2156,21 @@ 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
@@ -2327,6 +2403,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 085e6075f..6036154ac 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.
//
@@ -793,9 +793,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 +805,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 +928,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):
@@ -1495,11 +1502,17 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
}
THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice");
- outs.push_back(i);
if (tx_scan_info.money_transfered == 0 && !miner_tx)
{
tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device());
}
+ if (tx_scan_info.money_transfered == 0)
+ {
+ MERROR("Invalid output amount, skipping");
+ tx_scan_info.error = true;
+ return;
+ }
+ outs.push_back(i);
THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info.received->index] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.money_transfered,
error::wallet_internal_error, "Overflow in received amounts");
tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered;
@@ -2878,6 +2891,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);
@@ -3034,6 +3048,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()));
@@ -3217,6 +3232,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.
@@ -5085,7 +5120,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()
@@ -5463,8 +5505,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();
@@ -5472,25 +5518,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
@@ -6329,17 +6366,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)
{
@@ -6348,7 +6385,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;
}
@@ -6370,6 +6407,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));
@@ -7019,6 +7067,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);
@@ -9143,6 +9202,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);
}
}
@@ -9638,6 +9698,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;
@@ -9854,7 +9915,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);
@@ -9867,6 +9928,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
@@ -10269,7 +10406,8 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
{
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.");
+ bool found_tx_key = get_tx_key(txid, tx_key, additional_tx_keys);
+ THROW_WALLET_EXCEPTION_IF(!found_tx_key, 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);
@@ -11008,15 +11146,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;
@@ -11026,16 +11155,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
@@ -11277,6 +11405,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)
{
@@ -11386,6 +11525,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)
@@ -11439,32 +11579,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;
@@ -12445,22 +12605,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
@@ -12484,5 +12652,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 b7ebe03d9..c92404940 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.
//
@@ -67,6 +67,7 @@
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
class Serialization_portability_wallet_Test;
+class wallet_accessor_test;
namespace tools
{
@@ -102,9 +103,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() {}
@@ -114,9 +116,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;
};
@@ -171,6 +174,7 @@ namespace tools
class wallet2
{
friend class ::Serialization_portability_wallet_Test;
+ friend class ::wallet_accessor_test;
friend class wallet_keys_unlocker;
friend class wallet_device_callback;
public:
@@ -798,6 +802,7 @@ namespace tools
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);
@@ -816,7 +821,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;
@@ -1002,8 +1007,9 @@ 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);
std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message);
@@ -1125,7 +1131,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);
@@ -1246,6 +1253,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:
/*!
@@ -1267,6 +1277,7 @@ namespace tools
void detach_blockchain(uint64_t height);
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);
@@ -1331,9 +1342,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;
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_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 90afa91fd..7040597df 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.
//
@@ -1074,29 +1074,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 +1135,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 +1201,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 +1229,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);
}
@@ -3177,6 +3207,174 @@ namespace tools
}
}
//------------------------------------------------------------------------------------------------------------------------------
+ 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())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR;
+ er.message = "No wallet dir configured";
+ return false;
+ }
+
+ // early check for mandatory fields
+ if (req.filename.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to.";
+ return false;
+ }
+ 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 = 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 = "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
+ {
+ 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())
@@ -4040,6 +4238,7 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_rpc_ssl_private_key);
command_line::add_arg(desc_params, arg_rpc_ssl_certificate);
command_line::add_arg(desc_params, arg_rpc_ssl_allowed_certificates);
+ command_line::add_arg(desc_params, arg_rpc_ssl_allowed_fingerprints);
daemonizer::init_options(hidden_options, desc_params);
desc_params.add(hidden_options);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 8157344c2..affaf10f7 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.
//
@@ -137,6 +137,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)
@@ -217,6 +218,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);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 546e572bc..b0e8bed93 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;
@@ -2074,6 +2076,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.
//