Skip to content

Commit

Permalink
Merge pull request #169 from buffalojoec/core-bpf-conformance
Browse files Browse the repository at this point in the history
core-bpf: feature-flag conformance special-casing
  • Loading branch information
buffalojoec authored Dec 3, 2024
2 parents 978dc75 + 3f0fe45 commit cdf3980
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 21 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
6 changes: 3 additions & 3 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/build_core_bpf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 14 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -579,7 +576,7 @@ fn load_builtins(cache: &mut ProgramCacheForTxBatch) -> HashSet<Pubkey> {
}

fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
#[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.
Expand All @@ -595,7 +592,7 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
}
budget
};
#[cfg(not(feature = "core-bpf"))]
#[cfg(not(feature = "core-bpf-conformance"))]
let compute_budget = ComputeBudget {
compute_unit_limit: input.cu_avail,
..ComputeBudget::default()
Expand All @@ -607,7 +604,7 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
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
Expand Down Expand Up @@ -679,7 +676,7 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
.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.
Expand Down Expand Up @@ -756,7 +753,7 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
let mut newly_loaded_programs = HashSet::<Pubkey>::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.
Expand Down Expand Up @@ -888,21 +885,21 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
&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 {
Expand All @@ -912,14 +909,14 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
} 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.
//
Expand All @@ -944,7 +941,7 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
{
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.
Expand Down Expand Up @@ -987,7 +984,7 @@ fn execute_instr(mut input: InstrContext) -> Option<InstrEffects> {
.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).
//
Expand Down

0 comments on commit cdf3980

Please sign in to comment.