From fa23bff9af64a44df4319e6d85c0d393c90a3a25 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Fri, 2 May 2025 21:53:11 +0000 Subject: [PATCH 1/3] funk, flamenco: reverify program cache after epoch boundary --- src/discof/replay/fd_replay_tile.c | 6 + src/flamenco/runtime/fd_acc_mgr.c | 2 +- src/flamenco/runtime/fd_executor.c | 13 +- src/flamenco/runtime/fd_executor.h | 3 + src/flamenco/runtime/fd_runtime.c | 46 +++ src/flamenco/runtime/fd_runtime.h | 12 + .../runtime/program/fd_bpf_program_util.c | 352 +++++++++++------- .../runtime/program/fd_bpf_program_util.h | 8 + .../runtime/tests/harness/fd_instr_harness.c | 12 +- .../runtime/tests/harness/fd_txn_harness.c | 6 +- src/funk/fd_funk_rec.c | 81 +++- src/funk/fd_funk_rec.h | 46 +++ src/funk/fd_funk_txn.c | 4 +- src/funk/fd_funk_txn.h | 4 +- src/funk/fd_funk_val.h | 4 +- 15 files changed, 451 insertions(+), 148 deletions(-) diff --git a/src/discof/replay/fd_replay_tile.c b/src/discof/replay/fd_replay_tile.c index 239d24df58..0eb94e9cb3 100644 --- a/src/discof/replay/fd_replay_tile.c +++ b/src/discof/replay/fd_replay_tile.c @@ -1691,6 +1691,12 @@ exec_slice( fd_replay_tile_ctx_t * ctx, if( FD_UNLIKELY( !pay_sz || !txn_sz || txn_sz > FD_TXN_MTU ) ) { FD_LOG_ERR(( "failed to parse transaction in replay" )); } + + /* Reverify invoked programs for this epoch, if needed + FIXME: this should be done during txn parsing so that we don't have to loop + over all accounts a second time. */ + fd_runtime_reverify_cached_programs( ctx->slot_ctx, &txn_p, ctx->runtime_spad ); + fd_memcpy( txn_p.payload, ctx->mbatch + ctx->slice_exec_ctx.wmark, pay_sz ); txn_p.payload_sz = pay_sz; ctx->slice_exec_ctx.wmark += pay_sz; diff --git a/src/flamenco/runtime/fd_acc_mgr.c b/src/flamenco/runtime/fd_acc_mgr.c index 267238dc95..5bb5470bc4 100644 --- a/src/flamenco/runtime/fd_acc_mgr.c +++ b/src/flamenco/runtime/fd_acc_mgr.c @@ -67,7 +67,7 @@ fd_funk_get_acc_meta_mutable( fd_funk_t * funk, /* the record does not exist in the current funk transaction */ if( !rec ) { /* clones a record from an ancestor transaction */ - rec = fd_funk_rec_clone( funk, txn, &id, out_prepare, &funk_err ); + rec = fd_funk_rec_clone( funk, txn, &id, out_prepare, NULL, &funk_err ); if( rec == NULL ) { /* the record does not exist at all */ diff --git a/src/flamenco/runtime/fd_executor.c b/src/flamenco/runtime/fd_executor.c index c41b02328d..12bdb03429 100644 --- a/src/flamenco/runtime/fd_executor.c +++ b/src/flamenco/runtime/fd_executor.c @@ -110,6 +110,14 @@ fd_executor_lookup_native_precompile_program( fd_txn_account_t const * prog_acc return fd_native_precompile_program_fn_lookup_tbl_query( pubkey, &null_function )->fn; } +uchar +fd_executor_pubkey_is_bpf_loader( fd_pubkey_t const * pubkey ) { + return !memcmp( pubkey, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) || + !memcmp( pubkey, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) || + !memcmp( pubkey, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) || + !memcmp( pubkey, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ); +} + /* fd_executor_lookup_native_program returns the appropriate instruction processor for the given native program ID. Returns NULL if given ID is not a recognized native program. https://github.com/anza-xyz/agave/blob/v2.2.6/program-runtime/src/invoke_context.rs#L520-L544 */ @@ -134,10 +142,7 @@ fd_executor_lookup_native_program( fd_txn_account_t const * prog_acc, int is_native_program = !memcmp( owner, fd_solana_native_loader_id.key, sizeof(fd_pubkey_t) ); if( !is_native_program && FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, remove_accounts_executable_flag_checks ) ) { - if ( FD_UNLIKELY( memcmp( owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) ) { + if( FD_UNLIKELY( !fd_executor_pubkey_is_bpf_loader( owner ) ) ) { return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID; } } diff --git a/src/flamenco/runtime/fd_executor.h b/src/flamenco/runtime/fd_executor.h index 43fc9deafb..a549210a99 100644 --- a/src/flamenco/runtime/fd_executor.h +++ b/src/flamenco/runtime/fd_executor.h @@ -44,6 +44,9 @@ typedef int (* fd_exec_instr_fn_t)( fd_exec_instr_ctx_t * ctx ); fd_exec_instr_fn_t fd_executor_lookup_native_precompile_program( fd_txn_account_t const * prog_acc ); +uchar +fd_executor_pubkey_is_bpf_loader( fd_pubkey_t const * pubkey ); + int fd_executor_check_transactions( fd_exec_txn_ctx_t * txn_ctx ); diff --git a/src/flamenco/runtime/fd_runtime.c b/src/flamenco/runtime/fd_runtime.c index e9c66918f6..501139e2e9 100644 --- a/src/flamenco/runtime/fd_runtime.c +++ b/src/flamenco/runtime/fd_runtime.c @@ -2928,6 +2928,47 @@ fd_runtime_parse_microblock_hdr( void const * buf FD_PARAM_UNUSED, return 0; } +void +fd_runtime_reverify_cached_programs( fd_exec_slot_ctx_t * slot_ctx, + fd_txn_p_t const * txn_p, + fd_spad_t * runtime_spad ) { + fd_txn_t const * txn_descriptor = TXN( txn_p ); + + /* Iterate over account keys referenced directly in the transaction first */ + fd_acct_addr_t const * acc_addrs = fd_txn_get_acct_addrs( txn_descriptor, txn_p ); + for( ushort acc_idx=0; acc_idxacct_addr_cnt; acc_idx++ ) { + fd_pubkey_t const * account = fd_type_pun_const( &acc_addrs[acc_idx] ); + fd_bpf_program_reverify( slot_ctx, account, runtime_spad ); + } + + if( txn_descriptor->transaction_version==FD_TXN_V0 ) { + + /* Iterate over account keys referenced in ALUTs */ + fd_acct_addr_t alut_accounts[256]; + fd_slot_hashes_global_t const * slot_hashes_global = fd_sysvar_cache_slot_hashes( slot_ctx->sysvar_cache, slot_ctx->runtime_wksp ); + if( FD_UNLIKELY( !slot_hashes_global ) ) { + return; + } + + fd_slot_hash_t * slot_hash = deq_fd_slot_hash_t_join( (uchar *)slot_hashes_global + slot_hashes_global->hashes_offset ); + + if( FD_UNLIKELY( fd_runtime_load_txn_address_lookup_tables( txn_descriptor, + txn_p->payload, + slot_ctx->funk, + slot_ctx->funk_txn, + slot_ctx->slot_bank.slot, + slot_hash, + alut_accounts ) ) ) { + return; + } + + for( ushort alut_idx=0; alut_idxaddr_table_adtl_cnt; alut_idx++ ) { + fd_pubkey_t const * account = fd_type_pun_const( &alut_accounts[alut_idx] ); + fd_bpf_program_reverify( slot_ctx, account, runtime_spad ); + } + } +} + /* if we are currently in the middle of a batch, batch_cnt will include the current batch. if we are at the start of a batch, batch_cnt will include the current batch. */ static fd_raw_block_txn_iter_t @@ -4089,6 +4130,11 @@ fd_runtime_block_execute_tpool( fd_exec_slot_ctx_t * slot_ctx, if( !mblock_txn_cnt ) continue; + /* Reverify programs for this epoch if needed */ + for( ulong txn_idx=0UL; txn_idxsbpf_version = elf_info->sbpf_version; + /* Last verified epoch */ + validated_prog->last_verified_epoch = 0UL; + return (fd_sbpf_validated_program_t *)mem; } @@ -162,51 +166,171 @@ fd_bpf_get_sbpf_versions( uint * sbpf_min_version, } } +/* Parses the programdata from a program account.*/ static int -fd_bpf_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx, - fd_txn_account_t * program_acc, - fd_spad_t * runtime_spad ) { - FD_SPAD_FRAME_BEGIN( runtime_spad ) { +fd_bpf_get_programdata_from_account( fd_exec_slot_ctx_t * slot_ctx, + fd_txn_account_t * program_acc, + uchar const ** out_program_data, + ulong * out_program_data_len, + fd_spad_t * runtime_spad ) { + /* v1/v2 loaders: Programdata is just the account data. + v3 loader: Programdata lives in a separate account. Deserialize the program account + and lookup the programdata account. Deserialize the programdata account. + v4 loader: Programdata lives in the program account, offset by LOADER_V4_PROGRAM_DATA_OFFSET. */ + int res; + if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) { + res = fd_bpf_get_executable_program_content_for_upgradeable_loader( slot_ctx, program_acc, out_program_data, out_program_data_len, runtime_spad ); + } else if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) { + res = fd_bpf_get_executable_program_content_for_v4_loader( program_acc, out_program_data, out_program_data_len ); + } else if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) || + !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) ) { + res = fd_bpf_get_executable_program_content_for_v1_v2_loaders( program_acc, out_program_data, out_program_data_len ); + } else { + return -1; + } + return res; +} - fd_pubkey_t * program_pubkey = program_acc->pubkey; +/* Parse ELF info from programdata. */ +static int +fd_bpf_parse_elf_info( fd_exec_slot_ctx_t * slot_ctx, + fd_sbpf_elf_info_t * elf_info, + uchar const * program_data, + ulong program_data_len ) { + uint min_sbpf_version, max_sbpf_version; + fd_bpf_get_sbpf_versions( &min_sbpf_version, + &max_sbpf_version, + slot_ctx->slot_bank.slot, + &slot_ctx->epoch_ctx->features ); + if( FD_UNLIKELY( !fd_sbpf_elf_peek( elf_info, program_data, program_data_len, /* deploy checks */ 0, min_sbpf_version, max_sbpf_version ) ) ) { + FD_LOG_DEBUG(( "fd_sbpf_elf_peek() failed: %s", fd_sbpf_strerror() )); + return -1; + } + return 0; +} - fd_funk_t * funk = slot_ctx->funk; - fd_funk_txn_t * funk_txn = slot_ctx->funk_txn; - fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey ); +static int +fd_bpf_validate_sbpf_program( fd_exec_slot_ctx_t * slot_ctx, + fd_sbpf_elf_info_t * elf_info, + void * validated_prog_mem, + uchar const * program_data, + ulong program_data_len, + fd_spad_t * runtime_spad ) { + fd_sbpf_validated_program_t * validated_prog = fd_sbpf_validated_program_new( validated_prog_mem, elf_info ); + + ulong prog_align = fd_sbpf_program_align(); + ulong prog_footprint = fd_sbpf_program_footprint( elf_info ); + fd_sbpf_program_t * prog = fd_sbpf_program_new( fd_spad_alloc( runtime_spad, prog_align, prog_footprint ), elf_info, validated_prog->rodata ); + if( FD_UNLIKELY( !prog ) ) { + return -1; + } + + /* Allocate syscalls */ - uchar const * program_data = NULL; - ulong program_data_len = 0UL; + fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_spad_alloc( runtime_spad, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) ); + if( FD_UNLIKELY( !syscalls ) ) { + FD_LOG_CRIT(( "Call to fd_sbpf_syscalls_new() failed" )); + } - /* For v3 loaders, deserialize the program account and lookup the - programdata account. Deserialize the programdata account. */ + fd_vm_syscall_register_slot( syscalls, + slot_ctx->slot_bank.slot, + &slot_ctx->epoch_ctx->features, + 0 ); - int res; - if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) { - res = fd_bpf_get_executable_program_content_for_upgradeable_loader( slot_ctx, program_acc, &program_data, &program_data_len, runtime_spad ); - } else if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) { - res = fd_bpf_get_executable_program_content_for_v4_loader( program_acc, &program_data, &program_data_len ); - } else { - res = fd_bpf_get_executable_program_content_for_v1_v2_loaders( program_acc, &program_data, &program_data_len ); - } + /* Load program. */ + + if( FD_UNLIKELY( 0!=fd_sbpf_program_load( prog, program_data, program_data_len, syscalls, false ) ) ) { + /* Remove pending funk record */ + FD_LOG_DEBUG(( "fd_sbpf_program_load() failed: %s", fd_sbpf_strerror() )); + return -1; + } + + /* Validate the program. */ + + fd_vm_t _vm[ 1UL ]; + fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) ); + if( FD_UNLIKELY( !vm ) ) { + FD_LOG_CRIT(( "fd_vm_new() or fd_vm_join() failed" )); + } - if( res ) { + fd_exec_instr_ctx_t dummy_instr_ctx = {0}; + fd_exec_txn_ctx_t dummy_txn_ctx = {0}; + dummy_txn_ctx.slot = slot_ctx->slot_bank.slot; + dummy_txn_ctx.features = slot_ctx->epoch_ctx->features; + dummy_instr_ctx.txn_ctx = &dummy_txn_ctx; + vm = fd_vm_init( vm, + &dummy_instr_ctx, + 0UL, + 0UL, + prog->rodata, + prog->rodata_sz, + prog->text, + prog->text_cnt, + prog->text_off, + prog->text_sz, + prog->entry_pc, + prog->calldests, + elf_info->sbpf_version, + NULL, + NULL, + NULL, + NULL, + 0U, + NULL, + 0, + FD_FEATURE_ACTIVE( slot_ctx->slot_bank.slot, slot_ctx->epoch_ctx->features, bpf_account_data_direct_mapping ) ); + + if( FD_UNLIKELY( !vm ) ) { + FD_LOG_CRIT(( "fd_vm_init() failed" )); + } + + int res = fd_vm_validate( vm ); + if( FD_UNLIKELY( res ) ) { + FD_LOG_DEBUG(( "fd_vm_validate() failed" )); + return -1; + } + + fd_memcpy( validated_prog->calldests_shmem, prog->calldests_shmem, fd_sbpf_calldests_footprint( prog->rodata_sz/8UL ) ); + + validated_prog->calldests = fd_sbpf_calldests_join( validated_prog->calldests_shmem ); + validated_prog->entry_pc = prog->entry_pc; + validated_prog->last_updated_slot = slot_ctx->slot_bank.slot; + validated_prog->text_off = prog->text_off; + validated_prog->text_cnt = prog->text_cnt; + validated_prog->text_sz = prog->text_sz; + validated_prog->rodata_sz = prog->rodata_sz; + validated_prog->last_verified_epoch = fd_slot_to_epoch( &slot_ctx->epoch_ctx->epoch_bank.epoch_schedule, + slot_ctx->slot_bank.slot, + NULL ); + return 0; +} + +static int +fd_bpf_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx, + fd_txn_account_t * program_acc, + fd_spad_t * runtime_spad ) { + FD_SPAD_FRAME_BEGIN( runtime_spad ) { + + uchar const * program_data = NULL; + ulong program_data_len = 0UL; + + if( FD_UNLIKELY( fd_bpf_get_programdata_from_account( slot_ctx, program_acc, &program_data, &program_data_len, runtime_spad ) ) ) { return -1; } fd_sbpf_elf_info_t elf_info = {0}; - uint min_sbpf_version, max_sbpf_version; - fd_bpf_get_sbpf_versions( &min_sbpf_version, - &max_sbpf_version, - slot_ctx->slot_bank.slot, - &slot_ctx->epoch_ctx->features ); - if( fd_sbpf_elf_peek( &elf_info, program_data, program_data_len, /* deploy checks */ 0, min_sbpf_version, max_sbpf_version ) == NULL ) { - FD_LOG_DEBUG(( "fd_sbpf_elf_peek() failed: %s", fd_sbpf_strerror() )); - return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA; + if( FD_UNLIKELY( fd_bpf_parse_elf_info( slot_ctx, &elf_info, program_data, program_data_len ) ) ) { + return -1; } + fd_pubkey_t * program_pubkey = program_acc->pubkey; + fd_funk_t * funk = slot_ctx->funk; + fd_funk_txn_t * funk_txn = slot_ctx->funk_txn; + fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey ); + int funk_err = FD_FUNK_SUCCESS; fd_funk_rec_prepare_t prepare[1]; - fd_funk_rec_t * rec = fd_funk_rec_prepare( funk, funk_txn, &id, prepare, NULL ); + fd_funk_rec_t * rec = fd_funk_rec_prepare( funk, funk_txn, &id, prepare, &funk_err ); if( rec == NULL || funk_err != FD_FUNK_SUCCESS ) { return -1; } @@ -222,93 +346,12 @@ fd_bpf_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx, FD_LOG_ERR(( "fd_funk_val_truncate(sz=%lu) for account failed (%i-%s)", val_sz, funk_err, fd_funk_strerror( funk_err ) )); } - fd_sbpf_validated_program_t * validated_prog = fd_sbpf_validated_program_new( val, &elf_info ); - - ulong prog_align = fd_sbpf_program_align(); - ulong prog_footprint = fd_sbpf_program_footprint( &elf_info ); - fd_sbpf_program_t * prog = fd_sbpf_program_new( fd_spad_alloc( runtime_spad, prog_align, prog_footprint ), &elf_info, validated_prog->rodata ); - if( FD_UNLIKELY( !prog ) ) { - fd_funk_rec_cancel( funk, prepare ); - return -1; - } - - /* Allocate syscalls */ - - fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_spad_alloc( runtime_spad, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) ); - if( FD_UNLIKELY( !syscalls ) ) { - FD_LOG_ERR(( "Call to fd_sbpf_syscalls_new() failed" )); - } - - fd_vm_syscall_register_slot( syscalls, - slot_ctx->slot_bank.slot, - &slot_ctx->epoch_ctx->features, - 0 ); - - /* Load program. */ - - if( FD_UNLIKELY( 0!=fd_sbpf_program_load( prog, program_data, program_data_len, syscalls, false ) ) ) { - /* Remove pending funk record */ - FD_LOG_DEBUG(( "fd_sbpf_program_load() failed: %s", fd_sbpf_strerror() )); - fd_funk_rec_cancel( funk, prepare ); - return -1; - } - - /* Validate the program. */ - - fd_vm_t _vm[ 1UL ]; - fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) ); - if( FD_UNLIKELY( !vm ) ) { - FD_LOG_ERR(( "fd_vm_new() or fd_vm_join() failed" )); - } - fd_exec_instr_ctx_t dummy_instr_ctx = {0}; - fd_exec_txn_ctx_t dummy_txn_ctx = {0}; - dummy_txn_ctx.slot = slot_ctx->slot_bank.slot; - dummy_txn_ctx.features = slot_ctx->epoch_ctx->features; - dummy_instr_ctx.txn_ctx = &dummy_txn_ctx; - vm = fd_vm_init( vm, - &dummy_instr_ctx, - 0UL, - 0UL, - prog->rodata, - prog->rodata_sz, - prog->text, - prog->text_cnt, - prog->text_off, - prog->text_sz, - prog->entry_pc, - prog->calldests, - elf_info.sbpf_version, - NULL, - NULL, - NULL, - NULL, - 0U, - NULL, - 0, - FD_FEATURE_ACTIVE( slot_ctx->slot_bank.slot, slot_ctx->epoch_ctx->features, bpf_account_data_direct_mapping ) ); - - if( FD_UNLIKELY( !vm ) ) { - FD_LOG_ERR(( "fd_vm_init() failed" )); - } - - res = fd_vm_validate( vm ); + int res = fd_bpf_validate_sbpf_program( slot_ctx, &elf_info, val, program_data, program_data_len, runtime_spad ); if( FD_UNLIKELY( res ) ) { - /* Remove pending funk record */ - FD_LOG_DEBUG(( "fd_vm_validate() failed" )); fd_funk_rec_cancel( funk, prepare ); return -1; } - fd_memcpy( validated_prog->calldests_shmem, prog->calldests_shmem, fd_sbpf_calldests_footprint( prog->rodata_sz/8UL ) ); - validated_prog->calldests = fd_sbpf_calldests_join( validated_prog->calldests_shmem ); - - validated_prog->entry_pc = prog->entry_pc; - validated_prog->last_updated_slot = slot_ctx->slot_bank.slot; - validated_prog->text_off = prog->text_off; - validated_prog->text_cnt = prog->text_cnt; - validated_prog->text_sz = prog->text_sz; - validated_prog->rodata_sz = prog->rodata_sz; - fd_funk_rec_publish( funk, prepare ); return 0; @@ -324,13 +367,6 @@ fd_bpf_check_and_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx, return -1; } - if( memcmp( exec_rec->vt->get_owner( exec_rec ), fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( exec_rec->vt->get_owner( exec_rec ), fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( exec_rec->vt->get_owner( exec_rec ), fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( exec_rec->vt->get_owner( exec_rec ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) { - return -1; - } - if( fd_bpf_create_bpf_program_cache_entry( slot_ctx, exec_rec, runtime_spad ) != 0 ) { return -1; } @@ -351,12 +387,9 @@ fd_bpf_is_bpf_program( fd_funk_rec_t const * rec, void const * raw = fd_funk_val( rec, funk_wksp ); fd_account_meta_t const * metadata = fd_type_pun_const( raw ); + fd_pubkey_t const * owner = fd_type_pun_const( metadata->info.owner ); - if( metadata && - memcmp( metadata->info.owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( metadata->info.owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( metadata->info.owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) && - memcmp( metadata->info.owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) { + if( metadata && !fd_executor_pubkey_is_bpf_loader( owner ) ) { *is_bpf_program = 0; } else { *is_bpf_program = 1; @@ -571,7 +604,7 @@ fd_bpf_load_cache_entry( fd_funk_t * funk, } } - void const * data = fd_funk_val_const( rec, fd_funk_wksp(funk) ); + void const * data = fd_funk_val( rec, fd_funk_wksp(funk) ); /* TODO: magic check */ @@ -589,3 +622,72 @@ fd_bpf_load_cache_entry( fd_funk_t * funk, /* Try again */ } } + +void +fd_bpf_program_reverify( fd_exec_slot_ctx_t * slot_ctx, + fd_pubkey_t const * program_pubkey, + fd_spad_t * runtime_spad ) { +FD_SPAD_FRAME_BEGIN( runtime_spad ) { + + fd_sbpf_validated_program_t * prog = NULL; + fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey ); + + /* First check if the program is in the BPF cache */ + int err = fd_bpf_load_cache_entry( slot_ctx->funk, slot_ctx->funk_txn, program_pubkey, &prog ); + if( err ) { + return; + } + + /* If the program is cached, check if it's already been reverified for the current epoch */ + ulong current_epoch = fd_slot_to_epoch( &slot_ctx->epoch_ctx->epoch_bank.epoch_schedule, slot_ctx->slot_bank.slot, NULL ); + if( FD_LIKELY( prog->last_verified_epoch==current_epoch ) ) { + return; + } + + /* Program needs to be reverified with the new feature set. If the program fails verification, + remove it from the program cache. */ + FD_TXN_ACCOUNT_DECL( exec_rec ); + if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( exec_rec, + program_pubkey, + slot_ctx->funk, + slot_ctx->funk_txn ) ) ) { + return; + } + + /* Get the program data from the account */ + uchar const * program_data = NULL; + ulong program_data_len = 0UL; + if( FD_UNLIKELY( fd_bpf_get_programdata_from_account( slot_ctx, exec_rec, &program_data, &program_data_len, runtime_spad ) ) ) { + return; + } + + /* From here on, we know the program exists in the cache and is due for reverification. If any of the following checks fail, + we should evict the program from the cache. */ + fd_sbpf_elf_info_t elf_info = {0}; + + if( FD_UNLIKELY( fd_bpf_parse_elf_info( slot_ctx, &elf_info, program_data, program_data_len ) ) ) { + fd_funk_rec_remove( slot_ctx->funk, slot_ctx->funk_txn, &id, NULL, 0UL ); + return; + } + + /* Modify the record + lock the funk hash chain */ + fd_funk_rec_query_t query[1]; + fd_funk_rec_t * rec = fd_funk_rec_modify_try_global( slot_ctx->funk, slot_ctx->funk_txn, &id, NULL, query ); + if( FD_UNLIKELY( !rec ) ) { + /* The record does not exist (somehow). Ideally this should never happen since this function is called in a single-threaded context. */ + return; + } + + void * data = fd_funk_val( rec, fd_funk_wksp( slot_ctx->funk ) ); + + if( FD_UNLIKELY( fd_bpf_validate_sbpf_program( slot_ctx, &elf_info, data, program_data, program_data_len, runtime_spad ) ) ) { + fd_funk_rec_modify_test( query ); + fd_funk_rec_remove( slot_ctx->funk, slot_ctx->funk_txn, &id, NULL, 0UL ); + return; + } + + /* Finish modifying and release lock */ + fd_funk_rec_modify_test( query ); + +} FD_SPAD_FRAME_END; +} diff --git a/src/flamenco/runtime/program/fd_bpf_program_util.h b/src/flamenco/runtime/program/fd_bpf_program_util.h index 9cc71d4083..52cb20b66a 100644 --- a/src/flamenco/runtime/program/fd_bpf_program_util.h +++ b/src/flamenco/runtime/program/fd_bpf_program_util.h @@ -31,6 +31,9 @@ struct fd_sbpf_validated_program { /* SBPF version, SIMD-0161 */ ulong sbpf_version; + + /* Used to check if the program needs to be reverified */ + ulong last_verified_epoch; }; typedef struct fd_sbpf_validated_program fd_sbpf_validated_program_t; @@ -85,6 +88,11 @@ fd_bpf_get_sbpf_versions( uint * sbpf_min_version, ulong slot, fd_features_t const * features ); +void +fd_bpf_program_reverify( fd_exec_slot_ctx_t * slot_ctx, + fd_pubkey_t const * program_pubkey, + fd_spad_t * runtime_spad ); + FD_PROTOTYPES_END #endif /* HEADER_fd_src_flamenco_runtime_program_fd_bpf_program_util_h */ diff --git a/src/flamenco/runtime/tests/harness/fd_instr_harness.c b/src/flamenco/runtime/tests/harness/fd_instr_harness.c index fc04f382cf..10228c3062 100644 --- a/src/flamenco/runtime/tests/harness/fd_instr_harness.c +++ b/src/flamenco/runtime/tests/harness/fd_instr_harness.c @@ -169,11 +169,7 @@ fd_runtime_fuzz_instr_ctx_create( fd_runtime_fuzz_runner_t * runner, /* Load in executable accounts */ for( ulong i = 0; i < txn_ctx->accounts_cnt; i++ ) { fd_txn_account_t * acc = &accts[i]; - if ( memcmp( acc->vt->get_owner( acc ), fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) != 0 && - memcmp( acc->vt->get_owner( acc ), fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) != 0 && - memcmp( acc->vt->get_owner( acc ), fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) != 0 && - memcmp( acc->vt->get_owner( acc ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) != 0 - ) { + if ( !fd_executor_pubkey_is_bpf_loader( acc->vt->get_owner( acc ) ) ) { continue; } @@ -211,12 +207,12 @@ fd_runtime_fuzz_instr_ctx_create( fd_runtime_fuzz_runner_t * runner, } } - /* Add accounts to bpf program cache */ - fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, runner->spad ); - /* Restore sysvar cache */ fd_sysvar_cache_restore( slot_ctx->sysvar_cache, funk, funk_txn, runner->spad, runtime_wksp ); + /* Add accounts to bpf program cache */ + fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, runner->spad ); + /* Fill missing sysvar cache values with defaults */ /* We create mock accounts for each of the sysvars and hardcode the data fields before loading it into the account manager */ /* We use Agave sysvar defaults for data field values */ diff --git a/src/flamenco/runtime/tests/harness/fd_txn_harness.c b/src/flamenco/runtime/tests/harness/fd_txn_harness.c index d63253ece3..9c21333cc1 100644 --- a/src/flamenco/runtime/tests/harness/fd_txn_harness.c +++ b/src/flamenco/runtime/tests/harness/fd_txn_harness.c @@ -75,9 +75,6 @@ fd_runtime_fuzz_txn_ctx_create( fd_runtime_fuzz_runner_t * runner, /* Restore sysvar cache */ fd_sysvar_cache_restore( slot_ctx->sysvar_cache, funk, funk_txn, runner->spad, fd_wksp_containing( slot_ctx ) ); - /* Add accounts to bpf program cache */ - fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, runner->spad ); - /* Default slot */ ulong slot = test_ctx->slot_ctx.slot ? test_ctx->slot_ctx.slot : 10; // Arbitrary default > 0 @@ -194,6 +191,9 @@ fd_runtime_fuzz_txn_ctx_create( fd_runtime_fuzz_runner_t * runner, return NULL; } + /* Add accounts to bpf program cache */ + fd_bpf_scan_and_create_bpf_program_cache_entry( slot_ctx, runner->spad ); + /* Blockhash queue is given in txn message. We need to populate the following two fields: - slot_ctx->slot_bank.block_hash_queue - slot_ctx->slot_bank.recent_block_hashes */ diff --git a/src/funk/fd_funk_rec.c b/src/funk/fd_funk_rec.c index c404c87032..8f7f3ed1e9 100644 --- a/src/funk/fd_funk_rec.c +++ b/src/funk/fd_funk_rec.c @@ -99,6 +99,8 @@ fd_funk_rec_query_try_global( fd_funk_t const * funk, /* For cur_txn in path from [txn] to [root] where root is NULL */ + fd_funk_txn_start_read( funk ); + for( fd_funk_txn_t const * cur_txn = txn; ; cur_txn = fd_funk_txn_parent( cur_txn, funk->txn_pool ) ) { /* If record ele is part of transaction cur_txn, we have a match. According to the property above, this will be the @@ -112,11 +114,14 @@ fd_funk_rec_query_try_global( fd_funk_t const * funk, if( txn_out ) *txn_out = cur_txn; query->ele = ( FD_UNLIKELY( ele->flags & FD_FUNK_REC_FLAG_ERASE ) ? NULL : (fd_funk_rec_t *)ele ); + fd_funk_txn_end_read( funk ); return query->ele; } if( cur_txn == NULL ) break; } + + fd_funk_txn_end_read( funk ); } } return NULL; @@ -269,13 +274,14 @@ fd_funk_rec_clone( fd_funk_t * funk, fd_funk_txn_t * txn, fd_funk_rec_key_t const * key, fd_funk_rec_prepare_t * prepare, + fd_funk_txn_t const ** txn_out, int * opt_err ) { fd_funk_rec_t * new_rec = fd_funk_rec_prepare( funk, txn, key, prepare, opt_err ); if( !new_rec ) return NULL; for(;;) { fd_funk_rec_query_t query[1]; - fd_funk_rec_t const * old_rec = fd_funk_rec_query_try_global( funk, txn, key, NULL, query ); + fd_funk_rec_t const * old_rec = fd_funk_rec_query_try_global( funk, txn, key, txn_out, query ); if( !old_rec ) { fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_KEY ); fd_funk_rec_cancel( funk, prepare ); @@ -292,6 +298,7 @@ fd_funk_rec_clone( fd_funk_t * funk, memcpy( buf, fd_funk_val( old_rec, wksp ), val_sz ); if( !fd_funk_rec_query_test( query ) ) { + if( txn_out ) *txn_out = txn; return new_rec; } } @@ -418,6 +425,78 @@ fd_funk_rec_remove( fd_funk_t * funk, return FD_FUNK_SUCCESS; } +fd_funk_rec_t * +fd_funk_rec_modify_try( fd_funk_t * funk, + fd_funk_txn_t const * txn, + fd_funk_rec_key_t const * key, + fd_funk_rec_query_t * query ) { + fd_funk_rec_map_t * rec_map = fd_funk_rec_map( funk ); + fd_funk_xid_key_pair_t pair[1]; + if( txn == NULL ) { + fd_funk_txn_xid_set_root( pair->xid ); + } else { + fd_funk_txn_xid_copy( pair->xid, &txn->xid ); + } + fd_funk_rec_key_copy( pair->key, key ); + + int err = fd_funk_rec_map_modify_try( rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING ); + if( FD_UNLIKELY( err ) ) { + if( err == FD_MAP_ERR_KEY ) return NULL; + FD_LOG_ERR(( "modify returned err %d", err )); + } + + return fd_funk_rec_map_query_ele( query ); +} + +fd_funk_rec_t * +fd_funk_rec_modify_try_global( fd_funk_t * funk, + fd_funk_txn_t * txn, + fd_funk_rec_key_t const * key, + fd_funk_txn_t const ** txn_out, + fd_funk_rec_query_t * query ) { +#ifdef FD_FUNK_HANDHOLDING + if( FD_UNLIKELY( funk==NULL || key==NULL || query==NULL ) ) { + return NULL; + } + if( FD_UNLIKELY( txn && !fd_funk_txn_valid( funk, txn ) ) ) { + return NULL; + } +#endif + + /* Try to get a modifiable handle to the record within the current transaction. */ + fd_funk_rec_t * rec = fd_funk_rec_modify_try( funk, txn, key, query ); + if( rec ) { + if( txn_out ) *txn_out = txn; + return rec; + } + + /* Record does not exist in the current transaction. If it exists at all, it must + belong to a parent transaction, if it exists in funk. If so, clone it down + to the child transaction. */ + fd_funk_rec_prepare_t prepare; + rec = fd_funk_rec_clone( funk, txn, key, &prepare, txn_out, NULL ); + if( FD_UNLIKELY( rec==NULL ) ) { + /* Failed to clone the record or find the record in a parent txn. */ + return NULL; + } + + /* Publish record and release the lock */ + fd_funk_rec_publish( funk, &prepare ); + + /* Get a modifiable handle to the cloned record */ + rec = fd_funk_rec_modify_try( funk, txn, key, query ); + if( rec ) { + if( txn_out ) *txn_out = txn; + } + + return rec; +} + +void +fd_funk_rec_modify_test( fd_funk_rec_query_t * query ) { + fd_funk_rec_map_modify_test( query ); +} + void fd_funk_rec_set_erase_data( fd_funk_rec_t * rec, ulong erase_data ) { rec->flags |= ((erase_data & 0xFFFFFFFFFFUL) << (sizeof(unsigned long) * 8 - 40)); diff --git a/src/funk/fd_funk_rec.h b/src/funk/fd_funk_rec.h index d9c192767c..eab1979e7a 100644 --- a/src/funk/fd_funk_rec.h +++ b/src/funk/fd_funk_rec.h @@ -265,6 +265,7 @@ fd_funk_rec_clone( fd_funk_t * funk, fd_funk_txn_t * txn, fd_funk_rec_key_t const * key, fd_funk_rec_prepare_t * prepare, + fd_funk_txn_t const ** txn_out, int * opt_err ); /* fd_funk_rec_remove removes the live record with the @@ -297,6 +298,50 @@ fd_funk_rec_remove( fd_funk_t * funk, fd_funk_rec_t ** rec_out, ulong erase_data ); +/* fd_funk_rec_modify_try queries the in-prep transaction pointed to + by txn for the record whose key matches the key pointed to by the + key. If the query is successful, then we will acquire a lock on the + corresponding hash chain. Any updates made to the record can be + published into the funk transaction with a call to + fd_funk_rec_modify_test. */ + +fd_funk_rec_t * +fd_funk_rec_modify_try( fd_funk_t * funk, + fd_funk_txn_t const * txn, + fd_funk_rec_key_t const * key, + fd_funk_rec_query_t * query ); + +/* fd_funk_rec_modify_try_global is the same as fd_funk_rec_modify_try but will + query txn's ancestors for key from youngest to oldest if key is not + part of txn. As such, the txn of the returned record may not match + txn but will be the txn of most recent ancestor with the key + otherwise. *txn_out is set to the transaction where the record was + found. + + This code is mostly similar to fd_funk_rec_query_try_global but also + acquires a write lock on the hash chain. If the returned record is non-NULL, + it is the responsibility of the caller to release the lock with + fd_funk_rec_modify_test afterwards. + + This is reasonably fast O(in_prep_ancestor_cnt). + + Important safety tip! This function can encounter records + that have the ERASE flag set (i.e. are tombstones of erased + records). fd_funk_rec_modify_try_global will return a NULL in this case + but still set *txn_out to the relevant transaction. This behavior + differs from fd_funk_rec_modify_try. */ + +fd_funk_rec_t * +fd_funk_rec_modify_try_global( fd_funk_t * funk, + fd_funk_txn_t * txn, + fd_funk_rec_key_t const * key, + fd_funk_txn_t const ** txn_out, + fd_funk_rec_query_t * query ); + +/* Releases the lock on the hash chain. */ +void +fd_funk_rec_modify_test( fd_funk_rec_query_t * query ); + /* fd_funk_rec_hard_remove completely removes the record from Funk, and leaves no tombstone behind. @@ -308,6 +353,7 @@ fd_funk_rec_remove( fd_funk_t * funk, Always succeeds. */ + void fd_funk_rec_hard_remove( fd_funk_t * funk, fd_funk_txn_t * txn, diff --git a/src/funk/fd_funk_txn.c b/src/funk/fd_funk_txn.c index 12716ce8fa..0e7778f724 100644 --- a/src/funk/fd_funk_txn.c +++ b/src/funk/fd_funk_txn.c @@ -26,12 +26,12 @@ static fd_rwlock_t funk_txn_lock[ 1 ] = {0}; void -fd_funk_txn_start_read( fd_funk_t * funk FD_PARAM_UNUSED ) { +fd_funk_txn_start_read( fd_funk_t const * funk FD_PARAM_UNUSED ) { fd_rwlock_read( funk_txn_lock ); } void -fd_funk_txn_end_read( fd_funk_t * funk FD_PARAM_UNUSED ) { +fd_funk_txn_end_read( fd_funk_t const * funk FD_PARAM_UNUSED ) { fd_rwlock_unread( funk_txn_lock ); } diff --git a/src/funk/fd_funk_txn.h b/src/funk/fd_funk_txn.h index 2556ba1c6e..e19d0ce57b 100644 --- a/src/funk/fd_funk_txn.h +++ b/src/funk/fd_funk_txn.h @@ -114,10 +114,10 @@ fd_funk_txn_xid_t fd_funk_generate_xid(void); infrequently so not sure how much of a gain this would be. */ void -fd_funk_txn_start_read( fd_funk_t * funk ); +fd_funk_txn_start_read( fd_funk_t const * funk ); void -fd_funk_txn_end_read( fd_funk_t * funk ); +fd_funk_txn_end_read( fd_funk_t const * funk ); void fd_funk_txn_start_write( fd_funk_t * funk ); diff --git a/src/funk/fd_funk_val.h b/src/funk/fd_funk_val.h index 6f051ce439..9f2bc5f16d 100644 --- a/src/funk/fd_funk_val.h +++ b/src/funk/fd_funk_val.h @@ -41,7 +41,7 @@ fd_funk_val_max( fd_funk_rec_t const * rec ) { /* Assumes pointer in caller's ad FD_FN_PURE static inline void * /* Lifetime is the lesser of rec or the value size is modified */ fd_funk_val( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record */ - fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */ + fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */ ulong val_gaddr = rec->val_gaddr; if( !val_gaddr ) return NULL; /* Covers the marked ERASE case too */ /* TODO: consider branchless */ return fd_wksp_laddr_fast( wksp, val_gaddr ); @@ -49,7 +49,7 @@ fd_funk_val( fd_funk_rec_t const * rec, /* Assumes pointer in caller's addre FD_FN_PURE static inline void const * /* Lifetime is the lesser of rec or the value size is modified */ fd_funk_val_const( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record */ - fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */ + fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */ ulong val_gaddr = rec->val_gaddr; if( !val_gaddr ) return NULL; /* Covers the marked ERASE case too */ /* TODO: consider branchless */ return fd_wksp_laddr_fast( wksp, val_gaddr ); From dc237d2801109ff292c859d34ca41825f8bb7467 Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 12 May 2025 16:58:55 +0000 Subject: [PATCH 2/3] Restore funk changes --- src/funk/fd_funk_rec.c | 81 +----------------------------------------- src/funk/fd_funk_rec.h | 46 ------------------------ src/funk/fd_funk_txn.c | 4 +-- src/funk/fd_funk_txn.h | 4 +-- src/funk/fd_funk_val.h | 4 +-- 5 files changed, 7 insertions(+), 132 deletions(-) diff --git a/src/funk/fd_funk_rec.c b/src/funk/fd_funk_rec.c index 8f7f3ed1e9..c404c87032 100644 --- a/src/funk/fd_funk_rec.c +++ b/src/funk/fd_funk_rec.c @@ -99,8 +99,6 @@ fd_funk_rec_query_try_global( fd_funk_t const * funk, /* For cur_txn in path from [txn] to [root] where root is NULL */ - fd_funk_txn_start_read( funk ); - for( fd_funk_txn_t const * cur_txn = txn; ; cur_txn = fd_funk_txn_parent( cur_txn, funk->txn_pool ) ) { /* If record ele is part of transaction cur_txn, we have a match. According to the property above, this will be the @@ -114,14 +112,11 @@ fd_funk_rec_query_try_global( fd_funk_t const * funk, if( txn_out ) *txn_out = cur_txn; query->ele = ( FD_UNLIKELY( ele->flags & FD_FUNK_REC_FLAG_ERASE ) ? NULL : (fd_funk_rec_t *)ele ); - fd_funk_txn_end_read( funk ); return query->ele; } if( cur_txn == NULL ) break; } - - fd_funk_txn_end_read( funk ); } } return NULL; @@ -274,14 +269,13 @@ fd_funk_rec_clone( fd_funk_t * funk, fd_funk_txn_t * txn, fd_funk_rec_key_t const * key, fd_funk_rec_prepare_t * prepare, - fd_funk_txn_t const ** txn_out, int * opt_err ) { fd_funk_rec_t * new_rec = fd_funk_rec_prepare( funk, txn, key, prepare, opt_err ); if( !new_rec ) return NULL; for(;;) { fd_funk_rec_query_t query[1]; - fd_funk_rec_t const * old_rec = fd_funk_rec_query_try_global( funk, txn, key, txn_out, query ); + fd_funk_rec_t const * old_rec = fd_funk_rec_query_try_global( funk, txn, key, NULL, query ); if( !old_rec ) { fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_KEY ); fd_funk_rec_cancel( funk, prepare ); @@ -298,7 +292,6 @@ fd_funk_rec_clone( fd_funk_t * funk, memcpy( buf, fd_funk_val( old_rec, wksp ), val_sz ); if( !fd_funk_rec_query_test( query ) ) { - if( txn_out ) *txn_out = txn; return new_rec; } } @@ -425,78 +418,6 @@ fd_funk_rec_remove( fd_funk_t * funk, return FD_FUNK_SUCCESS; } -fd_funk_rec_t * -fd_funk_rec_modify_try( fd_funk_t * funk, - fd_funk_txn_t const * txn, - fd_funk_rec_key_t const * key, - fd_funk_rec_query_t * query ) { - fd_funk_rec_map_t * rec_map = fd_funk_rec_map( funk ); - fd_funk_xid_key_pair_t pair[1]; - if( txn == NULL ) { - fd_funk_txn_xid_set_root( pair->xid ); - } else { - fd_funk_txn_xid_copy( pair->xid, &txn->xid ); - } - fd_funk_rec_key_copy( pair->key, key ); - - int err = fd_funk_rec_map_modify_try( rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING ); - if( FD_UNLIKELY( err ) ) { - if( err == FD_MAP_ERR_KEY ) return NULL; - FD_LOG_ERR(( "modify returned err %d", err )); - } - - return fd_funk_rec_map_query_ele( query ); -} - -fd_funk_rec_t * -fd_funk_rec_modify_try_global( fd_funk_t * funk, - fd_funk_txn_t * txn, - fd_funk_rec_key_t const * key, - fd_funk_txn_t const ** txn_out, - fd_funk_rec_query_t * query ) { -#ifdef FD_FUNK_HANDHOLDING - if( FD_UNLIKELY( funk==NULL || key==NULL || query==NULL ) ) { - return NULL; - } - if( FD_UNLIKELY( txn && !fd_funk_txn_valid( funk, txn ) ) ) { - return NULL; - } -#endif - - /* Try to get a modifiable handle to the record within the current transaction. */ - fd_funk_rec_t * rec = fd_funk_rec_modify_try( funk, txn, key, query ); - if( rec ) { - if( txn_out ) *txn_out = txn; - return rec; - } - - /* Record does not exist in the current transaction. If it exists at all, it must - belong to a parent transaction, if it exists in funk. If so, clone it down - to the child transaction. */ - fd_funk_rec_prepare_t prepare; - rec = fd_funk_rec_clone( funk, txn, key, &prepare, txn_out, NULL ); - if( FD_UNLIKELY( rec==NULL ) ) { - /* Failed to clone the record or find the record in a parent txn. */ - return NULL; - } - - /* Publish record and release the lock */ - fd_funk_rec_publish( funk, &prepare ); - - /* Get a modifiable handle to the cloned record */ - rec = fd_funk_rec_modify_try( funk, txn, key, query ); - if( rec ) { - if( txn_out ) *txn_out = txn; - } - - return rec; -} - -void -fd_funk_rec_modify_test( fd_funk_rec_query_t * query ) { - fd_funk_rec_map_modify_test( query ); -} - void fd_funk_rec_set_erase_data( fd_funk_rec_t * rec, ulong erase_data ) { rec->flags |= ((erase_data & 0xFFFFFFFFFFUL) << (sizeof(unsigned long) * 8 - 40)); diff --git a/src/funk/fd_funk_rec.h b/src/funk/fd_funk_rec.h index eab1979e7a..d9c192767c 100644 --- a/src/funk/fd_funk_rec.h +++ b/src/funk/fd_funk_rec.h @@ -265,7 +265,6 @@ fd_funk_rec_clone( fd_funk_t * funk, fd_funk_txn_t * txn, fd_funk_rec_key_t const * key, fd_funk_rec_prepare_t * prepare, - fd_funk_txn_t const ** txn_out, int * opt_err ); /* fd_funk_rec_remove removes the live record with the @@ -298,50 +297,6 @@ fd_funk_rec_remove( fd_funk_t * funk, fd_funk_rec_t ** rec_out, ulong erase_data ); -/* fd_funk_rec_modify_try queries the in-prep transaction pointed to - by txn for the record whose key matches the key pointed to by the - key. If the query is successful, then we will acquire a lock on the - corresponding hash chain. Any updates made to the record can be - published into the funk transaction with a call to - fd_funk_rec_modify_test. */ - -fd_funk_rec_t * -fd_funk_rec_modify_try( fd_funk_t * funk, - fd_funk_txn_t const * txn, - fd_funk_rec_key_t const * key, - fd_funk_rec_query_t * query ); - -/* fd_funk_rec_modify_try_global is the same as fd_funk_rec_modify_try but will - query txn's ancestors for key from youngest to oldest if key is not - part of txn. As such, the txn of the returned record may not match - txn but will be the txn of most recent ancestor with the key - otherwise. *txn_out is set to the transaction where the record was - found. - - This code is mostly similar to fd_funk_rec_query_try_global but also - acquires a write lock on the hash chain. If the returned record is non-NULL, - it is the responsibility of the caller to release the lock with - fd_funk_rec_modify_test afterwards. - - This is reasonably fast O(in_prep_ancestor_cnt). - - Important safety tip! This function can encounter records - that have the ERASE flag set (i.e. are tombstones of erased - records). fd_funk_rec_modify_try_global will return a NULL in this case - but still set *txn_out to the relevant transaction. This behavior - differs from fd_funk_rec_modify_try. */ - -fd_funk_rec_t * -fd_funk_rec_modify_try_global( fd_funk_t * funk, - fd_funk_txn_t * txn, - fd_funk_rec_key_t const * key, - fd_funk_txn_t const ** txn_out, - fd_funk_rec_query_t * query ); - -/* Releases the lock on the hash chain. */ -void -fd_funk_rec_modify_test( fd_funk_rec_query_t * query ); - /* fd_funk_rec_hard_remove completely removes the record from Funk, and leaves no tombstone behind. @@ -353,7 +308,6 @@ fd_funk_rec_modify_test( fd_funk_rec_query_t * query ); Always succeeds. */ - void fd_funk_rec_hard_remove( fd_funk_t * funk, fd_funk_txn_t * txn, diff --git a/src/funk/fd_funk_txn.c b/src/funk/fd_funk_txn.c index 0e7778f724..12716ce8fa 100644 --- a/src/funk/fd_funk_txn.c +++ b/src/funk/fd_funk_txn.c @@ -26,12 +26,12 @@ static fd_rwlock_t funk_txn_lock[ 1 ] = {0}; void -fd_funk_txn_start_read( fd_funk_t const * funk FD_PARAM_UNUSED ) { +fd_funk_txn_start_read( fd_funk_t * funk FD_PARAM_UNUSED ) { fd_rwlock_read( funk_txn_lock ); } void -fd_funk_txn_end_read( fd_funk_t const * funk FD_PARAM_UNUSED ) { +fd_funk_txn_end_read( fd_funk_t * funk FD_PARAM_UNUSED ) { fd_rwlock_unread( funk_txn_lock ); } diff --git a/src/funk/fd_funk_txn.h b/src/funk/fd_funk_txn.h index e19d0ce57b..2556ba1c6e 100644 --- a/src/funk/fd_funk_txn.h +++ b/src/funk/fd_funk_txn.h @@ -114,10 +114,10 @@ fd_funk_txn_xid_t fd_funk_generate_xid(void); infrequently so not sure how much of a gain this would be. */ void -fd_funk_txn_start_read( fd_funk_t const * funk ); +fd_funk_txn_start_read( fd_funk_t * funk ); void -fd_funk_txn_end_read( fd_funk_t const * funk ); +fd_funk_txn_end_read( fd_funk_t * funk ); void fd_funk_txn_start_write( fd_funk_t * funk ); diff --git a/src/funk/fd_funk_val.h b/src/funk/fd_funk_val.h index 9f2bc5f16d..6f051ce439 100644 --- a/src/funk/fd_funk_val.h +++ b/src/funk/fd_funk_val.h @@ -41,7 +41,7 @@ fd_funk_val_max( fd_funk_rec_t const * rec ) { /* Assumes pointer in caller's ad FD_FN_PURE static inline void * /* Lifetime is the lesser of rec or the value size is modified */ fd_funk_val( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record */ - fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */ + fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */ ulong val_gaddr = rec->val_gaddr; if( !val_gaddr ) return NULL; /* Covers the marked ERASE case too */ /* TODO: consider branchless */ return fd_wksp_laddr_fast( wksp, val_gaddr ); @@ -49,7 +49,7 @@ fd_funk_val( fd_funk_rec_t const * rec, /* Assumes pointer in caller's addre FD_FN_PURE static inline void const * /* Lifetime is the lesser of rec or the value size is modified */ fd_funk_val_const( fd_funk_rec_t const * rec, /* Assumes pointer in caller's address space to a live funk record */ - fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */ + fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */ ulong val_gaddr = rec->val_gaddr; if( !val_gaddr ) return NULL; /* Covers the marked ERASE case too */ /* TODO: consider branchless */ return fd_wksp_laddr_fast( wksp, val_gaddr ); From fa08856cb85347d262a3f2acb75918ad181318df Mon Sep 17 00:00:00 2001 From: Manik Jain Date: Mon, 12 May 2025 17:12:06 +0000 Subject: [PATCH 3/3] Remove instr ctx zero setting --- src/flamenco/runtime/program/fd_bpf_program_util.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/flamenco/runtime/program/fd_bpf_program_util.c b/src/flamenco/runtime/program/fd_bpf_program_util.c index 5cb0f5b9b7..3e6c0fb402 100644 --- a/src/flamenco/runtime/program/fd_bpf_program_util.c +++ b/src/flamenco/runtime/program/fd_bpf_program_util.c @@ -253,11 +253,7 @@ fd_bpf_validate_sbpf_program( fd_exec_slot_ctx_t * slot_ctx, FD_LOG_CRIT(( "fd_vm_new() or fd_vm_join() failed" )); } - fd_exec_instr_ctx_t dummy_instr_ctx = {0}; - fd_exec_txn_ctx_t dummy_txn_ctx = {0}; - dummy_txn_ctx.slot = slot_ctx->slot_bank.slot; - dummy_txn_ctx.features = slot_ctx->epoch_ctx->features; - dummy_instr_ctx.txn_ctx = &dummy_txn_ctx; + fd_exec_instr_ctx_t dummy_instr_ctx; /* UNUSED in fd_vm_validate() */ vm = fd_vm_init( vm, &dummy_instr_ctx, 0UL,