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 {
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
func (b *Bytes256) UnmarshalJSON(text []byte) error {
......@@ -57,6 +63,12 @@ func (b Bytes256) String() string {
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 BytesMax32 []byte
......@@ -219,10 +231,10 @@ const (
type PayloadStatusV1 struct {
// the result of the payload execution
Status ExecutePayloadStatus `json:"status"`
// the hash of the most recent valid block in the branch defined by payload and its ancestors
LatestValidHash common.Hash `json:"latestValidHash"`
// additional details on the result
ValidationError string `json:"validationError"`
// the hash of the most recent valid block in the branch defined by payload and its ancestors (optional field)
LatestValidHash *common.Hash `json:"latestValidHash,omitempty"`
// additional details on the result (optional field)
ValidationError *string `json:"validationError,omitempty"`
}
type ForkchoiceState struct {
......
......@@ -11,7 +11,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup"
"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/hexutil"
"github.com/ethereum/go-ethereum/core/types"
......@@ -184,24 +183,24 @@ func (s *Source) blockCall(ctx context.Context, method string, id interface{}) (
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 {
return header.(*HeaderInfo), nil
}
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.
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.
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 txs, ok := s.transactionsCache.Get(hash); ok {
return header.(*HeaderInfo), txs.(types.Transactions), nil
......@@ -210,17 +209,17 @@ func (s *Source) InfoAndTxsByHash(ctx context.Context, hash common.Hash) (derive
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.
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.
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{}) {
return nil, nil, nil, ethereum.NotFound
}
......
......@@ -6,7 +6,6 @@ import (
"math/big"
"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/core/types"
"github.com/ethereum/go-ethereum/trie"
......@@ -35,7 +34,7 @@ type HeaderInfo struct {
receiptHash common.Hash
}
var _ derive.L1Info = (*HeaderInfo)(nil)
var _ eth.L1Info = (*HeaderInfo)(nil)
func (info *HeaderInfo) Hash() common.Hash {
return info.hash
......
......@@ -52,9 +52,9 @@ func (s *Source) PayloadByHash(ctx context.Context, hash common.Hash) (*eth.Exec
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.
block, err := s.client.BlockByNumber(ctx, number)
block, err := s.client.BlockByNumber(ctx, big.NewInt(int64(number)))
if err != nil {
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,
if attributes != nil {
e.Debug("Received payload id", "payloadId", result.PayloadID)
}
return &result, nil
} else {
e = e.New("err", err)
if rpcErr, ok := err.(rpc.Error); ok {
......@@ -88,24 +89,12 @@ func (s *Source) ForkchoiceUpdate(ctx context.Context, fc *eth.ForkchoiceState,
} else {
e.Error("Failed to share forkchoice-updated signal")
}
}
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))
return nil, err
}
}
// 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.Debug("sending payload for execution")
......@@ -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)
if err != nil {
e.Error("Payload execution failed", "err", err)
return 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 nil, fmt.Errorf("failed to execute payload: %v", err)
}
return &result, nil
}
// GetPayload gets the execution payload associated with the PayloadId
......@@ -161,6 +134,16 @@ func (s *Source) GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.
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.
func (s *Source) L2BlockRefByNumber(ctx context.Context, l2Num *big.Int) (eth.L2BlockRef, error) {
block, err := s.client.BlockByNumber(ctx, l2Num)
......
......@@ -23,17 +23,17 @@ type BatchSubmitter interface {
}
type Downloader interface {
InfoByHash(ctx context.Context, hash common.Hash) (derive.L1Info, error)
Fetch(ctx context.Context, blockHash common.Hash) (derive.L1Info, types.Transactions, types.Receipts, error)
InfoByHash(ctx context.Context, hash common.Hash) (eth.L1Info, 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)
}
type Engine interface {
GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.ExecutionPayload, 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)
PayloadByNumber(context.Context, *big.Int) (*eth.ExecutionPayload, error)
PayloadByNumber(context.Context, uint64) (*eth.ExecutionPayload, error)
}
type L1Chain interface {
......
......@@ -5,7 +5,6 @@ import (
"context"
"errors"
"fmt"
"math/big"
"time"
"github.com/ethereum-optimism/optimism/op-node/eth"
......@@ -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 {
d.log.Info("processing new block", "parent", payload.ParentID(), "l2Head", l2Head, "id", payload.ID())
if err := d.l2.NewPayload(ctx, payload); err != nil {
return fmt.Errorf("failed to insert new payload: %v", err)
if status, err := d.l2.NewPayload(ctx, payload); err != nil {
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
fc := eth.ForkchoiceState{
......@@ -80,7 +81,7 @@ func (d *outputImpl) createNewBlock(ctx context.Context, l2Head eth.L2BlockRef,
fetchCtx, cancel := context.WithTimeout(ctx, time.Second*20)
defer cancel()
var l1Info derive.L1Info
var l1Info eth.L1Info
var receipts types.Receipts
var err error
......@@ -309,7 +310,7 @@ func attributesMatchBlock(attrs *eth.PayloadAttributes, parentHash common.Hash,
// 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.
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 {
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
d.log.Error("Dropped deposits when executing L2 block")
}
err = d.l2.NewPayload(ctx, payload)
if err != nil {
if status, err := d.l2.NewPayload(ctx, payload); 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)
}
fc.HeadBlockHash = payload.BlockHash
if updateSafe {
fc.SafeBlockHash = payload.BlockHash
......
......@@ -141,6 +141,14 @@ func (m *FakeChainSource) L1HeadBlockRef(ctx context.Context) (eth.L1BlockRef, e
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) {
m.log.Trace("L2BlockRefByNumber", "l2Num", l2Num, "l2Head", m.l2head, "reorg", m.l2reorg)
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
import (
"crypto/ecdsa"
"math/big"
"math/rand"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/common"
)
......@@ -22,3 +26,57 @@ func RandomETH(rng *rand.Rand, max int64) *big.Int {
x = new(big.Int).Mul(x, big.NewInt(1e18))
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