client.go 6.98 KB
Newer Older
1 2 3 4 5 6
package node

import (
	"context"
	"errors"
	"fmt"
7
	"time"
8

9
	"github.com/ethereum-optimism/optimism/op-node/rollup"
Sabnock01's avatar
Sabnock01 committed
10 11
	"github.com/ethereum-optimism/optimism/op-service/client"
	"github.com/ethereum-optimism/optimism/op-service/sources"
12

13
	"github.com/ethereum/go-ethereum/log"
14
	gn "github.com/ethereum/go-ethereum/node"
15 16 17
	"github.com/ethereum/go-ethereum/rpc"
)

18
type L2EndpointSetup interface {
19
	// Setup a RPC client to a L2 execution engine to process rollup blocks with.
20
	Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (cl client.RPC, rpcCfg *sources.EngineClientConfig, err error)
21 22 23
	Check() error
}

clabby's avatar
clabby committed
24
type L2SyncEndpointSetup interface {
25 26
	// Setup a RPC client to another L2 node to sync L2 blocks from.
	// It may return a nil client with nil error if RPC based sync is not enabled.
27
	Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (cl client.RPC, rpcCfg *sources.SyncClientConfig, err error)
clabby's avatar
clabby committed
28 29 30
	Check() error
}

31 32
type L1EndpointSetup interface {
	// Setup a RPC client to a L1 node to pull rollup input-data from.
33 34
	// The results of the RPC client may be trusted for faster processing, or strictly validated.
	// The kind of the RPC may be non-basic, to optimize RPC usage.
35 36
	Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (cl client.RPC, rpcCfg *sources.L1ClientConfig, err error)
	Check() error
37 38
}

39 40
type L2EndpointConfig struct {
	L2EngineAddr string // Address of L2 Engine JSON-RPC endpoint to use (engine and eth namespace required)
41

42
	// JWT secrets for L2 Engine API authentication during HTTP or initial Websocket communication.
43
	// Any value for an IPC connection.
44
	L2EngineJWTSecret [32]byte
45 46
}

47
var _ L2EndpointSetup = (*L2EndpointConfig)(nil)
48

49 50 51
func (cfg *L2EndpointConfig) Check() error {
	if cfg.L2EngineAddr == "" {
		return errors.New("empty L2 Engine Address")
52
	}
53

54 55 56
	return nil
}

57
func (cfg *L2EndpointConfig) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.EngineClientConfig, error) {
58
	if err := cfg.Check(); err != nil {
59
		return nil, nil, err
60
	}
61
	auth := rpc.WithHTTPAuth(gn.NewJWTAuth(cfg.L2EngineJWTSecret))
62 63 64 65 66
	opts := []client.RPCOption{
		client.WithGethRPCOptions(auth),
		client.WithDialBackoff(10),
	}
	l2Node, err := client.NewRPC(ctx, log, cfg.L2EngineAddr, opts...)
67
	if err != nil {
68
		return nil, nil, err
69
	}
70

71
	return l2Node, sources.EngineClientDefaultConfig(rollupCfg), nil
72 73 74 75
}

// PreparedL2Endpoints enables testing with in-process pre-setup RPC connections to L2 engines
type PreparedL2Endpoints struct {
76
	Client client.RPC
77 78 79
}

func (p *PreparedL2Endpoints) Check() error {
80 81
	if p.Client == nil {
		return errors.New("client cannot be nil")
82 83 84 85
	}
	return nil
}

86
var _ L2EndpointSetup = (*PreparedL2Endpoints)(nil)
87

88 89
func (p *PreparedL2Endpoints) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.EngineClientConfig, error) {
	return p.Client, sources.EngineClientDefaultConfig(rollupCfg), nil
90 91
}

clabby's avatar
clabby committed
92 93
// L2SyncEndpointConfig contains configuration for the fallback sync endpoint
type L2SyncEndpointConfig struct {
94
	// Address of the L2 RPC to use for backup sync, may be empty if RPC alt-sync is disabled.
clabby's avatar
clabby committed
95
	L2NodeAddr string
96
	TrustRPC   bool
clabby's avatar
clabby committed
97 98 99 100
}

var _ L2SyncEndpointSetup = (*L2SyncEndpointConfig)(nil)

101 102
// Setup creates an RPC client to sync from.
// It will return nil without error if no sync method is configured.
103
func (cfg *L2SyncEndpointConfig) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.SyncClientConfig, error) {
104
	if cfg.L2NodeAddr == "" {
105
		return nil, nil, nil
106
	}
clabby's avatar
clabby committed
107 108
	l2Node, err := client.NewRPC(ctx, log, cfg.L2NodeAddr)
	if err != nil {
109
		return nil, nil, err
clabby's avatar
clabby committed
110 111
	}

112
	return l2Node, sources.SyncClientDefaultConfig(rollupCfg, cfg.TrustRPC), nil
clabby's avatar
clabby committed
113 114 115
}

