Skip to main content

Context

Struct Context 

Source
pub struct Context<'a> {
    pub program_id: &'a Address,
    pub instruction_data: &'a [u8],
    /* private fields */
}
Expand description

Execution context for a Hopper instruction handler.

Wraps the program_id, account slice, and instruction data into a single object with structured access patterns.

§Authored flow

ⓘ
pub fn deposit(ctx: &Context, amount: u64) -> ProgramResult {
    let authority = ctx.account(0)?;
    let vault = ctx.account(1)?;

    authority.require_signer()?;
    vault.require_writable()?;
    vault.check_disc(1)?;

    let mut state = vault.load_mut::<VaultState>()?;
    state.balance = state.balance.checked_add(amount).ok_or(ProgramError::ArithmeticOverflow)?;
    Ok(())
}

Fields§

§program_id: &'a Address

The program’s own address.

§instruction_data: &'a [u8]

Raw instruction data (past the discriminator byte, if applicable).

Implementations§

Source§

impl<'a> Context<'a>

Source

pub fn new( program_id: &'a Address, accounts: &'a [AccountView<'a>], instruction_data: &'a [u8], ) -> Self

Create a new context from the entrypoint parameters.

Source

pub fn set_write_policy(&mut self, policy: &'static WritePolicy)

Install a declared write policy (write-policy enforcement).

From this point on, every Context-mediated write acquire, segment writes, whole-account load_mut, and the raw escape hatches raw_mut / as_mut_ptr, must be fully contained in one of the policy’s declared ranges or it fails with Custom(0xD000 | account_index) before any byte is written. Whole-account paths claim [0, data_len), so a policy that declares only field ranges forces handlers onto the declared segment accessors.

#[hopper::context(strict_writes)] compiles the context’s mut / mut(seg, ...) declarations into a static policy and installs it during bind(); calling this by hand is the raw equivalent. Direct substrate access on the raw AccountView (via account) is outside the governed surface, like every other documented escape hatch.

Source

pub fn set_parametric_write_policy( &mut self, policy: &'static WritePolicy, args: &[u32], ) -> ProgramResult

Install a declared write policy and bind the invocation values used by its ParametricWriteRanges.

Source

pub fn write_policy(&self) -> Option<&'static WritePolicy>

The installed write policy, if any.

Source

pub fn first_unauthorized_write_byte( &self, index: usize, offset: u32, size: u32, ) -> Option<u64>

Return the first byte of a recorded write touch that falls outside the installed invocation-resolved policy.

This is the audit counterpart to the acquire-time gate. It checks the union of static ranges and selected parametric cells because the touch ledger may coalesce adjacent authorized acquires. With no installed policy, or an account index that cannot be represented on the wire, it fails closed by returning the touch’s first byte.

Source

pub fn program_id(&self) -> &Address

Program ID.

Source

pub fn instruction_data(&self) -> &'a [u8]

Raw instruction data.

Source

pub fn account(&self, index: usize) -> Result<&'a AccountView<'a>, ProgramError>

Get an account by index.

Source

pub fn account_mut( &self, index: usize, ) -> Result<&'a AccountView<'a>, ProgramError>

Get an account by index (mutation-intent variant).

Functionally identical to account() since AccountView uses interior mutability for data access (overlay_mut, load_mut, try_borrow_mut). The distinct name signals that the caller intends to write through the returned reference.

Source

pub fn num_accounts(&self) -> usize

Get the total number of accounts.

Source

pub fn accounts(&self) -> &'a [AccountView<'a>]

Get all accounts as a slice.

Source

pub fn borrows(&self) -> &SegmentBorrowRegistry

Access the instruction-scoped segment borrow registry.

Source

pub fn borrows_mut(&mut self) -> &mut SegmentBorrowRegistry

Mutably access the instruction-scoped segment borrow registry.

Source

pub fn audit_accounts(&self) -> AccountAudit<'a>

Inspect the instruction account slice for duplicate aliases.

Source

pub fn finish_with_touch_map(&self)

Zero-cost sibling of finish_with_touch_map, compiled when the touch-map feature is off.

Keeps the macro-generated opt-in epilogue call compiling on builds that never enabled the touch-map machinery, and emits nothing. This is what makes “opt-in present but feature off” produce no sol_log_data record and pay no compute for it.

Source

pub fn remaining_accounts(&self, from: usize) -> &'a [AccountView<'a>]

Get the remaining accounts starting at from.

NOTE (binary size): the slicing below goes through get(..), never self.accounts[from..]. A range index LLVM cannot statically bound emits slice_end_index_len_fail, which formats its arguments and links Formatter::pad_integral, do_count_chars and the integer Display impls, ~3.7 KiB of core::fmt, into every Hopper program’s .text. These are #[inline(always)] hot-path helpers, so one panicking index here taxes every program. Keep them get-based.

Source

pub fn remaining_accounts_strict(&self, from: usize) -> RemainingAccounts<'a>

Get remaining accounts in strict duplicate-rejecting mode.

Source

pub fn remaining_accounts_passthrough( &self, from: usize, ) -> RemainingAccounts<'a>

Get remaining accounts in duplicate-preserving passthrough mode.

Source

pub fn remaining_accounts_typed(&self, from: usize) -> RemainingTyped<'a>

Get remaining accounts in strict mode and bind a sequential typed parser.

Source

pub fn remaining_accounts_lazy(&self, from: usize) -> RemainingLazy<'a>

Get remaining accounts in strict mode and bind a lazy indexed parser.

Source

pub fn require_accounts(&self, n: usize) -> ProgramResult

Require at least n accounts are present.

Source

pub fn require_unique_accounts(&self) -> ProgramResult

Require all account addresses to be unique.

Source

pub fn require_unique_writable_accounts(&self) -> ProgramResult

Require that no duplicated account is writable in this instruction.

Source

pub fn require_unique_signer_accounts(&self) -> ProgramResult

Require that no duplicated account is used as a signer role.

Source

pub fn require_data_len(&self, n: usize) -> ProgramResult

Require at least n bytes of instruction data.

Source

pub fn load<T: LayoutContract + Pod>( &self, index: usize, ) -> Result<Ref<'_, T>, ProgramError>

Validate-and-load the full typed layout for an account.

This is the indexed shortcut for ctx.account(idx)?.load::<T>(). It’s the canonical “Tier A” access path: the runtime checks the Hopper header, validates the data length, and projects the typed view in one inlined call. no extra cost over the spelled-out form.

Source

pub fn load_mut<T: LayoutContract + Pod>( &mut self, index: usize, ) -> Result<RefMut<'_, T>, ProgramError>

Validate-and-load a mutable typed layout for an account.

Indexed shortcut for ctx.account(idx)?.load_mut::<T>(). The returned guard holds the account-level exclusive borrow until it drops.

As a whole-account write borrow, this claims [0, data_len): under an installed write policy it requires a whole-account allowance (a plain mut declaration), and with the touch-map feature it lands in the instruction touch map as a full-account write record.

Source

pub fn load_cross_program<T: LayoutContract + Pod>( &self, index: usize, ) -> Result<Ref<'_, T>, ProgramError>

Cross-program load: validate ABI fingerprint without ownership check.

Use this when reading an account whose owner is another program but whose layout is published as a Hopper layout contract.

Source

pub fn segment_ref<'b, T: Pod>( &'b mut self, index: usize, abs_offset: u32, ) -> Result<SegRef<'b, T>, ProgramError>

