aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt1
-rw-r--r--src/wallet/api/wallet.cpp14
-rw-r--r--src/wallet/api/wallet.h3
-rw-r--r--src/wallet/api/wallet2_api.h10
-rw-r--r--src/wallet/node_rpc_proxy.cpp72
-rw-r--r--src/wallet/node_rpc_proxy.h7
-rw-r--r--src/wallet/wallet2.cpp203
-rw-r--r--src/wallet/wallet2.h11
-rw-r--r--src/wallet/wallet_errors.h11
9 files changed, 216 insertions, 116 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/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 3f6bfec9e..680da26ce 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -2205,6 +2205,20 @@ void WalletImpl::keyReuseMitigation2(bool mitigation)
m_wallet->key_reuse_mitigation2(mitigation);
}
+bool WalletImpl::lockKeysFile()
+{
+ return m_wallet->lock_keys_file();
+}
+
+bool WalletImpl::unlockKeysFile()
+{
+ return m_wallet->unlock_keys_file();
+}
+
+bool WalletImpl::isKeysFileLocked()
+{
+ return m_wallet->is_keys_file_locked();
+}
} // namespace
namespace Bitmonero = Monero;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index eefb2fe94..58be686fc 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -188,6 +188,9 @@ public:
virtual void segregatePreForkOutputs(bool segregate) override;
virtual void segregationHeight(uint64_t height) override;
virtual void keyReuseMitigation2(bool mitigation) override;
+ virtual bool lockKeysFile() override;
+ virtual bool unlockKeysFile() override;
+ virtual bool isKeysFileLocked() override;
private:
void clearStatus() const;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index f54255e91..0cd0ff5cf 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -900,6 +900,12 @@ struct Wallet
//! Initiates a light wallet import wallet request
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0;
+
+ //! locks/unlocks the keys file; returns true on success
+ virtual bool lockKeysFile() = 0;
+ virtual bool unlockKeysFile() = 0;
+ //! returns true if the keys file is locked
+ virtual bool isKeysFileLocked() = 0;
};
/**
@@ -1070,6 +1076,10 @@ struct WalletManager
* @param password - password to verify
* @param no_spend_key - verify only view keys?
* @return - true if password is correct
+ *
+ * @note
+ * This function will fail when the wallet keys file is opened because the wallet program locks the keys file.
+ * In this case, Wallet::unlockKeysFile() and Wallet::lockKeysFile() need to be called before and after the call to this function, respectively.
*/
virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const = 0;
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 d99371673..90807803a 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),
@@ -1057,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;
@@ -1173,7 +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::unordered_set<crypto::public_key> public_keys_seen;
+ 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
@@ -1194,13 +1205,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
error::wallet_internal_error, "tx_cache_data is out of sync");
}
- if (public_keys_seen.find(pub_key_field.pub_key) != public_keys_seen.end())
- {
- MWARNING("The same transaction pubkey is present more than once, ignoring extra instance");
- continue;
- }
- public_keys_seen.insert(pub_key_field.pub_key);
-
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
tools::threadpool& tpool = tools::threadpool::getInstance();
@@ -1264,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
@@ -1274,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
@@ -1297,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);
@@ -1319,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)
{
@@ -1553,6 +1557,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
// We got a payment ID to go with this tx
LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8);
+ MINFO("Consider using subaddresses instead of encrypted payment IDs");
if (tx_pub_key != null_pkey)
{
if (!m_account.get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key))
@@ -1576,12 +1581,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
+ MWARNING("Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
}
}
- else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
- {
- LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
- }
for (const auto& i : tx_money_got_in_outs)
{
@@ -1820,7 +1822,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;
@@ -2052,8 +2054,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();
@@ -2266,12 +2268,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();
@@ -2436,6 +2438,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;
@@ -2445,7 +2448,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);
@@ -2475,6 +2504,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
@@ -2651,7 +2681,7 @@ void wallet2::detach_blockchain(uint64_t height)
bool wallet2::deinit()
{
m_is_initialized=false;
- m_keys_file_locker.reset();
+ unlock_keys_file();
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -2797,6 +2827,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());
@@ -2818,12 +2851,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
keys_file_data.account_data = cipher;
- m_keys_file_locker.reset();
+ unlock_keys_file();
std::string buf;
r = ::serialization::dump_binary(keys_file_data, buf);
r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read
CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name);
- m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
+ lock_keys_file();
return true;
}
@@ -2879,6 +2912,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;
@@ -3005,6 +3039,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);
@@ -3044,9 +3080,13 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
* can be used prior to rewriting wallet keys file, to ensure user has entered the correct password
*
*/
-bool wallet2::verify_password(const epee::wipeable_string& password) const
+bool wallet2::verify_password(const epee::wipeable_string& password)
{
- return verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device());
+ // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
+ unlock_keys_file();
+ bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device());
+ lock_keys_file();
+ return r;
}
/*!
@@ -3953,17 +3993,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
boost::system::error_code e;
bool exists = boost::filesystem::exists(m_keys_file, e);
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
- m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
- THROW_WALLET_EXCEPTION_IF(!m_keys_file_locker->locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
+ lock_keys_file();
+ THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
// this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
- m_keys_file_locker.reset();
+ unlock_keys_file();
if (!load_keys(m_keys_file, password))
{
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
}
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
- m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
+ lock_keys_file();
//keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem
@@ -5615,15 +5655,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;
@@ -5637,7 +5672,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");
@@ -6004,6 +6039,33 @@ bool wallet2::is_output_blackballed(const crypto::public_key &output) const
catch (const std::exception &e) { return false; }
}
+bool wallet2::lock_keys_file()
+{
+ if (m_keys_file_locker)
+ {
+ MDEBUG(m_keys_file << " is already locked.");
+ return false;
+ }
+ m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
+ return true;
+}
+
+bool wallet2::unlock_keys_file()
+{
+ if (!m_keys_file_locker)
+ {
+ MDEBUG(m_keys_file << " is already unlocked.");
+ return false;
+ }
+ m_keys_file_locker.reset();
+ return true;
+}
+
+bool wallet2::is_keys_file_locked() const
+{
+ return m_keys_file_locker->locked();
+}
+
bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const
{
if (!unlocked) // don't add locked outs
@@ -7688,12 +7750,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;
@@ -9399,31 +9473,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
@@ -10781,15 +10839,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 d33d8258b..220eca8a8 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -609,7 +609,7 @@ namespace tools
/*!
* \brief verifies given password is correct for default wallet keys file
*/
- bool verify_password(const epee::wipeable_string& password) const;
+ bool verify_password(const epee::wipeable_string& password);
cryptonote::account_base& get_account(){return m_account;}
const cryptonote::account_base& get_account()const{return m_account;}
@@ -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; }
@@ -1144,6 +1146,9 @@ namespace tools
bool unblackball_output(const crypto::public_key &output);
bool is_output_blackballed(const crypto::public_key &output) const;
+ bool lock_keys_file();
+ bool unlock_keys_file();
+ bool is_keys_file_locked() const;
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1167,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;
@@ -1181,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;
@@ -1283,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: