Skip to content

Commit

Permalink
Merge pull request #5452 from ethereum-optimism/aj/fpp-store-fetched-…
Browse files Browse the repository at this point in the history
…data

op-program: Populate preimage store
  • Loading branch information
OptimismBot authored Apr 17, 2023
2 parents e6f1f61 + 373c5aa commit 62b9850
Show file tree
Hide file tree
Showing 9 changed files with 675 additions and 27 deletions.
49 changes: 49 additions & 0 deletions op-node/sources/debug_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package sources

import (
"context"
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
)

type DebugClient struct {
callContext CallContextFn
}

func NewDebugClient(callContext CallContextFn) *DebugClient {
return &DebugClient{callContext}
}

func (o *DebugClient) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
// MPT nodes are stored as the hash of the node (with no prefix)
node, err := o.dbGet(ctx, hash[:])
if err != nil {
return nil, fmt.Errorf("failed to retrieve state MPT node: %w", err)
}
return node, nil
}

func (o *DebugClient) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
// First try retrieving with the new code prefix
code, err := o.dbGet(ctx, append(append(make([]byte, 0), rawdb.CodePrefix...), hash[:]...))
if err != nil {
// Fallback to the legacy un-prefixed version
code, err = o.dbGet(ctx, hash[:])
if err != nil {
return nil, fmt.Errorf("failed to retrieve contract code, using new and legacy keys, with codehash %s: %w", hash, err)
}
}
return code, nil
}

func (o *DebugClient) dbGet(ctx context.Context, key []byte) ([]byte, error) {
var node hexutil.Bytes
err := o.callContext(ctx, &node, "debug_dbGet", hexutil.Encode(key))
if err != nil {
return nil, fmt.Errorf("fetch error %x: %w", key, err)
}
return node, nil
}
30 changes: 30 additions & 0 deletions op-node/testutils/mock_debug_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package testutils

import (
"context"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/mock"
)

type MockDebugClient struct {
mock.Mock
}

func (m *MockDebugClient) ExpectNodeByHash(hash common.Hash, res []byte, err error) {
m.Mock.On("NodeByHash", hash).Once().Return(res, &err)
}

func (m *MockDebugClient) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
out := m.Mock.MethodCalled("NodeByHash", hash)
return out[0].([]byte), *out[1].(*error)
}

func (m *MockDebugClient) ExpectCodeByHash(hash common.Hash, res []byte, err error) {
m.Mock.On("CodeByHash", hash).Once().Return(res, &err)
}

func (m *MockDebugClient) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) {
out := m.Mock.MethodCalled("CodeByHash", hash)
return out[0].([]byte), *out[1].(*error)
}
12 changes: 9 additions & 3 deletions op-program/client/l1/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,32 @@ import (
"github.com/ethereum-optimism/optimism/op-program/preimage"
)

const (
HintL1BlockHeader = "l1-block-header"
HintL1Transactions = "l1-transactions"
HintL1Receipts = "l1-receipts"
)

type BlockHeaderHint common.Hash

var _ preimage.Hint = BlockHeaderHint{}

func (l BlockHeaderHint) Hint() string {
return "l1-block-header " + (common.Hash)(l).String()
return HintL1BlockHeader + " " + (common.Hash)(l).String()
}

type TransactionsHint common.Hash

var _ preimage.Hint = TransactionsHint{}

func (l TransactionsHint) Hint() string {
return "l1-transactions " + (common.Hash)(l).String()
return HintL1Transactions + " " + (common.Hash)(l).String()
}

type ReceiptsHint common.Hash

var _ preimage.Hint = ReceiptsHint{}

func (l ReceiptsHint) Hint() string {
return "l1-receipts " + (common.Hash)(l).String()
return HintL1Receipts + " " + (common.Hash)(l).String()
}
15 changes: 11 additions & 4 deletions op-program/client/l2/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,41 @@ import (
"github.com/ethereum-optimism/optimism/op-program/preimage"
)

const (
HintL2BlockHeader = "l2-block-header"
HintL2Transactions = "l2-transactions"
HintL2Code = "l2-code"
HintL2StateNode = "l2-state-node"
)

type BlockHeaderHint common.Hash

var _ preimage.Hint = BlockHeaderHint{}

