diff --git a/contracts/mocks/VerifierRollupHelperMock.sol b/contracts/mocks/VerifierRollupHelperMock.sol index 85e6b9192..4544d6efb 100644 --- a/contracts/mocks/VerifierRollupHelperMock.sol +++ b/contracts/mocks/VerifierRollupHelperMock.sol @@ -3,12 +3,20 @@ pragma solidity 0.8.20; import "../interfaces/IVerifierRollup.sol"; +import "../v2/interfaces/ISP1Verifier.sol"; -contract VerifierRollupHelperMock is IVerifierRollup { +contract VerifierRollupHelperMock is IVerifierRollup, ISP1Verifier { function verifyProof( bytes32[24] calldata proof, uint256[1] memory pubSignals ) public pure override returns (bool) { return true; } + + // SP1 interface + function verifyProof( + bytes32 programVKey, + bytes calldata publicValues, + bytes calldata proofBytes + ) public pure {} } diff --git a/contracts/v2/PolygonRollupManager.sol b/contracts/v2/PolygonRollupManager.sol index 18e554365..dd537a270 100644 --- a/contracts/v2/PolygonRollupManager.sol +++ b/contracts/v2/PolygonRollupManager.sol @@ -550,6 +550,7 @@ contract PolygonRollupManager is } else { rollup.batchNumToStateRoot[0] = initRoot; } + // rollup type is 0, since it does not follow any rollup type emit AddExistingRollup( rollupID, @@ -592,13 +593,6 @@ contract PolygonRollupManager is revert UpdateToOldRollupTypeID(); } - if ( - rollup.rollupVerifierType != - rollupTypeMap[newRollupTypeID].rollupVerifierType - ) { - revert UpdateNotCompatible(); - } - _updateRollup(rollupContract, newRollupTypeID, new bytes(0)); } @@ -654,13 +648,7 @@ contract PolygonRollupManager is // Check rollup types if (rollup.rollupVerifierType != newRollupType.rollupVerifierType) { - // Currently the transition from pessimistic to state transition is not allowed - if (rollup.rollupVerifierType == VerifierType.Pessimistic) { - revert RollupTypeObsolete(); - } - - // Update rollup verifier type - rollup.rollupVerifierType = newRollupType.rollupVerifierType; + revert UpdateNotCompatible(); } // Update rollup parameters diff --git a/test/contractsv2/PolygonPessimisticConsensus.test.ts b/test/contractsv2/PolygonPessimisticConsensus.test.ts new file mode 100644 index 000000000..8ea85906b --- /dev/null +++ b/test/contractsv2/PolygonPessimisticConsensus.test.ts @@ -0,0 +1,163 @@ +/* eslint-disable no-plusplus, no-await-in-loop */ +import {expect} from "chai"; +import {ethers, upgrades} from "hardhat"; +import {Address, PolygonPessimisticConsensus} from "../../typechain-types"; + +describe("PolygonPessimisticConsensus", () => { + let deployer: any; + let trustedSequencer: any; + let admin: any; + + let PolygonPPConsensusContract: PolygonPessimisticConsensus; + + const gerManagerAddress = "0xA00000000000000000000000000000000000000A" as unknown as Address; + const polTokenAddress = "0xB00000000000000000000000000000000000000B" as unknown as Address; + const rollupManagerAddress = "0xC00000000000000000000000000000000000000C" as unknown as Address; + const bridgeAddress = "0xD00000000000000000000000000000000000000D" as unknown as Address; + + const urlSequencer = "http://zkevm-json-rpc:8123"; + const networkName = "zkevm"; + const networkID = 1; + + // Native token will be ether + const gasTokenAddress = ethers.ZeroAddress; + + beforeEach("Deploy contract", async () => { + upgrades.silenceWarnings(); + + // load signers + [deployer, trustedSequencer, admin] = await ethers.getSigners(); + + // deploy consensus + // create polygonPessimisticConsensus implementation + const ppConsensusFactory = await ethers.getContractFactory("PolygonPessimisticConsensus"); + PolygonPPConsensusContract = await ppConsensusFactory.deploy( + gerManagerAddress, + polTokenAddress, + bridgeAddress, + rollupManagerAddress + ); + await PolygonPPConsensusContract.waitForDeployment(); + }); + + it("should check the initalized parameters", async () => { + // initialize zkEVM using non admin address + await expect( + PolygonPPConsensusContract.initialize( + admin.address, + trustedSequencer.address, + networkID, + gasTokenAddress, + urlSequencer, + networkName + ) + ).to.be.revertedWithCustomError(PolygonPPConsensusContract, "OnlyRollupManager"); + + // initialize using rollup manager + await ethers.provider.send("hardhat_impersonateAccount", [rollupManagerAddress]); + const rolllupManagerSigner = await ethers.getSigner(rollupManagerAddress as any); + await PolygonPPConsensusContract.connect(rolllupManagerSigner).initialize( + admin.address, + trustedSequencer.address, + networkID, + gasTokenAddress, + urlSequencer, + networkName, + {gasPrice: 0} + ); + + expect(await PolygonPPConsensusContract.admin()).to.be.equal(admin.address); + expect(await PolygonPPConsensusContract.trustedSequencer()).to.be.equal(trustedSequencer.address); + expect(await PolygonPPConsensusContract.trustedSequencerURL()).to.be.equal(urlSequencer); + expect(await PolygonPPConsensusContract.networkName()).to.be.equal(networkName); + expect(await PolygonPPConsensusContract.gasTokenAddress()).to.be.equal(gasTokenAddress); + + // initialize again + await expect( + PolygonPPConsensusContract.connect(rolllupManagerSigner).initialize( + admin.address, + trustedSequencer.address, + networkID, + gasTokenAddress, + urlSequencer, + networkName, + {gasPrice: 0} + ) + ).to.be.revertedWith("Initializable: contract is already initialized"); + }); + + it("should check admin functions", async () => { + // initialize using rollup manager + await ethers.provider.send("hardhat_impersonateAccount", [rollupManagerAddress]); + const rolllupManagerSigner = await ethers.getSigner(rollupManagerAddress as any); + await PolygonPPConsensusContract.connect(rolllupManagerSigner).initialize( + admin.address, + trustedSequencer.address, + networkID, + gasTokenAddress, + urlSequencer, + networkName, + {gasPrice: 0} + ); + + // setTrustedSequencer + await expect(PolygonPPConsensusContract.setTrustedSequencer(deployer.address)).to.be.revertedWithCustomError( + PolygonPPConsensusContract, + "OnlyAdmin" + ); + + await expect(PolygonPPConsensusContract.connect(admin).setTrustedSequencer(deployer.address)) + .to.emit(PolygonPPConsensusContract, "SetTrustedSequencer") + .withArgs(deployer.address); + + // setTrustedSequencerURL + await expect(PolygonPPConsensusContract.setTrustedSequencerURL("0x1253")).to.be.revertedWithCustomError( + PolygonPPConsensusContract, + "OnlyAdmin" + ); + await expect(PolygonPPConsensusContract.connect(admin).setTrustedSequencerURL("0x1253")) + .to.emit(PolygonPPConsensusContract, "SetTrustedSequencerURL") + .withArgs("0x1253"); + + // transferAdminRole & acceptAdminRole + await expect(PolygonPPConsensusContract.connect(admin).transferAdminRole(deployer.address)) + .to.emit(PolygonPPConsensusContract, "TransferAdminRole") + .withArgs(deployer.address); + + await expect(PolygonPPConsensusContract.connect(admin).acceptAdminRole()).to.be.revertedWithCustomError( + PolygonPPConsensusContract, + "OnlyPendingAdmin" + ); + + await expect(PolygonPPConsensusContract.connect(deployer).acceptAdminRole()) + .to.emit(PolygonPPConsensusContract, "AcceptAdminRole") + .withArgs(deployer.address); + }); + + it("should check getConsensusHash", async () => { + // initialize using rollup manager + await ethers.provider.send("hardhat_impersonateAccount", [rollupManagerAddress]); + const rolllupManagerSigner = await ethers.getSigner(rollupManagerAddress as any); + await PolygonPPConsensusContract.connect(rolllupManagerSigner).initialize( + admin.address, + trustedSequencer.address, + networkID, + gasTokenAddress, + urlSequencer, + networkName, + {gasPrice: 0} + ); + + // pessimistic constant CONSENSUS_TYPE = 0; + const CONSENSUS_TYPE = 0; + const consensusHashJs = ethers.solidityPackedKeccak256( + ["uint32", "address"], + [CONSENSUS_TYPE, trustedSequencer.address] + ); + + // getConsensusHash + const resGetConsensusHash = await PolygonPPConsensusContract.getConsensusHash(); + + expect(resGetConsensusHash).to.be.equal(consensusHashJs); + }); +}); diff --git a/test/contractsv2/PolygonRollupManager-Pessimistic.test.ts b/test/contractsv2/PolygonRollupManager-Pessimistic.test.ts new file mode 100644 index 000000000..9d955cb7f --- /dev/null +++ b/test/contractsv2/PolygonRollupManager-Pessimistic.test.ts @@ -0,0 +1,865 @@ +/* eslint-disable no-plusplus, no-await-in-loop */ +import {expect} from "chai"; +import {ethers, upgrades} from "hardhat"; +import { + VerifierRollupHelperMock, + ERC20PermitMock, + PolygonRollupManagerMock, + PolygonZkEVMGlobalExitRootV2, + PolygonZkEVMBridgeV2, + PolygonZkEVMEtrog, + PolygonRollupBaseEtrog, + TokenWrapped, + Address, + PolygonDataCommittee, + PolygonPessimisticConsensus, +} from "../../typechain-types"; +import {takeSnapshot, time} from "@nomicfoundation/hardhat-network-helpers"; + +enum VerifierType { + StateTransition = 0, + Pessimistic = 1, +} + +describe("Polygon Rollup Manager with Polygon Pessimistic Consensus", () => { + let deployer: any; + let timelock: any; + let emergencyCouncil: any; + let trustedAggregator: any; + let trustedSequencer: any; + let admin: any; + let beneficiary: any; + + let verifierContract: VerifierRollupHelperMock; + let polygonZkEVMBridgeContract: PolygonZkEVMBridgeV2; + let polTokenContract: ERC20PermitMock; + let polygonZkEVMGlobalExitRoot: PolygonZkEVMGlobalExitRootV2; + let rollupManagerContract: PolygonRollupManagerMock; + let PolygonPPConsensusContract: PolygonPessimisticConsensus; + + const polTokenName = "POL Token"; + const polTokenSymbol = "POL"; + const polTokenInitialBalance = ethers.parseEther("20000000"); + + const pendingStateTimeoutDefault = 100; + const trustedAggregatorTimeout = 100; + const FORCE_BATCH_TIMEOUT = 60 * 60 * 24 * 5; // 5 days + + // BRidge constants + const networkIDMainnet = 0; + const networkIDRollup = 1; + + const LEAF_TYPE_ASSET = 0; + const LEAF_TYPE_MESSAGE = 1; + + const globalExitRootL2Address = "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa" as unknown as Address; + + let firstDeployment = true; + + //roles + const DEFAULT_ADMIN_ROLE = ethers.ZeroHash; + const ADD_ROLLUP_TYPE_ROLE = ethers.id("ADD_ROLLUP_TYPE_ROLE"); + const OBSOLETE_ROLLUP_TYPE_ROLE = ethers.id("OBSOLETE_ROLLUP_TYPE_ROLE"); + const CREATE_ROLLUP_ROLE = ethers.id("CREATE_ROLLUP_ROLE"); + const ADD_EXISTING_ROLLUP_ROLE = ethers.id("ADD_EXISTING_ROLLUP_ROLE"); + const UPDATE_ROLLUP_ROLE = ethers.id("UPDATE_ROLLUP_ROLE"); + const TRUSTED_AGGREGATOR_ROLE = ethers.id("TRUSTED_AGGREGATOR_ROLE"); + const TRUSTED_AGGREGATOR_ROLE_ADMIN = ethers.id("TRUSTED_AGGREGATOR_ROLE_ADMIN"); + const TWEAK_PARAMETERS_ROLE = ethers.id("TWEAK_PARAMETERS_ROLE"); + const SET_FEE_ROLE = ethers.id("SET_FEE_ROLE"); + const STOP_EMERGENCY_ROLE = ethers.id("STOP_EMERGENCY_ROLE"); + const EMERGENCY_COUNCIL_ROLE = ethers.id("EMERGENCY_COUNCIL_ROLE"); + const EMERGENCY_COUNCIL_ADMIN = ethers.id("EMERGENCY_COUNCIL_ADMIN"); + + const SIGNATURE_BYTES = 32 + 32 + 1; + const EFFECTIVE_PERCENTAGE_BYTES = 1; + + beforeEach("Deploy contract", async () => { + upgrades.silenceWarnings(); + + // load signers + [deployer, trustedAggregator, trustedSequencer, admin, timelock, emergencyCouncil, beneficiary] = + await ethers.getSigners(); + + // deploy mock verifier + const VerifierRollupHelperFactory = await ethers.getContractFactory("VerifierRollupHelperMock"); + verifierContract = await VerifierRollupHelperFactory.deploy(); + + // deploy pol + const polTokenFactory = await ethers.getContractFactory("ERC20PermitMock"); + polTokenContract = await polTokenFactory.deploy( + polTokenName, + polTokenSymbol, + deployer.address, + polTokenInitialBalance + ); + + /* + * deploy global exit root manager + * In order to not have trouble with nonce deploy first proxy admin + */ + await upgrades.deployProxyAdmin(); + + if ((await upgrades.admin.getInstance()).target !== "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0") { + firstDeployment = false; + } + const nonceProxyBridge = + Number(await ethers.provider.getTransactionCount(deployer.address)) + (firstDeployment ? 3 : 2); + + const nonceProxyZkevm = nonceProxyBridge + 2; // Always have to redeploy impl since the polygonZkEVMGlobalExitRoot address changes + + const precalculateBridgeAddress = ethers.getCreateAddress({ + from: deployer.address, + nonce: nonceProxyBridge, + }); + const precalculateRollupManagerAddress = ethers.getCreateAddress({ + from: deployer.address, + nonce: nonceProxyZkevm, + }); + firstDeployment = false; + + // deploy globalExitRoot + const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory("PolygonZkEVMGlobalExitRootV2"); + polygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], { + constructorArgs: [precalculateRollupManagerAddress, precalculateBridgeAddress], + unsafeAllow: ["constructor", "state-variable-immutable"], + }); + + // deploy PolygonZkEVMBridge + const polygonZkEVMBridgeFactory = await ethers.getContractFactory("PolygonZkEVMBridgeV2"); + polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { + initializer: false, + unsafeAllow: ["constructor"], + }); + + // deploy polygon rollup manager mock + const PolygonRollupManagerFactory = await ethers.getContractFactory("PolygonRollupManagerMock"); + + rollupManagerContract = (await upgrades.deployProxy(PolygonRollupManagerFactory, [], { + initializer: false, + constructorArgs: [ + polygonZkEVMGlobalExitRoot.target, + polTokenContract.target, + polygonZkEVMBridgeContract.target, + ], + unsafeAllow: ["constructor", "state-variable-immutable"], + })) as unknown as PolygonRollupManagerMock; + + await rollupManagerContract.waitForDeployment(); + + // check precalculated address + expect(precalculateBridgeAddress).to.be.equal(polygonZkEVMBridgeContract.target); + expect(precalculateRollupManagerAddress).to.be.equal(rollupManagerContract.target); + + await polygonZkEVMBridgeContract.initialize( + networkIDMainnet, + ethers.ZeroAddress, // zero for ether + ethers.ZeroAddress, // zero for ether + polygonZkEVMGlobalExitRoot.target, + rollupManagerContract.target, + "0x" + ); + + // Initialize Mock + await rollupManagerContract.initializeMock( + trustedAggregator.address, + pendingStateTimeoutDefault, + trustedAggregatorTimeout, + admin.address, + timelock.address, + emergencyCouncil.address + ); + + // fund sequencer address with Matic tokens + await polTokenContract.transfer(trustedSequencer.address, ethers.parseEther("1000")); + }); + + it("should check the initalized parameters", async () => { + expect(await rollupManagerContract.globalExitRootManager()).to.be.equal(polygonZkEVMGlobalExitRoot.target); + expect(await rollupManagerContract.pol()).to.be.equal(polTokenContract.target); + expect(await rollupManagerContract.bridgeAddress()).to.be.equal(polygonZkEVMBridgeContract.target); + + expect(await rollupManagerContract.getBatchFee()).to.be.equal(ethers.parseEther("0.1")); + expect(await rollupManagerContract.getForcedBatchFee()).to.be.equal(ethers.parseEther("10")); + expect(await rollupManagerContract.calculateRewardPerBatch()).to.be.equal(0); + + // Check roles + expect(await rollupManagerContract.hasRole(DEFAULT_ADMIN_ROLE, timelock.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(ADD_ROLLUP_TYPE_ROLE, timelock.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(UPDATE_ROLLUP_ROLE, timelock.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(ADD_EXISTING_ROLLUP_ROLE, timelock.address)).to.be.equal(true); + + expect(await rollupManagerContract.hasRole(TRUSTED_AGGREGATOR_ROLE, trustedAggregator.address)).to.be.equal( + true + ); + + expect(await rollupManagerContract.hasRole(OBSOLETE_ROLLUP_TYPE_ROLE, admin.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(CREATE_ROLLUP_ROLE, admin.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(TRUSTED_AGGREGATOR_ROLE_ADMIN, admin.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(TWEAK_PARAMETERS_ROLE, admin.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(SET_FEE_ROLE, admin.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(STOP_EMERGENCY_ROLE, admin.address)).to.be.equal(true); + + expect(await rollupManagerContract.hasRole(EMERGENCY_COUNCIL_ROLE, emergencyCouncil.address)).to.be.equal(true); + expect(await rollupManagerContract.hasRole(EMERGENCY_COUNCIL_ADMIN, emergencyCouncil.address)).to.be.equal( + true + ); + }); + + it("should add a new rollup type: PolygonConsensusPessimistic", async () => { + // deploy consensus + // create polygonPessimisticConsensus implementation + const ppConsensusFactory = await ethers.getContractFactory("PolygonPessimisticConsensus"); + PolygonPPConsensusContract = await ppConsensusFactory.deploy( + polygonZkEVMGlobalExitRoot.target, + polTokenContract.target, + polygonZkEVMBridgeContract.target, + rollupManagerContract.target + ); + await PolygonPPConsensusContract.waitForDeployment(); + + // Try to add a new rollup type + const forkID = 11; // just metadata for pessimistic consensus + const genesis = ethers.ZeroHash; + const description = "new pessimistic consensus"; + const programVKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const newRollupTypeID = 1; + const nonZeroGenesis = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + + // sender does not have _ADD_ROLLUP_TYPE_ROLE role + await expect( + rollupManagerContract.addNewRollupType( + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + genesis, + description, + programVKey + ) + ).to.be.revertedWithCustomError(rollupManagerContract, "AddressDoNotHaveRequiredRole"); + + // genesis != 0 on Pessimistic Verifier type + await expect( + rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + nonZeroGenesis, + description, + programVKey + ) + ).to.be.revertedWithCustomError(rollupManagerContract, "InvalidRollupType"); + + // correct add new rollup via timelock + await expect( + rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + genesis, + description, + programVKey + ) + ) + .to.emit(rollupManagerContract, "AddNewRollupType") + .withArgs( + newRollupTypeID, + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + genesis, + description, + programVKey + ); + + // assert new rollup type + const createdRollupType = await rollupManagerContract.rollupTypeMap(newRollupTypeID); + + const expectedRollupType = [ + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + false, + genesis, + programVKey, + ]; + + expect(createdRollupType).to.be.deep.equal(expectedRollupType); + + // do obsoleteRollupType + await expect(rollupManagerContract.obsoleteRollupType(newRollupTypeID)).to.be.revertedWithCustomError( + rollupManagerContract, + "AddressDoNotHaveRequiredRole" + ); + + await expect(rollupManagerContract.connect(admin).obsoleteRollupType(newRollupTypeID)) + .to.emit(rollupManagerContract, "ObsoleteRollupType") + .withArgs(newRollupTypeID); + }); + + it("should create a new rollup: PolygonConsensusPessimistic", async () => { + // deploy consensus + // create polygonPessimisticConsensus implementation + const ppConsensusFactory = await ethers.getContractFactory("PolygonPessimisticConsensus"); + PolygonPPConsensusContract = await ppConsensusFactory.deploy( + polygonZkEVMGlobalExitRoot.target, + polTokenContract.target, + polygonZkEVMBridgeContract.target, + rollupManagerContract.target + ); + await PolygonPPConsensusContract.waitForDeployment(); + + // Try to add a new rollup type + const forkID = 11; // just metadata for pessimistic consensus + const genesis = ethers.ZeroHash; + const description = "new pessimistic consensus"; + const programVKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const newRollupTypeID = 1; + + // correct add new rollup via timelock + await expect( + rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + genesis, + description, + programVKey + ) + ) + .to.emit(rollupManagerContract, "AddNewRollupType") + .withArgs( + newRollupTypeID, + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + genesis, + description, + programVKey + ); + + // create new pessimsitic: only admin + const chainID = 1; + const gasTokenAddress = ethers.ZeroAddress; + const urlSequencer = "https://pessimistic:8545"; + const networkName = "testPessimistic"; + const newCreatedRollupID = 1; + + // Only admin can create new zkEVMs + await expect( + rollupManagerContract.createNewRollup( + newRollupTypeID, + chainID, + admin.address, + trustedSequencer.address, + gasTokenAddress, + urlSequencer, + networkName + ) + ).to.be.revertedWithCustomError(rollupManagerContract, "AddressDoNotHaveRequiredRole"); + + // create new pessimistic + const newZKEVMAddress = ethers.getCreateAddress({ + from: rollupManagerContract.target as string, + nonce: 1, + }); + const newZkEVMContract = ppConsensusFactory.attach(newZKEVMAddress) as PolygonPessimisticConsensus; + + await expect( + rollupManagerContract + .connect(admin) + .createNewRollup( + newRollupTypeID, + chainID, + admin.address, + trustedSequencer.address, + gasTokenAddress, + urlSequencer, + networkName + ) + ) + .to.emit(rollupManagerContract, "CreateNewRollup") + .withArgs(newCreatedRollupID, newRollupTypeID, newZKEVMAddress, chainID, gasTokenAddress); + + // assert new rollup created + expect(await newZkEVMContract.admin()).to.be.equal(admin.address); + expect(await newZkEVMContract.trustedSequencer()).to.be.equal(trustedSequencer.address); + expect(await newZkEVMContract.trustedSequencerURL()).to.be.equal(urlSequencer); + expect(await newZkEVMContract.networkName()).to.be.equal(networkName); + + // assert new rollup + const resRollupData = await rollupManagerContract.rollupIDToRollupData(newCreatedRollupID); + + const expectedRollupData = [ + newZKEVMAddress, + chainID, + verifierContract.target, + forkID, + ethers.ZeroHash, + 0, + 0, + 0, + 0, + newRollupTypeID, + VerifierType.Pessimistic, + ethers.ZeroHash, + programVKey, + ]; + + expect(expectedRollupData).to.be.deep.equal(resRollupData); + }); + + it("should add an existing rollup: PolygonConsensusPessimistic", async () => { + // add existing rollup + const rollupAddress = "0xAa000000000000000000000000000000000000Bb"; + const forkID = 1; + const chainID = 1; + const initLER = "0xff000000000000000000000000000000000000000000000000000000000000ff"; + const programVKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + + // add existing rollup: pessimistic type + const newCreatedRollupID = 1; + + await expect( + rollupManagerContract + .connect(timelock) + .addExistingRollup( + rollupAddress, + verifierContract.target, + forkID, + chainID, + initLER, + VerifierType.Pessimistic, + programVKey + ) + ) + .to.emit(rollupManagerContract, "AddExistingRollup") + .withArgs(newCreatedRollupID, forkID, rollupAddress, chainID, VerifierType.Pessimistic, 0, programVKey); + }); + + it("should prevent to update rollup with different VerifierTypes", async () => { + // deploy consensus + // create polygonPessimisticConsensus implementation + const ppConsensusFactory = await ethers.getContractFactory("PolygonPessimisticConsensus"); + PolygonPPConsensusContract = await ppConsensusFactory.deploy( + polygonZkEVMGlobalExitRoot.target, + polTokenContract.target, + polygonZkEVMBridgeContract.target, + rollupManagerContract.target + ); + await PolygonPPConsensusContract.waitForDeployment(); + + // Try to add a new rollup type + const forkID = 11; // just metadata for pessimistic consensus + const genesis = ethers.ZeroHash; + const description = "new pessimistic consensus"; + const programVKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const newRollupTypeID = 1; + + // correct add new rollup via timelock + await rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + genesis, + description, + programVKey + ); + + // create new pessimsitic: only admin + const chainID = 1; + const gasTokenAddress = ethers.ZeroAddress; + const urlSequencer = "https://pessimistic:8545"; + const networkName = "testPessimistic"; + const pessimisticRollupID = 1; + + // create new pessimistic + await rollupManagerContract + .connect(admin) + .createNewRollup( + newRollupTypeID, + chainID, + admin.address, + trustedSequencer.address, + gasTokenAddress, + urlSequencer, + networkName + ); + + // Create zkEVM implementation + const PolygonZKEVMV2Factory = await ethers.getContractFactory("PolygonZkEVMEtrog"); + const PolygonZKEVMV2Contract = await PolygonZKEVMV2Factory.deploy( + polygonZkEVMGlobalExitRoot.target, + polTokenContract.target, + polygonZkEVMBridgeContract.target, + rollupManagerContract.target + ); + await PolygonZKEVMV2Contract.waitForDeployment(); + + // Add a new rollup type with timelock + const genesisRandom = "0x0000000000000000000000000000000000000000000000000000000000000001"; + const description2 = "description"; + const chainID2 = 2; + const stateTransistionRollupID = 2; + + // add new rollup type StateTransistion with programVKey != 0 + await expect( + rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonZKEVMV2Contract.target, + verifierContract.target, + forkID, + VerifierType.StateTransition, + genesisRandom, + description2, + programVKey + ) + ).to.be.revertedWithCustomError(rollupManagerContract, "InvalidRollupType"); + + // add new rollup type stateTranstion correctly + const newRollupTypeID2 = 2; + + await rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonZKEVMV2Contract.target, + verifierContract.target, + forkID, + VerifierType.StateTransition, + genesisRandom, + description2, + ethers.ZeroHash + ); + + // create new rollup + await rollupManagerContract + .connect(admin) + .createNewRollup( + newRollupTypeID2, + chainID2, + admin.address, + trustedSequencer.address, + gasTokenAddress, + urlSequencer, + networkName + ); + + // get rollup data + const rollupPessimistic = await rollupManagerContract.rollupIDToRollupData(pessimisticRollupID); + const rollupStateTransition = await rollupManagerContract.rollupIDToRollupData(stateTransistionRollupID); + + // try to update rollup from Pessimistic to stateTransition + await expect( + rollupManagerContract.connect(timelock).updateRollup(rollupPessimistic[0] as unknown as Address, 2, "0x") + ).to.be.revertedWithCustomError(rollupManagerContract, "UpdateNotCompatible"); + + // try to update rollup from StateTransition to Pessimistic + await expect( + rollupManagerContract + .connect(timelock) + .updateRollup(rollupStateTransition[0] as unknown as Address, 1, "0x") + ).to.be.revertedWithCustomError(rollupManagerContract, "UpdateNotCompatible"); + }); + + it("should update rollup: pessismsitic type", async () => { + // deploy consensus + // create polygonPessimisticConsensus implementation + const ppConsensusFactory = await ethers.getContractFactory("PolygonPessimisticConsensus"); + PolygonPPConsensusContract = await ppConsensusFactory.deploy( + polygonZkEVMGlobalExitRoot.target, + polTokenContract.target, + polygonZkEVMBridgeContract.target, + rollupManagerContract.target + ); + await PolygonPPConsensusContract.waitForDeployment(); + + // Try to add a new rollup type + const forkID = 11; // just metadata for pessimistic consensus + const genesis = ethers.ZeroHash; + const description = "new pessimistic consensus"; + const programVKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const rollupTypeID = 1; + + // correct add new rollup via timelock + await rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + genesis, + description, + programVKey + ); + + // create new pessimsitic: only admin + const chainID = 1; + const gasTokenAddress = ethers.ZeroAddress; + const urlSequencer = "https://pessimistic:8545"; + const networkName = "testPessimistic"; + const pessimisticRollupID = 1; + + // create new pessimistic + const newZKEVMAddress = ethers.getCreateAddress({ + from: rollupManagerContract.target as string, + nonce: 1, + }); + + await rollupManagerContract + .connect(admin) + .createNewRollup( + rollupTypeID, + chainID, + admin.address, + trustedSequencer.address, + gasTokenAddress, + urlSequencer, + networkName + ); + + // Try to add a new rollup type + const newForkID = 11; // just metadata for pessimistic consensus + const newProgramVKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const newRollupTypeID = 2; + const newVerifier = "0xaa000000000000000000000000000000000000bb" as unknown as Address; + + // correct add new rollup via timelock + await rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonPPConsensusContract.target, + newVerifier, + newForkID, + VerifierType.Pessimistic, + genesis, + description, + newProgramVKey + ); + + // get rollup data + const rollupPessimistic = await rollupManagerContract.rollupIDToRollupData(pessimisticRollupID); + + // try to update rollup from StateTransition to Pessimistic + await rollupManagerContract + .connect(timelock) + .updateRollup(rollupPessimistic[0] as unknown as Address, newRollupTypeID, "0x"); + + // assert new rollup + const resRollupData = await rollupManagerContract.rollupIDToRollupData(pessimisticRollupID); + + const expectedRollupData = [ + newZKEVMAddress, + chainID, + newVerifier, + newForkID, + ethers.ZeroHash, + 0, + 0, + 0, + 0, + newRollupTypeID, + VerifierType.Pessimistic, + ethers.ZeroHash, + newProgramVKey, + ]; + + expect(expectedRollupData).to.be.deep.equal(resRollupData); + }); + + it("should verify pessimistic proof: pessismsitic type", async () => { + // deploy consensus + // create polygonPessimisticConsensus implementation + const ppConsensusFactory = await ethers.getContractFactory("PolygonPessimisticConsensus"); + PolygonPPConsensusContract = await ppConsensusFactory.deploy( + polygonZkEVMGlobalExitRoot.target, + polTokenContract.target, + polygonZkEVMBridgeContract.target, + rollupManagerContract.target + ); + await PolygonPPConsensusContract.waitForDeployment(); + + // Try to add a new rollup type + const forkID = 11; // just metadata for pessimistic consensus + const genesis = ethers.ZeroHash; + const description = "new pessimistic consensus"; + const programVKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const rollupTypeID = 1; + + // correct add new rollup via timelock + await rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonPPConsensusContract.target, + verifierContract.target, + forkID, + VerifierType.Pessimistic, + genesis, + description, + programVKey + ); + + // create new pessimsitic: only admin + const chainID = 1; + const gasTokenAddress = ethers.ZeroAddress; + const urlSequencer = "https://pessimistic:8545"; + const networkName = "testPessimistic"; + const pessimisticRollupID = 1; + + // create new pessimistic + const newZKEVMAddress = ethers.getCreateAddress({ + from: rollupManagerContract.target as string, + nonce: 1, + }); + + await rollupManagerContract + .connect(admin) + .createNewRollup( + rollupTypeID, + chainID, + admin.address, + trustedSequencer.address, + gasTokenAddress, + urlSequencer, + networkName + ); + + // select unexistent global exit root + const unexistentGER = "0xddff00000000000000000000000000000000000000000000000000000000ddff"; + const newLER = "0x0000000000000000000000000000000000000000000000000000000000000001"; + const newPPRoot = "0x0000000000000000000000000000000000000000000000000000000000000002"; + const proofPP = "0x00"; + + await expect( + rollupManagerContract + .connect(trustedAggregator) + .verifyPessimisticTrustedAggregator(pessimisticRollupID, unexistentGER, newLER, newPPRoot, proofPP) + ).to.be.revertedWithCustomError(rollupManagerContract, "GlobalExitRootNotExist"); + + // create a bridge to genenew rate a GER + // Just to have the metric of a low cost bridge Asset + const tokenAddress = ethers.ZeroAddress; + const amount = ethers.parseEther("1"); + await polygonZkEVMBridgeContract.bridgeAsset( + pessimisticRollupID, + polTokenContract.target, + amount, + tokenAddress, + true, + "0x", + { + value: amount, + } + ); + + const existingGER = await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot(); + + await expect( + rollupManagerContract + .connect(trustedAggregator) + .verifyPessimisticTrustedAggregator(pessimisticRollupID, existingGER, newLER, newPPRoot, proofPP) + ) + .to.emit(rollupManagerContract, "VerifyBatchesTrustedAggregator") + .withArgs(pessimisticRollupID, 0, ethers.ZeroHash, newLER, trustedAggregator.address); + + // assert rollup data + const resRollupData = await rollupManagerContract.rollupIDToRollupData(pessimisticRollupID); + + const expectedRollupData = [ + newZKEVMAddress, + chainID, + verifierContract.target, + forkID, + newLER, + 0, + 0, + 0, + 0, + rollupTypeID, + VerifierType.Pessimistic, + newPPRoot, + programVKey, + ]; + + expect(expectedRollupData).to.be.deep.equal(resRollupData); + }); + + it("should not verify pessimistic proof from stateTransistion chain", async () => { + // Create zkEVM implementation + const PolygonZKEVMV2Factory = await ethers.getContractFactory("PolygonZkEVMEtrog"); + const PolygonZKEVMV2Contract = await PolygonZKEVMV2Factory.deploy( + polygonZkEVMGlobalExitRoot.target, + polTokenContract.target, + polygonZkEVMBridgeContract.target, + rollupManagerContract.target + ); + await PolygonZKEVMV2Contract.waitForDeployment(); + + // Add a new rollup type with timelock + const gasTokenAddress = ethers.ZeroAddress; + const urlSequencer = "https://pessimistic:8545"; + const networkName = "testPessimistic"; + const genesisRandom = "0x0000000000000000000000000000000000000000000000000000000000000001"; + const description = "description"; + const forkID = 1; + const chainID = 1; + const stateTransistionRollupID = 1; + + // add new rollup type stateTranstion correctly + const newRollupTypeID = 1; + + await rollupManagerContract + .connect(timelock) + .addNewRollupType( + PolygonZKEVMV2Contract.target, + verifierContract.target, + forkID, + VerifierType.StateTransition, + genesisRandom, + description, + ethers.ZeroHash + ); + + // create new rollup + await rollupManagerContract + .connect(admin) + .createNewRollup( + newRollupTypeID, + chainID, + admin.address, + trustedSequencer.address, + gasTokenAddress, + urlSequencer, + networkName + ); + + // try to verify + const unexistentGER = "0xddff00000000000000000000000000000000000000000000000000000000ddff"; + const newLER = "0x0000000000000000000000000000000000000000000000000000000000000001"; + const newPPRoot = "0x0000000000000000000000000000000000000000000000000000000000000002"; + const proofPP = "0x00"; + + await expect( + rollupManagerContract + .connect(trustedAggregator) + .verifyPessimisticTrustedAggregator(stateTransistionRollupID, unexistentGER, newLER, newPPRoot, proofPP) + ).to.be.revertedWithCustomError(rollupManagerContract, "OnlyChainsWithPessimisticProofs"); + }); +});