diff --git a/Cargo.toml b/Cargo.toml index 559011e..ac4fd08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,5 +55,8 @@ solana-zk-token-sdk = { git = "https://github.com/firedancer-io/agave", rev = "f # This feature is used to compile a target with a builtin replaced by a BPF program. # Requires the `CORE_BPF_PROGRAM_ID` and `CORE_BPF_TARGET` environment variables. core-bpf = [] +# Same as the `core-bpf` feature, but also includes special-casing required for +# conformance testing a BPF program against a builtin. +core-bpf-conformance = [] # This feature is used to stub out certain parts of the agave runtime for fuzzing stub-agave = ["solana-program-runtime/stub-proc-instr"] diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 944e8ce..8c6814a 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -106,17 +106,17 @@ pub fn declare_core_bpf_default_compute_units(_: TokenStream) -> TokenStream { if program_id == solana_sdk::address_lookup_table::program::id() { tokens = quote! { - #[cfg(feature = "core-bpf")] + #[cfg(feature = "core-bpf-conformance")] const CORE_BPF_DEFAULT_COMPUTE_UNITS: u64 = solana_address_lookup_table_program::processor::DEFAULT_COMPUTE_UNITS; } } else if program_id == solana_sdk::config::program::id() { tokens = quote! { - #[cfg(feature = "core-bpf")] + #[cfg(feature = "core-bpf-conformance")] const CORE_BPF_DEFAULT_COMPUTE_UNITS: u64 = solana_config_program::config_processor::DEFAULT_COMPUTE_UNITS; } } else if program_id == solana_sdk::stake::program::id() { tokens = quote! { - #[cfg(feature = "core-bpf")] + #[cfg(feature = "core-bpf-conformance")] const CORE_BPF_DEFAULT_COMPUTE_UNITS: u64 = solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS; } } diff --git a/scripts/build_core_bpf.sh b/scripts/build_core_bpf.sh index c6a27fe..2fa995b 100755 --- a/scripts/build_core_bpf.sh +++ b/scripts/build_core_bpf.sh @@ -33,6 +33,6 @@ set_core_bpf_vars "$1" CORE_BPF_PROGRAM_ID=$CORE_BPF_PROGRAM_ID CORE_BPF_TARGET=$CORE_BPF_TARGET FORCE_RECOMPILE=true $CARGO build \ --target x86_64-unknown-linux-gnu \ - --features core-bpf \ + --features core-bpf-conformance \ --lib \ --release diff --git a/src/lib.rs b/src/lib.rs index 8526499..a3983ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,11 +50,8 @@ use std::ffi::c_int; use std::sync::Arc; use thiserror::Error; -#[cfg(feature = "core-bpf")] -use { - solana_sdk::account::WritableAccount, solana_sdk::slot_hashes::SlotHashes, - solana_sdk::sysvar::Sysvar, -}; +#[cfg(any(feature = "core-bpf", feature = "core-bpf-conformance"))] +use solana_sdk::{account::WritableAccount, slot_hashes::SlotHashes, sysvar::Sysvar}; // macro to rewrite &[IDENTIFIER, ...] to &[feature_u64(IDENTIFIER::id()), ...] #[macro_export] @@ -579,7 +576,7 @@ fn load_builtins(cache: &mut ProgramCacheForTxBatch) -> HashSet { } fn execute_instr(mut input: InstrContext) -> Option { - #[cfg(feature = "core-bpf")] + #[cfg(feature = "core-bpf-conformance")] // If the fixture declares `cu_avail` to be less than the builtin version's // `DEFAULT_COMPUTE_UNITS`, the program should fail on compute meter // exhaustion. @@ -595,7 +592,7 @@ fn execute_instr(mut input: InstrContext) -> Option { } budget }; - #[cfg(not(feature = "core-bpf"))] + #[cfg(not(feature = "core-bpf-conformance"))] let compute_budget = ComputeBudget { compute_unit_limit: input.cu_avail, ..ComputeBudget::default() @@ -607,7 +604,7 @@ fn execute_instr(mut input: InstrContext) -> Option { sysvar_cache.fill_missing_entries(|pubkey, callbackback| { if let Some(account) = input.accounts.iter().find(|(key, _)| key == pubkey) { if account.1.lamports > 0 { - #[cfg(feature = "core-bpf")] + #[cfg(any(feature = "core-bpf", feature = "core-bpf-conformance"))] // BPF versions of programs, such as Address Lookup Table, rely // on the new `SolGetSysvar` syscall. However, APIs for // querying slot hashes built on top of `SolGetSysvar` are @@ -679,7 +676,7 @@ fn execute_instr(mut input: InstrContext) -> Option { .accounts .iter() .map(|(pubkey, account)| { - #[cfg(feature = "core-bpf")] + #[cfg(any(feature = "core-bpf", feature = "core-bpf-conformance"))] // Fixtures provide the program account as a builtin (owned by // native loader), but the program-runtime will expect the account // owner to match the cache entry. @@ -756,7 +753,7 @@ fn execute_instr(mut input: InstrContext) -> Option { let mut newly_loaded_programs = HashSet::::new(); for acc in &input.accounts { - #[cfg(feature = "core-bpf")] + #[cfg(any(feature = "core-bpf", feature = "core-bpf-conformance"))] // The Core BPF program's ELF has already been added to the cache. // Its transaction account was stubbed out, so it can't be loaded via // callback (inputs), since the account doesn't contain the ELF. @@ -888,21 +885,21 @@ fn execute_instr(mut input: InstrContext) -> Option { &mut timings, ); - #[cfg(feature = "core-bpf")] + #[cfg(feature = "core-bpf-conformance")] // To keep alignment with a builtin run, deduct only the CUs the builtin // version would have consumed, so the fixture realizes the same CU // deduction across both BPF and builtin in its effects. let cu_avail = input .cu_avail .saturating_sub(CORE_BPF_DEFAULT_COMPUTE_UNITS); - #[cfg(not(feature = "core-bpf"))] + #[cfg(not(feature = "core-bpf-conformance"))] let cu_avail = input.cu_avail - compute_units_consumed; let return_data = transaction_context.get_return_data().1.to_vec(); Some(InstrEffects { custom_err: if let Err(InstructionError::Custom(code)) = result { - #[cfg(feature = "core-bpf")] + #[cfg(feature = "core-bpf-conformance")] // See comment below under `result` for special-casing of custom // errors for Core BPF programs. if program_id == &solana_sdk::address_lookup_table::program::id() && code == 10 { @@ -912,14 +909,14 @@ fn execute_instr(mut input: InstrContext) -> Option { } else { Some(code) } - #[cfg(not(feature = "core-bpf"))] + #[cfg(not(feature = "core-bpf-conformance"))] Some(code) } else { None }, #[allow(clippy::map_identity)] result: result.err().map(|err| { - #[cfg(feature = "core-bpf")] + #[cfg(feature = "core-bpf-conformance")] // Some errors don't directly map between builtins and their BPF // versions. // @@ -944,7 +941,7 @@ fn execute_instr(mut input: InstrContext) -> Option { { return InstructionError::ComputationalBudgetExceeded; } - #[cfg(feature = "core-bpf")] + #[cfg(feature = "core-bpf-conformance")] // Another such error case arises when a program performs a write // to an account, but the data it writes is the exact same data // that's currently stored in the account state. @@ -987,7 +984,7 @@ fn execute_instr(mut input: InstrContext) -> Option { .into_iter() .enumerate() .map(|(index, data)| { - #[cfg(feature = "core-bpf")] + #[cfg(any(feature = "core-bpf", feature = "core-bpf-conformance"))] // Fixtures provide the program account as a builtin account // (owned by native loader). //