Going with classTag || schemeTag || canonicalPayload — the tag namespace should scale with the cryptography, not fork the class. A TEE using SGX ECDSA-P256 attestation and a TEE using AWS Nitro’s secp256k1 attestation are both still class 0x02 in every way a consumer cares about (hardware-attested proof); they only differ in payload length/encoding. Folding that difference into new top-level classTags means the class enum grows every time a new hardware/curve combo ships, and a consumer that only wants to ask “is this hardware-attested” has to enumerate every scheme variant to answer a class-level question. A 1-byte schemeTag keeps that separation without growing the class enum:
// instanceId encoding: classTag (1 byte) || schemeTag (1 byte) || canonicalPayload
//
// classTag 0x01 — contract verifier
// schemeTag 0x00 (reserved, no scheme variance) payload: 20-byte address
//
// classTag 0x02 — TEE / hardware attestation
// schemeTag 0x01 — SGX, ECDSA-P256 identity payload: 33 bytes (compressed pubkey)
// schemeTag 0x02 — AWS Nitro, secp256k1 identity payload: 33 bytes (compressed pubkey)
// (new hardware/curve combos register a new schemeTag under 0x02, not a new classTag)
//
// classTag 0x03 — threshold-signing committee
// schemeTag 0x01 — BLS12-381 aggregate pubkey payload: 48 bytes (compressed G1)
// schemeTag 0x02 — Ed25519 multisig root payload: 32 bytes
Spec-text diff against the current instanceId: bytes bullet:
- **`instanceId: bytes`** — identifies the verifier deployment. On Ethereum:
- `abi.encodePacked(address(this))`. Other verifier classes (TEE enclave measurement,
- threshold-signing committee aggregate pubkey) use their own canonical encoding; the
- encoding is determined by the verifier class, and a consumer identifies the class
- from `agentProofProfile`.
+ **`instanceId: bytes`** — self-describing verifier-instance identifier, encoded as
+ `classTag (1 byte) || schemeTag (1 byte) || canonicalPayload`. `classTag` identifies
+ the verifier category; `schemeTag` identifies the cryptographic scheme within that
+ category (a new scheme registers a new `schemeTag`, not a new `classTag`);
+ `canonicalPayload`'s length and encoding are normatively fixed per
+ `(classTag, schemeTag)` pair. On Ethereum: `classTag = 0x01, schemeTag = 0x00`,
+ payload = `abi.encodePacked(address(this))` (20 bytes). A detached consumer decodes
+ `instanceId[0:2]` and knows how to parse the remainder — no external registry, no
+ lookup against `agentProofProfile` needed just to learn the shape.
+ `agentProofProfile` keeps its original, narrower job — distinguishing deployments
+ *within* a fixed (class, scheme) pair (circuit version, enclave measurement) — and
+ is no longer asked to also carry class information it was never defined to carry.
For chainId, pinning the exact bytes rather than leaving the CAIP-2 encoding implicit:
- **`chainId: bytes`** — identifies the chain. On Ethereum: `abi.encode(block.chainid)`.
+ **`chainId: bytes`** — identifies the chain as the UTF-8 byte encoding of its CAIP-2
+ identifier (`namespace:reference`, e.g. `eip155:1` for Ethereum mainnet). On Ethereum:
+ `bytes(string.concat("eip155:", Strings.toString(block.chainid)))`.
Flagging plainly since you raised it: this is a digest-encoding change, not a documentation clarification. chainId moves from abi.encode(block.chainid) (32-byte, zero-padded uint256) to UTF-8 CAIP-2 bytes (variable-length ASCII) — the bytes ABI type stays the same, but every digest computed after adopting CAIP-2 encoding is a different value than one computed today under the current wording. Should land as one atomic edit with a version note, not a silent follow-up that breaks digests computed against the current draft text.
Reference snippet for both together:
library InstanceId {
function encodeEthereumContract(address verifier) internal pure returns (bytes memory) {
return abi.encodePacked(bytes1(0x01), bytes1(0x00), verifier);
}
}
library ChainId {
function encodeCAIP2Eth(uint256 chainid) internal pure returns (bytes memory) {
return bytes(string.concat("eip155:", Strings.toString(chainid)));
}
}
Happy to open this as an actual PR against the draft if that’s easier to review than a forum diff.