header_traversal_test.go 6.03 KB
Newer Older
1 2 3 4 5 6
package node

import (
	"math/big"
	"testing"

Hamdi Allam's avatar
Hamdi Allam committed
7
	"github.com/ethereum-optimism/optimism/indexer/bigint"
8
	"github.com/ethereum-optimism/optimism/op-service/testutils"
9
	"github.com/stretchr/testify/require"
10 11 12 13

	"github.com/ethereum/go-ethereum/core/types"
)

14
// make a set of headers which chain correctly
15 16
func makeHeaders(numHeaders uint64, prevHeader *types.Header) []types.Header {
	headers := make([]types.Header, numHeaders)
17 18 19 20
	for i := range headers {
		if i == 0 {
			if prevHeader == nil {
				// genesis
21
				headers[i] = types.Header{Number: big.NewInt(0)}
22 23
			} else {
				// chain onto the previous header
24
				headers[i] = types.Header{Number: big.NewInt(prevHeader.Number.Int64() + 1)}
25 26 27
				headers[i].ParentHash = prevHeader.Hash()
			}
		} else {
28 29
			headers[i] = types.Header{Number: big.NewInt(headers[i-1].Number.Int64() + 1)}
			headers[i].ParentHash = headers[i-1].Hash()
30 31 32 33
		}
	}

	return headers
34 35
}

Hamdi Allam's avatar
Hamdi Allam committed
36
func TestHeaderTraversalNextHeadersNoOp(t *testing.T) {
37 38
	client := &testutils.MockClient{}
	t.Cleanup(func() { client.AssertExpectations(t) })
39

40
	// start from block 10 as the latest fetched block
Hamdi Allam's avatar
Hamdi Allam committed
41 42 43 44 45
	LastTraversedHeader := &types.Header{Number: big.NewInt(10)}
	headerTraversal := NewHeaderTraversal(client, LastTraversedHeader, bigint.Zero)

	require.Nil(t, headerTraversal.LatestHeader())
	require.NotNil(t, headerTraversal.LastTraversedHeader())
46

47
	// no new headers when matched with head
48
	client.ExpectHeaderByNumber(nil, LastTraversedHeader, nil)
Hamdi Allam's avatar
Hamdi Allam committed
49
	headers, err := headerTraversal.NextHeaders(100)
50 51
	require.NoError(t, err)
	require.Empty(t, headers)
Hamdi Allam's avatar
Hamdi Allam committed
52 53 54 55

	require.NotNil(t, headerTraversal.LatestHeader())
	require.NotNil(t, headerTraversal.LastTraversedHeader())
	require.Equal(t, LastTraversedHeader.Number.Uint64(), headerTraversal.LatestHeader().Number.Uint64())
56 57
}

Hamdi Allam's avatar
Hamdi Allam committed
58
func TestHeaderTraversalNextHeadersCursored(t *testing.T) {
59 60
	client := &testutils.MockClient{}
	t.Cleanup(func() { client.AssertExpectations(t) })
61

62 63 64 65 66
	rpc := &testutils.MockRPC{}
	client.Mock.On("RPC").Return(rpc)
	t.Cleanup(func() { rpc.AssertExpectations(t) })

	// start from genesis, 7 available headers
Hamdi Allam's avatar
Hamdi Allam committed
67
	headerTraversal := NewHeaderTraversal(client, nil, bigint.Zero)
68
	client.ExpectHeaderByNumber(nil, &types.Header{Number: big.NewInt(7)}, nil)
69

Hamdi Allam's avatar
Hamdi Allam committed
70
	headers := makeHeaders(10, nil)
71 72 73 74
	rpcElems := makeHeaderRpcElems(headers[0].Number, headers[9].Number)
	for i := 0; i < len(rpcElems); i++ {
		rpcElems[i].Result = &headers[i]
	}
Hamdi Allam's avatar
Hamdi Allam committed
75

76 77
	// traverse blocks [0..4]. Latest reported is 7
	rpc.ExpectBatchCallContext(rpcElems[:5], nil)
Hamdi Allam's avatar
Hamdi Allam committed
78
	_, err := headerTraversal.NextHeaders(5)
79
	require.NoError(t, err)
80

Hamdi Allam's avatar
Hamdi Allam committed
81 82 83 84
	require.Equal(t, uint64(7), headerTraversal.LatestHeader().Number.Uint64())
	require.Equal(t, uint64(4), headerTraversal.LastTraversedHeader().Number.Uint64())

	// blocks [5..9]. Latest Reported is 9
85 86
	client.ExpectHeaderByNumber(nil, &headers[9], nil)
	rpc.ExpectBatchCallContext(rpcElems[5:], nil)
Hamdi Allam's avatar
Hamdi Allam committed
87
	_, err = headerTraversal.NextHeaders(5)
88
	require.NoError(t, err)
Hamdi Allam's avatar
Hamdi Allam committed
89 90 91

	require.Equal(t, uint64(9), headerTraversal.LatestHeader().Number.Uint64())
	require.Equal(t, uint64(9), headerTraversal.LastTraversedHeader().Number.Uint64())
92 93
}