func (l BlockHeaderHint) Hint() string {
return "l2-block-header " + (common.Hash)(l).String()
return HintL2BlockHeader + " " + (common.Hash)(l).String()
}

type TransactionsHint common.Hash

var _ preimage.Hint = TransactionsHint{}

func (l TransactionsHint) Hint() string {
return "l2-transactions " + (common.Hash)(l).String()
return HintL2Transactions + " " + (common.Hash)(l).String()
}

type CodeHint common.Hash

var _ preimage.Hint = CodeHint{}

func (l CodeHint) Hint() string {
return "l2-code " + (common.Hash)(l).String()
return HintL2Code + " " + (common.Hash)(l).String()
}

type StateNodeHint common.Hash

var _ preimage.Hint = StateNodeHint{}

func (l StateNodeHint) Hint() string {
return "l2-state-node " + (common.Hash)(l).String()
return HintL2StateNode + " " + (common.Hash)(l).String()
}
69 changes: 59 additions & 10 deletions op-program/host/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ import (
"fmt"
"io"
"os"
"time"

"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
cldr "github.com/ethereum-optimism/optimism/op-program/client/driver"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum-optimism/optimism/op-program/host/flags"
"github.com/ethereum-optimism/optimism/op-program/host/kvstore"
"github.com/ethereum-optimism/optimism/op-program/host/l1"
"github.com/ethereum-optimism/optimism/op-program/host/l2"
"github.com/ethereum-optimism/optimism/op-program/host/prefetcher"
"github.com/ethereum-optimism/optimism/op-program/host/version"
"github.com/ethereum-optimism/optimism/op-program/preimage"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
Expand Down Expand Up @@ -96,6 +99,11 @@ func setupLogging(ctx *cli.Context) (log.Logger, error) {
return logger, nil
}

type L2Source struct {
*sources.L2Client
*sources.DebugClient
}

// FaultProofProgram is the programmatic entry-point for the fault proof program
func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
cfg.Rollup.LogDescription(logger, chaincfg.L2ChainIDToNetworkName)
Expand All @@ -104,27 +112,49 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
}

ctx := context.Background()
kv := kvstore.NewMemKV()

logger.Info("Connecting to L1 node", "l1", cfg.L1URL)
l1Source, err := l1.NewFetchingL1(ctx, logger, cfg)
l1RPC, err := client.NewRPC(ctx, logger, cfg.L1URL)
if err != nil {
return fmt.Errorf("connect l1 oracle: %w", err)
return fmt.Errorf("failed to setup L1 RPC: %w", err)
}

logger.Info("Connecting to L2 node", "l2", cfg.L2URL)
l2Source, err := l2.NewFetchingEngine(ctx, logger, cfg)
l2RPC, err := client.NewRPC(ctx, logger, cfg.L2URL)
if err != nil {
return fmt.Errorf("failed to setup L2 RPC: %w", err)
}

l1ClCfg := sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind)
l2ClCfg := sources.L2ClientDefaultConfig(cfg.Rollup, true)
l1Cl, err := sources.NewL1Client(l1RPC, logger, nil, l1ClCfg)
if err != nil {
return fmt.Errorf("failed to create L1 client: %w", err)
}
l2Cl, err := sources.NewL2Client(l2RPC, logger, nil, l2ClCfg)
if err != nil {
return fmt.Errorf("failed to create L2 client: %w", err)
}
l2DebugCl := &L2Source{L2Client: l2Cl, DebugClient: sources.NewDebugClient(l2RPC.CallContext)}

logger.Info("Setting up pre-fetcher")
prefetch := prefetcher.NewPrefetcher(l1Cl, l2DebugCl, kv)
preimageOracle := asOracleFn(ctx, prefetch)
hinter := asHinter(prefetch)
l1Source := l1.NewSource(logger, preimageOracle, hinter, cfg.L1Head)

logger.Info("Connecting to L2 node", "l2", cfg.L2URL)
l2Source, err := l2.NewEngine(logger, preimageOracle, hinter, cfg)
if err != nil {
return fmt.Errorf("connect l2 oracle: %w", err)
}

