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.cpp142
1 files changed, 105 insertions, 37 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 0faa5d1aa..f05a67315 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -44,6 +44,7 @@
using namespace epee;
#include "cryptonote_config.h"
+#include "cryptonote_core/tx_sanity_check.h"
#include "wallet_rpc_helpers.h"
#include "wallet2.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@@ -101,10 +102,6 @@ using namespace cryptonote;
// used to target a given block weight (additional outputs may be added on top to build fee)
#define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
-// arbitrary, used to generate different hashes from the same input
-#define CHACHA8_KEY_TAIL 0x8c
-#define CACHE_KEY_TAIL 0x8d
-
#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\004"
#define SIGNED_TX_PREFIX "Monero signed tx set\004"
#define MULTISIG_UNSIGNED_TX_PREFIX "Monero multisig unsigned tx set\001"
@@ -149,6 +146,9 @@ static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1";
+boost::mutex tools::wallet2::default_daemon_address_lock;
+std::string tools::wallet2::default_daemon_address = "";
+
namespace
{
std::string get_default_ringdb_path()
@@ -236,8 +236,6 @@ namespace
add_reason(reason, "overspend");
if (res.fee_too_low)
add_reason(reason, "fee too low");
- if (res.not_rct)
- add_reason(reason, "tx is not ringct");
if (res.sanity_check_failed)
add_reason(reason, "tx sanity check failed");
if (res.not_relayed)
@@ -357,7 +355,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty())
{
std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
- std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
+ std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex_locale::to_vector);
for (const auto &fpr: ssl_allowed_fingerprints)
{
THROW_WALLET_EXCEPTION_IF(fpr.size() != SSL_FINGERPRINT_SIZE, tools::error::wallet_internal_error,
@@ -412,6 +410,15 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
daemon_port = get_config(nettype).RPC_DEFAULT_PORT;
}
+ // if no daemon settings are given and we have a previous one, reuse that one
+ if (command_line::is_arg_defaulted(vm, opts.daemon_host) && command_line::is_arg_defaulted(vm, opts.daemon_port) && command_line::is_arg_defaulted(vm, opts.daemon_address))
+ {
+ // not a bug: taking a const ref to a temporary in this way is actually ok in a recent C++ standard
+ const std::string &def = tools::wallet2::get_default_daemon_address();
+ if (!def.empty())
+ daemon_address = def;
+ }
+
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
@@ -428,6 +435,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
verification_required && !ssl_options.has_strong_verification(real_daemon),
tools::error::wallet_internal_error,
tools::wallet2::tr("Enabling --") + std::string{use_proxy ? opts.proxy.name : opts.daemon_ssl.name} + tools::wallet2::tr(" requires --") +
+ opts.daemon_ssl_allow_any_cert.name + tools::wallet2::tr(" or --") +
opts.daemon_ssl_ca_certificates.name + tools::wallet2::tr(" or --") + opts.daemon_ssl_allowed_fingerprints.name + tools::wallet2::tr(" or use of a .onion/.i2p domain")
);
}
@@ -591,6 +599,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
}
viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
crypto::public_key pkey;
+ if (viewkey == crypto::null_skey)
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view secret key may not be all zeroes"));
if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
}
@@ -607,6 +617,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
}
spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
crypto::public_key pkey;
+ if (spendkey == crypto::null_skey)
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend secret key may not be all zeroes"));
if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
}
@@ -879,20 +891,6 @@ uint8_t get_bulletproof_fork()
return 8;
}
-uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
-{
- if (use_per_byte_fee)
- {
- const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
- return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
- }
- else
- {
- const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
- return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
- }
-}
-
uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{
if (use_per_byte_fee)
@@ -1313,8 +1311,15 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
m_node_rpc_proxy.invalidate();
}
- MINFO("setting daemon to " << get_daemon_address());
- return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options));
+ const std::string address = get_daemon_address();
+ MINFO("setting daemon to " << address);
+ bool ret = m_http_client.set_server(address, get_daemon_login(), std::move(ssl_options));
+ if (ret)
+ {
+ CRITICAL_REGION_LOCAL(default_daemon_address_lock);
+ default_daemon_address = address;
+ }
+ return ret;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
@@ -2874,7 +2879,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashe
}
//----------------------------------------------------------------------------------------------------
-void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed)
+void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed)
{
MTRACE("update_pool_state start");
@@ -2934,7 +2939,6 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b
pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
// the inputs aren't spent anymore, since the tx failed
- remove_rings(pit->second.m_tx);
for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
{
if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))
@@ -3064,7 +3068,7 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b
[tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
if (i != txids.end())
{
- process_txs.push_back(std::make_pair(tx, tx_entry.double_spend_seen));
+ process_txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen));
}
else
{
@@ -3095,14 +3099,14 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b
MTRACE("update_pool_state end");
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs)
+void wallet2::process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs)
{
const time_t now = time(NULL);
for (const auto &e: txs)
{
- const cryptonote::transaction &tx = e.first;
- const bool double_spend_seen = e.second;
- const crypto::hash tx_hash = get_transaction_hash(tx);
+ const cryptonote::transaction &tx = std::get<0>(e);
+ const crypto::hash &tx_hash = std::get<1>(e);
+ const bool double_spend_seen = std::get<2>(e);
process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, 0, now, false, true, double_spend_seen, {});
m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
@@ -3323,7 +3327,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// since that might cause a password prompt, which would introduce a data
// leak allowing a passive adversary with traffic analysis capability to
// infer when we get an incoming output
- std::vector<std::pair<cryptonote::transaction, bool>> process_pool_txs;
+ std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_pool_txs;
update_pool_state(process_pool_txs, true);
bool first = true, last = false;
@@ -3923,7 +3927,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
memcpy(cache_key_data.data(), &key, HASH_SIZE);
- cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL;
+ cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE;
cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
get_ringdb_key();
}
@@ -4088,9 +4092,18 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_always_confirm_transfers = field_always_confirm_transfers;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true);
m_print_ring_members = field_print_ring_members;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true);
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true);
- m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
+ if (json.HasMember("store_tx_info"))
+ {
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, true, true);
+ m_store_tx_info = field_store_tx_info;
+ }
+ else if (json.HasMember("store_tx_keys")) // backward compatibility
+ {
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, true, true);
+ m_store_tx_info = field_store_tx_keys;
+ }
+ else
+ m_store_tx_info = true;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0);
m_default_mixin = field_default_mixin;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0);
@@ -7134,6 +7147,20 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
return sign_multisig_tx_to_file(exported_txs, filename, txids);
}
//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
+{
+ if (use_per_byte_fee)
+ {
+ const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
+ }
+ else
+ {
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
+ }
+}
+
uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm)
{
static const struct
@@ -7775,8 +7802,50 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
}
}
+std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs)
+{
+ std::set<uint64_t> unique;
+ size_t total = 0;
+
+ for (const auto &it : outs)
+ {
+ for (const auto &out : it)
+ {
+ const uint64_t global_index = std::get<0>(out);
+ unique.insert(global_index);
+ }
+ total += it.size();
+ }
+
+ return std::make_pair(std::move(unique), total);
+}
+
void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count)
{
+ std::vector<uint64_t> rct_offsets;
+ for (size_t attempts = 3; attempts > 0; --attempts)
+ {
+ get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets);
+
+ const auto unique = outs_unique(outs);
+ if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back()))
+ {
+ return;
+ }
+
+ std::vector<crypto::key_image> key_images;
+ key_images.reserve(selected_transfers.size());
+ std::for_each(selected_transfers.begin(), selected_transfers.end(), [this, &key_images](size_t index) {
+ key_images.push_back(m_transfers[index].m_key_image);
+ });
+ unset_ring(key_images);
+ }
+
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed"));
+}
+
+void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets)
+{
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
outs.clear();
@@ -7797,7 +7866,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// if we have at least one rct out, get the distribution, or fall back to the previous system
uint64_t rct_start_height;
- std::vector<uint64_t> rct_offsets;
bool has_rct = false;
uint64_t max_rct_index = 0;
for (size_t idx: selected_transfers)
@@ -7806,7 +7874,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
has_rct = true;
max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
}
- const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
+ const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets));
if (has_rct_distribution)
{
// check we're clear enough of rct start, to avoid corner cases below