Hamdi Allam's avatar
Hamdi Allam committed
94
func TestHeaderTraversalNextHeadersMaxSize(t *testing.T) {
95 96
	client := &testutils.MockClient{}
	t.Cleanup(func() { client.AssertExpectations(t) })
97

98 99 100
	rpc := &testutils.MockRPC{}
	client.Mock.On("RPC").Return(rpc)
	t.Cleanup(func() { rpc.AssertExpectations(t) })
101

102 103 104
	// start from genesis, 100 available headers
	headerTraversal := NewHeaderTraversal(client, nil, bigint.Zero)
	client.ExpectHeaderByNumber(nil, &types.Header{Number: big.NewInt(100)}, nil)
105

106
	headers := makeHeaders(5, nil)
107 108 109 110 111 112 113
	rpcElems := makeHeaderRpcElems(headers[0].Number, headers[4].Number)
	for i := 0; i < len(rpcElems); i++ {
		rpcElems[i].Result = &headers[i]
	}

	// traverse only 5 headers [0..4]
	rpc.ExpectBatchCallContext(rpcElems, nil)
Hamdi Allam's avatar
Hamdi Allam committed
114
	headers, err := headerTraversal.NextHeaders(5)
115 116
	require.NoError(t, err)
	require.Len(t, headers, 5)
117

Hamdi Allam's avatar
Hamdi Allam committed
118 119 120
	require.Equal(t, uint64(100), headerTraversal.LatestHeader().Number.Uint64())
	require.Equal(t, uint64(4), headerTraversal.LastTraversedHeader().Number.Uint64())

121
	// clamped by the supplied size. FinalizedHeight == 100
122
	client.ExpectHeaderByNumber(nil, &types.Header{Number: big.NewInt(100)}, nil)
123
	headers = makeHeaders(10, &headers[len(headers)-1])
124 125 126 127 128 129
	rpcElems = makeHeaderRpcElems(headers[0].Number, headers[9].Number)
	for i := 0; i < len(rpcElems); i++ {
		rpcElems[i].Result = &headers[i]
	}

	rpc.ExpectBatchCallContext(rpcElems, nil)
Hamdi Allam's avatar
Hamdi Allam committed
130
	headers, err = headerTraversal.NextHeaders(10)
131 132
	require.NoError(t, err)
	require.Len(t, headers, 10)
Hamdi Allam's avatar
Hamdi Allam committed
133 134 135

	require.Equal(t, uint64(100), headerTraversal.LatestHeader().Number.Uint64())
	require.Equal(t, uint64(14), headerTraversal.LastTraversedHeader().Number.Uint64())
136 137
}

138
func TestHeaderTraversalMismatchedProviderStateError(t *testing.T) {
139 140 141 142 143 144
	client := &testutils.MockClient{}
	t.Cleanup(func() { client.AssertExpectations(t) })

	rpc := &testutils.MockRPC{}
	client.Mock.On("RPC").Return(rpc)
	t.Cleanup(func() { rpc.AssertExpectations(t) })
145 146

	// start from genesis
Hamdi Allam's avatar
Hamdi Allam committed
147
	headerTraversal := NewHeaderTraversal(client, nil, bigint.Zero)
148 149 150

	// blocks [0..4]
	headers := makeHeaders(5, nil)
151 152 153 154 155 156 157
	rpcElems := makeHeaderRpcElems(headers[0].Number, headers[4].Number)
	for i := 0; i < len(rpcElems); i++ {
		rpcElems[i].Result = &headers[i]
	}

	client.ExpectHeaderByNumber(nil, &headers[4], nil)
	rpc.ExpectBatchCallContext(rpcElems, nil)
Hamdi Allam's avatar
Hamdi Allam committed
158
	headers, err := headerTraversal.NextHeaders(5)
159 160
	require.NoError(t, err)
	require.Len(t, headers, 5)
161

162 163 164 165 166 167 168 169 170 171 172 173 174 175
	// Build on the wrong previous header, corrupting hashes
	prevHeader := headers[len(headers)-2]
	prevHeader.Number = headers[len(headers)-1].Number
	headers = makeHeaders(5, &prevHeader)
	rpcElems = makeHeaderRpcElems(headers[0].Number, headers[4].Number)
	for i := 0; i < len(rpcElems); i++ {
		rpcElems[i].Result = &headers[i]
	}

	// More headers are available (Latest == 9), but the mismatches will the last
	// traversed header
	client.ExpectHeaderByNumber(nil, &types.Header{Number: big.NewInt(9)}, nil)
	rpc.ExpectBatchCallContext(rpcElems[:2], nil)
	headers, err = headerTraversal.NextHeaders(2)
176 177
	require.Nil(t, headers)
	require.Equal(t, ErrHeaderTraversalAndProviderMismatchedState, err)
178
}