backend_test.go 5.54 KB
Newer Older
1 2 3 4 5 6 7
package backend

import (
	"context"
	"path/filepath"
	"testing"

8
	"github.com/stretchr/testify/mock"
9 10 11 12 13 14 15
	"github.com/stretchr/testify/require"

	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/common"
	types2 "github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/log"

16
	"github.com/ethereum-optimism/optimism/op-node/rollup/event"
17 18 19 20 21 22 23 24 25 26
	"github.com/ethereum-optimism/optimism/op-service/eth"
	oplog "github.com/ethereum-optimism/optimism/op-service/log"
	opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
	"github.com/ethereum-optimism/optimism/op-service/oppprof"
	oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
	"github.com/ethereum-optimism/optimism/op-service/testlog"
	"github.com/ethereum-optimism/optimism/op-service/testutils"
	"github.com/ethereum-optimism/optimism/op-supervisor/config"
	"github.com/ethereum-optimism/optimism/op-supervisor/metrics"
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
27
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors"
28
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/superevents"
29
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/syncnode"
30 31 32 33 34 35 36
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)

func TestBackendLifetime(t *testing.T) {
	logger := testlog.Logger(t, log.LvlInfo)
	m := metrics.NoopMetrics
	dataDir := t.TempDir()
37 38
	chainA := eth.ChainIDFromUInt64(900)
	chainB := eth.ChainIDFromUInt64(901)
39
	depSet, err := depset.NewStaticConfigDependencySet(
40
		map[eth.ChainID]*depset.StaticConfigDependency{
41 42 43 44 45 46 47 48 49
			chainA: {
				ChainIndex:     900,
				ActivationTime: 42,
				HistoryMinTime: 100,
			},
			chainB: {
				ChainIndex:     901,
				ActivationTime: 30,
				HistoryMinTime: 20,
50
			},
51 52 53 54 55 56 57 58 59
		})
	require.NoError(t, err)
	cfg := &config.Config{
		Version:               "test",
		LogConfig:             oplog.CLIConfig{},
		MetricsConfig:         opmetrics.CLIConfig{},
		PprofConfig:           oppprof.CLIConfig{},
		RPC:                   oprpc.CLIConfig{},
		DependencySetSource:   depSet,
60 61
		SynchronousProcessors: true,
		MockRun:               false,
62
		SyncSources:           &syncnode.CLISyncNodes{},
63 64 65
		Datadir:               dataDir,
	}

66 67
	ex := event.NewGlobalSynchronous(context.Background())
	b, err := NewSupervisorBackend(context.Background(), logger, m, cfg, ex)
68 69 70
	require.NoError(t, err)
	t.Log("initialized!")

71
	l1Src := &testutils.MockL1Source{}
72
	src := &MockProcessorSource{}
73 74 75 76 77 78 79 80 81 82 83 84 85 86

	blockX := eth.BlockRef{
		Hash:       common.Hash{0xaa},
		Number:     0,
		ParentHash: common.Hash{}, // genesis has no parent hash
		Time:       10000,
	}
	blockY := eth.BlockRef{
		Hash:       common.Hash{0xbb},
		Number:     blockX.Number + 1,
		ParentHash: blockX.Hash,
		Time:       blockX.Time + 2,
	}

87
	b.AttachL1Source(l1Src)
88 89 90 91 92 93 94 95 96 97 98 99 100
	require.NoError(t, b.AttachProcessorSource(chainA, src))

	require.FileExists(t, filepath.Join(cfg.Datadir, "900", "log.db"), "must have logs DB 900")
	require.FileExists(t, filepath.Join(cfg.Datadir, "901", "log.db"), "must have logs DB 901")
	require.FileExists(t, filepath.Join(cfg.Datadir, "900", "local_safe.db"), "must have local safe DB 900")
	require.FileExists(t, filepath.Join(cfg.Datadir, "901", "local_safe.db"), "must have local safe DB 901")
	require.FileExists(t, filepath.Join(cfg.Datadir, "900", "cross_safe.db"), "must have cross safe DB 900")
	require.FileExists(t, filepath.Join(cfg.Datadir, "901", "cross_safe.db"), "must have cross safe DB 901")

	err = b.Start(context.Background())
	require.NoError(t, err)
	t.Log("started!")

101
	_, err = b.LocalUnsafe(context.Background(), chainA)
102
	require.ErrorIs(t, err, types.ErrFuture, "no data yet, need local-unsafe")
103

104 105 106 107 108 109 110
	src.ExpectBlockRefByNumber(0, blockX, nil)
	src.ExpectFetchReceipts(blockX.Hash, nil, nil)

	src.ExpectBlockRefByNumber(1, blockY, nil)
	src.ExpectFetchReceipts(blockY.Hash, nil, nil)

	src.ExpectBlockRefByNumber(2, eth.L1BlockRef{}, ethereum.NotFound)
111

112 113 114 115
	b.emitter.Emit(superevents.LocalUnsafeReceivedEvent{
		ChainID:        chainA,
		NewLocalUnsafe: blockY,
	})
116 117
	// Make the processing happen, so we can rely on the new chain information,
	// and not run into errors for future data that isn't mocked at this time.
118
	require.NoError(t, ex.Drain())
119

120
	_, err = b.CrossUnsafe(context.Background(), chainA)
121
	require.ErrorIs(t, err, types.ErrFuture, "still no data yet, need cross-unsafe")
122 123 124 125 126 127 128 129

	err = b.chainDBs.UpdateCrossUnsafe(chainA, types.BlockSeal{
		Hash:      blockX.Hash,
		Number:    blockX.Number,
		Timestamp: blockX.Time,
	})
	require.NoError(t, err)

130 131 132
	v, err := b.CrossUnsafe(context.Background(), chainA)
	require.NoError(t, err, "have a functioning cross unsafe value now")
	require.Equal(t, blockX.ID(), v)
133 134 135 136 137

	err = b.Stop(context.Background())
	require.NoError(t, err)
	t.Log("stopped!")
}
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

type MockProcessorSource struct {
	mock.Mock
}

var _ processors.Source = (*MockProcessorSource)(nil)

func (m *MockProcessorSource) FetchReceipts(ctx context.Context, blockHash common.Hash) (types2.Receipts, error) {
	out := m.Mock.Called(blockHash)
	return out.Get(0).(types2.Receipts), out.Error(1)
}

func (m *MockProcessorSource) ExpectFetchReceipts(hash common.Hash, receipts types2.Receipts, err error) {
	m.Mock.On("FetchReceipts", hash).Once().Return(receipts, err)
}

func (m *MockProcessorSource) BlockRefByNumber(ctx context.Context, num uint64) (eth.BlockRef, error) {
	out := m.Mock.Called(num)
	return out.Get(0).(eth.BlockRef), out.Error(1)
}

func (m *MockProcessorSource) ExpectBlockRefByNumber(num uint64, ref eth.BlockRef, err error) {
	m.Mock.On("BlockRefByNumber", num).Once().Return(ref, err)
}