Commit 007f8a0f authored by Joshua Gutow's avatar Joshua Gutow Committed by GitHub

op-node: Refactor types and L1/L2 Source (#2874)

* op-node: Add eth package changes

* op-node: Add testutils package

* op-node: Pull in L1/L2 source changes

* op-node: Make the refactor build

* Apply suggestions from code review

Use `NewPayloadErr` instead of `ForkchoiceUpdateErr` as pointed out by @protolambda
Co-authored-by: default avatarDiederik Loerakker <proto@protolambda.com>
Co-authored-by: default avatarDiederik Loerakker <proto@protolambda.com>
Co-authored-by: default avatarmergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
parent 2c32cde9
package eth
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
type L1Info interface {
Hash() common.Hash
ParentHash() common.Hash
Root() common.Hash // state-root
NumberU64() uint64
Time() uint64
// MixDigest field, reused for randomness after The Merge (Bellatrix hardfork)
MixDigest() common.Hash
BaseFee() *big.Int
ID() BlockID
BlockRef() L1BlockRef
ReceiptHash() common.Hash
}
package eth
import (
"fmt"
)
func ForkchoiceUpdateErr(payloadStatus PayloadStatusV1) error {
switch payloadStatus.Status {
case ExecutionSyncing:
return fmt.Errorf("updated forkchoice, but node is syncing")
case ExecutionAccepted, ExecutionInvalidTerminalBlock, ExecutionInvalidBlockHash:
// ACCEPTED, INVALID_TERMINAL_BLOCK, INVALID_BLOCK_HASH are only for execution
return fmt.Errorf("unexpected %s status, could not update forkchoice", payloadStatus.Status)
case ExecutionInvalid:
return fmt.Errorf("cannot update forkchoice, block is invalid")
case ExecutionValid:
return nil
default:
return fmt.Errorf("unknown forkchoice status: %q", string(payloadStatus.Status))
}
}
func NewPayloadErr(payload *ExecutionPayload, payloadStatus *PayloadStatusV1) error {
switch payloadStatus.Status {
case ExecutionValid:
return nil
case ExecutionSyncing:
return fmt.Errorf("failed to execute payload %s, node is syncing", payload.ID())
case ExecutionInvalid:
return fmt.Errorf("execution payload %s was INVALID! Latest valid hash is %s, ignoring bad block: %v", payload.ID(), payloadStatus.LatestValidHash, payloadStatus.ValidationError)
case ExecutionInvalidBlockHash:
return fmt.Errorf("execution payload %s has INVALID BLOCKHASH! %v", payload.BlockHash, payloadStatus.ValidationError)
case ExecutionInvalidTerminalBlock:
return fmt.Errorf("engine is misconfigured. Received invalid-terminal-block error while engine API should be active at genesis. err: %v", payloadStatus.ValidationError)
case ExecutionAccepted:
return fmt.Errorf("execution payload cannot be validated yet, latest valid hash is %s", payloadStatus.LatestValidHash)
default:
return fmt.Errorf("unknown execution status on %s: %q, ", payload.ID(), string(payloadStatus.Status))
}
}
...@@ -39,6 +39,12 @@ func (b Bytes32) String() string { ...@@ -39,6 +39,12 @@ func (b Bytes32) String() string {
return hexutil.Encode(b[:]) return hexutil.Encode(b[:])
} }
// TerminalString implements log.TerminalStringer, formatting a string for console
// output during logging.
func (b Bytes32) TerminalString() string {
return fmt.Sprintf("%x..%x", b[:3], b[29:])
}
type Bytes256 [256]byte type Bytes256 [256]byte
func (b *Bytes256) UnmarshalJSON(text []byte) error { func (b *Bytes256) UnmarshalJSON(text []byte) error {
...@@ -57,6 +63,12 @@ func (b Bytes256) String() string { ...@@ -57,6 +63,12 @@ func (b Bytes256) String() string {
return hexutil.Encode(b[:]) return hexutil.Encode(b[:])
} }
// TerminalString implements log.TerminalStringer, formatting a string for console
// output during logging.
func (b Bytes256) TerminalString() string {
return fmt.Sprintf("%x..%x", b[:3], b[253:])
}
type Uint64Quantity = hexutil.Uint64 type Uint64Quantity = hexutil.Uint64
type BytesMax32 []byte type BytesMax32 []byte
...@@ -219,10 +231,10 @@ const ( ...@@ -219,10 +231,10 @@ const (
type PayloadStatusV1 struct { type PayloadStatusV1 struct {
// the result of the payload execution // the result of the payload execution
Status ExecutePayloadStatus `json:"status"` Status ExecutePayloadStatus `json:"status"`
// the hash of the most recent valid block in the branch defined by payload and its ancestors // the hash of the most recent valid block in the branch defined by payload and its ancestors (optional field)
LatestValidHash common.Hash `json:"latestValidHash"` LatestValidHash *common.Hash `json:"latestValidHash,omitempty"`
// additional details on the result // additional details on the result (optional field)
ValidationError string `json:"validationError"` ValidationError *string `json:"validationError,omitempty"`
} }
type ForkchoiceState struct { type ForkchoiceState struct {
......
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -184,24 +183,24 @@ func (s *Source) blockCall(ctx context.Context, method string, id interface{}) ( ...@@ -184,24 +183,24 @@ func (s *Source) blockCall(ctx context.Context, method string, id interface{}) (
return info, txs, nil return info, txs, nil
} }
func (s *Source) InfoByHash(ctx context.Context, hash common.Hash) (derive.L1Info, error) { func (s *Source) InfoByHash(ctx context.Context, hash common.Hash) (eth.L1Info, error) {
if header, ok := s.headersCache.Get(hash); ok { if header, ok := s.headersCache.Get(hash); ok {
return header.(*HeaderInfo), nil return header.(*HeaderInfo), nil
} }
return s.headerCall(ctx, "eth_getBlockByHash", hash) return s.headerCall(ctx, "eth_getBlockByHash", hash)
} }
func (s *Source) InfoByNumber(ctx context.Context, number uint64) (derive.L1Info, error) { func (s *Source) InfoByNumber(ctx context.Context, number uint64) (eth.L1Info, error) {
// can't hit the cache when querying by number due to reorgs. // can't hit the cache when querying by number due to reorgs.
return s.headerCall(ctx, "eth_getBlockByNumber", hexutil.EncodeUint64(number)) return s.headerCall(ctx, "eth_getBlockByNumber", hexutil.EncodeUint64(number))
} }
func (s *Source) InfoHead(ctx context.Context) (derive.L1Info, error) { func (s *Source) InfoHead(ctx context.Context) (eth.L1Info, error) {
// can't hit the cache when querying the head due to reorgs / changes. // can't hit the cache when querying the head due to reorgs / changes.
return s.headerCall(ctx, "eth_getBlockByNumber", "latest") return s.headerCall(ctx, "eth_getBlockByNumber", "latest")
} }
func (s *Source) InfoAndTxsByHash(ctx context.Context, hash common.Hash) (derive.L1Info, types.Transactions, error) { func (s *Source) InfoAndTxsByHash(ctx context.Context, hash common.Hash) (eth.L1Info, types.Transactions, error) {
if header, ok := s.headersCache.Get(hash); ok { if header, ok := s.headersCache.Get(hash); ok {
if txs, ok := s.transactionsCache.Get(hash); ok { if txs, ok := s.transactionsCache.Get(hash); ok {
return header.(*HeaderInfo), txs.(types.Transactions), nil return header.(*HeaderInfo), txs.(types.Transactions), nil
...@@ -210,17 +209,17 @@ func (s *Source) InfoAndTxsByHash(ctx context.Context, hash common.Hash) (derive ...@@ -210,17 +209,17 @@ func (s *Source) InfoAndTxsByHash(ctx context.Context, hash common.Hash) (derive
return s.blockCall(ctx, "eth_getBlockByHash", hash) return s.blockCall(ctx, "eth_getBlockByHash", hash)
} }
func (s *Source) InfoAndTxsByNumber(ctx context.Context, number uint64) (derive.L1Info, types.Transactions, error) { func (s *Source) InfoAndTxsByNumber(ctx context.Context, number uint64) (eth.L1Info, types.Transactions, error) {
// can't hit the cache when querying by number due to reorgs. // can't hit the cache when querying by number due to reorgs.
return s.blockCall(ctx, "eth_getBlockByNumber", hexutil.EncodeUint64(number)) return s.blockCall(ctx, "eth_getBlockByNumber", hexutil.EncodeUint64(number))
} }
func (s *Source) InfoAndTxsHead(ctx context.Context) (derive.L1Info, types.Transactions, error) { func (s *Source) InfoAndTxsHead(ctx context.Context) (eth.L1Info, types.Transactions, error) {
// can't hit the cache when querying the head due to reorgs / changes. // can't hit the cache when querying the head due to reorgs / changes.
return s.blockCall(ctx, "eth_getBlockByNumber", "latest") return s.blockCall(ctx, "eth_getBlockByNumber", "latest")
} }
func (s *Source) Fetch(ctx context.Context, blockHash common.Hash) (derive.L1Info, types.Transactions, types.Receipts, error) { func (s *Source) Fetch(ctx context.Context, blockHash common.Hash) (eth.L1Info, types.Transactions, types.Receipts, error) {
if blockHash == (common.Hash{}) { if blockHash == (common.Hash{}) {
return nil, nil, nil, ethereum.NotFound return nil, nil, nil, ethereum.NotFound
} }
......
...@@ -6,7 +6,6 @@ import ( ...@@ -6,7 +6,6 @@ import (
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
...@@ -35,7 +34,7 @@ type HeaderInfo struct { ...@@ -35,7 +34,7 @@ type HeaderInfo struct {
receiptHash common.Hash receiptHash common.Hash
} }
var _ derive.L1Info = (*HeaderInfo)(nil) var _ eth.L1Info = (*HeaderInfo)(nil)
func (info *HeaderInfo) Hash() common.Hash { func (info *HeaderInfo) Hash() common.Hash {
return info.hash return info.hash
......
...@@ -52,9 +52,9 @@ func (s *Source) PayloadByHash(ctx context.Context, hash common.Hash) (*eth.Exec ...@@ -52,9 +52,9 @@ func (s *Source) PayloadByHash(ctx context.Context, hash common.Hash) (*eth.Exec
return payload, nil return payload, nil
} }
func (s *Source) PayloadByNumber(ctx context.Context, number *big.Int) (*eth.ExecutionPayload, error) { func (s *Source) PayloadByNumber(ctx context.Context, number uint64) (*eth.ExecutionPayload, error) {
// TODO: we really do not need to parse every single tx and block detail, keeping transactions encoded is faster. // TODO: we really do not need to parse every single tx and block detail, keeping transactions encoded is faster.
block, err := s.client.BlockByNumber(ctx, number) block, err := s.client.BlockByNumber(ctx, big.NewInt(int64(number)))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to retrieve L2 block by number: %v", err) return nil, fmt.Errorf("failed to retrieve L2 block by number: %v", err)
} }
...@@ -80,6 +80,7 @@ func (s *Source) ForkchoiceUpdate(ctx context.Context, fc *eth.ForkchoiceState, ...@@ -80,6 +80,7 @@ func (s *Source) ForkchoiceUpdate(ctx context.Context, fc *eth.ForkchoiceState,
if attributes != nil { if attributes != nil {
e.Debug("Received payload id", "payloadId", result.PayloadID) e.Debug("Received payload id", "payloadId", result.PayloadID)
} }
return &result, nil
} else { } else {
e = e.New("err", err) e = e.New("err", err)
if rpcErr, ok := err.(rpc.Error); ok { if rpcErr, ok := err.(rpc.Error); ok {
...@@ -88,24 +89,12 @@ func (s *Source) ForkchoiceUpdate(ctx context.Context, fc *eth.ForkchoiceState, ...@@ -88,24 +89,12 @@ func (s *Source) ForkchoiceUpdate(ctx context.Context, fc *eth.ForkchoiceState,
} else { } else {
e.Error("Failed to share forkchoice-updated signal") e.Error("Failed to share forkchoice-updated signal")
} }
} return nil, err
switch result.PayloadStatus.Status {
case eth.ExecutionSyncing:
return nil, fmt.Errorf("updated forkchoice, but node is syncing: %v", err)
case eth.ExecutionAccepted, eth.ExecutionInvalidTerminalBlock, eth.ExecutionInvalidBlockHash:
// ACCEPTED, INVALID_TERMINAL_BLOCK, INVALID_BLOCK_HASH are only for execution
return nil, fmt.Errorf("unexpected %s status, could not update forkchoice: %v", result.PayloadStatus.Status, err)
case eth.ExecutionInvalid:
return nil, fmt.Errorf("cannot update forkchoice, block is invalid: %v", err)
case eth.ExecutionValid:
return &result, nil
default:
return nil, fmt.Errorf("unknown forkchoice status on %s: %q, ", fc.SafeBlockHash, string(result.PayloadStatus.Status))
} }
} }
// ExecutePayload executes a built block on the execution engine and returns an error if it was not successful. // ExecutePayload executes a built block on the execution engine and returns an error if it was not successful.
func (s *Source) NewPayload(ctx context.Context, payload *eth.ExecutionPayload) error { func (s *Source) NewPayload(ctx context.Context, payload *eth.ExecutionPayload) (*eth.PayloadStatusV1, error) {
e := s.log.New("block_hash", payload.BlockHash) e := s.log.New("block_hash", payload.BlockHash)
e.Debug("sending payload for execution") e.Debug("sending payload for execution")
...@@ -116,25 +105,9 @@ func (s *Source) NewPayload(ctx context.Context, payload *eth.ExecutionPayload) ...@@ -116,25 +105,9 @@ func (s *Source) NewPayload(ctx context.Context, payload *eth.ExecutionPayload)
e.Debug("Received payload execution result", "status", result.Status, "latestValidHash", result.LatestValidHash, "message", result.ValidationError) e.Debug("Received payload execution result", "status", result.Status, "latestValidHash", result.LatestValidHash, "message", result.ValidationError)
if err != nil { if err != nil {
e.Error("Payload execution failed", "err", err) e.Error("Payload execution failed", "err", err)
return fmt.Errorf("failed to execute payload: %v", err) return nil, fmt.Errorf("failed to execute payload: %v", err)
}
switch result.Status {
case eth.ExecutionValid:
return nil
case eth.ExecutionSyncing:
return fmt.Errorf("failed to execute payload %s, node is syncing", payload.ID())
case eth.ExecutionInvalid:
return fmt.Errorf("execution payload %s was INVALID! Latest valid hash is %s, ignoring bad block: %q", payload.ID(), result.LatestValidHash, result.ValidationError)
case eth.ExecutionInvalidBlockHash:
return fmt.Errorf("execution payload %s has INVALID BLOCKHASH! %v", payload.BlockHash, result.ValidationError)
case eth.ExecutionInvalidTerminalBlock:
return fmt.Errorf("engine is misconfigured. Received invalid-terminal-block error while engine API should be active at genesis. err: %v", result.ValidationError)
case eth.ExecutionAccepted:
return fmt.Errorf("execution payload cannot be validated yet, latest valid hash is %s", result.LatestValidHash)
default:
return fmt.Errorf("unknown execution status on %s: %q, ", payload.ID(), string(result.Status))
} }
return &result, nil
} }
// GetPayload gets the execution payload associated with the PayloadId // GetPayload gets the execution payload associated with the PayloadId
...@@ -161,6 +134,16 @@ func (s *Source) GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth. ...@@ -161,6 +134,16 @@ func (s *Source) GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.
return &result, nil return &result, nil
} }
// L2BlockRefHead returns the canonical block and parent ids.
func (s *Source) L2BlockRefHead(ctx context.Context) (eth.L2BlockRef, error) {
block, err := s.client.BlockByNumber(ctx, nil)
if err != nil {
// w%: wrap the error, we still need to detect if a canonical block is not found, a.k.a. end of chain.
return eth.L2BlockRef{}, fmt.Errorf("failed to determine block-hash of head, could not get header: %w", err)
}
return blockToBlockRef(block, s.genesis)
}
// L2BlockRefByNumber returns the canonical block and parent ids. // L2BlockRefByNumber returns the canonical block and parent ids.
func (s *Source) L2BlockRefByNumber(ctx context.Context, l2Num *big.Int) (eth.L2BlockRef, error) { func (s *Source) L2BlockRefByNumber(ctx context.Context, l2Num *big.Int) (eth.L2BlockRef, error) {
block, err := s.client.BlockByNumber(ctx, l2Num) block, err := s.client.BlockByNumber(ctx, l2Num)
......
...@@ -23,17 +23,17 @@ type BatchSubmitter interface { ...@@ -23,17 +23,17 @@ type BatchSubmitter interface {
} }
type Downloader interface { type Downloader interface {
InfoByHash(ctx context.Context, hash common.Hash) (derive.L1Info, error) InfoByHash(ctx context.Context, hash common.Hash) (eth.L1Info, error)
Fetch(ctx context.Context, blockHash common.Hash) (derive.L1Info, types.Transactions, types.Receipts, error) Fetch(ctx context.Context, blockHash common.Hash) (eth.L1Info, types.Transactions, types.Receipts, error)
FetchAllTransactions(ctx context.Context, window []eth.BlockID) ([]types.Transactions, error) FetchAllTransactions(ctx context.Context, window []eth.BlockID) ([]types.Transactions, error)
} }
type Engine interface { type Engine interface {
GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.ExecutionPayload, error) GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.ExecutionPayload, error)
ForkchoiceUpdate(ctx context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) ForkchoiceUpdate(ctx context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error)
NewPayload(ctx context.Context, payload *eth.ExecutionPayload) error NewPayload(ctx context.Context, payload *eth.ExecutionPayload) (*eth.PayloadStatusV1, error)
PayloadByHash(context.Context, common.Hash) (*eth.ExecutionPayload, error) PayloadByHash(context.Context, common.Hash) (*eth.ExecutionPayload, error)
PayloadByNumber(context.Context, *big.Int) (*eth.ExecutionPayload, error) PayloadByNumber(context.Context, uint64) (*eth.ExecutionPayload, error)
} }
type L1Chain interface { type L1Chain interface {
......
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"math/big"
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
...@@ -55,8 +54,10 @@ func lastDeposit(txns []eth.Data) (int, error) { ...@@ -55,8 +54,10 @@ func lastDeposit(txns []eth.Data) (int, error) {
func (d *outputImpl) processBlock(ctx context.Context, l2Head eth.L2BlockRef, l2SafeHead eth.BlockID, l2Finalized eth.BlockID, payload *eth.ExecutionPayload) error { func (d *outputImpl) processBlock(ctx context.Context, l2Head eth.L2BlockRef, l2SafeHead eth.BlockID, l2Finalized eth.BlockID, payload *eth.ExecutionPayload) error {
d.log.Info("processing new block", "parent", payload.ParentID(), "l2Head", l2Head, "id", payload.ID()) d.log.Info("processing new block", "parent", payload.ParentID(), "l2Head", l2Head, "id", payload.ID())
if err := d.l2.NewPayload(ctx, payload); err != nil { if status, err := d.l2.NewPayload(ctx, payload); err != nil {
return fmt.Errorf("failed to insert new payload: %v", err) return fmt.Errorf("failed to insert new payload: %w", err)
} else if err := eth.NewPayloadErr(payload, status); err != nil {
return fmt.Errorf("failed to insert new payload: %w", err)
} }
// now try to persist a reorg to the new payload // now try to persist a reorg to the new payload
fc := eth.ForkchoiceState{ fc := eth.ForkchoiceState{
...@@ -80,7 +81,7 @@ func (d *outputImpl) createNewBlock(ctx context.Context, l2Head eth.L2BlockRef, ...@@ -80,7 +81,7 @@ func (d *outputImpl) createNewBlock(ctx context.Context, l2Head eth.L2BlockRef,
fetchCtx, cancel := context.WithTimeout(ctx, time.Second*20) fetchCtx, cancel := context.WithTimeout(ctx, time.Second*20)
defer cancel() defer cancel()
var l1Info derive.L1Info var l1Info eth.L1Info
var receipts types.Receipts var receipts types.Receipts
var err error var err error
...@@ -309,7 +310,7 @@ func attributesMatchBlock(attrs *eth.PayloadAttributes, parentHash common.Hash, ...@@ -309,7 +310,7 @@ func attributesMatchBlock(attrs *eth.PayloadAttributes, parentHash common.Hash,
// verifySafeBlock reconciles the supplied payload attributes against the actual L2 block. // verifySafeBlock reconciles the supplied payload attributes against the actual L2 block.
// If they do not match, it inserts the new block and sets the head and safe head to the new block in the FC. // If they do not match, it inserts the new block and sets the head and safe head to the new block in the FC.
func (d *outputImpl) verifySafeBlock(ctx context.Context, fc eth.ForkchoiceState, attrs *eth.PayloadAttributes, parent eth.BlockID) (*eth.ExecutionPayload, bool, error) { func (d *outputImpl) verifySafeBlock(ctx context.Context, fc eth.ForkchoiceState, attrs *eth.PayloadAttributes, parent eth.BlockID) (*eth.ExecutionPayload, bool, error) {
payload, err := d.l2.PayloadByNumber(ctx, new(big.Int).SetUint64(parent.Number+1)) payload, err := d.l2.PayloadByNumber(ctx, parent.Number+1)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("failed to get L2 block: %w", err) return nil, false, fmt.Errorf("failed to get L2 block: %w", err)
} }
...@@ -389,10 +390,12 @@ func (d *outputImpl) insertHeadBlock(ctx context.Context, fc eth.ForkchoiceState ...@@ -389,10 +390,12 @@ func (d *outputImpl) insertHeadBlock(ctx context.Context, fc eth.ForkchoiceState
d.log.Error("Dropped deposits when executing L2 block") d.log.Error("Dropped deposits when executing L2 block")
} }
err = d.l2.NewPayload(ctx, payload) if status, err := d.l2.NewPayload(ctx, payload); err != nil {
if err != nil { return nil, fmt.Errorf("failed to insert execution payload: %w", err)
} else if err := eth.NewPayloadErr(payload, status); err != nil {
return nil, fmt.Errorf("failed to insert execution payload: %w", err) return nil, fmt.Errorf("failed to insert execution payload: %w", err)
} }
fc.HeadBlockHash = payload.BlockHash fc.HeadBlockHash = payload.BlockHash
if updateSafe { if updateSafe {
fc.SafeBlockHash = payload.BlockHash fc.SafeBlockHash = payload.BlockHash
......
...@@ -141,6 +141,14 @@ func (m *FakeChainSource) L1HeadBlockRef(ctx context.Context) (eth.L1BlockRef, e ...@@ -141,6 +141,14 @@ func (m *FakeChainSource) L1HeadBlockRef(ctx context.Context) (eth.L1BlockRef, e
return m.l1s[m.l1reorg][m.l1head], nil return m.l1s[m.l1reorg][m.l1head], nil
} }
func (m *FakeChainSource) L2BlockRefHead(ctx context.Context) (eth.L2BlockRef, error) {
m.log.Trace("L2BlockRefHead", "l2Head", m.l2head, "reorg", m.l2reorg)
if len(m.l2s[m.l2reorg]) == 0 {
panic("bad test, no l2 chain")
}
return m.l2s[m.l2reorg][m.l2head], nil
}
func (m *FakeChainSource) L2BlockRefByNumber(ctx context.Context, l2Num *big.Int) (eth.L2BlockRef, error) { func (m *FakeChainSource) L2BlockRefByNumber(ctx context.Context, l2Num *big.Int) (eth.L2BlockRef, error) {
m.log.Trace("L2BlockRefByNumber", "l2Num", l2Num, "l2Head", m.l2head, "reorg", m.l2reorg) m.log.Trace("L2BlockRefByNumber", "l2Num", l2Num, "l2Head", m.l2head, "reorg", m.l2reorg)
if len(m.l2s[m.l2reorg]) == 0 { if len(m.l2s[m.l2reorg]) == 0 {
......
package testutils
import (
"context"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/mock"
)
type MockEngine struct {
mock.Mock
}
func (m *MockEngine) L2BlockRefHead(ctx context.Context) (eth.L2BlockRef, error) {
out := m.Mock.MethodCalled("L2BlockRefHead")
return out[0].(eth.L2BlockRef), *out[1].(*error)
}
func (m *MockEngine) ExpectL2BlockRefHead(ref eth.L1BlockRef, err error) {
m.Mock.On("L2BlockRefHead").Once().Return(ref, &err)
}
func (m *MockEngine) L2BlockRefByHash(ctx context.Context, l2Hash common.Hash) (eth.L2BlockRef, error) {
out := m.Mock.MethodCalled("L2BlockRefByHash", l2Hash)
return out[0].(eth.L2BlockRef), *out[1].(*error)
}
func (m *MockEngine) ExpectL2BlockRefByHash(l2Hash common.Hash, ref eth.L1BlockRef, err error) {
m.Mock.On("L2BlockRefByHash", l2Hash).Once().Return(ref, &err)
}
func (m *MockEngine) GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.ExecutionPayload, error) {
out := m.Mock.MethodCalled("GetPayload", payloadId)
return out[0].(*eth.ExecutionPayload), *out[1].(*error)
}
func (m *MockEngine) ExpectGetPayload(payloadId eth.PayloadID, payload *eth.ExecutionPayload, err error) {
m.Mock.On("GetPayload", payloadId).Once().Return(payload, &err)
}
func (m *MockEngine) ForkchoiceUpdate(ctx context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) {
out := m.Mock.MethodCalled("ForkchoiceUpdate", state, attr)
return out[0].(*eth.ForkchoiceUpdatedResult), *out[1].(*error)
}
func (m *MockEngine) ExpectForkchoiceUpdate(state *eth.ForkchoiceState, attr *eth.PayloadAttributes, result *eth.ForkchoiceUpdatedResult, err error) {
m.Mock.On("ForkchoiceUpdate", state, attr).Once().Return(result, &err)
}
func (m *MockEngine) NewPayload(ctx context.Context, payload *eth.ExecutionPayload) (*eth.PayloadStatusV1, error) {
out := m.Mock.MethodCalled("NewPayload", payload)
return out[0].(*eth.PayloadStatusV1), *out[1].(*error)
}
func (m *MockEngine) ExpectNewPayload(payload *eth.ExecutionPayload, result *eth.PayloadStatusV1, err error) {
m.Mock.On("NewPayload", payload).Once().Return(result, &err)
}
func (m *MockEngine) PayloadByHash(ctx context.Context, hash common.Hash) (*eth.ExecutionPayload, error) {
out := m.Mock.MethodCalled("PayloadByHash", hash)
return out[0].(*eth.ExecutionPayload), *out[1].(*error)
}
func (m *MockEngine) ExpectPayloadByHash(hash common.Hash, payload *eth.ExecutionPayload, err error) {
m.Mock.On("PayloadByHash", hash).Once().Return(payload, &err)
}
func (m *MockEngine) PayloadByNumber(ctx context.Context, n uint64) (*eth.ExecutionPayload, error) {
out := m.Mock.MethodCalled("PayloadByNumber", n)
return out[0].(*eth.ExecutionPayload), *out[1].(*error)
}
func (m *MockEngine) ExpectPayloadByNumber(hash common.Hash, payload *eth.ExecutionPayload, err error) {
m.Mock.On("PayloadByNumber", hash).Once().Return(payload, &err)
}
package testutils
import (
"context"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/mock"
)
type MockL1Source struct {
mock.Mock
}
func (m *MockL1Source) L1BlockRefByNumber(ctx context.Context, u uint64) (eth.L1BlockRef, error) {
out := m.Mock.MethodCalled("L1BlockRefByNumber", u)
return out[0].(eth.L1BlockRef), *out[1].(*error)
}
func (m *MockL1Source) ExpectL1BlockRefByNumber(u uint64, ref eth.L1BlockRef, err error) {
m.Mock.On("L1BlockRefByNumber", u).Once().Return(ref, &err)
}
func (m *MockL1Source) L1BlockRefByHash(ctx context.Context, hash common.Hash) (eth.L1BlockRef, error) {
out := m.Mock.MethodCalled("L1BlockRefByHash", hash)
return out[0].(eth.L1BlockRef), *out[1].(*error)
}
func (m *MockL1Source) ExpectL1BlockRefByHash(hash common.Hash, ref eth.L1BlockRef, err error) {
m.Mock.On("L1BlockRefByHash", hash).Once().Return(ref, &err)
}
func (m *MockL1Source) Fetch(ctx context.Context, blockHash common.Hash) (eth.L1Info, types.Transactions, types.Receipts, error) {
out := m.Mock.MethodCalled("Fetch", blockHash)
return out[0].(eth.L1Info), out[1].(types.Transactions), out[2].(types.Receipts), *out[3].(*error)
}
func (m *MockL1Source) ExpectFetch(hash common.Hash, info eth.L1Info, transactions types.Transactions, receipts types.Receipts, err error) {
m.Mock.On("Fetch", hash).Once().Return(info, transactions, receipts, &err)
}
func (m *MockL1Source) InfoAndTxsByHash(ctx context.Context, hash common.Hash) (eth.L1Info, types.Transactions, error) {
out := m.Mock.MethodCalled("InfoAndTxsByHash", hash)
return out[0].(eth.L1Info), out[1].(types.Transactions), *out[2].(*error)
}
func (m *MockL1Source) ExpectInfoAndTxsByHash(hash common.Hash, info eth.L1Info, transactions types.Transactions, err error) {
m.Mock.On("InfoAndTxsByHash", hash).Once().Return(info, transactions, &err)
}
package testutils package testutils
import ( import (
"crypto/ecdsa"
"math/big" "math/big"
"math/rand" "math/rand"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
...@@ -22,3 +26,57 @@ func RandomETH(rng *rand.Rand, max int64) *big.Int { ...@@ -22,3 +26,57 @@ func RandomETH(rng *rand.Rand, max int64) *big.Int {
x = new(big.Int).Mul(x, big.NewInt(1e18)) x = new(big.Int).Mul(x, big.NewInt(1e18))
return x return x
} }
func RandomKey() *ecdsa.PrivateKey {
key, err := crypto.GenerateKey()
if err != nil {
panic("couldn't generate key: " + err.Error())
}
return key
}
func RandomData(rng *rand.Rand, size int) []byte {
out := make([]byte, size)
rng.Read(out)
return out
}
func RandomBlockID(rng *rand.Rand) eth.BlockID {
return eth.BlockID{
Hash: RandomHash(rng),
Number: rng.Uint64() & ((1 << 50) - 1), // be json friendly
}
}
func RandomBlockRef(rng *rand.Rand) eth.L1BlockRef {
return eth.L1BlockRef{
Hash: RandomHash(rng),
Number: rng.Uint64(),
ParentHash: RandomHash(rng),
Time: rng.Uint64(),
}
}
func NextRandomRef(rng *rand.Rand, parent eth.L1BlockRef) eth.L1BlockRef {
return eth.L1BlockRef{
Hash: RandomHash(rng),
Number: parent.Number + 1,
ParentHash: parent.Hash,
Time: parent.Time + uint64(rng.Intn(100)),
}
}
func NextRandomL2Ref(rng *rand.Rand, l2BlockTime uint64, parent eth.L2BlockRef, origin eth.BlockID) eth.L2BlockRef {
seq := parent.SequenceNumber + 1
if parent.L1Origin != origin {
seq = 0
}
return eth.L2BlockRef{
Hash: RandomHash(rng),
Number: parent.Number + 1,
ParentHash: parent.Hash,
Time: parent.Time + l2BlockTime,
L1Origin: eth.BlockID{},
SequenceNumber: seq,
}
}
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