Skip to content

Commit 83846fd

Browse files
authored
Merge pull request #20 from optimism-java/multi-rpcurls
mutiple rpc url suppot and fix sync move state from front
2 parents 7b0b6eb + 8fd3b34 commit 83846fd

25 files changed

Lines changed: 830 additions & 219 deletions

.env.example

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Environment Variables Configuration Example
2+
3+
# ============= RPC Configuration =============
4+
# Multi-RPC Configuration - Use comma-separated RPC nodes for load balancing and fault tolerance
5+
#
6+
# Benefits of Multi-RPC Setup:
7+
# - Load Distribution: Requests distributed across multiple endpoints
8+
# - High Availability: Continues working if one RPC fails
9+
# - Rate Limit Mitigation: Avoids hitting individual provider limits
10+
# - Performance: Potential response time improvements
11+
# - Cost Optimization: Distribute load across free/paid tiers
12+
#
13+
# Configuration Format: Use comma-separated URLs (no spaces)
14+
# Example: "url1,url2,url3"
15+
16+
# L1 RPC node list (Ethereum mainnet or testnet)
17+
L1_RPC_URLS="https://eth-sepolia.g.alchemy.com/v2/your_key1,https://rpc.ankr.com/eth_sepolia,https://sepolia.infura.io/v3/your_key2"
18+
19+
# L2 RPC node list (Optimism network)
20+
L2_RPC_URLS="https://opt-sepolia.g.alchemy.com/v2/your_key1,https://optimism-sepolia.gateway.pokt.network/v1/lb/your_key2"
21+
22+
# Node RPC node list (OP Node endpoints for rollup operations)
23+
NODE_RPC_URLS="https://light-radial-slug.optimism-sepolia.quiknode.pro/e9329f699b371572a8cc5dd22d19d5940bb842a5/,https://node2.optimism-sepolia.quiknode.pro/your_key2/"
24+
25+
# Single RPC Configuration (Legacy support):
26+
# If you prefer single endpoints, just provide one URL:
27+
# L1_RPC_URLS="https://eth-sepolia.g.alchemy.com/v2/your_key"
28+
29+
# ============= RPC Rate Limiting =============
30+
# RPC request rate limit (requests per second)
31+
RPC_RATE_LIMIT=5
32+
# RPC request burst limit
33+
RPC_RATE_BURST=2
34+
35+
# ============= Logging Configuration =============
36+
# Log level: debug, info, warn, error, panic, fatal
37+
LOG_LEVEL=info
38+
# Log format: console, json
39+
LOG_FORMAT=console
40+
41+
# ============= Database Configuration =============
42+
MYSQL_DATA_SOURCE=root:123456@tcp(127.0.0.1:3306)/dispute_explorer?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true
43+
MYSQL_MAX_IDLE_CONNS=10
44+
MYSQL_MAX_OPEN_CONNS=20
45+
MYSQL_CONN_MAX_LIFETIME=3600
46+
47+
# ============= Blockchain Configuration =============
48+
# Blockchain network name
49+
BLOCKCHAIN=sepolia
50+
51+
# Starting block number (ensure no games are missed)
52+
FROM_BLOCK_NUMBER=5515562
53+
# Starting block hash
54+
FROM_BLOCK_HASH=0x5205c17557759edaef9120f56af802aeaa2827a60d674a0413e77e9c515bdfba
55+
56+
# Dispute Game Factory Proxy contract address
57+
DISPUTE_GAME_PROXY_CONTRACT=0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1
58+
59+
# ============= API Configuration =============
60+
# API server port
61+
API_PORT=8088

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ MYSQL_DATA_SOURCE=<data-source>
3737
BLOCKCHAIN=<block-chain-name>
3838
3939
# l1 rpc url example: eth json rpc url
40-
L1_RPC_URL=<l1-rpc>
40+
L1_RPC_URLS=<l1-rpc>
4141
# l2 rpc url example: op json rpc url
42-
L2_RPC_URL=<l2-rpc>
42+
L2_RPC_URLS=<l2-rpc>
4343
44-
NODE_RPCURL=<op-node-rpc>
44+
NODE_RPCURLS=<op-node-rpc>
4545
4646
RPC_RATE_LIMIT=15
4747
RPC_RATE_BURST=5
Binary file not shown.

