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
10 changes: 8 additions & 2 deletions op-e2e/actions/interop/emitter_contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,26 @@ func newEmitMessageTx(t helpers.Testing, chain *dsl.Chain, user *userWithKeys, e
return tx
}

// newExecuteMessageTx creates a new executing message tx based on the given initializing tx.
func newExecuteMessageTx(t helpers.Testing, destChain *dsl.Chain, executor *userWithKeys, srcChain *dsl.Chain, srcTx *types.Transaction) *types.Transaction {
// Create the id and payload
id := idForTx(t, srcTx, srcChain)
receipt, err := srcChain.SequencerEngine.EthClient().TransactionReceipt(t.Ctx(), srcTx.Hash())
require.NoError(t, err)
payload := stypes.LogToMessagePayload(receipt.Logs[0])
hash := crypto.Keccak256Hash(payload)

// Create the tx to validate the message
return newExecuteMessageTxFromIDAndHash(t, executor, destChain, id, hash)
}

// newExecuteMessageTxFromIDAndHash creates a new executing message tx for the given id and hash.
func newExecuteMessageTxFromIDAndHash(t helpers.Testing, executor *userWithKeys, destChain *dsl.Chain, id inbox.Identifier, hash common.Hash) *types.Transaction {
inboxContract, err := inbox.NewInbox(predeploys.CrossL2InboxAddr, destChain.SequencerEngine.EthClient())
require.NoError(t, err)
auth := newL2TxOpts(t, executor.secret, destChain)
tx, err := inboxContract.ValidateMessage(auth, id, crypto.Keccak256Hash(payload))
tx, err := inboxContract.ValidateMessage(auth, id, hash)
require.NoError(t, err)

return tx
}

Expand Down
33 changes: 33 additions & 0 deletions op-e2e/actions/interop/interop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,3 +439,36 @@ func TestInteropCrossSafeDependencyDelay(gt *testing.T) {
require.NoError(t, err)
require.Equal(t, chainBSubmittedIn.NumberU64(), source.Number)
}

func TestInteropExecutingMessageOutOfRangeLogIndex(gt *testing.T) {
t := helpers.NewDefaultTesting(gt)
is := dsl.SetupInterop(t)
actors := is.CreateActors()
actors.PrepareChainState(t)
aliceA := setupUser(t, is, actors.ChainA, 0)

// Execute a fake log on chain A
chainBHead := actors.ChainB.Sequencer.SyncStatus().UnsafeL2
nonExistentID := inbox.Identifier{
Origin: aliceA.address,
BlockNumber: big.NewInt(int64(chainBHead.Number)),
LogIndex: common.Big0,
Timestamp: big.NewInt(int64(chainBHead.Time)),
ChainId: actors.ChainB.RollupCfg.L2ChainID,
}
nonExistentHash := crypto.Keccak256Hash([]byte("fake message"))
tx := newExecuteMessageTxFromIDAndHash(t, aliceA, actors.ChainA, nonExistentID, nonExistentHash)
includeTxOnChainBasic(t, actors.ChainA, tx, aliceA.address)
actors.ChainB.Sequencer.ActL2EmptyBlock(t)

// Sync the system
actors.ChainA.Sequencer.SyncSupervisor(t)
actors.ChainB.Sequencer.SyncSupervisor(t)
actors.Supervisor.ProcessFull(t)
actors.ChainA.Sequencer.ActL2PipelineFull(t)
actors.ChainB.Sequencer.ActL2PipelineFull(t)

// Assert that chainA's block is not cross-safe but chainB's is.
assertHeads(t, actors.ChainA, 1, 0, 0, 0)
assertHeads(t, actors.ChainB, 1, 0, 1, 0)
}
7 changes: 7 additions & 0 deletions op-supervisor/supervisor/backend/db/logs/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ func (db *DB) Contains(query types.ContainsQuery) (types.BlockSeal, error) {
return types.BlockSeal{}, err
}

// findLogInfo returns the hash of the log at the specified block number and log index.
// If the log index is out of range we return an ErrFuture if the block is complete,
// or ErrConflict if it's not.
func (db *DB) findLogInfo(blockNum uint64, logIdx uint32) (common.Hash, Iterator, error) {
if blockNum == 0 {
return common.Hash{}, nil, types.ErrConflict // no logs in block 0
Expand All @@ -361,6 +364,10 @@ func (db *DB) findLogInfo(blockNum uint64, logIdx uint32) (common.Hash, Iterator
return common.Hash{}, nil, err
}
if err := iter.NextInitMsg(); err != nil {
// if we get an ErrFuture but have a complete block, then we really have a conflict
if errors.Is(err, types.ErrFuture) && db.lastEntryContext.hasCompleteBlock() {
err = types.ErrConflict
}
return common.Hash{}, nil, fmt.Errorf("failed to read initiating message %d, on top of block %d: %w", logIdx, blockNum, err)
}
if _, x, ok := iter.SealedBlock(); !ok {
Expand Down