Commit 373c5aa3 authored by protolambda's avatar protolambda

op-program: prefetcher now backed with rpc source interface

parent a7e24c93
...@@ -8,7 +8,9 @@ import ( ...@@ -8,7 +8,9 @@ import (
"os" "os"
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "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/eth"
"github.com/ethereum-optimism/optimism/op-node/sources"
cldr "github.com/ethereum-optimism/optimism/op-program/client/driver" 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/config"
"github.com/ethereum-optimism/optimism/op-program/host/flags" "github.com/ethereum-optimism/optimism/op-program/host/flags"
...@@ -97,6 +99,11 @@ func setupLogging(ctx *cli.Context) (log.Logger, error) { ...@@ -97,6 +99,11 @@ func setupLogging(ctx *cli.Context) (log.Logger, error) {
return logger, nil return logger, nil
} }
type L2Source struct {
*sources.L2Client
*sources.DebugClient
}
// FaultProofProgram is the programmatic entry-point for the fault proof program // FaultProofProgram is the programmatic entry-point for the fault proof program
func FaultProofProgram(logger log.Logger, cfg *config.Config) error { func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
cfg.Rollup.LogDescription(logger, chaincfg.L2ChainIDToNetworkName) cfg.Rollup.LogDescription(logger, chaincfg.L2ChainIDToNetworkName)
...@@ -108,18 +115,32 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error { ...@@ -108,18 +115,32 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
kv := kvstore.NewMemKV() kv := kvstore.NewMemKV()
logger.Info("Connecting to L1 node", "l1", cfg.L1URL) logger.Info("Connecting to L1 node", "l1", cfg.L1URL)
l1Fetcher, err := l1.NewFetchingOracle(ctx, logger, cfg) l1RPC, err := client.NewRPC(ctx, logger, cfg.L1URL)
if err != nil { if err != nil {
return fmt.Errorf("connect l1 fetcher: %w", err) return fmt.Errorf("failed to setup L1 RPC: %w", err)
} }
logger.Info("Connecting to L2 node", "l2", cfg.L2URL) logger.Info("Connecting to L2 node", "l2", cfg.L2URL)
l2Fetcher, err := l2.NewFetchingOracle(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 { if err != nil {
return fmt.Errorf("connect l2 fetcher: %w", err) return fmt.Errorf("failed to create L2 client: %w", err)
} }
prefetch := prefetcher.NewPrefetcher(l1Fetcher, l2Fetcher, kv) l2DebugCl := &L2Source{L2Client: l2Cl, DebugClient: sources.NewDebugClient(l2RPC.CallContext)}
preimageOracle := asOracleFn(prefetch)
logger.Info("Setting up pre-fetcher")
prefetch := prefetcher.NewPrefetcher(l1Cl, l2DebugCl, kv)
preimageOracle := asOracleFn(ctx, prefetch)
hinter := asHinter(prefetch) hinter := asHinter(prefetch)
l1Source := l1.NewSource(logger, preimageOracle, hinter, cfg.L1Head) l1Source := l1.NewSource(logger, preimageOracle, hinter, cfg.L1Head)
...@@ -145,9 +166,9 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error { ...@@ -145,9 +166,9 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
return nil return nil
} }
func asOracleFn(prefetcher *prefetcher.Prefetcher) preimage.OracleFn { func asOracleFn(ctx context.Context, prefetcher *prefetcher.Prefetcher) preimage.OracleFn {
return func(key preimage.Key) []byte { return func(key preimage.Key) []byte {
pre, err := prefetcher.GetPreimage(key.PreimageKey()) pre, err := prefetcher.GetPreimage(ctx, key.PreimageKey())
if err != nil { if err != nil {
panic(fmt.Errorf("preimage unavailable for key %v: %w", key, err)) panic(fmt.Errorf("preimage unavailable for key %v: %w", key, err))
} }
......
package prefetcher package prefetcher
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-program/client/l1" "github.com/ethereum-optimism/optimism/op-program/client/l1"
"github.com/ethereum-optimism/optimism/op-program/client/l2" "github.com/ethereum-optimism/optimism/op-program/client/l2"
"github.com/ethereum-optimism/optimism/op-program/client/mpt" "github.com/ethereum-optimism/optimism/op-program/client/mpt"
"github.com/ethereum-optimism/optimism/op-program/host/kvstore" "github.com/ethereum-optimism/optimism/op-program/host/kvstore"
"github.com/ethereum-optimism/optimism/op-program/preimage" "github.com/ethereum-optimism/optimism/op-program/preimage"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
) )
type L1Source interface {
InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error)
InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error)
FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error)
}
type L2Source interface {
InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error)
NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error)
CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error)
}
type Prefetcher struct { type Prefetcher struct {
l1Fetcher l1.Oracle l1Fetcher L1Source
l2Fetcher l2.Oracle l2Fetcher L2Source
lastHint string lastHint string
kvStore kvstore.KV kvStore kvstore.KV
} }
func NewPrefetcher(l1Fetcher l1.Oracle, l2Fetcher l2.Oracle, kvStore kvstore.KV) *Prefetcher { func NewPrefetcher(l1Fetcher L1Source, l2Fetcher L2Source, kvStore kvstore.KV) *Prefetcher {
return &Prefetcher{ return &Prefetcher{
l1Fetcher: l1Fetcher, l1Fetcher: l1Fetcher,
l2Fetcher: l2Fetcher, l2Fetcher: l2Fetcher,
...@@ -38,12 +51,12 @@ func (p *Prefetcher) Hint(hint string) error { ...@@ -38,12 +51,12 @@ func (p *Prefetcher) Hint(hint string) error {
return nil return nil
} }
func (p *Prefetcher) GetPreimage(key common.Hash) ([]byte, error) { func (p *Prefetcher) GetPreimage(ctx context.Context, key common.Hash) ([]byte, error) {
pre, err := p.kvStore.Get(key) pre, err := p.kvStore.Get(key)
if errors.Is(err, kvstore.ErrNotFound) && p.lastHint != "" { if errors.Is(err, kvstore.ErrNotFound) && p.lastHint != "" {
hint := p.lastHint hint := p.lastHint
p.lastHint = "" p.lastHint = ""
if err := p.prefetch(hint); err != nil { if err := p.prefetch(ctx, hint); err != nil {
return nil, fmt.Errorf("prefetch failed: %w", err) return nil, fmt.Errorf("prefetch failed: %w", err)
} }
// Should now be available // Should now be available
...@@ -52,52 +65,72 @@ func (p *Prefetcher) GetPreimage(key common.Hash) ([]byte, error) { ...@@ -52,52 +65,72 @@ func (p *Prefetcher) GetPreimage(key common.Hash) ([]byte, error) {
return pre, err return pre, err
} }
func (p *Prefetcher) prefetch(hint string) error { func (p *Prefetcher) prefetch(ctx context.Context, hint string) error {
hintType, hash, err := parseHint(hint) hintType, hash, err := parseHint(hint)
if err != nil { if err != nil {
return err return err
} }
switch hintType { switch hintType {
case l1.HintL1BlockHeader: case l1.HintL1BlockHeader:
header := p.l1Fetcher.HeaderByBlockHash(hash) header, err := p.l1Fetcher.InfoByHash(ctx, hash)
if err != nil {
return fmt.Errorf("failed to fetch L1 block %s header: %w", hash, err)
}
data, err := header.HeaderRLP() data, err := header.HeaderRLP()
if err != nil { if err != nil {
return fmt.Errorf("marshall header: %w", err) return fmt.Errorf("marshall header: %w", err)
} }
return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), data) return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), data)
case l1.HintL1Transactions: case l1.HintL1Transactions:
_, txs := p.l1Fetcher.TransactionsByBlockHash(hash) _, txs, err := p.l1Fetcher.InfoAndTxsByHash(ctx, hash)
if err != nil {
return fmt.Errorf("failed to fetch L1 block %s txs: %w", hash, err)
}
return p.storeTransactions(txs) return p.storeTransactions(txs)
case l1.HintL1Receipts: case l1.HintL1Receipts:
_, rcpts := p.l1Fetcher.ReceiptsByBlockHash(hash) _, receipts, err := p.l1Fetcher.FetchReceipts(ctx, hash)
opaqueRcpts, err := eth.EncodeReceipts(rcpts)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to fetch L1 block %s receipts: %w", hash, err)
} }
return p.storeTrieNodes(opaqueRcpts) return p.storeReceipts(receipts)
case l2.HintL2BlockHeader: case l2.HintL2BlockHeader:
// Pre-fetch both block and transactions header, txs, err := p.l2Fetcher.InfoAndTxsByHash(ctx, hash)
block := p.l2Fetcher.BlockByHash(hash)
data, err := rlp.EncodeToBytes(block.Header())
if err != nil { if err != nil {
return fmt.Errorf("marshall header: %w", err) return fmt.Errorf("failed to fetch L2 block %s: %w", hash, err)
}
data, err := header.HeaderRLP()
if err != nil {
return fmt.Errorf("failed to encode header to RLP: %w", err)
} }
err = p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), data) err = p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), data)
if err != nil { if err != nil {
return err return err
} }
return p.storeTransactions(block.Transactions()) return p.storeTransactions(txs)
case l2.HintL2StateNode: case l2.HintL2StateNode:
node := p.l2Fetcher.NodeByHash(hash) node, err := p.l2Fetcher.NodeByHash(ctx, hash)
if err != nil {
return fmt.Errorf("failed to fetch L2 state node %s: %w", hash, err)
}
return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), node) return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), node)
case l2.HintL2Code: case l2.HintL2Code:
code := p.l2Fetcher.CodeByHash(hash) code, err := p.l2Fetcher.CodeByHash(ctx, hash)
if err != nil {
return fmt.Errorf("failed to fetch L2 contract code %s: %w", hash, err)
}
return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), code) return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), code)
} }
return fmt.Errorf("unknown hint type: %v", hintType) return fmt.Errorf("unknown hint type: %v", hintType)
} }
func (p *Prefetcher) storeReceipts(receipts types.Receipts) error {
opaqueReceipts, err := eth.EncodeReceipts(receipts)
if err != nil {
return err
}
return p.storeTrieNodes(opaqueReceipts)
}
func (p *Prefetcher) storeTransactions(txs types.Transactions) error { func (p *Prefetcher) storeTransactions(txs types.Transactions) error {
opaqueTxs, err := eth.EncodeTransactions(txs) opaqueTxs, err := eth.EncodeTransactions(txs)
if err != nil { if err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment