Skip to content
Merged
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ BLOCKCHAIN=<block-chain-name>

# l1 rpc url example: eth json rpc url
L1_RPC_URL=<l1-rpc>
# l2 rpc url example: op json rpc url
L2_RPC_URL=<l2-rpc>

NODE_RPCURL=<op-node-rpc>

RPC_RATE_LIMIT=15
RPC_RATE_BURST=5
Expand Down
32 changes: 17 additions & 15 deletions deploy/mysql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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`),
Expand All @@ -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
Expand Down
50 changes: 24 additions & 26 deletions internal/api/dispute_game_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ package api
import (
"context"
"fmt"
"math"
"math/big"
"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"

Expand All @@ -25,18 +24,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, // 新增赋值
}
}

Expand Down Expand Up @@ -294,22 +295,15 @@ 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)))
if err != nil {
return "", fmt.Errorf("block number is nil %d", blockNumber)
if blockNumber < 0 {
return "", fmt.Errorf("block number cannot be negative: %d", blockNumber)
}
var getProofResponse *eth.AccountResult
err = h.L2RPC.Client().CallContext(context.Background(), &getProofResponse, "eth_getProof",
predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, block.Hash().String())

output, err := h.RollupClient.OutputAtBlock(context.Background(), uint64(blockNumber))
if err != nil {
return "", fmt.Errorf("call eth_getProof error:%s", errors.WithStack(err))
}
output := &eth.OutputV0{
StateRoot: eth.Bytes32(block.Root()),
MessagePasserStorageRoot: eth.Bytes32(getProofResponse.StorageHash),
BlockHash: block.Hash(),
return "", fmt.Errorf("failed to get output at block %d: %w", blockNumber, err)
}
return fmt.Sprint(eth.OutputRoot(output)), nil
return output.OutputRoot.String(), nil
}

type CalculateClaim struct {
Expand Down Expand Up @@ -374,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
}
Expand Down
7 changes: 4 additions & 3 deletions internal/types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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_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"`
FromBlockHash string `env:"FROM_BLOCK_HASH" envDefault:"0x5205c17557759edaef9120f56af802aeaa2827a60d674a0413e77e9c515bdfba"`
DisputeGameProxyContract string `env:"DISPUTE_GAME_PROXY_CONTRACT" envDefault:"0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1"`
Expand Down
38 changes: 26 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand All @@ -46,17 +52,25 @@ 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.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))

err := router.Run(":" + cfg.APIPort)
if err != nil {
log.Errorf("start error %s", err)
disputeLog.Errorf("start error %s", err)
return
}
}

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)
}
Loading