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.cpp787
1 files changed, 345 insertions, 442 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index ac4f67b74..36c98fa8b 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -84,8 +84,8 @@ using namespace cryptonote;
// used to choose when to stop adding outputs to a tx
#define APPROXIMATE_INPUT_BYTES 80
-// used to target a given block size (additional outputs may be added on top to build fee)
-#define TX_SIZE_TARGET(bytes) (bytes*2/3)
+// 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
@@ -183,19 +183,21 @@ uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplie
return kB * fee_per_kb * fee_multiplier;
}
-uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, uint64_t fee_multiplier)
+uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{
- return calculate_fee(fee_per_kb, blob.size(), fee_multiplier);
+ uint64_t fee = weight * base_fee * fee_multiplier;
+ fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
+ return fee;
}
-std::string get_size_string(size_t sz)
+std::string get_weight_string(size_t weight)
{
- return std::to_string(sz) + " bytes (" + std::to_string((sz + 1023) / 1024) + " kB)";
+ return std::to_string(weight) + " weight";
}
-std::string get_size_string(const cryptonote::blobdata &tx)
+std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_size)
{
- return get_size_string(tx.size());
+ return get_weight_string(get_transaction_weight(tx, blob_size));
}
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@@ -576,7 +578,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// rangeSigs
if (bulletproof)
- size += ((2*6 + 4 + 5)*32 + 3) * n_outputs;
+ {
+ 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;
+ }
else
size += (2*64*32+32+64*32) * n_outputs;
@@ -595,23 +602,63 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// txnFee
size += 4;
- LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " with ring size " << (mixin+1) << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
+ 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)");
return size;
}
size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
{
if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size, bulletproof);
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
else
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
}
+uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
+{
+ size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ if (use_rct && bulletproof && n_outputs > 2)
+ {
+ const uint64_t bp_base = 368;
+ 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_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
+ MDEBUG("clawback on size " << size << ": " << bp_clawback);
+ size += bp_clawback;
+ }
+ return size;
+}
+
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)
+ return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
+ else
+ return calculate_fee(base_fee, blob_size, fee_multiplier);
+}
+
crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
{
crypto::hash8 payment_id8 = null_hash8;
@@ -822,14 +869,14 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl, bool trusted_daemon)
+bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon)
{
m_rpc = rpc;
m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected())
m_http_client.disconnect();
m_is_initialized = true;
- m_upper_transaction_size_limit = upper_transaction_size_limit;
+ m_upper_transaction_weight_limit = upper_transaction_weight_limit;
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
m_trusted_daemon = trusted_daemon;
@@ -1142,10 +1189,9 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
switch (rv.type)
{
case rct::RCTTypeSimple:
- case rct::RCTTypeSimpleBulletproof:
+ case rct::RCTTypeBulletproof:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull:
- case rct::RCTTypeFullBulletproof:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
default:
LOG_ERROR("Unsupported rct type: " << rv.type);
@@ -2021,9 +2067,11 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
{
drop_from_short_history(short_chain_history, 3);
+ THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
+
// prepend the last 3 blocks, should be enough to guard against a block or two's reorg
std::vector<parsed_block>::const_reverse_iterator i = prev_parsed_blocks.rbegin();
- for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n)
+ for (size_t n = 0; n < std::min((size_t)3, prev_parsed_blocks.size()); ++n)
{
short_chain_history.push_front(i->hash);
++i;
@@ -2343,6 +2391,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
while (missing_blocks-- > 0)
m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector
m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height));
+ m_blockchain.trim(checkpoint_height);
short_chain_history.clear();
get_short_chain_history(short_chain_history);
}
@@ -2554,10 +2603,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
break;
}
- // switch to the new blocks from the daemon
- blocks_start_height = next_blocks_start_height;
- blocks = std::move(next_blocks);
- parsed_blocks = std::move(next_parsed_blocks);
first = false;
// handle error from async fetching thread
@@ -2565,6 +2610,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
{
throw std::runtime_error("proxy exception in refresh thread");
}
+
+ // switch to the new blocks from the daemon
+ blocks_start_height = next_blocks_start_height;
+ blocks = std::move(next_blocks);
+ parsed_blocks = std::move(next_parsed_blocks);
}
catch (const tools::error::password_needed&)
{
@@ -3325,6 +3375,31 @@ void wallet2::decrypt_keys(const epee::wipeable_string &password)
decrypt_keys(key);
}
+void wallet2::setup_new_blockchain()
+{
+ cryptonote::block b;
+ generate_genesis(b);
+ m_blockchain.push_back(get_block_hash(b));
+ m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
+ add_subaddress_account(tr("Primary account"));
+}
+
+void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file)
+{
+ if (!wallet_.empty())
+ {
+ bool r = store_keys(m_keys_file, password, watch_only);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+
+ if (create_address_file)
+ {
+ r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
+ if(!r) MERROR("String with address text not saved");
+ }
+ }
+}
+
+
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -3402,23 +3477,8 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_key_on_device = false;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3462,23 +3522,9 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_refresh_from_block_height = estimate_blockchain_height();
}
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3559,23 +3605,9 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_key_on_device = false;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, true);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file);
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3613,23 +3645,9 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_key_on_device = false;
setup_keys(password);
- if (!wallet_.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (m_nettype != MAINNET || create_address_file)
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(wallet_, false, password, create_address_file);
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty())
store();
@@ -3641,7 +3659,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
* \param password Password of wallet file
* \param device_name device string address
*/
-void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name)
+void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file)
{
clear();
prepare_file_names(wallet_);
@@ -3660,24 +3678,14 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
m_multisig_signers.clear();
setup_keys(password);
- if (!wallet_.empty()) {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
+ create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
{
// the default lookahead setting (50:200) is clearly too much for hardware wallet
m_subaddress_lookahead_major = 5;
m_subaddress_lookahead_minor = 20;
}
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!wallet_.empty()) {
store();
}
@@ -3773,23 +3781,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
// re-encrypt keys
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
- if (!m_wallet_file.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+ create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
- if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
-
- cryptonote::block b;
- generate_genesis(b);
- m_blockchain.push_back(get_block_hash(b));
- m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (!m_wallet_file.empty())
store();
@@ -3891,17 +3885,7 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
// keys are encrypted again
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
- if (!m_wallet_file.empty())
- {
- bool r = store_keys(m_keys_file, password, false);
- THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
-
- if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
- {
- r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
- if(!r) MERROR("String with address text not saved");
- }
- }
+ create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
m_subaddresses.clear();
m_subaddress_labels.clear();
@@ -4712,12 +4696,7 @@ void wallet2::rescan_blockchain(bool refresh)
{
clear();
- cryptonote::block genesis;
- generate_genesis(genesis);
- crypto::hash genesis_hash = get_block_hash(genesis);
- m_blockchain.push_back(genesis_hash);
- m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
- add_subaddress_account(tr("Primary account"));
+ setup_new_blockchain();
if (refresh)
this->refresh(false);
@@ -4754,7 +4733,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig
uint64_t current_time = static_cast<uint64_t>(time(NULL));
// XXX: this needs to be fast, so we'd need to get the starting heights
// from the daemon to be correct once voting kicks in
- uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827;
+ uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827;
uint64_t leeway = block_height < v2height ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2;
if(current_time + leeway >= unlock_time)
return true;
@@ -4947,69 +4926,6 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo
}
//----------------------------------------------------------------------------------------------------
-void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx)
-{
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx);
-}
-//----------------------------------------------------------------------------------------------------
-void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra)
-{
- cryptonote::transaction tx;
- pending_tx ptx;
- transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx);
-}
-
-namespace {
-// split_amounts(vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
-//
-// split amount for each dst in dsts into num_splits parts
-// and make num_splits new vector<crypt...> instances to hold these new amounts
-std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
- std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
-{
- std::vector<std::vector<cryptonote::tx_destination_entry>> retVal;
-
- if (num_splits <= 1)
- {
- retVal.push_back(dsts);
- return retVal;
- }
-
- // for each split required
- for (size_t i=0; i < num_splits; i++)
- {
- std::vector<cryptonote::tx_destination_entry> new_dsts;
-
- // for each destination
- for (size_t j=0; j < dsts.size(); j++)
- {
- cryptonote::tx_destination_entry de;
- uint64_t amount;
-
- amount = dsts[j].amount;
- amount = amount / num_splits;
-
- // if last split, add remainder
- if (i + 1 == num_splits)
- {
- amount += dsts[j].amount % num_splits;
- }
-
- de.addr = dsts[j].addr;
- de.amount = amount;
-
- new_dsts.push_back(de);
- }
-
- retVal.push_back(new_dsts);
- }
-
- return retVal;
-}
-} // anonymous namespace
-//----------------------------------------------------------------------------------------------------
crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
{
std::vector<tx_extra_field> tx_extra_fields;
@@ -5268,10 +5184,18 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
+ rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
+ if (sd.use_bulletproofs)
+ {
+ range_proof_type = rct::RangeProofBulletproof;
+ for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
+ if (proof.V.size() > 1)
+ range_proof_type = rct::RangeProofPaddedBulletproof;
+ }
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, sd.use_bulletproofs, 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, range_proof_type, m_multisig ? &msout : NULL);
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,
@@ -5684,7 +5608,15 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
cryptonote::transaction tx;
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
auto sources = sd.sources;
- 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, sd.use_bulletproofs, &msout, false);
+ rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
+ if (sd.use_bulletproofs)
+ {
+ range_proof_type = rct::RangeProofBulletproof;
+ for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
+ if (proof.V.size() > 1)
+ range_proof_type = rct::RangeProofPaddedBulletproof;
+ }
+ 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, range_proof_type, &msout, false);
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),
@@ -5784,9 +5716,18 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
{
- static const uint64_t old_multipliers[3] = {1, 2, 3};
- static const uint64_t new_multipliers[3] = {1, 20, 166};
- static const uint64_t newer_multipliers[4] = {1, 4, 20, 166};
+ static const struct
+ {
+ size_t count;
+ uint64_t multipliers[4];
+ }
+ multipliers[] =
+ {
+ { 3, {1, 2, 3} },
+ { 3, {1, 20, 166} },
+ { 4, {1, 4, 20, 166} },
+ { 4, {1, 5, 25, 1000} },
+ };
if (fee_algorithm == -1)
fee_algorithm = get_fee_algorithm();
@@ -5802,47 +5743,68 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
priority = 1;
}
+ THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 3, error::invalid_priority);
+
// 1 to 3/4 are allowed as priorities
- uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3;
+ const uint32_t max_priority = multipliers[fee_algorithm].count;
if (priority >= 1 && priority <= max_priority)
{
- switch (fee_algorithm)
- {
- case 0: return old_multipliers[priority-1];
- case 1: return new_multipliers[priority-1];
- case 2: return newer_multipliers[priority-1];
- default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority);
- }
+ return multipliers[fee_algorithm].multipliers[priority-1];
}
THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority);
return 1;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_dynamic_per_kb_fee_estimate() const
+uint64_t wallet2::get_dynamic_base_fee_estimate() const
{
uint64_t fee;
- boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_per_kb_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
+ boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
if (!result)
return fee;
- LOG_PRINT_L1("Failed to query per kB fee, using " << print_money(FEE_PER_KB));
- return FEE_PER_KB;
+ const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : FEE_PER_KB;
+ LOG_PRINT_L1("Failed to query base fee, using " << print_money(base_fee));
+ return base_fee;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_per_kb_fee() const
+uint64_t wallet2::get_base_fee() const
{
if(m_light_wallet)
- return m_light_wallet_per_kb_fee;
+ {
+ if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
+ return m_light_wallet_per_kb_fee / 1024;
+ else
+ return m_light_wallet_per_kb_fee;
+ }
bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
if (!use_dyn_fee)
return FEE_PER_KB;
- return get_dynamic_per_kb_fee_estimate();
+ return get_dynamic_base_fee_estimate();
+}
+//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_fee_quantization_mask() const
+{
+ if(m_light_wallet)
+ {
+ return 1; // TODO
+ }
+ bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
+ if (!use_per_byte_fee)
+ return 1;
+
+ uint64_t fee_quantization_mask;
+ boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
+ if (result)
+ return 1;
+ return fee_quantization_mask;
}
//----------------------------------------------------------------------------------------------------
int wallet2::get_fee_algorithm() const
{
- // changes at v3 and v5
+ // changes at v3, v5, v8
+ if (use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0))
+ return 3;
if (use_fork_rules(5, 0))
return 2;
if (use_fork_rules(3, -720 * 14))
@@ -5850,19 +5812,39 @@ int wallet2::get_fee_algorithm() const
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_min_ring_size() const
+{
+ if (use_fork_rules(8, 10))
+ return 11;
+ if (use_fork_rules(7, 10))
+ return 7;
+ if (use_fork_rules(6, 10))
+ return 5;
+ if (use_fork_rules(2, 10))
+ return 3;
+ return 0;
+}
+//------------------------------------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_max_ring_size() const
+{
+ if (use_fork_rules(8, 10))
+ return 11;
+ return 0;
+}
+//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::adjust_mixin(uint64_t mixin) const
{
- if (mixin < 6 && use_fork_rules(7, 10)) {
- MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 7, using 7");
- mixin = 6;
- }
- else if (mixin < 4 && use_fork_rules(6, 10)) {
- MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5");
- mixin = 4;
+ const uint64_t min_ring_size = get_min_ring_size();
+ if (mixin + 1 < min_ring_size)
+ {
+ MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size);
+ mixin = min_ring_size-1;
}
- else if (mixin < 2 && use_fork_rules(2, 10)) {
- MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3");
- mixin = 2;
+ const uint64_t max_ring_size = get_max_ring_size();
+ if (max_ring_size && mixin + 1 > max_ring_size)
+ {
+ MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size);
+ mixin = max_ring_size-1;
}
return mixin;
}
@@ -5874,7 +5856,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
try
{
// check if there's a backlog in the tx pool
- const double fee_level = get_fee_multiplier(1) * get_per_kb_fee() * (12/(double)13) / (double)1024;
+ 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 std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
if (blocks.size() != 1)
{
@@ -5888,10 +5873,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
}
// get the current full reward zone
- uint64_t block_size_limit = 0;
- const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
+ uint64_t block_weight_limit = 0;
+ const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info");
- const uint64_t full_reward_zone = block_size_limit / 2;
+ const uint64_t full_reward_zone = block_weight_limit / 2;
// get the last N block headers and sum the block sizes
const size_t N = 10;
@@ -5915,14 +5900,14 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
MERROR("Bad blockheaders size");
return priority;
}
- size_t block_size_sum = 0;
+ size_t block_weight_sum = 0;
for (const cryptonote::block_header_response &i : getbh_res.headers)
{
- block_size_sum += i.block_size;
+ block_weight_sum += i.block_weight;
}
// estimate how 'full' the last N blocks are
- const size_t P = 100 * block_size_sum / (N * full_reward_zone);
+ const size_t P = 100 * block_weight_sum / (N * full_reward_zone);
MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
if (P > 80)
{
@@ -5940,115 +5925,6 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
return priority;
}
//----------------------------------------------------------------------------------------------------
-// separated the call(s) to wallet2::transfer into their own function
-//
-// this function will make multiple calls to wallet2::transfer if multiple
-// transactions will be required
-std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
-{
- const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true);
-
- const uint64_t fee_per_kb = get_per_kb_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
-
- // failsafe split attempt counter
- size_t attempt_count = 0;
-
- for(attempt_count = 1; ;attempt_count++)
- {
- size_t num_tx = 0.5 + pow(1.7,attempt_count-1);
-
- auto split_values = split_amounts(dsts, num_tx);
-
- // Throw if split_amounts comes back with a vector of size different than it should
- if (split_values.size() != num_tx)
- {
- throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested");
- }
-
- std::vector<pending_tx> ptx_vector;
- try
- {
- // for each new destination vector (i.e. for each new tx)
- for (auto & dst_vector : split_values)
- {
- cryptonote::transaction tx;
- pending_tx ptx;
-
- // loop until fee is met without increasing tx size to next KB boundary.
- const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size(), false);
- uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
- do
- {
- transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx);
- auto txBlob = t_serializable_object_to_blob(ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- } while (ptx.fee < needed_fee);
-
- ptx_vector.push_back(ptx);
-
- // mark transfers to be used as "spent"
- for(size_t idx: ptx.selected_transfers)
- {
- set_spent(idx, 0);
- }
- }
-
- // if we made it this far, we've selected our transactions. committing them will mark them spent,
- // so this is a failsafe in case they don't go through
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
-
- }
-
- // if we made it this far, we're OK to actually send the transactions
- return ptx_vector;
-
- }
- // only catch this here, other exceptions need to pass through to the calling function
- catch (const tools::error::tx_too_big& e)
- {
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- if (attempt_count >= MAX_SPLIT_ATTEMPTS)
- {
- throw;
- }
- }
- catch (...)
- {
- // in case of some other exception, make sure any tx in queue are marked unspent again
-
- // unmark pending tx transfers as spent
- for (auto & ptx : ptx_vector)
- {
- // mark transfers to be used as not spent
- for(size_t idx2: ptx.selected_transfers)
- {
- set_unspent(idx2);
- }
- }
-
- throw;
- }
- }
-}
-
bool wallet2::set_ring_database(const std::string &filename)
{
m_ring_database = filename;
@@ -6221,7 +6097,7 @@ bool wallet2::find_and_save_rings(bool force)
return true;
}
-bool wallet2::blackball_output(const crypto::public_key &output)
+bool wallet2::blackball_output(const std::pair<uint64_t, uint64_t> &output)
{
if (!m_ringdb)
return false;
@@ -6229,7 +6105,7 @@ bool wallet2::blackball_output(const crypto::public_key &output)
catch (const std::exception &e) { return false; }
}
-bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add)
+bool wallet2::set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add)
{
if (!m_ringdb)
return false;
@@ -6238,14 +6114,13 @@ bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &out
bool ret = true;
if (!add)
ret &= m_ringdb->clear_blackballs();
- for (const auto &output: outputs)
- ret &= m_ringdb->blackball(output);
+ ret &= m_ringdb->blackball(outputs);
return ret;
}
catch (const std::exception &e) { return false; }
}
-bool wallet2::unblackball_output(const crypto::public_key &output)
+bool wallet2::unblackball_output(const std::pair<uint64_t, uint64_t> &output)
{
if (!m_ringdb)
return false;
@@ -6253,7 +6128,7 @@ bool wallet2::unblackball_output(const crypto::public_key &output)
catch (const std::exception &e) { return false; }
}
-bool wallet2::is_output_blackballed(const crypto::public_key &output) const
+bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const
{
if (!m_ringdb)
return false;
@@ -6298,8 +6173,8 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
return false;
- if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
- return false;
+// if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
+// return false;
outs.back().push_back(item);
return true;
}
@@ -6796,6 +6671,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (seen_indices.count(i))
continue;
+ if (is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs
+ continue;
seen_indices.emplace(i);
LOG_PRINT_L2("picking " << i << " as " << type);
@@ -6818,7 +6695,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, daemon_resp.status);
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " +
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
@@ -6952,7 +6829,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
+ uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee;
LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money));
@@ -7054,10 +6931,10 @@ 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, 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, rct::RangeProofBulletproof, m_multisig ? &msout : NULL);
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_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
+ THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
std::string key_images;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
@@ -7103,13 +6980,13 @@ 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, bool bulletproof)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
+ uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee;
LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money));
LOG_PRINT_L2("selected transfers: " << strjoin(selected_transfers, " "));
@@ -7259,10 +7136,10 @@ 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, bulletproof, 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, range_proof_type, m_multisig ? &msout : NULL);
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_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
+ THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
// work out the permutation done on sources
std::vector<size_t> ins_order;
@@ -7304,10 +7181,10 @@ 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, bulletproof, &msout, 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, range_proof_type, &msout, false);
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_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
+ THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix");
multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout});
@@ -7975,11 +7852,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx;
pending_tx ptx;
- size_t bytes;
+ size_t weight;
uint64_t needed_fee;
std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
- TX() : bytes(0), needed_fee(0) {}
+ TX() : weight(0), needed_fee(0) {}
void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
if (merge_destinations)
@@ -8007,12 +7884,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<TX> txes;
bool adding_fee; // true if new outputs go towards fee, rather than destinations
uint64_t needed_fee, available_for_fee = 0;
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
+ uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
+ 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 rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
- const uint64_t fee_per_kb = get_per_kb_fee();
+ const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
+ const uint64_t fee_quantization_mask = get_fee_quantization_mask();
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
@@ -8043,7 +7923,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 * fee_per_kb * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024;
+ const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024;
uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
@@ -8061,11 +7941,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_size_one_ring = estimate_tx_size(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
- const size_t tx_size_two_rings = estimate_tx_size(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
- THROW_WALLET_EXCEPTION_IF(tx_size_one_ring > tx_size_two_rings, error::wallet_internal_error, "Estimated tx size with 1 input is larger than with 2 inputs!");
- const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring;
- const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024;
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
+ 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);
// gather all dust and non-dust outputs belonging to specified subaddresses
size_t num_nondust_outputs = 0;
@@ -8158,7 +8038,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 = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size(), bulletproof), fee_multiplier);
+ uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty())
{
@@ -8200,7 +8080,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
TX &tx = txes.back();
- LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size());
+ LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size() << ", tx.dsts.size() " << tx.dsts.size());
LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount)));
@@ -8263,7 +8143,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_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) < 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) <<
@@ -8275,7 +8155,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) {
+ if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < 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));
@@ -8287,7 +8167,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// 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_size_limit);
+ << upper_transaction_weight_limit);
bool try_tx = false;
// if we have preferred picks, but haven't yet used all of them, continue
if (preferred_inputs.empty())
@@ -8299,8 +8179,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
- try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
+ 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);
+ try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
}
}
@@ -8308,8 +8188,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction test_tx;
pending_tx test_ptx;
- const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
- needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_multiplier);
uint64_t inputs = 0, outputs = needed_fee;
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
@@ -8326,14 +8205,14 @@ 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, bulletproof);
+ test_tx, test_ptx, range_proof_type);
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);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, 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_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ 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)");
if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
@@ -8369,22 +8248,22 @@ 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, bulletproof);
+ test_tx, test_ptx, range_proof_type);
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);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, 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");
}
- LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made 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");
tx.tx = test_tx;
tx.ptx = test_ptx;
- tx.bytes = txBlob.size();
+ tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs;
tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
@@ -8442,7 +8321,7 @@ skip_tx:
extra, /* const std::vector<uint8_t>& extra, */
test_tx, /* OUT cryptonote::transaction& tx, */
test_ptx, /* OUT cryptonote::transaction& tx, */
- bulletproof);
+ range_proof_type);
} else {
transfer_selected(tx.dsts,
tx.selected_transfers,
@@ -8459,7 +8338,7 @@ skip_tx:
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx;
tx.ptx = test_ptx;
- tx.bytes = txBlob.size();
+ tx.weight = get_transaction_weight(test_tx, txBlob.size());
}
std::vector<wallet2::pending_tx> ptx_vector;
@@ -8470,7 +8349,7 @@ skip_tx:
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -8568,21 +8447,24 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx;
pending_tx ptx;
- size_t bytes;
+ size_t weight;
uint64_t needed_fee;
std::vector<std::vector<get_outs_entry>> outs;
- TX() : bytes(0), needed_fee(0) {}
+ TX() : weight(0), needed_fee(0) {}
};
std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0;
- uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
+ uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
std::vector<std::vector<get_outs_entry>> outs;
+ 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 uint64_t fee_per_kb = get_per_kb_fee();
+ const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
+ const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
+ 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");
@@ -8604,7 +8486,25 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// get a random unspent output and use it to pay next chunk. We try to alternate
// dust and non dust to ensure we never get with only dust, from which we might
// get a tx that can't pay for itself
- size_t idx = unused_transfers_indices.empty() ? pop_best_value(unused_dust_indices, tx.selected_transfers) : unused_dust_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_per_kb * fee_multiplier * (upper_transaction_size_limit + 1023) / 1024) ? pop_best_value(unused_dust_indices, tx.selected_transfers) : pop_best_value(unused_transfers_indices, tx.selected_transfers);
+ 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);
+ fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
+ }
+ else
+ {
+ fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
+ }
+
+ size_t idx =
+ unused_transfers_indices.empty()
+ ? pop_best_value(unused_dust_indices, tx.selected_transfers)
+ : unused_dust_indices.empty()
+ ? pop_best_value(unused_transfers_indices, tx.selected_transfers)
+ : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold)
+ ? pop_best_value(unused_dust_indices, tx.selected_transfers)
+ : pop_best_value(unused_transfers_indices, tx.selected_transfers);
const transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
@@ -8619,16 +8519,15 @@ 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_size_limit);
- const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size(), bulletproof);
- bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_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);
+ 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) {
cryptonote::transaction test_tx;
pending_tx test_ptx;
- const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
- needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
@@ -8636,14 +8535,14 @@ 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, bulletproof);
+ test_tx, test_ptx, range_proof_type);
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);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
- LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ 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)");
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
@@ -8653,22 +8552,22 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.dsts[0].amount = available_for_fee - needed_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, bulletproof);
+ test_tx, test_ptx, range_proof_type);
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);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
- needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
+ needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, 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);
- LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made 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");
tx.tx = test_tx;
tx.ptx = test_ptx;
- tx.bytes = txBlob.size();
+ tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs;
tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
@@ -8692,7 +8591,7 @@ 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, bulletproof);
+ test_tx, test_ptx, range_proof_type);
} 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);
@@ -8700,7 +8599,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx;
tx.ptx = test_ptx;
- tx.bytes = txBlob.size();
+ tx.weight = get_transaction_weight(test_tx, txBlob.size());
}
std::vector<wallet2::pending_tx> ptx_vector;
@@ -8711,7 +8610,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -8746,12 +8645,15 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
return close_enough;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_upper_transaction_size_limit() const
+uint64_t wallet2::get_upper_transaction_weight_limit() const
{
- if (m_upper_transaction_size_limit > 0)
- return m_upper_transaction_size_limit;
+ if (m_upper_transaction_weight_limit > 0)
+ return m_upper_transaction_weight_limit;
uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
- return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ if (use_fork_rules(8, 10))
+ return full_reward_zone / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ else
+ return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const
@@ -8857,16 +8759,14 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_unmixable_outputs()
{
- // request all outputs with less than 3 instances
- const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6
- return select_available_outputs_from_histogram(min_mixin + 1, false, true, false);
+ // request all outputs with less instances than the min ring size
+ return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false);
}
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_mixable_outputs()
{
- // request all outputs with at least 3 instances, so we can use mixin 2 with
- const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6
- return select_available_outputs_from_histogram(min_mixin + 1, true, true, true);
+ // request all outputs with at least as many instances as the min ring size
+ return select_available_outputs_from_histogram(get_min_ring_size(), true, true, true);
}
//----------------------------------------------------------------------------------------------------
std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
@@ -8875,7 +8775,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 fee_per_kb = get_per_kb_fee();
+ const uint64_t base_fee = get_base_fee();
// may throw
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
@@ -8890,7 +8790,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
for (auto n: unmixable_outputs)
{
- if (m_transfers[n].amount() < fee_per_kb)
+ if (m_transfers[n].amount() < base_fee)
unmixable_dust_outputs.push_back(n);
else
unmixable_transfer_outputs.push_back(n);
@@ -9283,6 +9183,8 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
const rct::key C = tx.rct_signatures.outPk[n].mask;
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");
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
if (rct::equalKeys(C, Ctmp))
amount = rct::h2d(ecdh_info.amount);
@@ -9570,6 +9472,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
if (account_minreserve)
{
+ THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
// minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
{ return m_transfers[a].amount() > m_transfers[b].amount(); });
@@ -9853,7 +9756,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const
// 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 = 148540;
+ static const uint64_t approximate_testnet_rolled_back_blocks = 303967;
if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks)
approx_blockchain_height -= approximate_testnet_rolled_back_blocks;
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
@@ -11211,46 +11114,46 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
- uint64_t block_size_limit = 0;
- const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
+ uint64_t block_weight_limit = 0;
+ const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info");
- uint64_t full_reward_zone = block_size_limit / 2;
- THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block size limit from daemon");
+ uint64_t full_reward_zone = block_weight_limit / 2;
+ THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon");
std::vector<std::pair<uint64_t, uint64_t>> blocks;
for (const auto &fee_level: fee_levels)
{
const double our_fee_byte_min = fee_level.first;
const double our_fee_byte_max = fee_level.second;
- uint64_t priority_size_min = 0, priority_size_max = 0;
+ uint64_t priority_weight_min = 0, priority_weight_max = 0;
for (const auto &i: res.backlog)
{
- if (i.blob_size == 0)
+ if (i.weight == 0)
{
- MWARNING("Got 0 sized blob from txpool, ignored");
+ MWARNING("Got 0 weight tx from txpool, ignored");
continue;
}
- double this_fee_byte = i.fee / (double)i.blob_size;
+ double this_fee_byte = i.fee / (double)i.weight;
if (this_fee_byte >= our_fee_byte_min)
- priority_size_min += i.blob_size;
+ priority_weight_min += i.weight;
if (this_fee_byte >= our_fee_byte_max)
- priority_size_max += i.blob_size;
+ priority_weight_max += i.weight;
}
- uint64_t nblocks_min = priority_size_min / full_reward_zone;
- uint64_t nblocks_max = priority_size_max / full_reward_zone;
- MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for "
+ uint64_t nblocks_min = priority_weight_min / full_reward_zone;
+ uint64_t nblocks_max = priority_weight_max / full_reward_zone;
+ MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for "
<< our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, "
- << nblocks_min << " - " << nblocks_max << " blocks at block size " << full_reward_zone);
+ << nblocks_min << " - " << nblocks_max << " blocks at block weight " << full_reward_zone);
blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
}
return blocks;
}
//----------------------------------------------------------------------------------------------------
-std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees)
+std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees)
{
- THROW_WALLET_EXCEPTION_IF(min_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
- THROW_WALLET_EXCEPTION_IF(max_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(min_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(max_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
for (uint64_t fee: fees)
{
THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
@@ -11258,7 +11161,7 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t mi
std::vector<std::pair<double, double>> fee_levels;
for (uint64_t fee: fees)
{
- double our_fee_byte_min = fee / (double)min_blob_size, our_fee_byte_max = fee / (double)max_blob_size;
+ double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (double)max_tx_weight;
fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
}
return estimate_backlog(fee_levels);