backend_test.go 5.36 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 16 17 18 19 20 21 22 23 24 25
	"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"

	"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"
26
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors"
27
	"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/syncnode"
28 29 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()
	chainA := types.ChainIDFromUInt64(900)
	chainB := types.ChainIDFromUInt64(901)
37 38 39 40 41 42 43 44 45 46 47
	depSet, err := depset.NewStaticConfigDependencySet(
		map[types.ChainID]*depset.StaticConfigDependency{
			chainA: {
				ChainIndex:     900,
				ActivationTime: 42,
				HistoryMinTime: 100,
			},
			chainB: {
				ChainIndex:     901,
				ActivationTime: 30,
				HistoryMinTime: 20,
48
			},
49 50 51 52 53 54 55 56 57
		})
	require.NoError(t, err)
	cfg := &config.Config{
		Version:               "test",
		LogConfig:             oplog.CLIConfig{},
		MetricsConfig:         opmetrics.CLIConfig{},
		PprofConfig:           oppprof.CLIConfig{},
		RPC:                   oprpc.CLIConfig{},
		DependencySetSource:   depSet,
58 59
		SynchronousProcessors: true,
		MockRun:               false,
60
		SyncSources:           &syncnode.CLISyncNodes{},
61 62 63 64 65 66 67
		Datadir:               dataDir,
	}

	b, err := NewSupervisorBackend(context.Background(), logger, m, cfg)
	require.NoError(t, err)
	t.Log("initialized!")

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

	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,
	}

84
	b.AttachL1Source(l1Src)
85 86 87 88 89 90 91 92 93 94 95 96 97
	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!")

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

101 102 103 104 105 106 107
	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)
108

109
	err = b.UpdateLocalUnsafe(context.Background(), chainA, blockY)
110 111 112
	require.NoError(t, err)
	// 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.
113 114
	proc, _ := b.chainProcessors.Get(chainA)
	proc.ProcessToHead()
115

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

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

126 127 128
	v, err := b.CrossUnsafe(context.Background(), chainA)
	require.NoError(t, err, "have a functioning cross unsafe value now")
	require.Equal(t, blockX.ID(), v)
129 130 131 132 133

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

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)
}