Commit a13ff5f8 authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #5463 from ethereum-optimism/aj/block-info-rlp

Add a HeaderRLP method to BlockInfo
parents e15d068f 98dced7a
......@@ -137,7 +137,7 @@ func newMiniL2BlockWithNumberParent(numTx int, number *big.Int, parent common.Ha
Difficulty: common.Big0,
Number: big.NewInt(100),
}, nil, nil, nil, trie.NewStackTrie(nil))
l1InfoTx, err := derive.L1InfoDeposit(0, l1Block, eth.SystemConfig{}, false)
l1InfoTx, err := derive.L1InfoDeposit(0, eth.BlockToInfo(l1Block), eth.SystemConfig{}, false)
if err != nil {
panic(err)
}
......@@ -517,7 +517,7 @@ func TestChannelBuilder_OutputFramesMaxFrameIndex(t *testing.T) {
Difficulty: common.Big0,
Number: common.Big0,
}, nil, nil, nil, trie.NewStackTrie(nil))
l1InfoTx, _ := derive.L1InfoDeposit(0, lBlock, eth.SystemConfig{}, false)
l1InfoTx, _ := derive.L1InfoDeposit(0, eth.BlockToInfo(lBlock), eth.SystemConfig{}, false)
txs := []*types.Transaction{types.NewTx(l1InfoTx)}
a := types.NewBlock(&types.Header{
Number: big.NewInt(0),
......
......@@ -100,7 +100,7 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e
SystemConfig: rollupGenesis.SystemConfig,
L1ChainConfig: l1Genesis.Config,
L2ChainConfig: l2Genesis.Config,
L1Head: l1Block,
L1Head: eth.BlockToInfo(l1Block),
L2Head: genesisPayload,
}, nil
}
......
......@@ -5,10 +5,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
var _ BlockInfo = (&types.Block{})
type BlockInfo interface {
Hash() common.Hash
ParentHash() common.Hash
......@@ -21,6 +20,10 @@ type BlockInfo interface {
BaseFee() *big.Int
ReceiptHash() common.Hash
GasUsed() uint64
// HeaderRLP returns the RLP of the block header as per consensus rules
// Returns an error if the header RLP could not be written
HeaderRLP() ([]byte, error)
}
func InfoToL1BlockRef(info BlockInfo) L1BlockRef {
......@@ -44,6 +47,19 @@ func ToBlockID(b NumberAndHash) BlockID {
}
}
// blockInfo is a conversion type of types.Block turning it into a BlockInfo
type blockInfo struct{ *types.Block }
func (b blockInfo) HeaderRLP() ([]byte, error) {
return rlp.EncodeToBytes(b.Header())
}
func BlockToInfo(b *types.Block) BlockInfo {
return blockInfo{b}
}
var _ BlockInfo = (*blockInfo)(nil)
// headerBlockInfo is a conversion type of types.Header turning it into a
// BlockInfo.
type headerBlockInfo struct{ *types.Header }
......@@ -84,6 +100,10 @@ func (h headerBlockInfo) GasUsed() uint64 {
return h.Header.GasUsed
}
func (h headerBlockInfo) HeaderRLP() ([]byte, error) {
return rlp.EncodeToBytes(h.Header)
}
// HeaderBlockInfo returns h as a BlockInfo implementation.
func HeaderBlockInfo(h *types.Header) BlockInfo {
return headerBlockInfo{h}
......
......@@ -15,7 +15,7 @@ import (
func RandomL2Block(rng *rand.Rand, txCount int) (*types.Block, []*types.Receipt) {
l1Block := types.NewBlock(testutils.RandomHeader(rng),
nil, nil, nil, trie.NewStackTrie(nil))
l1InfoTx, err := derive.L1InfoDeposit(0, l1Block, eth.SystemConfig{}, testutils.RandomBool(rng))
l1InfoTx, err := derive.L1InfoDeposit(0, eth.BlockToInfo(l1Block), eth.SystemConfig{}, testutils.RandomBool(rng))
if err != nil {
panic("L1InfoDeposit: " + err.Error())
}
......
......@@ -219,7 +219,7 @@ func (n numberID) CheckID(id eth.BlockID) error {
return nil
}
func (s *EthClient) headerCall(ctx context.Context, method string, id rpcBlockID) (*HeaderInfo, error) {
func (s *EthClient) headerCall(ctx context.Context, method string, id rpcBlockID) (eth.BlockInfo, error) {
var header *rpcHeader
err := s.client.CallContext(ctx, &header, method, id.Arg(), false) // headers are just blocks without txs
if err != nil {
......@@ -239,7 +239,7 @@ func (s *EthClient) headerCall(ctx context.Context, method string, id rpcBlockID
return info, nil
}
func (s *EthClient) blockCall(ctx context.Context, method string, id rpcBlockID) (*HeaderInfo, types.Transactions, error) {
func (s *EthClient) blockCall(ctx context.Context, method string, id rpcBlockID) (eth.BlockInfo, types.Transactions, error) {
var block *rpcBlock
err := s.client.CallContext(ctx, &block, method, id.Arg(), true)
if err != nil {
......@@ -292,7 +292,7 @@ func (s *EthClient) ChainID(ctx context.Context) (*big.Int, error) {
func (s *EthClient) InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error) {
if header, ok := s.headersCache.Get(hash); ok {
return header.(*HeaderInfo), nil
return header.(eth.BlockInfo), nil
}
return s.headerCall(ctx, "eth_getBlockByHash", hashID(hash))
}
......@@ -310,7 +310,7 @@ func (s *EthClient) InfoByLabel(ctx context.Context, label eth.BlockLabel) (eth.
func (s *EthClient) InfoAndTxsByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, types.Transactions, error) {
if header, ok := s.headersCache.Get(hash); ok {
if txs, ok := s.transactionsCache.Get(hash); ok {
return header.(*HeaderInfo), txs.(types.Transactions), nil
return header.(eth.BlockInfo), txs.(types.Transactions), nil
}
}
return s.blockCall(ctx, "eth_getBlockByHash", hashID(hash))
......
......@@ -6,6 +6,7 @@ import (
"math/big"
"strings"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common"
......@@ -33,69 +34,57 @@ type CallContextFn func(ctx context.Context, result any, method string, args ...
//
// This way we minimize RPC calls, enable batching, and can choose to verify what the RPC gives us.
// HeaderInfo contains all the header-info required to implement the eth.BlockInfo interface,
// used in the rollup state-transition, with pre-computed block hash.
type HeaderInfo struct {
hash common.Hash
parentHash common.Hash
coinbase common.Address
root common.Hash
number uint64
time uint64
mixDigest common.Hash // a.k.a. the randomness field post-merge.
baseFee *big.Int
txHash common.Hash
receiptHash common.Hash
gasUsed uint64
// withdrawalsRoot was added in Shapella and is thus optional
withdrawalsRoot *common.Hash
// headerInfo is a conversion type of types.Header turning it into a
// BlockInfo, but using a cached hash value.
type headerInfo struct {
hash common.Hash
*types.Header
}
var _ eth.BlockInfo = (*HeaderInfo)(nil)
var _ eth.BlockInfo = (*headerInfo)(nil)
func (info *HeaderInfo) Hash() common.Hash {
return info.hash
func (h headerInfo) Hash() common.Hash {
return h.hash
}
func (info *HeaderInfo) ParentHash() common.Hash {
return info.parentHash
func (h headerInfo) ParentHash() common.Hash {
return h.Header.ParentHash
}
func (info *HeaderInfo) Coinbase() common.Address {
return info.coinbase
func (h headerInfo) Coinbase() common.Address {
return h.Header.Coinbase
}
func (info *HeaderInfo) Root() common.Hash {
return info.root
func (h headerInfo) Root() common.Hash {
return h.Header.Root
}
func (info *HeaderInfo) NumberU64() uint64 {
return info.number
func (h headerInfo) NumberU64() uint64 {
return h.Header.Number.Uint64()
}
func (info *HeaderInfo) Time() uint64 {
return info.time
func (h headerInfo) Time() uint64 {
return h.Header.Time
}
func (info *HeaderInfo) MixDigest() common.Hash {
return info.mixDigest
func (h headerInfo) MixDigest() common.Hash {
return h.Header.MixDigest
}
func (info *HeaderInfo) BaseFee() *big.Int {
return info.baseFee
func (h headerInfo) BaseFee() *big.Int {
return h.Header.BaseFee
}
func (info *HeaderInfo) ID() eth.BlockID {
return eth.BlockID{Hash: info.hash, Number: info.number}
func (h headerInfo) ReceiptHash() common.Hash {
return h.Header.ReceiptHash
}
func (info *HeaderInfo) ReceiptHash() common.Hash {
return info.receiptHash
func (h headerInfo) GasUsed() uint64 {
return h.Header.GasUsed
}
func (info *HeaderInfo) GasUsed() uint64 {
return info.gasUsed
func (h headerInfo) HeaderRLP() ([]byte, error) {
return rlp.EncodeToBytes(h.Header)
}
type rpcHeader struct {
......@@ -149,7 +138,12 @@ func (hdr *rpcHeader) checkPostMerge() error {
}
func (hdr *rpcHeader) computeBlockHash() common.Hash {
gethHeader := types.Header{
gethHeader := hdr.createGethHeader()
return gethHeader.Hash()
}
func (hdr *rpcHeader) createGethHeader() *types.Header {
return &types.Header{
ParentHash: hdr.ParentHash,
UncleHash: hdr.UncleHash,
Coinbase: hdr.Coinbase,
......@@ -168,10 +162,9 @@ func (hdr *rpcHeader) computeBlockHash() common.Hash {
BaseFee: (*big.Int)(hdr.BaseFee),
WithdrawalsHash: hdr.WithdrawalsRoot,
}
return gethHeader.Hash()
}
func (hdr *rpcHeader) Info(trustCache bool, mustBePostMerge bool) (*HeaderInfo, error) {
func (hdr *rpcHeader) Info(trustCache bool, mustBePostMerge bool) (eth.BlockInfo, error) {
if mustBePostMerge {
if err := hdr.checkPostMerge(); err != nil {
return nil, err
......@@ -182,22 +175,7 @@ func (hdr *rpcHeader) Info(trustCache bool, mustBePostMerge bool) (*HeaderInfo,
return nil, fmt.Errorf("failed to verify block hash: computed %s but RPC said %s", computed, hdr.Hash)
}
}
info := HeaderInfo{
hash: hdr.Hash,
parentHash: hdr.ParentHash,
coinbase: hdr.Coinbase,
root: hdr.Root,
number: uint64(hdr.Number),
time: uint64(hdr.Time),
mixDigest: hdr.MixDigest,
baseFee: (*big.Int)(hdr.BaseFee),
txHash: hdr.TxHash,
receiptHash: hdr.ReceiptHash,
gasUsed: uint64(hdr.GasUsed),
withdrawalsRoot: hdr.WithdrawalsRoot,
}
return &info, nil
return &headerInfo{hdr.Hash, hdr.createGethHeader()}, nil
}
type rpcBlock struct {
......@@ -215,7 +193,7 @@ func (block *rpcBlock) verify() error {
return nil
}
func (block *rpcBlock) Info(trustCache bool, mustBePostMerge bool) (*HeaderInfo, types.Transactions, error) {
func (block *rpcBlock) Info(trustCache bool, mustBePostMerge bool) (eth.BlockInfo, types.Transactions, error) {
if mustBePostMerge {
if err := block.checkPostMerge(); err != nil {
return nil, nil, err
......
package testutils
import (
"errors"
"math/big"
"math/rand"
......@@ -22,6 +23,7 @@ type MockBlockInfo struct {
InfoBaseFee *big.Int
InfoReceiptRoot common.Hash
InfoGasUsed uint64
InfoHeaderRLP []byte
}
func (l *MockBlockInfo) Hash() common.Hash {
......@@ -68,6 +70,13 @@ func (l *MockBlockInfo) ID() eth.BlockID {
return eth.BlockID{Hash: l.InfoHash, Number: l.InfoNum}
}
func (l *MockBlockInfo) HeaderRLP() ([]byte, error) {
if l.InfoHeaderRLP == nil {
return nil, errors.New("header rlp not available")
}
return l.InfoHeaderRLP, nil
}
func (l *MockBlockInfo) BlockRef() eth.L1BlockRef {
return eth.L1BlockRef{
Hash: l.InfoHash,
......
......@@ -4,6 +4,7 @@ import (
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/stretchr/testify/require"
)
......@@ -35,17 +36,17 @@ func TestCachingOracle_TransactionsByBlockHash(t *testing.T) {
block, _ := testutils.RandomBlock(rng, 3)
// Initial call retrieves from the stub
stub.blocks[block.Hash()] = block
stub.blocks[block.Hash()] = eth.BlockToInfo(block)
stub.txs[block.Hash()] = block.Transactions()
actualBlock, actualTxs := oracle.TransactionsByBlockHash(block.Hash())
require.Equal(t, block, actualBlock)
require.Equal(t, eth.BlockToInfo(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, eth.BlockToInfo(block), actualBlock)
require.Equal(t, block.Transactions(), actualTxs)
}
......@@ -56,16 +57,16 @@ func TestCachingOracle_ReceiptsByBlockHash(t *testing.T) {
block, rcpts := testutils.RandomBlock(rng, 3)
// Initial call retrieves from the stub
stub.blocks[block.Hash()] = block
stub.blocks[block.Hash()] = eth.BlockToInfo(block)
stub.rcpts[block.Hash()] = rcpts
actualBlock, actualRcpts := oracle.ReceiptsByBlockHash(block.Hash())
require.Equal(t, block, actualBlock)
require.Equal(t, eth.BlockToInfo(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.Equal(t, eth.BlockToInfo(block), actualBlock)
require.EqualValues(t, rcpts, actualRcpts)
}
......@@ -7,7 +7,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum/go-ethereum"
......@@ -24,7 +23,7 @@ var head = blockNum(1000)
func TestInfoByHash(t *testing.T) {
client, oracle := newClient(t)
hash := common.HexToHash("0xAABBCC")
expected := &sources.HeaderInfo{}
expected := &testutils.MockBlockInfo{}
oracle.blocks[hash] = expected
info, err := client.InfoByHash(context.Background(), hash)
......@@ -35,7 +34,7 @@ func TestInfoByHash(t *testing.T) {
func TestL1BlockRefByHash(t *testing.T) {
client, oracle := newClient(t)
hash := common.HexToHash("0xAABBCC")
header := &sources.HeaderInfo{}
header := &testutils.MockBlockInfo{}
oracle.blocks[hash] = header
expected := eth.InfoToL1BlockRef(header)
......@@ -47,7 +46,7 @@ func TestL1BlockRefByHash(t *testing.T) {
func TestFetchReceipts(t *testing.T) {
client, oracle := newClient(t)
hash := common.HexToHash("0xAABBCC")
expectedInfo := &sources.HeaderInfo{}
expectedInfo := &testutils.MockBlockInfo{}
expectedReceipts := types.Receipts{
&types.Receipt{},
}
......@@ -63,7 +62,7 @@ func TestFetchReceipts(t *testing.T) {
func TestInfoAndTxsByHash(t *testing.T) {
client, oracle := newClient(t)
hash := common.HexToHash("0xAABBCC")
expectedInfo := &sources.HeaderInfo{}
expectedInfo := &testutils.MockBlockInfo{}
expectedTxs := types.Transactions{
&types.Transaction{},
}
......
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
cll1 "github.com/ethereum-optimism/optimism/op-program/client/l1"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -24,7 +25,7 @@ var _ Source = (*sources.L1Client)(nil)
func TestHeaderByHash(t *testing.T) {
t.Run("Success", func(t *testing.T) {
expected := &sources.HeaderInfo{}
expected := &testutils.MockBlockInfo{}
source := &stubSource{nextInfo: expected}
oracle := newFetchingOracle(t, source)
......@@ -54,7 +55,7 @@ func TestHeaderByHash(t *testing.T) {
func TestTransactionsByHash(t *testing.T) {
t.Run("Success", func(t *testing.T) {
expectedInfo := &sources.HeaderInfo{}
expectedInfo := &testutils.MockBlockInfo{}
expectedTxs := types.Transactions{
&types.Transaction{},
}
......@@ -75,7 +76,7 @@ func TestTransactionsByHash(t *testing.T) {
})
t.Run("UnknownBlock_NoTxs", func(t *testing.T) {
oracle := newFetchingOracle(t, &stubSource{nextInfo: &sources.HeaderInfo{}})
oracle := newFetchingOracle(t, &stubSource{nextInfo: &testutils.MockBlockInfo{}})
hash := common.HexToHash("0x4455")
require.PanicsWithError(t, fmt.Errorf("unknown block: %s", hash).Error(), func() {
oracle.TransactionsByBlockHash(hash)
......@@ -96,7 +97,7 @@ func TestTransactionsByHash(t *testing.T) {
func TestReceiptsByHash(t *testing.T) {
t.Run("Success", func(t *testing.T) {
expectedInfo := &sources.HeaderInfo{}
expectedInfo := &testutils.MockBlockInfo{}
expectedRcpts := types.Receipts{
&types.Receipt{},
}
......@@ -117,7 +118,7 @@ func TestReceiptsByHash(t *testing.T) {
})
t.Run("UnknownBlock_NoTxs", func(t *testing.T) {
oracle := newFetchingOracle(t, &stubSource{nextInfo: &sources.HeaderInfo{}})
oracle := newFetchingOracle(t, &stubSource{nextInfo: &testutils.MockBlockInfo{}})
hash := common.HexToHash("0x4455")
require.PanicsWithError(t, fmt.Errorf("unknown block: %s", hash).Error(), func() {
oracle.ReceiptsByBlockHash(hash)
......
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