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.cpp241
1 files changed, 177 insertions, 64 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 5d78bb7b0..3e2ccd1ff 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -243,6 +243,22 @@ namespace
add_reason(reason, "tx was not relayed");
return reason;
}
+
+ size_t get_num_outputs(const std::vector<cryptonote::tx_destination_entry> &dsts, const std::vector<tools::wallet2::transfer_details> &transfers, const std::vector<size_t> &selected_transfers)
+ {
+ size_t outputs = dsts.size();
+ uint64_t needed_money = 0;
+ for (const auto& dt: dsts)
+ needed_money += dt.amount;
+ uint64_t found_money = 0;
+ for(size_t idx: selected_transfers)
+ found_money += transfers[idx].amount();
+ if (found_money != needed_money)
+ ++outputs; // change
+ if (outputs < 2)
+ ++outputs; // extra 0 dummy output
+ return outputs;
+ }
}
namespace
@@ -795,7 +811,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)
+size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
{
size_t size = 0;
@@ -829,8 +845,11 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
else
size += (2*64*32+32+64*32) * n_outputs;
- // MGs
- size += n_inputs * (64 * (mixin+1) + 32);
+ // MGs/CLSAGs
+ if (clsag)
+ size += n_inputs * (32 * (mixin+1) + 64);
+ else
+ size += n_inputs * (64 * (mixin+1) + 32);
// mixRing - not serialized, can be reconstructed
/* size += 2 * 32 * (mixin+1) * n_inputs; */
@@ -848,17 +867,17 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
return size;
}
-size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
+size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
{
if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
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)
+uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
{
- size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
if (use_rct && bulletproof && n_outputs > 2)
{
const uint64_t bp_base = 368;
@@ -879,6 +898,11 @@ uint8_t get_bulletproof_fork()
return 8;
}
+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)
{
if (use_per_byte_fee)
@@ -1752,6 +1776,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
case rct::RCTTypeSimple:
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
+ case rct::RCTTypeCLSAG:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
@@ -1924,7 +1949,7 @@ 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;
+ tools::threadpool::waiter waiter(tpool);
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
@@ -1997,7 +2022,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
}
- waiter.wait(&tpool);
+ 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);
@@ -2024,7 +2049,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
}
- waiter.wait(&tpool);
+ 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);
@@ -2662,7 +2687,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error);
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
size_t num_txes = 0;
std::vector<tx_cache_data> tx_cache_data;
@@ -2689,7 +2714,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
}
}
THROW_WALLET_EXCEPTION_IF(txidx != num_txes, error::wallet_internal_error, "txidx does not match tx_cache_data size");
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
hw::device &hwdev = m_account.get_device();
hw::reset_mode rst(hwdev);
@@ -2718,7 +2743,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
gender(iod);
}, true);
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
auto geniod = [&](const cryptonote::transaction &tx, size_t n_vouts, size_t txidx) {
for (size_t k = 0; k < n_vouts; ++k)
@@ -2766,7 +2791,7 @@ 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 did not reach expected value");
- waiter.wait(&tpool);
+ 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;
@@ -2838,14 +2863,14 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
parsed_blocks.resize(blocks.size());
for (size_t i = 0; i < blocks.size(); ++i)
{
tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block),
std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error)), true);
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
for (size_t i = 0; i < blocks.size(); ++i)
{
if (parsed_blocks[i].error)
@@ -2871,7 +2896,7 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
}, true);
}
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
last = !blocks.empty() && cryptonote::get_block_height(parsed_blocks.back().block) + 1 == current_height;
}
catch(...)
@@ -3313,7 +3338,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
std::list<crypto::hash> short_chain_history;
tools::threadpool& tpool = tools::threadpool::getInstance();
- tools::threadpool::waiter waiter;
+ tools::threadpool::waiter waiter(tpool);
uint64_t blocks_start_height;
std::vector<cryptonote::block_complete_entry> blocks;
std::vector<parsed_block> parsed_blocks;
@@ -3421,7 +3446,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
}
blocks_fetched += added_blocks;
}
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
if(!first && blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
@@ -3453,19 +3478,19 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
catch (const tools::error::password_needed&)
{
blocks_fetched += added_blocks;
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
throw;
}
catch (const error::payment_required&)
{
// no point in trying again, it'd just eat up credits
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
throw;
}
catch (const std::exception&)
{
blocks_fetched += added_blocks;
- waiter.wait(&tpool);
+ THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
if(try_count < 3)
{
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
@@ -7354,16 +7379,16 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
return sign_multisig_tx_to_file(exported_txs, filename, txids);
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
+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
{
if (use_per_byte_fee)
{
- const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ 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);
}
else
{
- const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ 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);
}
}
@@ -9066,7 +9091,10 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.construction_data.extra = tx.extra;
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_SMALLER_BP, -10) ? 2 : 1};
+ 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
+ };
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
ptx.construction_data.subaddr_account = subaddr_account;
@@ -9752,9 +9780,10 @@ 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 clsag = use_fork_rules(get_clsag_fork(), 0);
const rct::RCTConfig rct_config {
bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
- bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
+ bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
};
const uint64_t base_fee = get_base_fee();
@@ -9790,7 +9819,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));
+ const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag));
uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
@@ -9808,8 +9837,8 @@ 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);
- const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
+ 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);
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);
@@ -9906,7 +9935,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, 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, 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())
{
@@ -10018,7 +10047,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) < 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) < 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) <<
@@ -10030,7 +10059,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- 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)) {
+ 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, clsag) < 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));
@@ -10054,7 +10083,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);
+ 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);
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);
}
@@ -10064,7 +10093,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction test_tx;
pending_tx test_ptx;
- 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);
+ 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);
uint64_t inputs = 0, outputs = needed_fee;
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
@@ -10313,10 +10343,11 @@ 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 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);
- const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
+ 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);
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);
@@ -10422,9 +10453,10 @@ 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 clsag = use_fork_rules(get_clsag_fork(), 0);
const rct::RCTConfig rct_config {
bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
- bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
+ bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
};
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
@@ -10453,7 +10485,7 @@ 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);
+ 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);
}
else
@@ -10484,14 +10516,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_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);
+ 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);
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;
- 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);
+ 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);
// add N - 1 outputs for correct initial fee estimation
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
@@ -11353,7 +11386,7 @@ 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);
+ 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::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
@@ -11997,7 +12030,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);
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
amount = rct::h2d(ecdh_info.amount);
}
total += amount;
@@ -12174,51 +12207,130 @@ void wallet2::set_account_tag_description(const std::string& tag, const std::str
m_account_tags.first[tag] = description;
}
-std::string wallet2::sign(const std::string &data, cryptonote::subaddress_index index) const
-{
+// Set up an address signature message hash
+// Hash data: domain separator, spend public key, view public key, mode identifier, payload data
+static crypto::hash get_message_hash(const std::string &data, const crypto::public_key &spend_key, const crypto::public_key &view_key, const uint8_t mode)
+{
+ KECCAK_CTX ctx;
+ keccak_init(&ctx);
+ keccak_update(&ctx, (const uint8_t*)config::HASH_KEY_MESSAGE_SIGNING, sizeof(config::HASH_KEY_MESSAGE_SIGNING)); // includes NUL
+ keccak_update(&ctx, (const uint8_t*)&spend_key, sizeof(crypto::public_key));
+ keccak_update(&ctx, (const uint8_t*)&view_key, sizeof(crypto::public_key));
+ keccak_update(&ctx, (const uint8_t*)&mode, sizeof(uint8_t));
+ char len_buf[(sizeof(size_t) * 8 + 6) / 7];
+ char *ptr = len_buf;
+ tools::write_varint(ptr, data.size());
+ CHECK_AND_ASSERT_THROW_MES(ptr > len_buf && ptr <= len_buf + sizeof(len_buf), "Length overflow");
+ keccak_update(&ctx, (const uint8_t*)len_buf, ptr - len_buf);
+ keccak_update(&ctx, (const uint8_t*)data.data(), data.size());
crypto::hash hash;
- crypto::cn_fast_hash(data.data(), data.size(), hash);
+ keccak_finish(&ctx, (uint8_t*)&hash);
+ return hash;
+}
+
+// Sign a message with a private key from either the base address or a subaddress
+// The signature is also bound to both keys and the signature mode (spend, view) to prevent unintended reuse
+std::string wallet2::sign(const std::string &data, message_signature_type_t signature_type, cryptonote::subaddress_index index) const
+{
const cryptonote::account_keys &keys = m_account.get_keys();
crypto::signature signature;
- crypto::secret_key skey;
+ crypto::secret_key skey, m;
+ crypto::secret_key skey_spend, skey_view;
crypto::public_key pkey;
+ crypto::public_key pkey_spend, pkey_view; // to include both in hash
+ crypto::hash hash;
+ uint8_t mode;
+
+ // Use the base address
if (index.is_zero())
{
- skey = keys.m_spend_secret_key;
- pkey = keys.m_account_address.m_spend_public_key;
+ switch (signature_type)
+ {
+ case sign_with_spend_key:
+ skey = keys.m_spend_secret_key;
+ pkey = keys.m_account_address.m_spend_public_key;
+ mode = 0;
+ break;
+ case sign_with_view_key:
+ skey = keys.m_view_secret_key;
+ pkey = keys.m_account_address.m_view_public_key;
+ mode = 1;
+ break;
+ default: CHECK_AND_ASSERT_THROW_MES(false, "Invalid signature type requested");
+ }
+ hash = get_message_hash(data,keys.m_account_address.m_spend_public_key,keys.m_account_address.m_view_public_key,mode);
}
+ // Use a subaddress
else
{
- skey = keys.m_spend_secret_key;
- crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(keys.m_view_secret_key, index);
- sc_add((unsigned char*)&skey, (unsigned char*)&m, (unsigned char*)&skey);
+ skey_spend = keys.m_spend_secret_key;
+ m = m_account.get_device().get_subaddress_secret_key(keys.m_view_secret_key, index);
+ sc_add((unsigned char*)&skey_spend, (unsigned char*)&m, (unsigned char*)&skey_spend);
+ secret_key_to_public_key(skey_spend,pkey_spend);
+ sc_mul((unsigned char*)&skey_view, (unsigned char*)&keys.m_view_secret_key, (unsigned char*)&skey_spend);
+ secret_key_to_public_key(skey_view,pkey_view);
+ switch (signature_type)
+ {
+ case sign_with_spend_key:
+ skey = skey_spend;
+ pkey = pkey_spend;
+ mode = 0;
+ break;
+ case sign_with_view_key:
+ skey = skey_view;
+ pkey = pkey_view;
+ mode = 1;
+ break;
+ default: CHECK_AND_ASSERT_THROW_MES(false, "Invalid signature type requested");
+ }
secret_key_to_public_key(skey, pkey);
+ hash = get_message_hash(data,pkey_spend,pkey_view,mode);
}
crypto::generate_signature(hash, pkey, skey, signature);
- return std::string("SigV1") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
+ return std::string("SigV2") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
}
-bool wallet2::verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const
+tools::wallet2::message_signature_result_t wallet2::verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const
{
- const size_t header_len = strlen("SigV1");
- if (signature.size() < header_len || signature.substr(0, header_len) != "SigV1") {
+ static const size_t v1_header_len = strlen("SigV1");
+ static const size_t v2_header_len = strlen("SigV2");
+ const bool v1 = signature.size() >= v1_header_len && signature.substr(0, v1_header_len) == "SigV1";
+ const bool v2 = signature.size() >= v2_header_len && signature.substr(0, v2_header_len) == "SigV2";
+ if (!v1 && !v2)
+ {
LOG_PRINT_L0("Signature header check error");
- return false;
+ return {};
}
crypto::hash hash;
- crypto::cn_fast_hash(data.data(), data.size(), hash);
+ if (v1)
+ {
+ crypto::cn_fast_hash(data.data(), data.size(), hash);
+ }
std::string decoded;
- if (!tools::base58::decode(signature.substr(header_len), decoded)) {
+ if (!tools::base58::decode(signature.substr(v1 ? v1_header_len : v2_header_len), decoded)) {
LOG_PRINT_L0("Signature decoding error");
- return false;
+ return {};
}
crypto::signature s;
if (sizeof(s) != decoded.size()) {
LOG_PRINT_L0("Signature decoding error");
- return false;
+ return {};
}
memcpy(&s, decoded.data(), sizeof(s));
- return crypto::check_signature(hash, address.m_spend_public_key, s);
+
+ // Test each mode and return which mode, if either, succeeded
+ if (v2)
+ hash = get_message_hash(data,address.m_spend_public_key,address.m_view_public_key,(uint8_t) 0);
+ if (crypto::check_signature(hash, address.m_spend_public_key, s))
+ return {true, v1 ? 1u : 2u, !v2, sign_with_spend_key };
+
+ if (v2)
+ hash = get_message_hash(data,address.m_spend_public_key,address.m_view_public_key,(uint8_t) 1);
+ if (crypto::check_signature(hash, address.m_view_public_key, s))
+ return {true, v1 ? 1u : 2u, !v2, sign_with_view_key };
+
+ // Both modes failed
+ return {};
}
std::string wallet2::sign_multisig_participant(const std::string& data) const
@@ -14036,8 +14148,9 @@ 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);
- size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof);
- uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof);
+ 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);
return std::make_pair(size, weight);
}
//----------------------------------------------------------------------------------------------------