header_traversal_test.go 5.15 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/stretchr/testify/mock"
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
	client := new(MockEthClient)
38

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

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

46
	// no new headers when matched with head
Hamdi Allam's avatar
Hamdi Allam committed
47 48
	client.On("BlockHeaderByNumber", (*big.Int)(nil)).Return(LastTraversedHeader, nil)
	headers, err := headerTraversal.NextHeaders(100)
49 50
	require.NoError(t, err)
	require.Empty(t, headers)
Hamdi Allam's avatar
Hamdi Allam committed
51 52 53 54

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

Hamdi Allam's avatar
Hamdi Allam committed
57
func TestHeaderTraversalNextHeadersCursored(t *testing.T) {
58 59
	client := new(MockEthClient)

60
	// start from genesis
Hamdi Allam's avatar
Hamdi Allam committed
61
	headerTraversal := NewHeaderTraversal(client, nil, bigint.Zero)
62

Hamdi Allam's avatar
Hamdi Allam committed
63 64 65 66 67 68
	headers := makeHeaders(10, nil)

	// blocks [0..4]. Latest reported is 7
	client.On("BlockHeaderByNumber", (*big.Int)(nil)).Return(&headers[7], nil).Times(1) // Times so that we can override next
	client.On("BlockHeadersByRange", mock.MatchedBy(bigint.Matcher(0)), mock.MatchedBy(bigint.Matcher(4))).Return(headers[:5], nil)
	_, err := headerTraversal.NextHeaders(5)
69
	require.NoError(t, err)
70

Hamdi Allam's avatar
Hamdi Allam committed
71 72 73 74 75 76 77
	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
	client.On("BlockHeaderByNumber", (*big.Int)(nil)).Return(&headers[9], nil)
	client.On("BlockHeadersByRange", mock.MatchedBy(bigint.Matcher(5)), mock.MatchedBy(bigint.Matcher(9))).Return(headers[5:], nil)
	_, err = headerTraversal.NextHeaders(5)
78
	require.NoError(t, err)
Hamdi Allam's avatar
Hamdi Allam committed
79 80 81

	require.Equal(t, uint64(9), headerTraversal.LatestHeader().Number.Uint64())
	require.Equal(t, uint64(9), headerTraversal.LastTraversedHeader().Number.Uint64())
82 83
}

Hamdi Allam's avatar
Hamdi Allam committed
84
func TestHeaderTraversalNextHeadersMaxSize(t *testing.T) {
85
	client := new(MockEthClient)
86 87

	// start from genesis
Hamdi Allam's avatar
Hamdi Allam committed
88
	headerTraversal := NewHeaderTraversal(client, nil, bigint.Zero)
89

90
	// 100 "available" headers
91
	client.On("BlockHeaderByNumber", (*big.Int)(nil)).Return(&types.Header{Number: big.NewInt(100)}, nil)
92

93 94
	// clamped by the supplied size
	headers := makeHeaders(5, nil)
Hamdi Allam's avatar
Hamdi Allam committed
95
	client.On("BlockHeadersByRange", mock.MatchedBy(bigint.Matcher(0)), mock.MatchedBy(bigint.Matcher(4))).Return(headers, nil)
Hamdi Allam's avatar
Hamdi Allam committed
96
	headers, err := headerTraversal.NextHeaders(5)
97 98
	require.NoError(t, err)
	require.Len(t, headers, 5)
99

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

103
	// clamped by the supplied size. FinalizedHeight == 100
104
	headers = makeHeaders(10, &headers[len(headers)-1])
Hamdi Allam's avatar
Hamdi Allam committed
105
	client.On("BlockHeadersByRange", mock.MatchedBy(bigint.Matcher(5)), mock.MatchedBy(bigint.Matcher(14))).Return(headers, nil)
Hamdi Allam's avatar
Hamdi Allam committed
106
	headers, err = headerTraversal.NextHeaders(10)
107 108
	require.NoError(t, err)
	require.Len(t, headers, 10)
Hamdi Allam's avatar
Hamdi Allam committed
109 110 111

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

114
func TestHeaderTraversalMismatchedProviderStateError(t *testing.T) {
115 116 117
	client := new(MockEthClient)

	// start from genesis
Hamdi Allam's avatar
Hamdi Allam committed
118
	headerTraversal := NewHeaderTraversal(client, nil, bigint.Zero)
119 120 121

	// blocks [0..4]
	headers := makeHeaders(5, nil)
122
	client.On("BlockHeaderByNumber", (*big.Int)(nil)).Return(&headers[4], nil).Times(1) // Times so that we can override next
Hamdi Allam's avatar
Hamdi Allam committed
123
	client.On("BlockHeadersByRange", mock.MatchedBy(bigint.Matcher(0)), mock.MatchedBy(bigint.Matcher(4))).Return(headers, nil)
Hamdi Allam's avatar
Hamdi Allam committed
124
	headers, err := headerTraversal.NextHeaders(5)
125 126
	require.NoError(t, err)
	require.Len(t, headers, 5)
127 128 129

	// blocks [5..9]. Next batch is not chained correctly (starts again from genesis)
	headers = makeHeaders(5, nil)
130
	client.On("BlockHeaderByNumber", (*big.Int)(nil)).Return(&types.Header{Number: big.NewInt(9)}, nil)
Hamdi Allam's avatar
Hamdi Allam committed
131
	client.On("BlockHeadersByRange", mock.MatchedBy(bigint.Matcher(5)), mock.MatchedBy(bigint.Matcher(9))).Return(headers, nil)
Hamdi Allam's avatar
Hamdi Allam committed
132
	headers, err = headerTraversal.NextHeaders(5)
133 134
	require.Nil(t, headers)
	require.Equal(t, ErrHeaderTraversalAndProviderMismatchedState, err)
135
}