diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/wallet/node_rpc_proxy.cpp | 72 | ||||
-rw-r--r-- | src/wallet/node_rpc_proxy.h | 7 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 171 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 6 | ||||
-rw-r--r-- | src/wallet/wallet_errors.h | 11 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 8 |
7 files changed, 170 insertions, 106 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index a5a4c7f56..a16f4fe19 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -85,7 +85,6 @@ monero_add_executable(wallet_rpc_server target_link_libraries(wallet_rpc_server PRIVATE wallet - epee rpc_base cryptonote_core cncrypto diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index c5d869354..401ada61b 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -41,21 +41,13 @@ static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::c NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex) : m_http_client(http_client) , m_daemon_rpc_mutex(mutex) - , m_height(0) - , m_height_time(0) - , m_earliest_height() - , m_dynamic_per_kb_fee_estimate(0) - , m_dynamic_per_kb_fee_estimate_cached_height(0) - , m_dynamic_per_kb_fee_estimate_grace_blocks(0) - , m_rpc_version(0) - , m_target_height(0) - , m_target_height_time(0) -{} +{ + invalidate(); +} void NodeRPCProxy::invalidate() { m_height = 0; - m_height_time = 0; for (size_t n = 0; n < 256; ++n) m_earliest_height[n] = 0; m_dynamic_per_kb_fee_estimate = 0; @@ -63,7 +55,8 @@ void NodeRPCProxy::invalidate() m_dynamic_per_kb_fee_estimate_grace_blocks = 0; m_rpc_version = 0; m_target_height = 0; - m_target_height_time = 0; + m_block_size_limit = 0; + m_get_info_time = 0; } boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const @@ -84,36 +77,15 @@ boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version return boost::optional<std::string>(); } -boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) const -{ - const time_t now = time(NULL); - if (m_height == 0 || now >= m_height_time + 30) // re-cache every 30 seconds - { - cryptonote::COMMAND_RPC_GET_HEIGHT::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_HEIGHT::response res = AUTO_VAL_INIT(res); - - m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client, rpc_timeout); - m_daemon_rpc_mutex.unlock(); - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); - CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, res.status, "Failed to connect to daemon"); - CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, res.status, "Failed to get current blockchain height"); - m_height = res.height; - m_height_time = now; - } - height = m_height; - return boost::optional<std::string>(); -} - void NodeRPCProxy::set_height(uint64_t h) { m_height = h; } -boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) const +boost::optional<std::string> NodeRPCProxy::get_info() const { const time_t now = time(NULL); - if (m_target_height == 0 || now >= m_target_height_time + 30) // re-cache every 30 seconds + if (now >= m_get_info_time + 30) // re-cache every 30 seconds { cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t); @@ -125,13 +97,41 @@ boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) c CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height"); + m_height = resp_t.height; m_target_height = resp_t.target_height; - m_target_height_time = now; + m_block_size_limit = resp_t.block_size_limit; + m_get_info_time = now; } + return boost::optional<std::string>(); +} + +boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) const +{ + auto res = get_info(); + if (res) + return res; + height = m_height; + return boost::optional<std::string>(); +} + +boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) const +{ + auto res = get_info(); + if (res) + return res; height = m_target_height; return boost::optional<std::string>(); } +boost::optional<std::string> NodeRPCProxy::get_block_size_limit(uint64_t &block_size_limit) const +{ + auto res = get_info(); + if (res) + return res; + block_size_limit = m_block_size_limit; + return boost::optional<std::string>(); +} + boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height) const { if (m_earliest_height[version] == 0) diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index 1b183212d..8a65884f7 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -47,22 +47,25 @@ public: boost::optional<std::string> get_height(uint64_t &height) const; void set_height(uint64_t h); boost::optional<std::string> get_target_height(uint64_t &height) const; + boost::optional<std::string> get_block_size_limit(uint64_t &block_size_limit) const; boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height) const; boost::optional<std::string> get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const; private: + boost::optional<std::string> get_info() const; + epee::net_utils::http::http_simple_client &m_http_client; boost::mutex &m_daemon_rpc_mutex; mutable uint64_t m_height; - mutable time_t m_height_time; mutable uint64_t m_earliest_height[256]; mutable uint64_t m_dynamic_per_kb_fee_estimate; mutable uint64_t m_dynamic_per_kb_fee_estimate_cached_height; mutable uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; mutable uint32_t m_rpc_version; mutable uint64_t m_target_height; - mutable time_t m_target_height_time; + mutable uint64_t m_block_size_limit; + mutable time_t m_get_info_time; }; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7433cb9a5..9e50f3229 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -676,6 +676,7 @@ wallet2::wallet2(network_type nettype, bool restricted): m_segregate_pre_fork_outputs(true), m_key_reuse_mitigation2(true), m_segregation_height(0), + m_ignore_fractional_outputs(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), @@ -950,6 +951,7 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) } m_subaddress_labels.resize(index.major + 1, {"Untitled account"}); m_subaddress_labels[index.major].resize(index.minor + 1); + get_account_tags(); } else if (m_subaddress_labels[index.major].size() <= index.minor) { @@ -1056,6 +1058,16 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio tx_scan_info.error = false; } //---------------------------------------------------------------------------------------------------- +void wallet2::check_acc_out_precomp_once(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const +{ + tx_scan_info.received = boost::none; + if (already_seen) + return; + check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info); + if (tx_scan_info.received) + already_seen = true; +} +//---------------------------------------------------------------------------------------------------- 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; @@ -1172,6 +1184,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // Don't try to extract tx public key if tx has no ouputs size_t pk_index = 0; std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size()); + std::deque<bool> output_found(tx.vout.size(), false); while (!tx.vout.empty()) { // if tx.vout is not empty, we loop through all tx pubkeys @@ -1216,16 +1229,19 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote memcpy(&derivation, rct::identity().bytes, sizeof(derivation)); } - // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses - if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) + if (pk_index == 1) { - for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) + // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses + if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) { - additional_derivations.push_back({}); - if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back())) + for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) { - MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid << ", skipping"); - memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation)); + additional_derivations.push_back({}); + if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back())) + { + MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid << ", skipping"); + memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation)); + } } } } @@ -1236,10 +1252,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote error::wallet_internal_error, "pk_index out of range of tx_cache_data"); is_out_data_ptr = &tx_cache_data.primary[pk_index - 1]; derivation = tx_cache_data.primary[pk_index - 1].derivation; - for (size_t n = 0; n < tx_cache_data.additional.size(); ++n) + if (pk_index == 1) { - additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey); - additional_derivations.push_back(tx_cache_data.additional[n].derivation); + for (size_t n = 0; n < tx_cache_data.additional.size(); ++n) + { + additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey); + additional_derivations.push_back(tx_cache_data.additional[n].derivation); + } } } @@ -1249,7 +1268,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) { - check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0]); + check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0], output_found[0]); THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); // this assumes that the miner tx pays a single address @@ -1259,8 +1278,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // the first one was already checked for (size_t i = 1; i < tx.vout.size(); ++i) { - tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true); + tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, + std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true); } waiter.wait(&tpool); // then scan all outputs from 0 @@ -1282,8 +1301,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { for (size_t i = 0; i < tx.vout.size(); ++i) { - tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true); + tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, + std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true); } waiter.wait(&tpool); @@ -1304,7 +1323,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { for (size_t i = 0; i < tx.vout.size(); ++i) { - check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i]); + check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i], output_found[i]); THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if (tx_scan_info[i].received) { @@ -1805,7 +1824,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry blocks_added = 0; THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch"); - THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::wallet_internal_error, "Index out of bounds of hashchain"); + THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error); tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; @@ -2037,8 +2056,8 @@ void wallet2::update_pool_state(bool refreshed) MDEBUG("update_pool_state start"); // get the pool state - cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request req; - cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response res; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res; m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); @@ -2251,12 +2270,12 @@ void wallet2::update_pool_state(bool refreshed) MDEBUG("update_pool_state end"); } //---------------------------------------------------------------------------------------------------- -void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history) +void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force) { std::vector<crypto::hash> hashes; const uint64_t checkpoint_height = m_checkpoints.get_max_height(); - if (stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) + if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force) { // we will drop all these, so don't bother getting them uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size(); @@ -2421,6 +2440,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo std::vector<cryptonote::block_complete_entry> next_blocks; std::vector<parsed_block> next_parsed_blocks; bool error = false; + added_blocks = 0; if (!first && blocks.empty()) { refreshed = false; @@ -2430,7 +2450,33 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo if (!first) { - process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks); + try + { + process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks); + } + catch (const tools::error::out_of_hashchain_bounds_error&) + { + MINFO("Daemon claims next refresh block is out of hash chain bounds, resetting hash chain"); + uint64_t stop_height = m_blockchain.offset(); + std::vector<crypto::hash> tip(m_blockchain.size() - m_blockchain.offset()); + for (size_t i = m_blockchain.offset(); i < m_blockchain.size(); ++i) + tip[i - m_blockchain.offset()] = m_blockchain[i]; + cryptonote::block b; + generate_genesis(b); + m_blockchain.clear(); + m_blockchain.push_back(get_block_hash(b)); + short_chain_history.clear(); + get_short_chain_history(short_chain_history); + fast_refresh(stop_height, blocks_start_height, short_chain_history, true); + THROW_WALLET_EXCEPTION_IF(m_blockchain.size() != stop_height, error::wallet_internal_error, "Unexpected hashchain size"); + THROW_WALLET_EXCEPTION_IF(m_blockchain.offset() != 0, error::wallet_internal_error, "Unexpected hashchain offset"); + for (const auto &h: tip) + m_blockchain.push_back(h); + short_chain_history.clear(); + get_short_chain_history(short_chain_history); + start_height = stop_height; + throw std::runtime_error(""); // loop again + } blocks_fetched += added_blocks; } waiter.wait(&tpool); @@ -2460,6 +2506,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo if(try_count < 3) { LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); + first = true; ++try_count; } else @@ -2782,6 +2829,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetUint(m_segregation_height); json.AddMember("segregation_height", value2, json.GetAllocator()); + value2.SetInt(m_ignore_fractional_outputs ? 1 : 0); + json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator()); + value2.SetUint(m_subaddress_lookahead_major); json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator()); @@ -2864,6 +2914,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_segregate_pre_fork_outputs = true; m_key_reuse_mitigation2 = true; m_segregation_height = 0; + m_ignore_fractional_outputs = true; m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_key_on_device = false; @@ -2990,6 +3041,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_key_reuse_mitigation2 = field_key_reuse_mitigation2; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregation_height, int, Uint, false, 0); m_segregation_height = field_segregation_height; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true); + m_ignore_fractional_outputs = field_ignore_fractional_outputs; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR); m_subaddress_lookahead_major = field_subaddress_lookahead_major; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR); @@ -5051,7 +5104,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f return false; } - if (!epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + ciphertext)) + if (!epee::file_io_utils::save_string_to_file(signed_filename, ciphertext)) { LOG_PRINT_L0("Failed to save file to " << signed_filename); return false; @@ -5604,15 +5657,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority) } // get the current full reward zone - cryptonote::COMMAND_RPC_GET_INFO::request getinfo_req = AUTO_VAL_INIT(getinfo_req); - cryptonote::COMMAND_RPC_GET_INFO::response getinfo_res = AUTO_VAL_INIT(getinfo_res); - m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", 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.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info"); - THROW_WALLET_EXCEPTION_IF(getinfo_res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); - const uint64_t full_reward_zone = getinfo_res.block_size_limit / 2; + uint64_t block_size_limit = 0; + const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); + throw_on_rpc_response_error(result, "get_info"); + const uint64_t full_reward_zone = block_size_limit / 2; // get the last N block headers and sum the block sizes const size_t N = 10; @@ -5626,7 +5674,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority) m_daemon_rpc_mutex.lock(); getbh_req.start_height = m_blockchain.size() - N; getbh_req.end_height = m_blockchain.size() - 1; - r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout); + bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", 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.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange"); @@ -7704,12 +7752,24 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp for (uint32_t i : subaddr_indices) LOG_PRINT_L2("Candidate subaddress index for spending: " << i); + // determine threshold for fractional amount + const size_t tx_size_one_ring = estimate_tx_size(use_rct, 1, fake_outs_count, 2, 0, bulletproof); + const size_t tx_size_two_rings = estimate_tx_size(use_rct, 2, fake_outs_count, 2, 0, bulletproof); + THROW_WALLET_EXCEPTION_IF(tx_size_one_ring > tx_size_two_rings, error::wallet_internal_error, "Estimated tx size with 1 input is larger than with 2 inputs!"); + const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring; + const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024; + // gather all dust and non-dust outputs belonging to specified subaddresses size_t num_nondust_outputs = 0; size_t num_dust_outputs = 0; for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; + if (m_ignore_fractional_outputs && td.amount() < fractional_threshold) + { + MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold)); + continue; + } if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { const uint32_t index_minor = td.m_subaddr_index.minor; @@ -9415,31 +9475,15 @@ uint64_t wallet2::get_daemon_blockchain_height(string &err) const uint64_t wallet2::get_daemon_blockchain_target_height(string &err) { - cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t); - cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t); - m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client); - m_daemon_rpc_mutex.unlock(); - if (ok) - { - if (resp_t.status == CORE_RPC_STATUS_BUSY) - { - err = "daemon is busy. Please try again later."; - } - else if (resp_t.status != CORE_RPC_STATUS_OK) - { - err = resp_t.status; - } - else // success, cleaning up error message - { - err = ""; - } - } - else + err = ""; + uint64_t target_height = 0; + const auto result = m_node_rpc_proxy.get_target_height(target_height); + if (result && *result != CORE_RPC_STATUS_OK) { - err = "possibly lost connection to daemon"; + err= *result; + return 0; } - return resp_t.target_height; + return target_height; } uint64_t wallet2::get_approximate_blockchain_height() const @@ -9857,7 +9901,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input. std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx // was created by sweep_all, so we can't know the spent height and other detailed info. - for(size_t i = 0; i < m_transfers.size(); ++i) + for(size_t i = 0; i < signed_key_images.size(); ++i) { transfer_details &td = m_transfers[i]; uint64_t amount = td.amount(); @@ -10797,15 +10841,10 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std:: THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); - cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t); - cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t); - m_daemon_rpc_mutex.lock(); - r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info"); - THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info"); - THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); - uint64_t full_reward_zone = resp_t.block_size_limit / 2; + uint64_t block_size_limit = 0; + const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); + throw_on_rpc_response_error(result, "get_info"); + uint64_t full_reward_zone = block_size_limit / 2; std::vector<std::pair<uint64_t, uint64_t>> blocks; for (const auto &fee_level: fee_levels) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c54587693..220eca8a8 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -925,6 +925,8 @@ namespace tools void key_reuse_mitigation2(bool value) { m_key_reuse_mitigation2 = value; } uint64_t segregation_height() const { return m_segregation_height; } void segregation_height(uint64_t height) { m_segregation_height = height; } + bool ignore_fractional_outputs() const { return m_ignore_fractional_outputs; } + void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; } bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; } void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; } @@ -1170,7 +1172,7 @@ namespace tools bool clear(); void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes); - void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history); + void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false); void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error); void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers, bool trusted_daemon) const; @@ -1184,6 +1186,7 @@ namespace tools crypto::hash get_payment_id(const pending_tx &ptx) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const; + void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; uint64_t get_upper_transaction_size_limit() const; std::vector<uint64_t> get_unspent_amounts_vector() const; @@ -1286,6 +1289,7 @@ namespace tools bool m_segregate_pre_fork_outputs; bool m_key_reuse_mitigation2; uint64_t m_segregation_height; + bool m_ignore_fractional_outputs; bool m_is_initialized; NodeRPCProxy m_node_rpc_proxy; std::unordered_set<crypto::hash> m_scanned_pool_txs[2]; diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 214d51cde..e80652750 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -70,6 +70,7 @@ namespace tools // get_out_indexes_error // tx_parse_error // get_tx_pool_error + // out_of_hashchain_bounds_error // transfer_error * // get_random_outs_general_error // not_enough_unlocked_money @@ -398,6 +399,16 @@ namespace tools std::string to_string() const { return refresh_error::to_string(); } }; //---------------------------------------------------------------------------------------------------- + struct out_of_hashchain_bounds_error : public refresh_error + { + explicit out_of_hashchain_bounds_error(std::string&& loc) + : refresh_error(std::move(loc), "Index out of bounds of of hashchain") + { + } + + std::string to_string() const { return refresh_error::to_string(); } + }; + //---------------------------------------------------------------------------------------------------- struct transfer_error : public wallet_logic_error { protected: diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 77246c42c..b9cf99635 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -381,6 +381,7 @@ namespace tools if (!m_wallet) return not_open(er); try { + THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound); res.addresses.clear(); std::vector<uint32_t> req_address_index; if (req.address_index.empty()) @@ -396,6 +397,7 @@ namespace tools m_wallet->get_transfers(transfers); for (uint32_t i : req_address_index) { + THROW_WALLET_EXCEPTION_IF(i >= m_wallet->get_num_subaddresses(req.account_index), error::address_index_outofbound); res.addresses.resize(res.addresses.size() + 1); auto& info = res.addresses.back(); const cryptonote::subaddress_index index = {req.account_index, i}; @@ -519,6 +521,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags(); for (const std::pair<std::string, std::string>& p : account_tags.first) { @@ -537,6 +540,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { m_wallet->set_account_tag(req.accounts, req.tag); @@ -551,6 +555,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { m_wallet->set_account_tag(req.accounts, ""); @@ -565,6 +570,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { m_wallet->set_account_tag_description(req.tag, req.description); @@ -2283,6 +2289,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); std::string error; std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error); if (uri.empty()) @@ -2496,6 +2503,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req; cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res; bool r = m_wallet->invoke_http_json("/stop_mining", daemon_req, daemon_res); |