Commit 46769845 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #4590 from ethereum-optimism/develop

Trigger release
parents 0df27c84 74f0a378
--- ---
'minimum-balance-agent': patch '@eth-optimism/balance-monitor': patch
--- ---
Added basic balance monitoring Added basic balance monitoring
--- ---
'minimum-balance-agent': patch '@eth-optimism/balance-monitor': patch
--- ---
Created the Balance Monitoring package Created the Balance Monitoring package
...@@ -18,6 +18,7 @@ jobs: ...@@ -18,6 +18,7 @@ jobs:
data-transport-layer: ${{ steps.packages.outputs.data-transport-layer }} data-transport-layer: ${{ steps.packages.outputs.data-transport-layer }}
contracts: ${{ steps.packages.outputs.contracts }} contracts: ${{ steps.packages.outputs.contracts }}
contracts-bedrock: ${{ steps.packages.outputs.contracts-bedrock }} contracts-bedrock: ${{ steps.packages.outputs.contracts-bedrock }}
balance-monitor: ${{ steps.packages.outputs.balance-monitor }}
gas-oracle: ${{ steps.packages.outputs.gas-oracle }} gas-oracle: ${{ steps.packages.outputs.gas-oracle }}
replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }} replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }}
proxyd: ${{ steps.packages.outputs.proxyd }} proxyd: ${{ steps.packages.outputs.proxyd }}
......
...@@ -16,6 +16,9 @@ import ( ...@@ -16,6 +16,9 @@ import (
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
) )
// validateReceipts validates that the receipt contents are valid.
// Warning: contractAddress is not verified, since it is a more expensive operation for data we do not use.
// See go-ethereum/crypto.CreateAddress to verify contract deployment address data based on sender and tx nonce.
func validateReceipts(block eth.BlockID, receiptHash common.Hash, txHashes []common.Hash, receipts []*types.Receipt) error { func validateReceipts(block eth.BlockID, receiptHash common.Hash, txHashes []common.Hash, receipts []*types.Receipt) error {
if len(receipts) != len(txHashes) { if len(receipts) != len(txHashes) {
return fmt.Errorf("got %d receipts but expected %d", len(receipts), len(txHashes)) return fmt.Errorf("got %d receipts but expected %d", len(receipts), len(txHashes))
...@@ -28,6 +31,7 @@ func validateReceipts(block eth.BlockID, receiptHash common.Hash, txHashes []com ...@@ -28,6 +31,7 @@ func validateReceipts(block eth.BlockID, receiptHash common.Hash, txHashes []com
// We don't trust the RPC to provide consistent cached receipt info that we use for critical rollup derivation work. // We don't trust the RPC to provide consistent cached receipt info that we use for critical rollup derivation work.
// Let's check everything quickly. // Let's check everything quickly.
logIndex := uint(0) logIndex := uint(0)
cumulativeGas := uint64(0)
for i, r := range receipts { for i, r := range receipts {
if r == nil { // on reorgs or other cases the receipts may disappear before they can be retrieved. if r == nil { // on reorgs or other cases the receipts may disappear before they can be retrieved.
return fmt.Errorf("receipt of tx %d returns nil on retrieval", i) return fmt.Errorf("receipt of tx %d returns nil on retrieval", i)
...@@ -44,6 +48,9 @@ func validateReceipts(block eth.BlockID, receiptHash common.Hash, txHashes []com ...@@ -44,6 +48,9 @@ func validateReceipts(block eth.BlockID, receiptHash common.Hash, txHashes []com
if r.BlockHash != block.Hash { if r.BlockHash != block.Hash {
return fmt.Errorf("receipt %d has unexpected block hash %s, expected %s", i, r.BlockHash, block.Hash) return fmt.Errorf("receipt %d has unexpected block hash %s, expected %s", i, r.BlockHash, block.Hash)
} }
if expected := r.CumulativeGasUsed - cumulativeGas; r.GasUsed != expected {
return fmt.Errorf("receipt %d has invalid gas used metadata: %d, expected %d", i, r.GasUsed, expected)
}
for j, log := range r.Logs { for j, log := range r.Logs {
if log.Index != logIndex { if log.Index != logIndex {
return fmt.Errorf("log %d (%d of tx %d) has unexpected log index %d", logIndex, j, i, log.Index) return fmt.Errorf("log %d (%d of tx %d) has unexpected log index %d", logIndex, j, i, log.Index)
...@@ -65,10 +72,10 @@ func validateReceipts(block eth.BlockID, receiptHash common.Hash, txHashes []com ...@@ -65,10 +72,10 @@ func validateReceipts(block eth.BlockID, receiptHash common.Hash, txHashes []com
} }
logIndex++ logIndex++
} }
cumulativeGas = r.CumulativeGasUsed
// Note: 3 non-consensus L1 receipt fields are ignored: // Note: 3 non-consensus L1 receipt fields are ignored:
// PostState - not part of L1 ethereum anymore since EIP 658 (part of Byzantium) // PostState - not part of L1 ethereum anymore since EIP 658 (part of Byzantium)
// ContractAddress - we do not care about contract deployments // ContractAddress - we do not care about contract deployments
// GasUsed - we do not care about L1 gas usage of txs
// And Optimism L1 fee meta-data in the receipt is ignored as well // And Optimism L1 fee meta-data in the receipt is ignored as well
} }
...@@ -250,7 +257,7 @@ const ( ...@@ -250,7 +257,7 @@ const (
func AvailableReceiptsFetchingMethods(kind RPCProviderKind) ReceiptsFetchingMethod { func AvailableReceiptsFetchingMethods(kind RPCProviderKind) ReceiptsFetchingMethod {
switch kind { switch kind {
case RPCKindAlchemy: case RPCKindAlchemy:
return AlchemyGetTransactionReceipts | EthGetTransactionReceiptBatch return AlchemyGetTransactionReceipts | EthGetBlockReceipts | EthGetTransactionReceiptBatch
case RPCKindQuickNode: case RPCKindQuickNode:
return DebugGetRawReceipts | EthGetBlockReceipts | EthGetTransactionReceiptBatch return DebugGetRawReceipts | EthGetBlockReceipts | EthGetTransactionReceiptBatch
case RPCKindInfura: case RPCKindInfura:
...@@ -287,9 +294,6 @@ func PickBestReceiptsFetchingMethod(kind RPCProviderKind, available ReceiptsFetc ...@@ -287,9 +294,6 @@ func PickBestReceiptsFetchingMethod(kind RPCProviderKind, available ReceiptsFetc
if available&EthGetBlockReceipts != 0 && txCount > 500/15 { if available&EthGetBlockReceipts != 0 && txCount > 500/15 {
return EthGetBlockReceipts return EthGetBlockReceipts
} }
if available&ParityGetBlockReceipts != 0 && txCount > 500/15 {
return ParityGetBlockReceipts
}
return EthGetTransactionReceiptBatch return EthGetTransactionReceiptBatch
} else if kind == RPCKindQuickNode { } else if kind == RPCKindQuickNode {
if available&DebugGetRawReceipts != 0 { if available&DebugGetRawReceipts != 0 {
...@@ -298,18 +302,20 @@ func PickBestReceiptsFetchingMethod(kind RPCProviderKind, available ReceiptsFetc ...@@ -298,18 +302,20 @@ func PickBestReceiptsFetchingMethod(kind RPCProviderKind, available ReceiptsFetc
if available&EthGetBlockReceipts != 0 && txCount > 59/2 { if available&EthGetBlockReceipts != 0 && txCount > 59/2 {
return EthGetBlockReceipts return EthGetBlockReceipts
} }
if available&ParityGetBlockReceipts != 0 && txCount > 59/2 {
return ParityGetBlockReceipts
}
return EthGetTransactionReceiptBatch return EthGetTransactionReceiptBatch
} }
// otherwise just find the first available method // in order of preference (based on cost): check available methods
x := ReceiptsFetchingMethod(1) if available&AlchemyGetTransactionReceipts != 0 {
for x != 0 { return AlchemyGetTransactionReceipts
if available&x != 0 { }
return x if available&DebugGetRawReceipts != 0 {
} return DebugGetRawReceipts
x <<= 1 }
if available&EthGetBlockReceipts != 0 {
return EthGetBlockReceipts
}
if available&ParityGetBlockReceipts != 0 {
return ParityGetBlockReceipts
} }
// otherwise fall back on per-tx fetching // otherwise fall back on per-tx fetching
return EthGetTransactionReceiptBatch return EthGetTransactionReceiptBatch
...@@ -415,6 +421,7 @@ func (job *receiptsFetchingJob) runAltMethod(ctx context.Context, m ReceiptsFetc ...@@ -415,6 +421,7 @@ func (job *receiptsFetchingJob) runAltMethod(ctx context.Context, m ReceiptsFetc
if len(rawReceipts) == len(job.txHashes) { if len(rawReceipts) == len(job.txHashes) {
result = make([]*types.Receipt, len(rawReceipts)) result = make([]*types.Receipt, len(rawReceipts))
totalIndex := uint(0) totalIndex := uint(0)
prevCumulativeGasUsed := uint64(0)
for i, r := range rawReceipts { for i, r := range rawReceipts {
var x types.Receipt var x types.Receipt
_ = x.UnmarshalBinary(r) // safe to ignore, we verify receipts against the receipts hash later _ = x.UnmarshalBinary(r) // safe to ignore, we verify receipts against the receipts hash later
...@@ -422,6 +429,9 @@ func (job *receiptsFetchingJob) runAltMethod(ctx context.Context, m ReceiptsFetc ...@@ -422,6 +429,9 @@ func (job *receiptsFetchingJob) runAltMethod(ctx context.Context, m ReceiptsFetc
x.BlockHash = job.block.Hash x.BlockHash = job.block.Hash
x.BlockNumber = new(big.Int).SetUint64(job.block.Number) x.BlockNumber = new(big.Int).SetUint64(job.block.Number)
x.TransactionIndex = uint(i) x.TransactionIndex = uint(i)
x.GasUsed = x.CumulativeGasUsed - prevCumulativeGasUsed
// contract address meta-data is not computed.
prevCumulativeGasUsed = x.CumulativeGasUsed
for _, l := range x.Logs { for _, l := range x.Logs {
l.BlockNumber = job.block.Number l.BlockNumber = job.block.Number
l.TxHash = x.TxHash l.TxHash = x.TxHash
......
package sources
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
type ethBackend struct {
*mock.Mock
}
func (b *ethBackend) GetBlockByHash(id common.Hash, fullTxs bool) (*rpcBlock, error) {
out := b.Mock.MethodCalled("eth_getBlockByHash", id, fullTxs)
return out[0].(*rpcBlock), nil
}
func (b *ethBackend) GetTransactionReceipt(txHash common.Hash) (*types.Receipt, error) {
out := b.Mock.MethodCalled("eth_getTransactionReceipt", txHash)
return out[0].(*types.Receipt), *out[1].(*error)
}
func (b *ethBackend) GetBlockReceipts(id string) ([]*types.Receipt, error) {
out := b.Mock.MethodCalled("eth_getBlockReceipts", id)
return out[0].([]*types.Receipt), *out[1].(*error)
}
type alchemyBackend struct {
*mock.Mock
}
func (b *alchemyBackend) GetTransactionReceipts(p blockHashParameter) (*receiptsWrapper, error) {
out := b.Mock.MethodCalled("alchemy_getTransactionReceipts", p.BlockHash.String())
return &receiptsWrapper{Receipts: out[0].([]*types.Receipt)}, *out[1].(*error)
}
type debugBackend struct {
*mock.Mock
}
func (b *debugBackend) GetRawReceipts(id string) ([]hexutil.Bytes, error) {
out := b.Mock.MethodCalled("debug_getRawReceipts", id)
return out[0].([]hexutil.Bytes), *out[1].(*error)
}
type parityBackend struct {
*mock.Mock
}
func (b *parityBackend) GetBlockReceipts(id string) ([]*types.Receipt, error) {
out := b.Mock.MethodCalled("parity_getBlockReceipts", id)
return out[0].([]*types.Receipt), *out[1].(*error)
}
type ReceiptsRequest struct {
method ReceiptsFetchingMethod
result []*types.Receipt
err error
}
type methodNotFoundError struct{ method string }
func (e *methodNotFoundError) ErrorCode() int { return -32601 }
func (e *methodNotFoundError) Error() string {
return fmt.Sprintf("the method %s does not exist/is not available", e.method)
}
// ReceiptsTestCase runs through a series of receipt fetching RPC requests with mocked results
// to test the prioritization/fallback logic of the receipt fetching in the EthClient.
type ReceiptsTestCase struct {
name string
providerKind RPCProviderKind
setup func(t *testing.T) (*rpcBlock, []ReceiptsRequest)
}
func (tc *ReceiptsTestCase) Run(t *testing.T) {
srv := rpc.NewServer()
defer srv.Stop()
m := &mock.Mock{}
require.NoError(t, srv.RegisterName("eth", &ethBackend{Mock: m}))
require.NoError(t, srv.RegisterName("alchemy", &alchemyBackend{Mock: m}))
require.NoError(t, srv.RegisterName("debug", &debugBackend{Mock: m}))
require.NoError(t, srv.RegisterName("parity", &parityBackend{Mock: m}))
block, requests := tc.setup(t)
// always expect a block request to fetch txs and receipts root hash etc.
m.On("eth_getBlockByHash", block.Hash, true).Once().Return(block)
for _, reqData := range requests {
req := reqData
// depending on the method, expect to serve receipts by request(s)
switch req.method {
case EthGetTransactionReceiptBatch:
for i, tx := range block.Transactions {
m.On("eth_getTransactionReceipt", tx.Hash()).Once().Return(req.result[i], &req.err)
}
case AlchemyGetTransactionReceipts:
m.On("alchemy_getTransactionReceipts", block.Hash.String()).Once().Return(req.result, &req.err)
case DebugGetRawReceipts:
var raw []hexutil.Bytes
for _, r := range req.result {
data, err := r.MarshalBinary()
require.NoError(t, err)
raw = append(raw, data)
}
m.On("debug_getRawReceipts", block.Hash.String()).Once().Return(raw, &req.err)
case ParityGetBlockReceipts:
m.On("parity_getBlockReceipts", block.Hash.String()).Once().Return(req.result, &req.err)
case EthGetBlockReceipts:
m.On("eth_getBlockReceipts", block.Hash.String()).Once().Return(req.result, &req.err)
default:
t.Fatalf("unrecognized request method: %d", uint64(req.method))
}
}
cl := rpc.DialInProc(srv)
testCfg := &EthClientConfig{
// receipts and transactions are cached per block
ReceiptsCacheSize: 1000,
TransactionsCacheSize: 1000,
HeadersCacheSize: 1000,
PayloadsCacheSize: 1000,
MaxRequestsPerBatch: 20,
MaxConcurrentRequests: 10,
TrustRPC: false,
MustBePostMerge: false,
RPCProviderKind: tc.providerKind,
}
logger := testlog.Logger(t, log.LvlError)
ethCl, err := NewEthClient(client.NewBaseRPCClient(cl), logger, nil, testCfg)
require.NoError(t, err)
defer ethCl.Close()
for i, req := range requests {
info, result, err := ethCl.FetchReceipts(context.Background(), block.Hash)
if err == nil {
require.Nil(t, req.err, "error")
require.Equal(t, block.Hash, info.Hash(), fmt.Sprintf("req %d blockhash", i))
expectedJson, err := json.MarshalIndent(req.result, "", " ")
require.NoError(t, err)
gotJson, err := json.MarshalIndent(result, "", " ")
require.NoError(t, err)
require.Equal(t, string(expectedJson), string(gotJson), fmt.Sprintf("req %d result", i))
} else {
require.NotNil(t, req.err, "error")
require.Equal(t, req.err.Error(), err.Error(), fmt.Sprintf("req %d err", i))
}
}
m.AssertExpectations(t)
}
func randomRpcBlockAndReceipts(rng *rand.Rand, txCount uint64) (*rpcBlock, []*types.Receipt) {
block, receipts := testutils.RandomBlock(rng, txCount)
return &rpcBlock{
rpcHeader: rpcHeader{
ParentHash: block.ParentHash(),
UncleHash: block.UncleHash(),
Coinbase: block.Coinbase(),
Root: block.Root(),
TxHash: block.TxHash(),
ReceiptHash: block.ReceiptHash(),
Bloom: eth.Bytes256(block.Bloom()),
Difficulty: *(*hexutil.Big)(block.Difficulty()),
Number: hexutil.Uint64(block.NumberU64()),
GasLimit: hexutil.Uint64(block.GasLimit()),
GasUsed: hexutil.Uint64(block.GasUsed()),
Time: hexutil.Uint64(block.Time()),
Extra: hexutil.Bytes(block.Extra()),
MixDigest: block.MixDigest(),
Nonce: types.EncodeNonce(block.Nonce()),
BaseFee: (*hexutil.Big)(block.BaseFee()),
Hash: block.Hash(),
},
Transactions: block.Transactions(),
}, receipts
}
func TestEthClient_FetchReceipts(t *testing.T) {
// Helper to quickly define the test case requests scenario:
// each method fails to fetch the receipts, except the last
fallbackCase := func(txCount uint64, methods ...ReceiptsFetchingMethod) func(t *testing.T) (*rpcBlock, []ReceiptsRequest) {
return func(t *testing.T) (*rpcBlock, []ReceiptsRequest) {
block, receipts := randomRpcBlockAndReceipts(rand.New(rand.NewSource(123)), txCount)
// zero out the data we don't want to verify
for _, r := range receipts {
r.ContractAddress = common.Address{}
}
var out []ReceiptsRequest
for _, m := range methods {
out = append(out, ReceiptsRequest{
method: m,
})
}
// all but the last request fail to fetch receipts
for i := 0; i < len(out)-1; i++ {
out[i].result = nil
out[i].err = new(methodNotFoundError)
}
// last request fetches receipts
out[len(out)-1].result = receipts
return block, out
}
}
testCases := []ReceiptsTestCase{
{
name: "alchemy",
providerKind: RPCKindAlchemy,
setup: fallbackCase(30, AlchemyGetTransactionReceipts),
},
{
name: "alchemy fallback 1",
providerKind: RPCKindAlchemy,
setup: fallbackCase(40, AlchemyGetTransactionReceipts, EthGetBlockReceipts),
},
{
name: "alchemy low tx count cost saving",
providerKind: RPCKindAlchemy,
// when it's cheaper to fetch individual receipts than the alchemy-bundled receipts we change methods.
setup: fallbackCase(5, EthGetTransactionReceiptBatch),
},
{
name: "quicknode",
providerKind: RPCKindQuickNode,
setup: fallbackCase(30, DebugGetRawReceipts),
},
{
name: "quicknode fallback 1",
providerKind: RPCKindQuickNode,
setup: fallbackCase(30,
DebugGetRawReceipts,
EthGetBlockReceipts,
),
},
{
name: "quicknode low tx count cost saving",
providerKind: RPCKindQuickNode,
// when it's cheaper to fetch individual receipts than the alchemy-bundled receipts we change methods.
setup: fallbackCase(5, DebugGetRawReceipts, EthGetTransactionReceiptBatch),
},
{
name: "infura",
providerKind: RPCKindInfura,
setup: fallbackCase(4, EthGetTransactionReceiptBatch),
},
{
name: "nethermind",
providerKind: RPCKindNethermind,
setup: fallbackCase(4, ParityGetBlockReceipts), // uses parity namespace method
},
{
name: "geth with debug rpc",
providerKind: RPCKindDebugGeth,
setup: fallbackCase(4, DebugGetRawReceipts),
},
{
name: "erigon",
providerKind: RPCKindErigon,
setup: fallbackCase(4, EthGetBlockReceipts),
},
{
name: "basic",
providerKind: RPCKindBasic,
setup: fallbackCase(4, EthGetTransactionReceiptBatch),
},
{
name: "any discovers alchemy",
providerKind: RPCKindAny,
setup: fallbackCase(4, AlchemyGetTransactionReceipts),
},
{
name: "any discovers parity",
providerKind: RPCKindAny,
// fallback through the least priority method: parity (nethermind supports this still)
setup: fallbackCase(4,
AlchemyGetTransactionReceipts,
DebugGetRawReceipts,
EthGetBlockReceipts,
ParityGetBlockReceipts,
),
},
}
for _, tc := range testCases {
t.Run(tc.name, tc.Run)
}
}
...@@ -5,6 +5,10 @@ import ( ...@@ -5,6 +5,10 @@ import (
"math/big" "math/big"
"math/rand" "math/rand"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -90,3 +94,139 @@ func NextRandomL2Ref(rng *rand.Rand, l2BlockTime uint64, parent eth.L2BlockRef, ...@@ -90,3 +94,139 @@ func NextRandomL2Ref(rng *rand.Rand, l2BlockTime uint64, parent eth.L2BlockRef,
SequenceNumber: seq, SequenceNumber: seq,
} }
} }
func InsecureRandomKey(rng *rand.Rand) *ecdsa.PrivateKey {
key, err := ecdsa.GenerateKey(crypto.S256(), rng)
if err != nil {
panic(err)
}
return key
}
func RandomLog(rng *rand.Rand) *types.Log {
topics := make([]common.Hash, rng.Intn(3))
for i := 0; i < len(topics); i++ {
topics[i] = RandomHash(rng)
}
return &types.Log{
Address: RandomAddress(rng),
Topics: topics,
Data: RandomData(rng, rng.Intn(1000)),
BlockNumber: 0,
TxHash: common.Hash{},
TxIndex: 0,
BlockHash: common.Hash{},
Index: 0,
Removed: false,
}
}
func RandomTo(rng *rand.Rand) *common.Address {
if rng.Intn(2) == 0 {
return nil
}
to := RandomAddress(rng)
return &to
}
func RandomTx(rng *rand.Rand, baseFee *big.Int, signer types.Signer) *types.Transaction {
gas := params.TxGas + uint64(rng.Int63n(2_000_000))
key := InsecureRandomKey(rng)
tip := big.NewInt(rng.Int63n(10 * params.GWei))
tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
ChainID: signer.ChainID(),
Nonce: rng.Uint64(),
GasTipCap: tip,
GasFeeCap: new(big.Int).Add(baseFee, tip),
Gas: gas,
To: RandomTo(rng),
Value: RandomETH(rng, 10),
Data: RandomData(rng, rng.Intn(1000)),
AccessList: nil,
})
if err != nil {
panic(err)
}
return tx
}
func RandomReceipt(rng *rand.Rand, signer types.Signer, tx *types.Transaction, txIndex uint64, cumulativeGasUsed uint64) *types.Receipt {
gasUsed := params.TxGas + uint64(rng.Int63n(int64(tx.Gas()-params.TxGas+1)))
logs := make([]*types.Log, rng.Intn(10))
for i := range logs {
logs[i] = RandomLog(rng)
}
var contractAddr common.Address
if tx.To() == nil {
sender, err := signer.Sender(tx)
if err != nil {
panic(err)
}
contractAddr = crypto.CreateAddress(sender, tx.Nonce())
}
return &types.Receipt{
Type: tx.Type(),
Status: uint64(rng.Intn(2)),
CumulativeGasUsed: cumulativeGasUsed + gasUsed,
Bloom: types.Bloom{},
Logs: logs,
TxHash: tx.Hash(),
ContractAddress: contractAddr,
GasUsed: gasUsed,
TransactionIndex: uint(txIndex),
}
}
func RandomHeader(rng *rand.Rand) *types.Header {
return &types.Header{
ParentHash: RandomHash(rng),
UncleHash: types.EmptyUncleHash,
Coinbase: RandomAddress(rng),
Root: RandomHash(rng),
TxHash: types.EmptyRootHash,
ReceiptHash: types.EmptyRootHash,
Bloom: types.Bloom{},
Difficulty: big.NewInt(0),
Number: big.NewInt(1 + rng.Int63n(100_000_000)),
GasLimit: 0,
GasUsed: 0,
Time: uint64(rng.Int63n(2_000_000_000)),
Extra: RandomData(rng, rng.Intn(33)),
MixDigest: common.Hash{},
Nonce: types.BlockNonce{},
BaseFee: big.NewInt(rng.Int63n(300_000_000_000)),
}
}
func RandomBlock(rng *rand.Rand, txCount uint64) (*types.Block, []*types.Receipt) {
header := RandomHeader(rng)
signer := types.NewLondonSigner(big.NewInt(rng.Int63n(1000)))
txs := make([]*types.Transaction, 0, txCount)
for i := uint64(0); i < txCount; i++ {
txs = append(txs, RandomTx(rng, header.BaseFee, signer))
}
receipts := make([]*types.Receipt, 0, txCount)
cumulativeGasUsed := uint64(0)
for i, tx := range txs {
r := RandomReceipt(rng, signer, tx, uint64(i), cumulativeGasUsed)
cumulativeGasUsed += r.GasUsed
receipts = append(receipts, r)
}
header.GasUsed = cumulativeGasUsed
header.GasLimit = cumulativeGasUsed + uint64(rng.Int63n(int64(cumulativeGasUsed)))
block := types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil))
logIndex := uint(0)
for i, r := range receipts {
r.BlockHash = block.Hash()
r.BlockNumber = block.Number()
for _, l := range r.Logs {
l.BlockHash = block.Hash()
l.BlockNumber = block.NumberU64()
l.TxIndex = uint(i)
l.TxHash = txs[i].Hash()
l.Index = logIndex
logIndex += 1
}
}
return block, receipts
}
{ {
"name": "@eth-optimism/minimum-balance-agent", "name": "@eth-optimism/balance-monitor",
"version": "0.0.1", "version": "0.0.1",
"description": "Forta Agent that reports whether certain accounts have fallen below some balance", "description": "Forta Agent that reports whether certain accounts have fallen below some balance",
"homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/packages/balance-monitor#readme", "homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/packages/balance-monitor#readme",
......
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