From 155af40dc9d80d0d7bffd078727b86b0540f3aa2 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:06:59 +0800 Subject: [PATCH 01/11] add move front --- .env | 11 - .gitignore | 4 +- MIGRATION_STATUS.md | 140 ++++++++++ RPC_MANAGER_GUIDE.md | 216 ++++++++++++++++ internal/api/frontend_move_api.go | 243 ++++++++++++++++++ internal/handler/frontend_move.go | 232 +++++++++++++++++ internal/schema/frontend_move_transaction.go | 29 +++ internal/schema/sync_blocks.go | 21 +- internal/schema/sync_events.go | 1 + main.go | 9 + migration/version/migration_version.go | 2 + .../version/v8/add_frontend_move_tracking.go | 66 +++++ 12 files changed, 951 insertions(+), 23 deletions(-) delete mode 100644 .env create mode 100644 MIGRATION_STATUS.md create mode 100644 RPC_MANAGER_GUIDE.md create mode 100644 internal/api/frontend_move_api.go create mode 100644 internal/handler/frontend_move.go create mode 100644 internal/schema/frontend_move_transaction.go create mode 100644 migration/version/v8/add_frontend_move_tracking.go diff --git a/.env b/.env deleted file mode 100644 index f71361e..0000000 --- a/.env +++ /dev/null @@ -1,11 +0,0 @@ -LOG_FORMAT=console -MYSQL_DATA_SOURCE=root:root@tcp(mysql:3306)/dispute_explorer?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true -BLOCKCHAIN=seplia -L1_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/lV2e-64nNnEMUA7UG0IT0uwjzlxEI512 -L2_RPC_URL=https://opt-sepolia.g.alchemy.com/v2/FPgbOkDCgG8t0ppZ6TwZXLucr1wl_us4 -RPC_RATE_LIMIT=15 -RPC_RATE_BURST=5 -FROM_BLOCK_NUMBER=5515562 -FROM_BLOCK_HASH=0x5205c17557759edaef9120f56af802aeaa2827a60d674a0413e77e9c515bdfba -DISPUTE_GAME_PROXY_CONTRACT=0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1 -API_PORT=8080 diff --git a/.gitignore b/.gitignore index f943960..c9176e4 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,6 @@ mysql1 meili_data postgres mysql -MIGRATION_STATUS.md -RPC_MANAGER_GUIDE.md +.env +FRONTEND_MOVE_API.md diff --git a/MIGRATION_STATUS.md b/MIGRATION_STATUS.md new file mode 100644 index 0000000..47a58f4 --- /dev/null +++ b/MIGRATION_STATUS.md @@ -0,0 +1,140 @@ +# RPC速率限制配置建议 + +## 当前状态 +✅ **已完成的迁移** +- `syncBlock.go` - 区块同步调用已迁移到RPC Manager +- `latestBlockNumber.go` - 最新区块号获取已迁移到RPC Manager +- `logFilter.go` - 日志过滤和区块查询已迁移到RPC Manager +- `handler.go` - 已启用RPC监控 + +## 配置建议 + +### 开发环境配置 +```bash +# 开发环境 - 保守的速率限制 +export RPC_RATE_LIMIT=5 # 每秒5个请求 +export RPC_RATE_BURST=2 # 允许突发2个请求 +``` + +### 测试环境配置 +```bash +# 测试环境 - 中等速率限制 +export RPC_RATE_LIMIT=10 # 每秒10个请求 +export RPC_RATE_BURST=5 # 允许突发5个请求 +``` + +### 生产环境配置 +```bash +# 生产环境 - 根据RPC提供商限制调整 +export RPC_RATE_LIMIT=25 # 每秒25个请求 +export RPC_RATE_BURST=10 # 允许突发10个请求 +``` + +## 监控和告警 + +### 查看RPC统计 +启动应用后,你会在日志中看到类似这样的监控信息: +``` +[RPC Stats] L1: 150 requests (0 limited), L2: 45 requests (0 limited), HTTP: 95 +[RPC Limits] L1: 25.0/s (burst 10, tokens 8.50), L2: 25.0/s (burst 10, tokens 9.20) +``` + +### 关键指标说明 +- **requests** - 总请求数 +- **limited** - 被速率限制拒绝的请求数 +- **tokens** - 当前可用的令牌数(越低表示使用越频繁) + +### 告警条件 +- ⚠️ `limited > 0` - 有请求被限制,需要关注 +- 🚨 `tokens < 1.0` - 令牌即将耗尽,需要立即调整 +- 📊 `requests增长过快` - 可能需要优化代码逻辑 + +## 优化建议 + +### 1. 根据使用模式调整限制 +```bash +# 如果经常看到 "limited" 请求,可以适当提高限制 +export RPC_RATE_LIMIT=30 +export RPC_RATE_BURST=15 + +# 如果tokens经常很低,可以降低请求频率或提高限制 +``` + +### 2. 分时段配置 +```bash +# 可以在应用中实现动态调整 +# 高峰期降低限制 +ctx.RpcManager.UpdateRateLimit(15, 5, true) # L1 + +# 低峰期提高限制 +ctx.RpcManager.UpdateRateLimit(35, 15, true) # L1 +``` + +### 3. 错误处理优化 +当前迁移的代码已经包含了错误处理,但可以进一步优化: + +```go +// 示例:在syncBlock.go中添加重试逻辑 +for retries := 0; retries < 3; retries++ { + blockJSON, err := ctx.RpcManager.HTTPPostJSON(context.Background(), requestBody, true) + if err != nil { + if strings.Contains(err.Error(), "rate limit exceeded") { + // 指数退避 + time.Sleep(time.Duration(1< 0 { + page = p + } + } + if sizeStr := c.Query("size"); sizeStr != "" { + if s, err := strconv.Atoi(sizeStr); err == nil && s > 0 && s <= 100 { + size = s + } + } + + moves, total, err := api.handler.GetFrontendMovesByGame(gameContract, page, size) + if err != nil { + log.Errorf("[FrontendMoveAPI] Failed to get frontend moves: %v", err) + c.JSON(http.StatusInternalServerError, FrontendMovesResponse{ + Success: false, + }) + return + } + + c.JSON(http.StatusOK, FrontendMovesResponse{ + Success: true, + Data: moves, + Total: total, + Page: page, + Size: size, + }) +} + +// @Summary Get frontend move by transaction hash +// @schemes +// @Description Get frontend move transaction details by transaction hash +// @Accept json +// @Produce json +// @Param txhash path string true "Transaction hash" +// @Success 200 {object} FrontendMoveDetailResponse +// @Router /disputegames/frontend-move/:txhash [get] +func (api *FrontendMoveAPI) GetMoveByTxHash(c *gin.Context) { + txHash := c.Param("txhash") + if txHash == "" { + c.JSON(http.StatusBadRequest, FrontendMoveDetailResponse{ + Success: false, + Message: "Transaction hash is required", + }) + return + } + + move, err := api.handler.GetFrontendMoveByTxHash(txHash) + if err != nil { + log.Errorf("[FrontendMoveAPI] Failed to get frontend move: %v", err) + statusCode := http.StatusInternalServerError + if err == gorm.ErrRecordNotFound { + statusCode = http.StatusNotFound + } + c.JSON(statusCode, FrontendMoveDetailResponse{ + Success: false, + Message: err.Error(), + }) + return + } + + c.JSON(http.StatusOK, FrontendMoveDetailResponse{ + Success: true, + Data: move, + }) +} + +// @Summary Get dispute games with frontend move flag +// @schemes +// @Description Get all dispute games with information about whether they contain frontend-initiated moves +// @Accept json +// @Produce json +// @Param page query int false "Page number (default: 1)" +// @Param size query int false "Page size (default: 10)" +// @Param frontend_only query bool false "Only show games with frontend moves" +// @Success 200 +// @Router /disputegames/with-frontend-flag [get] +func (api *FrontendMoveAPI) GetGamesWithFrontendFlag(c *gin.Context) { + // Parse pagination parameters + page := 1 + size := 10 + if pageStr := c.Query("page"); pageStr != "" { + if p, err := strconv.Atoi(pageStr); err == nil && p > 0 { + page = p + } + } + if sizeStr := c.Query("size"); sizeStr != "" { + if s, err := strconv.Atoi(sizeStr); err == nil && s > 0 && s <= 100 { + size = s + } + } + + frontendOnly := c.Query("frontend_only") == "true" + + // Build query + query := api.handler.GetServiceContext().DB.Model(&schema.DisputeGame{}). + Select("dispute_games.*, COALESCE(frontend_stats.has_frontend_move, false) as has_frontend_move"). + Joins(`LEFT JOIN ( + SELECT game_contract, true as has_frontend_move + FROM frontend_move_transactions + WHERE status = ? + GROUP BY game_contract + ) frontend_stats ON dispute_games.game_contract = frontend_stats.game_contract`, schema.FrontendMoveStatusConfirmed) + + if frontendOnly { + query = query.Where("frontend_stats.has_frontend_move = true") + } + + var total int64 + err := query.Count(&total).Error + if err != nil { + log.Errorf("[FrontendMoveAPI] Failed to count games: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{ + "success": false, + "message": "Failed to count games", + }) + return + } + + var games []map[string]interface{} + offset := (page - 1) * size + err = query.Offset(offset).Limit(size).Order("created_at DESC").Find(&games).Error + if err != nil { + log.Errorf("[FrontendMoveAPI] Failed to get games: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{ + "success": false, + "message": "Failed to get games", + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "success": true, + "data": games, + "total": total, + "page": page, + "size": size, + }) +} diff --git a/internal/handler/frontend_move.go b/internal/handler/frontend_move.go new file mode 100644 index 0000000..c5404b3 --- /dev/null +++ b/internal/handler/frontend_move.go @@ -0,0 +1,232 @@ +package handler + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/optimism-java/dispute-explorer/internal/schema" + "github.com/optimism-java/dispute-explorer/internal/svc" + "github.com/optimism-java/dispute-explorer/pkg/log" + "gorm.io/gorm" +) + +// FrontendMoveRequest request structure for frontend-initiated move transactions +type FrontendMoveRequest struct { + GameContract string `json:"game_contract" binding:"required"` // Dispute Game contract address + TxHash string `json:"tx_hash" binding:"required"` // Transaction hash + Claimant string `json:"claimant" binding:"required"` // Initiator address + ParentIndex string `json:"parent_index" binding:"required"` // Parent index + Claim string `json:"claim" binding:"required"` // Claim data + IsAttack bool `json:"is_attack"` // Whether it's an attack + ChallengeIndex string `json:"challenge_index" binding:"required"` // Challenge index + DisputedClaim string `json:"disputed_claim" binding:"required"` // Disputed claim +} + +// FrontendMoveHandler handles frontend-initiated move transactions +type FrontendMoveHandler struct { + svc *svc.ServiceContext +} + +// NewFrontendMoveHandler creates a new FrontendMoveHandler +func NewFrontendMoveHandler(svc *svc.ServiceContext) *FrontendMoveHandler { + return &FrontendMoveHandler{ + svc: svc, + } +} + +// GetServiceContext gets ServiceContext +func (h *FrontendMoveHandler) GetServiceContext() *svc.ServiceContext { + return h.svc +} + +// RecordFrontendMove records frontend-initiated move transactions +func (h *FrontendMoveHandler) RecordFrontendMove(ctx context.Context, req *FrontendMoveRequest) error { + // Validate contract address format + if !common.IsHexAddress(req.GameContract) { + return fmt.Errorf("invalid game contract address: %s", req.GameContract) + } + + // Validate transaction hash format + if len(req.TxHash) != 66 || !strings.HasPrefix(req.TxHash, "0x") { + return fmt.Errorf("invalid transaction hash format: %s", req.TxHash) + } + + // Validate initiator address format + if !common.IsHexAddress(req.Claimant) { + return fmt.Errorf("invalid claimant address: %s", req.Claimant) + } + + // Check if this transaction has already been recorded + var existingRecord schema.FrontendMoveTransaction + err := h.svc.DB.Where("tx_hash = ?", req.TxHash).First(&existingRecord).Error + if err == nil { + log.Warnf("[FrontendMoveHandler] Transaction %s already recorded", req.TxHash) + return fmt.Errorf("transaction %s already recorded", req.TxHash) + } else if err != gorm.ErrRecordNotFound { + return fmt.Errorf("failed to check existing record: %v", err) + } + + // Create new record + frontendMove := &schema.FrontendMoveTransaction{ + GameContract: req.GameContract, + TxHash: req.TxHash, + Claimant: req.Claimant, + ParentIndex: req.ParentIndex, + Claim: req.Claim, + IsAttack: req.IsAttack, + ChallengeIndex: req.ChallengeIndex, + DisputedClaim: req.DisputedClaim, + Status: schema.FrontendMoveStatusPending, + SubmittedAt: time.Now().Unix(), + } + + // Save to database + err = h.svc.DB.Create(frontendMove).Error + if err != nil { + return fmt.Errorf("failed to save frontend move record: %v", err) + } + + log.Infof("[FrontendMoveHandler] Recorded frontend move transaction: %s for game: %s", req.TxHash, req.GameContract) + + // Asynchronously check transaction status + go h.monitorTransactionStatus(frontendMove.ID, req.TxHash) + + return nil +} + +// monitorTransactionStatus monitors transaction status +func (h *FrontendMoveHandler) monitorTransactionStatus(recordID int64, txHash string) { + maxRetries := 60 // Maximum 60 retries, 10 seconds interval each + retryInterval := 10 * time.Second + + for i := 0; i < maxRetries; i++ { + time.Sleep(retryInterval) + + // Query transaction status + receipt, err := h.svc.L1RPC.TransactionReceipt(context.Background(), common.HexToHash(txHash)) + if err != nil { + log.Debugf("[FrontendMoveHandler] Transaction %s not yet mined or error: %v", txHash, err) + continue + } + + // Update record status + var status string + var confirmedAt int64 + if receipt.Status == 1 { + status = schema.FrontendMoveStatusConfirmed + confirmedAt = time.Now().Unix() + } else { + status = schema.FrontendMoveStatusFailed + } + + err = h.svc.DB.Model(&schema.FrontendMoveTransaction{}). + Where("id = ?", recordID). + Updates(map[string]interface{}{ + "status": status, + "block_number": receipt.BlockNumber.Int64(), + "confirmed_at": confirmedAt, + }).Error + + if err != nil { + log.Errorf("[FrontendMoveHandler] Failed to update transaction status for %s: %v", txHash, err) + return + } + + log.Infof("[FrontendMoveHandler] Transaction %s status updated to %s", txHash, status) + + // If transaction is successful, mark related records + if receipt.Status == 1 { + h.markRelatedRecords(txHash, receipt.BlockNumber.Int64()) + } + + return + } + + // If timeout without finding transaction, mark as failed + err := h.svc.DB.Model(&schema.FrontendMoveTransaction{}). + Where("id = ?", recordID). + Updates(map[string]interface{}{ + "status": schema.FrontendMoveStatusFailed, + "error_message": "Transaction timeout", + }).Error + + if err != nil { + log.Errorf("[FrontendMoveHandler] Failed to update timeout status for %s: %v", txHash, err) + } + + log.Warnf("[FrontendMoveHandler] Transaction %s monitoring timeout", txHash) +} + +// markRelatedRecords marks related block and event records +func (h *FrontendMoveHandler) markRelatedRecords(txHash string, blockNumber int64) { + // Mark related blocks + err := h.svc.DB.Model(&schema.SyncBlock{}). + Where("block_number = ?", blockNumber). + Update("has_frontend_move", true).Error + if err != nil { + log.Errorf("[FrontendMoveHandler] Failed to mark block %d for tx %s: %v", blockNumber, txHash, err) + } + + // Mark related events + err = h.svc.DB.Model(&schema.SyncEvent{}). + Where("tx_hash = ?", txHash). + Update("is_from_frontend", true).Error + if err != nil { + log.Errorf("[FrontendMoveHandler] Failed to mark events for tx %s: %v", txHash, err) + } + + log.Infof("[FrontendMoveHandler] Marked related records for tx %s in block %d", txHash, blockNumber) +} + +// GetFrontendMovesByGame gets frontend-initiated move transactions for specified game +func (h *FrontendMoveHandler) GetFrontendMovesByGame(gameContract string, page, size int) ([]schema.FrontendMoveTransaction, int64, error) { + var moves []schema.FrontendMoveTransaction + var total int64 + + // Validate contract address format + if !common.IsHexAddress(gameContract) { + return nil, 0, fmt.Errorf("invalid game contract address: %s", gameContract) + } + + // Get total count + err := h.svc.DB.Model(&schema.FrontendMoveTransaction{}). + Where("game_contract = ?", gameContract). + Count(&total).Error + if err != nil { + return nil, 0, fmt.Errorf("failed to count frontend moves: %v", err) + } + + // Get paginated data + offset := (page - 1) * size + err = h.svc.DB.Where("game_contract = ?", gameContract). + Order("created_at DESC"). + Offset(offset). + Limit(size). + Find(&moves).Error + if err != nil { + return nil, 0, fmt.Errorf("failed to get frontend moves: %v", err) + } + + return moves, total, nil +} + +// GetFrontendMoveByTxHash gets frontend-initiated move transaction by transaction hash +func (h *FrontendMoveHandler) GetFrontendMoveByTxHash(txHash string) (*schema.FrontendMoveTransaction, error) { + if len(txHash) != 66 || !strings.HasPrefix(txHash, "0x") { + return nil, fmt.Errorf("invalid transaction hash format: %s", txHash) + } + + var move schema.FrontendMoveTransaction + err := h.svc.DB.Where("tx_hash = ?", txHash).First(&move).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, fmt.Errorf("frontend move not found for tx: %s", txHash) + } + return nil, fmt.Errorf("failed to get frontend move: %v", err) + } + + return &move, nil +} diff --git a/internal/schema/frontend_move_transaction.go b/internal/schema/frontend_move_transaction.go new file mode 100644 index 0000000..36a5d88 --- /dev/null +++ b/internal/schema/frontend_move_transaction.go @@ -0,0 +1,29 @@ +package schema + +const ( + FrontendMoveStatusPending = "pending" + FrontendMoveStatusConfirmed = "confirmed" + FrontendMoveStatusFailed = "failed" +) + +// FrontendMoveTransaction records move transaction information initiated from frontend +type FrontendMoveTransaction struct { + Base + GameContract string `json:"game_contract" gorm:"index:idx_game_contract"` // Dispute Game contract address + TxHash string `json:"tx_hash" gorm:"type:varchar(128);uniqueIndex:idx_tx_hash"` // Transaction hash + Claimant string `json:"claimant" gorm:"type:varchar(128)"` // Initiator address + ParentIndex string `json:"parent_index"` // Parent index + Claim string `json:"claim"` // Claim data + IsAttack bool `json:"is_attack"` // Whether it's an attack + ChallengeIndex string `json:"challenge_index"` // Challenge index + DisputedClaim string `json:"disputed_claim"` // Disputed claim + BlockNumber int64 `json:"block_number"` // Block number + Status string `json:"status" gorm:"type:varchar(20);default:pending"` // Status + ErrorMessage string `json:"error_message,omitempty"` // Error message (if any) + SubmittedAt int64 `json:"submitted_at"` // Submission timestamp + ConfirmedAt int64 `json:"confirmed_at,omitempty"` // Confirmation timestamp +} + +func (FrontendMoveTransaction) TableName() string { + return "frontend_move_transactions" +} diff --git a/internal/schema/sync_blocks.go b/internal/schema/sync_blocks.go index ecc8d81..95a898d 100644 --- a/internal/schema/sync_blocks.go +++ b/internal/schema/sync_blocks.go @@ -9,16 +9,17 @@ const ( type SyncBlock struct { Base - Blockchain string `json:"blockchain"` - Miner string `json:"miner"` - BlockTime int64 `json:"block_time"` - BlockNumber int64 `json:"block_number"` - BlockHash string `json:"block_hash"` - TxCount int64 `json:"tx_count"` - EventCount int64 `json:"event_count"` - ParentHash string `json:"parent_hash"` - Status string `json:"status"` - CheckCount int64 `json:"check_count"` + Blockchain string `json:"blockchain"` + Miner string `json:"miner"` + BlockTime int64 `json:"block_time"` + BlockNumber int64 `json:"block_number"` + BlockHash string `json:"block_hash"` + TxCount int64 `json:"tx_count"` + EventCount int64 `json:"event_count"` + ParentHash string `json:"parent_hash"` + Status string `json:"status"` + CheckCount int64 `json:"check_count"` + HasFrontendMove bool `json:"has_frontend_move" gorm:"default:false"` // Whether contains frontend-initiated move transactions } func (SyncBlock) TableName() string { diff --git a/internal/schema/sync_events.go b/internal/schema/sync_events.go index 79c94cb..d08ae91 100644 --- a/internal/schema/sync_events.go +++ b/internal/schema/sync_events.go @@ -23,6 +23,7 @@ type SyncEvent struct { Data string `json:"data"` Status string `json:"status"` RetryCount int64 `json:"retry_count"` + IsFromFrontend bool `json:"is_from_frontend" gorm:"default:false"` // Whether initiated from frontend } func (SyncEvent) TableName() string { diff --git a/main.go b/main.go index 98f7aee..f3fc9a0 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,9 @@ func main() { log.Info("listener running...\n") router := gin.Default() disputeGameHandler := api.NewDisputeGameHandler(sCtx.DB, sCtx.L1RPC, sCtx.L2RPC, cfg) + + // 新增:前端 Move 交易处理器 + frontendMoveAPI := api.NewFrontendMoveAPI(sCtx) docs.SwaggerInfo.Title = "Dispute Game Swagger API" docs.SwaggerInfo.Description = "This is a dispute-explorer server." docs.SwaggerInfo.BasePath = "/" @@ -43,6 +46,12 @@ func main() { router.POST("/disputegames/calculate/claim", disputeGameHandler.GetGamesClaimByPosition) router.GET("/disputegames/chainname", disputeGameHandler.GetCurrentBlockChain) + // 新增:前端 Move 交易相关路由 + router.POST("/disputegames/frontend-move", frontendMoveAPI.RecordMove) // 记录前端发起的 move 交易 + router.GET("/disputegames/:address/frontend-moves", frontendMoveAPI.GetMovesByGame) // 获取指定游戏的前端 move 交易 + router.GET("/disputegames/frontend-move/:txhash", frontendMoveAPI.GetMoveByTxHash) // 根据交易哈希获取前端 move 交易详情 + router.GET("/disputegames/with-frontend-flag", frontendMoveAPI.GetGamesWithFrontendFlag) // 获取带有前端发起标记的游戏列表 + router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) err := router.Run(":" + cfg.APIPort) diff --git a/migration/version/migration_version.go b/migration/version/migration_version.go index 89ab971..c5023b6 100644 --- a/migration/version/migration_version.go +++ b/migration/version/migration_version.go @@ -9,6 +9,7 @@ import ( v5 "github.com/optimism-java/dispute-explorer/migration/version/v5" v6 "github.com/optimism-java/dispute-explorer/migration/version/v6" v7 "github.com/optimism-java/dispute-explorer/migration/version/v7" + v8 "github.com/optimism-java/dispute-explorer/migration/version/v8" ) var ModelSchemaList = []*gormigrate.Migration{ @@ -19,4 +20,5 @@ var ModelSchemaList = []*gormigrate.Migration{ &v5.AddOnChainStatusForDisputeGameTable, &v6.UpdateClaimDataClockColumnTable, &v7.AddClaimDataLenForDisputeGameTable, + &v8.AddFrontendMoveTrackingTable, } diff --git a/migration/version/v8/add_frontend_move_tracking.go b/migration/version/v8/add_frontend_move_tracking.go new file mode 100644 index 0000000..206d3c2 --- /dev/null +++ b/migration/version/v8/add_frontend_move_tracking.go @@ -0,0 +1,66 @@ +package v8 + +import ( + "fmt" + + gormigrate "github.com/go-gormigrate/gormigrate/v2" + "github.com/optimism-java/dispute-explorer/internal/schema" + "gorm.io/gorm" +) + +var AddFrontendMoveTrackingTable = gormigrate.Migration{ + ID: "20240805_add_frontend_move_tracking", + Migrate: func(db *gorm.DB) error { + return AddFrontendMoveTracking(db) + }, +} + +func AddFrontendMoveTracking(db *gorm.DB) error { + // Create new frontend_move_transactions table + err := db.AutoMigrate(&schema.FrontendMoveTransaction{}) + if err != nil { + return fmt.Errorf("failed to create frontend_move_transactions table: %v", err) + } + + // Check and add has_frontend_move field to sync_blocks table + if !db.Migrator().HasColumn(&schema.SyncBlock{}, "has_frontend_move") { + err = db.Migrator().AddColumn(&schema.SyncBlock{}, "has_frontend_move") + if err != nil { + return fmt.Errorf("failed to add has_frontend_move column to sync_blocks: %v", err) + } + + // Set default value for newly added field + err = db.Exec("UPDATE sync_blocks SET has_frontend_move = FALSE WHERE has_frontend_move IS NULL").Error + if err != nil { + return fmt.Errorf("failed to set default value for has_frontend_move: %v", err) + } + + // Create index for new field + err = db.Exec("CREATE INDEX idx_sync_blocks_has_frontend_move ON sync_blocks(has_frontend_move)").Error + if err != nil { + return fmt.Errorf("failed to create index on has_frontend_move: %v", err) + } + } + + // Check and add is_from_frontend field to sync_events table + if !db.Migrator().HasColumn(&schema.SyncEvent{}, "is_from_frontend") { + err = db.Migrator().AddColumn(&schema.SyncEvent{}, "is_from_frontend") + if err != nil { + return fmt.Errorf("failed to add is_from_frontend column to sync_events: %v", err) + } + + // Set default value for newly added field + err = db.Exec("UPDATE sync_events SET is_from_frontend = FALSE WHERE is_from_frontend IS NULL").Error + if err != nil { + return fmt.Errorf("failed to set default value for is_from_frontend: %v", err) + } + + // Create index for new field + err = db.Exec("CREATE INDEX idx_sync_events_is_from_frontend ON sync_events(is_from_frontend)").Error + if err != nil { + return fmt.Errorf("failed to create index on is_from_frontend: %v", err) + } + } + + return nil +} From 1644016c7cff2cd3fe0b9a53a202b5cf82793fa8 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:07:36 +0800 Subject: [PATCH 02/11] add move front --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c9176e4..f3bfae9 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ mysql1 meili_data postgres mysql +MIGRATION_STATUS.md +RPC_MANAGER_GUIDE.md .env FRONTEND_MOVE_API.md From 45ec8cc3bf465127f21f9ab5ea4ddde4a9eb1f39 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:08:11 +0800 Subject: [PATCH 03/11] add move front --- .gitignore | 2 - MIGRATION_STATUS.md | 140 ---------------------------- RPC_MANAGER_GUIDE.md | 216 ------------------------------------------- 3 files changed, 358 deletions(-) delete mode 100644 MIGRATION_STATUS.md delete mode 100644 RPC_MANAGER_GUIDE.md diff --git a/.gitignore b/.gitignore index f3bfae9..c9176e4 100644 --- a/.gitignore +++ b/.gitignore @@ -27,8 +27,6 @@ mysql1 meili_data postgres mysql -MIGRATION_STATUS.md -RPC_MANAGER_GUIDE.md .env FRONTEND_MOVE_API.md diff --git a/MIGRATION_STATUS.md b/MIGRATION_STATUS.md deleted file mode 100644 index 47a58f4..0000000 --- a/MIGRATION_STATUS.md +++ /dev/null @@ -1,140 +0,0 @@ -# RPC速率限制配置建议 - -## 当前状态 -✅ **已完成的迁移** -- `syncBlock.go` - 区块同步调用已迁移到RPC Manager -- `latestBlockNumber.go` - 最新区块号获取已迁移到RPC Manager -- `logFilter.go` - 日志过滤和区块查询已迁移到RPC Manager -- `handler.go` - 已启用RPC监控 - -## 配置建议 - -### 开发环境配置 -```bash -# 开发环境 - 保守的速率限制 -export RPC_RATE_LIMIT=5 # 每秒5个请求 -export RPC_RATE_BURST=2 # 允许突发2个请求 -``` - -### 测试环境配置 -```bash -# 测试环境 - 中等速率限制 -export RPC_RATE_LIMIT=10 # 每秒10个请求 -export RPC_RATE_BURST=5 # 允许突发5个请求 -``` - -### 生产环境配置 -```bash -# 生产环境 - 根据RPC提供商限制调整 -export RPC_RATE_LIMIT=25 # 每秒25个请求 -export RPC_RATE_BURST=10 # 允许突发10个请求 -``` - -## 监控和告警 - -### 查看RPC统计 -启动应用后,你会在日志中看到类似这样的监控信息: -``` -[RPC Stats] L1: 150 requests (0 limited), L2: 45 requests (0 limited), HTTP: 95 -[RPC Limits] L1: 25.0/s (burst 10, tokens 8.50), L2: 25.0/s (burst 10, tokens 9.20) -``` - -### 关键指标说明 -- **requests** - 总请求数 -- **limited** - 被速率限制拒绝的请求数 -- **tokens** - 当前可用的令牌数(越低表示使用越频繁) - -### 告警条件 -- ⚠️ `limited > 0` - 有请求被限制,需要关注 -- 🚨 `tokens < 1.0` - 令牌即将耗尽,需要立即调整 -- 📊 `requests增长过快` - 可能需要优化代码逻辑 - -## 优化建议 - -### 1. 根据使用模式调整限制 -```bash -# 如果经常看到 "limited" 请求,可以适当提高限制 -export RPC_RATE_LIMIT=30 -export RPC_RATE_BURST=15 - -# 如果tokens经常很低,可以降低请求频率或提高限制 -``` - -### 2. 分时段配置 -```bash -# 可以在应用中实现动态调整 -# 高峰期降低限制 -ctx.RpcManager.UpdateRateLimit(15, 5, true) # L1 - -# 低峰期提高限制 -ctx.RpcManager.UpdateRateLimit(35, 15, true) # L1 -``` - -### 3. 错误处理优化 -当前迁移的代码已经包含了错误处理,但可以进一步优化: - -```go -// 示例:在syncBlock.go中添加重试逻辑 -for retries := 0; retries < 3; retries++ { - blockJSON, err := ctx.RpcManager.HTTPPostJSON(context.Background(), requestBody, true) - if err != nil { - if strings.Contains(err.Error(), "rate limit exceeded") { - // 指数退避 - time.Sleep(time.Duration(1< Date: Fri, 8 Aug 2025 16:26:38 +0800 Subject: [PATCH 04/11] format --- internal/api/frontend_move_api.go | 2 +- internal/handler/frontend_move.go | 3 +-- migration/version/v8/add_frontend_move_tracking.go | 6 ++---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/internal/api/frontend_move_api.go b/internal/api/frontend_move_api.go index 9304619..e7b5d0d 100644 --- a/internal/api/frontend_move_api.go +++ b/internal/api/frontend_move_api.go @@ -64,7 +64,7 @@ func (api *FrontendMoveAPI) RecordMove(c *gin.Context) { return } - err := api.handler.RecordFrontendMove(c.Request.Context(), &req) + err := api.handler.RecordFrontendMove(&req) if err != nil { log.Errorf("[FrontendMoveAPI] Failed to record frontend move: %v", err) c.JSON(http.StatusInternalServerError, RecordMoveResponse{ diff --git a/internal/handler/frontend_move.go b/internal/handler/frontend_move.go index c5404b3..027dc32 100644 --- a/internal/handler/frontend_move.go +++ b/internal/handler/frontend_move.go @@ -43,7 +43,7 @@ func (h *FrontendMoveHandler) GetServiceContext() *svc.ServiceContext { } // RecordFrontendMove records frontend-initiated move transactions -func (h *FrontendMoveHandler) RecordFrontendMove(ctx context.Context, req *FrontendMoveRequest) error { +func (h *FrontendMoveHandler) RecordFrontendMove(req *FrontendMoveRequest) error { // Validate contract address format if !common.IsHexAddress(req.GameContract) { return fmt.Errorf("invalid game contract address: %s", req.GameContract) @@ -152,7 +152,6 @@ func (h *FrontendMoveHandler) monitorTransactionStatus(recordID int64, txHash st "status": schema.FrontendMoveStatusFailed, "error_message": "Transaction timeout", }).Error - if err != nil { log.Errorf("[FrontendMoveHandler] Failed to update timeout status for %s: %v", txHash, err) } diff --git a/migration/version/v8/add_frontend_move_tracking.go b/migration/version/v8/add_frontend_move_tracking.go index 206d3c2..293c0ed 100644 --- a/migration/version/v8/add_frontend_move_tracking.go +++ b/migration/version/v8/add_frontend_move_tracking.go @@ -9,10 +9,8 @@ import ( ) var AddFrontendMoveTrackingTable = gormigrate.Migration{ - ID: "20240805_add_frontend_move_tracking", - Migrate: func(db *gorm.DB) error { - return AddFrontendMoveTracking(db) - }, + ID: "20240805_add_frontend_move_tracking", + Migrate: AddFrontendMoveTracking, } func AddFrontendMoveTracking(db *gorm.DB) error { From 2f8b07c54946adca0bc77e8e340372649e3fb391 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:32:02 +0800 Subject: [PATCH 05/11] format --- internal/handler/frontend_move.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/handler/frontend_move.go b/internal/handler/frontend_move.go index 027dc32..26fa6fc 100644 --- a/internal/handler/frontend_move.go +++ b/internal/handler/frontend_move.go @@ -10,6 +10,7 @@ import ( "github.com/optimism-java/dispute-explorer/internal/schema" "github.com/optimism-java/dispute-explorer/internal/svc" "github.com/optimism-java/dispute-explorer/pkg/log" + "gorm.io/gorm" ) @@ -121,7 +122,6 @@ func (h *FrontendMoveHandler) monitorTransactionStatus(recordID int64, txHash st } else { status = schema.FrontendMoveStatusFailed } - err = h.svc.DB.Model(&schema.FrontendMoveTransaction{}). Where("id = ?", recordID). Updates(map[string]interface{}{ @@ -129,12 +129,10 @@ func (h *FrontendMoveHandler) monitorTransactionStatus(recordID int64, txHash st "block_number": receipt.BlockNumber.Int64(), "confirmed_at": confirmedAt, }).Error - if err != nil { log.Errorf("[FrontendMoveHandler] Failed to update transaction status for %s: %v", txHash, err) return } - log.Infof("[FrontendMoveHandler] Transaction %s status updated to %s", txHash, status) // If transaction is successful, mark related records From 65cef53b872a56f40ceebc246e28dab60b7a20d0 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:34:49 +0800 Subject: [PATCH 06/11] change ci --- .github/workflows/docker-build.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 671853e..ed9abbc 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -4,13 +4,8 @@ on: push: branches: - "main" - - "dev" tags: - "v*.*.*" - pull_request: - branches: - - "main" - - "dev" jobs: docker: From 14e92c0371b926430be8c83bc9fb0277e5008b29 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:26:24 +0800 Subject: [PATCH 07/11] fix bugs --- deploy/config.yml | 2 ++ internal/handler/frontend_move.go | 4 +-- internal/schema/dispute_game.go | 1 + internal/schema/game_claim_data.go | 25 ++++++++++--------- internal/schema/sync_blocks.go | 21 ++++++++-------- internal/schema/sync_events.go | 1 - .../version/v8/add_frontend_move_tracking.go | 22 ++++++++-------- 7 files changed, 39 insertions(+), 37 deletions(-) diff --git a/deploy/config.yml b/deploy/config.yml index 472a38a..65d518d 100644 --- a/deploy/config.yml +++ b/deploy/config.yml @@ -38,6 +38,7 @@ sync: on_chain_status: claim_data_len: get_len_status: + has_frontend_move: - table: game_claim_data index: gameclaims pk: id @@ -54,6 +55,7 @@ sync: position: clock: output_block: + is_from_frontend: - table: game_credit index: gamecredits pk: id diff --git a/internal/handler/frontend_move.go b/internal/handler/frontend_move.go index 26fa6fc..7b96814 100644 --- a/internal/handler/frontend_move.go +++ b/internal/handler/frontend_move.go @@ -160,7 +160,7 @@ func (h *FrontendMoveHandler) monitorTransactionStatus(recordID int64, txHash st // markRelatedRecords marks related block and event records func (h *FrontendMoveHandler) markRelatedRecords(txHash string, blockNumber int64) { // Mark related blocks - err := h.svc.DB.Model(&schema.SyncBlock{}). + err := h.svc.DB.Model(&schema.DisputeGame{}). Where("block_number = ?", blockNumber). Update("has_frontend_move", true).Error if err != nil { @@ -168,7 +168,7 @@ func (h *FrontendMoveHandler) markRelatedRecords(txHash string, blockNumber int6 } // Mark related events - err = h.svc.DB.Model(&schema.SyncEvent{}). + err = h.svc.DB.Model(&schema.GameClaimData{}). Where("tx_hash = ?", txHash). Update("is_from_frontend", true).Error if err != nil { diff --git a/internal/schema/dispute_game.go b/internal/schema/dispute_game.go index 164accc..d5cc970 100644 --- a/internal/schema/dispute_game.go +++ b/internal/schema/dispute_game.go @@ -33,6 +33,7 @@ type DisputeGame struct { OnChainStatus string `json:"on_chain_status"` ClaimDataLen int64 `json:"claim_data_len"` GetLenStatus bool `json:"get_len_status"` + HasFrontendMove bool `json:"has_frontend_move" gorm:"default:false"` // Whether contains frontend-initiated move transactions } func (DisputeGame) TableName() string { diff --git a/internal/schema/game_claim_data.go b/internal/schema/game_claim_data.go index 13463b5..91e29bb 100644 --- a/internal/schema/game_claim_data.go +++ b/internal/schema/game_claim_data.go @@ -7,18 +7,19 @@ const ( type GameClaimData struct { Base - GameContract string `json:"game_contract"` - DataIndex int64 `json:"data_index"` - ParentIndex uint32 `json:"parent_index"` - CounteredBy string `json:"countered_by"` - Claimant string `json:"claimant"` - Bond string `json:"bond"` - Claim string `json:"claim"` - Position string `json:"position"` - Clock string `json:"clock"` - OutputBlock uint64 `json:"output_block"` - EventID int64 `json:"event_id"` - OnChainStatus string `json:"on_chain_status"` + GameContract string `json:"game_contract"` + DataIndex int64 `json:"data_index"` + ParentIndex uint32 `json:"parent_index"` + CounteredBy string `json:"countered_by"` + Claimant string `json:"claimant"` + Bond string `json:"bond"` + Claim string `json:"claim"` + Position string `json:"position"` + Clock string `json:"clock"` + OutputBlock uint64 `json:"output_block"` + EventID int64 `json:"event_id"` + OnChainStatus string `json:"on_chain_status"` + IsFromFrontend bool `json:"is_from_frontend" gorm:"default:false"` // Whether initiated from frontend } func (GameClaimData) TableName() string { diff --git a/internal/schema/sync_blocks.go b/internal/schema/sync_blocks.go index 95a898d..ecc8d81 100644 --- a/internal/schema/sync_blocks.go +++ b/internal/schema/sync_blocks.go @@ -9,17 +9,16 @@ const ( type SyncBlock struct { Base - Blockchain string `json:"blockchain"` - Miner string `json:"miner"` - BlockTime int64 `json:"block_time"` - BlockNumber int64 `json:"block_number"` - BlockHash string `json:"block_hash"` - TxCount int64 `json:"tx_count"` - EventCount int64 `json:"event_count"` - ParentHash string `json:"parent_hash"` - Status string `json:"status"` - CheckCount int64 `json:"check_count"` - HasFrontendMove bool `json:"has_frontend_move" gorm:"default:false"` // Whether contains frontend-initiated move transactions + Blockchain string `json:"blockchain"` + Miner string `json:"miner"` + BlockTime int64 `json:"block_time"` + BlockNumber int64 `json:"block_number"` + BlockHash string `json:"block_hash"` + TxCount int64 `json:"tx_count"` + EventCount int64 `json:"event_count"` + ParentHash string `json:"parent_hash"` + Status string `json:"status"` + CheckCount int64 `json:"check_count"` } func (SyncBlock) TableName() string { diff --git a/internal/schema/sync_events.go b/internal/schema/sync_events.go index d08ae91..79c94cb 100644 --- a/internal/schema/sync_events.go +++ b/internal/schema/sync_events.go @@ -23,7 +23,6 @@ type SyncEvent struct { Data string `json:"data"` Status string `json:"status"` RetryCount int64 `json:"retry_count"` - IsFromFrontend bool `json:"is_from_frontend" gorm:"default:false"` // Whether initiated from frontend } func (SyncEvent) TableName() string { diff --git a/migration/version/v8/add_frontend_move_tracking.go b/migration/version/v8/add_frontend_move_tracking.go index 293c0ed..90019c5 100644 --- a/migration/version/v8/add_frontend_move_tracking.go +++ b/migration/version/v8/add_frontend_move_tracking.go @@ -21,40 +21,40 @@ func AddFrontendMoveTracking(db *gorm.DB) error { } // Check and add has_frontend_move field to sync_blocks table - if !db.Migrator().HasColumn(&schema.SyncBlock{}, "has_frontend_move") { - err = db.Migrator().AddColumn(&schema.SyncBlock{}, "has_frontend_move") + if !db.Migrator().HasColumn(&schema.DisputeGame{}, "has_frontend_move") { + err = db.Migrator().AddColumn(&schema.DisputeGame{}, "has_frontend_move") if err != nil { - return fmt.Errorf("failed to add has_frontend_move column to sync_blocks: %v", err) + return fmt.Errorf("failed to add has_frontend_move column to dispute_game: %v", err) } // Set default value for newly added field - err = db.Exec("UPDATE sync_blocks SET has_frontend_move = FALSE WHERE has_frontend_move IS NULL").Error + err = db.Exec("UPDATE dispute_game SET has_frontend_move = FALSE WHERE has_frontend_move IS NULL").Error if err != nil { return fmt.Errorf("failed to set default value for has_frontend_move: %v", err) } // Create index for new field - err = db.Exec("CREATE INDEX idx_sync_blocks_has_frontend_move ON sync_blocks(has_frontend_move)").Error + err = db.Exec("CREATE INDEX idx_dispute_game_has_frontend_move ON dispute_game(has_frontend_move)").Error if err != nil { return fmt.Errorf("failed to create index on has_frontend_move: %v", err) } } - // Check and add is_from_frontend field to sync_events table - if !db.Migrator().HasColumn(&schema.SyncEvent{}, "is_from_frontend") { - err = db.Migrator().AddColumn(&schema.SyncEvent{}, "is_from_frontend") + // Check and add is_from_frontend field to game_claim_data table + if !db.Migrator().HasColumn(&schema.GameClaimData{}, "is_from_frontend") { + err = db.Migrator().AddColumn(&schema.GameClaimData{}, "is_from_frontend") if err != nil { - return fmt.Errorf("failed to add is_from_frontend column to sync_events: %v", err) + return fmt.Errorf("failed to add is_from_frontend column to game_claim_data: %v", err) } // Set default value for newly added field - err = db.Exec("UPDATE sync_events SET is_from_frontend = FALSE WHERE is_from_frontend IS NULL").Error + err = db.Exec("UPDATE game_claim_data SET is_from_frontend = FALSE WHERE is_from_frontend IS NULL").Error if err != nil { return fmt.Errorf("failed to set default value for is_from_frontend: %v", err) } // Create index for new field - err = db.Exec("CREATE INDEX idx_sync_events_is_from_frontend ON sync_events(is_from_frontend)").Error + err = db.Exec("CREATE INDEX idx_game_claim_data_is_from_frontend ON game_claim_data(is_from_frontend)").Error if err != nil { return fmt.Errorf("failed to create index on is_from_frontend: %v", err) } From d58d4a1390d1e24007cac3013a2cde6c2e8f42e1 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:57:36 +0800 Subject: [PATCH 08/11] format --- pkg/log/log.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/log/log.go b/pkg/log/log.go index f617598..b541b8d 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -105,8 +105,6 @@ func Init(level, format string) { } // New create logger by opts which can custmoized by command arguments. -// -//nolint:revive func New(opts *Options) *zapLogger { if opts == nil { opts = NewOptions() From 110e66b6daf513d8edae83f66999fd79e0830fd1 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:03:15 +0800 Subject: [PATCH 09/11] format --- pkg/log/log.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/log/log.go b/pkg/log/log.go index b541b8d..55baebc 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -105,6 +105,8 @@ func Init(level, format string) { } // New create logger by opts which can custmoized by command arguments. +// +//nolint:revive // Intentionally returning unexported type for internal use func New(opts *Options) *zapLogger { if opts == nil { opts = NewOptions() From 177417e777884af93c21dbb7918169f2812930ef Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:06:50 +0800 Subject: [PATCH 10/11] format --- pkg/log/log.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/log/log.go b/pkg/log/log.go index 55baebc..a00c75d 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -105,7 +105,6 @@ func Init(level, format string) { } // New create logger by opts which can custmoized by command arguments. -// //nolint:revive // Intentionally returning unexported type for internal use func New(opts *Options) *zapLogger { if opts == nil { @@ -297,13 +296,13 @@ func (l *zapLogger) Errorf(format string, v ...interface{}) { //nolint:govet func ErrorR(format string, v ...interface{}) error { std.zapLogger.Sugar().Errorf(format, v...) - return fmt.Errorf(format, v) + return fmt.Errorf(format, v...) } //nolint:govet func (l *zapLogger) ErrorR(format string, v ...interface{}) error { l.zapLogger.Sugar().Errorf(format, v...) - return fmt.Errorf(format, v) + return fmt.Errorf(format, v...) } // Errorw method output error level log. From 79ac94da152fc9860ede8e657465ab8ab08e10d1 Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:13:51 +0800 Subject: [PATCH 11/11] format --- pkg/log/log.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/log/log.go b/pkg/log/log.go index a00c75d..25b4510 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -104,7 +104,6 @@ func Init(level, format string) { std = New(options) } -// New create logger by opts which can custmoized by command arguments. //nolint:revive // Intentionally returning unexported type for internal use func New(opts *Options) *zapLogger { if opts == nil { @@ -293,13 +292,11 @@ func (l *zapLogger) Errorf(format string, v ...interface{}) { l.zapLogger.Sugar().Errorf(format, v...) } -//nolint:govet func ErrorR(format string, v ...interface{}) error { std.zapLogger.Sugar().Errorf(format, v...) return fmt.Errorf(format, v...) } -//nolint:govet func (l *zapLogger) ErrorR(format string, v ...interface{}) error { l.zapLogger.Sugar().Errorf(format, v...) return fmt.Errorf(format, v...)