refactor(attestation): extract attestation-types crate without dcap-qvl - #3245
Closed
pbeza wants to merge 3 commits into
Closed
refactor(attestation): extract attestation-types crate without dcap-qvl#3245pbeza wants to merge 3 commits into
pbeza wants to merge 3 commits into
Conversation
…undary Introduces a no_std crate carrying the Borsh-stable types that cross the `tee-verifier` contract boundary. The crate has no `dcap-qvl` dependency so it can be linked into consumer contracts (e.g. `mpc-contract`) without dragging `ring`/`webpki`/X.509 parsing into their WASM. Types: - `QuoteBytes`, `Collateral`: standalone mirrors of `dcap_qvl`'s quote bytes and `QuoteCollateralV3`. - `VerifiedReport`, `Report`, `TDReport10`, `TDReport15`, `EnclaveReport`, `TcbStatus`, `TcbStatusWithAdvisory`: Borsh mirrors of the corresponding `dcap_qvl` types, fields one-to-one for forward-compat. - `VerifierError`: error type for future use. Conversions between these mirrors and the `dcap_qvl` types live in the verifier crate, not here, to avoid the orphan rule and to keep this crate `dcap-qvl`-free. Borsh-schema derives are gated behind an optional `borsh-schema` feature, enabled by the verifier crate when `cargo near build` generates ABI. Part of the work described in docs/design/attestation-verifier-contract.md (PR #3160).
Adds the `tee-verifier` contract — a stateless NEAR contract exposing
one method:
verify_quote(quote: QuoteBytes, collateral: Collateral) -> VerifiedReport
The method reads the current block timestamp inside the contract and
calls `dcap_qvl::verify::verify(quote, collateral, now)`. On success,
the parsed report is converted to the Borsh mirror types from
`tee-verifier-interface` and returned via Borsh. On verification
failure, the method panics with the upstream error rendered as a
string; callers handle this as `PromiseResult::Failed` in their
callback.
The contract has no state and no admin. All policy (allowlists,
report-data binding, RTMR3 replay, app-compose validation, etc.) is
the caller's responsibility — only the cryptographic dcap-qvl part
lives here.
Includes:
- `tee_verifier::TeeVerifier` (empty state struct, one method).
- `tee_verifier::conversions` (free functions converting between
`dcap_qvl` types and the mirror types in `tee-verifier-interface`;
free functions rather than `From` impls because of the orphan rule).
- An integration test (`tests/verify_quote.rs`) that calls
`verify_quote` directly against the real Dstack quote+collateral
fixture from `test-utils`, asserting the returned `VerifiedReport`
has status `UpToDate`, no advisory IDs, and a TD10 report.
WASM size (non-reproducible, default release): ~518 KiB.
This is the v1 verifier from
`docs/design/attestation-verifier-contract.md` (#3160). A follow-up
PR will wire `mpc-contract`'s `submit_participant_info` into it via
Promise + callback.
Stacked on #3235.
…-qvl Pure crate split, no behaviour change. Sets up the WASM-size win that the follow-up PR (mpc-contract Promise refactor) will land. What moves: - `app_compose`, `measurements`, `report_data`, `tcb_info` modules and their assets — none of these touched `dcap-qvl` to begin with. - The post-DCAP verification helpers (`verify_tcb_status`, `verify_report_data`, `verify_rtmr3`, `verify_app_compose`, `verify_any_measurements`, `verify_static_rtmrs`, `verify_key_provider_digest`, `verify_event_log_rtmr3`, `validate_app_compose_config`, `compare_hashes`, `compare_hex_hashes`, plus the `OrErr` and `GetSingleEvent` traits and `VerificationError`) — refactored from private methods on `DstackAttestation` to free functions in `attestation_types::verify_post_dcap`, taking the `tee_verifier_interface::VerifiedReport` mirror instead of the `dcap_qvl` type. What stays in `attestation`: - `DstackAttestation::verify` (the one and only `dcap_qvl::verify::verify` call site). It now converts the dcap-qvl-returned report to the Borsh mirror once and calls the free functions on it. - The `Collateral` newtype and `QuoteBytes` newtype. Their dedup with the `tee-verifier-interface` mirrors is deferred to a later PR. What this enables (next PR): - `mpc-contract` can depend on `attestation-types` alone (no `dcap-qvl`) and call the post-DCAP helpers from a Promise callback that receives the verifier-returned `VerifiedReport`. That is the actual WASM-size win for NEP-509. Drops the dead `TryFrom<dcap_qvl::verify::VerifiedReport> for Measurements` impl (no callers anywhere in the codebase). Re-exports the moved modules from `attestation::lib.rs` so existing consumers (`mpc-attestation`, `tee-authority`, `attestation-cli`, etc.) keep their import paths unchanged. Part of the verifier breakout from docs/design/attestation-verifier-contract.md (#3160). Stacked on #3237.
pbeza
force-pushed
the
feat/attestation-types-split
branch
from
May 15, 2026 10:56
4cf10c7 to
6d62b22
Compare
This was referenced May 15, 2026
pbeza
force-pushed
the
feat/tee-verifier-contract
branch
13 times, most recently
from
June 9, 2026 17:55
f178791 to
180333d
Compare
Contributor
Author
andrei-near
pushed a commit
that referenced
this pull request
Aug 1, 2026
…eBytes into the no-dcap-qvl crate
Pure crate split, no behavior change. Sets up the next PR to drop
`dcap-qvl` from `mpc-contract`'s WASM build graph (currently
`mpc-contract -> mpc-attestation -> attestation -> dcap-qvl`).
What moves into `attestation-types`:
- `DstackAttestation` — the wasm-friendly bundle struct
(`quote`, `collateral`, `tcb_info`). No methods on the struct itself;
the `dcap_qvl::verify::verify` entry point is a trait method on the
`attestation` side.
- `Collateral` — field-for-field mirror of `dcap_qvl::QuoteCollateralV3`.
Borsh wire layout matches the upstream type when `dcap-qvl` is built
with its `borsh` feature, so on-chain state that previously stored an
`attestation::collateral::Collateral` (newtype around
`dcap_qvl::QuoteCollateralV3`) decodes into this type byte-identical
without migration.
- `QuoteBytes` — unchanged.
What stays in `attestation`:
- `DstackVerify` trait + `impl DstackVerify for DstackAttestation`, which
is the one and only `dcap_qvl::verify::verify` call site. Defined as
a trait because inherent impls on foreign types are forbidden in Rust.
- `collateral_from_dcap` / `collateral_to_dcap` free functions for
converting between `dcap_qvl::QuoteCollateralV3` and the mirror. Used
by off-chain code (`tee-authority`'s PCCS fetch path, integration
tests) and re-exported through `mpc_attestation` so the call sites
don't need to add a direct `attestation` dep.
Why a trait instead of an inherent method:
- The `DstackAttestation` struct lives in `attestation-types`.
- The `verify` method requires `dcap-qvl` (heavy crypto closure).
- Rust forbids inherent impls on foreign types. A trait lets the
`verify` method be added from the `attestation` crate (which has the
`dcap-qvl` dep) to the type defined in `attestation-types` (which
doesn't).
Callers using `dstack_attestation.verify(...)` syntax now need to bring
`DstackVerify` into scope. `mpc-attestation` does this internally; no
other consumers were affected.
Drive-bys:
- `mpc-attestation::collateral_from_dcap` / `mpc_attestation::collateral_to_dcap`
re-exports so `tee-authority` and `mpc-node` can call them without
adding an `attestation` direct dep.
- `IntoContractType<Collateral>` / `IntoInterfaceType<dtos::Collateral>`
impls in `mpc-contract` and `mpc-node` simplified to direct field
assignment (no more `Collateral::from(QuoteCollateralV3 { ... })`).
- `attestation` crate's deps pruned: drops `borsh`, `derive_more`,
`serde`, `hex`, `thiserror` (no longer used after the body shrank to
the trait + conversion helpers); `serde_json` moved to dev-deps.
WASM size impact (non-reproducible release-contract profile): 1,470,007
→ 1,471,144 bytes (+1,137 bytes / 0.07%). Negligible — the next PR
drops `dcap-qvl` from the contract entirely.
Stacked on #3245.
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.
Closes #3267
Pure crate split, no behaviour change. Stacked on #3237. Sets up the WASM-size win that the follow-up PR (
mpc-contractPromise refactor) will land.What moves into the new
attestation-typescrateapp_compose,measurements,report_data,tcb_infomodules — none of these toucheddcap-qvlto begin with.DstackAttestationinto free functions inattestation_types::verify_post_dcap, taking thetee_verifier_interface::VerifiedReportmirror instead of thedcap_qvltype. Same logic, byte-for-byte.What stays in
attestationDstackAttestation::verify— the one and onlydcap_qvl::verify::verifycall site. It now converts the upstream report to the Borsh mirror once and calls the free functions on it.Collateralnewtype andQuoteBytesnewtype. Their dedup with thetee-verifier-interfacemirrors is deferred to a later PR (tee-authorityand friends need a migration path).What this enables (next PR, stacked on this one)
mpc-contractcan depend onattestation-typesalone — nodcap-qvl— and call the post-DCAP helpers from a Promise callback that receives the verifier-returnedVerifiedReport. That is the actual WASM-size win for NEP-509.Drive-bys
TryFrom<dcap_qvl::verify::VerifiedReport> for Measurementsimpl (no callers anywhere in the codebase).attestation::lib.rsso existing consumers (mpc-attestation,tee-authority,attestation-cli, etc.) keep their import paths unchanged.Part of the verifier breakout described in
docs/design/attestation-verifier-contract.md(#3160).