internal/api/dispute_game_handler.go

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import (
88
"net/http"
99

1010
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
11-
"github.com/ethereum-optimism/optimism/op-service/sources" // 新增导入
11+
"github.com/ethereum-optimism/optimism/op-service/client"
12+
"github.com/ethereum-optimism/optimism/op-service/sources"
1213
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1314
"github.com/ethereum/go-ethereum/common"
14-
"github.com/ethereum/go-ethereum/ethclient"
15+
gethlog "github.com/ethereum/go-ethereum/log"
1516
config "github.com/optimism-java/dispute-explorer/internal/types"
1617
"github.com/optimism-java/dispute-explorer/pkg/contract"
18+
"github.com/optimism-java/dispute-explorer/pkg/rpc"
1719

1820
"github.com/spf13/cast"
1921

@@ -24,20 +26,16 @@ import (
2426
)
2527

2628
type DisputeGameHandler struct {
27-
Config *config.Config
28-
DB *gorm.DB
29-
L1RPC *ethclient.Client
30-
L2RPC *ethclient.Client
31-
RollupClient *sources.RollupClient // 新增字段
29+
Config *config.Config
30+
DB *gorm.DB
31+
RPCManager *rpc.Manager
3232
}
3333

34-
func NewDisputeGameHandler(db *gorm.DB, l1rpc *ethclient.Client, l2rpc *ethclient.Client, config *config.Config, rollupClient *sources.RollupClient) *DisputeGameHandler {
34+
func NewDisputeGameHandler(db *gorm.DB, rpcManager *rpc.Manager, config *config.Config) *DisputeGameHandler {
3535
return &DisputeGameHandler{
36-
DB: db,
37-
L1RPC: l1rpc,
38-
L2RPC: l2rpc,
39-
Config: config,
40-
RollupClient: rollupClient, // 新增赋值
36+
DB: db,
37+
RPCManager: rpcManager,
38+
Config: config,
4139
}
4240
}
4341

@@ -299,7 +297,17 @@ func (h DisputeGameHandler) getClaimRoot(blockNumber int64) (string, error) {
299297
return "", fmt.Errorf("block number cannot be negative: %d", blockNumber)
300298
}
301299

302-
output, err := h.RollupClient.OutputAtBlock(context.Background(), uint64(blockNumber))
300+
// 使用RPCManager获取Node RPC URL,实现轮询
301+
nodeRPCURL := h.RPCManager.GetNodeRPCURL()
302+
303+
// 创建RollupClient(每次使用不同的Node RPC)
304+
rpcClient, err := client.NewRPC(context.Background(), gethlog.New(), nodeRPCURL)
305+
if err != nil {
306+
return "", fmt.Errorf("failed to connect to node RPC %s: %w", nodeRPCURL, err)
307+
}
308+
rollupClient := sources.NewRollupClient(rpcClient)
309+
310+
output, err := rollupClient.OutputAtBlock(context.Background(), uint64(blockNumber))
303311
if err != nil {
304312
return "", fmt.Errorf("failed to get output at block %d: %w", blockNumber, err)
305313
}
@@ -340,7 +348,9 @@ func (h DisputeGameHandler) GetGamesClaimByPosition(c *gin.Context) {
340348
}
341349

342350
func (h DisputeGameHandler) gamesClaimByPosition(req *CalculateClaim) (string, error) {
343-
newDisputeGame, err := contract.NewDisputeGame(common.HexToAddress(req.DisputeGame), h.L1RPC)
351+
// 获取L1客户端从RPCManager
352+
l1Client := h.RPCManager.GetRawClient(true)
353+
newDisputeGame, err := contract.NewDisputeGame(common.HexToAddress(req.DisputeGame), l1Client)
344354
if err != nil {
345355
return "", err
346356
}

internal/handler/frontend_move.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ func (h *FrontendMoveHandler) RecordFrontendMove(req *FrontendMoveRequest) error
8282
DisputedClaim: req.DisputedClaim,
8383
Status: schema.FrontendMoveStatusPending,
8484
SubmittedAt: time.Now().Unix(),
85+
IsSynced: false, // 新记录默认未同步
8586
}
8687

8788
// Save to database
@@ -106,8 +107,9 @@ func (h *FrontendMoveHandler) monitorTransactionStatus(recordID int64, txHash st
106107
for i := 0; i < maxRetries; i++ {
107108
time.Sleep(retryInterval)
108109

109-
// Query transaction status
110-
receipt, err := h.svc.L1RPC.TransactionReceipt(context.Background(), common.HexToHash(txHash))
110+
// Query transaction status using RPCManager
111+
l1Client := h.svc.RPCManager.GetRawClient(true)
112+
receipt, err := l1Client.TransactionReceipt(context.Background(), common.HexToHash(txHash))
111113
if err != nil {
112114
log.Debugf("[FrontendMoveHandler] Transaction %s not yet mined or error: %v", txHash, err)
113115
continue

internal/handler/handler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ func Run(ctx *svc.ServiceContext) {
2525
go CalculateLostBond(ctx)
2626
// sync claim len
2727
go SyncClaimDataLen(ctx)
28+
// sync frontend move transactions
29+
go SyncFrontendMoveTransactions(ctx)
2830
}
2931

3032
// startRPCMonitoring starts RPC monitoring (internal function)

internal/handler/latestBlockNumber.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func LatestBlackNumber(ctx *svc.ServiceContext) {
2121
}
2222

2323
ctx.LatestBlockNumber = cast.ToInt64(latest)
24-
log.Infof("[Handler.LatestBlackNumber] Latest block number: %d (via RPC Manager)\n", latest)
24+
log.Debugf("[Handler.LatestBlackNumber] Latest block number: %d (via RPC Manager)\n", latest)
2525
time.Sleep(12 * time.Second)
2626
}
2727
}

internal/handler/logFilter.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func LogFilter(ctx *svc.ServiceContext, block schema.SyncBlock, addresses []comm
2929
if err != nil {
3030
return nil, errors.WithStack(err)
3131
}
32-
log.Infof("[LogFilter] Event logs length is %d, block number is %d (via RPC Manager)\n", len(logs), block.BlockNumber)
32+
log.Debugf("[LogFilter] Event logs length is %d, block number is %d (via RPC Manager)\n", len(logs), block.BlockNumber)
3333
return LogsToEvents(ctx, logs, block.ID)
3434
}
3535

@@ -41,14 +41,14 @@ func LogsToEvents(ctx *svc.ServiceContext, logs []types.Log, syncBlockID int64)
4141
contractAddress := vlog.Address
4242
Event := blockchain.GetEvent(eventHash)
4343
if Event == nil {
44-
log.Infof("[LogsToEvents] logs[txHash: %s, contractAddress:%s, eventHash: %s]\n", vlog.TxHash, strings.ToLower(contractAddress.Hex()), eventHash)
44+
log.Debugf("[LogsToEvents] logs[txHash: %s, contractAddress:%s, eventHash: %s]\n", vlog.TxHash, strings.ToLower(contractAddress.Hex()), eventHash)
4545
continue
4646
}
4747

4848
blockTime := blockTimes[cast.ToInt64(vlog.BlockNumber)]
4949
if blockTime == 0 {
5050
blockNumber := cast.ToInt64(vlog.BlockNumber)
51-
log.Infof("[LogsToEvents] Fetching block info for block number: %d, txHash: %s", blockNumber, vlog.TxHash.Hex())
51+
log.Debugf("[LogsToEvents] Fetching block info for block number: %d, txHash: %s", blockNumber, vlog.TxHash.Hex())
5252

5353
// Use unified RPC manager to get block (automatically applies rate limiting)
5454
block, err := ctx.RPCManager.GetBlockByNumber(context.Background(), big.NewInt(blockNumber), true) // true indicates L1

internal/handler/rpc_manager_migration.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func LatestBlockNumberWithRateLimit(ctx *svc.ServiceContext) {
2424
}
2525

2626
ctx.LatestBlockNumber = cast.ToInt64(latest)
27-
log.Infof("[Handler.LatestBlockNumberWithRateLimit] Latest block number: %d (using RPC Manager)\n", latest)
27+
log.Debugf("[Handler.LatestBlockNumberWithRateLimit] Latest block number: %d (using RPC Manager)\n", latest)
2828
time.Sleep(12 * time.Second)
2929
}
3030
}

internal/handler/syncBlock.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ func SyncBlock(ctx *svc.ServiceContext) {
3131
ctx.SyncedBlockHash = common.HexToHash(syncedBlock.BlockHash)
3232
}
3333

34-
log.Infof("[Handler.SyncBlock]SyncedBlockNumber: %d", ctx.SyncedBlockNumber)
35-
log.Infof("[Handler.SyncBlock]SyncedBlockHash:%s", ctx.SyncedBlockHash.String())
34+
log.Debugf("[Handler.SyncBlock]SyncedBlockNumber: %d", ctx.SyncedBlockNumber)
35+
log.Debugf("[Handler.SyncBlock]SyncedBlockHash:%s", ctx.SyncedBlockHash.String())
3636

3737
for {
3838
// Check pending blocks count before syncing new blocks
@@ -52,7 +52,7 @@ func SyncBlock(ctx *svc.ServiceContext) {
5252
}
5353

5454
syncingBlockNumber := ctx.SyncedBlockNumber + 1
55-
log.Infof("[Handler.SyncBlock] Try to sync block number: %d\n", syncingBlockNumber)
55+
log.Debugf("[Handler.SyncBlock] Try to sync block number: %d\n", syncingBlockNumber)
5656

5757
if syncingBlockNumber > ctx.LatestBlockNumber {
5858
time.Sleep(3 * time.Second)
@@ -68,7 +68,7 @@ func SyncBlock(ctx *svc.ServiceContext) {
6868
continue
6969
}
7070
block := rpc.ParseJSONBlock(string(blockJSON))
71-
log.Infof("[Handler.SyncBlock] Syncing block number: %d, hash: %v, parent hash: %v (via RPC Manager)\n", block.Number(), block.Hash(), block.ParentHash())
71+
log.Debugf("[Handler.SyncBlock] Syncing block number: %d, hash: %v, parent hash: %v (via RPC Manager)\n", block.Number(), block.Hash(), block.ParentHash())
7272

7373
if common.HexToHash(block.ParentHash()) != ctx.SyncedBlockHash {
7474
log.Errorf("[Handler.SyncBlock] ParentHash of the block being synchronized is inconsistent: %s \n", ctx.SyncedBlockHash)
@@ -104,7 +104,7 @@ func rollbackBlock(ctx *svc.ServiceContext) {
104104
for {
105105
rollbackBlockNumber := ctx.SyncedBlockNumber
106106

107-
log.Infof("[Handler.SyncBlock.RollBackBlock] Try to rollback block number: %d\n", rollbackBlockNumber)
107+
log.Debugf("[Handler.SyncBlock.RollBackBlock] Try to rollback block number: %d\n", rollbackBlockNumber)
108108

109109
// use unified RPC manager for rollback operation (automatically applies rate limiting)
110110
requestBody := "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBlockByNumber\",\"params\":[\"" + fmt.Sprintf("0x%X", rollbackBlockNumber) + "\", true],\"id\":1}"

0 commit comments

Comments
 (0)