Commit 715e630b authored by inphi's avatar inphi

refactor stubs

parent a4a3ff36
...@@ -12,24 +12,21 @@ const cacheSize = 2000 ...@@ -12,24 +12,21 @@ const cacheSize = 2000
// CachingOracle is an implementation of Oracle that delegates to another implementation, adding caching of all results // CachingOracle is an implementation of Oracle that delegates to another implementation, adding caching of all results
type CachingOracle struct { type CachingOracle struct {
oracle Oracle oracle Oracle
blocks *simplelru.LRU[common.Hash, eth.BlockInfo] blocks *simplelru.LRU[common.Hash, eth.BlockInfo]
txs *simplelru.LRU[common.Hash, types.Transactions] txs *simplelru.LRU[common.Hash, types.Transactions]
rcpts *simplelru.LRU[common.Hash, types.Receipts] rcpts *simplelru.LRU[common.Hash, types.Receipts]
outputs *simplelru.LRU[common.Hash, eth.Output]
} }
func NewCachingOracle(oracle Oracle) *CachingOracle { func NewCachingOracle(oracle Oracle) *CachingOracle {
blockLRU, _ := simplelru.NewLRU[common.Hash, eth.BlockInfo](cacheSize, nil) blockLRU, _ := simplelru.NewLRU[common.Hash, eth.BlockInfo](cacheSize, nil)
txsLRU, _ := simplelru.NewLRU[common.Hash, types.Transactions](cacheSize, nil) txsLRU, _ := simplelru.NewLRU[common.Hash, types.Transactions](cacheSize, nil)
rcptsLRU, _ := simplelru.NewLRU[common.Hash, types.Receipts](cacheSize, nil) rcptsLRU, _ := simplelru.NewLRU[common.Hash, types.Receipts](cacheSize, nil)
outputsLRU, _ := simplelru.NewLRU[common.Hash, eth.Output](cacheSize, nil)
return &CachingOracle{ return &CachingOracle{
oracle: oracle, oracle: oracle,
blocks: blockLRU, blocks: blockLRU,
txs: txsLRU, txs: txsLRU,
rcpts: rcptsLRU, rcpts: rcptsLRU,
outputs: outputsLRU,
} }
} }
...@@ -64,13 +61,3 @@ func (o *CachingOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInf ...@@ -64,13 +61,3 @@ func (o *CachingOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInf
o.rcpts.Add(blockHash, rcpts) o.rcpts.Add(blockHash, rcpts)
return block, rcpts return block, rcpts
} }
func (o *CachingOracle) L2OutputByRoot(l2OutputRoot common.Hash) eth.Output {
output, ok := o.outputs.Get(l2OutputRoot)
if ok {
return output
}
output = o.oracle.L2OutputByRoot(l2OutputRoot)
o.outputs.Add(l2OutputRoot, output)
return output
}
...@@ -7,7 +7,6 @@ import ( ...@@ -7,7 +7,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testutils" "github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-program/client/l1/test" "github.com/ethereum-optimism/optimism/op-program/client/l1/test"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -72,29 +71,3 @@ func TestCachingOracle_ReceiptsByBlockHash(t *testing.T) { ...@@ -72,29 +71,3 @@ func TestCachingOracle_ReceiptsByBlockHash(t *testing.T) {
require.Equal(t, eth.BlockToInfo(block), actualBlock) require.Equal(t, eth.BlockToInfo(block), actualBlock)
require.EqualValues(t, rcpts, actualRcpts) require.EqualValues(t, rcpts, actualRcpts)
} }
func TestCachingOracle_L2OutputByRoot(t *testing.T) {
rng := rand.New(rand.NewSource(1))
stub := test.NewStubOracle(t)
oracle := NewCachingOracle(stub)
block, _ := testutils.RandomBlock(rng, 3)
var storageRoot [32]byte
rng.Read(storageRoot[:])
l2Output := &eth.OutputV0{
StateRoot: eth.Bytes32(block.Root()),
MessagePasserStorageRoot: storageRoot,
BlockHash: block.Hash(),
}
l2OutputRoot := common.Hash(eth.OutputRoot(l2Output))
// Initial call retrieves from the stub
stub.L2Outputs[l2OutputRoot] = l2Output
result := oracle.L2OutputByRoot(l2OutputRoot)
require.Equal(t, l2Output.Marshal(), result)
// Later calls should retrieve from cache
delete(stub.L2Outputs, l2OutputRoot)
result = oracle.L2OutputByRoot(l2OutputRoot)
require.Equal(t, l2Output.Marshal(), result)
}
...@@ -80,7 +80,3 @@ func (o *OracleL1Client) InfoAndTxsByHash(ctx context.Context, hash common.Hash) ...@@ -80,7 +80,3 @@ func (o *OracleL1Client) InfoAndTxsByHash(ctx context.Context, hash common.Hash)
info, txs := o.oracle.TransactionsByBlockHash(hash) info, txs := o.oracle.TransactionsByBlockHash(hash)
return info, txs, nil return info, txs, nil
} }
func (o *OracleL1Client) L2OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
return o.oracle.L2OutputByRoot(root), nil
}
...@@ -10,7 +10,6 @@ const ( ...@@ -10,7 +10,6 @@ const (
HintL1BlockHeader = "l1-block-header" HintL1BlockHeader = "l1-block-header"
HintL1Transactions = "l1-transactions" HintL1Transactions = "l1-transactions"
HintL1Receipts = "l1-receipts" HintL1Receipts = "l1-receipts"
HintL2Output = "l1-l2-output"
) )
type BlockHeaderHint common.Hash type BlockHeaderHint common.Hash
...@@ -36,11 +35,3 @@ var _ preimage.Hint = ReceiptsHint{} ...@@ -36,11 +35,3 @@ var _ preimage.Hint = ReceiptsHint{}
func (l ReceiptsHint) Hint() string { func (l ReceiptsHint) Hint() string {
return HintL1Receipts + " " + (common.Hash)(l).String() return HintL1Receipts + " " + (common.Hash)(l).String()
} }
type L2OutputHint common.Hash
var _ preimage.Hint = L2OutputHint{}
func (l L2OutputHint) Hint() string {
return HintL2Output + " " + (common.Hash)(l).String()
}
...@@ -21,9 +21,6 @@ type Oracle interface { ...@@ -21,9 +21,6 @@ type Oracle interface {
// ReceiptsByBlockHash retrieves the receipts from the block with the given hash. // ReceiptsByBlockHash retrieves the receipts from the block with the given hash.
ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts)
// L2OutputByRoot retrieves the L2 output for the given L2 output root.
L2OutputByRoot(l2OutputRoot common.Hash) eth.Output
} }
// PreimageOracle implements Oracle using by interfacing with the pure preimage.Oracle // PreimageOracle implements Oracle using by interfacing with the pure preimage.Oracle
...@@ -89,13 +86,3 @@ func (p *PreimageOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockIn ...@@ -89,13 +86,3 @@ func (p *PreimageOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockIn
return info, receipts return info, receipts
} }
func (p *PreimageOracle) L2OutputByRoot(l2OutputRoot common.Hash) eth.Output {
p.hint.Hint(L2OutputHint(l2OutputRoot))
data := p.oracle.Get(preimage.Keccak256Key(l2OutputRoot))
output, err := eth.UnmarshalOutput(data)
if err != nil {
panic(fmt.Errorf("invalidd L2 output data for root %s: %w", l2OutputRoot, err))
}
return output
}
...@@ -19,18 +19,14 @@ type StubOracle struct { ...@@ -19,18 +19,14 @@ type StubOracle struct {
// Rcpts maps Block hash to receipts // Rcpts maps Block hash to receipts
Rcpts map[common.Hash]types.Receipts Rcpts map[common.Hash]types.Receipts
// L2Outputs maps L2 output roots to L2 outputs
L2Outputs map[common.Hash]eth.Output
} }
func NewStubOracle(t *testing.T) *StubOracle { func NewStubOracle(t *testing.T) *StubOracle {
return &StubOracle{ return &StubOracle{
t: t, t: t,
Blocks: make(map[common.Hash]eth.BlockInfo), Blocks: make(map[common.Hash]eth.BlockInfo),
Txs: make(map[common.Hash]types.Transactions), Txs: make(map[common.Hash]types.Transactions),
Rcpts: make(map[common.Hash]types.Receipts), Rcpts: make(map[common.Hash]types.Receipts),
L2Outputs: make(map[common.Hash]eth.Output),
} }
} }
func (o StubOracle) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo { func (o StubOracle) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo {
...@@ -56,11 +52,3 @@ func (o StubOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, t ...@@ -56,11 +52,3 @@ func (o StubOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, t
} }
return o.HeaderByBlockHash(blockHash), rcpts return o.HeaderByBlockHash(blockHash), rcpts
} }
func (o StubOracle) L2OutputByRoot(l2OutputRoot common.Hash) eth.Output {
output, ok := o.L2Outputs[l2OutputRoot]
if !ok {
o.t.Fatalf("unknown output %s", l2OutputRoot)
}
return output
}
package l2 package l2
import ( import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/hashicorp/golang-lru/v2/simplelru" "github.com/hashicorp/golang-lru/v2/simplelru"
...@@ -13,21 +14,24 @@ const nodeCacheSize = 100_000 ...@@ -13,21 +14,24 @@ const nodeCacheSize = 100_000
const codeCacheSize = 10_000 const codeCacheSize = 10_000
type CachingOracle struct { type CachingOracle struct {
oracle Oracle oracle Oracle
blocks *simplelru.LRU[common.Hash, *types.Block] blocks *simplelru.LRU[common.Hash, *types.Block]
nodes *simplelru.LRU[common.Hash, []byte] nodes *simplelru.LRU[common.Hash, []byte]
codes *simplelru.LRU[common.Hash, []byte] codes *simplelru.LRU[common.Hash, []byte]
outputs *simplelru.LRU[common.Hash, eth.Output]
} }
func NewCachingOracle(oracle Oracle) *CachingOracle { func NewCachingOracle(oracle Oracle) *CachingOracle {
blockLRU, _ := simplelru.NewLRU[common.Hash, *types.Block](blockCacheSize, nil) blockLRU, _ := simplelru.NewLRU[common.Hash, *types.Block](blockCacheSize, nil)
nodeLRU, _ := simplelru.NewLRU[common.Hash, []byte](nodeCacheSize, nil) nodeLRU, _ := simplelru.NewLRU[common.Hash, []byte](nodeCacheSize, nil)
codeLRU, _ := simplelru.NewLRU[common.Hash, []byte](codeCacheSize, nil) codeLRU, _ := simplelru.NewLRU[common.Hash, []byte](codeCacheSize, nil)
outputLRU, _ := simplelru.NewLRU[common.Hash, eth.Output](codeCacheSize, nil)
return &CachingOracle{ return &CachingOracle{
oracle: oracle, oracle: oracle,
blocks: blockLRU, blocks: blockLRU,
nodes: nodeLRU, nodes: nodeLRU,
codes: codeLRU, codes: codeLRU,
outputs: outputLRU,
} }
} }
...@@ -60,3 +64,13 @@ func (o *CachingOracle) BlockByHash(blockHash common.Hash) *types.Block { ...@@ -60,3 +64,13 @@ func (o *CachingOracle) BlockByHash(blockHash common.Hash) *types.Block {
o.blocks.Add(blockHash, block) o.blocks.Add(blockHash, block)
return block return block
} }
func (o *CachingOracle) L2OutputByRoot(root common.Hash) eth.Output {
output, ok := o.outputs.Get(root)
if ok {
return output
}
output = o.oracle.L2OutputByRoot(root)
o.outputs.Add(root, output)
return output
}
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi" "github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
...@@ -39,8 +40,13 @@ type OracleBackedL2Chain struct { ...@@ -39,8 +40,13 @@ type OracleBackedL2Chain struct {
var _ engineapi.EngineBackend = (*OracleBackedL2Chain)(nil) var _ engineapi.EngineBackend = (*OracleBackedL2Chain)(nil)
func NewOracleBackedL2Chain(logger log.Logger, oracle Oracle, chainCfg *params.ChainConfig, l2Head common.Hash) (*OracleBackedL2Chain, error) { func NewOracleBackedL2Chain(logger log.Logger, oracle Oracle, chainCfg *params.ChainConfig, l2OutputRoot common.Hash) (*OracleBackedL2Chain, error) {
head := oracle.BlockByHash(l2Head) output := oracle.L2OutputByRoot(l2OutputRoot)
outputV0, ok := output.(*eth.OutputV0)
if !ok {
return nil, fmt.Errorf("unsupported L2 output version: %d", output.Version())
}
head := oracle.BlockByHash(outputV0.BlockHash)
logger.Info("Loaded L2 head", "hash", head.Hash(), "number", head.Number()) logger.Info("Loaded L2 head", "hash", head.Hash(), "number", head.Number())
return &OracleBackedL2Chain{ return &OracleBackedL2Chain{
log: logger, log: logger,
......
...@@ -11,6 +11,7 @@ const ( ...@@ -11,6 +11,7 @@ const (
HintL2Transactions = "l2-transactions" HintL2Transactions = "l2-transactions"
HintL2Code = "l2-code" HintL2Code = "l2-code"
HintL2StateNode = "l2-state-node" HintL2StateNode = "l2-state-node"
HintL2Output = "l2-output"
) )
type BlockHeaderHint common.Hash type BlockHeaderHint common.Hash
...@@ -44,3 +45,11 @@ var _ preimage.Hint = StateNodeHint{} ...@@ -44,3 +45,11 @@ var _ preimage.Hint = StateNodeHint{}
func (l StateNodeHint) Hint() string { func (l StateNodeHint) Hint() string {
return HintL2StateNode + " " + (common.Hash)(l).String() return HintL2StateNode + " " + (common.Hash)(l).String()
} }
type L2OutputHint common.Hash
var _ preimage.Hint = L2OutputHint{}
func (l L2OutputHint) Hint() string {
return HintL2Output + " " + (common.Hash)(l).String()
}
...@@ -32,6 +32,8 @@ type Oracle interface { ...@@ -32,6 +32,8 @@ type Oracle interface {
// BlockByHash retrieves the block with the given hash. // BlockByHash retrieves the block with the given hash.
BlockByHash(blockHash common.Hash) *types.Block BlockByHash(blockHash common.Hash) *types.Block
L2OutputByRoot(root common.Hash) eth.Output
} }
// PreimageOracle implements Oracle using by interfacing with the pure preimage.Oracle // PreimageOracle implements Oracle using by interfacing with the pure preimage.Oracle
...@@ -85,3 +87,13 @@ func (p *PreimageOracle) CodeByHash(codeHash common.Hash) []byte { ...@@ -85,3 +87,13 @@ func (p *PreimageOracle) CodeByHash(codeHash common.Hash) []byte {
p.hint.Hint(CodeHint(codeHash)) p.hint.Hint(CodeHint(codeHash))
return p.oracle.Get(preimage.Keccak256Key(codeHash)) return p.oracle.Get(preimage.Keccak256Key(codeHash))
} }
func (p *PreimageOracle) L2OutputByRoot(l2OutputRoot common.Hash) eth.Output {
p.hint.Hint(L2OutputHint(l2OutputRoot))
data := p.oracle.Get(preimage.Keccak256Key(l2OutputRoot))
output, err := eth.UnmarshalOutput(data)
if err != nil {
panic(fmt.Errorf("invalid L2 output data for root %s: %w", l2OutputRoot, err))
}
return output
}
...@@ -3,6 +3,7 @@ package test ...@@ -3,6 +3,7 @@ package test
import ( import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -16,8 +17,9 @@ type stateOracle interface { ...@@ -16,8 +17,9 @@ type stateOracle interface {
} }
type StubBlockOracle struct { type StubBlockOracle struct {
t *testing.T t *testing.T
Blocks map[common.Hash]*types.Block Blocks map[common.Hash]*types.Block
L2Outputs map[common.Hash]eth.Output
stateOracle stateOracle
} }
...@@ -50,6 +52,14 @@ func (o StubBlockOracle) BlockByHash(blockHash common.Hash) *types.Block { ...@@ -50,6 +52,14 @@ func (o StubBlockOracle) BlockByHash(blockHash common.Hash) *types.Block {
return block return block
} }
func (o StubBlockOracle) L2OutputByRoot(root common.Hash) eth.Output {
output, ok := o.L2Outputs[root]
if !ok {
o.t.Fatalf("requested unknown output root %s", root)
}
return output
}
// KvStateOracle loads data from a source ethdb.KeyValueStore // KvStateOracle loads data from a source ethdb.KeyValueStore
type KvStateOracle struct { type KvStateOracle struct {
t *testing.T t *testing.T
......
...@@ -64,16 +64,7 @@ func RunProgram(logger log.Logger, preimageOracle io.ReadWriter, preimageHinter ...@@ -64,16 +64,7 @@ func RunProgram(logger log.Logger, preimageOracle io.ReadWriter, preimageHinter
// runDerivation executes the L2 state transition, given a minimal interface to retrieve data. // runDerivation executes the L2 state transition, given a minimal interface to retrieve data.
func runDerivation(logger log.Logger, cfg *rollup.Config, l2Cfg *params.ChainConfig, l1Head common.Hash, l2OutputRoot common.Hash, l2Claim common.Hash, l2ClaimBlockNum uint64, l1Oracle l1.Oracle, l2Oracle l2.Oracle) error { func runDerivation(logger log.Logger, cfg *rollup.Config, l2Cfg *params.ChainConfig, l1Head common.Hash, l2OutputRoot common.Hash, l2Claim common.Hash, l2ClaimBlockNum uint64, l1Oracle l1.Oracle, l2Oracle l2.Oracle) error {
l1Source := l1.NewOracleL1Client(logger, l1Oracle, l1Head) l1Source := l1.NewOracleL1Client(logger, l1Oracle, l1Head)
output, err := l1Source.L2OutputByRoot(context.Background(), l2OutputRoot) engineBackend, err := l2.NewOracleBackedL2Chain(logger, l2Oracle, l2Cfg, l2OutputRoot)
if err != nil {
return fmt.Errorf("failed to find L2 output for %s: %w", l2OutputRoot, err)
}
outputV0, ok := output.(*eth.OutputV0)
if !ok {
return fmt.Errorf("unsupported L2 output version: %d", output.Version())
}
l2Head := outputV0.BlockHash
engineBackend, err := l2.NewOracleBackedL2Chain(logger, l2Oracle, l2Cfg, l2Head)
if err != nil { if err != nil {
return fmt.Errorf("failed to create oracle-backed L2 chain: %w", err) return fmt.Errorf("failed to create oracle-backed L2 chain: %w", err)
} }
......
...@@ -99,12 +99,6 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error { ...@@ -99,12 +99,6 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error {
return fmt.Errorf("failed to fetch L1 block %s receipts: %w", hash, err) return fmt.Errorf("failed to fetch L1 block %s receipts: %w", hash, err)
} }
return p.storeReceipts(receipts) return p.storeReceipts(receipts)
case l1.HintL2Output:
output, err := p.l2Fetcher.L2OutputByRoot(ctx, hash)
if err != nil {
return fmt.Errorf("failed to fetch L2 output root %s: %w", hash, err)
}
return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), output.Marshal())
case l2.HintL2BlockHeader: case l2.HintL2BlockHeader:
header, txs, err := p.l2Fetcher.InfoAndTxsByHash(ctx, hash) header, txs, err := p.l2Fetcher.InfoAndTxsByHash(ctx, hash)
if err != nil { if err != nil {
...@@ -131,6 +125,12 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error { ...@@ -131,6 +125,12 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error {
return fmt.Errorf("failed to fetch L2 contract code %s: %w", hash, err) 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)
case l2.HintL2Output:
output, err := p.l2Fetcher.L2OutputByRoot(ctx, hash)
if err != nil {
return fmt.Errorf("failed to fetch L2 output root %s: %w", hash, err)
}
return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), output.Marshal())
} }
return fmt.Errorf("unknown hint type: %v", hintType) return fmt.Errorf("unknown hint type: %v", hintType)
} }
......
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