diff --git a/op-e2e/e2eutils/interop/contracts/src/ICrossL2Inbox.sol b/op-e2e/e2eutils/interop/contracts/src/ICrossL2Inbox.sol index e495c13f61732..900b01b09e7cc 100644 --- a/op-e2e/e2eutils/interop/contracts/src/ICrossL2Inbox.sol +++ b/op-e2e/e2eutils/interop/contracts/src/ICrossL2Inbox.sol @@ -1,6 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +/// @notice The struct for a pointer to a message payload in a remote (or local) chain. +/// @custom:field origin The origin address of the message. +/// @custom:field blockNumber The block number of the message. +/// @custom:field logIndex The log index of the message. +/// @custom:field timestamp The timestamp of the message. +/// @custom:field chainId The origin chain ID of the message. struct Identifier { address origin; uint256 blockNumber; @@ -9,15 +15,43 @@ struct Identifier { uint256 chainId; } -/// @title ICrossL2Inbox -/// @notice Interface for the CrossL2Inbox contract. interface ICrossL2Inbox { + /// @notice Thrown when trying to execute a cross chain message on a deposit transaction. + error NoExecutingDeposits(); - /// @notice Validates a cross chain message on the destination chain - /// and emits an ExecutingMessage event. This function is useful - /// for applications that understand the schema of the _message payload and want to - /// process it in a custom way. - /// @param _id Identifier of the message. - /// @param _msgHash Hash of the message payload to call target with. + /// @notice Thrown when trying to validate a cross chain message with an identifier checksum that is + /// invalid or was not provided in the transaction's access list to set the slot as warm. + error NotInAccessList(); + + /// @notice Thrown when trying to validate a cross chain message with a block number that is + /// greater than 2^64. + error BlockNumberTooHigh(); + + /// @notice Thrown when trying to validate a cross chain message with a timestamp that is + /// greater than 2^64. + error TimestampTooHigh(); + + /// @notice Thrown when trying to validate a cross chain message with a log index that is + /// greater than 2^32. + error LogIndexTooHigh(); + + /// @notice Emitted when a message is being executed. + event ExecutingMessage(bytes32 indexed msgHash, Identifier id); + + /// @notice Returns the semantic version of the contract. + /// @return version_ The semantic version. + function version() external view returns (string memory version_); + + /// @notice Validates a message by checking that the identifier checksum slot is warm. + /// @dev To process the message, the tx must include the checksum composed by the message's + /// identifier and msgHash in the access list. + /// @param _id The identifier of the message. + /// @param _msgHash The hash of the message. function validateMessage(Identifier calldata _id, bytes32 _msgHash) external; + + /// @notice Calculates the checksum of an identifier and message hash. + /// @param _id The identifier of the message. + /// @param _msgHash The hash of the message. + /// @return checksum_ The checksum of the identifier and message hash. + function calculateChecksum(Identifier memory _id, bytes32 _msgHash) external pure returns (bytes32 checksum_); } diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index ea060f996456f..2260a2ebf4799 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -138,13 +138,6 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex } var afterForceIncludeTxs []hexutil.Bytes - if ba.rollupCfg.IsInterop(nextL2Time) { - depositsCompleteTx, err := DepositsCompleteBytes(seqNumber, l1Info) - if err != nil { - return nil, NewCriticalError(fmt.Errorf("failed to create depositsCompleteTx: %w", err)) - } - afterForceIncludeTxs = append(afterForceIncludeTxs, depositsCompleteTx) - } txs := make([]hexutil.Bytes, 0, 1+len(depositTxs)+len(afterForceIncludeTxs)+len(upgradeTxs)) txs = append(txs, l1InfoTx) diff --git a/op-node/rollup/derive/attributes_test.go b/op-node/rollup/derive/attributes_test.go index cf42e81696b46..2101b1e8bbab3 100644 --- a/op-node/rollup/derive/attributes_test.go +++ b/op-node/rollup/derive/attributes_test.go @@ -226,13 +226,11 @@ func TestPreparePayloadAttributes(t *testing.T) { epoch := l1Info.ID() l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, seqNumber, l1Info, 0) require.NoError(t, err) - depositsComplete, err := DepositsCompleteBytes(seqNumber, l1Info) require.NoError(t, err) var l2Txs []eth.Data l2Txs = append(l2Txs, l1InfoTx) l2Txs = append(l2Txs, userDepositTxs...) - l2Txs = append(l2Txs, depositsComplete) l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, receipts, nil) attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher) @@ -242,8 +240,7 @@ func TestPreparePayloadAttributes(t *testing.T) { require.Equal(t, l2Parent.Time+cfg.BlockTime, uint64(attrs.Timestamp)) require.Equal(t, eth.Bytes32(l1Info.InfoMixDigest), attrs.PrevRandao) require.Equal(t, predeploys.SequencerFeeVaultAddr, attrs.SuggestedFeeRecipient) - require.Equal(t, len(l2Txs), len(attrs.Transactions), "Expected txs to equal l1 info tx + user deposit txs + DepositsComplete") - require.Equal(t, eth.Data(depositsComplete).String(), attrs.Transactions[len(l2Txs)-1].String()) + require.Equal(t, len(l2Txs), len(attrs.Transactions), "Expected txs to equal l1 info tx + user deposit txs") require.Equal(t, l2Txs, attrs.Transactions) require.True(t, attrs.NoTxPool) }) @@ -267,12 +264,10 @@ func TestPreparePayloadAttributes(t *testing.T) { epoch := l1Info.ID() l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, seqNumber, l1Info, 0) require.NoError(t, err) - depositsComplete, err := DepositsCompleteBytes(seqNumber, l1Info) require.NoError(t, err) var l2Txs []eth.Data l2Txs = append(l2Txs, l1InfoTx) - l2Txs = append(l2Txs, depositsComplete) l1Fetcher.ExpectInfoByHash(epoch.Hash, l1Info, nil) attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher) @@ -282,8 +277,7 @@ func TestPreparePayloadAttributes(t *testing.T) { require.Equal(t, l2Parent.Time+cfg.BlockTime, uint64(attrs.Timestamp)) require.Equal(t, eth.Bytes32(l1Info.InfoMixDigest), attrs.PrevRandao) require.Equal(t, predeploys.SequencerFeeVaultAddr, attrs.SuggestedFeeRecipient) - require.Equal(t, len(l2Txs), len(attrs.Transactions), "Expected txs to equal l1 info tx + user deposit txs + DepositsComplete") - require.Equal(t, eth.Data(depositsComplete).String(), attrs.Transactions[len(l2Txs)-1].String()) + require.Equal(t, len(l2Txs), len(attrs.Transactions), "Expected txs to equal l1 info tx + user deposit txs") require.Equal(t, l2Txs, attrs.Transactions) require.True(t, attrs.NoTxPool) }) diff --git a/op-node/rollup/derive/fuzz_parsers_test.go b/op-node/rollup/derive/fuzz_parsers_test.go index f8f2374e6b301..5b435f4563edd 100644 --- a/op-node/rollup/derive/fuzz_parsers_test.go +++ b/op-node/rollup/derive/fuzz_parsers_test.go @@ -93,14 +93,6 @@ func FuzzL1InfoEcotoneRoundTrip(f *testing.F) { if !cmp.Equal(in, out, cmp.Comparer(testutils.BigEqual)) { t.Fatalf("The Ecotone data did not round trip correctly. in: %v. out: %v", in, out) } - enc, err = in.marshalBinaryInterop() - if err != nil { - t.Fatalf("Failed to marshal Interop binary: %v", err) - } - err = out.unmarshalBinaryInterop(enc) - if err != nil { - t.Fatalf("Failed to unmarshal Interop binary: %v", err) - } if !cmp.Equal(in, out, cmp.Comparer(testutils.BigEqual)) { t.Fatalf("The Interop data did not round trip correctly. in: %v. out: %v", in, out) } diff --git a/op-node/rollup/derive/l1_block_info.go b/op-node/rollup/derive/l1_block_info.go index 36bcba9095543..ddeb35c5cdb24 100644 --- a/op-node/rollup/derive/l1_block_info.go +++ b/op-node/rollup/derive/l1_block_info.go @@ -21,27 +21,16 @@ const ( L1InfoFuncBedrockSignature = "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)" L1InfoFuncEcotoneSignature = "setL1BlockValuesEcotone()" L1InfoFuncIsthmusSignature = "setL1BlockValuesIsthmus()" - L1InfoFuncInteropSignature = "setL1BlockValuesInterop()" - DepositsCompleteSignature = "depositsComplete()" L1InfoArguments = 8 L1InfoBedrockLen = 4 + 32*L1InfoArguments L1InfoEcotoneLen = 4 + 32*5 // after Ecotone upgrade, args are packed into 5 32-byte slots L1InfoIsthmusLen = 4 + 32*5 + 4 + 8 // after Isthmus upgrade, additionally pack in operator fee scalar and constant - DepositsCompleteLen = 4 // only the selector - // DepositsCompleteGas allocates 21k gas for intrinsic tx costs, and - // an additional 15k to ensure that the DepositsComplete call does not run out of gas. - // GasBenchMark_L1BlockInterop_DepositsComplete:test_depositsComplete_benchmark() (gas: 7768) - // GasBenchMark_L1BlockInterop_DepositsComplete_Warm:test_depositsComplete_benchmark() (gas: 5768) - // see `test_depositsComplete_benchmark` at: `/packages/contracts-bedrock/test/BenchmarkTest.t.sol` - DepositsCompleteGas = uint64(21_000 + 15_000) ) var ( L1InfoFuncBedrockBytes4 = crypto.Keccak256([]byte(L1InfoFuncBedrockSignature))[:4] L1InfoFuncEcotoneBytes4 = crypto.Keccak256([]byte(L1InfoFuncEcotoneSignature))[:4] L1InfoFuncIsthmusBytes4 = crypto.Keccak256([]byte(L1InfoFuncIsthmusSignature))[:4] - L1InfoFuncInteropBytes4 = crypto.Keccak256([]byte(L1InfoFuncInteropSignature))[:4] - DepositsCompleteBytes4 = crypto.Keccak256([]byte(DepositsCompleteSignature))[:4] L1InfoDepositerAddress = common.HexToAddress("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001") L1BlockAddress = predeploys.L1BlockAddr ErrInvalidFormat = errors.New("invalid ecotone l1 block info format") @@ -288,14 +277,6 @@ func (info *L1BlockInfo) marshalBinaryIsthmus() ([]byte, error) { return out, nil } -func (info *L1BlockInfo) marshalBinaryInterop() ([]byte, error) { - out, err := marshalBinaryWithSignature(info, L1InfoFuncInteropBytes4) - if err != nil { - return nil, fmt.Errorf("failed to marshal Interop l1 block info: %w", err) - } - return out, nil -} - func marshalBinaryWithSignature(info *L1BlockInfo, signature []byte) ([]byte, error) { w := bytes.NewBuffer(make([]byte, 0, L1InfoIsthmusLen)) if err := solabi.WriteSignature(w, signature); err != nil { @@ -342,10 +323,6 @@ func marshalBinaryWithSignature(info *L1BlockInfo, signature []byte) ([]byte, er return w.Bytes(), nil } -func (info *L1BlockInfo) unmarshalBinaryInterop(data []byte) error { - return unmarshalBinaryWithSignatureAndData(info, L1InfoFuncInteropBytes4, data) -} - func unmarshalBinaryWithSignatureAndData(info *L1BlockInfo, signature []byte, data []byte) error { if len(data) != L1InfoIsthmusLen { return fmt.Errorf("data is unexpected length: %d", len(data)) @@ -406,16 +383,6 @@ func isEcotoneButNotFirstBlock(rollupCfg *rollup.Config, l2Timestamp uint64) boo return rollupCfg.IsEcotone(l2Timestamp) && !rollupCfg.IsEcotoneActivationBlock(l2Timestamp) } -// isInteropButNotFirstBlock returns whether the specified block is subject to the Interop upgrade, -// but is not the activation block itself. -func isInteropButNotFirstBlock(rollupCfg *rollup.Config, l2Timestamp uint64) bool { - // Since we use the pre-interop L1 tx one last time during the upgrade block, - // we must disallow the deposit-txs from using the CrossL2Inbox during this block. - // If the CrossL2Inbox does not exist yet, then it is safe, - // but we have to ensure that the spec and code puts any Interop upgrade-txs after the user deposits. - return rollupCfg.IsInterop(l2Timestamp) && !rollupCfg.IsInteropActivationBlock(l2Timestamp) -} - // isIsthmusButNotFirstBlock returns whether the specified block is subject to the Isthmus upgrade, // but is not the activation block itself. func isIsthmusButNotFirstBlock(rollupCfg *rollup.Config, l2Timestamp uint64) bool { @@ -426,9 +393,6 @@ func isIsthmusButNotFirstBlock(rollupCfg *rollup.Config, l2Timestamp uint64) boo func L1BlockInfoFromBytes(rollupCfg *rollup.Config, l2BlockTime uint64, data []byte) (*L1BlockInfo, error) { var info L1BlockInfo // Important, this should be ordered from most recent to oldest - if isInteropButNotFirstBlock(rollupCfg, l2BlockTime) { - return &info, info.unmarshalBinaryInterop(data) - } if isIsthmusButNotFirstBlock(rollupCfg, l2BlockTime) { return &info, info.unmarshalBinaryIsthmus(data) } @@ -483,27 +447,20 @@ func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber l1BlockInfo.OperatorFeeConstant = operatorFee.Constant } - if isInteropButNotFirstBlock(rollupCfg, l2Timestamp) { - out, err := l1BlockInfo.marshalBinaryInterop() + if isIsthmusActivated { + out, err := l1BlockInfo.marshalBinaryIsthmus() if err != nil { - return nil, fmt.Errorf("failed to marshal Interop l1 block info: %w", err) + return nil, fmt.Errorf("failed to marshal Isthmus l1 block info: %w", err) } data = out } else { - if isIsthmusActivated { - out, err := l1BlockInfo.marshalBinaryIsthmus() - if err != nil { - return nil, fmt.Errorf("failed to marshal Isthmus l1 block info: %w", err) - } - data = out - } else { - out, err := l1BlockInfo.marshalBinaryEcotone() - if err != nil { - return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err) - } - data = out + out, err := l1BlockInfo.marshalBinaryEcotone() + if err != nil { + return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err) } + data = out } + } else { l1BlockInfo.L1FeeOverhead = sysCfg.Overhead l1BlockInfo.L1FeeScalar = sysCfg.Scalar @@ -551,34 +508,3 @@ func L1InfoDepositBytes(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNu } return opaqueL1Tx, nil } - -func DepositsCompleteDeposit(seqNumber uint64, block eth.BlockInfo) (*types.DepositTx, error) { - source := AfterForceIncludeSource{ - L1BlockHash: block.Hash(), - SeqNumber: seqNumber, - } - out := &types.DepositTx{ - SourceHash: source.SourceHash(), - From: L1InfoDepositerAddress, - To: &L1BlockAddress, - Mint: nil, - Value: big.NewInt(0), - Gas: DepositsCompleteGas, - IsSystemTransaction: false, - Data: DepositsCompleteBytes4, - } - return out, nil -} - -func DepositsCompleteBytes(seqNumber uint64, l1Info eth.BlockInfo) ([]byte, error) { - dep, err := DepositsCompleteDeposit(seqNumber, l1Info) - if err != nil { - return nil, fmt.Errorf("failed to create DepositsComplete tx: %w", err) - } - depositsCompleteTx := types.NewTx(dep) - opaqueDepositsCompleteTx, err := depositsCompleteTx.MarshalBinary() - if err != nil { - return nil, fmt.Errorf("failed to encode DepositsComplete tx: %w", err) - } - return opaqueDepositsCompleteTx, nil -} diff --git a/op-node/rollup/derive/l1_block_info_test.go b/op-node/rollup/derive/l1_block_info_test.go index 03e1b4ec65280..c634a9ae4ea25 100644 --- a/op-node/rollup/derive/l1_block_info_test.go +++ b/op-node/rollup/derive/l1_block_info_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -205,7 +204,6 @@ func TestParseL1InfoDepositTxData(t *testing.T) { require.False(t, depTx.IsSystemTransaction) require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas)) require.Equal(t, L1InfoIsthmusLen, len(depTx.Data), "the length is same in interop") - require.Equal(t, L1InfoFuncInteropBytes4, depTx.Data[:4], "upgrade is active, need interop signature") }) t.Run("activation-block interop", func(t *testing.T) { rng := rand.New(rand.NewSource(1234)) @@ -234,39 +232,3 @@ func TestParseL1InfoDepositTxData(t *testing.T) { require.Equal(t, L1InfoIsthmusLen, len(depTx.Data)) }) } - -func TestDepositsCompleteBytes(t *testing.T) { - randomSeqNr := func(rng *rand.Rand) uint64 { - return rng.Uint64() - } - t.Run("valid return bytes", func(t *testing.T) { - rng := rand.New(rand.NewSource(1234)) - info := testutils.MakeBlockInfo(nil)(rng) - depTxByes, err := DepositsCompleteBytes(randomSeqNr(rng), info) - require.NoError(t, err) - var depTx types.Transaction - require.NoError(t, depTx.UnmarshalBinary(depTxByes)) - require.Equal(t, uint8(types.DepositTxType), depTx.Type()) - require.Equal(t, depTx.Data(), DepositsCompleteBytes4) - require.Equal(t, DepositsCompleteLen, len(depTx.Data())) - require.Equal(t, DepositsCompleteGas, depTx.Gas()) - require.False(t, depTx.IsSystemTx()) - require.Equal(t, depTx.Value(), big.NewInt(0)) - signer := types.LatestSignerForChainID(depTx.ChainId()) - sender, err := signer.Sender(&depTx) - require.NoError(t, err) - require.Equal(t, L1InfoDepositerAddress, sender) - }) - t.Run("valid return Transaction", func(t *testing.T) { - rng := rand.New(rand.NewSource(1234)) - info := testutils.MakeBlockInfo(nil)(rng) - depTx, err := DepositsCompleteDeposit(randomSeqNr(rng), info) - require.NoError(t, err) - require.Equal(t, depTx.Data, DepositsCompleteBytes4) - require.Equal(t, DepositsCompleteLen, len(depTx.Data)) - require.Equal(t, DepositsCompleteGas, depTx.Gas) - require.False(t, depTx.IsSystemTransaction) - require.Equal(t, depTx.Value, big.NewInt(0)) - require.Equal(t, L1InfoDepositerAddress, depTx.From) - }) -} diff --git a/packages/contracts-bedrock/interfaces/L2/ICrossL2Inbox.sol b/packages/contracts-bedrock/interfaces/L2/ICrossL2Inbox.sol index 646bb699be67b..4c6237752c0ff 100644 --- a/packages/contracts-bedrock/interfaces/L2/ICrossL2Inbox.sol +++ b/packages/contracts-bedrock/interfaces/L2/ICrossL2Inbox.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +/// @notice Identifier of a cross chain message. + struct Identifier { address origin; uint256 blockNumber; @@ -9,21 +11,16 @@ struct Identifier { uint256 chainId; } -/// @title ICrossL2Inbox -/// @notice Interface for the CrossL2Inbox contract. interface ICrossL2Inbox { - /// @notice Thrown when trying to execute a cross chain message on a deposit transaction. error NoExecutingDeposits(); + error NotInAccessList(); + error BlockNumberTooHigh(); + error TimestampTooHigh(); + error LogIndexTooHigh(); event ExecutingMessage(bytes32 indexed msgHash, Identifier id); function version() external view returns (string memory); - /// @notice Validates a cross chain message on the destination chain - /// and emits an ExecutingMessage event. This function is useful - /// for applications that understand the schema of the _message payload and want to - /// process it in a custom way. - /// @param _id Identifier of the message. - /// @param _msgHash Hash of the message payload to call target with. function validateMessage(Identifier calldata _id, bytes32 _msgHash) external; } diff --git a/packages/contracts-bedrock/interfaces/L2/IDependencySet.sol b/packages/contracts-bedrock/interfaces/L2/IDependencySet.sol deleted file mode 100644 index fb294bee8d197..0000000000000 --- a/packages/contracts-bedrock/interfaces/L2/IDependencySet.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/// @title IDependencySet -/// @notice Interface for L1Block with only `isInDependencySet(uint256)` method. -interface IDependencySet { - /// @notice Returns true if the chain associated with input chain ID is in the interop dependency set. - /// Every chain is in the interop dependency set of itself. - /// @param _chainId Input chain ID. - /// @return True if the input chain ID corresponds to a chain in the interop dependency set, and false otherwise. - function isInDependencySet(uint256 _chainId) external view returns (bool); -} diff --git a/packages/contracts-bedrock/interfaces/L2/IL1BlockInterop.sol b/packages/contracts-bedrock/interfaces/L2/IL1BlockInterop.sol deleted file mode 100644 index 689cac0f9d01b..0000000000000 --- a/packages/contracts-bedrock/interfaces/L2/IL1BlockInterop.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface IL1BlockInterop { - error NotCrossL2Inbox(); - error NotDepositor(); - - function DEPOSITOR_ACCOUNT() external pure returns (address addr_); - function baseFeeScalar() external view returns (uint32); - function basefee() external view returns (uint256); - function batcherHash() external view returns (bytes32); - function blobBaseFee() external view returns (uint256); - function blobBaseFeeScalar() external view returns (uint32); - function depositsComplete() external; - function gasPayingToken() external pure returns (address addr_, uint8 decimals_); - function gasPayingTokenName() external pure returns (string memory name_); - function gasPayingTokenSymbol() external pure returns (string memory symbol_); - function hash() external view returns (bytes32); - function isCustomGasToken() external pure returns (bool is_); - function isDeposit() external view returns (bool isDeposit_); - function l1FeeOverhead() external view returns (uint256); - function l1FeeScalar() external view returns (uint256); - function number() external view returns (uint64); - function operatorFeeScalar() external view returns (uint32); - function operatorFeeConstant() external view returns (uint64); - function sequenceNumber() external view returns (uint64); - function setL1BlockValues( - uint64 _number, - uint64 _timestamp, - uint256 _basefee, - bytes32 _hash, - uint64 _sequenceNumber, - bytes32 _batcherHash, - uint256 _l1FeeOverhead, - uint256 _l1FeeScalar - ) - external; - function setL1BlockValuesEcotone() external; - function setL1BlockValuesInterop() external; - function setL1BlockValuesIsthmus() external; - function timestamp() external view returns (uint64); - function version() external pure returns (string memory); - - function __constructor__() external; -} diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index 7397388dec2d0..e8463c0dec814 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -395,16 +395,9 @@ contract L2Genesis is Deployer { /// @notice This predeploy is following the safety invariant #1. function setL1Block() public { - if (cfg.useInterop()) { - string memory cname = "L1BlockInterop"; - address impl = Predeploys.predeployToCodeNamespace(Predeploys.L1_BLOCK_ATTRIBUTES); - console.log("Setting %s implementation at: %s", cname, impl); - vm.etch(impl, vm.getDeployedCode(string.concat(cname, ".sol:", cname))); - } else { - _setImplementationCode(Predeploys.L1_BLOCK_ATTRIBUTES); - // Note: L1 block attributes are set to 0. - // Before the first user-tx the state is overwritten with actual L1 attributes. - } + // Note: L1 block attributes are set to 0. + // Before the first user-tx the state is overwritten with actual L1 attributes. + _setImplementationCode(Predeploys.L1_BLOCK_ATTRIBUTES); } /// @notice This predeploy is following the safety invariant #1. diff --git a/packages/contracts-bedrock/scripts/checks/check-frozen-files.sh b/packages/contracts-bedrock/scripts/checks/check-frozen-files.sh index 21d511854351f..4d39c462648aa 100755 --- a/packages/contracts-bedrock/scripts/checks/check-frozen-files.sh +++ b/packages/contracts-bedrock/scripts/checks/check-frozen-files.sh @@ -63,7 +63,6 @@ ALLOWED_FILES=( "src/L2/ETHLiquidity.sol:ETHLiquidity" "src/L2/GasPriceOracle.sol:GasPriceOracle" "src/L2/L1Block.sol:L1Block" - "src/L2/L1BlockInterop.sol:L1BlockInterop" "src/L2/L1FeeVault.sol:L1FeeVault" "src/L2/L2CrossDomainMessenger.sol:L2CrossDomainMessenger" "src/L2/L2ERC721Bridge.sol:L2ERC721Bridge" diff --git a/packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json b/packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json index b89344e51049e..2b29db32fb59d 100644 --- a/packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json +++ b/packages/contracts-bedrock/snapshots/abi/CrossL2Inbox.json @@ -103,9 +103,29 @@ "name": "ExecutingMessage", "type": "event" }, + { + "inputs": [], + "name": "BlockNumberTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "LogIndexTooHigh", + "type": "error" + }, { "inputs": [], "name": "NoExecutingDeposits", "type": "error" + }, + { + "inputs": [], + "name": "NotInAccessList", + "type": "error" + }, + { + "inputs": [], + "name": "TimestampTooHigh", + "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json b/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json deleted file mode 100644 index 1a9678f1718ef..0000000000000 --- a/packages/contracts-bedrock/snapshots/abi/L1BlockInterop.json +++ /dev/null @@ -1,353 +0,0 @@ -[ - { - "inputs": [], - "name": "DEPOSITOR_ACCOUNT", - "outputs": [ - { - "internalType": "address", - "name": "addr_", - "type": "address" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "baseFeeScalar", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "basefee", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "batcherHash", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "blobBaseFee", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "blobBaseFeeScalar", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "depositsComplete", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "gasPayingToken", - "outputs": [ - { - "internalType": "address", - "name": "addr_", - "type": "address" - }, - { - "internalType": "uint8", - "name": "decimals_", - "type": "uint8" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "gasPayingTokenName", - "outputs": [ - { - "internalType": "string", - "name": "name_", - "type": "string" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "gasPayingTokenSymbol", - "outputs": [ - { - "internalType": "string", - "name": "symbol_", - "type": "string" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "hash", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isCustomGasToken", - "outputs": [ - { - "internalType": "bool", - "name": "is_", - "type": "bool" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "isDeposit", - "outputs": [ - { - "internalType": "bool", - "name": "isDeposit_", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "l1FeeOverhead", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "l1FeeScalar", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "number", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "operatorFeeConstant", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "operatorFeeScalar", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "sequenceNumber", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "_number", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "_timestamp", - "type": "uint64" - }, - { - "internalType": "uint256", - "name": "_basefee", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_hash", - "type": "bytes32" - }, - { - "internalType": "uint64", - "name": "_sequenceNumber", - "type": "uint64" - }, - { - "internalType": "bytes32", - "name": "_batcherHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_l1FeeOverhead", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_l1FeeScalar", - "type": "uint256" - } - ], - "name": "setL1BlockValues", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "setL1BlockValuesEcotone", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "setL1BlockValuesInterop", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "setL1BlockValuesIsthmus", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "timestamp", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "NotCrossL2Inbox", - "type": "error" - }, - { - "inputs": [], - "name": "NotDepositor", - "type": "error" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index afb7d99f5f582..8be457768ac81 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -44,8 +44,8 @@ "sourceCodeHash": "0xfa56426153227e798150f6becc30a33fd20a3c6e0d73c797a3922dd631acbb57" }, "src/L2/CrossL2Inbox.sol:CrossL2Inbox": { - "initCodeHash": "0x1308d4d3ebd857c6df9901fccc1d2ffc10f1d17b0cc1b14f58135f9ea9cad7a3", - "sourceCodeHash": "0xb397f483d1d1e5bd15ab0184e9d994f662393c51e9f6d524bfc45e5f1d935601" + "initCodeHash": "0xa7d89c648e0dd2f0a95780feb7ce7674dbd35a1c2de354a0222da7cef8b596c6", + "sourceCodeHash": "0xe001c9d4cf55e90c227459573b7eaac6015dea6cd9622ebf117e9ecba54cb4cc" }, "src/L2/ETHLiquidity.sol:ETHLiquidity": { "initCodeHash": "0xfcb50f32bbbd7e426f83c5c320ea655b896c026324a5e51983c1b15041ef88ca", @@ -59,10 +59,6 @@ "initCodeHash": "0xa1f984b8ea199574261c19122b5a9c8c7dbd3633980b1e7aaf6b7af24af60478", "sourceCodeHash": "0xd04d64355dcf55247ac937748518e7f9620ae3f9eabe80fae9a82c0115ed77bc" }, - "src/L2/L1BlockInterop.sol:L1BlockInterop": { - "initCodeHash": "0xa19bc53228445924ae016d499e42ad5bcc33f45e7a3b0c51e2f930ffb8dacd7a", - "sourceCodeHash": "0xe959a150ea9ef814b13cdba6b9e9a4bf991e5c1325aa22c49795ce06cc35a8cd" - }, "src/L2/L1FeeVault.sol:L1FeeVault": { "initCodeHash": "0x6745b7be3895a5e8d373df0066d931bae29c47672ac46c2f5829bd0052cc6d9e", "sourceCodeHash": "0xd0471c328c1d17c5863261322bf8d5aff2e7e9e3a1135631a993aa75667621df" @@ -88,8 +84,8 @@ "sourceCodeHash": "0xaef8ea36c5b78cd12e0e62811d51db627ccf0dfd2cc5479fb707a10ef0d42048" }, "src/L2/L2ToL2CrossDomainMessenger.sol:L2ToL2CrossDomainMessenger": { - "initCodeHash": "0x2a6f9f717ca67d7920af1135a048e6cc3f1f655edc9aa72b6e00f23730319695", - "sourceCodeHash": "0x21590b7204311d006de0044a7c8f1c92e5468276e6db29e13c6103454c39c6d1" + "initCodeHash": "0x7ad26c308c506ca65e080c8a33eceac48201221cd291030844021cf1cfde4871", + "sourceCodeHash": "0xe59eec8a64d6c42d9cc2a67aa649718cad590961f92787d62435bf8cde93f7f3" }, "src/L2/OperatorFeeVault.sol:OperatorFeeVault": { "initCodeHash": "0x3d8c0d7736e8767f2f797da1c20c5fe30bd7f48a4cf75f376290481ad7c0f91f", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json deleted file mode 100644 index 2c23f06367859..0000000000000 --- a/packages/contracts-bedrock/snapshots/storageLayout/L1BlockInterop.json +++ /dev/null @@ -1,93 +0,0 @@ -[ - { - "bytes": "8", - "label": "number", - "offset": 0, - "slot": "0", - "type": "uint64" - }, - { - "bytes": "8", - "label": "timestamp", - "offset": 8, - "slot": "0", - "type": "uint64" - }, - { - "bytes": "32", - "label": "basefee", - "offset": 0, - "slot": "1", - "type": "uint256" - }, - { - "bytes": "32", - "label": "hash", - "offset": 0, - "slot": "2", - "type": "bytes32" - }, - { - "bytes": "8", - "label": "sequenceNumber", - "offset": 0, - "slot": "3", - "type": "uint64" - }, - { - "bytes": "4", - "label": "blobBaseFeeScalar", - "offset": 8, - "slot": "3", - "type": "uint32" - }, - { - "bytes": "4", - "label": "baseFeeScalar", - "offset": 12, - "slot": "3", - "type": "uint32" - }, - { - "bytes": "32", - "label": "batcherHash", - "offset": 0, - "slot": "4", - "type": "bytes32" - }, - { - "bytes": "32", - "label": "l1FeeOverhead", - "offset": 0, - "slot": "5", - "type": "uint256" - }, - { - "bytes": "32", - "label": "l1FeeScalar", - "offset": 0, - "slot": "6", - "type": "uint256" - }, - { - "bytes": "32", - "label": "blobBaseFee", - "offset": 0, - "slot": "7", - "type": "uint256" - }, - { - "bytes": "8", - "label": "operatorFeeConstant", - "offset": 0, - "slot": "8", - "type": "uint64" - }, - { - "bytes": "4", - "label": "operatorFeeScalar", - "offset": 8, - "slot": "8", - "type": "uint32" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol b/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol index 39ce43bc73a56..3c2fe0e56836f 100644 --- a/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol +++ b/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol @@ -1,15 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -// Libraries -import { Predeploys } from "src/libraries/Predeploys.sol"; - // Interfaces import { ISemver } from "interfaces/universal/ISemver.sol"; -import { IL1BlockInterop } from "interfaces/L2/IL1BlockInterop.sol"; - -/// @notice Thrown when trying to execute a cross chain message on a deposit transaction. -error NoExecutingDeposits(); /// @notice The struct for a pointer to a message payload in a remote (or local) chain. /// @custom:field origin The origin address of the message. @@ -30,26 +23,111 @@ struct Identifier { /// @title CrossL2Inbox /// @notice The CrossL2Inbox is responsible for executing a cross chain message on the destination /// chain. It is permissionless to execute a cross chain message on behalf of any user. +/// @dev Processes cross-chain messages that are pre-declared in EIP-2930 access lists. Each message +/// requires three specific access-list entries to be valid. It will verify that the storage +/// slot containing the message checksum is "warm" (pre-accessed), which fails if not included +/// in the tx's access list. Nodes pre-check message validity before execution. The checksum +/// combines the message's `Identifier` and `msgHash` with type-3 bit masking. contract CrossL2Inbox is ISemver { + /// @notice Thrown when trying to execute a cross chain message on a deposit transaction. + error NoExecutingDeposits(); + + /// @notice Thrown when trying to validate a cross chain message with a checksum + /// that is invalid or was not provided in the transaction's access list to set the slot + /// as warm. + error NotInAccessList(); + + /// @notice Thrown when trying to validate a cross chain message with a block number + /// that is greater than 2^64. + error BlockNumberTooHigh(); + + /// @notice Thrown when trying to validate a cross chain message with a timestamp + /// that is greater than 2^64. + error TimestampTooHigh(); + + /// @notice Thrown when trying to validate a cross chain message with a log index + /// that is greater than 2^32. + error LogIndexTooHigh(); + /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.13 - string public constant version = "1.0.0-beta.13"; + /// @custom:semver 1.0.0-beta.14 + string public constant version = "1.0.0-beta.14"; + + /// @notice The mask for the most significant bits of the checksum. + /// @dev Used to set the most significant byte to zero. + bytes32 internal constant _MSB_MASK = bytes32(~uint256(0xff << 248)); + + /// @notice Mask used to set the first byte of the bare checksum to 3 (0x03). + bytes32 internal constant _TYPE_3_MASK = bytes32(uint256(0x03 << 248)); + + /// @notice The threshold to use to know whether the slot is warm or not. + uint256 internal constant _WARM_READ_THRESHOLD = 1000; /// @notice Emitted when a cross chain message is being executed. /// @param msgHash Hash of message payload being executed. /// @param id Encoded Identifier of the message. event ExecutingMessage(bytes32 indexed msgHash, Identifier id); - /// @notice Validates a cross chain message on the destination chain - /// and emits an ExecutingMessage event. This function is useful - /// for applications that understand the schema of the _message payload and want to - /// process it in a custom way. + /// @notice Validates a cross chain message on the destination chain and emits an ExecutingMessage + /// event. This function is useful for applications that understand the schema of the + /// message payload and want to process it in a custom way. + /// @dev Makes sure the checksum's slot is warm to ensure the tx included it in the access list. + /// @dev `Identifier.blockNumber` and `Identifier.timestamp` must be less than 2^64, whereas + /// `Identifier.logIndex` must be less than 2^32 to properly fit into the checksum. /// @param _id Identifier of the message. /// @param _msgHash Hash of the message payload to call target with. function validateMessage(Identifier calldata _id, bytes32 _msgHash) external { - // We need to know if this is being called on a depositTx - if (IL1BlockInterop(Predeploys.L1_BLOCK_ATTRIBUTES).isDeposit()) revert NoExecutingDeposits(); + bytes32 checksum = _calculateChecksum(_id, _msgHash); + (bool isWarm,) = _isWarm(checksum); + if (!isWarm) revert NotInAccessList(); emit ExecutingMessage(_msgHash, _id); } + + /// @notice Calculates a custom checksum for a cross chain message `Identifier` and `msgHash`. + /// @param _id The identifier of the message. + /// @param _msgHash The hash of the message. + /// @return checksum_ The checksum of the message. + function _calculateChecksum(Identifier memory _id, bytes32 _msgHash) internal pure returns (bytes32 checksum_) { + if (_id.blockNumber > type(uint64).max) revert BlockNumberTooHigh(); + if (_id.logIndex > type(uint32).max) revert LogIndexTooHigh(); + if (_id.timestamp > type(uint64).max) revert TimestampTooHigh(); + + // Hash the origin address and message hash together + bytes32 logHash = keccak256(abi.encodePacked(_id.origin, _msgHash)); + + // Downsize the identifier fields to match the needed type for the custom checksum calculation. + uint64 blockNumber = uint64(_id.blockNumber); + uint64 timestamp = uint64(_id.timestamp); + uint32 logIndex = uint32(_id.logIndex); + + // Pack identifier fields with a left zero padding (uint96(0)) + bytes32 idPacked = bytes32(abi.encodePacked(uint96(0), blockNumber, timestamp, logIndex)); + + // Hash the logHash with the packed identifier data + bytes32 idLogHash = keccak256(abi.encodePacked(logHash, idPacked)); + + // Create the final hash by combining idLogHash with chainId + bytes32 bareChecksum = keccak256(abi.encodePacked(idLogHash, _id.chainId)); + + // Apply bit masking to create the final checksum + checksum_ = (bareChecksum & _MSB_MASK) | _TYPE_3_MASK; + } + + /// @notice Checks if a slot is warm by measuring the gas cost of loading the slot. + /// @dev Stores and returns the slot value so that the compiler doesn't optimize out the + /// `sload`, this adds cost to the read + /// @param _slot The slot to check. + /// @return isWarm_ Whether the slot is warm. + /// @return value_ The slot value. + function _isWarm(bytes32 _slot) internal view returns (bool isWarm_, uint256 value_) { + assembly { + // Get the gas cost of the reading the slot with `sload`. + let startGas := gas() + value_ := sload(_slot) + let endGas := gas() + // If the gas cost of the `sload` is below than the threshold, the slot is warm. + isWarm_ := iszero(gt(sub(startGas, endGas), _WARM_READ_THRESHOLD)) + } + } } diff --git a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol deleted file mode 100644 index 224a5f0a4c018..0000000000000 --- a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Contracts -import { L1Block } from "src/L2/L1Block.sol"; - -// Libraries -import { Predeploys } from "src/libraries/Predeploys.sol"; -import { NotDepositor, NotCrossL2Inbox } from "src/libraries/L1BlockErrors.sol"; - -/// @custom:proxied true -/// @custom:predeploy 0x4200000000000000000000000000000000000015 -/// @title L1BlockInterop -/// @notice Manages deposit contexts within L2 blocks. A deposit context represents a series of -/// deposited transactions within a single block, starting with an L1 attributes transaction -/// and ending after the final deposit. -/// The expected sequence of operations in a deposit context is: -/// 1. L1 attributes transaction opens the deposit context (isDeposit = true) -/// 2. User deposits are executed (if any exist) -/// 3. L1 attributes transaction closes the deposit context (isDeposit = false) -/// Note: During upgrades, additional deposits may follow after this sequence. -contract L1BlockInterop is L1Block { - /// @notice Storage slot that the isDeposit is stored at. - /// This is a custom slot that is not part of the standard storage layout. - /// keccak256(abi.encode(uint256(keccak256("l1Block.identifier.isDeposit")) - 1)) & ~bytes32(uint256(0xff)) - uint256 internal constant IS_DEPOSIT_SLOT = 0x921bd3a089295c6e5540e8fba8195448d253efd6f2e3e495b499b627dc36a300; - - /// @custom:semver +interop.7 - function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop.7"); - } - - /// @notice Returns whether the call was triggered from a a deposit or not. - /// @notice This function is only callable by the CrossL2Inbox contract. - function isDeposit() external view returns (bool isDeposit_) { - if (msg.sender != Predeploys.CROSS_L2_INBOX) revert NotCrossL2Inbox(); - assembly { - isDeposit_ := sload(IS_DEPOSIT_SLOT) - } - } - - /// @notice Updates the isDeposit flag and sets the L1 block values for an Interop upgraded chain. - /// It updates the L1 block values through the setL1BlockValuesEcotone function. - /// It forwards the calldata to the internally-used setL1BlockValuesEcotone function. - function setL1BlockValuesInterop() external { - // Set the isDeposit flag to true. - assembly { - sstore(IS_DEPOSIT_SLOT, 1) - } - - _setL1BlockValuesEcotone(); - } - - /// @notice Resets the isDeposit flag, marking the end of a deposit context. - /// @dev Should only be called by the depositor account after the deposits are complete. - function depositsComplete() external { - if (msg.sender != DEPOSITOR_ACCOUNT()) revert NotDepositor(); - - // Set the isDeposit flag to false. - assembly { - sstore(IS_DEPOSIT_SLOT, 0) - } - } -} diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index 95c23bb3c9572..1b77cc2c15432 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -61,8 +61,8 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { uint16 public constant messageVersion = uint16(0); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.15 - string public constant version = "1.0.0-beta.15"; + /// @custom:semver 1.0.0-beta.16 + string public constant version = "1.0.0-beta.16"; /// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this /// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again. diff --git a/packages/contracts-bedrock/src/libraries/Encoding.sol b/packages/contracts-bedrock/src/libraries/Encoding.sol index 6140b62c9e655..3e6ebaa471a43 100644 --- a/packages/contracts-bedrock/src/libraries/Encoding.sol +++ b/packages/contracts-bedrock/src/libraries/Encoding.sol @@ -229,46 +229,6 @@ library Encoding { ); } - /// @notice Returns an appropriately encoded call to L1Block.setL1BlockValuesInterop - /// @param _baseFeeScalar L1 base fee Scalar - /// @param _blobBaseFeeScalar L1 blob base fee Scalar - /// @param _sequenceNumber Number of L2 blocks since epoch start. - /// @param _timestamp L1 timestamp. - /// @param _number L1 blocknumber. - /// @param _baseFee L1 base fee. - /// @param _blobBaseFee L1 blob base fee. - /// @param _hash L1 blockhash. - /// @param _batcherHash Versioned hash to authenticate batcher by. - function encodeSetL1BlockValuesInterop( - uint32 _baseFeeScalar, - uint32 _blobBaseFeeScalar, - uint64 _sequenceNumber, - uint64 _timestamp, - uint64 _number, - uint256 _baseFee, - uint256 _blobBaseFee, - bytes32 _hash, - bytes32 _batcherHash - ) - internal - pure - returns (bytes memory) - { - bytes4 functionSignature = bytes4(keccak256("setL1BlockValuesInterop()")); - return abi.encodePacked( - functionSignature, - _baseFeeScalar, - _blobBaseFeeScalar, - _sequenceNumber, - _timestamp, - _number, - _baseFee, - _blobBaseFee, - _hash, - _batcherHash - ); - } - /// @notice Encodes a super root proof into the preimage of a Super Root. /// @param _superRootProof Super root proof to encode. /// @return Encoded super root proof. diff --git a/packages/contracts-bedrock/src/libraries/L1BlockErrors.sol b/packages/contracts-bedrock/src/libraries/L1BlockErrors.sol index 088b286e357e2..6b5d2e5704f9a 100644 --- a/packages/contracts-bedrock/src/libraries/L1BlockErrors.sol +++ b/packages/contracts-bedrock/src/libraries/L1BlockErrors.sol @@ -3,6 +3,3 @@ pragma solidity ^0.8.0; /// @notice Error returns when a non-depositor account tries to set L1 block values. error NotDepositor(); - -/// @notice Error when a non-cross L2 Inbox sender tries to call the `isDeposit()` method. -error NotCrossL2Inbox(); diff --git a/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol b/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol index 492eeab8613ad..8aea2454f6840 100644 --- a/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol +++ b/packages/contracts-bedrock/test/L2/CrossL2Inbox.t.sol @@ -1,64 +1,145 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.15; +pragma solidity 0.8.25; // Testing utilities -import { CommonTest } from "test/setup/CommonTest.sol"; +import { Test } from "forge-std/Test.sol"; -// Libraries -import { Predeploys } from "src/libraries/Predeploys.sol"; +// Interfaces +import { ICrossL2Inbox } from "interfaces/L2/ICrossL2Inbox.sol"; // Target contracts -import { ICrossL2Inbox, Identifier } from "interfaces/L2/ICrossL2Inbox.sol"; -import { IL1BlockInterop } from "interfaces/L2/IL1BlockInterop.sol"; +import { CrossL2InboxWithSlotWarming, Identifier } from "test/mocks/CrossL2InboxWithSlotWarming.sol"; /// @title CrossL2InboxTest /// @dev Contract for testing the CrossL2Inbox contract. -contract CrossL2InboxTest is CommonTest { - error NoExecutingDeposits(); - +contract CrossL2InboxTest is Test { event ExecutingMessage(bytes32 indexed msgHash, Identifier id); /// @dev CrossL2Inbox contract instance. - ICrossL2Inbox crossL2Inbox; + CrossL2InboxWithSlotWarming crossL2Inbox; /// @dev Sets up the test suite. - function setUp() public virtual override { - super.enableInterop(); - super.setUp(); + function setUp() public virtual { + crossL2Inbox = new CrossL2InboxWithSlotWarming(); + } + + /// Test that `validateMessage` reverts when the slot is not warm. + function testFuzz_validateMessage_accessList_reverts(Identifier memory _id, bytes32 _messageHash) external { + // Bound values types to ensure they are not too large + _id.blockNumber = bound(_id.blockNumber, 0, type(uint64).max); + _id.logIndex = bound(_id.logIndex, 0, type(uint32).max); + _id.timestamp = bound(_id.timestamp, 0, type(uint64).max); - crossL2Inbox = ICrossL2Inbox(Predeploys.CROSS_L2_INBOX); + // Expect revert + vm.expectRevert(ICrossL2Inbox.NotInAccessList.selector); + crossL2Inbox.validateMessage(_id, _messageHash); } - /// Tests that validateMessage succeeds for a non-deposit transaction. + /// Test that `validateMessage` succeeds when the slot for the message checksum is warm. function testFuzz_validateMessage_succeeds(Identifier memory _id, bytes32 _messageHash) external { - // Ensure is not a deposit transaction - vm.mockCall({ - callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), - returnData: abi.encode(false) - }); - - // Look for the emit ExecutingMessage event - vm.expectEmit(Predeploys.CROSS_L2_INBOX); + // Bound values types to ensure they are not too large + _id.blockNumber = bound(_id.blockNumber, 0, type(uint64).max); + _id.logIndex = bound(_id.logIndex, 0, type(uint32).max); + _id.timestamp = bound(_id.timestamp, 0, type(uint64).max); + + // Warm the slot + bytes32 slot = crossL2Inbox.calculateChecksum(_id, _messageHash); + crossL2Inbox.warmSlot(slot); + + // Expect `ExecutingMessage` event to be emitted + vm.expectEmit(address(crossL2Inbox)); emit ExecutingMessage(_messageHash, _id); - // Call the validateMessage function + // Validate the message crossL2Inbox.validateMessage(_id, _messageHash); } - /// Tests that validateMessage reverts for a deposit transaction. - function testFuzz_validateMessage_isDeposit_reverts(Identifier calldata _id, bytes32 _messageHash) external { - // Ensure it is a deposit transaction - vm.mockCall({ - callee: Predeploys.L1_BLOCK_ATTRIBUTES, - data: abi.encodeCall(IL1BlockInterop.isDeposit, ()), - returnData: abi.encode(true) - }); + /// Test that calculate checcksum reverts when the block number is greater than 2^64. + function testFuzz_calculateChecksum_withTooLargeBlockNumber_reverts( + Identifier memory _id, + bytes32 _messageHash + ) + external + { + // Set to the 2**64 + 1 + _id.blockNumber = 18446744073709551615 + 1; + vm.expectRevert(ICrossL2Inbox.BlockNumberTooHigh.selector); + crossL2Inbox.calculateChecksum(_id, _messageHash); + } - // Expect a revert with the NoExecutingDeposits selector - vm.expectRevert(NoExecutingDeposits.selector); + /// Test that calculate checcksum reverts when the log index is greater than 2^32. + function testFuzz_calculateChecksum_withTooLargeLogIndex_reverts( + Identifier memory _id, + bytes32 _messageHash + ) + external + { + _id.blockNumber = bound(_id.blockNumber, 0, type(uint64).max); - // Call the validateMessage function - crossL2Inbox.validateMessage(_id, _messageHash); + // Set to the 2**32 + 1 + _id.logIndex = 4294967295 + 1; + vm.expectRevert(ICrossL2Inbox.LogIndexTooHigh.selector); + crossL2Inbox.calculateChecksum(_id, _messageHash); + } + + /// Test that calculate checcksum reverts when the timestamp is greater than 2^64. + function testFuzz_calculateChecksum_withTooLargeTimestamp_reverts( + Identifier memory _id, + bytes32 _messageHash + ) + external + { + _id.blockNumber = bound(_id.blockNumber, 0, type(uint64).max); + _id.logIndex = bound(_id.logIndex, 0, type(uint32).max); + + // Set to the 2**64 + 1 + _id.timestamp = 18446744073709551615 + 1; + vm.expectRevert(ICrossL2Inbox.TimestampTooHigh.selector); + crossL2Inbox.calculateChecksum(_id, _messageHash); + } + + /// Test that `calculateChecksum` succeeds matching the expected calculated checksum. + /// Using a hardcoded checksum manually calculated and verified. + function test_calculateChecksum_succeeds() external view { + Identifier memory id = Identifier( + address(0), + uint64(0xa1a2a3a4a5a6a7a8), + uint32(0xb1b2b3b4), + uint64(0xc1c2c3c4c5c6c7c8), + uint256(0xd1d2d3d4d5d6d7d8) + ); + + // Calculate the expected checksum. + bytes32 messageHash = 0x8017559a85b12c04b14a1a425d53486d1015f833714a09bd62f04152a7e2ae9b; + bytes32 checksum = crossL2Inbox.calculateChecksum(id, messageHash); + bytes32 expectedChecksum = 0x03139ddd21106abad4bb82800fedfa3a103f53f242c2d5b7615b0baad8379531; + + // Expect it to match + assertEq(checksum, expectedChecksum); + } + + /// Test that `_isWarm` returns the correct value when the slot is not warm. + function testFuzz_isWarm_whenSlotIsNotInAccessList_succeeds(bytes32 _slot) external view { + // Assert that the slot is not warm + (bool isWarm, uint256 value) = crossL2Inbox.isWarm(_slot); + assertEq(isWarm, false); + assertEq(value, 0); + } + + /// Test that `_isWarm` returns the correct value when the slot is warm. + function testFuzz_isWarm_whenSlotIsWarm_succeeds(Identifier memory _id, bytes32 _messageHash) external view { + // Bound values types to ensure they are not too large + _id.blockNumber = bound(_id.blockNumber, 0, type(uint64).max); + _id.logIndex = bound(_id.logIndex, 0, type(uint32).max); + _id.timestamp = bound(_id.timestamp, 0, type(uint64).max); + + // Warm the slot + bytes32 slot = crossL2Inbox.calculateChecksum(_id, _messageHash); + crossL2Inbox.warmSlot(slot); + + // Assert that the slot is warm + (bool isWarm, uint256 value) = crossL2Inbox.isWarm(slot); + assertEq(isWarm, true); + assertEq(value, 0); } } diff --git a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol deleted file mode 100644 index 7bce56ded9282..0000000000000 --- a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Testing -import { CommonTest } from "test/setup/CommonTest.sol"; - -// Libraries -import { Predeploys } from "src/libraries/Predeploys.sol"; -import "src/libraries/L1BlockErrors.sol"; - -// Interfaces -import { IL1BlockInterop } from "interfaces/L2/IL1BlockInterop.sol"; - -contract L1BlockInteropTest is CommonTest { - modifier prankDepositor() { - vm.startPrank(_l1BlockInterop().DEPOSITOR_ACCOUNT()); - _; - vm.stopPrank(); - } - - /// @notice Marked virtual to be overridden in - /// test/kontrol/deployment/DeploymentSummary.t.sol - function setUp() public virtual override { - super.enableInterop(); - super.setUp(); - } - - /// @dev Returns the L1BlockInterop instance. - function _l1BlockInterop() internal view returns (IL1BlockInterop) { - return IL1BlockInterop(address(l1Block)); - } -} - -contract L1BlockInteropIsDeposit_Test is L1BlockInteropTest { - /// @dev Tests that `isDeposit` reverts if the caller is not the cross L2 inbox. - function test_isDeposit_notCrossL2Inbox_reverts(address _caller) external { - vm.assume(_caller != Predeploys.CROSS_L2_INBOX); - vm.expectRevert(NotCrossL2Inbox.selector); - _l1BlockInterop().isDeposit(); - } - - /// @dev Tests that `isDeposit` always returns the correct value. - function test_isDeposit_succeeds() external { - // Assert is false if the value is not updated - vm.prank(Predeploys.CROSS_L2_INBOX); - assertEq(_l1BlockInterop().isDeposit(), false); - - /// @dev Assuming that `setL1BlockValuesInterop` will set the proper value. That function is tested as well - vm.prank(_l1BlockInterop().DEPOSITOR_ACCOUNT()); - _l1BlockInterop().setL1BlockValuesInterop(); - - // Assert is true if the value is updated - vm.prank(Predeploys.CROSS_L2_INBOX); - assertEq(_l1BlockInterop().isDeposit(), true); - } -} - -contract L1BlockInteropSetL1BlockValuesInterop_Test is L1BlockInteropTest { - /// @dev Tests that `setL1BlockValuesInterop` reverts if sender address is not the depositor - function test_setL1BlockValuesInterop_notDepositor_reverts(address _caller) external { - vm.assume(_caller != _l1BlockInterop().DEPOSITOR_ACCOUNT()); - vm.prank(_caller); - vm.expectRevert(NotDepositor.selector); - _l1BlockInterop().setL1BlockValuesInterop(); - } - - /// @dev Tests that `setL1BlockValuesInterop` succeeds if sender address is the depositor - function test_setL1BlockValuesInterop_succeeds( - uint32 baseFeeScalar, - uint32 blobBaseFeeScalar, - uint64 sequenceNumber, - uint64 timestamp, - uint64 number, - uint256 baseFee, - uint256 blobBaseFee, - bytes32 hash, - bytes32 batcherHash - ) - external - { - // Ensure the `isDepositTransaction` flag is false before calling `setL1BlockValuesInterop` - vm.prank(Predeploys.CROSS_L2_INBOX); - assertEq(_l1BlockInterop().isDeposit(), false); - - bytes memory setValuesEcotoneCalldata = abi.encodePacked( - baseFeeScalar, blobBaseFeeScalar, sequenceNumber, timestamp, number, baseFee, blobBaseFee, hash, batcherHash - ); - - vm.prank(_l1BlockInterop().DEPOSITOR_ACCOUNT()); - (bool success,) = address(l1Block).call( - abi.encodePacked(IL1BlockInterop.setL1BlockValuesInterop.selector, setValuesEcotoneCalldata) - ); - assertTrue(success, "function call failed"); - - // Assert that the `isDepositTransaction` flag was properly set to true - vm.prank(Predeploys.CROSS_L2_INBOX); - assertEq(_l1BlockInterop().isDeposit(), true); - - // Assert `setL1BlockValuesEcotone` was properly called, forwarding the calldata to it - assertEq(_l1BlockInterop().baseFeeScalar(), baseFeeScalar, "base fee scalar not properly set"); - assertEq(_l1BlockInterop().blobBaseFeeScalar(), blobBaseFeeScalar, "blob base fee scalar not properly set"); - assertEq(_l1BlockInterop().sequenceNumber(), sequenceNumber, "sequence number not properly set"); - assertEq(_l1BlockInterop().timestamp(), timestamp, "timestamp not properly set"); - assertEq(_l1BlockInterop().number(), number, "number not properly set"); - assertEq(_l1BlockInterop().basefee(), baseFee, "base fee not properly set"); - assertEq(_l1BlockInterop().blobBaseFee(), blobBaseFee, "blob base fee not properly set"); - assertEq(_l1BlockInterop().hash(), hash, "hash not properly set"); - assertEq(_l1BlockInterop().batcherHash(), batcherHash, "batcher hash not properly set"); - } -} - -contract L1BlockDepositsComplete_Test is L1BlockInteropTest { - // @dev Tests that `depositsComplete` reverts if the caller is not the depositor. - function test_depositsComplete_notDepositor_reverts(address _caller) external { - vm.assume(_caller != _l1BlockInterop().DEPOSITOR_ACCOUNT()); - vm.expectRevert(NotDepositor.selector); - _l1BlockInterop().depositsComplete(); - } - - // @dev Tests that `depositsComplete` succeeds if the caller is the depositor. - function test_depositsComplete_succeeds() external { - // Set the `isDeposit` flag to true - vm.prank(_l1BlockInterop().DEPOSITOR_ACCOUNT()); - _l1BlockInterop().setL1BlockValuesInterop(); - - // Assert that the `isDeposit` flag was properly set to true - vm.prank(Predeploys.CROSS_L2_INBOX); - assertTrue(_l1BlockInterop().isDeposit()); - - // Call `depositsComplete` - vm.prank(_l1BlockInterop().DEPOSITOR_ACCOUNT()); - _l1BlockInterop().depositsComplete(); - - // Assert that the `isDeposit` flag was properly set to false - /// @dev Assuming that `isDeposit()` wil return the proper value. That function is tested as well - vm.prank(Predeploys.CROSS_L2_INBOX); - assertEq(_l1BlockInterop().isDeposit(), false); - } -} diff --git a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol index b07fe408387e2..10fdfcfdd992d 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol @@ -187,9 +187,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { address _target, bytes calldata _message, uint256 _value, - uint256 _blockNum, - uint256 _logIndex, - uint256 _time + uint64 _blockNum, + uint32 _logIndex, + uint64 _time ) external { @@ -245,9 +245,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { uint256 _nonce, bytes32 _msgHash, uint256 _value, - uint256 _blockNum, - uint256 _logIndex, - uint256 _time + uint64 _blockNum, + uint32 _logIndex, + uint64 _time ) external { @@ -292,9 +292,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { uint256 _nonce, address _sender, uint256 _value, - uint256 _blockNum, - uint256 _logIndex, - uint256 _time + uint64 _blockNum, + uint32 _logIndex, + uint64 _time ) external { @@ -352,9 +352,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { uint256 _nonce, address _sender, uint256 _value, - uint256 _blockNum, - uint256 _logIndex, - uint256 _time, + uint64 _blockNum, + uint32 _logIndex, + uint64 _time, address _target, bytes memory _mockedReturnData ) @@ -430,9 +430,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { address _sender2, // sender passed to `relayMessage` by the reentrant call. uint256 _nonce, uint256 _value, - uint256 _blockNum, - uint256 _logIndex, - uint256 _time + uint64 _blockNum, + uint32 _logIndex, + uint64 _time ) external { @@ -486,9 +486,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { bytes calldata _message, uint256 _value, address _origin, - uint256 _blockNum, - uint256 _logIndex, - uint256 _time + uint64 _blockNum, + uint32 _logIndex, + uint64 _time ) external { @@ -518,9 +518,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { address _target, bytes calldata _message, uint256 _value, - uint256 _blockNum, - uint256 _logIndex, - uint256 _time + uint64 _blockNum, + uint32 _logIndex, + uint64 _time ) external { @@ -557,9 +557,9 @@ contract L2ToL2CrossDomainMessengerTest is Test { address _target, bytes calldata _message, uint256 _value, - uint256 _blockNum, - uint256 _logIndex, - uint256 _time + uint64 _blockNum, + uint32 _logIndex, + uint64 _time ) external { diff --git a/packages/contracts-bedrock/test/integration/EventLogger.t.sol b/packages/contracts-bedrock/test/integration/EventLogger.t.sol index 17a49315d3b6a..277d6cebf4bca 100644 --- a/packages/contracts-bedrock/test/integration/EventLogger.t.sol +++ b/packages/contracts-bedrock/test/integration/EventLogger.t.sol @@ -9,19 +9,12 @@ import { EventLogger } from "../../src/integration/EventLogger.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { CrossL2Inbox, Identifier as ImplIdentifier } from "src/L2/CrossL2Inbox.sol"; - -// @title MockL1BlockInfo -// @notice mock L1 block info to fake a deposit-context. -contract MockL1BlockInfo { - // @notice mock deposit-context that is never active - // @return always false - function isDeposit() external pure returns (bool isDeposit_) { - return false; - } -} +import { Identifier as ImplIdentifier } from "src/L2/CrossL2Inbox.sol"; +import { CrossL2InboxWithSlotWarming as CrossL2Inbox } from "test/L2/CrossL2Inbox.t.sol"; contract EventLogger_Initializer is Test { + event ExecutingMessage(bytes32 indexed msgHash, ImplIdentifier id); + EventLogger eventLogger; function setUp() public { @@ -31,10 +24,6 @@ contract EventLogger_Initializer is Test { vm.etch(Predeploys.CROSS_L2_INBOX, address(new CrossL2Inbox()).code); vm.label(Predeploys.CROSS_L2_INBOX, "CrossL2Inbox"); - - // CrossL2Inbox needs this to do the deposit-context check - vm.etch(Predeploys.L1_BLOCK_ATTRIBUTES, address(new MockL1BlockInfo()).code); - vm.label(Predeploys.L1_BLOCK_ATTRIBUTES, "L1Block"); } } @@ -101,9 +90,9 @@ contract EventLoggerTest is EventLogger_Initializer { /// @notice It should succeed with any Identifier function test_validateMessage_succeeds( address _origin, - uint256 _blockNumber, - uint256 _logIndex, - uint256 _timestamp, + uint64 _blockNumber, + uint32 _logIndex, + uint64 _timestamp, uint256 _chainId, bytes32 _msgHash ) @@ -123,9 +112,16 @@ contract EventLoggerTest is EventLogger_Initializer { timestamp: _timestamp, chainId: _chainId }); + address emitter = Predeploys.CROSS_L2_INBOX; + + // Warm the slot for the function to succeed + bytes32 checksum = CrossL2Inbox(emitter).calculateChecksum(idImpl, _msgHash); + CrossL2Inbox(emitter).warmSlot(checksum); + vm.expectEmit(false, false, false, true, emitter); - emit CrossL2Inbox.ExecutingMessage(_msgHash, idImpl); + emit ExecutingMessage(_msgHash, idImpl); + eventLogger.validateMessage(idIface, _msgHash); } } diff --git a/packages/contracts-bedrock/test/mocks/CrossL2InboxWithSlotWarming.sol b/packages/contracts-bedrock/test/mocks/CrossL2InboxWithSlotWarming.sol new file mode 100644 index 0000000000000..722b957c44c60 --- /dev/null +++ b/packages/contracts-bedrock/test/mocks/CrossL2InboxWithSlotWarming.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +// Target contracts +import { CrossL2Inbox, Identifier } from "src/L2/CrossL2Inbox.sol"; + +/// @title CrossL2InboxWithSlotWarming +/// @dev CrossL2Inbox contract with a method to warm a slot. +contract CrossL2InboxWithSlotWarming is CrossL2Inbox { + // Getter for warming a slot on tests. + function warmSlot(bytes32 _slot) external view returns (uint256 res_) { + assembly { + res_ := sload(_slot) + } + } + + // Getter to expose `_isWarm` function for the tests. + function isWarm(bytes32 _slot) external view returns (bool isWarm_, uint256 value_) { + (isWarm_, value_) = _isWarm(_slot); + } + + // Getter to expose `_calculateChecksum` function for the tests. + function calculateChecksum(Identifier memory _id, bytes32 _msgHash) external pure returns (bytes32 checksum_) { + checksum_ = _calculateChecksum(_id, _msgHash); + } +} diff --git a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol index 0c82feda3b5f2..d78aeb37ae505 100644 --- a/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol +++ b/packages/contracts-bedrock/test/universal/BenchmarkTest.t.sol @@ -10,10 +10,8 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { SafeCall } from "src/libraries/SafeCall.sol"; import { Encoding } from "src/libraries/Encoding.sol"; -import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; // Interfaces -import { IL1BlockInterop } from "interfaces/L2/IL1BlockInterop.sol"; // Free function for setting the prevBaseFee param in the OptimismPortal. function setPrevBaseFee(Vm _vm, address _op, uint128 _prevBaseFee) { @@ -88,106 +86,3 @@ contract GasBenchMark_L1Block_SetValuesEcotone_Warm is GasBenchMark_L1Block { assertLt(vm.lastCallGas().gasTotalUsed, 200_000); } } - -contract GasBenchMark_L1BlockInterop is GasBenchMark_L1Block { - IL1BlockInterop l1BlockInterop; - - function setUp() public virtual override { - super.setUp(); - - // Create the L1BlockInterop contract. - l1BlockInterop = IL1BlockInterop( - DeployUtils.create1({ - _name: "L1BlockInterop", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1BlockInterop.__constructor__, ())) - }) - ); - - // Set up the calldata for setting the values. - setValuesCalldata = Encoding.encodeSetL1BlockValuesInterop( - type(uint32).max, - type(uint32).max, - type(uint64).max, - type(uint64).max, - type(uint64).max, - type(uint256).max, - type(uint256).max, - keccak256(abi.encode(1)), - bytes32(type(uint256).max) - ); - } -} - -contract GasBenchMark_L1BlockInterop_SetValuesInterop is GasBenchMark_L1BlockInterop { - function test_setL1BlockValuesInterop_benchmark() external { - // Skip if the test is running in coverage. - skipIfCoverage(); - - // Test - SafeCall.call({ _target: address(l1BlockInterop), _calldata: setValuesCalldata }); - - // Assert - // setL1BlockValuesInterop system tx ONLY gets 1m gas. - // 200k is a safe boundary to prevent hitting the limit. - assertLt(vm.lastCallGas().gasTotalUsed, 200_000); - } -} - -contract GasBenchMark_L1BlockInterop_SetValuesInterop_Warm is GasBenchMark_L1BlockInterop { - function test_setL1BlockValuesInterop_benchmark() external { - // Skip if the test is running in coverage. - skipIfCoverage(); - - // Setup - // Trigger so storage is warm. - SafeCall.call({ _target: address(l1BlockInterop), _calldata: setValuesCalldata }); - - // Test - SafeCall.call({ _target: address(l1BlockInterop), _calldata: setValuesCalldata }); - - // Assert - // setL1BlockValuesInterop system tx ONLY gets 1m gas. - // 200k is a safe boundary to prevent hitting the limit. - assertLt(vm.lastCallGas().gasTotalUsed, 200_000); - } -} - -contract GasBenchMark_L1BlockInterop_DepositsComplete is GasBenchMark_L1BlockInterop { - function test_depositsComplete_benchmark() external { - // Skip if the test is running in coverage. - skipIfCoverage(); - - // Test - SafeCall.call({ - _target: address(l1BlockInterop), - _calldata: abi.encodeCall(IL1BlockInterop.depositsComplete, ()) - }); - - // Assert - // depositsComplete system tx ONLY gets 15k gas. - // 5_000 is a safe boundary to prevent hitting the limit. - assertLt(vm.lastCallGas().gasTotalUsed, 5_000); - } -} - -contract GasBenchMark_L1BlockInterop_DepositsComplete_Warm is GasBenchMark_L1BlockInterop { - function test_depositsComplete_benchmark() external { - // Skip if the test is running in coverage. - skipIfCoverage(); - - // Setup - // Trigger so storage is warm. - SafeCall.call({ _target: address(l1BlockInterop), _calldata: setValuesCalldata }); - - // Test - SafeCall.call({ - _target: address(l1BlockInterop), - _calldata: abi.encodeCall(l1BlockInterop.depositsComplete, ()) - }); - - // Assert - // depositsComplete system tx ONLY gets 15k gas. - // 5_000 is a safe boundary to prevent hitting the limit. - assertLt(vm.lastCallGas().gasTotalUsed, 5_000); - } -}