@ -455,6 +455,59 @@ 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 )
{
size_t size = 0 ;
// tx prefix
// first few bytes
size + = 1 + 6 ;
// vin
size + = n_inputs * ( 1 + 6 + ( mixin + 1 ) * 2 + 32 ) ;
// vout
size + = n_outputs * ( 6 + 32 ) ;
// extra
size + = extra_size ;
// rct signatures
// type
size + = 1 ;
// rangeSigs
size + = ( 2 * 64 * 32 + 32 + 64 * 32 ) * n_outputs ;
// MGs
size + = n_inputs * ( 64 * ( mixin + 1 ) + 32 ) ;
// mixRing - not serialized, can be reconstructed
/* size += 2 * 32 * (mixin+1) * n_inputs; */
// pseudoOuts
size + = 32 * n_inputs ;
// ecdhInfo
size + = 2 * 32 * n_outputs ;
// outPk - only commitment is saved
size + = 32 * n_outputs ;
// 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) " ) ;
return size ;
}
size_t estimate_tx_size ( bool use_rct , int n_inputs , int mixin , int n_outputs , size_t extra_size )
{
if ( use_rct )
return estimate_rct_tx_size ( n_inputs , mixin , n_outputs + 1 , extra_size ) ;
else
return n_inputs * ( mixin + 1 ) * APPROXIMATE_INPUT_BYTES + extra_size ;
}
} //namespace
} //namespace
namespace tools
namespace tools
@ -3990,7 +4043,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
pending_tx ptx ;
pending_tx ptx ;
// loop until fee is met without increasing tx size to next KB boundary.
// loop until fee is met without increasing tx size to next KB boundary.
uint64_t needed_fee = 0 ;
const size_t estimated_tx_size = estimate_tx_size ( false , unused_transfers_indices . size ( ) , fake_outs_count , dst_vector . size ( ) , extra . size ( ) ) ;
uint64_t needed_fee = calculate_fee ( fee_per_kb , estimated_tx_size , fee_multiplier ) ;
do
do
{
{
transfer ( dst_vector , fake_outs_count , unused_transfers_indices , unlock_time , needed_fee , extra , tx , ptx , trusted_daemon ) ;
transfer ( dst_vector , fake_outs_count , unused_transfers_indices , unlock_time , needed_fee , extra , tx , ptx , trusted_daemon ) ;
@ -4725,59 +4779,6 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2 ( " transfer_selected_rct done " ) ;
LOG_PRINT_L2 ( " transfer_selected_rct done " ) ;
}
}
static size_t estimate_rct_tx_size ( int n_inputs , int mixin , int n_outputs )
{
size_t size = 0 ;
// tx prefix
// first few bytes
size + = 1 + 6 ;
// vin
size + = n_inputs * ( 1 + 6 + ( mixin + 1 ) * 2 + 32 ) ;
// vout
size + = n_outputs * ( 6 + 32 ) ;
// extra
size + = 40 ;
// rct signatures
// type
size + = 1 ;
// rangeSigs
size + = ( 2 * 64 * 32 + 32 + 64 * 32 ) * n_outputs ;
// MGs
size + = n_inputs * ( 64 * ( mixin + 1 ) + 32 ) ;
// mixRing - not serialized, can be reconstructed
/* size += 2 * 32 * (mixin+1) * n_inputs; */
// pseudoOuts
size + = 32 * n_inputs ;
// ecdhInfo
size + = 2 * 32 * n_outputs ;
// outPk - only commitment is saved
size + = 32 * n_outputs ;
// 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) " ) ;
return size ;
}
static size_t estimate_tx_size ( bool use_rct , int n_inputs , int mixin , int n_outputs )
{
if ( use_rct )
return estimate_rct_tx_size ( n_inputs , mixin , n_outputs + 1 ) ;
else
return n_inputs * ( mixin + 1 ) * APPROXIMATE_INPUT_BYTES ;
}
std : : vector < size_t > wallet2 : : pick_preferred_rct_inputs ( uint64_t needed_money , uint32_t subaddr_account , const std : : set < uint32_t > & subaddr_indices ) const
std : : vector < size_t > wallet2 : : pick_preferred_rct_inputs ( uint64_t needed_money , uint32_t subaddr_account , const std : : set < uint32_t > & subaddr_indices ) const
{
{
std : : vector < size_t > picks ;
std : : vector < size_t > picks ;
@ -4785,19 +4786,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
LOG_PRINT_L2 ( " pick_preferred_rct_inputs: needed_money " < < print_money ( needed_money ) ) ;
LOG_PRINT_L2 ( " pick_preferred_rct_inputs: needed_money " < < print_money ( needed_money ) ) ;
// try to find a rct input of enough size
// try to find two outputs
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
{
const transfer_details & td = m_transfers [ i ] ;
if ( ! td . m_spent & & td . is_rct ( ) & & td . amount ( ) > = needed_money & & is_transfer_unlocked ( td ) & & td . m_subaddr_index . major = = subaddr_account & & subaddr_indices . count ( td . m_subaddr_index . minor ) = = 1 )
{
LOG_PRINT_L2 ( " We can use " < < i < < " alone: " < < print_money ( td . amount ( ) ) ) ;
picks . push_back ( i ) ;
return picks ;
}
}
// then try to find two outputs
// this could be made better by picking one of the outputs to be a small one, since those
// this could be made better by picking one of the outputs to be a small one, since those
// are less useful since often below the needed money, so if one can be used in a pair,
// are less useful since often below the needed money, so if one can be used in a pair,
// it gets rid of it for the future
// it gets rid of it for the future
@ -5551,7 +5540,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
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
// will get us a known fee.
uint64_t estimated_fee = calculate_fee ( fee_per_kb , estimate_rct_tx_size ( 2 , fake_outs_count + 1 , 2 ) , fee_multiplier ) ;
uint64_t estimated_fee = calculate_fee ( fee_per_kb , estimate_rct_tx_size ( 2 , fake_outs_count , 2 , extra . size ( ) ) , fee_multiplier ) ;
preferred_inputs = pick_preferred_rct_inputs ( needed_money + estimated_fee , subaddr_account , subaddr_indices ) ;
preferred_inputs = pick_preferred_rct_inputs ( needed_money + estimated_fee , subaddr_account , subaddr_indices ) ;
if ( ! preferred_inputs . empty ( ) )
if ( ! preferred_inputs . empty ( ) )
{
{
@ -5588,7 +5577,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
unsigned int original_output_index = 0 ;
unsigned int original_output_index = 0 ;
std : : vector < size_t > * unused_transfers_indices = & unused_transfers_indices_per_subaddr [ 0 ] . second ;
std : : vector < size_t > * unused_transfers_indices = & unused_transfers_indices_per_subaddr [ 0 ] . second ;
std : : vector < size_t > * unused_dust_indices = & unused_dust_indices_per_subaddr [ 0 ] . second ;
std : : vector < size_t > * unused_dust_indices = & unused_dust_indices_per_subaddr [ 0 ] . second ;
while ( ( ! dsts . empty ( ) & & dsts [ 0 ] . amount > 0 ) | | adding_fee | | should_pick_a_second_output ( use_rct , txes . back ( ) . selected_transfers . size ( ) , * unused_transfers_indices , * unused_dust_indices ) ) {
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 ( ) ;
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 ( ) ) ;
@ -5606,7 +5595,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
// get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
// This could be more clever, but maybe at the cost of making probabilistic inferences easier
// This could be more clever, but maybe at the cost of making probabilistic inferences easier
size_t idx ;
size_t idx ;
if ( ( dsts . empty ( ) | | dsts [ 0 ] . amount = = 0 ) & & ! adding_fee ) {
if ( ! preferred_inputs . empty ( ) ) {
idx = pop_back ( preferred_inputs ) ;
pop_if_present ( * unused_transfers_indices , idx ) ;
pop_if_present ( * unused_dust_indices , idx ) ;
} else if ( ( dsts . empty ( ) | | dsts [ 0 ] . amount = = 0 ) & & ! adding_fee ) {
// the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
// the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
std : : vector < size_t > indices = get_only_rct ( * unused_dust_indices , * unused_transfers_indices ) ;
std : : vector < size_t > indices = get_only_rct ( * unused_dust_indices , * unused_transfers_indices ) ;
idx = pop_best_value ( indices , tx . selected_transfers , true ) ;
idx = pop_best_value ( indices , tx . selected_transfers , true ) ;
@ -5629,10 +5622,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
pop_if_present ( * unused_transfers_indices , idx ) ;
pop_if_present ( * unused_transfers_indices , idx ) ;
pop_if_present ( * unused_dust_indices , idx ) ;
pop_if_present ( * unused_dust_indices , idx ) ;
} else if ( ! preferred_inputs . empty ( ) ) {
idx = pop_back ( preferred_inputs ) ;
pop_if_present ( * unused_transfers_indices , idx ) ;
pop_if_present ( * unused_dust_indices , idx ) ;
} else
} else
idx = pop_best_value ( unused_transfers_indices - > empty ( ) ? * unused_dust_indices : * unused_transfers_indices , tx . selected_transfers ) ;
idx = pop_best_value ( unused_transfers_indices - > empty ( ) ? * unused_dust_indices : * unused_transfers_indices , tx . selected_transfers ) ;
@ -5654,7 +5643,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
else
else
{
{
while ( ! dsts . empty ( ) & & dsts [ 0 ] . amount < = available_amount & & estimate_tx_size ( use_rct , tx . selected_transfers . size ( ) , fake_outs_count , tx . dsts . size ( ) ) < TX_SIZE_TARGET ( upper_transaction_size_limit ) )
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 ( ) ) < TX_SIZE_TARGET ( upper_transaction_size_limit ) )
{
{
// we can fully pay that destination
// we can fully pay that destination
LOG_PRINT_L2 ( " We can fully pay " < < get_account_address_as_str ( m_testnet , dsts [ 0 ] . is_subaddress , dsts [ 0 ] . addr ) < <
LOG_PRINT_L2 ( " We can fully pay " < < get_account_address_as_str ( m_testnet , dsts [ 0 ] . is_subaddress , dsts [ 0 ] . addr ) < <
@ -5666,7 +5655,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
+ + original_output_index ;
+ + original_output_index ;
}
}
if ( available_amount > 0 & & ! dsts . empty ( ) & & estimate_tx_size ( use_rct , tx . selected_transfers . size ( ) , fake_outs_count , tx . dsts . size ( ) ) < TX_SIZE_TARGET ( upper_transaction_size_limit ) ) {
if ( available_amount > 0 & & ! dsts . empty ( ) & & estimate_tx_size ( use_rct , tx . selected_transfers . size ( ) , fake_outs_count , tx . dsts . size ( ) , extra . size ( ) ) < TX_SIZE_TARGET ( upper_transaction_size_limit ) ) {
// we can partially fill that destination
// we can partially fill that destination
LOG_PRINT_L2 ( " We can partially pay " < < get_account_address_as_str ( m_testnet , dsts [ 0 ] . is_subaddress , dsts [ 0 ] . addr ) < <
LOG_PRINT_L2 ( " We can partially pay " < < get_account_address_as_str ( m_testnet , dsts [ 0 ] . is_subaddress , dsts [ 0 ] . addr ) < <
" for " < < print_money ( available_amount ) < < " / " < < print_money ( dsts [ 0 ] . amount ) ) ;
" for " < < print_money ( available_amount ) < < " / " < < print_money ( dsts [ 0 ] . amount ) ) ;
@ -5679,26 +5668,31 @@ 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
// 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 "
LOG_PRINT_L2 ( " Considering whether to create a tx now, " < < tx . selected_transfers . size ( ) < < " inputs, tx limit "
< < upper_transaction_size_limit ) ;
< < upper_transaction_size_limit ) ;
bool try_tx ;
bool try_tx = false ;
if ( adding_fee )
// if we have preferred picks, but haven't yet used all of them, continue
{
if ( preferred_inputs . empty ( ) )
/* might not actually be enough if adding this output bumps size to next kB, but we need to try */
try_tx = available_for_fee > = needed_fee ;
}
else
{
{
const size_t estimated_rct_tx_size = estimate_tx_size ( use_rct , tx . selected_transfers . size ( ) , fake_outs_count , tx . dsts . size ( ) ) ;
if ( adding_fee )
try_tx = dsts . empty ( ) | | ( estimated_rct_tx_size > = TX_SIZE_TARGET ( upper_transaction_size_limit ) ) ;
{
/* might not actually be enough if adding this output bumps size to next kB, but we need to try */
try_tx = available_for_fee > = needed_fee ;
}
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 ( ) ) ;
try_tx = dsts . empty ( ) | | ( estimated_rct_tx_size > = TX_SIZE_TARGET ( upper_transaction_size_limit ) ) ;
}
}
}
if ( try_tx ) {
if ( try_tx ) {
cryptonote : : transaction test_tx ;
cryptonote : : transaction test_tx ;
pending_tx test_ptx ;
pending_tx test_ptx ;
needed_fee = 0 ;
const size_t estimated_tx_size = estimate_tx_size ( use_rct , tx . selected_transfers . size ( ) , fake_outs_count , tx . dsts . size ( ) , extra . size ( ) ) ;
needed_fee = calculate_fee ( fee_per_kb , estimated_tx_size , fee_multiplier ) ;
LOG_PRINT_L2 ( " Trying to create a tx now, with " < < tx . dsts . size ( ) < < " destination s and " < <
LOG_PRINT_L2 ( " Trying to create a tx now, with " < < tx . dsts . size ( ) < < " output s and " < <
tx . selected_transfers . size ( ) < < " out puts" ) ;
tx . selected_transfers . size ( ) < < " in puts" ) ;
if ( use_rct )
if ( use_rct )
transfer_selected_rct ( tx . dsts , tx . selected_transfers , fake_outs_count , outs , unlock_time , needed_fee , extra ,
transfer_selected_rct ( tx . dsts , tx . selected_transfers , fake_outs_count , outs , unlock_time , needed_fee , extra ,
test_tx , test_ptx ) ;
test_tx , test_ptx ) ;
@ -5740,8 +5734,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
else
else
{
{
LOG_PRINT_L2 ( " We made a tx, adjusting fee and saving it " ) ;
LOG_PRINT_L2 ( " We made a tx, adjusting fee and saving it , we need " < < print_money ( needed_fee ) < < " and we have " < < print_money ( test_ptx . fee ) ) ;
do {
while ( needed_fee > test_ptx . fee ) {
if ( use_rct )
if ( use_rct )
transfer_selected_rct ( tx . dsts , tx . selected_transfers , fake_outs_count , outs , unlock_time , needed_fee , extra ,
transfer_selected_rct ( tx . dsts , tx . selected_transfers , fake_outs_count , outs , unlock_time , needed_fee , extra ,
test_tx , test_ptx ) ;
test_tx , test_ptx ) ;
@ -5752,7 +5746,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
needed_fee = calculate_fee ( fee_per_kb , txBlob , fee_multiplier ) ;
needed_fee = calculate_fee ( fee_per_kb , txBlob , fee_multiplier ) ;
LOG_PRINT_L2 ( " Made an attempt at a final " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( test_ptx . fee ) < <
LOG_PRINT_L2 ( " Made an attempt at a final " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( test_ptx . fee ) < <
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
} while ( needed_fee > test_ptx . fee ) ;
}
LOG_PRINT_L2 ( " Made a final " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( test_ptx . fee ) < <
LOG_PRINT_L2 ( " Made a final " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( test_ptx . fee ) < <
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
@ -5934,14 +5928,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
// 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 "
LOG_PRINT_L2 ( " Considering whether to create a tx now, " < < tx . selected_transfers . size ( ) < < " inputs, tx limit "
< < upper_transaction_size_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 );
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 ( ) );
bool try_tx = ( unused_dust_indices . empty ( ) & & unused_transfers_indices . empty ( ) ) | | ( estimated_rct_tx_size > = TX_SIZE_TARGET ( upper_transaction_size_limit ) ) ;
bool try_tx = ( unused_dust_indices . empty ( ) & & unused_transfers_indices . empty ( ) ) | | ( estimated_rct_tx_size > = TX_SIZE_TARGET ( upper_transaction_size_limit ) ) ;
if ( try_tx ) {
if ( try_tx ) {
cryptonote : : transaction test_tx ;
cryptonote : : transaction test_tx ;
pending_tx test_ptx ;
pending_tx test_ptx ;
needed_fee = 0 ;
const size_t estimated_tx_size = estimate_tx_size ( use_rct , tx . selected_transfers . size ( ) , fake_outs_count , tx . dsts . size ( ) , extra . size ( ) ) ;
needed_fee = calculate_fee ( fee_per_kb , estimated_tx_size , fee_multiplier ) ;
tx . dsts . push_back ( tx_destination_entry ( 1 , address , is_subaddress ) ) ;
tx . dsts . push_back ( tx_destination_entry ( 1 , address , is_subaddress ) ) ;
@ -5961,7 +5956,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
THROW_WALLET_EXCEPTION_IF ( needed_fee > available_for_fee , error : : wallet_internal_error , " Transaction cannot pay for itself " ) ;
THROW_WALLET_EXCEPTION_IF ( needed_fee > available_for_fee , error : : wallet_internal_error , " Transaction cannot pay for itself " ) ;
do {
while ( needed_fee > test_ptx . fee ) {
LOG_PRINT_L2 ( " We made a tx, adjusting fee and saving it " ) ;
LOG_PRINT_L2 ( " We made a tx, adjusting fee and saving it " ) ;
tx . dsts [ 0 ] . amount = available_for_fee - needed_fee ;
tx . dsts [ 0 ] . amount = available_for_fee - needed_fee ;
if ( use_rct )
if ( use_rct )
@ -5974,7 +5969,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
needed_fee = calculate_fee ( fee_per_kb , txBlob , fee_multiplier ) ;
needed_fee = calculate_fee ( fee_per_kb , txBlob , fee_multiplier ) ;
LOG_PRINT_L2 ( " Made an attempt at a final " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( test_ptx . fee ) < <
LOG_PRINT_L2 ( " Made an attempt at a final " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( test_ptx . fee ) < <
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
} while ( needed_fee > test_ptx . fee ) ;
}
LOG_PRINT_L2 ( " Made a final " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( test_ptx . fee ) < <
LOG_PRINT_L2 ( " Made a final " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( test_ptx . fee ) < <
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;