Skip to main content

Module pod

Module pod 

Source
Expand description

Pod, the canonical runtime-layer “safe to interpret from raw bytes” marker.

Hopper’s typed access primitives (segment_ref, segment_mut, raw_ref, raw_mut, read_data) all overlay a T on a slice of account bytes. That overlay is only sound if every bit pattern of the right size decodes to a valid T and the type has alignment 1 (so the offset within the BPF input buffer is always valid for T).

The Hopper Safety Audit flagged that requiring only T: Copy is too loose: bool, char, references, and structs with padding are all Copy + Sized but not safe to overlay on raw bytes. This module carries the tightened marker.

§Contract

Implementing Pod for a type T asserts all of:

  1. Every [u8; size_of::<T>()] byte pattern represents a valid T. No “niches”, no enum-discriminant invariants, no bool-style forbidden bit patterns.
  2. align_of::<T>() == 1, the type can be read from any byte offset of an account buffer without alignment fault.
  3. T contains no padding (#[repr(C)] with alignment-1 fields, or #[repr(transparent)] over a Pod type).
  4. T contains no internal pointers / references, overlay always yields data that’s safe to Copy.

Hopper’s higher-layer macros (#[hopper::state], #[hopper::pod], hopper_layout!) enforce these conditions at compile time and emit the derived unsafe impl Pod. Hand-authored layouts opt in via unsafe impl Pod for MyLayout {}.

§Compile-fail demonstration (Hopper Safety Audit regression)

With the bytemuck feature on (default), the following mis-use patterns are all rejected at compile time. The audit’s Must-Fix #5 , “enforce field-level Pod proof at macro expansion time”, is now mechanically enforced by bytemuck’s own Pod + Zeroable bounds, so every zero-copy access path rejects them automatically.

bool is not Pod (the bit patterns 0x02..=0xFF don’t decode to a valid bool):

let _ = account.segment_ref::<bool>(borrows, 16, 1);

char is not Pod (valid Unicode scalar values form a sparse set):

let _ = account.segment_ref::<char>(borrows, 16, 4);

A #[repr(C)] struct with implicit padding is not bytemuck-Pod , bytemuck’s derive / Pod bound rejects the padding bytes because they’d leak uninitialised data through bytes_of:

#[derive(Copy, Clone)]
#[repr(C)]
struct Padded {
    a: u8,
    // implicit 7 bytes of padding to align b
    b: u64,
}
let _ = account.segment_ref::<Padded>(borrows, 16, 16);

A type-level user mis-spelling unsafe impl Pod for Padded {} without also satisfying bytemuck::Pod + Zeroable would fail at the Pod supertrait bound. The compile-fail block above exercises that path: no explicit impl Pod exists, and the access-path generic requires it.

A well-formed primitive or wire type is accepted:

let _: Result<hopper_runtime::SegRef<'_, u64>, _> =
    account.segment_ref::<u64>(borrows, 16, 8);

§Trait identity across layers

When hopper-native-backend is active (the default), this trait is a direct re-export of hopper_native::Pod. That keeps the entire Hopper stack, substrate, runtime, core, macros, on a single Pod trait: one unsafe impl Pod for MyStruct {} unlocks every Hopper access API from the lowest-level AccountView::raw_mut up to #[hopper::state]-generated accessors, across all crates, with no orphan-rule gymnastics. When a non-native backend is selected (legacy-pinocchio-compat, solana-program-backend), the trait is defined locally with the same contract so user code compiles unchanged.

Traits§

Pod
Marker for types that can be safely overlaid on raw account bytes.