diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/api/address_book.cpp | 6 | ||||
-rw-r--r-- | src/wallet/api/transaction_history.cpp | 2 | ||||
-rw-r--r-- | src/wallet/api/unsigned_transaction.cpp | 8 | ||||
-rw-r--r-- | src/wallet/api/wallet.cpp | 34 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 4 | ||||
-rw-r--r-- | src/wallet/api/wallet2_api.h | 93 | ||||
-rw-r--r-- | src/wallet/api/wallet_manager.cpp | 28 | ||||
-rw-r--r-- | src/wallet/api/wallet_manager.h | 12 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 463 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 28 | ||||
-rw-r--r-- | src/wallet/wallet_errors.h | 16 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 43 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_commands_defs.h | 3 |
13 files changed, 463 insertions, 277 deletions
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index 38c34a912..7ef011e06 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -49,7 +49,7 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa clearStatus(); cryptonote::address_parse_info info; - if(!cryptonote::get_account_address_from_str(info, m_wallet->m_wallet->testnet(), dst_addr)) { + if(!cryptonote::get_account_address_from_str(info, m_wallet->m_wallet->nettype(), dst_addr)) { m_errorString = tr("Invalid destination address"); m_errorCode = Invalid_Address; return false; @@ -105,13 +105,13 @@ void AddressBookImpl::refresh() tools::wallet2::address_book_row * row = &rows.at(i); std::string payment_id = (row->m_payment_id == crypto::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id); - std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->testnet(), row->m_is_subaddress, row->m_address); + std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->nettype(), row->m_is_subaddress, row->m_address); // convert the zero padded short payment id to integrated address if (!row->m_is_subaddress && payment_id.length() > 16 && payment_id.substr(16).find_first_not_of('0') == std::string::npos) { payment_id = payment_id.substr(0,16); crypto::hash8 payment_id_short; if(tools::wallet2::parse_short_payment_id(payment_id, payment_id_short)) { - address = cryptonote::get_account_integrated_address_as_str(m_wallet->m_wallet->testnet(), row->m_address, payment_id_short); + address = cryptonote::get_account_integrated_address_as_str(m_wallet->m_wallet->nettype(), row->m_address, payment_id_short); // Don't show payment id when integrated address is used payment_id = ""; } diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index 95a055f8f..ba46a6904 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -181,7 +181,7 @@ void TransactionHistoryImpl::refresh() // single output transaction might contain multiple transfers for (const auto &d: pd.m_dests) { - ti->m_transfers.push_back({d.amount, get_account_address_as_str(m_wallet->m_wallet->testnet(), d.is_subaddress, d.addr)}); + ti->m_transfers.push_back({d.amount, get_account_address_as_str(m_wallet->m_wallet->nettype(), d.is_subaddress, d.addr)}); } m_history.push_back(ti); } diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index c6ebcb009..29910a3b6 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -144,10 +144,10 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu for (size_t d = 0; d < cd.splitted_dsts.size(); ++d) { const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d]; - std::string address, standard_address = get_account_address_as_str(m_wallet.testnet(), entry.is_subaddress, entry.addr); + std::string address, standard_address = get_account_address_as_str(m_wallet.m_wallet->nettype(), entry.is_subaddress, entry.addr); if (has_encrypted_payment_id && !entry.is_subaddress) { - address = get_account_integrated_address_as_str(m_wallet.testnet(), entry.addr, payment_id8); + address = get_account_integrated_address_as_str(m_wallet.m_wallet->nettype(), entry.addr, payment_id8); address += std::string(" (" + standard_address + " with encrypted payment id " + epee::string_tools::pod_to_hex(payment_id8) + ")"); } else @@ -205,7 +205,7 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu std::string change_string; if (change > 0) { - std::string address = get_account_address_as_str(m_wallet.m_wallet->testnet(), get_tx(0).subaddr_account > 0, get_tx(0).change_dts.addr); + std::string address = get_account_address_as_str(m_wallet.m_wallet->nettype(), get_tx(0).subaddr_account > 0, get_tx(0).change_dts.addr); change_string += (boost::format(tr("%s change to %s")) % cryptonote::print_money(change) % address).str(); } else @@ -297,7 +297,7 @@ std::vector<std::string> UnsignedTransactionImpl::recipientAddress() const MERROR("empty destinations, skipped"); continue; } - result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->testnet(), utx.dests[0].is_subaddress, utx.dests[0].addr)); + result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->nettype(), utx.dests[0].is_subaddress, utx.dests[0].addr)); } return result; } diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 5ce8ede8d..0ed6601af 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -233,16 +233,16 @@ bool Wallet::paymentIdValid(const string &paiment_id) return false; } -bool Wallet::addressValid(const std::string &str, bool testnet) +bool Wallet::addressValid(const std::string &str, NetworkType nettype) { cryptonote::address_parse_info info; - return get_account_address_from_str(info, testnet, str); + return get_account_address_from_str(info, static_cast<cryptonote::network_type>(nettype), str); } -bool Wallet::keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) +bool Wallet::keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, NetworkType nettype, std::string &error) { cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, testnet, address_string)) { + if(!get_account_address_from_str(info, static_cast<cryptonote::network_type>(nettype), address_string)) { error = tr("Failed to parse address"); return false; } @@ -275,10 +275,10 @@ bool Wallet::keyValid(const std::string &secret_key_string, const std::string &a return true; } -std::string Wallet::paymentIdFromAddress(const std::string &str, bool testnet) +std::string Wallet::paymentIdFromAddress(const std::string &str, NetworkType nettype) { cryptonote::address_parse_info info; - if (!get_account_address_from_str(info, testnet, str)) + if (!get_account_address_from_str(info, static_cast<cryptonote::network_type>(nettype), str)) return ""; if (!info.has_payment_id) return ""; @@ -300,7 +300,7 @@ void Wallet::debug(const std::string &str) { } ///////////////////////// WalletImpl implementation //////////////////////// -WalletImpl::WalletImpl(bool testnet) +WalletImpl::WalletImpl(NetworkType nettype) :m_wallet(nullptr) , m_status(Wallet::Status_Ok) , m_trustedDaemon(false) @@ -310,7 +310,7 @@ WalletImpl::WalletImpl(bool testnet) , m_rebuildWalletCache(false) , m_is_connected(false) { - m_wallet = new tools::wallet2(testnet); + m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype)); m_history = new TransactionHistoryImpl(this); m_wallet2Callback = new Wallet2CallbackImpl(this); m_wallet->callback(m_wallet2Callback); @@ -388,7 +388,7 @@ bool WalletImpl::create(const std::string &path, const std::string &password, co bool WalletImpl::createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const { clearStatus(); - std::unique_ptr<tools::wallet2> view_wallet(new tools::wallet2(m_wallet->testnet())); + std::unique_ptr<tools::wallet2> view_wallet(new tools::wallet2(m_wallet->nettype())); // Store same refresh height as original wallet view_wallet->set_refresh_from_block_height(m_wallet->get_refresh_from_block_height()); @@ -469,7 +469,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path, const std::string &spendkey_string) { cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, m_wallet->testnet(), address_string)) + if(!get_account_address_from_str(info, m_wallet->nettype(), address_string)) { m_errorString = tr("failed to parse address"); m_status = Status_Error; @@ -1079,7 +1079,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); do { - if(!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), dst_addr)) { + if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr)) { // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 m_status = Status_Error; m_errorString = "Invalid destination address"; @@ -1464,7 +1464,7 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, } cryptonote::address_parse_info info; - if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str)) + if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address_str)) { m_status = Status_Error; m_errorString = tr("Failed to parse address"); @@ -1496,7 +1496,7 @@ std::string WalletImpl::getTxProof(const std::string &txid_str, const std::strin } cryptonote::address_parse_info info; - if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str)) + if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address_str)) { m_status = Status_Error; m_errorString = tr("Failed to parse address"); @@ -1527,7 +1527,7 @@ bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &ad } cryptonote::address_parse_info info; - if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address_str)) + if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address_str)) { m_status = Status_Error; m_errorString = tr("Failed to parse address"); @@ -1615,7 +1615,7 @@ std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64 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)) + if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address)) { m_status = Status_Error; m_errorString = tr("Failed to parse address"); @@ -1652,7 +1652,7 @@ bool WalletImpl::verifySignedMessage(const std::string &message, const std::stri { cryptonote::address_parse_info info; - if (!cryptonote::get_account_address_from_str(info, m_wallet->testnet(), address)) + if (!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), address)) return false; return m_wallet->verify(message, info.address, signature); @@ -1746,7 +1746,7 @@ void WalletImpl::doRefresh() m_synchronized = true; } // assuming if we have empty history, it wasn't initialized yet - // for futher history changes client need to update history in + // for further history changes client need to update history in // "on_money_received" and "on_money_sent" callbacks if (m_history->count() == 0) { m_history->refresh(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index fcd53c3f8..9b4a0cc12 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -52,7 +52,7 @@ struct Wallet2CallbackImpl; class WalletImpl : public Wallet { public: - WalletImpl(bool testnet = false); + WalletImpl(NetworkType nettype = MAINNET); ~WalletImpl(); bool create(const std::string &path, const std::string &password, const std::string &language); @@ -115,7 +115,7 @@ public: void setRecoveringFromSeed(bool recoveringFromSeed); bool watchOnly() const; bool rescanSpent(); - bool testnet() const {return m_wallet->testnet();} + NetworkType nettype() const {return static_cast<NetworkType>(m_wallet->nettype());} void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const; bool useForkRules(uint8_t version, int64_t early_blocks) const; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index a22788399..a6320545e 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -40,6 +40,12 @@ // Public interface for libwallet library namespace Monero { +enum NetworkType : uint8_t { + MAINNET = 0, + TESTNET, + STAGENET +}; + namespace Utils { bool isAddressLocal(const std::string &hostaddr); void onStartup(); @@ -358,7 +364,10 @@ struct Wallet virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0; std::string mainAddress() const { return address(0, 0); } virtual std::string path() const = 0; - virtual bool testnet() const = 0; + virtual NetworkType nettype() const = 0; + bool mainnet() const { return nettype() == MAINNET; } + bool testnet() const { return nettype() == TESTNET; } + bool stagenet() const { return nettype() == STAGENET; } //! returns current hard fork info virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; //! check if hard fork rules should be used @@ -529,9 +538,21 @@ struct Wallet static uint64_t amountFromDouble(double amount); static std::string genPaymentId(); static bool paymentIdValid(const std::string &paiment_id); - static bool addressValid(const std::string &str, bool testnet); - static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); - static std::string paymentIdFromAddress(const std::string &str, bool testnet); + static bool addressValid(const std::string &str, NetworkType nettype); + static bool addressValid(const std::string &str, bool testnet) // deprecated + { + return addressValid(str, testnet ? MAINNET : TESTNET); + } + static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, NetworkType nettype, std::string &error); + static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) // deprecated + { + return keyValid(secret_key_string, address_string, isViewKey, testnet ? TESTNET : MAINNET, error); + } + static std::string paymentIdFromAddress(const std::string &str, NetworkType nettype); + static std::string paymentIdFromAddress(const std::string &str, bool testnet) // deprecated + { + return paymentIdFromAddress(str, testnet ? TESTNET : MAINNET); + } static uint64_t maximumAllowedAmount(); // Easylogger wrapper static void init(const char *argv0, const char *default_log_base_name); @@ -685,7 +706,7 @@ struct Wallet * \brief setUserNote - attach an arbitrary string note to a txid * \param txid - the transaction id to attach the note to * \param note - the note - * \return true if succesful, false otherwise + * \return true if successful, false otherwise */ virtual bool setUserNote(const std::string &txid, const std::string ¬e) = 0; /*! @@ -750,47 +771,66 @@ struct WalletManager * \param path Name of wallet file * \param password Password of wallet file * \param language Language to be used to generate electrum seed mnemonic + * \param nettype Network type * \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; + virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype = MAINNET) = 0; + Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, bool testnet = false) // deprecated + { + return createWallet(path, password, language, testnet ? TESTNET : MAINNET); + } /*! * \brief Opens existing wallet * \param path Name of wallet file * \param password Password of wallet file + * \param nettype Network type * \return Wallet instance (Wallet::status() needs to be called to check if opened successfully) */ - virtual Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) = 0; + virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype = MAINNET) = 0; + Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) // deprecated + { + return openWallet(path, password, testnet ? TESTNET : MAINNET); + } /*! * \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 nettype Network type * \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; + NetworkType nettype = MAINNET, uint64_t restoreHeight = 0) = 0; + Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, + bool testnet = false, uint64_t restoreHeight = 0) // deprecated + { + return recoveryWallet(path, password, mnemonic, testnet ? TESTNET : MAINNET, restoreHeight); + } /*! * \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 mnemonic mnemonic (25 words electrum seed) - * \param testnet testnet + * \param nettype Network type * \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 &mnemonic, bool testnet = false, uint64_t restoreHeight = 0) = 0; + virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype = MAINNET, uint64_t restoreHeight = 0) = 0; + Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, bool testnet = false, uint64_t restoreHeight = 0) // deprecated + { + return recoveryWallet(path, mnemonic, testnet ? TESTNET : MAINNET, restoreHeight); + } /*! * \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 nettype Network type * \param restoreHeight restore from start height * \param addressString public address * \param viewKeyString view key @@ -800,18 +840,29 @@ struct WalletManager virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &password, const std::string &language, - bool testnet, + NetworkType nettype, uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, const std::string &spendKeyString = "") = 0; + 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 = "") // deprecated + { + return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); + } /*! * \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 - * \param testnet testnet + * \param nettype Network type * \param restoreHeight restore from start height * \param addressString public address * \param viewKeyString view key @@ -820,14 +871,24 @@ struct WalletManager */ virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &language, - bool testnet, + NetworkType nettype, uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, const std::string &spendKeyString = "") = 0; + Wallet * 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 = "") // deprecated + { + return createWalletFromKeys(path, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); + } /*! - * \brief Closes wallet. In case operation succeded, wallet object deleted. in case operation failed, wallet object not deleted + * \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted * \param wallet previously opened / created wallet instance * \return None */ diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index bb144227e..5b6df8a9c 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -37,7 +37,7 @@ #include "common/updates.h" #include "version.h" #include "net/http_client.h" - +#include "deviuce/device.hpp" #include <boost/filesystem.hpp> #include <boost/regex.hpp> @@ -60,46 +60,46 @@ namespace { namespace Monero { Wallet *WalletManagerImpl::createWallet(const std::string &path, const std::string &password, - const std::string &language, bool testnet) + const std::string &language, NetworkType nettype) { - WalletImpl * wallet = new WalletImpl(testnet); + WalletImpl * wallet = new WalletImpl(nettype); wallet->create(path, password, language); return wallet; } -Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, bool testnet) +Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, NetworkType nettype) { - WalletImpl * wallet = new WalletImpl(testnet); + WalletImpl * wallet = new WalletImpl(nettype); wallet->open(path, password); //Refresh addressBook wallet->addressBook()->refresh(); return wallet; } -Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &mnemonic, bool testnet, uint64_t restoreHeight) +Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) { - return recoveryWallet(path, "", mnemonic, testnet, restoreHeight); + return recoveryWallet(path, "", mnemonic, nettype, restoreHeight); } Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, const std::string &language, - bool testnet, + NetworkType nettype, uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, const std::string &spendKeyString) { - return createWalletFromKeys(path, "", language, testnet, restoreHeight, + return createWalletFromKeys(path, "", language, nettype, restoreHeight, addressString, viewKeyString, spendKeyString); } Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, - bool testnet, + NetworkType nettype, uint64_t restoreHeight) { - WalletImpl * wallet = new WalletImpl(testnet); + WalletImpl * wallet = new WalletImpl(nettype); if(restoreHeight > 0){ wallet->setRefreshFromBlockHeight(restoreHeight); } @@ -110,13 +110,13 @@ Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, const std::string &password, const std::string &language, - bool testnet, + NetworkType nettype, uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, const std::string &spendKeyString) { - WalletImpl * wallet = new WalletImpl(testnet); + WalletImpl * wallet = new WalletImpl(nettype); if(restoreHeight > 0){ wallet->setRefreshFromBlockHeight(restoreHeight); } @@ -151,7 +151,7 @@ bool WalletManagerImpl::walletExists(const std::string &path) bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const { - return tools::wallet2::verify_password(keys_file_name, password, no_spend_key); + return tools::wallet2::verify_password(keys_file_name, password, no_spend_key, hw::get_device("default")); } std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path) diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 6a4d9de2e..409a6d499 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -38,27 +38,27 @@ class WalletManagerImpl : public WalletManager { 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); + const std::string &language, NetworkType nettype); + Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype); virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, - bool testnet, + NetworkType nettype, uint64_t restoreHeight); virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &password, const std::string &language, - bool testnet, + NetworkType nettype, 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); + virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight); // deprecated: use createWalletFromKeys(..., password, ...) instead virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &language, - bool testnet, + NetworkType nettype, uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d97e53011..9b2595716 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -66,6 +66,7 @@ using namespace epee; #include "memwipe.h" #include "common/base58.h" #include "ringct/rctSigs.h" +#include "device/device.hpp" extern "C" { @@ -117,6 +118,7 @@ struct options { const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 18081"), 0}; const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true}; const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; + const command_line::arg_descriptor<bool> stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false}; const command_line::arg_descriptor<bool> restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false}; }; @@ -158,6 +160,7 @@ std::string get_size_string(const cryptonote::blobdata &tx) std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); + const bool stagenet = command_line::get_arg(vm, opts.stagenet); const bool restricted = command_line::get_arg(vm, opts.restricted); auto daemon_address = command_line::get_arg(vm, opts.daemon_address); @@ -186,13 +189,13 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl if (!daemon_port) { - daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; + daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : stagenet ? config::stagenet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; } if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(testnet, restricted)); + std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(testnet ? TESTNET : stagenet ? STAGENET : MAINNET, restricted)); wallet->init(std::move(daemon_address), std::move(login)); return wallet; } @@ -229,6 +232,8 @@ boost::optional<tools::password_container> get_password(const boost::program_opt std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); + const bool stagenet = command_line::get_arg(vm, opts.stagenet); + const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET; /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly @@ -328,7 +333,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, if (field_address_found) { cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, testnet, field_address)) + if(!get_account_address_from_str(info, nettype, field_address)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("invalid address")); } @@ -382,12 +387,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, if (field_spendkey.empty()) { - // if we have an addres but no spend key, we can deduce the spend public key + // if we have an address but no spend key, we can deduce the spend public key // from the address if (field_address_found) { cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, testnet, field_address)) + if(!get_account_address_from_str(info, nettype, field_address)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to parse address: ")) + field_address); } @@ -538,7 +543,7 @@ uint8_t get_bulletproof_fork() return 8; } -crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx) +crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) { crypto::hash8 payment_id8 = null_hash8; std::vector<tx_extra_field> tx_extra_fields; @@ -553,16 +558,16 @@ crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx) MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt"); return crypto::null_hash8; } - decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key); + decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key, hwdev); } } return payment_id8; } -tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx) +tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) { tools::wallet2::tx_construction_data construction_data = ptx.construction_data; - crypto::hash8 payment_id = get_short_payment_id(ptx); + crypto::hash8 payment_id = get_short_payment_id(ptx,hwdev); if (payment_id != null_hash8) { // Remove encrypted @@ -588,12 +593,12 @@ const size_t MAX_SPLIT_ATTEMPTS = 30; constexpr const std::chrono::seconds wallet2::rpc_timeout; const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); } -wallet2::wallet2(bool testnet, bool restricted): +wallet2::wallet2(network_type nettype, bool restricted): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), m_run(true), m_callback(0), - m_testnet(testnet), + m_nettype(nettype), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), @@ -623,7 +628,8 @@ wallet2::wallet2(bool testnet, bool restricted): m_light_wallet_blockchain_height(0), m_light_wallet_connected(false), m_light_wallet_balance(0), - m_light_wallet_unlocked_balance(0) + m_light_wallet_unlocked_balance(0), + m_key_on_device(false) { } @@ -632,6 +638,11 @@ bool wallet2::has_testnet_option(const boost::program_options::variables_map& vm return command_line::get_arg(vm, options().testnet); } +bool wallet2::has_stagenet_option(const boost::program_options::variables_map& vm) +{ + return command_line::get_arg(vm, options().stagenet); +} + void wallet2::init_options(boost::program_options::options_description& desc_params) { const options opts{}; @@ -642,6 +653,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.daemon_port); command_line::add_arg(desc_params, opts.daemon_login); command_line::add_arg(desc_params, opts.testnet); + command_line::add_arg(desc_params, opts.stagenet); command_line::add_arg(desc_params, opts.restricted); } @@ -688,7 +700,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia //---------------------------------------------------------------------------------------------------- bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl) { - m_checkpoints.init_default_checkpoints(m_testnet); + m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) m_http_client.disconnect(); m_is_initialized = true; @@ -817,52 +829,29 @@ void wallet2::set_seed_language(const std::string &language) //---------------------------------------------------------------------------------------------------- cryptonote::account_public_address wallet2::get_subaddress(const cryptonote::subaddress_index& index) const { - const cryptonote::account_keys& keys = m_account.get_keys(); - if (index.is_zero()) - return keys.m_account_address; - - crypto::public_key D = get_subaddress_spend_public_key(index); - - // C = a*D - crypto::public_key C = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(D), rct::sk2rct(keys.m_view_secret_key))); // could have defined secret_key_mult_public_key() under src/crypto - - // result: (C, D) cryptonote::account_public_address address; - address.m_view_public_key = C; - address.m_spend_public_key = D; + hw::device &hwdev = m_account.get_device(); + hwdev.get_subaddress(m_account.get_keys(), index,address); return address; } //---------------------------------------------------------------------------------------------------- crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const { - const cryptonote::account_keys& keys = m_account.get_keys(); - if (index.is_zero()) - return keys.m_account_address.m_spend_public_key; - - // m = Hs(a || index_major || index_minor) - crypto::secret_key m = cryptonote::get_subaddress_secret_key(keys.m_view_secret_key, index); - - // M = m*G - crypto::public_key M; - crypto::secret_key_to_public_key(m, M); - - // D = B + M - rct::key D_rct; - rct::addKeys(D_rct, rct::pk2rct(keys.m_account_address.m_spend_public_key), rct::pk2rct(M)); // could have defined add_public_key() under src/crypto - crypto::public_key D = rct::rct2pk(D_rct); - + crypto::public_key D ; + hw::device &hwdev = m_account.get_device(); + hwdev.get_subaddress_spend_public_key(m_account.get_keys(), index, D); return D; } //---------------------------------------------------------------------------------------------------- std::string wallet2::get_subaddress_as_str(const cryptonote::subaddress_index& index) const { cryptonote::account_public_address address = get_subaddress(index); - return cryptonote::get_account_address_as_str(m_testnet, !index.is_zero(), address); + return cryptonote::get_account_address_as_str(m_nettype, !index.is_zero(), address); } //---------------------------------------------------------------------------------------------------- std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id) const { - return cryptonote::get_account_integrated_address_as_str(m_testnet, get_address(), payment_id); + return cryptonote::get_account_integrated_address_as_str(m_nettype, get_address(), payment_id); } //---------------------------------------------------------------------------------------------------- void wallet2::add_subaddress_account(const std::string& label) @@ -882,20 +871,19 @@ void wallet2::add_subaddress(uint32_t index_major, const std::string& label) //---------------------------------------------------------------------------------------------------- void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) { + hw::device &hwdev = m_account.get_device(); if (m_subaddress_labels.size() <= index.major) { // add new accounts cryptonote::subaddress_index index2; for (index2.major = m_subaddress_labels.size(); index2.major < index.major + m_subaddress_lookahead_major; ++index2.major) { - for (index2.minor = 0; index2.minor < (index2.major == index.major ? index.minor : 0) + m_subaddress_lookahead_minor; ++index2.minor) + const uint32_t end = (index2.major == index.major ? index.minor : 0) + m_subaddress_lookahead_minor; + const std::vector<crypto::public_key> pkeys = cryptonote::get_subaddress_spend_public_keys(m_account.get_keys(), index2.major, 0, end, hwdev); + for (index2.minor = 0; index2.minor < end; ++index2.minor) { - if (m_subaddresses_inv.count(index2) == 0) - { - crypto::public_key D = get_subaddress_spend_public_key(index2); - m_subaddresses[D] = index2; - m_subaddresses_inv[index2] = D; - } + const crypto::public_key &D = pkeys[index2.minor]; + m_subaddresses[D] = index2; } } m_subaddress_labels.resize(index.major + 1, {"Untitled account"}); @@ -904,15 +892,14 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) else if (m_subaddress_labels[index.major].size() <= index.minor) { // add new subaddresses - cryptonote::subaddress_index index2 = index; - for (index2.minor = m_subaddress_labels[index.major].size(); index2.minor < index.minor + m_subaddress_lookahead_minor; ++index2.minor) + const uint32_t end = index.minor + m_subaddress_lookahead_minor; + const uint32_t begin = m_subaddress_labels[index.major].size(); + cryptonote::subaddress_index index2 = {index.major, begin}; + const std::vector<crypto::public_key> pkeys = cryptonote::get_subaddress_spend_public_keys(m_account.get_keys(), index2.major, index2.minor, end, hwdev); + for (; index2.minor < end; ++index2.minor) { - if (m_subaddresses_inv.count(index2) == 0) - { - crypto::public_key D = get_subaddress_spend_public_key(index2); - m_subaddresses[D] = index2; - m_subaddresses_inv[index2] = D; - } + const crypto::public_key &D = pkeys[index2.minor - begin]; + m_subaddresses[D] = index2; } m_subaddress_labels[index.major].resize(index.minor + 1); } @@ -973,7 +960,7 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio LOG_ERROR("wrong type id in transaction out"); return; } - tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, boost::get<txout_to_key>(o.target).key, derivation, additional_derivations, i); + tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, boost::get<txout_to_key>(o.target).key, derivation, additional_derivations, i, m_account.get_device()); if(tx_scan_info.received) { tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs @@ -985,20 +972,20 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio tx_scan_info.error = false; } //---------------------------------------------------------------------------------------------------- -static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask) +static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev) { crypto::secret_key scalar1; - crypto::derivation_to_scalar(derivation, i, scalar1); + crypto::derivation_to_scalar(derivation, i, scalar1, hwdev); try { switch (rv.type) { case rct::RCTTypeSimple: case rct::RCTTypeSimpleBulletproof: - return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask); + return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); case rct::RCTTypeFull: case rct::RCTTypeFullBulletproof: - return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask); + return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); default: LOG_ERROR("Unsupported rct type: " << rv.type); return 0; @@ -1022,7 +1009,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi } else { - 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); + 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, m_account.get_device()); 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"); @@ -1031,7 +1018,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi outs.push_back(i); if (tx_scan_info.money_transfered == 0) { - tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask); + tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device()); } tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered; tx_scan_info.amount = tx_scan_info.money_transfered; @@ -1079,8 +1066,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; const cryptonote::account_keys& keys = m_account.get_keys(); + hw::device &hwdev = m_account.get_device(); crypto::key_derivation derivation; - if (!generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation)) + if (!generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation, hwdev)) { MWARNING("Failed to generate key derivation from tx pubkey, skipping"); static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key"); @@ -1093,7 +1081,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) { additional_derivations.push_back({}); - if (!generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back())) + if (!generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back(),hwdev)) { MWARNING("Failed to generate key derivation from tx pubkey, skipping"); additional_derivations.pop_back(); @@ -1218,7 +1206,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_rct = false; } set_unspent(m_transfers.size()-1); - if (!m_multisig) + if (!m_multisig && !m_watch_only) m_key_images[td.m_key_image] = m_transfers.size()-1; m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1; if (m_multisig) @@ -1312,11 +1300,21 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount; if (amount > 0) { - THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error, - std::string("Inconsistent amount in tx input: got ") + print_money(amount) + - std::string(", expected ") + print_money(td.amount())); + if(amount != td.amount()) + { + MERROR("Inconsistent amount in tx input: got " << print_money(amount) << + ", expected " << print_money(td.amount())); + // this means: + // 1) the same output pub key was used as destination multiple times, + // 2) the wallet set the highest amount among them to transfer_details::m_amount, and + // 3) the wallet somehow spent that output with an amount smaller than the above amount, causing inconsistency + td.m_amount = amount; + } + } + else + { + amount = td.amount(); } - amount = td.amount(); tx_money_spent_in_ins += amount; if (subaddr_account && *subaddr_account != td.m_subaddr_index.major) LOG_ERROR("spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect"); @@ -1375,7 +1373,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8); if (tx_pub_key != null_pkey) { - if (!decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key)) + if (!decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key, m_account.get_device())) { LOG_PRINT_L0("Failed to decrypt payment ID: " << payment_id8); } @@ -2342,7 +2340,6 @@ bool wallet2::clear() m_address_book.clear(); m_local_bc_height = 1; m_subaddresses.clear(); - m_subaddresses_inv.clear(); m_subaddress_labels.clear(); return true; } @@ -2379,6 +2376,10 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable } rapidjson::Value value2(rapidjson::kNumberType); + + value2.SetInt(m_key_on_device?1:0); + json.AddMember("key_on_device", value2, json.GetAllocator()); + value2.SetInt(watch_only ? 1 :0); // WTF ? JSON has different true and false types, and not boolean ?? json.AddMember("watch_only", value2, json.GetAllocator()); @@ -2450,8 +2451,8 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable 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()); + value2.SetUint(m_nettype); + json.AddMember("nettype", value2, json.GetAllocator()); // Serialize the JSON object rapidjson::StringBuffer buffer; @@ -2476,16 +2477,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable return true; } //---------------------------------------------------------------------------------------------------- -namespace -{ - bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) - { - crypto::public_key pub; - bool r = crypto::secret_key_to_public_key(sec, pub); - return r && expected_pub == pub; - } -} - /*! * \brief Load wallet information from wallet file. * \param keys_file_name Name of wallet file @@ -2533,6 +2524,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_confirm_backlog_threshold = 0; m_confirm_export_overwrite = true; m_auto_low_priority = true; + m_key_on_device = false; } else if(json.IsObject()) { @@ -2549,6 +2541,12 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ const char *field_key_data = json["key_data"].GetString(); account_data = std::string(field_key_data, field_key_data + json["key_data"].GetStringLength()); + if (json.HasMember("key_on_device")) + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, false); + m_key_on_device = field_key_on_device; + } + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false, std::string()); if (field_seed_language_found) { @@ -2634,13 +2632,14 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ 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"); - // Wallet is being opened without testnet flag but is saved as a testnet wallet. - THROW_WALLET_EXCEPTION_IF(!m_testnet && field_testnet, error::wallet_internal_error, "Testnet wallet can not be opened as mainnet wallet"); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_low_priority, int, Int, false, true); + m_auto_low_priority = field_auto_low_priority; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, nettype, uint8_t, Uint, false, static_cast<uint8_t>(m_nettype)); + // The network type given in the program argument is inconsistent with the network type saved in the wallet + THROW_WALLET_EXCEPTION_IF(static_cast<uint8_t>(m_nettype) != field_nettype, error::wallet_internal_error, + (boost::format("%s wallet can not be opened as %s wallet") + % (field_nettype == 0 ? "Mainnet" : field_nettype == 1 ? "Testnet" : "Stagenet") + % (m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : "stagenet")).str()); } else { @@ -2648,11 +2647,20 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ return false; } - const cryptonote::account_keys& keys = m_account.get_keys(); r = epee::serialization::load_t_from_binary(m_account, account_data); - r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); + if (r && m_key_on_device) { + LOG_PRINT_L0("Account on device. Initing device..."); + hw::device &hwdev = hw::get_device("Ledger"); + hwdev.init(); + hwdev.connect(); + m_account.set_device(hwdev); + LOG_PRINT_L0("Device inited..."); + } + const cryptonote::account_keys& keys = m_account.get_keys(); + hw::device &hwdev = m_account.get_device(); + r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); if(!m_watch_only && !m_multisig) - r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); + r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); return true; } @@ -2669,7 +2677,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ */ bool wallet2::verify_password(const epee::wipeable_string& password) const { - return verify_password(m_keys_file, password, m_watch_only || m_multisig); + return verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device()); } /*! @@ -2684,7 +2692,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) const * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password * */ -bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key) +bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev) { rapidjson::Document json; wallet2::keys_file_data keys_file_data; @@ -2719,9 +2727,9 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip r = epee::serialization::load_t_from_binary(account_data_check, account_data); const cryptonote::account_keys& keys = account_data_check.get_keys(); - r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); + r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); if(!no_spend_key) - r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); + r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); return r; } @@ -2797,13 +2805,14 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig = true; m_multisig_threshold = threshold; m_multisig_signers = multisig_signers; + m_key_on_device = false; if (!wallet_.empty()) { bool r = store_keys(m_keys_file, password, false); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet)); + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); if(!r) MERROR("String with address text not saved"); } @@ -2845,17 +2854,11 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + m_key_on_device = false; - // -1 month for fluctuations in block time and machine date/time setup. - // avg seconds per block - const int seconds_per_block = DIFFICULTY_TARGET_V2; - // ~num blocks per month - const uint64_t blocks_per_month = 60*60*24*30/seconds_per_block; - - // try asking the daemon first + // calculate a starting refresh height if(m_refresh_from_block_height == 0 && !recover){ - uint64_t height = estimate_blockchain_height(); - m_refresh_from_block_height = height >= blocks_per_month ? height - blocks_per_month : 0; + m_refresh_from_block_height = estimate_blockchain_height(); } if (!wallet_.empty()) @@ -2863,7 +2866,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip bool r = store_keys(m_keys_file, password, false); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet)); + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); if(!r) MERROR("String with address text not saved"); } @@ -2890,20 +2893,30 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip std::string err; uint64_t height = 0; - // we get the max of approximated height and known height + // we get the max of approximated height and local height. // approximated height is the least of daemon target height // (the max of what the other daemons are claiming is their // height) and the theoretical height based on the local // clock. This will be wrong only if both the local clock // is bad *and* a peer daemon claims a highest height than // the real chain. - // known height is the height the local daemon is currently + // local height is the height the local daemon is currently // synced to, it will be lower than the real chain height if // the daemon is currently syncing. + // If we use the approximate height we subtract one month as + // a safety margin. height = get_approximate_blockchain_height(); uint64_t target_height = get_daemon_blockchain_target_height(err); - if (err.empty() && target_height < height) - height = target_height; + if (err.empty()) { + if (target_height < height) + height = target_height; + } else { + // if we couldn't talk to the daemon, check safety margin. + if (height > blocks_per_month) + height -= blocks_per_month; + else + height = 0; + } uint64_t local_height = get_daemon_blockchain_height(err); if (err.empty() && local_height > height) height = local_height; @@ -2936,13 +2949,14 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + m_key_on_device = false; if (!wallet_.empty()) { bool r = store_keys(m_keys_file, password, true); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet)); + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); if(!r) MERROR("String with address text not saved"); } @@ -2982,13 +2996,14 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + m_key_on_device = false; if (!wallet_.empty()) { bool r = store_keys(m_keys_file, password, false); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet)); + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); if(!r) MERROR("String with address text not saved"); } @@ -3001,6 +3016,46 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& store(); } +/*! +* \brief Creates a wallet from a device +* \param wallet_ Name of wallet file +* \param password Password of wallet file +* \param device_name device string address +*/ +void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name) +{ + clear(); + prepare_file_names(wallet_); + + boost::system::error_code ignored_ec; + if (!wallet_.empty()) { + THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file); + THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); + } + m_key_on_device = true; + m_account.create_from_device(device_name); + m_account_public_address = m_account.get_keys().m_account_address; + m_watch_only = false; + m_multisig = false; + m_multisig_threshold = 0; + m_multisig_signers.clear(); + + if (!wallet_.empty()) { + bool r = store_keys(m_keys_file, password, false); + THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); + + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); + if(!r) MERROR("String with address text not saved"); + } + cryptonote::block b; + generate_genesis(b); + m_blockchain.push_back(get_block_hash(b)); + add_subaddress_account(tr("Primary account")); + if (!wallet_.empty()) { + store(); + } +} + std::string wallet2::make_multisig(const epee::wipeable_string &password, const std::vector<crypto::secret_key> &view_keys, const std::vector<crypto::public_key> &spend_keys, @@ -3064,6 +3119,8 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, m_watch_only = false; m_multisig = true; m_multisig_threshold = threshold; + m_key_on_device = false; + if (threshold == spend_keys.size() + 1) { m_multisig_signers = spend_keys; @@ -3079,7 +3136,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, bool r = store_keys(m_keys_file, password, false); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet)); + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); if(!r) MERROR("String with address text not saved"); } @@ -3179,12 +3236,11 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor bool r = store_keys(m_keys_file, password, false); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet)); + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); if(!r) MERROR("String with address text not saved"); } m_subaddresses.clear(); - m_subaddresses_inv.clear(); m_subaddress_labels.clear(); add_subaddress_account(tr("Primary account")); @@ -3473,15 +3529,8 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) //---------------------------------------------------------------------------------------------------- bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const { - const account_keys &keys = m_account.get_keys(); - const crypto::secret_key &view_key = keys.m_view_secret_key; - const crypto::secret_key &spend_key = keys.m_spend_secret_key; - tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1> data; - memcpy(data.data(), &view_key, sizeof(view_key)); - memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key)); - data[sizeof(data) - 1] = CHACHA8_KEY_TAIL; - crypto::generate_chacha_key(data.data(), sizeof(data), key); - return true; + hw::device &hwdev = m_account.get_device(); + return hwdev.generate_chacha_key(m_account.get_keys(), key); } //---------------------------------------------------------------------------------------------------- void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password) @@ -3497,7 +3546,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass { THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file); } - LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_testnet)); + LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype)); //keys loaded ok! //try to load wallet file. but even if we failed, it is not big problem @@ -3636,7 +3685,7 @@ void wallet2::trim_hashchain() } //---------------------------------------------------------------------------------------------------- void wallet2::check_genesis(const crypto::hash& genesis_hash) const { - std::string what("Genesis block mismatch. You probably use wallet without testnet flag with blockchain from test network or vice versa"); + std::string what("Genesis block mismatch. You probably use wallet without testnet (or stagenet) flag with blockchain from test (or stage) network or vice versa"); THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain.genesis(), error::wallet_internal_error, what); } @@ -3713,7 +3762,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); // save address to the new file const std::string address_file = m_wallet_file + ".address.txt"; - r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet)); + r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_nettype)); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file); // remove old wallet file r = boost::filesystem::remove(old_file); @@ -3990,7 +4039,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig uint64_t current_time = static_cast<uint64_t>(time(NULL)); // XXX: this needs to be fast, so we'd need to get the starting heights // from the daemon to be correct once voting kicks in - uint64_t v2height = m_testnet ? 624634 : 1009827; + uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827; uint64_t leeway = block_height < v2height ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2; if(current_time + leeway >= unlock_time) return true; @@ -4255,7 +4304,7 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt"); return crypto::null_hash; } - if (decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key)) + if (decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key, m_account.get_device())) { memcpy(payment_id.data, payment_id8.data, 8); } @@ -4278,7 +4327,7 @@ void wallet2::commit_tx(pending_tx& ptx) { cryptonote::COMMAND_RPC_SUBMIT_RAW_TX::request oreq; cryptonote::COMMAND_RPC_SUBMIT_RAW_TX::response ores; - oreq.address = get_account().get_public_address_str(m_testnet); + oreq.address = get_account().get_public_address_str(m_nettype); oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); m_daemon_rpc_mutex.lock(); @@ -4364,7 +4413,7 @@ bool wallet2::save_tx(const std::vector<pending_tx>& ptx_vector, const std::stri // Short payment id is encrypted with tx_key. // Since sign_tx() generates new tx_keys and encrypts the payment id, we need to save the decrypted payment ID // Save tx construction_data to unsigned_tx_set - txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx)); + txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); } txs.transfers = m_transfers; @@ -4488,7 +4537,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f std::vector<crypto::secret_key> additional_tx_keys; rct::multisig_out msout; bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, bulletproof, m_multisig ? &msout : NULL); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_testnet); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); // we don't test tx size, because we don't know the current limit, due to not having a blockchain, // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway, // and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's @@ -4691,7 +4740,7 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs) for (auto &ptx: txs.m_ptx) { // Get decrypted payment id from pending_tx - ptx.construction_data = get_construction_data_with_decrypted_short_payment_id(ptx); + ptx.construction_data = get_construction_data_with_decrypted_short_payment_id(ptx, m_account.get_device()); } // save as binary @@ -4861,7 +4910,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto auto sources = sd.sources; const bool bulletproof = sd.use_rct && (ptx.tx.rct_signatures.type == rct::RCTTypeFullBulletproof || ptx.tx.rct_signatures.type == rct::RCTTypeSimpleBulletproof); bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, bulletproof, &msout); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_testnet); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx), error::wallet_internal_error, "Transaction prefix does not match data"); @@ -5270,7 +5319,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, m_http_client, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs"); - THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs recieved from light wallet node. Error: " + ores.Error); + THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error); // Check if we got enough outputs for each amount for(auto& out: ores.amount_outs) { @@ -5494,7 +5543,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> } } - // sort the subsection, to ensure the daemon doesn't know wich output is ours + // sort the subsection, to ensure the daemon doesn't know which output is ours std::sort(req.outputs.begin() + start, req.outputs.end(), [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; }); } @@ -5607,7 +5656,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); needed_money += dt.amount; LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money)); - THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_testnet); + THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype); } uint64_t found_money = 0; @@ -5700,7 +5749,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent LOG_PRINT_L2("constructing tx"); bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, m_multisig ? &msout : NULL); LOG_PRINT_L2("constructed tx, r="<<r); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); std::string key_images; @@ -5764,7 +5813,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); needed_money += dt.amount; LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money)); - THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_testnet); + THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype); } // if this is a multisig wallet, create a list of multisig signers we can use @@ -5904,7 +5953,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry auto sources_copy = sources; bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, bulletproof, m_multisig ? &msout : NULL); LOG_PRINT_L2("constructed tx, r="<<r); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_testnet); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); // work out the permutation done on sources @@ -5949,7 +5998,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry auto sources_copy_copy = sources_copy; bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, bulletproof, &msout); LOG_PRINT_L2("constructed tx, r="<<r); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); 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, std::unordered_set<crypto::public_key>(), msout}); @@ -6119,9 +6168,9 @@ bool wallet2::light_wallet_login(bool &new_address) m_light_wallet_connected = false; cryptonote::COMMAND_RPC_LOGIN::request request; cryptonote::COMMAND_RPC_LOGIN::response response; - request.address = get_account().get_public_address_str(m_testnet); + request.address = get_account().get_public_address_str(m_nettype); request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - // Always create account if it doesnt exist. + // Always create account if it doesn't exist. request.create_account = true; m_daemon_rpc_mutex.lock(); bool connected = epee::net_utils::invoke_http_json("/login", request, response, m_http_client, rpc_timeout, "POST"); @@ -6134,7 +6183,7 @@ bool wallet2::light_wallet_login(bool &new_address) MDEBUG("New wallet: " << response.new_address); if(m_light_wallet_connected) { - // Clear old data on successfull login. + // Clear old data on successful login. // m_transfers.clear(); // m_payments.clear(); // m_unconfirmed_payments.clear(); @@ -6146,7 +6195,7 @@ bool wallet2::light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_ { MDEBUG("Light wallet import wallet request"); cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq; - oreq.address = get_account().get_public_address_str(m_testnet); + oreq.address = get_account().get_public_address_str(m_nettype); oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_json("/import_wallet_request", oreq, response, m_http_client, rpc_timeout, "POST"); @@ -6165,7 +6214,7 @@ void wallet2::light_wallet_get_unspent_outs() cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::response ores; oreq.amount = "0"; - oreq.address = get_account().get_public_address_str(m_testnet); + oreq.address = get_account().get_public_address_str(m_nettype); oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); // openMonero specific oreq.dust_threshold = boost::lexical_cast<std::string>(::config::DEFAULT_DUST_THRESHOLD); @@ -6315,7 +6364,7 @@ bool wallet2::light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_ cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::request request; - request.address = get_account().get_public_address_str(m_testnet); + request.address = get_account().get_public_address_str(m_nettype); request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_json("/get_address_info", request, response, m_http_client, rpc_timeout, "POST"); @@ -6332,7 +6381,7 @@ void wallet2::light_wallet_get_address_txs() cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::request ireq; cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::response ires; - ireq.address = get_account().get_public_address_str(m_testnet); + ireq.address = get_account().get_public_address_str(m_nettype); ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_json("/get_address_txs", ireq, ires, m_http_client, rpc_timeout, "POST"); @@ -6506,7 +6555,7 @@ void wallet2::light_wallet_get_address_txs() // Calculate wallet balance m_light_wallet_balance = ires.total_received-wallet_total_sent; - // MyMonero doesnt send unlocked balance + // MyMonero doesn't send unlocked balance if(ires.total_received_unlocked > 0) m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent; else @@ -6657,7 +6706,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); needed_money += dt.amount; LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money)); - THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, 0, m_testnet); + THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, 0, m_nettype); } // throw if attempting a transaction with no money @@ -6783,7 +6832,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp { string s; for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + " (" + print_money(m_transfers[i].amount()) + ") "; - LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s); + LOG_PRINT_L1("Found preferred rct inputs for rct tx: " << s); // bring the list of available outputs stored by the same subaddress index to the front of the list uint32_t index_minor = m_transfers[preferred_inputs[0]].m_subaddr_index.minor; @@ -6814,6 +6863,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp unsigned int original_output_index = 0; std::vector<size_t>* unused_transfers_indices = &unused_transfers_indices_per_subaddr[0].second; std::vector<size_t>* unused_dust_indices = &unused_dust_indices_per_subaddr[0].second; + hw::device &hwdev = m_account.get_device(); + hwdev.set_signature_mode(hw::device::SIGNATURE_FAKE); while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) { TX &tx = txes.back(); @@ -6883,7 +6934,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can fully pay that destination - LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) << + LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(dsts[0].amount)); tx.add(dsts[0].addr, dsts[0].is_subaddress, dsts[0].amount, original_output_index, m_merge_destinations); available_amount -= dsts[0].amount; @@ -6894,7 +6945,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can partially fill that destination - LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) << + LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); tx.add(dsts[0].addr, dsts[0].is_subaddress, available_amount, original_output_index, m_merge_destinations); dsts[0].amount -= available_amount; @@ -6964,7 +7015,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp if (i->amount > needed_fee) { uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee; - LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_testnet, i->is_subaddress, i->addr) << " from " << + LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_nettype, i->is_subaddress, i->addr) << " from " << print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accommodate " << print_money(needed_fee) << " fee"); dsts[0].amount += i->amount - new_paid_amount; @@ -6999,6 +7050,37 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp 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"); + if ((!dsts.empty()) || + (dsts.empty() && !(adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) ) + ) { + hwdev.set_signature_mode(hw::device::SIGNATURE_REAL); + if (use_rct) { + transfer_selected_rct(tx.dsts, /* NOMOD std::vector<cryptonote::tx_destination_entry> dsts,*/ + tx.selected_transfers, /* const std::list<size_t> selected_transfers */ + fake_outs_count, /* CONST size_t fake_outputs_count, */ + outs, /* MOD std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, */ + unlock_time, /* CONST uint64_t unlock_time, */ + needed_fee, /* CONST uint64_t fee, */ + extra, /* const std::vector<uint8_t>& extra, */ + test_tx, /* OUT cryptonote::transaction& tx, */ + test_ptx, /* OUT cryptonote::transaction& tx, */ + bulletproof); + } else { + transfer_selected(tx.dsts, + tx.selected_transfers, + fake_outs_count, + outs, + unlock_time, + needed_fee, + extra, + detail::digit_split_strategy, + tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), + test_tx, + test_ptx); + } + hwdev.set_signature_mode(hw::device::SIGNATURE_FAKE); + } + tx.tx = test_tx; tx.ptx = test_ptx; tx.bytes = txBlob.size(); @@ -7167,6 +7249,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton needed_fee = 0; // while we have something to send + hw::device &hwdev = m_account.get_device(); + hwdev.set_signature_mode(hw::device::SIGNATURE_FAKE); while (!unused_dust_indices.empty() || !unused_transfers_indices.empty()) { TX &tx = txes.back(); @@ -7232,6 +7316,18 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton " fee and " << print_money(test_ptx.change_dts.amount) << " change"); } while (needed_fee > test_ptx.fee); + if (!unused_transfers_indices.empty() || !unused_dust_indices.empty()) { + hwdev.set_signature_mode(hw::device::SIGNATURE_REAL); + if (use_rct) { + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + test_tx, test_ptx, bulletproof); + } else { + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); + } + hwdev.set_signature_mode(hw::device::SIGNATURE_FAKE); + } + 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"); @@ -7521,7 +7617,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string const std::vector<crypto::public_key> in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx); keypair in_ephemeral; crypto::key_image in_img; - THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img), + THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device()), error::wallet_internal_error, "failed to generate key image"); THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch"); @@ -7696,13 +7792,13 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes void wallet2::check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) { crypto::key_derivation derivation; - THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation, m_account.get_device()), error::wallet_internal_error, "Failed to generate key derivation from supplied parameters"); std::vector<crypto::key_derivation> additional_derivations; additional_derivations.resize(additional_tx_keys.size()); for (size_t i = 0; i < additional_tx_keys.size(); ++i) - THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, additional_tx_keys[i], additional_derivations[i]), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(address.m_view_public_key, additional_tx_keys[i], additional_derivations[i], m_account.get_device()), error::wallet_internal_error, "Failed to generate key derivation from supplied parameters"); check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations); @@ -7736,6 +7832,7 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de "The size of additional derivations is wrong"); received = 0; + hw::device &hwdev = m_account.get_device(); for (size_t n = 0; n < tx.vout.size(); ++n) { const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target)); @@ -7743,13 +7840,13 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de continue; crypto::public_key derived_out_key; - bool r = derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key); + bool r = derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key, hwdev); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key"); bool found = out_key->key == derived_out_key; crypto::key_derivation found_derivation = derivation; if (!found && !additional_derivations.empty()) { - r = derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key); + r = derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key,hwdev); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key"); found = out_key->key == derived_out_key; found_derivation = additional_derivations[n]; @@ -7765,9 +7862,9 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de else { crypto::secret_key scalar1; - crypto::derivation_to_scalar(found_derivation, n, scalar1); + crypto::derivation_to_scalar(found_derivation, n, scalar1, hwdev); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; - rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1)); + rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), hwdev); const rct::key C = tx.rct_signatures.outPk[n].mask; rct::key Ctmp; rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H); @@ -8121,7 +8218,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, // 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); + 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, m_account.get_device()); 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"); @@ -8345,16 +8442,16 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) uint64_t wallet2::get_approximate_blockchain_height() const { // time of v2 fork - const time_t fork_time = m_testnet ? 1448285909 : 1458748658; + const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? (time_t)-1/*TODO*/ : 1458748658; // v2 fork block - const uint64_t fork_block = m_testnet ? 624634 : 1009827; + const uint64_t fork_block = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827; // avg seconds per block const int seconds_per_block = DIFFICULTY_TARGET_V2; // 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 static const uint64_t approximate_testnet_rolled_back_blocks = 148540; - if (m_testnet && approx_blockchain_height > approximate_testnet_rolled_back_blocks) + if (m_nettype == 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; @@ -8495,20 +8592,21 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle // more than one, loop and search const cryptonote::account_keys& keys = m_account.get_keys(); size_t pk_index = 0; + hw::device &hwdev = m_account.get_device(); const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); std::vector<crypto::key_derivation> additional_derivations; for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) { additional_derivations.push_back({}); - bool r = generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()); + bool r = generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back(), hwdev); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); } while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) { const crypto::public_key tx_pub_key = pub_key_field.pub_key; crypto::key_derivation derivation; - bool r = generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation); + bool r = generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation, hwdev); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); for (size_t i = 0; i < td.m_tx.vout.size(); ++i) @@ -8579,7 +8677,7 @@ std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key // generate ephemeral secret key crypto::key_image ki; cryptonote::keypair in_ephemeral; - bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki); + bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, ki, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); THROW_WALLET_EXCEPTION_IF(td.m_key_image_known && !td.m_key_image_partial && ki != td.m_key_image, @@ -8779,6 +8877,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag // process each outgoing tx auto spent_txid = spent_txids.begin(); + hw::device &hwdev = m_account.get_device(); for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs) { THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool"); @@ -8796,14 +8895,14 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag const cryptonote::account_keys& keys = m_account.get_keys(); const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx); crypto::key_derivation derivation; - bool r = generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation); + bool r = generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation, hwdev); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(spent_tx); std::vector<crypto::key_derivation> additional_derivations; for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) { additional_derivations.push_back({}); - r = generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()); + r = generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back(), hwdev); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); } size_t output_index = 0; @@ -8817,7 +8916,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag if (tx_scan_info.money_transfered == 0) { rct::key mask; - tx_scan_info.money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_scan_info.received->derivation, output_index, mask); + tx_scan_info.money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_scan_info.received->derivation, output_index, mask, hwdev); } tx_money_got_in_outs += tx_scan_info.money_transfered; } @@ -8977,7 +9076,7 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key; - bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image); + bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); expand_subaddresses(td.m_subaddr_index); td.m_key_image_known = true; @@ -9333,7 +9432,7 @@ std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext, 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)) + if(!get_account_address_from_str(info, nettype(), address)) { error = std::string("wrong address: ") + address; return std::string(); @@ -9397,7 +9496,7 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder; cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, testnet(), address)) + if(!get_account_address_from_str(info, nettype(), address)) { error = std::string("URI has wrong address: ") + address; return false; @@ -9646,10 +9745,14 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t mi } //---------------------------------------------------------------------------------------------------- void wallet2::generate_genesis(cryptonote::block& b) const { - if (m_testnet) + if (m_nettype == TESTNET) { cryptonote::generate_genesis_block(b, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE); } + else if (m_nettype == STAGENET) + { + cryptonote::generate_genesis_block(b, config::stagenet::GENESIS_TX, config::stagenet::GENESIS_NONCE); + } else { cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index f9995c2ee..57a61cb9d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -147,6 +147,7 @@ namespace tools static const char* tr(const char* str); static bool has_testnet_option(const boost::program_options::variables_map& vm); + static bool has_stagenet_option(const boost::program_options::variables_map& vm); static void init_options(boost::program_options::options_description& desc_params); //! Uses stdin and stdout. Returns a wallet2 if no errors. @@ -162,9 +163,9 @@ namespace tools //! Just parses variables. static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter); - static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key); + static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev); - wallet2(bool testnet = false, bool restricted = false); + wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, bool restricted = false); struct multisig_info { @@ -487,6 +488,14 @@ namespace tools const cryptonote::account_public_address &account_public_address, const crypto::secret_key& viewkey = crypto::secret_key()); /*! + * \brief Restore a wallet hold by an HW. + * \param wallet_ Name of wallet file + * \param password Password of wallet file + * \param device_name name of HW to use + */ + void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name); + + /*! * \brief Creates a multisig wallet * \return empty if done, non empty if we need to send another string * to other participants @@ -605,6 +614,7 @@ namespace tools cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index& index) const; cryptonote::account_public_address get_address() const { return get_subaddress({0,0}); } crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const; + std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const; std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const; std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); } std::string get_integrated_address_as_str(const crypto::hash8& payment_id) const; @@ -628,12 +638,13 @@ namespace tools void set_refresh_type(RefreshType refresh_type) { m_refresh_type = refresh_type; } RefreshType get_refresh_type() const { return m_refresh_type; } - bool testnet() const { return m_testnet; } + cryptonote::network_type nettype() const { return m_nettype; } bool restricted() const { return m_restricted; } bool watch_only() const { return m_watch_only; } bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; bool has_multisig_partial_key_images() const; bool get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const; + bool key_on_device() const { return m_key_on_device; } // locked & unlocked balance of given or current subaddress account uint64_t balance(uint32_t subaddr_index_major) const; @@ -782,7 +793,8 @@ namespace tools if (ver < 20) return; a & m_subaddresses; - a & m_subaddresses_inv; + std::unordered_map<cryptonote::subaddress_index, crypto::public_key> dummy_subaddresses_inv; + a & dummy_subaddresses_inv; a & m_subaddress_labels; a & m_additional_tx_keys; if(ver < 21) @@ -1089,7 +1101,6 @@ namespace tools std::unordered_map<crypto::public_key, size_t> m_pub_keys; cryptonote::account_public_address m_account_public_address; std::unordered_map<crypto::public_key, cryptonote::subaddress_index> m_subaddresses; - std::unordered_map<cryptonote::subaddress_index, crypto::public_key> m_subaddresses_inv; std::vector<std::vector<std::string>> m_subaddress_labels; std::unordered_map<crypto::hash, std::string> m_tx_notes; std::unordered_map<std::string, std::string> m_attributes; @@ -1104,7 +1115,8 @@ namespace tools boost::mutex m_daemon_rpc_mutex; i_wallet2_callback* m_callback; - bool m_testnet; + bool m_key_on_device; + cryptonote::network_type m_nettype; bool m_restricted; std::string seed_language; /*!< Language of the mnemonics (seed). */ bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ @@ -1635,7 +1647,7 @@ namespace tools { THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); needed_money += dt.amount; - THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_testnet); + THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype); } // randomly select inputs for transaction @@ -1757,7 +1769,7 @@ namespace tools std::vector<crypto::secret_key> additional_tx_keys; rct::multisig_out msout; bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, m_multisig ? &msout : NULL); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); std::string key_images; diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 5c1c49d5d..32a0231b1 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -529,13 +529,13 @@ namespace tools , sources_t const & sources , destinations_t const & destinations , uint64_t unlock_time - , bool testnet + , cryptonote::network_type nettype ) : transfer_error(std::move(loc), "transaction was not constructed") , m_sources(sources) , m_destinations(destinations) , m_unlock_time(unlock_time) - , m_testnet(testnet) + , m_nettype(nettype) { } @@ -569,7 +569,7 @@ namespace tools for (size_t i = 0; i < m_destinations.size(); ++i) { const cryptonote::tx_destination_entry& dst = m_destinations[i]; - ss << "\n " << i << ": " << cryptonote::get_account_address_as_str(m_testnet, dst.is_subaddress, dst.addr) << " " << + ss << "\n " << i << ": " << cryptonote::get_account_address_as_str(m_nettype, dst.is_subaddress, dst.addr) << " " << cryptonote::print_money(dst.amount); } @@ -582,7 +582,7 @@ namespace tools sources_t m_sources; destinations_t m_destinations; uint64_t m_unlock_time; - bool m_testnet; + cryptonote::network_type m_nettype; }; //---------------------------------------------------------------------------------------------------- struct tx_rejected : public transfer_error @@ -624,12 +624,12 @@ namespace tools std::string && loc , const std::vector<cryptonote::tx_destination_entry>& destinations , uint64_t fee - , bool testnet + , cryptonote::network_type nettype ) : transfer_error(std::move(loc), "transaction sum + fee exceeds " + cryptonote::print_money(std::numeric_limits<uint64_t>::max())) , m_destinations(destinations) , m_fee(fee) - , m_testnet(testnet) + , m_nettype(nettype) { } @@ -644,7 +644,7 @@ namespace tools ", destinations:"; for (const auto& dst : m_destinations) { - ss << '\n' << cryptonote::print_money(dst.amount) << " -> " << cryptonote::get_account_address_as_str(m_testnet, dst.is_subaddress, dst.addr); + ss << '\n' << cryptonote::print_money(dst.amount) << " -> " << cryptonote::get_account_address_as_str(m_nettype, dst.is_subaddress, dst.addr); } return ss.str(); } @@ -652,7 +652,7 @@ namespace tools private: std::vector<cryptonote::tx_destination_entry> m_destinations; uint64_t m_fee; - bool m_testnet; + cryptonote::network_type m_nettype; }; //---------------------------------------------------------------------------------------------------- struct tx_too_big : public transfer_error diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 5c644983b..b50133e4a 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -39,6 +39,7 @@ using namespace epee; #include "wallet/wallet_args.h" #include "common/command_line.h" #include "common/i18n.h" +#include "cryptonote_config.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/account.h" #include "multisig/multisig.h" @@ -279,7 +280,7 @@ namespace tools entry.destinations.push_back(wallet_rpc::transfer_destination()); wallet_rpc::transfer_destination &td = entry.destinations.back(); td.amount = d.amount; - td.address = get_account_address_as_str(m_wallet->testnet(), d.is_subaddress, d.addr); + td.address = get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr); } entry.type = "out"; @@ -583,7 +584,7 @@ namespace tools cryptonote::address_parse_info info; cryptonote::tx_destination_entry de; er.message = ""; - if(!get_account_address_from_str_or_url(info, m_wallet->testnet(), it->address, + if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), it->address, [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { if (!dnssec_valid) { @@ -1074,7 +1075,7 @@ namespace tools { cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, m_wallet->testnet(), req.integrated_address)) + if(!get_account_address_from_str(info, m_wallet->nettype(), req.integrated_address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = "Invalid address"; @@ -1086,7 +1087,7 @@ namespace tools er.message = "Address is not an integrated address"; return false; } - res.standard_address = get_account_address_as_str(m_wallet->testnet(), info.is_subaddress, info.address); + res.standard_address = get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address); res.payment_id = epee::string_tools::pod_to_hex(info.payment_id); return true; } @@ -1384,7 +1385,7 @@ namespace tools cryptonote::address_parse_info info; er.message = ""; - if(!get_account_address_from_str_or_url(info, m_wallet->testnet(), req.address, + if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address, [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { if (!dnssec_valid) { @@ -1594,7 +1595,7 @@ namespace tools } cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, m_wallet->testnet(), req.address)) + if(!get_account_address_from_str(info, m_wallet->nettype(), req.address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = "Invalid address"; @@ -1627,7 +1628,7 @@ namespace tools } cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, m_wallet->testnet(), req.address)) + if(!get_account_address_from_str(info, m_wallet->nettype(), req.address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = "Invalid address"; @@ -1660,7 +1661,7 @@ namespace tools } cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, m_wallet->testnet(), req.address)) + if(!get_account_address_from_str(info, m_wallet->nettype(), req.address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = "Invalid address"; @@ -1767,7 +1768,7 @@ namespace tools if (!m_wallet) return not_open(er); cryptonote::address_parse_info info; - if (!get_account_address_from_str(info, m_wallet->testnet(), req.address)) + if (!get_account_address_from_str(info, m_wallet->nettype(), req.address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = "Invalid address"; @@ -1803,11 +1804,11 @@ namespace tools return false; } - uint64_t min_height = 0, max_height = (uint64_t)-1; + uint64_t min_height = 0, max_height = CRYPTONOTE_MAX_BLOCK_NUMBER; if (req.filter_by_height) { min_height = req.min_height; - max_height = req.max_height; + max_height = req.max_height <= max_height ? req.max_height : max_height; } if (req.in) @@ -2057,7 +2058,7 @@ namespace tools { uint64_t idx = 0; for (const auto &entry: ab) - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->testnet(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); } else { @@ -2070,7 +2071,7 @@ namespace tools return false; } const auto &entry = ab[idx]; - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->testnet(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); } } return true; @@ -2089,7 +2090,7 @@ namespace tools cryptonote::address_parse_info info; crypto::hash payment_id = crypto::null_hash; er.message = ""; - if(!get_account_address_from_str_or_url(info, m_wallet->testnet(), req.address, + if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address, [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { if (!dnssec_valid) { @@ -2218,7 +2219,7 @@ namespace tools } cryptonote::COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req); - daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); daemon_req.threads_count = req.threads_count; daemon_req.do_background_mining = req.do_background_mining; daemon_req.ignore_battery = req.ignore_battery; @@ -2534,7 +2535,7 @@ namespace tools try { res.multisig_info = m_wallet->make_multisig(req.password, req.multisig_info, req.threshold); - res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } catch (const std::exception &e) { @@ -2705,7 +2706,7 @@ namespace tools er.message = std::string("Error calling finalize_multisig: ") + e.what(); return false; } - res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); return true; } @@ -2882,6 +2883,14 @@ int main(int argc, char** argv) { std::unique_ptr<tools::wallet2> wal; try { + const bool testnet = tools::wallet2::has_testnet_option(*vm); + const bool stagenet = tools::wallet2::has_stagenet_option(*vm); + if (testnet && stagenet) + { + MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet")); + return 1; + } + const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file); const auto from_json = command_line::get_arg(*vm, arg_from_json); const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 82b6b78f9..e38cba5a5 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once +#include "cryptonote_config.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/subaddress_index.h" @@ -1262,7 +1263,7 @@ namespace wallet_rpc KV_SERIALIZE(pool); KV_SERIALIZE(filter_by_height); KV_SERIALIZE(min_height); - KV_SERIALIZE(max_height); + KV_SERIALIZE_OPT(max_height, (uint64_t)CRYPTONOTE_MAX_BLOCK_NUMBER); KV_SERIALIZE(account_index); KV_SERIALIZE(subaddr_indices); END_KV_SERIALIZE_MAP() |