Skip to content

Commit 8a79a33

Browse files
authored
feat: Elastic verifier, that allows owner to set multiple VKs (#1599)
1 parent 9495316 commit 8a79a33

File tree

15 files changed

+91
-39
lines changed

15 files changed

+91
-39
lines changed

l1-contracts/contracts/state-transition/chain-deps/GatewayCTMDeployer.sol

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ contract GatewayCTMDeployer {
188188
_isZKsyncOS: _config.isZKsyncOS,
189189
_deployedContracts: contracts
190190
});
191-
_deployVerifier(salt, _config.testnetVerifier, contracts);
191+
_deployVerifier(salt, _config.testnetVerifier, contracts, _config.aliasedGovernanceAddress);
192192

193193
_deployProxyAdmin(salt, _config.aliasedGovernanceAddress, contracts);
194194

@@ -299,23 +299,25 @@ contract GatewayCTMDeployer {
299299
/// @param _salt Salt used for CREATE2 deployments.
300300
/// @param _testnetVerifier Whether testnet verifier should be used.
301301
/// @param _deployedContracts The struct with deployed contracts, that will be mofiied
302+
/// @param _verifierOwner The owner that can add additional verification keys.
302303
/// in the process of the execution of this function.
303304
function _deployVerifier(
304305
bytes32 _salt,
305306
bool _testnetVerifier,
306-
DeployedContracts memory _deployedContracts
307+
DeployedContracts memory _deployedContracts,
308+
address _verifierOwner
307309
) internal {
308310
L1VerifierFflonk fflonkVerifier = new L1VerifierFflonk{salt: _salt}();
309311
_deployedContracts.stateTransition.verifierFflonk = address(fflonkVerifier);
310312
L1VerifierPlonk verifierPlonk = new L1VerifierPlonk{salt: _salt}();
311313
_deployedContracts.stateTransition.verifierPlonk = address(verifierPlonk);
312314
if (_testnetVerifier) {
313315
_deployedContracts.stateTransition.verifier = address(
314-
new TestnetVerifier{salt: _salt}(fflonkVerifier, verifierPlonk)
316+
new TestnetVerifier{salt: _salt}(fflonkVerifier, verifierPlonk, _verifierOwner)
315317
);
316318
} else {
317319
_deployedContracts.stateTransition.verifier = address(
318-
new DualVerifier{salt: _salt}(fflonkVerifier, verifierPlonk)
320+
new DualVerifier{salt: _salt}(fflonkVerifier, verifierPlonk, _verifierOwner)
319321
);
320322
}
321323
}

l1-contracts/contracts/state-transition/verifiers/DualVerifier.sol

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ error UnsupportedChainIdForMockVerifier();
2222
/// contract, while abusing on of the fields (`_recursiveAggregationInput`) for proof verification type. The contract is
2323
/// needed for the smooth transition from PLONK based verifier to the FFLONK verifier.
2424
contract DualVerifier is IVerifier {
25-
/// @notice The latest FFLONK verifier contract.
26-
IVerifierV2 public immutable FFLONK_VERIFIER;
27-
28-
/// @notice PLONK verifier contract.
29-
IVerifier public immutable PLONK_VERIFIER;
30-
3125
/// @notice Type of verification for FFLONK verifier.
3226
uint256 internal constant FFLONK_VERIFICATION_TYPE = 0;
3327

@@ -39,11 +33,31 @@ contract DualVerifier is IVerifier {
3933
// @notice This is test only verifier (mock), and must be removed before prod.
4034
uint256 internal constant OHBENDER_MOCK_VERIFICATION_TYPE = 3;
4135

36+
address public ctmOwner;
37+
38+
mapping(uint32 => IVerifierV2) public fflonkVerifiers;
39+
mapping(uint32 => IVerifier) public plonkVerifiers;
40+
4241
/// @param _fflonkVerifier The address of the FFLONK verifier contract.
4342
/// @param _plonkVerifier The address of the PLONK verifier contract.
44-
constructor(IVerifierV2 _fflonkVerifier, IVerifier _plonkVerifier) {
45-
FFLONK_VERIFIER = _fflonkVerifier;
46-
PLONK_VERIFIER = _plonkVerifier;
43+
/// @param _ctmOwner The address of the contract owner, who can add or remove verifiers.
44+
constructor(IVerifierV2 _fflonkVerifier, IVerifier _plonkVerifier, address _ctmOwner) {
45+
ctmOwner = _ctmOwner;
46+
fflonkVerifiers[0] = _fflonkVerifier;
47+
plonkVerifiers[0] = _plonkVerifier;
48+
}
49+
50+
function addVerifier(uint32 version, IVerifierV2 _fflonkVerifier, IVerifier _plonkVerifier) external {
51+
require(msg.sender == ctmOwner, "Only ctmOwner can add verifiers");
52+
// Add logic to add verifiers
53+
fflonkVerifiers[version] = _fflonkVerifier;
54+
plonkVerifiers[version] = _plonkVerifier;
55+
}
56+
57+
function removeVerifier(uint32 version) external {
58+
require(msg.sender == ctmOwner, "Only ctmOwner can remove verifiers");
59+
delete fflonkVerifiers[version];
60+
delete plonkVerifiers[version];
4761
}
4862

4963
/// @notice Routes zk-SNARK proof verification to the appropriate verifier (FFLONK or PLONK) based on the proof type.
@@ -61,16 +75,23 @@ contract DualVerifier is IVerifier {
6175
}
6276

6377
// The first element of `_proof` determines the verifier type (either FFLONK or PLONK).
64-
uint256 verifierType = _proof[0];
78+
uint256 verifierType = _proof[0] & 255;
79+
uint32 verifierVersion = uint32(_proof[0] >> 8);
80+
require(
81+
fflonkVerifiers[verifierVersion] != IVerifierV2(address(0)) ||
82+
plonkVerifiers[verifierVersion] != IVerifier(address(0)),
83+
"Unknown verifier version"
84+
);
85+
6586
if (verifierType == FFLONK_VERIFICATION_TYPE) {
66-
return FFLONK_VERIFIER.verify(_publicInputs, _extractProof(_proof));
87+
return fflonkVerifiers[verifierVersion].verify(_publicInputs, _extractProof(_proof));
6788
} else if (verifierType == PLONK_VERIFICATION_TYPE) {
68-
return PLONK_VERIFIER.verify(_publicInputs, _extractProof(_proof));
89+
return plonkVerifiers[verifierVersion].verify(_publicInputs, _extractProof(_proof));
6990
} else if (verifierType == OHBENDER_PLONK_VERIFICATION_TYPE) {
7091
uint256[] memory args = new uint256[](1);
7192
args[0] = computeOhBenderHash(_proof[1], _publicInputs);
7293

73-
return PLONK_VERIFIER.verify(args, _extractOhBenderProof(_proof));
94+
return plonkVerifiers[verifierVersion].verify(args, _extractOhBenderProof(_proof));
7495
} else if (verifierType == OHBENDER_MOCK_VERIFICATION_TYPE) {
7596
// just for safety - only allowing default anvil chain and sepolia testnet
7697
if (block.chainid != 31337 && block.chainid != 11155111) {
@@ -104,16 +125,25 @@ contract DualVerifier is IVerifier {
104125
/// @inheritdoc IVerifier
105126
/// @dev Used for backward compatibility with older Verifier implementation. Returns PLONK verification key hash.
106127
function verificationKeyHash() external view returns (bytes32) {
107-
return PLONK_VERIFIER.verificationKeyHash();
128+
return plonkVerifiers[0].verificationKeyHash();
108129
}
109130

110131
/// @notice Calculates a keccak256 hash of the runtime loaded verification keys from the selected verifier.
111132
/// @return The keccak256 hash of the loaded verification keys based on the verifier.
112133
function verificationKeyHash(uint256 _verifierType) external view returns (bytes32) {
113-
if (_verifierType == FFLONK_VERIFICATION_TYPE) {
114-
return FFLONK_VERIFIER.verificationKeyHash();
115-
} else if (_verifierType == PLONK_VERIFICATION_TYPE) {
116-
return PLONK_VERIFIER.verificationKeyHash();
134+
uint256 verifierType = _verifierType & 255;
135+
uint32 verifierVersion = uint32(verifierType >> 8);
136+
137+
require(
138+
fflonkVerifiers[verifierVersion] != IVerifierV2(address(0)) ||
139+
plonkVerifiers[verifierVersion] != IVerifier(address(0)),
140+
"Unknown verifier version"
141+
);
142+
143+
if (verifierType == FFLONK_VERIFICATION_TYPE) {
144+
return fflonkVerifiers[verifierVersion].verificationKeyHash();
145+
} else if (verifierType == PLONK_VERIFICATION_TYPE) {
146+
return plonkVerifiers[verifierVersion].verificationKeyHash();
117147
}
118148
// If the verifier type is unknown, revert with an error.
119149
else {

l1-contracts/contracts/state-transition/verifiers/TestnetVerifier.sol

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,20 @@ import {IVerifier} from "../chain-interfaces/IVerifier.sol";
1313
/// If the proof is not empty, it will verify it using the main verifier contract,
1414
/// otherwise, it will skip the verification.
1515
contract TestnetVerifier is DualVerifier {
16-
constructor(IVerifierV2 _fflonkVerifier, IVerifier _plonkVerifier) DualVerifier(_fflonkVerifier, _plonkVerifier) {
16+
constructor(
17+
IVerifierV2 _fflonkVerifier,
18+
IVerifier _plonkVerifier,
19+
address _ctmOwner
20+
) DualVerifier(_fflonkVerifier, _plonkVerifier, _ctmOwner) {
1721
assert(block.chainid != 1);
1822
}
1923

2024
/// @dev Verifies a zk-SNARK proof, skipping the verification if the proof is empty.
2125
/// @inheritdoc IVerifier
22-
function verify(uint256[] calldata _publicInputs, uint256[] calldata _proof) public view override returns (bool) {
26+
function verify(
27+
uint256[] calldata _publicInputs,
28+
uint256[] calldata _proof
29+
) public view override returns (bool) {
2330
// We allow skipping the zkp verification for the test(net) environment
2431
// If the proof is not empty, verify it, otherwise, skip the verification
2532
if (_proof.length == 0) {

l1-contracts/deploy-scripts/DeployUtils.s.sol

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,14 @@ abstract contract DeployUtils is Create2FactoryUtils {
416416
} else if (compareStrings(contractName, "DummyAvailBridge")) {
417417
return abi.encode();
418418
} else if (compareStrings(contractName, "Verifier")) {
419-
return abi.encode(addresses.stateTransition.verifierFflonk, addresses.stateTransition.verifierPlonk);
419+
/// TODO: Currently setting it to owner address (which means whole bridgehub owner).
420+
/// In practice we might want to set it to CTM owner (which in production will be less restritive).
421+
return
422+
abi.encode(
423+
addresses.stateTransition.verifierFflonk,
424+
addresses.stateTransition.verifierPlonk,
425+
config.ownerAddress
426+
);
420427
} else if (compareStrings(contractName, "VerifierFflonk")) {
421428
return abi.encode();
422429
} else if (compareStrings(contractName, "VerifierPlonk")) {

l1-contracts/deploy-scripts/gateway/GatewayCTMDeployerHelper.sol

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ library GatewayCTMDeployerHelper {
6262
contracts,
6363
innerConfig
6464
);
65-
contracts = _deployVerifier(config.testnetVerifier, contracts, innerConfig);
65+
contracts = _deployVerifier(config.testnetVerifier, contracts, innerConfig, config.aliasedGovernanceAddress);
6666

6767
contracts.stateTransition.validatorTimelockImplementation = _deployInternal(
6868
"ValidatorTimelock",
@@ -189,15 +189,16 @@ library GatewayCTMDeployerHelper {
189189
function _deployVerifier(
190190
bool _testnetVerifier,
191191
DeployedContracts memory _deployedContracts,
192-
InnerDeployConfig memory innerConfig
192+
InnerDeployConfig memory innerConfig,
193+
address _verifierOwner
193194
) internal returns (DeployedContracts memory) {
194195
address verifierFflonk = _deployInternal("L1VerifierFflonk", "L1VerifierFflonk.sol", hex"", innerConfig);
195196
address verifierPlonk = _deployInternal("L1VerifierPlonk", "L1VerifierPlonk.sol", hex"", innerConfig);
196197

197198
_deployedContracts.stateTransition.verifierFflonk = verifierFflonk;
198199
_deployedContracts.stateTransition.verifierPlonk = verifierPlonk;
199200

200-
bytes memory constructorParams = abi.encode(verifierFflonk, verifierPlonk);
201+
bytes memory constructorParams = abi.encode(verifierFflonk, verifierPlonk, _verifierOwner);
201202

202203
if (_testnetVerifier) {
203204
_deployedContracts.stateTransition.verifier = _deployInternal(

l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ contract TestExecutorFacet is ExecutorFacet {
4747
contract ExecutorProofTest is Test {
4848
UtilsFacet internal utilsFacet;
4949
TestExecutorFacet internal executor;
50-
address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0))));
50+
address internal testnetVerifier =
51+
address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)), address(0)));
5152

5253
function getTestExecutorFacetSelectors() private pure returns (bytes4[] memory) {
5354
bytes4[] memory selectors = new bytes4[](3);

l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,11 @@ contract ExecutorTest is Test {
224224
timestamp: 0,
225225
commitment: bytes32("")
226226
});
227-
TestnetVerifier testnetVerifier = new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)));
227+
TestnetVerifier testnetVerifier = new TestnetVerifier(
228+
IVerifierV2(address(0)),
229+
IVerifier(address(0)),
230+
address(0)
231+
);
228232

229233
InitializeData memory params = InitializeData({
230234
// TODO REVIEW

l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ contract ChainTypeManagerTest is Test {
5353
address internal constant serverNotifier = address(0x7070707);
5454
address internal newChainAdmin;
5555
uint256 chainId = 112;
56-
address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0))));
56+
address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)), address(0)));
5757
bytes internal forceDeploymentsData = hex"";
5858

5959
uint256 eraChainId = 9;

l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/_DiamondInit_Shared.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.s
1313

1414
contract DiamondInitTest is Test {
1515
Diamond.FacetCut[] internal facetCuts;
16-
address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0))));
16+
address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)), address(0)));
1717

1818
function setUp() public virtual {
1919
facetCuts.push(

l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondProxy/DiamondProxy.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ contract TestFacet is ZKChainBase {
2727

2828
contract DiamondProxyTest is Test {
2929
Diamond.FacetCut[] internal facetCuts;
30-
address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0))));
30+
address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)), address(0)));
3131

3232
function getTestFacetSelectors() public pure returns (bytes4[] memory selectors) {
3333
selectors = new bytes4[](1);

0 commit comments

Comments
 (0)