// SPDX-License-Identifier: MIT pragma solidity 0.8.23; /// @dev The one AerePQCKeyRegistry read used to verify a mandate's post-quantum signature. Live at /// 0x1eCa…3691 on chain 2800; verifyWithKey routes to the native precompile for the key's /// scheme (0x0AE1 Falcon-512) and verifies in full on-chain, fail-closed. interface IAerePQCKeyRegistryMandate { function verifyWithKey(uint256 keyId, bytes32 message, bytes calldata signature) external view returns (bool); function statusOf(uint256 keyId) external view returns (uint8); function schemeOf(uint256 keyId) external view returns (uint8); } /** * @title AereAP2MandateVerifier, a post-quantum x402 / AP2 payment-mandate verifier * * @notice A minimal verifier for AP2-style payment MANDATES whose authorizing signature is * POST-QUANTUM (Falcon-512), verified in full on-chain by Aere Network's LIVE native * precompile (0x0AE1) through the already-deployed AerePQCKeyRegistry. Settlement of the * authorized spend is routed through the EXISTING AERE402 rail (AERE402FacilitatorPQC / * AERE402FacilitatorV2), which is NOT redeployed or modified here. * * WHAT AN AP2 / x402 MANDATE IS. A signed, standing authorization of the form * "agent X may spend up to N of token T per period P for purpose Q". In AP2 (Google) and * x402 (Coinbase) today that authorization is an ECDSA signature, which a quantum computer * forges. Here the authorizing key is a Falcon-512 key in AerePQCKeyRegistry, so the mandate * is quantum-durable. The struct and the verify path are below. * * THE STRUCT. A Mandate binds the paying agent's Falcon key, the token, the spend cap, the * validity window, an opaque purpose tag, and a mandate nonce (so an agent can issue several * independent standing mandates). Its `mandateHash` binds chainId + this contract so a * signed mandate cannot be replayed against another verifier or chain. * * THE VERIFY PATH. * - verifyMandate(m, falconSig): pure on-chain PQC check that the agent's Falcon key * signed `m` (view; anyone can check for free via eth_call). * - authorizeSpend(m, amount, falconSig): the enforced path. Verifies the Falcon mandate * signature via the live precompile, checks the validity window and the cumulative cap, * records the spend against the mandate, and emits MandateAuthorized. Reverts fail-closed * on an invalid / tampered signature, an out-of-window mandate, or a cap overflow. * * SETTLEMENT ROUTING. authorizeSpend records that `amount` is authorized under the mandate; * the actual token movement + 25-bps fee to AereSink is performed by the live AERE402 rail. * To keep the mandate's cumulative cap honest (only real settlements should consume it), * authorizeSpend is gated to an immutable SETTLEMENT_EXECUTOR when one is configured (the * AERE402 facilitator, which calls authorizeSpend atomically with settlement). When * SETTLEMENT_EXECUTOR is the zero address the accounting path is open (for verification and * testing); the pure PQC check verifyMandate is always open. * * [DEPLOY CONSTRAINT, accepted-and-documented risk] A Falcon mandate signature is a STANDING * authorization by design: one signature authorizes repeated spends up to the cap, and the * signature is public. So in the OPEN accounting path (SETTLEMENT_EXECUTOR == address(0)) ANY * address that observes the signature can replay authorizeSpend to consume the mandate's * remaining cap WITHOUT a real settlement (cap-exhaustion griefing; no custody, no theft, since * this contract moves no funds). The mitigation is built in and MUST be used in production: set * SETTLEMENT_EXECUTOR to the AERE402 facilitator so cap consumption only ever happens ALONGSIDE * a real settlement (the gate is enforced in authorizeSpend and proven in the tests). The open * path is intended ONLY for eth_call verification and unit testing and MUST NOT be used for a * production deployment. A per-authorization nonce is deliberately NOT added here because it * would break the intended standing-mandate ("sign once, spend repeatedly up to the cap") * semantics; the executor gate closes the vector without that trade-off. * * @dev [VERIFY: confirm against the finalized x402 / AP2 mandate schema] The x402 and AP2 mandate * field sets are still evolving (AP2 distinguishes Intent vs Cart mandates; x402 carries an * HTTP-402 payment payload). This struct captures the common "spend up to N per period for * purpose Q" authorization; reconcile the exact field set and encoding with the final specs * before claiming wire-level conformance. * * HONEST SCOPE. Application-layer payment authorization only. Does NOT change AERE consensus * (still Besu QBFT classical ECDSA). Holds NO funds (the cap is accounting, not custody; the * AERE402 rail custodies and moves value). No admin. */ contract AereAP2MandateVerifier { /// @notice Falcon-512 is the mandate signing scheme (live precompile 0x0AE1). uint8 public constant SCHEME_FALCON512 = 1; uint8 internal constant REGISTRY_STATUS_ACTIVE = 1; /// @notice Domain separator binding a mandate hash to this verifier. bytes32 public constant MANDATE_DOMAIN = keccak256("AereAP2MandateVerifier.v1.mandate"); /// @notice The LIVE AerePQCKeyRegistry that verifies the Falcon mandate signature via 0x0AE1. IAerePQCKeyRegistryMandate public immutable KEY_REGISTRY; /// @notice The AERE402 settlement executor allowed to consume a mandate's cap. Zero = open path. address public immutable SETTLEMENT_EXECUTOR; /// @notice An AP2 / x402 payment mandate: "agentKey may spend up to maxAmount of token per the /// [periodStart, periodEnd] window for `purpose`". mandateNonce distinguishes an agent's /// several standing mandates. struct Mandate { uint256 agentKeyId; // the paying agent's Falcon-512 key in AerePQCKeyRegistry address token; // the settlement token the cap is denominated in uint256 maxAmount; // cumulative spend cap over the window uint64 periodStart; // unix seconds, inclusive uint64 periodEnd; // unix seconds, inclusive (>= periodStart) bytes32 purpose; // opaque purpose tag, e.g. keccak256("inference:anthropic") uint256 mandateNonce; // per-agent mandate index (lets an agent hold several mandates) } /// @notice mandateHash => cumulative amount already authorized under that mandate. mapping(bytes32 => uint256) public spentUnder; event MandateAuthorized( bytes32 indexed mandateHash, uint256 indexed agentKeyId, address indexed token, uint256 amount, uint256 cumulative, bytes32 purpose ); error ZeroAddress(); error NotSettlementExecutor(); error AgentKeyNotActiveFalcon(uint256 agentKeyId); error InvalidMandateWindow(uint64 periodStart, uint64 periodEnd); error MandateNotYetValid(uint64 periodStart); error MandateExpired(uint64 periodEnd); error ZeroAmount(); error MandateCapExceeded(uint256 cap, uint256 attempted); error MandateSignatureInvalid(); constructor(address keyRegistry_, address settlementExecutor_) { if (keyRegistry_ == address(0)) revert ZeroAddress(); KEY_REGISTRY = IAerePQCKeyRegistryMandate(keyRegistry_); SETTLEMENT_EXECUTOR = settlementExecutor_; // zero allowed = open accounting path } // ========================================================================== // Mandate hash + pure verify // ========================================================================== /// @notice The canonical hash a Falcon mandate signature must sign. Binds chainId + this /// verifier so a mandate cannot be replayed against another verifier or chain. function mandateHash(Mandate calldata m) public view returns (bytes32) { return keccak256( abi.encode( MANDATE_DOMAIN, block.chainid, address(this), m.agentKeyId, m.token, m.maxAmount, m.periodStart, m.periodEnd, m.purpose, m.mandateNonce ) ); } /// @notice Pure on-chain post-quantum check that the agent's Falcon key signed this mandate. /// View, permissionless: anyone can verify a mandate via eth_call for free. Returns /// false (never reverts) if the key is not an ACTIVE Falcon key or the signature is /// invalid. function verifyMandate(Mandate calldata m, bytes calldata falconSig) external view returns (bool) { if (KEY_REGISTRY.statusOf(m.agentKeyId) != REGISTRY_STATUS_ACTIVE) return false; if (KEY_REGISTRY.schemeOf(m.agentKeyId) != SCHEME_FALCON512) return false; return KEY_REGISTRY.verifyWithKey(m.agentKeyId, mandateHash(m), falconSig); } // ========================================================================== // Authorize (enforced path) // ========================================================================== /** * @notice Authorize `amount` of spend under a Falcon-signed mandate. Verifies the post-quantum * mandate signature on-chain via the live precompile, enforces the validity window and * the cumulative cap, records the spend, and emits MandateAuthorized. Fail-closed on any * invalid / tampered signature, an out-of-window mandate, or a cap overflow. * * Gated to SETTLEMENT_EXECUTOR when one is configured, so a mandate's cap is only ever * consumed alongside a real AERE402 settlement (the executor calls this atomically). * PRODUCTION MUST configure SETTLEMENT_EXECUTOR: in the open path (executor == 0) the public * standing signature is replayable by anyone to exhaust the cap (see the contract-level * DEPLOY CONSTRAINT note). The open path is verification / test only. * * @param m the mandate being drawn against. * @param amount the spend to authorize now (charged against the mandate's cumulative cap). * @param falconSig the agent's Falcon-512 signature over mandateHash(m). * @return mh the mandate hash the spend was recorded under. */ function authorizeSpend(Mandate calldata m, uint256 amount, bytes calldata falconSig) external returns (bytes32 mh) { if (SETTLEMENT_EXECUTOR != address(0) && msg.sender != SETTLEMENT_EXECUTOR) revert NotSettlementExecutor(); if (amount == 0) revert ZeroAmount(); // Window checks. if (m.periodEnd < m.periodStart) revert InvalidMandateWindow(m.periodStart, m.periodEnd); if (block.timestamp < m.periodStart) revert MandateNotYetValid(m.periodStart); if (block.timestamp > m.periodEnd) revert MandateExpired(m.periodEnd); // The mandate signer must be an ACTIVE Falcon key (rotating / revoking it halts the mandate). if ( KEY_REGISTRY.statusOf(m.agentKeyId) != REGISTRY_STATUS_ACTIVE || KEY_REGISTRY.schemeOf(m.agentKeyId) != SCHEME_FALCON512 ) revert AgentKeyNotActiveFalcon(m.agentKeyId); mh = mandateHash(m); // Full on-chain post-quantum verification of the mandate authorization. if (!KEY_REGISTRY.verifyWithKey(m.agentKeyId, mh, falconSig)) revert MandateSignatureInvalid(); // Cap check (overflow-safe) then record. uint256 already = spentUnder[mh]; uint256 next = already + amount; if (next < already || next > m.maxAmount) revert MandateCapExceeded(m.maxAmount, next); spentUnder[mh] = next; emit MandateAuthorized(mh, m.agentKeyId, m.token, amount, next, m.purpose); } // ========================================================================== // Views // ========================================================================== /// @notice Remaining spendable amount under a mandate (0 if exhausted). Does NOT check the /// time window or signature; use verifyMandate / authorizeSpend for the full gate. function remainingUnder(Mandate calldata m) external view returns (uint256) { uint256 already = spentUnder[mandateHash(m)]; return already >= m.maxAmount ? 0 : m.maxAmount - already; } /// @notice True iff `m` is within its validity window at the current block time. function isWithinWindow(Mandate calldata m) external view returns (bool) { return block.timestamp >= m.periodStart && block.timestamp <= m.periodEnd && m.periodEnd >= m.periodStart; } }