Register a read borrow for a segment of an account and return a SegRef<T> that releases both the account-level byte guard and the segment registry lease on drop.

index is the account index. abs_offset is the absolute byte offset within the account data (including header bytes).

§Type Safety

T must implement Pod (substrate-level “safe to overlay on raw bytes” contract: every bit pattern valid, align-1, no padding, no interior pointers). Segment borrow tracking prevents conflicting write access to the same byte range for the guard’s lifetime.

§Canonical path

Three variants exist for different offset sources:

VariantUse when
segment_ref_typed (canonical)Offset is a compile-time constant (the common case). The const OFFSET: u32 generic becomes an immediate in the pointer arithmetic.
segment_ref_constOffset comes from a runtime crate::Segment value (dispatching dynamically between named fields).
segment_ref (this method)Offset is fully dynamic (iterating segments in a loop, for example).

#[hopper::context]-generated accessors default to the canonical typed path; reach for the others only when the use case genuinely needs a runtime offset.

Source

pub fn split_segments_mut<'b, T: Pod, const N: usize>( &'b mut self, index: usize, ranges: [(u32, u32); N], ) -> Result<SegmentsMut<'b, T, N>, ProgramError>

Borrow several disjoint typed sub-ranges of one account mutably at the same time. See AccountView::split_segments_mut.

