Some checks failed
contracts-ci / Install (lockfile) → compile → full test suite (push) Has been cancelled
contracts-ci / Ethereum interop (EIP-2537 BLS, prague hardfork) (push) Has been cancelled
contracts-ci / PQC known-answer tests (NIST vectors) (push) Has been cancelled
contracts-ci / Coverage (scoped, with artifacts) (push) Has been cancelled
The published line and the local line had no common ancestor: the public one carried the redaction pass, the local one carried three weeks of corrections that never shipped. This commit ports the local work onto the public line, keeps every public redaction, and extends the same discretion to seven client mentions that were still named in published comments. Carried: LICENSE year and LICENSING.md; the measured burn figures replacing the deflation claim (the vault holds ~0.137 AERE of 2.8 billion, and burn is a share of validator coinbase revenue, which is zero today); 'audited' removed from next to Bouncy Castle; citation paths rewritten to published form with CITATIONS-UNRESOLVED.md remeasured 2026-08-11; VERIFY-POLICY.md; slashing and ownership comments brought down to what the code does; the AerePyth repair; the shutter test helper the tests cite; runnable package.json entries; the CI file split into a GitHub/Gitea twin pair with a real measured test-run status; and the .gitignore hardening written after a compiled artifact leaked a local path in a sibling repository. A false '2-of-3 multisig' description of the owner account is corrected to what the chain measures: an externally owned account. The self-audit findings catalog stays unpublished pending an explicit decision.
230 lines
8.6 KiB
Solidity
230 lines
8.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import {ISP1Verifier} from "./ISP1Verifier.sol";
|
|
import {IRiscZeroVerifier, Receipt as R0Receipt, ReceiptClaim, ReceiptClaimLib, ExitCode, SystemExitCode, Output, OutputLib, SystemState, SystemStateLib} from "./risczero/IRiscZeroVerifier.sol";
|
|
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
|
|
/// @title AereProofRegistry — multi-prover zk proof registry on AERE chain 2800
|
|
/// @notice Verifies proofs from SP1 (Succinct Labs) and RISC Zero zkVM, then records each
|
|
/// successful verification as a permanent on-chain attestation.
|
|
///
|
|
/// Architecture:
|
|
/// - Proofs route through the SP1VerifierGateway and RiscZeroVerifierRouter.
|
|
/// That means future prover versions (SP1 v7, RISC Zero v2.0+) plug in by
|
|
/// calling addRoute on the upstream gateway — no migration of this registry.
|
|
/// - Programs (a vkey for SP1 / imageId for RISC Zero) must be registered before
|
|
/// proofs of them can be recorded. Registration is permissionless: anyone can
|
|
/// register their own program with a human-readable name + URL. The Foundation
|
|
/// can disable malicious programs but cannot disable legitimate ones.
|
|
/// - Proof submission emits ProofVerified with the program key indexed, so
|
|
/// off-chain consumers (partner KYC, dApp loyalty, exchange withdrawal proofs)
|
|
/// can subscribe to events filtered by their program of interest.
|
|
///
|
|
/// The verifyXOnly view functions provide free verification without recording,
|
|
/// for dApps that need the verdict but not a permanent attestation.
|
|
contract AereProofRegistry is Ownable {
|
|
using ReceiptClaimLib for ReceiptClaim;
|
|
|
|
/// @notice The SP1 verifier gateway (routes by selector → concrete SP1 verifier version)
|
|
address public immutable sp1Gateway;
|
|
|
|
/// @notice The RISC Zero verifier router (routes by selector → concrete R0 verifier)
|
|
address public immutable risc0Router;
|
|
|
|
/// @notice Program registry — vkey (SP1) or imageId (R0) → Program metadata
|
|
enum ProverSystem { SP1, RiscZero }
|
|
|
|
struct Program {
|
|
bool enabled;
|
|
ProverSystem system;
|
|
string name;
|
|
string url;
|
|
address registrant;
|
|
uint64 registeredAt;
|
|
}
|
|
mapping(bytes32 => Program) public programs;
|
|
bytes32[] public programKeys;
|
|
|
|
/// @notice Permanent proof record
|
|
struct ProofRecord {
|
|
address submitter;
|
|
uint64 timestamp;
|
|
uint64 blockNumber;
|
|
bytes32 programKey; // vkey or imageId
|
|
bytes32 publicValuesDigest; // sha256 of public values / journalDigest
|
|
string description;
|
|
ProverSystem system;
|
|
}
|
|
mapping(uint256 => ProofRecord) public proofs;
|
|
uint256 public proofCount;
|
|
|
|
mapping(bytes32 => uint256[]) public programProofs;
|
|
mapping(address => uint256[]) public submitterProofs;
|
|
|
|
event ProgramRegistered(
|
|
bytes32 indexed programKey,
|
|
ProverSystem indexed system,
|
|
address indexed registrant,
|
|
string name,
|
|
string url
|
|
);
|
|
event ProgramDisabled(bytes32 indexed programKey, ProverSystem indexed system);
|
|
event ProofVerified(
|
|
uint256 indexed id,
|
|
address indexed submitter,
|
|
bytes32 indexed programKey,
|
|
ProverSystem system,
|
|
bytes32 publicValuesDigest,
|
|
string description,
|
|
uint64 timestamp,
|
|
uint64 blockNumber
|
|
);
|
|
|
|
error ProgramAlreadyRegistered();
|
|
error ProgramNotApproved();
|
|
error EmptyName();
|
|
error EmptyDescription();
|
|
|
|
constructor(address _sp1Gateway, address _risc0Router) Ownable() {
|
|
require(_sp1Gateway != address(0), "AereProofRegistry: sp1Gateway=0");
|
|
require(_risc0Router != address(0), "AereProofRegistry: risc0Router=0");
|
|
sp1Gateway = _sp1Gateway;
|
|
risc0Router = _risc0Router;
|
|
}
|
|
|
|
// ─── Program registration (permissionless) ────────────────────────────────
|
|
|
|
function registerSP1Program(
|
|
bytes32 vkey,
|
|
string calldata name,
|
|
string calldata url
|
|
) external {
|
|
_register(vkey, ProverSystem.SP1, name, url);
|
|
}
|
|
|
|
function registerRiscZeroProgram(
|
|
bytes32 imageId,
|
|
string calldata name,
|
|
string calldata url
|
|
) external {
|
|
_register(imageId, ProverSystem.RiscZero, name, url);
|
|
}
|
|
|
|
function _register(
|
|
bytes32 key,
|
|
ProverSystem system,
|
|
string calldata name,
|
|
string calldata url
|
|
) internal {
|
|
if (bytes(name).length == 0) revert EmptyName();
|
|
if (programs[key].registeredAt != 0) revert ProgramAlreadyRegistered();
|
|
programs[key] = Program({
|
|
enabled: true,
|
|
system: system,
|
|
name: name,
|
|
url: url,
|
|
registrant: msg.sender,
|
|
registeredAt: uint64(block.timestamp)
|
|
});
|
|
programKeys.push(key);
|
|
emit ProgramRegistered(key, system, msg.sender, name, url);
|
|
}
|
|
|
|
/// @notice Owner (Foundation) can disable a program known to be malicious or compromised.
|
|
/// This does not retroactively invalidate already-recorded proofs.
|
|
function disableProgram(bytes32 key) external onlyOwner {
|
|
Program storage p = programs[key];
|
|
require(p.registeredAt != 0, "not registered");
|
|
require(p.enabled, "already disabled");
|
|
p.enabled = false;
|
|
emit ProgramDisabled(key, p.system);
|
|
}
|
|
|
|
// ─── Proof submission (permissionless once program registered) ────────────
|
|
|
|
function submitSP1Proof(
|
|
bytes32 vkey,
|
|
bytes calldata publicValues,
|
|
bytes calldata proofBytes,
|
|
string calldata description
|
|
) external returns (uint256 id) {
|
|
Program storage p = programs[vkey];
|
|
if (!p.enabled || p.system != ProverSystem.SP1) revert ProgramNotApproved();
|
|
if (bytes(description).length == 0) revert EmptyDescription();
|
|
|
|
ISP1Verifier(sp1Gateway).verifyProof(vkey, publicValues, proofBytes);
|
|
id = _record(vkey, sha256(publicValues), description, ProverSystem.SP1);
|
|
}
|
|
|
|
function submitRiscZeroProof(
|
|
bytes32 imageId,
|
|
bytes32 journalDigest,
|
|
bytes calldata seal,
|
|
string calldata description
|
|
) external returns (uint256 id) {
|
|
Program storage p = programs[imageId];
|
|
if (!p.enabled || p.system != ProverSystem.RiscZero) revert ProgramNotApproved();
|
|
if (bytes(description).length == 0) revert EmptyDescription();
|
|
|
|
IRiscZeroVerifier(risc0Router).verify(seal, imageId, journalDigest);
|
|
id = _record(imageId, journalDigest, description, ProverSystem.RiscZero);
|
|
}
|
|
|
|
function _record(
|
|
bytes32 key,
|
|
bytes32 pvDigest,
|
|
string calldata description,
|
|
ProverSystem system
|
|
) internal returns (uint256 id) {
|
|
id = proofCount++;
|
|
proofs[id] = ProofRecord({
|
|
submitter: msg.sender,
|
|
timestamp: uint64(block.timestamp),
|
|
blockNumber: uint64(block.number),
|
|
programKey: key,
|
|
publicValuesDigest: pvDigest,
|
|
description: description,
|
|
system: system
|
|
});
|
|
programProofs[key].push(id);
|
|
submitterProofs[msg.sender].push(id);
|
|
emit ProofVerified(
|
|
id, msg.sender, key, system, pvDigest, description,
|
|
uint64(block.timestamp), uint64(block.number)
|
|
);
|
|
}
|
|
|
|
// ─── Free verification (view, no record) ──────────────────────────────────
|
|
|
|
function verifySP1Only(
|
|
bytes32 vkey,
|
|
bytes calldata publicValues,
|
|
bytes calldata proofBytes
|
|
) external view {
|
|
ISP1Verifier(sp1Gateway).verifyProof(vkey, publicValues, proofBytes);
|
|
}
|
|
|
|
function verifyRiscZeroOnly(
|
|
bytes32 imageId,
|
|
bytes32 journalDigest,
|
|
bytes calldata seal
|
|
) external view {
|
|
IRiscZeroVerifier(risc0Router).verify(seal, imageId, journalDigest);
|
|
}
|
|
|
|
// ─── Views ────────────────────────────────────────────────────────────────
|
|
|
|
function programCount() external view returns (uint256) {
|
|
return programKeys.length;
|
|
}
|
|
|
|
function submitterProofCount(address submitter) external view returns (uint256) {
|
|
return submitterProofs[submitter].length;
|
|
}
|
|
|
|
function programProofCount(bytes32 key) external view returns (uint256) {
|
|
return programProofs[key].length;
|
|
}
|
|
}
|