github.com/ethereum-optimism/optimism@v1.7.2/op-node/node/client.go (about) 1 package node 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "strings" 9 "time" 10 11 "github.com/ethereum-optimism/optimism/op-node/rollup" 12 "github.com/ethereum-optimism/optimism/op-service/client" 13 "github.com/ethereum-optimism/optimism/op-service/sources" 14 15 "github.com/ethereum/go-ethereum/log" 16 gn "github.com/ethereum/go-ethereum/node" 17 "github.com/ethereum/go-ethereum/rpc" 18 ) 19 20 type L2EndpointSetup interface { 21 // Setup a RPC client to a L2 execution engine to process rollup blocks with. 22 Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (cl client.RPC, rpcCfg *sources.EngineClientConfig, err error) 23 Check() error 24 } 25 26 type L1EndpointSetup interface { 27 // Setup a RPC client to a L1 node to pull rollup input-data from. 28 // The results of the RPC client may be trusted for faster processing, or strictly validated. 29 // The kind of the RPC may be non-basic, to optimize RPC usage. 30 Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (cl client.RPC, rpcCfg *sources.L1ClientConfig, err error) 31 Check() error 32 } 33 34 type L1BeaconEndpointSetup interface { 35 Setup(ctx context.Context, log log.Logger) (cl sources.BeaconClient, fb []sources.BlobSideCarsFetcher, err error) 36 // ShouldIgnoreBeaconCheck returns true if the Beacon-node version check should not halt startup. 37 ShouldIgnoreBeaconCheck() bool 38 ShouldFetchAllSidecars() bool 39 Check() error 40 } 41 42 type L2EndpointConfig struct { 43 // L2EngineAddr is the address of the L2 Engine JSON-RPC endpoint to use. The engine and eth 44 // namespaces must be enabled by the endpoint. 45 L2EngineAddr string 46 47 // JWT secrets for L2 Engine API authentication during HTTP or initial Websocket communication. 48 // Any value for an IPC connection. 49 L2EngineJWTSecret [32]byte 50 } 51 52 var _ L2EndpointSetup = (*L2EndpointConfig)(nil) 53 54 func (cfg *L2EndpointConfig) Check() error { 55 if cfg.L2EngineAddr == "" { 56 return errors.New("empty L2 Engine Address") 57 } 58 59 return nil 60 } 61 62 func (cfg *L2EndpointConfig) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.EngineClientConfig, error) { 63 if err := cfg.Check(); err != nil { 64 return nil, nil, err 65 } 66 auth := rpc.WithHTTPAuth(gn.NewJWTAuth(cfg.L2EngineJWTSecret)) 67 opts := []client.RPCOption{ 68 client.WithGethRPCOptions(auth), 69 client.WithDialBackoff(10), 70 } 71 l2Node, err := client.NewRPC(ctx, log, cfg.L2EngineAddr, opts...) 72 if err != nil { 73 return nil, nil, err 74 } 75 76 return l2Node, sources.EngineClientDefaultConfig(rollupCfg), nil 77 } 78 79 // PreparedL2Endpoints enables testing with in-process pre-setup RPC connections to L2 engines 80 type PreparedL2Endpoints struct { 81 Client client.RPC 82 } 83 84 func (p *PreparedL2Endpoints) Check() error { 85 if p.Client == nil { 86 return errors.New("client cannot be nil") 87 } 88 return nil 89 } 90 91 var _ L2EndpointSetup = (*PreparedL2Endpoints)(nil) 92 93 func (p *PreparedL2Endpoints) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.EngineClientConfig, error) { 94 return p.Client, sources.EngineClientDefaultConfig(rollupCfg), nil 95 } 96 97 type L1EndpointConfig struct { 98 L1NodeAddr string // Address of L1 User JSON-RPC endpoint to use (eth namespace required) 99 100 // L1TrustRPC: if we trust the L1 RPC we do not have to validate L1 response contents like headers 101 // against block hashes, or cached transaction sender addresses. 102 // Thus we can sync faster at the risk of the source RPC being wrong. 103 L1TrustRPC bool 104 105 // L1RPCKind identifies the RPC provider kind that serves the RPC, 106 // to inform the optimal usage of the RPC for transaction receipts fetching. 107 L1RPCKind sources.RPCProviderKind 108 109 // RateLimit specifies a self-imposed rate-limit on L1 requests. 0 is no rate-limit. 110 RateLimit float64 111 112 // BatchSize specifies the maximum batch-size, which also applies as L1 rate-limit burst amount (if set). 113 BatchSize int 114 115 // MaxConcurrency specifies the maximum number of concurrent requests to the L1 RPC. 116 MaxConcurrency int 117 118 // HttpPollInterval specifies the interval between polling for the latest L1 block, 119 // when the RPC is detected to be an HTTP type. 120 // It is recommended to use websockets or IPC for efficient following of the changing block. 121 // Setting this to 0 disables polling. 122 HttpPollInterval time.Duration 123 } 124 125 var _ L1EndpointSetup = (*L1EndpointConfig)(nil) 126 127 func (cfg *L1EndpointConfig) Check() error { 128 if cfg.BatchSize < 1 || cfg.BatchSize > 500 { 129 return fmt.Errorf("batch size is invalid or unreasonable: %d", cfg.BatchSize) 130 } 131 if cfg.RateLimit < 0 { 132 return fmt.Errorf("rate limit cannot be negative") 133 } 134 if cfg.MaxConcurrency < 1 { 135 return fmt.Errorf("max concurrent requests cannot be less than 1, was %d", cfg.MaxConcurrency) 136 } 137 return nil 138 } 139 140 func (cfg *L1EndpointConfig) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.L1ClientConfig, error) { 141 opts := []client.RPCOption{ 142 client.WithHttpPollInterval(cfg.HttpPollInterval), 143 client.WithDialBackoff(10), 144 } 145 if cfg.RateLimit != 0 { 146 opts = append(opts, client.WithRateLimit(cfg.RateLimit, cfg.BatchSize)) 147 } 148 149 l1Node, err := client.NewRPC(ctx, log, cfg.L1NodeAddr, opts...) 150 if err != nil { 151 return nil, nil, fmt.Errorf("failed to dial L1 address (%s): %w", cfg.L1NodeAddr, err) 152 } 153 rpcCfg := sources.L1ClientDefaultConfig(rollupCfg, cfg.L1TrustRPC, cfg.L1RPCKind) 154 rpcCfg.MaxRequestsPerBatch = cfg.BatchSize 155 rpcCfg.MaxConcurrentRequests = cfg.MaxConcurrency 156 return l1Node, rpcCfg, nil 157 } 158 159 // PreparedL1Endpoint enables testing with an in-process pre-setup RPC connection to L1 160 type PreparedL1Endpoint struct { 161 Client client.RPC 162 TrustRPC bool 163 RPCProviderKind sources.RPCProviderKind 164 } 165 166 var _ L1EndpointSetup = (*PreparedL1Endpoint)(nil) 167 168 func (p *PreparedL1Endpoint) Setup(ctx context.Context, log log.Logger, rollupCfg *rollup.Config) (client.RPC, *sources.L1ClientConfig, error) { 169 return p.Client, sources.L1ClientDefaultConfig(rollupCfg, p.TrustRPC, p.RPCProviderKind), nil 170 } 171 172 func (cfg *PreparedL1Endpoint) Check() error { 173 if cfg.Client == nil { 174 return errors.New("rpc client cannot be nil") 175 } 176 177 return nil 178 } 179 180 type L1BeaconEndpointConfig struct { 181 BeaconAddr string // Address of L1 User Beacon-API endpoint to use (beacon namespace required) 182 BeaconHeader string // Optional HTTP header for all requests to L1 Beacon 183 BeaconArchiverAddr string // Address of L1 User Beacon-API Archive endpoint to use for expired blobs (beacon namespace required) 184 BeaconCheckIgnore bool // When false, halt startup if the beacon version endpoint fails 185 BeaconFetchAllSidecars bool // Whether to fetch all blob sidecars and filter locally 186 } 187 188 var _ L1BeaconEndpointSetup = (*L1BeaconEndpointConfig)(nil) 189 190 func (cfg *L1BeaconEndpointConfig) Setup(ctx context.Context, log log.Logger) (cl sources.BeaconClient, fb []sources.BlobSideCarsFetcher, err error) { 191 var opts []client.BasicHTTPClientOption 192 if cfg.BeaconHeader != "" { 193 hdr, err := parseHTTPHeader(cfg.BeaconHeader) 194 if err != nil { 195 return nil, nil, fmt.Errorf("parsing beacon header: %w", err) 196 } 197 opts = append(opts, client.WithHeader(hdr)) 198 } 199 200 a := client.NewBasicHTTPClient(cfg.BeaconAddr, log, opts...) 201 if cfg.BeaconArchiverAddr != "" { 202 b := client.NewBasicHTTPClient(cfg.BeaconArchiverAddr, log) 203 fb = append(fb, sources.NewBeaconHTTPClient(b)) 204 } 205 return sources.NewBeaconHTTPClient(a), fb, nil 206 } 207 208 func (cfg *L1BeaconEndpointConfig) Check() error { 209 if cfg.BeaconAddr == "" && !cfg.BeaconCheckIgnore { 210 return errors.New("expected L1 Beacon API endpoint, but got none") 211 } 212 return nil 213 } 214 215 func (cfg *L1BeaconEndpointConfig) ShouldIgnoreBeaconCheck() bool { 216 return cfg.BeaconCheckIgnore 217 } 218 219 func (cfg *L1BeaconEndpointConfig) ShouldFetchAllSidecars() bool { 220 return cfg.BeaconFetchAllSidecars 221 } 222 223 func parseHTTPHeader(headerStr string) (http.Header, error) { 224 h := make(http.Header, 1) 225 s := strings.SplitN(headerStr, ": ", 2) 226 if len(s) != 2 { 227 return nil, errors.New("invalid header format") 228 } 229 h.Add(s[0], s[1]) 230 return h, nil 231 }