@ -2267,8 +2267,9 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
MERROR ( " Unexpected output data size: expected " < < req . outputs . size ( ) < < " , got " < < data . size ( ) ) ;
return false ;
}
const uint8_t hf_version = m_hardfork - > get_current_version ( ) ;
for ( const auto & t : data )
res . outs . push_back ( { t . pubkey , t . commitment , is_tx_spendtime_unlocked ( t . unlock_time ), t . height , crypto : : null_hash } ) ;
res . outs . push_back ( { t . pubkey , t . commitment , is_tx_spendtime_unlocked ( t . unlock_time , hf_version ), t . height , crypto : : null_hash } ) ;
if ( req . get_txid )
{
@ -2292,7 +2293,8 @@ void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint
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 ) ) ;
const uint8_t hf_version = m_hardfork - > get_current_version ( ) ;
unlocked = is_tx_spendtime_unlocked ( m_db - > get_tx_unlock_time ( toi . first ) , hf_version ) ;
}
//------------------------------------------------------------------
bool Blockchain : : get_output_distribution ( uint64_t amount , uint64_t from_height , uint64_t to_height , uint64_t & start_height , std : : vector < uint64_t > & distribution , uint64_t & base ) const
@ -3363,7 +3365,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
// make sure that output being spent matches up correctly with the
// signature spending it.
if ( ! check_tx_input ( tx . version , in_to_key , tx_prefix_hash , tx . version = = 1 ? tx . signatures [ sig_index ] : std : : vector < crypto : : signature > ( ) , tx . rct_signatures , pubkeys [ sig_index ] , pmax_used_block_height ))
if ( ! check_tx_input ( tx . version , in_to_key , tx_prefix_hash , tx . version = = 1 ? tx . signatures [ sig_index ] : std : : vector < crypto : : signature > ( ) , tx . rct_signatures , pubkeys [ sig_index ] , pmax_used_block_height , hf_version ))
{
MERROR_VER ( " Failed to check ring signature for tx " < < get_transaction_hash ( tx ) < < " vin key with k_image: " < < in_to_key . k_image < < " sig_index: " < < sig_index ) ;
if ( pmax_used_block_height ) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
@ -3756,7 +3758,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
//------------------------------------------------------------------
// This function checks to see if a tx is unlocked. unlock_time is either
// a block index or a unix time.
bool Blockchain : : is_tx_spendtime_unlocked ( uint64_t unlock_time ) const
bool Blockchain : : is_tx_spendtime_unlocked ( uint64_t unlock_time , uint8_t hf_version ) const
{
LOG_PRINT_L3 ( " Blockchain:: " < < __func__ ) ;
if ( unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER )
@ -3771,7 +3773,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
else
{
//interpret as time
uint64_t current_time = static_cast < uint64_t > ( time ( NULL ) ) ;
const uint64_t current_time = hf_version > = HF_VERSION_DETERMINISTIC_UNLOCK_TIME ? get_adjusted_time ( m_db - > height ( ) ) : static_cast < uint64_t > ( time ( NULL ) ) ;
if ( current_time + ( get_current_hard_fork_version ( ) < 2 ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 ) > = unlock_time )
return true ;
else
@ -3783,7 +3785,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
// This function locates all outputs associated with a given input (mixins)
// and validates that they exist and are usable. It also checks the ring
// signature for each input.
bool Blockchain : : check_tx_input ( size_t tx_version , const txin_to_key & txin , const crypto : : hash & tx_prefix_hash , const std : : vector < crypto : : signature > & sig , const rct : : rctSig & rct_signatures , std : : vector < rct : : ctkey > & output_keys , uint64_t * pmax_related_block_height ) const
bool Blockchain : : check_tx_input ( size_t tx_version , const txin_to_key & txin , const crypto : : hash & tx_prefix_hash , const std : : vector < crypto : : signature > & sig , const rct : : rctSig & rct_signatures , std : : vector < rct : : ctkey > & output_keys , uint64_t * pmax_related_block_height , uint8_t hf_version ) const
{
LOG_PRINT_L3 ( " Blockchain:: " < < __func__ ) ;
@ -3795,14 +3797,15 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
{
std : : vector < rct : : ctkey > & m_output_keys ;
const Blockchain & m_bch ;
outputs_visitor ( std : : vector < rct : : ctkey > & output_keys , const Blockchain & bch ) :
m_output_keys ( output_keys ) , m_bch ( bch )
const uint8_t hf_version ;
outputs_visitor ( std : : vector < rct : : ctkey > & output_keys , const Blockchain & bch , uint8_t hf_version ) :
m_output_keys ( output_keys ) , m_bch ( bch ) , hf_version ( hf_version )
{
}
bool handle_output ( uint64_t unlock_time , const crypto : : public_key & pubkey , const rct : : key & commitment )
{
//check tx unlock time
if ( ! m_bch . is_tx_spendtime_unlocked ( unlock_time ))
if ( ! m_bch . is_tx_spendtime_unlocked ( unlock_time , hf_version ))
{
MERROR_VER ( " One of outputs for one of inputs has wrong tx.unlock_time = " < < unlock_time ) ;
return false ;
@ -3821,7 +3824,7 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
output_keys . clear ( ) ;
// collect output keys
outputs_visitor vi ( output_keys , * this );
outputs_visitor vi ( output_keys , * this , hf_version );
if ( ! scan_outputkeys_for_indexes ( tx_version , txin , vi , tx_prefix_hash , pmax_related_block_height ) )
{
MERROR_VER ( " Failed to get output keys for tx with amount = " < < print_money ( txin . amount ) < < " and count indexes " < < txin . key_offsets . size ( ) ) ;
@ -3840,12 +3843,38 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
return true ;
}
//------------------------------------------------------------------
// TODO: Is this intended to do something else? Need to look into the todo there.
uint64_t Blockchain : : get_adjusted_time ( ) const
// only works on the main chain
uint64_t Blockchain : : get_adjusted_time ( uint64_t height ) const
{
LOG_PRINT_L3 ( " Blockchain:: " < < __func__ ) ;
//TODO: add collecting median time
return time ( NULL ) ;
// if not enough blocks, no proper median yet, return current time
if ( height < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW )
{
return static_cast < uint64_t > ( time ( NULL ) ) ;
}
std : : vector < uint64_t > timestamps ;
// need most recent 60 blocks, get index of first of those
size_t offset = height - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ;
timestamps . reserve ( height - offset ) ;
for ( ; offset < height ; + + offset )
{
timestamps . push_back ( m_db - > get_block_timestamp ( offset ) ) ;
}
uint64_t median_ts = epee : : misc_utils : : median ( timestamps ) ;
// project the median to match approximately when the block being validated will appear
// the median is calculated from a chunk of past blocks, so we use +1 to offset onto the current block
median_ts + = ( BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW + 1 ) * DIFFICULTY_TARGET_V2 / 2 ;
// project the current block's time based on the previous block's time
// we don't use the current block's time directly to mitigate timestamp manipulation
uint64_t adjusted_current_block_ts = timestamps . back ( ) + DIFFICULTY_TARGET_V2 ;
// return minimum of ~current block time and adjusted median time
// we do this since it's better to report a time in the past than a time in the future
return ( adjusted_current_block_ts < median_ts ? adjusted_current_block_ts : median_ts ) ;
}
//------------------------------------------------------------------
//TODO: revisit, has changed a bit on upstream
@ -3873,9 +3902,9 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
bool Blockchain : : check_block_timestamp ( const block & b , uint64_t & median_ts ) const
{
LOG_PRINT_L3 ( " Blockchain:: " < < __func__ ) ;
if ( b . timestamp > get_adjusted_time ( ) + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT )
if ( b . timestamp > ( uint64_t ) time ( NULL ) + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT )
{
MERROR_VER ( " Timestamp of block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < < " , bigger than adjusted time + 2 hours" ) ;
MERROR_VER ( " Timestamp of block with id: " < < get_block_hash ( b ) < < " , " < < b . timestamp < < " , bigger than local time + 2 hours" ) ;
return false ;
}