feat: --mock-prove flag in saya-tee + saya-ops mock TEE registry deploy#60
Open
feat: --mock-prove flag in saya-tee + saya-ops mock TEE registry deploy#60
Conversation
Adds a new `core-contract declare-and-deploy-tee-registry-mock` subcommand to `saya-ops` that declares and deploys a permissive `IAMDTeeRegistry` mock contract on the settlement chain. The mock bypasses on-chain Groth16 verification of SP1 attestation proofs and just round-trips a `VerifierJournal` through Cairo Serde — see `piltover_mock_amd_tee_registry` (cartridge-gg/piltover#15). Used by end-to-end tests for `saya-tee --mock-prove` (companion PR in this same branch) where we want to exercise the Piltover TEE settlement path without producing or verifying real AMD SEV-SNP attestations. Mirrors the existing `declare-and-deploy-fact-registry-mock` exactly: same path/salt env vars, same UDC deploy with no constructor calldata, same `info!` log line for the resulting address. The `setup-program --fact-registry-address` flow is unchanged — the deployed mock TEE registry is a drop-in replacement at the `fact_registry_address` config slot. The contract artifact at `contracts/tee_registry_mock.json` is the compiled Sierra class produced by `scarb build` against the `feat/tee-mock-registry` branch of cartridge-gg/piltover. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a `--mock-prove` flag to `saya-tee tee start` that skips the
entire SP1 / AMD KDS / cert chain pipeline and synthesizes a stub
`VerifierJournal` whose `report_data` field encodes the Poseidon
commitment Piltover would otherwise extract from a real attestation
report.
When `--mock-prove` is set:
- `TeeProver::prove` does NOT call AMD KDS, validate any cert
chain, or submit anything to the SP1 prover network.
- It computes the same Poseidon commitment that piltover's
`validate_input` reconstructs:
Poseidon(prev_state_root, state_root, prev_block_hash,
block_hash, prev_block_number, block_number,
messages_commitment)
- It builds a 296-word stub `raw_report` with the commitment
placed in the `report_data` field at the byte layout piltover
reads (see `mock_proof::commitment_to_report_words` for the
derivation against `get_u128_at` + `u128_byte_reverse`).
- It Cairo-Serde-serializes the stub `VerifierJournal` and stores
the felts in `TeeProof.data` as raw big-endian bytes.
- `TeePiltoverSettlementBackend` decodes `TeeProof.data` directly
as felts (no `OnchainProof::decode_json` /
`StarknetCalldata::from_proof` round-trip) and forwards them to
`TEEInput.sp1_proof`.
The paired `piltover_mock_amd_tee_registry` Cairo contract
(cartridge-gg/piltover#15) decodes the felts back into a
`VerifierJournal` via Cairo Serde and returns it without any
cryptographic checks. Piltover's downstream `report_data` and
`messages_commitment` assertions then run unchanged.
`--mock-prove` is intended exclusively for end-to-end tests on
machines without AMD SEV-SNP hardware. Do not use in production.
The new `mock_proof` module is fully self-contained and depends only
on `starknet_types_core`. It does not touch the existing
SP1/KDS/cert-chain code paths and adds 5 unit tests covering the
felt encoding, byte layout, and round-trip invariants.
Companion PRs:
- saya bin/ops: declare-and-deploy-tee-registry-mock subcommand
(previous commit on this branch)
- cartridge-gg/piltover#15: mock_amd_tee_registry Cairo contract
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kariy
added a commit
to dojoengine/katana
that referenced
this pull request
Apr 7, 2026
Adds `tests/saya-tee/` — a new cargo crate (`saya-tee-e2e-test`) that
runs Saya's `saya-tee` binary against an in-process Katana, exercising
the persistent-TEE settlement path with all proving mocked out.
## Pipeline
1. Spawn an L2 dev Katana via `katana_utils::TestNode`.
2. Shell out to `saya-ops` to declare and deploy:
- `mock_amd_tee_registry` (the on-chain mock from
cartridge-gg/piltover#15), and
- the Piltover core contract pointed at the mock registry.
3. Spawn an L3 rollup Katana via `TestNode` with
`SettlementLayer::Starknet { ... }` pointing at L2's Piltover, and
`TeeConfig { provider_type: Mock, .. }` so its `tee_generateQuote`
RPC serves a stub attestation.
4. Spawn `saya-tee tee start --mock-prove` (dojoengine/saya#60) as a
child process pointed at both Katanas.
5. Drive a few L3 blocks by submitting no-op transfers.
6. Poll Piltover's `get_state()` until `block_number != Felt::MAX`.
## Required external binaries
- `saya-ops`: discovered via `SAYA_OPS_BIN` env var or `$PATH`. Built
from `dojoengine/saya@feat/mock-prove`.
- `saya-tee`: discovered via `SAYA_TEE_BIN` env var or `$PATH`. Built
from `dojoengine/saya@feat/mock-prove`. Transitively pulls
`cartridge-gg/katana-tee` over SSH.
## CI gating
Adds a new `saya-tee-e2e` job to `.github/workflows/test.yml` that:
- Loads the `KATANA_TEE_DEPLOY_KEY` secret into the SSH agent via
`webfactory/ssh-agent` so the cargo install of saya-tee can fetch
the private cartridge-gg/katana-tee deps.
- Clones `dojoengine/saya@feat/mock-prove` and `cargo install`s both
`saya-ops` and `saya-tee` from their sub-workspaces.
- Runs `cargo run -p saya-tee-e2e-test`.
The new test crate is excluded from the default `cargo nextest` run
just like `vrf-e2e-test` and `db-compat-test`.
## Companion PRs
- cartridge-gg/piltover#15: `feat(input): add mock_amd_tee_registry`
- dojoengine/saya#60: `feat: --mock-prove flag in saya-tee + saya-ops
mock TEE registry deploy`
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 7, 2026
Now that `cartridge-gg/katana-tee` is public, the SSH URLs in `bin/persistent-tee/Cargo.toml` for `katana_tee_client`, `amd-sev-snp-attestation-prover`, `amd-sev-snp-attestation-verifier`, `amd_tee_registry_client`, and `x509-verifier-rust-crypto` no longer need authentication. Switching to HTTPS removes the requirement for SSH agent setup or deploy keys in CI environments that build saya-tee. No version changes — same pinned rev (56aa752d). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removes the `webfactory/ssh-agent` step from lint.yml (×2),
test.yml, build.yml, release.yml, and release-dispatch.yml. All
five workflows tried to load `secrets.KATANA_TEE_DEPLOY_KEY` to
fetch the (then-private) `cartridge-gg/katana-tee` git deps used by
`bin/persistent-tee`, but the secret was never actually configured
on the dojoengine/saya repo, so every workflow run that touched
saya-tee build/test/release was failing with:
The ssh-private-key argument is empty. Maybe the secret has
not been configured, or you are using a wrong secret name in
your workflow file.
Now that `cartridge-gg/katana-tee` is public and the companion
commit on this branch switched the git URLs in
`bin/persistent-tee/Cargo.toml` from `ssh://` to `https://`, no
auth is needed at all.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`scripts/lint.sh` runs `cargo fmt --all --check` per sub-workspace
(`bin/persistent`, `bin/ops`, `bin/persistent-tee`) in addition to
the root workspace. The formatting expectations differ slightly from
the root workspace's `cargo fmt --all`, so the new code in this
branch needs both passes.
- `bin/persistent-tee/src/mock_proof.rs`: reformats the
`read_limb` test helper.
- `bin/ops/src/core_contract/cli.rs`: collapses a 2-line
`declare_contract(...).await?` into one.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cartridge-gg/piltover#15 has been merged, so the comment on TEE_REGISTRY_MOCK_BYTES no longer needs to point at the (now stale) `feat/tee-mock-registry` source branch. Replaces it with a generic "piltover#15" reference that survives once that branch is deleted. The vendored `contracts/tee_registry_mock.json` artifact is byte-identical to the one rebuilt from the merged base (piltover@feat/tee-persistent), so no artifact refresh is needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Member
Author
|
Status update — piltover companion merged ✅ cartridge-gg/piltover#15 has been merged into Doc comments and PR description references to the now-stale This PR is now ready for review on its own merits. The only remaining cross-repo dependency is the downstream Katana e2e PR that consumes the new |
`bin/persistent-tee` transitively depends on `boundless-market 1.1.0`
(via the `katana_tee_client` dep), which requires Rust >= 1.91
because of `aws-smithy-*` dep version constraints. Building it with
Rust 1.89 fails with `error[E0790]: cannot call associated function
on trait without specifying the corresponding impl type` deep inside
`boundless-market` and a cargo error chain ending in:
aws-smithy-runtime@1.10.3 requires rustc 1.91.1
aws-smithy-types@1.4.7 requires rustc 1.91.1
...
Either upgrade rustc or select compatible dependency versions
This was masked from CI until now because the saya workflows that
build/test/lint `bin/persistent-tee` had a `webfactory/ssh-agent`
step that always failed first (the `KATANA_TEE_DEPLOY_KEY` secret
was never configured). With that step removed in the previous
commit on this branch, the real toolchain compatibility problem
surfaces.
`release.yml` already references 1.91 alongside 1.87 (for different
target binaries), so 1.91 is a known-good toolchain in this repo.
Updated:
- rust-toolchain.toml
- .github/workflows/{build,lint,test,e2e-tests}.yml
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two new clippy lints surfaced now that CI runs Rust 1.91:
- `manual implementation of .is_multiple_of()` in `bytes_to_felts`:
`bytes.len() % 32 != 0` → `!bytes.len().is_multiple_of(32)`.
- `needless_range_loop` in `report_data_zero_outside_first_32_bytes`
test: replace `for i in 0..N { …raw_report[i]… }` with
`for (i, word) in raw_report.iter().enumerate().take(N)`.
Both flagged with `-D warnings` so they're errors, not warnings.
The clippy step ran clean before the toolchain bump because it
never reached these lints — it was failing earlier on the
`boundless-market` crate compile error that 1.91 fixes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This reverts commit 4d75d2b1 (bump to 1.91) along with the clippy/fmt fixes that were only needed because of the bump. Empirically, saya main passes CI on Rust 1.89 with the SAME Cargo.lock dep versions (boundless-market 1.1.0, aws-smithy-runtime 1.10.3, etc.) as this branch. Bumping to 1.91 trips a different broken path: alloy::sol! macro fails to parse boundless-market's generated Solidity bindings. The "aws-smithy requires 1.91" cargo error that motivated the original bump appears to be a stricter MSRV check enforced by newer local cargo binaries that doesn't apply on CI's 1.89 cargo. Reverting to 1.89 to match main and let CI prove the build passes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This reverts commit 666f6b3.
This reverts commit 66dc7a1.
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.
Summary
Adds a
--mock-proveflag tosaya-tee tee startplus a pairedsaya-ops core-contract declare-and-deploy-tee-registry-mocksubcommand. Together they let end-to-end tests exercise Piltover's
TEE settlement path without producing or verifying real AMD SEV-SNP
attestations or burning SP1 prover network credits.
This is the off-chain half of the e2e mock plumbing. The on-chain
half is the new
piltover_mock_amd_tee_registrycontract incartridge-gg/piltover#15.
What
--mock-provedoesWhen set,
saya-teeskips the entire pipeline that:AttestationReport::from_bytes)KDS::fetch_report_cert_chain)CertChain::check_valid)StarknetRegistryClient::fetch_trusted_prefix_len)AmdSevSnpProver::prove)Instead, it computes the same Poseidon commitment that Piltover's
validate_inputwould otherwise extract from a real attestation:…and stuffs it into a 296-word stub
raw_reportwhosereport_datafield is laid out so that Piltover's existing
u128_byte_reverse-basedreconstruction yields the original commitment. The stub
VerifierJournalis Cairo-Serde-serialized, the felts are stored inTeeProof.dataas raw big-endian bytes, andTeePiltoverSettlementBackendforwards them directly toTEEInput.sp1_proof(skippingOnchainProof::decode_jsonandStarknetCalldata::from_proof).Why both pieces in one PR
The
--mock-proveflag is useless without a permissiveIAMDTeeRegistrydeployed at the--tee-registry-addressslot, andthe new
saya-opssubcommand is the deploy mechanism. Bundling themkeeps the e2e wiring atomic.
The
saya-opssubcommand mirrors the existingdeclare-and-deploy-fact-registry-mockexactly (same path/salt envvars, same UDC deploy, same
info!log line). It embeds a vendoredSierra artifact at
contracts/tee_registry_mock.jsonbuilt fromcartridge-gg/piltover#15's
piltover_mock_amd_tee_registrycontract.Safety
--mock-proveis gated behind an explicit flag that defaults tofalse. The CLI help, the field doc onStart, the module-leveldocs in
mock_proof.rs, and the runtime log line all clearly mark itas test-only. The real proving path is unchanged in non-mock mode.
Test plan
cargo buildclean acrossbin/persistent-teeandbin/ops.cargo testpasses (5 new unit tests inmock_proofcover thefelt round-trip, raw_report layout, report_data byte ordering vs
Piltover's
u128_byte_reversereconstruction, and serializedjournal length).
saya-tee tee start --helpshows--mock-provewith fulldocumentation.
saya-ops core-contract --helpshowsdeclare-and-deploy-tee-registry-mock.against the piltover branch and a katana node with TEE-mode RPC.
This is the next PR.
Companion PRs
mock_amd_tee_registryfor e2e tests cartridge-gg/piltover#15:mock_amd_tee_registryCairo contract.tests/saya-teee2e test crate thatspawns Katana + saya-ops + saya-tee with all of these.
Maintaining the embedded mock TEE registry artifact
The
contracts/tee_registry_mock.jsonfile embedded intosaya-opsvia
include_bytes!is the compiled Sierra class of thepiltover_mock_amd_tee_registryCairo contract fromcartridge-gg/piltover#15.
It is not built or maintained from source in this repo — saya
only carries the pre-built artifact, mirroring how the existing
fact_registry_mock.jsonis handled.If the Cairo source ever changes (e.g. the
IAMDTeeRegistryinterface gains a field, or the mock's pass-through behavior is
adjusted), the artifact must be manually rebuilt and re-vendored:
The
setup-programflow does not need any changes when the mockregistry's class hash changes —
saya-ops'sdeclare-and-deploy-tee-registry-mockdeclares the (potentially new)class on the fly each time.