Panic asm exit, unsafe_no_disc accounts, PDA bump auto-detection#98
Merged
Panic asm exit, unsafe_no_disc accounts, PDA bump auto-detection#98
Conversation
Eliminate log("PANIC") + loop {} in panic handler, no_alloc!, and
panic_handler! macro. The new abort_program() function emits two SBF
instructions (lddw r0, 0x100000000; exit) that immediately terminate
with ProgramError::Custom(0). Removes the "PANIC" string from .rodata
and the sol_log_ syscall path.
Vault binary: 6,928 → 6,888 bytes (-40 bytes)
Add opt-in `#[account(unsafe_no_disc)]` that generates size-only AccountCheck (no discriminator validation), zero Deref offset, and empty Discriminator::DISCRIMINATOR. Replicates the SPL Token pattern for accounts that don't need disc bytes. Mutually exclusive with discriminator=, rejected for dynamic fields. The `unsafe_` prefix signals the developer's responsibility for ensuring account type uniqueness via size.
For bare `bump` (no `= expr`), auto-detect the bump value from two
sources before falling back to based_try_find_program_address:
1. Instruction args: `{field}_bump: u8` or single-PDA `bump: u8`
→ uses verify_program_address (~200 CU vs ~544 CU)
2. Inner account's stored bump: if Account<T> where T has a `bump: u8`
field, reads it via Discriminator::BUMP_OFFSET and verifies
→ compiler constant-folds the None branch away via LTO
Priority: instruction arg > stored bump > find (current fallback)
Saves ~344 CU per PDA when bump is available from either source.
⚡ CU Benchmark (Vault)
Binary size: 6,888 bytes (-40 🟢 bytes) |
8cfc626 to
50586d8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent improvements to the derive macros and runtime, plus a GitHub issue for future work.
Changes
1. Panic infrastructure → inline asm exit
Before: Panic handler calls
sol_log_("PANIC", 5)then entersloop {}, burning all remaining CU before the SVM kills the program. Theno_alloc!allocator triggers the same path viapanic!(""), pulling in format machinery.After: New
abort_program()function inquasar-langemits two SBF instructions:lddw r0, 0x100000000; exit. Immediately terminates withProgramError::Custom(0). No string in.rodata, no syscall, no loop. The off-chain path still panics with a descriptive message for test ergonomics.Three call sites updated:
#[program]-emitted panic handler (derive/src/program.rs)no_alloc!allocator (lang/src/entrypoint.rs)panic_handler!macro (lang/src/entrypoint.rs)Required
#![feature(asm_experimental_arch)]on thequasar-langcrate (SBF target only). The asm lives inquasar-langso user crates don't need the feature gate.Vault binary: 6,928 → 6,888 bytes (−40 bytes)
2.
#[account(unsafe_no_disc)]Opt-in attribute for accounts that don't need a discriminator — replicates the SPL Token
AccountCheckpattern (size-only validation, no disc offset in Deref).#[account(unsafe_no_disc)]generates:Discriminator::DISCRIMINATOR = &[]Space::SPACE = sizeof(ZcType)(no disc bytes added)AccountCheck::check— size-only, no disc byte validationDerefatdata_ptr().add(0)— no offsetThe
unsafe_prefix is the safety contract: the developer must ensure account type uniqueness via size. Rejected for dynamic fields (String/Vec/tail). Init and close paths work unchanged —copy_nonoverlappingwith length 0 is a no-op, and close zeroes the first 8 bytes of actual data as defense-in-depth.3. PDA bump auto-detection
When
bumpis bare (no= expr), we now auto-detect the bump from two sources before falling back tobased_try_find_program_address(~544 CU):Source 1 — Instruction args: If the Accounts struct has
#[instruction(vault_bump: u8)]and a PDA field namedvaultwith barebump, thevault_bumparg auto-binds toverify_program_address(~200 CU). Also matches a barebump: u8arg when there's exactly one bare-bump PDA in the struct.Source 2 — Stored bump in account data: If the inner type of
Account<T>has abump: u8field, the#[account]macro setsDiscriminator::BUMP_OFFSET = Some(disc_len + offset_of!(ZcType, bump)). At PDA check time, for non-init fields, the generated code reads the bump from the raw account data at that offset and usesverify_program_address. Theif let Some(offset) = BUMP_OFFSETbranch is constant-folded by LLVM via LTO — types without a stored bump compile down to the find path with zero overhead.Priority: instruction arg > stored bump > find.