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