Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions tools/analysis-check/cmd/analysis-check/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package main

import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"

"github.com/okx/xlayer-toolkit/tools/analysis-check/internal/analyzer"
"github.com/okx/xlayer-toolkit/tools/analysis-check/internal/rpc"
"github.com/urfave/cli/v2"
)

var (
rpcURLFlag = &cli.StringFlag{
Name: "rpc-url",
Usage: "RPC endpoint URL",
Required: true,
}
blockFlag = &cli.StringFlag{
Name: "block",
Usage: "Block number or hash to analyze (if not set, follows latest blocks)",
}
innerTxFlag = &cli.StringFlag{
Name: "innertx",
Usage: "Internal transaction fetch mode: 'block' or 'tx'",
Value: "block",
}
batchSizeFlag = &cli.IntFlag{
Name: "batch-size",
Usage: "Batch size for RPC requests",
Value: 200,
}
pollIntervalFlag = &cli.DurationFlag{
Name: "poll-interval",
Usage: "Polling interval for latest block mode",
Value: time.Second,
}
)

func main() {
app := &cli.App{
Name: "analysis-check",
Usage: "Simulate block analysis by fetching block data, receipts, internal transactions, and account states",
Flags: []cli.Flag{
rpcURLFlag,
blockFlag,
innerTxFlag,
batchSizeFlag,
pollIntervalFlag,
},
Action: run,
}

if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}

func run(c *cli.Context) error {
rpcURL := c.String("rpc-url")
blockArg := c.String("block")
innerTxMode := c.String("innertx")
batchSize := c.Int("batch-size")
pollInterval := c.Duration("poll-interval")

if innerTxMode != "block" && innerTxMode != "tx" {
return fmt.Errorf("invalid innertx mode: %s (must be 'block' or 'tx')", innerTxMode)
}

client, err := rpc.NewClient(rpcURL)
if err != nil {
return fmt.Errorf("create rpc client: %w", err)
}
defer client.Close()

a := analyzer.New(client, analyzer.Config{
InnerTxMode: innerTxMode,
BatchSize: batchSize,
})

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigCh
fmt.Println("\nShutting down...")
cancel()
}()

if blockArg != "" {
return runSingleBlock(ctx, a, blockArg)
}
return runLoop(ctx, client, a, pollInterval)
}

func runSingleBlock(ctx context.Context, a *analyzer.Analyzer, blockArg string) error {
num, hash, isHash, err := rpc.ParseBlockIdentifier(blockArg)
if err != nil {
return err
}

var result *analyzer.Result
if isHash {
result, err = a.AnalyzeBlockByHash(ctx, hash)
} else {
result, err = a.AnalyzeBlockByNumber(ctx, num)
}
if err != nil {
return fmt.Errorf("analyze block: %w", err)
}

printResult(result)
return nil
}

func runLoop(ctx context.Context, client *rpc.Client, a *analyzer.Analyzer, pollInterval time.Duration) error {
var lastProcessed uint64

ticker := time.NewTicker(pollInterval)
defer ticker.Stop()

for {
select {
case <-ctx.Done():
return nil
case <-ticker.C:
latestNum, err := client.GetBlockNumber(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "get block number: %v\n", err)
continue
}

if latestNum <= lastProcessed {
continue
}

for blockNum := lastProcessed + 1; blockNum <= latestNum; blockNum++ {
if lastProcessed == 0 {
blockNum = latestNum
}

select {
case <-ctx.Done():
return nil
default:
}

result, err := a.AnalyzeBlockByNumber(ctx, blockNum)
if err != nil {
fmt.Fprintf(os.Stderr, "analyze block %d: %v\n", blockNum, err)
continue
}

printResult(result)
lastProcessed = blockNum
}
}
}
}

func printResult(r *analyzer.Result) {
fmt.Printf("block=%d, hash=%s, txs=%d, accounts=%d, tokens=%d, innerTxs=%d, analysis elapse: %d ms\n",
r.BlockNumber, r.BlockHash.Hex(), r.TxCount, r.AccountCount, r.TokenCount, r.InnerTxCount, r.ElapsedMs)
}
40 changes: 40 additions & 0 deletions tools/analysis-check/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module github.com/okx/xlayer-toolkit/tools/analysis-check

go 1.23.0

require (
github.com/ethereum/go-ethereum v1.14.12
github.com/urfave/cli/v2 v2.27.5
)

require (
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/consensys/gnark-crypto v0.18.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/supranational/blst v0.3.15 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
)

replace github.com/ethereum/go-ethereum => github.com/okx/op-geth v0.0.10
Loading