Skip to content

Commit

Permalink
Merge pull request #5506 from ethereum-optimism/aj/fpp-existing-node
Browse files Browse the repository at this point in the history
op-program: Handle pre-images that already exist
  • Loading branch information
OptimismBot authored Apr 20, 2023
2 parents 171b59e + e57c753 commit ad7a387
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 8 deletions.
21 changes: 13 additions & 8 deletions op-program/host/prefetcher/prefetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@ import (
"fmt"
"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/go-ethereum/log"

"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/log"
)

type L1Source interface {
Expand All @@ -33,6 +32,7 @@ type L2Source interface {
}

type Prefetcher struct {
logger log.Logger
l1Fetcher L1Source
l2Fetcher L2Source
lastHint string
Expand All @@ -41,6 +41,7 @@ type Prefetcher struct {

func NewPrefetcher(logger log.Logger, l1Fetcher L1Source, l2Fetcher L2Source, kvStore kvstore.KV) *Prefetcher {
return &Prefetcher{
logger: logger,
l1Fetcher: NewRetryingL1Source(logger, l1Fetcher),
l2Fetcher: NewRetryingL2Source(logger, l2Fetcher),
kvStore: kvStore,
Expand Down Expand Up @@ -71,6 +72,7 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error {
if err != nil {
return err
}
p.logger.Debug("Prefetching", "type", hintType, "hash", hash)
switch hintType {
case l1.HintL1BlockHeader:
header, err := p.l1Fetcher.InfoByHash(ctx, hash)
Expand Down Expand Up @@ -143,8 +145,11 @@ func (p *Prefetcher) storeTransactions(txs types.Transactions) error {
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 {
key := preimage.Keccak256Key(crypto.Keccak256Hash(node)).PreimageKey()
if err := p.kvStore.Put(key, node); errors.Is(err, kvstore.ErrAlreadyExists) {
// It's not uncommon for different tries to contain common nodes (esp for receipts)
continue
} else if err != nil {
return fmt.Errorf("failed to store node: %w", err)
}
}
Expand Down
22 changes: 22 additions & 0 deletions op-program/host/prefetcher/prefetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,28 @@ func TestFetchL1Receipts(t *testing.T) {
require.EqualValues(t, hash, header.Hash())
assertReceiptsEqual(t, receipts, actualReceipts)
})

// Blocks may have identical RLP receipts for different transactions.
// Check that the node already existing is handled
t.Run("CommonTrieNodes", func(t *testing.T) {
prefetcher, l1Cl, _, kv := createPrefetcher(t)
l1Cl.ExpectInfoByHash(hash, eth.BlockToInfo(block), nil)
l1Cl.ExpectInfoAndTxsByHash(hash, eth.BlockToInfo(block), block.Transactions(), nil)
l1Cl.ExpectFetchReceipts(hash, eth.BlockToInfo(block), receipts, nil)
defer l1Cl.AssertExpectations(t)

// Pre-store one receipt node (but not the whole trie leading to it)
// This would happen if an identical receipt was in an earlier block
opaqueRcpts, err := eth.EncodeReceipts(receipts)
require.NoError(t, err)
_, nodes := mpt.WriteTrie(opaqueRcpts)
require.NoError(t, kv.Put(preimage.Keccak256Key(crypto.Keccak256Hash(nodes[0])).PreimageKey(), nodes[0]))

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) {
Expand Down
8 changes: 8 additions & 0 deletions op-program/preimage/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ func (k Keccak256Key) PreimageKey() (out common.Hash) {
return
}

func (k Keccak256Key) String() string {
return common.Hash(k).String()
}

func (k Keccak256Key) TerminalString() string {
return common.Hash(k).String()
}

// Hint is an interface to enable any program type to function as a hint,
// when passed to the Hinter interface, returning a string representation
// of what data the host should prepare pre-images for.
Expand Down

0 comments on commit ad7a387

Please sign in to comment.