Skip to content
Merged
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
5 changes: 3 additions & 2 deletions op-e2e/actions/helpers/l1_miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (s *L1Miner) ActL1IncludeTxByHash(txHash common.Hash) Action {
}
}

func (s *L1Miner) IncludeTx(t Testing, tx *types.Transaction) {
func (s *L1Miner) IncludeTx(t Testing, tx *types.Transaction) *types.Receipt {
from, err := s.l1Signer.Sender(tx)
require.NoError(t, err)
s.log.Info("including tx", "nonce", tx.Nonce(), "from", from, "to", tx.To())
Expand All @@ -175,7 +175,7 @@ func (s *L1Miner) IncludeTx(t Testing, tx *types.Transaction) {
}
if tx.Gas() > uint64(*s.L1GasPool) {
t.InvalidAction("action takes too much gas: %d, only have %d", tx.Gas(), uint64(*s.L1GasPool))
return
return nil
}
s.l1BuildingState.SetTxContext(tx.Hash(), len(s.L1Transactions))
blockCtx := core.NewEVMBlockContext(s.l1BuildingHeader, s.l1Chain, nil, s.l1Cfg.Config, s.l1BuildingState)
Expand All @@ -196,6 +196,7 @@ func (s *L1Miner) IncludeTx(t Testing, tx *types.Transaction) {
}
*s.l1BuildingHeader.BlobGasUsed += receipt.BlobGasUsed
}
return receipt
}

