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  }