Commit 1cbeeb61 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into jg/unit_test_channel_bank

parents f2aef6df 7af83e47
...@@ -228,6 +228,7 @@ func (s *channelManager) processBlocks() error { ...@@ -228,6 +228,7 @@ func (s *channelManager) processBlocks() error {
} }
blocksAdded += 1 blocksAdded += 1
latestL2ref = l2BlockRefFromBlockAndL1Info(block, l1info) latestL2ref = l2BlockRefFromBlockAndL1Info(block, l1info)
s.metr.RecordL2BlockInChannel(block)
// current block got added but channel is now full // current block got added but channel is now full
if s.currentChannel.IsFull() { if s.currentChannel.IsFull() {
break break
...@@ -298,6 +299,8 @@ func (s *channelManager) AddL2Block(block *types.Block) error { ...@@ -298,6 +299,8 @@ func (s *channelManager) AddL2Block(block *types.Block) error {
if s.tip != (common.Hash{}) && s.tip != block.ParentHash() { if s.tip != (common.Hash{}) && s.tip != block.ParentHash() {
return ErrReorg return ErrReorg
} }
s.metr.RecordL2BlockInPendingQueue(block)
s.blocks = append(s.blocks, block) s.blocks = append(s.blocks, block)
s.tip = block.Hash() s.tip = block.Hash()
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
...@@ -30,6 +31,8 @@ type Metricer interface { ...@@ -30,6 +31,8 @@ type Metricer interface {
RecordL2BlocksLoaded(l2ref eth.L2BlockRef) RecordL2BlocksLoaded(l2ref eth.L2BlockRef)
RecordChannelOpened(id derive.ChannelID, numPendingBlocks int) RecordChannelOpened(id derive.ChannelID, numPendingBlocks int)
RecordL2BlocksAdded(l2ref eth.L2BlockRef, numBlocksAdded, numPendingBlocks, inputBytes, outputComprBytes int) RecordL2BlocksAdded(l2ref eth.L2BlockRef, numBlocksAdded, numPendingBlocks, inputBytes, outputComprBytes int)
RecordL2BlockInPendingQueue(block *types.Block)
RecordL2BlockInChannel(block *types.Block)
RecordChannelClosed(id derive.ChannelID, numPendingBlocks int, numFrames int, inputBytes int, outputComprBytes int, reason error) RecordChannelClosed(id derive.ChannelID, numPendingBlocks int, numFrames int, inputBytes int, outputComprBytes int, reason error)
RecordChannelFullySubmitted(id derive.ChannelID) RecordChannelFullySubmitted(id derive.ChannelID)
RecordChannelTimedOut(id derive.ChannelID) RecordChannelTimedOut(id derive.ChannelID)
...@@ -56,6 +59,8 @@ type Metrics struct { ...@@ -56,6 +59,8 @@ type Metrics struct {
channelEvs opmetrics.EventVec channelEvs opmetrics.EventVec
pendingBlocksCount prometheus.GaugeVec pendingBlocksCount prometheus.GaugeVec
pendingBlocksBytesTotal prometheus.Counter
pendingBlocksBytesCurrent prometheus.Gauge
blocksAddedCount prometheus.Gauge blocksAddedCount prometheus.Gauge
channelInputBytes prometheus.GaugeVec channelInputBytes prometheus.GaugeVec
...@@ -109,6 +114,16 @@ func NewMetrics(procName string) *Metrics { ...@@ -109,6 +114,16 @@ func NewMetrics(procName string) *Metrics {
Name: "pending_blocks_count", Name: "pending_blocks_count",
Help: "Number of pending blocks, not added to a channel yet.", Help: "Number of pending blocks, not added to a channel yet.",
}, []string{"stage"}), }, []string{"stage"}),
pendingBlocksBytesTotal: factory.NewCounter(prometheus.CounterOpts{
Namespace: ns,
Name: "pending_blocks_bytes_total",
Help: "Total size of transactions in pending blocks as they are fetched from L2",
}),
pendingBlocksBytesCurrent: factory.NewGauge(prometheus.GaugeOpts{
Namespace: ns,
Name: "pending_blocks_bytes_current",
Help: "Current size of transactions in the pending (fetched from L2 but not in a channel) stage.",
}),
blocksAddedCount: factory.NewGauge(prometheus.GaugeOpts{ blocksAddedCount: factory.NewGauge(prometheus.GaugeOpts{
Namespace: ns, Namespace: ns,
Name: "blocks_added_count", Name: "blocks_added_count",
...@@ -243,6 +258,18 @@ func (m *Metrics) RecordChannelClosed(id derive.ChannelID, numPendingBlocks int, ...@@ -243,6 +258,18 @@ func (m *Metrics) RecordChannelClosed(id derive.ChannelID, numPendingBlocks int,
m.channelClosedReason.Set(float64(ClosedReasonToNum(reason))) m.channelClosedReason.Set(float64(ClosedReasonToNum(reason)))
} }
func (m *Metrics) RecordL2BlockInPendingQueue(block *types.Block) {
size := float64(estimateBatchSize(block))
m.pendingBlocksBytesTotal.Add(size)
m.pendingBlocksBytesCurrent.Add(size)
}
func (m *Metrics) RecordL2BlockInChannel(block *types.Block) {
size := float64(estimateBatchSize(block))
m.pendingBlocksBytesCurrent.Add(-1 * size)
// Refer to RecordL2BlocksAdded to see the current + count of bytes added to a channel
}
func ClosedReasonToNum(reason error) int { func ClosedReasonToNum(reason error) int {
// CLI-3640 // CLI-3640
return 0 return 0
...@@ -267,3 +294,17 @@ func (m *Metrics) RecordBatchTxSuccess() { ...@@ -267,3 +294,17 @@ func (m *Metrics) RecordBatchTxSuccess() {
func (m *Metrics) RecordBatchTxFailed() { func (m *Metrics) RecordBatchTxFailed() {
m.batcherTxEvs.Record(TxStageFailed) m.batcherTxEvs.Record(TxStageFailed)
} }
// estimateBatchSize estimates the size of the batch
func estimateBatchSize(block *types.Block) uint64 {
size := uint64(70) // estimated overhead of batch metadata
for _, tx := range block.Transactions() {
// Don't include deposit transactions in the batch.
if tx.IsDepositTx() {
continue
}
// Add 2 for the overhead of encoding the tx bytes in a RLP list
size += tx.Size() + 2
}
return size
}
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
"github.com/ethereum/go-ethereum/core/types"
) )
type noopMetrics struct { type noopMetrics struct {
...@@ -23,6 +24,8 @@ func (*noopMetrics) RecordLatestL1Block(l1ref eth.L1BlockRef) {} ...@@ -23,6 +24,8 @@ func (*noopMetrics) RecordLatestL1Block(l1ref eth.L1BlockRef) {}
func (*noopMetrics) RecordL2BlocksLoaded(eth.L2BlockRef) {} func (*noopMetrics) RecordL2BlocksLoaded(eth.L2BlockRef) {}
func (*noopMetrics) RecordChannelOpened(derive.ChannelID, int) {} func (*noopMetrics) RecordChannelOpened(derive.ChannelID, int) {}
func (*noopMetrics) RecordL2BlocksAdded(eth.L2BlockRef, int, int, int, int) {} func (*noopMetrics) RecordL2BlocksAdded(eth.L2BlockRef, int, int, int, int) {}
func (*noopMetrics) RecordL2BlockInPendingQueue(*types.Block) {}
func (*noopMetrics) RecordL2BlockInChannel(*types.Block) {}
func (*noopMetrics) RecordChannelClosed(derive.ChannelID, int, int, int, int, error) {} func (*noopMetrics) RecordChannelClosed(derive.ChannelID, int, int, int, int, error) {}
......
...@@ -1084,11 +1084,6 @@ func TestWithdrawals(t *testing.T) { ...@@ -1084,11 +1084,6 @@ func TestWithdrawals(t *testing.T) {
proveReceipt, finalizeReceipt := ProveAndFinalizeWithdrawal(t, cfg, l1Client, sys.Nodes["verifier"], ethPrivKey, receipt) proveReceipt, finalizeReceipt := ProveAndFinalizeWithdrawal(t, cfg, l1Client, sys.Nodes["verifier"], ethPrivKey, receipt)
// Verify balance after withdrawal // Verify balance after withdrawal
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
header, err = l1Client.HeaderByNumber(ctx, finalizeReceipt.BlockNumber)
require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
endBalance, err = l1Client.BalanceAt(ctx, fromAddr, nil) endBalance, err = l1Client.BalanceAt(ctx, fromAddr, nil)
...@@ -1098,7 +1093,9 @@ func TestWithdrawals(t *testing.T) { ...@@ -1098,7 +1093,9 @@ func TestWithdrawals(t *testing.T) {
// Fun fact, the fee is greater than the withdrawal amount // Fun fact, the fee is greater than the withdrawal amount
// NOTE: The gas fees include *both* the ProveWithdrawalTransaction and FinalizeWithdrawalTransaction transactions. // NOTE: The gas fees include *both* the ProveWithdrawalTransaction and FinalizeWithdrawalTransaction transactions.
diff = new(big.Int).Sub(endBalance, startBalance) diff = new(big.Int).Sub(endBalance, startBalance)
fees = calcGasFees(proveReceipt.GasUsed+finalizeReceipt.GasUsed, tx.GasTipCap(), tx.GasFeeCap(), header.BaseFee) proveFee := new(big.Int).Mul(new(big.Int).SetUint64(proveReceipt.GasUsed), proveReceipt.EffectiveGasPrice)
finalizeFee := new(big.Int).Mul(new(big.Int).SetUint64(finalizeReceipt.GasUsed), finalizeReceipt.EffectiveGasPrice)
fees = new(big.Int).Add(proveFee, finalizeFee)
withdrawAmount = withdrawAmount.Sub(withdrawAmount, fees) withdrawAmount = withdrawAmount.Sub(withdrawAmount, fees)
require.Equal(t, withdrawAmount, diff) require.Equal(t, withdrawAmount, diff)
} }
......
...@@ -179,6 +179,7 @@ func (r ReceiptsFetchingMethod) String() string { ...@@ -179,6 +179,7 @@ func (r ReceiptsFetchingMethod) String() string {
addMaybe(DebugGetRawReceipts, "debug_getRawReceipts") addMaybe(DebugGetRawReceipts, "debug_getRawReceipts")
addMaybe(ParityGetBlockReceipts, "parity_getBlockReceipts") addMaybe(ParityGetBlockReceipts, "parity_getBlockReceipts")
addMaybe(EthGetBlockReceipts, "eth_getBlockReceipts") addMaybe(EthGetBlockReceipts, "eth_getBlockReceipts")
addMaybe(ErigonGetBlockReceiptsByBlockHash, "erigon_getBlockReceiptsByBlockHash")
addMaybe(^ReceiptsFetchingMethod(0), "unknown") // if anything is left, describe it as unknown addMaybe(^ReceiptsFetchingMethod(0), "unknown") // if anything is left, describe it as unknown
return out return out
} }
...@@ -230,10 +231,9 @@ const ( ...@@ -230,10 +231,9 @@ const (
// - Nethermind: https://docs.nethermind.io/nethermind/ethereum-client/json-rpc/parity#parity_getblockreceipts // - Nethermind: https://docs.nethermind.io/nethermind/ethereum-client/json-rpc/parity#parity_getblockreceipts
ParityGetBlockReceipts ParityGetBlockReceipts
// EthGetBlockReceipts is a non-standard receipt fetching method in the eth namespace, // EthGetBlockReceipts is a non-standard receipt fetching method in the eth namespace,
// supported by some RPC platforms and Erigon. // supported by some RPC platforms.
// Available in: // Available in:
// - Alchemy: 500 CU total (and deprecated) // - Alchemy: 500 CU total (and deprecated)
// - Erigon: free
// - QuickNode: 59 credits total (does not seem to work with block hash arg, inaccurate docs) // - QuickNode: 59 credits total (does not seem to work with block hash arg, inaccurate docs)
// Method: eth_getBlockReceipts // Method: eth_getBlockReceipts
// Params: // Params:
...@@ -243,7 +243,21 @@ const ( ...@@ -243,7 +243,21 @@ const (
// See: // See:
// - QuickNode: https://www.quicknode.com/docs/ethereum/eth_getBlockReceipts // - QuickNode: https://www.quicknode.com/docs/ethereum/eth_getBlockReceipts
// - Alchemy: https://docs.alchemy.com/reference/eth-getblockreceipts // - Alchemy: https://docs.alchemy.com/reference/eth-getblockreceipts
// Erigon has this available, but does not support block-hash argument to the method:
// https://github.com/ledgerwatch/erigon/blob/287a3d1d6c90fc6a7a088b5ae320f93600d5a167/cmd/rpcdaemon/commands/eth_receipts.go#L571
EthGetBlockReceipts EthGetBlockReceipts
// ErigonGetBlockReceiptsByBlockHash is an Erigon-specific receipt fetching method,
// the same as EthGetBlockReceipts but supporting a block-hash argument.
// Available in:
// - Erigon
// Method: erigon_getBlockReceiptsByBlockHash
// Params:
// - Erigon: string, hex-encoded block hash
// Returns:
// - Erigon: array of json-ified receipts
// See:
// https://github.com/ledgerwatch/erigon/blob/287a3d1d6c90fc6a7a088b5ae320f93600d5a167/cmd/rpcdaemon/commands/erigon_receipts.go#LL391C24-L391C51
ErigonGetBlockReceiptsByBlockHash
// Other: // Other:
// - 250 credits, not supported, strictly worse than other options. In quicknode price-table. // - 250 credits, not supported, strictly worse than other options. In quicknode price-table.
...@@ -269,13 +283,14 @@ func AvailableReceiptsFetchingMethods(kind RPCProviderKind) ReceiptsFetchingMeth ...@@ -269,13 +283,14 @@ func AvailableReceiptsFetchingMethods(kind RPCProviderKind) ReceiptsFetchingMeth
case RPCKindDebugGeth: case RPCKindDebugGeth:
return DebugGetRawReceipts | EthGetTransactionReceiptBatch return DebugGetRawReceipts | EthGetTransactionReceiptBatch
case RPCKindErigon: case RPCKindErigon:
return EthGetBlockReceipts | EthGetTransactionReceiptBatch return ErigonGetBlockReceiptsByBlockHash | EthGetTransactionReceiptBatch
case RPCKindBasic: case RPCKindBasic:
return EthGetTransactionReceiptBatch return EthGetTransactionReceiptBatch
case RPCKindAny: case RPCKindAny:
// if it's any kind of RPC provider, then try all methods // if it's any kind of RPC provider, then try all methods
return AlchemyGetTransactionReceipts | EthGetBlockReceipts | return AlchemyGetTransactionReceipts | EthGetBlockReceipts |
DebugGetRawReceipts | ParityGetBlockReceipts | EthGetTransactionReceiptBatch DebugGetRawReceipts | ErigonGetBlockReceiptsByBlockHash |
ParityGetBlockReceipts | EthGetTransactionReceiptBatch
default: default:
return EthGetTransactionReceiptBatch return EthGetTransactionReceiptBatch
} }
...@@ -310,6 +325,9 @@ func PickBestReceiptsFetchingMethod(kind RPCProviderKind, available ReceiptsFetc ...@@ -310,6 +325,9 @@ func PickBestReceiptsFetchingMethod(kind RPCProviderKind, available ReceiptsFetc
if available&DebugGetRawReceipts != 0 { if available&DebugGetRawReceipts != 0 {
return DebugGetRawReceipts return DebugGetRawReceipts
} }
if available&ErigonGetBlockReceiptsByBlockHash != 0 {
return ErigonGetBlockReceiptsByBlockHash
}
if available&EthGetBlockReceipts != 0 { if available&EthGetBlockReceipts != 0 {
return EthGetBlockReceipts return EthGetBlockReceipts
} }
...@@ -428,6 +446,8 @@ func (job *receiptsFetchingJob) runAltMethod(ctx context.Context, m ReceiptsFetc ...@@ -428,6 +446,8 @@ func (job *receiptsFetchingJob) runAltMethod(ctx context.Context, m ReceiptsFetc
err = job.client.CallContext(ctx, &result, "parity_getBlockReceipts", job.block.Hash) err = job.client.CallContext(ctx, &result, "parity_getBlockReceipts", job.block.Hash)
case EthGetBlockReceipts: case EthGetBlockReceipts:
err = job.client.CallContext(ctx, &result, "eth_getBlockReceipts", job.block.Hash) err = job.client.CallContext(ctx, &result, "eth_getBlockReceipts", job.block.Hash)
case ErigonGetBlockReceiptsByBlockHash:
err = job.client.CallContext(ctx, &result, "erigon_getBlockReceiptsByBlockHash", job.block.Hash)
default: default:
err = fmt.Errorf("unknown receipt fetching method: %d", uint64(m)) err = fmt.Errorf("unknown receipt fetching method: %d", uint64(m))
} }
......
...@@ -40,6 +40,15 @@ func (b *ethBackend) GetBlockReceipts(id string) ([]*types.Receipt, error) { ...@@ -40,6 +40,15 @@ func (b *ethBackend) GetBlockReceipts(id string) ([]*types.Receipt, error) {
return out[0].([]*types.Receipt), *out[1].(*error) return out[0].([]*types.Receipt), *out[1].(*error)
} }
type erigonBackend struct {
*mock.Mock
}
func (b *erigonBackend) GetBlockReceiptsByBlockHash(id string) ([]*types.Receipt, error) {
out := b.Mock.MethodCalled("erigon_getBlockReceiptsByBlockHash", id)
return out[0].([]*types.Receipt), *out[1].(*error)
}
type alchemyBackend struct { type alchemyBackend struct {
*mock.Mock *mock.Mock
} }
...@@ -99,6 +108,7 @@ func (tc *ReceiptsTestCase) Run(t *testing.T) { ...@@ -99,6 +108,7 @@ func (tc *ReceiptsTestCase) Run(t *testing.T) {
require.NoError(t, srv.RegisterName("alchemy", &alchemyBackend{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("debug", &debugBackend{Mock: m}))
require.NoError(t, srv.RegisterName("parity", &parityBackend{Mock: m})) require.NoError(t, srv.RegisterName("parity", &parityBackend{Mock: m}))
require.NoError(t, srv.RegisterName("erigon", &erigonBackend{Mock: m}))
block, requests := tc.setup(t) block, requests := tc.setup(t)
...@@ -127,6 +137,8 @@ func (tc *ReceiptsTestCase) Run(t *testing.T) { ...@@ -127,6 +137,8 @@ func (tc *ReceiptsTestCase) Run(t *testing.T) {
m.On("parity_getBlockReceipts", block.Hash.String()).Once().Return(req.result, &req.err) m.On("parity_getBlockReceipts", block.Hash.String()).Once().Return(req.result, &req.err)
case EthGetBlockReceipts: case EthGetBlockReceipts:
m.On("eth_getBlockReceipts", block.Hash.String()).Once().Return(req.result, &req.err) m.On("eth_getBlockReceipts", block.Hash.String()).Once().Return(req.result, &req.err)
case ErigonGetBlockReceiptsByBlockHash:
m.On("erigon_getBlockReceiptsByBlockHash", block.Hash.String()).Once().Return(req.result, &req.err)
default: default:
t.Fatalf("unrecognized request method: %d", uint64(req.method)) t.Fatalf("unrecognized request method: %d", uint64(req.method))
} }
...@@ -286,7 +298,7 @@ func TestEthClient_FetchReceipts(t *testing.T) { ...@@ -286,7 +298,7 @@ func TestEthClient_FetchReceipts(t *testing.T) {
{ {
name: "erigon", name: "erigon",
providerKind: RPCKindErigon, providerKind: RPCKindErigon,
setup: fallbackCase(4, EthGetBlockReceipts), setup: fallbackCase(4, ErigonGetBlockReceiptsByBlockHash),
}, },
{ {
name: "basic", name: "basic",
...@@ -305,6 +317,7 @@ func TestEthClient_FetchReceipts(t *testing.T) { ...@@ -305,6 +317,7 @@ func TestEthClient_FetchReceipts(t *testing.T) {
setup: fallbackCase(4, setup: fallbackCase(4,
AlchemyGetTransactionReceipts, AlchemyGetTransactionReceipts,
DebugGetRawReceipts, DebugGetRawReceipts,
ErigonGetBlockReceiptsByBlockHash,
EthGetBlockReceipts, EthGetBlockReceipts,
ParityGetBlockReceipts, ParityGetBlockReceipts,
), ),
......
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