From 49734ede036b49c32e8f715efccd8888d09194ad Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Fri, 22 Aug 2025 16:29:19 +0800 Subject: [PATCH 1/3] fix bugs --- deploy/mysql.sql | 32 +++++++++++---------- internal/api/dispute_game_handler.go | 43 +++++++++++----------------- internal/types/config.go | 7 +++-- main.go | 31 ++++++++++++++------ 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/deploy/mysql.sql b/deploy/mysql.sql index 657390a..b6db4b8 100644 --- a/deploy/mysql.sql +++ b/deploy/mysql.sql @@ -91,6 +91,7 @@ CREATE TABLE `dispute_game` `on_chain_status` varchar(32) NOT NULL DEFAULT 'valid', `claim_data_len` bigint NOT NULL DEFAULT '1', `get_len_status` tinyint(1) NOT NULL DEFAULT '0', + `has_frontend_move` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `status_index` (`status`), KEY `dispute_game_index` (`contract_address`, `game_contract`), @@ -106,21 +107,22 @@ CREATE TABLE `dispute_game` DROP TABLE IF EXISTS `game_claim_data`; CREATE TABLE `game_claim_data` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `created_at` datetime DEFAULT CURRENT_TIMESTAMP, - `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `game_contract` varchar(42) NOT NULL, - `data_index` bigint NOT NULL, - `parent_index` bigint NOT NULL, - `countered_by` varchar(42) NOT NULL, - `claimant` varchar(64) NOT NULL, - `bond` varchar(128) NOT NULL, - `claim` varchar(64) NOT NULL, - `position` varchar(128) NOT NULL, - `clock` varchar(128) NOT NULL, - `output_block` bigint UNSIGNED NOT NULL, - `event_id` bigint UNSIGNED NOT NULL, - `on_chain_status` varchar(32) NOT NULL DEFAULT 'valid', + `id` bigint NOT NULL AUTO_INCREMENT, + `created_at` datetime DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `game_contract` varchar(42) NOT NULL, + `data_index` bigint NOT NULL, + `parent_index` bigint NOT NULL, + `countered_by` varchar(42) NOT NULL, + `claimant` varchar(64) NOT NULL, + `bond` varchar(128) NOT NULL, + `claim` varchar(64) NOT NULL, + `position` varchar(128) NOT NULL, + `clock` varchar(128) NOT NULL, + `output_block` bigint UNSIGNED NOT NULL, + `event_id` bigint UNSIGNED NOT NULL, + `on_chain_status` varchar(32) NOT NULL DEFAULT 'valid', + `is_from_frontend` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `claim_on_chain_status_index` (`on_chain_status`) ) ENGINE = InnoDB diff --git a/internal/api/dispute_game_handler.go b/internal/api/dispute_game_handler.go index 11c567c..8adee48 100644 --- a/internal/api/dispute_game_handler.go +++ b/internal/api/dispute_game_handler.go @@ -7,14 +7,12 @@ import ( "net/http" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" - "github.com/ethereum-optimism/optimism/op-service/eth" - "github.com/ethereum-optimism/optimism/op-service/predeploys" + "github.com/ethereum-optimism/optimism/op-service/sources" // 新增导入 "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" config "github.com/optimism-java/dispute-explorer/internal/types" "github.com/optimism-java/dispute-explorer/pkg/contract" - "github.com/pkg/errors" "github.com/spf13/cast" @@ -25,18 +23,20 @@ import ( ) type DisputeGameHandler struct { - Config *config.Config - DB *gorm.DB - L1RPC *ethclient.Client - L2RPC *ethclient.Client + Config *config.Config + DB *gorm.DB + L1RPC *ethclient.Client + L2RPC *ethclient.Client + RollupClient *sources.RollupClient // 新增字段 } -func NewDisputeGameHandler(db *gorm.DB, l1rpc *ethclient.Client, l2rpc *ethclient.Client, config *config.Config) *DisputeGameHandler { +func NewDisputeGameHandler(db *gorm.DB, l1rpc *ethclient.Client, l2rpc *ethclient.Client, config *config.Config, rollupClient *sources.RollupClient) *DisputeGameHandler { return &DisputeGameHandler{ - DB: db, - L1RPC: l1rpc, - L2RPC: l2rpc, - Config: config, + DB: db, + L1RPC: l1rpc, + L2RPC: l2rpc, + Config: config, + RollupClient: rollupClient, // 新增赋值 } } @@ -294,22 +294,13 @@ func (h DisputeGameHandler) GetClaimRoot(c *gin.Context) { } func (h DisputeGameHandler) getClaimRoot(blockNumber int64) (string, error) { - block, err := h.L2RPC.BlockByNumber(context.Background(), big.NewInt(cast.ToInt64(blockNumber))) + // 使用 RollupClient 替代 eth_getProof + output, err := h.RollupClient.OutputAtBlock(context.Background(), uint64(blockNumber)) if err != nil { - return "", fmt.Errorf("block number is nil %d", blockNumber) + return "", fmt.Errorf("failed to get output at block %d: %w", blockNumber, err) } - var getProofResponse *eth.AccountResult - err = h.L2RPC.Client().CallContext(context.Background(), &getProofResponse, "eth_getProof", - predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, block.Hash().String()) - if err != nil { - return "", fmt.Errorf("call eth_getProof error:%s", errors.WithStack(err)) - } - output := ð.OutputV0{ - StateRoot: eth.Bytes32(block.Root()), - MessagePasserStorageRoot: eth.Bytes32(getProofResponse.StorageHash), - BlockHash: block.Hash(), - } - return fmt.Sprint(eth.OutputRoot(output)), nil + + return output.OutputRoot.String(), nil } type CalculateClaim struct { diff --git a/internal/types/config.go b/internal/types/config.go index dc5249d..3f57685 100644 --- a/internal/types/config.go +++ b/internal/types/config.go @@ -11,15 +11,16 @@ type Config struct { LogLevel string `env:"LOG_LEVEL" envDefault:"info"` // "console","json" LogFormat string `env:"LOG_FORMAT" envDefault:"console"` - MySQLDataSource string `env:"MYSQL_DATA_SOURCE" envDefault:"root:root@tcp(127.0.0.1:3367)/dispute_explorer?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true"` + MySQLDataSource string `env:"MYSQL_DATA_SOURCE" envDefault:"root:123456@tcp(127.0.0.1:3306)/dispute_explorer?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true"` MySQLMaxIdleConns int `env:"MYSQL_MAX_IDLE_CONNS" envDefault:"10"` MySQLMaxOpenConns int `env:"MYSQL_MAX_OPEN_CONNS" envDefault:"20"` MySQLConnMaxLifetime int `env:"MYSQL_CONN_MAX_LIFETIME" envDefault:"3600"` Blockchain string `env:"BLOCKCHAIN" envDefault:"sepolia"` L1RPCUrl string `env:"L1_RPC_URL" envDefault:"https://eth-sepolia.g.alchemy.com/v2/RT1mCGRyVMx1F-XlY4Es4Zz-Q8Jrasg6"` L2RPCUrl string `env:"L2_RPC_URL" envDefault:"https://opt-sepolia.g.alchemy.com/v2/RT1mCGRyVMx1F-XlY4Es4Zz-Q8Jrasg6"` - RPCRateLimit int `env:"RPC_RATE_LIMIT" envDefault:"15"` - RPCRateBurst int `env:"RPC_RATE_BURST" envDefault:"5"` + NodeRpcUrl string `env:"NODE_RPC_URL" envDefault:"https://light-radial-slug.optimism-sepolia.quiknode.pro/e9329f699b371572a8cc5dd22d19d5940bb842a5/"` + RPCRateLimit int `env:"RPC_RATE_LIMIT" envDefault:"5"` + RPCRateBurst int `env:"RPC_RATE_BURST" envDefault:"2"` FromBlockNumber int64 `env:"FROM_BLOCK_NUMBER" envDefault:"5515562"` FromBlockHash string `env:"FROM_BLOCK_HASH" envDefault:"0x5205c17557759edaef9120f56af802aeaa2827a60d674a0413e77e9c515bdfba"` DisputeGameProxyContract string `env:"DISPUTE_GAME_PROXY_CONTRACT" envDefault:"0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1"` diff --git a/main.go b/main.go index f3fc9a0..b5f8300 100644 --- a/main.go +++ b/main.go @@ -10,24 +10,30 @@ import ( "github.com/optimism-java/dispute-explorer/internal/svc" "github.com/optimism-java/dispute-explorer/internal/types" "github.com/optimism-java/dispute-explorer/migration/migrate" - "github.com/optimism-java/dispute-explorer/pkg/log" + disputeLog "github.com/optimism-java/dispute-explorer/pkg/log" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" + + "github.com/ethereum-optimism/optimism/op-service/client" + "github.com/ethereum-optimism/optimism/op-service/sources" + gethlog "github.com/ethereum/go-ethereum/log" ) func main() { ctx := context.Background() cfg := types.GetConfig() - log.Init(cfg.LogLevel, cfg.LogFormat) - log.Infof("config: %v\n", cfg) + disputeLog.Init(cfg.LogLevel, cfg.LogFormat) + disputeLog.Infof("config: %v\n", cfg) sCtx := svc.NewServiceContext(ctx, cfg) migrate.Migrate(sCtx.DB) handler.Run(sCtx) - log.Info("listener running...\n") + disputeLog.Info("listener running...\n") + + rollupClient := initRollupClient(cfg) + router := gin.Default() - disputeGameHandler := api.NewDisputeGameHandler(sCtx.DB, sCtx.L1RPC, sCtx.L2RPC, cfg) + disputeGameHandler := api.NewDisputeGameHandler(sCtx.DB, sCtx.L1RPC, sCtx.L2RPC, cfg, rollupClient) - // 新增:前端 Move 交易处理器 frontendMoveAPI := api.NewFrontendMoveAPI(sCtx) docs.SwaggerInfo.Title = "Dispute Game Swagger API" docs.SwaggerInfo.Description = "This is a dispute-explorer server." @@ -46,7 +52,6 @@ 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 交易详情 @@ -56,7 +61,17 @@ func main() { err := router.Run(":" + cfg.APIPort) if err != nil { - log.Errorf("start error %s", err) + disputeLog.Errorf("start error %s", err) return } } + +// 新增:初始化 RollupClient 的函数 +func initRollupClient(cfg *types.Config) *sources.RollupClient { + rpcClient, err := client.NewRPC(context.Background(), gethlog.New(), cfg.NodeRpcUrl) + if err != nil { + disputeLog.Errorf("failed to connect to node RPC: %v", err) + panic(err) + } + return sources.NewRollupClient(rpcClient) +} From 4cafb9d8d5294dfa20aeb3582d4aa40ed1865ade Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Fri, 22 Aug 2025 16:39:19 +0800 Subject: [PATCH 2/3] format --- internal/api/dispute_game_handler.go | 13 ++++++++++--- internal/types/config.go | 2 +- main.go | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/internal/api/dispute_game_handler.go b/internal/api/dispute_game_handler.go index 8adee48..a7cb0ee 100644 --- a/internal/api/dispute_game_handler.go +++ b/internal/api/dispute_game_handler.go @@ -3,6 +3,7 @@ package api import ( "context" "fmt" + "math" "math/big" "net/http" @@ -294,12 +295,14 @@ func (h DisputeGameHandler) GetClaimRoot(c *gin.Context) { } func (h DisputeGameHandler) getClaimRoot(blockNumber int64) (string, error) { - // 使用 RollupClient 替代 eth_getProof + if blockNumber < 0 { + return "", fmt.Errorf("block number cannot be negative: %d", blockNumber) + } + output, err := h.RollupClient.OutputAtBlock(context.Background(), uint64(blockNumber)) if err != nil { return "", fmt.Errorf("failed to get output at block %d: %w", blockNumber, err) } - return output.OutputRoot.String(), nil } @@ -365,7 +368,11 @@ func (h DisputeGameHandler) gamesClaimByPosition(req *CalculateClaim) (string, e outputBlock = poststateBlock.Uint64() } - root, err := h.getClaimRoot(cast.ToInt64(outputBlock)) + if outputBlock > math.MaxInt64 { + return "", fmt.Errorf("output block number too large: %d", outputBlock) + } + + root, err := h.getClaimRoot(int64(outputBlock)) if err != nil { return "", err } diff --git a/internal/types/config.go b/internal/types/config.go index 3f57685..c10d284 100644 --- a/internal/types/config.go +++ b/internal/types/config.go @@ -18,7 +18,7 @@ type Config struct { Blockchain string `env:"BLOCKCHAIN" envDefault:"sepolia"` L1RPCUrl string `env:"L1_RPC_URL" envDefault:"https://eth-sepolia.g.alchemy.com/v2/RT1mCGRyVMx1F-XlY4Es4Zz-Q8Jrasg6"` L2RPCUrl string `env:"L2_RPC_URL" envDefault:"https://opt-sepolia.g.alchemy.com/v2/RT1mCGRyVMx1F-XlY4Es4Zz-Q8Jrasg6"` - NodeRpcUrl string `env:"NODE_RPC_URL" envDefault:"https://light-radial-slug.optimism-sepolia.quiknode.pro/e9329f699b371572a8cc5dd22d19d5940bb842a5/"` + NodeRPCURL string `env:"NODE_RPCURL" envDefault:"https://light-radial-slug.optimism-sepolia.quiknode.pro/e9329f699b371572a8cc5dd22d19d5940bb842a5/"` RPCRateLimit int `env:"RPC_RATE_LIMIT" envDefault:"5"` RPCRateBurst int `env:"RPC_RATE_BURST" envDefault:"2"` FromBlockNumber int64 `env:"FROM_BLOCK_NUMBER" envDefault:"5515562"` diff --git a/main.go b/main.go index b5f8300..87a6ebf 100644 --- a/main.go +++ b/main.go @@ -68,7 +68,7 @@ func main() { // 新增:初始化 RollupClient 的函数 func initRollupClient(cfg *types.Config) *sources.RollupClient { - rpcClient, err := client.NewRPC(context.Background(), gethlog.New(), cfg.NodeRpcUrl) + rpcClient, err := client.NewRPC(context.Background(), gethlog.New(), cfg.NodeRPCURL) if err != nil { disputeLog.Errorf("failed to connect to node RPC: %v", err) panic(err) From 6da07f0350b706274100a7489feb3ee46a7a48aa Mon Sep 17 00:00:00 2001 From: zhouop0 <11733741+zhouop0@users.noreply.github.com> Date: Fri, 22 Aug 2025 17:08:33 +0800 Subject: [PATCH 3/3] format --- README.md | 4 ++++ main.go | 9 ++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3693096..b0fb538 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ BLOCKCHAIN= # l1 rpc url example: eth json rpc url L1_RPC_URL= +# l2 rpc url example: op json rpc url +L2_RPC_URL= + +NODE_RPCURL= RPC_RATE_LIMIT=15 RPC_RATE_BURST=5 diff --git a/main.go b/main.go index 87a6ebf..d040834 100644 --- a/main.go +++ b/main.go @@ -52,10 +52,10 @@ func main() { router.POST("/disputegames/calculate/claim", disputeGameHandler.GetGamesClaimByPosition) router.GET("/disputegames/chainname", disputeGameHandler.GetCurrentBlockChain) - 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.POST("/disputegames/frontend-move", frontendMoveAPI.RecordMove) + router.GET("/disputegames/:address/frontend-moves", frontendMoveAPI.GetMovesByGame) + router.GET("/disputegames/frontend-move/:txhash", frontendMoveAPI.GetMoveByTxHash) + router.GET("/disputegames/with-frontend-flag", frontendMoveAPI.GetGamesWithFrontendFlag) router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) @@ -66,7 +66,6 @@ func main() { } } -// 新增:初始化 RollupClient 的函数 func initRollupClient(cfg *types.Config) *sources.RollupClient { rpcClient, err := client.NewRPC(context.Background(), gethlog.New(), cfg.NodeRPCURL) if err != nil {