logger.Info("Starting derivation")
d := cldr.NewDriver(logger, cfg.Rollup, l1Source, l2Source)
for {
if err = d.Step(ctx); errors.Is(err, io.EOF) {
break
} else if cfg.FetchingEnabled() && errors.Is(err, derive.ErrTemporary) {
// When in fetching mode, recover from temporary errors to allow us to keep fetching data
// TODO(CLI-3780) Ideally the retry would happen in the fetcher so this is not needed
logger.Warn("Temporary error in pipeline", "err", err)
time.Sleep(5 * time.Second)
} else if err != nil {
return err
}
Expand All @@ -135,3 +165,22 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
}
return nil
}

func asOracleFn(ctx context.Context, prefetcher *prefetcher.Prefetcher) preimage.OracleFn {
return func(key preimage.Key) []byte {
pre, err := prefetcher.GetPreimage(ctx, key.PreimageKey())
if err != nil {
panic(fmt.Errorf("preimage unavailable for key %v: %w", key, err))
}
return pre
}
}

func asHinter(prefetcher *prefetcher.Prefetcher) preimage.HinterFn {
return func(v preimage.Hint) {
err := prefetcher.Hint(v.Hint())
if err != nil {
panic(fmt.Errorf("hint rejected %v: %w", v, err))
}
}
}
12 changes: 9 additions & 3 deletions op-program/host/l1/l1.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import (
"github.com/ethereum-optimism/optimism/op-node/sources"
cll1 "github.com/ethereum-optimism/optimism/op-program/client/l1"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum-optimism/optimism/op-program/preimage"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)

func NewFetchingL1(ctx context.Context, logger log.Logger, cfg *config.Config) (derive.L1Fetcher, error) {
func NewFetchingOracle(ctx context.Context, logger log.Logger, cfg *config.Config) (cll1.Oracle, error) {
rpc, err := client.NewRPC(ctx, logger, cfg.L1URL)
if err != nil {
return nil, err
Expand All @@ -21,6 +23,10 @@ func NewFetchingL1(ctx context.Context, logger log.Logger, cfg *config.Config) (
if err != nil {
return nil, err
}
oracle := cll1.NewCachingOracle(NewFetchingL1Oracle(ctx, logger, source))
return cll1.NewOracleL1Client(logger, oracle, cfg.L1Head), err
return NewFetchingL1Oracle(ctx, logger, source), nil
}

func NewSource(logger log.Logger, oracle preimage.Oracle, hint preimage.Hinter, l1Head common.Hash) derive.L1Fetcher {
l1Oracle := cll1.NewCachingOracle(cll1.NewPreimageOracle(oracle, hint))
return cll1.NewOracleL1Client(logger, l1Oracle, l1Head)
}
18 changes: 11 additions & 7 deletions op-program/host/l2/l2.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,33 @@ import (

cll2 "github.com/ethereum-optimism/optimism/op-program/client/l2"
"github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum-optimism/optimism/op-program/preimage"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)

func NewFetchingEngine(ctx context.Context, logger log.Logger, cfg *config.Config) (*cll2.OracleEngine, error) {
func NewEngine(logger log.Logger, pre preimage.Oracle, hint preimage.Hinter, cfg *config.Config) (*cll2.OracleEngine, error) {
oracle := cll2.NewCachingOracle(cll2.NewPreimageOracle(pre, hint))
genesis, err := loadL2Genesis(cfg)
if err != nil {
return nil, err
}
fetcher, err := NewFetchingL2Oracle(ctx, logger, cfg.L2URL, cfg.L2Head)
if err != nil {
return nil, fmt.Errorf("connect l2 oracle: %w", err)
}
oracle := cll2.NewCachingOracle(fetcher)

engineBackend, err := cll2.NewOracleBackedL2Chain(logger, oracle, genesis, cfg.L2Head)
if err != nil {
return nil, fmt.Errorf("create l2 chain: %w", err)
}
return cll2.NewOracleEngine(cfg.Rollup, logger, engineBackend), nil
}

func NewFetchingOracle(ctx context.Context, logger log.Logger, cfg *config.Config) (cll2.Oracle, error) {
oracle, err := NewFetchingL2Oracle(ctx, logger, cfg.L2URL, cfg.L2Head)
if err != nil {
return nil, fmt.Errorf("connect l2 oracle: %w", err)
}
return oracle, nil
}

func loadL2Genesis(cfg *config.Config) (*params.ChainConfig, error) {
data, err := os.ReadFile(cfg.L2GenesisPath)
if err != nil {
Expand Down
Loading

0 comments on commit 62b9850

Please sign in to comment.