From acd603a785cbaee9341b360cf80785449d06f5e7 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 16 Aug 2024 09:47:33 +0100 Subject: [PATCH 01/51] bridgehub small fixes, some cleanup --- .../contracts/bridgehub/Bridgehub.sol | 5 ++++- .../deploy-shared-bridge-on-l2-through-l1.ts | 22 +++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 2f9ef12c9..9b6a88b3f 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -113,6 +113,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus // This is indeed true, since the only methods where this immutable is used are the ones with `onlyL1` modifier. ETH_TOKEN_ASSET_ID = DataEncoding.encodeNTVAssetId(block.chainid, ETH_TOKEN_ADDRESS); _transferOwnership(_owner); + whitelistedSettlementLayers[_l1ChainId] = true; } /// @notice used to initialize the contract @@ -120,6 +121,8 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @param _owner the owner of the contract function initialize(address _owner) external reentrancyGuardInitializer { _transferOwnership(_owner); + + whitelistedSettlementLayers[L1_CHAIN_ID] = true; } //// Initialization and registration @@ -538,7 +541,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32 _assetId, address _prevMsgSender, bytes calldata _data - ) external payable override onlyAssetRouter onlyL1 returns (bytes memory bridgehubMintData) { + ) external payable override onlyAssetRouter returns (bytes memory bridgehubMintData) { require(whitelistedSettlementLayers[_settlementChainId], "BH: SL not whitelisted"); (uint256 _chainId, bytes memory _stmData, bytes memory _chainData) = abi.decode(_data, (uint256, bytes, bytes)); diff --git a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts index 3ca81bf74..849a118f1 100644 --- a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts +++ b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts @@ -59,16 +59,16 @@ async function setL2TokenBeacon(deployer: Deployer, chainId: string, gasPrice: B } const l2NTV = L2NativeTokenVaultFactory.connect(L2_NATIVE_TOKEN_VAULT_ADDRESS, deployer.deployWallet); - const receipt = await deployer.executeUpgradeOnL2( - chainId, - L2_NATIVE_TOKEN_VAULT_ADDRESS, - gasPrice, - l2NTV.interface.encodeFunctionData("configureL2TokenBeacon", [false, ethers.constants.AddressZero]), - priorityTxMaxGasLimit - ); - if (deployer.verbose) { - console.log("Set L2Token Beacon, upgrade hash", receipt.transactionHash); - } + // const receipt = await deployer.executeUpgradeOnL2( + // chainId, + // L2_NATIVE_TOKEN_VAULT_ADDRESS, + // gasPrice, + // l2NTV.interface.encodeFunctionData("configureL2TokenBeacon", [false, ethers.constants.AddressZero]), + // priorityTxMaxGasLimit + // ); + // if (deployer.verbose) { + // console.log("Set L2Token Beacon, upgrade hash", receipt.transactionHash); + // } const bridgehub = BridgehubFactory.connect(L2_BRIDGEHUB_ADDRESS, deployer.deployWallet); const receipt2 = await deployer.executeUpgradeOnL2( chainId, @@ -140,7 +140,7 @@ async function main() { console.log("Initialization of the chain governance will be skipped"); } - await deploySharedBridgeOnL2ThroughL1(deployer, chainId, gasPrice); + // await deploySharedBridgeOnL2ThroughL1(deployer, chainId, gasPrice); }); await program.parseAsync(process.argv); From 6d0ceb9bb87af267467138adf1ca10ecad0b6d23 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 16 Aug 2024 12:49:06 +0100 Subject: [PATCH 02/51] small fixes --- .../contracts/bridgehub/Bridgehub.sol | 33 +++++++++++++++---- .../StateTransitionManager.sol | 11 +++++-- .../chain-deps/facets/Admin.sol | 1 + 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index d84f2a909..2e1a368cc 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -89,6 +89,8 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @dev Sync layer chain is expected to have .. as the base token. mapping(uint256 chainId => bool isWhitelistedSettlementLayer) public whitelistedSettlementLayers; + bool private migrationPuased; + modifier onlyOwnerOrAdmin() { require(msg.sender == admin || msg.sender == owner(), "BH: not owner or admin"); _; @@ -115,6 +117,11 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus _; } + modifier whenMigrationsNotPaused() { + require(!migrationPuased, "BH: migrations paused"); + _; + } + /// @notice to avoid parity hack constructor(uint256 _l1ChainId, address _owner, uint256 _maxNumberOfHyperchains) reentrancyGuardInitializer { _disableInitializers(); @@ -592,7 +599,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32 _assetId, address _prevMsgSender, bytes calldata _data - ) external payable override onlyAssetRouter returns (bytes memory bridgehubMintData) { + ) external payable override onlyAssetRouter whenMigrationsNotPaused returns (bytes memory bridgehubMintData) { require(whitelistedSettlementLayers[_settlementChainId], "BH: SL not whitelisted"); (uint256 _chainId, bytes memory _stmData, bytes memory _chainData) = abi.decode(_data, (uint256, bytes, bytes)); @@ -623,7 +630,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus uint256, // originChainId bytes32 _assetId, bytes calldata _bridgehubMintData - ) external payable override onlyAssetRouter returns (address l1Receiver) { + ) external payable override onlyAssetRouter whenMigrationsNotPaused returns (address l1Receiver) { (uint256 _chainId, bytes memory _stmData, bytes memory _chainMintData) = abi.decode( _bridgehubMintData, (uint256, bytes, bytes) @@ -634,15 +641,17 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus settlementLayer[_chainId] = block.chainid; stateTransitionManager[_chainId] = stm; - address hyperchain; + address hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(_chainId, _stmData); if (hyperchainMap.contains(_chainId)) { + require(hyperchain == address(0), "BH: deployed again"); hyperchain = hyperchainMap.get(_chainId); } else { - hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(_chainId, _stmData); + require(hyperchain != address(0), "BH: chain not registered"); + _registerNewHyperchain(_chainId, hyperchain); + /// Question: why do we need addNewChainIfNeeded? Why is addNewChain not enough? + messageRoot.addNewChainIfNeeded(_chainId); } - messageRoot.addNewChainIfNeeded(_chainId); - _registerNewHyperchain(_chainId, hyperchain); IZkSyncHyperchain(hyperchain).forwardedBridgeMint(_chainMintData); return address(0); } @@ -656,7 +665,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32 _assetId, address _depositSender, bytes calldata _data - ) external payable override onlyAssetRouter onlyL1 {} + ) external payable override onlyAssetRouter whenMigrationsNotPaused {} /*////////////////////////////////////////////////////////////// PAUSE @@ -671,4 +680,14 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus function unpause() external onlyOwner { _unpause(); } + + /// @notice Pauses migration functions. + function pauseMigration() external onlyOwner { + migrationPuased = true; + } + + /// @notice Unpauses migration functions. + function unpauseMigration() external onlyOwner { + migrationPuased = false; + } } diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index 087a1fc50..4e48f5446 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -445,15 +445,15 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own ) external view override onlyBridgehub returns (bytes memory stmForwardedBridgeMintData) { // Note that the `_diamondCut` here is not for the current chain, for the chain where the migration // happens. The correctness of it will be checked on the STM on the new settlement layer. - (address _newGatewayAdmin, bytes memory _diamondCut) = abi.decode(_data, (address, bytes)); - require(_newGatewayAdmin != address(0), "STM: admin zero"); + (address _newSettlmentLayerAdmin, bytes memory _diamondCut) = abi.decode(_data, (address, bytes)); + require(_newSettlmentLayerAdmin != address(0), "STM: admin zero"); // We ensure that the chain has the latest protocol version to avoid edge cases // related to different protocol version support. address hyperchain = getHyperchain(_chainId); require(IZkSyncHyperchain(hyperchain).getProtocolVersion() == protocolVersion, "STM: outdated pv"); - return abi.encode(IBridgehub(BRIDGE_HUB).baseToken(_chainId), _newGatewayAdmin, protocolVersion, _diamondCut); + return abi.encode(IBridgehub(BRIDGE_HUB).baseToken(_chainId), _newSettlmentLayerAdmin, protocolVersion, _diamondCut); } /// @notice Called by the bridgehub during the migration of a chain to the current settlement layer. @@ -471,6 +471,11 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own // We ensure that the chain has the latest protocol version to avoid edge cases // related to different protocol version support. require(_protocolVersion == protocolVersion, "STM, outdated pv"); + if (getHyperchain(_chainId) != address(0)) { + // Hyperchain already registered + chainAddress = address(0); + return; + } chainAddress = _deployNewChain({ _chainId: _chainId, _baseToken: _baseToken, diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index e68bf5623..f427b3462 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -260,6 +260,7 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { s.storedBatchHashes[batchesExecuted + i] = _commitment.batchHashes[i]; } + // if (block.chainId == L1_CHAIN_ID) s.priorityTree.initFromCommitment(_commitment.priorityTree); s.l2SystemContractsUpgradeTxHash = _commitment.l2SystemContractsUpgradeTxHash; From c16818369980df9107b20fb6c6922873b08abb6c Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 16 Aug 2024 14:41:29 +0100 Subject: [PATCH 03/51] more fixes --- .../contracts/bridge/L1NativeTokenVault.sol | 5 +- .../bridge/interfaces/IL1AssetHandler.sol | 6 +- .../contracts/bridgehub/Bridgehub.sol | 15 +- .../bridgehub/STMDeploymentTracker.sol | 7 +- .../StateTransitionManager.sol | 13 +- .../chain-deps/facets/Admin.sol | 24 +++- .../chain-interfaces/IAdmin.sol | 2 +- .../libraries/PriorityTree.sol | 13 ++ l1-contracts/deploy-scripts/Gateway.s.sol | 5 +- .../foundry/integration/GatewayTests.t.sol | 11 +- .../{synclayer.spec.ts => gateway.spec.ts} | 3 +- .../deploy-shared-bridge-on-l2-through-l1.ts | 129 +----------------- 12 files changed, 79 insertions(+), 154 deletions(-) rename l1-contracts/test/unit_tests/{synclayer.spec.ts => gateway.spec.ts} (97%) diff --git a/l1-contracts/contracts/bridge/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/L1NativeTokenVault.sol index 366bbf260..74c4cae78 100644 --- a/l1-contracts/contracts/bridge/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/L1NativeTokenVault.sol @@ -114,11 +114,10 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, Ownable2Ste uint256 _chainId, bytes32 _assetId, bytes calldata _data - ) external payable override onlyBridge whenNotPaused returns (address l1Receiver) { + ) external payable override onlyBridge whenNotPaused { // here we are minting the tokens after the bridgeBurn has happened on an L2, so we can assume the l1Token is not zero address l1Token = tokenAddress[_assetId]; - uint256 amount; - (amount, l1Receiver) = abi.decode(_data, (uint256, address)); + (uint256 amount, address l1Receiver) = abi.decode(_data, (uint256, address)); // Check that the chain has sufficient balance require(chainBalance[_chainId][l1Token] >= amount, "NTV: not enough funds"); // not enough funds chainBalance[_chainId][l1Token] -= amount; diff --git a/l1-contracts/contracts/bridge/interfaces/IL1AssetHandler.sol b/l1-contracts/contracts/bridge/interfaces/IL1AssetHandler.sol index a707da173..8f8e4f81c 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1AssetHandler.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1AssetHandler.sol @@ -25,11 +25,7 @@ interface IL1AssetHandler { /// @param _chainId the chainId that the message is from /// @param _assetId the assetId of the asset being bridged /// @param _data the actual data specified for the function - function bridgeMint( - uint256 _chainId, - bytes32 _assetId, - bytes calldata _data - ) external payable returns (address l1Receiver); + function bridgeMint(uint256 _chainId, bytes32 _assetId, bytes calldata _data) external payable; /// @param _chainId the chainId that the message will be sent to /// @param _l2Value the msg.value of the L2 transaction diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 82ff936ae..7c853ed80 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -640,7 +640,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus uint256, // originChainId bytes32 _assetId, bytes calldata _bridgehubMintData - ) external payable override onlyAssetRouter whenMigrationsNotPaused returns (address l1Receiver) { + ) external payable override onlyAssetRouter whenMigrationsNotPaused { (uint256 _chainId, bytes memory _stmData, bytes memory _chainMintData) = abi.decode( _bridgehubMintData, (uint256, bytes, bytes) @@ -651,19 +651,18 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus settlementLayer[_chainId] = block.chainid; stateTransitionManager[_chainId] = stm; - address hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(_chainId, _stmData); - if (hyperchainMap.contains(_chainId)) { - require(hyperchain == address(0), "BH: deployed again"); - hyperchain = hyperchainMap.get(_chainId); - } else { + + address hyperchain = getHyperchain(_chainId); + bool contractAlreadyDeployed = hyperchain != address(0); + if (hyperchain == address(0)) { + hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(_chainId, _stmData); require(hyperchain != address(0), "BH: chain not registered"); _registerNewHyperchain(_chainId, hyperchain); /// Question: why do we need addNewChainIfNeeded? Why is addNewChain not enough? messageRoot.addNewChainIfNeeded(_chainId); } - IZkSyncHyperchain(hyperchain).forwardedBridgeMint(_chainMintData); - return address(0); + IZkSyncHyperchain(hyperchain).forwardedBridgeMint(_chainMintData, contractAlreadyDeployed); } /// @dev IL1AssetHandler interface, used to undo a failed migration of a chain. diff --git a/l1-contracts/contracts/bridgehub/STMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/STMDeploymentTracker.sol index e9ccacb5a..62bf9a9d5 100644 --- a/l1-contracts/contracts/bridgehub/STMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/STMDeploymentTracker.sol @@ -25,6 +25,9 @@ contract STMDeploymentTracker is ISTMDeploymentTracker, ReentrancyGuard, Ownable /// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication. IL1AssetRouter public immutable override L1_ASSET_ROUTER; + /// @dev The encoding version of the data. + bytes1 internal constant ENCODING_VERSION = 0x01; + /// @notice Checks that the message sender is the bridgehub. modifier onlyBridgehub() { // solhint-disable-next-line gas-custom-errors @@ -89,7 +92,9 @@ contract STMDeploymentTracker is ISTMDeploymentTracker, ReentrancyGuard, Ownable // solhint-disable-next-line gas-custom-errors require(_prevMsgSender == owner(), "STMDT: not owner"); - (address _stmL1Address, address _stmL2Address) = abi.decode(_data, (address, address)); + bytes1 encodingVersion = _data[0]; + require(encodingVersion == ENCODING_VERSION, "STMDT: wrong encoding version"); + (address _stmL1Address, address _stmL2Address) = abi.decode(_data[1:], (address, address)); request = _registerSTMAssetOnL2Bridgehub(_chainId, _stmL1Address, _stmL2Address); } diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index 4e48f5446..2409894ac 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -453,7 +453,13 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own address hyperchain = getHyperchain(_chainId); require(IZkSyncHyperchain(hyperchain).getProtocolVersion() == protocolVersion, "STM: outdated pv"); - return abi.encode(IBridgehub(BRIDGE_HUB).baseToken(_chainId), _newSettlmentLayerAdmin, protocolVersion, _diamondCut); + return + abi.encode( + IBridgehub(BRIDGE_HUB).baseToken(_chainId), + _newSettlmentLayerAdmin, + protocolVersion, + _diamondCut + ); } /// @notice Called by the bridgehub during the migration of a chain to the current settlement layer. @@ -471,11 +477,6 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own // We ensure that the chain has the latest protocol version to avoid edge cases // related to different protocol version support. require(_protocolVersion == protocolVersion, "STM, outdated pv"); - if (getHyperchain(_chainId) != address(0)) { - // Hyperchain already registered - chainAddress = address(0); - return; - } chainAddress = _deployNewChain({ _chainId: _chainId, _baseToken: _baseToken, diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index 7304093b1..4460ca785 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -244,9 +244,17 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { } /// @inheritdoc IAdmin - function forwardedBridgeMint(bytes calldata _data) external payable override onlyBridgehub { + function forwardedBridgeMint( + bytes calldata _data, + bool _contractAlreadyDeployed + ) external payable override onlyBridgehub { HyperchainCommitment memory _commitment = abi.decode(_data, (HyperchainCommitment)); + if (_contractAlreadyDeployed) { + s.priorityTree.checkReinit(_commitment.priorityTree); + require(s.settlementLayer != address(0), "Af: not migrated"); + } + uint256 batchesExecuted = _commitment.totalBatchesExecuted; uint256 batchesVerified = _commitment.totalBatchesVerified; uint256 batchesCommitted = _commitment.totalBatchesCommitted; @@ -274,8 +282,18 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { s.storedBatchHashes[batchesExecuted + i] = _commitment.batchHashes[i]; } - // if (block.chainId == L1_CHAIN_ID) - s.priorityTree.initFromCommitment(_commitment.priorityTree); + if (block.chainid == L1_CHAIN_ID) { + // L1 PTree contains all L1->L2 transactions. + require( + s.priorityTree.isHistoricalRoot( + _commitment.priorityTree.sides[_commitment.priorityTree.sides.length - 1] + ), + "Admin: not historical root" + ); + require(_contractAlreadyDeployed, "Af: contract not deployed"); + } else { + s.priorityTree.initFromCommitment(_commitment.priorityTree); + } s.l2SystemContractsUpgradeTxHash = _commitment.l2SystemContractsUpgradeTxHash; s.l2SystemContractsUpgradeBatchNumber = _commitment.l2SystemContractsUpgradeBatchNumber; diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol index a8ebbbc3c..784f6d9fe 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol @@ -147,5 +147,5 @@ interface IAdmin is IZkSyncHyperchainBase { ) external payable; /// @dev Similar to IL1AssetHandler interface, used to receive chains. - function forwardedBridgeMint(bytes calldata _data) external payable; + function forwardedBridgeMint(bytes calldata _data, bool _contractAlreadyDeployed) external payable; } diff --git a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol index 6bf3649e5..a311e19f7 100644 --- a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol +++ b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol @@ -60,6 +60,12 @@ library PriorityTree { return _tree.tree.root(); } + /// @param _root The root to check. + /// @return Returns true if the root is a historical root. + function isHistoricalRoot(Tree storage _tree, bytes32 _root) internal view returns (bool) { + return _tree.historicalRoots[_root]; + } + /// @notice Process the priority operations of a batch. function processBatch(Tree storage _tree, PriorityOpsBatchInfo calldata _priorityOpsData) internal { if (_priorityOpsData.itemHashes.length > 0) { @@ -91,6 +97,13 @@ library PriorityTree { _tree.historicalRoots[_tree.tree.root()] = true; } + /// @notice Reinitialize the tree from a commitment. + function checkReinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal { + require(_tree.startIndex == _commitment.startIndex, "PT: invalid start index"); + require(_tree.unprocessedIndex <= _commitment.unprocessedIndex, "PT: invalid unprocessed index"); + require(_tree.tree._nextLeafIndex <= _commitment.nextLeafIndex, "PT: invalid next leaf index"); + } + /// @notice Returns the commitment to the priority tree. function getCommitment(Tree storage _tree) internal view returns (PriorityTreeCommitment memory commitment) { commitment.nextLeafIndex = _tree.tree._nextLeafIndex; diff --git a/l1-contracts/deploy-scripts/Gateway.s.sol b/l1-contracts/deploy-scripts/Gateway.s.sol index 6e24be0cf..82dff8dec 100644 --- a/l1-contracts/deploy-scripts/Gateway.s.sol +++ b/l1-contracts/deploy-scripts/Gateway.s.sol @@ -214,7 +214,10 @@ contract GatewayScript is Script { refundRecipient: ownable.owner(), secondBridgeAddress: config.stmDeploymentTracker, secondBridgeValue: 0, - secondBridgeCalldata: abi.encode(config.stateTransitionProxy, config.stateTransitionProxy) + secondBridgeCalldata: bytes.concat( + bytes1(0x01), + abi.encode(config.stateTransitionProxy, config.stateTransitionProxy) + ) }); vm.startBroadcast(ownable.owner()); bridgehub.requestL2TransactionTwoBridges{value: expectedCost}(assetRouterRegistrationRequest); diff --git a/l1-contracts/test/foundry/integration/GatewayTests.t.sol b/l1-contracts/test/foundry/integration/GatewayTests.t.sol index dc5e23643..cc13a552b 100644 --- a/l1-contracts/test/foundry/integration/GatewayTests.t.sol +++ b/l1-contracts/test/foundry/integration/GatewayTests.t.sol @@ -167,16 +167,23 @@ contract GatewayTests is L1ContractDeployer, HyperchainDeployer, TokenDeployer, function finishMoveChain() public { IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); IStateTransitionManager stm = IStateTransitionManager(l1Script.getSTM()); - IZkSyncHyperchain chain = IZkSyncHyperchain(bridgehub.getHyperchain(migratingChainId)); + IZkSyncHyperchain migratingChain = IZkSyncHyperchain(bridgehub.getHyperchain(migratingChainId)); bytes32 assetId = bridgehub.stmAssetIdFromChainId(migratingChainId); + vm.startBroadcast(address(stm)); + bridgehub.registerSettlementLayer(gatewayChainId, true); + vm.stopBroadcast(); + bytes memory initialDiamondCut = l1Script.getInitialDiamondCutData(); - bytes memory chainData = abi.encode(AdminFacet(address(chain)).prepareChainCommitment()); + bytes memory chainData = abi.encode(AdminFacet(address(migratingChain)).prepareChainCommitment()); bytes memory stmData = abi.encode(address(1), msg.sender, stm.protocolVersion(), initialDiamondCut); bytes memory bridgehubMintData = abi.encode(mintChainId, stmData, chainData); vm.startBroadcast(address(bridgehub.sharedBridge())); + uint256 currentChainId = block.chainid; + vm.chainId(migratingChainId); bridgehub.bridgeMint(gatewayChainId, assetId, bridgehubMintData); vm.stopBroadcast(); + vm.chainId(currentChainId); } // add this to be excluded from coverage report diff --git a/l1-contracts/test/unit_tests/synclayer.spec.ts b/l1-contracts/test/unit_tests/gateway.spec.ts similarity index 97% rename from l1-contracts/test/unit_tests/synclayer.spec.ts rename to l1-contracts/test/unit_tests/gateway.spec.ts index 0e2ec50aa..6a88c20f6 100644 --- a/l1-contracts/test/unit_tests/synclayer.spec.ts +++ b/l1-contracts/test/unit_tests/gateway.spec.ts @@ -128,7 +128,8 @@ describe("Gateway", function () { refundRecipient: migratingDeployer.deployWallet.address, secondBridgeAddress: stmDeploymentTracker.address, secondBridgeValue: 0, - secondBridgeCalldata: ethers.utils.defaultAbiCoder.encode(["address", "address"], [stm.address, stm.address]), + secondBridgeCalldata: + "0x01" + ethers.utils.defaultAbiCoder.encode(["address", "address"], [stm.address, stm.address]).slice(2), }, ]) ); diff --git a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts index 849a118f1..5d87b95ab 100644 --- a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts +++ b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts @@ -1,102 +1,11 @@ import { Command } from "commander"; -import type { BigNumberish } from "ethers"; -import { Wallet, ethers } from "ethers"; -import { formatUnits, parseUnits } from "ethers/lib/utils"; -import { provider, publishBytecodeFromL1, priorityTxMaxGasLimit } from "./utils"; -import { ethTestConfig } from "./deploy-utils"; - -import { Deployer } from "../../l1-contracts/src.ts/deploy"; -import { GAS_MULTIPLIER } from "../../l1-contracts/scripts/utils"; import * as hre from "hardhat"; -import { - ADDRESS_ONE, - L2_ASSET_ROUTER_ADDRESS, - L2_BRIDGEHUB_ADDRESS, - L2_MESSAGE_ROOT_ADDRESS, - L2_NATIVE_TOKEN_VAULT_ADDRESS, -} from "../../l1-contracts/src.ts/utils"; - -import { L2NativeTokenVaultFactory } from "../typechain"; -import { BridgehubFactory } from "../../l1-contracts/typechain"; +import { L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS } from "../../l1-contracts/src.ts/utils"; export const L2_SHARED_BRIDGE_ABI = hre.artifacts.readArtifactSync("L2SharedBridge").abi; export const L2_STANDARD_TOKEN_PROXY_BYTECODE = hre.artifacts.readArtifactSync("BeaconProxy").bytecode; -export async function publishL2NativeTokenVaultDependencyBytecodesOnL2( - deployer: Deployer, - chainId: string, - gasPrice: BigNumberish -) { - if (deployer.verbose) { - console.log("Providing necessary L2 bytecodes"); - } - - const L2_STANDARD_ERC20_PROXY_FACTORY_BYTECODE = hre.artifacts.readArtifactSync("UpgradeableBeacon").bytecode; - const L2_STANDARD_ERC20_IMPLEMENTATION_BYTECODE = hre.artifacts.readArtifactSync("L2StandardERC20").bytecode; - - const receipt = await ( - await publishBytecodeFromL1( - chainId, - deployer.deployWallet, - [ - L2_STANDARD_ERC20_PROXY_FACTORY_BYTECODE, - L2_STANDARD_ERC20_IMPLEMENTATION_BYTECODE, - L2_STANDARD_TOKEN_PROXY_BYTECODE, - ], - gasPrice - ) - ).wait(); - - if (deployer.verbose) { - console.log("Bytecodes published on L2, hash: ", receipt.transactionHash); - } -} - -async function setL2TokenBeacon(deployer: Deployer, chainId: string, gasPrice: BigNumberish) { - if (deployer.verbose) { - console.log("Setting L2 token beacon"); - } - const l2NTV = L2NativeTokenVaultFactory.connect(L2_NATIVE_TOKEN_VAULT_ADDRESS, deployer.deployWallet); - - // const receipt = await deployer.executeUpgradeOnL2( - // chainId, - // L2_NATIVE_TOKEN_VAULT_ADDRESS, - // gasPrice, - // l2NTV.interface.encodeFunctionData("configureL2TokenBeacon", [false, ethers.constants.AddressZero]), - // priorityTxMaxGasLimit - // ); - // if (deployer.verbose) { - // console.log("Set L2Token Beacon, upgrade hash", receipt.transactionHash); - // } - const bridgehub = BridgehubFactory.connect(L2_BRIDGEHUB_ADDRESS, deployer.deployWallet); - const receipt2 = await deployer.executeUpgradeOnL2( - chainId, - L2_BRIDGEHUB_ADDRESS, - gasPrice, - bridgehub.interface.encodeFunctionData("setAddresses", [ - L2_ASSET_ROUTER_ADDRESS, - ADDRESS_ONE, - L2_MESSAGE_ROOT_ADDRESS, - ]), - priorityTxMaxGasLimit - ); - if (deployer.verbose) { - console.log("Set addresses in BH, upgrade hash", receipt2.transactionHash); - } -} - -export async function deploySharedBridgeOnL2ThroughL1(deployer: Deployer, chainId: string, gasPrice: BigNumberish) { - await publishL2NativeTokenVaultDependencyBytecodesOnL2(deployer, chainId, gasPrice); - await setL2TokenBeacon(deployer, chainId, gasPrice); - if (deployer.verbose) { - console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_IMPL_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); - console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_PROXY_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); - console.log(`CONTRACTS_L2_SHARED_BRIDGE_IMPL_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); - console.log(`CONTRACTS_L2_SHARED_BRIDGE_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); - } -} - async function main() { const program = new Command(); @@ -110,37 +19,11 @@ async function main() { .option("--nonce ") .option("--erc20-bridge ") .option("--skip-initialize-chain-governance ") - .action(async (cmd) => { - const chainId: string = cmd.chainId ? cmd.chainId : process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID; - const deployWallet = cmd.privateKey - ? new Wallet(cmd.privateKey, provider) - : Wallet.fromMnemonic( - process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, - "m/44'/60'/0'/0/1" - ).connect(provider); - console.log(`Using deployer wallet: ${deployWallet.address}`); - - const deployer = new Deployer({ - deployWallet, - ownerAddress: deployWallet.address, - verbose: true, - }); - - const nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployer.deployWallet.getTransactionCount(); - console.log(`Using nonce: ${nonce}`); - - const gasPrice = cmd.gasPrice - ? parseUnits(cmd.gasPrice, "gwei") - : (await provider.getGasPrice()).mul(GAS_MULTIPLIER); - console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); - - const skipInitializeChainGovernance = - !!cmd.skipInitializeChainGovernance && cmd.skipInitializeChainGovernance === "true"; - if (skipInitializeChainGovernance) { - console.log("Initialization of the chain governance will be skipped"); - } - - // await deploySharedBridgeOnL2ThroughL1(deployer, chainId, gasPrice); + .action(async () => { + console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_IMPL_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); + console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_PROXY_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); + console.log(`CONTRACTS_L2_SHARED_BRIDGE_IMPL_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); + console.log(`CONTRACTS_L2_SHARED_BRIDGE_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); }); await program.parseAsync(process.argv); From a9a4646805a3d3ce3f74e7ac710260d7818254a5 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 16 Aug 2024 14:42:57 +0100 Subject: [PATCH 04/51] typo --- .../contracts/state-transition/StateTransitionManager.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index 2409894ac..8f3edcafc 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -445,8 +445,8 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own ) external view override onlyBridgehub returns (bytes memory stmForwardedBridgeMintData) { // Note that the `_diamondCut` here is not for the current chain, for the chain where the migration // happens. The correctness of it will be checked on the STM on the new settlement layer. - (address _newSettlmentLayerAdmin, bytes memory _diamondCut) = abi.decode(_data, (address, bytes)); - require(_newSettlmentLayerAdmin != address(0), "STM: admin zero"); + (address _newSettlementLayerAdmin, bytes memory _diamondCut) = abi.decode(_data, (address, bytes)); + require(_newSettlementLayerAdmin != address(0), "STM: admin zero"); // We ensure that the chain has the latest protocol version to avoid edge cases // related to different protocol version support. @@ -456,7 +456,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own return abi.encode( IBridgehub(BRIDGE_HUB).baseToken(_chainId), - _newSettlmentLayerAdmin, + _newSettlementLayerAdmin, protocolVersion, _diamondCut ); From ff1023805b4287f4bfb2ca47f5b60f623d184dc4 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 16 Aug 2024 16:28:26 +0100 Subject: [PATCH 05/51] Stas issues --- l1-contracts/contracts/bridgehub/Bridgehub.sol | 13 ++++++------- l1-contracts/contracts/bridgehub/IMessageRoot.sol | 2 -- l1-contracts/contracts/bridgehub/MessageRoot.sol | 8 -------- .../state-transition/chain-deps/facets/Admin.sol | 13 +++++++------ .../state-transition/libraries/PriorityTree.sol | 11 +++++++++-- .../src/deploy-shared-bridge-on-l2-through-l1.ts | 1 - 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 7c853ed80..f1e20b447 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -89,7 +89,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @dev Sync layer chain is expected to have .. as the base token. mapping(uint256 chainId => bool isWhitelistedSettlementLayer) public whitelistedSettlementLayers; - bool private migrationPuased; + bool private migrationPaused; modifier onlyOwnerOrAdmin() { require(msg.sender == admin || msg.sender == owner(), "BH: not owner or admin"); @@ -118,7 +118,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus } modifier whenMigrationsNotPaused() { - require(!migrationPuased, "BH: migrations paused"); + require(!migrationPaused, "BH: migrations paused"); _; } @@ -654,12 +654,11 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus address hyperchain = getHyperchain(_chainId); bool contractAlreadyDeployed = hyperchain != address(0); - if (hyperchain == address(0)) { + if (!contractAlreadyDeployed) { hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(_chainId, _stmData); require(hyperchain != address(0), "BH: chain not registered"); _registerNewHyperchain(_chainId, hyperchain); - /// Question: why do we need addNewChainIfNeeded? Why is addNewChain not enough? - messageRoot.addNewChainIfNeeded(_chainId); + messageRoot.addNewChain(_chainId); } IZkSyncHyperchain(hyperchain).forwardedBridgeMint(_chainMintData, contractAlreadyDeployed); @@ -692,11 +691,11 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @notice Pauses migration functions. function pauseMigration() external onlyOwner { - migrationPuased = true; + migrationPaused = true; } /// @notice Unpauses migration functions. function unpauseMigration() external onlyOwner { - migrationPuased = false; + migrationPaused = false; } } diff --git a/l1-contracts/contracts/bridgehub/IMessageRoot.sol b/l1-contracts/contracts/bridgehub/IMessageRoot.sol index a0791b922..2e15e6f63 100644 --- a/l1-contracts/contracts/bridgehub/IMessageRoot.sol +++ b/l1-contracts/contracts/bridgehub/IMessageRoot.sol @@ -12,6 +12,4 @@ interface IMessageRoot { function addNewChain(uint256 _chainId) external; function addChainBatchRoot(uint256 _chainId, uint256 _batchNumber, bytes32 _chainBatchRoot) external; - - function addNewChainIfNeeded(uint256 _chainId) external; } diff --git a/l1-contracts/contracts/bridgehub/MessageRoot.sol b/l1-contracts/contracts/bridgehub/MessageRoot.sol index 9f70febd4..61b5066f2 100644 --- a/l1-contracts/contracts/bridgehub/MessageRoot.sol +++ b/l1-contracts/contracts/bridgehub/MessageRoot.sol @@ -95,14 +95,6 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { _addNewChain(_chainId); } - /// @dev Adds a new chain to the message root if it has not been added yet. - /// @param _chainId the chainId of the chain - function addNewChainIfNeeded(uint256 _chainId) external onlyBridgehub { - if (!chainRegistered[_chainId]) { - _addNewChain(_chainId); - } - } - /// @dev add a new chainBatchRoot to the chainTree function addChainBatchRoot( uint256 _chainId, diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index 4460ca785..f263e30cc 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -250,11 +250,6 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { ) external payable override onlyBridgehub { HyperchainCommitment memory _commitment = abi.decode(_data, (HyperchainCommitment)); - if (_contractAlreadyDeployed) { - s.priorityTree.checkReinit(_commitment.priorityTree); - require(s.settlementLayer != address(0), "Af: not migrated"); - } - uint256 batchesExecuted = _commitment.totalBatchesExecuted; uint256 batchesVerified = _commitment.totalBatchesVerified; uint256 batchesCommitted = _commitment.totalBatchesCommitted; @@ -291,7 +286,13 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { "Admin: not historical root" ); require(_contractAlreadyDeployed, "Af: contract not deployed"); - } else { + require(s.settlementLayer != address(0), "Af: not migrated"); + s.priorityTree.checkL1Reinit(_commitment.priorityTree); + } else if (_contractAlreadyDeployed) { + require(s.settlementLayer != address(0), "Af: not migrated 2"); + s.priorityTree.checkGWReinit(_commitment.priorityTree); + + } else { s.priorityTree.initFromCommitment(_commitment.priorityTree); } diff --git a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol index a311e19f7..7fdf713f2 100644 --- a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol +++ b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol @@ -97,8 +97,15 @@ library PriorityTree { _tree.historicalRoots[_tree.tree.root()] = true; } - /// @notice Reinitialize the tree from a commitment. - function checkReinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal { + /// @notice Reinitialize the tree from a commitment on L1. + function checkL1Reinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal { + require(_tree.startIndex == _commitment.startIndex, "PT: invalid start index"); + require(_tree.unprocessedIndex >= _commitment.unprocessedIndex, "PT: invalid unprocessed index"); + require(_tree.tree._nextLeafIndex >= _commitment.nextLeafIndex, "PT: invalid next leaf index"); + } + + /// @notice Reinitialize the tree from a commitment on GW. + function checkGWReinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal { require(_tree.startIndex == _commitment.startIndex, "PT: invalid start index"); require(_tree.unprocessedIndex <= _commitment.unprocessedIndex, "PT: invalid unprocessed index"); require(_tree.tree._nextLeafIndex <= _commitment.nextLeafIndex, "PT: invalid next leaf index"); diff --git a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts index 5d87b95ab..a84e18cb3 100644 --- a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts +++ b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts @@ -3,7 +3,6 @@ import { Command } from "commander"; import * as hre from "hardhat"; import { L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS } from "../../l1-contracts/src.ts/utils"; -export const L2_SHARED_BRIDGE_ABI = hre.artifacts.readArtifactSync("L2SharedBridge").abi; export const L2_STANDARD_TOKEN_PROXY_BYTECODE = hre.artifacts.readArtifactSync("BeaconProxy").bytecode; async function main() { From 2a22e238f57b9db9c3437832c8b8723724a3f928 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 16 Aug 2024 16:42:58 +0100 Subject: [PATCH 06/51] fixes --- l1-contracts/contracts/bridge/L1AssetRouter.sol | 4 +++- l1-contracts/contracts/bridgehub/Bridgehub.sol | 2 +- .../contracts/state-transition/chain-deps/facets/Admin.sol | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1AssetRouter.sol b/l1-contracts/contracts/bridge/L1AssetRouter.sol index 20317cd2d..fee60d7f0 100644 --- a/l1-contracts/contracts/bridge/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/L1AssetRouter.sol @@ -607,7 +607,9 @@ contract L1AssetRouter is address l1AssetHandler = assetHandlerAddress[assetId]; // slither-disable-next-line unused-return IL1AssetHandler(l1AssetHandler).bridgeMint(_chainId, assetId, transferData); - (amount, l1Receiver) = abi.decode(transferData, (uint256, address)); + if (l1AssetHandler == address(nativeTokenVault)) { + (amount, l1Receiver) = abi.decode(transferData, (uint256, address)); + } emit WithdrawalFinalizedSharedBridge(_chainId, l1Receiver, assetId, amount); } diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index f1e20b447..ba84b9ebb 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -654,7 +654,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus address hyperchain = getHyperchain(_chainId); bool contractAlreadyDeployed = hyperchain != address(0); - if (!contractAlreadyDeployed) { + if (!contractAlreadyDeployed) { hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(_chainId, _stmData); require(hyperchain != address(0), "BH: chain not registered"); _registerNewHyperchain(_chainId, hyperchain); diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index f263e30cc..46166f2f0 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -291,8 +291,7 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { } else if (_contractAlreadyDeployed) { require(s.settlementLayer != address(0), "Af: not migrated 2"); s.priorityTree.checkGWReinit(_commitment.priorityTree); - - } else { + } else { s.priorityTree.initFromCommitment(_commitment.priorityTree); } From fb536f2d07877d0b2580e5325d3f0f4b7d8e55b9 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 20 Aug 2024 20:01:47 +0100 Subject: [PATCH 07/51] fix chainRegistered --- .../contracts/bridgehub/MessageRoot.sol | 28 +++++++------------ .../unit/concrete/Bridgehub/MessageRoot.t.sol | 6 ++-- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/MessageRoot.sol b/l1-contracts/contracts/bridgehub/MessageRoot.sol index 9f70febd4..3fa3ed08e 100644 --- a/l1-contracts/contracts/bridgehub/MessageRoot.sol +++ b/l1-contracts/contracts/bridgehub/MessageRoot.sol @@ -45,19 +45,12 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { /// @notice The number of chains that are registered. uint256 public chainCount; - /// @notice The mapping from chainId to chainIndex. + /// @notice The mapping from chainId to chainIndex. Note index 0 is maintained for the chain the contract is on. mapping(uint256 chainId => uint256 chainIndex) public chainIndex; /// @notice The mapping from chainIndex to chainId. mapping(uint256 chainIndex => uint256 chainId) public chainIndexToId; - // There are two ways to distinguish chains: - // - Either by reserving the index 0 as a special value which denotes an unregistered chain - // - Use a separate mapping - // The second approach is used due to explicitness. - /// @notice The mapping from chainId to whether the chain is registered. Used because the chainIndex can be 0. - mapping(uint256 chainId => bool isRegistered) public chainRegistered; - /// @notice The shared full merkle tree storing the aggregate hash. FullMerkle.FullTree public sharedTree; @@ -91,14 +84,18 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { } function addNewChain(uint256 _chainId) external onlyBridgehub { - require(!chainRegistered[_chainId], "MR: chain exists"); + require(!chainRegistered(_chainId), "MR: chain exists"); _addNewChain(_chainId); } + function chainRegistered(uint256 _chainId) public view returns (bool) { + return (_chainId == block.chainid || chainIndex[_chainId] != 0); + } + /// @dev Adds a new chain to the message root if it has not been added yet. /// @param _chainId the chainId of the chain function addNewChainIfNeeded(uint256 _chainId) external onlyBridgehub { - if (!chainRegistered[_chainId]) { + if (!chainRegistered(_chainId)) { _addNewChain(_chainId); } } @@ -109,7 +106,7 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { uint256 _batchNumber, bytes32 _chainBatchRoot ) external onlyChain(_chainId) { - require(chainRegistered[_chainId], "MR: not registered"); + require(chainRegistered(_chainId), "MR: not registered"); bytes32 chainRoot; // slither-disable-next-line unused-return (, chainRoot) = chainTree[_chainId].push(MessageHashing.batchLeafHash(_chainBatchRoot, _batchNumber)); @@ -146,18 +143,12 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { function _initialize() internal { // slither-disable-next-line unused-return sharedTree.setup(SHARED_ROOT_TREE_EMPTY_HASH); + _addNewChain(block.chainid); } /// @dev Adds a single chain to the message root. /// @param _chainId the chainId of the chain function _addNewChain(uint256 _chainId) internal { - // The chain itself can not be the part of the message root. - // The message root will only aggregate chains that settle on it. - require(_chainId != block.chainid, "MR: chainId is this chain"); - - chainRegistered[_chainId] = true; - - // We firstly increment `chainCount` and then apply it to ensure that `0` is reserved for chains that are not present. uint256 cachedChainCount = chainCount; require(cachedChainCount < MAX_NUMBER_OF_HYPERCHAINS, "MR: too many chains"); @@ -167,6 +158,7 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { // slither-disable-next-line unused-return bytes32 initialHash = chainTree[_chainId].setup(CHAIN_TREE_EMPTY_ENTRY_HASH); + // slither-disable-next-line unused-return sharedTree.pushNewLeaf(MessageHashing.chainIdLeafHash(initialHash, _chainId)); diff --git a/l1-contracts/test/foundry/unit/concrete/Bridgehub/MessageRoot.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridgehub/MessageRoot.t.sol index 497ec4731..eb01a35d2 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridgehub/MessageRoot.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridgehub/MessageRoot.t.sol @@ -5,6 +5,8 @@ pragma solidity 0.8.24; import {Test} from "forge-std/Test.sol"; import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {Merkle} from "contracts/common/libraries/Merkle.sol"; +import {MessageHashing} from "contracts/common/libraries/MessageHashing.sol"; // Chain tree consists of batch commitments as their leaves. We use hash of "new bytes(96)" as the hash of an empty leaf. bytes32 constant CHAIN_TREE_EMPTY_ENTRY_HASH = bytes32( @@ -26,7 +28,7 @@ contract MessageRootTest is Test { } function test_init() public { - assertEq(messageRoot.getAggregatedRoot(), CHAIN_TREE_EMPTY_ENTRY_HASH); + assertEq(messageRoot.getAggregatedRoot(), (MessageHashing.chainIdLeafHash(0x00, block.chainid))); } function test_RevertWhen_addChainNotBridgeHub() public { @@ -110,6 +112,6 @@ contract MessageRootTest is Test { messageRoot.updateFullTree(); - assertEq(messageRoot.getAggregatedRoot(), 0xbad7e1cf889e30252b8ce93820f79d50651b78587844bc1c588dea123effa4ea); + assertEq(messageRoot.getAggregatedRoot(), 0x0ef1ac67d77f177a33449c47a8f05f0283300a81adca6f063c92c774beed140c); } } From 86d2cc08dc87e55fba5ea5b3f21384353351a3de Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 20 Aug 2024 22:25:36 +0100 Subject: [PATCH 08/51] script fixes --- l1-contracts/scripts/register-hyperchain.ts | 5 +- l1-contracts/src.ts/deploy-utils.ts | 13 ++++- l1-contracts/src.ts/deploy.ts | 18 ++++--- l1-contracts/src.ts/utils.ts | 11 +++-- .../deploy-shared-bridge-on-l2-through-l1.ts | 48 ++----------------- l2-contracts/src/utils.ts | 5 +- 6 files changed, 42 insertions(+), 58 deletions(-) diff --git a/l1-contracts/scripts/register-hyperchain.ts b/l1-contracts/scripts/register-hyperchain.ts index 5401778b5..211518b38 100644 --- a/l1-contracts/scripts/register-hyperchain.ts +++ b/l1-contracts/scripts/register-hyperchain.ts @@ -92,20 +92,19 @@ async function main() { deployWallet, ownerAddress, verbose: true, - l1ChainId: process.env.CONTRACTS_ETH_CHAIN_ID || "31337", }); const baseTokenAddress = await chooseBaseTokenAddress(cmd.baseTokenName, cmd.baseTokenAddress); await checkTokenAddress(baseTokenAddress); console.log(`Using base token address: ${baseTokenAddress}`); console.log(deployer.addresses.Bridgehub.BridgehubProxy); - const baseTokenAssetId = encodeNTVAssetId(deployer.l1ChainId, utils.hexZeroPad(baseTokenAddress, 32)); + const baseTokenAssetId = encodeNTVAssetId(deployer.l1ChainId, baseTokenAddress); if (!(await deployer.bridgehubContract(deployWallet).assetIdIsRegistered(baseTokenAssetId))) { await deployer.registerTokenBridgehub(baseTokenAddress, cmd.useGovernance); } await deployer.registerTokenInNativeTokenVault(baseTokenAddress); await deployer.registerHyperchain( - baseTokenAddress, + baseTokenAssetId, cmd.validiumMode, null, gasPrice, diff --git a/l1-contracts/src.ts/deploy-utils.ts b/l1-contracts/src.ts/deploy-utils.ts index 99d3232f0..3c18645a0 100644 --- a/l1-contracts/src.ts/deploy-utils.ts +++ b/l1-contracts/src.ts/deploy-utils.ts @@ -3,7 +3,7 @@ import "@nomiclabs/hardhat-ethers"; import { ethers } from "ethers"; import { SingletonFactoryFactory } from "../typechain"; -import { getAddressFromEnv } from "./utils"; +import { encodeNTVAssetId, getAddressFromEnv, getNumberFromEnv } from "./utils"; export async function deployViaCreate2( deployWallet: ethers.Wallet, @@ -133,6 +133,7 @@ export interface DeployedAddresses { NativeTokenVaultImplementation: string; NativeTokenVaultProxy: string; }; + BaseTokenAssetId: string; BaseToken: string; TransparentProxyAdmin: string; L2ProxyAdmin: string; @@ -147,6 +148,15 @@ export interface DeployedAddresses { } export function deployedAddressesFromEnv(): DeployedAddresses { + let baseTokenAssetId = "0"; + try { + baseTokenAssetId = getAddressFromEnv("CONTRACTS_BASE_TOKEN_ASSET_ID"); + } catch (error) { + baseTokenAssetId = encodeNTVAssetId( + parseInt(getNumberFromEnv("ETH_CLIENT_CHAIN_ID")), + ethers.utils.hexZeroPad(getAddressFromEnv("CONTRACTS_BASE_TOKEN_ADDR"), 32) + ); + } return { Bridgehub: { BridgehubProxy: getAddressFromEnv("CONTRACTS_BRIDGEHUB_PROXY_ADDR"), @@ -186,6 +196,7 @@ export function deployedAddressesFromEnv(): DeployedAddresses { ValidiumL1DAValidator: getAddressFromEnv("CONTRACTS_L1_VALIDIUM_DA_VALIDATOR"), RelayedSLDAValidator: getAddressFromEnv("CONTRACTS_L1_RELAYED_SL_DA_VALIDATOR"), BaseToken: getAddressFromEnv("CONTRACTS_BASE_TOKEN_ADDR"), + BaseTokenAssetId: baseTokenAssetId, TransparentProxyAdmin: getAddressFromEnv("CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR"), L2ProxyAdmin: getAddressFromEnv("CONTRACTS_L2_PROXY_ADMIN_ADDR"), Create2Factory: getAddressFromEnv("CONTRACTS_CREATE2_FACTORY_ADDR"), diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index d0c8a151a..09f43e712 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -50,6 +50,7 @@ import { applyL1ToL2Alias, // priorityTxMaxGasLimit, encodeNTVAssetId, + ETH_ADDRESS_IN_CONTRACTS, } from "./utils"; import type { ChainAdminCall } from "./utils"; import { IGovernanceFactory } from "../typechain/IGovernanceFactory"; @@ -88,7 +89,7 @@ export interface DeployerConfig { defaultAccountBytecodeHash?: string; deployedLogPrefix?: string; l1Deployer?: Deployer; - l1ChainId: string; + l1ChainId?: string; } export interface Operation { @@ -124,7 +125,7 @@ export class Deployer { : hexlify(hashL2Bytecode(readSystemContractsBytecode("DefaultAccount"))); this.ownerAddress = config.ownerAddress != null ? config.ownerAddress : this.deployWallet.address; this.chainId = parseInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!); - this.l1ChainId = parseInt(config.l1ChainId); + this.l1ChainId = parseInt(config.l1ChainId || getNumberFromEnv("ETH_CLIENT_CHAIN_ID")); this.deployedLogPrefix = config.deployedLogPrefix ?? "CONTRACTS"; } @@ -966,7 +967,7 @@ export class Deployer { } /// registering ETH as a valid token, with address 1. - const baseTokenAssetId = encodeNTVAssetId(this.l1ChainId, ethers.utils.hexZeroPad(ADDRESS_ONE, 32)); + const baseTokenAssetId = encodeNTVAssetId(this.l1ChainId, ETH_ADDRESS_IN_CONTRACTS); const upgradeData2 = bridgehub.interface.encodeFunctionData("addTokenAssetId", [baseTokenAssetId]); await this.executeUpgrade(this.addresses.Bridgehub.BridgehubProxy, 0, upgradeData2); if (this.verbose) { @@ -976,7 +977,7 @@ export class Deployer { public async registerTokenBridgehub(tokenAddress: string, useGovernance: boolean = false) { const bridgehub = this.bridgehubContract(this.deployWallet); - const baseTokenAssetId = encodeNTVAssetId(this.l1ChainId, ethers.utils.hexZeroPad(tokenAddress, 32)); + const baseTokenAssetId = encodeNTVAssetId(this.l1ChainId, tokenAddress); const receipt = await this.executeDirectOrGovernance( useGovernance, bridgehub, @@ -996,7 +997,7 @@ export class Deployer { const data = nativeTokenVault.interface.encodeFunctionData("registerToken", [token]); await this.executeUpgrade(this.addresses.Bridges.NativeTokenVaultProxy, 0, data); if (this.verbose) { - console.log("Native token vault registered with ETH"); + console.log("Native token vault registered with token", token); } } @@ -1225,7 +1226,7 @@ export class Deployer { } public async registerHyperchain( - baseTokenAddress: string, + baseTokenAssetId: string, validiumMode: boolean, extraFacets?: FacetCut[], gasPrice?: BigNumberish, @@ -1240,6 +1241,8 @@ export class Deployer { const bridgehub = this.bridgehubContract(this.deployWallet); const stateTransitionManager = this.stateTransitionManagerContract(this.deployWallet); + const ntv = this.nativeTokenVault(this.deployWallet); + const baseTokenAddress = await ntv.tokenAddress(baseTokenAssetId); const inputChainId = predefinedChainId || getNumberFromEnv("CHAIN_ETH_ZKSYNC_NETWORK_ID"); const alreadyRegisteredInSTM = @@ -1266,7 +1269,7 @@ export class Deployer { [ inputChainId, this.addresses.StateTransition.StateTransitionProxy, - baseTokenAddress, + baseTokenAssetId, Date.now(), admin, initData, @@ -1288,6 +1291,7 @@ export class Deployer { } this.addresses.BaseToken = baseTokenAddress; + this.addresses.BaseTokenAssetId = baseTokenAssetId; if (this.verbose) { console.log(`Hyperchain registered, gas used: ${receipt.gasUsed.toString()} and ${receipt.gasUsed.toString()}`); diff --git a/l1-contracts/src.ts/utils.ts b/l1-contracts/src.ts/utils.ts index 95291a623..291e66e0c 100644 --- a/l1-contracts/src.ts/utils.ts +++ b/l1-contracts/src.ts/utils.ts @@ -22,7 +22,6 @@ export const REQUIRED_L2_GAS_PRICE_PER_PUBDATA = require("../../SystemConfig.jso export const SYSTEM_UPGRADE_L2_TX_TYPE = 254; export const ADDRESS_ONE = "0x0000000000000000000000000000000000000001"; -export const ADDRESS_TWO_NTV = "0x0000000000000000000000000000000000010004"; export const ETH_ADDRESS_IN_CONTRACTS = ADDRESS_ONE; export const L1_TO_L2_ALIAS_OFFSET = "0x1111000000000000000000000000000000001111"; export const L2_BRIDGEHUB_ADDRESS = "0x0000000000000000000000000000000000010002"; @@ -105,9 +104,15 @@ export function computeL2Create2Address( return ethers.utils.hexDataSlice(data, 12); } -export function encodeNTVAssetId(chainId: number, assetData: BytesLike) { +export function encodeNTVAssetId(chainId: number, tokenAddress: BytesLike) { + console.log("chainId", chainId); + console.log("L2_NATIVE_TOKEN_VAULT_ADDRESS", L2_NATIVE_TOKEN_VAULT_ADDRESS); + console.log("tokenAddress", tokenAddress); return ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode(["uint256", "address", "bytes32"], [chainId, ADDRESS_TWO_NTV, assetData]) + ethers.utils.defaultAbiCoder.encode( + ["uint256", "address", "bytes32"], + [chainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, ethers.utils.hexZeroPad(tokenAddress, 32)] + ) ); } diff --git a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts index e0d4d3ddd..ea86fc114 100644 --- a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts +++ b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts @@ -1,5 +1,4 @@ import { Command } from "commander"; -import type { BigNumberish } from "ethers"; import { Wallet } from "ethers"; import { formatUnits, parseUnits } from "ethers/lib/utils"; import { provider, publishBytecodeFromL1 } from "./utils"; @@ -11,49 +10,9 @@ import { GAS_MULTIPLIER } from "../../l1-contracts/scripts/utils"; import * as hre from "hardhat"; import { L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS } from "../../l1-contracts/src.ts/utils"; -export const L2_SHARED_BRIDGE_ABI = hre.artifacts.readArtifactSync("L2AssetRouter").abi; +// export const L2_SHARED_BRIDGE_ABI = hre.artifacts.readArtifactSync("L2AssetRouter").abi; export const L2_STANDARD_TOKEN_PROXY_BYTECODE = hre.artifacts.readArtifactSync("BeaconProxy").bytecode; -export async function publishL2NativeTokenVaultDependencyBytecodesOnL2( - deployer: Deployer, - chainId: string, - gasPrice: BigNumberish -) { - if (deployer.verbose) { - console.log("Providing necessary L2 bytecodes"); - } - - const L2_STANDARD_ERC20_PROXY_FACTORY_BYTECODE = hre.artifacts.readArtifactSync("UpgradeableBeacon").bytecode; - const L2_STANDARD_ERC20_IMPLEMENTATION_BYTECODE = hre.artifacts.readArtifactSync("L2StandardERC20").bytecode; - - const receipt = await ( - await publishBytecodeFromL1( - chainId, - deployer.deployWallet, - [ - L2_STANDARD_ERC20_PROXY_FACTORY_BYTECODE, - L2_STANDARD_ERC20_IMPLEMENTATION_BYTECODE, - L2_STANDARD_TOKEN_PROXY_BYTECODE, - ], - gasPrice - ) - ).wait(); - - if (deployer.verbose) { - console.log("Bytecodes published on L2, hash: ", receipt.transactionHash); - } -} - -export async function deploySharedBridgeOnL2ThroughL1(deployer: Deployer, chainId: string, gasPrice: BigNumberish) { - await publishL2NativeTokenVaultDependencyBytecodesOnL2(deployer, chainId, gasPrice); - if (deployer.verbose) { - console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_IMPL_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); - console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_PROXY_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); - console.log(`CONTRACTS_L2_SHARED_BRIDGE_IMPL_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); - console.log(`CONTRACTS_L2_SHARED_BRIDGE_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); - } -} - async function main() { const program = new Command(); @@ -97,7 +56,10 @@ async function main() { console.log("Initialization of the chain governance will be skipped"); } - await deploySharedBridgeOnL2ThroughL1(deployer, chainId, gasPrice); + console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_IMPL_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); + console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_PROXY_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); + console.log(`CONTRACTS_L2_SHARED_BRIDGE_IMPL_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); + console.log(`CONTRACTS_L2_SHARED_BRIDGE_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); }); await program.parseAsync(process.argv); diff --git a/l2-contracts/src/utils.ts b/l2-contracts/src/utils.ts index 67883e600..8601e64fd 100644 --- a/l2-contracts/src/utils.ts +++ b/l2-contracts/src/utils.ts @@ -13,6 +13,7 @@ import type { Provider } from "zksync-ethers"; import { REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT, sleep } from "zksync-ethers/build/utils"; import { IERC20Factory } from "../typechain/IERC20Factory"; +import { IL1NativeTokenVaultFactory } from "../../l1-contracts/typechain/IL1NativeTokenVaultFactory"; export const provider = web3Provider(); @@ -132,6 +133,7 @@ export async function requestL2TransactionDirect( const deployedAddresses = deployedAddressesFromEnv(); const bridgehubAddress = deployedAddresses.Bridgehub.BridgehubProxy; const bridgehub = IBridgehubFactory.connect(bridgehubAddress, wallet); + const ntv = IL1NativeTokenVaultFactory.connect(deployedAddresses.Bridges.NativeTokenVaultProxy, wallet); gasPrice ??= await bridgehub.provider.getGasPrice(); const expectedCost = await bridgehub.l2TransactionBaseCost( @@ -141,7 +143,8 @@ export async function requestL2TransactionDirect( REQUIRED_L2_GAS_PRICE_PER_PUBDATA ); - const baseTokenAddress = await bridgehub.baseToken(chainId); + const baseTokenAssetId = await bridgehub.baseTokenAssetId(chainId); + const baseTokenAddress = await ntv.tokenAddress(baseTokenAssetId); const baseTokenBridge = deployedAddresses.Bridges.SharedBridgeProxy; const baseToken = IERC20Factory.connect(baseTokenAddress, wallet); const ethIsBaseToken = ADDRESS_ONE == baseTokenAddress; From 092d1985b583663d1e1d6961d717721b86825bf9 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 20 Aug 2024 22:36:58 +0100 Subject: [PATCH 09/51] local zk fmt breaks... --- l1-contracts/scripts/register-hyperchain.ts | 2 +- l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/l1-contracts/scripts/register-hyperchain.ts b/l1-contracts/scripts/register-hyperchain.ts index 211518b38..d5e7f49a5 100644 --- a/l1-contracts/scripts/register-hyperchain.ts +++ b/l1-contracts/scripts/register-hyperchain.ts @@ -2,7 +2,7 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars import * as hardhat from "hardhat"; import { Command } from "commander"; -import { Wallet, utils } from "ethers"; +import { Wallet } from "ethers"; import { formatUnits, parseUnits } from "ethers/lib/utils"; import * as fs from "fs"; import * as path from "path"; diff --git a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts index ea86fc114..917e8e814 100644 --- a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts +++ b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts @@ -1,7 +1,7 @@ import { Command } from "commander"; import { Wallet } from "ethers"; import { formatUnits, parseUnits } from "ethers/lib/utils"; -import { provider, publishBytecodeFromL1 } from "./utils"; +import { provider } from "./utils"; import { ethTestConfig } from "./deploy-utils"; @@ -27,7 +27,7 @@ async function main() { .option("--erc20-bridge ") .option("--skip-initialize-chain-governance ") .action(async (cmd) => { - const chainId: string = cmd.chainId ? cmd.chainId : process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID; + // const chainId: string = cmd.chainId ? cmd.chainId : process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID; const deployWallet = cmd.privateKey ? new Wallet(cmd.privateKey, provider) : Wallet.fromMnemonic( From 739fabb5657fb3f9644cfb88d51bf36ce2e8c18f Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 10:14:23 +0100 Subject: [PATCH 10/51] some contract fixes --- .../bridge/interfaces/IL1AssetHandler.sol | 3 --- .../interfaces/IL1BaseTokenAssetHandler.sol | 12 ++++++++++++ .../bridge/interfaces/IL1NativeTokenVault.sol | 3 ++- l1-contracts/contracts/bridgehub/Bridgehub.sol | 16 +++++++--------- l1-contracts/contracts/bridgehub/IBridgehub.sol | 2 +- .../chain-deps/facets/Getters.sol | 2 +- 6 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 l1-contracts/contracts/bridge/interfaces/IL1BaseTokenAssetHandler.sol diff --git a/l1-contracts/contracts/bridge/interfaces/IL1AssetHandler.sol b/l1-contracts/contracts/bridge/interfaces/IL1AssetHandler.sol index 266ee51f6..a707da173 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1AssetHandler.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1AssetHandler.sol @@ -54,7 +54,4 @@ interface IL1AssetHandler { address _depositSender, bytes calldata _data ) external payable; - - /// @notice Used to get the token address of an assetId - function tokenAddress(bytes32 _assetId) external view returns (address); } diff --git a/l1-contracts/contracts/bridge/interfaces/IL1BaseTokenAssetHandler.sol b/l1-contracts/contracts/bridge/interfaces/IL1BaseTokenAssetHandler.sol new file mode 100644 index 000000000..1e8d08bdd --- /dev/null +++ b/l1-contracts/contracts/bridge/interfaces/IL1BaseTokenAssetHandler.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +/// @title L1 Base Token Asset Handler contract interface +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @notice Used for any asset handler and called by the L1AssetRouter +interface IL1BaseTokenAssetHandler { + /// @notice Used to get the token address of an assetId + function tokenAddress(bytes32 _assetId) external view returns (address); +} diff --git a/l1-contracts/contracts/bridge/interfaces/IL1NativeTokenVault.sol b/l1-contracts/contracts/bridge/interfaces/IL1NativeTokenVault.sol index 368fee4fe..805a45507 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1NativeTokenVault.sol @@ -4,12 +4,13 @@ pragma solidity 0.8.24; import {IL1AssetRouter} from "./IL1AssetRouter.sol"; import {IL1AssetHandler} from "./IL1AssetHandler.sol"; +import {IL1BaseTokenAssetHandler} from "./IL1BaseTokenAssetHandler.sol"; /// @title L1 Native token vault contract interface /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice The NTV is an Asset Handler for the L1AssetRouter to handle native tokens -interface IL1NativeTokenVault is IL1AssetHandler { +interface IL1NativeTokenVault is IL1AssetHandler, IL1BaseTokenAssetHandler { /// @notice The L1AssetRouter contract function L1_SHARED_BRIDGE() external view returns (IL1AssetRouter); diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 93f0a267f..7be15353d 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -11,7 +11,7 @@ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ import {IBridgehub, L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter, L2TransactionRequestTwoBridgesInner} from "./IBridgehub.sol"; import {IL1AssetRouter} from "../bridge/interfaces/IL1AssetRouter.sol"; -import {IL1AssetHandler} from "../bridge/interfaces/IL1AssetHandler.sol"; +import {IL1BaseTokenAssetHandler} from "../bridge/interfaces/IL1BaseTokenAssetHandler.sol"; import {IStateTransitionManager} from "../state-transition/IStateTransitionManager.sol"; import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; @@ -336,15 +336,13 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus Getters //////////////////////////////////////////////////////////////*/ - /// @notice Asset Handlers' token address function, which takes assetId as input and return the token address - function tokenAddress(bytes32 _baseTokenAssetId) public view returns (address) { - return baseToken(_baseTokenAssetId); - } - /// @notice baseToken function, which takes assetId as input, reads assetHandler from AR, and tokenAddress from AH - function baseToken(bytes32 _baseTokenAssetId) public view returns (address) { - IL1AssetHandler assetHandlerAddress = IL1AssetHandler(sharedBridge.assetHandlerAddress(_baseTokenAssetId)); - return assetHandlerAddress.tokenAddress(_baseTokenAssetId); + function baseToken(uint256 _chainId) public view returns (address) { + bytes32 baseTokenAssetId = baseTokenAssetId[_chainId]; + IL1BaseTokenAssetHandler assetHandlerAddress = IL1BaseTokenAssetHandler( + sharedBridge.assetHandlerAddress(baseTokenAssetId) + ); + return assetHandlerAddress.tokenAddress(baseTokenAssetId); } /// @notice Returns all the registered hyperchain addresses diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index e029f50e2..9efbe9ed4 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -75,7 +75,7 @@ interface IBridgehub is IL1AssetHandler { function assetIdIsRegistered(bytes32 _baseTokenAssetId) external view returns (bool); - function baseToken(bytes32 _baseTokenAssetId) external view returns (address); + function baseToken(uint256 _chainId) external view returns (address); function baseTokenAssetId(uint256 _chainId) external view returns (bytes32); diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol index b630af962..e8838d8c6 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol @@ -67,7 +67,7 @@ contract GettersFacet is ZkSyncHyperchainBase, IGetters, ILegacyGetters { /// @inheritdoc IGetters function getBaseToken() external view returns (address) { - return IBridgehub(s.bridgehub).baseToken(s.baseTokenAssetId); + return IBridgehub(s.bridgehub).baseToken(s.chainId); } /// @inheritdoc IGetters From d7928bac144a5aee2511e963b743acfcce88a3e9 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 10:20:03 +0100 Subject: [PATCH 11/51] commented out code --- l1-contracts/src.ts/deploy.ts | 2 +- l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 09f43e712..323ec1272 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -125,7 +125,7 @@ export class Deployer { : hexlify(hashL2Bytecode(readSystemContractsBytecode("DefaultAccount"))); this.ownerAddress = config.ownerAddress != null ? config.ownerAddress : this.deployWallet.address; this.chainId = parseInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!); - this.l1ChainId = parseInt(config.l1ChainId || getNumberFromEnv("ETH_CLIENT_CHAIN_ID")); + this.l1ChainId = parseInt(config.l1ChainId) || getL1ChainId(); this.deployedLogPrefix = config.deployedLogPrefix ?? "CONTRACTS"; } diff --git a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts index 917e8e814..ea5af24e4 100644 --- a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts +++ b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts @@ -10,7 +10,6 @@ import { GAS_MULTIPLIER } from "../../l1-contracts/scripts/utils"; import * as hre from "hardhat"; import { L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS } from "../../l1-contracts/src.ts/utils"; -// export const L2_SHARED_BRIDGE_ABI = hre.artifacts.readArtifactSync("L2AssetRouter").abi; export const L2_STANDARD_TOKEN_PROXY_BYTECODE = hre.artifacts.readArtifactSync("BeaconProxy").bytecode; async function main() { @@ -27,7 +26,6 @@ async function main() { .option("--erc20-bridge ") .option("--skip-initialize-chain-governance ") .action(async (cmd) => { - // const chainId: string = cmd.chainId ? cmd.chainId : process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID; const deployWallet = cmd.privateKey ? new Wallet(cmd.privateKey, provider) : Wallet.fromMnemonic( From 8b45eff0b5acb135022fdd1eac71906a0d432310 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 10:30:17 +0100 Subject: [PATCH 12/51] some more fixes --- l1-contracts/src.ts/deploy.ts | 2 +- l1-contracts/src.ts/utils.ts | 3 --- l1-contracts/test/unit_tests/custom_base_token.spec.ts | 5 ++--- l1-contracts/test/unit_tests/utils.ts | 5 ++--- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 323ec1272..04e3c6e90 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -125,7 +125,7 @@ export class Deployer { : hexlify(hashL2Bytecode(readSystemContractsBytecode("DefaultAccount"))); this.ownerAddress = config.ownerAddress != null ? config.ownerAddress : this.deployWallet.address; this.chainId = parseInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!); - this.l1ChainId = parseInt(config.l1ChainId) || getL1ChainId(); + this.l1ChainId = parseInt(config.l1ChainId) || this.getL1ChainId(); this.deployedLogPrefix = config.deployedLogPrefix ?? "CONTRACTS"; } diff --git a/l1-contracts/src.ts/utils.ts b/l1-contracts/src.ts/utils.ts index 291e66e0c..5f931a330 100644 --- a/l1-contracts/src.ts/utils.ts +++ b/l1-contracts/src.ts/utils.ts @@ -105,9 +105,6 @@ export function computeL2Create2Address( } export function encodeNTVAssetId(chainId: number, tokenAddress: BytesLike) { - console.log("chainId", chainId); - console.log("L2_NATIVE_TOKEN_VAULT_ADDRESS", L2_NATIVE_TOKEN_VAULT_ADDRESS); - console.log("tokenAddress", tokenAddress); return ethers.utils.keccak256( ethers.utils.defaultAbiCoder.encode( ["uint256", "address", "bytes32"], diff --git a/l1-contracts/test/unit_tests/custom_base_token.spec.ts b/l1-contracts/test/unit_tests/custom_base_token.spec.ts index 4b0a28dac..6db056b6f 100644 --- a/l1-contracts/test/unit_tests/custom_base_token.spec.ts +++ b/l1-contracts/test/unit_tests/custom_base_token.spec.ts @@ -13,7 +13,7 @@ import { IL1NativeTokenVaultFactory } from "../../typechain/IL1NativeTokenVaultF import { getTokens } from "../../src.ts/deploy-token"; import type { Deployer } from "../../src.ts/deploy"; -import { ethTestConfig, encodeNTVAssetId } from "../../src.ts/utils"; +import { ethTestConfig } from "../../src.ts/utils"; import { initialTestnetDeploymentProcess } from "../../src.ts/deploy-test-process"; import { getCallRevertReason, REQUIRED_L2_GAS_PRICE_PER_PUBDATA } from "./utils"; @@ -73,8 +73,7 @@ describe("Custom base token chain and bridge tests", () => { it("Should have correct base token", async () => { // we should still be able to deploy the erc20 bridge - const baseTokenAssetId = encodeNTVAssetId(deployer.l1ChainId, ethers.utils.hexZeroPad(baseTokenAddress, 32)); - const baseTokenAddressInBridgehub = await bridgehub.baseToken(baseTokenAssetId); + const baseTokenAddressInBridgehub = await bridgehub.baseToken(deployer.chainId); expect(baseTokenAddress).equal(baseTokenAddressInBridgehub); }); diff --git a/l1-contracts/test/unit_tests/utils.ts b/l1-contracts/test/unit_tests/utils.ts index 51bbd0361..0b7a034aa 100644 --- a/l1-contracts/test/unit_tests/utils.ts +++ b/l1-contracts/test/unit_tests/utils.ts @@ -10,7 +10,7 @@ import type { IMailbox } from "../../typechain/IMailbox"; import type { ExecutorFacet } from "../../typechain"; import type { FeeParams, L2CanonicalTransaction } from "../../src.ts/utils"; -import { ADDRESS_ONE, PubdataPricingMode, EMPTY_STRING_KECCAK, encodeNTVAssetId } from "../../src.ts/utils"; +import { ADDRESS_ONE, PubdataPricingMode, EMPTY_STRING_KECCAK } from "../../src.ts/utils"; import { packSemver } from "../../scripts/utils"; import { keccak256 } from "ethers/lib/utils"; @@ -369,8 +369,7 @@ export async function depositERC20( const gasPrice = await bridge.provider.getGasPrice(); const gasPerPubdata = REQUIRED_L2_GAS_PRICE_PER_PUBDATA; const neededValue = await bridgehubContract.l2TransactionBaseCost(chainId, gasPrice, l2GasLimit, gasPerPubdata); - const baseTokenAssetId = encodeNTVAssetId(l1ChainId, ethers.utils.hexZeroPad(ADDRESS_ONE, 32)); - const ethIsBaseToken = (await bridgehubContract.baseToken(baseTokenAssetId)) == ADDRESS_ONE; + const ethIsBaseToken = (await bridgehubContract.baseToken(chainId)) == ADDRESS_ONE; const deposit = await bridge["deposit(address,address,uint256,uint256,uint256,address)"]( l2Receiver, From 95f508592ee33da151f0cde10a50b2e051f4f10f Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 10:31:28 +0100 Subject: [PATCH 13/51] forgot await --- l1-contracts/src.ts/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 04e3c6e90..fc233a511 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -125,7 +125,7 @@ export class Deployer { : hexlify(hashL2Bytecode(readSystemContractsBytecode("DefaultAccount"))); this.ownerAddress = config.ownerAddress != null ? config.ownerAddress : this.deployWallet.address; this.chainId = parseInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!); - this.l1ChainId = parseInt(config.l1ChainId) || this.getL1ChainId(); + this.l1ChainId = parseInt(config.l1ChainId) || await this.getL1ChainId(); this.deployedLogPrefix = config.deployedLogPrefix ?? "CONTRACTS"; } From 4d5cdc91af06585d581d75cee06e5408dfaa2a52 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 10:34:19 +0100 Subject: [PATCH 14/51] undo getL1ChainId, its async --- l1-contracts/src.ts/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index fc233a511..de0f16866 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -125,7 +125,7 @@ export class Deployer { : hexlify(hashL2Bytecode(readSystemContractsBytecode("DefaultAccount"))); this.ownerAddress = config.ownerAddress != null ? config.ownerAddress : this.deployWallet.address; this.chainId = parseInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!); - this.l1ChainId = parseInt(config.l1ChainId) || await this.getL1ChainId(); + this.l1ChainId = parseInt(config.l1ChainId) || getNumberFromEnv("ETH_CLIENT_CHAIN_ID"); this.deployedLogPrefix = config.deployedLogPrefix ?? "CONTRACTS"; } From 96fa8e432e40141b92106525a561cd11526ed514 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 21 Aug 2024 11:55:46 +0200 Subject: [PATCH 15/51] wip --- l2-contracts/foundry.toml | 13 +++--- l2-contracts/package.json | 1 + .../foundry/unit/erc20/L1ERC20Bridge.t.sol | 44 +++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index d34805bfe..33d7f57a6 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -1,13 +1,14 @@ [profile.default] src = "contracts" out = "out" -libs = ["lib"] +libs = ["node_modules", "../lib"] +test = "test/foundry" +solc_version = "0.8.20" cache_path = "cache-forge" evm_version = "paris" +ignored_error_codes = ["missing-receive-ether", "code-size"] +ignored_warnings_from = ["test", "contracts/dev-contracts"] remappings = [ - "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", - "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", + "forge-std/=../lib/forge-std/src/", + "foundry-test/=test/foundry/", ] - -[profile.default.zksync] -zksolc = "1.5.0" diff --git a/l2-contracts/package.json b/l2-contracts/package.json index 474a5a25a..1d5ed3fff 100644 --- a/l2-contracts/package.json +++ b/l2-contracts/package.json @@ -32,6 +32,7 @@ }, "scripts": { "build": "hardhat compile", + "test:foundry": "forge test", "clean": "hardhat clean", "test": "hardhat test", "verify": "hardhat run src/verify.ts", diff --git a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol b/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol new file mode 100644 index 000000000..87bf1db99 --- /dev/null +++ b/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {L2StandardERC20} from "contracts/bridge/L2StandardERC20.sol"; + +contract L1Erc20BridgeTest is Test { + // L1ERC20Bridge internal bridge; + + // ReenterL1ERC20Bridge internal reenterL1ERC20Bridge; + // L1ERC20Bridge internal bridgeReenterItself; + + // TestnetERC20Token internal token; + // TestnetERC20Token internal feeOnTransferToken; + // address internal randomSigner; + // address internal alice; + // address sharedBridgeAddress; + + constructor() { + // randomSigner = makeAddr("randomSigner"); + // alice = makeAddr("alice"); + + // sharedBridgeAddress = makeAddr("shared bridge"); + // bridge = new L1ERC20Bridge(IL1SharedBridge(sharedBridgeAddress)); + + // reenterL1ERC20Bridge = new ReenterL1ERC20Bridge(); + // bridgeReenterItself = new L1ERC20Bridge(IL1SharedBridge(address(reenterL1ERC20Bridge))); + // reenterL1ERC20Bridge.setBridge(bridgeReenterItself); + + // token = new TestnetERC20Token("TestnetERC20Token", "TET", 18); + // feeOnTransferToken = new FeeOnTransferToken("FeeOnTransferToken", "FOT", 18); + // token.mint(alice, type(uint256).max); + // feeOnTransferToken.mint(alice, type(uint256).max); + } + + // add this to be excluded from coverage report + // function test() internal virtual {} + + function test_Stuff() public { + L2StandardERC20 l2StandardERC20 = new L2StandardERC20(); + } +} From c7b71238d8b7c06ee0b5b7b837e3de2bc62097aa Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 10:58:19 +0100 Subject: [PATCH 16/51] foundry test fixes --- l1-contracts/deploy-scripts/Utils.sol | 3 +-- l1-contracts/src.ts/deploy.ts | 2 +- .../foundry/integration/BridgeHubInvariantTests.t.sol | 9 +++------ .../test/foundry/integration/BridgehubTests.t.sol | 9 +++------ .../foundry/integration/_SharedHyperchainDeployer.t.sol | 4 ++-- .../Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol | 3 ++- .../Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol | 5 +++-- 7 files changed, 15 insertions(+), 20 deletions(-) diff --git a/l1-contracts/deploy-scripts/Utils.sol b/l1-contracts/deploy-scripts/Utils.sol index 0ea6f2a8d..d9abf2231 100644 --- a/l1-contracts/deploy-scripts/Utils.sol +++ b/l1-contracts/deploy-scripts/Utils.sol @@ -253,8 +253,7 @@ library Utils { refundRecipient: msg.sender }); - bytes32 baseTokenAssetId = bridgehub.baseTokenAssetId(chainId); - address baseTokenAddress = bridgehub.baseToken(baseTokenAssetId); + address baseTokenAddress = bridgehub.baseToken(chainId); if (ADDRESS_ONE != baseTokenAddress) { IERC20 baseToken = IERC20(baseTokenAddress); vm.broadcast(); diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index de0f16866..09f43e712 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -125,7 +125,7 @@ export class Deployer { : hexlify(hashL2Bytecode(readSystemContractsBytecode("DefaultAccount"))); this.ownerAddress = config.ownerAddress != null ? config.ownerAddress : this.deployWallet.address; this.chainId = parseInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!); - this.l1ChainId = parseInt(config.l1ChainId) || getNumberFromEnv("ETH_CLIENT_CHAIN_ID"); + this.l1ChainId = parseInt(config.l1ChainId || getNumberFromEnv("ETH_CLIENT_CHAIN_ID")); this.deployedLogPrefix = config.deployedLogPrefix ?? "CONTRACTS"; } diff --git a/l1-contracts/test/foundry/integration/BridgeHubInvariantTests.t.sol b/l1-contracts/test/foundry/integration/BridgeHubInvariantTests.t.sol index ef60e6231..031060691 100644 --- a/l1-contracts/test/foundry/integration/BridgeHubInvariantTests.t.sol +++ b/l1-contracts/test/foundry/integration/BridgeHubInvariantTests.t.sol @@ -100,8 +100,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, HyperchainDeployer, Toke // use base token as main token // watch out, do not use with ETH modifier useBaseToken() { - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(currentChainId, currentTokenAddress); - currentToken = TestnetERC20Token(getHyperchainBaseToken(baseTokenAssetId)); + currentToken = TestnetERC20Token(getHyperchainBaseToken(currentChainId)); currentTokenAddress = address(currentToken); _; } @@ -616,8 +615,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, HyperchainDeployer, Toke uint256 tokenIndexSeed, uint256 l2Value ) public virtual useUser(userIndexSeed) useHyperchain(chainIndexSeed) useERC20Token(tokenIndexSeed) { - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(currentChainId, currentTokenAddress); - address chainBaseToken = getHyperchainBaseToken(baseTokenAssetId); + address chainBaseToken = getHyperchainBaseToken(currentChainId); if (chainBaseToken == ETH_TOKEN_ADDRESS) { depositERC20ToEthChain(l2Value, currentTokenAddress); @@ -635,8 +633,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, HyperchainDeployer, Toke uint256 chainIndexSeed, uint256 amountToWithdraw ) public virtual useUser(userIndexSeed) useHyperchain(chainIndexSeed) { - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(currentChainId, currentTokenAddress); - address token = getHyperchainBaseToken(baseTokenAssetId); + address token = getHyperchainBaseToken(currentChainId); if (token != ETH_TOKEN_ADDRESS) { withdrawERC20Token(amountToWithdraw, token); diff --git a/l1-contracts/test/foundry/integration/BridgehubTests.t.sol b/l1-contracts/test/foundry/integration/BridgehubTests.t.sol index 50d34848e..a422b792a 100644 --- a/l1-contracts/test/foundry/integration/BridgehubTests.t.sol +++ b/l1-contracts/test/foundry/integration/BridgehubTests.t.sol @@ -100,8 +100,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, HyperchainDeployer, Toke // use base token as main token // watch out, do not use with ETH modifier useBaseToken() { - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(currentChainId, currentTokenAddress); - currentToken = TestnetERC20Token(getHyperchainBaseToken(baseTokenAssetId)); + currentToken = TestnetERC20Token(getHyperchainBaseToken(currentChainId)); currentTokenAddress = address(currentToken); _; } @@ -616,8 +615,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, HyperchainDeployer, Toke uint256 tokenIndexSeed, uint256 l2Value ) public virtual useUser(userIndexSeed) useHyperchain(chainIndexSeed) useERC20Token(tokenIndexSeed) { - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(currentChainId, currentTokenAddress); - address chainBaseToken = getHyperchainBaseToken(baseTokenAssetId); + address chainBaseToken = getHyperchainBaseToken(currentChainId); if (chainBaseToken == ETH_TOKEN_ADDRESS) { depositERC20ToEthChain(l2Value, currentTokenAddress); @@ -635,8 +633,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, HyperchainDeployer, Toke uint256 chainIndexSeed, uint256 amountToWithdraw ) public virtual useUser(userIndexSeed) useHyperchain(chainIndexSeed) { - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(currentChainId, currentTokenAddress); - address token = getHyperchainBaseToken(baseTokenAssetId); + address token = getHyperchainBaseToken(currentChainId); if (token != ETH_TOKEN_ADDRESS) { withdrawERC20Token(amountToWithdraw, token); diff --git a/l1-contracts/test/foundry/integration/_SharedHyperchainDeployer.t.sol b/l1-contracts/test/foundry/integration/_SharedHyperchainDeployer.t.sol index 08185eabf..9e032fdc9 100644 --- a/l1-contracts/test/foundry/integration/_SharedHyperchainDeployer.t.sol +++ b/l1-contracts/test/foundry/integration/_SharedHyperchainDeployer.t.sol @@ -118,8 +118,8 @@ contract HyperchainDeployer is L1ContractDeployer { return bridgeHub.getHyperchain(_chainId); } - function getHyperchainBaseToken(bytes32 _baseTokenAssetId) public view returns (address) { - return bridgeHub.baseToken(_baseTokenAssetId); + function getHyperchainBaseToken(uint256 _chainId) public view returns (address) { + return bridgeHub.baseToken(_chainId); } function acceptPendingAdmin() public { diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol index af81edea0..84f76b024 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol @@ -11,6 +11,7 @@ import {L2Message, TxStatus} from "contracts/common/Messaging.sol"; import {IMailbox} from "contracts/state-transition/chain-interfaces/IMailbox.sol"; import {IL1AssetRouter} from "contracts/bridge/interfaces/IL1AssetRouter.sol"; import {IL1AssetHandler} from "contracts/bridge/interfaces/IL1AssetHandler.sol"; +import {IL1BaseTokenAssetHandler} from "contracts/bridge/interfaces/IL1BaseTokenAssetHandler.sol"; import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IGetters} from "contracts/state-transition/chain-interfaces/IGetters.sol"; import {L1NativeTokenVault} from "contracts/bridge/L1NativeTokenVault.sol"; @@ -103,7 +104,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // ToDo: remove the mock call and register custom asset handler? vm.mockCall( address(nativeTokenVault), - abi.encodeWithSelector(IL1AssetHandler.tokenAddress.selector, tokenAssetId), + abi.encodeWithSelector(IL1BaseTokenAssetHandler.tokenAddress.selector, tokenAssetId), abi.encode(address(0)) ); vm.prank(bridgehubAddress); diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol index db1f70d3f..928d593c0 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol @@ -14,6 +14,7 @@ import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; import {L1NativeTokenVault} from "contracts/bridge/L1NativeTokenVault.sol"; import {IL1NativeTokenVault} from "contracts/bridge/interfaces/IL1NativeTokenVault.sol"; import {IL1AssetHandler} from "contracts/bridge/interfaces/IL1AssetHandler.sol"; +import {IL1BaseTokenAssetHandler} from "contracts/bridge/interfaces/IL1BaseTokenAssetHandler.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; import {L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; @@ -217,12 +218,12 @@ contract L1AssetRouterTest is Test { vm.mockCall( address(nativeTokenVault), - abi.encodeWithSelector(IL1AssetHandler.tokenAddress.selector, tokenAssetId), + abi.encodeWithSelector(IL1BaseTokenAssetHandler.tokenAddress.selector, tokenAssetId), abi.encode(address(token)) ); vm.mockCall( address(nativeTokenVault), - abi.encodeWithSelector(IL1AssetHandler.tokenAddress.selector, ETH_TOKEN_ASSET_ID), + abi.encodeWithSelector(IL1BaseTokenAssetHandler.tokenAddress.selector, ETH_TOKEN_ASSET_ID), abi.encode(address(ETH_TOKEN_ADDRESS)) ); } From 1e13d500b0d8ad976d11ac638b075d49858ed4fb Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 11:58:02 +0100 Subject: [PATCH 17/51] add a getAssetId function --- l1-contracts/contracts/bridge/L1NativeTokenVault.sol | 7 ++++++- .../contracts/bridge/interfaces/IL1NativeTokenVault.sol | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/l1-contracts/contracts/bridge/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/L1NativeTokenVault.sol index b2620e953..c28136c00 100644 --- a/l1-contracts/contracts/bridge/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/L1NativeTokenVault.sol @@ -219,10 +219,15 @@ contract L1NativeTokenVault is IL1NativeTokenVault, Ownable2StepUpgradeable, Pau } /// @dev Receives and parses (name, symbol, decimals) from the token contract - function getERC20Getters(address _token) public view returns (bytes memory) { + function getERC20Getters(address _token) public view override returns (bytes memory) { return BridgeHelper.getERC20Getters(_token, ETH_TOKEN_ADDRESS); } + /// @dev Shows the assetId for a given chain and token address + function getAssetId(uint256 _chainId, address _l1Token) external pure override returns (bytes32) { + return DataEncoding.encodeNTVAssetId(_chainId, _l1Token); + } + /// @dev Transfers tokens from the depositor address to the smart contract address. /// @return The difference between the contract balance before and after the transferring of funds. function _depositFunds(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) { diff --git a/l1-contracts/contracts/bridge/interfaces/IL1NativeTokenVault.sol b/l1-contracts/contracts/bridge/interfaces/IL1NativeTokenVault.sol index 805a45507..d8cb389d2 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1NativeTokenVault.sol @@ -25,4 +25,7 @@ interface IL1NativeTokenVault is IL1AssetHandler, IL1BaseTokenAssetHandler { /// @notice Used the get token balance for specific ZK chain in shared bridge function chainBalance(uint256 _chainId, address _l1Token) external view returns (uint256); + + /// @dev Shows the assetId for a given chain and token address + function getAssetId(uint256 _chainId, address _l1Token) external pure returns (bytes32); } From ad7c0ad539af6ff78e14e063160f1713a2ba3306 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 12:56:22 +0100 Subject: [PATCH 18/51] Stas issue fixes --- l1-contracts/contracts/bridge/L1AssetRouter.sol | 5 ++--- .../contracts/bridge/interfaces/IL1AssetRouter.sol | 9 ++------- l1-contracts/contracts/bridgehub/Bridgehub.sol | 3 ++- l1-contracts/contracts/bridgehub/IBridgehub.sol | 2 ++ .../state-transition/chain-deps/facets/Admin.sol | 8 ++++++++ 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1AssetRouter.sol b/l1-contracts/contracts/bridge/L1AssetRouter.sol index fee60d7f0..1d38c08ef 100644 --- a/l1-contracts/contracts/bridge/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/L1AssetRouter.sol @@ -523,7 +523,7 @@ contract L1AssetRouter is _assetData ); - emit ClaimedFailedDepositSharedBridge(_chainId, _depositSender, _assetId, _assetData); + emit ClaimedFailedDepositAssetRouter(_chainId, _depositSender, _assetId, _assetData); } /// @dev Receives and parses (name, symbol, decimals) from the token contract @@ -610,8 +610,7 @@ contract L1AssetRouter is if (l1AssetHandler == address(nativeTokenVault)) { (amount, l1Receiver) = abi.decode(transferData, (uint256, address)); } - - emit WithdrawalFinalizedSharedBridge(_chainId, l1Receiver, assetId, amount); + emit WithdrawalFinalizedAssetRouter(_chainId, assetId, transferData); } /// @notice Decodes the transfer input for legacy data and transfers allowance to NTV diff --git a/l1-contracts/contracts/bridge/interfaces/IL1AssetRouter.sol b/l1-contracts/contracts/bridge/interfaces/IL1AssetRouter.sol index 945f272f2..b89d9f8fa 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1AssetRouter.sol @@ -43,14 +43,9 @@ interface IL1AssetRouter { bytes32 indexed l2DepositTxHash ); - event WithdrawalFinalizedSharedBridge( - uint256 indexed chainId, - address indexed to, - bytes32 indexed assetId, - uint256 amount - ); + event WithdrawalFinalizedAssetRouter(uint256 indexed chainId, bytes32 indexed assetId, bytes assetData); - event ClaimedFailedDepositSharedBridge( + event ClaimedFailedDepositAssetRouter( uint256 indexed chainId, address indexed to, bytes32 indexed assetId, diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index ba84b9ebb..4c8a0d6aa 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -89,7 +89,8 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @dev Sync layer chain is expected to have .. as the base token. mapping(uint256 chainId => bool isWhitelistedSettlementLayer) public whitelistedSettlementLayers; - bool private migrationPaused; + /// @notice used to pause the migrations of chains. Used for upgrades. + bool public migrationPaused; modifier onlyOwnerOrAdmin() { require(msg.sender == admin || msg.sender == owner(), "BH: not owner or admin"); diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index 723b32aa9..731fb2a51 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -89,6 +89,8 @@ interface IBridgehub is IL1AssetHandler { function getAllHyperchainChainIDs() external view returns (uint256[] memory); + function migrationPaused() external view returns (bool); + /// Mailbox forwarder function proveL2MessageInclusion( diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index 46166f2f0..4a33eac7b 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -250,6 +250,13 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { ) external payable override onlyBridgehub { HyperchainCommitment memory _commitment = abi.decode(_data, (HyperchainCommitment)); + IStateTransitionManager stm = IStateTransitionManager(s.stateTransitionManager); + + uint256 currentProtocolVersion = s.protocolVersion; + uint256 protocolVersion = stm.protocolVersion(); + require(currentProtocolVersion == protocolVersion, "STM: protocolVersion not up to date"); + require(currentProtocolVersion == _commitment.protocolVersion, "Af: protocolVersion mismatch"); + uint256 batchesExecuted = _commitment.totalBatchesExecuted; uint256 batchesVerified = _commitment.totalBatchesVerified; uint256 batchesCommitted = _commitment.totalBatchesCommitted; @@ -291,6 +298,7 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { } else if (_contractAlreadyDeployed) { require(s.settlementLayer != address(0), "Af: not migrated 2"); s.priorityTree.checkGWReinit(_commitment.priorityTree); + s.priorityTree.initFromCommitment(_commitment.priorityTree); } else { s.priorityTree.initFromCommitment(_commitment.priorityTree); } From 327450737754d5f5218b2891a4a384a00c67dacf Mon Sep 17 00:00:00 2001 From: Danil Date: Wed, 21 Aug 2024 14:05:52 +0200 Subject: [PATCH 19/51] fix remappings Signed-off-by: Danil --- l2-contracts/foundry.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index 33d7f57a6..1b88dd9c0 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -1,7 +1,7 @@ [profile.default] src = "contracts" out = "out" -libs = ["node_modules", "../lib"] +libs = ["node_modules", "lib"] test = "test/foundry" solc_version = "0.8.20" cache_path = "cache-forge" @@ -9,6 +9,6 @@ evm_version = "paris" ignored_error_codes = ["missing-receive-ether", "code-size"] ignored_warnings_from = ["test", "contracts/dev-contracts"] remappings = [ - "forge-std/=../lib/forge-std/src/", + "forge-std/=lib/forge-std/src/", "foundry-test/=test/foundry/", ] From 37245fa764a481fab1e40e6b8fb219eff45488bc Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 13:48:22 +0100 Subject: [PATCH 20/51] foundry test and small fixes --- .../contracts/bridgehub/Bridgehub.sol | 2 +- .../chain-deps/facets/Admin.sol | 1 - .../foundry/integration/GatewayTests.t.sol | 2 +- .../L1SharedBridge/L1SharedBridgeBase.t.sol | 16 +++++++------- .../L1SharedBridgeHyperEnabled.t.sol | 22 +++++++++---------- .../L1SharedBridge/L1SharedBridgeLegacy.t.sol | 10 ++++----- .../_L1SharedBridge_Shared.t.sol | 9 ++------ 7 files changed, 28 insertions(+), 34 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 02c173fd1..205280b92 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -94,7 +94,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @notice we store registered assetIds (for arbitrary base token) mapping(bytes32 baseTokenAssetId => bool) public assetIdIsRegistered; - + /// @notice used to pause the migrations of chains. Used for upgrades. bool public migrationPaused; diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index 15f3a23d5..e76f05f95 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -256,7 +256,6 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { uint256 currentProtocolVersion = s.protocolVersion; uint256 protocolVersion = stm.protocolVersion(); require(currentProtocolVersion == protocolVersion, "STM: protocolVersion not up to date"); - require(currentProtocolVersion == _commitment.protocolVersion, "Af: protocolVersion mismatch"); uint256 batchesExecuted = _commitment.totalBatchesExecuted; uint256 batchesVerified = _commitment.totalBatchesVerified; diff --git a/l1-contracts/test/foundry/integration/GatewayTests.t.sol b/l1-contracts/test/foundry/integration/GatewayTests.t.sol index cc13a552b..a20ea03cc 100644 --- a/l1-contracts/test/foundry/integration/GatewayTests.t.sol +++ b/l1-contracts/test/foundry/integration/GatewayTests.t.sol @@ -170,7 +170,7 @@ contract GatewayTests is L1ContractDeployer, HyperchainDeployer, TokenDeployer, IZkSyncHyperchain migratingChain = IZkSyncHyperchain(bridgehub.getHyperchain(migratingChainId)); bytes32 assetId = bridgehub.stmAssetIdFromChainId(migratingChainId); - vm.startBroadcast(address(stm)); + vm.startBroadcast(Ownable(address(bridgehub)).owner()); bridgehub.registerSettlementLayer(gatewayChainId, true); vm.stopBroadcast(); diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol index af81edea0..4e672d975 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol @@ -142,7 +142,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit ClaimedFailedDepositSharedBridge({ + emit ClaimedFailedDepositAssetRouter({ chainId: chainId, to: alice, assetId: tokenAssetId, @@ -184,7 +184,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit ClaimedFailedDepositSharedBridge({ + emit ClaimedFailedDepositAssetRouter({ chainId: chainId, to: alice, assetId: ETH_TOKEN_ASSET_ID, @@ -227,7 +227,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit ClaimedFailedDepositSharedBridge({ + emit ClaimedFailedDepositAssetRouter({ chainId: chainId, to: alice, assetId: ETH_TOKEN_ASSET_ID, @@ -270,7 +270,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, ETH_TOKEN_ASSET_ID, amount); + emit WithdrawalFinalizedAssetRouter(chainId, ETH_TOKEN_ASSET_ID, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, @@ -310,7 +310,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, tokenAssetId, amount); + emit WithdrawalFinalizedAssetRouter(chainId, tokenAssetId, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, @@ -355,7 +355,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, ETH_TOKEN_ASSET_ID, amount); + emit WithdrawalFinalizedAssetRouter(chainId, ETH_TOKEN_ASSET_ID, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, @@ -397,7 +397,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, tokenAssetId, amount); + emit WithdrawalFinalizedAssetRouter(chainId, tokenAssetId, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, @@ -442,7 +442,7 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, tokenAssetId, amount); + emit WithdrawalFinalizedAssetRouter(chainId, tokenAssetId, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeHyperEnabled.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeHyperEnabled.t.sol index c0a170689..acf185191 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeHyperEnabled.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeHyperEnabled.t.sol @@ -118,7 +118,7 @@ contract L1AssetRouterHyperEnabledTest is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit ClaimedFailedDepositSharedBridge(chainId, alice, tokenAssetId, abi.encode(bytes32(0))); + emit ClaimedFailedDepositAssetRouter(chainId, alice, tokenAssetId, abi.encode(bytes32(0))); vm.prank(bridgehubAddress); sharedBridge.claimFailedDeposit({ _chainId: chainId, @@ -161,7 +161,7 @@ contract L1AssetRouterHyperEnabledTest is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit ClaimedFailedDepositSharedBridge(chainId, alice, ETH_TOKEN_ASSET_ID, abi.encode(bytes32(0))); + emit ClaimedFailedDepositAssetRouter(chainId, alice, ETH_TOKEN_ASSET_ID, abi.encode(bytes32(0))); vm.prank(bridgehubAddress); sharedBridge.claimFailedDeposit({ _chainId: chainId, @@ -201,8 +201,8 @@ contract L1AssetRouterHyperEnabledTest is L1AssetRouterTest { ); // solhint-disable-next-line func-named-parameters - vm.expectEmit(true, true, true, true, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, ETH_TOKEN_ASSET_ID, amount); + vm.expectEmit(true, true, true, false, address(sharedBridge)); + emit WithdrawalFinalizedAssetRouter(chainId, ETH_TOKEN_ASSET_ID, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, @@ -242,8 +242,8 @@ contract L1AssetRouterHyperEnabledTest is L1AssetRouterTest { ); // solhint-disable-next-line func-named-parameters - vm.expectEmit(true, true, true, true, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, tokenAssetId, amount); + vm.expectEmit(true, true, true, false, address(sharedBridge)); + emit WithdrawalFinalizedAssetRouter(chainId, tokenAssetId, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, @@ -283,8 +283,8 @@ contract L1AssetRouterHyperEnabledTest is L1AssetRouterTest { ); // solhint-disable-next-line func-named-parameters - vm.expectEmit(true, true, true, true, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, ETH_TOKEN_ASSET_ID, amount); + vm.expectEmit(true, true, true, false, address(sharedBridge)); + emit WithdrawalFinalizedAssetRouter(chainId, ETH_TOKEN_ASSET_ID, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, @@ -325,7 +325,7 @@ contract L1AssetRouterHyperEnabledTest is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, tokenAssetId, amount); + emit WithdrawalFinalizedAssetRouter(chainId, tokenAssetId, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, @@ -364,8 +364,8 @@ contract L1AssetRouterHyperEnabledTest is L1AssetRouterTest { ); // solhint-disable-next-line func-named-parameters - vm.expectEmit(true, true, true, true, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(chainId, alice, tokenAssetId, amount); + vm.expectEmit(true, true, true, false, address(sharedBridge)); + emit WithdrawalFinalizedAssetRouter(chainId, tokenAssetId, message); sharedBridge.finalizeWithdrawal({ _chainId: chainId, _l2BatchNumber: l2BatchNumber, diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeLegacy.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeLegacy.t.sol index 84ab99ab7..c9368be8c 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeLegacy.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeLegacy.t.sol @@ -81,8 +81,8 @@ contract L1AssetRouterLegacyTest is L1AssetRouterTest { ); // solhint-disable-next-line func-named-parameters - vm.expectEmit(true, true, true, true, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(eraChainId, alice, ETH_TOKEN_ASSET_ID, amount); + vm.expectEmit(true, true, true, false, address(sharedBridge)); + emit WithdrawalFinalizedAssetRouter(eraChainId, ETH_TOKEN_ASSET_ID, message); vm.prank(l1ERC20BridgeAddress); sharedBridge.finalizeWithdrawalLegacyErc20Bridge({ _l2BatchNumber: l2BatchNumber, @@ -130,8 +130,8 @@ contract L1AssetRouterLegacyTest is L1AssetRouterTest { bytes32(uint256(uint160(address(nativeTokenVault)))) ); // solhint-disable-next-line func-named-parameters - vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit WithdrawalFinalizedSharedBridge(eraChainId, alice, tokenAssetId, amount); + vm.expectEmit(true, true, false, false, address(sharedBridge)); + emit WithdrawalFinalizedAssetRouter(eraChainId, tokenAssetId, message); vm.prank(l1ERC20BridgeAddress); sharedBridge.finalizeWithdrawalLegacyErc20Bridge({ _l2BatchNumber: l2BatchNumber, @@ -184,7 +184,7 @@ contract L1AssetRouterLegacyTest is L1AssetRouterTest { // solhint-disable-next-line func-named-parameters vm.expectEmit(true, true, true, false, address(sharedBridge)); - emit ClaimedFailedDepositSharedBridge(eraChainId, alice, (tokenAssetId), abi.encode(bytes32(0))); + emit ClaimedFailedDepositAssetRouter(eraChainId, alice, (tokenAssetId), abi.encode(bytes32(0))); vm.prank(l1ERC20BridgeAddress); sharedBridge.claimFailedDeposit({ diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol index db1f70d3f..0dfd9f3cd 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol @@ -42,14 +42,9 @@ contract L1AssetRouterTest is Test { bytes32 indexed l2DepositTxHash ); - event WithdrawalFinalizedSharedBridge( - uint256 indexed chainId, - address indexed to, - bytes32 indexed assetId, - uint256 amount - ); + event WithdrawalFinalizedAssetRouter(uint256 indexed chainId, bytes32 indexed assetId, bytes assetData); - event ClaimedFailedDepositSharedBridge( + event ClaimedFailedDepositAssetRouter( uint256 indexed chainId, address indexed to, bytes32 indexed assetId, From a7e3d0a0110a6cf3d366f49efe370bf6ed595ee0 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 13:49:15 +0100 Subject: [PATCH 21/51] linting --- l1-contracts/deploy-scripts/Gateway.s.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/l1-contracts/deploy-scripts/Gateway.s.sol b/l1-contracts/deploy-scripts/Gateway.s.sol index 8ba714f09..00e34db8a 100644 --- a/l1-contracts/deploy-scripts/Gateway.s.sol +++ b/l1-contracts/deploy-scripts/Gateway.s.sol @@ -21,7 +21,6 @@ import {L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; // import {IL1AssetRouter} from "contracts/bridge/interfaces/IL1AssetRouter.sol"; -import {IStateTransitionManager} from "contracts/state-transition/IStateTransitionManager.sol"; import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol"; contract GatewayScript is Script { From a59077c754472e0af362e2d79396e7cbc47cfabe Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 14:01:39 +0100 Subject: [PATCH 22/51] script fix --- l1-contracts/scripts/register-hyperchain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/scripts/register-hyperchain.ts b/l1-contracts/scripts/register-hyperchain.ts index d5e7f49a5..cc7cb417f 100644 --- a/l1-contracts/scripts/register-hyperchain.ts +++ b/l1-contracts/scripts/register-hyperchain.ts @@ -66,7 +66,7 @@ async function main() { .option("--validium-mode") .option("--base-token-name ") .option("--base-token-address ") - .option("--use-governance ") + .option("--use-governance") .option("--token-multiplier-setter-address ") .action(async (cmd) => { const deployWallet = cmd.privateKey From 8421abcc7c247ca4d50df8f9963f015f65dc50d0 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 21 Aug 2024 22:06:52 +0100 Subject: [PATCH 23/51] forceDeploymetns data --- .../GenerateForceDeploymentsData.s.sol | 18 ++++++++++++++++-- l1-contracts/src.ts/deploy.ts | 12 +++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol b/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol index 98daeebad..2a502a4e6 100644 --- a/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol +++ b/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol @@ -4,7 +4,7 @@ import {Script} from "forge-std/Script.sol"; import {stdToml} from "forge-std/StdToml.sol"; import {Utils} from "./Utils.sol"; -import {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDRESS} from "contracts/common/L2ContractAddresses.sol"; +import {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_MESSAGE_ROOT} from "contracts/common/L2ContractAddresses.sol"; import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {ForceDeployment} from "contracts/state-transition/l2-deps/IL2GenesisUpgrade.sol"; @@ -33,6 +33,7 @@ contract GenerateForceDeploymentsData is Script { bytes l2StandardErc20FactoryBytecode; bytes l2TokenProxyBytecode; bytes l2StandardErc20Bytecode; + bytes messageRootBytecode; } function run() public { @@ -62,6 +63,9 @@ contract GenerateForceDeploymentsData is Script { contracts.bridgehubBytecode = Utils.readHardhatBytecode( "/../l1-contracts/artifacts-zk/contracts/bridgehub/Bridgehub.sol/Bridgehub.json" ); + contracts.messageRootBytecode = Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/bridgehub/MessageRoot.sol/MessageRoot.json" + ); contracts.l2NtvBytecode = Utils.readHardhatBytecode( "/../l2-contracts/artifacts-zk/contracts/bridge/L2NativeTokenVault.sol/L2NativeTokenVault.json" ); @@ -89,7 +93,7 @@ contract GenerateForceDeploymentsData is Script { function genesisForceDeploymentsData() internal { address aliasedGovernance = AddressAliasHelper.applyL1ToL2Alias(config.governance); - ForceDeployment[] memory forceDeployments = new ForceDeployment[](3); + ForceDeployment[] memory forceDeployments = new ForceDeployment[](4); forceDeployments[0] = ForceDeployment({ bytecodeHash: keccak256(contracts.bridgehubBytecode), @@ -123,6 +127,16 @@ contract GenerateForceDeploymentsData is Script { config.contractsDeployedAlready ) }); + + forceDeployments[3] = ForceDeployment({ + bytecodeHash: keccak256(contracts.messageRootBytecode), + newAddress: L2_MESSAGE_ROOT, + callConstructor: true, + value: 0, + // solhint-disable-next-line func-named-parameters + input: L2_BRIDGEHUB_ADDR + }); + config.forceDeploymentsData = abi.encode(forceDeployments); } } diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 09f43e712..9931cc77e 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -51,6 +51,7 @@ import { // priorityTxMaxGasLimit, encodeNTVAssetId, ETH_ADDRESS_IN_CONTRACTS, + L2_MESSAGE_ROOT_ADDRESS, } from "./utils"; import type { ChainAdminCall } from "./utils"; import { IGovernanceFactory } from "../typechain/IGovernanceFactory"; @@ -184,10 +185,12 @@ export class Deployer { let assetRouterZKBytecode = ethers.constants.HashZero; let nativeTokenVaultZKBytecode = ethers.constants.HashZero; let l2TokenProxyBytecodeHash = ethers.constants.HashZero; + let messageRootZKBytecode = ethers.constants.HashZero; if (process.env.CHAIN_ETH_NETWORK != "hardhat") { bridgehubZKBytecode = readBytecode("./artifacts-zk/contracts/bridgehub", "Bridgehub"); assetRouterZKBytecode = readBytecode("../l2-contracts/artifacts-zk/contracts/bridge", "L2AssetRouter"); nativeTokenVaultZKBytecode = readBytecode("../l2-contracts/artifacts-zk/contracts/bridge", "L2NativeTokenVault"); + messageRootZKBytecode = readBytecode("./artifacts-zk/contracts/bridgehub", "MessageRoot"); const l2TokenProxyBytecode = readBytecode( "../l2-contracts/artifacts-zk/@openzeppelin/contracts/proxy/beacon", "BeaconProxy" @@ -237,8 +240,15 @@ export class Deployer { ] ), }; + const messageRootDeployment = { + bytecodeHash: ethers.utils.hexlify(hashL2Bytecode(messageRootZKBytecode)), + newAddress: L2_MESSAGE_ROOT_ADDRESS, + callConstructor: true, + value: 0, + input: ethers.utils.defaultAbiCoder.encode(["address"], [L2_BRIDGEHUB_ADDRESS]), + }; - const forceDeployments = [bridgehubDeployment, assetRouterDeployment, ntvDeployment]; + const forceDeployments = [bridgehubDeployment, assetRouterDeployment, ntvDeployment, messageRootDeployment]; return ethers.utils.defaultAbiCoder.encode([FORCE_DEPLOYMENT_ABI_STRING], [forceDeployments]); } From a80a6673d50dd574d943c968be10d8843f2af9ac Mon Sep 17 00:00:00 2001 From: kelemeno Date: Thu, 22 Aug 2024 10:56:54 +0100 Subject: [PATCH 24/51] message root fix --- l1-contracts/contracts/bridgehub/MessageRoot.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/l1-contracts/contracts/bridgehub/MessageRoot.sol b/l1-contracts/contracts/bridgehub/MessageRoot.sol index 3fa3ed08e..37b60b1c6 100644 --- a/l1-contracts/contracts/bridgehub/MessageRoot.sol +++ b/l1-contracts/contracts/bridgehub/MessageRoot.sol @@ -121,6 +121,9 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { /// @dev Gets the aggregated root of all chains. function getAggregatedRoot() external view returns (bytes32) { + if (sharedTree._nodes.length == 0) { + return bytes32(0); + } return sharedTree.root(); } From 4255c632625ca5dbf6be59093a8e0e5961fdccb2 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Thu, 22 Aug 2024 11:26:43 +0100 Subject: [PATCH 25/51] renaming and some fixes --- l1-contracts/contracts/bridge/L1AssetRouter.sol | 4 ++-- l1-contracts/contracts/common/L2ContractAddresses.sol | 5 ++++- l1-contracts/contracts/common/libraries/DataEncoding.sol | 6 +++--- .../contracts/dev-contracts/test/DummyBridgehub.sol | 4 ++-- .../contracts/dev-contracts/test/DummySharedBridge.sol | 4 ++-- .../deploy-scripts/GenerateForceDeploymentsData.s.sol | 8 ++++---- .../unit/concrete/Bridgehub/experimental_bridge.t.sol | 4 ++-- .../Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol | 4 ++-- 8 files changed, 21 insertions(+), 18 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1AssetRouter.sol b/l1-contracts/contracts/bridge/L1AssetRouter.sol index 20317cd2d..1c682404d 100644 --- a/l1-contracts/contracts/bridge/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/L1AssetRouter.sol @@ -25,7 +25,7 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {TWO_BRIDGES_MAGIC_VALUE, ETH_TOKEN_ADDRESS} from "../common/Config.sol"; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../common/L2ContractAddresses.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../common/L2ContractAddresses.sol"; import {IBridgehub, L2TransactionRequestTwoBridgesInner, L2TransactionRequestDirect} from "../bridgehub/IBridgehub.sol"; import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "../common/L2ContractAddresses.sol"; @@ -269,7 +269,7 @@ contract L1AssetRouter is /// @param _assetHandlerAddress The address of the asset handler to be set for the provided asset. function setAssetHandlerAddressThisChain(bytes32 _assetRegistrationData, address _assetHandlerAddress) external { bool senderIsNTV = msg.sender == address(nativeTokenVault); - address sender = senderIsNTV ? L2_NATIVE_TOKEN_VAULT_ADDRESS : msg.sender; + address sender = senderIsNTV ? L2_NATIVE_TOKEN_VAULT_ADDR : msg.sender; bytes32 assetId = DataEncoding.encodeAssetId(block.chainid, _assetRegistrationData, sender); require(senderIsNTV || msg.sender == assetDeploymentTracker[assetId], "ShB: not NTV or ADT"); assetHandlerAddress[assetId] = _assetHandlerAddress; diff --git a/l1-contracts/contracts/common/L2ContractAddresses.sol b/l1-contracts/contracts/common/L2ContractAddresses.sol index 9ddb1635b..2e25163a1 100644 --- a/l1-contracts/contracts/common/L2ContractAddresses.sol +++ b/l1-contracts/contracts/common/L2ContractAddresses.sol @@ -47,4 +47,7 @@ address constant L2_ASSET_ROUTER_ADDR = address(0x10003); /// @dev An l2 system contract address, used in the assetId calculation for native assets. /// This is needed for automatic bridging, i.e. without deploying the AssetHandler contract, /// if the assetId can be calculated with this address then it is in fact an NTV asset -address constant L2_NATIVE_TOKEN_VAULT_ADDRESS = address(0x10004); +address constant L2_NATIVE_TOKEN_VAULT_ADDR = address(0x10004); + +/// @dev the address of the l2 asse3t router. +address constant L2_MESSAGE_ROOT_ADDR = address(0x10005); \ No newline at end of file diff --git a/l1-contracts/contracts/common/libraries/DataEncoding.sol b/l1-contracts/contracts/common/libraries/DataEncoding.sol index 39dcef4d5..435390f5f 100644 --- a/l1-contracts/contracts/common/libraries/DataEncoding.sol +++ b/l1-contracts/contracts/common/libraries/DataEncoding.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.24; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../L2ContractAddresses.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../L2ContractAddresses.sol"; /** * @author Matter Labs @@ -77,7 +77,7 @@ library DataEncoding { /// @param _assetData The asset data that has to be encoded. /// @return The encoded asset data. function encodeNTVAssetId(uint256 _chainId, bytes32 _assetData) internal pure returns (bytes32) { - return keccak256(abi.encode(_chainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, _assetData)); + return keccak256(abi.encode(_chainId, L2_NATIVE_TOKEN_VAULT_ADDR, _assetData)); } /// @notice Encodes the asset data by combining chain id, NTV as asset deployment tracker and asset data. @@ -85,6 +85,6 @@ library DataEncoding { /// @param _tokenAddress The address of token that has to be encoded (asset data is the address itself). /// @return The encoded asset data. function encodeNTVAssetId(uint256 _chainId, address _tokenAddress) internal pure returns (bytes32) { - return keccak256(abi.encode(_chainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, _tokenAddress)); + return keccak256(abi.encode(_chainId, L2_NATIVE_TOKEN_VAULT_ADDR, _tokenAddress)); } } diff --git a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol index 82e2a864c..463cbe017 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.24; import {ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../../common/L2ContractAddresses.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../../common/L2ContractAddresses.sol"; import {IMessageRoot} from "../../bridgehub/IMessageRoot.sol"; import {IGetters} from "../../state-transition/chain-interfaces/IGetters.sol"; @@ -23,7 +23,7 @@ contract DummyBridgehub { keccak256( abi.encode( block.chainid, - L2_NATIVE_TOKEN_VAULT_ADDRESS, + L2_NATIVE_TOKEN_VAULT_ADDR, ETH_TOKEN_ADDRESS // bytes32(uint256(uint160(IGetters(msg.sender).getBaseToken()))) ) diff --git a/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol b/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol index 989b1e523..33ce3302d 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol @@ -8,7 +8,7 @@ import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.so import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import {TWO_BRIDGES_MAGIC_VALUE, ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; import {IL1NativeTokenVault} from "../../bridge/L1NativeTokenVault.sol"; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../../common/L2ContractAddresses.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../../common/L2ContractAddresses.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IL2Bridge} from "../../bridge/interfaces/IL2Bridge.sol"; import {IL2BridgeLegacy} from "../../bridge/interfaces/IL2BridgeLegacy.sol"; @@ -217,7 +217,7 @@ contract DummySharedBridge is PausableUpgradeable { /// @dev Used to set the assedAddress for a given assetId. function setAssetHandlerAddressThisChain(bytes32 _additionalData, address _assetHandlerAddress) external { - address sender = msg.sender == address(nativeTokenVault) ? L2_NATIVE_TOKEN_VAULT_ADDRESS : msg.sender; + address sender = msg.sender == address(nativeTokenVault) ? L2_NATIVE_TOKEN_VAULT_ADDR : msg.sender; bytes32 assetId = keccak256(abi.encode(uint256(block.chainid), sender, _additionalData)); assetHandlerAddress[assetId] = _assetHandlerAddress; // assetDeploymentTracker[assetId] = sender; diff --git a/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol b/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol index 2a502a4e6..09ff467f1 100644 --- a/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol +++ b/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol @@ -4,7 +4,7 @@ import {Script} from "forge-std/Script.sol"; import {stdToml} from "forge-std/StdToml.sol"; import {Utils} from "./Utils.sol"; -import {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_MESSAGE_ROOT} from "contracts/common/L2ContractAddresses.sol"; +import {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_MESSAGE_ROOT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {ForceDeployment} from "contracts/state-transition/l2-deps/IL2GenesisUpgrade.sol"; @@ -114,7 +114,7 @@ contract GenerateForceDeploymentsData is Script { forceDeployments[2] = ForceDeployment({ bytecodeHash: keccak256(contracts.l2NtvBytecode), - newAddress: L2_NATIVE_TOKEN_VAULT_ADDRESS, + newAddress: L2_NATIVE_TOKEN_VAULT_ADDR, callConstructor: true, value: 0, // solhint-disable-next-line func-named-parameters @@ -130,11 +130,11 @@ contract GenerateForceDeploymentsData is Script { forceDeployments[3] = ForceDeployment({ bytecodeHash: keccak256(contracts.messageRootBytecode), - newAddress: L2_MESSAGE_ROOT, + newAddress: L2_MESSAGE_ROOT_ADDR, callConstructor: true, value: 0, // solhint-disable-next-line func-named-parameters - input: L2_BRIDGEHUB_ADDR + input: abi.encode(L2_BRIDGEHUB_ADDR) }); config.forceDeploymentsData = abi.encode(forceDeployments); diff --git a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol index efeca97fa..be76e1bba 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -19,7 +19,7 @@ import {L1AssetRouter} from "contracts/bridge/L1AssetRouter.sol"; import {L1NativeTokenVault} from "contracts/bridge/L1NativeTokenVault.sol"; import {L2Message, L2Log, TxStatus, BridgehubL2TransactionRequest} from "contracts/common/Messaging.sol"; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "contracts/common/L2ContractAddresses.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {ISTMDeploymentTracker} from "contracts/bridgehub/ISTMDeploymentTracker.sol"; @@ -52,7 +52,7 @@ contract ExperimentalBridgeTest is Test { bytes32 ETH_TOKEN_ASSET_ID = keccak256( - abi.encode(block.chainid, L2_NATIVE_TOKEN_VAULT_ADDRESS, bytes32(uint256(uint160(ETH_TOKEN_ADDRESS)))) + abi.encode(block.chainid, L2_NATIVE_TOKEN_VAULT_ADDR, bytes32(uint256(uint160(ETH_TOKEN_ADDRESS)))) ); TestnetERC20Token testToken6; diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol index 928d593c0..0b66cc708 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol @@ -16,7 +16,7 @@ import {IL1NativeTokenVault} from "contracts/bridge/interfaces/IL1NativeTokenVau import {IL1AssetHandler} from "contracts/bridge/interfaces/IL1AssetHandler.sol"; import {IL1BaseTokenAssetHandler} from "contracts/bridge/interfaces/IL1BaseTokenAssetHandler.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR, L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; contract L1AssetRouterTest is Test { @@ -101,7 +101,7 @@ contract L1AssetRouterTest is Test { uint256 legacyBatchNumber = 0; uint256 isWithdrawalFinalizedStorageLocation = uint256(8 - 1 + (1 + 49) + 0 + (1 + 49) + 50 + 1 + 50); - bytes32 ETH_TOKEN_ASSET_ID = keccak256(abi.encode(block.chainid, L2_NATIVE_TOKEN_VAULT_ADDRESS, ETH_TOKEN_ADDRESS)); + bytes32 ETH_TOKEN_ASSET_ID = keccak256(abi.encode(block.chainid, L2_NATIVE_TOKEN_VAULT_ADDR, ETH_TOKEN_ADDRESS)); function setUp() public { owner = makeAddr("owner"); From 51d5dc4a86eeabc6a0a49618449ff0cbea9e0905 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Thu, 22 Aug 2024 11:29:17 +0100 Subject: [PATCH 26/51] lint --- l1-contracts/contracts/common/L2ContractAddresses.sol | 2 +- .../foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/l1-contracts/contracts/common/L2ContractAddresses.sol b/l1-contracts/contracts/common/L2ContractAddresses.sol index 2e25163a1..ca7cfd07d 100644 --- a/l1-contracts/contracts/common/L2ContractAddresses.sol +++ b/l1-contracts/contracts/common/L2ContractAddresses.sol @@ -50,4 +50,4 @@ address constant L2_ASSET_ROUTER_ADDR = address(0x10003); address constant L2_NATIVE_TOKEN_VAULT_ADDR = address(0x10004); /// @dev the address of the l2 asse3t router. -address constant L2_MESSAGE_ROOT_ADDR = address(0x10005); \ No newline at end of file +address constant L2_MESSAGE_ROOT_ADDR = address(0x10005); diff --git a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol index be76e1bba..458b15042 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -51,9 +51,7 @@ contract ExperimentalBridgeTest is Test { bytes32 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; bytes32 ETH_TOKEN_ASSET_ID = - keccak256( - abi.encode(block.chainid, L2_NATIVE_TOKEN_VAULT_ADDR, bytes32(uint256(uint160(ETH_TOKEN_ADDRESS)))) - ); + keccak256(abi.encode(block.chainid, L2_NATIVE_TOKEN_VAULT_ADDR, bytes32(uint256(uint160(ETH_TOKEN_ADDRESS))))); TestnetERC20Token testToken6; TestnetERC20Token testToken8; From f741346ab9b84a14c6158aaf5b3d095fa2b538e8 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 22 Aug 2024 13:13:01 +0200 Subject: [PATCH 27/51] wip --- l2-contracts/contracts/L2ContractHelper.sol | 38 +++++++++++ l2-contracts/foundry.toml | 5 ++ l2-contracts/package.json | 2 +- .../foundry/unit/erc20/L1ERC20Bridge.t.sol | 66 ++++++++++++++++++- package.json | 3 +- 5 files changed, 111 insertions(+), 3 deletions(-) diff --git a/l2-contracts/contracts/L2ContractHelper.sol b/l2-contracts/contracts/L2ContractHelper.sol index 492d40015..0d2a93951 100644 --- a/l2-contracts/contracts/L2ContractHelper.sol +++ b/l2-contracts/contracts/L2ContractHelper.sol @@ -56,6 +56,13 @@ interface IContractDeployer { /// @param _bytecodeHash the bytecodehash of the new contract to be deployed /// @param _input the calldata to be sent to the constructor of the new contract function create2(bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input) external returns (address); + + function getNewAddressCreate2( + address _sender, + bytes32 _bytecodeHash, + bytes32 _salt, + bytes calldata _input + ) external view returns (address newAddress) ; } /** @@ -193,6 +200,37 @@ library L2ContractHelper { // Setting the length hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224); } + + /// @notice Validate the bytecode format and calculate its hash. + /// @param _bytecode The bytecode to hash. + /// @return hashedBytecode The 32-byte hash of the bytecode. + /// Note: The function reverts the execution if the bytecode has non expected format: + /// - Bytecode bytes length is not a multiple of 32 + /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words) + /// - Bytecode words length is not odd + function hashL2BytecodeMemory(bytes memory _bytecode) internal view returns (bytes32 hashedBytecode) { + // Note that the length of the bytecode must be provided in 32-byte words. + if (_bytecode.length % 32 != 0) { + revert MalformedBytecode(BytecodeError.Length); + } + + uint256 bytecodeLenInWords = _bytecode.length / 32; + // bytecode length must be less than 2^16 words + if (bytecodeLenInWords >= 2 ** 16) { + revert MalformedBytecode(BytecodeError.NumberOfWords); + } + // bytecode length in words must be odd + if (bytecodeLenInWords % 2 == 0) { + revert MalformedBytecode(BytecodeError.WordsMustBeOdd); + } + hashedBytecode = + sha256(_bytecode) & + 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + // Setting the version of the hash + hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248))); + // Setting the length + hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224); + } } /// @notice Structure used to represent a ZKsync transaction. diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index 1b88dd9c0..7f3936b3b 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -5,6 +5,7 @@ libs = ["node_modules", "lib"] test = "test/foundry" solc_version = "0.8.20" cache_path = "cache-forge" +via_ir = true evm_version = "paris" ignored_error_codes = ["missing-receive-ether", "code-size"] ignored_warnings_from = ["test", "contracts/dev-contracts"] @@ -12,3 +13,7 @@ remappings = [ "forge-std/=lib/forge-std/src/", "foundry-test/=test/foundry/", ] +fs_permissions = [ + { access = "read", path = "zkout" }, +] + diff --git a/l2-contracts/package.json b/l2-contracts/package.json index 1f20c3495..6c4469960 100644 --- a/l2-contracts/package.json +++ b/l2-contracts/package.json @@ -33,7 +33,7 @@ }, "scripts": { "build": "hardhat compile", - "test:foundry": "forge test", + "test:foundry": "forge test --zksync", "clean": "hardhat clean", "test": "hardhat test", "verify": "hardhat run src/verify.ts", diff --git a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol b/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol index 87bf1db99..6aef454b0 100644 --- a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol +++ b/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol @@ -2,9 +2,73 @@ pragma solidity 0.8.20; +import {Vm} from "forge-std/Vm.sol"; + +import {Script, console2 as console} from "forge-std/Script.sol"; + import {Test} from "forge-std/Test.sol"; import {L2StandardERC20} from "contracts/bridge/L2StandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/L2AssetRouter.sol"; + +import { IContractDeployer, DEPLOYER_SYSTEM_CONTRACT, L2ContractHelper, L2_ASSET_ROUTER } from "contracts/L2ContractHelper.sol"; + + +contract SomeOtherContract { + constructor() { + + } +} + +library Utils { + address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); + Vm internal constant vm = Vm(VM_ADDRESS); + + address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); + + string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; + + function readEraBytecode(string memory _path) internal returns (bytes memory bytecode) { + string memory artifact = vm.readFile(_path); + bytecode = vm.parseJsonBytes(artifact, ".bytecode.object"); + } + + function forceDeployAssetRouter( + uint256 _l1ChainId, uint256 _eraChainId, address _l1AssetRouter, address _legacySharedBridge + ) internal { + // to ensure that the bytecode is known + { + L2AssetRouter dummy = new L2AssetRouter(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge); + bytes32 myhash; + assembly { + myhash := extcodehash(dummy) + } + console.logBytes32(myhash); + } + + bytes memory bytecode = readEraBytecode(L2_ASSET_ROUTER_PATH); + + bytes32 bytecodehash = L2ContractHelper.hashL2BytecodeMemory(bytecode); + console.logBytes32(bytecodehash); + + IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); + deployments[0] = IContractDeployer.ForceDeployment({ + bytecodeHash: L2ContractHelper.hashL2BytecodeMemory(bytecode), + newAddress: address(L2_ASSET_ROUTER), + callConstructor: true, + value: 0, + input: abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge) + }); + + // console.log(IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).getNewAddressCreate2(address(0), bytes32(0), bytes32(0), new bytes(0))); + + // vm.zkVm(true); + vm.prank(L2_FORCE_DEPLOYER_ADDR); + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( + deployments + ); + } +} contract L1Erc20BridgeTest is Test { // L1ERC20Bridge internal bridge; @@ -39,6 +103,6 @@ contract L1Erc20BridgeTest is Test { // function test() internal virtual {} function test_Stuff() public { - L2StandardERC20 l2StandardERC20 = new L2StandardERC20(); + Utils.forceDeployAssetRouter(9, 9, address(1), address(0)); } } diff --git a/package.json b/package.json index b17e3cb21..701d86666 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "gas-bound-caller" ], "nohoist": [ - "**/@openzeppelin/**" + "**/@openzeppelin/**", + "**/@matterlabs/**" ] }, "devDependencies": { From f6173013df58eb75a5de752e796edc1ecb1e1232 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 22 Aug 2024 22:38:49 +0200 Subject: [PATCH 28/51] erc20 tests in foundry work --- l2-contracts/foundry.toml | 6 +- .../foundry/unit/erc20/L1ERC20Bridge.t.sol | 244 +++++++++++++++--- 2 files changed, 210 insertions(+), 40 deletions(-) diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index 7f3936b3b..f7e77091f 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -11,9 +11,13 @@ ignored_error_codes = ["missing-receive-ether", "code-size"] ignored_warnings_from = ["test", "contracts/dev-contracts"] remappings = [ "forge-std/=lib/forge-std/src/", - "foundry-test/=test/foundry/", + "foundry-test/=test/foundry/" ] fs_permissions = [ { access = "read", path = "zkout" }, + { access = "read", path = "../system-contracts/bootloader/build/artifacts" }, + { access = "read", path = "../system-contracts/artifacts-zk/contracts-preprocessed" } ] +[profile.default.zksync] +enable_eravm_extensions = true diff --git a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol b/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol index 6aef454b0..1963687da 100644 --- a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol +++ b/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol @@ -11,14 +11,15 @@ import {Test} from "forge-std/Test.sol"; import {L2StandardERC20} from "contracts/bridge/L2StandardERC20.sol"; import {L2AssetRouter} from "contracts/bridge/L2AssetRouter.sol"; -import { IContractDeployer, DEPLOYER_SYSTEM_CONTRACT, L2ContractHelper, L2_ASSET_ROUTER } from "contracts/L2ContractHelper.sol"; +import { UpgradeableBeacon } from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import { BeaconProxy } from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; +import { IContractDeployer, DEPLOYER_SYSTEM_CONTRACT, L2ContractHelper, L2_ASSET_ROUTER, L2_NATIVE_TOKEN_VAULT } from "contracts/L2ContractHelper.sol"; -contract SomeOtherContract { - constructor() { +import {L2NativeTokenVault} from "contracts/bridge/L2NativeTokenVault.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; - } -} +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; library Utils { address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); @@ -27,82 +28,247 @@ library Utils { address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; + string internal constant L2_NATIVE_TOKEN_VAULT_PATH = "./zkout/L2NativeTokenVault.sol/L2NativeTokenVault.json"; + function readEraBytecode(string memory _path) internal returns (bytes memory bytecode) { string memory artifact = vm.readFile(_path); bytecode = vm.parseJsonBytes(artifact, ".bytecode.object"); } + /** + * @dev Returns the bytecode of a given system contract. + */ + function readSystemContractsBytecode(string memory filename) internal view returns (bytes memory) { + string memory file = vm.readFile( + // solhint-disable-next-line func-named-parameters + string.concat( + "../system-contracts/artifacts-zk/contracts-preprocessed/", + filename, + ".sol/", + filename, + ".json" + ) + ); + bytes memory bytecode = vm.parseJson(file, "$.bytecode"); + return bytecode; + } + + function initSystemContext() internal { + bytes memory contractDeployerBytecode = readSystemContractsBytecode("ContractDeployer"); + vm.etch(DEPLOYER_SYSTEM_CONTRACT, contractDeployerBytecode); + } + function forceDeployAssetRouter( uint256 _l1ChainId, uint256 _eraChainId, address _l1AssetRouter, address _legacySharedBridge ) internal { // to ensure that the bytecode is known { - L2AssetRouter dummy = new L2AssetRouter(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge); - bytes32 myhash; - assembly { - myhash := extcodehash(dummy) - } - console.logBytes32(myhash); + new L2AssetRouter(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge); } bytes memory bytecode = readEraBytecode(L2_ASSET_ROUTER_PATH); bytes32 bytecodehash = L2ContractHelper.hashL2BytecodeMemory(bytecode); - console.logBytes32(bytecodehash); IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); deployments[0] = IContractDeployer.ForceDeployment({ - bytecodeHash: L2ContractHelper.hashL2BytecodeMemory(bytecode), + bytecodeHash: bytecodehash, newAddress: address(L2_ASSET_ROUTER), callConstructor: true, value: 0, input: abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge) }); - // console.log(IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).getNewAddressCreate2(address(0), bytes32(0), bytes32(0), new bytes(0))); + vm.prank(L2_FORCE_DEPLOYER_ADDR); + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( + deployments + ); + } + + function forceDeployNativeTokenVault( + uint256 _l1ChainId, + address _aliasedOwner, + bytes32 _l2TokenProxyBytecodeHash, + address _legacySharedBridge, + address _l2TokenBeacon, + bool _contractsDeployedAlready + ) internal { + // to ensure that the bytecode is known + { + new L2NativeTokenVault(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready); + } + + bytes memory bytecode = readEraBytecode(L2_NATIVE_TOKEN_VAULT_PATH); + + bytes32 bytecodehash = L2ContractHelper.hashL2BytecodeMemory(bytecode); + + IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); + deployments[0] = IContractDeployer.ForceDeployment({ + bytecodeHash: bytecodehash, + newAddress: address(L2_NATIVE_TOKEN_VAULT), + callConstructor: true, + value: 0, + input: abi.encode(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready) + }); - // vm.zkVm(true); vm.prank(L2_FORCE_DEPLOYER_ADDR); IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( deployments ); } + + function encodeTokenData(string memory name, string memory symbol, uint8 decimals) internal pure returns (bytes memory) { + bytes memory encodedName = abi.encode(name); + bytes memory encodedSymbol = abi.encode(symbol); + bytes memory encodedDecimals = abi.encode(decimals); + + return abi.encode(encodedName, encodedSymbol, encodedDecimals); + } + } contract L1Erc20BridgeTest is Test { - // L1ERC20Bridge internal bridge; + // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. + // It is a bit easier to use EOA and it is sufficient for the tests. + address l1BridgeWallet = address(1); + address aliasedL1BridgeWallet; + + // The owner of the beacon and the native token vault + address ownerWallet = address(2); - // ReenterL1ERC20Bridge internal reenterL1ERC20Bridge; - // L1ERC20Bridge internal bridgeReenterItself; + L2StandardERC20 standardErc20Impl; - // TestnetERC20Token internal token; - // TestnetERC20Token internal feeOnTransferToken; - // address internal randomSigner; - // address internal alice; - // address sharedBridgeAddress; + UpgradeableBeacon beacon; - constructor() { - // randomSigner = makeAddr("randomSigner"); - // alice = makeAddr("alice"); - // sharedBridgeAddress = makeAddr("shared bridge"); - // bridge = new L1ERC20Bridge(IL1SharedBridge(sharedBridgeAddress)); + uint256 l1ChainId = 9; + uint256 eraChainId = 270; - // reenterL1ERC20Bridge = new ReenterL1ERC20Bridge(); - // bridgeReenterItself = new L1ERC20Bridge(IL1SharedBridge(address(reenterL1ERC20Bridge))); - // reenterL1ERC20Bridge.setBridge(bridgeReenterItself); + // We won't actually deploy an L1 token in these tests, but we need some address for it. + address L1_TOKEN_ADDRESS = 0x1111100000000000000000000000000000011111; - // token = new TestnetERC20Token("TestnetERC20Token", "TET", 18); - // feeOnTransferToken = new FeeOnTransferToken("FeeOnTransferToken", "FOT", 18); - // token.mint(alice, type(uint256).max); - // feeOnTransferToken.mint(alice, type(uint256).max); + string constant TOKEN_DEFAULT_NAME = "TestnetERC20Token"; + string constant TOKEN_DEFAULT_SYMBOL = "TET"; + uint8 constant TOKEN_DEFAULT_DECIMALS = 18; + + + function setUp() public { + aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); + + standardErc20Impl = new L2StandardERC20(); + + beacon = new UpgradeableBeacon(address(standardErc20Impl)); + beacon.transferOwnership(ownerWallet); + + // One of the purposes of deploying it here is to publish its bytecode + BeaconProxy proxy = new BeaconProxy(address(beacon), new bytes(0)); + + bytes32 beaconProxyBytecodeHash; + assembly { + beaconProxyBytecodeHash := extcodehash(proxy) + } + + Utils.initSystemContext(); + Utils.forceDeployAssetRouter( + l1ChainId, + eraChainId, + l1BridgeWallet, + address(0) + ); + Utils.forceDeployNativeTokenVault( + l1ChainId, + ownerWallet, + beaconProxyBytecodeHash, + address(0), + address(beacon), + true + ); + } + + function performDeposit( + address depositor, + address receiver, + uint256 amount + ) internal { + vm.prank(aliasedL1BridgeWallet); + L2AssetRouter(address(L2_ASSET_ROUTER)).finalizeDeposit( + depositor, + receiver, + L1_TOKEN_ADDRESS, + amount, + Utils.encodeTokenData(TOKEN_DEFAULT_NAME, TOKEN_DEFAULT_SYMBOL, TOKEN_DEFAULT_DECIMALS) + ); } - // add this to be excluded from coverage report - // function test() internal virtual {} + function initializeTokenByDeposit() internal returns (address l2TokenAddress) { + performDeposit( + makeAddr("someDepositor"), + makeAddr("someReeiver"), + 1 + ); + + l2TokenAddress = L2_NATIVE_TOKEN_VAULT.l2TokenAddress(L1_TOKEN_ADDRESS); + require(l2TokenAddress != address(0), "Token not initialized"); + } - function test_Stuff() public { - Utils.forceDeployAssetRouter(9, 9, address(1), address(0)); + function test_shouldFinalizeERC20Deposit() public { + address depositor = makeAddr("depositor"); + address receiver = makeAddr("receiver"); + + performDeposit( + depositor, + receiver, + 100 + ); + + address l2TokenAddress = L2_NATIVE_TOKEN_VAULT.l2TokenAddress(L1_TOKEN_ADDRESS); + + assertEq(L2StandardERC20(l2TokenAddress).balanceOf(receiver), 100); + assertEq(L2StandardERC20(l2TokenAddress).totalSupply(), 100); + assertEq(L2StandardERC20(l2TokenAddress).name(), TOKEN_DEFAULT_NAME); + assertEq(L2StandardERC20(l2TokenAddress).symbol(), TOKEN_DEFAULT_SYMBOL); + assertEq(L2StandardERC20(l2TokenAddress).decimals(), TOKEN_DEFAULT_DECIMALS); + } + + function test_governanceShouldBeAbleToReinitializeToken() public { + address l2TokenAddress = initializeTokenByDeposit(); + + L2StandardERC20.ERC20Getters memory getters = L2StandardERC20.ERC20Getters({ + ignoreName: false, + ignoreSymbol: false, + ignoreDecimals: false + }); + + vm.prank(ownerWallet); + L2StandardERC20(l2TokenAddress).reinitializeToken( + getters, + "TestTokenNewName", + "TTN", + 2 + ); + assertEq(L2StandardERC20(l2TokenAddress).name(), "TestTokenNewName"); + assertEq(L2StandardERC20(l2TokenAddress).symbol(), "TTN"); + // The decimals should stay the same + assertEq(L2StandardERC20(l2TokenAddress).decimals(), 18); + } + + function test_governanceShoudNotBeAbleToSkipInitializerVersions() public { + address l2TokenAddress = initializeTokenByDeposit(); + + L2StandardERC20.ERC20Getters memory getters = L2StandardERC20.ERC20Getters({ + ignoreName: false, + ignoreSymbol: false, + ignoreDecimals: false + }); + + vm.expectRevert(); + vm.prank(ownerWallet); + L2StandardERC20(l2TokenAddress).reinitializeToken( + getters, + "TestTokenNewName", + "TTN", + 20 + ); } } From 0d7ee23e51bd340f6fd133082739f335dbcc6edc Mon Sep 17 00:00:00 2001 From: kelemeno Date: Thu, 22 Aug 2024 21:59:01 +0100 Subject: [PATCH 29/51] aggregated root return value --- l1-contracts/contracts/bridgehub/MessageRoot.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/MessageRoot.sol b/l1-contracts/contracts/bridgehub/MessageRoot.sol index 37b60b1c6..d8290fd91 100644 --- a/l1-contracts/contracts/bridgehub/MessageRoot.sol +++ b/l1-contracts/contracts/bridgehub/MessageRoot.sol @@ -121,8 +121,8 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { /// @dev Gets the aggregated root of all chains. function getAggregatedRoot() external view returns (bytes32) { - if (sharedTree._nodes.length == 0) { - return bytes32(0); + if (chainCount == 0) { + return SHARED_ROOT_TREE_EMPTY_HASH; } return sharedTree.root(); } From d2f83db9562ba300d120bcbefc8d0e940da44cfd Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 22 Aug 2024 23:05:28 +0200 Subject: [PATCH 30/51] cover most of weth tsts --- .../test/foundry/unit/weth/WETH.t.sol | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 l2-contracts/test/foundry/unit/weth/WETH.t.sol diff --git a/l2-contracts/test/foundry/unit/weth/WETH.t.sol b/l2-contracts/test/foundry/unit/weth/WETH.t.sol new file mode 100644 index 000000000..3029ed8ee --- /dev/null +++ b/l2-contracts/test/foundry/unit/weth/WETH.t.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.20; + +import {Vm} from "forge-std/Vm.sol"; + +import {Script, console2 as console} from "forge-std/Script.sol"; + +import {Test} from "forge-std/Test.sol"; + +import { L2WrappedBaseToken } from "contracts/bridge/L2WrappedBaseToken.sol"; +import { TransparentUpgradeableProxy } from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; + +contract WethTest is Test { + L2WrappedBaseToken internal weth; + + // The owner of the proxy + address ownerWallet = address(2); + + function setUp() public { + ownerWallet = makeAddr("owner"); + L2WrappedBaseToken impl = new L2WrappedBaseToken(); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(address(impl), ownerWallet, ""); + + weth = L2WrappedBaseToken(payable(proxy)); + } + + function test_shouldDepositWethByCallingDeposit() public { + uint256 amount = 100; + weth.deposit{value: amount}(); + assertEq(weth.balanceOf(address(this)), amount); + } + + function test_shouldDepositWethBySendingEth() public { + uint256 amount = 100; + address(weth).call{value: amount}(""); + assertEq(weth.balanceOf(address(this)), amount); + } + + function test_revertWhenDepositingWithRandomCalldata() public { + (bool success, ) = address(weth).call{value: 100}(hex"00000000"); + assertEq(success, false); + } + + function test_shouldWithdrawWethToL2Eth() public { + address sender = makeAddr("sender"); + uint256 amount = 100; + + vm.deal(sender, amount); + + + vm.prank(sender); + weth.deposit{value: amount}(); + + vm.prank(sender); + weth.withdraw(amount); + + assertEq(weth.balanceOf(sender), 0); + assertEq(address(sender).balance, amount); + } + + function test_shouldDepositWethToAnotherAccount() public { + address sender = makeAddr("sender"); + address receiver = makeAddr("receiver"); + + uint256 amount = 100; + + vm.deal(sender, amount); + + vm.prank(sender); + weth.depositTo{value: amount}(receiver); + + assertEq(weth.balanceOf(receiver), amount); + assertEq(weth.balanceOf(sender), 0); + } + + function test_shouldWithdrawWethToAnotherAccount() public { + address sender = makeAddr("sender"); + address receiver = makeAddr("receiver"); + + uint256 amount = 100; + + vm.deal(sender, amount); + + vm.prank(sender); + weth.deposit{value: amount}(); + + vm.prank(sender); + weth.withdrawTo(receiver, amount); + + assertEq(receiver.balance, amount); + assertEq(sender.balance, 0); + } + + function test_revertWhenWithdrawingMoreThanBalance() public { + vm.expectRevert(); + weth.withdraw(1); + } + + // function test_revertWhenCallingBridgeMint() public { + // weth.bridge + // } + + // function test_revertWhenCallingBridgeBurn() public { + + // } +} + From ebfb6d297a60880a7869f1afd29a22ab52b7d5d1 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 22 Aug 2024 23:12:35 +0200 Subject: [PATCH 31/51] port the rest of the tests --- .../test/foundry/unit/weth/WETH.t.sol | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/l2-contracts/test/foundry/unit/weth/WETH.t.sol b/l2-contracts/test/foundry/unit/weth/WETH.t.sol index 3029ed8ee..26e66e890 100644 --- a/l2-contracts/test/foundry/unit/weth/WETH.t.sol +++ b/l2-contracts/test/foundry/unit/weth/WETH.t.sol @@ -11,12 +11,17 @@ import {Test} from "forge-std/Test.sol"; import { L2WrappedBaseToken } from "contracts/bridge/L2WrappedBaseToken.sol"; import { TransparentUpgradeableProxy } from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {Unauthorized, UnimplementedMessage, BRIDGE_MINT_NOT_IMPLEMENTED} from "contracts/errors/L2ContractErrors.sol"; + contract WethTest is Test { L2WrappedBaseToken internal weth; // The owner of the proxy address ownerWallet = address(2); + address l2BridgeAddress = address(3); + address l1Address = address(4); + function setUp() public { ownerWallet = makeAddr("owner"); L2WrappedBaseToken impl = new L2WrappedBaseToken(); @@ -24,6 +29,8 @@ contract WethTest is Test { TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(address(impl), ownerWallet, ""); weth = L2WrappedBaseToken(payable(proxy)); + + weth.initializeV2("Wrapped Ether", "WETH", l2BridgeAddress, l1Address); } function test_shouldDepositWethByCallingDeposit() public { @@ -98,12 +105,20 @@ contract WethTest is Test { weth.withdraw(1); } - // function test_revertWhenCallingBridgeMint() public { - // weth.bridge - // } + function test_revertWhenCallingBridgeMint() public { + vm.expectRevert(abi.encodeWithSelector(UnimplementedMessage.selector, BRIDGE_MINT_NOT_IMPLEMENTED)); + vm.prank(l2BridgeAddress); + weth.bridgeMint(address(1), 1); + } - // function test_revertWhenCallingBridgeBurn() public { + function test_revertWhenCallingBridgeMintDirectly() public { + vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, address(this))); + weth.bridgeMint(address(1), 1); + } - // } + function test_revertWhenCallingBridgeBurnDirectly() public { + vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, address(this))); + weth.bridgeBurn(address(1), 1); + } } From c90c9f65af905b320c45b0028599d4445c3edd89 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Fri, 23 Aug 2024 10:35:30 +0200 Subject: [PATCH 32/51] remove old tests --- l2-contracts/test/erc20.test.ts | 179 ------------------ .../foundry/unit/erc20/L1ERC20Bridge.t.sol | 120 +----------- .../test/foundry/unit/utils/Utils.sol | 145 ++++++++++++++ l2-contracts/test/test-utils.ts | 57 ------ l2-contracts/test/weth.test.ts | 132 ------------- 5 files changed, 152 insertions(+), 481 deletions(-) delete mode 100644 l2-contracts/test/erc20.test.ts create mode 100644 l2-contracts/test/foundry/unit/utils/Utils.sol delete mode 100644 l2-contracts/test/test-utils.ts delete mode 100644 l2-contracts/test/weth.test.ts diff --git a/l2-contracts/test/erc20.test.ts b/l2-contracts/test/erc20.test.ts deleted file mode 100644 index 7ded7e8f1..000000000 --- a/l2-contracts/test/erc20.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; -import { expect } from "chai"; -import { ethers } from "ethers"; -import * as hre from "hardhat"; -import { Provider, Wallet } from "zksync-ethers"; -import { hashBytecode } from "zksync-ethers/build/utils"; -import { unapplyL1ToL2Alias, setCode } from "./test-utils"; -import type { L2AssetRouter, L2NativeTokenVault, L2StandardERC20 } from "../typechain"; -import { L2AssetRouterFactory, L2NativeTokenVaultFactory, L2StandardERC20Factory } from "../typechain"; - -const richAccount = [ - { - address: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", - privateKey: "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110", - }, - { - address: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", - privateKey: "0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3", - }, - { - address: "0x0D43eB5B8a47bA8900d84AA36656c92024e9772e", - privateKey: "0xd293c684d884d56f8d6abd64fc76757d3664904e309a0645baf8522ab6366d9e", - }, - { - address: "0xA13c10C0D5bd6f79041B9835c63f91de35A15883", - privateKey: "0x850683b40d4a740aa6e745f889a6fdc8327be76e122f5aba645a5b02d0248db8", - }, -]; - -describe("ERC20Bridge", function () { - const provider = new Provider(hre.config.networks.localhost.url); - const deployerWallet = new Wallet(richAccount[0].privateKey, provider); - const governorWallet = new Wallet(richAccount[1].privateKey, provider); - const proxyAdminWallet = new Wallet(richAccount[3].privateKey, provider); - - // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. - // It is a bit easier to use EOA and it is sufficient for the tests. - const l1BridgeWallet = new Wallet(richAccount[2].privateKey, provider); - - // We won't actually deploy an L1 token in these tests, but we need some address for it. - const L1_TOKEN_ADDRESS = "0x1111000000000000000000000000000000001111"; - const L2_ASSET_ROUTER_ADDRESS = "0x0000000000000000000000000000000000010003"; - const L2_NATIVE_TOKEN_VAULT_ADDRESS = "0x0000000000000000000000000000000000010004"; - - const testChainId = 9; - - let erc20Bridge: L2AssetRouter; - let erc20NativeTokenVault: L2NativeTokenVault; - let erc20Token: L2StandardERC20; - - before("Deploy token and bridge", async function () { - const deployer = new Deployer(hre, deployerWallet); - - // While we formally don't need to deploy the token and the beacon proxy, it is a neat way to have the bytecode published - const l2TokenImplAddress = await deployer.deploy(await deployer.loadArtifact("L2StandardERC20")); - const l2Erc20TokenBeacon = await deployer.deploy( - await deployer.loadArtifact("@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol:UpgradeableBeacon"), - [l2TokenImplAddress.address] - ); - await deployer.deploy( - await deployer.loadArtifact("@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol:BeaconProxy"), - [l2Erc20TokenBeacon.address, "0x"] - ); - const beaconProxyBytecodeHash = hashBytecode( - (await deployer.loadArtifact("@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol:BeaconProxy")).bytecode - ); - let constructorArgs = ethers.utils.defaultAbiCoder.encode( - ["uint256", "uint256", "address", "address"], - /// note in real deployment we have to transfer ownership of standard deployer here - [testChainId, 1, unapplyL1ToL2Alias(l1BridgeWallet.address), ethers.constants.AddressZero] - ); - await setCode( - deployerWallet, - L2_ASSET_ROUTER_ADDRESS, - (await deployer.loadArtifact("L2AssetRouter")).bytecode, - true, - constructorArgs - ); - - erc20Bridge = L2AssetRouterFactory.connect(L2_ASSET_ROUTER_ADDRESS, deployerWallet); - const l2NativeTokenVaultArtifact = await deployer.loadArtifact("L2NativeTokenVault"); - constructorArgs = ethers.utils.defaultAbiCoder.encode( - ["uint256", "address", "bytes32", "address", "address", "bool"], - /// note in real deployment we have to transfer ownership of standard deployer here - [ - 9, - governorWallet.address, - beaconProxyBytecodeHash, - ethers.constants.AddressZero, - ethers.constants.AddressZero, - false, - ] - ); - await setCode( - deployerWallet, - L2_NATIVE_TOKEN_VAULT_ADDRESS, - l2NativeTokenVaultArtifact.bytecode, - true, - constructorArgs - ); - - erc20NativeTokenVault = L2NativeTokenVaultFactory.connect(L2_NATIVE_TOKEN_VAULT_ADDRESS, l1BridgeWallet); - }); - - it("Should finalize deposit ERC20 deposit", async function () { - const erc20BridgeWithL1BridgeWallet = L2AssetRouterFactory.connect(erc20Bridge.address, proxyAdminWallet); - const l1Depositor = ethers.Wallet.createRandom(); - const l2Receiver = ethers.Wallet.createRandom(); - const l1Bridge = await hre.ethers.getImpersonatedSigner(l1BridgeWallet.address); - const tx = await ( - await erc20BridgeWithL1BridgeWallet.connect(l1Bridge)["finalizeDeposit(address,address,address,uint256,bytes)"]( - // Depositor and l2Receiver can be any here - l1Depositor.address, - l2Receiver.address, - L1_TOKEN_ADDRESS, - 100, - encodedTokenData("TestToken", "TT", 18) - ) - ).wait(); - const l2TokenInfo = tx.events.find((event) => event.event === "FinalizeDepositSharedBridge").args.assetId; - const l2TokenAddress = await erc20NativeTokenVault.tokenAddress(l2TokenInfo); - // Checking the correctness of the balance: - erc20Token = L2StandardERC20Factory.connect(l2TokenAddress, deployerWallet); - expect(await erc20Token.balanceOf(l2Receiver.address)).to.equal(100); - expect(await erc20Token.totalSupply()).to.equal(100); - expect(await erc20Token.name()).to.equal("TestToken"); - expect(await erc20Token.symbol()).to.equal("TT"); - expect(await erc20Token.decimals()).to.equal(18); - }); - - it("Governance should be able to reinitialize the token", async () => { - const erc20TokenWithGovernor = L2StandardERC20Factory.connect(erc20Token.address, governorWallet); - - await ( - await erc20TokenWithGovernor.reinitializeToken( - { - ignoreName: false, - ignoreSymbol: false, - ignoreDecimals: false, - }, - "TestTokenNewName", - "TTN", - 2 - ) - ).wait(); - - expect(await erc20Token.name()).to.equal("TestTokenNewName"); - expect(await erc20Token.symbol()).to.equal("TTN"); - // The decimals should stay the same - expect(await erc20Token.decimals()).to.equal(18); - }); - - it("Governance should not be able to skip initializer versions", async () => { - const erc20TokenWithGovernor = L2StandardERC20Factory.connect(erc20Token.address, governorWallet); - - await expect( - erc20TokenWithGovernor.reinitializeToken( - { - ignoreName: false, - ignoreSymbol: false, - ignoreDecimals: false, - }, - "TestTokenNewName", - "TTN", - 20, - { gasLimit: 10000000 } - ) - ).to.be.reverted; - }); -}); - -function encodedTokenData(name: string, symbol: string, decimals: number) { - const abiCoder = ethers.utils.defaultAbiCoder; - const encodedName = abiCoder.encode(["string"], [name]); - const encodedSymbol = abiCoder.encode(["string"], [symbol]); - const encodedDecimals = abiCoder.encode(["uint8"], [decimals]); - - return abiCoder.encode(["bytes", "bytes", "bytes"], [encodedName, encodedSymbol, encodedDecimals]); -} diff --git a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol b/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol index 1963687da..a7b528e6f 100644 --- a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol +++ b/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol @@ -21,112 +21,7 @@ import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; -library Utils { - address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); - Vm internal constant vm = Vm(VM_ADDRESS); - - address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); - - string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; - string internal constant L2_NATIVE_TOKEN_VAULT_PATH = "./zkout/L2NativeTokenVault.sol/L2NativeTokenVault.json"; - - - function readEraBytecode(string memory _path) internal returns (bytes memory bytecode) { - string memory artifact = vm.readFile(_path); - bytecode = vm.parseJsonBytes(artifact, ".bytecode.object"); - } - - /** - * @dev Returns the bytecode of a given system contract. - */ - function readSystemContractsBytecode(string memory filename) internal view returns (bytes memory) { - string memory file = vm.readFile( - // solhint-disable-next-line func-named-parameters - string.concat( - "../system-contracts/artifacts-zk/contracts-preprocessed/", - filename, - ".sol/", - filename, - ".json" - ) - ); - bytes memory bytecode = vm.parseJson(file, "$.bytecode"); - return bytecode; - } - - function initSystemContext() internal { - bytes memory contractDeployerBytecode = readSystemContractsBytecode("ContractDeployer"); - vm.etch(DEPLOYER_SYSTEM_CONTRACT, contractDeployerBytecode); - } - - function forceDeployAssetRouter( - uint256 _l1ChainId, uint256 _eraChainId, address _l1AssetRouter, address _legacySharedBridge - ) internal { - // to ensure that the bytecode is known - { - new L2AssetRouter(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge); - } - - bytes memory bytecode = readEraBytecode(L2_ASSET_ROUTER_PATH); - - bytes32 bytecodehash = L2ContractHelper.hashL2BytecodeMemory(bytecode); - - IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); - deployments[0] = IContractDeployer.ForceDeployment({ - bytecodeHash: bytecodehash, - newAddress: address(L2_ASSET_ROUTER), - callConstructor: true, - value: 0, - input: abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge) - }); - - vm.prank(L2_FORCE_DEPLOYER_ADDR); - IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( - deployments - ); - } - - function forceDeployNativeTokenVault( - uint256 _l1ChainId, - address _aliasedOwner, - bytes32 _l2TokenProxyBytecodeHash, - address _legacySharedBridge, - address _l2TokenBeacon, - bool _contractsDeployedAlready - ) internal { - // to ensure that the bytecode is known - { - new L2NativeTokenVault(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready); - } - - bytes memory bytecode = readEraBytecode(L2_NATIVE_TOKEN_VAULT_PATH); - - bytes32 bytecodehash = L2ContractHelper.hashL2BytecodeMemory(bytecode); - - IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); - deployments[0] = IContractDeployer.ForceDeployment({ - bytecodeHash: bytecodehash, - newAddress: address(L2_NATIVE_TOKEN_VAULT), - callConstructor: true, - value: 0, - input: abi.encode(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready) - }); - - vm.prank(L2_FORCE_DEPLOYER_ADDR); - IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( - deployments - ); - } - - function encodeTokenData(string memory name, string memory symbol, uint8 decimals) internal pure returns (bytes memory) { - bytes memory encodedName = abi.encode(name); - bytes memory encodedSymbol = abi.encode(symbol); - bytes memory encodedDecimals = abi.encode(decimals); - - return abi.encode(encodedName, encodedSymbol, encodedDecimals); - } - -} +import {Utils } from "../utils/Utils.sol"; contract L1Erc20BridgeTest is Test { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. @@ -141,9 +36,8 @@ contract L1Erc20BridgeTest is Test { UpgradeableBeacon beacon; - - uint256 l1ChainId = 9; - uint256 eraChainId = 270; + uint256 constant L1_CHAIN_ID = 9; + uint256 ERA_CHAIN_ID = 270; // We won't actually deploy an L1 token in these tests, but we need some address for it. address L1_TOKEN_ADDRESS = 0x1111100000000000000000000000000000011111; @@ -169,15 +63,15 @@ contract L1Erc20BridgeTest is Test { beaconProxyBytecodeHash := extcodehash(proxy) } - Utils.initSystemContext(); + Utils.initSystemContracts(); Utils.forceDeployAssetRouter( - l1ChainId, - eraChainId, + L1_CHAIN_ID, + ERA_CHAIN_ID, l1BridgeWallet, address(0) ); Utils.forceDeployNativeTokenVault( - l1ChainId, + L1_CHAIN_ID, ownerWallet, beaconProxyBytecodeHash, address(0), diff --git a/l2-contracts/test/foundry/unit/utils/Utils.sol b/l2-contracts/test/foundry/unit/utils/Utils.sol new file mode 100644 index 000000000..faa4b999c --- /dev/null +++ b/l2-contracts/test/foundry/unit/utils/Utils.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.20; + +import {Vm} from "forge-std/Vm.sol"; + +import { IContractDeployer, DEPLOYER_SYSTEM_CONTRACT, L2ContractHelper, L2_ASSET_ROUTER, L2_NATIVE_TOKEN_VAULT } from "contracts/L2ContractHelper.sol"; + +import {L2AssetRouter} from "contracts/bridge/L2AssetRouter.sol"; +import {L2NativeTokenVault} from "contracts/bridge/L2NativeTokenVault.sol"; + +library Utils { + address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); + Vm internal constant vm = Vm(VM_ADDRESS); + + address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); + + string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; + string internal constant L2_NATIVE_TOKEN_VAULT_PATH = "./zkout/L2NativeTokenVault.sol/L2NativeTokenVault.json"; + + /// @notice Returns the bytecode of a given era contract from a `zkout` folder. + function readEraBytecode(string memory _filename) internal returns (bytes memory bytecode) { + string memory artifact = vm.readFile( + // solhint-disable-next-line func-named-parameters + string.concat( + "./zkout/", + _filename, + ".sol/", + _filename, + ".json" + ) + ); + + bytecode = vm.parseJsonBytes(artifact, ".bytecode.object"); + } + + + /// @notice Returns the bytecode of a given system contract. + function readSystemContractsBytecode(string memory _filename) internal view returns (bytes memory) { + string memory file = vm.readFile( + // solhint-disable-next-line func-named-parameters + string.concat( + "../system-contracts/artifacts-zk/contracts-preprocessed/", + _filename, + ".sol/", + _filename, + ".json" + ) + ); + bytes memory bytecode = vm.parseJson(file, "$.bytecode"); + return bytecode; + } + + /** + * @dev Initializes the system contracts. + * @dev It is a hack needed to make the tests be able to call system contracts directly. + */ + function initSystemContracts() internal { + bytes memory contractDeployerBytecode = readSystemContractsBytecode("ContractDeployer"); + vm.etch(DEPLOYER_SYSTEM_CONTRACT, contractDeployerBytecode); + } + + /// @notice Deploys the L2AssetRouter contract. + /// @param _l1ChainId The chain ID of the L1 chain. + /// @param _eraChainId The chain ID of the era chain. + /// @param _l1AssetRouter The address of the L1 asset router. + /// @param _legacySharedBridge The address of the legacy shared bridge. + function forceDeployAssetRouter( + uint256 _l1ChainId, uint256 _eraChainId, address _l1AssetRouter, address _legacySharedBridge + ) internal { + // to ensure that the bytecode is known + { + new L2AssetRouter(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge); + } + + bytes memory bytecode = readEraBytecode("L2AssetRouter"); + + bytes32 bytecodehash = L2ContractHelper.hashL2BytecodeMemory(bytecode); + + IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); + deployments[0] = IContractDeployer.ForceDeployment({ + bytecodeHash: bytecodehash, + newAddress: address(L2_ASSET_ROUTER), + callConstructor: true, + value: 0, + input: abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge) + }); + + vm.prank(L2_FORCE_DEPLOYER_ADDR); + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( + deployments + ); + } + + /// @notice Deploys the L2NativeTokenVault contract. + /// @param _l1ChainId The chain ID of the L1 chain. + /// @param _aliasedOwner The address of the aliased owner. + /// @param _l2TokenProxyBytecodeHash The hash of the L2 token proxy bytecode. + /// @param _legacySharedBridge The address of the legacy shared bridge. + /// @param _l2TokenBeacon The address of the L2 token beacon. + /// @param _contractsDeployedAlready Whether the contracts are deployed already. + function forceDeployNativeTokenVault( + uint256 _l1ChainId, + address _aliasedOwner, + bytes32 _l2TokenProxyBytecodeHash, + address _legacySharedBridge, + address _l2TokenBeacon, + bool _contractsDeployedAlready + ) internal { + // to ensure that the bytecode is known + { + new L2NativeTokenVault(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready); + } + + bytes memory bytecode = readEraBytecode("L2NativeTokenVault"); + + bytes32 bytecodehash = L2ContractHelper.hashL2BytecodeMemory(bytecode); + + IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); + deployments[0] = IContractDeployer.ForceDeployment({ + bytecodeHash: bytecodehash, + newAddress: address(L2_NATIVE_TOKEN_VAULT), + callConstructor: true, + value: 0, + input: abi.encode(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready) + }); + + vm.prank(L2_FORCE_DEPLOYER_ADDR); + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( + deployments + ); + } + + /// @notice Encodes the token data. + /// @param name The name of the token. + /// @param symbol The symbol of the token. + /// @param decimals The decimals of the token. + function encodeTokenData(string memory name, string memory symbol, uint8 decimals) internal pure returns (bytes memory) { + bytes memory encodedName = abi.encode(name); + bytes memory encodedSymbol = abi.encode(symbol); + bytes memory encodedDecimals = abi.encode(decimals); + + return abi.encode(encodedName, encodedSymbol, encodedDecimals); + } +} diff --git a/l2-contracts/test/test-utils.ts b/l2-contracts/test/test-utils.ts deleted file mode 100644 index 241a32a49..000000000 --- a/l2-contracts/test/test-utils.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ethers } from "ethers"; -import * as hre from "hardhat"; -import * as zksync from "zksync-ethers"; -import type { BytesLike } from "ethers"; -import { ContractDeployerFactory } from "../typechain/ContractDeployerFactory"; - -const L1_TO_L2_ALIAS_OFFSET = "0x1111000000000000000000000000000000001111"; -const ADDRESS_MODULO = ethers.BigNumber.from(2).pow(160); - -export function unapplyL1ToL2Alias(address: string): string { - // We still add ADDRESS_MODULO to avoid negative numbers - return ethers.utils.hexlify( - ethers.BigNumber.from(address).sub(L1_TO_L2_ALIAS_OFFSET).add(ADDRESS_MODULO).mod(ADDRESS_MODULO) - ); -} - -// Force deploy bytecode on the address -export async function setCode( - deployerWallet: zksync.Wallet, - address: string, - bytecode: BytesLike, - callConstructor: boolean = false, - constructorArgs: BytesLike -) { - const REAL_DEPLOYER_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000008006"; - // TODO: think about factoryDeps with eth_sendTransaction - try { - // publish bytecode in a separate tx - await publishBytecode(bytecode, deployerWallet); - } catch { - // ignore error - } - - const deployerAccount = await hre.ethers.getImpersonatedSigner(REAL_DEPLOYER_SYSTEM_CONTRACT_ADDRESS); - const deployerContract = ContractDeployerFactory.connect(REAL_DEPLOYER_SYSTEM_CONTRACT_ADDRESS, deployerAccount); - - const deployment = { - bytecodeHash: zksync.utils.hashBytecode(bytecode), - newAddress: address, - callConstructor, - value: 0, - input: constructorArgs, - }; - await deployerContract.forceDeployOnAddress(deployment, ethers.constants.AddressZero); -} - -export async function publishBytecode(bytecode: BytesLike, deployerWallet: zksync.Wallet) { - await deployerWallet.sendTransaction({ - type: 113, - to: ethers.constants.AddressZero, - data: "0x", - customData: { - factoryDeps: [ethers.utils.hexlify(bytecode)], - gasPerPubdata: 50000, - }, - }); -} diff --git a/l2-contracts/test/weth.test.ts b/l2-contracts/test/weth.test.ts deleted file mode 100644 index b9f5c9a86..000000000 --- a/l2-contracts/test/weth.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; -import { expect } from "chai"; -import { ethers } from "ethers"; -import * as hre from "hardhat"; -import { Provider, Wallet } from "zksync-ethers"; -import type { L2WrappedBaseToken } from "../typechain/L2WrappedBaseToken"; -import type { L2AssetRouter } from "../typechain/L2AssetRouter"; -import { L2AssetRouterFactory } from "../typechain/L2AssetRouterFactory"; -import { L2WrappedBaseTokenFactory } from "../typechain/L2WrappedBaseTokenFactory"; - -const richAccount = { - address: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", - privateKey: "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110", -}; - -const eth18 = ethers.utils.parseEther("18"); -const testChainId = 9; - -describe("WETH token & WETH bridge", function () { - const provider = new Provider(hre.config.networks.localhost.url); - const wallet = new Wallet(richAccount.privateKey, provider); - let wethToken: L2WrappedBaseToken; - let wethBridge: L2AssetRouter; - - before("Deploy token and bridge", async function () { - const deployer = new Deployer(hre, wallet); - const wethTokenImpl = await deployer.deploy(await deployer.loadArtifact("L2WrappedBaseToken")); - const wethBridgeImpl = await deployer.deploy(await deployer.loadArtifact("L2AssetRouter"), [ - testChainId, - 1, - richAccount.address, - ethers.constants.AddressZero, - ]); - const randomAddress = ethers.utils.hexlify(ethers.utils.randomBytes(20)); - - const wethTokenProxy = await deployer.deploy( - await deployer.loadArtifact( - "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy" - ), - [wethTokenImpl.address, randomAddress, "0x"] - ); - const wethBridgeProxy = await deployer.deploy( - await deployer.loadArtifact( - "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy" - ), - [wethBridgeImpl.address, randomAddress, "0x"] - ); - - wethToken = L2WrappedBaseTokenFactory.connect(wethTokenProxy.address, wallet); - wethBridge = L2AssetRouterFactory.connect(wethBridgeProxy.address, wallet); - - // await wethToken.initialize(); - await wethToken.initializeV2("Wrapped Ether", "WETH", wethBridge.address, randomAddress); - - // await wethBridge.initialize(randomAddress, randomAddress, wethToken.address); - }); - - it("Should deposit WETH by calling deposit()", async function () { - await wethToken.deposit({ value: eth18 }).then((tx) => tx.wait()); - expect(await wethToken.balanceOf(wallet.address)).to.equal(eth18); - }); - - it("Should deposit WETH by sending", async function () { - await wallet - .sendTransaction({ - to: wethToken.address, - value: eth18, - }) - .then((tx) => tx.wait()); - expect(await wethToken.balanceOf(wallet.address)).to.equal(eth18.mul(2)); - }); - - it("Should fail depositing with random calldata", async function () { - await expect( - wallet.sendTransaction({ - data: ethers.utils.randomBytes(36), - to: wethToken.address, - value: eth18, - gasLimit: 100_000, - }) - ).to.be.reverted; - }); - - it("Should withdraw WETH to L2 ETH", async function () { - await wethToken.withdraw(eth18).then((tx) => tx.wait()); - expect(await wethToken.balanceOf(wallet.address)).to.equal(eth18); - }); - - // bridging not supported - // it("Should withdraw WETH to L1 ETH", async function () { - // await expect(wethBridge.withdraw(wallet.address, wethToken.address, eth18.div(2))) - // .to.emit(wethBridge, "WithdrawalInitiated") - // .and.to.emit(wethToken, "BridgeBurn"); - // expect(await wethToken.balanceOf(wallet.address)).to.equal(eth18.div(2)); - // }); - - it("Should deposit WETH to another account", async function () { - const anotherWallet = new Wallet(ethers.utils.randomBytes(32), provider); - await wethToken.depositTo(anotherWallet.address, { value: eth18 }).then((tx) => tx.wait()); - expect(await wethToken.balanceOf(anotherWallet.address)).to.equal(eth18); - }); - - it("Should withdraw WETH to another account", async function () { - const anotherWallet = new Wallet(ethers.utils.randomBytes(32), provider); - await wethToken.withdrawTo(anotherWallet.address, eth18.div(2)).then((tx) => tx.wait()); - expect(await anotherWallet.getBalance()).to.equal(eth18.div(2)); - expect(await wethToken.balanceOf(wallet.address)).to.equal(eth18.div(2)); - }); - - it("Should fail withdrawing with insufficient balance", async function () { - await expect(wethToken.withdraw(1, { gasLimit: 100_000 })).to.be.reverted; - }); - - // bridging not supported - // it("Should fail depositing directly to WETH bridge", async function () { - // await expect( - // wallet.sendTransaction({ - // to: wethBridge.address, - // value: eth18, - // gasLimit: 100_000, - // }) - // ).to.be.reverted; - // }); - - it("Should fail calling bridgeMint()", async function () { - await expect(await wethToken.bridgeMint(wallet.address, eth18, { gasLimit: 1_000_000 })).to.be.reverted; - }); - - it("Should fail calling bridgeBurn() directly", async function () { - await expect(wethToken.bridgeBurn(wallet.address, eth18, { gasLimit: 100_000 })).to.be.reverted; - }); -}); From a557c0146bbf631e3ea344ede697020fbe73722a Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:01:24 +0200 Subject: [PATCH 33/51] upd workflow --- .github/workflows/l2-contracts-ci.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/l2-contracts-ci.yaml b/.github/workflows/l2-contracts-ci.yaml index 4b8fbcb12..36e9ad4de 100644 --- a/.github/workflows/l2-contracts-ci.yaml +++ b/.github/workflows/l2-contracts-ci.yaml @@ -89,11 +89,5 @@ jobs: l2-contracts/cache-zk l2-contracts/typechain - - name: Run Era test node - uses: dutterbutter/era-test-node-action@v0.1.3 - - - name: Copy typechain from System Contracts - run: yarn sc build && yarn sc copy:typechain - - name: Run tests - run: yarn l2 test + run: yarn l2 test:foundry From 487dd71c412240d2ac931c65c8bc6fdde168253e Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:01:57 +0200 Subject: [PATCH 34/51] forge install: v2-testnet-contracts 0.6.1 --- .gitmodules | 3 +++ lib/v2-testnet-contracts | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/v2-testnet-contracts diff --git a/.gitmodules b/.gitmodules index 3451bd884..127426c7a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/v2-testnet-contracts"] + path = lib/v2-testnet-contracts + url = https://github.com/matter-labs/v2-testnet-contracts diff --git a/lib/v2-testnet-contracts b/lib/v2-testnet-contracts new file mode 160000 index 000000000..b8449bf9c --- /dev/null +++ b/lib/v2-testnet-contracts @@ -0,0 +1 @@ +Subproject commit b8449bf9c819098cc8bfee0549ff5094456be51d From 4bac7cbe0fe3e66bf4b53e68011202fab089326d Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:05:24 +0200 Subject: [PATCH 35/51] wip --- .gitmodules | 3 --- l2-contracts/foundry.toml | 2 +- lib/v2-testnet-contracts | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) delete mode 160000 lib/v2-testnet-contracts diff --git a/.gitmodules b/.gitmodules index 127426c7a..3451bd884 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,6 +12,3 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "lib/v2-testnet-contracts"] - path = lib/v2-testnet-contracts - url = https://github.com/matter-labs/v2-testnet-contracts diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index f7e77091f..53f518cb1 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -1,7 +1,7 @@ [profile.default] src = "contracts" out = "out" -libs = ["node_modules", "lib"] +libs = ["lib"] test = "test/foundry" solc_version = "0.8.20" cache_path = "cache-forge" diff --git a/lib/v2-testnet-contracts b/lib/v2-testnet-contracts deleted file mode 160000 index b8449bf9c..000000000 --- a/lib/v2-testnet-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b8449bf9c819098cc8bfee0549ff5094456be51d From 1a2390c03d68e6358c6429226438d4039f4ad2d7 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:05:29 +0200 Subject: [PATCH 36/51] forge install: @matterlabs/zksync-contracts 0.6.1 --- .gitmodules | 3 +++ lib/@matterlabs/zksync-contracts | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/@matterlabs/zksync-contracts diff --git a/.gitmodules b/.gitmodules index 3451bd884..f94071e53 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/@matterlabs/zksync-contracts"] + path = lib/@matterlabs/zksync-contracts + url = https://github.com/matter-labs/v2-testnet-contracts diff --git a/lib/@matterlabs/zksync-contracts b/lib/@matterlabs/zksync-contracts new file mode 160000 index 000000000..b8449bf9c --- /dev/null +++ b/lib/@matterlabs/zksync-contracts @@ -0,0 +1 @@ +Subproject commit b8449bf9c819098cc8bfee0549ff5094456be51d From 2d9ad08b1047bc243b135d01f83ef5665b1dcfff Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:11:48 +0200 Subject: [PATCH 37/51] using foundry deps --- l2-contracts/foundry.toml | 5 ++++- l2-contracts/lib/@matterlabs | 1 + l2-contracts/lib/openzeppelin-contracts | 1 - l2-contracts/lib/openzeppelin-contracts-upgradeable | 1 - l2-contracts/lib/openzeppelin-contracts-upgradeable-v4 | 1 + l2-contracts/lib/openzeppelin-contracts-v4 | 1 + 6 files changed, 7 insertions(+), 3 deletions(-) create mode 120000 l2-contracts/lib/@matterlabs delete mode 120000 l2-contracts/lib/openzeppelin-contracts delete mode 120000 l2-contracts/lib/openzeppelin-contracts-upgradeable create mode 120000 l2-contracts/lib/openzeppelin-contracts-upgradeable-v4 create mode 120000 l2-contracts/lib/openzeppelin-contracts-v4 diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index 53f518cb1..687b806cd 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -11,7 +11,10 @@ ignored_error_codes = ["missing-receive-ether", "code-size"] ignored_warnings_from = ["test", "contracts/dev-contracts"] remappings = [ "forge-std/=lib/forge-std/src/", - "foundry-test/=test/foundry/" + "foundry-test/=test/foundry/", + "@openzeppelin/contracts-v4/=./lib/openzeppelin-contracts-v4/contracts/", + "@openzeppelin/contracts-upgradeable-v4/=./lib/openzeppelin-contracts-upgradeable-v4/contracts/", + "@matterlabs/zksync-contracts/=./lib/@matterlabs/zksync-contracts/", ] fs_permissions = [ { access = "read", path = "zkout" }, diff --git a/l2-contracts/lib/@matterlabs b/l2-contracts/lib/@matterlabs new file mode 120000 index 000000000..beffd09fc --- /dev/null +++ b/l2-contracts/lib/@matterlabs @@ -0,0 +1 @@ +../../lib/@matterlabs \ No newline at end of file diff --git a/l2-contracts/lib/openzeppelin-contracts b/l2-contracts/lib/openzeppelin-contracts deleted file mode 120000 index 99aa45507..000000000 --- a/l2-contracts/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -../../lib/openzeppelin-contracts \ No newline at end of file diff --git a/l2-contracts/lib/openzeppelin-contracts-upgradeable b/l2-contracts/lib/openzeppelin-contracts-upgradeable deleted file mode 120000 index f1fc7a76a..000000000 --- a/l2-contracts/lib/openzeppelin-contracts-upgradeable +++ /dev/null @@ -1 +0,0 @@ -../../lib/openzeppelin-contracts-upgradeable \ No newline at end of file diff --git a/l2-contracts/lib/openzeppelin-contracts-upgradeable-v4 b/l2-contracts/lib/openzeppelin-contracts-upgradeable-v4 new file mode 120000 index 000000000..0551b6016 --- /dev/null +++ b/l2-contracts/lib/openzeppelin-contracts-upgradeable-v4 @@ -0,0 +1 @@ +../../lib/openzeppelin-contracts-upgradeable-v4 \ No newline at end of file diff --git a/l2-contracts/lib/openzeppelin-contracts-v4 b/l2-contracts/lib/openzeppelin-contracts-v4 new file mode 120000 index 000000000..693e94537 --- /dev/null +++ b/l2-contracts/lib/openzeppelin-contracts-v4 @@ -0,0 +1 @@ +../../lib/openzeppelin-contracts-v4 \ No newline at end of file From bc54d47bc05233b49ac88a9d52f2b22f27a01784 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:13:31 +0200 Subject: [PATCH 38/51] remove unneeded changes --- l2-contracts/foundry.toml | 1 + package.json | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index 687b806cd..b7b94a587 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -24,3 +24,4 @@ fs_permissions = [ [profile.default.zksync] enable_eravm_extensions = true +zksolc = "1.5.0" diff --git a/package.json b/package.json index 701d86666..b17e3cb21 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,7 @@ "gas-bound-caller" ], "nohoist": [ - "**/@openzeppelin/**", - "**/@matterlabs/**" + "**/@openzeppelin/**" ] }, "devDependencies": { From e4689f0c010b6edd2368a41d29a4a94fe69440dc Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:25:53 +0200 Subject: [PATCH 39/51] wip --- l2-contracts/foundry.toml | 2 +- ...20Bridge.t.sol => L2Erc20BridgeTest.t.sol} | 61 ++++++++----------- .../test/foundry/unit/utils/Utils.sol | 12 +++- .../test/foundry/unit/weth/WETH.t.sol | 10 +-- 4 files changed, 41 insertions(+), 44 deletions(-) rename l2-contracts/test/foundry/unit/erc20/{L1ERC20Bridge.t.sol => L2Erc20BridgeTest.t.sol} (76%) diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index b7b94a587..0dd8c668f 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -24,4 +24,4 @@ fs_permissions = [ [profile.default.zksync] enable_eravm_extensions = true -zksolc = "1.5.0" +zksolc = "1.5.1" diff --git a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol b/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol similarity index 76% rename from l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol rename to l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol index a7b528e6f..7854fc545 100644 --- a/l2-contracts/test/foundry/unit/erc20/L1ERC20Bridge.t.sol +++ b/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol @@ -2,9 +2,6 @@ pragma solidity 0.8.20; -import {Vm} from "forge-std/Vm.sol"; - -import {Script, console2 as console} from "forge-std/Script.sol"; import {Test} from "forge-std/Test.sol"; @@ -14,38 +11,34 @@ import {L2AssetRouter} from "contracts/bridge/L2AssetRouter.sol"; import { UpgradeableBeacon } from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; import { BeaconProxy } from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; -import { IContractDeployer, DEPLOYER_SYSTEM_CONTRACT, L2ContractHelper, L2_ASSET_ROUTER, L2_NATIVE_TOKEN_VAULT } from "contracts/L2ContractHelper.sol"; +import { L2_ASSET_ROUTER, L2_NATIVE_TOKEN_VAULT } from "contracts/L2ContractHelper.sol"; -import {L2NativeTokenVault} from "contracts/bridge/L2NativeTokenVault.sol"; import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; -import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; - import {Utils } from "../utils/Utils.sol"; -contract L1Erc20BridgeTest is Test { +contract L2Erc20BridgeTest is Test { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. // It is a bit easier to use EOA and it is sufficient for the tests. - address l1BridgeWallet = address(1); - address aliasedL1BridgeWallet; + address internal l1BridgeWallet = address(1); + address internal aliasedL1BridgeWallet; // The owner of the beacon and the native token vault - address ownerWallet = address(2); + address internal ownerWallet = address(2); - L2StandardERC20 standardErc20Impl; + L2StandardERC20 internal standardErc20Impl; UpgradeableBeacon beacon; - uint256 constant L1_CHAIN_ID = 9; - uint256 ERA_CHAIN_ID = 270; + uint256 internal constant L1_CHAIN_ID = 9; + uint256 internal ERA_CHAIN_ID = 270; // We won't actually deploy an L1 token in these tests, but we need some address for it. - address L1_TOKEN_ADDRESS = 0x1111100000000000000000000000000000011111; - - string constant TOKEN_DEFAULT_NAME = "TestnetERC20Token"; - string constant TOKEN_DEFAULT_SYMBOL = "TET"; - uint8 constant TOKEN_DEFAULT_DECIMALS = 18; + address internal L1_TOKEN_ADDRESS = 0x1111100000000000000000000000000000011111; + string internal constant TOKEN_DEFAULT_NAME = "TestnetERC20Token"; + string internal constant TOKEN_DEFAULT_SYMBOL = "TET"; + uint8 internal constant TOKEN_DEFAULT_DECIMALS = 18; function setUp() public { aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); @@ -70,14 +63,14 @@ contract L1Erc20BridgeTest is Test { l1BridgeWallet, address(0) ); - Utils.forceDeployNativeTokenVault( - L1_CHAIN_ID, - ownerWallet, - beaconProxyBytecodeHash, - address(0), - address(beacon), - true - ); + Utils.forceDeployNativeTokenVault({ + _l1ChainId: L1_CHAIN_ID, + _aliasedOwner: ownerWallet, + _l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, + _legacySharedBridge: address(0), + _l2TokenBeacon: address(beacon), + _contractsDeployedAlready: true + }); } function performDeposit( @@ -86,13 +79,13 @@ contract L1Erc20BridgeTest is Test { uint256 amount ) internal { vm.prank(aliasedL1BridgeWallet); - L2AssetRouter(address(L2_ASSET_ROUTER)).finalizeDeposit( - depositor, - receiver, - L1_TOKEN_ADDRESS, - amount, - Utils.encodeTokenData(TOKEN_DEFAULT_NAME, TOKEN_DEFAULT_SYMBOL, TOKEN_DEFAULT_DECIMALS) - ); + L2AssetRouter(address(L2_ASSET_ROUTER)).finalizeDeposit({ + _l1Sender: depositor, + _l2Receiver: receiver, + _l1Token: L1_TOKEN_ADDRESS, + _amount: amount, + _data: Utils.encodeTokenData(TOKEN_DEFAULT_NAME, TOKEN_DEFAULT_SYMBOL, TOKEN_DEFAULT_DECIMALS) + }); } function initializeTokenByDeposit() internal returns (address l2TokenAddress) { diff --git a/l2-contracts/test/foundry/unit/utils/Utils.sol b/l2-contracts/test/foundry/unit/utils/Utils.sol index faa4b999c..51a43e52e 100644 --- a/l2-contracts/test/foundry/unit/utils/Utils.sol +++ b/l2-contracts/test/foundry/unit/utils/Utils.sol @@ -13,7 +13,7 @@ library Utils { address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); Vm internal constant vm = Vm(VM_ADDRESS); - address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); + address internal constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; string internal constant L2_NATIVE_TOKEN_VAULT_PATH = "./zkout/L2NativeTokenVault.sol/L2NativeTokenVault.json"; @@ -109,7 +109,14 @@ library Utils { ) internal { // to ensure that the bytecode is known { - new L2NativeTokenVault(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready); + new L2NativeTokenVault({ + _l1ChainId: _l1ChainId, + _aliasedOwner: _aliasedOwner, + _l2TokenProxyBytecodeHash: _l2TokenProxyBytecodeHash, + _legacySharedBridge: _legacySharedBridge, + _l2TokenBeacon: _l2TokenBeacon, + _contractsDeployedAlready: _contractsDeployedAlready + }); } bytes memory bytecode = readEraBytecode("L2NativeTokenVault"); @@ -122,6 +129,7 @@ library Utils { newAddress: address(L2_NATIVE_TOKEN_VAULT), callConstructor: true, value: 0, + // solhint-disable-next-line func-named-parameters input: abi.encode(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready) }); diff --git a/l2-contracts/test/foundry/unit/weth/WETH.t.sol b/l2-contracts/test/foundry/unit/weth/WETH.t.sol index 26e66e890..a35a6dd0b 100644 --- a/l2-contracts/test/foundry/unit/weth/WETH.t.sol +++ b/l2-contracts/test/foundry/unit/weth/WETH.t.sol @@ -2,10 +2,6 @@ pragma solidity 0.8.20; -import {Vm} from "forge-std/Vm.sol"; - -import {Script, console2 as console} from "forge-std/Script.sol"; - import {Test} from "forge-std/Test.sol"; import { L2WrappedBaseToken } from "contracts/bridge/L2WrappedBaseToken.sol"; @@ -17,10 +13,10 @@ contract WethTest is Test { L2WrappedBaseToken internal weth; // The owner of the proxy - address ownerWallet = address(2); + address internal ownerWallet = address(2); - address l2BridgeAddress = address(3); - address l1Address = address(4); + address internal l2BridgeAddress = address(3); + address internal l1Address = address(4); function setUp() public { ownerWallet = makeAddr("owner"); From 0a5e318b7888c1176c59dea95844d45f76aab221 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:37:25 +0200 Subject: [PATCH 40/51] fix typo --- l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol b/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol index 7854fc545..4bcb387d0 100644 --- a/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol +++ b/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol @@ -140,7 +140,7 @@ contract L2Erc20BridgeTest is Test { assertEq(L2StandardERC20(l2TokenAddress).decimals(), 18); } - function test_governanceShoudNotBeAbleToSkipInitializerVersions() public { + function test_governanceShouldNotBeAbleToSkipInitializerVersions() public { address l2TokenAddress = initializeTokenByDeposit(); L2StandardERC20.ERC20Getters memory getters = L2StandardERC20.ERC20Getters({ From 6775587f842550d596136bb904cf909981b6e0f4 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:39:25 +0200 Subject: [PATCH 41/51] install foundry zksync in ci --- .github/workflows/l2-contracts-ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/l2-contracts-ci.yaml b/.github/workflows/l2-contracts-ci.yaml index 36e9ad4de..c842fa332 100644 --- a/.github/workflows/l2-contracts-ci.yaml +++ b/.github/workflows/l2-contracts-ci.yaml @@ -89,5 +89,8 @@ jobs: l2-contracts/cache-zk l2-contracts/typechain + - name: Install foundry zksync + run: cargo install --git https://github.com/matter-labs/foundry-zksync --locked forge cast + - name: Run tests run: yarn l2 test:foundry From 12cb8d02556326f9775adcc6e55c6c37455d1ba8 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 11:41:53 +0200 Subject: [PATCH 42/51] fix lint --- l2-contracts/contracts/L2ContractHelper.sol | 6 +- .../unit/erc20/L2Erc20BridgeTest.t.sol | 60 ++++++------------- .../test/foundry/unit/utils/Utils.sol | 55 +++++++++-------- .../test/foundry/unit/weth/WETH.t.sol | 8 +-- 4 files changed, 51 insertions(+), 78 deletions(-) diff --git a/l2-contracts/contracts/L2ContractHelper.sol b/l2-contracts/contracts/L2ContractHelper.sol index 0d2a93951..fde9b34d1 100644 --- a/l2-contracts/contracts/L2ContractHelper.sol +++ b/l2-contracts/contracts/L2ContractHelper.sol @@ -62,7 +62,7 @@ interface IContractDeployer { bytes32 _bytecodeHash, bytes32 _salt, bytes calldata _input - ) external view returns (address newAddress) ; + ) external view returns (address newAddress); } /** @@ -223,9 +223,7 @@ library L2ContractHelper { if (bytecodeLenInWords % 2 == 0) { revert MalformedBytecode(BytecodeError.WordsMustBeOdd); } - hashedBytecode = - sha256(_bytecode) & - 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // Setting the version of the hash hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248))); // Setting the length diff --git a/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol b/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol index 4bcb387d0..2a408055d 100644 --- a/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol +++ b/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol @@ -2,20 +2,21 @@ pragma solidity 0.8.20; +// solhint-disable gas-custom-errors import {Test} from "forge-std/Test.sol"; import {L2StandardERC20} from "contracts/bridge/L2StandardERC20.sol"; -import {L2AssetRouter} from "contracts/bridge/L2AssetRouter.sol"; +import {L2AssetRouter} from "contracts/bridge/L2AssetRouter.sol"; -import { UpgradeableBeacon } from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; -import { BeaconProxy } from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; -import { L2_ASSET_ROUTER, L2_NATIVE_TOKEN_VAULT } from "contracts/L2ContractHelper.sol"; +import {L2_ASSET_ROUTER, L2_NATIVE_TOKEN_VAULT} from "contracts/L2ContractHelper.sol"; import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; -import {Utils } from "../utils/Utils.sol"; +import {Utils} from "../utils/Utils.sol"; contract L2Erc20BridgeTest is Test { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. @@ -28,7 +29,7 @@ contract L2Erc20BridgeTest is Test { L2StandardERC20 internal standardErc20Impl; - UpgradeableBeacon beacon; + UpgradeableBeacon internal beacon; uint256 internal constant L1_CHAIN_ID = 9; uint256 internal ERA_CHAIN_ID = 270; @@ -55,14 +56,9 @@ contract L2Erc20BridgeTest is Test { assembly { beaconProxyBytecodeHash := extcodehash(proxy) } - + Utils.initSystemContracts(); - Utils.forceDeployAssetRouter( - L1_CHAIN_ID, - ERA_CHAIN_ID, - l1BridgeWallet, - address(0) - ); + Utils.forceDeployAssetRouter(L1_CHAIN_ID, ERA_CHAIN_ID, l1BridgeWallet, address(0)); Utils.forceDeployNativeTokenVault({ _l1ChainId: L1_CHAIN_ID, _aliasedOwner: ownerWallet, @@ -73,11 +69,7 @@ contract L2Erc20BridgeTest is Test { }); } - function performDeposit( - address depositor, - address receiver, - uint256 amount - ) internal { + function performDeposit(address depositor, address receiver, uint256 amount) internal { vm.prank(aliasedL1BridgeWallet); L2AssetRouter(address(L2_ASSET_ROUTER)).finalizeDeposit({ _l1Sender: depositor, @@ -89,11 +81,7 @@ contract L2Erc20BridgeTest is Test { } function initializeTokenByDeposit() internal returns (address l2TokenAddress) { - performDeposit( - makeAddr("someDepositor"), - makeAddr("someReeiver"), - 1 - ); + performDeposit(makeAddr("someDepositor"), makeAddr("someReeiver"), 1); l2TokenAddress = L2_NATIVE_TOKEN_VAULT.l2TokenAddress(L1_TOKEN_ADDRESS); require(l2TokenAddress != address(0), "Token not initialized"); @@ -103,11 +91,7 @@ contract L2Erc20BridgeTest is Test { address depositor = makeAddr("depositor"); address receiver = makeAddr("receiver"); - performDeposit( - depositor, - receiver, - 100 - ); + performDeposit(depositor, receiver, 100); address l2TokenAddress = L2_NATIVE_TOKEN_VAULT.l2TokenAddress(L1_TOKEN_ADDRESS); @@ -121,19 +105,14 @@ contract L2Erc20BridgeTest is Test { function test_governanceShouldBeAbleToReinitializeToken() public { address l2TokenAddress = initializeTokenByDeposit(); - L2StandardERC20.ERC20Getters memory getters = L2StandardERC20.ERC20Getters({ + L2StandardERC20.ERC20Getters memory getters = L2StandardERC20.ERC20Getters({ ignoreName: false, ignoreSymbol: false, ignoreDecimals: false }); vm.prank(ownerWallet); - L2StandardERC20(l2TokenAddress).reinitializeToken( - getters, - "TestTokenNewName", - "TTN", - 2 - ); + L2StandardERC20(l2TokenAddress).reinitializeToken(getters, "TestTokenNewName", "TTN", 2); assertEq(L2StandardERC20(l2TokenAddress).name(), "TestTokenNewName"); assertEq(L2StandardERC20(l2TokenAddress).symbol(), "TTN"); // The decimals should stay the same @@ -142,8 +121,8 @@ contract L2Erc20BridgeTest is Test { function test_governanceShouldNotBeAbleToSkipInitializerVersions() public { address l2TokenAddress = initializeTokenByDeposit(); - - L2StandardERC20.ERC20Getters memory getters = L2StandardERC20.ERC20Getters({ + + L2StandardERC20.ERC20Getters memory getters = L2StandardERC20.ERC20Getters({ ignoreName: false, ignoreSymbol: false, ignoreDecimals: false @@ -151,11 +130,6 @@ contract L2Erc20BridgeTest is Test { vm.expectRevert(); vm.prank(ownerWallet); - L2StandardERC20(l2TokenAddress).reinitializeToken( - getters, - "TestTokenNewName", - "TTN", - 20 - ); + L2StandardERC20(l2TokenAddress).reinitializeToken(getters, "TestTokenNewName", "TTN", 20); } } diff --git a/l2-contracts/test/foundry/unit/utils/Utils.sol b/l2-contracts/test/foundry/unit/utils/Utils.sol index 51a43e52e..945026720 100644 --- a/l2-contracts/test/foundry/unit/utils/Utils.sol +++ b/l2-contracts/test/foundry/unit/utils/Utils.sol @@ -4,9 +4,9 @@ pragma solidity 0.8.20; import {Vm} from "forge-std/Vm.sol"; -import { IContractDeployer, DEPLOYER_SYSTEM_CONTRACT, L2ContractHelper, L2_ASSET_ROUTER, L2_NATIVE_TOKEN_VAULT } from "contracts/L2ContractHelper.sol"; +import {IContractDeployer, DEPLOYER_SYSTEM_CONTRACT, L2ContractHelper, L2_ASSET_ROUTER, L2_NATIVE_TOKEN_VAULT} from "contracts/L2ContractHelper.sol"; -import {L2AssetRouter} from "contracts/bridge/L2AssetRouter.sol"; +import {L2AssetRouter} from "contracts/bridge/L2AssetRouter.sol"; import {L2NativeTokenVault} from "contracts/bridge/L2NativeTokenVault.sol"; library Utils { @@ -15,26 +15,19 @@ library Utils { address internal constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); - string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; - string internal constant L2_NATIVE_TOKEN_VAULT_PATH = "./zkout/L2NativeTokenVault.sol/L2NativeTokenVault.json"; + string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; + string internal constant L2_NATIVE_TOKEN_VAULT_PATH = "./zkout/L2NativeTokenVault.sol/L2NativeTokenVault.json"; /// @notice Returns the bytecode of a given era contract from a `zkout` folder. function readEraBytecode(string memory _filename) internal returns (bytes memory bytecode) { string memory artifact = vm.readFile( // solhint-disable-next-line func-named-parameters - string.concat( - "./zkout/", - _filename, - ".sol/", - _filename, - ".json" - ) + string.concat("./zkout/", _filename, ".sol/", _filename, ".json") ); bytecode = vm.parseJsonBytes(artifact, ".bytecode.object"); } - /// @notice Returns the bytecode of a given system contract. function readSystemContractsBytecode(string memory _filename) internal view returns (bytes memory) { string memory file = vm.readFile( @@ -66,7 +59,10 @@ library Utils { /// @param _l1AssetRouter The address of the L1 asset router. /// @param _legacySharedBridge The address of the legacy shared bridge. function forceDeployAssetRouter( - uint256 _l1ChainId, uint256 _eraChainId, address _l1AssetRouter, address _legacySharedBridge + uint256 _l1ChainId, + uint256 _eraChainId, + address _l1AssetRouter, + address _legacySharedBridge ) internal { // to ensure that the bytecode is known { @@ -87,9 +83,7 @@ library Utils { }); vm.prank(L2_FORCE_DEPLOYER_ADDR); - IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( - deployments - ); + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses(deployments); } /// @notice Deploys the L2NativeTokenVault contract. @@ -110,11 +104,11 @@ library Utils { // to ensure that the bytecode is known { new L2NativeTokenVault({ - _l1ChainId: _l1ChainId, - _aliasedOwner: _aliasedOwner, - _l2TokenProxyBytecodeHash: _l2TokenProxyBytecodeHash, - _legacySharedBridge: _legacySharedBridge, - _l2TokenBeacon: _l2TokenBeacon, + _l1ChainId: _l1ChainId, + _aliasedOwner: _aliasedOwner, + _l2TokenProxyBytecodeHash: _l2TokenProxyBytecodeHash, + _legacySharedBridge: _legacySharedBridge, + _l2TokenBeacon: _l2TokenBeacon, _contractsDeployedAlready: _contractsDeployedAlready }); } @@ -130,20 +124,29 @@ library Utils { callConstructor: true, value: 0, // solhint-disable-next-line func-named-parameters - input: abi.encode(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready) + input: abi.encode( + _l1ChainId, + _aliasedOwner, + _l2TokenProxyBytecodeHash, + _legacySharedBridge, + _l2TokenBeacon, + _contractsDeployedAlready + ) }); vm.prank(L2_FORCE_DEPLOYER_ADDR); - IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses( - deployments - ); + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses(deployments); } /// @notice Encodes the token data. /// @param name The name of the token. /// @param symbol The symbol of the token. /// @param decimals The decimals of the token. - function encodeTokenData(string memory name, string memory symbol, uint8 decimals) internal pure returns (bytes memory) { + function encodeTokenData( + string memory name, + string memory symbol, + uint8 decimals + ) internal pure returns (bytes memory) { bytes memory encodedName = abi.encode(name); bytes memory encodedSymbol = abi.encode(symbol); bytes memory encodedDecimals = abi.encode(decimals); diff --git a/l2-contracts/test/foundry/unit/weth/WETH.t.sol b/l2-contracts/test/foundry/unit/weth/WETH.t.sol index a35a6dd0b..350bebe06 100644 --- a/l2-contracts/test/foundry/unit/weth/WETH.t.sol +++ b/l2-contracts/test/foundry/unit/weth/WETH.t.sol @@ -4,8 +4,8 @@ pragma solidity 0.8.20; import {Test} from "forge-std/Test.sol"; -import { L2WrappedBaseToken } from "contracts/bridge/L2WrappedBaseToken.sol"; -import { TransparentUpgradeableProxy } from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {L2WrappedBaseToken} from "contracts/bridge/L2WrappedBaseToken.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Unauthorized, UnimplementedMessage, BRIDGE_MINT_NOT_IMPLEMENTED} from "contracts/errors/L2ContractErrors.sol"; @@ -49,9 +49,8 @@ contract WethTest is Test { function test_shouldWithdrawWethToL2Eth() public { address sender = makeAddr("sender"); uint256 amount = 100; - - vm.deal(sender, amount); + vm.deal(sender, amount); vm.prank(sender); weth.deposit{value: amount}(); @@ -117,4 +116,3 @@ contract WethTest is Test { weth.bridgeBurn(address(1), 1); } } - From db7eafb0ed7740155bd4267060d6d7a2a77bd439 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 12:05:26 +0200 Subject: [PATCH 43/51] more efficient forge install --- .github/workflows/l2-contracts-ci.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/l2-contracts-ci.yaml b/.github/workflows/l2-contracts-ci.yaml index c842fa332..427b1ba76 100644 --- a/.github/workflows/l2-contracts-ci.yaml +++ b/.github/workflows/l2-contracts-ci.yaml @@ -90,7 +90,14 @@ jobs: l2-contracts/typechain - name: Install foundry zksync - run: cargo install --git https://github.com/matter-labs/foundry-zksync --locked forge cast + run: | + wget https://github.com/matter-labs/foundry-zksync/releases/download/nightly-f908ce43834bc1ffb4de6576ea5600eaab49dddb/foundry_nightly_linux_amd64.tar.gz -O foundry-zksync.tar.gz + tar -xzf foundry-zksync.tar.gz + sudo mv forge /usr/local/bin/forge + sudo mv cast /usr/local/bin/cast + sudo chmod +x /usr/local/bin/forge + sudo chmod +x /usr/local/bin/cast + forge --version - name: Run tests run: yarn l2 test:foundry From e70b722001580164beb3169f313e4abdae04e0ed Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 12:10:23 +0200 Subject: [PATCH 44/51] fmt --- .github/workflows/l2-contracts-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/l2-contracts-ci.yaml b/.github/workflows/l2-contracts-ci.yaml index 427b1ba76..82cba17bc 100644 --- a/.github/workflows/l2-contracts-ci.yaml +++ b/.github/workflows/l2-contracts-ci.yaml @@ -90,7 +90,7 @@ jobs: l2-contracts/typechain - name: Install foundry zksync - run: | + run: | wget https://github.com/matter-labs/foundry-zksync/releases/download/nightly-f908ce43834bc1ffb4de6576ea5600eaab49dddb/foundry_nightly_linux_amd64.tar.gz -O foundry-zksync.tar.gz tar -xzf foundry-zksync.tar.gz sudo mv forge /usr/local/bin/forge From e572c9159273eaee5e9b4008508a831522f89eef Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 12:25:54 +0200 Subject: [PATCH 45/51] compile sc contracts also --- .github/workflows/l2-contracts-ci.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/l2-contracts-ci.yaml b/.github/workflows/l2-contracts-ci.yaml index 82cba17bc..5663a9f19 100644 --- a/.github/workflows/l2-contracts-ci.yaml +++ b/.github/workflows/l2-contracts-ci.yaml @@ -26,6 +26,9 @@ jobs: - name: Build L2 artifacts run: yarn l2 build + - name: Build system contract artifacts + run: yarn sc build + - name: Create cache uses: actions/cache/save@v3 with: @@ -37,6 +40,9 @@ jobs: l2-contracts/artifacts-zk l2-contracts/cache-zk l2-contracts/typechain + system-contracts/artifacts-zk + system-contracts/cache-zk + system-contracts/typechain lint: runs-on: ubuntu-latest @@ -88,6 +94,9 @@ jobs: l2-contracts/artifacts-zk l2-contracts/cache-zk l2-contracts/typechain + system-contracts/artifacts-zk + system-contracts/cache-zk + system-contracts/typechain - name: Install foundry zksync run: | From 8affc3c5a0f21e8bc9a2ef11f9f26405d7a7de24 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 15:50:31 +0200 Subject: [PATCH 46/51] fix compile --- l1-contracts/contracts/bridgehub/Bridgehub.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index d49654ae3..9bd433794 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -735,8 +735,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus messageRoot.addNewChain(_chainId); } - messageRoot.addNewChainIfNeeded(_chainId); - _registerNewHyperchain(_chainId, hyperchain); IZkSyncHyperchain(hyperchain).forwardedBridgeMint(_chainMintData, contractAlreadyDeployed); emit MigrationFinalized(_chainId, _assetId, hyperchain); From 464382e65b0b1bc4b2fa5f213b01907c3994df76 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 29 Aug 2024 17:53:57 +0200 Subject: [PATCH 47/51] fix compile --- .../contracts/bridgehub/Bridgehub.sol | 8 +-- .../script-out/output-deploy-l1.toml | 52 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index d7b2c7393..8eaa029ec 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -721,10 +721,10 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus require(stm != address(0), "BH: assetInfo 2"); require(settlementLayer[bridgeData.chainId] != block.chainid, "BH: already current SL"); - settlementLayer[_chainId] = block.chainid; - stateTransitionManager[_chainId] = stm; + settlementLayer[bridgeData.chainId] = block.chainid; + stateTransitionManager[bridgeData.chainId] = stm; - address hyperchain = getHyperchain(_chainId); + address hyperchain = getHyperchain(bridgeData.chainId); bool contractAlreadyDeployed = hyperchain != address(0); if (!contractAlreadyDeployed) { hyperchain = IStateTransitionManager(stm).forwardedBridgeMint(bridgeData.chainId, bridgeData.stmData); @@ -733,7 +733,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus messageRoot.addNewChain(bridgeData.chainId); } - IZkSyncHyperchain(hyperchain).forwardedBridgeMint(bridgeData.chainData); + IZkSyncHyperchain(hyperchain).forwardedBridgeMint(bridgeData.chainData, contractAlreadyDeployed); emit MigrationFinalized(bridgeData.chainId, _assetId, hyperchain); } diff --git a/l1-contracts/test/foundry/integration/deploy-scripts/script-out/output-deploy-l1.toml b/l1-contracts/test/foundry/integration/deploy-scripts/script-out/output-deploy-l1.toml index f81e096b5..dba974302 100644 --- a/l1-contracts/test/foundry/integration/deploy-scripts/script-out/output-deploy-l1.toml +++ b/l1-contracts/test/foundry/integration/deploy-scripts/script-out/output-deploy-l1.toml @@ -2,13 +2,13 @@ create2_factory_addr = "0x4e59b44847b379578588920cA78FbF26c0B4956C" create2_factory_salt = "0x00000000000000000000000000000000000000000000000000000000000000ff" deployer_addr = "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496" era_chain_id = 9 -force_deployments_data = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000002a0bc59a5ff202d6a9b1ce2e00f9fda0f4e963deef77e8fc6952c887b780a04fb7400000000000000000000000000000000000000000000000000000000000100020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010e00000000000000000000000081aa7970c51812dc3a010c7d01b50e0d17dc8ad9d3de98fb9db900fd0eb69c4e082f0a9b60872c04e318308d2128ee8ac82d634900000000000000000000000000000000000000000000000000000000000100030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010e000000000000000000000000000000000000000000000000000000000000000900000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c800000000000000000000000000000000000000000000000000000000000000013927048defd1ff97c7b682da4c45b7f2f838b0ad9d56f6754033951c9e6f3e1800000000000000000000000000000000000000000000000000000000000100040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010e00000000000000000000000081aa7970c51812dc3a010c7d01b50e0d17dc8ad9349c400bc5cef9910dcd7cd1328960b14d2c095f0e5d26c14f4f8a467160578500000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c800000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000000000000000000" +force_deployments_data = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000002a006c0107302b929bfaa26e37f1f16425630f553e88f5d300b535ae7a1a8235f8d00000000000000000000000000000000000000000000000000000000000100020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010e00000000000000000000000081aa7970c51812dc3a010c7d01b50e0d17dc8ad979ed796c3d846e1b70fae457404e519165deaa186a5f2357eeb1aef830c86b9600000000000000000000000000000000000000000000000000000000000100030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010e000000000000000000000000000000000000000000000000000000000000000900000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000000000000000001629ac8f4a74ebd8df4688cdedcfe5120809a334ecdc277c65e30475a8ed6616100000000000000000000000000000000000000000000000000000000000100040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010e00000000000000000000000081aa7970c51812dc3a010c7d01b50e0d17dc8ad9349c400bc5cef9910dcd7cd1328960b14d2c095f0e5d26c14f4f8a467160578500000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c800000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000000000000000000" l1_chain_id = 31337 -multicall3_addr = "0xA16c08D75103437F1cde2Bf27EFf7e37Be0d5f96" +multicall3_addr = "0x75ACdD102012453c342E06b01365a58d1108BbDB" owner_address = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" [contracts_config] -diamond_cut_data = "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000020975eebf38b4d893eae1556ac7372fc7435b5050000000000000000000000000000000000000000000000000000000000000d6000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000009c00000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000092cf215a8374aca867648c5ab7b6dbd273ac630b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000130e18b681000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000fc57565f000000000000000000000000000000000000000000000000000000001cc5d1030000000000000000000000000000000000000000000000000000000021f603d700000000000000000000000000000000000000000000000000000000235d9eb50000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000002878fe740000000000000000000000000000000000000000000000000000000041cf49bb000000000000000000000000000000000000000000000000000000004623c91d000000000000000000000000000000000000000000000000000000004dd18bf5000000000000000000000000000000000000000000000000000000006223258e0000000000000000000000000000000000000000000000000000000064b554ad0000000000000000000000000000000000000000000000000000000064bf8d660000000000000000000000000000000000000000000000000000000082b5774900000000000000000000000000000000000000000000000000000000a9f6d94100000000000000000000000000000000000000000000000000000000b784610700000000000000000000000000000000000000000000000000000000be6f11cf00000000000000000000000000000000000000000000000000000000e76db86500000000000000000000000000000000000000000000000000000000000000000000000000000000f48946296df791b85ead742b0945a7b4f9659302000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002d06d49e5b00000000000000000000000000000000000000000000000000000000086a56f8000000000000000000000000000000000000000000000000000000000ec6b0b700000000000000000000000000000000000000000000000000000000fe26699e0000000000000000000000000000000000000000000000000000000018e3a941000000000000000000000000000000000000000000000000000000001de72e340000000000000000000000000000000000000000000000000000000029b98c670000000000000000000000000000000000000000000000000000000033ce93fe000000000000000000000000000000000000000000000000000000003408e470000000000000000000000000000000000000000000000000000000003591c1a000000000000000000000000000000000000000000000000000000000396073820000000000000000000000000000000000000000000000000000000039d7d4aa0000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000052ef6b2c000000000000000000000000000000000000000000000000000000005518c73b000000000000000000000000000000000000000000000000000000005a59033500000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000006a27e8b5000000000000000000000000000000000000000000000000000000006e9960c30000000000000000000000000000000000000000000000000000000074f4d30d0000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000007a0ed627000000000000000000000000000000000000000000000000000000007b30c8da00000000000000000000000000000000000000000000000000000000960dcf240000000000000000000000000000000000000000000000000000000098acd7a6000000000000000000000000000000000000000000000000000000009cd939e4000000000000000000000000000000000000000000000000000000009d1b5a8100000000000000000000000000000000000000000000000000000000a1954fc500000000000000000000000000000000000000000000000000000000adfca15e00000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000b22dd78e00000000000000000000000000000000000000000000000000000000b8c2f66f00000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000cdffacc600000000000000000000000000000000000000000000000000000000d046815600000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000db1f0bf900000000000000000000000000000000000000000000000000000000e5355c7500000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000ea6c029c00000000000000000000000000000000000000000000000000000000ef3f0bae00000000000000000000000000000000000000000000000000000000f5c1182c00000000000000000000000000000000000000000000000000000000facd743b00000000000000000000000000000000000000000000000000000000fd791f3c00000000000000000000000000000000000000000000000000000000000000000000000000000000e354c85b01b7de2ded968f4959484fe51da46c51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b042901c70000000000000000000000000000000000000000000000000000000012f43dab0000000000000000000000000000000000000000000000000000000013f3756e00000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000263b7f8e000000000000000000000000000000000000000000000000000000006c0960f9000000000000000000000000000000000000000000000000000000007efda2ae00000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000dc8cc04600000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000e717bab700000000000000000000000000000000000000000000000000000000000000000000000000000000ba52c2746ce35de8b8a2ef92e5a0cc1048fb43cd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400a22e22000000000000000000000000000000000000000000000000000000000f23da4300000000000000000000000000000000000000000000000000000000c37533bb000000000000000000000000000000000000000000000000000000006edd4f120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000bd2803bf3ad46adead5d1d5c883b2cbc394eb396000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c4b400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000001d4c00000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000182b8000000000000000000000000000000000000000000000000000000000ee6b280000000000000000000000000a4cb26d6933d2c3e76718d30de8547bcdf8dd241" +diamond_cut_data = "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000b98c82863a46c019895d8a241b177b47080be2d20000000000000000000000000000000000000000000000000000000000000d6000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000009c00000000000000000000000000000000000000000000000000000000000000bc0000000000000000000000000ee3aa46ce7a642b7bf9b72ea773cfde0163dec1500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000130e18b681000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000fc57565f000000000000000000000000000000000000000000000000000000001cc5d1030000000000000000000000000000000000000000000000000000000021f603d700000000000000000000000000000000000000000000000000000000235d9eb50000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000002878fe74000000000000000000000000000000000000000000000000000000003f42d5dd0000000000000000000000000000000000000000000000000000000041cf49bb000000000000000000000000000000000000000000000000000000004623c91d000000000000000000000000000000000000000000000000000000004dd18bf5000000000000000000000000000000000000000000000000000000006223258e0000000000000000000000000000000000000000000000000000000064b554ad0000000000000000000000000000000000000000000000000000000064bf8d6600000000000000000000000000000000000000000000000000000000a9f6d94100000000000000000000000000000000000000000000000000000000b784610700000000000000000000000000000000000000000000000000000000be6f11cf00000000000000000000000000000000000000000000000000000000e76db8650000000000000000000000000000000000000000000000000000000000000000000000000000000088645d63e8477437a1506784acbfa8bc1f77c1b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002d06d49e5b00000000000000000000000000000000000000000000000000000000086a56f8000000000000000000000000000000000000000000000000000000000ec6b0b700000000000000000000000000000000000000000000000000000000fe26699e0000000000000000000000000000000000000000000000000000000018e3a941000000000000000000000000000000000000000000000000000000001de72e340000000000000000000000000000000000000000000000000000000029b98c670000000000000000000000000000000000000000000000000000000033ce93fe000000000000000000000000000000000000000000000000000000003408e470000000000000000000000000000000000000000000000000000000003591c1a000000000000000000000000000000000000000000000000000000000396073820000000000000000000000000000000000000000000000000000000039d7d4aa0000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000052ef6b2c000000000000000000000000000000000000000000000000000000005518c73b000000000000000000000000000000000000000000000000000000005a59033500000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000006a27e8b5000000000000000000000000000000000000000000000000000000006e9960c30000000000000000000000000000000000000000000000000000000074f4d30d0000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000007a0ed627000000000000000000000000000000000000000000000000000000007b30c8da00000000000000000000000000000000000000000000000000000000960dcf240000000000000000000000000000000000000000000000000000000098acd7a6000000000000000000000000000000000000000000000000000000009cd939e4000000000000000000000000000000000000000000000000000000009d1b5a8100000000000000000000000000000000000000000000000000000000a1954fc500000000000000000000000000000000000000000000000000000000adfca15e00000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000b22dd78e00000000000000000000000000000000000000000000000000000000b8c2f66f00000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000cdffacc600000000000000000000000000000000000000000000000000000000d046815600000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000db1f0bf900000000000000000000000000000000000000000000000000000000e5355c7500000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000ea6c029c00000000000000000000000000000000000000000000000000000000ef3f0bae00000000000000000000000000000000000000000000000000000000f5c1182c00000000000000000000000000000000000000000000000000000000facd743b00000000000000000000000000000000000000000000000000000000fd791f3c00000000000000000000000000000000000000000000000000000000000000000000000000000000d5bd18e281e0093b2dc67f4e9296b6a29f9a09b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b042901c70000000000000000000000000000000000000000000000000000000012f43dab0000000000000000000000000000000000000000000000000000000013f3756e00000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000263b7f8e000000000000000000000000000000000000000000000000000000006c0960f9000000000000000000000000000000000000000000000000000000007efda2ae00000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000dc8cc04600000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000e717bab700000000000000000000000000000000000000000000000000000000000000000000000000000000b873183bf3f7af16138a74816b26a14a6ef2c2b8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400a22e22000000000000000000000000000000000000000000000000000000000f23da4300000000000000000000000000000000000000000000000000000000c37533bb000000000000000000000000000000000000000000000000000000006edd4f120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000a3a0db4be1c102227fc9037a82af66f8dc8fb41f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c4b400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000001d4c00000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000182b8000000000000000000000000000000000000000000000000000000000ee6b280000000000000000000000000a4cb26d6933d2c3e76718d30de8547bcdf8dd241" diamond_init_batch_overhead_l1_gas = 1000000 diamond_init_max_l2_gas_per_batch = 80000000 diamond_init_max_pubdata_per_batch = 120000 @@ -23,34 +23,34 @@ recursion_node_level_vk_hash = "0x0000000000000000000000000000000000000000000000 [deployed_addresses] blob_versioned_hash_retriever_addr = "0xA4cB26d6933D2c3E76718D30de8547bCDF8dD241" -governance_addr = "0x5ED60563e4ea8aD522DaC519Bd50D8ef491f6a9B" -native_token_vault_addr = "0xC3682AF58CC5F86A5Ccc4f8a98baE40169aa0C59" +governance_addr = "0xd5Fb39C4d0167d6C6D384d7e8962423a8A9BC9F4" +native_token_vault_addr = "0x84449FFBC89C6BF0Ba5B71E3AC6A6C3dFD0A33F3" transparent_proxy_admin_addr = "0xD718d5A27a29FF1cD22403426084bA0d479869a0" -validator_timelock_addr = "0xcf55a79136cfd934DE34473CDA109ADCFe4EE7dC" +validator_timelock_addr = "0xb90B836643Def03F00727D7e8Fd331EEC8Ec0adf" [deployed_addresses.bridgehub] -bridgehub_implementation_addr = "0x7A8C660e8581b601d899a25F0B2aAc26F3466751" -bridgehub_proxy_addr = "0x79f5bf4dabA9596F64d344d475d06f8781bC6c79" -message_root_implementation_addr = "0x4e072A3F1E3Abf44a42b8bC0fb6656F472bf7014" -message_root_proxy_addr = "0x505c23F0decBaFB1507c1FC2B590D149FDF45092" -stm_deployment_tracker_implementation_addr = "0xA8c661B86f4ca8011eD3528c7b45A44E54cFB3C2" -stm_deployment_tracker_proxy_addr = "0xe95354DDaE439580D0c2bB4877AA67b3b34cbd6a" +bridgehub_implementation_addr = "0x8D94C2Af67a96B40572b132ac3C3d8D869247204" +bridgehub_proxy_addr = "0x7D94E8030A23ED1cAEa46Dd6642Acdfe03a7F893" +message_root_implementation_addr = "0x00D6289936efaC8764eF18DEab970a2CaF4DA69d" +message_root_proxy_addr = "0xC76A3ee1eC86E3C3776aAe3BFeF69be37B4DAc4C" +stm_deployment_tracker_implementation_addr = "0x4F3f1278301A2FE7A29636C03653F63Fbc22123a" +stm_deployment_tracker_proxy_addr = "0x09742A20490b2d9D1df9E94bFbd8026fcD349CBb" [deployed_addresses.bridges] -erc20_bridge_implementation_addr = "0xd2C471c66a8dE57e1D8f69E63F8C08b2a6042cB3" -erc20_bridge_proxy_addr = "0x27155C06862e0394dBF6feB738bf351C4b14571e" -shared_bridge_implementation_addr = "0x40ce021Ac45fE0f9E87c76d5fDF10141E61B7779" -shared_bridge_proxy_addr = "0xdd89ac0745987C2a568fAe443fdf4fe72a15f6C7" +erc20_bridge_implementation_addr = "0xdA583c78dDA194421aA45779026A097A2e6d963c" +erc20_bridge_proxy_addr = "0x8944BCDc7A23550C9bc290B197a3FF67BE18D31e" +shared_bridge_implementation_addr = "0x4E4020D1ea4B4c7EAe0267a9a00c81f2168ceA09" +shared_bridge_proxy_addr = "0x384DBe7285f29beb705E51B19f350EC6d718967E" [deployed_addresses.state_transition] -admin_facet_addr = "0x92cF215A8374ACa867648c5ab7B6dbD273ac630b" -default_upgrade_addr = "0x69fa08Db88Ee2265501B0F135D2ba6EEA6aA018C" -diamond_init_addr = "0x20975EEbf38B4d893eae1556aC7372fC7435b505" +admin_facet_addr = "0xeE3aa46ce7A642B7bF9B72eA773cFDe0163DEc15" +default_upgrade_addr = "0xDa3532D626dEAa25420fa61EA84f328622da5E62" +diamond_init_addr = "0xB98c82863A46c019895D8A241B177b47080Be2D2" diamond_proxy_addr = "0x0000000000000000000000000000000000000000" -executor_facet_addr = "0xBa52c2746ce35de8B8A2Ef92e5A0Cc1048FB43CD" -genesis_upgrade_addr = "0x4C48D458bFb60ac226ce0708FEd8cA9c0215EC7C" -getters_facet_addr = "0xF48946296dF791B85Ead742b0945a7b4f9659302" -mailbox_facet_addr = "0xe354c85b01B7DE2dEd968f4959484Fe51da46C51" -state_transition_implementation_addr = "0xcFef01D82405C0b90B2bF5C4F431870145bAbf19" -state_transition_proxy_addr = "0x43194d1BF637f4113b93660F4132d9d15FfA2f71" -verifier_addr = "0xbD2803bF3aD46AdeAD5D1D5c883b2CBc394eB396" +executor_facet_addr = "0xB873183BF3f7AF16138A74816B26a14a6Ef2C2B8" +genesis_upgrade_addr = "0x43Dc0bb98dFF37Db24808531838d73a0bc75dbEA" +getters_facet_addr = "0x88645D63e8477437a1506784aCbfa8bC1F77C1B8" +mailbox_facet_addr = "0xd5BD18e281e0093B2Dc67f4e9296B6a29F9a09b8" +state_transition_implementation_addr = "0x3cB743a54C47A7fCD41ade062207dC4503C4b8aB" +state_transition_proxy_addr = "0x71dC224e61EC6f1293Ff990dc0D31235a25EEC60" +verifier_addr = "0xa3a0Db4Be1c102227Fc9037A82aF66F8Dc8fb41F" From 989f2c14945f3f0afa34f03510fa1fb47d97a895 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Fri, 30 Aug 2024 11:59:52 +0200 Subject: [PATCH 48/51] L2 verifier support (#756) --- .github/workflows/l1-contracts-ci.yaml | 2 +- .github/workflows/l2-contracts-ci.yaml | 17 + .solhintignore | 1 + .../contracts/ForceDeployUpgrader.sol | 2 +- .../contracts/bridge/L2AssetRouter.sol | 2 +- .../contracts/bridge/L2NativeTokenVault.sol | 2 +- .../contracts/bridge/L2SharedBridgeLegacy.sol | 2 +- .../contracts/bridge/L2StandardERC20.sol | 2 +- .../contracts/bridge/L2WrappedBaseToken.sol | 2 +- .../bridge/interfaces/IL2AssetHandler.sol | 2 +- .../bridge/interfaces/IL2AssetRouter.sol | 2 +- .../bridge/interfaces/IL2NativeTokenVault.sol | 2 +- .../interfaces/IL2SharedBridgeLegacy.sol | 2 +- .../common/libraries/DataEncoding.sol | 2 +- .../contracts/data-availability/DAErrors.sol | 2 +- .../data-availability/RollupL2DAValidator.sol | 2 +- .../StateDiffL2DAValidator.sol | 2 +- .../ValidiumL2DAValidator.sol | 2 +- .../dev-contracts/VerifierRecursiveTest.sol | 70 + .../contracts/dev-contracts/VerifierTest.sol | 70 + .../contracts/interfaces/IL2DAValidator.sol | 2 +- l2-contracts/contracts/verifier/Verifier.sol | 1710 +++++++++++++++++ .../verifier/chain-interfaces/IVerifier.sol | 28 + l2-contracts/foundry.toml | 4 +- l2-contracts/hardhat.config.ts | 4 +- l2-contracts/package.json | 2 +- .../unit/erc20/L2Erc20BridgeTest.t.sol | 2 +- .../test/foundry/unit/utils/Utils.sol | 2 +- .../test/foundry/unit/verifier/Verifier.t.sol | 197 ++ .../unit/verifier/VerifierRecursive.t.sol | 55 + .../test/foundry/unit/weth/WETH.t.sol | 2 +- tools/README.md | 8 + tools/data/verifier_contract_template.txt | 13 +- tools/src/main.rs | 48 +- 34 files changed, 2228 insertions(+), 39 deletions(-) create mode 100644 l2-contracts/contracts/dev-contracts/VerifierRecursiveTest.sol create mode 100644 l2-contracts/contracts/dev-contracts/VerifierTest.sol create mode 100644 l2-contracts/contracts/verifier/Verifier.sol create mode 100644 l2-contracts/contracts/verifier/chain-interfaces/IVerifier.sol create mode 100644 l2-contracts/test/foundry/unit/verifier/Verifier.t.sol create mode 100644 l2-contracts/test/foundry/unit/verifier/VerifierRecursive.t.sol diff --git a/.github/workflows/l1-contracts-ci.yaml b/.github/workflows/l1-contracts-ci.yaml index 8ad16570f..3b5c269d1 100644 --- a/.github/workflows/l1-contracts-ci.yaml +++ b/.github/workflows/l1-contracts-ci.yaml @@ -155,7 +155,7 @@ jobs: - name: Run tests run: yarn l1 test --no-compile - check-verifier-generator: + check-verifier-generator-l1: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/l2-contracts-ci.yaml b/.github/workflows/l2-contracts-ci.yaml index 5663a9f19..e7e4b9541 100644 --- a/.github/workflows/l2-contracts-ci.yaml +++ b/.github/workflows/l2-contracts-ci.yaml @@ -63,6 +63,23 @@ jobs: - name: Lint run: yarn lint:check + check-verifier-generator-l2: + needs: [build] + runs-on: ubuntu-latest + + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Generate Verifier.sol + working-directory: tools + run: cargo run --bin zksync_verifier_contract_generator --release -- --input_path data/scheduler_key.json --l2_mode + + - name: Compare + run: diff tools/data/Verifier.sol l2-contracts/contracts/verifier/Verifier.sol + test: needs: [build, lint] runs-on: ubuntu-latest diff --git a/.solhintignore b/.solhintignore index abcb64f98..a8cd0e44e 100644 --- a/.solhintignore +++ b/.solhintignore @@ -16,6 +16,7 @@ l1-contracts-foundry/lib # l2-contracts l2-contracts/cache-zk l2-contracts/node_modules +l2-contracts/test # system-contracts system-contracts/contracts/openzeppelin diff --git a/l2-contracts/contracts/ForceDeployUpgrader.sol b/l2-contracts/contracts/ForceDeployUpgrader.sol index 02841cfdd..a7de60a2a 100644 --- a/l2-contracts/contracts/ForceDeployUpgrader.sol +++ b/l2-contracts/contracts/ForceDeployUpgrader.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {IContractDeployer, DEPLOYER_SYSTEM_CONTRACT} from "./L2ContractHelper.sol"; diff --git a/l2-contracts/contracts/bridge/L2AssetRouter.sol b/l2-contracts/contracts/bridge/L2AssetRouter.sol index d37c9c40b..dc2ee05fe 100644 --- a/l2-contracts/contracts/bridge/L2AssetRouter.sol +++ b/l2-contracts/contracts/bridge/L2AssetRouter.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {Initializable} from "@openzeppelin/contracts-v4/proxy/utils/Initializable.sol"; diff --git a/l2-contracts/contracts/bridge/L2NativeTokenVault.sol b/l2-contracts/contracts/bridge/L2NativeTokenVault.sol index 337808d84..e556ddc6c 100644 --- a/l2-contracts/contracts/bridge/L2NativeTokenVault.sol +++ b/l2-contracts/contracts/bridge/L2NativeTokenVault.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; diff --git a/l2-contracts/contracts/bridge/L2SharedBridgeLegacy.sol b/l2-contracts/contracts/bridge/L2SharedBridgeLegacy.sol index aeb727a2a..39d2769ec 100644 --- a/l2-contracts/contracts/bridge/L2SharedBridgeLegacy.sol +++ b/l2-contracts/contracts/bridge/L2SharedBridgeLegacy.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {Initializable} from "@openzeppelin/contracts-v4/proxy/utils/Initializable.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; diff --git a/l2-contracts/contracts/bridge/L2StandardERC20.sol b/l2-contracts/contracts/bridge/L2StandardERC20.sol index 5c45783e2..a3f493991 100644 --- a/l2-contracts/contracts/bridge/L2StandardERC20.sol +++ b/l2-contracts/contracts/bridge/L2StandardERC20.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; diff --git a/l2-contracts/contracts/bridge/L2WrappedBaseToken.sol b/l2-contracts/contracts/bridge/L2WrappedBaseToken.sol index 32d1a3bed..17b0f1b8f 100644 --- a/l2-contracts/contracts/bridge/L2WrappedBaseToken.sol +++ b/l2-contracts/contracts/bridge/L2WrappedBaseToken.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol"; diff --git a/l2-contracts/contracts/bridge/interfaces/IL2AssetHandler.sol b/l2-contracts/contracts/bridge/interfaces/IL2AssetHandler.sol index 53f6708d7..b19f4b420 100644 --- a/l2-contracts/contracts/bridge/interfaces/IL2AssetHandler.sol +++ b/l2-contracts/contracts/bridge/interfaces/IL2AssetHandler.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l2-contracts/contracts/bridge/interfaces/IL2AssetRouter.sol b/l2-contracts/contracts/bridge/interfaces/IL2AssetRouter.sol index a4d2c8b57..2fb8fc8b1 100644 --- a/l2-contracts/contracts/bridge/interfaces/IL2AssetRouter.sol +++ b/l2-contracts/contracts/bridge/interfaces/IL2AssetRouter.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l2-contracts/contracts/bridge/interfaces/IL2NativeTokenVault.sol b/l2-contracts/contracts/bridge/interfaces/IL2NativeTokenVault.sol index 4ad41addb..ba3ed0e0b 100644 --- a/l2-contracts/contracts/bridge/interfaces/IL2NativeTokenVault.sol +++ b/l2-contracts/contracts/bridge/interfaces/IL2NativeTokenVault.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; // import {IL2AssetRouter} from "./IL2AssetRouter.sol"; import {IL2AssetHandler} from "./IL2AssetHandler.sol"; diff --git a/l2-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol b/l2-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol index 33705debb..ac1acce22 100644 --- a/l2-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol +++ b/l2-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l2-contracts/contracts/common/libraries/DataEncoding.sol b/l2-contracts/contracts/common/libraries/DataEncoding.sol index 16c97c11a..be8a32210 100644 --- a/l2-contracts/contracts/common/libraries/DataEncoding.sol +++ b/l2-contracts/contracts/common/libraries/DataEncoding.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {L2_NATIVE_TOKEN_VAULT} from "../../L2ContractHelper.sol"; diff --git a/l2-contracts/contracts/data-availability/DAErrors.sol b/l2-contracts/contracts/data-availability/DAErrors.sol index 0d24845a8..457c7ff8b 100644 --- a/l2-contracts/contracts/data-availability/DAErrors.sol +++ b/l2-contracts/contracts/data-availability/DAErrors.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; enum PubdataField { NumberOfLogs, diff --git a/l2-contracts/contracts/data-availability/RollupL2DAValidator.sol b/l2-contracts/contracts/data-availability/RollupL2DAValidator.sol index 3f669996a..febedf625 100644 --- a/l2-contracts/contracts/data-availability/RollupL2DAValidator.sol +++ b/l2-contracts/contracts/data-availability/RollupL2DAValidator.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {IL2DAValidator} from "../interfaces/IL2DAValidator.sol"; import {StateDiffL2DAValidator} from "./StateDiffL2DAValidator.sol"; diff --git a/l2-contracts/contracts/data-availability/StateDiffL2DAValidator.sol b/l2-contracts/contracts/data-availability/StateDiffL2DAValidator.sol index 2102b5c28..ab7d48636 100644 --- a/l2-contracts/contracts/data-availability/StateDiffL2DAValidator.sol +++ b/l2-contracts/contracts/data-availability/StateDiffL2DAValidator.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {ReconstructionMismatch, PubdataField} from "./DAErrors.sol"; import {COMPRESSOR_CONTRACT, L2ContractHelper} from "../L2ContractHelper.sol"; diff --git a/l2-contracts/contracts/data-availability/ValidiumL2DAValidator.sol b/l2-contracts/contracts/data-availability/ValidiumL2DAValidator.sol index 78a49aea8..5930131fc 100644 --- a/l2-contracts/contracts/data-availability/ValidiumL2DAValidator.sol +++ b/l2-contracts/contracts/data-availability/ValidiumL2DAValidator.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity 0.8.24; import {IL2DAValidator} from "../interfaces/IL2DAValidator.sol"; diff --git a/l2-contracts/contracts/dev-contracts/VerifierRecursiveTest.sol b/l2-contracts/contracts/dev-contracts/VerifierRecursiveTest.sol new file mode 100644 index 000000000..2b1da08f0 --- /dev/null +++ b/l2-contracts/contracts/dev-contracts/VerifierRecursiveTest.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {Verifier} from "../verifier/Verifier.sol"; + +/// @author Matter Labs +contract VerifierRecursiveTest is Verifier { + // add this to be excluded from coverage report + function test() internal virtual {} + + function _loadVerificationKey() internal pure override { + assembly { + // gate setup commitments + mstore(VK_GATE_SETUP_0_X_SLOT, 0x046e45fd137982bd0f6cf731b4650d2d520e8d675827744e1edf1308583599bb) + mstore(VK_GATE_SETUP_0_Y_SLOT, 0x177f14d16b716d4298be5e07b83add3fb61ff1ee08dce19f9a54fa8f04937f7e) + mstore(VK_GATE_SETUP_1_X_SLOT, 0x169ad5156d25b56f7b67ea6382f88b845ed5bae5b91aacfe51d8f0716afff2fb) + mstore(VK_GATE_SETUP_1_Y_SLOT, 0x2406e3268e4d5fa672142998ecf834034638a4a6f8b5e90205552c6aa1dde163) + mstore(VK_GATE_SETUP_2_X_SLOT, 0x05fd0ce0fdc590938d29c738c8dc956b32ca8e69c3babfbb49dc1c13a6d9a8d4) + mstore(VK_GATE_SETUP_2_Y_SLOT, 0x0a27dac323a04dd319d9805be879875c95063d0a55c96214cd45c913fba84460) + mstore(VK_GATE_SETUP_3_X_SLOT, 0x0d58a2a86b208a4976beb9bfd918514d448656e0ee66175eb344a4a17bba99f8) + mstore(VK_GATE_SETUP_3_Y_SLOT, 0x215fa609a1a425b84c9dc218c6cf999596d9eba6d35597ad7aaf2d077a6616ed) + mstore(VK_GATE_SETUP_4_X_SLOT, 0x1a26e6deccf91174ab13613363eb4939680828f0c6031f5039f9e6f264afa68c) + mstore(VK_GATE_SETUP_4_Y_SLOT, 0x1f5b2d6bffac1839edfd02cd0e41acc411f0ecbf6c5c4b1da0e12b68b99cb25d) + mstore(VK_GATE_SETUP_5_X_SLOT, 0x09b71be2e8a45dcbe7654cf369c4f1f2e7eab4b97869a469fb7a149d989f7226) + mstore(VK_GATE_SETUP_5_Y_SLOT, 0x197e1e2cefbd4f99558b89ca875e01fec0f14f05e5128bd869c87d6bf2f307fa) + mstore(VK_GATE_SETUP_6_X_SLOT, 0x0d7cef745da686fd44760403d452d72be504bb41b0a7f4fbe973a07558893871) + mstore(VK_GATE_SETUP_6_Y_SLOT, 0x1e9a863307cdfd3fdcf119f72279ddfda08b6f23c3672e8378dbb9d548734c29) + mstore(VK_GATE_SETUP_7_X_SLOT, 0x16af3f5d978446fdb37d84f5cf12e59f5c1088bde23f8260c0bb6792c5f78e99) + mstore(VK_GATE_SETUP_7_Y_SLOT, 0x167d3aeee50c0e53fd1e8a33941a806a34cfae5dc8b66578486e5d7207b5d546) + + // gate selectors commitments + mstore(VK_GATE_SELECTORS_0_X_SLOT, 0x1addc8e154c74bed403dc19558096ce22f1ceb2c656a2a5e85e56d2be6580ed1) + mstore(VK_GATE_SELECTORS_0_Y_SLOT, 0x1420d38f0ef206828efc36d0f5ad2b4d85fe768097f358fc671b7b3ec0239234) + mstore(VK_GATE_SELECTORS_1_X_SLOT, 0x2d5c06d0c8aa6a3520b8351f82341affcbb1a0bf27bceb9bab175e3e1d38cf47) + mstore(VK_GATE_SELECTORS_1_Y_SLOT, 0x0ff8d923a0374308147f6dd4fc513f6d0640f5df699f4836825ef460df3f8d6a) + + // permutation commitments + mstore(VK_PERMUTATION_0_X_SLOT, 0x1de8943a8f67d9f6fcbda10a1f37a82de9e9ffd0a0102ea5ce0ce6dd13b4031b) + mstore(VK_PERMUTATION_0_Y_SLOT, 0x1e04b0824853ab5d7c3412a217a1c5b88a2b4011be7e7e849485be8ed7332e41) + mstore(VK_PERMUTATION_1_X_SLOT, 0x2aa1817b9cc40b6cc7a7b3f832f3267580f9fb8e539666c00541e1a77e34a3da) + mstore(VK_PERMUTATION_1_Y_SLOT, 0x0edb3cde226205b01212fc1861303c49ef3ff66f060b5833dc9a3f661ef31dd9) + mstore(VK_PERMUTATION_2_X_SLOT, 0x13f5ae93c8eccc1455a0095302923442d4b0b3c8233d66ded99ffcf2ad641c27) + mstore(VK_PERMUTATION_2_Y_SLOT, 0x2dd42d42ccdea8b1901435ace12bc9e52c7dbbeb409d20c517ba942ed0cc7519) + mstore(VK_PERMUTATION_3_X_SLOT, 0x1a15a70a016be11af71e46e9c8a8d31ece32a7e657ae90356dd9535e6566645f) + mstore(VK_PERMUTATION_3_Y_SLOT, 0x0381d23e115521c6fc233c5346f79a6777bfa8871b7ee623d990cdcb5d8c3ce1) + + // lookup tables commitments + mstore(VK_LOOKUP_TABLE_0_X_SLOT, 0x2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b) + mstore(VK_LOOKUP_TABLE_0_Y_SLOT, 0x04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d472981468) + mstore(VK_LOOKUP_TABLE_1_X_SLOT, 0x1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac) + mstore(VK_LOOKUP_TABLE_1_Y_SLOT, 0x1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d) + mstore(VK_LOOKUP_TABLE_2_X_SLOT, 0x0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89) + mstore(VK_LOOKUP_TABLE_2_Y_SLOT, 0x1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283) + mstore(VK_LOOKUP_TABLE_3_X_SLOT, 0x02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf) + mstore(VK_LOOKUP_TABLE_3_Y_SLOT, 0x09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a6) + + // lookup selector commitment + mstore(VK_LOOKUP_SELECTOR_X_SLOT, 0x1641f5d312e6f62720b1e6cd1d1be5bc0e69d10d20a12dc97ff04e2107e10ccc) + mstore(VK_LOOKUP_SELECTOR_Y_SLOT, 0x277f435d376acc3261ef9d5748e6705086214daf46d04edc80fbd657f8d9e73d) + + // table type commitment + mstore(VK_LOOKUP_TABLE_TYPE_X_SLOT, 0x1b5f1cfddd6713cf25d9e6850a1b3fe80d6ef7fe2c67248f25362d5f9b31893c) + mstore(VK_LOOKUP_TABLE_TYPE_Y_SLOT, 0x0945076de03a0d240067e5f02b8fc11eaa589df3343542576eb59fdb3ecb57e0) + + // flag for using recursive part + mstore(VK_RECURSIVE_FLAG_SLOT, 1) + } + } +} diff --git a/l2-contracts/contracts/dev-contracts/VerifierTest.sol b/l2-contracts/contracts/dev-contracts/VerifierTest.sol new file mode 100644 index 000000000..9c2db1c84 --- /dev/null +++ b/l2-contracts/contracts/dev-contracts/VerifierTest.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {Verifier} from "../verifier/Verifier.sol"; + +/// @author Matter Labs +contract VerifierTest is Verifier { + // add this to be excluded from coverage report + function test() internal virtual {} + + function _loadVerificationKey() internal pure override { + assembly { + // gate setup commitments + mstore(VK_GATE_SETUP_0_X_SLOT, 0x046e45fd137982bd0f6cf731b4650d2d520e8d675827744e1edf1308583599bb) + mstore(VK_GATE_SETUP_0_Y_SLOT, 0x177f14d16b716d4298be5e07b83add3fb61ff1ee08dce19f9a54fa8f04937f7e) + mstore(VK_GATE_SETUP_1_X_SLOT, 0x169ad5156d25b56f7b67ea6382f88b845ed5bae5b91aacfe51d8f0716afff2fb) + mstore(VK_GATE_SETUP_1_Y_SLOT, 0x2406e3268e4d5fa672142998ecf834034638a4a6f8b5e90205552c6aa1dde163) + mstore(VK_GATE_SETUP_2_X_SLOT, 0x05fd0ce0fdc590938d29c738c8dc956b32ca8e69c3babfbb49dc1c13a6d9a8d4) + mstore(VK_GATE_SETUP_2_Y_SLOT, 0x0a27dac323a04dd319d9805be879875c95063d0a55c96214cd45c913fba84460) + mstore(VK_GATE_SETUP_3_X_SLOT, 0x0d58a2a86b208a4976beb9bfd918514d448656e0ee66175eb344a4a17bba99f8) + mstore(VK_GATE_SETUP_3_Y_SLOT, 0x215fa609a1a425b84c9dc218c6cf999596d9eba6d35597ad7aaf2d077a6616ed) + mstore(VK_GATE_SETUP_4_X_SLOT, 0x1a26e6deccf91174ab13613363eb4939680828f0c6031f5039f9e6f264afa68c) + mstore(VK_GATE_SETUP_4_Y_SLOT, 0x1f5b2d6bffac1839edfd02cd0e41acc411f0ecbf6c5c4b1da0e12b68b99cb25d) + mstore(VK_GATE_SETUP_5_X_SLOT, 0x09b71be2e8a45dcbe7654cf369c4f1f2e7eab4b97869a469fb7a149d989f7226) + mstore(VK_GATE_SETUP_5_Y_SLOT, 0x197e1e2cefbd4f99558b89ca875e01fec0f14f05e5128bd869c87d6bf2f307fa) + mstore(VK_GATE_SETUP_6_X_SLOT, 0x0d7cef745da686fd44760403d452d72be504bb41b0a7f4fbe973a07558893871) + mstore(VK_GATE_SETUP_6_Y_SLOT, 0x1e9a863307cdfd3fdcf119f72279ddfda08b6f23c3672e8378dbb9d548734c29) + mstore(VK_GATE_SETUP_7_X_SLOT, 0x16af3f5d978446fdb37d84f5cf12e59f5c1088bde23f8260c0bb6792c5f78e99) + mstore(VK_GATE_SETUP_7_Y_SLOT, 0x167d3aeee50c0e53fd1e8a33941a806a34cfae5dc8b66578486e5d7207b5d546) + + // gate selectors commitments + mstore(VK_GATE_SELECTORS_0_X_SLOT, 0x1addc8e154c74bed403dc19558096ce22f1ceb2c656a2a5e85e56d2be6580ed1) + mstore(VK_GATE_SELECTORS_0_Y_SLOT, 0x1420d38f0ef206828efc36d0f5ad2b4d85fe768097f358fc671b7b3ec0239234) + mstore(VK_GATE_SELECTORS_1_X_SLOT, 0x2d5c06d0c8aa6a3520b8351f82341affcbb1a0bf27bceb9bab175e3e1d38cf47) + mstore(VK_GATE_SELECTORS_1_Y_SLOT, 0x0ff8d923a0374308147f6dd4fc513f6d0640f5df699f4836825ef460df3f8d6a) + + // permutation commitments + mstore(VK_PERMUTATION_0_X_SLOT, 0x1de8943a8f67d9f6fcbda10a1f37a82de9e9ffd0a0102ea5ce0ce6dd13b4031b) + mstore(VK_PERMUTATION_0_Y_SLOT, 0x1e04b0824853ab5d7c3412a217a1c5b88a2b4011be7e7e849485be8ed7332e41) + mstore(VK_PERMUTATION_1_X_SLOT, 0x2aa1817b9cc40b6cc7a7b3f832f3267580f9fb8e539666c00541e1a77e34a3da) + mstore(VK_PERMUTATION_1_Y_SLOT, 0x0edb3cde226205b01212fc1861303c49ef3ff66f060b5833dc9a3f661ef31dd9) + mstore(VK_PERMUTATION_2_X_SLOT, 0x13f5ae93c8eccc1455a0095302923442d4b0b3c8233d66ded99ffcf2ad641c27) + mstore(VK_PERMUTATION_2_Y_SLOT, 0x2dd42d42ccdea8b1901435ace12bc9e52c7dbbeb409d20c517ba942ed0cc7519) + mstore(VK_PERMUTATION_3_X_SLOT, 0x1a15a70a016be11af71e46e9c8a8d31ece32a7e657ae90356dd9535e6566645f) + mstore(VK_PERMUTATION_3_Y_SLOT, 0x0381d23e115521c6fc233c5346f79a6777bfa8871b7ee623d990cdcb5d8c3ce1) + + // lookup tables commitments + mstore(VK_LOOKUP_TABLE_0_X_SLOT, 0x2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b) + mstore(VK_LOOKUP_TABLE_0_Y_SLOT, 0x04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d472981468) + mstore(VK_LOOKUP_TABLE_1_X_SLOT, 0x1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac) + mstore(VK_LOOKUP_TABLE_1_Y_SLOT, 0x1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d) + mstore(VK_LOOKUP_TABLE_2_X_SLOT, 0x0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89) + mstore(VK_LOOKUP_TABLE_2_Y_SLOT, 0x1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283) + mstore(VK_LOOKUP_TABLE_3_X_SLOT, 0x02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf) + mstore(VK_LOOKUP_TABLE_3_Y_SLOT, 0x09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a6) + + // lookup selector commitment + mstore(VK_LOOKUP_SELECTOR_X_SLOT, 0x1641f5d312e6f62720b1e6cd1d1be5bc0e69d10d20a12dc97ff04e2107e10ccc) + mstore(VK_LOOKUP_SELECTOR_Y_SLOT, 0x277f435d376acc3261ef9d5748e6705086214daf46d04edc80fbd657f8d9e73d) + + // table type commitment + mstore(VK_LOOKUP_TABLE_TYPE_X_SLOT, 0x1b5f1cfddd6713cf25d9e6850a1b3fe80d6ef7fe2c67248f25362d5f9b31893c) + mstore(VK_LOOKUP_TABLE_TYPE_Y_SLOT, 0x0945076de03a0d240067e5f02b8fc11eaa589df3343542576eb59fdb3ecb57e0) + + // flag for using recursive part + mstore(VK_RECURSIVE_FLAG_SLOT, 0) + } + } +} diff --git a/l2-contracts/contracts/interfaces/IL2DAValidator.sol b/l2-contracts/contracts/interfaces/IL2DAValidator.sol index 3289bfc54..1e053307d 100644 --- a/l2-contracts/contracts/interfaces/IL2DAValidator.sol +++ b/l2-contracts/contracts/interfaces/IL2DAValidator.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; interface IL2DAValidator { function validatePubdata( diff --git a/l2-contracts/contracts/verifier/Verifier.sol b/l2-contracts/contracts/verifier/Verifier.sol new file mode 100644 index 000000000..07f0cc268 --- /dev/null +++ b/l2-contracts/contracts/verifier/Verifier.sol @@ -0,0 +1,1710 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {IVerifier} from "./chain-interfaces/IVerifier.sol"; + +/* solhint-disable max-line-length */ +/// @author Matter Labs +/// @notice Modified version of the Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of +/// Knowledge (PLONK) verifier. +/// Modifications have been made to optimize the proof system for ZKsync hyperchain circuits. +/// @dev Contract was generated from a verification key with a hash of 0x14f97b81e54b35fe673d8708cc1a19e1ea5b5e348e12d31e39824ed4f42bbca2 +/// @dev It uses a custom memory layout inside the inline assembly block. Each reserved memory cell is declared in the +/// constants below. +/// @dev For a better understanding of the verifier algorithm please refer to the following papers: +/// * Original Plonk Article: https://eprint.iacr.org/2019/953.pdf +/// * Original LookUp Article: https://eprint.iacr.org/2020/315.pdf +/// * Plonk for ZKsync v1.1: https://github.com/matter-labs/solidity_plonk_verifier/raw/recursive/bellman_vk_codegen_recursive/RecursivePlonkUnrolledForEthereum.pdf +/// The notation used in the code is the same as in the papers. +/* solhint-enable max-line-length */ +contract Verifier is IVerifier { + /*////////////////////////////////////////////////////////////// + Verification keys + //////////////////////////////////////////////////////////////*/ + + // Memory slots from 0x000 to 0x200 are reserved for intermediate computations and call to precompiles. + + uint256 internal constant VK_GATE_SETUP_0_X_SLOT = 0x200 + 0x000; + uint256 internal constant VK_GATE_SETUP_0_Y_SLOT = 0x200 + 0x020; + uint256 internal constant VK_GATE_SETUP_1_X_SLOT = 0x200 + 0x040; + uint256 internal constant VK_GATE_SETUP_1_Y_SLOT = 0x200 + 0x060; + uint256 internal constant VK_GATE_SETUP_2_X_SLOT = 0x200 + 0x080; + uint256 internal constant VK_GATE_SETUP_2_Y_SLOT = 0x200 + 0x0a0; + uint256 internal constant VK_GATE_SETUP_3_X_SLOT = 0x200 + 0x0c0; + uint256 internal constant VK_GATE_SETUP_3_Y_SLOT = 0x200 + 0x0e0; + uint256 internal constant VK_GATE_SETUP_4_X_SLOT = 0x200 + 0x100; + uint256 internal constant VK_GATE_SETUP_4_Y_SLOT = 0x200 + 0x120; + uint256 internal constant VK_GATE_SETUP_5_X_SLOT = 0x200 + 0x140; + uint256 internal constant VK_GATE_SETUP_5_Y_SLOT = 0x200 + 0x160; + uint256 internal constant VK_GATE_SETUP_6_X_SLOT = 0x200 + 0x180; + uint256 internal constant VK_GATE_SETUP_6_Y_SLOT = 0x200 + 0x1a0; + uint256 internal constant VK_GATE_SETUP_7_X_SLOT = 0x200 + 0x1c0; + uint256 internal constant VK_GATE_SETUP_7_Y_SLOT = 0x200 + 0x1e0; + + uint256 internal constant VK_GATE_SELECTORS_0_X_SLOT = 0x200 + 0x200; + uint256 internal constant VK_GATE_SELECTORS_0_Y_SLOT = 0x200 + 0x220; + uint256 internal constant VK_GATE_SELECTORS_1_X_SLOT = 0x200 + 0x240; + uint256 internal constant VK_GATE_SELECTORS_1_Y_SLOT = 0x200 + 0x260; + + uint256 internal constant VK_PERMUTATION_0_X_SLOT = 0x200 + 0x280; + uint256 internal constant VK_PERMUTATION_0_Y_SLOT = 0x200 + 0x2a0; + uint256 internal constant VK_PERMUTATION_1_X_SLOT = 0x200 + 0x2c0; + uint256 internal constant VK_PERMUTATION_1_Y_SLOT = 0x200 + 0x2e0; + uint256 internal constant VK_PERMUTATION_2_X_SLOT = 0x200 + 0x300; + uint256 internal constant VK_PERMUTATION_2_Y_SLOT = 0x200 + 0x320; + uint256 internal constant VK_PERMUTATION_3_X_SLOT = 0x200 + 0x340; + uint256 internal constant VK_PERMUTATION_3_Y_SLOT = 0x200 + 0x360; + + uint256 internal constant VK_LOOKUP_SELECTOR_X_SLOT = 0x200 + 0x380; + uint256 internal constant VK_LOOKUP_SELECTOR_Y_SLOT = 0x200 + 0x3a0; + + uint256 internal constant VK_LOOKUP_TABLE_0_X_SLOT = 0x200 + 0x3c0; + uint256 internal constant VK_LOOKUP_TABLE_0_Y_SLOT = 0x200 + 0x3e0; + uint256 internal constant VK_LOOKUP_TABLE_1_X_SLOT = 0x200 + 0x400; + uint256 internal constant VK_LOOKUP_TABLE_1_Y_SLOT = 0x200 + 0x420; + uint256 internal constant VK_LOOKUP_TABLE_2_X_SLOT = 0x200 + 0x440; + uint256 internal constant VK_LOOKUP_TABLE_2_Y_SLOT = 0x200 + 0x460; + uint256 internal constant VK_LOOKUP_TABLE_3_X_SLOT = 0x200 + 0x480; + uint256 internal constant VK_LOOKUP_TABLE_3_Y_SLOT = 0x200 + 0x4a0; + + uint256 internal constant VK_LOOKUP_TABLE_TYPE_X_SLOT = 0x200 + 0x4c0; + uint256 internal constant VK_LOOKUP_TABLE_TYPE_Y_SLOT = 0x200 + 0x4e0; + + uint256 internal constant VK_RECURSIVE_FLAG_SLOT = 0x200 + 0x500; + + /*////////////////////////////////////////////////////////////// + Proof + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant PROOF_PUBLIC_INPUT = 0x200 + 0x520 + 0x000; + + uint256 internal constant PROOF_STATE_POLYS_0_X_SLOT = 0x200 + 0x520 + 0x020; + uint256 internal constant PROOF_STATE_POLYS_0_Y_SLOT = 0x200 + 0x520 + 0x040; + uint256 internal constant PROOF_STATE_POLYS_1_X_SLOT = 0x200 + 0x520 + 0x060; + uint256 internal constant PROOF_STATE_POLYS_1_Y_SLOT = 0x200 + 0x520 + 0x080; + uint256 internal constant PROOF_STATE_POLYS_2_X_SLOT = 0x200 + 0x520 + 0x0a0; + uint256 internal constant PROOF_STATE_POLYS_2_Y_SLOT = 0x200 + 0x520 + 0x0c0; + uint256 internal constant PROOF_STATE_POLYS_3_X_SLOT = 0x200 + 0x520 + 0x0e0; + uint256 internal constant PROOF_STATE_POLYS_3_Y_SLOT = 0x200 + 0x520 + 0x100; + + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT = 0x200 + 0x520 + 0x120; + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x520 + 0x140; + + uint256 internal constant PROOF_LOOKUP_S_POLY_X_SLOT = 0x200 + 0x520 + 0x160; + uint256 internal constant PROOF_LOOKUP_S_POLY_Y_SLOT = 0x200 + 0x520 + 0x180; + + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT = 0x200 + 0x520 + 0x1a0; + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x520 + 0x1c0; + + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT = 0x200 + 0x520 + 0x1e0; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT = 0x200 + 0x520 + 0x200; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT = 0x200 + 0x520 + 0x220; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT = 0x200 + 0x520 + 0x240; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT = 0x200 + 0x520 + 0x260; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT = 0x200 + 0x520 + 0x280; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT = 0x200 + 0x520 + 0x2a0; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT = 0x200 + 0x520 + 0x2c0; + + uint256 internal constant PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x2e0; + uint256 internal constant PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x300; + uint256 internal constant PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x320; + uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x340; + + uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x360; + uint256 internal constant PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x380; + + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3a0; + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3c0; + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3e0; + + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x400; + uint256 internal constant PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x420; + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x440; + uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x460; + uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x480; + uint256 internal constant PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4a0; + uint256 internal constant PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4c0; + uint256 internal constant PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4e0; + uint256 internal constant PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x500; + + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_X_SLOT = 0x200 + 0x520 + 0x520; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_Y_SLOT = 0x200 + 0x520 + 0x540; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT = 0x200 + 0x520 + 0x560; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x520 + 0x580; + + uint256 internal constant PROOF_RECURSIVE_PART_P1_X_SLOT = 0x200 + 0x520 + 0x5a0; + uint256 internal constant PROOF_RECURSIVE_PART_P1_Y_SLOT = 0x200 + 0x520 + 0x5c0; + + uint256 internal constant PROOF_RECURSIVE_PART_P2_X_SLOT = 0x200 + 0x520 + 0x5e0; + uint256 internal constant PROOF_RECURSIVE_PART_P2_Y_SLOT = 0x200 + 0x520 + 0x600; + + /*////////////////////////////////////////////////////////////// + Transcript slot + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant TRANSCRIPT_BEGIN_SLOT = 0x200 + 0x520 + 0x620 + 0x00; + uint256 internal constant TRANSCRIPT_DST_BYTE_SLOT = 0x200 + 0x520 + 0x620 + 0x03; + uint256 internal constant TRANSCRIPT_STATE_0_SLOT = 0x200 + 0x520 + 0x620 + 0x04; + uint256 internal constant TRANSCRIPT_STATE_1_SLOT = 0x200 + 0x520 + 0x620 + 0x24; + uint256 internal constant TRANSCRIPT_CHALLENGE_SLOT = 0x200 + 0x520 + 0x620 + 0x44; + + /*////////////////////////////////////////////////////////////// + Partial verifier state + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant STATE_ALPHA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x000; + uint256 internal constant STATE_BETA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x020; + uint256 internal constant STATE_GAMMA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x040; + uint256 internal constant STATE_POWER_OF_ALPHA_2_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x060; + uint256 internal constant STATE_POWER_OF_ALPHA_3_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x080; + uint256 internal constant STATE_POWER_OF_ALPHA_4_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0a0; + uint256 internal constant STATE_POWER_OF_ALPHA_5_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0c0; + uint256 internal constant STATE_POWER_OF_ALPHA_6_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0e0; + uint256 internal constant STATE_POWER_OF_ALPHA_7_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x100; + uint256 internal constant STATE_POWER_OF_ALPHA_8_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x120; + uint256 internal constant STATE_ETA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x140; + uint256 internal constant STATE_BETA_LOOKUP_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x160; + uint256 internal constant STATE_GAMMA_LOOKUP_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x180; + uint256 internal constant STATE_BETA_PLUS_ONE_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1a0; + uint256 internal constant STATE_BETA_GAMMA_PLUS_GAMMA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1c0; + uint256 internal constant STATE_V_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1e0; + uint256 internal constant STATE_U_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x200; + uint256 internal constant STATE_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x220; + uint256 internal constant STATE_Z_MINUS_LAST_OMEGA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x240; + uint256 internal constant STATE_L_0_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x260; + uint256 internal constant STATE_L_N_MINUS_ONE_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x280; + uint256 internal constant STATE_Z_IN_DOMAIN_SIZE = 0x200 + 0x520 + 0x620 + 0x80 + 0x2a0; + + /*////////////////////////////////////////////////////////////// + Queries + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant QUERIES_BUFFER_POINT_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x00; + + uint256 internal constant QUERIES_AT_Z_0_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x40; + uint256 internal constant QUERIES_AT_Z_0_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x60; + uint256 internal constant QUERIES_AT_Z_1_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x80; + uint256 internal constant QUERIES_AT_Z_1_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xa0; + + uint256 internal constant QUERIES_T_POLY_AGGREGATED_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xc0; + uint256 internal constant QUERIES_T_POLY_AGGREGATED_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xe0; + + /*////////////////////////////////////////////////////////////// + Aggregated commitment + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant AGGREGATED_AT_Z_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x00; + uint256 internal constant AGGREGATED_AT_Z_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x20; + + uint256 internal constant AGGREGATED_AT_Z_OMEGA_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x40; + uint256 internal constant AGGREGATED_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x60; + + uint256 internal constant AGGREGATED_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x80; + uint256 internal constant AGGREGATED_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xa0; + + /*////////////////////////////////////////////////////////////// + Pairing data + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant PAIRING_BUFFER_POINT_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x00; + uint256 internal constant PAIRING_BUFFER_POINT_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x20; + + uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_X_SLOT = + 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x40; + uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_Y_SLOT = + 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x60; + + uint256 internal constant PAIRING_PAIR_WITH_X_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x80; + uint256 internal constant PAIRING_PAIR_WITH_X_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xa0; + + /*////////////////////////////////////////////////////////////// + Slots for scalar multiplication optimizations + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xc0; + uint256 internal constant LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xe0; + uint256 internal constant LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x100; + + /*////////////////////////////////////////////////////////////// + Constants + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant OMEGA = 0x1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb; + uint256 internal constant DOMAIN_SIZE = 0x1000000; // 2^24 + uint256 internal constant Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 internal constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + /// @dev flip of 0xe000000000000000000000000000000000000000000000000000000000000000; + uint256 internal constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + + // non residues + uint256 internal constant NON_RESIDUES_0 = 0x05; + uint256 internal constant NON_RESIDUES_1 = 0x07; + uint256 internal constant NON_RESIDUES_2 = 0x0a; + + // trusted setup g2 elements + uint256 internal constant G2_ELEMENTS_0_X1 = 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2; + uint256 internal constant G2_ELEMENTS_0_X2 = 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed; + uint256 internal constant G2_ELEMENTS_0_Y1 = 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b; + uint256 internal constant G2_ELEMENTS_0_Y2 = 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa; + uint256 internal constant G2_ELEMENTS_1_X1 = 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1; + uint256 internal constant G2_ELEMENTS_1_X2 = 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0; + uint256 internal constant G2_ELEMENTS_1_Y1 = 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4; + uint256 internal constant G2_ELEMENTS_1_Y2 = 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55; + + /// @inheritdoc IVerifier + function verificationKeyHash() external pure returns (bytes32 vkHash) { + _loadVerificationKey(); + + assembly { + let start := VK_GATE_SETUP_0_X_SLOT + let end := VK_RECURSIVE_FLAG_SLOT + let length := add(sub(end, start), 0x20) + + vkHash := keccak256(start, length) + } + } + + /// @notice Load verification keys to memory in runtime. + /// @dev The constants are loaded into memory in a specific layout declared in the constants starting from + /// `VK_` prefix. + /// NOTE: Function may corrupt the memory state if some memory was used before this function was called. + /// The VK consists of commitments to setup polynomials: + /// [q_a], [q_b], [q_c], [q_d], - main gate setup commitments + /// [q_{d_next}], [q_ab], [q_ac], [q_const] / + /// [main_gate_selector], [custom_gate_selector] - gate selectors commitments + /// [sigma_0], [sigma_1], [sigma_2], [sigma_3] - permutation polynomials commitments + /// [lookup_selector] - lookup selector commitment + /// [col_0], [col_1], [col_2], [col_3] - lookup columns commitments + /// [table_type] - lookup table type commitment + function _loadVerificationKey() internal pure virtual { + assembly { + // gate setup commitments + mstore(VK_GATE_SETUP_0_X_SLOT, 0x110deb1e0863737f9a3d7b4de641a03aa00a77bc9f1a05acc9d55b76ab9fdd4d) + mstore(VK_GATE_SETUP_0_Y_SLOT, 0x2c9dc252441e9298b7f6df6335a252517b7bccb924adf537b87c5cd3383fd7a9) + mstore(VK_GATE_SETUP_1_X_SLOT, 0x04659caf7b05471ba5ba85b1ab62267aa6c456836e625f169f7119d55b9462d2) + mstore(VK_GATE_SETUP_1_Y_SLOT, 0x0ea63403692148d2ad22189a1e5420076312f4d46e62036a043a6b0b84d5b410) + mstore(VK_GATE_SETUP_2_X_SLOT, 0x0e6696d09d65fce1e42805be03fca1f14aea247281f688981f925e77d4ce2291) + mstore(VK_GATE_SETUP_2_Y_SLOT, 0x0228f6cf8fe20c1e07e5b78bf8c41d50e55975a126d22a198d1e56acd4bbb3dd) + mstore(VK_GATE_SETUP_3_X_SLOT, 0x14685dafe340b1dec5eafcd5e7faddaf24f3781ddc53309cc25d0b42c00541dd) + mstore(VK_GATE_SETUP_3_Y_SLOT, 0x0e651cff9447cb360198899b80fa23e89ec13bc94ff161729aa841d2b55ea5be) + mstore(VK_GATE_SETUP_4_X_SLOT, 0x16e9ef76cb68f2750eb0ee72382dd9911a982308d0ab10ef94dada13c382ae73) + mstore(VK_GATE_SETUP_4_Y_SLOT, 0x22e404bc91350f3bc7daad1d1025113742436983c85eac5ab7b42221a181b81e) + mstore(VK_GATE_SETUP_5_X_SLOT, 0x0d9b29613037a5025655c82b143d2b7449c98f3aea358307c8529249cc54f3b9) + mstore(VK_GATE_SETUP_5_Y_SLOT, 0x15b3c4c946ad1babfc4c03ff7c2423fd354af3a9305c499b7fb3aaebe2fee746) + mstore(VK_GATE_SETUP_6_X_SLOT, 0x2a4cb6c495dbc7201142cc773da895ae2046e790073988fb850aca6aead27b8a) + mstore(VK_GATE_SETUP_6_Y_SLOT, 0x28ef9200c3cb67da82030520d640292014f5f7c2e2909da608812e04671a3acf) + mstore(VK_GATE_SETUP_7_X_SLOT, 0x283344a1ab3e55ecfd904d0b8e9f4faea338df5a4ead2fa9a42f0e103da40abc) + mstore(VK_GATE_SETUP_7_Y_SLOT, 0x223b37b83b9687512d322993edd70e508dd80adb10bcf7321a3cc8a44c269521) + + // gate selectors commitments + mstore(VK_GATE_SELECTORS_0_X_SLOT, 0x1f67f0ba5f7e837bc680acb4e612ebd938ad35211aa6e05b96cad19e66b82d2d) + mstore(VK_GATE_SELECTORS_0_Y_SLOT, 0x2820641a84d2e8298ac2ac42bd4b912c0c37f768ecc83d3a29e7c720763d15a1) + mstore(VK_GATE_SELECTORS_1_X_SLOT, 0x0353257957562270292a17860ca8e8827703f828f440ee004848b1e23fdf9de2) + mstore(VK_GATE_SELECTORS_1_Y_SLOT, 0x305f4137fee253dff8b2bfe579038e8f25d5bd217865072af5d89fc8800ada24) + + // permutation commitments + mstore(VK_PERMUTATION_0_X_SLOT, 0x13a600154b369ff3237706d00948e465ee1c32c7a6d3e18bccd9c4a15910f2e5) + mstore(VK_PERMUTATION_0_Y_SLOT, 0x138aa24fbf4cdddc75114811b3d59040394c218ecef3eb46ef9bd646f7e53776) + mstore(VK_PERMUTATION_1_X_SLOT, 0x277fff1f80c409357e2d251d79f6e3fd2164b755ce69cfd72de5c690289df662) + mstore(VK_PERMUTATION_1_Y_SLOT, 0x25235588e28c70eea3e35531c80deac25cd9b53ea3f98993f120108bc7abf670) + mstore(VK_PERMUTATION_2_X_SLOT, 0x0990e07a9b001048b947d0e5bd6157214c7359b771f01bf52bd771ba563a900e) + mstore(VK_PERMUTATION_2_Y_SLOT, 0x05e5fb090dd40914c8606d875e301167ae3047d684a02b44d9d36f1eaf43d0b4) + mstore(VK_PERMUTATION_3_X_SLOT, 0x1d4656690b33299db5631401a282afab3e16c78ee2c9ad9efea628171dcbc6bc) + mstore(VK_PERMUTATION_3_Y_SLOT, 0x0ebda2ebe582f601f813ec1e3970d13ef1500c742a85cce9b7f190f333de03b0) + + // lookup tables commitments + mstore(VK_LOOKUP_TABLE_0_X_SLOT, 0x2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b) + mstore(VK_LOOKUP_TABLE_0_Y_SLOT, 0x04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d472981468) + mstore(VK_LOOKUP_TABLE_1_X_SLOT, 0x1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac) + mstore(VK_LOOKUP_TABLE_1_Y_SLOT, 0x1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d) + mstore(VK_LOOKUP_TABLE_2_X_SLOT, 0x0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89) + mstore(VK_LOOKUP_TABLE_2_Y_SLOT, 0x1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283) + mstore(VK_LOOKUP_TABLE_3_X_SLOT, 0x02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf) + mstore(VK_LOOKUP_TABLE_3_Y_SLOT, 0x09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a6) + + // lookup selector commitment + mstore(VK_LOOKUP_SELECTOR_X_SLOT, 0x2f4d347c7fb61daaadfff881e24f4b5dcfdc0d70a95bcb148168b90ef93e0007) + mstore(VK_LOOKUP_SELECTOR_Y_SLOT, 0x2322632465ba8e28cd0a4befd813ea85a972f4f6fa8e8603cf5d062dbcb14065) + + // table type commitment + mstore(VK_LOOKUP_TABLE_TYPE_X_SLOT, 0x1e3c9fc98c118e4bc34f1f93d214a5d86898e980c40d8e2c180c6ada377a7467) + mstore(VK_LOOKUP_TABLE_TYPE_Y_SLOT, 0x2260a13535c35a15c173f5e5797d4b675b55d164a9995bfb7624971324bd84a8) + + // flag for using recursive part + mstore(VK_RECURSIVE_FLAG_SLOT, 0) + } + } + + /// @inheritdoc IVerifier + function verify( + uint256[] calldata, // _publicInputs + uint256[] calldata, // _proof + uint256[] calldata // _recursiveAggregationInput + ) public view virtual returns (bool) { + // No memory was accessed yet, so keys can be loaded into the right place and not corrupt any other memory. + _loadVerificationKey(); + + // Beginning of the big inline assembly block that makes all the verification work. + // Note: We use the custom memory layout, so the return value should be returned from the assembly, not + // Solidity code. + assembly { + /*////////////////////////////////////////////////////////////// + Utils + //////////////////////////////////////////////////////////////*/ + + /// @dev Reverts execution with a provided revert reason. + /// @param len The byte length of the error message string, which is expected to be no more than 32. + /// @param reason The 1-word revert reason string, encoded in ASCII. + function revertWithMessage(len, reason) { + // "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // Data offset + mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) + // Length of revert string + mstore(0x24, len) + // Revert reason + mstore(0x44, reason) + // Revert + revert(0x00, 0x64) + } + + /// @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD. + function modexp(value, power) -> res { + res := 1 + for { + + } gt(power, 0) { + + } { + if mod(power, 2) { + res := mulmod(res, value, R_MOD) + } + value := mulmod(value, value, R_MOD) + power := shr(1, power) + } + } + + /// @dev Performs a point multiplication operation and stores the result in a given memory destination. + function pointMulIntoDest(point, s, dest) { + mstore(0x00, mload(point)) + mstore(0x20, mload(add(point, 0x20))) + mstore(0x40, s) + if iszero(staticcall(gas(), 7, 0, 0x60, dest, 0x40)) { + revertWithMessage(30, "pointMulIntoDest: ecMul failed") + } + } + + /// @dev Performs a point addition operation and stores the result in a given memory destination. + function pointAddIntoDest(p1, p2, dest) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, mload(add(p2, 0x20))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) { + revertWithMessage(30, "pointAddIntoDest: ecAdd failed") + } + } + + /// @dev Performs a point subtraction operation and updates the first point with the result. + function pointSubAssign(p1, p2) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, sub(Q_MOD, mload(add(p2, 0x20)))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) { + revertWithMessage(28, "pointSubAssign: ecAdd failed") + } + } + + /// @dev Performs a point addition operation and updates the first point with the result. + function pointAddAssign(p1, p2) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, mload(add(p2, 0x20))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) { + revertWithMessage(28, "pointAddAssign: ecAdd failed") + } + } + + /// @dev Performs a point multiplication operation and then adds the result to the destination point. + function pointMulAndAddIntoDest(point, s, dest) { + mstore(0x00, mload(point)) + mstore(0x20, mload(add(point, 0x20))) + mstore(0x40, s) + let success := staticcall(gas(), 7, 0, 0x60, 0, 0x40) + + mstore(0x40, mload(dest)) + mstore(0x60, mload(add(dest, 0x20))) + success := and(success, staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) + + if iszero(success) { + revertWithMessage(22, "pointMulAndAddIntoDest") + } + } + + /// @dev Negates an elliptic curve point by changing the sign of the y-coordinate. + function pointNegate(point) { + let pY := mload(add(point, 0x20)) + switch pY + case 0 { + if mload(point) { + revertWithMessage(26, "pointNegate: invalid point") + } + } + default { + mstore(add(point, 0x20), sub(Q_MOD, pY)) + } + } + + /*////////////////////////////////////////////////////////////// + Transcript helpers + //////////////////////////////////////////////////////////////*/ + + /// @dev Updates the transcript state with a new challenge value. + function updateTranscript(value) { + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x00) + mstore(TRANSCRIPT_CHALLENGE_SLOT, value) + let newState0 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64) + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x01) + let newState1 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64) + mstore(TRANSCRIPT_STATE_1_SLOT, newState1) + mstore(TRANSCRIPT_STATE_0_SLOT, newState0) + } + + /// @dev Retrieves a transcript challenge. + function getTranscriptChallenge(numberOfChallenge) -> challenge { + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x02) + mstore(TRANSCRIPT_CHALLENGE_SLOT, shl(224, numberOfChallenge)) + challenge := and(keccak256(TRANSCRIPT_BEGIN_SLOT, 0x48), FR_MASK) + } + + /*////////////////////////////////////////////////////////////// + 1. Load Proof + //////////////////////////////////////////////////////////////*/ + + /// @dev This function loads a zk-SNARK proof, ensures it's properly formatted, and stores it in memory. + /// It ensures the number of inputs and the elliptic curve point's validity. + /// Note: It does NOT reject inputs that exceed these module sizes, but rather wraps them within the + /// module bounds. + /// The proof consists of: + /// 1. Public input: (1 field element from F_r) + /// + /// 2. Polynomial commitments (elliptic curve points over F_q): + /// [a], [b], [c], [d] - state polynomials commitments + /// [z_perm] - copy-permutation grand product commitment + /// [s] - polynomial for lookup argument commitment + /// [z_lookup] - lookup grand product commitment + /// [t_0], [t_1], [t_2], [t_3] - quotient polynomial parts commitments + /// [W], [W'] - proof openings commitments + /// + /// 3. Polynomial evaluations at z and z*omega (field elements from F_r): + /// t(z) - quotient polynomial opening + /// a(z), b(z), c(z), d(z), d(z*omega) - state polynomials openings + /// main_gate_selector(z) - main gate selector opening + /// sigma_0(z), sigma_1(z), sigma_2(z) - permutation polynomials openings + /// z_perm(z*omega) - copy-permutation grand product opening + /// z_lookup(z*omega) - lookup grand product opening + /// lookup_selector(z) - lookup selector opening + /// s(x*omega), t(z*omega), table_type(z) - lookup argument polynomial openings + /// r(z) - linearisation polynomial opening + /// + /// 4. Recursive proof (0 or 2 elliptic curve points over F_q) + function loadProof() { + // 1. Load public input + let offset := calldataload(0x04) + let publicInputLengthInWords := calldataload(add(offset, 0x04)) + let isValid := eq(publicInputLengthInWords, 1) // We expect only one public input + mstore(PROOF_PUBLIC_INPUT, and(calldataload(add(offset, 0x24)), FR_MASK)) + + // 2. Load the proof (except for the recursive part) + offset := calldataload(0x24) + let proofLengthInWords := calldataload(add(offset, 0x04)) + isValid := and(eq(proofLengthInWords, 44), isValid) + + // PROOF_STATE_POLYS_0 + { + let x := mod(calldataload(add(offset, 0x024)), Q_MOD) + let y := mod(calldataload(add(offset, 0x044)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_0_X_SLOT, x) + mstore(PROOF_STATE_POLYS_0_Y_SLOT, y) + } + // PROOF_STATE_POLYS_1 + { + let x := mod(calldataload(add(offset, 0x064)), Q_MOD) + let y := mod(calldataload(add(offset, 0x084)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_1_X_SLOT, x) + mstore(PROOF_STATE_POLYS_1_Y_SLOT, y) + } + // PROOF_STATE_POLYS_2 + { + let x := mod(calldataload(add(offset, 0x0a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x0c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_2_X_SLOT, x) + mstore(PROOF_STATE_POLYS_2_Y_SLOT, y) + } + // PROOF_STATE_POLYS_3 + { + let x := mod(calldataload(add(offset, 0x0e4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x104)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_3_X_SLOT, x) + mstore(PROOF_STATE_POLYS_3_Y_SLOT, y) + } + // PROOF_COPY_PERMUTATION_GRAND_PRODUCT + { + let x := mod(calldataload(add(offset, 0x124)), Q_MOD) + let y := mod(calldataload(add(offset, 0x144)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, x) + mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT, y) + } + // PROOF_LOOKUP_S_POLY + { + let x := mod(calldataload(add(offset, 0x164)), Q_MOD) + let y := mod(calldataload(add(offset, 0x184)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_LOOKUP_S_POLY_X_SLOT, x) + mstore(PROOF_LOOKUP_S_POLY_Y_SLOT, y) + } + // PROOF_LOOKUP_GRAND_PRODUCT + { + let x := mod(calldataload(add(offset, 0x1a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x1c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, x) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_0 + { + let x := mod(calldataload(add(offset, 0x1e4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x204)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_1 + { + let x := mod(calldataload(add(offset, 0x224)), Q_MOD) + let y := mod(calldataload(add(offset, 0x244)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_2 + { + let x := mod(calldataload(add(offset, 0x264)), Q_MOD) + let y := mod(calldataload(add(offset, 0x284)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_3 + { + let x := mod(calldataload(add(offset, 0x2a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x2c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT, y) + } + + mstore(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x2e4)), R_MOD)) + mstore(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x304)), R_MOD)) + mstore(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x324)), R_MOD)) + mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x344)), R_MOD)) + + mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x364)), R_MOD)) + mstore(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x384)), R_MOD)) + + mstore(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3a4)), R_MOD)) + mstore(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3c4)), R_MOD)) + mstore(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3e4)), R_MOD)) + + mstore( + PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, + mod(calldataload(add(offset, 0x404)), R_MOD) + ) + mstore(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x424)), R_MOD)) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x444)), R_MOD)) + mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x464)), R_MOD)) + mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x484)), R_MOD)) + mstore(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4a4)), R_MOD)) + mstore(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4c4)), R_MOD)) + mstore(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4e4)), R_MOD)) + mstore(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x504)), R_MOD)) + + // PROOF_OPENING_PROOF_AT_Z + { + let x := mod(calldataload(add(offset, 0x524)), Q_MOD) + let y := mod(calldataload(add(offset, 0x544)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_OPENING_PROOF_AT_Z_X_SLOT, x) + mstore(PROOF_OPENING_PROOF_AT_Z_Y_SLOT, y) + } + // PROOF_OPENING_PROOF_AT_Z_OMEGA + { + let x := mod(calldataload(add(offset, 0x564)), Q_MOD) + let y := mod(calldataload(add(offset, 0x584)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, x) + mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT, y) + } + + // 3. Load the recursive part of the proof + offset := calldataload(0x44) + let recursiveProofLengthInWords := calldataload(add(offset, 0x04)) + + switch mload(VK_RECURSIVE_FLAG_SLOT) + case 0 { + // recursive part should be empty + isValid := and(iszero(recursiveProofLengthInWords), isValid) + } + default { + // recursive part should be consist of 2 points + isValid := and(eq(recursiveProofLengthInWords, 4), isValid) + // PROOF_RECURSIVE_PART_P1 + { + let x := mod(calldataload(add(offset, 0x024)), Q_MOD) + let y := mod(calldataload(add(offset, 0x044)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_RECURSIVE_PART_P1_X_SLOT, x) + mstore(PROOF_RECURSIVE_PART_P1_Y_SLOT, y) + } + // PROOF_RECURSIVE_PART_P2 + { + let x := mod(calldataload(add(offset, 0x064)), Q_MOD) + let y := mod(calldataload(add(offset, 0x084)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_RECURSIVE_PART_P2_X_SLOT, x) + mstore(PROOF_RECURSIVE_PART_P2_Y_SLOT, y) + } + } + + // Revert if a proof is not valid + if iszero(isValid) { + revertWithMessage(27, "loadProof: Proof is invalid") + } + } + + /*////////////////////////////////////////////////////////////// + 2. Transcript initialization + //////////////////////////////////////////////////////////////*/ + + /// @notice Recomputes all challenges + /// @dev The process is the following: + /// Commit: PI, [a], [b], [c], [d] + /// Get: eta + /// Commit: [s] + /// Get: beta, gamma + /// Commit: [z_perm] + /// Get: beta', gamma' + /// Commit: [z_lookup] + /// Get: alpha + /// Commit: [t_0], [t_1], [t_2], [t_3] + /// Get: z + /// Commit: t(z), a(z), b(z), c(z), d(z), d(z*omega), + /// main_gate_selector(z), + /// sigma_0(z), sigma_1(z), sigma_2(z), + /// z_perm(z*omega), + /// t(z), lookup_selector(z), table_type(z), + /// s(x*omega), z_lookup(z*omega), t(z*omega), + /// r(z) + /// Get: v + /// Commit: [W], [W'] + /// Get: u + function initializeTranscript() { + // Round 1 + updateTranscript(mload(PROOF_PUBLIC_INPUT)) + updateTranscript(mload(PROOF_STATE_POLYS_0_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_0_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_Y_SLOT)) + + mstore(STATE_ETA_SLOT, getTranscriptChallenge(0)) + + // Round 1.5 + updateTranscript(mload(PROOF_LOOKUP_S_POLY_X_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_S_POLY_Y_SLOT)) + + mstore(STATE_BETA_SLOT, getTranscriptChallenge(1)) + mstore(STATE_GAMMA_SLOT, getTranscriptChallenge(2)) + + // Round 2 + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT)) + + mstore(STATE_BETA_LOOKUP_SLOT, getTranscriptChallenge(3)) + mstore(STATE_GAMMA_LOOKUP_SLOT, getTranscriptChallenge(4)) + + // Round 2.5 + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT)) + + mstore(STATE_ALPHA_SLOT, getTranscriptChallenge(5)) + + // Round 3 + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT)) + + { + let z := getTranscriptChallenge(6) + + mstore(STATE_Z_SLOT, z) + mstore(STATE_Z_IN_DOMAIN_SIZE, modexp(z, DOMAIN_SIZE)) + } + + // Round 4 + updateTranscript(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT)) + + mstore(STATE_V_SLOT, getTranscriptChallenge(7)) + + // Round 5 + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT)) + + mstore(STATE_U_SLOT, getTranscriptChallenge(8)) + } + + /*////////////////////////////////////////////////////////////// + 3. Verifying quotient evaluation + //////////////////////////////////////////////////////////////*/ + + /// @notice Compute linearisation polynomial's constant term: r_0 + /// @dev To save a verifier scalar multiplication, we split linearisation polynomial + /// into its constant and non-constant terms. The constant term is computed with the formula: + /// + /// r_0 = alpha^0 * L_0(z) * PI * q_{main selector}(z) + r(z) -- main gate contribution + /// + /// - alpha^4 * z_perm(z*omega)(sigma_0(z) * beta + gamma + a(z)) \ + /// (sigma_1(z) * beta + gamma + b(z)) | + /// (sigma_2(z) * beta + gamma + c(z)) | - permutation contribution + /// (sigma_3(z) + gamma) | + /// - alpha^5 * L_0(z) / + /// + /// + alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) \ + /// * (z - omega^{n-1}) * z_lookup(z*omega) | - lookup contribution + /// - alpha^7 * L_0(z) | + /// - alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1} / + /// + /// In the end we should check that t(z)*Z_H(z) = r(z) + r_0! + function verifyQuotientEvaluation() { + // Compute power of alpha + { + let alpha := mload(STATE_ALPHA_SLOT) + let currentAlpha := mulmod(alpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_2_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_3_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_4_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_5_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_6_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_7_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_8_SLOT, currentAlpha) + } + + // z + let stateZ := mload(STATE_Z_SLOT) + // L_0(z) + mstore(STATE_L_0_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(0, stateZ)) + // L_{n-1}(z) + mstore(STATE_L_N_MINUS_ONE_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(sub(DOMAIN_SIZE, 1), stateZ)) + // L_0(z) * PI + let stateT := mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(PROOF_PUBLIC_INPUT), R_MOD) + + // Compute main gate contribution + let result := mulmod(stateT, mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), R_MOD) + + // Compute permutation contribution + result := addmod(result, permutationQuotientContribution(), R_MOD) + + // Compute lookup contribution + result := addmod(result, lookupQuotientContribution(), R_MOD) + + // Check that r(z) + r_0 = t(z) * Z_H(z) + result := addmod(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), result, R_MOD) + + let vanishing := addmod(mload(STATE_Z_IN_DOMAIN_SIZE), sub(R_MOD, 1), R_MOD) + let lhs := mulmod(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT), vanishing, R_MOD) + if iszero(eq(lhs, result)) { + revertWithMessage(27, "invalid quotient evaluation") + } + } + + /// @notice Evaluating L_{polyNum}(at) out of domain + /// @dev L_i is a Lagrange polynomial for our domain such that: + /// L_i(omega^i) = 1 and L_i(omega^j) = 0 for all j != i + function evaluateLagrangePolyOutOfDomain(polyNum, at) -> res { + let omegaPower := 1 + if polyNum { + omegaPower := modexp(OMEGA, polyNum) + } + + res := addmod(modexp(at, DOMAIN_SIZE), sub(R_MOD, 1), R_MOD) + + // Vanishing polynomial can not be zero at point `at` + if iszero(res) { + revertWithMessage(28, "invalid vanishing polynomial") + } + res := mulmod(res, omegaPower, R_MOD) + let denominator := addmod(at, sub(R_MOD, omegaPower), R_MOD) + denominator := mulmod(denominator, DOMAIN_SIZE, R_MOD) + denominator := modexp(denominator, sub(R_MOD, 2)) + res := mulmod(res, denominator, R_MOD) + } + + /// @notice Compute permutation contribution to linearisation polynomial's constant term + function permutationQuotientContribution() -> res { + // res = alpha^4 * z_perm(z*omega) + res := mulmod( + mload(STATE_POWER_OF_ALPHA_4_SLOT), + mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), + R_MOD + ) + + { + let gamma := mload(STATE_GAMMA_SLOT) + let beta := mload(STATE_BETA_SLOT) + + let factorMultiplier + { + // res *= sigma_0(z) * beta + gamma + a(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + { + // res *= sigma_1(z) * beta + gamma + b(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + { + // res *= sigma_2(z) * beta + gamma + c(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + + // res *= sigma_3(z) + gamma + res := mulmod(res, addmod(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), gamma, R_MOD), R_MOD) + } + + // res = -res + res := sub(R_MOD, res) + + // -= L_0(z) * alpha^5 + let l0AtZ := mload(STATE_L_0_AT_Z_SLOT) + l0AtZ := mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD) + res := addmod(res, sub(R_MOD, l0AtZ), R_MOD) + } + + /// @notice Compute lookup contribution to linearisation polynomial's constant term + function lookupQuotientContribution() -> res { + let betaLookup := mload(STATE_BETA_LOOKUP_SLOT) + let gammaLookup := mload(STATE_GAMMA_LOOKUP_SLOT) + let betaPlusOne := addmod(betaLookup, 1, R_MOD) + let betaGamma := mulmod(betaPlusOne, gammaLookup, R_MOD) + + mstore(STATE_BETA_PLUS_ONE_SLOT, betaPlusOne) + mstore(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT, betaGamma) + + // res = alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) * z_lookup(z*omega) + res := mulmod(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT), betaLookup, R_MOD) + res := addmod(res, betaGamma, R_MOD) + res := mulmod(res, mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD) + res := mulmod(res, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + + // res *= z - omega^{n-1} + { + let lastOmega := modexp(OMEGA, sub(DOMAIN_SIZE, 1)) + let zMinusLastOmega := addmod(mload(STATE_Z_SLOT), sub(R_MOD, lastOmega), R_MOD) + mstore(STATE_Z_MINUS_LAST_OMEGA_SLOT, zMinusLastOmega) + res := mulmod(res, zMinusLastOmega, R_MOD) + } + + // res -= alpha^7 * L_{0}(z) + { + let intermediateValue := mulmod( + mload(STATE_L_0_AT_Z_SLOT), + mload(STATE_POWER_OF_ALPHA_7_SLOT), + R_MOD + ) + res := addmod(res, sub(R_MOD, intermediateValue), R_MOD) + } + + // res -= alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1} + { + let lnMinusOneAtZ := mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT) + let betaGammaPowered := modexp(betaGamma, sub(DOMAIN_SIZE, 1)) + let alphaPower8 := mload(STATE_POWER_OF_ALPHA_8_SLOT) + + let subtrahend := mulmod(mulmod(lnMinusOneAtZ, betaGammaPowered, R_MOD), alphaPower8, R_MOD) + res := addmod(res, sub(R_MOD, subtrahend), R_MOD) + } + } + + /// @notice Compute main gate contribution to linearisation polynomial commitment multiplied by v + function mainGateLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + // += a(z) * [q_a] + pointMulIntoDest(VK_GATE_SETUP_0_X_SLOT, stateOpening0AtZ, dest) + // += b(z) * [q_b] + pointMulAndAddIntoDest(VK_GATE_SETUP_1_X_SLOT, stateOpening1AtZ, dest) + // += c(z) * [q_c] + pointMulAndAddIntoDest(VK_GATE_SETUP_2_X_SLOT, stateOpening2AtZ, dest) + // += d(z) * [q_d] + pointMulAndAddIntoDest(VK_GATE_SETUP_3_X_SLOT, stateOpening3AtZ, dest) + // += a(z) * b(z) * [q_ab] + pointMulAndAddIntoDest(VK_GATE_SETUP_4_X_SLOT, mulmod(stateOpening0AtZ, stateOpening1AtZ, R_MOD), dest) + // += a(z) * c(z) * [q_ac] + pointMulAndAddIntoDest(VK_GATE_SETUP_5_X_SLOT, mulmod(stateOpening0AtZ, stateOpening2AtZ, R_MOD), dest) + // += [q_const] + pointAddAssign(dest, VK_GATE_SETUP_6_X_SLOT) + // += d(z*omega) * [q_{d_next}] + pointMulAndAddIntoDest(VK_GATE_SETUP_7_X_SLOT, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT), dest) + + // *= v * main_gate_selector(z) + let coeff := mulmod(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), mload(STATE_V_SLOT), R_MOD) + pointMulIntoDest(dest, coeff, dest) + } + + /// @notice Compute custom gate contribution to linearisation polynomial commitment multiplied by v + function addAssignRescueCustomGateLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + let accumulator + let intermediateValue + // = alpha * (a(z)^2 - b(z)) + accumulator := mulmod(stateOpening0AtZ, stateOpening0AtZ, R_MOD) + accumulator := addmod(accumulator, sub(R_MOD, stateOpening1AtZ), R_MOD) + accumulator := mulmod(accumulator, mload(STATE_ALPHA_SLOT), R_MOD) + // += alpha^2 * (b(z)^2 - c(z)) + intermediateValue := mulmod(stateOpening1AtZ, stateOpening1AtZ, R_MOD) + intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening2AtZ), R_MOD) + intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_2_SLOT), R_MOD) + accumulator := addmod(accumulator, intermediateValue, R_MOD) + // += alpha^3 * (c(z) * a(z) - d(z)) + intermediateValue := mulmod(stateOpening2AtZ, stateOpening0AtZ, R_MOD) + intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening3AtZ), R_MOD) + intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_3_SLOT), R_MOD) + accumulator := addmod(accumulator, intermediateValue, R_MOD) + + // *= v * [custom_gate_selector] + accumulator := mulmod(accumulator, mload(STATE_V_SLOT), R_MOD) + pointMulAndAddIntoDest(VK_GATE_SELECTORS_1_X_SLOT, accumulator, dest) + } + + /// @notice Compute copy-permutation contribution to linearisation polynomial commitment multiplied by v + function addAssignPermutationLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + // alpha^4 + let factor := mload(STATE_POWER_OF_ALPHA_4_SLOT) + // Calculate the factor + { + // *= (a(z) + beta * z + gamma) + let zMulBeta := mulmod(mload(STATE_Z_SLOT), mload(STATE_BETA_SLOT), R_MOD) + let gamma := mload(STATE_GAMMA_SLOT) + + let intermediateValue := addmod(addmod(zMulBeta, gamma, R_MOD), stateOpening0AtZ, R_MOD) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (b(z) + beta * z * k0 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_0, R_MOD), gamma, R_MOD), + stateOpening1AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (c(z) + beta * z * k1 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_1, R_MOD), gamma, R_MOD), + stateOpening2AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (d(z) + beta * z * k2 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_2, R_MOD), gamma, R_MOD), + stateOpening3AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + } + + // += alpha^5 * L_0(z) + let l0AtZ := mload(STATE_L_0_AT_Z_SLOT) + factor := addmod(factor, mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD), R_MOD) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [z_perm] during + // computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_perm] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + mstore(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + + // alpha^4 * beta * z_perm(z*omega) + factor := mulmod(mload(STATE_POWER_OF_ALPHA_4_SLOT), mload(STATE_BETA_SLOT), R_MOD) + factor := mulmod(factor, mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD) + { + // *= (a(z) + beta * sigma_0(z) + gamma) + let beta := mload(STATE_BETA_SLOT) + let gamma := mload(STATE_GAMMA_SLOT) + + let intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening0AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // *= (b(z) + beta * sigma_1(z) + gamma) + intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening1AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // *= (c(z) + beta * sigma_2(z) + gamma) + intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening2AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + } + + // *= v * [sigma_3] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + pointMulIntoDest(VK_PERMUTATION_3_X_SLOT, factor, QUERIES_BUFFER_POINT_SLOT) + + pointSubAssign(dest, QUERIES_BUFFER_POINT_SLOT) + } + + /// @notice Compute lookup contribution to linearisation polynomial commitment multiplied by v + function addAssignLookupLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ + ) { + // alpha^6 * v * z_lookup(z*omega) * (z - omega^{n-1}) * [s] + let factor := mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT) + factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD) + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [s] during + // computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [s] + mstore(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + + // gamma(1 + beta) + t(x) + beta * t(x*omega) + factor := mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT) + factor := mulmod(factor, mload(STATE_BETA_LOOKUP_SLOT), R_MOD) + factor := addmod(factor, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD) + factor := addmod(factor, mload(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT), R_MOD) + + // *= (gamma + f(z)) + // We should use fact that f(x) = + // lookup_selector(x) * (a(x) + eta * b(x) + eta^2 * c(x) + eta^3 * table_type(x)) + // to restore f(z) + let fReconstructed + { + fReconstructed := stateOpening0AtZ + let eta := mload(STATE_ETA_SLOT) + let currentEta := eta + + fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening1AtZ, R_MOD), R_MOD) + currentEta := mulmod(currentEta, eta, R_MOD) + fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening2AtZ, R_MOD), R_MOD) + currentEta := mulmod(currentEta, eta, R_MOD) + + // add type of table + fReconstructed := addmod( + fReconstructed, + mulmod(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT), currentEta, R_MOD), + R_MOD + ) + fReconstructed := mulmod(fReconstructed, mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT), R_MOD) + fReconstructed := addmod(fReconstructed, mload(STATE_GAMMA_LOOKUP_SLOT), R_MOD) + } + // *= -alpha^6 * (beta + 1) * (z - omega^{n-1}) + factor := mulmod(factor, fReconstructed, R_MOD) + factor := mulmod(factor, mload(STATE_BETA_PLUS_ONE_SLOT), R_MOD) + factor := sub(R_MOD, factor) + factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + + factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD) + + // += alpha^7 * L_0(z) + factor := addmod( + factor, + mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_7_SLOT), R_MOD), + R_MOD + ) + + // += alpha^8 * L_{n-1}(z) + factor := addmod( + factor, + mulmod(mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_8_SLOT), R_MOD), + R_MOD + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [z_lookup] during + // computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_lookup] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + mstore(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + } + + /*////////////////////////////////////////////////////////////// + 4. Prepare queries + //////////////////////////////////////////////////////////////*/ + + /// @dev Here we compute the first and second parts of batched polynomial commitment + /// We use the formula: + /// [D0] = [t_0] + z^n * [t_1] + z^{2n} * [t_2] + z^{3n} * [t_3] + /// and + /// [D1] = main_gate_selector(z) * ( \ + /// a(z) * [q_a] + b(z) * [q_b] + c(z) * [q_c] + d(z) * [q_d] + | - main gate contribution + /// a(z) * b(z) * [q_ab] + a(z) * c(z) * [q_ac] + | + /// [q_const] + d(z*omega) * [q_{d_next}]) / + /// + /// + alpha * [custom_gate_selector] * ( \ + /// (a(z)^2 - b(z)) + | - custom gate contribution + /// (b(z)^2 - c(z)) * alpha + | + /// (a(z)*c(z) - d(z)) * alpha^2 ) / + /// + /// + alpha^4 * [z_perm] * \ + /// (a(z) + beta * z + gamma) * | + /// (b(z) + beta * z * k0 + gamma) * | + /// (c(z) + beta * z * k1 + gamma) * | + /// (d(z) + beta * z * k2 + gamma) | - permutation contribution + /// - alpha^4 * z_perm(z*omega) * beta * [sigma_3] * | + /// (a(z) + beta * sigma_0(z) + gamma) * | + /// (b(z) + beta * sigma_1(z) + gamma) * | + /// (c(z) + beta * sigma_2(z) + gamma) * | + /// + alpha^5 * L_0(z) * [z_perm] / + /// + /// - alpha^6 * (1 + beta') * (gamma' + f(z)) * (z - omega^{n-1}) * \ + /// (gamma'(1 + beta') + t(z) + beta' * t(z*omega)) * [z_lookup] | + /// + alpha^6 * z_lookup(z*omega) * (z - omega^{n-1}) * [s] | - lookup contribution + /// + alpha^7 * L_0(z) * [z_lookup] | + /// + alpha^8 * L_{n-1}(z) * [z_lookup] / + function prepareQueries() { + // Calculate [D0] + { + let zInDomainSize := mload(STATE_Z_IN_DOMAIN_SIZE) + let currentZ := zInDomainSize + + mstore(QUERIES_AT_Z_0_X_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT)) + mstore(QUERIES_AT_Z_0_Y_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT)) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + currentZ := mulmod(currentZ, zInDomainSize, R_MOD) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + currentZ := mulmod(currentZ, zInDomainSize, R_MOD) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + } + + // Calculate v * [D1] + // We are going to multiply all the points in the sum by v to save + // one scalar multiplication during [F] computation + { + let stateOpening0AtZ := mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT) + let stateOpening1AtZ := mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT) + let stateOpening2AtZ := mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT) + let stateOpening3AtZ := mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT) + + mainGateLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignRescueCustomGateLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignPermutationLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignLookupLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ + ) + } + + // Also we should restore [t] for future computations + // [t] = [col_0] + eta*[col_1] + eta^2*[col_2] + eta^3*[col_3] + { + mstore(QUERIES_T_POLY_AGGREGATED_X_SLOT, mload(VK_LOOKUP_TABLE_0_X_SLOT)) + mstore(QUERIES_T_POLY_AGGREGATED_Y_SLOT, mload(VK_LOOKUP_TABLE_0_Y_SLOT)) + + let eta := mload(STATE_ETA_SLOT) + let currentEta := eta + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_1_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + currentEta := mulmod(currentEta, eta, R_MOD) + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_2_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + currentEta := mulmod(currentEta, eta, R_MOD) + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_3_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + } + } + + /*////////////////////////////////////////////////////////////// + 5. Prepare aggregated commitment + //////////////////////////////////////////////////////////////*/ + + /// @dev Here we compute aggregated commitment for the final pairing + /// We use the formula: + /// [E] = ( t(z) + v * r(z) + /// + v^2*a(z) + v^3*b(z) + v^4*c(z) + v^5*d(z) + /// + v^6*main_gate_selector(z) + /// + v^7*sigma_0(z) + v^8*sigma_1(z) + v^9*sigma_2(z) + /// + v^10*t(z) + v^11*lookup_selector(z) + v^12*table_type(z) + /// + u * (v^13*z_perm(z*omega) + v^14*d(z*omega) + /// + v^15*s(z*omega) + v^16*z_lookup(z*omega) + v^17*t(z*omega) + /// ) + /// ) * [1] + /// and + /// [F] = [D0] + v * [D1] + /// + v^2*[a] + v^3*[b] + v^4*[c] + v^5*[d] + /// + v^6*[main_gate_selector] + /// + v^7*[sigma_0] + v^8*[sigma_1] + v^9*[sigma_2] + /// + v^10*[t] + v^11*[lookup_selector] + v^12*[table_type] + /// + u * ( v^13*[z_perm] + v^14*[d] + /// + v^15*[s] + v^16*[z_lookup] + v^17*[t] + /// ) + function prepareAggregatedCommitment() { + // Here we compute parts of [E] and [F] without u multiplier + let aggregationChallenge := 1 + let firstDCoeff + let firstTCoeff + + mstore(AGGREGATED_AT_Z_X_SLOT, mload(QUERIES_AT_Z_0_X_SLOT)) + mstore(AGGREGATED_AT_Z_Y_SLOT, mload(QUERIES_AT_Z_0_Y_SLOT)) + let aggregatedOpeningAtZ := mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT) + { + function updateAggregationChallenge( + queriesCommitmentPoint, + valueAtZ, + curAggregationChallenge, + curAggregatedOpeningAtZ + ) -> newAggregationChallenge, newAggregatedOpeningAtZ { + newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD) + pointMulAndAddIntoDest(queriesCommitmentPoint, newAggregationChallenge, AGGREGATED_AT_Z_X_SLOT) + newAggregatedOpeningAtZ := addmod( + curAggregatedOpeningAtZ, + mulmod(newAggregationChallenge, mload(valueAtZ), R_MOD), + R_MOD + ) + } + + // We don't need to multiply by v, because we have already computed v * [D1] + pointAddIntoDest(AGGREGATED_AT_Z_X_SLOT, QUERIES_AT_Z_1_X_SLOT, AGGREGATED_AT_Z_X_SLOT) + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_0_X_SLOT, + PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_1_X_SLOT, + PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_2_X_SLOT, + PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [d] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [d] + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + firstDCoeff := aggregationChallenge + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_GATE_SELECTORS_0_X_SLOT, + PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_0_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_1_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_2_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [t] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [t] + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + firstTCoeff := aggregationChallenge + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_LOOKUP_SELECTOR_X_SLOT, + PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_LOOKUP_TABLE_TYPE_X_SLOT, + PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + } + mstore(AGGREGATED_OPENING_AT_Z_SLOT, aggregatedOpeningAtZ) + + // Here we compute parts of [E] and [F] with u multiplier + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + + let copyPermutationCoeff := addmod( + mload(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF), + mulmod(aggregationChallenge, mload(STATE_U_SLOT), R_MOD), + R_MOD + ) + + pointMulIntoDest( + PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, + copyPermutationCoeff, + AGGREGATED_AT_Z_OMEGA_X_SLOT + ) + let aggregatedOpeningAtZOmega := mulmod( + mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), + aggregationChallenge, + R_MOD + ) + + { + function updateAggregationChallenge( + queriesCommitmentPoint, + valueAtZ_Omega, + previousCoeff, + curAggregationChallenge, + curAggregatedOpeningAtZ_Omega + ) -> newAggregationChallenge, newAggregatedOpeningAtZ_Omega { + newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD) + let finalCoeff := addmod( + previousCoeff, + mulmod(newAggregationChallenge, mload(STATE_U_SLOT), R_MOD), + R_MOD + ) + pointMulAndAddIntoDest(queriesCommitmentPoint, finalCoeff, AGGREGATED_AT_Z_OMEGA_X_SLOT) + newAggregatedOpeningAtZ_Omega := addmod( + curAggregatedOpeningAtZ_Omega, + mulmod(newAggregationChallenge, mload(valueAtZ_Omega), R_MOD), + R_MOD + ) + } + + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_STATE_POLYS_3_X_SLOT, + PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, + firstDCoeff, + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_LOOKUP_S_POLY_X_SLOT, + PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, + mload(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF), + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, + PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, + mload(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF), + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + QUERIES_T_POLY_AGGREGATED_X_SLOT, + PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, + firstTCoeff, + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + } + mstore(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT, aggregatedOpeningAtZOmega) + + // Now we can merge both parts and get [E] and [F] + let u := mload(STATE_U_SLOT) + + // [F] + pointAddIntoDest( + AGGREGATED_AT_Z_X_SLOT, + AGGREGATED_AT_Z_OMEGA_X_SLOT, + PAIRING_PAIR_WITH_GENERATOR_X_SLOT + ) + + // [E] = (aggregatedOpeningAtZ + u * aggregatedOpeningAtZOmega) * [1] + let aggregatedValue := addmod( + mulmod(mload(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT), u, R_MOD), + mload(AGGREGATED_OPENING_AT_Z_SLOT), + R_MOD + ) + + mstore(PAIRING_BUFFER_POINT_X_SLOT, 1) + mstore(PAIRING_BUFFER_POINT_Y_SLOT, 2) + pointMulIntoDest(PAIRING_BUFFER_POINT_X_SLOT, aggregatedValue, PAIRING_BUFFER_POINT_X_SLOT) + } + + /*////////////////////////////////////////////////////////////// + 5. Pairing + //////////////////////////////////////////////////////////////*/ + + /// @notice Checks the final pairing + /// @dev We should check the equation: + /// e([W] + u * [W'], [x]_2) = e(z * [W] + u * z * omega * [W'] + [F] - [E], [1]_2), + /// where [F] and [E] were computed previously + /// + /// Also we need to check that e([P1], [x]_2) = e([P2], [1]_2) + /// if we have the recursive part of the proof + /// where [P1] and [P2] are parts of the recursive proof + /// + /// We can aggregate both pairings into one for gas optimization: + /// e([W] + u * [W'] + u^2 * [P1], [x]_2) = + /// e(z * [W] + u * z * omega * [W'] + [F] - [E] + u^2 * [P2], [1]_2) + /// + /// u is a valid challenge for such aggregation, + /// because [P1] and [P2] are used in PI + function finalPairing() { + let u := mload(STATE_U_SLOT) + let z := mload(STATE_Z_SLOT) + let zOmega := mulmod(mload(STATE_Z_SLOT), OMEGA, R_MOD) + + // [F] - [E] + pointSubAssign(PAIRING_PAIR_WITH_GENERATOR_X_SLOT, PAIRING_BUFFER_POINT_X_SLOT) + + // +z * [W] + u * z * omega * [W'] + pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_X_SLOT, z, PAIRING_PAIR_WITH_GENERATOR_X_SLOT) + pointMulAndAddIntoDest( + PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, + mulmod(zOmega, u, R_MOD), + PAIRING_PAIR_WITH_GENERATOR_X_SLOT + ) + + // [W] + u * [W'] + mstore(PAIRING_PAIR_WITH_X_X_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT)) + mstore(PAIRING_PAIR_WITH_X_Y_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT)) + pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, u, PAIRING_PAIR_WITH_X_X_SLOT) + pointNegate(PAIRING_PAIR_WITH_X_X_SLOT) + + // Add recursive proof part if needed + if mload(VK_RECURSIVE_FLAG_SLOT) { + let uu := mulmod(u, u, R_MOD) + pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P1_X_SLOT, uu, PAIRING_PAIR_WITH_GENERATOR_X_SLOT) + pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P2_X_SLOT, uu, PAIRING_PAIR_WITH_X_X_SLOT) + } + + // Calculate pairing + { + mstore(0x000, mload(PAIRING_PAIR_WITH_GENERATOR_X_SLOT)) + mstore(0x020, mload(PAIRING_PAIR_WITH_GENERATOR_Y_SLOT)) + + mstore(0x040, G2_ELEMENTS_0_X1) + mstore(0x060, G2_ELEMENTS_0_X2) + mstore(0x080, G2_ELEMENTS_0_Y1) + mstore(0x0a0, G2_ELEMENTS_0_Y2) + + mstore(0x0c0, mload(PAIRING_PAIR_WITH_X_X_SLOT)) + mstore(0x0e0, mload(PAIRING_PAIR_WITH_X_Y_SLOT)) + + mstore(0x100, G2_ELEMENTS_1_X1) + mstore(0x120, G2_ELEMENTS_1_X2) + mstore(0x140, G2_ELEMENTS_1_Y1) + mstore(0x160, G2_ELEMENTS_1_Y2) + + let success := staticcall(gas(), 8, 0, 0x180, 0x00, 0x20) + if iszero(success) { + revertWithMessage(32, "finalPairing: precompile failure") + } + if iszero(mload(0)) { + revertWithMessage(29, "finalPairing: pairing failure") + } + } + } + + /*////////////////////////////////////////////////////////////// + Verification + //////////////////////////////////////////////////////////////*/ + + // Step 1: Load the proof and check the correctness of its parts + loadProof() + + // Step 2: Recompute all the challenges with the transcript + initializeTranscript() + + // Step 3: Check the quotient equality + verifyQuotientEvaluation() + + // Step 4: Compute queries [D0] and v * [D1] + prepareQueries() + + // Step 5: Compute [E] and [F] + prepareAggregatedCommitment() + + // Step 6: Check the final pairing with aggregated recursive proof + finalPairing() + + mstore(0, true) + return(0, 32) + } + } +} diff --git a/l2-contracts/contracts/verifier/chain-interfaces/IVerifier.sol b/l2-contracts/contracts/verifier/chain-interfaces/IVerifier.sol new file mode 100644 index 000000000..dbca3bf0c --- /dev/null +++ b/l2-contracts/contracts/verifier/chain-interfaces/IVerifier.sol @@ -0,0 +1,28 @@ +// 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; + +/// @notice Part of the configuration parameters of ZKP circuits +struct VerifierParams { + bytes32 recursionNodeLevelVkHash; + bytes32 recursionLeafLevelVkHash; + bytes32 recursionCircuitsSetVksHash; +} + +/// @title The interface of the Verifier contract, responsible for the zero knowledge proof verification. +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +interface IVerifier { + /// @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, + uint256[] calldata _recursiveAggregationInput + ) 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 pure returns (bytes32); +} diff --git a/l2-contracts/foundry.toml b/l2-contracts/foundry.toml index 0dd8c668f..b5c0f534d 100644 --- a/l2-contracts/foundry.toml +++ b/l2-contracts/foundry.toml @@ -3,7 +3,7 @@ src = "contracts" out = "out" libs = ["lib"] test = "test/foundry" -solc_version = "0.8.20" +solc_version = "0.8.24" cache_path = "cache-forge" via_ir = true evm_version = "paris" @@ -24,4 +24,4 @@ fs_permissions = [ [profile.default.zksync] enable_eravm_extensions = true -zksolc = "1.5.1" +zksolc = "1.5.3" diff --git a/l2-contracts/hardhat.config.ts b/l2-contracts/hardhat.config.ts index c0aaca03e..235930123 100644 --- a/l2-contracts/hardhat.config.ts +++ b/l2-contracts/hardhat.config.ts @@ -12,14 +12,14 @@ if (!process.env.CHAIN_ETH_NETWORK) { export default { zksolc: { - version: "1.3.18", + version: "1.5.3", compilerSource: "binary", settings: { isSystem: true, }, }, solidity: { - version: "0.8.20", + version: "0.8.24", }, defaultNetwork: "localhost", networks: { diff --git a/l2-contracts/package.json b/l2-contracts/package.json index 6c4469960..9f074e14a 100644 --- a/l2-contracts/package.json +++ b/l2-contracts/package.json @@ -33,7 +33,7 @@ }, "scripts": { "build": "hardhat compile", - "test:foundry": "forge test --zksync", + "test:foundry": "forge test --zksync --gas-limit 2000000000", "clean": "hardhat clean", "test": "hardhat test", "verify": "hardhat run src/verify.ts", diff --git a/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol b/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol index 2a408055d..f3af1882d 100644 --- a/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol +++ b/l2-contracts/test/foundry/unit/erc20/L2Erc20BridgeTest.t.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; // solhint-disable gas-custom-errors diff --git a/l2-contracts/test/foundry/unit/utils/Utils.sol b/l2-contracts/test/foundry/unit/utils/Utils.sol index 945026720..609f0f26f 100644 --- a/l2-contracts/test/foundry/unit/utils/Utils.sol +++ b/l2-contracts/test/foundry/unit/utils/Utils.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {Vm} from "forge-std/Vm.sol"; diff --git a/l2-contracts/test/foundry/unit/verifier/Verifier.t.sol b/l2-contracts/test/foundry/unit/verifier/Verifier.t.sol new file mode 100644 index 000000000..5d2a429e4 --- /dev/null +++ b/l2-contracts/test/foundry/unit/verifier/Verifier.t.sol @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {Test} from "forge-std/Test.sol"; + +import {Script, console2 as console} from "forge-std/Script.sol"; + +import {Verifier} from "contracts/verifier/Verifier.sol"; +import {VerifierTest} from "contracts/dev-contracts/VerifierTest.sol"; + +contract VerifierCaller { + Verifier public verifier; + + constructor(Verifier _verifier) { + verifier = _verifier; + } + + function verify( + uint256[] memory publicInputs, + uint256[] memory serializedProof, + uint256[] memory recursiveAggregationInput + ) public view returns (bool result, uint256 gasUsed) { + uint256 gasBefore = gasleft(); + result = verifier.verify(publicInputs, serializedProof, recursiveAggregationInput); + gasUsed = gasBefore - gasleft(); + } +} + +contract VerifierTestTest is Test { + uint256 Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + uint256[] public publicInputs; + uint256[] public serializedProof; + uint256[] public recursiveAggregationInput; + + Verifier public verifier; + + function setUp() public virtual { + publicInputs.push(17257057577815541751225964212897374444694342989384539141520877492729); + + serializedProof.push(10032255692304426541958487424837706541667730769782503366592797609781788557424); + serializedProof.push(11856023086316274558845067687080284266010851703055534566998849536424959073766); + serializedProof.push(1946976494418613232642071265529572704802622739887191787991738703483400525159); + serializedProof.push(1328106069458824013351862477593422369726189688844441245167676630500797673929); + serializedProof.push(15488976127650523079605218040232167291115155239002840072043251018873550258833); + serializedProof.push(4352460820258659596860226525221943504756149602617718032378962471842121872064); + serializedProof.push(10499239305859992443759785453270906003243074359959242371675950941500942473773); + serializedProof.push(21347231097799123231227724221565041889687686131480556177475242020711996173235); + serializedProof.push(21448274562455512652922184359722637546669181231038098300951155169465175447933); + serializedProof.push(5224615512030263722410009061780530125927659699046094954022444377569738464640); + serializedProof.push(457781538876079938778845275495204146302569607395268192839148474821758081582); + serializedProof.push(18861735728246155975127314860333796285284072325207684293054713266899263027595); + serializedProof.push(16303944945368742900183889655415585360236645961122617249176044814801835577336); + serializedProof.push(13035945439947210396602249585896632733250124877036427100939804737514358838409); + serializedProof.push(5344210729159253547334947774998425118220137275601995670629358314205854915831); + serializedProof.push(5798533246034358556434877465898581616792677631188370022078168611592512620805); + serializedProof.push(17389657286129893116489015409587246992530648956814855147744210777822507444908); + serializedProof.push(2287244647342394712608648573347732257083870498255199596324312699868511383792); + serializedProof.push(4008043766112513713076111464601725311991199944328610186851424132679188418647); + serializedProof.push(1192776719848445147414966176395169615865534126881763324071908049917030138759); + serializedProof.push(21297794452895123333253856666749932934399762330444876027734824957603009458926); + serializedProof.push(17125994169200693606182326100834606153690416627082476471630567824088261322122); + serializedProof.push(13696978282153979214307382954559709118587582183649354744253374201589715565327); + serializedProof.push(19885518441500677676836488338931187143852666523909650686513498826535451677070); + serializedProof.push(1205434280320863211046275554464591162919269140938371417889032165323835178587); + serializedProof.push(17633172995805911347980792921300006225132501482343225088847242025756974009163); + serializedProof.push(16438080406761371143473961144300947125022788905488819913014533292593141026205); + serializedProof.push(5069081552536259237104332491140391551180511112980430307676595350165020188468); + serializedProof.push(21217317205917200275887696442048162383709998732382676029165079037795626916156); + serializedProof.push(19474466610515117278975027596198570980840609656738255347763182823792179771539); + serializedProof.push(9744176601826774967534277982058590459006781888895542911226406188087317156914); + serializedProof.push(13171230402193025939763214267878900142876558410430734782028402821166810894141); + serializedProof.push(11775403006142607980192261369108550982244126464568678337528680604943636677964); + serializedProof.push(6903612341636669639883555213872265187697278660090786759295896380793937349335); + serializedProof.push(10197105415769290664169006387603164525075746474380469980600306405504981186043); + serializedProof.push(10143152486514437388737642096964118742712576889537781270260677795662183637771); + serializedProof.push(7662095231333811948165764727904932118187491073896301295018543320499906824310); + serializedProof.push(929422796511992741418500336817719055655694499787310043166783539202506987065); + serializedProof.push(13837024938095280064325737989251964639823205065380219552242839155123572433059); + serializedProof.push(11738888513780631372636453609299803548810759208935038785934252961078387526204); + serializedProof.push(16528875312985292109940444015943812939751717229020635856725059316776921546668); + serializedProof.push(17525167117689648878398809303253004706004801107861280044640132822626802938868); + serializedProof.push(7419167499813234488108910149511390953153207250610705609008080038658070088540); + serializedProof.push(11628425014048216611195735618191126626331446742771562481735017471681943914146); + + verifier = new VerifierTest(); + } + + function testShouldVerify() public view { + bool success = verifier.verify(publicInputs, serializedProof, recursiveAggregationInput); + assert(success); + } + + function testShouldVerifyWithGas() public { + // `gas snapshot` does not work well with zksync setup, so in order to obtain the amount of + // zkevm gas consumed we do the following: + // - Deploy a VerifierCaller contract, which would execute in zkevm context + // - Call the verify function from the VerifierCaller contract and return the gas used + + VerifierCaller caller = new VerifierCaller(verifier); + (bool success, uint256 gasUsed) = caller.verify(publicInputs, serializedProof, recursiveAggregationInput); + assert(success); + + console.log("Gas used: %d", gasUsed); + } + + function testShouldVerifyWithDirtyBits() public view { + uint256[] memory newPublicInputs = publicInputs; + newPublicInputs[0] += uint256(bytes32(0xe000000000000000000000000000000000000000000000000000000000000000)); + + bool success = verifier.verify(newPublicInputs, serializedProof, recursiveAggregationInput); + assert(success); + } + + function testEllipticCurvePointsOverModulo() public view { + uint256[] memory newSerializedProof = serializedProof; + newSerializedProof[0] += Q_MOD; + newSerializedProof[1] += Q_MOD; + newSerializedProof[1] += Q_MOD; + + bool success = verifier.verify(publicInputs, newSerializedProof, recursiveAggregationInput); + assert(success); + } + + function testFrOverModulo() public view { + uint256[] memory newSerializedProof = serializedProof; + newSerializedProof[22] += R_MOD; + + bool success = verifier.verify(publicInputs, newSerializedProof, recursiveAggregationInput); + assert(success); + } + + function testMoreThanOnePublicInput_shouldRevert() public { + uint256[] memory newPublicInputs = new uint256[](2); + newPublicInputs[0] = publicInputs[0]; + newPublicInputs[1] = publicInputs[0]; + + vm.expectRevert(bytes("loadProof: Proof is invalid")); + verifier.verify(newPublicInputs, serializedProof, recursiveAggregationInput); + } + + function testEmptyPublicInput_shouldRevert() public { + uint256[] memory newPublicInputs; + + vm.expectRevert(bytes("loadProof: Proof is invalid")); + verifier.verify(newPublicInputs, serializedProof, recursiveAggregationInput); + } + + function testMoreThan44WordsProof_shouldRevert() public { + uint256[] memory newSerializedProof = new uint256[](serializedProof.length + 1); + + for (uint256 i = 0; i < serializedProof.length; i++) { + newSerializedProof[i] = serializedProof[i]; + } + newSerializedProof[newSerializedProof.length - 1] = serializedProof[serializedProof.length - 1]; + + vm.expectRevert(bytes("loadProof: Proof is invalid")); + verifier.verify(publicInputs, newSerializedProof, recursiveAggregationInput); + } + + function testEmptyProof_shouldRevert() public { + uint256[] memory newSerializedProof; + + vm.expectRevert(bytes("loadProof: Proof is invalid")); + verifier.verify(publicInputs, newSerializedProof, recursiveAggregationInput); + } + + function testNotEmptyRecursiveAggregationInput_shouldRevert() public { + uint256[] memory newRecursiveAggregationInput = publicInputs; + + vm.expectRevert(bytes("loadProof: Proof is invalid")); + verifier.verify(publicInputs, serializedProof, newRecursiveAggregationInput); + } + + function testEllipticCurvePointAtInfinity_shouldRevert() public { + uint256[] memory newSerializedProof = serializedProof; + newSerializedProof[0] = 0; + newSerializedProof[1] = 0; + + vm.expectRevert(bytes("loadProof: Proof is invalid")); + verifier.verify(publicInputs, newSerializedProof, recursiveAggregationInput); + } + + function testInvalidPublicInput_shouldRevert() public { + uint256[] memory newPublicInputs = publicInputs; + newPublicInputs[0] = 0; + + vm.expectRevert(bytes("invalid quotient evaluation")); + verifier.verify(newPublicInputs, serializedProof, recursiveAggregationInput); + } + + function testVerificationKeyHash() public virtual { + bytes32 verificationKeyHash = verifier.verificationKeyHash(); + assertEq(verificationKeyHash, 0x6625fa96781746787b58306d414b1e25bd706d37d883a9b3acf57b2bd5e0de52); + } +} diff --git a/l2-contracts/test/foundry/unit/verifier/VerifierRecursive.t.sol b/l2-contracts/test/foundry/unit/verifier/VerifierRecursive.t.sol new file mode 100644 index 000000000..cf9d1ef69 --- /dev/null +++ b/l2-contracts/test/foundry/unit/verifier/VerifierRecursive.t.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {VerifierTestTest} from "./Verifier.t.sol"; +import {VerifierRecursiveTest} from "contracts/dev-contracts/VerifierRecursiveTest.sol"; + +contract VerifierRecursiveTestTest is VerifierTestTest { + function setUp() public override { + super.setUp(); + + recursiveAggregationInput.push(2257920826825449939414463854743099397427742128922725774525544832270890253504); + recursiveAggregationInput.push(9091218701914748532331969127001446391756173432977615061129552313204917562530); + recursiveAggregationInput.push(16188304989094043810949359833767911976672882599560690320245309499206765021563); + recursiveAggregationInput.push(3201093556796962656759050531176732990872300033146738631772984017549903765305); + + verifier = new VerifierRecursiveTest(); + } + + function testMoreThan4WordsRecursiveInput_shouldRevert() public { + uint256[] memory newRecursiveAggregationInput = new uint256[](recursiveAggregationInput.length + 1); + + for (uint256 i = 0; i < recursiveAggregationInput.length; i++) { + newRecursiveAggregationInput[i] = recursiveAggregationInput[i]; + } + newRecursiveAggregationInput[newRecursiveAggregationInput.length - 1] = recursiveAggregationInput[ + recursiveAggregationInput.length - 1 + ]; + + vm.expectRevert(bytes("loadProof: Proof is invalid")); + verifier.verify(publicInputs, serializedProof, newRecursiveAggregationInput); + } + + function testEmptyRecursiveInput_shouldRevert() public { + uint256[] memory newRecursiveAggregationInput; + + vm.expectRevert(bytes("loadProof: Proof is invalid")); + verifier.verify(publicInputs, serializedProof, newRecursiveAggregationInput); + } + + function testInvalidRecursiveInput_shouldRevert() public { + uint256[] memory newRecursiveAggregationInput = new uint256[](4); + newRecursiveAggregationInput[0] = 1; + newRecursiveAggregationInput[1] = 2; + newRecursiveAggregationInput[2] = 1; + newRecursiveAggregationInput[3] = 2; + + vm.expectRevert(bytes("finalPairing: pairing failure")); + verifier.verify(publicInputs, serializedProof, newRecursiveAggregationInput); + } + + function testVerificationKeyHash() public override { + bytes32 verificationKeyHash = verifier.verificationKeyHash(); + assertEq(verificationKeyHash, 0x88b3ddc4ed85974c7e14297dcad4097169440305c05fdb6441ca8dfd77cd7fa7); + } +} diff --git a/l2-contracts/test/foundry/unit/weth/WETH.t.sol b/l2-contracts/test/foundry/unit/weth/WETH.t.sol index 350bebe06..ab5f8a214 100644 --- a/l2-contracts/test/foundry/unit/weth/WETH.t.sol +++ b/l2-contracts/test/foundry/unit/weth/WETH.t.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; diff --git a/tools/README.md b/tools/README.md index 081ab8d70..a49cf4c73 100644 --- a/tools/README.md +++ b/tools/README.md @@ -7,3 +7,11 @@ To generate the verifier from the scheduler key in 'data' directory, just run: ```shell cargo run --bin zksync_verifier_contract_generator --release -- --input_path data/scheduler_key.json --output_path ../l1-contracts/contracts/state-transition/Verifier.sol ``` + +## L2 mode + +At the time of this writing, `modexp` precompile is not present on zkSync Era. In order to deploy the verifier on top of a ZK Chain, a different version has to be used with custom implementation of modular exponentiation. + +```shell +cargo run --bin zksync_verifier_contract_generator --release -- --input_path data/scheduler_key.json --output_path ../l2-contracts/contracts/verifier/Verifier.sol --l2_mode +``` diff --git a/tools/data/verifier_contract_template.txt b/tools/data/verifier_contract_template.txt index 5ef32b2c5..b9290f83c 100644 --- a/tools/data/verifier_contract_template.txt +++ b/tools/data/verifier_contract_template.txt @@ -309,18 +309,7 @@ contract Verifier is IVerifier { } /// @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD. - function modexp(value, power) -> res { - mstore(0x00, 0x20) - mstore(0x20, 0x20) - mstore(0x40, 0x20) - mstore(0x60, value) - mstore(0x80, power) - mstore(0xa0, R_MOD) - if iszero(staticcall(gas(), 5, 0, 0xc0, 0x00, 0x20)) { - revertWithMessage(24, "modexp precompile failed") - } - res := mload(0x00) - } + {{modexp_function}} /// @dev Performs a point multiplication operation and stores the result in a given memory destination. function pointMulIntoDest(point, s, dest) { diff --git a/tools/src/main.rs b/tools/src/main.rs index 746373fe4..4da69d921 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -115,6 +115,10 @@ struct Opt { /// Output path to verifier contract file. #[structopt(short = "o", long = "output_path", default_value = "data/Verifier.sol")] output_path: String, + + /// The Verifier is to be compiled for an L2 network, where modexp precompile is not available. + #[structopt(short = "l2", long = "l2_mode")] + l2_mode: bool, } fn main() -> Result<(), Box> { @@ -135,7 +139,7 @@ fn main() -> Result<(), Box> { let vk_hash = hex::encode(calculate_verification_key_hash(verification_key).to_fixed_bytes()); let verifier_contract_template = - insert_residue_elements_and_commitments(&verifier_contract_template, &vk, &vk_hash)?; + insert_residue_elements_and_commitments(&verifier_contract_template, &vk, &vk_hash, opt.l2_mode)?; let mut file = File::create(opt.output_path)?; @@ -147,6 +151,7 @@ fn insert_residue_elements_and_commitments( template: &str, vk: &HashMap, vk_hash: &str, + l2_mode: bool, ) -> Result> { let reg = Handlebars::new(); let residue_g2_elements = generate_residue_g2_elements(vk); @@ -155,11 +160,16 @@ fn insert_residue_elements_and_commitments( let verifier_contract_template = template.replace("{{residue_g2_elements}}", &residue_g2_elements); + let modexp_function = get_modexp_function(l2_mode); + let verifier_contract_template = verifier_contract_template.replace("{{modexp_function}}", &modexp_function); + + Ok(reg.render_template( &verifier_contract_template, &json!({"residue_g2_elements": residue_g2_elements, "commitments": commitments, - "vk_hash": vk_hash}), + "vk_hash": vk_hash, + "modexp_function": modexp_function}), )?) } @@ -334,3 +344,37 @@ fn generate_residue_g2_elements(vk: &HashMap) -> String { residue_g2_elements } + + +fn get_modexp_function(l2_mode: bool) -> String { + if l2_mode { + r#"function modexp(value, power) -> res { + res := 1 + for { + + } gt(power, 0) { + + } { + if mod(power, 2) { + res := mulmod(res, value, R_MOD) + } + value := mulmod(value, value, R_MOD) + power := shr(1, power) + } + }"#.to_string() + } else { + r#"function modexp(value, power) -> res { + mstore(0x00, 0x20) + mstore(0x20, 0x20) + mstore(0x40, 0x20) + mstore(0x60, value) + mstore(0x80, power) + mstore(0xa0, R_MOD) + if iszero(staticcall(gas(), 5, 0, 0xc0, 0x00, 0x20)) { + revertWithMessage(24, "modexp precompile failed") + } + res := mload(0x00) + }"#.to_string() + } +} + From 840df354f871c18a3796590b094ce118725468db Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Fri, 30 Aug 2024 12:41:32 +0200 Subject: [PATCH 49/51] fix comile --- .../contracts/dev-contracts/test/DummySharedBridge.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol b/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol index cd77eff8a..b346b2819 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol @@ -7,8 +7,8 @@ import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol"; import {TWO_BRIDGES_MAGIC_VALUE, ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; -import {IL1NativeTokenVault} from "../../bridge/L1NativeTokenVault.sol"; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../../common/L2ContractAddresses.sol"; +import {IL1NativeTokenVault} from "../../bridge/interfaces/IL1NativeTokenVault.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../../common/L2ContractAddresses.sol"; import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; import {IL2Bridge} from "../../bridge/interfaces/IL2Bridge.sol"; import {IL2BridgeLegacy} from "../../bridge/interfaces/IL2BridgeLegacy.sol"; From 9fa2d6623c52c3c944bf6b7be8644981aed9d8a1 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Fri, 30 Aug 2024 13:06:20 +0200 Subject: [PATCH 50/51] fix lint --- l1-contracts/contracts/bridgehub/MessageRoot.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/bridgehub/MessageRoot.sol b/l1-contracts/contracts/bridgehub/MessageRoot.sol index 16b7ff7f5..650d88e47 100644 --- a/l1-contracts/contracts/bridgehub/MessageRoot.sol +++ b/l1-contracts/contracts/bridgehub/MessageRoot.sol @@ -91,7 +91,7 @@ contract MessageRoot is IMessageRoot, ReentrancyGuard { function chainRegistered(uint256 _chainId) public view returns (bool) { return (_chainId == block.chainid || chainIndex[_chainId] != 0); } - + /// @dev add a new chainBatchRoot to the chainTree function addChainBatchRoot( uint256 _chainId, From f469e7c2cf7fb4c10f1e87ea32f582cdc2056c8d Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Fri, 30 Aug 2024 15:07:33 +0200 Subject: [PATCH 51/51] fix script --- l1-contracts/scripts/sync-layer.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/l1-contracts/scripts/sync-layer.ts b/l1-contracts/scripts/sync-layer.ts index d440044d3..e51a6da3c 100644 --- a/l1-contracts/scripts/sync-layer.ts +++ b/l1-contracts/scripts/sync-layer.ts @@ -395,10 +395,8 @@ async function registerSLContractsOnL1(deployer: Deployer) { refundRecipient: deployer.deployWallet.address, secondBridgeAddress: stmDeploymentTracker.address, secondBridgeValue: 0, - secondBridgeCalldata: ethers.utils.defaultAbiCoder.encode( - ["address", "address"], - [l1STM.address, l2STMAddress] - ), + secondBridgeCalldata: + "0x01" + ethers.utils.defaultAbiCoder.encode(["address", "address"], [l1STM.address, l2STMAddress]).slice(2), }, ]) );