api.go 5.04 KB
Newer Older
1 2 3 4 5 6
package node

import (
	"context"
	"fmt"

7 8 9 10
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/log"

11
	"github.com/ethereum-optimism/optimism/op-node/rollup"
12
	"github.com/ethereum-optimism/optimism/op-node/version"
13
	"github.com/ethereum-optimism/optimism/op-service/eth"
14
	"github.com/ethereum-optimism/optimism/op-service/metrics"
15
	"github.com/ethereum-optimism/optimism/op-service/rpc"
16 17 18
)

type l2EthClient interface {
19
	InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error)
20
	// GetProof returns a proof of the account, it may return a nil result without error if the address was not found.
21 22
	// Optionally keys of the account storage trie can be specified to include with corresponding values in the proof.
	GetProof(ctx context.Context, address common.Address, storage []common.Hash, blockTag string) (*eth.AccountResult, error)
23
	OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error)
24 25
}

26
type driverClient interface {
27
	SyncStatus(ctx context.Context) (*eth.SyncStatus, error)
28
	BlockRefWithStatus(ctx context.Context, num uint64) (eth.L2BlockRef, *eth.SyncStatus, error)
29
	ResetDerivationPipeline(context.Context) error
30 31
	StartSequencer(ctx context.Context, blockHash common.Hash) error
	StopSequencer(context.Context) (common.Hash, error)
32
	SequencerActive(context.Context) (bool, error)
33
	OnUnsafeL2Payload(ctx context.Context, payload *eth.ExecutionPayloadEnvelope) error
34 35 36
}

type adminAPI struct {
37 38
	*rpc.CommonAdminAPI
	dr driverClient
39 40
}

41
func NewAdminAPI(dr driverClient, m metrics.RPCMetricer, log log.Logger) *adminAPI {
42
	return &adminAPI{
43
		CommonAdminAPI: rpc.NewCommonAdminAPI(m, log),
44
		dr:             dr,
45 46 47 48
	}
}

func (n *adminAPI) ResetDerivationPipeline(ctx context.Context) error {
49
	recordDur := n.M.RecordRPCServerRequest("admin_resetDerivationPipeline")
50 51
	defer recordDur()
	return n.dr.ResetDerivationPipeline(ctx)
52 53
}

54
func (n *adminAPI) StartSequencer(ctx context.Context, blockHash common.Hash) error {
55
	recordDur := n.M.RecordRPCServerRequest("admin_startSequencer")
56
	defer recordDur()
57
	return n.dr.StartSequencer(ctx, blockHash)
58 59
}

60
func (n *adminAPI) StopSequencer(ctx context.Context) (common.Hash, error) {
61
	recordDur := n.M.RecordRPCServerRequest("admin_stopSequencer")
62 63 64 65
	defer recordDur()
	return n.dr.StopSequencer(ctx)
}

66
func (n *adminAPI) SequencerActive(ctx context.Context) (bool, error) {
67
	recordDur := n.M.RecordRPCServerRequest("admin_sequencerActive")
68 69 70 71
	defer recordDur()
	return n.dr.SequencerActive(ctx)
}

72 73
// PostUnsafePayload is a special API that allow posting an unsafe payload to the L2 derivation pipeline.
// It should only be used by op-conductor for sequencer failover scenarios.
74 75
// TODO(ethereum-optimism/optimism#9064): op-conductor Dencun changes.
func (n *adminAPI) PostUnsafePayload(ctx context.Context, envelope *eth.ExecutionPayloadEnvelope) error {
76 77 78
	recordDur := n.M.RecordRPCServerRequest("admin_postUnsafePayload")
	defer recordDur()

79 80
	payload := envelope.ExecutionPayload
	if actual, ok := envelope.CheckBlockHash(); !ok {
81 82 83 84
		log.Error("payload has bad block hash", "bad_hash", payload.BlockHash.String(), "actual", actual.String())
		return fmt.Errorf("payload has bad block hash: %s, actual block hash is: %s", payload.BlockHash.String(), actual.String())
	}

85
	return n.dr.OnUnsafeL2Payload(ctx, envelope)
86 87
}

88 89 90
type nodeAPI struct {
	config *rollup.Config
	client l2EthClient
91
	dr     driverClient
92
	log    log.Logger
93
	m      metrics.RPCMetricer
94 95
}

96
func NewNodeAPI(config *rollup.Config, l2Client l2EthClient, dr driverClient, log log.Logger, m metrics.RPCMetricer) *nodeAPI {
97 98 99
	return &nodeAPI{
		config: config,
		client: l2Client,
100
		dr:     dr,
101
		log:    log,
102
		m:      m,
103 104 105
	}
}

106
func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*eth.OutputResponse, error) {
107 108
	recordDur := n.m.RecordRPCServerRequest("optimism_outputAtBlock")
	defer recordDur()
109

110
	ref, status, err := n.dr.BlockRefWithStatus(ctx, uint64(number))
111
	if err != nil {
112 113 114
		return nil, fmt.Errorf("failed to get L2 block ref with sync status: %w", err)
	}

115
	output, err := n.client.OutputV0AtBlock(ctx, ref.Hash)
116
	if err != nil {
117
		return nil, fmt.Errorf("failed to get L2 output at block %s: %w", ref, err)
118
	}
119
	return &eth.OutputResponse{
120 121
		Version:               output.Version(),
		OutputRoot:            eth.OutputRoot(output),
122
		BlockRef:              ref,
123 124
		WithdrawalStorageRoot: common.Hash(output.MessagePasserStorageRoot),
		StateRoot:             common.Hash(output.StateRoot),
125 126
		Status:                status,
	}, nil
127 128
}

129
func (n *nodeAPI) SyncStatus(ctx context.Context) (*eth.SyncStatus, error) {
130 131
	recordDur := n.m.RecordRPCServerRequest("optimism_syncStatus")
	defer recordDur()
132 133 134
	return n.dr.SyncStatus(ctx)
}

135 136 137 138 139 140
func (n *nodeAPI) RollupConfig(_ context.Context) (*rollup.Config, error) {
	recordDur := n.m.RecordRPCServerRequest("optimism_rollupConfig")
	defer recordDur()
	return n.config, nil
}

141
func (n *nodeAPI) Version(ctx context.Context) (string, error) {
142 143
	recordDur := n.m.RecordRPCServerRequest("optimism_version")
	defer recordDur()
144 145
	return version.Version + "-" + version.Meta, nil
}