hpsvm 0.2.1

A fast and lightweight Solana VM simulator for testing solana programs
Documentation
//! Resident program-entry store (evm2 `ResidentMap` analogue).
//!
//! Two layers:
//!
//! * **Slice-pointer fast path** — the pre-existing `DEFAULT_PROGRAM_CACHE` memo for
//!   `include_bytes!` statics under the shared default environment. Pointer identity is stable for
//!   statics, so hashing is unnecessary.
//! * **Content-keyed store** — [`ProgramEntryStore`] keyed by `(environment identity, program
//!   content hash, loader, slot bucket)`. Generalizes the memo to `add_program`,
//!   `add_program_with_loader`, `add_program_from_file`, fixture loads and `rebuild_program_cache`.
//!
//! A hit skips ELF parsing, verification and JIT compilation entirely by
//! re-linking the resident `Arc<ProgramCacheEntry>`. A program account whose
//! bytes change hashes to a different key, so the old entry is simply
//! unreachable (content addressing; no eviction scan).
//!
//! Safety: the environment component is the `Arc` pointer identity of the
//! `ProgramRuntimeEnvironments` the entry was built under — the same argument
//! the previous memo documented. Only VMs sharing the exact same environment
//! `Arc` can observe a cached entry. Contents-equivalent environments are
//! distinct objects by construction (custom syscalls / feature sets build
//! fresh environments).
//!
//! The store key carries no policy component, so resident entries — including
//! their compiled status and invocation counters — are shared process-wide
//! across `Eager`/`Lazy`/`Off` VMs on the same environment (mirroring the
//! process-global stats precedent). Concretely: `Off`/`Lazy` select the
//! uncompiled load path on a store miss and never promote, but a store hit on
//! an already-compiled entry re-links it as-is. Policy therefore governs
//! loading and promotion, not retroactive downgrading of resident entries.

use std::{
    collections::HashMap,
    sync::{Arc, OnceLock},
};

use parking_lot::RwLock;
use solana_address::Address;
use solana_program_runtime::program_cache_entry::ProgramCacheEntry;

/// Cache key for a memoized default-program executable:
/// `(env_ptr, elf_ptr, elf_len, loader_id)`.
///
/// The ELF slice pointer is stable because the only producer is
/// `add_program_preverified`, invoked exclusively with `include_bytes!`
/// statics.
pub(crate) type DefaultProgramKey = (usize, usize, usize, Address);

/// Memoized verified executables for default programs.
pub(crate) static DEFAULT_PROGRAM_CACHE: OnceLock<
    RwLock<HashMap<DefaultProgramKey, Arc<ProgramCacheEntry>>>,
> = OnceLock::new();

/// Returns the process-wide default-program cache, creating it on first use.
pub(crate) fn default_program_cache()
-> &'static RwLock<HashMap<DefaultProgramKey, Arc<ProgramCacheEntry>>> {
    DEFAULT_PROGRAM_CACHE.get_or_init(|| RwLock::new(HashMap::new()))
}

/// Content-addressed key for the resident entry store.
///
/// `(env_ptr, content_hash, loader, slot_bucket)`:
/// * `env_ptr` — `Arc` pointer identity of the `ProgramRuntimeEnvironments` the entry was built
///   under.
/// * `content_hash` — SHA-256 over the exact ELF bytes that were verified and compiled (not the
///   account wrapper).
/// * `loader` — owning loader, part of the entry's identity.
/// * `slot_bucket` — deployment-slot constant family. All current loads use
///   `IMMEDIATE_DEPLOYMENT_SLOT`; the field keeps the key honest if that ever changes.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ProgramEntryKey {
    /// Environment `Arc` pointer identity.
    pub(crate) env: usize,
    /// SHA-256 over the ELF bytes.
    pub(crate) hash: [u8; 32],
    /// Owning loader.
    pub(crate) loader: Address,
    /// Deployment-slot bucket.
    pub(crate) slot_bucket: u64,
}

