diff --git a/l1-contracts/contracts/dev-contracts/test/AddressAliasHelperTest.sol b/l1-contracts/contracts/dev-contracts/test/AddressAliasHelperTest.sol new file mode 100644 index 000000000..a4031cfef --- /dev/null +++ b/l1-contracts/contracts/dev-contracts/test/AddressAliasHelperTest.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.20; + +import "../../vendor/AddressAliasHelper.sol"; + +contract AddressAliasHelperTest { + function applyL1ToL2Alias(address _l1Address) external pure returns (address) { + return AddressAliasHelper.applyL1ToL2Alias(_l1Address); + } + + function undoL1ToL2Alias(address _l2Address) external pure returns (address) { + return AddressAliasHelper.undoL1ToL2Alias(_l2Address); + } +} diff --git a/l1-contracts/contracts/dev-contracts/test/PriorityQueueTest.sol b/l1-contracts/contracts/dev-contracts/test/PriorityQueueTest.sol index ab8f7b719..8fd0e00c2 100644 --- a/l1-contracts/contracts/dev-contracts/test/PriorityQueueTest.sol +++ b/l1-contracts/contracts/dev-contracts/test/PriorityQueueTest.sol @@ -5,38 +5,33 @@ pragma solidity 0.8.20; import "../../state-transition/libraries/PriorityQueue.sol"; contract PriorityQueueTest { - // add this to be excluded from coverage report - function test() internal virtual {} - - using PriorityQueue for PriorityQueue.Queue; - PriorityQueue.Queue priorityQueue; function getFirstUnprocessedPriorityTx() external view returns (uint256) { - return priorityQueue.getFirstUnprocessedPriorityTx(); + return PriorityQueue.getFirstUnprocessedPriorityTx(priorityQueue); } function getTotalPriorityTxs() external view returns (uint256) { - return priorityQueue.getTotalPriorityTxs(); + return PriorityQueue.getTotalPriorityTxs(priorityQueue); } function getSize() external view returns (uint256) { - return priorityQueue.getSize(); + return PriorityQueue.getSize(priorityQueue); } function isEmpty() external view returns (bool) { - return priorityQueue.isEmpty(); + return PriorityQueue.isEmpty(priorityQueue); } function pushBack(PriorityOperation memory _operation) external { - return priorityQueue.pushBack(_operation); + return PriorityQueue.pushBack(priorityQueue, _operation); } function front() external view returns (PriorityOperation memory) { - return priorityQueue.front(); + return PriorityQueue.front(priorityQueue); } function popFront() external returns (PriorityOperation memory operation) { - return priorityQueue.popFront(); + return PriorityQueue.popFront(priorityQueue); } } diff --git a/l1-contracts/contracts/dev-contracts/test/UncheckedMathTest.sol b/l1-contracts/contracts/dev-contracts/test/UncheckedMathTest.sol new file mode 100644 index 000000000..4af25cf0d --- /dev/null +++ b/l1-contracts/contracts/dev-contracts/test/UncheckedMathTest.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.20; + +import "../../common/libraries/UncheckedMath.sol"; + +contract UncheckedMathTest { + function uncheckedInc(uint256 _number) external pure returns (uint256) { + return UncheckedMath.uncheckedInc(_number); + } + + function uncheckedAdd(uint256 _lhs, uint256 _rhs) external pure returns (uint256) { + return UncheckedMath.uncheckedAdd(_lhs, _rhs); + } +} diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol index 81c55591d..3212af146 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol @@ -41,6 +41,23 @@ struct InitializeData { address blobVersionedHashRetriever; } +/// @param verifier address of Verifier contract +/// @param verifierParams Verifier config parameters that describes the circuit to be verified +/// @param l2BootloaderBytecodeHash The hash of bootloader L2 bytecode +/// @param l2DefaultAccountBytecodeHash The hash of default account L2 bytecode +/// @param priorityTxMaxGasLimit maximum number of the L2 gas that a user can request for L1 -> L2 transactions +/// @param feeParams Fee parameters to be used for L1->L2 transactions +/// @param blobVersionedHashRetriever Address of contract used to pull the blob versioned hash for a transaction. +struct InitializeDataNewChain { + IVerifier verifier; + VerifierParams verifierParams; + bytes32 l2BootloaderBytecodeHash; + bytes32 l2DefaultAccountBytecodeHash; + uint256 priorityTxMaxGasLimit; + FeeParams feeParams; + address blobVersionedHashRetriever; +} + interface IDiamondInit { function initialize(InitializeData calldata _initData) external returns (bytes32); } diff --git a/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/_AddressAliasHelper_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/_AddressAliasHelper_Shared.t.sol new file mode 100644 index 000000000..034baeb10 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/_AddressAliasHelper_Shared.t.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {Test} from "forge-std/Test.sol"; +import {AddressAliasHelperTest} from "solpp/dev-contracts/test/AddressAliasHelperTest.sol"; + +contract AddressAliasHelperSharedTest is Test { + AddressAliasHelperTest addressAliasHelper; + + function setUp() public { + addressAliasHelper = new AddressAliasHelperTest(); + } + + // add this to be excluded from coverage report + function test() internal virtual {} +} diff --git a/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/applyL1ToL2Alias.t.sol b/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/applyL1ToL2Alias.t.sol new file mode 100644 index 000000000..8916b46aa --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/applyL1ToL2Alias.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {AddressAliasHelperSharedTest} from "./_AddressAliasHelper_Shared.t.sol"; + +contract applyL1ToL2AliasTest is AddressAliasHelperSharedTest { + function testL1toL2AddressConversion() public { + address[2] memory l1Addresses = [ + 0xEEeEfFfffffFffFFFFffFFffFfFfFfffFfFFEEeE, + 0x0000000000000000000000000000081759a874B3 + ]; + address[2] memory l2ExpectedAddresses = [ + 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF, + 0x1111000000000000000000000000081759a885c4 + ]; + + for (uint i; i < l1Addresses.length; i++) { + address l2Address = addressAliasHelper.applyL1ToL2Alias(l1Addresses[i]); + + assertEq(l2Address, l2ExpectedAddresses[i], "L1 to L2 address conversion is not correct"); + } + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/undoL1ToL2Alias.t.sol b/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/undoL1ToL2Alias.t.sol new file mode 100644 index 000000000..1833acf10 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/AddressAliasHelper/undoL1ToL2Alias.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {AddressAliasHelperSharedTest} from "./_AddressAliasHelper_Shared.t.sol"; + +contract undoL1ToL2AliasTest is AddressAliasHelperSharedTest { + function testL2toL1AddressConversion() public { + address[2] memory l2Addresses = [ + 0x1111000000000000000000000000000000001110, + 0x1111000000000000000000000000081759a885c4 + ]; + address[2] memory l1ExpectedAddresses = [ + 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF, + 0x0000000000000000000000000000081759a874B3 + ]; + + for (uint i; i < l2Addresses.length; i++) { + address l1Address = addressAliasHelper.undoL1ToL2Alias(l2Addresses[i]); + + assertEq(l1Address, l1ExpectedAddresses[i], "L2 to L1 address conversion is not correct"); + } + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/Utils/Utils.sol b/l1-contracts/test/foundry/unit/concrete/Utils/Utils.sol index 31904d883..d65b839d3 100644 --- a/l1-contracts/test/foundry/unit/concrete/Utils/Utils.sol +++ b/l1-contracts/test/foundry/unit/concrete/Utils/Utils.sol @@ -7,11 +7,13 @@ import {UtilsFacet} from "../Utils/UtilsFacet.sol"; import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; import {DiamondInit} from "solpp/state-transition/chain-deps/DiamondInit.sol"; import {DiamondProxy} from "solpp/state-transition/chain-deps/DiamondProxy.sol"; +import {AdminFacet} from "solpp/state-transition/chain-deps/facets/Admin.sol"; +import {ExecutorFacet} from "solpp/state-transition/chain-deps/facets/Executor.sol"; import {GettersFacet} from "solpp/state-transition/chain-deps/facets/Getters.sol"; import {MailboxFacet} from "solpp/state-transition/chain-deps/facets/Mailbox.sol"; import {IVerifier, VerifierParams} from "solpp/state-transition/chain-deps/ZkSyncStateTransitionStorage.sol"; import {FeeParams, PubdataPricingMode} from "solpp/state-transition/chain-deps/ZkSyncStateTransitionStorage.sol"; -import {InitializeData} from "solpp/state-transition/chain-interfaces/IDiamondInit.sol"; +import {InitializeData, InitializeDataNewChain} from "solpp/state-transition/chain-interfaces/IDiamondInit.sol"; import {IExecutor, SystemLogKey} from "solpp/state-transition/chain-interfaces/IExecutor.sol"; bytes32 constant DEFAULT_L2_LOGS_TREE_ROOT_HASH = 0x0000000000000000000000000000000000000000000000000000000000000000; @@ -92,6 +94,23 @@ library Utils { return logs; } + function createSystemLogsWithUpgradeTransaction( + bytes32 _expectedSystemContractUpgradeTxHash + ) public pure returns (bytes[] memory) { + bytes[] memory logsWithoutUpgradeTx = createSystemLogs(); + bytes[] memory logs = new bytes[](logsWithoutUpgradeTx.length + 1); + for (uint256 i = 0; i < logsWithoutUpgradeTx.length; i++) { + logs[i] = logsWithoutUpgradeTx[i]; + } + logs[logsWithoutUpgradeTx.length] = constructL2Log( + true, + L2_BOOTLOADER_ADDRESS, + uint256(SystemLogKey.EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY), + _expectedSystemContractUpgradeTxHash + ); + return logs; + } + function createStoredBatchInfo() public pure returns (IExecutor.StoredBatchInfo memory) { return IExecutor.StoredBatchInfo({ @@ -141,6 +160,31 @@ library Utils { return result; } + function getAdminSelectors() public view returns (bytes4[] memory) { + bytes4[] memory selectors = new bytes4[](11); + selectors[0] = AdminFacet.setPendingAdmin.selector; + selectors[1] = AdminFacet.acceptAdmin.selector; + selectors[2] = AdminFacet.setValidator.selector; + selectors[3] = AdminFacet.setPorterAvailability.selector; + selectors[4] = AdminFacet.setPriorityTxMaxGasLimit.selector; + selectors[5] = AdminFacet.changeFeeParams.selector; + selectors[6] = AdminFacet.setTokenMultiplier.selector; + selectors[7] = AdminFacet.upgradeChainFromVersion.selector; + selectors[8] = AdminFacet.executeUpgrade.selector; + selectors[9] = AdminFacet.freezeDiamond.selector; + selectors[10] = AdminFacet.unfreezeDiamond.selector; + return selectors; + } + + function getExecutorSelectors() public view returns (bytes4[] memory) { + bytes4[] memory selectors = new bytes4[](4); + selectors[0] = ExecutorFacet.commitBatches.selector; + selectors[1] = ExecutorFacet.proveBatches.selector; + selectors[2] = ExecutorFacet.executeBatches.selector; + selectors[3] = ExecutorFacet.revertBatches.selector; + return selectors; + } + function getGettersSelectors() public pure returns (bytes4[] memory) { bytes4[] memory selectors = new bytes4[](29); selectors[0] = GettersFacet.getVerifier.selector; @@ -171,6 +215,7 @@ library Utils { selectors[25] = GettersFacet.getTotalBatchesCommitted.selector; selectors[26] = GettersFacet.getTotalBatchesVerified.selector; selectors[27] = GettersFacet.getTotalBatchesExecuted.selector; + selectors[28] = GettersFacet.getL2SystemContractsUpgradeTxHash.selector; return selectors; } @@ -321,6 +366,19 @@ library Utils { }); } + function makeInitializeDataForNewChain() public pure returns (InitializeDataNewChain memory) { + return + InitializeDataNewChain({ + verifier: makeVerifier(), + verifierParams: makeVerifierParams(), + l2BootloaderBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, + l2DefaultAccountBytecodeHash: 0x0100000000000000000000000000000000000000000000000000000000000000, + priorityTxMaxGasLimit: 80000000, + feeParams: makeFeeParams(), + blobVersionedHashRetriever: address(0x23746765237749923040872834) + }); + } + function makeDiamondProxy(Diamond.FacetCut[] memory facetCuts) public returns (address) { DiamondInit diamondInit = new DiamondInit(); bytes memory diamondInitData = abi.encodeWithSelector(diamondInit.initialize.selector, makeInitializeData()); diff --git a/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/UncheckedAdd.t.sol b/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/UncheckedAdd.t.sol index 8091ff8c8..98f21cfae 100644 --- a/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/UncheckedAdd.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/UncheckedAdd.t.sol @@ -1,17 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.20; -import {UncheckedMathTest} from "./_UncheckedMath_Shared.t.sol"; - -import {UncheckedMath} from "solpp/common/libraries/UncheckedMath.sol"; - -contract UncheckedAddTest is UncheckedMathTest { - using UncheckedMath for uint256; +import {UncheckedMathSharedTest} from "./_UncheckedMath_Shared.t.sol"; +contract UncheckedAddTest is UncheckedMathSharedTest { function test_Add() public { uint256 a = 1234; uint256 b = 4321; - uint256 c = a.uncheckedAdd(b); + uint256 c = uncheckedMath.uncheckedAdd(a, b); assertEq(c, 5555); } @@ -20,7 +16,7 @@ contract UncheckedAddTest is UncheckedMathTest { uint256 b = 1; // uncheckedAdd does not fail - uint256 c = a.uncheckedAdd(b); + uint256 c = uncheckedMath.uncheckedAdd(a, b); assertEq(c, 0); // regular addition fails with overflow diff --git a/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/UncheckedInc.t.sol b/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/UncheckedInc.t.sol index 7b007de20..9efc8a17a 100644 --- a/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/UncheckedInc.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/UncheckedInc.t.sol @@ -1,16 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.20; -import {UncheckedMathTest} from "./_UncheckedMath_Shared.t.sol"; - -import {UncheckedMath} from "solpp/common/libraries/UncheckedMath.sol"; - -contract UncheckedIncTest is UncheckedMathTest { - using UncheckedMath for uint256; +import {UncheckedMathSharedTest} from "./_UncheckedMath_Shared.t.sol"; +contract UncheckedIncTest is UncheckedMathSharedTest { function test_Inc() public { uint256 a = 1234; - uint256 c = a.uncheckedInc(); + uint256 c = uncheckedMath.uncheckedInc(a); assertEq(c, 1235); } @@ -18,7 +14,7 @@ contract UncheckedIncTest is UncheckedMathTest { uint256 a = type(uint256).max; // uncheckedInc does not fail - uint256 c = a.uncheckedInc(); + uint256 c = uncheckedMath.uncheckedInc(a); assertEq(c, 0); // regular addition fails with overflow diff --git a/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/_UncheckedMath_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/_UncheckedMath_Shared.t.sol index badc0233e..c39ca64e5 100644 --- a/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/_UncheckedMath_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/common/libraries/UncheckedMath/_UncheckedMath_Shared.t.sol @@ -2,5 +2,15 @@ pragma solidity 0.8.20; import {Test} from "forge-std/Test.sol"; +import {UncheckedMathTest} from "solpp/dev-contracts/test/UncheckedMathTest.sol"; -contract UncheckedMathTest is Test {} +contract UncheckedMathSharedTest is Test { + UncheckedMathTest uncheckedMath; + + function setUp() public { + uncheckedMath = new UncheckedMathTest(); + } + + // add this to be excluded from coverage report + function test() internal virtual {} +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol new file mode 100644 index 000000000..b3435cf05 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol @@ -0,0 +1,34 @@ +// // SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; +import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; +import {DiamondProxy} from "solpp/state-transition/chain-deps/DiamondProxy.sol"; + +contract createNewChainTest is StateTransitionManagerTest { + function test_RevertWhen_InitialDiamondCutHashMismatch() public { + Diamond.DiamondCutData memory initialDiamondCutData = getDiamondCutData(sharedBridge); + + vm.expectRevert(bytes("StateTransition: initial cutHash mismatch")); + + createNewChain(initialDiamondCutData); + } + + function test_RevertWhen_CalledNotByBridgehub() public { + Diamond.DiamondCutData memory initialDiamondCutData = getDiamondCutData(diamondInit); + + vm.expectRevert(bytes("StateTransition: only bridgehub")); + + chainContractAddress.createNewChain(chainId, baseToken, sharedBridge, admin, abi.encode(initialDiamondCutData)); + } + + function test_SuccessfulCreationOfNewChain() public { + createNewChain(getDiamondCutData(diamondInit)); + + address admin = chainContractAddress.getChainAdmin(chainId); + address newChainAddress = chainContractAddress.stateTransition(chainId); + + assertEq(newChainAdmin, admin); + assertNotEq(newChainAddress, address(0)); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/FreezeChain.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/FreezeChain.t.sol new file mode 100644 index 000000000..b96f93081 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/FreezeChain.t.sol @@ -0,0 +1,31 @@ +// // SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; +import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; +import {DiamondProxy} from "solpp/state-transition/chain-deps/DiamondProxy.sol"; +import {GettersFacet} from "solpp/state-transition/chain-deps/facets/Getters.sol"; + +contract freezeChainTest is StateTransitionManagerTest { + function test_FreezingChain() public { + createNewChain(getDiamondCutData(diamondInit)); + + address newChainAddress = chainContractAddress.stateTransition(chainId); + GettersFacet gettersFacet = GettersFacet(newChainAddress); + bool isChainFrozen = gettersFacet.isDiamondStorageFrozen(); + assertEq(isChainFrozen, false); + + vm.stopPrank(); + vm.startPrank(governor); + + chainContractAddress.freezeChain(block.chainid); + + // Repeated call should revert + vm.expectRevert(bytes.concat("q1")); // storage frozen + chainContractAddress.freezeChain(block.chainid); + + // Call fails as storage is frozen + vm.expectRevert(bytes.concat("q1")); + isChainFrozen = gettersFacet.isDiamondStorageFrozen(); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/RevertBatches.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/RevertBatches.t.sol new file mode 100644 index 000000000..f1c23d582 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/RevertBatches.t.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {Vm} from "forge-std/Test.sol"; + +import {Utils, L2_SYSTEM_CONTEXT_ADDRESS} from "../../Utils/Utils.sol"; +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; + +import {COMMIT_TIMESTAMP_NOT_OLDER, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY_STRING_KECCAK} from "solpp/common/Config.sol"; +import {IExecutor, SystemLogKey} from "solpp/state-transition/chain-interfaces/IExecutor.sol"; +import {GettersFacet} from "solpp/state-transition/chain-deps/facets/Getters.sol"; +import {AdminFacet} from "solpp/state-transition/chain-deps/facets/Admin.sol"; +import {ExecutorFacet} from "solpp/state-transition/chain-deps/facets/Executor.sol"; +import {IExecutor} from "solpp/state-transition/chain-interfaces/IExecutor.sol"; +import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; + +contract revertBatchesTest is StateTransitionManagerTest { + // Items for logs & commits + uint256 internal currentTimestamp; + IExecutor.CommitBatchInfo internal newCommitBatchInfo; + IExecutor.StoredBatchInfo internal newStoredBatchInfo; + IExecutor.StoredBatchInfo internal genesisStoredBatchInfo; + IExecutor.ProofInput internal proofInput; + + // Facets exposing the diamond + AdminFacet internal adminFacet; + ExecutorFacet internal executorFacet; + GettersFacet internal gettersFacet; + + function test_SuccessfulBatchReverting() public { + createNewChain(getDiamondCutData(diamondInit)); + + address newChainAddress = chainContractAddress.stateTransition(chainId); + + executorFacet = ExecutorFacet(address(newChainAddress)); + gettersFacet = GettersFacet(address(newChainAddress)); + adminFacet = AdminFacet(address(newChainAddress)); + + // Initital setup for loge & commits + vm.stopPrank(); + vm.startPrank(newChainAdmin); + + genesisStoredBatchInfo = IExecutor.StoredBatchInfo({ + batchNumber: 0, + batchHash: bytes32(""), + indexRepeatedStorageChanges: 0, + numberOfLayer1Txs: 0, + priorityOperationsHash: EMPTY_STRING_KECCAK, + l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, + timestamp: 0, + commitment: bytes32("") + }); + + adminFacet.setTokenMultiplier(1, 1); + + uint256[] memory recursiveAggregationInput; + uint256[] memory serializedProof; + proofInput = IExecutor.ProofInput(recursiveAggregationInput, serializedProof); + + // foundry's default value is 1 for the block's timestamp, it is expected + // that block.timestamp > COMMIT_TIMESTAMP_NOT_OLDER + 1 + vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1 + 1); + currentTimestamp = block.timestamp; + + bytes memory l2Logs = Utils.encodePacked(Utils.createSystemLogs()); + newCommitBatchInfo = IExecutor.CommitBatchInfo({ + batchNumber: 1, + timestamp: uint64(currentTimestamp), + indexRepeatedStorageChanges: 0, + newStateRoot: Utils.randomBytes32("newStateRoot"), + numberOfLayer1Txs: 0, + priorityOperationsHash: keccak256(""), + bootloaderHeapInitialContentsHash: Utils.randomBytes32("bootloaderHeapInitialContentsHash"), + eventsQueueStateHash: Utils.randomBytes32("eventsQueueStateHash"), + systemLogs: l2Logs, + pubdataCommitments: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + }); + + // Commit & prove batches + vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1); + currentTimestamp = block.timestamp; + + bytes32 expectedSystemContractUpgradeTxHash = gettersFacet.getL2SystemContractsUpgradeTxHash(); + bytes[] memory correctL2Logs = Utils.createSystemLogsWithUpgradeTransaction( + expectedSystemContractUpgradeTxHash + ); + + correctL2Logs[uint256(uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY))] = Utils.constructL2Log( + true, + L2_SYSTEM_CONTEXT_ADDRESS, + uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY), + Utils.packBatchTimestampAndBlockTimestamp(currentTimestamp, currentTimestamp) + ); + + l2Logs = Utils.encodePacked(correctL2Logs); + newCommitBatchInfo.timestamp = uint64(currentTimestamp); + newCommitBatchInfo.systemLogs = l2Logs; + + IExecutor.CommitBatchInfo[] memory commitBatchInfoArray = new IExecutor.CommitBatchInfo[](1); + commitBatchInfoArray[0] = newCommitBatchInfo; + + vm.stopPrank(); + vm.startPrank(validator); + vm.recordLogs(); + executorFacet.commitBatches(genesisStoredBatchInfo, commitBatchInfoArray); + Vm.Log[] memory entries = vm.getRecordedLogs(); + + newStoredBatchInfo = IExecutor.StoredBatchInfo({ + batchNumber: 1, + batchHash: entries[0].topics[2], + indexRepeatedStorageChanges: 0, + numberOfLayer1Txs: 0, + priorityOperationsHash: keccak256(""), + l2LogsTreeRoot: 0, + timestamp: currentTimestamp, + commitment: entries[0].topics[3] + }); + + IExecutor.StoredBatchInfo[] memory storedBatchInfoArray = new IExecutor.StoredBatchInfo[](1); + storedBatchInfoArray[0] = newStoredBatchInfo; + + executorFacet.proveBatches(genesisStoredBatchInfo, storedBatchInfoArray, proofInput); + + // Test batch revert triggered from STM + vm.stopPrank(); + vm.startPrank(governor); + + uint256 totalBlocksCommittedBefore = gettersFacet.getTotalBlocksCommitted(); + assertEq(totalBlocksCommittedBefore, 1, "totalBlocksCommittedBefore"); + + uint256 totalBlocksVerifiedBefore = gettersFacet.getTotalBlocksVerified(); + assertEq(totalBlocksVerifiedBefore, 1, "totalBlocksVerifiedBefore"); + + chainContractAddress.revertBatches(chainId, 0); + + uint256 totalBlocksCommitted = gettersFacet.getTotalBlocksCommitted(); + assertEq(totalBlocksCommitted, 0, "totalBlocksCommitted"); + + uint256 totalBlocksVerified = gettersFacet.getTotalBlocksVerified(); + assertEq(totalBlocksVerified, 0, "totalBlocksVerified"); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetInitialCutHash.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetInitialCutHash.t.sol new file mode 100644 index 000000000..29e2e89e4 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetInitialCutHash.t.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; +import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; + +contract setInitialCutHashTest is StateTransitionManagerTest { + function test_SettingInitialCutHash() public { + bytes32 initialCutHash = keccak256(abi.encode(getDiamondCutData(address(diamondInit)))); + address randomDiamondInit = address(0x303030303030303030303); + + assertEq(chainContractAddress.initialCutHash(), initialCutHash, "Initial cut hash is not correct"); + + Diamond.DiamondCutData memory newDiamondCutData = getDiamondCutData(address(randomDiamondInit)); + bytes32 newCutHash = keccak256(abi.encode(newDiamondCutData)); + + chainContractAddress.setInitialCutHash(newDiamondCutData); + + assertEq(chainContractAddress.initialCutHash(), newCutHash, "Initial cut hash update was not successful"); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetNewVersionUpgrade.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetNewVersionUpgrade.t.sol new file mode 100644 index 000000000..a27ba2943 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetNewVersionUpgrade.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; +import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; + +contract setNewVersionUpgradeTest is StateTransitionManagerTest { + function test_SettingNewVersionUpgrade() public { + assertEq(chainContractAddress.protocolVersion(), 0, "Initial protocol version is not correct"); + + address randomDiamondInit = address(0x303030303030303030303); + Diamond.DiamondCutData memory newDiamondCutData = getDiamondCutData(address(randomDiamondInit)); + bytes32 newCutHash = keccak256(abi.encode(newDiamondCutData)); + + chainContractAddress.setNewVersionUpgrade(newDiamondCutData, 0, 1); + + assertEq(chainContractAddress.upgradeCutHash(0), newCutHash, "Diamond cut upgrade was not successful"); + assertEq(chainContractAddress.protocolVersion(), 1, "New protocol version is not correct"); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetUpgradeDiamondCut.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetUpgradeDiamondCut.t.sol new file mode 100644 index 000000000..44b00ac1a --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetUpgradeDiamondCut.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; +import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; + +contract setUpgradeDiamondCutTest is StateTransitionManagerTest { + function test_SettingUpgradeDiamondCut() public { + assertEq(chainContractAddress.protocolVersion(), 0, "Initial protocol version is not correct"); + + address randomDiamondInit = address(0x303030303030303030303); + Diamond.DiamondCutData memory newDiamondCutData = getDiamondCutData(address(randomDiamondInit)); + bytes32 newCutHash = keccak256(abi.encode(newDiamondCutData)); + + chainContractAddress.setUpgradeDiamondCut(newDiamondCutData, 0); + + assertEq(chainContractAddress.upgradeCutHash(0), newCutHash, "Diamond cut upgrade was not successful"); + assertEq(chainContractAddress.protocolVersion(), 0, "Protocol version should not change"); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetValidatorTimelock.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetValidatorTimelock.t.sol new file mode 100644 index 000000000..1ae878fd2 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/SetValidatorTimelock.t.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; + +contract setValidatorTimelockTest is StateTransitionManagerTest { + function test_SettingValidatorTimelock() public { + assertEq( + chainContractAddress.validatorTimelock(), + validator, + "Initial validator timelock address is not correct" + ); + + address newValidatorTimelock = address(0x0000000000000000000000000000000000004235); + chainContractAddress.setValidatorTimelock(newValidatorTimelock); + + assertEq(chainContractAddress.validatorTimelock(), validator, "Validator timelock update was not successful"); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/StateTransitionGovernorZero.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/StateTransitionGovernorZero.t.sol new file mode 100644 index 000000000..8ce1a16ec --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/StateTransitionGovernorZero.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; +import {StateTransitionManager} from "solpp/state-transition/StateTransitionManager.sol"; +import {StateTransitionManagerInitializeData} from "solpp/state-transition/IStateTransitionManager.sol"; +import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; + +contract initializingSTMGovernorZeroTest is StateTransitionManagerTest { + function test_InitializingSTMWithGovernorZeroShouldRevert() public { + StateTransitionManagerInitializeData memory stmInitializeDataNoGovernor = StateTransitionManagerInitializeData({ + governor: address(0), + validatorTimelock: validator, + genesisUpgrade: address(genesisUpgradeContract), + genesisBatchHash: bytes32(""), + genesisIndexRepeatedStorageChanges: 0, + genesisBatchCommitment: bytes32(""), + diamondCut: getDiamondCutData(address(diamondInit)), + protocolVersion: 0 + }); + + vm.expectRevert(bytes("StateTransition: governor zero")); + TransparentUpgradeableProxy transparentUpgradeableProxyReverting = new TransparentUpgradeableProxy( + address(stateTransitionManager), + admin, + abi.encodeCall(StateTransitionManager.initialize, stmInitializeDataNoGovernor) + ); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol new file mode 100644 index 000000000..d4292ca22 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {Utils} from "foundry-test/unit/concrete/Utils/Utils.sol"; +import {UtilsFacet} from "foundry-test/unit/concrete/Utils/UtilsFacet.sol"; +import {AdminFacet} from "solpp/state-transition/chain-deps/facets/Admin.sol"; +import {ExecutorFacet} from "solpp/state-transition/chain-deps/facets/Executor.sol"; +import {GettersFacet} from "solpp/state-transition/chain-deps/facets/Getters.sol"; +import {Diamond} from "solpp/state-transition/libraries/Diamond.sol"; +import {DiamondInit} from "solpp/state-transition/chain-deps/DiamondInit.sol"; +import {GenesisUpgrade} from "solpp/upgrades/GenesisUpgrade.sol"; +import {IDiamondInit} from "solpp/state-transition/chain-interfaces/IDiamondInit.sol"; +import {InitializeData, InitializeDataNewChain} from "solpp/state-transition/chain-interfaces/IDiamondInit.sol"; +import {IVerifier} from "solpp/state-transition/chain-interfaces/IVerifier.sol"; +import {StateTransitionManager} from "solpp/state-transition/StateTransitionManager.sol"; +import {StateTransitionManagerInitializeData} from "solpp/state-transition/IStateTransitionManager.sol"; + +contract StateTransitionManagerTest is Test { + StateTransitionManager internal stateTransitionManager; + StateTransitionManager internal chainContractAddress; + GenesisUpgrade internal genesisUpgradeContract; + address internal bridgehub; + address internal diamondInit; + address internal constant governor = address(0x1010101); + address internal constant admin = address(0x2020202); + address internal constant baseToken = address(0x3030303); + address internal constant sharedBridge = address(0x4040404); + address internal constant validator = address(0x5050505); + address internal newChainAdmin; + uint256 chainId = block.chainid; + + Diamond.FacetCut[] internal facetCuts; + + function setUp() public { + bridgehub = makeAddr("bridgehub"); + newChainAdmin = makeAddr("chainadmin"); + + vm.startPrank(bridgehub); + stateTransitionManager = new StateTransitionManager(bridgehub); + diamondInit = address(new DiamondInit()); + genesisUpgradeContract = new GenesisUpgrade(); + + facetCuts.push( + Diamond.FacetCut({ + facet: address(new UtilsFacet()), + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getUtilsFacetSelectors() + }) + ); + facetCuts.push( + Diamond.FacetCut({ + facet: address(new AdminFacet()), + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAdminSelectors() + }) + ); + facetCuts.push( + Diamond.FacetCut({ + facet: address(new ExecutorFacet()), + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getExecutorSelectors() + }) + ); + facetCuts.push( + Diamond.FacetCut({ + facet: address(new GettersFacet()), + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getGettersSelectors() + }) + ); + + StateTransitionManagerInitializeData memory stmInitializeDataNoGovernor = StateTransitionManagerInitializeData({ + governor: address(0), + validatorTimelock: validator, + genesisUpgrade: address(genesisUpgradeContract), + genesisBatchHash: bytes32(""), + genesisIndexRepeatedStorageChanges: 0, + genesisBatchCommitment: bytes32(""), + diamondCut: getDiamondCutData(address(diamondInit)), + protocolVersion: 0 + }); + + vm.expectRevert(bytes.concat("StateTransition: governor zero")); + TransparentUpgradeableProxy transparentUpgradeableProxyReverting = new TransparentUpgradeableProxy( + address(stateTransitionManager), + admin, + abi.encodeCall(StateTransitionManager.initialize, stmInitializeDataNoGovernor) + ); + + StateTransitionManagerInitializeData memory stmInitializeData = StateTransitionManagerInitializeData({ + governor: governor, + validatorTimelock: validator, + genesisUpgrade: address(genesisUpgradeContract), + genesisBatchHash: bytes32(""), + genesisIndexRepeatedStorageChanges: 0, + genesisBatchCommitment: bytes32(""), + diamondCut: getDiamondCutData(address(diamondInit)), + protocolVersion: 0 + }); + + TransparentUpgradeableProxy transparentUpgradeableProxy = new TransparentUpgradeableProxy( + address(stateTransitionManager), + admin, + abi.encodeCall(StateTransitionManager.initialize, stmInitializeData) + ); + chainContractAddress = StateTransitionManager(address(transparentUpgradeableProxy)); + + vm.stopPrank(); + vm.startPrank(governor); + } + + function getDiamondCutData(address _diamondInit) internal view returns (Diamond.DiamondCutData memory) { + InitializeDataNewChain memory initializeData = Utils.makeInitializeDataForNewChain(); + + bytes memory initCalldata = abi.encode(initializeData); + + return Diamond.DiamondCutData({facetCuts: facetCuts, initAddress: _diamondInit, initCalldata: initCalldata}); + } + + function createNewChain(Diamond.DiamondCutData memory _diamondCut) internal { + vm.stopPrank(); + vm.startPrank(bridgehub); + + chainContractAddress.createNewChain(chainId, baseToken, sharedBridge, newChainAdmin, abi.encode(_diamondCut)); + } + + // add this to be excluded from coverage report + function test() internal virtual {} +}