github.com/ethereum-optimism/optimism@v1.7.2/op-node/node/api.go (about)

     1  package node
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/ethereum-optimism/optimism/op-node/node/safedb"
     9  	"github.com/ethereum/go-ethereum/common"
    10  	"github.com/ethereum/go-ethereum/common/hexutil"
    11  	"github.com/ethereum/go-ethereum/log"
    12  
    13  	"github.com/ethereum-optimism/optimism/op-node/rollup"
    14  	"github.com/ethereum-optimism/optimism/op-node/version"
    15  	"github.com/ethereum-optimism/optimism/op-service/eth"
    16  	"github.com/ethereum-optimism/optimism/op-service/metrics"
    17  	"github.com/ethereum-optimism/optimism/op-service/rpc"
    18  )
    19  
    20  type l2EthClient interface {
    21  	InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error)
    22  	// GetProof returns a proof of the account, it may return a nil result without error if the address was not found.
    23  	// Optionally keys of the account storage trie can be specified to include with corresponding values in the proof.
    24  	GetProof(ctx context.Context, address common.Address, storage []common.Hash, blockTag string) (*eth.AccountResult, error)
    25  	OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error)
    26  }
    27  
    28  type driverClient interface {
    29  	SyncStatus(ctx context.Context) (*eth.SyncStatus, error)
    30  	BlockRefWithStatus(ctx context.Context, num uint64) (eth.L2BlockRef, *eth.SyncStatus, error)
    31  	ResetDerivationPipeline(context.Context) error
    32  	StartSequencer(ctx context.Context, blockHash common.Hash) error
    33  	StopSequencer(context.Context) (common.Hash, error)
    34  	SequencerActive(context.Context) (bool, error)
    35  	OnUnsafeL2Payload(ctx context.Context, payload *eth.ExecutionPayloadEnvelope) error
    36  }
    37  
    38  type SafeDBReader interface {
    39  	SafeHeadAtL1(ctx context.Context, l1BlockNum uint64) (l1 eth.BlockID, l2 eth.BlockID, err error)
    40  }
    41  
    42  type adminAPI struct {
    43  	*rpc.CommonAdminAPI
    44  	dr driverClient
    45  }
    46  
    47  func NewAdminAPI(dr driverClient, m metrics.RPCMetricer, log log.Logger) *adminAPI {
    48  	return &adminAPI{
    49  		CommonAdminAPI: rpc.NewCommonAdminAPI(m, log),
    50  		dr:             dr,
    51  	}
    52  }
    53  
    54  func (n *adminAPI) ResetDerivationPipeline(ctx context.Context) error {
    55  	recordDur := n.M.RecordRPCServerRequest("admin_resetDerivationPipeline")
    56  	defer recordDur()
    57  	return n.dr.ResetDerivationPipeline(ctx)
    58  }
    59  
    60  func (n *adminAPI) StartSequencer(ctx context.Context, blockHash common.Hash) error {
    61  	recordDur := n.M.RecordRPCServerRequest("admin_startSequencer")
    62  	defer recordDur()
    63  	return n.dr.StartSequencer(ctx, blockHash)
    64  }
    65  
    66  func (n *adminAPI) StopSequencer(ctx context.Context) (common.Hash, error) {
    67  	recordDur := n.M.RecordRPCServerRequest("admin_stopSequencer")
    68  	defer recordDur()
    69  	return n.dr.StopSequencer(ctx)
    70  }
    71  
    72  func (n *adminAPI) SequencerActive(ctx context.Context) (bool, error) {
    73  	recordDur := n.M.RecordRPCServerRequest("admin_sequencerActive")
    74  	defer recordDur()
    75  	return n.dr.SequencerActive(ctx)
    76  }
    77  
    78  // PostUnsafePayload is a special API that allow posting an unsafe payload to the L2 derivation pipeline.
    79  // It should only be used by op-conductor for sequencer failover scenarios.
    80  // TODO(ethereum-optimism/optimism#9064): op-conductor Dencun changes.
    81  func (n *adminAPI) PostUnsafePayload(ctx context.Context, envelope *eth.ExecutionPayloadEnvelope) error {
    82  	recordDur := n.M.RecordRPCServerRequest("admin_postUnsafePayload")
    83  	defer recordDur()
    84  
    85  	payload := envelope.ExecutionPayload
    86  	if actual, ok := envelope.CheckBlockHash(); !ok {
    87  		log.Error("payload has bad block hash", "bad_hash", payload.BlockHash.String(), "actual", actual.String())
    88  		return fmt.Errorf("payload has bad block hash: %s, actual block hash is: %s", payload.BlockHash.String(), actual.String())
    89  	}
    90  
    91  	return n.dr.OnUnsafeL2Payload(ctx, envelope)
    92  }
    93  
    94  type nodeAPI struct {
    95  	config *rollup.Config
    96  	client l2EthClient
    97  	dr     driverClient
    98  	safeDB SafeDBReader
    99  	log    log.Logger
   100  	m      metrics.RPCMetricer
   101  }
   102  
   103  func NewNodeAPI(config *rollup.Config, l2Client l2EthClient, dr driverClient, safeDB SafeDBReader, log log.Logger, m metrics.RPCMetricer) *nodeAPI {
   104  	return &nodeAPI{
   105  		config: config,
   106  		client: l2Client,
   107  		dr:     dr,
   108  		safeDB: safeDB,
   109  		log:    log,
   110  		m:      m,
   111  	}
   112  }
   113  
   114  func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*eth.OutputResponse, error) {
   115  	recordDur := n.m.RecordRPCServerRequest("optimism_outputAtBlock")
   116  	defer recordDur()
   117  
   118  	ref, status, err := n.dr.BlockRefWithStatus(ctx, uint64(number))
   119  	if err != nil {
   120  		return nil, fmt.Errorf("failed to get L2 block ref with sync status: %w", err)
   121  	}
   122  
   123  	output, err := n.client.OutputV0AtBlock(ctx, ref.Hash)
   124  	if err != nil {
   125  		return nil, fmt.Errorf("failed to get L2 output at block %s: %w", ref, err)
   126  	}
   127  	return &eth.OutputResponse{
   128  		Version:               output.Version(),
   129  		OutputRoot:            eth.OutputRoot(output),
   130  		BlockRef:              ref,
   131  		WithdrawalStorageRoot: common.Hash(output.MessagePasserStorageRoot),
   132  		StateRoot:             common.Hash(output.StateRoot),
   133  		Status:                status,
   134  	}, nil
   135  }
   136  
   137  func (n *nodeAPI) SafeHeadAtL1Block(ctx context.Context, number hexutil.Uint64) (*eth.SafeHeadResponse, error) {
   138  	recordDur := n.m.RecordRPCServerRequest("optimism_safeHeadAtL1Block")
   139  	defer recordDur()
   140  	l1Block, safeHead, err := n.safeDB.SafeHeadAtL1(ctx, uint64(number))
   141  	if errors.Is(err, safedb.ErrNotFound) {
   142  		return nil, err
   143  	} else if err != nil {
   144  		return nil, fmt.Errorf("failed to get safe head at l1 block %s: %w", number, err)
   145  	}
   146  	return &eth.SafeHeadResponse{
   147  		L1Block:  l1Block,
   148  		SafeHead: safeHead,
   149  	}, nil
   150  }
   151  
   152  func (n *nodeAPI) SyncStatus(ctx context.Context) (*eth.SyncStatus, error) {
   153  	recordDur := n.m.RecordRPCServerRequest("optimism_syncStatus")
   154  	defer recordDur()
   155  	return n.dr.SyncStatus(ctx)
   156  }
   157  
   158  func (n *nodeAPI) RollupConfig(_ context.Context) (*rollup.Config, error) {
   159  	recordDur := n.m.RecordRPCServerRequest("optimism_rollupConfig")
   160  	defer recordDur()
   161  	return n.config, nil
   162  }
   163  
   164  func (n *nodeAPI) Version(ctx context.Context) (string, error) {
   165  	recordDur := n.m.RecordRPCServerRequest("optimism_version")
   166  	defer recordDur()
   167  	return version.Version + "-" + version.Meta, nil
   168  }