aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp293
1 files changed, 203 insertions, 90 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 56eee6ecf..dce1ee9de 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -452,10 +452,10 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.restricted);
}
-boost::optional<password_container> wallet2::password_prompt(const bool is_new_wallet)
+boost::optional<password_container> wallet2::password_prompt(const bool new_password)
{
auto pwd_container = tools::password_container::prompt(
- is_new_wallet, (is_new_wallet ? tr("Enter a password for your new wallet") : tr("Wallet password"))
+ new_password, (new_password ? tr("Enter new wallet password") : tr("Wallet password"))
);
if (!pwd_container)
{
@@ -633,28 +633,13 @@ bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& a
return true;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
+void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
{
- class lazy_txid_getter
- {
- const cryptonote::transaction &tx;
- crypto::hash lazy_txid;
- bool computed;
- public:
- lazy_txid_getter(const transaction &tx): tx(tx), computed(false) {}
- const crypto::hash &operator()()
- {
- if (!computed)
- {
- lazy_txid = cryptonote::get_transaction_hash(tx);
- computed = true;
- }
- return lazy_txid;
- }
- } txid(tx);
+ // In this function, tx (probably) only contains the base information
+ // (that is, the prunable stuff may or may not be included)
if (!miner_tx)
- process_unconfirmed(tx, height);
+ process_unconfirmed(txid, tx, height);
std::vector<size_t> outs;
uint64_t tx_money_got_in_outs = 0;
crypto::public_key tx_pub_key = null_pkey;
@@ -663,7 +648,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
if(!parse_tx_extra(tx.extra, tx_extra_fields))
{
// Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
- LOG_PRINT_L0("Transaction extra has unsupported format: " << txid());
+ LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
}
// Don't try to extract tx public key if tx has no ouputs
@@ -677,9 +662,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
{
if (pk_index > 1)
break;
- LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid());
+ LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid);
if(0 != m_callback)
- m_callback->on_skip_transaction(height, tx);
+ m_callback->on_skip_transaction(height, txid, tx);
return;
}
@@ -879,7 +864,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
- td.m_txid = txid();
+ td.m_txid = txid;
td.m_key_image = ki[o];
td.m_key_image_known = !m_watch_only;
td.m_amount = tx.vout[o].amount;
@@ -903,9 +888,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
set_unspent(m_transfers.size()-1);
m_key_images[td.m_key_image] = m_transfers.size()-1;
m_pub_keys[in_ephemeral[o].pub] = m_transfers.size()-1;
- LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid());
+ LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
- m_callback->on_money_received(height, tx, td.m_amount);
+ m_callback->on_money_received(height, txid, tx, td.m_amount);
}
}
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
@@ -930,7 +915,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
- td.m_txid = txid();
+ td.m_txid = txid;
td.m_amount = tx.vout[o].amount;
td.m_pk_index = pk_index - 1;
if (td.m_amount == 0)
@@ -952,9 +937,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
THROW_WALLET_EXCEPTION_IF(td.get_public_key() != in_ephemeral[o].pub, error::wallet_internal_error, "Inconsistent public keys");
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
- LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid());
+ LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
- m_callback->on_money_received(height, tx, td.m_amount);
+ m_callback->on_money_received(height, txid, tx, td.m_amount);
}
}
}
@@ -982,17 +967,17 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
tx_money_spent_in_ins += amount;
if (!pool)
{
- LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid());
+ LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid);
set_spent(it->second, height);
if (0 != m_callback)
- m_callback->on_money_spent(height, tx, amount, tx);
+ m_callback->on_money_spent(height, txid, tx, amount, tx);
}
}
}
if (tx_money_spent_in_ins > 0)
{
- process_outgoing(tx, height, ts, tx_money_spent_in_ins, tx_money_got_in_outs);
+ process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, tx_money_got_in_outs);
}
uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0;
@@ -1038,7 +1023,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
payment_details payment;
- payment.m_tx_hash = txid();
+ payment.m_tx_hash = txid;
payment.m_amount = received;
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
@@ -1046,7 +1031,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
if (pool) {
m_unconfirmed_payments.emplace(payment_id, payment);
if (0 != m_callback)
- m_callback->on_unconfirmed_money_received(height, tx, payment.m_amount);
+ m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount);
}
else
m_payments.emplace(payment_id, payment);
@@ -1054,12 +1039,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t height)
+void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height)
{
if (m_unconfirmed_txs.empty())
return;
- crypto::hash txid = get_transaction_hash(tx);
auto unconf_it = m_unconfirmed_txs.find(txid);
if(unconf_it != m_unconfirmed_txs.end()) {
if (store_tx_info()) {
@@ -1075,9 +1059,8 @@ void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t he
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_outgoing(const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received)
+void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received)
{
- crypto::hash txid = get_transaction_hash(tx);
std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details()));
// fill with the info we know, some info might already be there
if (entry.second)
@@ -1120,16 +1103,19 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
{
TIME_MEASURE_START(miner_tx_handle_time);
- process_new_transaction(b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false);
+ process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false);
TIME_MEASURE_FINISH(miner_tx_handle_time);
TIME_MEASURE_START(txs_handle_time);
- for(auto& txblob: bche.txs)
+ THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
+ size_t idx = 0;
+ for (const auto& txblob: bche.txs)
{
cryptonote::transaction tx;
- bool r = parse_and_validate_tx_from_blob(txblob, tx);
+ bool r = parse_and_validate_tx_base_from_blob(txblob, tx);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
- process_new_transaction(tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false);
+ process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false);
+ ++idx;
}
TIME_MEASURE_FINISH(txs_handle_time);
LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
@@ -1185,6 +1171,34 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
req.block_ids = short_chain_history;
+ uint32_t rpc_version;
+ boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
+ // no error
+ if (!!result)
+ {
+ // empty string -> not connection
+ THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion");
+ THROW_WALLET_EXCEPTION_IF(*result == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "getversion");
+ if (*result != CORE_RPC_STATUS_OK)
+ {
+ MDEBUG("Cannot determined daemon RPC version, not asking for pruned blocks");
+ req.prune = false; // old daemon
+ }
+ }
+ else
+ {
+ if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 7))
+ {
+ MDEBUG("Daemon is recent enough, asking for pruned blocks");
+ req.prune = true;
+ }
+ else
+ {
+ MDEBUG("Daemon is too old, not asking for pruned blocks");
+ req.prune = false;
+ }
+ }
+
req.start_height = start_height;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout);
@@ -1367,6 +1381,8 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
//----------------------------------------------------------------------------------------------------
void wallet2::update_pool_state()
{
+ MDEBUG("update_pool_state start");
+
// get the pool state
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res;
@@ -1376,6 +1392,7 @@ void wallet2::update_pool_state()
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
+ MDEBUG("update_pool_state got pool");
// remove any pending tx that's not in the pool
std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin();
@@ -1430,6 +1447,7 @@ void wallet2::update_pool_state()
}
}
}
+ MDEBUG("update_pool_state done first loop");
// remove pool txes to us that aren't in the pool anymore
std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
@@ -1451,6 +1469,7 @@ void wallet2::update_pool_state()
m_unconfirmed_payments.erase(pit);
}
}
+ MDEBUG("update_pool_state done second loop");
// add new pool txes to us
for (auto it: res.transactions)
@@ -1459,6 +1478,11 @@ void wallet2::update_pool_state()
if(epee::string_tools::parse_hexstr_to_binbuff(it.id_hash, txid_data) && txid_data.size() == sizeof(crypto::hash))
{
const crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
+ if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
+ {
+ LOG_PRINT_L2("Already seen " << txid << ", skipped");
+ continue;
+ }
if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end())
{
LOG_PRINT_L1("Found new pool tx: " << txid);
@@ -1477,9 +1501,11 @@ void wallet2::update_pool_state()
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
req.txs_hashes.push_back(it.id_hash);
+ MDEBUG("asking for " << it.id_hash);
req.decode_as_json = false;
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ MDEBUG("asked for " << it.id_hash << ", got " << r << " and " << res.status);
m_daemon_rpc_mutex.unlock();
if (r && res.status == CORE_RPC_STATUS_OK)
{
@@ -1497,7 +1523,13 @@ void wallet2::update_pool_state()
{
if (tx_hash == txid)
{
- process_new_transaction(tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
+ process_new_transaction(txid, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
+ m_scanned_pool_txs[0].insert(txid);
+ if (m_scanned_pool_txs[0].size() > 5000)
+ {
+ std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
+ m_scanned_pool_txs[0].clear();
+ }
}
else
{
@@ -1536,7 +1568,7 @@ void wallet2::update_pool_state()
}
else
{
- LOG_PRINT_L1("Already saw that one");
+ LOG_PRINT_L1("Already saw that one, it's for us");
}
}
else
@@ -1544,6 +1576,7 @@ void wallet2::update_pool_state()
LOG_PRINT_L0("Failed to parse txid");
}
}
+ 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)
@@ -1880,6 +1913,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p
value2.SetInt(m_ask_password ? 1 :0);
json.AddMember("ask_password", value2, json.GetAllocator());
+ value2.SetInt(cryptonote::get_default_decimal_point());
+ json.AddMember("default_decimal_point", value2, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -2010,6 +2046,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
m_confirm_missing_payment_id = field_confirm_missing_payment_id;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true);
m_ask_password = field_ask_password;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
+ cryptonote::set_default_decimal_point(field_default_decimal_point);
}
const cryptonote::account_keys& keys = m_account.get_keys();
@@ -2274,6 +2312,16 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash&
return false;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::set_default_decimal_point(unsigned int decimal_point)
+{
+ cryptonote::set_default_decimal_point(decimal_point);
+}
+//----------------------------------------------------------------------------------------------------
+unsigned int wallet2::get_default_decimal_point() const
+{
+ return cryptonote::get_default_decimal_point();
+}
+//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_file_names(const std::string& file_path)
{
do_prepare_file_names(file_path, m_keys_file, m_wallet_file);
@@ -2286,6 +2334,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout)
if(!m_http_client.is_connected())
{
+ m_node_rpc_proxy.invalidate();
if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
return false;
}
@@ -2298,7 +2347,11 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout)
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "get_version";
bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
- if (!r || resp_t.result.status != CORE_RPC_STATUS_OK)
+ if(!r) {
+ *version = 0;
+ return false;
+ }
+ if (resp_t.result.status != CORE_RPC_STATUS_OK)
*version = 0;
else
*version = resp_t.result.version;
@@ -2605,28 +2658,31 @@ void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2:
//----------------------------------------------------------------------------------------------------
void wallet2::rescan_spent()
{
- std::vector<std::string> key_images;
-
- // make a list of key images for all our outputs
- for (size_t i = 0; i < m_transfers.size(); ++i)
- {
- const transfer_details& td = m_transfers[i];
- key_images.push_back(string_tools::pod_to_hex(td.m_key_image));
+ // This is RPC call that can take a long time if there are many outputs,
+ // so we call it several times, in stripes, so we don't time out spuriously
+ std::vector<int> spent_status;
+ spent_status.reserve(m_transfers.size());
+ const size_t chunk_size = 1000;
+ for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
+ {
+ const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset);
+ MDEBUG("Calling is_key_image_spent on " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size());
+ COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req);
+ COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
+ for (size_t n = start_offset; n < start_offset + n_outputs; ++n)
+ req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image));
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error,
+ "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
+ std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs));
+ std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
}
- COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req);
- COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
- req.key_images = key_images;
- m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
- THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != key_images.size(), error::wallet_internal_error,
- "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
- std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(key_images.size()));
-
// update spent status
for (size_t i = 0; i < m_transfers.size(); ++i)
{
@@ -2634,7 +2690,7 @@ void wallet2::rescan_spent()
// a view wallet may not know about key images
if (!td.m_key_image_known)
continue;
- if (td.m_spent != (daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT))
+ if (td.m_spent != (spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT))
{
if (td.m_spent)
{
@@ -3286,20 +3342,35 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
return true;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_fee_multiplier(uint32_t priority, bool use_new_fee) const
+uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
{
static const uint64_t old_multipliers[3] = {1, 2, 3};
static const uint64_t new_multipliers[3] = {1, 20, 166};
+ static const uint64_t newer_multipliers[4] = {1, 4, 20, 166};
- // 0 -> default (here, x1)
+ // 0 -> default (here, x1 till fee algorithm 2, x4 from it)
if (priority == 0)
priority = m_default_priority;
if (priority == 0)
- priority = 1;
+ {
+ if (fee_algorithm >= 2)
+ priority = 2;
+ else
+ priority = 1;
+ }
- // 1 to 3 are allowed as priorities
- if (priority >= 1 && priority <= 3)
- return (use_new_fee ? new_multipliers : old_multipliers)[priority-1];
+ // 1 to 3/4 are allowed as priorities
+ uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3;
+ if (priority >= 1 && priority <= max_priority)
+ {
+ switch (fee_algorithm)
+ {
+ case 0: return old_multipliers[priority-1];
+ case 1: return new_multipliers[priority-1];
+ case 2: return newer_multipliers[priority-1];
+ default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority);
+ }
+ }
THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority);
return 1;
@@ -3324,6 +3395,16 @@ uint64_t wallet2::get_per_kb_fee()
return get_dynamic_per_kb_fee_estimate();
}
//----------------------------------------------------------------------------------------------------
+int wallet2::get_fee_algorithm()
+{
+ // changes at v3 and v5
+ if (use_fork_rules(5, 0))
+ return 2;
+ if (use_fork_rules(3, -720 * 14))
+ return 1;
+ return 0;
+}
+//----------------------------------------------------------------------------------------------------
// separated the call(s) to wallet2::transfer into their own function
//
// this function will make multiple calls to wallet2::transfer if multiple
@@ -3332,9 +3413,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{
const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true, trusted_daemon);
- const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = get_per_kb_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
+ const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
// failsafe split attempt counter
size_t attempt_count = 0;
@@ -3453,7 +3533,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
req_t.params.amounts.resize(std::distance(req_t.params.amounts.begin(), end));
req_t.params.unlocked = true;
req_t.params.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
- bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
@@ -4059,7 +4139,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money) co
return picks;
}
-static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices)
+bool wallet2::should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const
{
if (!use_rct)
return false;
@@ -4067,9 +4147,43 @@ static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const
return false;
if (unused_dust_indices.empty() && unused_transfers_indices.empty())
return false;
+ // we want at least one free rct output to avoid a corner case where
+ // we'd choose a non rct output which doesn't have enough "siblings"
+ // value-wise on the chain, and thus can't be mixed
+ bool found = false;
+ for (auto i: unused_dust_indices)
+ {
+ if (m_transfers[i].is_rct())
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found) for (auto i: unused_transfers_indices)
+ {
+ if (m_transfers[i].is_rct())
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return false;
return true;
}
+std::vector<size_t> wallet2::get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const
+{
+ std::vector<size_t> indices;
+ for (size_t n: unused_dust_indices)
+ if (m_transfers[n].is_rct())
+ indices.push_back(n);
+ for (size_t n: unused_transfers_indices)
+ if (m_transfers[n].is_rct())
+ indices.push_back(n);
+ return indices;
+}
+
// Another implementation of transaction creation that is hopefully better
// While there is anything left to pay, it goes through random outputs and tries
// to fill the next destination/amount. If it fully fills it, it will use the
@@ -4113,9 +4227,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
const bool use_rct = use_fork_rules(4, 0);
- const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = get_per_kb_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
+ const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
@@ -4135,8 +4248,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination);
// gather all our dust and non dust outputs
- const std::vector<size_t> unused_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true, trusted_daemon);
- for (size_t i: unused_indices)
+ for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
@@ -4219,7 +4331,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
size_t idx;
if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
// the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
- idx = pop_best_value(unused_dust_indices.empty() ? unused_transfers_indices : unused_dust_indices, tx.selected_transfers, true);
+ std::vector<size_t> indices = get_only_rct(unused_dust_indices, unused_transfers_indices);
+ idx = pop_best_value(indices, tx.selected_transfers, true);
// since we're trying to add a second output which is not strictly needed,
// we only add it if it's unrelated enough to the first one
@@ -4229,6 +4342,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("Second outout was not strictly needed, and relatedness " << relatedness << ", not adding");
break;
}
+ pop_if_present(unused_transfers_indices, idx);
+ pop_if_present(unused_dust_indices, idx);
} else if (!prefered_inputs.empty()) {
idx = pop_back(prefered_inputs);
pop_if_present(unused_transfers_indices, idx);
@@ -4440,9 +4555,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<std::vector<get_outs_entry>> outs;
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
- const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = get_per_kb_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
+ const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
@@ -4603,7 +4717,7 @@ uint64_t wallet2::get_upper_tranaction_size_limit()
{
if (m_upper_transaction_size_limit > 0)
return m_upper_transaction_size_limit;
- uint64_t full_reward_zone = use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
+ uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
return ((full_reward_zone * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
//----------------------------------------------------------------------------------------------------
@@ -4653,7 +4767,7 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
req_t.params.min_count = count;
req_t.params.max_count = 0;
req_t.params.unlocked = unlocked;
- bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
@@ -4692,7 +4806,7 @@ uint64_t wallet2::get_num_rct_outputs()
req_t.params.amounts.push_back(0);
req_t.params.min_count = 0;
req_t.params.max_count = 0;
- bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
@@ -4729,7 +4843,6 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
- const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = get_per_kb_fee();
// may throw
@@ -5424,7 +5537,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
height_mid,
height_max
};
- bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client);
+ bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client, rpc_timeout);
if (!r || res.status != CORE_RPC_STATUS_OK)
{
std::ostringstream oss;