@ -74,6 +74,9 @@ using namespace cryptonote;
// arbitrary, used to generate different hashes from the same input
# define CHACHA8_KEY_TAIL 0x8c
# define UNSIGNED_TX_PREFIX "Monero unsigned tx set\001"
# define SIGNED_TX_PREFIX "Monero signed tx set\001"
# define KILL_IOSERVICE() \
do { \
work . reset ( ) ; \
@ -173,15 +176,17 @@ bool wallet2::is_deprecated() const
return is_old_file_format ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : set_spent ( transfer_details & td , uint64_t height )
void wallet2 : : set_spent ( size_t idx , uint64_t height )
{
transfer_details & td = m_transfers [ idx ] ;
LOG_PRINT_L2 ( " Setting SPENT at " < < height < < " : ki " < < td . m_key_image < < " , amount " < < print_money ( td . m_amount ) ) ;
td . m_spent = true ;
td . m_spent_height = height ;
}
//----------------------------------------------------------------------------------------------------
void wallet2 : : set_unspent ( transfer_details & td )
void wallet2 : : set_unspent ( size_t idx )
{
transfer_details & td = m_transfers [ idx ] ;
LOG_PRINT_L2 ( " Setting UNSPENT: ki " < < td . m_key_image < < " , amount " < < print_money ( td . m_amount ) ) ;
td . m_spent = false ;
td . m_spent_height = 0 ;
@ -501,7 +506,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
td . m_mask = rct : : identity ( ) ;
td . m_rct = false ;
}
set_unspent ( td ) ;
set_unspent ( m_transfers. size ( ) - 1 ) ;
m_key_images [ td . m_key_image ] = m_transfers . size ( ) - 1 ;
LOG_PRINT_L0 ( " Received money: " < < print_money ( td . amount ( ) ) < < " , with tx: " < < txid ( ) ) ;
if ( 0 ! = m_callback )
@ -580,7 +585,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
amount = td . amount ( ) ;
LOG_PRINT_L0 ( " Spent money: " < < print_money ( amount ) < < " , with tx: " < < txid ( ) ) ;
tx_money_spent_in_ins + = amount ;
set_spent ( td, height ) ;
set_spent ( i t- > secon d, height ) ;
if ( 0 ! = m_callback )
m_callback - > on_money_spent ( height , tx , amount , tx ) ;
}
@ -997,12 +1002,13 @@ void wallet2::update_pool_state()
if ( pit - > second . m_tx . vin [ vini ] . type ( ) = = typeid ( txin_to_key ) )
{
txin_to_key & tx_in_to_key = boost : : get < txin_to_key > ( pit - > second . m_tx . vin [ vini ] ) ;
for ( auto & td : m_transfers )
for ( size_t i = 0 ; i < m_transfers . size ( ) ; + + i )
{
const transfer_details & td = m_transfers [ i ] ;
if ( td . m_key_image = = tx_in_to_key . k_image )
{
LOG_PRINT_L1 ( " Resetting spent status for output " < < vini < < " : " < < td . m_key_image ) ;
set_unspent ( td ) ;
set_unspent ( i ) ;
break ;
}
}
@ -1301,7 +1307,7 @@ void wallet2::detach_blockchain(uint64_t height)
if ( td . m_spent & & td . m_spent_height > = height )
{
LOG_PRINT_L1 ( " Resetting spent status for output " < < i < < " : " < < td . m_key_image ) ;
set_unspent ( td ) ;
set_unspent ( i ) ;
}
}
@ -1590,7 +1596,8 @@ bool wallet2::verify_password(const std::string& password) const
const cryptonote : : account_keys & keys = account_data_check . get_keys ( ) ;
r = r & & verify_keys ( keys . m_view_secret_key , keys . m_account_address . m_view_public_key ) ;
r = r & & verify_keys ( keys . m_spend_secret_key , keys . m_account_address . m_spend_public_key ) ;
if ( ! m_watch_only )
r = r & & verify_keys ( keys . m_spend_secret_key , keys . m_account_address . m_spend_public_key ) ;
return r ;
}
@ -2128,13 +2135,13 @@ void wallet2::rescan_spent()
if ( td . m_spent )
{
LOG_PRINT_L0 ( " Marking output " < < i < < " ( " < < td . m_key_image < < " ) as unspent, it was marked as spent " ) ;
set_unspent ( td ) ;
set_unspent ( i ) ;
td . m_spent_height = 0 ;
}
else
{
LOG_PRINT_L0 ( " Marking output " < < i < < " ( " < < td . m_key_image < < " ) as spent, it was marked as unspent " ) ;
set_spent ( td , td . m_spent_height ) ;
set_spent ( i , td . m_spent_height ) ;
// unknown height, if this gets reorged, it might still be missed
}
}
@ -2260,7 +2267,7 @@ float wallet2::get_output_relatedness(const transfer_details &td0, const transfe
return 0.0f ;
}
//----------------------------------------------------------------------------------------------------
size_t wallet2 : : pop_best_value_from ( const transfer_container & transfers , std : : vector < size_t > & unused_indices , const std : : list < transfer_container: : iterator > & selected_transfers ) const
size_t wallet2 : : pop_best_value_from ( const transfer_container & transfers , std : : vector < size_t > & unused_indices , const std : : list < size_t > & selected_transfers ) const
{
std : : vector < size_t > candidates ;
float best_relatedness = 1.0f ;
@ -2268,9 +2275,9 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve
{
const transfer_details & candidate = transfers [ unused_indices [ n ] ] ;
float relatedness = 0.0f ;
for ( const auto & i : selected_transfers )
for ( size_t i = 0 ; i < selected_transfers . size ( ) ; + + i )
{
float r = get_output_relatedness ( candidate , * i ) ;
float r = get_output_relatedness ( candidate , transfers [ i ] ) ;
if ( r > relatedness )
{
relatedness = r ;
@ -2292,7 +2299,7 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve
return pop_index ( unused_indices , candidates [ idx ] ) ;
}
//----------------------------------------------------------------------------------------------------
size_t wallet2 : : pop_best_value ( std : : vector < size_t > & unused_indices , const std : : list < transfer_container: : iterator > & selected_transfers ) const
size_t wallet2 : : pop_best_value ( std : : vector < size_t > & unused_indices , const std : : list < size_t > & selected_transfers ) const
{
return pop_best_value_from ( m_transfers , unused_indices , selected_transfers ) ;
}
@ -2301,7 +2308,7 @@ size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::l
// returns:
// direct return: amount of money found
// modified reference: selected_transfers, a list of iterators/indices of input sources
uint64_t wallet2 : : select_transfers ( uint64_t needed_money , std : : vector < size_t > unused_transfers_indices , std : : list < transfer_container: : iterator > & selected_transfers , bool trusted_daemon )
uint64_t wallet2 : : select_transfers ( uint64_t needed_money , std : : vector < size_t > unused_transfers_indices , std : : list < size_t > & selected_transfers , bool trusted_daemon )
{
uint64_t found_money = 0 ;
while ( found_money < needed_money & & ! unused_transfers_indices . empty ( ) )
@ -2309,7 +2316,7 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> un
size_t idx = pop_best_value ( unused_transfers_indices , selected_transfers ) ;
transfer_container : : iterator it = m_transfers . begin ( ) + idx ;
selected_transfers . push_back ( i t ) ;
selected_transfers . push_back ( i dx ) ;
found_money + = it - > amount ( ) ;
}
@ -2513,8 +2520,8 @@ void wallet2::commit_tx(pending_tx& ptx)
{
payment_id = get_payment_id ( ptx ) ;
dests = ptx . dests ;
BOOST_FOREACH ( transfer_container: : iterator it , ptx . selected_transfers )
amount_in + = it- > amount ( ) ;
BOOST_FOREACH ( size_t idx , ptx . selected_transfers )
amount_in + = m_transfers[ idx ] . amount ( ) ;
}
add_unconfirmed_tx ( ptx . tx , amount_in , dests , payment_id , ptx . change_dts . amount ) ;
if ( store_tx_info ( ) )
@ -2524,9 +2531,9 @@ void wallet2::commit_tx(pending_tx& ptx)
LOG_PRINT_L2 ( " transaction " < < txid < < " generated ok and sent to daemon, key_images: [ " < < ptx . key_images < < " ] " ) ;
BOOST_FOREACH ( transfer_container: : iterator it , ptx . selected_transfers )
BOOST_FOREACH ( size_t idx , ptx . selected_transfers )
{
set_spent ( * it , 0 ) ;
set_spent ( idx , 0 ) ;
}
//fee includes dust if dust policy specified it.
@ -2544,7 +2551,153 @@ void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
commit_tx ( ptx ) ;
}
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : save_tx ( const std : : vector < pending_tx > & ptx_vector , const std : : string & filename )
{
LOG_PRINT_L0 ( " saving " < < ptx_vector . size ( ) < < " transactions " ) ;
unsigned_tx_set txs ;
for ( auto & tx : ptx_vector )
txs . txes . push_back ( tx . construction_data ) ;
std : : string s = obj_to_json_str ( txs ) ;
if ( s . empty ( ) )
return false ;
LOG_PRINT_L2 ( " Saving unsigned tx data: " < < s ) ;
// save as binary as there's no implementation of loading a json_archive
if ( ! : : serialization : : dump_binary ( txs , s ) )
return false ;
return epee : : file_io_utils : : save_string_to_file ( filename , std : : string ( UNSIGNED_TX_PREFIX ) + s ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : sign_tx ( const std : : string & unsigned_filename , const std : : string & signed_filename , std : : function < bool ( const unsigned_tx_set & ) > accept_func )
{
std : : string s ;
boost : : system : : error_code errcode ;
if ( ! boost : : filesystem : : exists ( unsigned_filename , errcode ) )
{
LOG_PRINT_L0 ( " File " < < unsigned_filename < < " does not exist: " < < errcode ) ;
return false ;
}
if ( ! epee : : file_io_utils : : load_file_to_string ( unsigned_filename . c_str ( ) , s ) )
{
LOG_PRINT_L0 ( " Failed to load from " < < unsigned_filename ) ;
return false ;
}
const size_t magiclen = strlen ( UNSIGNED_TX_PREFIX ) ;
if ( strncmp ( s . c_str ( ) , UNSIGNED_TX_PREFIX , magiclen ) )
{
LOG_PRINT_L0 ( " Bad magic from " < < unsigned_filename ) ;
return false ;
}
unsigned_tx_set exported_txs ;
if ( ! : : serialization : : parse_binary ( std : : string ( s . c_str ( ) + magiclen , s . size ( ) - magiclen ) , exported_txs ) )
{
LOG_PRINT_L0 ( " Failed to parse data from " < < unsigned_filename ) ;
return false ;
}
LOG_PRINT_L1 ( " Loaded tx unsigned data from binary: " < < exported_txs . txes . size ( ) < < " transactions " ) ;
if ( accept_func & & ! accept_func ( exported_txs ) )
{
LOG_PRINT_L1 ( " Transactions rejected by callback " ) ;
return false ;
}
// sign the transactions
signed_tx_set signed_txes ;
for ( size_t n = 0 ; n < exported_txs . txes . size ( ) ; + + n )
{
const tools : : wallet2 : : tx_construction_data & sd = exported_txs . txes [ n ] ;
LOG_PRINT_L1 ( " " < < ( n + 1 ) < < " : " < < sd . sources . size ( ) < < " inputs, mixin " < < ( sd . sources [ 0 ] . outputs . size ( ) - 1 ) ) ;
signed_txes . ptx . push_back ( pending_tx ( ) ) ;
tools : : wallet2 : : pending_tx & ptx = signed_txes . ptx . back ( ) ;
crypto : : secret_key tx_key ;
std : : vector < cryptonote : : tx_destination_entry > dests = sd . destinations ;
if ( sd . change_dts . amount > 0 )
dests . push_back ( sd . change_dts ) ;
bool r = cryptonote : : construct_tx_and_get_tx_key ( m_account . get_keys ( ) , sd . sources , dests , sd . extra , ptx . tx , sd . unlock_time , tx_key , sd . use_rct ) ;
THROW_WALLET_EXCEPTION_IF ( ! r , error : : tx_not_constructed , sd . sources , sd . destinations , sd . unlock_time , m_testnet ) ;
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
// and if we really go over limit, the daemon will reject when it gets submitted. Chances are it's
// OK anyway since it was generated in the first place, and rerolling should be within a few bytes.
// normally, the tx keys are saved in commit_tx, when the tx is actually sent to the daemon.
// we can't do that here since the tx will be sent from the compromised wallet, which we don't want
// to see that info, so we save it here
if ( store_tx_info ( ) )
{
const crypto : : hash txid = get_transaction_hash ( ptx . tx ) ;
m_tx_keys . insert ( std : : make_pair ( txid , tx_key ) ) ;
}
std : : string key_images ;
bool all_are_txin_to_key = std : : all_of ( ptx . tx . vin . begin ( ) , ptx . tx . vin . end ( ) , [ & ] ( const txin_v & s_e ) - > bool
{
CHECKED_GET_SPECIFIC_VARIANT ( s_e , const txin_to_key , in , false ) ;
key_images + = boost : : to_string ( in . k_image ) + " " ;
return true ;
} ) ;
THROW_WALLET_EXCEPTION_IF ( ! all_are_txin_to_key , error : : unexpected_txin_type , ptx . tx ) ;
ptx . key_images = key_images ;
ptx . fee = 0 ;
for ( const auto & i : sd . sources ) ptx . fee + = i . amount ;
for ( const auto & i : dests ) ptx . fee - = i . amount ;
ptx . dust = 0 ;
ptx . dust_added_to_fee = false ;
ptx . change_dts = sd . change_dts ;
// ptx.selected_transfers = selected_transfers;
ptx . tx_key = rct : : rct2sk ( rct : : identity ( ) ) ; // don't send it back to the untrusted view wallet
ptx . dests = sd . destinations ;
ptx . construction_data = sd ;
}
s = obj_to_json_str ( signed_txes ) ;
if ( s . empty ( ) )
return false ;
LOG_PRINT_L2 ( " Saving signed tx data: " < < s ) ;
// save as binary as there's no implementation of loading a json_archive
if ( ! : : serialization : : dump_binary ( signed_txes , s ) )
return false ;
return epee : : file_io_utils : : save_string_to_file ( signed_filename , std : : string ( SIGNED_TX_PREFIX ) + s ) ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : load_tx ( const std : : string & signed_filename , std : : vector < tools : : wallet2 : : pending_tx > & ptx )
{
std : : string s ;
boost : : system : : error_code errcode ;
signed_tx_set signed_txs ;
if ( ! boost : : filesystem : : exists ( signed_filename , errcode ) )
{
LOG_PRINT_L0 ( " File " < < signed_filename < < " does not exist: " < < errcode ) ;
return false ;
}
if ( ! epee : : file_io_utils : : load_file_to_string ( signed_filename . c_str ( ) , s ) )
{
LOG_PRINT_L0 ( " Failed to load from " < < signed_filename ) ;
return false ;
}
const size_t magiclen = strlen ( SIGNED_TX_PREFIX ) ;
if ( strncmp ( s . c_str ( ) , SIGNED_TX_PREFIX , magiclen ) )
{
LOG_PRINT_L0 ( " Bad magic from " < < signed_filename ) ;
return false ;
}
if ( ! : : serialization : : parse_binary ( std : : string ( s . c_str ( ) + magiclen , s . size ( ) - magiclen ) , signed_txs ) )
{
LOG_PRINT_L0 ( " Failed to parse data from " < < signed_filename ) ;
return false ;
}
LOG_PRINT_L1 ( " Loaded signed tx data from binary: " < < signed_txs . ptx . size ( ) < < " transactions " ) ;
ptx = signed_txs . ptx ;
return true ;
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2 : : get_fee_multiplier ( uint32_t priority , bool use_new_fee ) const
{
static const uint64_t old_multipliers [ 3 ] = { 1 , 2 , 3 } ;
@ -2563,7 +2716,6 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, bool use_new_fee) const
THROW_WALLET_EXCEPTION_IF ( false , error : : invalid_priority ) ;
return 1 ;
}
//----------------------------------------------------------------------------------------------------
// separated the call(s) to wallet2::transfer into their own function
//
@ -2613,9 +2765,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
ptx_vector . push_back ( ptx ) ;
// mark transfers to be used as "spent"
BOOST_FOREACH ( transfer_container: : iterator it , ptx . selected_transfers )
BOOST_FOREACH ( size_t idx , ptx . selected_transfers )
{
set_spent ( * it , 0 ) ;
set_spent ( idx , 0 ) ;
}
}
@ -2625,9 +2777,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
for ( auto & ptx : ptx_vector )
{
// mark transfers to be used as not spent
BOOST_FOREACH ( transfer_container: : iterator it 2, ptx . selected_transfers )
BOOST_FOREACH ( size_t idx 2, ptx . selected_transfers )
{
set_unspent ( * it 2) ;
set_unspent ( idx 2) ;
}
}
@ -2644,9 +2796,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
for ( auto & ptx : ptx_vector )
{
// mark transfers to be used as not spent
BOOST_FOREACH ( transfer_container: : iterator it 2, ptx . selected_transfers )
BOOST_FOREACH ( size_t idx 2, ptx . selected_transfers )
{
set_unspent ( * it 2) ;
set_unspent ( idx 2) ;
}
}
@ -2663,9 +2815,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
for ( auto & ptx : ptx_vector )
{
// mark transfers to be used as not spent
BOOST_FOREACH ( transfer_container: : iterator it 2, ptx . selected_transfers )
BOOST_FOREACH ( size_t idx 2, ptx . selected_transfers )
{
set_unspent ( * it 2) ;
set_unspent ( idx 2) ;
}
}
@ -2675,7 +2827,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
}
template < typename entry >
void wallet2 : : get_outs ( std : : vector < std : : vector < entry > > & outs , const std : : list < transfer_container: : iterator > & selected_transfers , size_t fake_outputs_count )
void wallet2 : : get_outs ( std : : vector < std : : vector < 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 ( ) ;
@ -2688,8 +2840,8 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<tr
req_t . jsonrpc = " 2.0 " ;
req_t . id = epee : : serialization : : storage_entry ( 0 ) ;
req_t . method = " get_output_histogram " ;
for ( auto it : selected_transfers )
req_t . params . amounts . push_back ( it- > is_rct ( ) ? 0 : it - > amount ( ) ) ;
for ( size_t idx : selected_transfers )
req_t . params . amounts . push_back ( m_transfers[ idx ] . is_rct ( ) ? 0 : m_transfers [ idx ] . amount ( ) ) ;
std : : sort ( req_t . params . amounts . begin ( ) , req_t . params . amounts . end ( ) ) ;
auto end = std : : unique ( req_t . params . amounts . begin ( ) , req_t . params . amounts . end ( ) ) ;
req_t . params . amounts . resize ( std : : distance ( req_t . params . amounts . begin ( ) , end ) ) ;
@ -2708,12 +2860,13 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<tr
COMMAND_RPC_GET_OUTPUTS : : request req = AUTO_VAL_INIT ( req ) ;
COMMAND_RPC_GET_OUTPUTS : : response daemon_resp = AUTO_VAL_INIT ( daemon_resp ) ;
for ( transfer_container: : iterator it : selected_transfers )
for ( size_t idx : selected_transfers )
{
const uint64_t amount = it - > is_rct ( ) ? 0 : it - > amount ( ) ;
const transfer_details & td = m_transfers [ idx ] ;
const uint64_t amount = td . is_rct ( ) ? 0 : td . amount ( ) ;
std : : unordered_set < uint64_t > seen_indices ;
// request more for rct in base recent (locked) coinbases are picked, since they're locked for longer
size_t requested_outputs_count = base_requested_outputs_count + ( it- > is_rct ( ) ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0 ) ;
size_t requested_outputs_count = base_requested_outputs_count + ( td. is_rct ( ) ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0 ) ;
size_t start = req . outputs . size ( ) ;
// if there are just enough outputs to mix with, use all of them.
@ -2745,8 +2898,8 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<tr
{
// start with real one
uint64_t num_found = 1 ;
seen_indices . emplace ( it- > m_global_output_index ) ;
req . outputs . push_back ( { amount , it- > m_global_output_index } ) ;
seen_indices . emplace ( td. m_global_output_index ) ;
req . outputs . push_back ( { amount , td. m_global_output_index } ) ;
// while we still need more mixins
while ( num_found < requested_outputs_count )
@ -2798,15 +2951,16 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<tr
std : : unordered_map < uint64_t , uint64_t > scanty_outs ;
size_t base = 0 ;
outs . reserve ( selected_transfers . size ( ) ) ;
for ( transfer_container: : iterator it : selected_transfers )
for ( size_t idx : selected_transfers )
{
size_t requested_outputs_count = base_requested_outputs_count + ( it - > is_rct ( ) ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0 ) ;
const transfer_details & td = m_transfers [ idx ] ;
size_t requested_outputs_count = base_requested_outputs_count + ( td . is_rct ( ) ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0 ) ;
outs . push_back ( std : : vector < entry > ( ) ) ;
outs . back ( ) . reserve ( fake_outputs_count + 1 ) ;
const rct : : key mask = it- > is_rct ( ) ? rct : : commit ( it- > amount ( ) , it - > m_mask ) : rct : : zeroCommit ( it - > amount ( ) ) ;
const rct : : key mask = td. is_rct ( ) ? rct : : commit ( td. amount ( ) , td . m_mask ) : rct : : zeroCommit ( td . amount ( ) ) ;
// pick real out first (it will be sorted when done)
outs . back ( ) . push_back ( std : : make_tuple ( it- > m_global_output_index , boost : : get < txout_to_key > ( it- > m_tx . vout [ it - > m_internal_output_index ] . target ) . key , mask ) ) ;
outs . back ( ) . 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 ) ) ;
// 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
@ -2816,12 +2970,12 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<tr
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 of size " < < print_money ( it- > is_rct ( ) ? 0 : it - > amount ( ) ) ) ;
LOG_PRINT_L2 ( " Looking for " < < ( fake_outputs_count + 1 ) < < " outputs of size " < < print_money ( td. is_rct ( ) ? 0 : td . amount ( ) ) ) ;
for ( size_t o = 0 ; o < requested_outputs_count & & outs . back ( ) . size ( ) < fake_outputs_count + 1 ; + + o )
{
size_t i = base + order [ o ] ;
LOG_PRINT_L2 ( " Index " < < i < < " / " < < requested_outputs_count < < " : idx " < < req . outputs [ i ] . index < < " (real " < < it- > m_global_output_index < < " ), unlocked " < < daemon_resp . outs [ i ] . unlocked < < " , key " < < daemon_resp . outs [ i ] . key ) ;
if ( req . outputs [ i ] . index = = it- > m_global_output_index ) // don't re-add real one
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 ;
@ -2832,7 +2986,7 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<tr
}
if ( outs . back ( ) . size ( ) < fake_outputs_count + 1 )
{
scanty_outs [ it- > is_rct ( ) ? 0 : it - > amount ( ) ] = outs . back ( ) . size ( ) ;
scanty_outs [ td. is_rct ( ) ? 0 : td . amount ( ) ] = outs . back ( ) . size ( ) ;
}
else
{
@ -2845,18 +2999,19 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<tr
}
else
{
for ( transfer_container: : iterator it : selected_transfers )
for ( size_t idx : selected_transfers )
{
const transfer_details & td = m_transfers [ idx ] ;
std : : vector < entry > v ;
const rct : : key mask = it- > is_rct ( ) ? rct : : commit ( it- > amount ( ) , it - > m_mask ) : rct : : zeroCommit ( it - > amount ( ) ) ;
v . push_back ( std : : make_tuple ( it- > m_global_output_index , boost : : get < txout_to_key > ( it- > m_tx . vout [ it - > m_internal_output_index ] . target ) . key , mask ) ) ;
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 ) ) ;
outs . push_back ( v ) ;
}
}
}
template < typename T >
void wallet2 : : transfer_selected ( const std : : vector < cryptonote : : tx_destination_entry > & dsts , const std : : list < transfer_container: : iterator > selected_transfers , size_t fake_outputs_count ,
void wallet2 : : transfer_selected ( const std : : vector < cryptonote : : tx_destination_entry > & dsts , const std : : list < size_t > selected_transfers , size_t fake_outputs_count ,
uint64_t unlock_time , uint64_t fee , const std : : vector < uint8_t > & extra , T destination_split_strategy , const tx_dust_policy & dust_policy , cryptonote : : transaction & tx , pending_tx & ptx )
{
using namespace cryptonote ;
@ -2878,9 +3033,9 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
}
uint64_t found_money = 0 ;
BOOST_FOREACH ( auto it , selected_transfers )
BOOST_FOREACH ( size_t idx , selected_transfers )
{
found_money + = it- > amount ( ) ;
found_money + = m_transfers[ idx ] . amount ( ) ;
}
LOG_PRINT_L2 ( " wanted " < < print_money ( needed_money ) < < " , found " < < print_money ( found_money ) < < " , fee " < < print_money ( fee ) ) ;
@ -2894,11 +3049,11 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
typedef cryptonote : : tx_source_entry : : output_entry tx_output_entry ;
size_t i = 0 , out_index = 0 ;
std : : vector < cryptonote : : tx_source_entry > sources ;
BOOST_FOREACH ( transfer_container: : iterator it , selected_transfers )
BOOST_FOREACH ( size_t idx , selected_transfers )
{
sources . resize ( sources . size ( ) + 1 ) ;
cryptonote : : tx_source_entry & src = sources . back ( ) ;
transfer_details & td = * it ;
const transfer_details & td = m_transfers [ idx ] ;
src . amount = td . amount ( ) ;
src . rct = td . is_rct ( ) ;
//paste keys (fake and real)
@ -2983,9 +3138,15 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
ptx . selected_transfers = selected_transfers ;
ptx . tx_key = tx_key ;
ptx . dests = dsts ;
ptx . construction_data . sources = sources ;
ptx . construction_data . destinations = dsts ;
ptx . construction_data . change_dts = change_dts ;
ptx . construction_data . extra = tx . extra ;
ptx . construction_data . unlock_time = unlock_time ;
ptx . construction_data . use_rct = false ;
}
void wallet2 : : transfer_selected_rct ( std : : vector < cryptonote : : tx_destination_entry > dsts , const std : : list < transfer_container : : iterator > selected_transfers , size_t fake_outputs_count ,
void wallet2 : : transfer_selected_rct ( std : : vector < cryptonote : : tx_destination_entry > dsts , const std : : list < size_t > selected_transfers , size_t fake_outputs_count ,
uint64_t unlock_time , uint64_t fee , const std : : vector < uint8_t > & extra , cryptonote : : transaction & tx , pending_tx & ptx )
{
using namespace cryptonote ;
@ -3007,9 +3168,9 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
}
uint64_t found_money = 0 ;
BOOST_FOREACH ( auto it , selected_transfers )
BOOST_FOREACH ( size_t idx , selected_transfers )
{
found_money + = it- > amount ( ) ;
found_money + = m_transfers[ idx ] . amount ( ) ;
}
LOG_PRINT_L2 ( " wanted " < < print_money ( needed_money ) < < " , found " < < print_money ( found_money ) < < " , fee " < < print_money ( fee ) ) ;
@ -3022,11 +3183,11 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
//prepare inputs
size_t i = 0 , out_index = 0 ;
std : : vector < cryptonote : : tx_source_entry > sources ;
BOOST_FOREACH ( transfer_container: : iterator it , selected_transfers )
BOOST_FOREACH ( size_t idx , selected_transfers )
{
sources . resize ( sources . size ( ) + 1 ) ;
cryptonote : : tx_source_entry & src = sources . back ( ) ;
transfer_details & td = * it ;
const transfer_details & td = m_transfers [ idx ] ;
src . amount = td . amount ( ) ;
src . rct = td . is_rct ( ) ;
//paste mixin transaction
@ -3096,6 +3257,12 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx . selected_transfers = selected_transfers ;
ptx . tx_key = tx_key ;
ptx . dests = dsts ;
ptx . construction_data . sources = sources ;
ptx . construction_data . destinations = dsts ;
ptx . construction_data . change_dts = change_dts ;
ptx . construction_data . extra = tx . extra ;
ptx . construction_data . unlock_time = unlock_time ;
ptx . construction_data . use_rct = true ;
}
static size_t estimate_rct_tx_size ( int n_inputs , int mixin , int n_outputs )
@ -3225,7 +3392,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
uint64_t needed_money ;
uint64_t accumulated_fee , accumulated_outputs , accumulated_change ;
struct TX {
std : : list < transfer_container: : iterator > selected_transfers ;
std : : list < size_t > selected_transfers ;
std : : vector < cryptonote : : tx_destination_entry > dsts ;
cryptonote : : transaction tx ;
pending_tx ptx ;
@ -3332,7 +3499,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2 ( " Picking output " < < idx < < " , amount " < < print_money ( td . amount ( ) ) ) ;
// add this output to the list to spend
tx . selected_transfers . push_back ( m_transfers. begin ( ) + idx) ;
tx . selected_transfers . push_back ( idx) ;
uint64_t available_amount = td . amount ( ) ;
accumulated_outputs + = available_amount ;
@ -3400,7 +3567,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
auto txBlob = t_serializable_object_to_blob ( test_ptx . tx ) ;
needed_fee = calculate_fee ( fee_per_kb , txBlob , fee_multiplier ) ;
available_for_fee = test_ptx . fee + test_ptx . change_dts . amount + ( ! test_ptx . dust_added_to_fee ? test_ptx . dust : 0 ) ;
LOG_PRINT_L2 ( " Made a " < < txBlob . size ( ) < < " kB tx, with " < < print_money ( available_for_fee ) < < " available for fee ( " < <
LOG_PRINT_L2 ( " Made a " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( available_for_fee ) < < " available for fee ( " < <
print_money ( needed_fee ) < < " needed) " ) ;
if ( needed_fee > available_for_fee & & dsts [ 0 ] . amount > 0 )
@ -3472,8 +3639,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
TX & tx = * i ;
uint64_t tx_money = 0 ;
for ( s td: : list < transfer_container : : iterator > : : const_iterator mi = tx . selected_transfers . begin ( ) ; mi ! = tx . selected_transfers . end ( ) ; + + mi )
tx_money + = ( * mi ) - > amount ( ) ;
for ( s ize_t idx : tx . selected_transfers )
tx_money + = m_transfers [ idx ] . amount ( ) ;
LOG_PRINT_L1 ( " Transaction " < < ( 1 + std : : distance ( txes . begin ( ) , i ) ) < < " / " < < txes . size ( ) < <
" : " < < ( tx . bytes + 1023 ) / 1024 < < " kB, sending " < < print_money ( tx_money ) < < " in " < < tx . selected_transfers . size ( ) < <
" outputs to " < < tx . dsts . size ( ) < < " destination(s), including " < <
@ -3491,7 +3658,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
std : : vector < size_t > unused_dust_indices ;
uint64_t accumulated_fee , accumulated_outputs , accumulated_change ;
struct TX {
std : : list < transfer_container: : iterator > selected_transfers ;
std : : list < size_t > selected_transfers ;
std : : vector < cryptonote : : tx_destination_entry > dsts ;
cryptonote : : transaction tx ;
pending_tx ptx ;
@ -3543,7 +3710,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
LOG_PRINT_L2 ( " Picking output " < < idx < < " , amount " < < print_money ( td . amount ( ) ) ) ;
// add this output to the list to spend
tx . selected_transfers . push_back ( m_transfers. begin ( ) + idx) ;
tx . selected_transfers . push_back ( idx) ;
uint64_t available_amount = td . amount ( ) ;
accumulated_outputs + = available_amount ;
@ -3576,7 +3743,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
auto txBlob = t_serializable_object_to_blob ( test_ptx . tx ) ;
needed_fee = calculate_fee ( fee_per_kb , txBlob , fee_multiplier ) ;
available_for_fee = test_ptx . fee + test_ptx . dests [ 0 ] . amount + test_ptx . change_dts . amount ;
LOG_PRINT_L2 ( " Made a " < < txBlob . size ( ) < < " kB tx, with " < < print_money ( available_for_fee ) < < " available for fee ( " < <
LOG_PRINT_L2 ( " Made a " < < ( ( txBlob . size ( ) + 1023 ) / 1024 ) < < " kB tx, with " < < print_money ( available_for_fee ) < < " available for fee ( " < <
print_money ( needed_fee ) < < " needed) " ) ;
THROW_WALLET_EXCEPTION_IF ( needed_fee > available_for_fee , error : : wallet_internal_error , " Transaction cannot pay for itself " ) ;
@ -3620,8 +3787,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
{
TX & tx = * i ;
uint64_t tx_money = 0 ;
for ( s td: : list < transfer_container : : iterator > : : const_iterator mi = tx . selected_transfers . begin ( ) ; mi ! = tx . selected_transfers . end ( ) ; + + mi )
tx_money + = ( * mi ) - > amount ( ) ;
for ( s ize_t idx : tx . selected_transfers )
tx_money + = m_transfers [ idx ] . amount ( ) ;
LOG_PRINT_L1 ( " Transaction " < < ( 1 + std : : distance ( txes . begin ( ) , i ) ) < < " / " < < txes . size ( ) < <
" : " < < ( tx . bytes + 1023 ) / 1024 < < " kB, sending " < < print_money ( tx_money ) < < " in " < < tx . selected_transfers . size ( ) < <
" outputs to " < < tx . dsts . size ( ) < < " destination(s), including " < <
@ -3658,14 +3825,14 @@ void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs,
// select all dust inputs for transaction
// throw if there are none
uint64_t money = 0 ;
std : : list < transfer_container: : iterator > selected_transfers ;
std : : list < size_t > 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 ] ) ;
selected_transfers . push_back ( outs[ n ] ) ;
money + = td . amount ( ) ;
if ( selected_transfers . size ( ) > = num_outputs )
break ;
@ -3693,11 +3860,11 @@ void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs,
//prepare inputs
size_t i = 0 ;
std : : vector < cryptonote : : tx_source_entry > sources ;
BOOST_FOREACH ( transfer_container: : iterator it , selected_transfers )
BOOST_FOREACH ( size_t idx , selected_transfers )
{
sources . resize ( sources . size ( ) + 1 ) ;
cryptonote : : tx_source_entry & src = sources . back ( ) ;
transfer_details & td = * it ;
const transfer_details & td = m_transfers [ idx ] ;
src . amount = td . amount ( ) ;
src . rct = td . is_rct ( ) ;
@ -3754,6 +3921,12 @@ void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs,
ptx . selected_transfers = selected_transfers ;
ptx . tx_key = tx_key ;
ptx . dests = dsts ;
ptx . construction_data . sources = sources ;
ptx . construction_data . destinations = dsts ;
ptx . construction_data . change_dts = change_dts ;
ptx . construction_data . extra = tx . extra ;
ptx . construction_data . unlock_time = unlock_time ;
ptx . construction_data . use_rct = false ;
}
//----------------------------------------------------------------------------------------------------
@ -3969,9 +4142,9 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
ptx_vector . push_back ( ptx ) ;
// mark transfers to be used as "spent"
BOOST_FOREACH ( transfer_container: : iterator it , ptx . selected_transfers )
BOOST_FOREACH ( size_t idx , ptx . selected_transfers )
{
set_spent ( * it , 0 ) ;
set_spent ( idx , 0 ) ;
}
}
@ -3981,9 +4154,9 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
for ( auto & ptx : ptx_vector )
{
// mark transfers to be used as not spent
BOOST_FOREACH ( transfer_container: : iterator it 2, ptx . selected_transfers )
BOOST_FOREACH ( size_t idx 2, ptx . selected_transfers )
{
set_unspent ( * it 2) ;
set_unspent ( idx 2) ;
}
}
@ -3999,9 +4172,9 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
for ( auto & ptx : ptx_vector )
{
// mark transfers to be used as not spent
BOOST_FOREACH ( transfer_container: : iterator it 2, ptx . selected_transfers )
BOOST_FOREACH ( size_t idx 2, ptx . selected_transfers )
{
set_unspent ( * it 2) ;
set_unspent ( idx 2) ;
}
}
@ -4018,9 +4191,9 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
for ( auto & ptx : ptx_vector )
{
// mark transfers to be used as not spent
BOOST_FOREACH ( transfer_container: : iterator it 2, ptx . selected_transfers )
BOOST_FOREACH ( size_t idx 2, ptx . selected_transfers )
{
set_unspent ( * it 2) ;
set_unspent ( idx 2) ;
}
}