@ -3981,10 +3981,134 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
}
}
bool wallet2 : : tx_add_fake_output ( std : : vector < std : : vector < tools : : wallet2 : : get_outs_entry > > & outs , uint64_t global_index , const crypto : : public_key & tx_public_key , const rct : : key & mask , uint64_t real_index , bool unlocked ) const
{
if ( ! unlocked ) // don't add locked outs
return false ;
if ( global_index = = real_index ) // don't re-add real one
return false ;
auto item = std : : make_tuple ( global_index , tx_public_key , mask ) ;
if ( std : : find ( outs . back ( ) . begin ( ) , outs . back ( ) . end ( ) , item ) ! = outs . back ( ) . end ( ) ) // don't add duplicates
return false ;
outs . back ( ) . push_back ( item ) ;
return true ;
}
void wallet2 : : light_wallet_get_outs ( std : : vector < std : : vector < tools : : wallet2 : : get_outs_entry > > & outs , const std : : list < size_t > & selected_transfers , size_t fake_outputs_count ) {
MDEBUG ( " LIGHTWALLET - Getting random outs " ) ;
cryptonote : : COMMAND_RPC_GET_RANDOM_OUTS : : request oreq ;
cryptonote : : COMMAND_RPC_GET_RANDOM_OUTS : : response ores ;
size_t light_wallet_requested_outputs_count = ( size_t ) ( ( fake_outputs_count + 1 ) * 1.5 + 1 ) ;
// Amounts to ask for
// MyMonero api handle amounts and fees as strings
for ( size_t idx : selected_transfers ) {
const uint64_t ask_amount = m_transfers [ idx ] . is_rct ( ) ? 0 : m_transfers [ idx ] . amount ( ) ;
std : : ostringstream amount_ss ;
amount_ss < < ask_amount ;
oreq . amounts . push_back ( amount_ss . str ( ) ) ;
}
oreq . count = light_wallet_requested_outputs_count ;
m_daemon_rpc_mutex . lock ( ) ;
bool r = epee : : net_utils : : invoke_http_json ( " /get_random_outs " , oreq , ores , m_http_client , rpc_timeout , " POST " ) ;
m_daemon_rpc_mutex . unlock ( ) ;
THROW_WALLET_EXCEPTION_IF ( ! r , error : : no_connection_to_daemon , " get_random_outs " ) ;
THROW_WALLET_EXCEPTION_IF ( ores . amount_outs . empty ( ) , error : : wallet_internal_error , " No outputs recieved from light wallet node. Error: " + ores . Error ) ;
// Check if we got enough outputs for each amount
for ( auto & out : ores . amount_outs ) {
const uint64_t out_amount = boost : : lexical_cast < uint64_t > ( out . amount ) ;
THROW_WALLET_EXCEPTION_IF ( out . outputs . size ( ) < light_wallet_requested_outputs_count , error : : wallet_internal_error , " Not enough outputs for amount: " + boost : : lexical_cast < std : : string > ( out . amount ) ) ;
MDEBUG ( out . outputs . size ( ) < < " outputs for amount " + boost : : lexical_cast < std : : string > ( out . amount ) + " received from light wallet node " ) ;
}
MDEBUG ( " selected transfers size: " < < selected_transfers . size ( ) ) ;
for ( size_t idx : selected_transfers )
{
// Create new index
outs . push_back ( std : : vector < get_outs_entry > ( ) ) ;
outs . back ( ) . reserve ( fake_outputs_count + 1 ) ;
// add real output first
const transfer_details & td = m_transfers [ idx ] ;
const uint64_t amount = td . is_rct ( ) ? 0 : td . amount ( ) ;
outs . back ( ) . push_back ( std : : make_tuple ( td . m_global_output_index , td . get_public_key ( ) , rct : : commit ( td . amount ( ) , td . m_mask ) ) ) ;
MDEBUG ( " added real output " < < string_tools : : pod_to_hex ( td . get_public_key ( ) ) ) ;
// Even if the lightwallet server returns random outputs, we pick them randomly.
std : : vector < size_t > order ;
order . resize ( light_wallet_requested_outputs_count ) ;
for ( size_t n = 0 ; n < order . size ( ) ; + + n )
order [ n ] = n ;
std : : shuffle ( order . begin ( ) , order . end ( ) , std : : default_random_engine ( crypto : : rand < unsigned > ( ) ) ) ;
LOG_PRINT_L2 ( " Looking for " < < ( fake_outputs_count + 1 ) < < " outputs with amounts " < < print_money ( td . is_rct ( ) ? 0 : td . amount ( ) ) ) ;
MDEBUG ( " OUTS SIZE: " < < outs . back ( ) . size ( ) ) ;
for ( size_t o = 0 ; o < light_wallet_requested_outputs_count & & outs . back ( ) . size ( ) < fake_outputs_count + 1 ; + + o )
{
// Random pick
size_t i = order [ o ] ;
// Find which random output key to use
bool found_amount = false ;
size_t amount_key ;
for ( amount_key = 0 ; amount_key < ores . amount_outs . size ( ) ; + + amount_key )
{
if ( boost : : lexical_cast < uint64_t > ( ores . amount_outs [ amount_key ] . amount ) = = amount ) {
found_amount = true ;
break ;
}
}
THROW_WALLET_EXCEPTION_IF ( ! found_amount , error : : wallet_internal_error , " Outputs for amount " + boost : : lexical_cast < std : : string > ( ores . amount_outs [ amount_key ] . amount ) + " not found " ) ;
LOG_PRINT_L2 ( " Index " < < i < < " / " < < light_wallet_requested_outputs_count < < " : idx " < < ores . amount_outs [ amount_key ] . outputs [ i ] . global_index < < " (real " < < td . m_global_output_index < < " ), unlocked " < < " (always in light) " < < " , key " < < ores . amount_outs [ 0 ] . outputs [ i ] . public_key ) ;
// Convert light wallet string data to proper data structures
crypto : : public_key tx_public_key ;
rct : : key mask = AUTO_VAL_INIT ( mask ) ; // decrypted mask - not used here
rct : : key rct_commit = AUTO_VAL_INIT ( rct_commit ) ;
THROW_WALLET_EXCEPTION_IF ( string_tools : : validate_hex ( 64 , ores . amount_outs [ amount_key ] . outputs [ i ] . public_key ) , error : : wallet_internal_error , " Invalid public_key " ) ;
string_tools : : hex_to_pod ( ores . amount_outs [ amount_key ] . outputs [ i ] . public_key , tx_public_key ) ;
const uint64_t global_index = ores . amount_outs [ amount_key ] . outputs [ i ] . global_index ;
if ( ! light_wallet_parse_rct_str ( ores . amount_outs [ amount_key ] . outputs [ i ] . rct , tx_public_key , 0 , mask , rct_commit , false ) )
rct_commit = rct : : zeroCommit ( td . amount ( ) ) ;
if ( tx_add_fake_output ( outs , global_index , tx_public_key , rct_commit , td . m_global_output_index , true ) ) {
MDEBUG ( " added fake output " < < ores . amount_outs [ amount_key ] . outputs [ i ] . public_key ) ;
MDEBUG ( " index " < < global_index ) ;
}
}
THROW_WALLET_EXCEPTION_IF ( outs . back ( ) . size ( ) < fake_outputs_count + 1 , error : : wallet_internal_error , " Not enough fake outputs found " ) ;
// Real output is the first. Shuffle outputs
MTRACE ( outs . back ( ) . size ( ) < < " outputs added. Sorting outputs by index: " ) ;
std : : sort ( outs . back ( ) . begin ( ) , outs . back ( ) . end ( ) , [ ] ( const get_outs_entry & a , const get_outs_entry & b ) { return std : : get < 0 > ( a ) < std : : get < 0 > ( b ) ; } ) ;
// Print output order
for ( auto added_out : outs . back ( ) )
MTRACE ( std : : get < 0 > ( added_out ) ) ;
}
}
void wallet2 : : get_outs ( std : : vector < std : : vector < tools : : wallet2 : : get_outs_entry > > & outs , const std : : list < size_t > & selected_transfers , size_t fake_outputs_count )
{
LOG_PRINT_L2 ( " fake_outputs_count: " < < fake_outputs_count ) ;
outs . clear ( ) ;
if ( m_light_wallet & & fake_outputs_count > 0 ) {
light_wallet_get_outs ( outs , selected_transfers , fake_outputs_count ) ;
return ;
}
if ( fake_outputs_count > 0 )
{
// get histogram for the amounts we need
@ -4181,14 +4305,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{
size_t i = base + order [ o ] ;
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 ) ;
if ( req . outputs [ i ] . index = = td . m_global_output_index ) // don't re-add real one
continue ;
if ( ! daemon_resp . outs [ i ] . unlocked ) // don't add locked outs
continue ;
auto item = std : : make_tuple ( req . outputs [ i ] . index , daemon_resp . outs [ i ] . key , daemon_resp . outs [ i ] . mask ) ;
if ( std : : find ( outs . back ( ) . begin ( ) , outs . back ( ) . end ( ) , item ) ! = outs . back ( ) . end ( ) ) // don't add duplicates
continue ;
outs . back ( ) . push_back ( item ) ;
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 ) ;
}
if ( outs . back ( ) . size ( ) < fake_outputs_count + 1 )
{
@ -4210,7 +4327,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
const transfer_details & td = m_transfers [ idx ] ;
std : : vector < get_outs_entry > v ;
const rct : : key mask = td . is_rct ( ) ? rct : : commit ( td . amount ( ) , td . m_mask ) : rct : : zeroCommit ( td . amount ( ) ) ;
v . push_back ( std : : make_tuple ( td . m_global_output_index , boost: : get < txout_to_key > ( td . m_tx . vout [ td . m_internal_output_index ] . target ) . key , mask ) ) ;
v . push_back ( std : : make_tuple ( td . m_global_output_index , td. get_public_key ( ) , mask ) ) ;
outs . push_back ( v ) ;
}
}
@ -4446,7 +4563,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
tx_output_entry real_oe ;
real_oe . first = td . m_global_output_index ;
real_oe . second . dest = rct : : pk2rct ( boost: : get < txout_to_key > ( td . m_tx . vout [ td . m_internal_output_index ] . target ) . key ) ;
real_oe . second . dest = rct : : pk2rct ( td. get_public_key ( ) ) ;
real_oe . second . mask = rct : : commit ( td . amount ( ) , td . m_mask ) ;
* it_to_replace = real_oe ;
src . real_out_tx_key = get_tx_pub_key_from_extra ( td . m_tx , td . m_pk_index ) ;