ⓘ
let mut segs = ctx.split_segments_mut::<WireU64, 2>(
    vault_idx, [(BALANCE_OFF, 8), (NONCE_OFF, 8)])?;
let [bal, nonce] = segs.all_mut();
bal.set(bal.get() + amount);
nonce.set(nonce.get() + 1);
Source

pub fn segment_mut<'b, T: Pod>( &'b mut self, index: usize, abs_offset: u32, ) -> Result<SegRefMut<'b, T>, ProgramError>

Register a write borrow for a segment of an account.

Validates bounds, checks writable, and registers a leased exclusive borrow, then returns a SegRefMut<T> that releases on drop.

This primitive permits concurrent mutation of non-overlapping account regions. The lease model also permits sequential same-region borrows within one instruction.

Source

pub fn tail_seq_mut<'b, T: SeqElement>( &'b mut self, index: usize, body_end: u32, ) -> Result<SeqTailWrite<'b, T>, ProgramError>

Acquire a growable Seq<T> tail for writing at body_end (the layout’s TAIL_PREFIX_OFFSET), returning a SeqTailWrite guard whose seq_mut yields the O(1) streaming cursor.

The tail region is [body_end, data_len), the whole account past the fixed head. Under an installed write policy this whole region must be granted (a mut(<seq_field>) declaration compiles to an open-ended tail_from range), so the fixed head stays protected. Exactly ONE segment lease is registered, covering the entire tail region, NOT one per element; so overlap detection and the touch map see a single tail-region write record regardless of how many elements are pushed.

Source

pub fn tail_seq_ref<'b, T: SeqElement>( &'b mut self, index: usize, body_end: u32, ) -> Result<SeqTailRead<'b, T>, ProgramError>

Acquire a Seq<T> tail for reading at body_end, returning a SeqTailRead guard whose seq yields the streaming read cursor. Registers one shared tail-region lease (reads are not gated by the write policy, but the lease still powers overlap detection against concurrent writers).

Source

pub fn segment_ref_const<'b, T: Pod>( &'b mut self, index: usize, segment: Segment, ) -> Result<SegRef<'b, T>, ProgramError>

Const-driven segment read: pass a compile-time crate::Segment and the account index. Lowers to the same pointer-plus-const-offset shape as segment_ref but without the caller hand-rolling the offset + size arguments.

Source

pub fn segment_mut_const<'b, T: Pod>( &'b mut self, index: usize, segment: Segment, ) -> Result<SegRefMut<'b, T>, ProgramError>

Const-driven exclusive segment access. Pair with #[hopper::state] constants for zero-overhead field writes.

Source

pub fn segment_ref_typed<'b, T: Pod, const OFFSET: u32>( &'b mut self, index: usize, segment: TypedSegment<T, OFFSET>, ) -> Result<SegRef<'b, T>, ProgramError>

