From 30c44bce0627260134d3606a9a6d0b453aad763d Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 15 Jan 2018 12:05:16 +0900 Subject: [PATCH] wallet: automatically use low priority if safe (no backlog & recent blocks not full) --- src/simplewallet/simplewallet.cpp | 39 +++++++++---- src/simplewallet/simplewallet.h | 1 + src/wallet/api/wallet.cpp | 6 +- src/wallet/api/wallet2_api.h | 1 + src/wallet/wallet2.cpp | 91 +++++++++++++++++++++++++++++++ src/wallet/wallet2.h | 4 ++ src/wallet/wallet_rpc_server.cpp | 12 ++-- 7 files changed, 136 insertions(+), 18 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 987c8b286..a6cef1bb9 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1519,6 +1519,19 @@ bool simple_wallet::set_refresh_from_block_height(const std::vector return true; } +bool simple_wallet::set_auto_low_priority(const std::vector &args/* = std::vector()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->auto_low_priority(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + bool simple_wallet::help(const std::vector &args/* = std::vector()*/) { if(args.empty()) @@ -1691,7 +1704,9 @@ simple_wallet::simple_wallet() "confirm-backlog-threshold [n]\n " " Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks.\n " "refresh-from-block-height [n]\n " - " Set the height before which to ignore blocks.")); + " Set the height before which to ignore blocks.\n " + "auto-low-priority <1|0>\n " + " Whether to automatically use the low priority fee level when it's safe to do so.")); m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display the encrypted Electrum-style mnemonic seed.")); @@ -1858,6 +1873,7 @@ bool simple_wallet::set_variable(const std::vector &args) success_msg_writer() << "confirm-backlog-threshold = " << m_wallet->get_confirm_backlog_threshold(); success_msg_writer() << "confirm-export-overwrite = " << m_wallet->confirm_export_overwrite(); success_msg_writer() << "refresh-from-block-height = " << m_wallet->get_refresh_from_block_height(); + success_msg_writer() << "auto-low-priority = " << m_wallet->auto_low_priority(); return true; } else @@ -1907,6 +1923,7 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("confirm-backlog-threshold", set_confirm_backlog_threshold, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("confirm-export-overwrite", set_confirm_export_overwrite, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("refresh-from-block-height", set_refresh_from_block_height, tr("block height")); + CHECK_SIMPLE_VARIABLE("auto-low-priority", set_auto_low_priority, tr("0 or 1")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; @@ -3667,6 +3684,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector 0 && parse_priority(local_args[0], priority)) local_args.erase(local_args.begin()); + priority = m_wallet->adjust_priority(priority); + size_t fake_outs_count = 0; if(local_args.size() > 0) { size_t ring_size; @@ -4144,6 +4163,8 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a if (local_args.size() > 0 && parse_priority(local_args[0], priority)) local_args.erase(local_args.begin()); + priority = m_wallet->adjust_priority(priority); + size_t fake_outs_count = 0; if(local_args.size() > 0) { size_t ring_size; @@ -4356,17 +4377,11 @@ bool simple_wallet::sweep_single(const std::vector &args_) std::vector local_args = args_; - int priority = 0; - if(local_args.size() > 0) { - auto priority_pos = std::find( - allowed_priority_strings.begin(), - allowed_priority_strings.end(), - local_args[0]); - if(priority_pos != allowed_priority_strings.end()) { - local_args.erase(local_args.begin()); - priority = std::distance(allowed_priority_strings.begin(), priority_pos); - } - } + uint32_t priority = 0; + if (local_args.size() > 0 && parse_priority(local_args[0], priority)) + local_args.erase(local_args.begin()); + + priority = m_wallet->adjust_priority(priority); size_t fake_outs_count = 0; if(local_args.size() > 0) { diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index f51b27e0e..f6df06ebb 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -130,6 +130,7 @@ namespace cryptonote bool set_confirm_backlog_threshold(const std::vector &args = std::vector()); bool set_confirm_export_overwrite(const std::vector &args = std::vector()); bool set_refresh_from_block_height(const std::vector &args = std::vector()); + bool set_auto_low_priority(const std::vector &args = std::vector()); bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index fcf5c8844..82948081e 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1074,6 +1074,8 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const if (fake_outs_count == 0) fake_outs_count = DEFAULT_MIXIN; + uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast(priority)); + PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); do { @@ -1133,7 +1135,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const de.is_subaddress = info.is_subaddress; dsts.push_back(de); transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, - static_cast(priority), + adjusted_priority, extra, subaddr_account, subaddr_indices, m_trustedDaemon); } else { // for the GUI, sweep_all (i.e. amount set as "(all)") will always sweep all the funds in all the addresses @@ -1143,7 +1145,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const subaddr_indices.insert(index); } transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, - static_cast(priority), + adjusted_priority, extra, subaddr_account, subaddr_indices, m_trustedDaemon); } diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 672b48775..a22788399 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -70,6 +70,7 @@ struct PendingTransaction }; enum Priority { + Priority_Default = 0, Priority_Low = 1, Priority_Medium = 2, Priority_High = 3, diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 589259ec8..7dc8a1e47 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -612,6 +612,7 @@ wallet2::wallet2(bool testnet, bool restricted): m_confirm_backlog(true), m_confirm_backlog_threshold(0), m_confirm_export_overwrite(true), + m_auto_low_priority(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), @@ -2447,6 +2448,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetInt(m_confirm_export_overwrite ? 1 :0); json.AddMember("confirm_export_overwrite", value2, json.GetAllocator()); + value2.SetInt(m_auto_low_priority ? 1 : 0); + json.AddMember("auto_low_priority", value2, json.GetAllocator()); + value2.SetInt(m_testnet ? 1 :0); json.AddMember("testnet", value2, json.GetAllocator()); @@ -2529,6 +2533,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_confirm_backlog = true; m_confirm_backlog_threshold = 0; m_confirm_export_overwrite = true; + m_auto_low_priority = true; } else if(json.IsObject()) { @@ -2630,6 +2635,8 @@ 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"); @@ -5033,6 +5040,90 @@ uint64_t wallet2::adjust_mixin(uint64_t mixin) const return mixin; } //---------------------------------------------------------------------------------------------------- +uint32_t wallet2::adjust_priority(uint32_t priority) +{ + if (priority == 0 && get_default_priority() != 1 && auto_low_priority()) + { + try + { + // check if there's a backlog in the tx pool + const double fee_level = get_fee_multiplier(1) * get_per_kb_fee() * (12/(double)13) / (double)1024; + const std::vector> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)}); + if (blocks.size() != 1) + { + MERROR("Bad estimated backlog array size"); + return priority; + } + else if (blocks[0].first > 0) + { + MINFO("We don't use the low priority because there's a backlog in the tx pool."); + return priority; + } + + // get the current full reward zone + epee::json_rpc::request getinfo_req = AUTO_VAL_INIT(getinfo_req); + epee::json_rpc::response getinfo_res = AUTO_VAL_INIT(getinfo_res); + m_daemon_rpc_mutex.lock(); + getinfo_req.jsonrpc = "2.0"; + getinfo_req.id = epee::serialization::storage_entry(0); + getinfo_req.method = "get_info"; + bool r = net_utils::invoke_http_json("/json_rpc", getinfo_req, getinfo_res, m_http_client); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info"); + THROW_WALLET_EXCEPTION_IF(getinfo_res.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info"); + THROW_WALLET_EXCEPTION_IF(getinfo_res.result.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); + const uint64_t full_reward_zone = getinfo_res.result.block_size_limit / 2; + + // get the last N block headers and sum the block sizes + const size_t N = 10; + if (m_blockchain.size() < N) + { + MERROR("The blockchain is too short"); + return priority; + } + epee::json_rpc::request getbh_req = AUTO_VAL_INIT(getbh_req); + epee::json_rpc::response getbh_res = AUTO_VAL_INIT(getbh_res); + m_daemon_rpc_mutex.lock(); + getbh_req.jsonrpc = "2.0"; + getbh_req.id = epee::serialization::storage_entry(0); + getbh_req.method = "getblockheadersrange"; + getbh_req.params.start_height = m_blockchain.size() - N; + getbh_req.params.end_height = m_blockchain.size() - 1; + r = net_utils::invoke_http_json("/json_rpc", getbh_req, getbh_res, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange"); + THROW_WALLET_EXCEPTION_IF(getbh_res.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange"); + THROW_WALLET_EXCEPTION_IF(getbh_res.result.status != CORE_RPC_STATUS_OK, error::get_blocks_error, getbh_res.result.status); + if (getbh_res.result.headers.size() != N) + { + MERROR("Bad blockheaders size"); + return priority; + } + size_t block_size_sum = 0; + for (const cryptonote::block_header_response &i : getbh_res.result.headers) + { + block_size_sum += i.block_size; + } + + // estimate how 'full' the last N blocks are + const size_t P = 100 * block_size_sum / (N * full_reward_zone); + MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str()); + if (P > 80) + { + MINFO("We don't use the low priority because recent blocks are quite full."); + return priority; + } + MINFO("We'll use the low priority because probably it's safe to do so."); + return 1; + } + catch (const std::exception &e) + { + MERROR(e.what()); + } + } + return priority; +} +//---------------------------------------------------------------------------------------------------- // separated the call(s) to wallet2::transfer into their own function // // this function will make multiple calls to wallet2::transfer if multiple diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 57ede86c2..f768581b2 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -838,6 +838,8 @@ namespace tools uint32_t get_confirm_backlog_threshold() const { return m_confirm_backlog_threshold; }; bool confirm_export_overwrite() const { return m_confirm_export_overwrite; } void confirm_export_overwrite(bool always) { m_confirm_export_overwrite = always; } + bool auto_low_priority() const { return m_auto_low_priority; } + void auto_low_priority(bool value) { m_auto_low_priority = value; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector &additional_tx_keys) const; void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); @@ -962,6 +964,7 @@ namespace tools uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const; uint64_t get_per_kb_fee() const; uint64_t adjust_mixin(uint64_t mixin) const; + uint32_t adjust_priority(uint32_t priority); // Light wallet specific functions // fetch unspent outs from lw node and store in m_transfers @@ -1122,6 +1125,7 @@ namespace tools bool m_confirm_backlog; uint32_t m_confirm_backlog_threshold; bool m_confirm_export_overwrite; + bool m_auto_low_priority; bool m_is_initialized; NodeRPCProxy m_node_rpc_proxy; std::unordered_set m_scanned_pool_txs[2]; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 7782e6952..97faf0b56 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -786,7 +786,8 @@ namespace tools try { uint64_t mixin = m_wallet->adjust_mixin(req.mixin); - std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + uint32_t priority = m_wallet->adjust_priority(req.priority); + std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); if (ptx_vector.empty()) { @@ -837,8 +838,9 @@ namespace tools try { uint64_t mixin = m_wallet->adjust_mixin(req.mixin); + uint32_t priority = m_wallet->adjust_priority(req.priority); LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); - std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay, @@ -903,7 +905,8 @@ namespace tools try { uint64_t mixin = m_wallet->adjust_mixin(req.mixin); - std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + uint32_t priority = m_wallet->adjust_priority(req.priority); + std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); @@ -950,7 +953,8 @@ namespace tools try { uint64_t mixin = m_wallet->adjust_mixin(req.mixin); - std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); + uint32_t priority = m_wallet->adjust_priority(req.priority); + std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, m_trusted_daemon); if (ptx_vector.empty()) {