func (s *L1Miner) ActL1SetFeeRecipient(coinbase common.Address) {
Expand Down
26 changes: 26 additions & 0 deletions op-e2e/actions/interop/dsl/dsl.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ type SubmitBatchDataOpts struct {
SkipCrossSafeUpdate bool
}

func WithSkipCrossSafeUpdate() func(*SubmitBatchDataOpts) {
return func(o *SubmitBatchDataOpts) {
o.SkipCrossSafeUpdate = true
}
}

// SubmitBatchData submits batch data to L1 and processes the new L1 blocks, advancing the safe heads.
// By default, submits all batch data for all chains.
func (d *InteropDSL) SubmitBatchData(optionalArgs ...func(*SubmitBatchDataOpts)) {
Expand Down Expand Up @@ -250,6 +256,12 @@ type AdvanceL1Opts struct {
TxInclusion []helpers.Action
}

func WithActIncludeTx(includeTxAction helpers.Action) func(*AdvanceL1Opts) {
return func(o *AdvanceL1Opts) {
o.TxInclusion = append(o.TxInclusion, includeTxAction)
}
}

// AdvanceL1 adds a new L1 block with the specified transactions and ensures it is processed by the specified chains
// and the supervisor.
func (d *InteropDSL) AdvanceL1(optionalArgs ...func(*AdvanceL1Opts)) {
Expand Down Expand Up @@ -332,3 +344,17 @@ func (d *InteropDSL) AdvanceSafeHeads(optionalArgs ...func(*AdvanceSafeHeadsOpts
})
}
}

// AdvanceL2ToLastBlockOfOrigin advances the chain to the last block of the epoch at the specified L1 origin.
func (d *InteropDSL) AdvanceL2ToLastBlockOfOrigin(chain *Chain, l1OriginHeight uint64) {
const l1BlockTime = uint64(12)
require.Equal(d.t, l1BlockTime%chain.RollupCfg.BlockTime, uint64(0), "L2 block time must be a multiple of L1 block time")
endOfEpoch := (l1BlockTime/chain.RollupCfg.BlockTime)*(l1OriginHeight+1) - 1
require.LessOrEqual(d.t, chain.Sequencer.L2Unsafe().Number, endOfEpoch, "end of epoch is in the future")
for {
if n := chain.Sequencer.L2Unsafe().Number; n == endOfEpoch {
break
}
d.AddL2Block(chain)
}
}
6 changes: 3 additions & 3 deletions op-e2e/actions/interop/dsl/dsl_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ type DSLUser struct {
keys devkeys.Keys
}

func (u *DSLUser) TransactOpts(chain *Chain) (*bind.TransactOpts, common.Address) {
privKey, err := u.keys.Secret(devkeys.ChainUserKeys(chain.ChainID.ToBig())(u.index))
func (u *DSLUser) TransactOpts(chainID *big.Int) (*bind.TransactOpts, common.Address) {
privKey, err := u.keys.Secret(devkeys.ChainUserKeys(chainID)(u.index))
require.NoError(u.t, err)
opts, err := bind.NewKeyedTransactorWithChainID(privKey, chain.ChainID.ToBig())
opts, err := bind.NewKeyedTransactorWithChainID(privKey, chainID)
require.NoError(u.t, err)
opts.GasTipCap = big.NewInt(params.GWei)

Expand Down
4 changes: 2 additions & 2 deletions op-e2e/actions/interop/dsl/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func NewEmitterContract(t helpers.Testing) *EmitterContract {

func (c *EmitterContract) Deploy(user *DSLUser) TransactionCreator {
return func(chain *Chain) *GeneratedTransaction {
opts, from := user.TransactOpts(chain)
opts, from := user.TransactOpts(chain.ChainID.ToBig())
emitContract, tx, _, err := emit.DeployEmit(opts, chain.SequencerEngine.EthClient())
require.NoError(c.t, err)
c.addressByChain[chain.ChainID] = emitContract
Expand All @@ -34,7 +34,7 @@ func (c *EmitterContract) Deploy(user *DSLUser) TransactionCreator {

func (c *EmitterContract) EmitMessage(user *DSLUser, message string) TransactionCreator {
return func(chain *Chain) *GeneratedTransaction {
opts, from := user.TransactOpts(chain)
opts, from := user.TransactOpts(chain.ChainID.ToBig())
address, ok := c.addressByChain[chain.ChainID]
require.Truef(c.t, ok, "not deployed on chain %d", chain.ChainID)
bindings, err := emit.NewEmitTransactor(address, chain.SequencerEngine.EthClient())
Expand Down
9 changes: 4 additions & 5 deletions op-e2e/actions/interop/dsl/inbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,12 @@ func WithPayload(payload []byte) func(opts *ExecuteOpts) {
}
}

func WithPendingMessage(emitter *EmitterContract, chain *Chain, logIndex int, msg string) func(opts *ExecuteOpts) {
func WithPendingMessage(emitter *EmitterContract, chain *Chain, number uint64, logIndex int, msg string) func(opts *ExecuteOpts) {
return func(opts *ExecuteOpts) {
head := chain.Sequencer.L2Unsafe()
blockTime := chain.RollupCfg.TimestampForBlock(head.Number)
blockTime := chain.RollupCfg.TimestampForBlock(number)
id := inbox.Identifier{
Origin: emitter.Address(chain),
BlockNumber: big.NewInt(int64(head.Number)),
BlockNumber: big.NewInt(int64(number)),
LogIndex: big.NewInt(int64(logIndex)),
Timestamp: big.NewInt(int64(blockTime)),
ChainId: chain.RollupCfg.L2ChainID,
Expand Down Expand Up @@ -82,7 +81,7 @@ func (i *InboxContract) Execute(user *DSLUser, initTx *GeneratedTransaction, arg
} else {
payload = initTx.MessagePayload()
}
txOpts, from := user.TransactOpts(chain)
txOpts, from := user.TransactOpts(chain.ChainID.ToBig())
contract, err := inbox.NewInbox(predeploys.CrossL2InboxAddr, chain.SequencerEngine.EthClient())
require.NoError(i.t, err)
tx, err := contract.ValidateMessage(txOpts, ident, crypto.Keccak256Hash(payload))
Expand Down
29 changes: 29 additions & 0 deletions op-e2e/actions/interop/dsl/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Message struct {
message string
emitter *EmitterContract
inbox *InboxContract
l1Miner *helpers.L1Miner

initTx *GeneratedTransaction
execTx *GeneratedTransaction
Expand All @@ -25,6 +26,7 @@ func NewMessage(dsl *InteropDSL, chain *Chain, emitter *EmitterContract, message
chain: chain,
emitter: emitter,
inbox: dsl.InboxContract,
l1Miner: dsl.Actors.L1Miner,
message: message,
}
}
Expand All @@ -36,6 +38,22 @@ func (m *Message) Emit() *Message {
return m
}

// EmitDeposit emits a message via a user deposit transaction.
func (m *Message) EmitDeposit(l1User *DSLUser) *Message {
emitAction := m.emitter.EmitMessage(m.user, m.message)
m.initTx = emitAction(m.chain)
opts, _ := m.user.TransactOpts(m.chain.ChainID.ToBig())
m.initTx.IncludeDepositOK(l1User, opts, m.l1Miner)
return m
}

// ActEmitDeposit returns an action that emits a message via a user deposit transaction.
func (m *Message) ActEmitDeposit(l1User *DSLUser) helpers.Action {
return func(t helpers.Testing) {
m.EmitDeposit(l1User)
}
}

func (m *Message) ExecuteOn(target *Chain, execOpts ...func(*ExecuteOpts)) *Message {
require.NotNil(m.t, m.initTx, "message must be emitted before it can be executed")
execAction := m.inbox.Execute(m.user, m.initTx, execOpts...)
Expand All @@ -44,6 +62,17 @@ func (m *Message) ExecuteOn(target *Chain, execOpts ...func(*ExecuteOpts)) *Mess
return m
}

// ExecutePendingOn executes a message that may not have been emitted yet.
func (m *Message) ExecutePendingOn(target *Chain, pendingMessageBlockNumber uint64, execOpts ...func(*ExecuteOpts)) *Message {
var opts []func(*ExecuteOpts)
opts = append(opts, WithPendingMessage(m.emitter, m.chain, pendingMessageBlockNumber, 0, m.message))
opts = append(opts, execOpts...)
execAction := m.inbox.Execute(m.user, nil, opts...)
m.execTx = execAction(target)
m.execTx.IncludeOK()
return m
}

func (m *Message) CheckEmitted() {
require.NotNil(m.t, m.initTx, "message must be emitted before it can be checked")
m.initTx.CheckIncluded()
Expand Down
24 changes: 22 additions & 2 deletions op-e2e/actions/interop/dsl/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"math/big"

"github.com/ethereum-optimism/optimism/op-e2e/actions/helpers"
"github.com/ethereum-optimism/optimism/op-e2e/bindingspreview"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/interop/contracts/bindings/inbox"
stypes "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -47,6 +49,24 @@ func (m *GeneratedTransaction) IncludeOK() {
require.Equal(m.t, types.ReceiptStatusSuccessful, rcpt.Status)
}

// IncludeDepositOK includes the GeneratedTransaction via a user deposit transaction.
func (m *GeneratedTransaction) IncludeDepositOK(l1User *DSLUser, depositTxOpts *bind.TransactOpts, l1Miner *helpers.L1Miner) {
optimismPortal2, err := bindingspreview.NewOptimismPortal2(m.chain.RollupCfg.DepositContractAddress, l1Miner.EthClient())
require.NoError(m.t, err)

l1Opts, _ := l1User.TransactOpts(l1Miner.L1Chain().Config().ChainID)
l1Opts.Value = depositTxOpts.Value

to := m.tx.To()
min, err := optimismPortal2.MinimumGasLimit(&bind.CallOpts{}, uint64(len(m.tx.Data())))
require.NoError(m.t, err)
gas := max(m.tx.Gas(), min)
tx, err := optimismPortal2.DepositTransaction(l1Opts, *to, m.tx.Value(), gas, to == nil, m.tx.Data())
require.NoError(m.t, err, "failed to create deposit tx")
rcpt := l1Miner.IncludeTx(m.t, tx)
require.Equal(m.t, types.ReceiptStatusSuccessful, rcpt.Status, "deposit tx failed")
}

func (m *GeneratedTransaction) Identifier() inbox.Identifier {
require.NotZero(m.t, len(m.rcpt.Logs), "Transaction did not include any logs to reference")

Expand All @@ -71,8 +91,8 @@ func (m *GeneratedTransaction) MessagePayload() []byte {

func (m *GeneratedTransaction) CheckIncluded() {
rcpt, err := m.chain.SequencerEngine.EthClient().TransactionReceipt(m.t.Ctx(), m.tx.Hash())
require.NoError(m.t, err)
require.NotNil(m.t, rcpt)
require.NoError(m.t, err, "Transaction should have been included")
require.NotNil(m.t, rcpt, "No receipt found")
}

func (m *GeneratedTransaction) CheckNotIncluded() {
Expand Down
144 changes: 143 additions & 1 deletion op-e2e/actions/interop/proofs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,138 @@ func TestInteropFaultProofs_VariedBlockTimes_FasterChainB(gt *testing.T) {
runFppAndChallengerTests(gt, system, tests)
}

func TestInteropFaultProofs_DepositMessage(gt *testing.T) {
t := helpers.NewDefaultTesting(gt)

system := dsl.NewInteropDSL(t)
actors := system.Actors
emitter := system.DeployEmitterContracts()

// Advance L1 a couple times to avoid deposit gas metering issues near genesis
system.AdvanceL1()
system.AdvanceL1()

l1User := system.CreateUser()
depositMessage := dsl.NewMessage(system, actors.ChainA, emitter, "hello")
system.AdvanceL1(
dsl.WithActIncludeTx(
depositMessage.ActEmitDeposit(l1User)))

// As such, the next block timestamp across both chains will contain a user-deposit message and an executing message
system.AdvanceL2ToLastBlockOfOrigin(actors.ChainA, 2)
system.AdvanceL2ToLastBlockOfOrigin(actors.ChainB, 2)

actors.ChainA.Sequencer.ActL2StartBlock(t)
actors.ChainB.Sequencer.ActL2StartBlock(t)
// The pending block on chain A will contain the user deposit
depositMessage.ExecutePendingOn(actors.ChainB, actors.ChainA.Sequencer.L2Unsafe().Number+1)
actors.ChainA.Sequencer.ActL2EndBlock(t)
actors.ChainB.Sequencer.ActL2EndBlock(t)
system.SubmitBatchData(dsl.WithSkipCrossSafeUpdate())

endTimestamp := actors.ChainB.Sequencer.L2Unsafe().Time
startTimestamp := endTimestamp - 1
preConsolidation := system.Outputs.TransitionState(startTimestamp, consolidateStep,
system.Outputs.OptimisticBlockAtTimestamp(actors.ChainA, endTimestamp),
system.Outputs.OptimisticBlockAtTimestamp(actors.ChainB, endTimestamp),
).Marshal()

system.ProcessCrossSafe()
depositMessage.CheckExecuted()
assertUserDepositEmitted(t, system.Actors.ChainA, nil, emitter)
crossSafeEnd := system.Outputs.SuperRoot(endTimestamp)

tests := []*transitionTest{
{
name: "Consolidate",
agreedClaim: preConsolidation,
disputedClaim: crossSafeEnd.Marshal(),
disputedTraceIndex: consolidateStep,
expectValid: true,
},
{
name: "Consolidate-InvalidNoChange",
agreedClaim: preConsolidation,
disputedClaim: preConsolidation,
disputedTraceIndex: consolidateStep,
expectValid: false,
},
}
runFppAndChallengerTests(gt, system, tests)
}

func TestInteropFaultProofs_DepositMessage_InvalidExecution(gt *testing.T) {
t := helpers.NewDefaultTesting(gt)

system := dsl.NewInteropDSL(t)
actors := system.Actors
emitter := system.DeployEmitterContracts()

// Advance L1 a couple times to avoid deposit gas metering issues near genesis
system.AdvanceL1()
system.AdvanceL1()

l1User := system.CreateUser()
depositMessage := dsl.NewMessage(system, actors.ChainA, emitter, "hello")
system.AdvanceL1(
dsl.WithActIncludeTx(
depositMessage.ActEmitDeposit(l1User)))

// As such, the next block timestamp across both chains will contain a user-deposit message and an executing message
system.AdvanceL2ToLastBlockOfOrigin(actors.ChainA, 2)
system.AdvanceL2ToLastBlockOfOrigin(actors.ChainB, 2)

actors.ChainA.Sequencer.ActL2StartBlock(t)
actors.ChainB.Sequencer.ActL2StartBlock(t)
// The pending block on chain A will contain the user deposit
depositMessage.ExecutePendingOn(actors.ChainB,
actors.ChainA.Sequencer.L2Unsafe().Number+1,
dsl.WithPayload([]byte("this message was never emitted")),
)
actors.ChainA.Sequencer.ActL2EndBlock(t)
actors.ChainB.Sequencer.ActL2EndBlock(t)
system.SubmitBatchData(dsl.WithSkipCrossSafeUpdate())

endTimestamp := actors.ChainB.Sequencer.L2Unsafe().Time
startTimestamp := endTimestamp - 1
optimisticEnd := system.Outputs.SuperRoot(endTimestamp)

preConsolidation := system.Outputs.TransitionState(startTimestamp, consolidateStep,
system.Outputs.OptimisticBlockAtTimestamp(actors.ChainA, endTimestamp),
system.Outputs.OptimisticBlockAtTimestamp(actors.ChainB, endTimestamp),
).Marshal()

system.ProcessCrossSafe()
depositMessage.CheckNotExecuted()
assertUserDepositEmitted(t, system.Actors.ChainA, nil, emitter)
crossSafeEnd := system.Outputs.SuperRoot(endTimestamp)

tests := []*transitionTest{
{
name: "Consolidate",
agreedClaim: preConsolidation,
disputedClaim: crossSafeEnd.Marshal(),
disputedTraceIndex: consolidateStep,
expectValid: true,
},
{
name: "Consolidate-InvalidNoChange",
agreedClaim: preConsolidation,
disputedClaim: preConsolidation,
disputedTraceIndex: consolidateStep,
expectValid: false,
},
}
tests = append(tests, &transitionTest{
name: "Consolidate-ExpectInvalidPendingBlock",
agreedClaim: preConsolidation,
disputedClaim: optimisticEnd.Marshal(),
disputedTraceIndex: consolidateStep,
expectValid: false,
})
runFppAndChallengerTests(gt, system, tests)
}

func runFppAndChallengerTests(gt *testing.T, system *dsl.InteropDSL, tests []*transitionTest) {
for _, test := range tests {
test := test
Expand Down Expand Up @@ -1248,6 +1380,15 @@ func assertTime(t helpers.Testing, chain *dsl.Chain, unsafe, crossUnsafe, localS
require.Equal(t, start+safe, status.SafeL2.Time, "Safe")
}

func assertUserDepositEmitted(t helpers.Testing, chain *dsl.Chain, number *big.Int, emitter *dsl.EmitterContract) {
block, err := chain.SequencerEngine.EthClient().BlockByNumber(t.Ctx(), number)
require.NoError(t, err)
require.GreaterOrEqual(t, len(block.Transactions()), 3) // l1-attrs + user-deposit + l1-attrs (end deposit contxt) + [txs]
userDepositTx := block.Transactions()[1]
require.NotNil(t, userDepositTx.To())
require.Equal(t, emitter.Address(chain), *userDepositTx.To())
}

type transitionTest struct {
name string
agreedClaim []byte
Expand Down Expand Up @@ -1337,7 +1478,8 @@ func (c *cyclicDependencyInvalidCase) Setup(t helpers.StatefulTesting, system *d
alice := system.CreateUser()

// Create an exec message for chain B without including it
pendingExecBOpts := dsl.WithPendingMessage(emitter, actors.ChainB, 0, "message from B")
pendingBlockNumber := actors.ChainB.Sequencer.L2Unsafe().Number + 1
pendingExecBOpts := dsl.WithPendingMessage(emitter, actors.ChainB, pendingBlockNumber, 0, "message from B")

// Exec(A) -> Exec(B) -> Exec(A)
actExecA := system.InboxContract.Execute(alice, nil, pendingExecBOpts)
Expand Down