Rust CPI / Anchor
Consumer programs depend on teton-cpi. The crate uses solana-program, teton-core, thiserror, and bytemuck. It does not depend on solana-sdk or anchor-lang.
teton-cpi = { path = "../teton-cpi" }Program id matches the live verifier: EKfHCTnsbRQfURsdmzkmuoxUx7DVt8WsQ5X7t49ZZZh.
Mode B — three-line helper
Section titled “Mode B — three-line helper”assert_atomic_attestation introspects the preceding Teton instruction. Pass the Instructions sysvar, the expectation, and program_id.
let claims = teton_cpi::assert_atomic_attestation( instructions_sysvar, &expectation, program_id,)?;Build the expectation from this instruction. Hash the discriminator, accounts, and payload with hash_action (@spec TETON-VERIFY-014). Do not accept a caller-supplied hash.
use teton_cpi::{assert_atomic_attestation, hash_action, AtomicAttestationExpectation};
let action_hash = hash_action(&[IX_EXECUTE_ATOMIC_ACTION], account_keys, payload);let expectation = AtomicAttestationExpectation { action_hash, expected_measurement: EXPECTED_MEASUREMENT, expected_signer: None, require_nonce: true, teton_ix_index: None, // preceding instruction};let claims = assert_atomic_attestation(instructions_sysvar, &expectation, program_id)?;require_nonce must stay true. false returns CpiError::NonceRequired. SNP Mode B requires VERIFY_ATOMIC_WITH_NONCE_DISCRIMINATOR. Nitro Mode B requires verify_nitro_attestation with mode == 1.
teton_ix_index: None selects current_index - 1. An explicit index may name any earlier Teton instruction.
Anchor adapters
Section titled “Anchor adapters”The two-arg macro reads ctx.accounts.instructions and ctx.program_id (@spec TETON-ANCHOR-002):
teton_cpi::assert_atomic_attestation_ctx!(ctx, &expectation)?;Function form:
teton_cpi::anchor::assert_atomic_attestation_ctx( &ctx.accounts.instructions.to_account_info(), &expectation, ctx.program_id,)?;Declare the sysvar as an unchecked account. Pin it to sysvar::instructions::ID in your own constraint if you want an extra check. The helper already asserts instructions_sysvar.key == instructions::ID.
Mode A — session signer
Section titled “Mode A — session signer”use teton_cpi::AttestedSignerAccount;
let attested = AttestedSignerAccount::try_from_account_info(attested_info)?;attested.validate_session_signer(workload_signer, &EXPECTED_MEASUREMENT)?;try_from_account_info asserts owner == teton_cpi::ID and the 8-byte discriminator. validate_session_signer requires workload_signer.is_signer, pubkey equality, measurement match, and Clock::get()?.slot <= expires_slot (@spec TETON-ANCHOR-001).
The lower-level helper also re-derives the PDA:
teton_cpi::assert_attested_signer( attested_info, workload_signer, &EXPECTED_MEASUREMENT, &teton_cpi::ID,)?;hash_action
Section titled “hash_action”Length-prefix discriminator, account count, and argument bytes, then hash each field. This blocks concatenation collisions (@spec TETON-VERIFY-014).
let digest = teton_cpi::hash_action(discriminator, &[&account_a, &account_b], data);CpiError
Section titled “CpiError”Codes align with VerifierError:
| Variant | Code | When |
|---|---|---|
DisallowedMeasurement |
3 | Measurement mismatch |
UnauthorizedPolicy |
7 | Consumer program id mismatch |
AttestationExpired |
8 | clock.slot > expires_slot |
ActionHashMismatch |
12 | Header action_hash mismatch |
SignerMismatch |
13 | Workload pubkey mismatch |
NonceRequired |
16 | require_nonce == false or verify_atomic discriminator |
See the instruction matrix for the full VerifierError table.
Reference consumer
Section titled “Reference consumer”programs/teton-consumer-reference shows both paths. Mode A: execute_session_action. Mode B: execute_atomic_action with assert_atomic_attestation_ctx.