Mode B — Stateless Atomic Attestation
Mode B attests and executes in one transaction. Instruction 0 is Teton. Instruction 1 is the consumer. The pair succeeds or fails together.
Use Mode B for discrete actions: NAV marks, liquidations, hourly rebalances.
The canonical instruction is verify_atomic_with_nonce. verify_atomic returns VerifierError::NonceRequired and writes no state (@spec TETON-SEC-005).
- The enclave binds
REPORT_DATA[32..64] = SHA-256(slot_hash ‖ action_hash ‖ consumer_program_id). - The client packs Teton at index 0 and the consumer at index 1.
- The verifier loads
slot_hashfromSlotHashesatnonce_slot(age ≤ 150). - The verifier checks VCEK / Nitro signature, measurement, TCB, and the action commitment (
@spec TETON-VERIFY-012). - The verifier allocates a 17-byte
UsedNonceAccountPDA seeded[b"nonce", signer_pubkey, commitment]. - The consumer calls
assert_atomic_attestationand reads the preceding Teton instruction throughsysvar::instructions.
Stage 2 stays under 550,000 CUs (@spec TETON-PERF-002). Mock-crypto SBF harness: verify_atomic_with_nonce ≈ 59,638 CUs. The consumer introspection check is bounded at 2,500 CUs (@spec TETON-CONSUMER-002).
80-byte header
Section titled “80-byte header”On the wire, Mode B SNP data is:
discriminator (8) ‖ VerifyAtomicHeader (72) ‖ SNP report (1,184) = 1,264 bytes.
The 80-byte prefix at 0x00..0x50 is the discriminator plus header:
| Offset | Size | Field |
|---|---|---|
| 0 | 8 | VERIFY_ATOMIC_WITH_NONCE_DISCRIMINATOR [209, 4, 10, 239, 3, 142, 223, 250] |
| 8 | 8 | nonce_slot (u64 LE) |
| 16 | 32 | action_hash |
| 48 | 32 | consumer_program_id |
| 80 | 1184 | SNP report (zero-copy, @spec TETON-ARCH-002) |
teton_core::VerifyAtomicHeader is the 72-byte body after the discriminator (nonce_slot, action_hash, consumer_program_id).
SlotHashes freshness
Section titled “SlotHashes freshness”The verifier pins slot_hashes_account.key == sysvar::slot_hashes::ID and loads the 32-byte hash at nonce_slot. Age above 150 slots is VerifierError::StaleNonce.
The commitment is native hashv:
expected = SHA-256(slot_hash ‖ header.action_hash ‖ header.consumer_program_id)REPORT_DATA[32..64] == expectedA mismatch is VerifierError::ActionHashMismatch.
UsedNonceAccount replay defense
Section titled “UsedNonceAccount replay defense”| Item | Value |
|---|---|
| Size | 17 bytes (disc 8 + slot 8 + bump 1) |
| Discriminator | [157, 75, 223, 206, 73, 132, 216, 154] |
| Seeds | [b"nonce", signer_pubkey, commitment] |
| Allocate | System Program create_account, funded by payer (@spec TETON-ARCH-001) |
| Replay | VerifierError::NonceAlreadyUsed |
teton_cpi::assert_atomic_attestation rejects require_nonce == false and rejects the verify_atomic discriminator (@spec TETON-VERIFY-013).
Accounts
Section titled “Accounts”payer, policy_registry, vcek, used_nonce, SlotHashes, Clock, system_program.
Nitro Mode B uses verify_nitro_attestation with mode = 1 and appends the Instructions sysvar. The consumer program id is the next instruction’s program id.
Packet size
Section titled “Packet size”A 1,264-byte SNP payload exceeds the 1,232-byte legacy UDP MTU. Use Solana v1 transactions (4,096-byte MTU, SIMD-0296). The TypeScript SDK throws PacketTooLargeError on the legacy path. See the TypeScript SDK.