diff options
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r-- | src/wallet/wallet2.cpp | 177 |
1 files changed, 95 insertions, 82 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b6c10c0e5..a153967ce 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -94,11 +94,12 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file, } } -uint64_t calculate_fee(const cryptonote::blobdata &blob) +uint64_t calculate_fee(const cryptonote::blobdata &blob, uint64_t fee_multiplier) { + THROW_WALLET_EXCEPTION_IF(fee_multiplier <= 0 || fee_multiplier > 3, tools::error::invalid_fee_multiplier); uint64_t bytes = blob.size(); uint64_t kB = (bytes + 1023) / 1024; - return kB * FEE_PER_KB; + return kB * FEE_PER_KB * fee_multiplier; } } //namespace @@ -1054,6 +1055,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p value2.SetUint(m_default_mixin); json.AddMember("default_mixin", value2, json.GetAllocator()); + value2.SetUint(m_default_fee_multiplier); + json.AddMember("default_fee_multiplier", value2, json.GetAllocator()); + value2.SetInt(m_auto_refresh ? 1 :0); json.AddMember("auto_refresh", value2, json.GetAllocator()); @@ -1125,6 +1129,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa m_watch_only = false; m_always_confirm_transfers = false; m_default_mixin = 0; + m_default_fee_multiplier = 0; m_auto_refresh = true; m_refresh_type = RefreshType::RefreshDefault; } @@ -1158,6 +1163,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa || (field_store_tx_info_found && (field_store_tx_info != 0)); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false); m_default_mixin = field_default_mixin_found ? field_default_mixin : 0; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_fee_multiplier, unsigned int, Uint, false); + m_default_fee_multiplier = field_default_fee_multiplier_found ? field_default_fee_multiplier : 0; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false); m_auto_refresh = !field_auto_refresh_found || (field_auto_refresh != 0); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_type, int, Int, false); @@ -1545,6 +1552,42 @@ void wallet2::check_genesis(const crypto::hash& genesis_hash) const { //---------------------------------------------------------------------------------------------------- void wallet2::store() { + store_to("", ""); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::store_to(const std::string &path, const std::string &password) +{ + // if file is the same, we do: + // 1. save wallet to the *.new file + // 2. remove old wallet file + // 3. rename *.new to wallet_name + + // handle if we want just store wallet state to current files (ex store() replacement); + bool same_file = true; + if (!path.empty()) + { + std::string canonical_path = boost::filesystem::canonical(m_wallet_file).string(); + size_t pos = canonical_path.find(path); + same_file = pos != std::string::npos; + } + + + if (!same_file) + { + // check if we want to store to directory which doesn't exists yet + boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path(); + + // if path is not exists, try to create it + if (!parent_path.empty() && !boost::filesystem::exists(parent_path)) + { + boost::system::error_code ec; + if (!boost::filesystem::create_directories(parent_path, ec)) + { + throw std::logic_error(ec.message()); + } + } + } + // preparing wallet data std::stringstream oss; boost::archive::binary_oarchive ar(oss); ar << *this; @@ -1559,10 +1602,10 @@ void wallet2::store() crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]); cache_file_data.cache_data = cipher; - // save to new file, rename main to old, rename new to main - // at all times, there should be a valid file on disk - const std::string new_file = m_wallet_file + ".new"; - const std::string old_file = m_wallet_file + ".old"; + const std::string new_file = same_file ? m_wallet_file + ".new" : path; + const std::string old_file = m_wallet_file; + const std::string old_keys_file = m_keys_file; + const std::string old_address_file = m_wallet_file + ".address.txt"; // save to new file std::ofstream ostr; @@ -1572,87 +1615,35 @@ void wallet2::store() ostr.close(); THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file); - // rename - boost::filesystem::remove(old_file); // probably does not exist - if (boost::filesystem::exists(m_wallet_file)) { - std::error_code e = tools::replace_file(m_wallet_file, old_file); - THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e); - } - std::error_code e = tools::replace_file(new_file, m_wallet_file); - THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e); - boost::filesystem::remove(old_file); -} - -void wallet2::store_to(const std::string &path, const std::string &password) -{ - // TODO: merge it with wallet2::store() function - - // check if we want to store to directory which doesn't exists yet - boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path(); - - // if path is not exists, try to create it - if (!parent_path.empty() && !boost::filesystem::exists(parent_path)) { - boost::system::error_code ec; - if (!boost::filesystem::create_directories(parent_path, ec)) { - throw std::logic_error(ec.message()); - } - } - - - std::stringstream oss; - boost::archive::binary_oarchive ar(oss); - ar << *this; - - wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>(); - cache_file_data.cache_data = oss.str(); - crypto::chacha8_key key; - generate_chacha8_key_from_secret_keys(key); - std::string cipher; - cipher.resize(cache_file_data.cache_data.size()); - cache_file_data.iv = crypto::rand<crypto::chacha8_iv>(); - crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]); - cache_file_data.cache_data = cipher; - - - const std::string new_file = path; - const std::string old_file = m_wallet_file; - const std::string old_keys_file = m_keys_file; - const std::string old_address_file = m_wallet_file + ".address.txt"; - - // save to new file - std::ofstream ostr; - ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); - binary_archive<true> oar(ostr); - bool success = ::serialization::serialize(oar, cache_file_data); - ostr.close(); - THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file); - - // save keys to the new file - // if we here, main wallet file is saved and we only need to save keys and address files + // save keys to the new file + // if we here, main wallet file is saved and we only need to save keys and address files + if (!same_file) { prepare_file_names(path); store_keys(m_keys_file, password, false); - // save address to the new file const std::string address_file = m_wallet_file + ".address.txt"; bool r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet)); THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file); - - // remove old wallet file r = boost::filesystem::remove(old_file); if (!r) { - LOG_ERROR("error removing file: " << old_file); + LOG_ERROR("error removing file: " << old_file); } // remove old keys file r = boost::filesystem::remove(old_keys_file); if (!r) { - LOG_ERROR("error removing file: " << old_keys_file); + LOG_ERROR("error removing file: " << old_keys_file); } // remove old address file r = boost::filesystem::remove(old_address_file); if (!r) { - LOG_ERROR("error removing file: " << old_address_file); + LOG_ERROR("error removing file: " << old_address_file); } + } else { + // here we have "*.new" file, we need to rename it to be without ".new" + std::error_code e = tools::replace_file(new_file, m_wallet_file); + THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e); + } } //---------------------------------------------------------------------------------------------------- uint64_t wallet2::unlocked_balance() const @@ -2081,15 +2072,29 @@ void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector) } } +uint64_t wallet2::sanitize_fee_multiplier(uint64_t fee_multiplier) const +{ + // 0, default value used for previous fee argument, defaults to normal fee + if (fee_multiplier == 0) + return m_default_fee_multiplier > 0 ? m_default_fee_multiplier : 1; + // 1 to 3 are allowed as multipliers + if (fee_multiplier >= 1 && fee_multiplier <= 3) + return fee_multiplier; + THROW_WALLET_EXCEPTION_IF (false, error::invalid_fee_multiplier); + return 1; +} + //---------------------------------------------------------------------------------------------------- // separated the call(s) to wallet2::transfer into their own function // // this function will make multiple calls to wallet2::transfer if multiple // transactions will be required -std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra, bool trusted_daemon) +std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon) { const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, trusted_daemon); + fee_multiplier = sanitize_fee_multiplier(fee_multiplier); + // failsafe split attempt counter size_t attempt_count = 0; @@ -2120,7 +2125,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto { transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx, trusted_daemon); auto txBlob = t_serializable_object_to_blob(ptx.tx); - needed_fee = calculate_fee(txBlob); + needed_fee = calculate_fee(txBlob, fee_multiplier); } while (ptx.fee < needed_fee); ptx_vector.push_back(ptx); @@ -2358,7 +2363,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent // This system allows for sending (almost) the entire balance, since it does // not generate spurious change in all txes, thus decreasing the instantaneous // usable balance. -std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra, bool trusted_daemon) +std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon) { std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_dust_indices; @@ -2388,6 +2393,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // throw if attempting a transaction with no destinations THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); + fee_multiplier = sanitize_fee_multiplier (fee_multiplier); + // calculate total amount being sent to all destinations // throw if total amount overflows uint64_t needed_money = 0; @@ -2499,7 +2506,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); - needed_fee = calculate_fee(txBlob); + needed_fee = calculate_fee(txBlob, fee_multiplier); available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0); LOG_PRINT_L2("Made a " << txBlob.size() << " kB tx, with " << print_money(available_for_fee) << " available for fee (" << print_money(needed_fee) << " needed)"); @@ -2582,7 +2589,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp return ptx_vector; } -std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra, bool trusted_daemon) +std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon) { std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_dust_indices; @@ -2598,6 +2605,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono uint64_t needed_fee, available_for_fee = 0; uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit(); + fee_multiplier = sanitize_fee_multiplier(fee_multiplier); + // gather all our dust and non dust outputs for (size_t i = 0; i < m_transfers.size(); ++i) { @@ -2626,7 +2635,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono // get a random unspent output and use it to pay next chunk. We try to alternate // dust and non dust to ensure we never get with only dust, from which we might // get a tx that can't pay for itself - size_t idx = unused_transfers_indices.empty() ? pop_random_value(unused_dust_indices) : unused_dust_indices.empty() ? pop_random_value(unused_transfers_indices) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > FEE_PER_KB * (upper_transaction_size_limit + 1023) / 1024) ? pop_random_value(unused_dust_indices) : pop_random_value(unused_transfers_indices); + size_t idx = unused_transfers_indices.empty() ? pop_random_value(unused_dust_indices) : unused_dust_indices.empty() ? pop_random_value(unused_transfers_indices) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > FEE_PER_KB * fee_multiplier * (upper_transaction_size_limit + 1023) / 1024) ? pop_random_value(unused_dust_indices) : pop_random_value(unused_transfers_indices); const transfer_details &td = m_transfers[idx]; LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount())); @@ -2654,7 +2663,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); - needed_fee = calculate_fee(txBlob); + needed_fee = calculate_fee(txBlob, fee_multiplier); available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount; LOG_PRINT_L2("Made a " << txBlob.size() << " kB tx, with " << print_money(available_for_fee) << " available for fee (" << print_money(needed_fee) << " needed)"); @@ -2667,7 +2676,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); txBlob = t_serializable_object_to_blob(test_ptx.tx); - needed_fee = calculate_fee(txBlob); + needed_fee = calculate_fee(txBlob, fee_multiplier); LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) << " fee and " << print_money(test_ptx.change_dts.amount) << " change"); } while (needed_fee > test_ptx.fee); @@ -2994,13 +3003,13 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo { transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx); auto txBlob = t_serializable_object_to_blob(ptx.tx); - needed_fee = calculate_fee(txBlob); + needed_fee = calculate_fee(txBlob, 1); // reroll the tx with the actual amount minus the fee // if there's not enough for the fee, it'll throw transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx); txBlob = t_serializable_object_to_blob(ptx.tx); - needed_fee = calculate_fee(txBlob); + needed_fee = calculate_fee(txBlob, 1); } while (ptx.fee < needed_fee); ptx_vector.push_back(ptx); @@ -3072,12 +3081,17 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) c std::string wallet2::get_wallet_file() const { - return m_wallet_file; + return m_wallet_file; } std::string wallet2::get_keys_file() const { - return m_keys_file; + return m_keys_file; +} + +std::string wallet2::get_daemon_address() const +{ + return m_daemon_address; } void wallet2::set_tx_note(const crypto::hash &txid, const std::string ¬e) @@ -3092,7 +3106,6 @@ std::string wallet2::get_tx_note(const crypto::hash &txid) const return std::string(); return i->second; } - //---------------------------------------------------------------------------------------------------- void wallet2::generate_genesis(cryptonote::block& b) { if (m_testnet) |