Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
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 (Bank28 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;
|
|
}
|
|
}
|