@ -1571,6 +1571,98 @@ void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_A
output_data_t data = m_db - > get_output_key ( amount , i ) ;
oen . out_key = data . pubkey ;
}
uint64_t Blockchain : : get_num_mature_outputs ( uint64_t amount ) const
{
auto num_outs = m_db - > get_num_outputs ( amount ) ;
// ensure we don't include outputs that aren't yet eligible to be used
// outpouts are sorted by height
while ( num_outs > 0 )
{
const tx_out_index toi = m_db - > get_output_tx_and_index ( amount , num_outs - 1 ) ;
const uint64_t height = m_db - > get_tx_block_height ( toi . first ) ;
if ( height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE < = m_db - > height ( ) )
break ;
- - num_outs ;
}
return num_outs ;
}
std : : vector < uint64_t > Blockchain : : get_random_outputs ( uint64_t amount , uint64_t count ) const
{
uint64_t num_outs = get_num_mature_outputs ( amount ) ;
std : : vector < uint64_t > indices ;
std : : unordered_set < uint64_t > seen_indices ;
// if there aren't enough outputs to mix with (or just enough),
// use all of them. Eventually this should become impossible.
if ( num_outs < = count )
{
for ( uint64_t i = 0 ; i < num_outs ; i + + )
{
// get tx_hash, tx_out_index from DB
tx_out_index toi = m_db - > get_output_tx_and_index ( amount , i ) ;
// if tx is unlocked, add output to indices
if ( is_tx_spendtime_unlocked ( m_db - > get_tx_unlock_time ( toi . first ) ) )
{
indices . push_back ( i ) ;
}
}
}
else
{
// while we still need more mixins
while ( indices . size ( ) < count )
{
// if we've gone through every possible output, we've gotten all we can
if ( seen_indices . size ( ) = = num_outs )
{
break ;
}
// get a random output index from the DB. If we've already seen it,
// return to the top of the loop and try again, otherwise add it to the
// list of output indices we've seen.
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
uint64_t r = crypto : : rand < uint64_t > ( ) % ( ( uint64_t ) 1 < < 53 ) ;
double frac = std : : sqrt ( ( double ) r / ( ( uint64_t ) 1 < < 53 ) ) ;
uint64_t i = ( uint64_t ) ( frac * num_outs ) ;
// just in case rounding up to 1 occurs after sqrt
if ( i = = num_outs )
- - i ;
if ( seen_indices . count ( i ) )
{
continue ;
}
seen_indices . emplace ( i ) ;
// get tx_hash, tx_out_index from DB
tx_out_index toi = m_db - > get_output_tx_and_index ( amount , i ) ;
// if the output's transaction is unlocked, add the output's index to
// our list.
if ( is_tx_spendtime_unlocked ( m_db - > get_tx_unlock_time ( toi . first ) ) )
{
indices . push_back ( i ) ;
}
}
}
return indices ;
}
crypto : : public_key Blockchain : : get_output_key ( uint64_t amount , uint64_t global_index ) const
{
output_data_t data = m_db - > get_output_key ( amount , global_index ) ;
return data . pubkey ;
}
//------------------------------------------------------------------
// This function takes an RPC request for mixins and creates an RPC response
// with the requested mixins.
@ -1585,80 +1677,18 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
// from BlockchainDB where <n> is req.outs_count (number of mixins).
for ( uint64_t amount : req . amounts )
{
auto num_outs = m_db - > get_num_outputs ( amount ) ;
// ensure we don't include outputs that aren't yet eligible to be used
// outpouts are sorted by height
while ( num_outs > 0 )
{
const tx_out_index toi = m_db - > get_output_tx_and_index ( amount , num_outs - 1 ) ;
const uint64_t height = m_db - > get_tx_block_height ( toi . first ) ;
if ( height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE < = m_db - > height ( ) )
break ;
- - num_outs ;
}
// create outs_for_amount struct and populate amount field
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount & result_outs = * res . outs . insert ( res . outs . end ( ) , COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : outs_for_amount ( ) ) ;
result_outs . amount = amount ;
std : : unordered_set < uint64_t > seen_indices ;
// if there aren't enough outputs to mix with (or just enough),
// use all of them. Eventually this should become impossible.
if ( num_outs < = req . outs_count )
{
for ( uint64_t i = 0 ; i < num_outs ; i + + )
{
// get tx_hash, tx_out_index from DB
tx_out_index toi = m_db - > get_output_tx_and_index ( amount , i ) ;
// if tx is unlocked, add output to result_outs
if ( is_tx_spendtime_unlocked ( m_db - > get_tx_unlock_time ( toi . first ) ) )
{
add_out_to_get_random_outs ( result_outs , amount , i ) ;
}
std : : vector < uint64_t > indices = get_random_outputs ( amount , req . outs_count ) ;
}
}
else
for ( auto i : indices )
{
// while we still need more mixins
while ( result_outs . outs . size ( ) < req . outs_count )
{
// if we've gone through every possible output, we've gotten all we can
if ( seen_indices . size ( ) = = num_outs )
{
break ;
}
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : out_entry & oe = * result_outs . outs . insert ( result_outs . outs . end ( ) , COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS : : out_entry ( ) ) ;
// get a random output index from the DB. If we've already seen it,
// return to the top of the loop and try again, otherwise add it to the
// list of output indices we've seen.
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
uint64_t r = crypto : : rand < uint64_t > ( ) % ( ( uint64_t ) 1 < < 53 ) ;
double frac = std : : sqrt ( ( double ) r / ( ( uint64_t ) 1 < < 53 ) ) ;
uint64_t i = ( uint64_t ) ( frac * num_outs ) ;
// just in case rounding up to 1 occurs after sqrt
if ( i = = num_outs )
- - i ;
if ( seen_indices . count ( i ) )
{
continue ;
}
seen_indices . emplace ( i ) ;
// get tx_hash, tx_out_index from DB
tx_out_index toi = m_db - > get_output_tx_and_index ( amount , i ) ;
// if the output's transaction is unlocked, add the output's index to
// our list.
if ( is_tx_spendtime_unlocked ( m_db - > get_tx_unlock_time ( toi . first ) ) )
{
add_out_to_get_random_outs ( result_outs , amount , i ) ;
}
}
oe . global_amount_index = i ;
oe . out_key = get_output_key ( amount , i ) ;
}
}
return true ;
@ -1816,6 +1846,15 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
return true ;
}
//------------------------------------------------------------------
void Blockchain : : get_output_key_mask_unlocked ( const uint64_t & amount , const uint64_t & index , crypto : : public_key & key , rct : : key & mask , bool & unlocked ) const
{
const auto o_data = m_db - > get_output_key ( amount , index ) ;
key = o_data . pubkey ;
mask = o_data . commitment ;
tx_out_index toi = m_db - > get_output_tx_and_index ( amount , index ) ;
unlocked = is_tx_spendtime_unlocked ( m_db - > get_tx_unlock_time ( toi . first ) ) ;
}
//------------------------------------------------------------------
// This function takes a list of block hashes from another node
// on the network to find where the split point is between us and them.
// This is used to see what to send another node that needs to sync.
@ -2025,28 +2064,39 @@ void Blockchain::print_blockchain_outs(const std::string& file) const
// Find the split point between us and foreign blockchain and return
// (by reference) the most recent common block hash along with up to
// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
bool Blockchain : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , NOTIFY_RESPONSE_CHAIN_ENTRY: : request & resp ) const
bool Blockchain : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , std: : list < crypto : : hash > & hashes , uint64_t & start_height , uint64_t & current_height ) const
{
LOG_PRINT_L3 ( " Blockchain:: " < < __func__ ) ;
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
// if we can't find the split point, return false
if ( ! find_blockchain_supplement ( qblock_ids , resp. start_height) )
if ( ! find_blockchain_supplement ( qblock_ids , start_height) )
{
return false ;
}
m_db - > block_txn_start ( true ) ;
resp. total _height = get_current_blockchain_height ( ) ;
current _height = get_current_blockchain_height ( ) ;
size_t count = 0 ;
for ( size_t i = resp. start_height; i < resp. total _height & & count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT ; i + + , count + + )
for ( size_t i = start_height; i < current _height & & count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT ; i + + , count + + )
{
resp. m_block_id s. push_back ( m_db - > get_block_hash_from_height ( i ) ) ;
hashe s. push_back ( m_db - > get_block_hash_from_height ( i ) ) ;
}
resp . cumulative_difficulty = m_db - > get_block_cumulative_difficulty ( m_db - > height ( ) - 1 ) ;
m_db - > block_txn_stop ( ) ;
return true ;
}
bool Blockchain : : find_blockchain_supplement ( const std : : list < crypto : : hash > & qblock_ids , NOTIFY_RESPONSE_CHAIN_ENTRY : : request & resp ) const
{
LOG_PRINT_L3 ( " Blockchain:: " < < __func__ ) ;
CRITICAL_REGION_LOCAL ( m_blockchain_lock ) ;
bool result = find_blockchain_supplement ( qblock_ids , resp . m_block_ids , resp . start_height , resp . total_height ) ;
resp . cumulative_difficulty = m_db - > get_block_cumulative_difficulty ( m_db - > height ( ) - 1 ) ;
return result ;
}
//------------------------------------------------------------------
//FIXME: change argument to std::vector, low priority
// find split point between ours and foreign blockchain (or start at