diff options
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r-- | src/wallet/wallet2.cpp | 414 |
1 files changed, 256 insertions, 158 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 3bad743cf..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,6 +871,7 @@ 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 @@ -889,7 +879,7 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) for (index2.major = m_subaddress_labels.size(); index2.major < index.major + m_subaddress_lookahead_major; ++index2.major) { 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); + 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) { const crypto::public_key &D = pkeys[index2.minor]; @@ -905,7 +895,7 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) 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); + 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) { const crypto::public_key &D = pkeys[index2.minor - begin]; @@ -970,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 @@ -982,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; @@ -1019,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"); @@ -1028,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; @@ -1076,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"); @@ -1090,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(); @@ -1382,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); } @@ -2385,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()); @@ -2456,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; @@ -2482,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 @@ -2539,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()) { @@ -2555,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) { @@ -2642,11 +2634,12 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_confirm_export_overwrite = field_confirm_export_overwrite; 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, 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, 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 { @@ -2654,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; } @@ -2675,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()); } /*! @@ -2690,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; @@ -2725,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; } @@ -2803,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"); } @@ -2851,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()) @@ -2869,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"); } @@ -2896,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; @@ -2942,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"); } @@ -2988,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"); } @@ -3007,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, @@ -3070,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; @@ -3085,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"); } @@ -3185,7 +3236,7 @@ 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"); } @@ -3478,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) @@ -3502,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 @@ -3641,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); } @@ -3718,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); @@ -3995,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; @@ -4260,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); } @@ -4283,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(); @@ -4369,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; @@ -4493,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 @@ -4696,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 @@ -4866,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"); @@ -5275,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) { @@ -5499,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; }); } @@ -5612,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; @@ -5705,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; @@ -5769,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 @@ -5909,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 @@ -5954,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}); @@ -6124,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"); @@ -6139,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(); @@ -6151,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"); @@ -6170,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); @@ -6320,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"); @@ -6337,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"); @@ -6511,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 @@ -6662,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 @@ -6788,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; @@ -6819,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(); @@ -6888,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; @@ -6899,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; @@ -6969,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; @@ -7004,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(); @@ -7172,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(); @@ -7237,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"); @@ -7526,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"); @@ -7701,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); @@ -7741,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)); @@ -7748,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]; @@ -7770,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); @@ -8126,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"); @@ -8350,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; @@ -8500,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) @@ -8584,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, @@ -8784,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"); @@ -8801,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; @@ -8822,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; } @@ -8982,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; @@ -9338,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(); @@ -9402,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; @@ -9651,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); |