Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dual verifier #968

Open
wants to merge 16 commits into
base: release-v25-protocol-defense
Choose a base branch
from
Open
9 changes: 6 additions & 3 deletions .github/workflows/l1-contracts-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,15 @@ jobs:
with:
toolchain: 1.72.0

- name: Generate Verifier.sol
- name: Generate verifiers
working-directory: tools
run: cargo run

- name: Compare
run: diff tools/data/Verifier.sol l1-contracts/contracts/state-transition/Verifier.sol
- name: Compare VerifierPlonk.sol
run: diff tools/data/VerifierPlonk.sol l1-contracts/contracts/state-transition/verifiers/VerifierPlonk.sol

- name: Compare VerifierFflonk.sol
run: diff tools/data/VerifierFflonk.sol l1-contracts/contracts/state-transition/verifiers/VerifierFflonk.sol

coverage:
defaults:
Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/slither.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ jobs:
- name: Remove non-compiled files
run: |
rm -rf ./l1-contracts/contracts/state-transition/utils/
rm -rf ./l1-contracts/contracts/state-transition/Verifier.sol
rm -rf ./l1-contracts/contracts/state-transition/TestnetVerifier.sol
rm -rf ./l1-contracts/contracts/dev-contracts/test/VerifierTest.sol
rm -rf ./l1-contracts/contracts/dev-contracts/test/VerifierRecursiveTest.sol
rm -rf ./l1-contracts/contracts/state-transition/verifiers/DualVerifier.sol
rm -rf ./l1-contracts/contracts/state-transition/verifiers/VerifierPlonk.sol
rm -rf ./l1-contracts/contracts/state-transition/verifiers/VerifierFflonk.sol
rm -rf ./l1-contracts/contracts/state-transition/verifiers/TestnetVerifier.sol
rm -rf ./l1-contracts/contracts/dev-contracts/test/PlonkVerifierTest.sol
rm -rf ./l1-contracts/contracts/dev-contracts/test/PlonkVerifierRecursiveTest.sol

