1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package common
import (
"context"
"fmt"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
)
func TestNewL2Sources(t *testing.T) {
t.Run("NoSources", func(t *testing.T) {
logger := testlog.Logger(t, log.LevelInfo)
_, err := NewL2Sources(context.Background(), logger, nil, nil, nil)
require.ErrorIs(t, err, ErrNoSources)
})
t.Run("SingleSource", func(t *testing.T) {
logger := testlog.Logger(t, log.LevelDebug)
config, l2Rpc, experimentalRpc := chain(4)
src, err := NewL2Sources(context.Background(), logger,
[]*rollup.Config{config},
[]client.RPC{l2Rpc},
[]client.RPC{experimentalRpc})
require.NoError(t, err)
require.Len(t, src.Sources, 1)
require.True(t, src.Sources[uint64(4)].ExperimentalEnabled())
})
t.Run("MultipleSources", func(t *testing.T) {
logger := testlog.Logger(t, log.LevelDebug)
config1, l2Rpc1, experimentalRpc1 := chain(1)
config2, l2Rpc2, experimentalRpc2 := chain(2)
src, err := NewL2Sources(context.Background(), logger,
[]*rollup.Config{config1, config2},
[]client.RPC{l2Rpc1, l2Rpc2},
[]client.RPC{experimentalRpc1, experimentalRpc2})
require.NoError(t, err)
require.Len(t, src.Sources, 2)
require.True(t, src.Sources[uint64(1)].ExperimentalEnabled())
require.True(t, src.Sources[uint64(2)].ExperimentalEnabled())
})
t.Run("ExperimentalRPCsAreOptional", func(t *testing.T) {
logger := testlog.Logger(t, log.LevelDebug)
config1, l2Rpc1, _ := chain(1)
config2, l2Rpc2, experimentalRpc2 := chain(2)
src, err := NewL2Sources(context.Background(), logger,
[]*rollup.Config{config1, config2},
[]client.RPC{l2Rpc1, l2Rpc2},
[]client.RPC{experimentalRpc2})
require.NoError(t, err)
require.Len(t, src.Sources, 2)
require.Same(t, src.Sources[uint64(1)].RollupConfig(), config1)
require.False(t, src.Sources[uint64(1)].ExperimentalEnabled())
require.Same(t, src.Sources[uint64(2)].RollupConfig(), config2)
require.True(t, src.Sources[uint64(2)].ExperimentalEnabled())
})
t.Run("RollupMissingL2URL", func(t *testing.T) {
logger := testlog.Logger(t, log.LevelDebug)
config1, _, _ := chain(1)
config2, l2Rpc2, experimentalRpc2 := chain(2)
_, err := NewL2Sources(context.Background(), logger,
[]*rollup.Config{config1, config2},
[]client.RPC{l2Rpc2},
[]client.RPC{experimentalRpc2})
require.ErrorIs(t, err, ErrNoL2ForRollup)
})
t.Run("L2URLWithoutConfig", func(t *testing.T) {
logger := testlog.Logger(t, log.LevelDebug)
_, l2Rpc1, _ := chain(1)
config2, l2Rpc2, experimentalRpc2 := chain(2)
_, err := NewL2Sources(context.Background(), logger,
[]*rollup.Config{config2},
[]client.RPC{l2Rpc1, l2Rpc2},
[]client.RPC{experimentalRpc2})
require.ErrorIs(t, err, ErrNoRollupForL2)
})
t.Run("ExperimentalURLWithoutConfig", func(t *testing.T) {
logger := testlog.Logger(t, log.LevelDebug)
_, _, experimentalRpc1 := chain(1)
config2, l2Rpc2, experimentalRpc2 := chain(2)
_, err := NewL2Sources(context.Background(), logger,
[]*rollup.Config{config2},
[]client.RPC{l2Rpc2},
[]client.RPC{experimentalRpc1, experimentalRpc2})
require.ErrorIs(t, err, ErrNoRollupForExperimental)
})
}
func chain(id uint64) (*rollup.Config, client.RPC, client.RPC) {
chainID := new(big.Int).SetUint64(id)
return &rollup.Config{L2ChainID: chainID}, &chainIDRPC{id: chainID}, &chainIDRPC{id: chainID}
}
type chainIDRPC struct {
id *big.Int
}
func (c *chainIDRPC) Close() {
panic("implement me")
}
func (c *chainIDRPC) CallContext(ctx context.Context, result any, method string, args ...any) error {
if method != "eth_chainId" {
return fmt.Errorf("invalid method: %s", method)
}
resultOut := result.(*hexutil.Big)
*resultOut = (hexutil.Big)(*c.id)
return nil
}
func (c *chainIDRPC) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
panic("implement me")
}
func (c *chainIDRPC) Subscribe(ctx context.Context, namespace string, channel any, args ...any) (ethereum.Subscription, error) {
panic("implement me")
}