• Inphi's avatar
    op-program: Generic precompile oracle (#9699) · 44201560
    Inphi authored
    * op-program: Generic precompile oracle
    
    The generic precompile oracle replaces the point evaluation precompile
    oracle. The new oracle can be used to retrieve the return data and
    status of any precompile, including bn256Pairing and ecrecover.
    
    * op-challenger: Use generic precompile oracle
    
    Replace the KZG point evaluation oracle with a generic precompile oracle
    
    * op-program: Simplify required gas logic.
    Use FromHex instead of Hex2Bytes
    
    * op-program: Set beacon URL when capturing and verifying sepolia chain data
    
    * op-program: Actually use the beacon URL
    
    * op-program: Use default l1-rpckind if not set rather than overriding to debug_geth
    
    * Use freshly generated sepolia test data
    
    * Use sepolia compatibility data
    
    * Fix spelling
    
    * update PreimageOracle.sol snapshot
    
    ---------
    Co-authored-by: default avatarAdrian Sutton <adrian@oplabs.co>
    44201560
cache.go 3.21 KB
package l1

import (
	"encoding/binary"

	"github.com/hashicorp/golang-lru/v2/simplelru"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"

	"github.com/ethereum-optimism/optimism/op-service/eth"
)

// Cache size is quite high as retrieving data from the pre-image oracle can be quite expensive
const cacheSize = 2000

// CachingOracle is an implementation of Oracle that delegates to another implementation, adding caching of all results
type CachingOracle struct {
	oracle Oracle
	blocks *simplelru.LRU[common.Hash, eth.BlockInfo]
	txs    *simplelru.LRU[common.Hash, types.Transactions]
	rcpts  *simplelru.LRU[common.Hash, types.Receipts]
	blobs  *simplelru.LRU[common.Hash, *eth.Blob]
	pcmps  *simplelru.LRU[common.Hash, precompileResult]
}

type precompileResult struct {
	result []byte
	ok     bool
}

func NewCachingOracle(oracle Oracle) *CachingOracle {
	blockLRU, _ := simplelru.NewLRU[common.Hash, eth.BlockInfo](cacheSize, nil)
	txsLRU, _ := simplelru.NewLRU[common.Hash, types.Transactions](cacheSize, nil)
	rcptsLRU, _ := simplelru.NewLRU[common.Hash, types.Receipts](cacheSize, nil)
	blobsLRU, _ := simplelru.NewLRU[common.Hash, *eth.Blob](cacheSize, nil)
	pcmps, _ := simplelru.NewLRU[common.Hash, precompileResult](cacheSize, nil)
	return &CachingOracle{
		oracle: oracle,
		blocks: blockLRU,
		txs:    txsLRU,
		rcpts:  rcptsLRU,
		blobs:  blobsLRU,
		pcmps:  pcmps,
	}
}

func (o *CachingOracle) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo {
	block, ok := o.blocks.Get(blockHash)
	if ok {
		return block
	}
	block = o.oracle.HeaderByBlockHash(blockHash)
	o.blocks.Add(blockHash, block)
	return block
}

func (o *CachingOracle) TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions) {
	txs, ok := o.txs.Get(blockHash)
	if ok {
		return o.HeaderByBlockHash(blockHash), txs
	}
	block, txs := o.oracle.TransactionsByBlockHash(blockHash)
	o.blocks.Add(blockHash, block)
	o.txs.Add(blockHash, txs)
	return block, txs
}

func (o *CachingOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts) {
	rcpts, ok := o.rcpts.Get(blockHash)
	if ok {
		return o.HeaderByBlockHash(blockHash), rcpts
	}
	block, rcpts := o.oracle.ReceiptsByBlockHash(blockHash)
	o.blocks.Add(blockHash, block)
	o.rcpts.Add(blockHash, rcpts)
	return block, rcpts
}

func (o *CachingOracle) GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash) *eth.Blob {
	// Create a 32 byte hash key by hashing `blobHash.Hash ++ ref.Time ++ blobHash.Index`
	hashBuf := make([]byte, 48)
	copy(hashBuf[0:32], blobHash.Hash[:])
	binary.BigEndian.PutUint64(hashBuf[32:], ref.Time)
	binary.BigEndian.PutUint64(hashBuf[40:], blobHash.Index)
	cacheKey := crypto.Keccak256Hash(hashBuf)

	blob, ok := o.blobs.Get(cacheKey)
	if ok {
		return blob
	}
	blob = o.oracle.GetBlob(ref, blobHash)
	o.blobs.Add(cacheKey, blob)
	return blob
}

func (o *CachingOracle) Precompile(address common.Address, input []byte) ([]byte, bool) {
	cacheKey := crypto.Keccak256Hash(append(address.Bytes(), input...))
	if val, ok := o.pcmps.Get(cacheKey); ok {
		return val.result, val.ok
	}
	res, ok := o.oracle.Precompile(address, input)
	o.pcmps.Add(cacheKey, precompileResult{res, ok})
	return res, ok
}