Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip(interop) block dependency graph #10044

Closed
Closed
Changes from 1 commit
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
Prev Previous commit
wip
  • Loading branch information
hamdiallam committed Apr 4, 2024
commit 73b1a368df6d7ec89826b5730a89431adfe0cbed
26 changes: 5 additions & 21 deletions op-superchain/backend.go
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)

type Backend interface {
@@ -81,38 +80,23 @@ func (b *backend) MessageSafety(ctx context.Context, id MessageIdentifier, paylo
return MessageUnknown, fmt.Errorf("peer with chain id %d is not configured", id.ChainId)
}

blockNum := rpc.BlockNumber(id.BlockNumber.Int64())
block, logs, err := logsProvider.FetchLogs(ctx, rpc.BlockNumberOrHash{BlockNumber: &blockNum})
safety, err := MessageValidity(ctx, id, payload, logsProvider)
if err != nil {
return MessageUnknown, fmt.Errorf("unable to fetch logs: %w", err)
return safety, err
}

// validity with the block
if id.Timestamp != block.Time() {
return MessageInvalid, fmt.Errorf("message id and header timestamp mismatch")
}
if id.LogIndex >= uint64(len(logs)) {
return MessageInvalid, fmt.Errorf("invalid log index")
}

// Check message validity against the remote log
log := logs[id.LogIndex]
if err := CheckMessageLog(id, payload, &log); err != nil {
return MessageInvalid, fmt.Errorf("failed log check: %w", err)
}

// Message Safety
var finalizedL2Timestamp uint64
b.mu.RLock()
finalizedL2Timestamp = b.l2FinalizedBlockRef.Time
b.mu.RUnlock()

// Dependency verification
if id.Timestamp <= finalizedL2Timestamp {
return MessageFinalized, nil
}

// TODO: support for the other safety labels
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create a GitHub ticket for this TODO.

Ignore this finding from todos_require_linear.


// Cant determine validity
return MessageUnknown, nil
// Cant determine higher levels of safety beyond validity
return safety, nil
}
34 changes: 25 additions & 9 deletions op-superchain/block_dependencies.go
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ type BlockDependencies struct {
mu sync.Mutex
}

