aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/wallet.cpp26
-rw-r--r--src/wallet/api/wallet.h4
-rw-r--r--src/wallet/api/wallet2_api.h12
-rw-r--r--src/wallet/api/wallet_manager.cpp5
-rw-r--r--src/wallet/api/wallet_manager.h3
-rw-r--r--src/wallet/ringdb.cpp60
-rw-r--r--src/wallet/wallet2.cpp143
-rw-r--r--src/wallet/wallet2.h8
8 files changed, 201 insertions, 60 deletions
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 6200c7a1f..4612b0397 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -725,7 +725,7 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed)
return recover(path, "", seed);
}
-bool WalletImpl::recover(const std::string &path, const std::string &password, const std::string &seed)
+bool WalletImpl::recover(const std::string &path, const std::string &password, const std::string &seed, const std::string &seed_offset/* = {}*/)
{
clearStatus();
m_errorString.clear();
@@ -743,6 +743,10 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c
setStatusError(tr("Electrum-style word list failed verification"));
return false;
}
+ if (!seed_offset.empty())
+ {
+ recovery_key = cryptonote::decrypt_key(recovery_key, seed_offset);
+ }
if (old_language == crypto::ElectrumWords::old_language_name)
old_language = Language::English().get_language_name();
@@ -1671,6 +1675,26 @@ void WalletImpl::disposeTransaction(PendingTransaction *t)
delete t;
}
+uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
+ PendingTransaction::Priority priority) const
+{
+ const size_t pubkey_size = 33;
+ const size_t encrypted_paymentid_size = 11;
+ const size_t extra_size = pubkey_size + encrypted_paymentid_size;
+
+ return m_wallet->estimate_fee(
+ m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0),
+ m_wallet->use_fork_rules(4, 0),
+ 1,
+ m_wallet->get_min_ring_size() - 1,
+ destinations.size() + 1,
+ extra_size,
+ m_wallet->use_fork_rules(8, 0),
+ m_wallet->get_base_fee(),
+ m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))),
+ m_wallet->get_fee_quantization_mask());
+}
+
TransactionHistory *WalletImpl::history()
{
return m_history.get();
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 331bf4b38..66eeb0e73 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -60,7 +60,7 @@ public:
const std::string &language) const override;
bool open(const std::string &path, const std::string &password);
bool recover(const std::string &path,const std::string &password,
- const std::string &seed);
+ const std::string &seed, const std::string &seed_offset = {});
bool recoverFromKeysWithPassword(const std::string &path,
const std::string &password,
const std::string &language,
@@ -166,6 +166,8 @@ public:
bool importKeyImages(const std::string &filename) override;
virtual void disposeTransaction(PendingTransaction * t) override;
+ virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
+ PendingTransaction::Priority priority) const override;
virtual TransactionHistory * history() override;
virtual AddressBook * addressBook() override;
virtual Subaddress * subaddress() override;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index e543a115b..09c64106e 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -879,6 +879,14 @@ struct Wallet
*/
virtual void disposeTransaction(PendingTransaction * t) = 0;
+ /*!
+ * \brief Estimates transaction fee.
+ * \param destinations Vector consisting of <address, amount> pairs.
+ * \return Estimated fee.
+ */
+ virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
+ PendingTransaction::Priority priority) const = 0;
+
/*!
* \brief exportKeyImages - exports key images to file
* \param filename
@@ -1085,10 +1093,12 @@ struct WalletManager
* \param nettype Network type
* \param restoreHeight restore from start height
* \param kdf_rounds Number of rounds for key derivation function
+ * \param seed_offset Seed offset passphrase (optional)
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
- NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1) = 0;
+ NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1,
+ const std::string &seed_offset = {}) = 0;
Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
bool testnet = false, uint64_t restoreHeight = 0) // deprecated
{
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index d589dcc75..44a184304 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -93,13 +93,14 @@ Wallet *WalletManagerImpl::recoveryWallet(const std::string &path,
const std::string &mnemonic,
NetworkType nettype,
uint64_t restoreHeight,
- uint64_t kdf_rounds)
+ uint64_t kdf_rounds,
+ const std::string &seed_offset/* = {}*/)
{
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
}
- wallet->recover(path, password, mnemonic);
+ wallet->recover(path, password, mnemonic, seed_offset);
return wallet;
}
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 537fc5ba6..0595b8327 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -46,7 +46,8 @@ public:
const std::string &mnemonic,
NetworkType nettype,
uint64_t restoreHeight,
- uint64_t kdf_rounds = 1) override;
+ uint64_t kdf_rounds = 1,
+ const std::string &seed_offset = {}) override;
virtual Wallet * createWalletFromKeys(const std::string &path,
const std::string &password,
const std::string &language,
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index b7efdd75c..dfeb987ca 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -35,10 +35,13 @@
#include "misc_language.h"
#include "wallet_errors.h"
#include "ringdb.h"
+#include "cryptonote_config.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb"
+#define V1TAG ((uint64_t)798237759845202)
+
static const char zerokey[8] = {0};
static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey };
@@ -63,15 +66,16 @@ static int compare_uint64(const MDB_val *a, const MDB_val *b)
return va < vb ? -1 : va > vb;
}
-static std::string compress_ring(const std::vector<uint64_t> &ring)
+static std::string compress_ring(const std::vector<uint64_t> &ring, uint64_t tag)
{
std::string s;
+ s += tools::get_varint_data(tag);
for (uint64_t out: ring)
s += tools::get_varint_data(out);
return s;
}
-static std::vector<uint64_t> decompress_ring(const std::string &s)
+static std::vector<uint64_t> decompress_ring(const std::string &s, uint64_t tag)
{
std::vector<uint64_t> ring;
int read = 0;
@@ -81,6 +85,13 @@ static std::vector<uint64_t> decompress_ring(const std::string &s)
std::string tmp(i, s.cend());
read = tools::read_varint(tmp.begin(), tmp.end(), out);
THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring");
+ if (tag)
+ {
+ if (tag != out)
+ return {};
+ tag = 0;
+ continue;
+ }
ring.push_back(out);
}
return ring;
@@ -93,25 +104,25 @@ std::string get_rings_filename(boost::filesystem::path filename)
return filename.string();
}
-static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key)
+static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- static const char salt[] = "ringdsb";
-
- uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt)];
+ uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(config::HASH_KEY_RINGDB) + sizeof(field)];
memcpy(buffer, &key_image, sizeof(key_image));
memcpy(buffer + sizeof(key_image), &key, sizeof(key));
- memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt));
+ memcpy(buffer + sizeof(key_image) + sizeof(key), config::HASH_KEY_RINGDB, sizeof(config::HASH_KEY_RINGDB));
+ memcpy(buffer + sizeof(key_image) + sizeof(key) + sizeof(config::HASH_KEY_RINGDB), &field, sizeof(field));
crypto::hash hash;
- crypto::cn_fast_hash(buffer, sizeof(buffer), hash.data);
+ // if field is 0, backward compat mode: hash without the field
+ crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data);
static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes");
crypto::chacha_iv iv;
memcpy(&iv, &hash, CHACHA_IV_SIZE);
return iv;
}
-static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- const crypto::chacha_iv iv = make_iv(key_image, key);
+ const crypto::chacha_iv iv = make_iv(key_image, key, field);
std::string ciphertext;
ciphertext.resize(plaintext.size() + sizeof(iv));
crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
@@ -119,14 +130,14 @@ static std::string encrypt(const std::string &plaintext, const crypto::key_image
return ciphertext;
}
-static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key);
+ return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key, field);
}
-static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- const crypto::chacha_iv iv = make_iv(key_image, key);
+ const crypto::chacha_iv iv = make_iv(key_image, key, field);
std::string plaintext;
THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text");
plaintext.resize(ciphertext.size() - sizeof(iv));
@@ -137,11 +148,11 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag
static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector<uint64_t> &relative_ring, const crypto::chacha_key &chacha_key)
{
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
- std::string compressed_ring = compress_ring(relative_ring);
- std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key);
+ std::string compressed_ring = compress_ring(relative_ring, V1TAG);
+ std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1);
data.mv_size = data_ciphertext.size();
data.mv_data = (void*)data_ciphertext.c_str();
int dbr = mdb_put(txn, dbi, &key, &data, 0);
@@ -297,7 +308,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto
for (const crypto::key_image &key_image: key_images)
{
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
@@ -349,7 +360,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
tx_active = true;
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
dbr = mdb_get(txn, dbi_rings, &key, &data);
@@ -358,8 +369,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return false;
THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size");
- std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key);
- outs = decompress_ring(data_plaintext);
+ bool try_v0 = false;
+ std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1);
+ try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; }
+ catch(...) { try_v0 = true; }
+ if (try_v0)
+ {
+ data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 0);
+ outs = decompress_ring(data_plaintext, 0);
+ }
MDEBUG("Found ring for key image " << key_image << ":");
MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
outs = cryptonote::relative_output_offsets_to_absolute(outs);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 7ee32a436..bc8219c69 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)
@@ -1548,6 +1553,12 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
}
}
//----------------------------------------------------------------------------------------------------
+void wallet2::create_one_off_subaddress(const cryptonote::subaddress_index& index)
+{
+ const crypto::public_key pkey = get_subaddress_spend_public_key(index);
+ m_subaddresses[pkey] = index;
+}
+//----------------------------------------------------------------------------------------------------
std::string wallet2::get_subaddress_label(const cryptonote::subaddress_index& index) const
{
if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size())
@@ -2089,7 +2100,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_amount = amount;
td.m_pk_index = pk_index - 1;
td.m_subaddr_index = tx_scan_info[o].received->index;
- expand_subaddresses(tx_scan_info[o].received->index);
+ if (tx_scan_info[o].received->index.major < m_subaddress_labels.size() && tx_scan_info[o].received->index.minor < m_subaddress_labels[tx_scan_info[o].received->index.major].size())
+ expand_subaddresses(tx_scan_info[o].received->index);
if (tx.vout[o].amount == 0)
{
td.m_mask = tx_scan_info[o].mask;
@@ -2167,7 +2179,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_amount = amount;
td.m_pk_index = pk_index - 1;
td.m_subaddr_index = tx_scan_info[o].received->index;
- expand_subaddresses(tx_scan_info[o].received->index);
+ if (tx_scan_info[o].received->index.major < m_subaddress_labels.size() && tx_scan_info[o].received->index.minor < m_subaddress_labels[tx_scan_info[o].received->index.major].size())
+ expand_subaddresses(tx_scan_info[o].received->index);
if (tx.vout[o].amount == 0)
{
td.m_mask = tx_scan_info[o].mask;
@@ -2934,7 +2947,6 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
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))
@@ -3923,7 +3935,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 +4100,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 +7155,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 +7810,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 +7874,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 +7882,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
@@ -12608,7 +12684,8 @@ process:
const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device());
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
- expand_subaddresses(td.m_subaddr_index);
+ if (td.m_subaddr_index.major < m_subaddress_labels.size() && td.m_subaddr_index.minor < m_subaddress_labels[td.m_subaddr_index.major].size())
+ expand_subaddresses(td.m_subaddr_index);
td.m_key_image_known = true;
td.m_key_image_request = true;
td.m_key_image_partial = false;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 8ecc4d8fa..7620d09d8 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -793,6 +793,7 @@ private:
size_t get_num_subaddresses(uint32_t index_major) const { return index_major < m_subaddress_labels.size() ? m_subaddress_labels[index_major].size() : 0; }
void add_subaddress(uint32_t index_major, const std::string& label); // throws when index is out of bound
void expand_subaddresses(const cryptonote::subaddress_index& index);
+ void create_one_off_subaddress(const cryptonote::subaddress_index& index);
std::string get_subaddress_label(const cryptonote::subaddress_index& index) const;
void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label);
void set_subaddress_lookahead(size_t major, size_t minor);
@@ -1246,6 +1247,7 @@ private:
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
+ 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) const;
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
uint64_t get_base_fee();
uint64_t get_fee_quantization_mask();
@@ -1389,6 +1391,8 @@ private:
uint64_t credits() const { return m_rpc_payment_state.credits; }
void credit_report(uint64_t &expected_spent, uint64_t &discrepancy) const { expected_spent = m_rpc_payment_state.expected_spent; discrepancy = m_rpc_payment_state.discrepancy; }
+ static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
+
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1440,6 +1444,7 @@ private:
bool is_spent(const transfer_details &td, bool strict = true) const;
bool is_spent(size_t idx, bool strict = true) const;
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
+ void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
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) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
@@ -1628,6 +1633,9 @@ private:
std::unique_ptr<wallet_device_callback> m_device_callback;
ExportFormat m_export_format;
+
+ static boost::mutex default_daemon_address_lock;
+ static std::string default_daemon_address;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 29)