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.cpp596
1 files changed, 410 insertions, 186 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 9e95a26c1..9be36bc26 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2020, The Monero Project
+// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
@@ -290,15 +290,15 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
mms_file = file_path + ".mms";
}
-uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
+uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes)
{
uint64_t kB = (bytes + 1023) / 1024;
- return kB * fee_per_kb * fee_multiplier;
+ return kB * fee_per_kb;
}
-uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
+uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_quantization_mask)
{
- uint64_t fee = weight * base_fee * fee_multiplier;
+ uint64_t fee = weight * base_fee;
fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
return fee;
}
@@ -781,7 +781,7 @@ void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_
}
}
-size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
{
size_t size = 0;
@@ -805,12 +805,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
size += 1;
// rangeSigs
- if (bulletproof)
+ if (bulletproof || bulletproof_plus)
{
size_t log_padded_outputs = 0;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
- size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
+ size += (2 * (6 + log_padded_outputs) + (bulletproof_plus ? 6 : (4 + 5))) * 32 + 3;
}
else
size += (2*64*32+32+64*32) * n_outputs;
@@ -821,6 +821,9 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
else
size += n_inputs * (64 * (mixin+1) + 32);
+ if (use_view_tags)
+ size += n_outputs * sizeof(crypto::view_tag);
+
// mixRing - not serialized, can be reconstructed
/* size += 2 * 32 * (mixin+1) * n_inputs; */
@@ -833,29 +836,29 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// txnFee
size += 4;
- LOG_PRINT_L2("estimated " << (bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
+ LOG_PRINT_L2("estimated " << (bulletproof_plus ? "bulletproof plus" : bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
return size;
}
-size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
{
if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
else
- return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
+ return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size + (use_view_tags ? (n_outputs * sizeof(crypto::view_tag)) : 0);
}
-uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
{
- size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
- if (use_rct && bulletproof && n_outputs > 2)
+ size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
+ if (use_rct && (bulletproof || bulletproof_plus) && n_outputs > 2)
{
- const uint64_t bp_base = 368;
+ const uint64_t bp_base = (32 * ((bulletproof_plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
size_t log_padded_outputs = 2;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
uint64_t nlr = 2 * (6 + log_padded_outputs);
- const uint64_t bp_size = 32 * (9 + nlr);
+ const uint64_t bp_size = 32 * ((bulletproof_plus ? 6 : 9) + nlr);
const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
MDEBUG("clawback on size " << size << ": " << bp_clawback);
size += bp_clawback;
@@ -868,17 +871,27 @@ uint8_t get_bulletproof_fork()
return 8;
}
+uint8_t get_bulletproof_plus_fork()
+{
+ return HF_VERSION_BULLETPROOF_PLUS;
+}
+
uint8_t get_clsag_fork()
{
return HF_VERSION_CLSAG;
}
-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)
+uint8_t get_view_tag_fork()
+{
+ return HF_VERSION_VIEW_TAGS;
+}
+
+uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_quantization_mask)
{
if (use_per_byte_fee)
- return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
+ return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_quantization_mask);
else
- return calculate_fee(base_fee, blob_size, fee_multiplier);
+ return calculate_fee(base_fee, blob_size);
}
bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
@@ -1168,6 +1181,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_ignore_outputs_above(MONEY_SUPPLY),
m_ignore_outputs_below(0),
m_track_uses(false),
+ m_show_wallet_name_when_locked(false),
m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT),
m_setup_background_mining(BackgroundMiningMaybe),
m_persistent_rpc_client_id(false),
@@ -1759,13 +1773,14 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio
hw::device &hwdev = m_account.get_device();
boost::unique_lock<hw::device> hwdev_lock (hwdev);
hwdev.set_mode(hw::device::TRANSACTION_PARSE);
- if (o.target.type() != typeid(txout_to_key))
+ crypto::public_key output_public_key;
+ if (!get_output_public_key(o, output_public_key))
{
tx_scan_info.error = true;
LOG_ERROR("wrong type id in transaction out");
return;
}
- tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, boost::get<txout_to_key>(o.target).key, derivation, additional_derivations, i, hwdev);
+ tx_scan_info.received = is_out_to_acc_precomp(m_subaddresses, output_public_key, derivation, additional_derivations, i, hwdev, get_output_view_tag(o));
if(tx_scan_info.received)
{
tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs
@@ -1816,6 +1831,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
+ case rct::RCTTypeBulletproofPlus:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
@@ -1849,17 +1865,20 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
}
}
+ crypto::public_key output_public_key;
+ THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key");
+
if (m_multisig)
{
- tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key;
+ tx_scan_info.in_ephemeral.pub = output_public_key;
tx_scan_info.in_ephemeral.sec = crypto::null_skey;
tx_scan_info.ki = rct::rct2ki(rct::zero());
}
else
{
- bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device());
+ bool r = cryptonote::generate_key_image_helper_precomp(m_account.get_keys(), output_public_key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device());
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
- THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != output_public_key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
}
@@ -1986,8 +2005,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
- tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter(tpool);
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
@@ -2057,10 +2074,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_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);
+ 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(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
// then scan all outputs from 0
hw::device &hwdev = m_account.get_device();
boost::unique_lock<hw::device> hwdev_lock (hwdev);
@@ -2080,32 +2095,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
}
}
- else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1 && !is_out_data_ptr)
- {
- for (size_t i = 0; i < tx.vout.size(); ++i)
- {
- 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);
- }
- THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
-
- hw::device &hwdev = m_account.get_device();
- boost::unique_lock<hw::device> hwdev_lock (hwdev);
- hwdev.set_mode(hw::device::NONE);
- for (size_t i = 0; i < tx.vout.size(); ++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)
- {
- hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
- scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool);
- if (!tx_scan_info[i].error)
- {
- tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered);
- }
- }
- }
- }
else
{
for (size_t i = 0; i < tx.vout.size(); ++i)
@@ -2786,25 +2775,34 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
for (size_t k = 0; k < n_vouts; ++k)
{
const auto &o = tx.vout[k];
- if (o.target.type() == typeid(cryptonote::txout_to_key))
+ crypto::public_key output_public_key;
+ if (get_output_public_key(o, output_public_key))
{
std::vector<crypto::key_derivation> additional_derivations;
additional_derivations.reserve(tx_cache_data[txidx].additional.size());
for (const auto &iod: tx_cache_data[txidx].additional)
additional_derivations.push_back(iod.derivation);
- const auto &key = boost::get<txout_to_key>(o.target).key;
for (size_t l = 0; l < tx_cache_data[txidx].primary.size(); ++l)
{
THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].primary[l].received.size() != n_vouts,
error::wallet_internal_error, "Unexpected received array size");
- tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev);
+ tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, output_public_key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev, get_output_view_tag(o));
additional_derivations.clear();
}
}
}
};
+ struct geniod_params
+ {
+ const cryptonote::transaction &tx;
+ size_t n_outs;
+ size_t txidx;
+ };
+ std::vector<geniod_params> geniods;
+ geniods.reserve(num_txes);
txidx = 0;
+ uint8_t hf_version_view_tags = get_view_tag_fork();
for (size_t i = 0; i < blocks.size(); ++i)
{
if (should_skip_block(parsed_blocks[i].block, start_height + i))
@@ -2818,18 +2816,51 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
const cryptonote::transaction& tx = parsed_blocks[i].block.miner_tx;
const size_t n_vouts = (m_refresh_type == RefreshType::RefreshOptimizeCoinbase && tx.version < 2) ? 1 : tx.vout.size();
- tpool.submit(&waiter, [&, n_vouts, txidx](){ geniod(tx, n_vouts, txidx); }, true);
+ if (parsed_blocks[i].block.major_version >= hf_version_view_tags)
+ geniods.push_back(geniod_params{ tx, n_vouts, txidx });
+ else
+ tpool.submit(&waiter, [&, n_vouts, txidx](){ geniod(tx, n_vouts, txidx); }, true);
}
++txidx;
for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
{
THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
- tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
+ if (parsed_blocks[i].block.major_version >= hf_version_view_tags)
+ geniods.push_back(geniod_params{ parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx });
+ else
+ tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true);
++txidx;
}
}
THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value");
+
+ // View tags significantly speed up the geniod function that determines if an output belongs to the account.
+ // Because the speedup is so large, the overhead from submitting individual geniods to the thread pool eats into
+ // the benefit of executing in parallel. So to maximize the benefit from threads when view tags are enabled,
+ // the wallet starts submitting geniod function calls to the thread pool in batches of size GENIOD_BATCH_SIZE.
+ if (geniods.size())
+ {
+ size_t GENIOD_BATCH_SIZE = 100;
+ size_t num_batch_txes = 0;
+ size_t batch_start = 0;
+ while (batch_start < geniods.size())
+ {
+ size_t batch_end = std::min(batch_start + GENIOD_BATCH_SIZE, geniods.size());
+ THROW_WALLET_EXCEPTION_IF(batch_end < batch_start, error::wallet_internal_error, "Thread batch end overflow");
+ tpool.submit(&waiter, [&geniods, &geniod, batch_start, batch_end]() {
+ for (size_t i = batch_start; i < batch_end; ++i)
+ {
+ const geniod_params &gp = geniods[i];
+ geniod(gp.tx, gp.n_outs, gp.txidx);
+ }
+ }, true);
+ num_batch_txes += batch_end - batch_start;
+ batch_start = batch_end;
+ }
+ THROW_WALLET_EXCEPTION_IF(num_batch_txes != geniods.size(), error::wallet_internal_error, "txes batched for thread pool did not reach expected value");
+ }
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
+
hwdev.set_mode(hw::device::NONE);
size_t tx_cache_data_offset = 0;
@@ -3966,6 +3997,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
value2.SetInt(m_track_uses ? 1 : 0);
json.AddMember("track_uses", value2, json.GetAllocator());
+ value2.SetInt(m_show_wallet_name_when_locked ? 1 : 0);
+ json.AddMember("show_wallet_name_when_locked", value2, json.GetAllocator());
+
value2.SetInt(m_inactivity_lock_timeout);
json.AddMember("inactivity_lock_timeout", value2, json.GetAllocator());
@@ -4150,6 +4184,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_ignore_outputs_above = MONEY_SUPPLY;
m_ignore_outputs_below = 0;
m_track_uses = false;
+ m_show_wallet_name_when_locked = false;
m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT;
m_setup_background_mining = BackgroundMiningMaybe;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
@@ -4324,6 +4359,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_ignore_outputs_below = field_ignore_outputs_below;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
m_track_uses = field_track_uses;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, show_wallet_name_when_locked, int, Int, false, false);
+ m_show_wallet_name_when_locked = field_show_wallet_name_when_locked;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, inactivity_lock_timeout, uint32_t, Uint, false, DEFAULT_INACTIVITY_LOCK_TIMEOUT);
m_inactivity_lock_timeout = field_inactivity_lock_timeout;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, setup_background_mining, BackgroundMiningSetupType, Int, false, BackgroundMiningMaybe);
@@ -5088,7 +5125,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
// reconstruct multisig account
crypto::public_key dummy;
- multisig::multisig_account::kex_origins_map_t kex_origins_map;
+ multisig::multisig_keyset_map_memsafe_t kex_origins_map;
for (const auto &derivation : m_multisig_derivations)
kex_origins_map[derivation];
@@ -5101,7 +5138,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
get_account().get_keys().m_multisig_keys,
get_account().get_keys().m_view_secret_key,
m_account_public_address.m_spend_public_key,
- dummy, //common pubkey: not used
+ m_account_public_address.m_view_public_key,
m_multisig_rounds_passed,
std::move(kex_origins_map),
""
@@ -5188,7 +5225,10 @@ bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const
if (total)
*total = m_multisig_signers.size();
if (ready)
- *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity()));
+ {
+ *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity())) &&
+ (m_multisig_rounds_passed == multisig::multisig_kex_rounds_required(m_multisig_signers.size(), m_multisig_threshold) + 1);
+ }
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -6442,7 +6482,7 @@ std::string wallet2::dump_tx_to_str(const std::vector<pending_tx> &ptx_vector) c
txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
}
- txs.transfers = export_outputs();
+ txs.new_transfers = export_outputs();
// save as binary
std::ostringstream oss;
binary_archive<true> ar(oss);
@@ -6583,7 +6623,10 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
//----------------------------------------------------------------------------------------------------
bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
{
- import_outputs(exported_txs.transfers);
+ if (!exported_txs.new_transfers.second.empty())
+ import_outputs(exported_txs.new_transfers);
+ else
+ import_outputs(exported_txs.transfers);
// sign the transactions
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
@@ -6597,7 +6640,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, m_multisig ? &msout : NULL);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, m_multisig ? &msout : NULL, sd.use_view_tags);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
@@ -6682,16 +6725,17 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
+ crypto::public_key output_public_key;
+ if (!get_output_public_key(tx.vout[i], output_public_key))
continue;
- const cryptonote::txout_to_key &out = boost::get<cryptonote::txout_to_key>(tx.vout[i].target);
+
// if this output is back to this wallet, we can calculate its key image already
- if (!is_out_to_acc_precomp(m_subaddresses, out.key, derivation, additional_derivations, i, hwdev))
+ if (!is_out_to_acc_precomp(m_subaddresses, output_public_key, derivation, additional_derivations, i, hwdev, get_output_view_tag(tx.vout[i])))
continue;
crypto::key_image ki;
cryptonote::keypair in_ephemeral;
- if (generate_key_image_helper(keys, m_subaddresses, out.key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
- signed_txes.tx_key_images[out.key] = ki;
+ if (generate_key_image_helper(keys, m_subaddresses, output_public_key, tx_pub_key, additional_tx_pub_keys, i, in_ephemeral, ki, hwdev))
+ signed_txes.tx_key_images[output_public_key] = ki;
else
MERROR("Failed to calculate key image");
}
@@ -7116,7 +7160,8 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
auto sources = sd.sources;
rct::RCTConfig rct_config = sd.rct_config;
- bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, false);
+ bool shuffle_outs = false;
+ bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, shuffle_outs, sd.use_view_tags);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx),
@@ -7216,17 +7261,17 @@ 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, bool clsag, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
+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, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, 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, clsag);
- return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
+ const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
+ return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_quantization_mask);
}
else
{
- const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
- return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
+ return calculate_fee(base_fee, estimated_tx_size);
}
}
@@ -7299,6 +7344,40 @@ uint64_t wallet2::get_base_fee()
return get_dynamic_base_fee_estimate();
}
//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_base_fee(uint32_t priority)
+{
+ const bool use_2021_scaling = use_fork_rules(HF_VERSION_2021_SCALING, -30 * 1);
+ if (use_2021_scaling)
+ {
+ // clamp and map to 0..3 indices, mapping 0 (default, but should not end up here) to 0, and 1..4 to 0..3
+ if (priority == 0)
+ priority = 1;
+ else if (priority > 4)
+ priority = 4;
+ --priority;
+
+ std::vector<uint64_t> fees;
+ boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate_2021_scaling(FEE_ESTIMATE_GRACE_BLOCKS, fees);
+ if (result)
+ {
+ MERROR("Failed to determine base fee, using default");
+ return FEE_PER_BYTE;
+ }
+ if (priority >= fees.size())
+ {
+ MERROR("Failed to determine base fee for priority " << priority << ", using default");
+ return FEE_PER_BYTE;
+ }
+ return fees[priority];
+ }
+ else
+ {
+ const uint64_t base_fee = get_base_fee();
+ const uint64_t fee_multiplier = get_fee_multiplier(priority);
+ return base_fee * fee_multiplier;
+ }
+}
+//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_fee_quantization_mask()
{
if(m_light_wallet)
@@ -7330,6 +7409,8 @@ int wallet2::get_fee_algorithm()
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_min_ring_size()
{
+ if (use_fork_rules(HF_VERSION_MIN_MIXIN_15, 0))
+ return 16;
if (use_fork_rules(8, 10))
return 11;
if (use_fork_rules(7, 10))
@@ -7343,6 +7424,8 @@ uint64_t wallet2::get_min_ring_size()
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_max_ring_size()
{
+ if (use_fork_rules(HF_VERSION_MIN_MIXIN_15, 0))
+ return 16;
if (use_fork_rules(8, 10))
return 11;
return 0;
@@ -7373,9 +7456,8 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
{
// check if there's a backlog in the tx pool
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
- const uint64_t base_fee = get_base_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(1);
- const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
+ const uint64_t base_fee = get_base_fee(1);
+ const double fee_level = base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
if (blocks.size() != 1)
{
@@ -8412,7 +8494,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{
size_t i = base + n;
if (req.outputs[i].index == td.m_global_output_index)
- if (daemon_resp.outs[i].key == boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key)
+ if (daemon_resp.outs[i].key == td.get_public_key())
if (daemon_resp.outs[i].mask == mask)
if (daemon_resp.outs[i].unlocked)
real_out_found = true;
@@ -8421,7 +8503,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
"Daemon response did not include the requested real output");
// pick real out first (it will be sorted when done)
- outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask));
+ outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), mask));
// then pick outs from an existing ring, if any
if (td.m_key_image_known && !td.m_key_image_partial)
@@ -8512,7 +8594,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
template<typename T>
void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx,
+ bool use_view_tags)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
@@ -8585,7 +8668,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
- real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
+ real_oe.second.dest = rct::pk2rct(td.get_public_key());
real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
*it_to_replace = real_oe;
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index);
@@ -8623,7 +8706,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
std::vector<crypto::secret_key> additional_tx_keys;
rct::multisig_out msout;
LOG_PRINT_L2("constructing tx");
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, m_multisig ? &msout : NULL);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, m_multisig ? &msout : NULL, use_view_tags);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
@@ -8661,6 +8744,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = false;
ptx.construction_data.rct_config = { rct::RangeProofBorromean, 0 };
+ ptx.construction_data.use_view_tags = use_view_tags;
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -8672,7 +8756,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, bool use_view_tags)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
@@ -8857,7 +8941,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
rct::multisig_out msout;
LOG_PRINT_L2("constructing tx");
auto sources_copy = sources;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, m_multisig ? &msout : NULL);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, m_multisig ? &msout : NULL, use_view_tags);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
@@ -8902,7 +8986,8 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("Creating supplementary multisig transaction");
cryptonote::transaction ms_tx;
auto sources_copy_copy = sources_copy;
- bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, rct_config, &msout, false);
+ bool shuffle_outs = false;
+ bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, rct_config, &msout, shuffle_outs, use_view_tags);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
@@ -8946,9 +9031,10 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = true;
ptx.construction_data.rct_config = {
- tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof,
- use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1
+ rct::RangeProofPaddedBulletproof,
+ use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, -10) ? 4 : 3
};
+ ptx.construction_data.use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -9642,14 +9728,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const bool use_rct = use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
const rct::RCTConfig rct_config {
- bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
- bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
+ rct::RangeProofPaddedBulletproof,
+ bulletproof_plus ? 4 : 3
};
+ const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
- const uint64_t base_fee = get_base_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
+ const uint64_t base_fee = get_base_fee(priority);
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
// throw if attempting a transaction with no destinations
@@ -9681,7 +9768,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// early out if we know we can't make it anyway
// we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through
- const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag));
+ const uint64_t min_fee = (base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags));
uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
@@ -9699,11 +9786,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
// determine threshold for fractional amount
- const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag);
- const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag);
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags);
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
- const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
+ const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
// gather all dust and non-dust outputs belonging to specified subaddresses
size_t num_nondust_outputs = 0;
@@ -9797,7 +9884,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
- uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty())
{
@@ -9910,7 +9997,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
+ while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
{
// we can fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
@@ -9927,7 +10014,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- if (!out_slots_exhausted && available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
+ if (!out_slots_exhausted && available_amount > 0 && !dsts.empty() &&
+ estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
// we can partially fill that destination
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@@ -9965,7 +10053,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag);
+ const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags);
try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
}
@@ -9976,7 +10064,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
pending_tx test_ptx;
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
- needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
auto try_carving_from_partial_payment = [&](uint64_t needed_fee, uint64_t available_for_fee)
{
@@ -10025,12 +10113,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
tx.selected_transfers.size() << " inputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx, rct_config);
+ test_tx, test_ptx, rct_config, use_view_tags);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
@@ -10050,12 +10138,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while (needed_fee > test_ptx.fee) {
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx, rct_config);
+ test_tx, test_ptx, rct_config, use_view_tags);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
}
@@ -10123,7 +10211,8 @@ skip_tx:
extra, /* const std::vector<uint8_t>& extra, */
test_tx, /* OUT cryptonote::transaction& tx, */
test_ptx, /* OUT cryptonote::transaction& tx, */
- rct_config);
+ rct_config,
+ use_view_tags); /* const bool use_view_tags */
} else {
transfer_selected(tx.dsts,
tx.selected_transfers,
@@ -10135,7 +10224,8 @@ skip_tx:
detail::digit_split_strategy,
tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD),
test_tx,
- test_ptx);
+ test_ptx,
+ use_view_tags);
}
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx;
@@ -10236,14 +10326,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
// determine threshold for fractional amount
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
- const uint64_t base_fee = get_base_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
- const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag);
- const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag);
+ const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
+ const uint64_t base_fee = get_base_fee(priority);
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus, use_view_tags);
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
- const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
+ const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, false) == 0, error::wallet_internal_error, "No unlocked balance in the specified account");
@@ -10346,13 +10437,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
const rct::RCTConfig rct_config {
- bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
- bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
+ rct::RangeProofPaddedBulletproof,
+ bulletproof_plus ? 4 : 3
};
- const uint64_t base_fee = get_base_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
+ const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
+ const uint64_t base_fee = get_base_fee(priority);
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
@@ -10378,12 +10470,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
uint64_t fee_dust_threshold;
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
{
- const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag);
- fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
+ const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags);
+ fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_quantization_mask);
}
else
{
- fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
+ fee_dust_threshold = base_fee * (upper_transaction_weight_limit + 1023) / 1024;
}
size_t idx =
@@ -10409,7 +10501,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_weight_limit);
- const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag);
+ const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags);
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
if (try_tx) {
@@ -10417,7 +10509,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
pending_tx test_ptx;
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
- needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags, base_fee, fee_quantization_mask);
// add N - 1 outputs for correct initial fee estimation
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
@@ -10427,12 +10519,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.selected_transfers.size() << " outputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx, rct_config);
+ test_tx, test_ptx, rct_config, use_view_tags);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
for (auto &dt: test_ptx.dests)
available_for_fee += dt.amount;
@@ -10464,12 +10556,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
}
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx, rct_config);
+ test_tx, test_ptx, rct_config, use_view_tags);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
} while (needed_fee > test_ptx.fee);
@@ -10503,10 +10595,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
pending_tx test_ptx;
if (use_rct) {
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
- test_tx, test_ptx, rct_config);
+ test_tx, test_ptx, rct_config, use_view_tags);
} else {
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
}
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx;
@@ -10575,7 +10667,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
hw::wallet_shim wallet_shim;
setup_shim(&wallet_shim, this);
aux_data.tx_recipients = dsts_info;
- aux_data.bp_version = (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1);
+ aux_data.bp_version = (use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, -10) ? 4 : use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1);
aux_data.hard_fork = get_current_hard_fork();
dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
tx_device_aux = aux_data.tx_device_aux;
@@ -10796,7 +10888,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
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 uint64_t base_fee = get_base_fee();
+ const uint64_t base_fee = get_base_fee(1);
// may throw
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
@@ -11045,13 +11137,12 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
// derive the real output keypair
const transfer_details& in_td = m_transfers[found->second];
- const txout_to_key* const in_tx_out_pkey = boost::get<txout_to_key>(std::addressof(in_td.m_tx.vout[in_td.m_internal_output_index].target));
- THROW_WALLET_EXCEPTION_IF(in_tx_out_pkey == nullptr, error::wallet_internal_error, "Output is not txout_to_key");
+ crypto::public_key in_tx_out_pkey = in_td.get_public_key();
const crypto::public_key in_tx_pub_key = get_tx_pub_key_from_extra(in_td.m_tx, in_td.m_pk_index);
const std::vector<crypto::public_key> in_additionakl_tx_pub_keys = get_additional_tx_pub_keys_from_extra(in_td.m_tx);
keypair in_ephemeral;
crypto::key_image in_img;
- THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey->key, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device()),
+ THROW_WALLET_EXCEPTION_IF(!generate_key_image_helper(m_account.get_keys(), m_subaddresses, in_tx_out_pkey, in_tx_pub_key, in_additionakl_tx_pub_keys, in_td.m_internal_output_index, in_ephemeral, in_img, m_account.get_device()),
error::wallet_internal_error, "failed to generate key image");
THROW_WALLET_EXCEPTION_IF(in_key->k_image != in_img, error::wallet_internal_error, "key image mismatch");
@@ -11250,24 +11341,12 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
for (size_t n = 0; n < tx.vout.size(); ++n)
{
- const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target));
- if (!out_key)
+ crypto::public_key output_public_key;
+ if (!get_output_public_key(tx.vout[n], output_public_key))
continue;
- crypto::public_key derived_out_key;
- bool r = crypto::derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
- THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
- bool found = out_key->key == derived_out_key;
- crypto::key_derivation found_derivation = derivation;
- if (!found && !additional_derivations.empty())
- {
- r = crypto::derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
- THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
- found = out_key->key == derived_out_key;
- found_derivation = additional_derivations[n];
- }
-
- if (found)
+ crypto::key_derivation found_derivation;
+ if (is_out_to_acc(address, output_public_key, derivation, additional_derivations, n, get_output_view_tag(tx.vout[n]), found_derivation))
{
uint64_t amount;
if (tx.version == 1 || tx.rct_signatures.type == rct::RCTTypeNull)
@@ -11279,8 +11358,10 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
crypto::secret_key scalar1;
crypto::derivation_to_scalar(found_derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
- const rct::key C = tx.rct_signatures.outPk[n].mask;
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
+ rct::key C = tx.rct_signatures.outPk[n].mask;
+ if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type))
+ C = rct::scalarmult8(C);
rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount");
@@ -11359,6 +11440,42 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
}
}
+bool wallet2::is_out_to_acc(const cryptonote::account_public_address &address, const crypto::public_key& out_key, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const size_t output_index, const boost::optional<crypto::view_tag> &view_tag_opt, crypto::key_derivation &found_derivation) const
+{
+ crypto::public_key derived_out_key;
+ bool found = false;
+ bool r;
+ // first run quick check if output has matching view tag, otherwise output should not belong to account
+ if (out_can_be_to_acc(view_tag_opt, derivation, output_index))
+ {
+ // if view tag match, run slower check deriving output pub key and comparing to expected
+ r = crypto::derive_public_key(derivation, output_index, address.m_spend_public_key, derived_out_key);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
+ if (out_key == derived_out_key)
+ {
+ found = true;
+ found_derivation = derivation;
+ }
+ }
+
+ if (!found && !additional_derivations.empty())
+ {
+ const crypto::key_derivation &additional_derivation = additional_derivations[output_index];
+ if (out_can_be_to_acc(view_tag_opt, additional_derivation, output_index))
+ {
+ r = crypto::derive_public_key(additional_derivation, output_index, address.m_spend_public_key, derived_out_key);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
+ if (out_key == derived_out_key)
+ {
+ found = true;
+ found_derivation = additional_derivation;
+ }
+ }
+ }
+
+ return found;
+}
+
std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
{
// fetch tx pubkey from the daemon
@@ -11895,8 +12012,8 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
THROW_WALLET_EXCEPTION_IF(proof.index_in_tx >= tx.vout.size(), error::wallet_internal_error, "index_in_tx is out of bound");
- const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[proof.index_in_tx].target));
- THROW_WALLET_EXCEPTION_IF(!out_key, error::wallet_internal_error, "Output key wasn't found")
+ crypto::public_key output_public_key;
+ THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[proof.index_in_tx], output_public_key), error::wallet_internal_error, "Output key wasn't found");
// get tx pub key
const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
@@ -11911,7 +12028,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
return false;
// check signature for key image
- const std::vector<const crypto::public_key*> pubs = { &out_key->key };
+ const std::vector<const crypto::public_key*> pubs = { &output_public_key };
ok = crypto::check_ring_signature(prefix_hash, proof.key_image, &pubs[0], 1, &proof.key_image_sig);
if (!ok)
return false;
@@ -11920,7 +12037,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::key_derivation derivation;
THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
crypto::public_key subaddr_spendkey;
- crypto::derive_subaddress_public_key(out_key->key, derivation, proof.index_in_tx, subaddr_spendkey);
+ crypto::derive_subaddress_public_key(output_public_key, derivation, proof.index_in_tx, subaddr_spendkey);
THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(subaddr_spendkey) == 0, error::wallet_internal_error,
"The address doesn't seem to have received the fund");
@@ -11932,7 +12049,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::secret_key shared_secret;
crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
amount = rct::h2d(ecdh_info.amount);
}
total += amount;
@@ -12017,10 +12134,10 @@ uint64_t wallet2::get_approximate_blockchain_height() const
const int seconds_per_block = DIFFICULTY_TARGET_V2;
// Calculated blockchain height
uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
- // testnet got some huge rollbacks, so the estimation is way off
- static const uint64_t approximate_testnet_rolled_back_blocks = 342100;
- if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
- approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
+ // testnet and stagenet got some huge rollbacks, so the estimation is way off
+ static const uint64_t approximate_rolled_back_blocks = m_nettype == TESTNET ? 342100 : 30000;
+ if ((m_nettype == TESTNET || m_nettype == STAGENET) && approx_blockchain_height > approximate_rolled_back_blocks)
+ approx_blockchain_height -= approximate_rolled_back_blocks;
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
return approx_blockchain_height;
}
@@ -12374,11 +12491,7 @@ std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>
const transfer_details &td = m_transfers[n];
// get ephemeral public key
- const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index];
- THROW_WALLET_EXCEPTION_IF(out.target.type() != typeid(txout_to_key), error::wallet_internal_error,
- "Output is not txout_to_key");
- const cryptonote::txout_to_key &o = boost::get<const cryptonote::txout_to_key>(out.target);
- const crypto::public_key pkey = o.key;
+ const crypto::public_key pkey = td.get_public_key();
// get tx pub key
std::vector<tx_extra_field> tx_extra_fields;
@@ -12495,11 +12608,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const crypto::signature &signature = signed_key_images[n].second;
// get ephemeral public key
- const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index];
- THROW_WALLET_EXCEPTION_IF(out.target.type() != typeid(txout_to_key), error::wallet_internal_error,
- "Non txout_to_key output found");
- const cryptonote::txout_to_key &o = boost::get<cryptonote::txout_to_key>(out.target);
- const crypto::public_key pkey = o.key;
+ const crypto::public_key pkey = td.get_public_key();
if (!td.m_key_image_known || !(key_image == td.m_key_image))
{
@@ -12853,10 +12962,10 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
}
//----------------------------------------------------------------------------------------------------
-std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const
+std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wallet2::export_outputs(bool all) const
{
PERF_TIMER(export_outputs);
- std::vector<tools::wallet2::transfer_details> outs;
+ std::vector<tools::wallet2::exported_transfer_details> outs;
size_t offset = 0;
if (!all)
@@ -12868,7 +12977,22 @@ std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> wallet2::expo
{
const transfer_details &td = m_transfers[n];
- outs.push_back(td);
+ exported_transfer_details etd;
+ etd.m_pubkey = td.get_public_key();
+ etd.m_tx_pubkey = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index);
+ etd.m_internal_output_index = td.m_internal_output_index;
+ etd.m_global_output_index = td.m_global_output_index;
+ etd.m_flags.flags = 0;
+ etd.m_flags.m_spent = td.m_spent;
+ etd.m_flags.m_frozen = td.m_frozen;
+ etd.m_flags.m_rct = td.m_rct;
+ etd.m_flags.m_key_image_known = td.m_key_image_known;
+ etd.m_flags.m_key_image_request = td.m_key_image_request;
+ etd.m_flags.m_key_image_partial = td.m_key_image_partial;
+ etd.m_amount = td.m_amount;
+ etd.m_additional_tx_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
+
+ outs.push_back(etd);
}
return std::make_pair(offset, outs);
@@ -12940,9 +13064,7 @@ process:
THROW_WALLET_EXCEPTION_IF(td.m_internal_output_index >= td.m_tx.vout.size(),
error::wallet_internal_error, "Internal index is out of range");
- THROW_WALLET_EXCEPTION_IF(td.m_tx.vout[td.m_internal_output_index].target.type() != typeid(cryptonote::txout_to_key),
- error::wallet_internal_error, "Unsupported output type");
- const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
+ crypto::public_key out_key = td.get_public_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");
if (should_expand(td.m_subaddr_index))
@@ -12961,6 +13083,93 @@ process:
return m_transfers.size();
}
//----------------------------------------------------------------------------------------------------
+size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs)
+{
+ PERF_TIMER(import_outputs);
+
+ THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
+ "Imported outputs omit more outputs that we know of. Try using export_outputs all.");
+
+ const size_t offset = outputs.first;
+ const size_t original_size = m_transfers.size();
+ m_transfers.resize(offset + outputs.second.size());
+ for (size_t i = 0; i < offset; ++i)
+ m_transfers[i].m_key_image_request = false;
+ for (size_t i = 0; i < outputs.second.size(); ++i)
+ {
+ exported_transfer_details etd = outputs.second[i];
+ transfer_details &td = m_transfers[i + offset];
+
+ // setup td with "cheao" loaded data
+ td.m_block_height = 0;
+ td.m_txid = crypto::null_hash;
+ td.m_global_output_index = etd.m_global_output_index;
+ td.m_spent = etd.m_flags.m_spent;
+ td.m_frozen = etd.m_flags.m_frozen;
+ td.m_spent_height = 0;
+ td.m_mask = rct::identity();
+ td.m_amount = etd.m_amount;
+ td.m_rct = etd.m_flags.m_rct;
+ td.m_key_image_known = etd.m_flags.m_key_image_known;
+ td.m_key_image_request = etd.m_flags.m_key_image_request;
+ td.m_key_image_partial = false;
+
+ // skip those we've already imported, or which have different data
+ if (i + offset < original_size)
+ {
+ bool needs_processing = false;
+ if (!td.m_key_image_known)
+ needs_processing = true;
+ else if (!(etd.m_internal_output_index == td.m_internal_output_index))
+ needs_processing = true;
+ else if (!(etd.m_pubkey == td.get_public_key()))
+ needs_processing = true;
+
+ if (!needs_processing)
+ continue;
+ }
+
+ // construct a synthetix tx prefix that has the info we'll need: the output with its pubkey, the tx pubkey in extra
+ td.m_tx = {};
+
+ THROW_WALLET_EXCEPTION_IF(etd.m_internal_output_index >= 65536, error::wallet_internal_error, "internal output index seems outrageously high, rejecting");
+ td.m_internal_output_index = etd.m_internal_output_index;
+ cryptonote::txout_to_key tk;
+ tk.key = etd.m_pubkey;
+ cryptonote::tx_out out;
+ out.amount = etd.m_amount;
+ out.target = tk;
+ td.m_tx.vout.resize(etd.m_internal_output_index);
+ td.m_tx.vout.push_back(out);
+
+ td.m_pk_index = 0;
+ add_tx_pub_key_to_extra(td.m_tx, etd.m_tx_pubkey);
+ if (!etd.m_additional_tx_keys.empty())
+ add_additional_tx_pub_keys_to_extra(td.m_tx.extra, etd.m_additional_tx_keys);
+
+ // the hot wallet wouldn't have known about key images (except if we already exported them)
+ cryptonote::keypair in_ephemeral;
+
+ const crypto::public_key &tx_pub_key = etd.m_tx_pubkey;
+ const std::vector<crypto::public_key> &additional_tx_pub_keys = etd.m_additional_tx_keys;
+ const crypto::public_key& out_key = etd.m_pubkey;
+ 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");
+ if (should_expand(td.m_subaddr_index))
+ expand_subaddresses(td.m_subaddr_index);
+ td.m_key_image_known = true;
+ td.m_key_image_request = true;
+ td.m_key_image_partial = false;
+ THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
+ error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i + offset));
+
+ m_key_images[td.m_key_image] = i + offset;
+ m_pub_keys[td.get_public_key()] = i + offset;
+ }
+
+ return m_transfers.size();
+}
+//----------------------------------------------------------------------------------------------------
size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
{
PERF_TIMER(import_outputs_from_str);
@@ -12999,10 +13208,23 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
try
{
std::string body(data, headerlen);
- std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
+
+ std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> new_outputs;
try
{
binary_archive<false> ar{epee::strspan<std::uint8_t>(body)};
+ if (::serialization::serialize(ar, new_outputs))
+ if (::serialization::check_stream_state(ar))
+ loaded = true;
+ }
+ catch (...) {}
+ if (!loaded)
+ new_outputs.second.clear();
+
+ std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
+ if (!loaded) try
+ {
+ binary_archive<false> ar{epee::strspan<std::uint8_t>(body)};
if (::serialization::serialize(ar, outputs))
if (::serialization::check_stream_state(ar))
loaded = true;
@@ -13028,7 +13250,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
outputs.second = {};
}
- imported_outputs = import_outputs(outputs);
+ imported_outputs = new_outputs.second.empty() ? import_outputs(outputs) : import_outputs(new_outputs);
}
catch (const std::exception &e)
{
@@ -14066,9 +14288,11 @@ std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, i
n_outputs = 2; // extra dummy output
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
- size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag);
- uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag);
+ const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
+ size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
+ uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
return std::make_pair(size, weight);
}
//----------------------------------------------------------------------------------------------------