impl ProgramEntryKey {
    /// Builds a key from its components.
    pub(crate) const fn new(env: usize, hash: [u8; 32], loader: Address, slot_bucket: u64) -> Self {
        Self { env, hash, loader, slot_bucket }
    }
}

/// Computes the content hash over ELF bytes.
///
/// Uses the already-mandatory `solana-sha256-hasher` (no new dependency).
#[must_use]
pub(crate) fn content_hash(elf_bytes: &[u8]) -> [u8; 32] {
    solana_sha256_hasher::hashv(&[elf_bytes]).to_bytes()
}

/// Builds a [`ProgramEntryKey`] for a load attempt.
#[must_use]
pub(crate) fn content_key(
    env_ptr: usize,
    elf_bytes: &[u8],
    loader: &Address,
    slot_bucket: u64,
) -> ProgramEntryKey {
    ProgramEntryKey::new(env_ptr, content_hash(elf_bytes), *loader, slot_bucket)
}

/// Process-wide content-keyed resident entry store.
///
/// Maps [`ProgramEntryKey`] to the resident `Arc<ProgramCacheEntry>`.
/// Entries are content-addressed: a mutated program resolves to a different
/// key and triggers a fresh load; the old entry stays until the last `Arc`
/// holder drops it. No eviction scan is needed.
pub(crate) struct ProgramEntryStore {
    inner: RwLock<HashMap<ProgramEntryKey, Arc<ProgramCacheEntry>>>,
}

impl ProgramEntryStore {
    /// Creates an empty store.
    pub(crate) fn new() -> Self {
        Self { inner: RwLock::new(HashMap::new()) }
    }

    /// Looks up a resident entry.
    pub(crate) fn get(&self, key: &ProgramEntryKey) -> Option<Arc<ProgramCacheEntry>> {
        self.inner.read().get(key).cloned()
    }

    /// Inserts an entry, keeping the first writer on a race, and returns the
    /// resident entry (the winner).
    pub(crate) fn insert(
        &self,
        key: ProgramEntryKey,
        entry: Arc<ProgramCacheEntry>,
    ) -> Arc<ProgramCacheEntry> {
        self.inner.write().entry(key).or_insert(entry).clone()
    }

    /// Number of resident entries (stats / tests only).
    pub(crate) fn len(&self) -> usize {
        self.inner.read().len()
    }

    /// Whether the store holds no entries.
    #[cfg(test)]
    pub(crate) fn is_empty(&self) -> bool {
        self.inner.read().is_empty()
    }

    /// Clears the store (tests only).
    #[cfg(test)]
    pub(crate) fn clear(&self) {
        self.inner.write().clear();
    }
}

impl Default for ProgramEntryStore {
    fn default() -> Self {
        Self::new()
    }
}

impl std::fmt::Debug for ProgramEntryStore {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ProgramEntryStore").field("len", &self.len()).finish_non_exhaustive()
    }
}

/// Global resident entry store.
static PROGRAM_ENTRY_STORE: OnceLock<ProgramEntryStore> = OnceLock::new();

/// Returns the process-wide resident entry store.
pub(crate) fn program_entry_store() -> &'static ProgramEntryStore {
    PROGRAM_ENTRY_STORE.get_or_init(ProgramEntryStore::new)
}

#[cfg(test)]
mod tests {
    use proptest::prelude::*;

    use super::*;

    #[test]
    fn content_hash_is_deterministic_and_sensitive_to_one_byte() {
        let a = vec![1u8, 2, 3, 4];
        let mut b = a.clone();
        b[2] = 9;
        assert_eq!(content_hash(&a), content_hash(&a));
        assert_ne!(content_hash(&a), content_hash(&b));
    }

