eth_client_test.go 4.13 KB
Newer Older
1 2 3 4 5 6 7 8
package sources

import (
	"context"
	"math/big"
	"math/rand"
	"testing"

9
	"github.com/ethereum/go-ethereum"
10 11 12
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"

13 14 15 16
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/rpc"
17 18 19 20

	"github.com/ethereum-optimism/optimism/op-node/client"
	"github.com/ethereum-optimism/optimism/op-node/eth"
	"github.com/ethereum-optimism/optimism/op-node/rollup"
21 22 23 24 25 26 27 28 29 30
)

type mockRPC struct {
	mock.Mock
}

func (m *mockRPC) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
	return m.MethodCalled("BatchCallContext", ctx, b).Get(0).([]error)[0]
}

31
func (m *mockRPC) CallContext(ctx context.Context, result any, method string, args ...any) error {
32 33 34
	return m.MethodCalled("CallContext", ctx, result, method, args).Get(0).([]error)[0]
}

35
func (m *mockRPC) EthSubscribe(ctx context.Context, channel any, args ...any) (ethereum.Subscription, error) {
36 37 38 39 40 41 42 43 44 45 46 47 48 49
	called := m.MethodCalled("EthSubscribe", channel, args)
	return called.Get(0).(*rpc.ClientSubscription), called.Get(1).([]error)[0]
}

func (m *mockRPC) Close() {
	m.MethodCalled("Close")
}

var _ client.RPC = (*mockRPC)(nil)

var testEthClientConfig = &EthClientConfig{
	ReceiptsCacheSize:     10,
	TransactionsCacheSize: 10,
	HeadersCacheSize:      10,
50
	PayloadsCacheSize:     10,
51 52 53
	MaxRequestsPerBatch:   20,
	MaxConcurrentRequests: 10,
	TrustRPC:              false,
54
	MustBePostMerge:       false,
55
	RPCProviderKind:       RPCKindBasic,
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
}

func randHash() (out common.Hash) {
	rand.Read(out[:])
	return out
}

func randHeader() (*types.Header, *rpcHeader) {
	hdr := &types.Header{
		ParentHash:  randHash(),
		UncleHash:   randHash(),
		Coinbase:    common.Address{},
		Root:        randHash(),
		TxHash:      randHash(),
		ReceiptHash: randHash(),
		Bloom:       types.Bloom{},
		Difficulty:  big.NewInt(42),
		Number:      big.NewInt(1234),
		GasLimit:    0,
		GasUsed:     0,
		Time:        123456,
		Extra:       make([]byte, 0),
		MixDigest:   randHash(),
		Nonce:       types.BlockNonce{},
		BaseFee:     big.NewInt(100),
	}
	rhdr := &rpcHeader{
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
		ParentHash:  hdr.ParentHash,
		UncleHash:   hdr.UncleHash,
		Coinbase:    hdr.Coinbase,
		Root:        hdr.Root,
		TxHash:      hdr.TxHash,
		ReceiptHash: hdr.ReceiptHash,
		Bloom:       eth.Bytes256(hdr.Bloom),
		Difficulty:  *(*hexutil.Big)(hdr.Difficulty),
		Number:      hexutil.Uint64(hdr.Number.Uint64()),
		GasLimit:    hexutil.Uint64(hdr.GasLimit),
		GasUsed:     hexutil.Uint64(hdr.GasUsed),
		Time:        hexutil.Uint64(hdr.Time),
		Extra:       hdr.Extra,
		MixDigest:   hdr.MixDigest,
		Nonce:       hdr.Nonce,
		BaseFee:     (*hexutil.Big)(hdr.BaseFee),
		Hash:        hdr.Hash(),
100 101 102 103 104 105 106
	}
	return hdr, rhdr
}

func TestEthClient_InfoByHash(t *testing.T) {
	m := new(mockRPC)
	_, rhdr := randHeader()
107
	expectedInfo, _ := rhdr.Info(true, false)
108 109
	ctx := context.Background()
	m.On("CallContext", ctx, new(*rpcHeader),
110
		"eth_getBlockByHash", []any{rhdr.Hash, false}).Run(func(args mock.Arguments) {
111 112 113 114
		*args[1].(**rpcHeader) = rhdr
	}).Return([]error{nil})
	s, err := NewEthClient(m, nil, nil, testEthClientConfig)
	require.NoError(t, err)
115
	info, err := s.InfoByHash(ctx, rhdr.Hash)
116 117 118 119
	require.NoError(t, err)
	require.Equal(t, info, expectedInfo)
	m.Mock.AssertExpectations(t)
	// Again, without expecting any calls from the mock, the cache will return the block
120
	info, err = s.InfoByHash(ctx, rhdr.Hash)
121 122 123 124 125 126 127 128
	require.NoError(t, err)
	require.Equal(t, info, expectedInfo)
	m.Mock.AssertExpectations(t)
}

func TestEthClient_InfoByNumber(t *testing.T) {
	m := new(mockRPC)
	_, rhdr := randHeader()
129 130
	expectedInfo, _ := rhdr.Info(true, false)
	n := rhdr.Number
131 132
	ctx := context.Background()
	m.On("CallContext", ctx, new(*rpcHeader),
133
		"eth_getBlockByNumber", []any{n.String(), false}).Run(func(args mock.Arguments) {
134 135
		*args[1].(**rpcHeader) = rhdr
	}).Return([]error{nil})
136
	s, err := NewL1Client(m, nil, nil, L1ClientDefaultConfig(&rollup.Config{SeqWindowSize: 10}, true, RPCKindBasic))
137
	require.NoError(t, err)
138
	info, err := s.InfoByNumber(ctx, uint64(n))
139 140 141 142
	require.NoError(t, err)
	require.Equal(t, info, expectedInfo)
	m.Mock.AssertExpectations(t)
}