func (cfg *L2SyncEndpointConfig) Check() error {
116
	// empty addr is valid, as it is optional.
clabby's avatar
clabby committed
117 118 119
	return nil
}

120 121 122 123
type PreparedL2SyncEndpoint struct {
	// RPC endpoint to use for syncing, may be nil if RPC alt-sync is disabled.
	Client   client.RPC
	TrustRPC bool
clabby's avatar
clabby committed
124 125
}

126
var _ L2SyncEndpointSetup = (*PreparedL2SyncEndpoint)(nil)
clabby's avatar
clabby committed
127

128 129
func (cfg *PreparedL2SyncEndpoint) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.SyncClientConfig, error) {
	return cfg.Client, sources.SyncClientDefaultConfig(rollupCfg, cfg.TrustRPC), nil
clabby's avatar
clabby committed
130 131
}

132
func (cfg *PreparedL2SyncEndpoint) Check() error {
clabby's avatar
clabby committed
133 134 135
	return nil
}

136 137 138 139 140 141 142
type L1EndpointConfig struct {
	L1NodeAddr string // Address of L1 User JSON-RPC endpoint to use (eth namespace required)

	// L1TrustRPC: if we trust the L1 RPC we do not have to validate L1 response contents like headers
	// against block hashes, or cached transaction sender addresses.
	// Thus we can sync faster at the risk of the source RPC being wrong.
	L1TrustRPC bool
143 144 145 146

	// L1RPCKind identifies the RPC provider kind that serves the RPC,
	// to inform the optimal usage of the RPC for transaction receipts fetching.
	L1RPCKind sources.RPCProviderKind
147 148 149 150 151 152 153 154 155 156 157 158

	// RateLimit specifies a self-imposed rate-limit on L1 requests. 0 is no rate-limit.
	RateLimit float64

	// BatchSize specifies the maximum batch-size, which also applies as L1 rate-limit burst amount (if set).
	BatchSize int

	// HttpPollInterval specifies the interval between polling for the latest L1 block,
	// when the RPC is detected to be an HTTP type.
	// It is recommended to use websockets or IPC for efficient following of the changing block.
	// Setting this to 0 disables polling.
	HttpPollInterval time.Duration
159 160 161 162
}

var _ L1EndpointSetup = (*L1EndpointConfig)(nil)

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
func (cfg *L1EndpointConfig) Check() error {
	if cfg.BatchSize < 1 || cfg.BatchSize > 500 {
		return fmt.Errorf("batch size is invalid or unreasonable: %d", cfg.BatchSize)
	}
	if cfg.RateLimit < 0 {
		return fmt.Errorf("rate limit cannot be negative")
	}
	return nil
}

func (cfg *L1EndpointConfig) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.L1ClientConfig, error) {
	opts := []client.RPCOption{
		client.WithHttpPollInterval(cfg.HttpPollInterval),
		client.WithDialBackoff(10),
	}
	if cfg.RateLimit != 0 {
		opts = append(opts, client.WithRateLimit(cfg.RateLimit, cfg.BatchSize))
	}

	l1Node, err := client.NewRPC(ctx, log, cfg.L1NodeAddr, opts...)
183
	if err != nil {
184
		return nil, nil, fmt.Errorf("failed to dial L1 address (%s): %w", cfg.L1NodeAddr, err)
185
	}
186 187 188
	rpcCfg := sources.L1ClientDefaultConfig(rollupCfg, cfg.L1TrustRPC, cfg.L1RPCKind)
	rpcCfg.MaxRequestsPerBatch = cfg.BatchSize
	return l1Node, rpcCfg, nil
189 190 191 192
}

// PreparedL1Endpoint enables testing with an in-process pre-setup RPC connection to L1
type PreparedL1Endpoint struct {
193 194 195
	Client          client.RPC
	TrustRPC        bool
	RPCProviderKind sources.RPCProviderKind
196 197 198 199
}

var _ L1EndpointSetup = (*PreparedL1Endpoint)(nil)

200 201 202 203 204 205 206 207 208 209
func (p *PreparedL1Endpoint) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.L1ClientConfig, error) {
	return p.Client, sources.L1ClientDefaultConfig(rollupCfg, p.TrustRPC, p.RPCProviderKind), nil
}

func (cfg *PreparedL1Endpoint) Check() error {
	if cfg.Client == nil {
		return errors.New("rpc client cannot be nil")
	}

	return nil
210
}