Skip to content

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.

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.

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.

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,
)?;

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);

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.

programs/teton-consumer-reference shows both paths. Mode A: execute_session_action. Mode B: execute_atomic_action with assert_atomic_attestation_ctx.