@ -218,17 +218,6 @@ namespace
return reason ;
return reason ;
}
}
bool is_preferred_input ( const std : : vector < crypto : : key_image > & preferred_input_list , const crypto : : key_image & input ) {
bool res = true ;
if ( preferred_input_list . size ( ) > 0 ) {
auto it = std : : find ( preferred_input_list . begin ( ) , preferred_input_list . end ( ) , input ) ;
if ( it = = preferred_input_list . end ( ) ) {
res = false ;
}
}
return res ;
}
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 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 ( ) ;
size_t outputs = dsts . size ( ) ;
@ -4404,10 +4393,7 @@ void wallet2::handle_reorg(uint64_t height, std::map<std::pair<uint64_t, uint64_
// C
// C
THROW_WALLET_EXCEPTION_IF ( height < m_blockchain . offset ( ) & & m_blockchain . size ( ) > m_blockchain . offset ( ) ,
THROW_WALLET_EXCEPTION_IF ( height < m_blockchain . offset ( ) & & m_blockchain . size ( ) > m_blockchain . offset ( ) ,
error : : wallet_internal_error , " Daemon claims reorg below last checkpoint " ) ;
error : : wallet_internal_error , " Daemon claims reorg below last checkpoint " ) ;
detached_blockchain_data dbd = detach_blockchain ( height , output_tracker_cache ) ;
detach_blockchain ( height , output_tracker_cache ) ;
if ( m_callback )
m_callback - > on_reorg ( height , dbd . detached_blockchain . size ( ) , dbd . detached_tx_hashes . size ( ) ) ;
}
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
bool wallet2 : : deinit ( )
bool wallet2 : : deinit ( )
@ -6642,20 +6628,6 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
if ( ! m_persistent_rpc_client_id )
if ( ! m_persistent_rpc_client_id )
set_rpc_client_secret_key ( rct : : rct2sk ( rct : : skGen ( ) ) ) ;
set_rpc_client_secret_key ( rct : : rct2sk ( rct : : skGen ( ) ) ) ;
// Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks.
// Here we erase these multisig keys if they're zero'd out to free up space.
for ( auto & td : m_transfers )
{
auto mk_it = td . m_multisig_k . begin ( ) ;
while ( mk_it ! = td . m_multisig_k . end ( ) )
{
if ( * mk_it = = rct : : zero ( ) )
mk_it = td . m_multisig_k . erase ( mk_it ) ;
else
+ + mk_it ;
}
}
cryptonote : : block genesis ;
cryptonote : : block genesis ;
generate_genesis ( genesis ) ;
generate_genesis ( genesis ) ;
crypto : : hash genesis_hash = get_block_hash ( genesis ) ;
crypto : : hash genesis_hash = get_block_hash ( genesis ) ;
@ -7535,10 +7507,7 @@ void wallet2::commit_tx(pending_tx& ptx)
// tx generated, get rid of used k values
// tx generated, get rid of used k values
for ( size_t idx : ptx . selected_transfers )
for ( size_t idx : ptx . selected_transfers )
{
memwipe ( m_transfers [ idx ] . m_multisig_k . data ( ) , m_transfers [ idx ] . m_multisig_k . size ( ) * sizeof ( m_transfers [ idx ] . m_multisig_k [ 0 ] ) ) ;
memwipe ( m_transfers [ idx ] . m_multisig_k . data ( ) , m_transfers [ idx ] . m_multisig_k . size ( ) * sizeof ( m_transfers [ idx ] . m_multisig_k [ 0 ] ) ) ;
m_transfers [ idx ] . m_multisig_k . clear ( ) ;
}
//fee includes dust if dust policy specified it.
//fee includes dust if dust policy specified it.
LOG_PRINT_L1 ( " Transaction successfully sent. < " < < txid < < " > " < < ENDL
LOG_PRINT_L1 ( " Transaction successfully sent. < " < < txid < < " > " < < ENDL
@ -8042,10 +8011,7 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs)
// txes generated, get rid of used k values
// txes generated, get rid of used k values
for ( size_t n = 0 ; n < txs . m_ptx . size ( ) ; + + n )
for ( size_t n = 0 ; n < txs . m_ptx . size ( ) ; + + n )
for ( size_t idx : txs . m_ptx [ n ] . construction_data . selected_transfers )
for ( size_t idx : txs . m_ptx [ n ] . construction_data . selected_transfers )
{
memwipe ( m_transfers [ idx ] . m_multisig_k . data ( ) , m_transfers [ idx ] . m_multisig_k . size ( ) * sizeof ( m_transfers [ idx ] . m_multisig_k [ 0 ] ) ) ;
memwipe ( m_transfers [ idx ] . m_multisig_k . data ( ) , m_transfers [ idx ] . m_multisig_k . size ( ) * sizeof ( m_transfers [ idx ] . m_multisig_k [ 0 ] ) ) ;
m_transfers [ idx ] . m_multisig_k . clear ( ) ;
}
// zero out some data we don't want to share
// zero out some data we don't want to share
for ( auto & ptx : txs . m_ptx )
for ( auto & ptx : txs . m_ptx )
@ -8369,10 +8335,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
// inputs in the transactions worked on here)
// inputs in the transactions worked on here)
for ( size_t n = 0 ; n < exported_txs . m_ptx . size ( ) ; + + n )
for ( size_t n = 0 ; n < exported_txs . m_ptx . size ( ) ; + + n )
for ( size_t idx : exported_txs . m_ptx [ n ] . construction_data . selected_transfers )
for ( size_t idx : exported_txs . m_ptx [ n ] . construction_data . selected_transfers )
{
memwipe ( m_transfers [ idx ] . m_multisig_k . data ( ) , m_transfers [ idx ] . m_multisig_k . size ( ) * sizeof ( m_transfers [ idx ] . m_multisig_k [ 0 ] ) ) ;
memwipe ( m_transfers [ idx ] . m_multisig_k . data ( ) , m_transfers [ idx ] . m_multisig_k . size ( ) * sizeof ( m_transfers [ idx ] . m_multisig_k [ 0 ] ) ) ;
m_transfers [ idx ] . m_multisig_k . clear ( ) ;
}
exported_txs . m_signers . insert ( get_multisig_signer_public_key ( ) ) ;
exported_txs . m_signers . insert ( get_multisig_signer_public_key ( ) ) ;
@ -8601,7 +8564,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
else if ( blocks [ 0 ] . first > 0 )
else if ( blocks [ 0 ] . first > 0 )
{
{
MINFO ( " We don't use the low priority because there's a backlog in the tx pool. " ) ;
MINFO ( " We don't use the low priority because there's a backlog in the tx pool. " ) ;
return 2 ;
return priority ;
}
}
// get the current full reward zone
// get the current full reward zone
@ -8649,7 +8612,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
if ( P > 80 )
if ( P > 80 )
{
{
MINFO ( " We don't use the low priority because recent blocks are quite full. " ) ;
MINFO ( " We don't use the low priority because recent blocks are quite full. " ) ;
return 2 ;
return priority ;
}
}
MINFO ( " We'll use the low priority because probably it's safe to do so. " ) ;
MINFO ( " We'll use the low priority because probably it's safe to do so. " ) ;
return 1 ;
return 1 ;
@ -9301,26 +9264,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN : : request req = AUTO_VAL_INIT ( req ) ;
COMMAND_RPC_GET_OUTPUTS_BIN : : request req = AUTO_VAL_INIT ( req ) ;
COMMAND_RPC_GET_OUTPUTS_BIN : : response daemon_resp = AUTO_VAL_INIT ( daemon_resp ) ;
COMMAND_RPC_GET_OUTPUTS_BIN : : response daemon_resp = AUTO_VAL_INIT ( daemon_resp ) ;
// The secret picking order contains outputs in the order that we selected them.
//
// We will later sort the output request entries in a pre-determined order so that the daemon
// that we're requesting information from doesn't learn any information about the true spend
// for each ring. However, internally, we want to prefer to construct our rings using the
// outputs that we picked first versus outputs picked later.
//
// The reason why is because each consecutive output pick within a ring becomes increasing less
// statistically independent from other picks, since we pick outputs from a finite set
// *without replacement*, due to the protocol not allowing duplicate ring members. This effect
// is exacerbated by the fact that we pick 1.5x + 75 as many outputs as we need per RPC
// request to account for unusable outputs. This effect is small, but non-neglibile and gets
// worse with larger ring sizes.
std : : vector < get_outputs_out > secret_picking_order ;
// Convenience/safety lambda to make sure that both output lists req.outputs and secret_picking_order are updated together
// Each ring section of req.outputs gets sorted later after selecting all outputs for that ring
const auto add_output_to_lists = [ & req , & secret_picking_order ] ( const get_outputs_out & goo )
{ req . outputs . push_back ( goo ) ; secret_picking_order . push_back ( goo ) ; } ;
std : : unique_ptr < gamma_picker > gamma ;
std : : unique_ptr < gamma_picker > gamma ;
if ( has_rct )
if ( has_rct )
gamma . reset ( new gamma_picker ( rct_offsets ) ) ;
gamma . reset ( new gamma_picker ( rct_offsets ) ) ;
@ -9455,7 +9398,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if ( out < num_outs )
if ( out < num_outs )
{
{
MINFO ( " Using it " ) ;
MINFO ( " Using it " ) ;
add_output_to_lists ( { amount , out } ) ;
req. outputs . push_back ( { amount , out } ) ;
+ + num_found ;
+ + num_found ;
seen_indices . emplace ( out ) ;
seen_indices . emplace ( out ) ;
if ( out = = td . m_global_output_index )
if ( out = = td . m_global_output_index )
@ -9477,12 +9420,12 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if ( num_outs < = requested_outputs_count )
if ( num_outs < = requested_outputs_count )
{
{
for ( uint64_t i = 0 ; i < num_outs ; i + + )
for ( uint64_t i = 0 ; i < num_outs ; i + + )
add_output_to_lists ( { amount , i } ) ;
req. outputs . push_back ( { amount , i } ) ;
// duplicate to make up shortfall: this will be caught after the RPC call,
// duplicate to make up shortfall: this will be caught after the RPC call,
// so we can also output the amounts for which we can't reach the required
// so we can also output the amounts for which we can't reach the required
// mixin after checking the actual unlockedness
// mixin after checking the actual unlockedness
for ( uint64_t i = num_outs ; i < requested_outputs_count ; + + i )
for ( uint64_t i = num_outs ; i < requested_outputs_count ; + + i )
add_output_to_lists ( { amount , num_outs - 1 } ) ;
req. outputs . push_back ( { amount , num_outs - 1 } ) ;
}
}
else
else
{
{
@ -9491,7 +9434,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{
{
num_found = 1 ;
num_found = 1 ;
seen_indices . emplace ( td . m_global_output_index ) ;
seen_indices . emplace ( td . m_global_output_index ) ;
add_output_to_lists ( { amount , td . m_global_output_index } ) ;
req. outputs . push_back ( { amount , td . m_global_output_index } ) ;
LOG_PRINT_L1 ( " Selecting real output: " < < td . m_global_output_index < < " for " < < print_money ( amount ) ) ;
LOG_PRINT_L1 ( " Selecting real output: " < < td . m_global_output_index < < " for " < < print_money ( amount ) ) ;
}
}
@ -9599,7 +9542,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
seen_indices . emplace ( i ) ;
seen_indices . emplace ( i ) ;
picks [ type ] . insert ( i ) ;
picks [ type ] . insert ( i ) ;
add_output_to_lists ( { amount , i } ) ;
req. outputs . push_back ( { amount , i } ) ;
+ + num_found ;
+ + num_found ;
MDEBUG ( " picked " < < i < < " , " < < num_found < < " now picked " ) ;
MDEBUG ( " picked " < < i < < " , " < < num_found < < " now picked " ) ;
}
}
@ -9613,7 +9556,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// we'll error out later
// we'll error out later
while ( num_found < requested_outputs_count )
while ( num_found < requested_outputs_count )
{
{
add_output_to_lists ( { amount , 0 } ) ;
req. outputs . push_back ( { amount , 0 } ) ;
+ + num_found ;
+ + num_found ;
}
}
}
}
@ -9623,10 +9566,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
[ ] ( const get_outputs_out & a , const get_outputs_out & b ) { return a . index < b . index ; } ) ;
[ ] ( const get_outputs_out & a , const get_outputs_out & b ) { return a . index < b . index ; } ) ;
}
}
THROW_WALLET_EXCEPTION_IF ( req . outputs . size ( ) ! = secret_picking_order . size ( ) , error : : wallet_internal_error ,
" bug: we did not update req.outputs/secret_picking_order in tandem " ) ;
// List all requested outputs to debug log
if ( ELPP - > vRegistry ( ) - > allowed ( el : : Level : : Debug , MONERO_DEFAULT_LOG_CATEGORY ) )
if ( ELPP - > vRegistry ( ) - > allowed ( el : : Level : : Debug , MONERO_DEFAULT_LOG_CATEGORY ) )
{
{
std : : map < uint64_t , std : : set < uint64_t > > outs ;
std : : map < uint64_t , std : : set < uint64_t > > outs ;
@ -9747,21 +9686,18 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
}
}
}
// While we are still lacking outputs in this result ring, in our secret pick order...
// then pick others in random order till we reach the required number
// since we use an equiprobable pick here, we don't upset the triangular distribution
std : : vector < size_t > order ;
order . resize ( requested_outputs_count ) ;
for ( size_t n = 0 ; n < order . size ( ) ; + + n )
order [ n ] = n ;
std : : shuffle ( order . begin ( ) , order . end ( ) , crypto : : random_device { } ) ;
LOG_PRINT_L2 ( " Looking for " < < ( fake_outputs_count + 1 ) < < " outputs of size " < < print_money ( td . is_rct ( ) ? 0 : td . amount ( ) ) ) ;
LOG_PRINT_L2 ( " Looking for " < < ( fake_outputs_count + 1 ) < < " outputs of size " < < print_money ( td . is_rct ( ) ? 0 : td . amount ( ) ) ) ;
for ( size_t ring_pick_idx = base ; ring_pick_idx < base + requested_outputs_count & & outs . back ( ) . size ( ) < fake_outputs_count + 1 ; + + ring_pick_idx )
for ( size_t o = 0 ; o < requested_outputs_count & & outs . back ( ) . size ( ) < fake_outputs_count + 1 ; + + o )
{
{
const get_outputs_out attempted_output = secret_picking_order [ ring_pick_idx ] ;
size_t i = base + order [ o ] ;
// Find the index i of our pick in the request/response arrays
size_t i ;
for ( i = base ; i < base + requested_outputs_count ; + + i )
if ( req . outputs [ i ] . index = = attempted_output . index )
break ;
THROW_WALLET_EXCEPTION_IF ( i = = base + requested_outputs_count , error : : wallet_internal_error ,
" Could not find index of picked output in requested outputs " ) ;
// Try adding this output's information to result ring if output isn't invalid
LOG_PRINT_L2 ( " Index " < < i < < " / " < < requested_outputs_count < < " : idx " < < req . outputs [ i ] . index < < " (real " < < td . m_global_output_index < < " ), unlocked " < < daemon_resp . outs [ i ] . unlocked < < " , key " < < daemon_resp . outs [ i ] . key ) ;
LOG_PRINT_L2 ( " Index " < < i < < " / " < < requested_outputs_count < < " : idx " < < req . outputs [ i ] . index < < " (real " < < td . m_global_output_index < < " ), unlocked " < < daemon_resp . outs [ i ] . unlocked < < " , key " < < daemon_resp . outs [ i ] . key ) ;
tx_add_fake_output ( outs , req . outputs [ i ] . index , daemon_resp . outs [ i ] . key , daemon_resp . outs [ i ] . mask , td . m_global_output_index , daemon_resp . outs [ i ] . unlocked , valid_public_keys_cache ) ;
tx_add_fake_output ( outs , req . outputs [ i ] . index , daemon_resp . outs [ i ] . key , daemon_resp . outs [ i ] . mask , td . m_global_output_index , daemon_resp . outs [ i ] . unlocked , valid_public_keys_cache ) ;
}
}
@ -10328,7 +10264,7 @@ 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 " ) ;
}
}
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 < crypto : : key_image > & preferred_input_list )
std : : vector < size_t > wallet2 : : pick_preferred_rct_inputs ( uint64_t needed_money , uint32_t subaddr_account , const std : : set < uint32_t > & subaddr_indices )
{
{
std : : vector < size_t > picks ;
std : : vector < size_t > picks ;
float current_output_relatdness = 1.0f ;
float current_output_relatdness = 1.0f ;
@ -10339,10 +10275,6 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
{
{
const transfer_details & td = m_transfers [ i ] ;
const transfer_details & td = m_transfers [ i ] ;
if ( ! is_preferred_input ( preferred_input_list , td . m_key_image ) ) {
continue ;
}
if ( ! is_spent ( td , false ) & & ! td . m_frozen & & 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 )
if ( ! is_spent ( td , false ) & & ! td . m_frozen & & 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 )
{
{
if ( td . amount ( ) > m_ignore_outputs_above | | td . amount ( ) < m_ignore_outputs_below )
if ( td . amount ( ) > m_ignore_outputs_above | | td . amount ( ) < m_ignore_outputs_below )
@ -10363,10 +10295,6 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
{
{
const transfer_details & td = m_transfers [ i ] ;
const transfer_details & td = m_transfers [ i ] ;
if ( ! is_preferred_input ( preferred_input_list , td . m_key_image ) ) {
continue ;
}
if ( ! is_spent ( td , false ) & & ! td . m_frozen & & ! td . m_key_image_partial & & td . is_rct ( ) & & is_transfer_unlocked ( td ) & & td . m_subaddr_index . major = = subaddr_account & & subaddr_indices . count ( td . m_subaddr_index . minor ) = = 1 )
if ( ! is_spent ( td , false ) & & ! td . m_frozen & & ! td . m_key_image_partial & & td . is_rct ( ) & & is_transfer_unlocked ( td ) & & td . m_subaddr_index . major = = subaddr_account & & subaddr_indices . count ( td . m_subaddr_index . minor ) = = 1 )
{
{
if ( td . amount ( ) > m_ignore_outputs_above | | td . amount ( ) < m_ignore_outputs_below )
if ( td . amount ( ) > m_ignore_outputs_above | | td . amount ( ) < m_ignore_outputs_below )
@ -10378,10 +10306,6 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for ( size_t j = i + 1 ; j < m_transfers . size ( ) ; + + j )
for ( size_t j = i + 1 ; j < m_transfers . size ( ) ; + + j )
{
{
const transfer_details & td2 = m_transfers [ j ] ;
const transfer_details & td2 = m_transfers [ j ] ;
if ( ! is_preferred_input ( preferred_input_list , td . m_key_image ) ) {
continue ;
}
if ( td2 . amount ( ) > m_ignore_outputs_above | | td2 . amount ( ) < m_ignore_outputs_below )
if ( td2 . amount ( ) > m_ignore_outputs_above | | td2 . amount ( ) < m_ignore_outputs_below )
{
{
MDEBUG ( " Ignoring output " < < j < < " of amount " < < print_money ( td2 . amount ( ) ) < < " which is outside prescribed range [ " < < print_money ( m_ignore_outputs_below ) < < " , " < < print_money ( m_ignore_outputs_above ) < < " ] " ) ;
MDEBUG ( " Ignoring output " < < j < < " of amount " < < print_money ( td2 . amount ( ) ) < < " which is outside prescribed range [ " < < print_money ( m_ignore_outputs_below ) < < " , " < < print_money ( m_ignore_outputs_above ) < < " ] " ) ;
@ -10954,7 +10878,7 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image,
// This system allows for sending (almost) the entire balance, since it does
// This system allows for sending (almost) the entire balance, since it does
// not generate spurious change in all txes, thus decreasing the instantaneous
// not generate spurious change in all txes, thus decreasing the instantaneous
// usable balance.
// usable balance.
std : : vector < wallet2 : : pending_tx > wallet2 : : create_transactions_2 ( 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 , uint32_t subaddr_account , std : : set < uint32_t > subaddr_indices , const unique_index_container & subtract_fee_from_outputs , const std : : vector < crypto : : key_image > & preferred_input_list )
std : : vector < wallet2 : : pending_tx > wallet2 : : create_transactions_2 ( 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 , uint32_t subaddr_account , std : : set < uint32_t > subaddr_indices )
{
{
//ensure device is let in NONE mode in any case
//ensure device is let in NONE mode in any case
hw : : device & hwdev = m_account . get_device ( ) ;
hw : : device & hwdev = m_account . get_device ( ) ;
@ -10969,12 +10893,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
std : : vector < std : : pair < uint32_t , std : : vector < size_t > > > unused_transfers_indices_per_subaddr ;
std : : vector < std : : pair < uint32_t , std : : vector < size_t > > > unused_transfers_indices_per_subaddr ;
std : : vector < std : : pair < uint32_t , std : : vector < size_t > > > unused_dust_indices_per_subaddr ;
std : : vector < std : : pair < uint32_t , std : : vector < size_t > > > unused_dust_indices_per_subaddr ;
uint64_t needed_money , total_needed_money ; // 'needed_money' is the sum of the destination amounts, while 'total_needed_money' includes 'needed_money' plus the fee if not 'subtract_fee_from_outputs'
uint64_t needed_money ;
uint64_t accumulated_fee , accumulated_change ;
uint64_t accumulated_fee , accumulated_change ;
struct TX {
struct TX {
std : : vector < size_t > selected_transfers ;
std : : vector < size_t > selected_transfers ;
std : : vector < cryptonote : : tx_destination_entry > dsts ;
std : : vector < cryptonote : : tx_destination_entry > dsts ;
std : : vector < bool > dsts_are_fee_subtractable ;
cryptonote : : transaction tx ;
cryptonote : : transaction tx ;
pending_tx ptx ;
pending_tx ptx ;
size_t weight ;
size_t weight ;
@ -10984,11 +10907,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
TX ( ) : weight ( 0 ) , needed_fee ( 0 ) { }
TX ( ) : weight ( 0 ) , needed_fee ( 0 ) { }
/* Add an output to the transaction.
/* Add an output to the transaction.
* If merge_destinations is true , when adding a destination with an existing address , to increment the amount of the existing tx output instead of creating a new one
* If subtracting_fee is true , when we generate a final list of destinations for transfer_selected [ _rct ] , this destination will be used to fund the tx fee
* Returns True if the output was added , False if there are no more available output slots .
* Returns True if the output was added , False if there are no more available output slots .
*/
*/
bool add ( const cryptonote : : tx_destination_entry & de , uint64_t amount , unsigned int original_output_index , bool merge_destinations , size_t max_dsts , bool subtracting_fee ) {
bool add ( const cryptonote : : tx_destination_entry & de , uint64_t amount , unsigned int original_output_index , bool merge_destinations , size_t max_dsts ) {
if ( merge_destinations )
if ( merge_destinations )
{
{
std : : vector < cryptonote : : tx_destination_entry > : : iterator i ;
std : : vector < cryptonote : : tx_destination_entry > : : iterator i ;
@ -10998,7 +10919,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
if ( dsts . size ( ) > = max_dsts )
if ( dsts . size ( ) > = max_dsts )
return false ;
return false ;
dsts . push_back ( de ) ;
dsts . push_back ( de ) ;
dsts_are_fee_subtractable . push_back ( subtracting_fee ) ;
i = dsts . end ( ) - 1 ;
i = dsts . end ( ) - 1 ;
i - > amount = 0 ;
i - > amount = 0 ;
}
}
@ -11014,67 +10934,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
return false ;
return false ;
dsts . push_back ( de ) ;
dsts . push_back ( de ) ;
dsts . back ( ) . amount = 0 ;
dsts . back ( ) . amount = 0 ;
dsts_are_fee_subtractable . push_back ( subtracting_fee ) ;
}
}
THROW_WALLET_EXCEPTION_IF ( memcmp ( & dsts [ original_output_index ] . addr , & de . addr , sizeof ( de . addr ) ) , error : : wallet_internal_error , " Mismatched destination address " ) ;
THROW_WALLET_EXCEPTION_IF ( memcmp ( & dsts [ original_output_index ] . addr , & de . addr , sizeof ( de . addr ) ) , error : : wallet_internal_error , " Mismatched destination address " ) ;
dsts [ original_output_index ] . amount + = amount ;
dsts [ original_output_index ] . amount + = amount ;
}
}
return true ;
return true ;
}
}
// Returns destinations adjusted for given fee if subtract_fee_from_outputs is enabled
std : : vector < cryptonote : : tx_destination_entry > get_adjusted_dsts ( uint64_t needed_fee ) const
{
uint64_t dest_total = 0 ;
uint64_t subtractable_dest_total = 0 ;
std : : vector < size_t > subtractable_indices ;
subtractable_indices . reserve ( dsts . size ( ) ) ;
for ( size_t i = 0 ; i < dsts . size ( ) ; + + i )
{
dest_total + = dsts [ i ] . amount ;
if ( dsts_are_fee_subtractable [ i ] )
{
subtractable_dest_total + = dsts [ i ] . amount ;
subtractable_indices . push_back ( i ) ;
}
}
if ( subtractable_indices . empty ( ) ) // if subtract_fee_from_outputs is not enabled for this tx
return dsts ;
THROW_WALLET_EXCEPTION_IF ( subtractable_dest_total < needed_fee , error : : tx_not_possible ,
subtractable_dest_total , dest_total , needed_fee ) ;
std : : vector < cryptonote : : tx_destination_entry > res = dsts ;
// subtract fees from destinations equally, rounded down, until dust is left where we subtract 1
uint64_t subtractable_remaining = needed_fee ;
auto si_it = subtractable_indices . cbegin ( ) ;
uint64_t amount_to_subtract = 0 ;
while ( subtractable_remaining )
{
// Set the amount to subtract iterating at the beginning of the list so equal amounts are
// subtracted throughout the list of destinations. We use max(x, 1) so that we we still step
// forwards even when the amount remaining is less than the number of subtractable indices
if ( si_it = = subtractable_indices . cbegin ( ) )
amount_to_subtract = std : : max < uint64_t > ( subtractable_remaining / subtractable_indices . size ( ) , 1 ) ;
cryptonote : : tx_destination_entry & d = res [ * si_it ] ;
THROW_WALLET_EXCEPTION_IF ( d . amount < = amount_to_subtract , error : : zero_amount ) ;
subtractable_remaining - = amount_to_subtract ;
d . amount - = amount_to_subtract ;
+ + si_it ;
// Wrap around to first subtractable index once we hit the end of the list
if ( si_it = = subtractable_indices . cend ( ) )
si_it = subtractable_indices . cbegin ( ) ;
}
return res ;
}
} ;
} ;
std : : vector < TX > txes ;
std : : vector < TX > txes ;
bool adding_fee ; // true if new outputs go towards fee, rather than destinations
bool adding_fee ; // true if new outputs go towards fee, rather than destinations
uint64_t needed_fee , available_for_fee = 0 ;
uint64_t needed_fee , available_for_fee = 0 ;
@ -11097,14 +10963,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// throw if attempting a transaction with no destinations
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF ( dsts . empty ( ) , error : : zero_destination ) ;
THROW_WALLET_EXCEPTION_IF ( dsts . empty ( ) , error : : zero_destination ) ;
// throw if subtract_fee_from_outputs has a bad index
THROW_WALLET_EXCEPTION_IF ( subtract_fee_from_outputs . size ( ) & & * subtract_fee_from_outputs . crbegin ( ) > = dsts . size ( ) ,
error : : subtract_fee_from_bad_index , * subtract_fee_from_outputs . crbegin ( ) ) ;
// throw if subtract_fee_from_outputs is enabled and we have too many outputs to fit into one tx
THROW_WALLET_EXCEPTION_IF ( subtract_fee_from_outputs . size ( ) & & dsts . size ( ) > BULLETPROOF_MAX_OUTPUTS - 1 ,
error : : wallet_internal_error , " subtractfeefrom transfers cannot be split over multiple transactions yet " ) ;
// calculate total amount being sent to all destinations
// calculate total amount being sent to all destinations
// throw if total amount overflows uint64_t
// throw if total amount overflows uint64_t
needed_money = 0 ;
needed_money = 0 ;
@ -11132,7 +10990,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// we could also check for being within FEE_PER_KB, but if the fee calculation
// 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
// ever changes, this might be missed, so let this go through
const uint64_t min_fee = ( base_fee * estimate_tx_size ( use_rct , 1 , fake_outs_count , 2 , extra . size ( ) , bulletproof , clsag , bulletproof_plus , use_view_tags ) ) ;
const uint64_t min_fee = ( base_fee * estimate_tx_size ( use_rct , 1 , fake_outs_count , 2 , extra . size ( ) , bulletproof , clsag , bulletproof_plus , use_view_tags ) ) ;
total_needed_money = needed_money + ( subtract_fee_from_outputs . size ( ) ? 0 : min_fee ) ;
uint64_t balance_subtotal = 0 ;
uint64_t balance_subtotal = 0 ;
uint64_t unlocked_balance_subtotal = 0 ;
uint64_t unlocked_balance_subtotal = 0 ;
for ( uint32_t index_minor : subaddr_indices )
for ( uint32_t index_minor : subaddr_indices )
@ -11140,10 +10997,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
balance_subtotal + = balance_per_subaddr [ index_minor ] ;
balance_subtotal + = balance_per_subaddr [ index_minor ] ;
unlocked_balance_subtotal + = unlocked_balance_per_subaddr [ index_minor ] . first ;
unlocked_balance_subtotal + = unlocked_balance_per_subaddr [ index_minor ] . first ;
}
}
THROW_WALLET_EXCEPTION_IF ( total_needed_money > balance_subtotal | | min_fee > balance_subtotal , error : : not_enough_money ,
THROW_WALLET_EXCEPTION_IF ( needed_money + min_fee > balance_subtotal , error : : not_enough_money ,
balance_subtotal , needed_money , 0 ) ;
balance_subtotal , needed_money , 0 ) ;
// first check overall balance is enough, then unlocked one, so we throw distinct exceptions
// first check overall balance is enough, then unlocked one, so we throw distinct exceptions
THROW_WALLET_EXCEPTION_IF ( total_needed_money > unlocked_balance_subtotal | | min_fee > unlocked_balance_subtotal , error : : not_enough_unlocked_money ,
THROW_WALLET_EXCEPTION_IF ( needed_money + min_fee > unlocked_balance_subtotal , error : : not_enough_unlocked_money ,
unlocked_balance_subtotal , needed_money , 0 ) ;
unlocked_balance_subtotal , needed_money , 0 ) ;
for ( uint32_t i : subaddr_indices )
for ( uint32_t i : subaddr_indices )
@ -11162,10 +11019,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
{
{
const transfer_details & td = m_transfers [ i ] ;
const transfer_details & td = m_transfers [ i ] ;
if ( ! is_preferred_input ( preferred_input_list , td . m_key_image ) ) {
continue ;
}
if ( m_ignore_fractional_outputs & & td . amount ( ) < fractional_threshold )
if ( m_ignore_fractional_outputs & & td . amount ( ) < fractional_threshold )
{
{
MDEBUG ( " Ignoring output " < < i < < " of amount " < < print_money ( td . amount ( ) ) < < " which is below fractional threshold " < < print_money ( fractional_threshold ) ) ;
MDEBUG ( " Ignoring output " < < i < < " of amount " < < print_money ( td . amount ( ) ) < < " which is below fractional threshold " < < print_money ( fractional_threshold ) ) ;
@ -11250,8 +11103,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 = estimate_fee ( use_per_byte_fee , use_rct , 2 , fake_outs_count , 2 , extra . size ( ) , bulletproof , clsag , bulletproof_plus , use_view_tags , base_fee , fee_quantization_mask ) ;
uint64_t estimated_fee = estimate_fee ( use_per_byte_fee , use_rct , 2 , fake_outs_count , 2 , extra . size ( ) , bulletproof , clsag , bulletproof_plus , use_view_tags , base_fee , fee_quantization_mask ) ;
total_needed_money = needed_money + ( subtract_fee_from_outputs . size ( ) ? 0 : estimated_fee ) ;
preferred_inputs = pick_preferred_rct_inputs ( needed_money + estimated_fee , subaddr_account , subaddr_indices ) ;
preferred_inputs = pick_preferred_rct_inputs ( total_needed_money , subaddr_account , subaddr_indices , preferred_input_list ) ;
if ( ! preferred_inputs . empty ( ) )
if ( ! preferred_inputs . empty ( ) )
{
{
string s ;
string s ;
@ -11284,10 +11136,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// - we have something to send
// - we have something to send
// - or we need to gather more fee
// - or we need to gather more fee
// - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2)
// - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2)
unsigned int original_output_index = 0 , destination_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 ;
hwdev . set_mode ( hw : : device : : TRANSACTION_CREATE_FAKE ) ;
hwdev . set_mode ( hw : : device : : TRANSACTION_CREATE_FAKE ) ;
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 ) ) {
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 ( ) ;
@ -11367,8 +11219,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// 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_nettype , dsts [ 0 ] . is_subaddress , dsts [ 0 ] . addr ) < <
LOG_PRINT_L2 ( " We can fully pay " < < get_account_address_as_str ( m_nettype , dsts [ 0 ] . is_subaddress , dsts [ 0 ] . addr ) < <
" for " < < print_money ( dsts [ 0 ] . amount ) ) ;
" for " < < print_money ( dsts [ 0 ] . amount ) ) ;
const bool subtract_fee_from_this_dest = subtract_fee_from_outputs . count ( destination_index ) ;
if ( ! tx . add ( dsts [ 0 ] , dsts [ 0 ] . amount , original_output_index , m_merge_destinations , BULLETPROOF_MAX_OUTPUTS - 1 ) )
if ( ! tx . add ( dsts [ 0 ] , dsts [ 0 ] . amount , original_output_index , m_merge_destinations , BULLETPROOF_MAX_OUTPUTS - 1 , subtract_fee_from_this_dest ) )
{
{
LOG_PRINT_L2 ( " Didn't pay: ran out of output slots " ) ;
LOG_PRINT_L2 ( " Didn't pay: ran out of output slots " ) ;
out_slots_exhausted = true ;
out_slots_exhausted = true ;
@ -11378,7 +11229,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
dsts [ 0 ] . amount = 0 ;
dsts [ 0 ] . amount = 0 ;
pop_index ( dsts , 0 ) ;
pop_index ( dsts , 0 ) ;
+ + original_output_index ;
+ + original_output_index ;
+ + destination_index ;
}
}
if ( ! out_slots_exhausted & & available_amount > 0 & & ! dsts . empty ( ) & &
if ( ! out_slots_exhausted & & available_amount > 0 & & ! dsts . empty ( ) & &
@ -11386,8 +11236,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// 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_nettype , dsts [ 0 ] . is_subaddress , dsts [ 0 ] . addr ) < <
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 ) ) ;
" for " < < print_money ( available_amount ) < < " / " < < print_money ( dsts [ 0 ] . amount ) ) ;
const bool subtract_fee_from_this_dest = subtract_fee_from_outputs . count ( destination_index ) ;
if ( tx . add ( dsts [ 0 ] , available_amount , original_output_index , m_merge_destinations , BULLETPROOF_MAX_OUTPUTS - 1 ) )
if ( tx . add ( dsts [ 0 ] , available_amount , original_output_index , m_merge_destinations , BULLETPROOF_MAX_OUTPUTS - 1 , subtract_fee_from_this_dest ) )
{
{
dsts [ 0 ] . amount - = available_amount ;
dsts [ 0 ] . amount - = available_amount ;
available_amount = 0 ;
available_amount = 0 ;
@ -11466,13 +11315,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// Try to carve the estimated fee from the partial payment (if there is one)
// Try to carve the estimated fee from the partial payment (if there is one)
available_for_fee = try_carving_from_partial_payment ( needed_fee , available_for_fee ) ;
available_for_fee = try_carving_from_partial_payment ( needed_fee , available_for_fee ) ;
uint64_t inputs = 0 , outputs = 0 ;
uint64_t inputs = 0 , outputs = needed_fee ;
for ( size_t idx : tx . selected_transfers ) inputs + = m_transfers [ idx ] . amount ( ) ;
for ( size_t idx : tx . selected_transfers ) inputs + = m_transfers [ idx ] . amount ( ) ;
for ( const auto & o : tx . dsts ) outputs + = o . amount ;
for ( const auto & o : tx . dsts ) outputs + = o . amount ;
if ( subtract_fee_from_outputs . empty ( ) ) // if normal tx that doesn't subtract fees
{
outputs + = needed_fee ;
}
if ( inputs < outputs )
if ( inputs < outputs )
{
{
@ -11483,32 +11328,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2 ( " Trying to create a tx now, with " < < tx . dsts . size ( ) < < " outputs and " < <
LOG_PRINT_L2 ( " Trying to create a tx now, with " < < tx . dsts . size ( ) < < " outputs and " < <
tx . selected_transfers . size ( ) < < " inputs " ) ;
tx . selected_transfers . size ( ) < < " inputs " ) ;
auto tx_dsts = tx . get_adjusted_dsts ( needed_fee ) ;
if ( use_rct )
if ( use_rct )
transfer_selected_rct ( tx _ dsts, tx . selected_transfers , fake_outs_count , outs , valid_public_keys_cache , unlock_time , needed_fee , extra ,
transfer_selected_rct ( tx . dsts, tx . selected_transfers , fake_outs_count , outs , valid_public_keys_cache , unlock_time , needed_fee , extra ,
test_tx , test_ptx , rct_config , use_view_tags ) ;
test_tx , test_ptx , rct_config , use_view_tags ) ;
else
else
transfer_selected ( tx _ dsts, tx . selected_transfers , fake_outs_count , outs , valid_public_keys_cache , unlock_time , needed_fee , extra ,
transfer_selected ( tx . dsts, tx . selected_transfers , fake_outs_count , outs , valid_public_keys_cache , unlock_time , needed_fee , extra ,
detail : : digit_split_strategy , tx_dust_policy ( : : config : : DEFAULT_DUST_THRESHOLD ) , test_tx , test_ptx , use_view_tags ) ;
detail : : digit_split_strategy , tx_dust_policy ( : : config : : DEFAULT_DUST_THRESHOLD ) , test_tx , test_ptx , use_view_tags ) ;
auto txBlob = t_serializable_object_to_blob ( test_ptx . tx ) ;
auto txBlob = t_serializable_object_to_blob ( test_ptx . tx ) ;
needed_fee = calculate_fee ( use_per_byte_fee , test_ptx . tx , txBlob . size ( ) , base_fee , fee_quantization_mask ) ;
needed_fee = calculate_fee ( use_per_byte_fee , test_ptx . tx , txBlob . size ( ) , base_fee , fee_quantization_mask ) ;
available_for_fee = test_ptx . fee + test_ptx . change_dts . amount + ( ! test_ptx . dust_added_to_fee ? test_ptx . dust : 0 ) ;
// Depending on the mode, we take extra fees from either our change output or the destination outputs for which subtract_fee_from_outputs is true
uint64_t output_available_for_fee = 0 ;
bool tx_has_subtractable_output = false ;
for ( size_t di = 0 ; di < tx . dsts . size ( ) ; + + di )
{
if ( tx . dsts_are_fee_subtractable [ di ] )
{
output_available_for_fee + = tx . dsts [ di ] . amount ;
tx_has_subtractable_output = true ;
}
}
if ( ! tx_has_subtractable_output )
{
output_available_for_fee = test_ptx . change_dts . amount ;
}
available_for_fee = test_ptx . fee + output_available_for_fee + ( ! test_ptx . dust_added_to_fee ? test_ptx . dust : 0 ) ;
LOG_PRINT_L2 ( " Made a " < < get_weight_string ( test_ptx . tx , txBlob . size ( ) ) < < " tx, with " < < print_money ( available_for_fee ) < < " available for fee ( " < <
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) " ) ;
print_money ( needed_fee ) < < " needed) " ) ;
@ -11524,24 +11352,18 @@ 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, we need " < < print_money ( needed_fee ) < < " and we have " < < print_money ( test_ptx . fee ) ) ;
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 ) ) ;
size_t fee_tries ;
do {
for ( fee_tries = 0 ; fee_tries < 10 & & needed_fee > test_ptx . fee ; + + fee_tries ) {
tx_dsts = tx . get_adjusted_dsts ( needed_fee ) ;
if ( use_rct )
if ( use_rct )
transfer_selected_rct ( tx _ dsts, tx . selected_transfers , fake_outs_count , outs , valid_public_keys_cache , unlock_time , needed_fee , extra ,
transfer_selected_rct ( tx . dsts , tx . selected_transfers , fake_outs_count , outs , valid_public_keys_cache , unlock_time , needed_fee , extra ,
test_tx , test_ptx , rct_config , use_view_tags ) ;
test_tx , test_ptx , rct_config , use_view_tags ) ;
else
else
transfer_selected ( tx _ dsts, tx . selected_transfers , fake_outs_count , outs , valid_public_keys_cache , unlock_time , needed_fee , extra ,
transfer_selected ( tx . dsts, tx . selected_transfers , fake_outs_count , outs , valid_public_keys_cache , unlock_time , needed_fee , extra ,
detail : : digit_split_strategy , tx_dust_policy ( : : config : : DEFAULT_DUST_THRESHOLD ) , test_tx , test_ptx , use_view_tags ) ;
detail : : digit_split_strategy , tx_dust_policy ( : : config : : DEFAULT_DUST_THRESHOLD ) , test_tx , test_ptx , use_view_tags ) ;
txBlob = t_serializable_object_to_blob ( test_ptx . tx ) ;
txBlob = t_serializable_object_to_blob ( test_ptx . tx ) ;
needed_fee = calculate_fee ( use_per_byte_fee , test_ptx . tx , txBlob . size ( ) , base_fee , fee_quantization_mask ) ;
needed_fee = calculate_fee ( use_per_byte_fee , test_ptx . tx , txBlob . size ( ) , base_fee , fee_quantization_mask ) ;
LOG_PRINT_L2 ( " Made an attempt at a final " < < get_weight_string ( test_ptx . tx , txBlob . size ( ) ) < < " tx, with " < < print_money ( test_ptx . fee ) < <
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 " ) ;
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
} ;
} while ( needed_fee > test_ptx . fee ) ;
THROW_WALLET_EXCEPTION_IF ( fee_tries = = 10 , error : : wallet_internal_error ,
" Too many attempts to raise pending tx fee to level of needed fee " ) ;
LOG_PRINT_L2 ( " Made a final " < < get_weight_string ( test_ptx . tx , txBlob . size ( ) ) < < " 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 " ) ;
" fee and " < < print_money ( test_ptx . change_dts . amount ) < < " change " ) ;
@ -11594,13 +11416,10 @@ skip_tx:
for ( std : : vector < TX > : : iterator i = txes . begin ( ) ; i ! = txes . end ( ) ; + + i )
for ( std : : vector < TX > : : iterator i = txes . begin ( ) ; i ! = txes . end ( ) ; + + i )
{
{
TX & tx = * i ;
TX & tx = * i ;
const auto tx_dsts = tx . get_adjusted_dsts ( tx . needed_fee ) ;
cryptonote : : transaction test_tx ;
cryptonote : : transaction test_tx ;
pending_tx test_ptx ;
pending_tx test_ptx ;
if ( use_rct ) {
if ( use_rct ) {
transfer_selected_rct ( tx _ dsts, /* NOMOD std::vector<cryptonote::tx_destination_entry> dsts,*/
transfer_selected_rct ( tx . dsts, /* NOMOD std::vector<cryptonote::tx_destination_entry> dsts,*/
tx . selected_transfers , /* const std::list<size_t> selected_transfers */
tx . selected_transfers , /* const std::list<size_t> selected_transfers */
fake_outs_count , /* CONST size_t fake_outputs_count, */
fake_outs_count , /* CONST size_t fake_outputs_count, */
tx . outs , /* MOD std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, */
tx . outs , /* MOD std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, */
@ -11613,7 +11432,7 @@ skip_tx:
rct_config ,
rct_config ,
use_view_tags ) ; /* const bool use_view_tags */
use_view_tags ) ; /* const bool use_view_tags */
} else {
} else {
transfer_selected ( tx _ dsts,
transfer_selected ( tx . dsts,
tx . selected_transfers ,
tx . selected_transfers ,
fake_outs_count ,
fake_outs_count ,
tx . outs ,
tx . outs ,
@ -11647,38 +11466,23 @@ skip_tx:
ptx_vector . push_back ( tx . ptx ) ;
ptx_vector . push_back ( tx . ptx ) ;
}
}
THROW_WALLET_EXCEPTION_IF ( ! sanity_check ( ptx_vector , original_dsts , subtract_fee_from_outputs ), error : : wallet_internal_error , " Created transaction(s) failed sanity check " ) ;
THROW_WALLET_EXCEPTION_IF ( ! sanity_check ( ptx_vector , original_dsts ), error : : wallet_internal_error , " Created transaction(s) failed sanity check " ) ;
// if we made it this far, we're OK to actually send the transactions
// if we made it this far, we're OK to actually send the transactions
return ptx_vector ;
return ptx_vector ;
}
}
bool wallet2 : : sanity_check ( const std : : vector < wallet2 : : pending_tx > & ptx_vector , const std : : vector < cryptonote : : tx_destination_entry > & ds ts, const unique_index_container & subtract_fee_from_outpu ts) const
bool wallet2 : : sanity_check ( const std : : vector < wallet2 : : pending_tx > & ptx_vector , std : : vector < cryptonote : : tx_destination_entry > ds ts) const
{
{
MDEBUG ( " sanity_check: " < < ptx_vector . size ( ) < < " txes, " < < dsts . size ( ) < < " destinations, subtract_fee_from_outputs " < <
MDEBUG ( " sanity_check: " < < ptx_vector . size ( ) < < " txes, " < < dsts . size ( ) < < " destinations " ) ;
( subtract_fee_from_outputs . size ( ) ? " enabled " : " disabled " ) ) ;
THROW_WALLET_EXCEPTION_IF ( ptx_vector . empty ( ) , error : : wallet_internal_error , " No transactions " ) ;
THROW_WALLET_EXCEPTION_IF ( ptx_vector . empty ( ) , error : : wallet_internal_error , " No transactions " ) ;
THROW_WALLET_EXCEPTION_IF ( ! subtract_fee_from_outputs . empty ( ) & & ptx_vector . size ( ) ! = 1 ,
error : : wallet_internal_error , " feature subtractfeefrom not supported for split transactions " ) ;
// For destinations from where the fee is subtracted, the required amount has to be at least
// target amount - (tx fee / num_subtractable + 1). +1 since fee might not be evenly divisble by
// the number of subtractble destinations. For non-subtractable destinations, we need at least
// the target amount.
const size_t num_subtractable_dests = subtract_fee_from_outputs . size ( ) ;
const uint64_t fee0 = ptx_vector [ 0 ] . fee ;
const uint64_t subtractable_fee_deduction = fee0 / std : : max < size_t > ( num_subtractable_dests , 1 ) + 1 ;
// check every party in there does receive at least the required amount
// check every party in there does receive at least the required amount
std : : unordered_map < account_public_address , std : : pair < uint64_t , bool > > required ;
std : : unordered_map < account_public_address , std : : pair < uint64_t , bool > > required ;
for ( size_t i = 0 ; i < dsts . size ( ) ; + + i )
for ( const auto & d : dsts )
{
{
const cryptonote : : tx_destination_entry & d = dsts [ i ] ;
required [ d . addr ] . first + = d . amount ;
const bool dest_is_subtractable = subtract_fee_from_outputs . count ( i ) ;
const uint64_t fee_deduction = dest_is_subtractable ? subtractable_fee_deduction : 0 ;
const uint64_t required_amount = d . amount - std : : min ( fee_deduction , d . amount ) ;
required [ d . addr ] . first + = required_amount ;
required [ d . addr ] . second = d . is_subaddress ;
required [ d . addr ] . second = d . is_subaddress ;
}
}
@ -11732,7 +11536,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, c
return true ;
return true ;
}
}
std : : vector < wallet2 : : pending_tx > wallet2 : : create_transactions_all ( uint64_t below , const cryptonote : : account_public_address & address , bool is_subaddress , const size_t outputs , const size_t fake_outs_count , const uint64_t unlock_time , uint32_t priority , const std : : vector < uint8_t > & extra , uint32_t subaddr_account , std : : set < uint32_t > subaddr_indices , const std : : vector < crypto : : key_image > & preferred_input_list )
std : : vector < wallet2 : : pending_tx > wallet2 : : create_transactions_all ( uint64_t below , const cryptonote : : account_public_address & address , bool is_subaddress , const size_t outputs , const size_t fake_outs_count , const uint64_t unlock_time , uint32_t priority , const std : : vector < uint8_t > & extra , uint32_t subaddr_account , std : : set < uint32_t > subaddr_indices )
{
{
std : : vector < size_t > unused_transfers_indices ;
std : : vector < size_t > unused_transfers_indices ;
std : : vector < size_t > unused_dust_indices ;
std : : vector < size_t > unused_dust_indices ;
@ -11761,10 +11565,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
{
{
const transfer_details & td = m_transfers [ i ] ;
const transfer_details & td = m_transfers [ i ] ;
if ( ! is_preferred_input ( preferred_input_list , td . m_key_image ) ) {
continue ;
}
if ( m_ignore_fractional_outputs & & td . amount ( ) < fractional_threshold )
if ( m_ignore_fractional_outputs & & td . amount ( ) < fractional_threshold )
{
{
MDEBUG ( " Ignoring output " < < i < < " of amount " < < print_money ( td . amount ( ) ) < < " which is below threshold " < < print_money ( fractional_threshold ) ) ;
MDEBUG ( " Ignoring output " < < i < < " of amount " < < print_money ( td . amount ( ) ) < < " which is below threshold " < < print_money ( fractional_threshold ) ) ;
@ -14835,10 +14635,7 @@ cryptonote::blobdata wallet2::export_multisig()
transfer_details & td = m_transfers [ n ] ;
transfer_details & td = m_transfers [ n ] ;
crypto : : key_image ki ;
crypto : : key_image ki ;
if ( td . m_multisig_k . size ( ) )
if ( td . m_multisig_k . size ( ) )
{
memwipe ( td . m_multisig_k . data ( ) , td . m_multisig_k . size ( ) * sizeof ( td . m_multisig_k [ 0 ] ) ) ;
memwipe ( td . m_multisig_k . data ( ) , td . m_multisig_k . size ( ) * sizeof ( td . m_multisig_k [ 0 ] ) ) ;
td . m_multisig_k . clear ( ) ;
}
info [ n ] . m_LR . clear ( ) ;
info [ n ] . m_LR . clear ( ) ;
info [ n ] . m_partial_key_images . clear ( ) ;
info [ n ] . m_partial_key_images . clear ( ) ;