Commit 4739b0f8 authored by Adrian Sutton's avatar Adrian Sutton

op-program: Implement prefetcher to use hints to populate the preimage oracle

op-program now uses an in-memory key-value store as the backing for all data with the same key-based retrieval and deserialization as in non-fetching mode.
parent e6f1f61c
......@@ -6,12 +6,18 @@ 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
......@@ -19,7 +25,7 @@ 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
......@@ -27,5 +33,5 @@ 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()
}
......@@ -6,12 +6,19 @@ 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
......@@ -19,7 +26,7 @@ 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
......@@ -27,7 +34,7 @@ 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
......@@ -35,5 +42,5 @@ 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()
}
......@@ -6,17 +6,18 @@ import (
"fmt"
"io"
"os"
"time"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
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"
......@@ -104,27 +105,35 @@ 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)
l1Fetcher, err := l1.NewFetchingOracle(ctx, logger, cfg)
if err != nil {
return fmt.Errorf("connect l1 fetcher: %w", err)
}
logger.Info("Connecting to L2 node", "l2", cfg.L2URL)
l2Fetcher, err := l2.NewFetchingOracle(ctx, logger, cfg)
if err != nil {
return fmt.Errorf("connect l1 oracle: %w", err)
return fmt.Errorf("connect l2 fetcher: %w", err)
}
prefetch := prefetcher.NewPrefetcher(l1Fetcher, l2Fetcher, kv)
preimageOracle := asOracleFn(prefetch)
hinter := asHinter(prefetch)
l1Source := l1.NewSource(logger, preimageOracle, hinter, cfg.L1Head)
logger.Info("Connecting to L2 node", "l2", cfg.L2URL)
l2Source, err := l2.NewFetchingEngine(ctx, logger, cfg)
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
}
......@@ -135,3 +144,22 @@ func FaultProofProgram(logger log.Logger, cfg *config.Config) error {
}
return nil
}
func asOracleFn(prefetcher *prefetcher.Prefetcher) preimage.OracleFn {
return func(key preimage.Key) []byte {
pre, err := prefetcher.GetPreimage(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))
}
}
}
......@@ -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
......@@ -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)
}
......@@ -8,22 +8,18 @@ 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)
......@@ -31,6 +27,14 @@ func NewFetchingEngine(ctx context.Context, logger log.Logger, cfg *config.Confi
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 {
......
package prefetcher
import (
"errors"
"fmt"
"strings"
"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/l2"
"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/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 Prefetcher struct {
l1Fetcher l1.Oracle
l2Fetcher l2.Oracle
lastHint string
kvStore kvstore.KV
}
func NewPrefetcher(l1Fetcher l1.Oracle, l2Fetcher l2.Oracle, kvStore kvstore.KV) *Prefetcher {
return &Prefetcher{
l1Fetcher: l1Fetcher,
l2Fetcher: l2Fetcher,
kvStore: kvStore,
}
}
func (p *Prefetcher) Hint(hint string) error {
p.lastHint = hint
return nil
}
func (p *Prefetcher) GetPreimage(key common.Hash) ([]byte, error) {
pre, err := p.kvStore.Get(key)
if errors.Is(err, kvstore.ErrNotFound) && p.lastHint != "" {
hint := p.lastHint
p.lastHint = ""
if err := p.prefetch(hint); err != nil {
return nil, fmt.Errorf("prefetch failed: %w", err)
}
// Should now be available
return p.kvStore.Get(key)
}
return pre, err
}
func (p *Prefetcher) prefetch(hint string) error {
hintType, hash, err := parseHint(hint)
if err != nil {
return err
}
switch hintType {
case l1.HintL1BlockHeader:
header := p.l1Fetcher.HeaderByBlockHash(hash)
data, err := header.HeaderRLP()
if err != nil {
return fmt.Errorf("marshall header: %w", err)
}
return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), data)
case l1.HintL1Transactions:
_, txs := p.l1Fetcher.TransactionsByBlockHash(hash)
return p.storeTransactions(txs)
case l1.HintL1Receipts:
_, rcpts := p.l1Fetcher.ReceiptsByBlockHash(hash)
opaqueRcpts, err := eth.EncodeReceipts(rcpts)
if err != nil {
return err
}
return p.storeTrieNodes(opaqueRcpts)
case l2.HintL2BlockHeader:
// Pre-fetch both block and transactions
block := p.l2Fetcher.BlockByHash(hash)
data, err := rlp.EncodeToBytes(block.Header())
if err != nil {
return fmt.Errorf("marshall header: %w", err)
}
err = p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), data)
if err != nil {
return err
}
return p.storeTransactions(block.Transactions())
case l2.HintL2StateNode:
node := p.l2Fetcher.NodeByHash(hash)
return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), node)
case l2.HintL2Code:
code := p.l2Fetcher.CodeByHash(hash)
return p.kvStore.Put(preimage.Keccak256Key(hash).PreimageKey(), code)
}
return fmt.Errorf("unknown hint type: %v", hintType)
}
func (p *Prefetcher) storeTransactions(txs types.Transactions) error {
opaqueTxs, err := eth.EncodeTransactions(txs)
if err != nil {
return err
}
return p.storeTrieNodes(opaqueTxs)
}
func (p *Prefetcher) storeTrieNodes(values []hexutil.Bytes) error {
_, nodes := mpt.WriteTrie(values)
for _, node := range nodes {
err := p.kvStore.Put(preimage.Keccak256Key(crypto.Keccak256Hash(node)).PreimageKey(), node)
if err != nil {
return fmt.Errorf("failed to store node: %w", err)
}
}
return nil
}
// parseHint parses a hint string in wire protocol. Returns the hint type, requested hash and error (if any).
func parseHint(hint string) (string, common.Hash, error) {
hintType, hashStr, found := strings.Cut(hint, " ")
if !found {
return "", common.Hash{}, fmt.Errorf("unsupported hint: %s", hint)
}
hash := common.HexToHash(hashStr)
if hash == (common.Hash{}) {
return "", common.Hash{}, fmt.Errorf("invalid hash: %s", hashStr)
}
return hintType, hash, nil
}
package prefetcher
import (
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-program/client/l1"
l1test "github.com/ethereum-optimism/optimism/op-program/client/l1/test"
"github.com/ethereum-optimism/optimism/op-program/client/l2"
l2test "github.com/ethereum-optimism/optimism/op-program/client/l2/test"
"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/preimage"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/require"
)
func TestNoHint(t *testing.T) {
t.Run("NotFound", func(t *testing.T) {
prefetcher, _, _, _, _ := createPrefetcher(t)
res, err := prefetcher.GetPreimage(common.Hash{0xab})
require.ErrorIs(t, err, kvstore.ErrNotFound)
require.Nil(t, res)
})
t.Run("Exists", func(t *testing.T) {
prefetcher, _, _, _, kv := createPrefetcher(t)
data := []byte{1, 2, 3}
hash := crypto.Keccak256Hash(data)
require.NoError(t, kv.Put(hash, data))
res, err := prefetcher.GetPreimage(hash)
require.NoError(t, err)
require.Equal(t, res, data)
})
}
func TestFetchL1BlockHeader(t *testing.T) {
rng := rand.New(rand.NewSource(123))
block, rcpts := testutils.RandomBlock(rng, 2)
hash := block.Hash()
key := preimage.Keccak256Key(hash).PreimageKey()
pre, err := rlp.EncodeToBytes(block.Header())
require.NoError(t, err)
t.Run("AlreadyKnown", func(t *testing.T) {
prefetcher, _, _, _, kv := createPrefetcher(t)
storeBlock(t, kv, block, rcpts)
oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result := oracle.HeaderByBlockHash(hash)
require.Equal(t, eth.HeaderBlockInfo(block.Header()), result)
})
t.Run("Unknown", func(t *testing.T) {
prefetcher, l1Oracle, _, _, _ := createPrefetcher(t)
l1Oracle.Blocks[hash] = eth.HeaderBlockInfo(block.Header())
require.NoError(t, prefetcher.Hint(l1.BlockHeaderHint(hash).Hint()))
result, err := prefetcher.GetPreimage(key)
require.NoError(t, err)
require.Equal(t, pre, result)
})
}
func TestFetchL1Transactions(t *testing.T) {
rng := rand.New(rand.NewSource(123))
block, rcpts := testutils.RandomBlock(rng, 10)
hash := block.Hash()
t.Run("AlreadyKnown", func(t *testing.T) {
prefetcher, _, _, _, kv := createPrefetcher(t)
storeBlock(t, kv, block, rcpts)
// Check the data is available (note the oracle does not know about the block, only the kvstore does)
oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
header, txs := oracle.TransactionsByBlockHash(hash)
require.EqualValues(t, hash, header.Hash())
assertTransactionsEqual(t, block.Transactions(), txs)
})
t.Run("Unknown", func(t *testing.T) {
prefetcher, l1Oracle, _, _, _ := createPrefetcher(t)
l1Oracle.Blocks[hash] = eth.BlockToInfo(block)
l1Oracle.Txs[hash] = block.Transactions()
oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
header, txs := oracle.TransactionsByBlockHash(hash)
require.EqualValues(t, hash, header.Hash())
assertTransactionsEqual(t, block.Transactions(), txs)
})
}
func TestFetchL1Receipts(t *testing.T) {
rng := rand.New(rand.NewSource(123))
block, receipts := testutils.RandomBlock(rng, 10)
hash := block.Hash()
t.Run("AlreadyKnown", func(t *testing.T) {
prefetcher, _, _, _, kv := createPrefetcher(t)
storeBlock(t, kv, block, receipts)
// Check the data is available (note the oracle does not know about the block, only the kvstore does)
oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
header, actualReceipts := oracle.ReceiptsByBlockHash(hash)
require.EqualValues(t, hash, header.Hash())
assertReceiptsEqual(t, receipts, actualReceipts)
})
t.Run("Unknown", func(t *testing.T) {
prefetcher, l1Oracle, _, _, _ := createPrefetcher(t)
l1Oracle.Blocks[hash] = eth.BlockToInfo(block)
l1Oracle.Txs[hash] = block.Transactions()
l1Oracle.Rcpts[hash] = receipts
oracle := l1.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
header, actualReceipts := oracle.ReceiptsByBlockHash(hash)
require.EqualValues(t, hash, header.Hash())
assertReceiptsEqual(t, receipts, actualReceipts)
})
}
func TestFetchL2Block(t *testing.T) {
rng := rand.New(rand.NewSource(123))
block, rcpts := testutils.RandomBlock(rng, 10)
hash := block.Hash()
t.Run("AlreadyKnown", func(t *testing.T) {
prefetcher, _, _, _, kv := createPrefetcher(t)
storeBlock(t, kv, block, rcpts)
oracle := l2.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result := oracle.BlockByHash(hash)
require.EqualValues(t, block.Header(), result.Header())
assertTransactionsEqual(t, block.Transactions(), result.Transactions())
})
t.Run("Unknown", func(t *testing.T) {
prefetcher, _, l2BlockOracle, _, _ := createPrefetcher(t)
l2BlockOracle.Blocks[hash] = block
oracle := l2.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result := oracle.BlockByHash(hash)
require.EqualValues(t, block.Header(), result.Header())
assertTransactionsEqual(t, block.Transactions(), result.Transactions())
})
}
func TestFetchL2Node(t *testing.T) {
rng := rand.New(rand.NewSource(123))
node := testutils.RandomData(rng, 30)
hash := crypto.Keccak256Hash(node)
key := preimage.Keccak256Key(hash).PreimageKey()
t.Run("AlreadyKnown", func(t *testing.T) {
prefetcher, _, _, _, kv := createPrefetcher(t)
require.NoError(t, kv.Put(key, node))
oracle := l2.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result := oracle.NodeByHash(hash)
require.EqualValues(t, node, result)
})
t.Run("Unknown", func(t *testing.T) {
prefetcher, _, _, l2StateOracle, _ := createPrefetcher(t)
l2StateOracle.Data[hash] = node
oracle := l2.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result := oracle.NodeByHash(hash)
require.EqualValues(t, node, result)
})
}
func TestFetchL2Code(t *testing.T) {
rng := rand.New(rand.NewSource(123))
code := testutils.RandomData(rng, 30)
hash := crypto.Keccak256Hash(code)
key := preimage.Keccak256Key(hash).PreimageKey()
t.Run("AlreadyKnown", func(t *testing.T) {
prefetcher, _, _, _, kv := createPrefetcher(t)
require.NoError(t, kv.Put(key, code))
oracle := l2.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result := oracle.CodeByHash(hash)
require.EqualValues(t, code, result)
})
t.Run("Unknown", func(t *testing.T) {
prefetcher, _, _, l2StateOracle, _ := createPrefetcher(t)
l2StateOracle.Code[hash] = code
oracle := l2.NewPreimageOracle(asOracleFn(t, prefetcher), asHinter(t, prefetcher))
result := oracle.CodeByHash(hash)
require.EqualValues(t, code, result)
})
}
func TestBadHints(t *testing.T) {
prefetcher, _, _, _, kv := createPrefetcher(t)
hash := common.Hash{0xad}
t.Run("NoSpace", func(t *testing.T) {
// Accept the hint
require.NoError(t, prefetcher.Hint(l1.HintL1BlockHeader))
// But it will fail to prefetch when the pre-image isn't available
pre, err := prefetcher.GetPreimage(hash)
require.ErrorContains(t, err, "unsupported hint")
require.Nil(t, pre)
})
t.Run("InvalidHash", func(t *testing.T) {
// Accept the hint
require.NoError(t, prefetcher.Hint(l1.HintL1BlockHeader+" asdfsadf"))
// But it will fail to prefetch when the pre-image isn't available
pre, err := prefetcher.GetPreimage(hash)
require.ErrorContains(t, err, "invalid hash")
require.Nil(t, pre)
})
t.Run("UnknownType", func(t *testing.T) {
// Accept the hint
require.NoError(t, prefetcher.Hint("unknown "+hash.Hex()))
// But it will fail to prefetch when the pre-image isn't available
pre, err := prefetcher.GetPreimage(hash)
require.ErrorContains(t, err, "unknown hint type")
require.Nil(t, pre)
})
// Should not return hint errors if the preimage is already available
t.Run("KeyExists", func(t *testing.T) {
// Prepopulate the requested preimage
value := []byte{1, 2, 3, 4}
require.NoError(t, kv.Put(hash, value))
// Hint is invalid
require.NoError(t, prefetcher.Hint("asdfsadf"))
// But fetching the key fails because prefetching isn't required
pre, err := prefetcher.GetPreimage(hash)
require.NoError(t, err)
require.Equal(t, value, pre)
})
}
func createPrefetcher(t *testing.T) (*Prefetcher, *l1test.StubOracle, *l2test.StubBlockOracle, *l2test.StubStateOracle, kvstore.KV) {
kv := kvstore.NewMemKV()
l1Oracle := l1test.NewStubOracle(t)
l2BlockOracle, l2StateOracle := l2test.NewStubOracle(t)
prefetcher := NewPrefetcher(l1Oracle, l2BlockOracle, kv)
return prefetcher, l1Oracle, l2BlockOracle, l2StateOracle, kv
}
func storeBlock(t *testing.T, kv kvstore.KV, block *types.Block, receipts types.Receipts) {
// Pre-store receipts
opaqueRcpts, err := eth.EncodeReceipts(receipts)
require.NoError(t, err)
_, nodes := mpt.WriteTrie(opaqueRcpts)
for _, p := range nodes {
require.NoError(t, kv.Put(preimage.Keccak256Key(crypto.Keccak256Hash(p)).PreimageKey(), p))
}
// Pre-store transactions
opaqueTxs, err := eth.EncodeTransactions(block.Transactions())
require.NoError(t, err)
_, txsNodes := mpt.WriteTrie(opaqueTxs)
for _, p := range txsNodes {
require.NoError(t, kv.Put(preimage.Keccak256Key(crypto.Keccak256Hash(p)).PreimageKey(), p))
}
// Pre-store block
headerRlp, err := rlp.EncodeToBytes(block.Header())
require.NoError(t, err)
require.NoError(t, kv.Put(preimage.Keccak256Key(block.Hash()).PreimageKey(), headerRlp))
}
func asOracleFn(t *testing.T, prefetcher *Prefetcher) preimage.OracleFn {
return func(key preimage.Key) []byte {
pre, err := prefetcher.GetPreimage(key.PreimageKey())
require.NoError(t, err)
return pre
}
}
func asHinter(t *testing.T, prefetcher *Prefetcher) preimage.HinterFn {
return func(v preimage.Hint) {
err := prefetcher.Hint(v.Hint())
require.NoError(t, err)
}
}
func assertTransactionsEqual(t *testing.T, blockTx types.Transactions, txs types.Transactions) {
require.Equal(t, len(blockTx), len(txs))
for i, tx := range txs {
require.Equal(t, blockTx[i].Hash(), tx.Hash())
}
}
func assertReceiptsEqual(t *testing.T, expectedRcpt types.Receipts, actualRcpt types.Receipts) {
require.Equal(t, len(expectedRcpt), len(actualRcpt))
for i, rcpt := range actualRcpt {
// Make a copy of each to zero out fields we expect to be different
expected := *expectedRcpt[i]
actual := *rcpt
expected.ContractAddress = common.Address{}
actual.ContractAddress = common.Address{}
require.Equal(t, expected, actual)
}
}
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