Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 42 additions & 8 deletions op-e2e/e2eutils/interop/contracts/src/ICrossL2Inbox.sol
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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_);
}
7 changes: 0 additions & 7 deletions op-node/rollup/derive/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 2 additions & 8 deletions op-node/rollup/derive/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
})
Expand All @@ -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)
Expand All @@ -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)
})
Expand Down
8 changes: 0 additions & 8 deletions op-node/rollup/derive/fuzz_parsers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
90 changes: 8 additions & 82 deletions op-node/rollup/derive/l1_block_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
38 changes: 0 additions & 38 deletions op-node/rollup/derive/l1_block_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
})
}
15 changes: 6 additions & 9 deletions packages/contracts-bedrock/interfaces/L2/ICrossL2Inbox.sol
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
}
12 changes: 0 additions & 12 deletions packages/contracts-bedrock/interfaces/L2/IDependencySet.sol

This file was deleted.

Loading