- name: Run Slither
working-directory: l1-contracts
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ era_test_node.log*
node_modules/
out/
target/
tools/data/Verifier.sol
tools/data/*.sol
typechain/
yarn-debug.log*
yarn-error.log*
Expand Down
4 changes: 4 additions & 0 deletions l1-contracts/contracts/common/L1ContractErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ error ZeroAddress();
error ZeroBalance();
// 0xc84885d4
error ZeroChainId();
// 0xc352bb73
error UnknownVerifierType();
// 0xdf320f0a
error EmptyRecursiveAggregationInputLength();

enum SharedBridgeKey {
PostUpgradeFirstBatch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

pragma solidity 0.8.24;

import {Verifier} from "../../state-transition/Verifier.sol";
import {VerifierPlonk} from "../../state-transition/verifiers/VerifierPlonk.sol";

/// @author Matter Labs
contract VerifierRecursiveTest is Verifier {
contract PlonkVerifierRecursiveTest is VerifierPlonk {
// add this to be excluded from coverage report
function test() internal virtual {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

pragma solidity 0.8.24;

import {Verifier} from "../../state-transition/Verifier.sol";
import {VerifierPlonk} from "../../state-transition/verifiers/VerifierPlonk.sol";

/// @author Matter Labs
contract VerifierTest is Verifier {
contract PlonkVerifierTest is VerifierPlonk {
// add this to be excluded from coverage report
function test() internal virtual {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ interface IVerifier {

/// @notice Calculates a keccak256 hash of the runtime loaded verification keys.
/// @return vkHash The keccak256 hash of the loaded verification keys.
function verificationKeyHash() external pure returns (bytes32);
function verificationKeyHash() external view returns (bytes32);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
pragma solidity ^0.8.21;

/// @title The interface of the Verifier contract, responsible for the zero knowledge proof verification.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IVerifierV2 {
/// @dev Verifies a zk-SNARK proof.
/// @return A boolean value indicating whether the zk-SNARK proof is valid.
/// Note: The function may revert execution instead of returning false in some cases.
function verify(uint256[] calldata _publicInputs, uint256[] calldata _proof) external view returns (bool);

/// @notice Calculates a keccak256 hash of the runtime loaded verification keys.
/// @return vkHash The keccak256 hash of the loaded verification keys.
function verificationKeyHash() external view returns (bytes32);
}
108 changes: 108 additions & 0 deletions l1-contracts/contracts/state-transition/verifiers/DualVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import {IVerifierV2} from "../chain-interfaces/IVerifierV2.sol";
import {IVerifier} from "../chain-interfaces/IVerifier.sol";
import {UnknownVerifierType, EmptyRecursiveAggregationInputLength} from "../../common/L1ContractErrors.sol";

/// @title Dual Verifier
/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @notice This contract wraps two different verifiers (FFLONK and PLONK) and routes zk-SNARK proof verification
/// to the correct verifier based on the provided proof type. It reuses the same interface as on the original `Verifier`
/// contract, while abusing on of the fields (`_recursiveAggregationInput`) for proof verification type. The contract is
/// needed for the smooth transition from PLONK based verifier to the FFLONK verifier.
contract DualVerifier is IVerifier {
/// @notice The latest FFLONK verifier contract.
IVerifierV2 public immutable FFLONK_VERIFIER;

/// @notice PLONK verifier contract.
IVerifier public immutable PLONK_VERIFIER;

/// @notice Type of verification for FFLONK verifier.
uint256 internal constant FFLONK_VERIFICATION_TYPE = 0;

/// @notice Type of verification for PLONK verifier.
uint256 internal constant PLONK_VERIFICATION_TYPE = 1;

/// @param _fflonkVerifier The address of the FFLONK verifier contract.
/// @param _plonkVerifier The address of the PLONK verifier contract.
constructor(IVerifierV2 _fflonkVerifier, IVerifier _plonkVerifier) {
FFLONK_VERIFIER = _fflonkVerifier;
PLONK_VERIFIER = _plonkVerifier;
}

/// @notice Routes zk-SNARK proof verification to the appropriate verifier (FFLONK or PLONK) based on the proof type.
/// @param _publicInputs The public inputs to the proof.
/// @param _proof The zk-SNARK proof itself.
/// @param _recursiveAggregationInput The recursive aggregation input, used to determine which verifier to use.
/// The first element determines the verifier type.
/// - 0 indicates the FFLONK verifier should be used.
/// - 1 indicates the PLONK verifier should be used.
/// @return Returns `true` if the proof verification succeeds, otherwise throws an error.
function verify(
uint256[] calldata _publicInputs,
uint256[] calldata _proof,
uint256[] calldata _recursiveAggregationInput
) public view virtual returns (bool) {
// Ensure the recursive aggregation input has a valid length (at least one element
// for the proof system differentiator).
if (_recursiveAggregationInput.length == 0) {
revert EmptyRecursiveAggregationInputLength();
}

// The first element of `_recursiveAggregationInput` determines the verifier type (either FFLONK or PLONK).
uint256 verifierType = _recursiveAggregationInput[0];
if (verifierType == FFLONK_VERIFICATION_TYPE) {
return FFLONK_VERIFIER.verify(_publicInputs, _proof);
} else if (verifierType == PLONK_VERIFICATION_TYPE) {
return
PLONK_VERIFIER.verify(
_publicInputs,
_proof,
_extractRecursiveAggregationInput(_recursiveAggregationInput)
);
}
// If the verifier type is unknown, revert with an error.
else {
revert UnknownVerifierType();
}
}

/// @inheritdoc IVerifier
function verificationKeyHash() external view returns (bytes32) {
return PLONK_VERIFIER.verificationKeyHash();
}

/// @notice Calculates a keccak256 hash of the runtime loaded verification keys from the selected verifier.
/// @return The keccak256 hash of the loaded verification keys based on the verifier.
function verificationKeyHash(uint256 _verifierType) external view returns (bytes32) {
if (_verifierType == FFLONK_VERIFICATION_TYPE) {
return FFLONK_VERIFIER.verificationKeyHash();
} else if (_verifierType == PLONK_VERIFICATION_TYPE) {
return PLONK_VERIFIER.verificationKeyHash();
}
// If the verifier type is unknown, revert with an error.
else {
revert UnknownVerifierType();
}
}

/// @notice Extract the recursive aggregation input by removing the first element (proof type differentiator).
/// @param _recursiveAggregationInput The original recursive aggregation input array.
/// @return result A new array with the first element removed. The first element was used as a hack for
/// differentiator between FFLONK and PLONK proofs.
function _extractRecursiveAggregationInput(
uint256[] calldata _recursiveAggregationInput
) internal pure returns (uint256[] memory result) {
uint256 length = _recursiveAggregationInput.length;
// Allocate memory for the new array (length - 1) since the first element is omitted.
result = new uint256[](length - 1);

// Copy elements starting from index 1 (the second element) of the original array.
for (uint256 i = 1; i < length; ++i) {
result[i - 1] = _recursiveAggregationInput[i];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

pragma solidity 0.8.24;

import {Verifier} from "./Verifier.sol";
import {IVerifier} from "./chain-interfaces/IVerifier.sol";
import {DualVerifier} from "./DualVerifier.sol";
import {IVerifierV2} from "../chain-interfaces/IVerifierV2.sol";
import {IVerifier} from "../chain-interfaces/IVerifier.sol";

/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @notice Modified version of the main verifier contract for the testnet environment
/// @dev This contract is used to skip the zkp verification for the testnet environment.
/// If the proof is not empty, it will verify it using the main verifier contract,
/// otherwise, it will skip the verification.
contract TestnetVerifier is Verifier {
constructor() {
contract TestnetVerifier is DualVerifier {
constructor(IVerifierV2 _fflonkVerifier, IVerifier _plonkVerifier) DualVerifier(_fflonkVerifier, _plonkVerifier) {
assert(block.chainid != 1);
}

Expand Down
Loading
Loading