func (deps *BlockDependencies) BlockSafety(chainId *big.Int, blockRef eth.L2BlockRef) (BlockSafetyLabel, error) {
func (deps *BlockDependencies) BlockSafety(ctx context.Context, chainId *big.Int, blockRef eth.L2BlockRef) (BlockSafetyLabel, error) {
deps.mu.Lock()
defer deps.mu.Unlock()

@@ -74,15 +74,15 @@ func (deps *BlockDependencies) BlockSafety(chainId *big.Int, blockRef eth.L2Bloc
// right block using the block number as the initiating message in the remote
// block was validated.
//
// We also know the remote block specified by number hasn't been reorg'd, otherwise
// the invalidation would have cascaded to this block on a reset.
// We also know the remote block specified by number hasn't been reorg'd,
// otherwise the invalidation would have cascaded to this block
chain := deps.chains[blockDependency.chainId.String()]
block, err := chain.L2BlockRefByNumber(context.TODO(), blockDependency.blockNumber)
block, err := chain.L2BlockRefByNumber(ctx, blockDependency.blockNumber)
if err != nil {
return BlockUnsafe, err
}

dependencyBlockSafety, err := deps.BlockSafety(blockDependency.chainId, block)
dependencyBlockSafety, err := deps.BlockSafety(ctx, blockDependency.chainId, block)
if err != nil {
return BlockUnsafe, err
}
@@ -92,6 +92,7 @@ func (deps *BlockDependencies) BlockSafety(chainId *big.Int, blockRef eth.L2Bloc
}
}

// TODO: we need references to the safe head for every chain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create a GitHub ticket for this TODO.

Ignore this finding from todos_require_linear.

return BlockCrossUnsafe, nil
}

@@ -127,18 +128,22 @@ func (deps *BlockDependencies) AddBlock(chainId *big.Int, blockRef eth.L2BlockRe
if IsInboxExecutingMessageTx(tx) {
_, id, payload, err := ParseInboxExecuteMessageTxData(tx.Data())
if err != nil {
// TODO: revisit bad txs to the inbox address
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create a GitHub ticket for this TODO.

Ignore this finding from todos_require_linear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO in error handling code

Ignore this finding from err-todo.

log.Warn("skipping inbox tx with bad tx data", "err", err)
continue
}

dependent := blockDependent{id.ChainId, blockRef}
dependency := blockDependency{id.ChainId, id.BlockNumber.Uint64()}

// todo: de-dup edges
deps.unverifiedExecutingMessages[blockRef.Hash] = append(deps.unverifiedExecutingMessages[blockRef.Hash], Message{id, payload})
deps.dependents[id.ChainId.String()][id.BlockNumber.Uint64()] = append(deps.dependents[id.ChainId.String()][id.BlockNumber.Uint64()], blockDependent{id.ChainId, blockRef})
deps.dependencies[chainIdStr][blockRef.Hash] = append(deps.dependencies[chainIdStr][blockRef.Hash], blockDependency{id.ChainId, id.BlockNumber.Uint64()})
deps.dependents[id.ChainId.String()][id.BlockNumber.Uint64()] = append(deps.dependents[id.ChainId.String()][id.BlockNumber.Uint64()], dependent)
deps.dependencies[chainIdStr][blockRef.Hash] = append(deps.dependencies[chainIdStr][blockRef.Hash], dependency)
}
}

// attempt resolution for this block & any set dependents
// attempt resolution for this block & any existing dependents
deps.resolveUnverifiedExecutingMessages(chainId, blockRef)
for _, dependentBlock := range deps.dependents[chainIdStr][blockRef.Number] {
deps.resolveUnverifiedExecutingMessages(dependentBlock.chainId, dependentBlock.blockRef)
@@ -152,7 +157,18 @@ func (deps *BlockDependencies) resolveUnverifiedExecutingMessages(chainId *big.I

unverifiedMessages := deps.unverifiedExecutingMessages[blockRef.Hash]
remainingUnverifiedMessages := make([]Message, 0, len(unverifiedMessages))
for _, _ = range unverifiedMessages {
for _, msg := range unverifiedMessages {
safety, err := MessageValidity(context.TODO(), msg.Id, msg.Payload, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider to use well-defined context

Ignore this finding from context-todo.

if err != nil {
if safety == MessageInvalid {
deps.log.Error("invalidated msg", "id", msg.Id)
deps.handleInvalidation(chainId, blockRef)
}
if safety == MessageUnknown {
remainingUnverifiedMessages = append(remainingUnverifiedMessages, msg)
}
}
// msg is valid (unsafe) and can be dropped.
}

deps.unverifiedExecutingMessages[blockRef.Hash] = remainingUnverifiedMessages
28 changes: 28 additions & 0 deletions op-superchain/message.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package superchain

import (
"bytes"
"context"
"errors"
"fmt"
"math/big"
@@ -11,6 +12,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"
)

var (
@@ -64,6 +66,32 @@ func IsInboxExecutingMessageTx(tx *types.Transaction) bool {
return len(txData) >= 4 && bytes.Equal(txData[:4], inboxExecuteMessageBytes4)
}

// Check the validity of a message against the fetched log. If valid, the
// returned label is `MessageUnsafe` as dependencies still need to be verified
func MessageValidity(ctx context.Context, id MessageIdentifier, payload hexutil.Bytes, p LogsProvider) (MessageSafetyLabel, error) {
blockNum := rpc.BlockNumber(id.BlockNumber.Int64())
block, logs, err := p.FetchLogs(ctx, rpc.BlockNumberOrHash{BlockNumber: &blockNum})
if err != nil {
return MessageUnknown, fmt.Errorf("unable to fetch logs: %w", err)
}

// block validity
if id.Timestamp != block.Time() {
return MessageInvalid, nil
}
if id.LogIndex >= uint64(len(logs)) {
return MessageInvalid, fmt.Errorf("invalid log index")
}

// log validity
log := logs[id.LogIndex]
if err := CheckMessageLog(id, payload, &log); err != nil {
return MessageInvalid, fmt.Errorf("failed message id & log check: %w", err)
}

return MessageUnsafe, nil
}

// Check the message id and payload against the fields of the log.
func CheckMessageLog(id MessageIdentifier, payload hexutil.Bytes, log *types.Log) error {
if id.LogIndex != uint64(log.Index) {