api.go 6.41 KB
Newer Older
1 2 3 4
package node

import (
	"context"
5
	"errors"
6 7
	"fmt"

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

12
	"github.com/ethereum-optimism/optimism/op-node/node/safedb"
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 24
	// 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)
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 33
	StartSequencer(ctx context.Context, blockHash common.Hash) error
	StopSequencer(context.Context) (common.Hash, error)
34
	SequencerActive(context.Context) (bool, error)
35
	OnUnsafeL2Payload(ctx context.Context, payload *eth.ExecutionPayloadEnvelope) error
36
	OverrideLeader(ctx context.Context) error
37
	ConductorEnabled(ctx context.Context) (bool, error)
38 39
}

40 41 42 43
type SafeDBReader interface {
	SafeHeadAtL1(ctx context.Context, l1BlockNum uint64) (l1 eth.BlockID, l2 eth.BlockID, err error)
}

44
type adminAPI struct {
45 46
	*rpc.CommonAdminAPI
	dr driverClient
47 48
}

49
func NewAdminAPI(dr driverClient, m metrics.RPCMetricer, log log.Logger) *adminAPI {
50
	return &adminAPI{
51
		CommonAdminAPI: rpc.NewCommonAdminAPI(m, log),
52
		dr:             dr,
53 54 55 56
	}
}

func (n *adminAPI) ResetDerivationPipeline(ctx context.Context) error {
57
	recordDur := n.M.RecordRPCServerRequest("admin_resetDerivationPipeline")
58 59
	defer recordDur()
	return n.dr.ResetDerivationPipeline(ctx)
60 61
}

62
func (n *adminAPI) StartSequencer(ctx context.Context, blockHash common.Hash) error {
63
	recordDur := n.M.RecordRPCServerRequest("admin_startSequencer")
64
	defer recordDur()
65
	return n.dr.StartSequencer(ctx, blockHash)
66 67
}

68
func (n *adminAPI) StopSequencer(ctx context.Context) (common.Hash, error) {
69
	recordDur := n.M.RecordRPCServerRequest("admin_stopSequencer")
70 71 72 73
	defer recordDur()
	return n.dr.StopSequencer(ctx)
}

74
func (n *adminAPI) SequencerActive(ctx context.Context) (bool, error) {
75
	recordDur := n.M.RecordRPCServerRequest("admin_sequencerActive")
76 77 78 79
	defer recordDur()
	return n.dr.SequencerActive(ctx)
}

80
// PostUnsafePayload is a special API that allows posting an unsafe payload to the L2 derivation pipeline.
81
// It should only be used by op-conductor for sequencer failover scenarios.
82
func (n *adminAPI) PostUnsafePayload(ctx context.Context, envelope *eth.ExecutionPayloadEnvelope) error {
83 84 85
	recordDur := n.M.RecordRPCServerRequest("admin_postUnsafePayload")
	defer recordDur()

86 87
	payload := envelope.ExecutionPayload
	if actual, ok := envelope.CheckBlockHash(); !ok {
88 89 90 91
		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())
	}

92
	return n.dr.OnUnsafeL2Payload(ctx, envelope)
93 94
}

95 96 97 98 99 100 101
// OverrideLeader disables sequencer conductor interactions and allow sequencer to run in non-HA mode during disaster recovery scenarios.
func (n *adminAPI) OverrideLeader(ctx context.Context) error {
	recordDur := n.M.RecordRPCServerRequest("admin_overrideLeader")
	defer recordDur()
	return n.dr.OverrideLeader(ctx)
}

102 103 104 105 106 107 108
// ConductorEnabled returns true if the sequencer conductor is enabled.
func (n *adminAPI) ConductorEnabled(ctx context.Context) (bool, error) {
	recordDur := n.M.RecordRPCServerRequest("admin_conductorEnabled")
	defer recordDur()
	return n.dr.ConductorEnabled(ctx)
}

109 110 111
type nodeAPI struct {
	config *rollup.Config
	client l2EthClient
112
	dr     driverClient
113
	safeDB SafeDBReader
114
	log    log.Logger
115
	m      metrics.RPCMetricer
116 117
}

118
func NewNodeAPI(config *rollup.Config, l2Client l2EthClient, dr driverClient, safeDB SafeDBReader, log log.Logger, m metrics.RPCMetricer) *nodeAPI {
119 120 121
	return &nodeAPI{
		config: config,
		client: l2Client,
122
		dr:     dr,
123
		safeDB: safeDB,
124
		log:    log,
125
		m:      m,
126 127 128
	}
}

129
func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*eth.OutputResponse, error) {
130 131
	recordDur := n.m.RecordRPCServerRequest("optimism_outputAtBlock")
	defer recordDur()
132

133
	ref, status, err := n.dr.BlockRefWithStatus(ctx, uint64(number))
134
	if err != nil {
135 136 137
		return nil, fmt.Errorf("failed to get L2 block ref with sync status: %w", err)
	}

138
	output, err := n.client.OutputV0AtBlock(ctx, ref.Hash)
139
	if err != nil {
140
		return nil, fmt.Errorf("failed to get L2 output at block %s: %w", ref, err)
141
	}
142
	return &eth.OutputResponse{
143 144
		Version:               output.Version(),
		OutputRoot:            eth.OutputRoot(output),
145
		BlockRef:              ref,
146 147
		WithdrawalStorageRoot: common.Hash(output.MessagePasserStorageRoot),
		StateRoot:             common.Hash(output.StateRoot),
148 149
		Status:                status,
	}, nil
150 151
}

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
func (n *nodeAPI) SafeHeadAtL1Block(ctx context.Context, number hexutil.Uint64) (*eth.SafeHeadResponse, error) {
	recordDur := n.m.RecordRPCServerRequest("optimism_safeHeadAtL1Block")
	defer recordDur()
	l1Block, safeHead, err := n.safeDB.SafeHeadAtL1(ctx, uint64(number))
	if errors.Is(err, safedb.ErrNotFound) {
		return nil, err
	} else if err != nil {
		return nil, fmt.Errorf("failed to get safe head at l1 block %s: %w", number, err)
	}
	return &eth.SafeHeadResponse{
		L1Block:  l1Block,
		SafeHead: safeHead,
	}, nil
}

167
func (n *nodeAPI) SyncStatus(ctx context.Context) (*eth.SyncStatus, error) {
168 169
	recordDur := n.m.RecordRPCServerRequest("optimism_syncStatus")
	defer recordDur()
170 171 172
	return n.dr.SyncStatus(ctx)
}

173 174 175 176 177 178
func (n *nodeAPI) RollupConfig(_ context.Context) (*rollup.Config, error) {
	recordDur := n.m.RecordRPCServerRequest("optimism_rollupConfig")
	defer recordDur()
	return n.config, nil
}

179
func (n *nodeAPI) Version(ctx context.Context) (string, error) {
180 181
	recordDur := n.m.RecordRPCServerRequest("optimism_version")
	defer recordDur()
182 183
	return version.Version + "-" + version.Meta, nil
}