Skip to content

feat(protocol): Proposal0020 — move SGX legs onto a PCCS-backed DCAP entrypoint - #21964

Draft
davidtaikocha wants to merge 4 commits into
mainfrom
david/proposal0020-sgx-swap
Draft

feat(protocol): Proposal0020 — move SGX legs onto a PCCS-backed DCAP entrypoint#21964
davidtaikocha wants to merge 4 commits into
mainfrom
david/proposal0020-sgx-swap

Conversation

@davidtaikocha

@davidtaikocha davidtaikocha commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Moves Taiko's two SGX proof legs off the legacy stripped AutomataDcapV3Attestation proxies and onto a Taiko-owned upstream AutomataDcapAttestationFee entrypoint backed by Automata's on-chain PCCS. RISC0 and SP1 are reused unchanged; the Inbox is re-pointed at a new ZkRequiredVerifier composing the new SGX verifiers with them.

The proof policy is unchanged — at least one ZK proof per batch; SGX-GETH + SGX-RETH alone still can never finalize.

Important

Stacks on #21935 / Proposal0019, which must execute first. 0020 assumes 0019 has already rotated the RISC0/SP1 trusted IDs to raiko2 v0.6.0 on the reused ZK verifiers, run init3, and upgraded the Inbox.

Why now

The upstream Automata entrypoint resolves quotes against PCCS router 0xE2Cd5aA4…, which was unprovisioned on mainnet until 2026-07-10, when Automata deployed a V2 async-upsert DAO stack behind it and upserted the collateral. getStandardTcbEvaluationDataNumber(SGX) now returns 19 and the full read path resolves — which is what unblocked this migration.

Why a new verifier and a new inbox impl

ComposeVerifier holds its sub-verifiers as immutables and Inbox._proofVerifier is immutable. So changing the SGX legs cascades: new SGX verifiers → new ZkRequiredVerifier → new MainnetInbox impl → upgrade. The existing 0x7284aaC0… cannot be re-pointed.

The new impl is identical to Proposal0019's (0x5253D4C9…) in every Config field except proofVerifier — verified against both deployments on-chain, and asserted at deploy time by DeploySgxSwapProofStack._assertOnlyProofVerifierChanged.

Deployed addresses (mainnet block 25543404, verified on-chain)

Contract Address
AutomataDcapAttestationFee 0x49216ad7d4DbafbE2F14525a863E621e2041ECB6
V3QuoteVerifier 0x560bd80fa0C0109954f0a8EFacb06779df397072
SecureSgxVerifier (geth) 0xA8A78d008b5745dd8487A8E912cD3d5A8618b496
SecureSgxVerifier (reth) 0x4bFaB16Bd9DA86bF6498a640B4d076eF4Ef5dfaA
ZkRequiredVerifier 0x0676334976D6578229829fAf92fb72Bd9378995b
MainnetInbox (new impl) 0x05C9620F9cc7154Ab1a47029014960e673586138

Reused: RISC0 0x059dAF31…, SP1 0x73A0Db39…. Entrypoint is DAO-owned with getBp() == 0.

9 actions

0–2 establish SGX-geth trust (setMrSigner / setMrEnclave / setEnclaveAttributePolicy), 3–7 the same for SGX-reth (two measurements: non-EDMM + EDMM), 8 upgrades the Inbox. Note the trust surface moved: on the new stack these are onlyOwner on the verifier itself, whereas 0019 rotates measurements on the legacy attester proxies.

ATTRIBUTES pin — Profile A ✅

ENCLAVE_ATTRIBUTE_MASK     = 0xffffffffffffffff0000000000000000
ENCLAVE_ATTRIBUTE_EXPECTED = 0x05000000000000000000000000000000   // INIT | MODE64BIT

Profile A ("strict FLAGS pin") from enclave-attribute-policies.md — the documented default for both prover enclaves and the exact pin SgxVerifier.t.sol exercises (STRICT_MASK/STRICT_EXPECTED). Mask checks all 8 FLAGS bytes, leaves XFRM unchecked (so provers with XFRM 0x03/0x07/0xE7 all pass); expected requires INIT|MODE64BIT and forces every other FLAGS bit — forbidden floor, CET, KSS, AEX_NOTIFY, reserved — to zero.

FLAGS = 0x05 established from the builds, not assumed. The two enclaves use different TEE frameworks:

Enclave Framework Evidence
SGX-reth raiko2, Gramine v0.6.0 manifest: sgx.debug = false, no KSS/CET/AEX-Notify. sgx.edmm_enable is templated and EDMM sets no FLAGS bit → both reth measurements share the pin.
SGX-geth gaiko2, EGo v1.9 (Open Enclave) ego/enclave.json: "debug": false; EGo's schema exposes no knob for KSS/CET/AEX-Notify.

Important

