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.
185 lines
8.0 KiB
Solidity
185 lines
8.0 KiB
Solidity
// Copyright 2024 RISC Zero, Inc.
|
|
//
|
|
// The RiscZeroGroth16Verifier is a free software: you can redistribute it
|
|
// and/or modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the License,
|
|
// or (at your option) any later version.
|
|
//
|
|
// The RiscZeroGroth16Verifier is distributed in the hope that it will be
|
|
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
// Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along with
|
|
// the RiscZeroGroth16Verifier. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0
|
|
|
|
pragma solidity ^0.8.9;
|
|
|
|
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
|
|
import {ControlID} from "./ControlID.sol";
|
|
import {Groth16Verifier} from "./Groth16Verifier.sol";
|
|
import {
|
|
ExitCode,
|
|
IRiscZeroVerifier,
|
|
Output,
|
|
OutputLib,
|
|
Receipt,
|
|
ReceiptClaim,
|
|
ReceiptClaimLib,
|
|
SystemExitCode,
|
|
VerificationFailed
|
|
} from "./IRiscZeroVerifier.sol";
|
|
import {StructHash} from "./StructHash.sol";
|
|
import {reverseByteOrderUint256, reverseByteOrderUint32} from "./Util.sol";
|
|
import {IRiscZeroSelectable} from "./IRiscZeroSelectable.sol";
|
|
|
|
/// @notice A Groth16 seal over the claimed receipt claim.
|
|
struct Seal {
|
|
uint256[2] a;
|
|
uint256[2][2] b;
|
|
uint256[2] c;
|
|
}
|
|
|
|
/// @notice Error raised when this verifier receives a receipt with a selector that does not match
|
|
/// its own. The selector value is calculated from the verifier parameters, and so this
|
|
/// usually indicates a mismatch between the version of the prover and this verifier.
|
|
error SelectorMismatch(bytes4 received, bytes4 expected);
|
|
|
|
/// @notice Groth16 verifier contract for RISC Zero receipts of execution.
|
|
contract RiscZeroGroth16Verifier is IRiscZeroVerifier, IRiscZeroSelectable, Groth16Verifier {
|
|
using ReceiptClaimLib for ReceiptClaim;
|
|
using OutputLib for Output;
|
|
using SafeCast for uint256;
|
|
|
|
/// @notice Semantic version of the RISC Zero system of which this contract is part.
|
|
/// @dev This is set to be equal to the version of the risc0-zkvm crate.
|
|
string public constant VERSION = "5.0.0-rc.1";
|
|
|
|
/// @notice Control root hash binding the set of circuits in the RISC Zero system.
|
|
/// @dev This value controls what set of recursion programs (e.g. lift, join, resolve), and
|
|
/// therefore what version of the zkVM circuit, will be accepted by this contract. Each
|
|
/// instance of this verifier contract will accept a single release of the RISC Zero circuits.
|
|
///
|
|
/// New releases of RISC Zero's zkVM require updating these values. These values can be
|
|
/// calculated from the [risc0 monorepo][1] using: `cargo xtask bootstrap`.
|
|
///
|
|
/// [1]: https://github.com/risc0/risc0
|
|
bytes16 public immutable CONTROL_ROOT_0;
|
|
bytes16 public immutable CONTROL_ROOT_1;
|
|
bytes32 public immutable BN254_CONTROL_ID;
|
|
|
|
/// @notice A short key attached to the seal to select the correct verifier implementation.
|
|
/// @dev The selector is taken from the hash of the verifier parameters including the Groth16
|
|
/// verification key and the control IDs that commit to the RISC Zero circuits. If two
|
|
/// receipts have different selectors (i.e. different verifier parameters), then it can
|
|
/// generally be assumed that they need distinct verifier implementations. This is used as
|
|
/// part of the RISC Zero versioning mechanism.
|
|
///
|
|
/// A selector is not intended to be collision resistant, in that it is possible to find
|
|
/// two preimages that result in the same selector. This is acceptable since it's purpose
|
|
/// to a route a request among a set of trusted verifiers, and to make errors of sending a
|
|
/// receipt to a mismatching verifiers easier to debug. It is analogous to the ABI
|
|
/// function selectors.
|
|
bytes4 public immutable SELECTOR;
|
|
|
|
/// @notice Identifier for the Groth16 verification key encoded into the base contract.
|
|
/// @dev This value is computed at compile time.
|
|
function verifier_key_digest() internal pure returns (bytes32) {
|
|
bytes32[] memory ic_digests = new bytes32[](6);
|
|
ic_digests[0] = sha256(abi.encodePacked(IC0x, IC0y));
|
|
ic_digests[1] = sha256(abi.encodePacked(IC1x, IC1y));
|
|
ic_digests[2] = sha256(abi.encodePacked(IC2x, IC2y));
|
|
ic_digests[3] = sha256(abi.encodePacked(IC3x, IC3y));
|
|
ic_digests[4] = sha256(abi.encodePacked(IC4x, IC4y));
|
|
ic_digests[5] = sha256(abi.encodePacked(IC5x, IC5y));
|
|
|
|
return sha256(
|
|
abi.encodePacked(
|
|
// tag
|
|
sha256("risc0_groth16.VerifyingKey"),
|
|
// down
|
|
sha256(abi.encodePacked(alphax, alphay)),
|
|
sha256(abi.encodePacked(betax1, betax2, betay1, betay2)),
|
|
sha256(abi.encodePacked(gammax1, gammax2, gammay1, gammay2)),
|
|
sha256(abi.encodePacked(deltax1, deltax2, deltay1, deltay2)),
|
|
StructHash.taggedList(sha256("risc0_groth16.VerifyingKey.IC"), ic_digests),
|
|
// down length
|
|
uint16(5) << 8
|
|
)
|
|
);
|
|
}
|
|
|
|
constructor(bytes32 control_root, bytes32 bn254_control_id) {
|
|
(CONTROL_ROOT_0, CONTROL_ROOT_1) = splitDigest(control_root);
|
|
BN254_CONTROL_ID = bn254_control_id;
|
|
|
|
SELECTOR = bytes4(
|
|
sha256(
|
|
abi.encodePacked(
|
|
// tag
|
|
sha256("risc0.Groth16ReceiptVerifierParameters"),
|
|
// down
|
|
control_root,
|
|
reverseByteOrderUint256(uint256(bn254_control_id)),
|
|
verifier_key_digest(),
|
|
// down length
|
|
uint16(3) << 8
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
/// @notice splits a digest into two 128-bit halves to use as public signal inputs.
|
|
/// @dev RISC Zero's Circom verifier circuit takes each of two hash digests in two 128-bit
|
|
/// chunks. These values can be derived from the digest by splitting the digest in half and
|
|
/// then reversing the bytes of each.
|
|
function splitDigest(bytes32 digest) internal pure returns (bytes16, bytes16) {
|
|
uint256 reversed = reverseByteOrderUint256(uint256(digest));
|
|
return (bytes16(uint128(reversed)), bytes16(uint128(reversed >> 128)));
|
|
}
|
|
|
|
/// @inheritdoc IRiscZeroVerifier
|
|
function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) external view {
|
|
_verifyIntegrity(seal, ReceiptClaimLib.ok(imageId, journalDigest).digest());
|
|
}
|
|
|
|
/// @inheritdoc IRiscZeroVerifier
|
|
function verifyIntegrity(Receipt calldata receipt) external view {
|
|
return _verifyIntegrity(receipt.seal, receipt.claimDigest);
|
|
}
|
|
|
|
/// @notice internal implementation of verifyIntegrity, factored to avoid copying calldata bytes to memory.
|
|
function _verifyIntegrity(bytes calldata seal, bytes32 claimDigest) internal view {
|
|
// Check that the seal has a matching selector. Mismatch generally indicates that the
|
|
// prover and this verifier are using different parameters, and so the verification
|
|
// will not succeed.
|
|
if (SELECTOR != bytes4(seal[:4])) {
|
|
revert SelectorMismatch({received: bytes4(seal[:4]), expected: SELECTOR});
|
|
}
|
|
|
|
// Run the Groth16 verify procedure.
|
|
(bytes16 claim0, bytes16 claim1) = splitDigest(claimDigest);
|
|
Seal memory decodedSeal = abi.decode(seal[4:], (Seal));
|
|
bool verified = this.verifyProof(
|
|
decodedSeal.a,
|
|
decodedSeal.b,
|
|
decodedSeal.c,
|
|
[
|
|
uint256(uint128(CONTROL_ROOT_0)),
|
|
uint256(uint128(CONTROL_ROOT_1)),
|
|
uint256(uint128(claim0)),
|
|
uint256(uint128(claim1)),
|
|
uint256(BN254_CONTROL_ID)
|
|
]
|
|
);
|
|
|
|
// Revert is verification failed.
|
|
if (!verified) {
|
|
revert VerificationFailed();
|
|
}
|
|
}
|
|
}
|