aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt6
-rw-r--r--src/wallet/api/CMakeLists.txt6
-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.cpp2
-rw-r--r--src/wallet/api/pending_transaction.h2
-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.cpp78
-rw-r--r--src/wallet/api/wallet.h15
-rw-r--r--src/wallet/api/wallet2_api.h59
-rw-r--r--src/wallet/api/wallet_manager.cpp32
-rw-r--r--src/wallet/api/wallet_manager.h19
-rw-r--r--src/wallet/node_rpc_proxy.cpp2
-rw-r--r--src/wallet/node_rpc_proxy.h2
-rw-r--r--src/wallet/wallet2.cpp562
-rw-r--r--src/wallet/wallet2.h99
-rw-r--r--src/wallet/wallet_args.cpp2
-rw-r--r--src/wallet/wallet_args.h2
-rw-r--r--src/wallet/wallet_errors.h2
-rw-r--r--src/wallet/wallet_rpc_server.cpp91
-rw-r--r--src/wallet/wallet_rpc_server.h9
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h60
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h2
33 files changed, 901 insertions, 177 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 2d664ba15..c6d0bd9da 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2017, The Monero Project
+# Copyright (c) 2014-2018, The Monero Project
#
# All rights reserved.
#
@@ -126,6 +126,6 @@ if (BUILD_GUI_DEPS)
endif()
install(TARGETS wallet_merged
ARCHIVE DESTINATION ${lib_folder})
-
- add_subdirectory(api)
endif()
+
+add_subdirectory(api)
diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt
index 26127b75c..1e67495f1 100644
--- a/src/wallet/api/CMakeLists.txt
+++ b/src/wallet/api/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2017, The Monero Project
+# Copyright (c) 2014-2018, The Monero Project
#
# All rights reserved.
#
@@ -78,6 +78,8 @@ target_link_libraries(wallet_api
PRIVATE
${EXTRA_LIBRARIES})
+set_property(TARGET wallet_api PROPERTY EXCLUDE_FROM_ALL TRUE)
+set_property(TARGET obj_wallet_api PROPERTY EXCLUDE_FROM_ALL TRUE)
if(IOS)
set(lib_folder lib-${ARCH})
@@ -86,4 +88,4 @@ else()
endif()
install(FILES ${wallet_api_headers}
- DESTINATION include/wallet)
+ DESTINATION include/wallet/api)
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp
index c2238f9d6..38c34a912 100644
--- a/src/wallet/api/address_book.cpp
+++ b/src/wallet/api/address_book.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h
index b92743fe6..7d9200550 100644
--- a/src/wallet/api/address_book.h
+++ b/src/wallet/api/address_book.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp
index e17931de1..ff4619f0f 100644
--- a/src/wallet/api/pending_transaction.cpp
+++ b/src/wallet/api/pending_transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h
index c98da59da..d0bd66eb5 100644
--- a/src/wallet/api/pending_transaction.h
+++ b/src/wallet/api/pending_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp
index ceda9a9da..61dbbf4b0 100644
--- a/src/wallet/api/subaddress.cpp
+++ b/src/wallet/api/subaddress.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017, The Monero Project
+// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h
index 45fef6e67..3f9e37ac8 100644
--- a/src/wallet/api/subaddress.h
+++ b/src/wallet/api/subaddress.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017, The Monero Project
+// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/subaddress_account.cpp b/src/wallet/api/subaddress_account.cpp
index 736ef874e..19ed8fb38 100644
--- a/src/wallet/api/subaddress_account.cpp
+++ b/src/wallet/api/subaddress_account.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017, The Monero Project
+// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/subaddress_account.h b/src/wallet/api/subaddress_account.h
index 0a4db9671..b052182f8 100644
--- a/src/wallet/api/subaddress_account.h
+++ b/src/wallet/api/subaddress_account.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017, The Monero Project
+// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index 8a8243047..95a055f8f 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h
index e5207e53a..7bdce97e2 100644
--- a/src/wallet/api/transaction_history.h
+++ b/src/wallet/api/transaction_history.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp
index 1a5df454c..cc3209609 100644
--- a/src/wallet/api/transaction_info.cpp
+++ b/src/wallet/api/transaction_info.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h
index c961c0a9e..5df9a44ef 100644
--- a/src/wallet/api/transaction_info.h
+++ b/src/wallet/api/transaction_info.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index d22719189..c6ebcb009 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h
index 33c994d5f..a35630535 100644
--- a/src/wallet/api/unsigned_transaction.h
+++ b/src/wallet/api/unsigned_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp
index e54dd9f1c..aebe41e59 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 3073bcc56..f648160c9 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -458,6 +458,16 @@ bool WalletImpl::recoverFromKeys(const std::string &path,
const std::string &viewkey_string,
const std::string &spendkey_string)
{
+ return recoverFromKeysWithPassword(path, "", language, address_string, viewkey_string, spendkey_string);
+}
+
+bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
+ const std::string &password,
+ const std::string &language,
+ const std::string &address_string,
+ const std::string &viewkey_string,
+ const std::string &spendkey_string)
+{
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, m_wallet->testnet(), address_string))
{
@@ -524,12 +534,12 @@ bool WalletImpl::recoverFromKeys(const std::string &path,
try
{
if (has_spendkey) {
- m_wallet->generate(path, "", info.address, spendkey, viewkey);
+ m_wallet->generate(path, password, info.address, spendkey, viewkey);
setSeedLanguage(language);
LOG_PRINT_L1("Generated new wallet from keys with seed language: " + language);
}
else {
- m_wallet->generate(path, "", info.address, viewkey);
+ m_wallet->generate(path, password, info.address, viewkey);
LOG_PRINT_L1("Generated new view only wallet from keys");
}
@@ -570,6 +580,11 @@ bool WalletImpl::open(const std::string &path, const std::string &password)
bool WalletImpl::recover(const std::string &path, const std::string &seed)
{
+ return recover(path, "", seed);
+}
+
+bool WalletImpl::recover(const std::string &path, const std::string &password, const std::string &seed)
+{
clearStatus();
m_errorString.clear();
if (seed.empty()) {
@@ -590,7 +605,7 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed)
try {
m_wallet->set_seed_language(old_language);
- m_wallet->generate(path, "", recovery_key, true, false);
+ m_wallet->generate(path, password, recovery_key, true, false);
} catch (const std::exception &e) {
m_status = Status_Critical;
@@ -1059,6 +1074,8 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIXIN;
+ uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast<uint32_t>(priority));
+
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
do {
@@ -1118,7 +1135,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
de.is_subaddress = info.is_subaddress;
dsts.push_back(de);
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */,
- static_cast<uint32_t>(priority),
+ adjusted_priority,
extra, subaddr_account, subaddr_indices, m_trustedDaemon);
} else {
// for the GUI, sweep_all (i.e. amount set as "(all)") will always sweep all the funds in all the addresses
@@ -1128,7 +1145,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
subaddr_indices.insert(index);
}
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */,
- static_cast<uint32_t>(priority),
+ adjusted_priority,
extra, subaddr_account, subaddr_indices, m_trustedDaemon);
}
@@ -1576,6 +1593,55 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string
}
}
+std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const {
+ try
+ {
+ m_status = Status_Ok;
+ boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
+ if (!all)
+ {
+ account_minreserve = std::make_pair(account_index, amount);
+ }
+ return m_wallet->get_reserve_proof(account_minreserve, message);
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return "";
+ }
+}
+
+bool WalletImpl::checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const {
+ cryptonote::address_parse_info info;
+ if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Failed to parse address");
+ return false;
+ }
+ if (info.is_subaddress)
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Address must not be a subaddress");
+ return false;
+ }
+
+ good = false;
+ try
+ {
+ m_status = Status_Ok;
+ good = m_wallet->check_reserve_proof(info.address, message, signature, total, spent);
+ return true;
+ }
+ catch (const std::exception &e)
+ {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
+ }
+}
+
std::string WalletImpl::signMessage(const std::string &message)
{
return m_wallet->sign(message);
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 01359ffc6..fcd53c3f8 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -59,7 +59,18 @@ public:
bool createWatchOnly(const std::string &path, const std::string &password,
const std::string &language) const;
bool open(const std::string &path, const std::string &password);
+ bool recover(const std::string &path,const std::string &password,
+ const std::string &seed);
+ bool recoverFromKeysWithPassword(const std::string &path,
+ const std::string &password,
+ const std::string &language,
+ const std::string &address_string,
+ const std::string &viewkey_string,
+ const std::string &spendkey_string = "");
+ // following two methods are deprecated since they create passwordless wallets
+ // use the two equivalent methods above
bool recover(const std::string &path, const std::string &seed);
+ // deprecated: use recoverFromKeysWithPassword() instead
bool recoverFromKeys(const std::string &path,
const std::string &language,
const std::string &address_string,
@@ -142,6 +153,8 @@ public:
virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations);
virtual std::string getSpendProof(const std::string &txid, const std::string &message) const;
virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const;
+ virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const;
+ virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const;
virtual std::string signMessage(const std::string &message);
virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const;
virtual void startRefresh();
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index ab1a48d6e..a22788399 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -70,6 +70,7 @@ struct PendingTransaction
};
enum Priority {
+ Priority_Default = 0,
Priority_Low = 1,
Priority_Medium = 2,
Priority_High = 3,
@@ -105,13 +106,6 @@ struct UnsignedTransaction
Status_Critical
};
- enum Priority {
- Priority_Low = 1,
- Priority_Medium = 2,
- Priority_High = 3,
- Priority_Last
- };
-
virtual ~UnsignedTransaction() = 0;
virtual int status() const = 0;
virtual std::string errorString() const = 0;
@@ -706,6 +700,12 @@ struct Wallet
virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0;
virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const = 0;
+ /*!
+ * \brief getReserveProof - Generates a proof that proves the reserve of unspent funds
+ * Parameters `account_index` and `amount` are ignored when `all` is true
+ */
+ virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const = 0;
+ virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const = 0;
/*
* \brief signMessage - sign a message with the spend private key
@@ -749,7 +749,7 @@ struct WalletManager
* \brief Creates new wallet
* \param path Name of wallet file
* \param password Password of wallet file
- * \param language Language to be used to generate electrum seed memo
+ * \param language Language to be used to generate electrum seed mnemonic
* \return Wallet instance (Wallet::status() needs to be called to check if created successfully)
*/
virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, bool testnet = false) = 0;
@@ -763,16 +763,51 @@ struct WalletManager
virtual Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) = 0;
/*!
- * \brief recovers existing wallet using memo (electrum seed)
+ * \brief recovers existing wallet using mnemonic (electrum seed)
+ * \param path Name of wallet file to be created
+ * \param password Password of wallet file
+ * \param mnemonic mnemonic (25 words electrum seed)
+ * \param testnet testnet
+ * \param restoreHeight restore from start height
+ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
+ */
+ virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
+ bool testnet = false, uint64_t restoreHeight = 0) = 0;
+
+ /*!
+ * \deprecated this method creates a wallet WITHOUT a passphrase, use the alternate recoverWallet() method
+ * \brief recovers existing wallet using mnemonic (electrum seed)
* \param path Name of wallet file to be created
- * \param memo memo (25 words electrum seed)
+ * \param mnemonic mnemonic (25 words electrum seed)
* \param testnet testnet
* \param restoreHeight restore from start height
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
- virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet = false, uint64_t restoreHeight = 0) = 0;
+ virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, bool testnet = false, uint64_t restoreHeight = 0) = 0;
+
+ /*!
+ * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted
+ * \param path Name of wallet file to be created
+ * \param password Password of wallet file
+ * \param language language
+ * \param testnet testnet
+ * \param restoreHeight restore from start height
+ * \param addressString public address
+ * \param viewKeyString view key
+ * \param spendKeyString spend key (optional)
+ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
+ */
+ virtual Wallet * createWalletFromKeys(const std::string &path,
+ const std::string &password,
+ const std::string &language,
+ bool testnet,
+ uint64_t restoreHeight,
+ const std::string &addressString,
+ const std::string &viewKeyString,
+ const std::string &spendKeyString = "") = 0;
/*!
+ * \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead
* \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted
* \param path Name of wallet file to be created
* \param language language
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index a6e5e551e..bb144227e 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -76,17 +76,39 @@ Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string
return wallet;
}
-Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &memo, bool testnet, uint64_t restoreHeight)
+Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &mnemonic, bool testnet, uint64_t restoreHeight)
+{
+ return recoveryWallet(path, "", mnemonic, testnet, restoreHeight);
+}
+
+Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
+ const std::string &language,
+ bool testnet,
+ uint64_t restoreHeight,
+ const std::string &addressString,
+ const std::string &viewKeyString,
+ const std::string &spendKeyString)
+{
+ return createWalletFromKeys(path, "", language, testnet, restoreHeight,
+ addressString, viewKeyString, spendKeyString);
+}
+
+Wallet *WalletManagerImpl::recoveryWallet(const std::string &path,
+ const std::string &password,
+ const std::string &mnemonic,
+ bool testnet,
+ uint64_t restoreHeight)
{
WalletImpl * wallet = new WalletImpl(testnet);
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
}
- wallet->recover(path, memo);
+ wallet->recover(path, password, mnemonic);
return wallet;
}
-Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
+Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
+ const std::string &password,
const std::string &language,
bool testnet,
uint64_t restoreHeight,
@@ -98,7 +120,7 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
}
- wallet->recoverFromKeys(path, language, addressString, viewKeyString, spendKeyString);
+ wallet->recoverFromKeysWithPassword(path, password, language, addressString, viewKeyString, spendKeyString);
return wallet;
}
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index ef5b8f91b..6a4d9de2e 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -40,7 +40,22 @@ public:
Wallet * createWallet(const std::string &path, const std::string &password,
const std::string &language, bool testnet);
Wallet * openWallet(const std::string &path, const std::string &password, bool testnet);
- virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet, uint64_t restoreHeight);
+ virtual Wallet * recoveryWallet(const std::string &path,
+ const std::string &password,
+ const std::string &mnemonic,
+ bool testnet,
+ uint64_t restoreHeight);
+ virtual Wallet * createWalletFromKeys(const std::string &path,
+ const std::string &password,
+ const std::string &language,
+ bool testnet,
+ uint64_t restoreHeight,
+ const std::string &addressString,
+ const std::string &viewKeyString,
+ const std::string &spendKeyString = "");
+ // next two methods are deprecated - use the above version which allow setting of a password
+ virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, bool testnet, uint64_t restoreHeight);
+ // deprecated: use createWalletFromKeys(..., password, ...) instead
virtual Wallet * createWalletFromKeys(const std::string &path,
const std::string &language,
bool testnet,
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 07185d12c..a9562291e 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017, The Monero Project
+// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h
index fb288189a..1b183212d 100644
--- a/src/wallet/node_rpc_proxy.h
+++ b/src/wallet/node_rpc_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017, The Monero Project
+// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index f809e424f..f83aad63d 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -53,6 +53,7 @@ using namespace epee;
#include "profile_tools.h"
#include "crypto/crypto.h"
#include "serialization/binary_utils.h"
+#include "serialization/string.h"
#include "cryptonote_basic/blobdatatype.h"
#include "mnemonics/electrum-words.h"
#include "common/i18n.h"
@@ -62,7 +63,7 @@ using namespace epee;
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "common/json_util.h"
-#include "common/memwipe.h"
+#include "memwipe.h"
#include "common/base58.h"
#include "ringct/rctSigs.h"
@@ -356,7 +357,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
const bool deprecated_wallet = restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) ||
crypto::ElectrumWords::get_is_old_style_seed(field_seed));
THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error,
- tools::wallet2::tr("Cannot create deprecated wallets from JSON"));
+ tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
wallet.reset(make_basic(vm, opts, password_prompter).release());
wallet->set_refresh_from_block_height(field_scan_from_height);
@@ -531,20 +532,16 @@ size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, si
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
}
-uint8_t get_bulletproof_fork(bool testnet)
+uint8_t get_bulletproof_fork()
{
- if (testnet)
- return 7;
- else
- return 255; // TODO
+ return 8;
}
crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx)
{
crypto::hash8 payment_id8 = null_hash8;
std::vector<tx_extra_field> tx_extra_fields;
- if(!parse_tx_extra(ptx.tx.extra, tx_extra_fields))
- return payment_id8;
+ parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
cryptonote::tx_extra_nonce extra_nonce;
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
@@ -611,6 +608,8 @@ wallet2::wallet2(bool testnet, bool restricted):
m_merge_destinations(false),
m_confirm_backlog(true),
m_confirm_backlog_threshold(0),
+ m_confirm_export_overwrite(true),
+ m_auto_low_priority(true),
m_is_initialized(false),
m_restricted(restricted),
is_old_file_format(false),
@@ -1010,7 +1009,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs)
+void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs) const
{
THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
if (m_multisig)
@@ -1021,7 +1020,7 @@ void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote
}
else
{
- bool r = cryptonote::generate_key_image_helper_precomp(keys, boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki);
+ bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
@@ -1126,7 +1125,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
+ scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
@@ -1144,7 +1143,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
+ scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
@@ -1156,7 +1155,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
- scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
+ scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
@@ -1331,6 +1330,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
}
+ uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee;
+
if (tx_money_spent_in_ins > 0 && !pool)
{
uint64_t self_received = std::accumulate<decltype(tx_money_got_in_outs.begin()), uint64_t>(tx_money_got_in_outs.begin(), tx_money_got_in_outs.end(), 0,
@@ -1340,7 +1341,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
});
process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
// if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing
- uint64_t fee = tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee;
if (tx_money_spent_in_ins == self_received + fee)
{
auto i = m_confirmed_txs.find(txid);
@@ -1405,6 +1405,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
payment_details payment;
payment.m_tx_hash = txid;
+ payment.m_fee = fee;
payment.m_amount = i.second;
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
@@ -1459,14 +1460,12 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
entry.first->second.m_change = received;
std::vector<tx_extra_field> tx_extra_fields;
- if(parse_tx_extra(tx.extra, tx_extra_fields))
+ parse_tx_extra(tx.extra, tx_extra_fields); // ok if partially parsed
+ tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
- tx_extra_nonce extra_nonce;
- if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
- {
- // we do not care about failure here
- get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, entry.first->second.m_payment_id);
- }
+ // we do not care about failure here
+ get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, entry.first->second.m_payment_id);
}
entry.first->second.m_subaddr_account = subaddr_account;
entry.first->second.m_subaddr_indices = subaddr_indices;
@@ -2035,6 +2034,11 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
pull_hashes(0, blocks_start_height, short_chain_history, hashes);
if (hashes.size() <= 3)
return;
+ if (blocks_start_height < m_blockchain.offset())
+ {
+ MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset());
+ return;
+ }
if (hashes.size() + current_index < stop_height) {
drop_from_short_history(short_chain_history, 3);
std::list<crypto::hash>::iterator right = hashes.end();
@@ -2438,6 +2442,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(m_confirm_backlog_threshold);
json.AddMember("confirm_backlog_threshold", value2, json.GetAllocator());
+ value2.SetInt(m_confirm_export_overwrite ? 1 :0);
+ json.AddMember("confirm_export_overwrite", value2, json.GetAllocator());
+
+ value2.SetInt(m_auto_low_priority ? 1 : 0);
+ json.AddMember("auto_low_priority", value2, json.GetAllocator());
+
value2.SetInt(m_testnet ? 1 :0);
json.AddMember("testnet", value2, json.GetAllocator());
@@ -2519,6 +2529,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_merge_destinations = false;
m_confirm_backlog = true;
m_confirm_backlog_threshold = 0;
+ m_confirm_export_overwrite = true;
+ m_auto_low_priority = true;
}
else if(json.IsObject())
{
@@ -2618,6 +2630,10 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_confirm_backlog = field_confirm_backlog;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_backlog_threshold, uint32_t, Uint, false, 0);
m_confirm_backlog_threshold = field_confirm_backlog_threshold;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_export_overwrite, int, Int, false, true);
+ m_confirm_export_overwrite = field_confirm_export_overwrite;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, m_auto_low_priority, int, Int, false, true);
+ m_auto_low_priority = field_m_auto_low_priority;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, testnet, int, Int, false, m_testnet);
// Wallet is being opened with testnet flag, but is saved as a mainnet wallet
THROW_WALLET_EXCEPTION_IF(m_testnet && !field_testnet, error::wallet_internal_error, "Mainnet wallet can not be opened as testnet wallet");
@@ -2977,6 +2993,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
cryptonote::block b;
generate_genesis(b);
m_blockchain.push_back(get_block_hash(b));
+ add_subaddress_account(tr("Primary account"));
if (!wallet_.empty())
store();
@@ -3406,16 +3423,6 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash&
return false;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::set_default_decimal_point(unsigned int decimal_point)
-{
- cryptonote::set_default_decimal_point(decimal_point);
-}
-//----------------------------------------------------------------------------------------------------
-unsigned int wallet2::get_default_decimal_point() const
-{
- return cryptonote::get_default_decimal_point();
-}
-//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_file_names(const std::string& file_path)
{
do_prepare_file_names(file_path, m_keys_file, m_wallet_file);
@@ -4131,7 +4138,7 @@ size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::v
// returns:
// direct return: amount of money found
// modified reference: selected_transfers, a list of iterators/indices of input sources
-uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon)
+uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon) const
{
uint64_t found_money = 0;
selected_transfers.reserve(unused_transfers_indices.size());
@@ -4139,7 +4146,7 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> un
{
size_t idx = pop_best_value(unused_transfers_indices, selected_transfers);
- transfer_container::iterator it = m_transfers.begin() + idx;
+ const transfer_container::const_iterator it = m_transfers.begin() + idx;
selected_transfers.push_back(idx);
found_money += it->amount();
}
@@ -4233,8 +4240,7 @@ std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
{
std::vector<tx_extra_field> tx_extra_fields;
- if(!parse_tx_extra(ptx.tx.extra, tx_extra_fields))
- return crypto::null_hash;
+ parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed
tx_extra_nonce extra_nonce;
crypto::hash payment_id = null_hash;
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
@@ -4347,7 +4353,7 @@ void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
}
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename)
+bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const
{
LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions");
unsigned_tx_set txs;
@@ -4376,7 +4382,7 @@ bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::stri
return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + ciphertext);
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs)
+bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const
{
std::string s;
boost::system::error_code errcode;
@@ -4950,7 +4956,7 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
return sign_multisig_tx_to_file(exported_txs, filename, txids);
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm)
+uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
{
static const uint64_t old_multipliers[3] = {1, 2, 3};
static const uint64_t new_multipliers[3] = {1, 20, 166};
@@ -4987,7 +4993,7 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm)
return 1;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_dynamic_per_kb_fee_estimate()
+uint64_t wallet2::get_dynamic_per_kb_fee_estimate() const
{
uint64_t fee;
boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_per_kb_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
@@ -4997,7 +5003,7 @@ uint64_t wallet2::get_dynamic_per_kb_fee_estimate()
return FEE_PER_KB;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_per_kb_fee()
+uint64_t wallet2::get_per_kb_fee() const
{
if(m_light_wallet)
return m_light_wallet_per_kb_fee;
@@ -5008,7 +5014,7 @@ uint64_t wallet2::get_per_kb_fee()
return get_dynamic_per_kb_fee_estimate();
}
//----------------------------------------------------------------------------------------------------
-int wallet2::get_fee_algorithm()
+int wallet2::get_fee_algorithm() const
{
// changes at v3 and v5
if (use_fork_rules(5, 0))
@@ -5018,7 +5024,7 @@ int wallet2::get_fee_algorithm()
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
-uint64_t wallet2::adjust_mixin(uint64_t mixin)
+uint64_t wallet2::adjust_mixin(uint64_t mixin) const
{
if (mixin < 4 && use_fork_rules(6, 10)) {
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5");
@@ -5031,6 +5037,90 @@ uint64_t wallet2::adjust_mixin(uint64_t mixin)
return mixin;
}
//----------------------------------------------------------------------------------------------------
+uint32_t wallet2::adjust_priority(uint32_t priority)
+{
+ if (priority == 0 && get_default_priority() != 1 && auto_low_priority())
+ {
+ try
+ {
+ // check if there's a backlog in the tx pool
+ const double fee_level = get_fee_multiplier(1) * get_per_kb_fee() * (12/(double)13) / (double)1024;
+ const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
+ if (blocks.size() != 1)
+ {
+ MERROR("Bad estimated backlog array size");
+ return priority;
+ }
+ else if (blocks[0].first > 0)
+ {
+ MINFO("We don't use the low priority because there's a backlog in the tx pool.");
+ return priority;
+ }
+
+ // get the current full reward zone
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_INFO::request> getinfo_req = AUTO_VAL_INIT(getinfo_req);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_INFO::response, std::string> getinfo_res = AUTO_VAL_INIT(getinfo_res);
+ m_daemon_rpc_mutex.lock();
+ getinfo_req.jsonrpc = "2.0";
+ getinfo_req.id = epee::serialization::storage_entry(0);
+ getinfo_req.method = "get_info";
+ bool r = net_utils::invoke_http_json("/json_rpc", getinfo_req, getinfo_res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info");
+ THROW_WALLET_EXCEPTION_IF(getinfo_res.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info");
+ THROW_WALLET_EXCEPTION_IF(getinfo_res.result.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
+ const uint64_t full_reward_zone = getinfo_res.result.block_size_limit / 2;
+
+ // get the last N block headers and sum the block sizes
+ const size_t N = 10;
+ if (m_blockchain.size() < N)
+ {
+ MERROR("The blockchain is too short");
+ return priority;
+ }
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request> getbh_req = AUTO_VAL_INIT(getbh_req);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response, std::string> getbh_res = AUTO_VAL_INIT(getbh_res);
+ m_daemon_rpc_mutex.lock();
+ getbh_req.jsonrpc = "2.0";
+ getbh_req.id = epee::serialization::storage_entry(0);
+ getbh_req.method = "getblockheadersrange";
+ getbh_req.params.start_height = m_blockchain.size() - N;
+ getbh_req.params.end_height = m_blockchain.size() - 1;
+ r = net_utils::invoke_http_json("/json_rpc", getbh_req, getbh_res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange");
+ THROW_WALLET_EXCEPTION_IF(getbh_res.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange");
+ THROW_WALLET_EXCEPTION_IF(getbh_res.result.status != CORE_RPC_STATUS_OK, error::get_blocks_error, getbh_res.result.status);
+ if (getbh_res.result.headers.size() != N)
+ {
+ MERROR("Bad blockheaders size");
+ return priority;
+ }
+ size_t block_size_sum = 0;
+ for (const cryptonote::block_header_response &i : getbh_res.result.headers)
+ {
+ block_size_sum += i.block_size;
+ }
+
+ // estimate how 'full' the last N blocks are
+ const size_t P = 100 * block_size_sum / (N * full_reward_zone);
+ MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
+ if (P > 80)
+ {
+ MINFO("We don't use the low priority because recent blocks are quite full.");
+ return priority;
+ }
+ MINFO("We'll use the low priority because probably it's safe to do so.");
+ return 1;
+ }
+ catch (const std::exception &e)
+ {
+ MERROR(e.what());
+ }
+ }
+ return priority;
+}
+//----------------------------------------------------------------------------------------------------
// separated the call(s) to wallet2::transfer into their own function
//
// this function will make multiple calls to wallet2::transfer if multiple
@@ -5833,7 +5923,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
if (m_multisig)
{
crypto::public_key ignore = m_multisig_threshold == m_multisig_signers.size() ? crypto::null_pkey : multisig_signers.front();
- multisig_sigs.push_back({tx.rct_signatures, ignore, used_L, {}, msout});
+ multisig_sigs.push_back({tx.rct_signatures, ignore, used_L, std::unordered_set<crypto::public_key>(), msout});
if (m_multisig_threshold < m_multisig_signers.size())
{
@@ -5860,7 +5950,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix");
- multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, {}, msout});
+ multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout});
ms_tx.rct_signatures = tx.rct_signatures;
THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_hash(ms_tx) != cryptonote::get_transaction_hash(tx), error::wallet_internal_error, "Multisig txes differ by more than the signatures");
@@ -6159,7 +6249,8 @@ void wallet2::light_wallet_get_unspent_outs()
add_tx_pub_key_to_extra(td.m_tx, tx_pub_key);
td.m_key_image = unspent_key_image;
- td.m_key_image_known = !m_watch_only;
+ td.m_key_image_known = !m_watch_only && !m_multisig;
+ td.m_key_image_partial = m_multisig;
td.m_amount = o.amount;
td.m_pk_index = 0;
td.m_internal_output_index = o.index;
@@ -6305,6 +6396,7 @@ void wallet2::light_wallet_get_address_txs()
address_tx.m_tx_hash = tx_hash;
address_tx.m_incoming = incoming;
address_tx.m_amount = incoming ? total_received - total_sent : total_sent - total_received;
+ address_tx.m_fee = 0; // TODO
address_tx.m_block_height = t.height;
address_tx.m_unlock_time = t.unlock_time;
address_tx.m_timestamp = t.timestamp;
@@ -6318,6 +6410,7 @@ void wallet2::light_wallet_get_address_txs()
payment_details payment;
payment.m_tx_hash = tx_hash;
payment.m_amount = total_received - total_sent;
+ payment.m_fee = 0; // TODO
payment.m_block_height = t.height;
payment.m_unlock_time = t.unlock_time;
payment.m_timestamp = t.timestamp;
@@ -6546,7 +6639,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
const bool use_rct = use_fork_rules(4, 0);
- const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0);
+ const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
const uint64_t fee_per_kb = get_per_kb_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
@@ -6833,6 +6926,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
+ uint64_t inputs = 0, outputs = needed_fee;
+ for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
+ for (const auto &o: tx.dsts) outputs += o.amount;
+
+ if (inputs < outputs)
+ {
+ LOG_PRINT_L2("We don't have enough for the basic fee, switching to adding_fee");
+ adding_fee = true;
+ goto skip_tx;
+ }
+
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
tx.selected_transfers.size() << " inputs");
if (use_rct)
@@ -6908,6 +7012,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
+skip_tx:
// if unused_*_indices is empty while unused_*_indices_per_subaddr has multiple elements, and if we still have something to pay,
// pop front of unused_*_indices_per_subaddr and have unused_*_indices point to the front of unused_*_indices_per_subaddr
if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
@@ -6960,37 +7065,48 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
- std::map<uint32_t, uint64_t> balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
+ std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
- if (subaddr_indices.empty())
- {
- // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
- if (balance_per_subaddr.count(0) == 1 && balance_per_subaddr.size() > 1)
- balance_per_subaddr.erase(0);
- auto i = balance_per_subaddr.begin();
- std::advance(i, crypto::rand<size_t>() % balance_per_subaddr.size());
- subaddr_indices.insert(i->first);
- }
- for (uint32_t i : subaddr_indices)
- LOG_PRINT_L2("Spending from subaddress index " << i);
-
- // gather all dust and non-dust outputs of specified subaddress
+ // gather all dust and non-dust outputs of specified subaddress (if any) and below specified threshold (if any)
+ bool fund_found = false;
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
{
+ fund_found = true;
if (below == 0 || td.amount() < below)
{
if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
- unused_transfers_indices.push_back(i);
+ unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].first.push_back(i);
else
- unused_dust_indices.push_back(i);
+ unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].second.push_back(i);
}
}
}
+ THROW_WALLET_EXCEPTION_IF(!fund_found, error::wallet_internal_error, "No unlocked balance in the specified subaddress(es)");
+ THROW_WALLET_EXCEPTION_IF(unused_transfer_dust_indices_per_subaddr.empty(), error::wallet_internal_error, "The smallest amount found is not below the specified threshold");
- THROW_WALLET_EXCEPTION_IF(unused_transfers_indices.empty() && unused_dust_indices.empty(), error::not_enough_money, 0, 0, 0); // not sure if a new error class (something like 'cant_sweep_empty'?) should be introduced
+ if (subaddr_indices.empty())
+ {
+ // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
+ if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
+ unused_transfer_dust_indices_per_subaddr.erase(0);
+ auto i = unused_transfer_dust_indices_per_subaddr.begin();
+ std::advance(i, crypto::rand<size_t>() % unused_transfer_dust_indices_per_subaddr.size());
+ unused_transfers_indices = i->second.first;
+ unused_dust_indices = i->second.second;
+ LOG_PRINT_L2("Spending from subaddress index " << i->first);
+ }
+ else
+ {
+ for (const auto& p : unused_transfer_dust_indices_per_subaddr)
+ {
+ unused_transfers_indices.insert(unused_transfers_indices.end(), p.second.first.begin(), p.second.first.end());
+ unused_dust_indices.insert(unused_dust_indices.end(), p.second.second.begin(), p.second.second.end());
+ LOG_PRINT_L2("Spending from subaddress index " << p.first);
+ }
+ }
return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
}
@@ -7032,7 +7148,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<std::vector<get_outs_entry>> outs;
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
- const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0);
+ const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
const uint64_t fee_per_kb = get_per_kb_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
@@ -7099,7 +7215,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
- while (needed_fee > test_ptx.fee) {
+ do {
LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
tx.dsts[0].amount = available_for_fee - needed_fee;
if (use_rct)
@@ -7112,7 +7228,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
- }
+ } while (needed_fee > test_ptx.fee);
LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
@@ -7151,13 +7267,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
return ptx_vector;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height)
+void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
{
boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
throw_on_rpc_response_error(result, "get_hard_fork_info");
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks)
+bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
{
// TODO: How to get fork rule info from light wallet node?
if(m_light_wallet)
@@ -7176,7 +7292,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks)
return close_enough;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_upper_transaction_size_limit()
+uint64_t wallet2::get_upper_transaction_size_limit() const
{
if (m_upper_transaction_size_limit > 0)
return m_upper_transaction_size_limit;
@@ -7184,7 +7300,7 @@ uint64_t wallet2::get_upper_transaction_size_limit()
return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
//----------------------------------------------------------------------------------------------------
-std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f)
+std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const
{
std::vector<size_t> outputs;
size_t n = 0;
@@ -7202,7 +7318,7 @@ std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(c
return outputs;
}
//----------------------------------------------------------------------------------------------------
-std::vector<uint64_t> wallet2::get_unspent_amounts_vector()
+std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const
{
std::set<uint64_t> set;
for (const auto &td: m_transfers)
@@ -7917,6 +8033,251 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
return false;
}
+std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, uint64_t>> &account_minreserve, const std::string &message)
+{
+ THROW_WALLET_EXCEPTION_IF(m_watch_only || m_multisig, error::wallet_internal_error, "Reserve proof can only be generated by a full wallet");
+ THROW_WALLET_EXCEPTION_IF(balance_all() == 0, error::wallet_internal_error, "Zero balance");
+ THROW_WALLET_EXCEPTION_IF(account_minreserve && balance(account_minreserve->first) < account_minreserve->second, error::wallet_internal_error,
+ "Not enough balance in this account for the requested minimum reserve amount");
+
+ // determine which outputs to include in the proof
+ std::vector<size_t> selected_transfers;
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details &td = m_transfers[i];
+ if (!td.m_spent && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major))
+ selected_transfers.push_back(i);
+ }
+
+ if (account_minreserve)
+ {
+ // minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
+ std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
+ { return m_transfers[a].amount() > m_transfers[b].amount(); });
+ while (selected_transfers.size() >= 2 && m_transfers[selected_transfers[1]].amount() >= account_minreserve->second)
+ selected_transfers.erase(selected_transfers.begin());
+ size_t sz = 0;
+ uint64_t total = 0;
+ while (total < account_minreserve->second)
+ {
+ total += m_transfers[selected_transfers[sz]].amount();
+ ++sz;
+ }
+ selected_transfers.resize(sz);
+ }
+
+ // compute signature prefix hash
+ std::string prefix_data = message;
+ prefix_data.append((const char*)&m_account.get_keys().m_account_address, sizeof(cryptonote::account_public_address));
+ for (size_t i = 0; i < selected_transfers.size(); ++i)
+ {
+ prefix_data.append((const char*)&m_transfers[selected_transfers[i]].m_key_image, sizeof(crypto::key_image));
+ }
+ crypto::hash prefix_hash;
+ crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
+
+ // generate proof entries
+ std::vector<reserve_proof_entry> proofs(selected_transfers.size());
+ std::unordered_set<cryptonote::subaddress_index> subaddr_indices = { {0,0} };
+ for (size_t i = 0; i < selected_transfers.size(); ++i)
+ {
+ const transfer_details &td = m_transfers[selected_transfers[i]];
+ reserve_proof_entry& proof = proofs[i];
+ proof.txid = td.m_txid;
+ proof.index_in_tx = td.m_internal_output_index;
+ proof.key_image = td.m_key_image;
+ subaddr_indices.insert(td.m_subaddr_index);
+
+ // get tx pub key
+ const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index);
+ THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
+ const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
+
+ // determine which tx pub key was used for deriving the output key
+ const crypto::public_key *tx_pub_key_used = &tx_pub_key;
+ for (int i = 0; i < 2; ++i)
+ {
+ proof.shared_secret = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(*tx_pub_key_used), rct::sk2rct(m_account.get_keys().m_view_secret_key)));
+ crypto::key_derivation derivation;
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation),
+ error::wallet_internal_error, "Failed to generate key derivation");
+ crypto::public_key subaddress_spendkey;
+ THROW_WALLET_EXCEPTION_IF(!derive_subaddress_public_key(td.get_public_key(), derivation, proof.index_in_tx, subaddress_spendkey),
+ error::wallet_internal_error, "Failed to derive subaddress public key");
+ if (m_subaddresses.count(subaddress_spendkey) == 1)
+ break;
+ THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.empty(), error::wallet_internal_error,
+ "Normal tx pub key doesn't derive the expected output, while the additional tx pub keys are empty");
+ THROW_WALLET_EXCEPTION_IF(i == 1, error::wallet_internal_error,
+ "Neither normal tx pub key nor additional tx pub key derive the expected output key");
+ tx_pub_key_used = &additional_tx_pub_keys[proof.index_in_tx];
+ }
+
+ // generate signature for shared secret
+ crypto::generate_tx_proof(prefix_hash, m_account.get_keys().m_account_address.m_view_public_key, *tx_pub_key_used, boost::none, proof.shared_secret, m_account.get_keys().m_view_secret_key, proof.shared_secret_sig);
+
+ // derive ephemeral secret key
+ crypto::key_image ki;
+ cryptonote::keypair ephemeral;
+ const bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, td.get_public_key(), tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, ephemeral, ki);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
+ THROW_WALLET_EXCEPTION_IF(ephemeral.pub != td.get_public_key(), error::wallet_internal_error, "Derived public key doesn't agree with the stored one");
+
+ // generate signature for key image
+ const std::vector<const crypto::public_key*> pubs = { &ephemeral.pub };
+ crypto::generate_ring_signature(prefix_hash, td.m_key_image, &pubs[0], 1, ephemeral.sec, 0, &proof.key_image_sig);
+ }
+
+ // collect all subaddress spend keys that received those outputs and generate their signatures
+ std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
+ for (const cryptonote::subaddress_index &index : subaddr_indices)
+ {
+ crypto::secret_key subaddr_spend_skey = m_account.get_keys().m_spend_secret_key;
+ if (!index.is_zero())
+ {
+ crypto::secret_key m = cryptonote::get_subaddress_secret_key(m_account.get_keys().m_view_secret_key, index);
+ crypto::secret_key tmp = subaddr_spend_skey;
+ sc_add((unsigned char*)&subaddr_spend_skey, (unsigned char*)&m, (unsigned char*)&tmp);
+ }
+ crypto::public_key subaddr_spend_pkey;
+ secret_key_to_public_key(subaddr_spend_skey, subaddr_spend_pkey);
+ crypto::generate_signature(prefix_hash, subaddr_spend_pkey, subaddr_spend_skey, subaddr_spendkeys[subaddr_spend_pkey]);
+ }
+
+ // serialize & encode
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ ar << proofs << subaddr_spendkeys;
+ return "ReserveProofV1" + tools::base58::encode(oss.str());
+}
+
+bool wallet2::check_reserve_proof(const cryptonote::account_public_address &address, const std::string &message, const std::string &sig_str, uint64_t &total, uint64_t &spent)
+{
+ uint32_t rpc_version;
+ THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version), error::wallet_internal_error, "Failed to connect to daemon: " + get_daemon_address());
+ THROW_WALLET_EXCEPTION_IF(rpc_version < MAKE_CORE_RPC_VERSION(1, 0), error::wallet_internal_error, "Daemon RPC version is too old");
+
+ static constexpr char header[] = "ReserveProofV1";
+ THROW_WALLET_EXCEPTION_IF(!boost::string_ref{sig_str}.starts_with(header), error::wallet_internal_error,
+ "Signature header check error");
+
+ std::string sig_decoded;
+ THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(std::strlen(header)), sig_decoded), error::wallet_internal_error,
+ "Signature decoding error");
+
+ std::istringstream iss(sig_decoded);
+ boost::archive::portable_binary_iarchive ar(iss);
+ std::vector<reserve_proof_entry> proofs;
+ std::unordered_map<crypto::public_key, crypto::signature> subaddr_spendkeys;
+ ar >> proofs >> subaddr_spendkeys;
+
+ THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(address.m_spend_public_key) == 0, error::wallet_internal_error,
+ "The given address isn't found in the proof");
+
+ // compute signature prefix hash
+ std::string prefix_data = message;
+ prefix_data.append((const char*)&address, sizeof(cryptonote::account_public_address));
+ for (size_t i = 0; i < proofs.size(); ++i)
+ {
+ prefix_data.append((const char*)&proofs[i].key_image, sizeof(crypto::key_image));
+ }
+ crypto::hash prefix_hash;
+ crypto::cn_fast_hash(prefix_data.data(), prefix_data.size(), prefix_hash);
+
+ // fetch txes from daemon
+ COMMAND_RPC_GET_TRANSACTIONS::request gettx_req;
+ COMMAND_RPC_GET_TRANSACTIONS::response gettx_res;
+ for (size_t i = 0; i < proofs.size(); ++i)
+ gettx_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(proofs[i].txid));
+ m_daemon_rpc_mutex.lock();
+ bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ // check spent status
+ COMMAND_RPC_IS_KEY_IMAGE_SPENT::request kispent_req;
+ COMMAND_RPC_IS_KEY_IMAGE_SPENT::response kispent_res;
+ for (size_t i = 0; i < proofs.size(); ++i)
+ kispent_req.key_images.push_back(epee::string_tools::pod_to_hex(proofs[i].key_image));
+ m_daemon_rpc_mutex.lock();
+ ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(),
+ error::wallet_internal_error, "Failed to get key image spent status from daemon");
+
+ total = spent = 0;
+ for (size_t i = 0; i < proofs.size(); ++i)
+ {
+ const reserve_proof_entry& proof = proofs[i];
+ THROW_WALLET_EXCEPTION_IF(gettx_res.txs[i].in_pool, error::wallet_internal_error, "Tx is unconfirmed");
+
+ cryptonote::blobdata tx_data;
+ ok = string_tools::parse_hexstr_to_binbuff(gettx_res.txs[i].as_hex, tx_data);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
+
+ crypto::hash tx_hash, tx_prefix_hash;
+ cryptonote::transaction tx;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error,
+ "Failed to validate transaction from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != proof.txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+
+ THROW_WALLET_EXCEPTION_IF(proof.index_in_tx >= tx.vout.size(), error::wallet_internal_error, "index_in_tx is out of bound");
+
+ const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[proof.index_in_tx].target));
+ THROW_WALLET_EXCEPTION_IF(!out_key, error::wallet_internal_error, "Output key wasn't found")
+
+ // get tx pub key
+ const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
+ THROW_WALLET_EXCEPTION_IF(tx_pub_key == crypto::null_pkey, error::wallet_internal_error, "The tx public key isn't found");
+ const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
+
+ // check singature for shared secret
+ ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, proof.shared_secret, proof.shared_secret_sig);
+ if (!ok && additional_tx_pub_keys.size() == tx.vout.size())
+ ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[proof.index_in_tx], boost::none, proof.shared_secret, proof.shared_secret_sig);
+ if (!ok)
+ return false;
+
+ // check signature for key image
+ const std::vector<const crypto::public_key*> pubs = { &out_key->key };
+ ok = crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, &proof.key_image_sig);
+ if (!ok)
+ return false;
+
+ // check if the address really received the fund
+ crypto::key_derivation derivation;
+ THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
+ crypto::public_key subaddr_spendkey;
+ crypto::derive_subaddress_public_key(out_key->key, derivation, proof.index_in_tx, subaddr_spendkey);
+ THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(subaddr_spendkey) == 0, error::wallet_internal_error,
+ "The address doesn't seem to have received the fund");
+
+ // check amount
+ uint64_t amount = tx.vout[proof.index_in_tx].amount;
+ if (amount == 0)
+ {
+ // decode rct
+ crypto::secret_key shared_secret;
+ crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
+ rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret));
+ amount = rct::h2d(ecdh_info.amount);
+ }
+ total += amount;
+ if (kispent_res.spent_status[i])
+ spent += amount;
+ }
+
+ // check signatures for all subaddress spend keys
+ for (const auto &i : subaddr_spendkeys)
+ {
+ if (!crypto::check_signature(prefix_hash, i.first, i.second))
+ return false;
+ }
+ return true;
+}
+
std::string wallet2::get_wallet_file() const
{
return m_wallet_file;
@@ -7932,7 +8293,7 @@ std::string wallet2::get_daemon_address() const
return m_daemon_address;
}
-uint64_t wallet2::get_daemon_blockchain_height(string &err)
+uint64_t wallet2::get_daemon_blockchain_height(string &err) const
{
uint64_t height;
@@ -7990,8 +8351,9 @@ uint64_t wallet2::get_approximate_blockchain_height() const
// Calculated blockchain height
uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
// testnet got some huge rollbacks, so the estimation is way off
- if (m_testnet && approx_blockchain_height > 105000)
- approx_blockchain_height -= 105000;
+ static const uint64_t approximate_testnet_rolled_back_blocks = 148540;
+ if (m_testnet && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
+ approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
return approx_blockchain_height;
}
@@ -8162,7 +8524,7 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
return crypto::null_pkey;
}
-bool wallet2::export_key_images(const std::string &filename)
+bool wallet2::export_key_images(const std::string &filename) const
{
std::vector<std::pair<crypto::key_image, crypto::signature>> ski = export_key_images();
std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC));
@@ -8567,7 +8929,7 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
m_blockchain.clear();
if (std::get<0>(bc))
{
- for (size_t n = std::get<0>(bc); n > 0; ++n)
+ for (size_t n = std::get<0>(bc); n > 0; --n)
m_blockchain.push_back(std::get<1>(bc));
m_blockchain.trim(std::get<0>(bc));
}
@@ -8607,11 +8969,8 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
// the hot wallet wouldn't have known about key images (except if we already exported them)
cryptonote::keypair in_ephemeral;
- std::vector<tx_extra_field> tx_extra_fields;
THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast<std::string>(i));
- THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(td.m_tx.extra, tx_extra_fields), error::wallet_internal_error,
- "Transaction extra has unsupported format at index " + boost::lexical_cast<std::string>(i));
crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
@@ -8969,7 +9328,7 @@ std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext,
return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated);
}
//----------------------------------------------------------------------------------------------------
-std::string wallet2::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error)
+std::string wallet2::make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const
{
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, testnet(), address))
@@ -9203,13 +9562,12 @@ bool wallet2::is_synced() const
return get_blockchain_current_height() >= height;
}
//----------------------------------------------------------------------------------------------------
-std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees)
+std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels)
{
- THROW_WALLET_EXCEPTION_IF(min_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
- THROW_WALLET_EXCEPTION_IF(max_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
- for (uint64_t fee: fees)
+ for (const auto &fee_level: fee_levels)
{
- THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(fee_level.first == 0.0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(fee_level.second == 0.0, error::wallet_internal_error, "Invalid 0 fee");
}
// get txpool backlog
@@ -9239,9 +9597,10 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t mi
uint64_t full_reward_zone = resp_t.result.block_size_limit / 2;
std::vector<std::pair<uint64_t, uint64_t>> blocks;
- for (uint64_t fee: fees)
+ for (const auto &fee_level: fee_levels)
{
- double our_fee_byte_min = fee / (double)min_blob_size, our_fee_byte_max = fee / (double)max_blob_size;
+ const double our_fee_byte_min = fee_level.first;
+ const double our_fee_byte_max = fee_level.second;
uint64_t priority_size_min = 0, priority_size_max = 0;
for (const auto &i: res.result.backlog)
{
@@ -9259,15 +9618,32 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t mi
uint64_t nblocks_min = priority_size_min / full_reward_zone;
uint64_t nblocks_max = priority_size_max / full_reward_zone;
- MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for " << fee
- << " (" << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee), "
+ MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for "
+ << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, "
<< nblocks_min << " - " << nblocks_max << " blocks at block size " << full_reward_zone);
blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
}
return blocks;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::generate_genesis(cryptonote::block& b) {
+std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees)
+{
+ THROW_WALLET_EXCEPTION_IF(min_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(max_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
+ for (uint64_t fee: fees)
+ {
+ THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
+ }
+ std::vector<std::pair<double, double>> fee_levels;
+ for (uint64_t fee: fees)
+ {
+ double our_fee_byte_min = fee / (double)min_blob_size, our_fee_byte_max = fee / (double)max_blob_size;
+ fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
+ }
+ return estimate_backlog(fee_levels);
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::generate_genesis(cryptonote::block& b) const {
if (m_testnet)
{
cryptonote::generate_genesis_block(b, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 2fbe05f89..f768581b2 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -252,6 +252,7 @@ namespace tools
{
crypto::hash m_tx_hash;
uint64_t m_amount;
+ uint64_t m_fee;
uint64_t m_block_height;
uint64_t m_unlock_time;
uint64_t m_timestamp;
@@ -433,6 +434,16 @@ namespace tools
bool m_is_subaddress;
};
+ struct reserve_proof_entry
+ {
+ crypto::hash txid;
+ uint64_t index_in_tx;
+ crypto::public_key shared_secret;
+ crypto::key_image key_image;
+ crypto::signature shared_secret_sig;
+ crypto::signature key_image_sig;
+ };
+
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
/*!
@@ -646,7 +657,7 @@ namespace tools
void commit_tx(pending_tx& ptx_vector);
void commit_tx(std::vector<pending_tx>& ptx_vector);
- bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename);
+ bool save_tx(const std::vector<pending_tx>& ptx_vector, const std::string &filename) const;
std::string save_multisig_tx(multisig_tx_set txs);
bool save_multisig_tx(const multisig_tx_set &txs, const std::string &filename);
std::string save_multisig_tx(const std::vector<pending_tx>& ptx_vector);
@@ -656,7 +667,7 @@ namespace tools
// sign unsigned tx. Takes unsigned_tx_set as argument. Used by GUI
bool sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector<wallet2::pending_tx> &ptx, bool export_raw = false);
// load unsigned_tx_set from file.
- bool load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs);
+ bool load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const;
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose
@@ -815,8 +826,6 @@ namespace tools
void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; }
bool ask_password() const { return m_ask_password; }
void ask_password(bool always) { m_ask_password = always; }
- void set_default_decimal_point(unsigned int decimal_point);
- unsigned int get_default_decimal_point() const;
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
uint32_t get_min_output_count() const { return m_min_output_count; }
void set_min_output_value(uint64_t value) { m_min_output_value = value; }
@@ -827,6 +836,10 @@ namespace tools
void confirm_backlog(bool always) { m_confirm_backlog = always; }
void set_confirm_backlog_threshold(uint32_t threshold) { m_confirm_backlog_threshold = threshold; };
uint32_t get_confirm_backlog_threshold() const { return m_confirm_backlog_threshold; };
+ bool confirm_export_overwrite() const { return m_confirm_export_overwrite; }
+ void confirm_export_overwrite(bool always) { m_confirm_export_overwrite = always; }
+ bool auto_low_priority() const { return m_auto_low_priority; }
+ void auto_low_priority(bool value) { m_auto_low_priority = value; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
@@ -836,6 +849,26 @@ namespace tools
std::string get_spend_proof(const crypto::hash &txid, const std::string &message);
bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str);
+
+ /*!
+ * \brief Generates a proof that proves the reserve of unspent funds
+ * \param account_minreserve When specified, collect outputs only belonging to the given account and prove the smallest reserve above the given amount
+ * When unspecified, proves for all unspent outputs across all accounts
+ * \param message Arbitrary challenge message to be signed together
+ * \return Signature string
+ */
+ std::string get_reserve_proof(const boost::optional<std::pair<uint32_t, uint64_t>> &account_minreserve, const std::string &message);
+ /*!
+ * \brief Verifies a proof of reserve
+ * \param address The signer's address
+ * \param message Challenge message used for signing
+ * \param sig_str Signature string
+ * \param total [OUT] the sum of funds included in the signature
+ * \param spent [OUT] the sum of spent funds included in the signature
+ * \return true if the signature verifies correctly
+ */
+ bool check_reserve_proof(const cryptonote::account_public_address &address, const std::string &message, const std::string &sig_str, uint64_t &total, uint64_t &spent);
+
/*!
* \brief GUI Address book get/store
*/
@@ -847,15 +880,15 @@ namespace tools
size_t get_num_transfer_details() const { return m_transfers.size(); }
const transfer_details &get_transfer_details(size_t idx) const;
- void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
- bool use_fork_rules(uint8_t version, int64_t early_blocks = 0);
- int get_fee_algorithm();
+ void get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const;
+ bool use_fork_rules(uint8_t version, int64_t early_blocks = 0) const;
+ int get_fee_algorithm() const;
std::string get_wallet_file() const;
std::string get_keys_file() const;
std::string get_daemon_address() const;
const boost::optional<epee::net_utils::http::login>& get_daemon_login() const { return m_daemon_login; }
- uint64_t get_daemon_blockchain_height(std::string& err);
+ uint64_t get_daemon_blockchain_height(std::string& err) const;
uint64_t get_daemon_blockchain_target_height(std::string& err);
/*!
* \brief Calculates the approximate blockchain height from current date/time.
@@ -863,7 +896,7 @@ namespace tools
uint64_t get_approximate_blockchain_height() const;
uint64_t estimate_blockchain_height();
std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon);
- std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
+ std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const;
std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon);
std::vector<size_t> select_available_mixable_outputs(bool trusted_daemon);
@@ -905,7 +938,7 @@ namespace tools
void import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments);
std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const;
void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc);
- bool export_key_images(const std::string &filename);
+ bool export_key_images(const std::string &filename) const;
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const;
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, 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);
@@ -918,18 +951,20 @@ namespace tools
std::string decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated = true) const;
std::string decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated = true) const;
- std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error);
+ std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const;
bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error);
uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31
bool is_synced() const;
+ std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees);
- uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
- uint64_t get_per_kb_fee();
- uint64_t adjust_mixin(uint64_t mixin);
+ uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const;
+ uint64_t get_per_kb_fee() const;
+ uint64_t adjust_mixin(uint64_t mixin) const;
+ uint32_t adjust_priority(uint32_t priority);
// Light wallet specific functions
// fetch unspent outs from lw node and store in m_transfers
@@ -999,20 +1034,20 @@ namespace tools
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history);
void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, bool &error);
void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added);
- uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon);
+ uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon) const;
bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
- void generate_genesis(cryptonote::block& b);
+ void generate_genesis(cryptonote::block& b) const;
void check_genesis(const crypto::hash& genesis_hash) const; //throws
bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
- uint64_t get_upper_transaction_size_limit();
- std::vector<uint64_t> get_unspent_amounts_vector();
- uint64_t get_dynamic_per_kb_fee_estimate();
+ uint64_t get_upper_transaction_size_limit() const;
+ std::vector<uint64_t> get_unspent_amounts_vector() const;
+ uint64_t get_dynamic_per_kb_fee_estimate() const;
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
void set_spent(size_t idx, uint64_t height);
@@ -1022,7 +1057,7 @@ namespace tools
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
- void scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs);
+ void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs) const;
void trim_hashchain();
crypto::key_image get_multisig_composite_key_image(size_t n) const;
rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const;
@@ -1089,6 +1124,8 @@ namespace tools
bool m_merge_destinations;
bool m_confirm_backlog;
uint32_t m_confirm_backlog_threshold;
+ bool m_confirm_export_overwrite;
+ bool m_auto_low_priority;
bool m_is_initialized;
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
@@ -1114,11 +1151,12 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
-BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2)
+BOOST_CLASS_VERSION(tools::wallet2::payment_details, 3)
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 7)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 5)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
+BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 2)
@@ -1378,6 +1416,12 @@ namespace boost
return;
}
a & x.m_subaddr_index;
+ if (ver < 3)
+ {
+ x.m_fee = 0;
+ return;
+ }
+ a & x.m_fee;
}
template <class Archive>
@@ -1402,6 +1446,17 @@ namespace boost
}
template <class Archive>
+ inline void serialize(Archive& a, tools::wallet2::reserve_proof_entry& x, const boost::serialization::version_type ver)
+ {
+ a & x.txid;
+ a & x.index_in_tx;
+ a & x.shared_secret;
+ a & x.key_image;
+ a & x.shared_secret_sig;
+ a & x.key_image_sig;
+ }
+
+ template <class Archive>
inline void serialize(Archive &a, tools::wallet2::unsigned_tx_set &x, const boost::serialization::version_type ver)
{
a & x.txes;
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 2273f14ad..a6ff63dd3 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index 212958988..af6685845 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 023b53f28..5c1c49d5d 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index e7435daf6..7d17591db 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -89,6 +89,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::~wallet_rpc_server()
{
+ if (m_wallet)
+ delete m_wallet;
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::set_wallet(wallet2 *cr)
@@ -229,8 +231,9 @@ namespace tools
m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
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(
- std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -251,10 +254,11 @@ namespace tools
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.unlock_time = pd.m_unlock_time;
- entry.fee = 0; // TODO
+ entry.fee = pd.m_fee;
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "in";
entry.subaddr_index = pd.m_subaddr_index;
+ entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd)
@@ -280,6 +284,7 @@ namespace tools
entry.type = "out";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
+ entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd)
@@ -298,6 +303,7 @@ namespace tools
entry.note = m_wallet->get_tx_note(txid);
entry.type = is_failed ? "failed" : "pending";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
+ entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd)
@@ -311,11 +317,12 @@ namespace tools
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.unlock_time = pd.m_unlock_time;
- entry.fee = 0; // TODO
+ entry.fee = pd.m_fee;
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.double_spend_seen = ppd.m_double_spend_seen;
entry.type = "pool";
entry.subaddr_index = pd.m_subaddr_index;
+ entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er)
@@ -779,7 +786,8 @@ namespace tools
try
{
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ uint32_t priority = m_wallet->adjust_priority(req.priority);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
if (ptx_vector.empty())
{
@@ -830,8 +838,9 @@ namespace tools
try
{
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
+ uint32_t priority = m_wallet->adjust_priority(req.priority);
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
@@ -896,7 +905,8 @@ namespace tools
try
{
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ uint32_t priority = m_wallet->adjust_priority(req.priority);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
@@ -943,7 +953,8 @@ namespace tools
try
{
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
+ uint32_t priority = m_wallet->adjust_priority(req.priority);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, m_trusted_daemon);
if (ptx_vector.empty())
{
@@ -1312,6 +1323,10 @@ namespace tools
{
res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key);
}
+ else if(req.key_type.compare("spend_key") == 0)
+ {
+ res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key);
+ }
else
{
er.message = "key_type " + req.key_type + " not found";
@@ -1718,6 +1733,66 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
+ if (!req.all)
+ {
+ if (req.account_index >= m_wallet->get_num_subaddress_accounts())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Account index is out of bound";
+ return false;
+ }
+ account_minreserve = std::make_pair(req.account_index, req.amount);
+ }
+
+ try
+ {
+ res.signature = m_wallet->get_reserve_proof(account_minreserve, req.message);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_check_reserve_proof(const wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+
+ cryptonote::address_parse_info info;
+ if (!get_account_address_from_str(info, m_wallet->testnet(), req.address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+ if (info.is_subaddress)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Address must not be a subaddress";
+ return false;
+ }
+
+ try
+ {
+ res.good = m_wallet->check_reserve_proof(info.address, req.message, req.signature, res.total, res.spent);
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 88f2a85a4..bb458aa99 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -67,6 +67,8 @@ namespace tools
BEGIN_URI_MAP2()
BEGIN_JSON_RPC_MAP("/json_rpc")
+ MAP_JON_RPC_WE("get_balance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
+ MAP_JON_RPC_WE("get_address", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("create_address", on_create_address, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS)
@@ -78,6 +80,7 @@ namespace tools
MAP_JON_RPC_WE("tag_accounts", on_tag_accounts, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS)
MAP_JON_RPC_WE("untag_accounts", on_untag_accounts, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS)
MAP_JON_RPC_WE("set_account_tag_description", on_set_account_tag_description, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION)
+ MAP_JON_RPC_WE("get_height", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
MAP_JON_RPC_WE("getheight", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
@@ -104,6 +107,8 @@ namespace tools
MAP_JON_RPC_WE("check_tx_proof", on_check_tx_proof, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF)
MAP_JON_RPC_WE("get_spend_proof", on_get_spend_proof, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF)
MAP_JON_RPC_WE("check_spend_proof", on_check_spend_proof, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF)
+ MAP_JON_RPC_WE("get_reserve_proof", on_get_reserve_proof, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF)
+ MAP_JON_RPC_WE("check_reserve_proof", on_check_reserve_proof, wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF)
MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS)
MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID)
MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN)
@@ -170,6 +175,8 @@ namespace tools
bool on_check_tx_proof(const wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::response& res, epee::json_rpc::error& er);
bool on_get_spend_proof(const wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::response& res, epee::json_rpc::error& er);
bool on_check_spend_proof(const wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::response& res, epee::json_rpc::error& er);
+ bool on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er);
+ bool on_check_reserve_proof(const wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::response& res, epee::json_rpc::error& er);
bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er);
bool on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er);
bool on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index d797af5c1..e9f112b63 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-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
@@ -1114,6 +1114,7 @@ namespace wallet_rpc
std::string type;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
+ std::string address;
bool double_spend_seen;
BEGIN_KV_SERIALIZE_MAP()
@@ -1128,6 +1129,7 @@ namespace wallet_rpc
KV_SERIALIZE(type);
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index);
+ KV_SERIALIZE(address);
KV_SERIALIZE(double_spend_seen)
END_KV_SERIALIZE_MAP()
};
@@ -1180,6 +1182,62 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_GET_RESERVE_PROOF
+ {
+ struct request
+ {
+ bool all;
+ uint32_t account_index; // ignored when `all` is true
+ uint64_t amount; // ignored when `all` is true
+ std::string message;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(all)
+ KV_SERIALIZE(account_index)
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(message)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_CHECK_RESERVE_PROOF
+ {
+ struct request
+ {
+ std::string address;
+ std::string message;
+ std::string signature;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(message)
+ KV_SERIALIZE(signature)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ bool good;
+ uint64_t total;
+ uint64_t spent;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(good)
+ KV_SERIALIZE(total)
+ KV_SERIALIZE(spent)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_GET_TRANSFERS
{
struct request
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index 578413e38..311556657 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-2017, The Monero Project
+// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//