Reconcile against a real v0.6.0 quote before submission — re-pinning bumps the policy version and revokes every instance registered under the old pin. ATTRIBUTES = bytes [96:112] of a raw quote (raiko2-sgx-prover bootstrap emits one; it's also persisted on any running prover). The SGX-geth/EGo side has no on-chain precedent: the live 0x41e79EB4/0x9D3C595B are the pre-#21827 SgxVerifier and carry no attribute policy at all — I verified enclaveAttributePolicy/instanceValidityDelay don't even exist on them. Neither project's build metadata exposes the attributes (both scripts drop them), so use gramine-sgx-sigstruct-view or a quote.

Post-execution (operational, not DAO)

The registrar 0x9CBeE534… calls registerInstance on both new verifiers with fresh v0.6.0 quotes. It cannot run earlier — registration fail-closes until the DAO sets the pin. Until then no SGX leg verifies and finalization runs on RISC0 + SP1, which ZkRequiredVerifier accepts.

Testing

  • FOUNDRY_PROFILE=layer1 forge build — clean.
  • P=0020 pnpm proposal generates Proposal0020.action.md; decoded calldata = 9 actions (2 setMrSigner, 3 setMrEnclave, 3 setEnclaveAttributePolicy, 1 upgradeTo; 3 hits on sgxGeth, 5 on sgxReth, 1 on INBOX). The AttributePolicyNotSet() guard was verified to fire while the pin was unset.
  • Fork-simulated the full bundle against mainnet (MODE=l1dryrun, no --broadcast) → DryrunSucceeded(), 280,336 gas: the pin is accepted on-chain (EnclaveAttributePolicySet(..., version: 1)) and upgradeTo passes the UUPS proxiableUUID() check.
  • DeploySgxSwapProofStack fork-simulated against mainnet before the real run; its drift assertion passed against the live 0x5253d4c9.
  • All deployed addresses re-verified on-chain (codesize + full wiring).

Notes

  • Also includes DeploySgxSwapProofStack.s.sol so the proposal's addresses are reproducible — it exists in no other PR, and it asserts at deploy time that the new inbox impl differs from the reference impl only in proofVerifier. The entrypoint comes from the existing DeployAutomataDcapAttestation (profile layer1o); this script (profile layer1) takes it via DCAP_ATTESTATION.
  • Orphaned contracts: an earlier partially-failed run (3 of 4 txs dropped by the RPC) left unreferenced contracts on mainnet — entrypoint 0x55862381…, SGX-geth 0xe78129f8…, P256 0x03Fad3a6…, V3 0xFf269552…. Nothing references them; the live set is the table above. Documented in Proposal0020.md so reviewers aren't confused by two entrypoints on Etherscan.
  • Etherscan verification of the new contracts is still pending.

🤖 Generated with Claude Code

…entrypoint

Retires the Proposal0017 SGX verifiers, which attest through Taiko's legacy
stripped AutomataDcapV3Attestation proxies, in favour of two fresh
SecureSgxVerifiers attesting through a Taiko-owned upstream
AutomataDcapAttestationFee entrypoint backed by Automata's on-chain PCCS
(router 0xE2Cd5aA4, provisioned by Automata on 2026-07-10).

The RISC0 and SP1 verifiers are reused unchanged, so the raiko2 v0.6.0 image
IDs and program keys that Proposal0019 trusts carry over. Because
ComposeVerifier holds its sub-verifiers as immutables and Inbox._proofVerifier
is immutable, swapping the SGX legs requires both a new ZkRequiredVerifier and
a new MainnetInbox impl; the latter is identical to Proposal0019's impl in
every Config field except proofVerifier.

Stacks on Proposal0019, which must execute first.

Also adds the deploy script that produced the addresses, so the proposal is
reproducible: DeploySgxSwapProofStack asserts at deploy time that the new inbox
impl differs from the reference impl only in proofVerifier.

The ATTRIBUTES pin is deliberately unset — buildL1Actions reverts with
AttributePolicyNotSet() until it is filled from a real raiko2 v0.6.0 quote, so
the proposal cannot emit executable action data with an unset pin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
davidtaikocha and others added 3 commits July 16, 2026 14:57
Sets ENCLAVE_ATTRIBUTE_MASK / ENCLAVE_ATTRIBUTE_EXPECTED to Profile A (the
"strict FLAGS pin" from enclave-attribute-policies.md): mask checks all 8 FLAGS
bytes and leaves XFRM unchecked, expected requires INIT|MODE64BIT and forces
every other FLAGS bit — including CET, KSS and AEX_NOTIFY — to zero.

Both v0.6.0 enclaves attest with FLAGS = 0x05, established from their builds
rather than assumed. They use different TEE frameworks: raiko2's SGX-reth is
Gramine (manifest sets sgx.debug=false, enables no KSS/CET/AEX-Notify; EDMM sets
no FLAGS bit so both reth measurements share the pin), while gaiko2's SGX-geth is
EGo v1.9 / Open Enclave (enclave.json "debug": false, and the schema exposes no
knob for those bits).

This clears the AttributePolicyNotSet guard, so Proposal0020.action.md is now
generated. Fork-simulating the bundle against mainnet executes all 9 actions
cleanly (280,336 gas): the pin is accepted on-chain (EnclaveAttributePolicySet
version 1) and the inbox upgradeTo passes the UUPS proxiableUUID check.

The pin still wants reconciling against a real v0.6.0 quote before submission —
re-pinning later revokes instances registered under the old pin — and the
SGX-geth/EGo side has no on-chain precedent to cross-check against.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removes script/layer1/verifiers/deploy_sgx_swap.sh. It was only a convenience
wrapper around two forge broadcasts, and the deployment it automated has already
run; DeploySgxSwapProofStack.s.sol remains as the substantive, reproducible
artifact behind the proposal's addresses.

Repoints the two references that named it (the Proposal0020 constant block and
the deployed-addresses section) at the underlying scripts instead:
DeployAutomataDcapAttestation (profile layer1o) for the entrypoint, then
DeploySgxSwapProofStack (profile layer1) with DCAP_ATTESTATION set to it.

Comment-only change to the proposal, so the generated action data is unaffected —
re-running `P=0020 pnpm proposal` reproduces byte-identical calldata.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant