Skip to content

feat: offline verification and revocation helpers, client digest fix#8

Open
L0STE wants to merge 3 commits into
masterfrom
feat/institutional-dx
Open

feat: offline verification and revocation helpers, client digest fix#8
L0STE wants to merge 3 commits into
masterfrom
feat/institutional-dx

Conversation

@L0STE

@L0STE L0STE commented Jun 11, 2026

Copy link
Copy Markdown

Summary

Three focused, client-side changes — flat in the existing crates/SDK, no new crates, no on-chain changes.

1. Client digest fix (crates/core/src/digest.rs)

advance_vector_digest diverged from the buffer the on-chain program actually hashes, in two ways:

  • The instructions sysvar's trailing 2-byte current_instruction_index footer was left zeroed, so any advance not at index 0 (e.g. preceded by a timeout or compute-budget instruction) produced a digest whose signature fails on-chain. The footer is now patched to the advance's index before hashing, matching the TS SDK and the runtime.
  • Per-instruction account flags were serialized verbatim, but the live sysvar reflects message-level flag promotion. New advance_vector_digest_with_fee_payer mirrors digest.ts exactly; the 5-arg function keeps verbatim flags for harnesses (mollusk) that serialize per-instruction flags.

2. Offline verification (vector-core::verify, vector-sdk/verify)

Per-scheme verify_advance_signature_* / verifyAdvanceSignature* functions mirroring the signers: recompute the exact digest the program will hash and verify the signature, returning the digest (= the next nonce) on success. Sign-time validation instead of broadcast-time failure discovery — relevant for cold-key flows where a re-sign is expensive.

  • High-S ECDSA normalized to match on-chain acceptance; EIP-191 legacy v=27/28 rejected with guidance (normalizeEip191RecoveryByte) since sol_secp256k1_recover requires 0–3
  • All five schemes covered, including Falcon-512/Hawk-512
  • A deterministic digest constant — with the advance at index 1 — is pinned in both the Rust and TS suites, locking cross-language digest compatibility and the footer fix against regression

3. Revocation helpers

Revocation falls out of the protocol: an inert advance (no pre/post instructions) signed at the outstanding nonce bumps the nonce and orphans whatever was pre-signed against it. This adds the thin helpers — revocation_digest and sign_revocation_instruction_{ed25519, secp256k1_ecdsa, secp256k1_eip191} with TS mirrors — next to the advance signers, so a revocation can be pre-signed in the same session as the transaction it guards and held as a kill-switch.

  • The digest commits to the broadcasting transaction's instruction sysvar, so a pre-signed revocation broadcasts as a transaction containing only the advance instruction (fits the default compute budget on ed25519/eip191/secp256k1; Falcon/Hawk sign via the advance path with a compute-budget pre-instruction)
  • A mollusk test proves the semantics end to end: revocation lands → the orphaned pre-signed advance fails → replaying the revocation fails
  • Cross-language pinned revocation digest constant in both suites

Docs

Readme gains an Operational Patterns section (offline verification, concurrency via one-identity-one-lane, unilateral revocation, ordered pre-signing via digest-as-next-nonce, expiry via in-buffer timeout) and corrects stale content: Passthrough was missing from the instruction table, Hawk-512 registration is three-step (not two), and several SDK listings had nonexistent signatures.

Test results

Suite Result
cargo test -p vector-tests (mollusk) 28 passed
cargo test -p vector-core 8 passed
cargo check --workspace clean
bun x tsc clean
bun vitest run sdk/ts/test/verify.test.ts 11 passed

Note: bare cargo build-sbf at the workspace root fails on a pre-existing getrandom SBF issue (unrelated); per-program cargo build-sbf --manifest-path programs/<scheme>/Cargo.toml works as documented.

@L0STE L0STE force-pushed the feat/institutional-dx branch from cbd689d to 59272cc Compare June 11, 2026 23:43
L0STE added 2 commits June 12, 2026 01:53
Two divergences between vector_core::advance_vector_digest and the
buffer the on-chain program actually hashes:

- The trailing 2-byte current_instruction_index footer was left zeroed
  by construct_instructions_data, so any advance not at index 0 (e.g.
  with a timeout pre-instruction) produced a digest whose signature
  fails on-chain. The footer is now patched to the advance's index
  before hashing, matching the TS SDK (digest.ts) and the runtime.
- Per-instruction account flags were serialized verbatim, but the live
  sysvar reflects message-level flag promotion (signer/writable ORed
  per account across all instructions, fee payer included). Added
  promote_to_message_flags and advance_vector_digest_with_fee_payer
  mirroring the TS implementation.

The 5-arg advance_vector_digest keeps its signature (footer patch only,
no promotion) for verbatim harnesses like mollusk; see doc comments.
Per-scheme verify functions mirroring the signers, flat in vector-core
and the TS SDK (vector-sdk/verify): recompute the exact digest the
on-chain program hashes and verify the signature against it, returning
the digest (the next nonce) on success. Covers ed25519, secp256k1
ECDSA (high-S normalized to match on-chain acceptance), EIP-191
(legacy v=27/28 rejected with guidance), Falcon-512, and Hawk-512.

A deterministic digest constant is pinned in both the Rust and TS test
suites — with the advance at index 1 — locking cross-language digest
compatibility and the sysvar index-footer fix against regression.

Readme: adds an Operational Patterns section (offline verification,
lanes, revocation via inert advance, ordered pre-signing, expiry) and
corrects stale content: Passthrough missing from the instruction
table, Hawk-512 registration is three-step not two, SDK listings with
nonexistent signatures.
@L0STE L0STE force-pushed the feat/institutional-dx branch from 59272cc to 1eaf141 Compare June 12, 2026 00:45
@L0STE L0STE changed the title feat: institutional toolkit — offline verify, lane pools, ordered chains, CLI, reference relayer feat: offline verification, Revoke instruction, client digest fix Jun 12, 2026
Revocation is an inert advance: signing the advance digest with no
pre/post instructions at the outstanding nonce bumps the nonce and
orphans whatever was pre-signed against it. Adds the thin helpers —
revocation_digest / sign_revocation_instruction_{ed25519,
secp256k1_ecdsa, secp256k1_eip191} and their TS mirrors — next to the
advance signers, plus a mollusk test proving the semantics end to end
(revocation lands, the orphaned pre-signed advance fails, replay
fails) and a cross-language pinned revocation digest constant.

The digest commits to the broadcasting transaction's instruction
sysvar, so a pre-signed revocation is broadcast as a transaction
containing only the advance instruction; Falcon/Hawk sign via the
advance path with a compute-budget pre-instruction (Hawk exceeds the
default budget). Documented in the Readme revocation pattern.
@L0STE L0STE force-pushed the feat/institutional-dx branch from 1eaf141 to d9830df Compare June 12, 2026 09:30
@L0STE L0STE changed the title feat: offline verification, Revoke instruction, client digest fix feat: offline verification and revocation helpers, client digest fix Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant