Skip to content

Commit

Permalink
feat(monitor): add zkevm batch counters (#373)
Browse files Browse the repository at this point in the history
* feat(monitor): add trusted, virtual and verified batches counters

* chore: update ui

* chore: nit

* chore: refactor

* chore: refactor txpoolstatus

* chore: refactor monitor top row

* fix: lint

* chore: nit

* chore: better zkevm batch ux
  • Loading branch information
leovct committed Sep 17, 2024
1 parent efa3f1b commit 0c533ee
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 46 deletions.
73 changes: 55 additions & 18 deletions cmd/monitor/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ type (
HeadBlock *big.Int
PeerCount uint64
GasPrice *big.Int
PendingCount uint64
QueuedCount uint64
TxPoolStatus txPoolStatus
ZkEVMBatches zkEVMBatches
SelectedBlock rpctypes.PolyBlock
SelectedTransaction rpctypes.PolyTransaction
BlockCache *lru.Cache `json:"-"`
Expand All @@ -76,8 +76,17 @@ type (
ChainID *big.Int
PeerCount uint64
GasPrice *big.Int
PendingCount uint64
QueuedCount uint64
TxPoolStatus txPoolStatus
ZkEVMBatches zkEVMBatches
}
txPoolStatus struct {
pending uint64
queued uint64
}
zkEVMBatches struct {
trusted uint64
virtual uint64
verified uint64
}
historicalDataPoint struct {
SampleTime time.Time
Expand Down Expand Up @@ -111,6 +120,22 @@ func monitor(ctx context.Context) error {
return errBatchRequestsNotSupported
}

// Check if tx pool status is supported.
txPoolStatusSupported := false
if _, _, err = util.GetTxPoolStatus(rpc); err != nil {
log.Debug().Err(err).Msg("Unable to get tx pool status")
} else {
txPoolStatusSupported = true
}

// Check if zkevm batches are supported.
zkEVMBatchesSupported := false
if _, _, _, err = util.GetZkEVMBatches(rpc); err != nil {
log.Debug().Err(err).Msg("Unable to get zkevm batches")
} else {
zkEVMBatchesSupported = true
}

ms := new(monitorStatus)
ms.BlocksLock.Lock()
ms.BlockCache, err = lru.New(blockCacheLimit)
Expand All @@ -121,8 +146,8 @@ func monitor(ctx context.Context) error {
ms.BlocksLock.Unlock()

ms.ChainID = big.NewInt(0)
ms.PendingCount = 0
ms.QueuedCount = 0
ms.TxPoolStatus = txPoolStatus{}
ms.ZkEVMBatches = zkEVMBatches{}

observedPendingTxs = make(historicalRange, 0)

Expand All @@ -148,7 +173,7 @@ func monitor(ctx context.Context) error {
}
if !isUiRendered {
go func() {
errChan <- renderMonitorUI(ctx, ec, ms, rpc)
errChan <- renderMonitorUI(ctx, ec, ms, rpc, txPoolStatusSupported, zkEVMBatchesSupported)
}()
isUiRendered = true
}
Expand Down Expand Up @@ -186,9 +211,14 @@ func getChainState(ctx context.Context, ec *ethclient.Client) (*chainState, erro
return nil, fmt.Errorf("couldn't estimate gas: %s", err.Error())
}

cs.PendingCount, cs.QueuedCount, err = util.GetTxPoolStatus(ec.Client())
cs.TxPoolStatus.pending, cs.TxPoolStatus.queued, err = util.GetTxPoolStatus(ec.Client())
if err != nil {
log.Debug().Err(err).Msg("Unable to get pending and queued transaction count")
log.Debug().Err(err).Msg("Unable to get tx pool status")
}

cs.ZkEVMBatches.trusted, cs.ZkEVMBatches.virtual, cs.ZkEVMBatches.verified, err = util.GetZkEVMBatches(ec.Client())
if err != nil {
log.Debug().Err(err).Msg("Unable to get zkevm batches")
}

return cs, nil
Expand All @@ -214,7 +244,7 @@ func fetchCurrentBlockData(ctx context.Context, ec *ethclient.Client, ms *monito
time.Sleep(interval)
return err
}
observedPendingTxs = append(observedPendingTxs, historicalDataPoint{SampleTime: time.Now(), SampleValue: float64(cs.PendingCount)})
observedPendingTxs = append(observedPendingTxs, historicalDataPoint{SampleTime: time.Now(), SampleValue: float64(cs.TxPoolStatus.pending)})
if len(observedPendingTxs) > maxDataPoints {
observedPendingTxs = observedPendingTxs[len(observedPendingTxs)-maxDataPoints:]
}
Expand All @@ -231,8 +261,8 @@ func fetchCurrentBlockData(ctx context.Context, ec *ethclient.Client, ms *monito
ms.ChainID = cs.ChainID
ms.PeerCount = cs.PeerCount
ms.GasPrice = cs.GasPrice
ms.PendingCount = cs.PendingCount
ms.QueuedCount = cs.QueuedCount
ms.TxPoolStatus = cs.TxPoolStatus
ms.ZkEVMBatches = cs.ZkEVMBatches

return
}
Expand Down Expand Up @@ -366,7 +396,7 @@ func (ms *monitorStatus) processBatchesConcurrently(ctx context.Context, rpc *et
return errors.Join(errs...)
}

func renderMonitorUI(ctx context.Context, ec *ethclient.Client, ms *monitorStatus, rpc *ethrpc.Client) error {
func renderMonitorUI(ctx context.Context, ec *ethclient.Client, ms *monitorStatus, rpc *ethrpc.Client, txPoolStatusSupported, zkEVMBatchesSupported bool) error {
if err := termui.Init(); err != nil {
log.Error().Err(err).Msg("Failed to initialize UI")
return err
Expand All @@ -375,7 +405,7 @@ func renderMonitorUI(ctx context.Context, ec *ethclient.Client, ms *monitorStatu

currentMode := monitorModeExplorer

blockTable, blockInfo, transactionList, transactionInformationList, transactionInfo, grid, selectGrid, blockGrid, transactionGrid, skeleton := ui.SetUISkeleton()
blockTable, blockInfo, transactionList, transactionInformationList, transactionInfo, grid, selectGrid, blockGrid, transactionGrid, skeleton := ui.SetUISkeleton(txPoolStatusSupported, zkEVMBatchesSupported)

termWidth, termHeight := termui.TerminalDimensions()
windowSize = termHeight/2 - 4
Expand Down Expand Up @@ -476,8 +506,8 @@ func renderMonitorUI(ctx context.Context, ec *ethclient.Client, ms *monitorStatu
Str("HeadBlock", ms.HeadBlock.String()).
Uint64("PeerCount", ms.PeerCount).
Str("GasPrice", ms.GasPrice.String()).
Uint64("PendingCount", ms.PendingCount).
Uint64("QueuedCount", ms.QueuedCount).
Interface("TxPoolStatus", ms.TxPoolStatus).
Interface("ZkEVMBatches", ms.ZkEVMBatches).
Msg("Redrawing")

if blockTable.SelectedRow == 0 {
Expand Down Expand Up @@ -509,8 +539,15 @@ func renderMonitorUI(ctx context.Context, ec *ethclient.Client, ms *monitorStatu
ms.BlocksLock.RUnlock()
renderedBlocks = renderedBlocksTemp

log.Debug().Int("skeleton.Current.Inner.Dy()", skeleton.Current.Inner.Dy()).Int("skeleton.Current.Inner.Dx()", skeleton.Current.Inner.Dx()).Msg("the dimension of the current box")
skeleton.Current.Text = ui.GetCurrentBlockInfo(ms.HeadBlock, ms.GasPrice, ms.PeerCount, ms.PendingCount, ms.QueuedCount, ms.ChainID, rpcUrl, renderedBlocks, skeleton.Current.Inner.Dx(), skeleton.Current.Inner.Dy())
skeleton.Current.Text = ui.GetCurrentText(skeleton.Current, ms.HeadBlock, ms.GasPrice, ms.PeerCount, ms.ChainID, rpcUrl)
if txPoolStatusSupported {
skeleton.TxPool.Text = ui.GetTxPoolText(skeleton.TxPool, ms.TxPoolStatus.pending, ms.TxPoolStatus.queued)
}

if zkEVMBatchesSupported {
skeleton.ZkEVM.Text = ui.GetZkEVMText(skeleton.ZkEVM, ms.ZkEVMBatches.trusted, ms.ZkEVMBatches.virtual, ms.ZkEVMBatches.verified)
}

skeleton.TxPerBlockChart.Data = metrics.GetTxsPerBlock(renderedBlocks)
skeleton.GasPriceChart.Data = metrics.GetMeanGasPricePerBlock(renderedBlocks)
skeleton.BlockSizeChart.Data = metrics.GetSizePerBlock(renderedBlocks)
Expand Down
103 changes: 75 additions & 28 deletions cmd/monitor/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,58 @@ import (
)

type UiSkeleton struct {
Current *widgets.Paragraph
TxPerBlockChart *widgets.Sparkline
GasPriceChart *widgets.Sparkline
BlockSizeChart *widgets.Sparkline
PendingTxChart *widgets.Sparkline
GasChart *widgets.Sparkline
BlockInfo *widgets.List
TxInfo *widgets.List
Receipts *widgets.List
Current, TxPool, ZkEVM *widgets.Paragraph
TxPerBlockChart *widgets.Sparkline
GasPriceChart *widgets.Sparkline
BlockSizeChart *widgets.Sparkline
PendingTxChart *widgets.Sparkline
GasChart *widgets.Sparkline
BlockInfo *widgets.List
TxInfo *widgets.List
Receipts *widgets.List
}

func GetCurrentBlockInfo(headBlock *big.Int, gasPrice *big.Int, peerCount uint64, pendingCount uint64, queuedCount uint64, chainID *big.Int, rpcURL string, blocks []rpctypes.PolyBlock, dx int, dy int) string {
// Return an appropriate message if dy is 0 or less.
if dy <= 0 {
return "Invalid display configuration."
}

func GetCurrentText(widget *widgets.Paragraph, headBlock, gasPrice *big.Int, peerCount uint64, chainID *big.Int, rpcURL string) string {
// First column
height := fmt.Sprintf("Height: %s", headBlock.String())
timeInfo := fmt.Sprintf("Time: %s", time.Now().Format("02 Jan 06 15:04:05 MST"))
gasPriceString := fmt.Sprintf("Gas Price: %s gwei", new(big.Int).Div(gasPrice, metrics.UnitShannon).String())
peers := fmt.Sprintf("Peers: %d", peerCount)
pendingTx := fmt.Sprintf("Pending Tx: %d", pendingCount)
queuedTx := fmt.Sprintf("Queued Tx: %d", queuedCount)
chainIdString := fmt.Sprintf("Chain ID: %s", chainID.String())

// Second column
rpcURLString := fmt.Sprintf("RPC URL: %s", rpcURL)
chainIdString := fmt.Sprintf("Chain ID: %s", chainID.String())

return formatParagraph(widget, []string{height, timeInfo, gasPriceString, peers, chainIdString, rpcURLString})
}

func GetTxPoolText(widget *widgets.Paragraph, pendingTxCount, queuedTxCount uint64) string {
pendingTx := fmt.Sprintf("Pending Tx: %d", pendingTxCount)
queuedTx := fmt.Sprintf("Queued Tx: %d", queuedTxCount)
return formatParagraph(widget, []string{pendingTx, queuedTx})
}

func GetZkEVMText(widget *widgets.Paragraph, trustedBatchesCount, virtualBatchesCount, verifiedBatchesCount uint64) string {
trustedBatches := fmt.Sprintf("Trusted: %d", trustedBatchesCount)
trustedVirtualBatchesGap := trustedBatchesCount - virtualBatchesCount
virtualBatches := fmt.Sprintf("Virtual: %d (%d)", virtualBatchesCount, trustedVirtualBatchesGap)

trustedVerifiedBatchesGap := trustedBatchesCount - verifiedBatchesCount
verifiedBatches := fmt.Sprintf("Verified: %d (%d)", verifiedBatchesCount, trustedVerifiedBatchesGap)
return formatParagraph(widget, []string{trustedBatches, virtualBatches, verifiedBatches})
}

info := []string{height, timeInfo, gasPriceString, peers, pendingTx, queuedTx, chainIdString, rpcURLString}
columns := len(info) / dy
if len(info)%dy != 0 {
func formatParagraph(widget *widgets.Paragraph, content []string) string {
dx := widget.Inner.Dx()
dy := widget.Inner.Dy()

// Return an appropriate message if dy is 0 or less.
if dy <= 0 {
return "Invalid display configuration."
}

columns := len(content) / dy
if len(content)%dy != 0 {
columns += 1 // Add an extra column for the remaining items
}

Expand All @@ -57,8 +80,8 @@ func GetCurrentBlockInfo(headBlock *big.Int, gasPrice *big.Int, peerCount uint64
for i := 0; i < columns; i++ {
for j := 0; j < dy; j++ {
index := i*dy + j
if index < len(info) && len(info[index]) > columnWidths[i] {
columnWidths[i] = len(info[index])
if index < len(content) && len(content[index]) > columnWidths[i] {
columnWidths[i] = len(content[index])
}
}
// Add padding and ensure it doesn't exceed 'dx'
Expand All @@ -72,9 +95,9 @@ func GetCurrentBlockInfo(headBlock *big.Int, gasPrice *big.Int, peerCount uint64
for i := 0; i < dy; i++ {
for j := 0; j < columns; j++ {
index := j*dy + i
if index < len(info) {
if index < len(content) {
formatString := fmt.Sprintf("%%-%ds", columnWidths[j])
formattedInfo.WriteString(fmt.Sprintf(formatString, info[index]))
formattedInfo.WriteString(fmt.Sprintf(formatString, content[index]))
}
}
formattedInfo.WriteString("\n")
Expand Down Expand Up @@ -449,7 +472,7 @@ func GetSimpleReceipt(ctx context.Context, rpc *ethrpc.Client, tx rpctypes.PolyT
return fields
}

func SetUISkeleton() (blockList *widgets.List, blockInfo *widgets.List, transactionList *widgets.List, transactionInformationList *widgets.List, transactionInfo *widgets.Table, grid *ui.Grid, selectGrid *ui.Grid, blockGrid *ui.Grid, transactionGrid *ui.Grid, termUi UiSkeleton) {
func SetUISkeleton(txPoolStatusSupported, zkEVMBatchesSupported bool) (blockList *widgets.List, blockInfo *widgets.List, transactionList *widgets.List, transactionInformationList *widgets.List, transactionInfo *widgets.Table, grid *ui.Grid, selectGrid *ui.Grid, blockGrid *ui.Grid, transactionGrid *ui.Grid, termUi UiSkeleton) {
// help := widgets.NewParagraph()
// help.Title = "Block Headers"
// help.Text = "Use the arrow keys to scroll through the transactions. Press <Esc> to go back to the explorer view"
Expand All @@ -470,8 +493,32 @@ func SetUISkeleton() (blockList *widgets.List, blockInfo *widgets.List, transact

termUi = UiSkeleton{}

// Top row
termUi.Current = widgets.NewParagraph()
termUi.Current.Title = "Current"
totalWidgets := 1

if txPoolStatusSupported {
termUi.TxPool = widgets.NewParagraph()
termUi.TxPool.Title = "TxPool"
totalWidgets++
}

if zkEVMBatchesSupported {
termUi.ZkEVM = widgets.NewParagraph()
termUi.ZkEVM.Title = "ZkEVM Batch No."
totalWidgets++
}

topRowBlocks := []interface{}{
ui.NewCol((5.0-float64(totalWidgets-1))/5.0, termUi.Current),
}
if txPoolStatusSupported {
topRowBlocks = append(topRowBlocks, ui.NewCol(1.0/5.0, termUi.TxPool))
}
if zkEVMBatchesSupported {
topRowBlocks = append(topRowBlocks, ui.NewCol(1.0/5.0, termUi.ZkEVM))
}

termUi.TxPerBlockChart = widgets.NewSparkline()
termUi.TxPerBlockChart.LineColor = ui.ColorRed
Expand Down Expand Up @@ -538,7 +585,7 @@ func SetUISkeleton() (blockList *widgets.List, blockInfo *widgets.List, transact
termUi.Receipts.WrapText = true

grid.Set(
ui.NewRow(1.0/10, termUi.Current),
ui.NewRow(1.0/10, topRowBlocks...),

ui.NewRow(2.0/10,
ui.NewCol(1.0/5, slg0),
Expand All @@ -558,7 +605,7 @@ func SetUISkeleton() (blockList *widgets.List, blockInfo *widgets.List, transact
)

selectGrid.Set(
ui.NewRow(1.0/10, termUi.Current),
ui.NewRow(1.0/10, topRowBlocks...),

ui.NewRow(2.0/10,
ui.NewCol(1.0/5, slg0),
Expand Down
40 changes: 40 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/cenkalti/backoff"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/clique"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
Expand Down Expand Up @@ -203,6 +204,45 @@ func GetTxPoolStatus(rpc *ethrpc.Client) (uint64, uint64, error) {
return pendingCount, queuedCount, nil
}

func GetZkEVMBatches(rpc *ethrpc.Client) (uint64, uint64, uint64, error) {
trustedBatches, err := getZkEVMBatch(rpc, trusted)
if err != nil {
return 0, 0, 0, err
}

virtualBatches, err := getZkEVMBatch(rpc, virtual)
if err != nil {
return trustedBatches, 0, 0, err
}

verifiedBatches, err := getZkEVMBatch(rpc, verified)
if err != nil {
return trustedBatches, virtualBatches, 0, err
}

return trustedBatches, virtualBatches, verifiedBatches, nil
}

type batch string

const (
trusted batch = "zkevm_batchNumber"
virtual batch = "zkevm_virtualBatchNumber"
verified batch = "zkevm_verifiedBatchNumber"
)

func getZkEVMBatch(rpc *ethrpc.Client, batchType batch) (uint64, error) {
var raw interface{}
if err := rpc.Call(&raw, string(batchType)); err != nil {
return 0, err
}
batch, err := hexutil.DecodeUint64(fmt.Sprintf("%v", raw))
if err != nil {
return 0, err
}
return batch, nil
}

func tryCastToUint64(val any) (uint64, error) {
switch t := val.(type) {
case float64:
Expand Down

0 comments on commit 0c533ee

Please sign in to comment.