Commit 9b8b5281 authored by protolambda's avatar protolambda Committed by GitHub

op-supervisor: Fix chain id translation (#13733)

* op-supervisor: fix chain ID to index translation

* op-supervisor: fix chain index to ID translation
parent ce2ce43b
......@@ -150,7 +150,7 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf
// For each chain initialize a chain processor service,
// after cross-unsafe workers are ready to receive updates
for _, chainID := range chains {
logProcessor := processors.NewLogProcessor(chainID, su.chainDBs)
logProcessor := processors.NewLogProcessor(chainID, su.chainDBs, su.depSet)
chainProcessor := processors.NewChainProcessor(su.logger, chainID, logProcessor, su.chainDBs, su.onIndexedLocalUnsafeData)
su.chainProcessors.Set(chainID, chainProcessor)
}
......
......@@ -6,6 +6,7 @@ import (
"strings"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
......@@ -68,8 +69,8 @@ func (g *graph) addEdge(from, to node) {
// succeeds if and only if a graph is acyclic.
//
// Returns nil if no cycles are found or ErrCycle if a cycle is detected.
func HazardCycleChecks(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) error {
g, err := buildGraph(d, inTimestamp, hazards)
func HazardCycleChecks(depSet depset.ChainIDFromIndex, d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) error {
g, err := buildGraph(depSet, d, inTimestamp, hazards)
if err != nil {
return err
}
......@@ -81,7 +82,7 @@ func HazardCycleChecks(d CycleCheckDeps, inTimestamp uint64, hazards map[types.C
// Returns:
// - map of chain index to its log count
// - map of chain index to map of log index to executing message (nil if doesn't exist or ignored)
func gatherLogs(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) (
func gatherLogs(depSet depset.ChainIDFromIndex, d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) (
map[types.ChainIndex]uint32,
map[types.ChainIndex]map[uint32]*types.ExecutingMessage,
error,
......@@ -90,8 +91,10 @@ func gatherLogs(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainInd
execMsgs := make(map[types.ChainIndex]map[uint32]*types.ExecutingMessage)
for hazardChainIndex, hazardBlock := range hazards {
// TODO(#11105): translate chain index to chain ID
hazardChainID := types.ChainIDFromUInt64(uint64(hazardChainIndex))
hazardChainID, err := depSet.ChainIDFromIndex(hazardChainIndex)
if err != nil {
return nil, nil, err
}
bl, logCount, msgs, err := d.OpenBlock(hazardChainID, hazardBlock.Number)
if err != nil {
return nil, nil, fmt.Errorf("failed to open block: %w", err)
......@@ -127,14 +130,14 @@ func gatherLogs(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainInd
}
// buildGraph constructs a dependency graph from the hazard blocks.
func buildGraph(d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) (*graph, error) {
func buildGraph(depSet depset.ChainIDFromIndex, d CycleCheckDeps, inTimestamp uint64, hazards map[types.ChainIndex]types.BlockSeal) (*graph, error) {
g := &graph{
inDegree0: make(map[node]struct{}),
inDegreeNon0: make(map[node]uint32),
outgoingEdges: make(map[node][]node),
}
logCounts, execMsgs, err := gatherLogs(d, inTimestamp, hazards)
logCounts, execMsgs, err := gatherLogs(depSet, d, inTimestamp, hazards)
if err != nil {
return nil, err
}
......
......@@ -9,9 +9,24 @@ import (
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
type testDepSet struct {
mapping map[types.ChainIndex]types.ChainID
}
func (t testDepSet) ChainIDFromIndex(index types.ChainIndex) (types.ChainID, error) {
v, ok := t.mapping[index]
if !ok {
return types.ChainID{}, types.ErrUnknownChain
}
return v, nil
}
var _ depset.ChainIDFromIndex = (*testDepSet)(nil)
type mockCycleCheckDeps struct {
openBlockFn func(chainID types.ChainID, blockNum uint64) (eth.BlockRef, uint32, map[uint32]*types.ExecutingMessage, error)
}
......@@ -78,8 +93,15 @@ func runHazardCycleChecksTestCase(t *testing.T, tc hazardCycleChecksTestCase) {
}
}
depSet := &testDepSet{
mapping: make(map[types.ChainIndex]types.ChainID),
}
for chainStr := range tc.chainBlocks {
index := chainIndex(chainStr)
depSet.mapping[index] = types.ChainIDFromUInt64(uint64(index))
}
// Run the test
err := HazardCycleChecks(deps, 100, hazards)
err := HazardCycleChecks(depSet, deps, 100, hazards)
// No error expected
if tc.expectErr == nil {
......
......@@ -185,11 +185,17 @@ func (m mockDependencySet) ChainIDFromIndex(index types.ChainIndex) (types.Chain
if m.chainIDFromIndexfn != nil {
return m.chainIDFromIndexfn()
}
return types.ChainID{}, nil
id := types.ChainIDFromUInt64(uint64(index) - 1000)
return id, nil
}
func (m mockDependencySet) ChainIndexFromID(chain types.ChainID) (types.ChainIndex, error) {
return types.ChainIndex(0), nil
v, err := chain.ToUInt32()
if err != nil {
return 0, err
}
// offset, so we catch improper manual conversion that doesn't apply this offset
return types.ChainIndex(v + 1000), nil
}
func (m mockDependencySet) Chains() []types.ChainID {
......
......@@ -89,7 +89,7 @@ func scopedCrossSafeUpdate(logger log.Logger, chainID types.ChainID, d CrossSafe
if err := HazardSafeFrontierChecks(d, candidateScope.ID(), hazards); err != nil {
return candidateScope, fmt.Errorf("failed to verify block %s in cross-safe frontier: %w", candidate, err)
}
if err := HazardCycleChecks(d, candidate.Time, hazards); err != nil {
if err := HazardCycleChecks(d.DependencySet(), d, candidate.Time, hazards); err != nil {
return candidateScope, fmt.Errorf("failed to verify block %s in cross-safe check for cycle hazards: %w", candidate, err)
}
......
......@@ -60,7 +60,7 @@ func CrossUnsafeUpdate(ctx context.Context, logger log.Logger, chainID types.Cha
if err := HazardUnsafeFrontierChecks(d, hazards); err != nil {
return fmt.Errorf("failed to verify block %s in cross-unsafe frontier: %w", candidate, err)
}
if err := HazardCycleChecks(d, candidate.Timestamp, hazards); err != nil {
if err := HazardCycleChecks(d.DependencySet(), d, candidate.Timestamp, hazards); err != nil {
return fmt.Errorf("failed to verify block %s in cross-unsafe check for cycle hazards: %w", candidate, err)
}
......
......@@ -200,7 +200,7 @@ func (l *logContext) processEntry(entry Entry) error {
return err
}
l.execMsg = &types.ExecutingMessage{
Chain: types.ChainIndex(link.chain), // TODO(#11105): translate chain ID to chain index
Chain: types.ChainIndex(link.chain),
BlockNum: link.blockNum,
LogIdx: link.logIdx,
Timestamp: link.timestamp,
......
......@@ -33,9 +33,18 @@ type DependencySet interface {
// See CanExecuteAt and CanInitiateAt to check if a chain may message at a given time.
HasChain(chainID types.ChainID) bool
ChainIndexFromID(id types.ChainID) (types.ChainIndex, error)
ChainIndexFromID
ChainIDFromIndex
}
type ChainIndexFromID interface {
// ChainIndexFromID converts a ChainID to a ChainIndex.
ChainIndexFromID(id types.ChainID) (types.ChainIndex, error)
}
type ChainIDFromIndex interface {
// ChainIDFromIndex converts a ChainIndex to a ChainID.
ChainIDFromIndex(index types.ChainIndex) (types.ChainID, error)
}
......@@ -119,7 +119,7 @@ func (ds *StaticConfigDependencySet) HasChain(chainID types.ChainID) bool {
func (ds *StaticConfigDependencySet) ChainIndexFromID(id types.ChainID) (types.ChainIndex, error) {
dep, ok := ds.dependencies[id]
if !ok {
return 0, types.ErrUnknownChain
return 0, fmt.Errorf("failed to translate chain ID %s to chain index: %w", id, types.ErrUnknownChain)
}
return dep.ChainIndex, nil
}
......@@ -127,7 +127,7 @@ func (ds *StaticConfigDependencySet) ChainIndexFromID(id types.ChainID) (types.C
func (ds *StaticConfigDependencySet) ChainIDFromIndex(index types.ChainIndex) (types.ChainID, error) {
id, ok := ds.indexToID[index]
if !ok {
return types.ChainID{}, types.ErrUnknownChain
return types.ChainID{}, fmt.Errorf("failed to translate chain index %s to chain ID: %w", index, types.ErrUnknownChain)
}
return id, nil
}
......@@ -7,12 +7,13 @@ import (
"github.com/ethereum/go-ethereum/core/types/interoptypes"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
type EventDecoderFn func(*ethTypes.Log) (*types.ExecutingMessage, error)
type EventDecoderFn func(*ethTypes.Log, depset.ChainIndexFromID) (*types.ExecutingMessage, error)
func DecodeExecutingMessageLog(l *ethTypes.Log) (*types.ExecutingMessage, error) {
func DecodeExecutingMessageLog(l *ethTypes.Log, depSet depset.ChainIndexFromID) (*types.ExecutingMessage, error) {
if l.Address != params.InteropCrossL2InboxAddress {
return nil, nil
}
......@@ -27,9 +28,12 @@ func DecodeExecutingMessageLog(l *ethTypes.Log) (*types.ExecutingMessage, error)
return nil, fmt.Errorf("invalid executing message: %w", err)
}
logHash := types.PayloadHashToLogHash(msg.PayloadHash, msg.Identifier.Origin)
index, err := depSet.ChainIndexFromID(types.ChainID(msg.Identifier.ChainID))
if err != nil {
return nil, err
}
return &types.ExecutingMessage{
// TODO(#11105): translate chain index to chain ID
Chain: types.ChainIndex(msg.Identifier.ChainID.Uint64()),
Chain: index,
BlockNum: msg.Identifier.BlockNumber,
LogIdx: msg.Identifier.LogIndex,
Timestamp: msg.Identifier.Timestamp,
......
......@@ -9,9 +9,24 @@ import (
"github.com/ethereum/go-ethereum/common"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
type testDepSet struct {
mapping map[types.ChainID]types.ChainIndex
}
func (t *testDepSet) ChainIndexFromID(id types.ChainID) (types.ChainIndex, error) {
v, ok := t.mapping[id]
if !ok {
return 0, types.ErrUnknownChain
}
return v, nil
}
var _ depset.ChainIndexFromID = (*testDepSet)(nil)
func TestDecodeExecutingMessageLog(t *testing.T) {
data := `
{
......@@ -32,7 +47,11 @@ func TestDecodeExecutingMessageLog(t *testing.T) {
var logEvent ethTypes.Log
require.NoError(t, json.Unmarshal([]byte(data), &logEvent))
msg, err := DecodeExecutingMessageLog(&logEvent)
msg, err := DecodeExecutingMessageLog(&logEvent, &testDepSet{
mapping: map[types.ChainID]types.ChainIndex{
types.ChainIDFromUInt64(900200): types.ChainIndex(123),
},
})
require.NoError(t, err)
require.NotNil(t, msg)
......@@ -53,5 +72,5 @@ func TestDecodeExecutingMessageLog(t *testing.T) {
require.Equal(t, uint64(4509), msg.BlockNum)
require.Equal(t, uint32(0), msg.LogIdx)
require.Equal(t, uint64(1730467171), msg.Timestamp)
require.Equal(t, types.ChainIndex(900200), msg.Chain)
require.Equal(t, types.ChainIndex(123), msg.Chain)
}
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
......@@ -26,13 +27,15 @@ type logProcessor struct {
chain types.ChainID
logStore LogStorage
eventDecoder EventDecoderFn
depSet depset.ChainIndexFromID
}
func NewLogProcessor(chain types.ChainID, logStore LogStorage) LogProcessor {
func NewLogProcessor(chain types.ChainID, logStore LogStorage, depSet depset.ChainIndexFromID) LogProcessor {
return &logProcessor{
chain: chain,
logStore: logStore,
eventDecoder: DecodeExecutingMessageLog,
depSet: depSet,
}
}
......@@ -44,7 +47,7 @@ func (p *logProcessor) ProcessLogs(_ context.Context, block eth.BlockRef, rcpts
// log hash represents the hash of *this* log as a potentially initiating message
logHash := logToLogHash(l)
// The log may be an executing message emitted by the CrossL2Inbox
execMsg, err := p.eventDecoder(l)
execMsg, err := p.eventDecoder(l, p.depSet)
if err != nil {
return fmt.Errorf("invalid log %d from block %s: %w", l.Index, block.ID(), err)
}
......
......@@ -5,12 +5,15 @@ import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum/go-ethereum/common"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
)
var logProcessorChainID = types.ChainIDFromUInt64(4)
......@@ -23,9 +26,15 @@ func TestLogProcessor(t *testing.T) {
Hash: common.Hash{0x11},
Time: 1111,
}
depSet := &testDepSet{
mapping: map[types.ChainID]types.ChainIndex{
types.ChainIDFromUInt64(100): 4,
},
}
t.Run("NoOutputWhenLogsAreEmpty", func(t *testing.T) {
store := &stubLogStorage{}
processor := NewLogProcessor(logProcessorChainID, store)
processor := NewLogProcessor(logProcessorChainID, store, depSet)
err := processor.ProcessLogs(ctx, block1, ethTypes.Receipts{})
require.NoError(t, err)
......@@ -59,7 +68,7 @@ func TestLogProcessor(t *testing.T) {
},
}
store := &stubLogStorage{}
processor := NewLogProcessor(logProcessorChainID, store)
processor := NewLogProcessor(logProcessorChainID, store, depSet)
err := processor.ProcessLogs(ctx, block1, rcpts)
require.NoError(t, err)
......@@ -108,15 +117,15 @@ func TestLogProcessor(t *testing.T) {
},
}
execMsg := &types.ExecutingMessage{
Chain: 4, // TODO(#11105): translate chain ID to chain index
Chain: 4,
BlockNum: 6,
LogIdx: 8,
Timestamp: 10,
Hash: common.Hash{0xaa},
}
store := &stubLogStorage{}
processor := NewLogProcessor(types.ChainID{4}, store).(*logProcessor)
processor.eventDecoder = func(l *ethTypes.Log) (*types.ExecutingMessage, error) {
processor := NewLogProcessor(types.ChainID{4}, store, depSet).(*logProcessor)
processor.eventDecoder = func(l *ethTypes.Log, translator depset.ChainIndexFromID) (*types.ExecutingMessage, error) {
require.Equal(t, rcpts[0].Logs[0], l)
return execMsg, nil
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment