Line data Source code
1 : #include "fd_solfuzz_private.h" 2 : #include "fd_txn_harness.h" 3 : #include "generated/cost.pb.h" 4 : 5 : #include "../fd_cost_tracker.h" 6 : #include "../program/fd_compute_budget_program.h" 7 : 8 : ulong 9 : fd_solfuzz_pb_cost_run( fd_solfuzz_runner_t * runner, 10 : void const * input_, 11 : void ** output_, 12 : void * output_buf, 13 0 : ulong output_bufsz ) { 14 0 : fd_exec_test_cost_context_t const * input = fd_type_pun_const( input_ ); 15 0 : fd_exec_test_cost_result_t ** output = fd_type_pun( output_ ); 16 : 17 0 : if( FD_UNLIKELY( !input->has_tx || !input->has_features ) ) return 0UL; 18 : 19 0 : fd_banks_clear_bank( runner->banks, runner->bank, 64UL ); 20 0 : runner->bank->f.slot = 1UL; 21 0 : FD_TEST( fd_solfuzz_pb_restore_features( &runner->bank->f.features, &input->features ) ); 22 : 23 0 : fd_txn_p_t * txn_p = fd_spad_alloc( runner->spad, alignof(fd_txn_p_t), sizeof(fd_txn_p_t) ); 24 0 : ulong txn_sz = fd_solfuzz_pb_txn_serialize( txn_p->payload, &input->tx ); 25 0 : if( FD_UNLIKELY( txn_sz==ULONG_MAX ) ) return 0UL; 26 : 27 0 : txn_p->payload_sz = txn_sz; 28 0 : if( FD_UNLIKELY( !fd_txn_parse( txn_p->payload, txn_p->payload_sz, TXN( txn_p ), NULL ) ) ) { 29 0 : return 0UL; 30 0 : } 31 : 32 0 : fd_txn_in_t txn_in = {0}; 33 0 : txn_in.txn = txn_p; 34 : 35 0 : fd_txn_out_t * txn_out = fd_spad_alloc( runner->spad, alignof(fd_txn_out_t), sizeof(fd_txn_out_t) ); 36 0 : fd_memset( txn_out, 0, sizeof(fd_txn_out_t) ); 37 0 : fd_compute_budget_details_new( &txn_out->details.compute_budget ); 38 0 : txn_out->details.loaded_accounts_data_size = txn_out->details.compute_budget.loaded_accounts_data_size_limit; 39 0 : txn_out->details.is_simple_vote = fd_txn_is_simple_vote_transaction( TXN( txn_p ), txn_p->payload ); 40 : 41 0 : int err = fd_executor_compute_budget_program_execute_instructions( runner->bank, &txn_in, txn_out ); 42 0 : if( FD_LIKELY( !err ) ) err = fd_sanitize_compute_unit_limits( txn_out ); 43 0 : if( FD_UNLIKELY( err ) ) return 0UL; 44 : 45 0 : if( input->mode==FD_EXEC_TEST_TXN_COST_MODE_ACTUAL ) { 46 0 : ulong actual_cost = (ulong)input->actual_programs_execution_cost; 47 0 : ulong limit = txn_out->details.compute_budget.compute_unit_limit; 48 0 : txn_out->details.compute_budget.compute_meter = fd_ulong_sat_sub( limit, fd_ulong_min( actual_cost, limit ) ); 49 0 : txn_out->details.loaded_accounts_data_size = (ulong)input->actual_loaded_accounts_data_size_bytes; 50 0 : } else { 51 : /* In ESTIMATE mode, programs_execution_cost = compute_unit_limit (see 52 : agave cost_model.rs:get_estimated_execution_cost). fd_cost_tracker 53 : computes programs_execution_cost as (limit - compute_meter), so zero 54 : the meter here to yield the full limit. */ 55 0 : txn_out->details.compute_budget.compute_meter = 0UL; 56 0 : txn_out->details.loaded_accounts_data_size = txn_out->details.compute_budget.loaded_accounts_data_size_limit; 57 0 : } 58 : 59 0 : fd_cost_tracker_calculate_cost( runner->bank, &txn_in, txn_out ); 60 : 61 : /* In ACTUAL mode agave passes actual_programs_execution_cost through 62 : without clamping to compute_unit_limit (see agave cost_model.rs: 63 : calculate_cost_for_executed_transaction). The (limit - meter) 64 : formula in fd_cost_tracker inherently clamps, so overwrite with the 65 : raw actual value here for parity. */ 66 0 : if( input->mode==FD_EXEC_TEST_TXN_COST_MODE_ACTUAL && 67 0 : txn_out->details.txn_cost.type==FD_TXN_COST_TYPE_TRANSACTION ) { 68 0 : txn_out->details.txn_cost.transaction.programs_execution_cost = input->actual_programs_execution_cost; 69 0 : } 70 : 71 : /* Build output into the caller-provided scratch buffer */ 72 0 : ulong output_end = (ulong)output_buf + output_bufsz; 73 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf ); 74 0 : fd_exec_test_cost_result_t * result = 75 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_cost_result_t), sizeof(fd_exec_test_cost_result_t) ); 76 0 : FD_TEST( _l <= output_end ); 77 0 : *result = (fd_exec_test_cost_result_t)FD_EXEC_TEST_COST_RESULT_INIT_ZERO; 78 : 79 0 : result->has_cost = 1; 80 0 : if( txn_out->details.txn_cost.type==FD_TXN_COST_TYPE_TRANSACTION ) { 81 0 : result->signature_cost = txn_out->details.txn_cost.transaction.signature_cost; 82 0 : result->write_lock_cost = txn_out->details.txn_cost.transaction.write_lock_cost; 83 0 : result->data_bytes_cost = txn_out->details.txn_cost.transaction.data_bytes_cost; 84 0 : result->programs_execution_cost = txn_out->details.txn_cost.transaction.programs_execution_cost; 85 0 : result->loaded_accounts_data_size_cost = txn_out->details.txn_cost.transaction.loaded_accounts_data_size_cost; 86 0 : result->allocated_accounts_data_size = txn_out->details.txn_cost.transaction.allocated_accounts_data_size; 87 0 : } else { 88 : /* Simple-vote branch: proto CostResult fields have to match agave's 89 : TransactionCost::SimpleVote component-wise (cost-model/src/ 90 : transaction_cost.rs:48-51 and :86-93). The 2100 here is 91 : solana_vote_program::vote_processor::DEFAULT_COMPUTE_UNITS -- 92 : the vote program's per-invocation cost. Components sum to 93 : FD_SIMPLE_VOTE_USAGE_COST (3428). */ 94 0 : result->signature_cost = FD_PACK_COST_PER_SIGNATURE; 95 0 : result->write_lock_cost = FD_WRITE_LOCK_UNITS * 2UL; 96 0 : result->data_bytes_cost = 0UL; 97 0 : result->programs_execution_cost = 2100UL; 98 0 : result->loaded_accounts_data_size_cost = 8UL; 99 0 : result->allocated_accounts_data_size = 0UL; 100 0 : } 101 : /* Saturating add to match agave's UsageCostDetails::sum (transaction_cost.rs: 102 : saturating_add chain). Plain + wraps on u64::MAX inputs (e.g. adversarial 103 : actual_programs_execution_cost = u64::MAX). */ 104 0 : result->total_cost = fd_ulong_sat_add( result->signature_cost, result->write_lock_cost ); 105 0 : result->total_cost = fd_ulong_sat_add( result->total_cost, result->data_bytes_cost ); 106 0 : result->total_cost = fd_ulong_sat_add( result->total_cost, result->programs_execution_cost ); 107 0 : result->total_cost = fd_ulong_sat_add( result->total_cost, result->loaded_accounts_data_size_cost ); 108 : 109 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL ); 110 0 : *output = result; 111 0 : return actual_end - (ulong)output_buf; 112 0 : }