    #[test]
    fn key_is_deterministic_for_fixed_tuple() {
        let loader = Address::new_unique();
        let bytes = vec![7u8; 64];
        let k1 = content_key(0x1234, &bytes, &loader, u64::MAX);
        let k2 = content_key(0x1234, &bytes, &loader, u64::MAX);
        assert_eq!(k1, k2);
        let mut mutated = bytes.clone();
        mutated[0] ^= 0xff;
        let k3 = content_key(0x1234, &mutated, &loader, u64::MAX);
        assert_ne!(k1, k3);
        // Environment scoping: same bytes under a different env must miss.
        let k4 = content_key(0x5678, &bytes, &loader, u64::MAX);
        assert_ne!(k1, k4);
    }

    #[test]
    fn store_insert_is_first_writer_wins() {
        let store = ProgramEntryStore::new();
        let loader = Address::new_unique();
        let key = content_key(1, &[1, 2, 3], &loader, 0);
        assert!(store.get(&key).is_none());
        assert_eq!(store.len(), 0);
        // Insert path is exercised with a placeholder via len accounting;
        // entry-level identity is covered by the VM-level tests.
        assert!(store.is_empty());
    }

    #[test]
    fn default_program_cache_returns_a_stable_singleton() {
        // Kills the `Box::leak`-replacement mutants: every call must return
        // the same `&'static` (pointer identity), not a fresh leak.
        assert!(std::ptr::eq(default_program_cache(), default_program_cache()));
    }

    #[test]
    fn isolated_store_get_and_len_observe_inserts() {
        // Kills `get → None` and `len → 0`: a fresh store reports the
        // inserted entry via both accessors.
        use solana_program_runtime::{
            loaded_programs::ProgramRuntimeEnvironment, solana_sbpf::program::BuiltinProgram,
        };

        use crate::programs::load_program_uncompiled;
        let env = ProgramRuntimeEnvironment::from(BuiltinProgram::new_loader(Default::default()));
        let elf = include_bytes!("../../elf/spl_memo-3.0.0.so");
        let loader = solana_sdk_ids::bpf_loader::id();
        let entry =
            Arc::new(load_program_uncompiled(&loader, &env, elf).expect("load should succeed"));
        let store = ProgramEntryStore::new();
        let key = content_key(0xabc, elf, &loader, crate::accounts_db::IMMEDIATE_DEPLOYMENT_SLOT);
        assert_eq!(store.len(), 0);
        let resident = store.insert(key, Arc::clone(&entry));
        assert!(Arc::ptr_eq(&resident, &entry), "first insert must win");
        assert_eq!(store.len(), 1);
        let hit = store.get(&key).expect("inserted entry must be retrievable");
        assert!(Arc::ptr_eq(&hit, &entry));
    }

    proptest! {
        /// Store lookup is deterministic for fixed (env, bytes, loader, bucket).
        #[test]
        fn key_is_deterministic_for_arbitrary_inputs(
            bytes in prop::collection::vec(any::<u8>(), 0..512),
            env in any::<usize>(),
            bucket in any::<u64>(),
            loader_seed in any::<[u8; 32]>(),
        ) {
            let loader = Address::new_from_array(loader_seed);
            let k1 = content_key(env, &bytes, &loader, bucket);
            let k2 = content_key(env, &bytes, &loader, bucket);
            prop_assert_eq!(k1, k2);
        }

        /// Differing bytes never collide for a fixed (env, loader, bucket).
        #[test]
        fn differing_bytes_never_collide(
            base in prop::collection::vec(any::<u8>(), 1..512),
            flip_at in 0usize..512,
            flip_to in any::<u8>(),
        ) {
            let idx = flip_at % base.len();
            let mut mutated = base.clone();
            // Ensure a real difference; skip the (rare) no-op flip.
            prop_assume!(mutated[idx] != flip_to);
            mutated[idx] = flip_to;
            let loader = Address::new_unique();
            let k1 = content_key(0x1234, &base, &loader, u64::MAX);
            let k2 = content_key(0x1234, &mutated, &loader, u64::MAX);
            prop_assert_ne!(k1, k2);
        }
    }
}