-
Notifications
You must be signed in to change notification settings - Fork 395
Expand file tree
/
Copy pathAlignedProofAggregationService.sol
More file actions
176 lines (151 loc) · 7.85 KB
/
AlignedProofAggregationService.sol
File metadata and controls
176 lines (151 loc) · 7.85 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
// 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);
/// The unique identifier (image ID) of the RISC Zero aggregator program.
/// This ensures that only proofs generated by a trusted Risc0 program can be verified.
bytes32 public risc0AggregatorProgramImageId;
/// The verification key hash for the SP1 aggregator program.
/// This ensures that only proofs generated by a trusted SP1 program can be verified.
bytes32 public sp1AggregatorProgramVKHash;
constructor() {
_disableInitializers();
}
function initialize(
address newOwner,
address _alignedAggregatorAddress,
address _sp1VerifierAddress,
address _risc0VerifierAddress,
bytes32 _risc0AggregatorProgramImageId,
bytes32 _sp1AggregatorProgramVKHash
) public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
_transferOwnership(newOwner);
alignedAggregatorAddress = _alignedAggregatorAddress;
sp1VerifierAddress = _sp1VerifierAddress;
risc0VerifierAddress = _risc0VerifierAddress;
risc0AggregatorProgramImageId = _risc0AggregatorProgramImageId;
sp1AggregatorProgramVKHash = _sp1AggregatorProgramVKHash;
}
function verifySP1(bytes32 blobVersionedHash, bytes calldata sp1PublicValues, bytes calldata sp1ProofBytes)
public
onlyAlignedAggregator
{
(bytes32 merkleRoot) = abi.decode(sp1PublicValues, (bytes32));
// In dev mode, proofs are mocked, so we skip the verification part
if (_isSP1VerificationEnabled()) {
ISP1Verifier(sp1VerifierAddress).verifyProof(sp1AggregatorProgramVKHash, sp1PublicValues, sp1ProofBytes);
}
aggregatedProofs[merkleRoot] = true;
emit AggregatedProofVerified(merkleRoot, blobVersionedHash);
}
function verifyRisc0(bytes32 blobVersionedHash, bytes calldata risc0ReceiptSeal, bytes calldata risc0JournalBytes)
public
onlyAlignedAggregator
{
(bytes32 merkleRoot) = abi.decode(risc0JournalBytes, (bytes32));
// In dev mode, proofs are mocked, so we skip the verification part
if (_isRisc0VerificationEnabled()) {
bytes32 risc0JournalDigest = sha256(risc0JournalBytes);
IRiscZeroVerifier(risc0VerifierAddress).verify(
risc0ReceiptSeal, risc0AggregatorProgramImageId, 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 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, bytes32 programId, bytes calldata publicInputs)
public
view
returns (bool)
{
bytes32 proofCommitment = keccak256(abi.encodePacked(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 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 image id of the Risc0 program
/// @param _risc0AggregatorProgramImageId The new imageid for the Risc0 aggregator program
function setRisc0AggregatorProgramImageId(bytes32 _risc0AggregatorProgramImageId) external onlyOwner {
risc0AggregatorProgramImageId = _risc0AggregatorProgramImageId;
emit Risc0AggregatorProgramImageIdUpdated(_risc0AggregatorProgramImageId);
}
/// @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 Sets the vk hash of the sp1 program
/// @param _sp1AggregatorProgramVKHash The new vk hash for the sp1 aggregator program
function setSP1AggregatorProgramVKHash(bytes32 _sp1AggregatorProgramVKHash) external onlyOwner {
sp1AggregatorProgramVKHash = _sp1AggregatorProgramVKHash;
emit SP1AggregatorProgramVKHashUpdated(_sp1AggregatorProgramVKHash);
}
}