Typed-segment read: the type and offset are both compile-time constants, baked into a crate::TypedSegment zero-sized marker.

Source

pub fn segment_mut_typed<'b, T: Pod, const OFFSET: u32>( &'b mut self, index: usize, _segment: TypedSegment<T, OFFSET>, ) -> Result<SegRefMut<'b, T>, ProgramError>

Typed-segment write. Mirrors Self::segment_ref_typed for the exclusive path.

Source

pub unsafe fn raw_ref<T: Pod>( &self, index: usize, ) -> Result<Ref<'_, T>, ProgramError>

Explicit unsafe whole-account typed read.

§Safety

Caller must uphold the invariants documented for this unsafe API before invoking it.

Source

pub unsafe fn raw_mut<T: Pod>( &self, index: usize, ) -> Result<RefMut<'_, T>, ProgramError>

Explicit unsafe whole-account typed write.

§Safety

Caller must uphold the invariants documented for this unsafe API before invoking it.

Source

pub unsafe fn raw_unchecked<T: Pod>( &self, index: usize, ) -> Result<RefMut<'_, T>, ProgramError>

Legacy alias for raw_mut.

Despite the name, this does not bypass borrow tracking: it delegates to raw_mut, which routes through the checked segment_mut(0, size_of::<T>()) path (bounds, writable, and account-level exclusive borrow all enforced). The caller remains responsible for using a type that matches the account bytes. For a genuinely untracked pointer, use as_mut_ptr.

§Safety

Caller must uphold the invariants documented for this unsafe API before invoking it.

Source

pub unsafe fn as_mut_ptr(&self, index: usize) -> Result<*mut u8, ProgramError>

Canonical raw-pointer escape hatch to an account’s data buffer.

Returns a pointer to the first byte of accounts[index]’s data region (after the runtime account header, before any Hopper 16-byte layout header). The pointer is valid for reads and writes for the lifetime of the account view and carries no borrow-tracking obligations. Dereferencing it is unsafe because the caller takes over alias-safety responsibility that the segment registry normally upholds.

This is the explicit power-user primitive the audit asks for: safe code reaches for segment_ref_typed / segment_mut_typed / the generated ctx.<field>_segment_mut(...) accessors; raw code drops to unsafe { ctx.as_mut_ptr(0)?.add(offset) as *mut T }.

§Safety

The caller must guarantee no aliasing mutable borrow is held on the same account for the duration of any write through the returned pointer. The returned pointer must be dereferenced within the 'info lifetime of the account view; reading past AccountView::data_len() is undefined behaviour.

Source

pub fn as_ptr(&self, index: usize) -> Result<*const u8, ProgramError>

Immutable sibling of as_mut_ptr. Returns a *const u8.

Shared-borrow checking still runs, so calling this while an exclusive borrow is live on the same account fails with AccountBorrowFailed. The return value is safe to obtain; the caller only needs unsafe to dereference it.

Source

pub fn read_data<T: ValuePod>(&self, offset: usize) -> Result<T, ProgramError>

Read instruction data as a typed value (unaligned, little-endian safe).

Reads size_of::<T>() bytes starting at offset via read_unaligned. Caller must ensure T is a plain-old-data type where all bit patterns are valid.

Source

pub fn data_slice( &self, offset: usize, len: usize, ) -> Result<&[u8], ProgramError>

Get a byte slice from instruction data.

Source

pub fn instruction_tag(&self) -> Result<u8, ProgramError>

Read the first byte of instruction data as an instruction tag.

Common pattern for byte-tag dispatch.

Auto Trait Implementations§

§

impl<'a> !Send for Context<'a>

§

impl<'a> !Sync for Context<'a>

§

impl<'a> Freeze for Context<'a>

§

impl<'a> RefUnwindSafe for Context<'a>

§

impl<'a> Unpin for Context<'a>

§

impl<'a> UnsafeUnpin for Context<'a>

§

impl<'a> UnwindSafe for Context<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.