Commit cfc45742 authored by Adrian Sutton's avatar Adrian Sutton

op-program: Implement caching for the L1 oracle.

parent 6cebf39c
package l1
import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
// CachingOracle is an implementation of Oracle that delegates to another implementation, adding caching of all results
type CachingOracle struct {
oracle Oracle
blocks map[common.Hash]eth.BlockInfo
txs map[common.Hash]types.Transactions
rcpts map[common.Hash]types.Receipts
}
func NewCachingOracle(oracle Oracle) *CachingOracle {
return &CachingOracle{
oracle: oracle,
blocks: make(map[common.Hash]eth.BlockInfo),
txs: make(map[common.Hash]types.Transactions),
rcpts: make(map[common.Hash]types.Receipts),
}
}
func (o CachingOracle) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo {
block, ok := o.blocks[blockHash]
if ok {
return block
}
block = o.oracle.HeaderByBlockHash(blockHash)
o.blocks[blockHash] = block
return block
}
func (o CachingOracle) TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions) {
txs, ok := o.txs[blockHash]
if ok {
return o.HeaderByBlockHash(blockHash), txs
}
block, txs := o.oracle.TransactionsByBlockHash(blockHash)
o.blocks[blockHash] = block
o.txs[blockHash] = txs
return block, txs
}
func (o CachingOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts) {
rcpts, ok := o.rcpts[blockHash]
if ok {
return o.HeaderByBlockHash(blockHash), rcpts
}
block, rcpts := o.oracle.ReceiptsByBlockHash(blockHash)
o.blocks[blockHash] = block
o.rcpts[blockHash] = rcpts
return block, rcpts
}
package l1
import (
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/stretchr/testify/require"
)
// Should implement Oracle
var _ Oracle = (*CachingOracle)(nil)
func TestCachingOracle_HeaderByBlockHash(t *testing.T) {
rng := rand.New(rand.NewSource(1))
stub := newStubOracle(t)
oracle := NewCachingOracle(stub)
block := testutils.RandomBlockInfo(rng)
// Initial call retrieves from the stub
stub.blocks[block.Hash()] = block
result := oracle.HeaderByBlockHash(block.Hash())
require.Equal(t, block, result)
// Later calls should retrieve from cache
delete(stub.blocks, block.Hash())
result = oracle.HeaderByBlockHash(block.Hash())
require.Equal(t, block, result)
}
func TestCachingOracle_TransactionsByBlockHash(t *testing.T) {
rng := rand.New(rand.NewSource(1))
stub := newStubOracle(t)
oracle := NewCachingOracle(stub)
block, _ := testutils.RandomBlock(rng, 3)
// Initial call retrieves from the stub
stub.blocks[block.Hash()] = block
stub.txs[block.Hash()] = block.Transactions()
actualBlock, actualTxs := oracle.TransactionsByBlockHash(block.Hash())
require.Equal(t, block, actualBlock)
require.Equal(t, block.Transactions(), actualTxs)
// Later calls should retrieve from cache
delete(stub.blocks, block.Hash())
delete(stub.txs, block.Hash())
actualBlock, actualTxs = oracle.TransactionsByBlockHash(block.Hash())
require.Equal(t, block, actualBlock)
require.Equal(t, block.Transactions(), actualTxs)
}
func TestCachingOracle_ReceiptsByBlockHash(t *testing.T) {
rng := rand.New(rand.NewSource(1))
stub := newStubOracle(t)
oracle := NewCachingOracle(stub)
block, rcpts := testutils.RandomBlock(rng, 3)
// Initial call retrieves from the stub
stub.blocks[block.Hash()] = block
stub.rcpts[block.Hash()] = rcpts
actualBlock, actualRcpts := oracle.ReceiptsByBlockHash(block.Hash())
require.Equal(t, block, actualBlock)
require.EqualValues(t, rcpts, actualRcpts)
// Later calls should retrieve from cache
delete(stub.blocks, block.Hash())
delete(stub.rcpts, block.Hash())
actualBlock, actualRcpts = oracle.ReceiptsByBlockHash(block.Hash())
require.Equal(t, block, actualBlock)
require.EqualValues(t, rcpts, actualRcpts)
}
...@@ -145,54 +145,12 @@ func TestL1BlockRefByNumber(t *testing.T) { ...@@ -145,54 +145,12 @@ func TestL1BlockRefByNumber(t *testing.T) {
} }
func newClient(t *testing.T) (*OracleL1Client, *stubOracle) { func newClient(t *testing.T) (*OracleL1Client, *stubOracle) {
stub := &stubOracle{ stub := newStubOracle(t)
t: t,
blocks: make(map[common.Hash]eth.BlockInfo),
txs: make(map[common.Hash]types.Transactions),
rcpts: make(map[common.Hash]types.Receipts),
}
stub.blocks[head.Hash()] = head stub.blocks[head.Hash()] = head
client := NewOracleL1Client(testlog.Logger(t, log.LvlDebug), stub, head.Hash()) client := NewOracleL1Client(testlog.Logger(t, log.LvlDebug), stub, head.Hash())
return client, stub return client, stub
} }
type stubOracle struct {
t *testing.T
// blocks maps block hash to eth.BlockInfo
blocks map[common.Hash]eth.BlockInfo
// txs maps block hash to transactions
txs map[common.Hash]types.Transactions
// rcpts maps Block hash to receipts
rcpts map[common.Hash]types.Receipts
}
func (o stubOracle) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo {
info, ok := o.blocks[blockHash]
if !ok {
o.t.Fatalf("unknown block %s", blockHash)
}
return info
}
func (o stubOracle) TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions) {
txs, ok := o.txs[blockHash]
if !ok {
o.t.Fatalf("unknown txs %s", blockHash)
}
return o.HeaderByBlockHash(blockHash), txs
}
func (o stubOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts) {
rcpts, ok := o.rcpts[blockHash]
if !ok {
o.t.Fatalf("unknown rcpts %s", blockHash)
}
return o.HeaderByBlockHash(blockHash), rcpts
}
func blockNum(num uint64) eth.BlockInfo { func blockNum(num uint64) eth.BlockInfo {
parentNum := num - 1 parentNum := num - 1
return &testutils.MockBlockInfo{ return &testutils.MockBlockInfo{
......
package l1
import (
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type stubOracle struct {
t *testing.T
// blocks maps block hash to eth.BlockInfo
blocks map[common.Hash]eth.BlockInfo
// txs maps block hash to transactions
txs map[common.Hash]types.Transactions
// rcpts maps Block hash to receipts
rcpts map[common.Hash]types.Receipts
}
func newStubOracle(t *testing.T) *stubOracle {
return &stubOracle{
t: t,
blocks: make(map[common.Hash]eth.BlockInfo),
txs: make(map[common.Hash]types.Transactions),
rcpts: make(map[common.Hash]types.Receipts),
}
}
func (o stubOracle) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo {
info, ok := o.blocks[blockHash]
if !ok {
o.t.Fatalf("unknown block %s", blockHash)
}
return info
}
func (o stubOracle) TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions) {
txs, ok := o.txs[blockHash]
if !ok {
o.t.Fatalf("unknown txs %s", blockHash)
}
return o.HeaderByBlockHash(blockHash), txs
}
func (o stubOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts) {
rcpts, ok := o.rcpts[blockHash]
if !ok {
o.t.Fatalf("unknown rcpts %s", blockHash)
}
return o.HeaderByBlockHash(blockHash), rcpts
}
...@@ -21,6 +21,6 @@ func NewFetchingL1(ctx context.Context, logger log.Logger, cfg *config.Config) ( ...@@ -21,6 +21,6 @@ func NewFetchingL1(ctx context.Context, logger log.Logger, cfg *config.Config) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
oracle := NewFetchingL1Oracle(ctx, logger, source) oracle := cll1.NewCachingOracle(NewFetchingL1Oracle(ctx, logger, source))
return cll1.NewOracleL1Client(logger, oracle, cfg.L1Head), err return cll1.NewOracleL1Client(logger, oracle, cfg.L1Head), err
} }
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