eth_client_test.go 4.08 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
	"github.com/ethereum-optimism/optimism/op-node/client"
14
	"github.com/ethereum-optimism/optimism/op-node/eth"
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
	"github.com/ethereum-optimism/optimism/op-node/rollup"
	"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"
)

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

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

34
func (m *mockRPC) EthSubscribe(ctx context.Context, channel any, args ...any) (ethereum.Subscription, error) {
35 36 37 38 39 40 41 42 43 44 45 46 47 48
	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,
49
	PayloadsCacheSize:     10,
50 51 52
	MaxRequestsPerBatch:   20,
	MaxConcurrentRequests: 10,
	TrustRPC:              false,
53
	MustBePostMerge:       false,
54 55 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
}

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{
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
		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(),
98 99 100 101 102 103 104
	}
	return hdr, rhdr
}

func TestEthClient_InfoByHash(t *testing.T) {
	m := new(mockRPC)
	_, rhdr := randHeader()
105
	expectedInfo, _ := rhdr.Info(true, false)
106 107
	ctx := context.Background()
	m.On("CallContext", ctx, new(*rpcHeader),
108
		"eth_getBlockByHash", []any{rhdr.Hash, false}).Run(func(args mock.Arguments) {
109 110 111 112
		*args[1].(**rpcHeader) = rhdr
	}).Return([]error{nil})
	s, err := NewEthClient(m, nil, nil, testEthClientConfig)
	require.NoError(t, err)
113
	info, err := s.InfoByHash(ctx, rhdr.Hash)
114 115 116 117
	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
118
	info, err = s.InfoByHash(ctx, rhdr.Hash)
119 120 121 122 123 124 125 126
	require.NoError(t, err)
	require.Equal(t, info, expectedInfo)
	m.Mock.AssertExpectations(t)
}

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