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

import (
	"context"
	"fmt"

7
	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
8 9
	"github.com/ethereum-optimism/optimism/op-node/eth"
	"github.com/ethereum-optimism/optimism/op-node/rollup"
10
	"github.com/ethereum-optimism/optimism/op-node/version"
11 12 13 14 15 16 17
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/log"
	"github.com/ethereum/go-ethereum/rpc"
)

type l2EthClient interface {
18
	InfoByRpcNumber(ctx context.Context, num rpc.BlockNumber) (eth.BlockInfo, error)
19
	// GetProof returns a proof of the account, it may return a nil result without error if the address was not found.
20
	GetProof(ctx context.Context, address common.Address, blockTag string) (*eth.AccountResult, error)
21 22
}

23
type driverClient interface {
24
	SyncStatus(ctx context.Context) (*eth.SyncStatus, error)
25 26 27
	ResetDerivationPipeline(context.Context) error
}

28 29 30 31 32
type rpcMetrics interface {
	// RecordRPCServerRequest returns a function that records the duration of serving the given RPC method
	RecordRPCServerRequest(method string) func()
}

33 34
type adminAPI struct {
	dr driverClient
35
	m  rpcMetrics
36 37
}

38
func NewAdminAPI(dr driverClient, m rpcMetrics) *adminAPI {
39 40 41 42 43 44 45 46 47 48
	return &adminAPI{
		dr: dr,
		m:  m,
	}
}

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

51 52 53
type nodeAPI struct {
	config *rollup.Config
	client l2EthClient
54
	dr     driverClient
55
	log    log.Logger
56
	m      rpcMetrics
57 58
}

59
func NewNodeAPI(config *rollup.Config, l2Client l2EthClient, dr driverClient, log log.Logger, m rpcMetrics) *nodeAPI {
60 61 62
	return &nodeAPI{
		config: config,
		client: l2Client,
63
		dr:     dr,
64
		log:    log,
65
		m:      m,
66 67 68
	}
}

69
func (n *nodeAPI) OutputAtBlock(ctx context.Context, number rpc.BlockNumber) ([]eth.Bytes32, error) {
70 71
	recordDur := n.m.RecordRPCServerRequest("optimism_outputAtBlock")
	defer recordDur()
72 73
	// TODO: rpc.BlockNumber doesn't support the "safe" tag. Need a new type

74
	head, err := n.client.InfoByRpcNumber(ctx, number)
75 76 77 78 79 80 81 82
	if err != nil {
		n.log.Error("failed to get block", "err", err)
		return nil, err
	}
	if head == nil {
		return nil, ethereum.NotFound
	}

83
	proof, err := n.client.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, toBlockNumArg(number))
84 85 86 87 88 89 90 91
	if err != nil {
		n.log.Error("failed to get contract proof", "err", err)
		return nil, err
	}
	if proof == nil {
		return nil, ethereum.NotFound
	}
	// make sure that the proof (including storage hash) that we retrieved is correct by verifying it against the state-root
92 93
	if err := proof.Verify(head.Root()); err != nil {
		n.log.Error("invalid withdrawal root detected in block", "stateRoot", head.Root(), "blocknum", number, "msg", err)
94 95 96
		return nil, fmt.Errorf("invalid withdrawal root hash")
	}

97
	var l2OutputRootVersion eth.Bytes32 // it's zero for now
98
	l2OutputRoot := rollup.ComputeL2OutputRoot(l2OutputRootVersion, head.Hash(), head.Root(), proof.StorageHash)
99

100
	return []eth.Bytes32{l2OutputRootVersion, l2OutputRoot}, nil
101 102
}

103
func (n *nodeAPI) SyncStatus(ctx context.Context) (*eth.SyncStatus, error) {
104 105
	recordDur := n.m.RecordRPCServerRequest("optimism_syncStatus")
	defer recordDur()
106 107 108
	return n.dr.SyncStatus(ctx)
}

109 110 111 112 113 114
func (n *nodeAPI) RollupConfig(_ context.Context) (*rollup.Config, error) {
	recordDur := n.m.RecordRPCServerRequest("optimism_rollupConfig")
	defer recordDur()
	return n.config, nil
}

115
func (n *nodeAPI) Version(ctx context.Context) (string, error) {
116 117
	recordDur := n.m.RecordRPCServerRequest("optimism_version")
	defer recordDur()
118 119 120 121
	return version.Version + "-" + version.Meta, nil
}

func toBlockNumArg(number rpc.BlockNumber) string {
122 123 124
	// never returns an error
	out, _ := number.MarshalText()
	return string(out)
125
}