-
Notifications
You must be signed in to change notification settings - Fork 396
Expand file tree
/
Copy pathAlignedProofAggregationService.sol
More file actions
208 lines (178 loc) · 9.42 KB
/
AlignedProofAggregationService.sol
File metadata and controls
208 lines (178 loc) · 9.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin-upgrades/contracts/proxy/utils/UUPSUpgradeable.sol";
import {IAlignedProofAggregationService} from "./IAlignedProofAggregationService.sol";
import {ISP1Verifier} from "@sp1-contracts/ISP1Verifier.sol";
import {IRiscZeroVerifier} from "@risc0-contracts/IRiscZeroVerifier.sol";
import {MerkleProof} from "../../lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol";
contract AlignedProofAggregationService is
IAlignedProofAggregationService,
Initializable,
OwnableUpgradeable,
UUPSUpgradeable
{
/// @notice Map the merkle root to a boolean to indicate it was verified
mapping(bytes32 => bool) public aggregatedProofs;
/// @notice The address of the SP1 verifier contract.
/// @dev This can either be a specific SP1Verifier for a specific version, or the
/// SP1VerifierGateway which can be used to verify proofs for any version of SP1.
/// For the list of supported verifiers on each chain, see:
/// https://docs.succinct.xyz/docs/sp1/verification/contract-addresses
address public sp1VerifierAddress;
/// @notice The address of the Wallet that is allowed to call the verify function.
address public alignedAggregatorAddress;
/// @notice The address of the Risc0 verifier contract
/// @dev See supported verifier here:
/// https://dev.risczero.com/api/blockchain-integration/contracts/verifier#contract-addresses
address public risc0VerifierAddress;
/// @notice whether we are in dev mode or not
/// if the sp1 verifier address is set to this address, then we skip verification
address public constant VERIFIER_MOCK_ADDRESS = address(0xFF);
/// @notice A map to track aggregation program IDs (image IDs for RISC Zero or vk hashes for SP1)
/// with their proving system. These program IDs are used to validate that the proofs to verify are indeed from
/// a trusted aggregation program.
mapping(bytes32 => uint8) public programIds;
constructor() {
_disableInitializers();
}
function initialize(
address newOwner,
address _alignedAggregatorAddress,
address _sp1VerifierAddress,
address _risc0VerifierAddress,
bytes32[] memory _programIds,
uint8[] memory _verifierTypes
) public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
_transferOwnership(newOwner);
alignedAggregatorAddress = _alignedAggregatorAddress;
sp1VerifierAddress = _sp1VerifierAddress;
risc0VerifierAddress = _risc0VerifierAddress;
for (uint256 i = 0; i < _programIds.length; i++) {
programIds[_programIds[i]] = _verifierTypes[i];
}
}
function verifySP1(bytes32 blobVersionedHash, bytes calldata sp1PublicValues, bytes calldata sp1ProofBytes, bytes32 programId)
public
onlyAlignedAggregator
{
(bytes32 merkleRoot) = abi.decode(sp1PublicValues, (bytes32));
if (programIds[programId] != uint8(IAlignedProofAggregationService.VerifierType.SP1)) {
revert InvalidProgramId(programId, IAlignedProofAggregationService.VerifierType.SP1, programIds[programId]);
}
// In dev mode, proofs are mocked, so we skip the verification part
if (_isSP1VerificationEnabled()) {
ISP1Verifier(sp1VerifierAddress).verifyProof(programId, sp1PublicValues, sp1ProofBytes);
}
aggregatedProofs[merkleRoot] = true;
emit AggregatedProofVerified(merkleRoot, blobVersionedHash);
}
function verifyRisc0(bytes32 blobVersionedHash, bytes calldata risc0ReceiptSeal, bytes calldata risc0JournalBytes, bytes32 programId)
public
onlyAlignedAggregator
{
(bytes32 merkleRoot) = abi.decode(risc0JournalBytes, (bytes32));
if (programIds[programId] != uint8(IAlignedProofAggregationService.VerifierType.RISC0)) {
revert InvalidProgramId(programId, IAlignedProofAggregationService.VerifierType.RISC0, programIds[programId]);
}
// In dev mode, proofs are mocked, so we skip the verification part
if (_isRisc0VerificationEnabled()) {
bytes32 risc0JournalDigest = sha256(risc0JournalBytes);
IRiscZeroVerifier(risc0VerifierAddress).verify(
risc0ReceiptSeal, programId, risc0JournalDigest
);
}
aggregatedProofs[merkleRoot] = true;
emit AggregatedProofVerified(merkleRoot, blobVersionedHash);
}
/// @notice Verifies the inclusion of proof in an aggregated proof via Merkle tree proof.
///
/// @dev
/// - The `programId` parameter represents the unique identifier for the vm program:
/// - In RISC Zero, this corresponds to the `image_id`.
/// - In SP1, this corresponds to the `vk` (verification key) hash.
/// - The proof commitment is derived by hashing together the `programId` and the `publicInputs`.
/// - The `merklePath` is then used to compute the Merkle root from this commitment.
/// - The function returns `true` if this Merkle root is known to correspond to a valid aggregated proof.
///
/// @param merklePath The Merkle proof (sibling hashes) needed to reconstruct the Merkle root.
/// @param provingSystemId The id of the proving system (1 for SP1, 2 for RISC0).
/// @param programId The identifier for the ZK program (image_id in RISC0 or vk hash in SP1).
/// @param publicInputs The public inputs bytes of the proof.
///
/// @return bool Returns true if the computed Merkle root is a recognized valid aggregated proof.
function verifyProofInclusion(
bytes32[] calldata merklePath,
uint16 provingSystemId,
bytes32 programId,
bytes calldata publicInputs
) public view returns (bool) {
bytes32 proofCommitment = keccak256(abi.encodePacked(provingSystemId, programId, publicInputs));
bytes32 merkleRoot = MerkleProof.processProofCalldata(merklePath, proofCommitment);
return aggregatedProofs[merkleRoot];
}
function _isSP1VerificationEnabled() internal view returns (bool) {
return sp1VerifierAddress != VERIFIER_MOCK_ADDRESS;
}
function _isRisc0VerificationEnabled() internal view returns (bool) {
return risc0VerifierAddress != VERIFIER_MOCK_ADDRESS;
}
function _authorizeUpgrade(address newImplementation)
internal
override
onlyOwner // solhint-disable-next-line no-empty-blocks
{}
modifier onlyAlignedAggregator() {
if (msg.sender != alignedAggregatorAddress) {
revert OnlyAlignedAggregator(msg.sender);
}
_;
}
/// @notice Modifier to ensure the provided verifier type is one of the valid enum values.
modifier onValidVerifierType(IAlignedProofAggregationService.VerifierType verifierType) {
if (verifierType != IAlignedProofAggregationService.VerifierType.SP1 &&
verifierType != IAlignedProofAggregationService.VerifierType.RISC0){
revert IAlignedProofAggregationService.InvalidVerifierType(uint8(verifierType));
}
_;
}
/// @notice Sets the address of the Risc0 verifier contract
/// @param _risc0VerifierAddress The new address for the Risc0 verifier contract
function setRisc0VerifierAddress(address _risc0VerifierAddress) external onlyOwner {
risc0VerifierAddress = _risc0VerifierAddress;
emit Risc0VerifierAddressUpdated(_risc0VerifierAddress);
}
/// @notice Sets the address of the SP1 verifier contract
/// @param _sp1VerifierAddress The new address for the SP1 verifier contract
function setSP1VerifierAddress(address _sp1VerifierAddress) external onlyOwner {
sp1VerifierAddress = _sp1VerifierAddress;
emit SP1VerifierAddressUpdated(_sp1VerifierAddress);
}
/// @notice Adds a new program ID to the list of valid program IDs.
/// @param programId The program ID to add (image ID for RISC0 or vk hash for SP1).
/// @param verifierType The type of verifier associated with the program ID.
function addProgramId(bytes32 programId, IAlignedProofAggregationService.VerifierType verifierType)
external
onlyOwner
onValidVerifierType(verifierType)
{
programIds[programId] = uint8(verifierType);
emit ProgramIdAdded(programId, verifierType);
}
/// @notice Deletes a program ID from the list of valid program IDs.
/// @param programId The program ID to delete (image ID for RISC0 or vk hash for SP1).
function deleteProgramId(bytes32 programId, IAlignedProofAggregationService.VerifierType verifierType) external onlyOwner onValidVerifierType(verifierType) {
// Preserve the verifier type so we can emit it with the event
uint8 verifierTypeRaw = programIds[programId];
uint8 rawReceivedVerifierType = uint8(verifierType);
// Check if the obtained verifier type matches the one received by param
if (verifierTypeRaw != rawReceivedVerifierType) {
revert IAlignedProofAggregationService.VerifierTypeMismatch(verifierTypeRaw, rawReceivedVerifierType);
}
delete programIds[programId];
emit ProgramIdDeleted(programId, IAlignedProofAggregationService.VerifierType(verifierTypeRaw));
}
}