@ -2508,7 +2508,7 @@ uint64_t wallet2::unlocked_dust_balance(const tx_dust_policy &dust_policy) const
}
template < typename T >
void wallet2 : : transfer_ dust( size_t num_outputs , uint64_t unlock_time , uint64_t needed_fee , T destination_split_strategy , const tx_dust_policy & dust_policy , const std : : vector < uint8_t > & extra , cryptonote : : transaction & tx , pending_tx & ptx )
void wallet2 : : transfer_ from( const std : : vector < size_t > & outs , size_t num_outputs , uint64_t unlock_time , uint64_t needed_fee , T destination_split_strategy , const tx_dust_policy & dust_policy , const std : : vector < uint8_t > & extra , cryptonote : : transaction & tx , pending_tx & ptx )
{
using namespace cryptonote ;
@ -2518,6 +2518,19 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
// throw if there are none
uint64_t money = 0 ;
std : : list < transfer_container : : iterator > selected_transfers ;
# if 1
for ( size_t n = 0 ; n < outs . size ( ) ; + + n )
{
const transfer_details & td = m_transfers [ outs [ n ] ] ;
if ( ! td . m_spent )
{
selected_transfers . push_back ( m_transfers . begin ( ) + outs [ n ] ) ;
money + = td . amount ( ) ;
if ( selected_transfers . size ( ) > = num_outputs )
break ;
}
}
# else
for ( transfer_container : : iterator i = m_transfers . begin ( ) ; i ! = m_transfers . end ( ) ; + + i )
{
const transfer_details & td = * i ;
@ -2529,6 +2542,7 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
break ;
}
}
# endif
// we don't allow no output to self, easier, but one may want to burn the dust if = fee
THROW_WALLET_EXCEPTION_IF ( money < = needed_fee , error : : not_enough_money , money , needed_fee , needed_fee ) ;
@ -2622,8 +2636,8 @@ bool wallet2::use_fork_rules(uint8_t version)
r = net_utils : : invoke_http_json_remote_command2 ( m_daemon_address + " /json_rpc " , req_t , resp_t , m_http_client ) ;
m_daemon_rpc_mutex . unlock ( ) ;
CHECK_AND_ASSERT_MES ( r , false , " Failed to connect to daemon " ) ;
CHECK_AND_ASSERT_MES ( res . status ! = CORE_RPC_STATUS_BUSY , false , " Failed to connect to daemon " ) ;
CHECK_AND_ASSERT_MES ( res . status = = CORE_RPC_STATUS_OK , false , " Failed to get hard fork status " ) ;
CHECK_AND_ASSERT_MES ( res p_t. result . status ! = CORE_RPC_STATUS_BUSY , false , " Failed to connect to daemon " ) ;
CHECK_AND_ASSERT_MES ( res p_t. result . status = = CORE_RPC_STATUS_OK , false , " Failed to get hard fork status " ) ;
bool close_enough = res . height > = resp_t . result . earliest_height - 10 ; // start using the rules a bit beforehand
if ( close_enough )
@ -2641,20 +2655,84 @@ uint64_t wallet2::get_upper_tranaction_size_limit()
return ( ( full_reward_zone * 125 ) / 100 ) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE ;
}
//----------------------------------------------------------------------------------------------------
std : : vector < wallet2 : : pending_tx > wallet2 : : create_dust_sweep_transactions ( )
std : : vector < size_t > wallet2 : : select_available_outputs ( std : : function < bool ( const transfer_details & td ) > f )
{
std : : vector < size_t > outputs ;
size_t n = 0 ;
for ( transfer_container : : const_iterator i = m_transfers . begin ( ) ; i ! = m_transfers . end ( ) ; + + i , + + n )
{
if ( i - > m_spent )
continue ;
if ( ! is_transfer_unlocked ( * i ) )
continue ;
if ( f ( * i ) )
outputs . push_back ( n ) ;
}
return outputs ;
}
//----------------------------------------------------------------------------------------------------
std : : vector < uint64_t > wallet2 : : get_unspent_amounts_vector ( )
{
std : : set < uint64_t > set ;
for ( const auto & td : m_transfers )
{
if ( ! td . m_spent )
set . insert ( td . amount ( ) ) ;
}
std : : vector < uint64_t > vector ;
vector . reserve ( set . size ( ) ) ;
for ( const auto & i : set )
{
vector . push_back ( i ) ;
}
return vector ;
}
//----------------------------------------------------------------------------------------------------
std : : vector < size_t > wallet2 : : select_available_unmixable_outputs ( )
{
// request all outputs with at least 3 instances, so we can use mixin 2 with
epee : : json_rpc : : request < cryptonote : : COMMAND_RPC_GET_OUTPUT_HISTOGRAM : : request > req_t = AUTO_VAL_INIT ( req_t ) ;
epee : : json_rpc : : response < cryptonote : : COMMAND_RPC_GET_OUTPUT_HISTOGRAM : : response , std : : string > resp_t = AUTO_VAL_INIT ( resp_t ) ;
m_daemon_rpc_mutex . lock ( ) ;
req_t . jsonrpc = " 2.0 " ;
req_t . id = epee : : serialization : : storage_entry ( 0 ) ;
req_t . method = " get_output_histogram " ;
req_t . params . amounts = get_unspent_amounts_vector ( ) ;
req_t . params . min_count = 3 ;
req_t . params . max_count = 0 ;
bool r = net_utils : : invoke_http_json_remote_command2 ( m_daemon_address + " /json_rpc " , req_t , resp_t , m_http_client ) ;
m_daemon_rpc_mutex . unlock ( ) ;
THROW_WALLET_EXCEPTION_IF ( ! r , error : : no_connection_to_daemon , " select_available_unmixable_outputs " ) ;
THROW_WALLET_EXCEPTION_IF ( resp_t . result . status = = CORE_RPC_STATUS_BUSY , error : : daemon_busy , " get_output_histogram " ) ;
THROW_WALLET_EXCEPTION_IF ( resp_t . result . status ! = CORE_RPC_STATUS_OK , error : : get_histogram_error , resp_t . result . status ) ;
std : : set < uint64_t > mixable ;
for ( const auto & i : resp_t . result . histogram )
{
mixable . insert ( i . amount ) ;
}
return select_available_outputs ( [ mixable ] ( const transfer_details & td ) {
const uint64_t amount = td . amount ( ) ;
if ( mixable . find ( amount ) = = mixable . end ( ) )
return true ;
return false ;
} ) ;
}
//----------------------------------------------------------------------------------------------------
std : : vector < wallet2 : : pending_tx > wallet2 : : create_unmixable_sweep_transactions ( )
{
// From hard fork 1, we don't consider small amounts to be dust anymore
const bool hf1_rules = use_fork_rules ( 2 ) ; // first hard fork has version 2
tx_dust_policy dust_policy ( hf1_rules ? 0 : : : config : : DEFAULT_DUST_THRESHOLD ) ;
size_t num_dust_outputs = 0 ;
for ( transfer_container : : const_iterator i = m_transfers . begin ( ) ; i ! = m_transfers . end ( ) ; + + i )
// may throw
std : : vector < size_t > unmixable_outputs = select_available_unmixable_outputs ( ) ;
size_t num_dust_outputs = unmixable_outputs . size ( ) ;
if ( num_dust_outputs = = 0 )
{
const transfer_details & td = * i ;
if ( ! td . m_spent & & ( td . amount ( ) < dust_policy . dust_threshold | | ! is_valid_decomposed_amount ( td . amount ( ) ) ) & & is_transfer_unlocked ( td ) )
{
num_dust_outputs + + ;
}
return std : : vector < wallet2 : : pending_tx > ( ) ;
}
// failsafe split attempt counter
@ -2679,13 +2757,13 @@ std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
uint64_t needed_fee = 0 ;
do
{
transfer_ dust( num_outputs_per_tx , ( uint64_t ) 0 /* unlock_time */ , 0 , detail : : digit_split_strategy , dust_policy , extra , tx , ptx ) ;
transfer_ from( unmixable_outputs , num_outputs_per_tx , ( uint64_t ) 0 /* unlock_time */ , 0 , detail : : digit_split_strategy , dust_policy , extra , tx , ptx ) ;
auto txBlob = t_serializable_object_to_blob ( ptx . tx ) ;
needed_fee = calculate_fee ( txBlob ) ;
// reroll the tx with the actual amount minus the fee
// if there's not enough for the fee, it'll throw
transfer_ dust( num_outputs_per_tx , ( uint64_t ) 0 /* unlock_time */ , needed_fee , detail : : digit_split_strategy , dust_policy , extra , tx , ptx ) ;
transfer_ from( unmixable_outputs , num_outputs_per_tx , ( uint64_t ) 0 /* unlock_time */ , needed_fee , detail : : digit_split_strategy , dust_policy , extra , tx , ptx ) ;
txBlob = t_serializable_object_to_blob ( ptx . tx ) ;
needed_fee = calculate_fee ( txBlob ) ;
} while ( ptx . fee < needed_fee ) ;