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