Skip to content

Commit

Permalink
add claim interface
Browse files Browse the repository at this point in the history
  • Loading branch information
zhouop0 committed Aug 9, 2024
1 parent c026859 commit f23443b
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 13 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ LOG_FORMAT=console
MYSQL_DATA_SOURCE=root:root@tcp(127.0.0.1:3367)/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=6034337
Expand Down
1 change: 1 addition & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ LOG_FORMAT=console
MYSQL_DATA_SOURCE=<data-source>
BLOCKCHAIN=<block-chain-name>
L1_RPC_URL=<l1-rpc>
L2_RPC_URL=<l2-rpc>
RPC_RATE_LIMIT=15
RPC_RATE_BURST=5
FROM_BLOCK_NUMBER=6034337
Expand Down
41 changes: 41 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,47 @@ const docTemplate = `{
}
}
},
"/disputegames/calculate/claim": {
"post": {
"description": "calculate dispute game honest claim by postion",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/disputegames/claimroot/:blockNumber": {
"get": {
"description": "calculate l2 block claim roo",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "calculate l2 block claim root",
"parameters": [
{
"type": "integer",
"description": "dispute game l2 block number",
"name": "blockNumber",
"in": "path"
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/disputegames/count": {
"get": {
"description": "Get dispute games count group by status and per day",
Expand Down
41 changes: 41 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,47 @@
}
}
},
"/disputegames/calculate/claim": {
"post": {
"description": "calculate dispute game honest claim by postion",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/disputegames/claimroot/:blockNumber": {
"get": {
"description": "calculate l2 block claim roo",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "calculate l2 block claim root",
"parameters": [
{
"type": "integer",
"description": "dispute game l2 block number",
"name": "blockNumber",
"in": "path"
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/disputegames/count": {
"get": {
"description": "Get dispute games count group by status and per day",
Expand Down
26 changes: 26 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@ paths:
"200":
description: OK
summary: Get credit details
/disputegames/calculate/claim:
post:
consumes:
- application/json
description: calculate dispute game honest claim by postion
produces:
- application/json
responses:
"200":
description: OK
/disputegames/claimroot/:blockNumber:
get:
consumes:
- application/json
description: calculate l2 block claim roo
parameters:
- description: dispute game l2 block number
in: path
name: blockNumber
type: integer
produces:
- application/json
responses:
"200":
description: OK
summary: calculate l2 block claim root
/disputegames/count:
get:
consumes:
Expand Down
132 changes: 129 additions & 3 deletions internal/api/dispute_game_handler.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
package api

import (
"context"
"fmt"

Check failure on line 5 in internal/api/dispute_game_handler.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

File is not `gofumpt`-ed (gofumpt)
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/optimism-java/dispute-explorer/pkg/contract"
"math/big"

Check failure on line 9 in internal/api/dispute_game_handler.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

File is not `gofumpt`-ed (gofumpt)
"net/http"

"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/pkg/errors"

"github.com/spf13/cast"

"github.com/gin-gonic/gin"
Expand All @@ -12,12 +24,16 @@ import (
)

type DisputeGameHandler struct {
DB *gorm.DB
DB *gorm.DB
L1RPC *ethclient.Client
L2RPC *ethclient.Client
}

func NewDisputeGameHandler(db *gorm.DB) *DisputeGameHandler {
func NewDisputeGameHandler(db *gorm.DB, l1rpc *ethclient.Client, l2rpc *ethclient.Client) *DisputeGameHandler {
return &DisputeGameHandler{
DB: db,
DB: db,
L1RPC: l1rpc,
L2RPC: l2rpc,
}
}

Expand Down Expand Up @@ -251,3 +267,113 @@ func (h DisputeGameHandler) ListGameEvents(c *gin.Context) {
"records": events,
})
}

// @Summary calculate l2 block claim root
// @schemes
// @Description calculate l2 block claim roo
// @Accept json
// @Produce json
// @Param blockNumber path int false "dispute game l2 block number"
// @Success 200
// @Router /disputegames/claimroot/:blockNumber [get]
func (h DisputeGameHandler) GetClaimRoot(c *gin.Context) {
blockNumber := c.Param("blockNumber")
res, err := h.getClaimRoot(cast.ToInt64(blockNumber))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("error %s", err.Error()),
})
}

c.JSON(http.StatusOK, gin.H{
"output": res,
})
}

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)
}
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 := &eth.OutputV0{
StateRoot: eth.Bytes32(block.Root()),
MessagePasserStorageRoot: eth.Bytes32(getProofResponse.StorageHash),
BlockHash: block.Hash(),
}
return fmt.Sprint(eth.OutputRoot(output)), nil
}

type CalculateClaim struct {
DisputeGame string `json:"disputeGame"`
Position int64 `json:"position"`
}

// @Sumary calculate claim by position

Check failure on line 317 in internal/api/dispute_game_handler.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

`Sumary` is a misspelling of `Summary` (misspell)
// @Schemes
// @Description calculate dispute game honest claim by postion

Check failure on line 319 in internal/api/dispute_game_handler.go

View workflow job for this annotation

GitHub Actions / Run golangci-lint

`postion` is a misspelling of `position` (misspell)
// @Accept json
// @Produce json
// @Success 200
// @Router /disputegames/calculate/claim [post]
func (h DisputeGameHandler) GetGamesClaimByPosition(c *gin.Context) {
json := &CalculateClaim{}
err := c.BindJSON(&json)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("error %s", err.Error()),
})
}
res, err := h.gamesClaimByPosition(json)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("error %s", err.Error()),
})
}
c.JSON(http.StatusOK, gin.H{
"DisputeGame": json.DisputeGame,
"Position": json.Position,
"claims": res,
})
}

func (h DisputeGameHandler) gamesClaimByPosition(req *CalculateClaim) (string, error) {
newDisputeGame, err := contract.NewDisputeGame(common.HexToAddress(req.DisputeGame), h.L1RPC)
if err != nil {
return "", err
}
prestateBlock, err := newDisputeGame.StartingBlockNumber(&bind.CallOpts{})
if err != nil {
return "", err
}
poststateBlock, err := newDisputeGame.L2BlockNumber(&bind.CallOpts{})
if err != nil {
return "", err
}
splitDepth, err := newDisputeGame.SplitDepth(&bind.CallOpts{})
if err != nil {
return "", err
}
splitDepths := types.Depth(splitDepth.Uint64())

pos := types.NewPositionFromGIndex(big.NewInt(req.Position))
traceIndex := pos.TraceIndex(splitDepths)
if !traceIndex.IsUint64() {
return "", fmt.Errorf("err:%s", traceIndex)
}
outputBlock := traceIndex.Uint64() + prestateBlock.Uint64() + 1
if outputBlock > poststateBlock.Uint64() {
outputBlock = poststateBlock.Uint64()
}

root, err := h.getClaimRoot(cast.ToInt64(outputBlock))
if err != nil {
return "", err
}
return root, nil
}
7 changes: 7 additions & 0 deletions internal/svc/svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var svc *ServiceContext
type ServiceContext struct {
Config *types.Config
L1RPC *ethclient.Client
L2RPC *ethclient.Client
DB *gorm.DB
LatestBlockNumber int64
SyncedBlockNumber int64
Expand Down Expand Up @@ -49,9 +50,15 @@ func NewServiceContext(ctx context.Context, cfg *types.Config) *ServiceContext {
log.Panicf("[svc] get eth client panic: %s\n", err)
}

rpc2, err := ethclient.Dial(cfg.L2RPCUrl)
if err != nil {
log.Panicf("[svc] get eth client panic: %s\n", err)
}

svc = &ServiceContext{
Config: cfg,
L1RPC: rpc,
L2RPC: rpc2,
DB: storage,
Context: ctx,
}
Expand Down
3 changes: 2 additions & 1 deletion internal/types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ 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:3366)/dispute_explorer?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true"`
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"`
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/PNunSRFo0FWRJMu5yrwBd6jF7G78YHrv"`
L2RPCUrl string `env:"L2_RPC_URL" envDefault:"https://opt-sepolia.g.alchemy.com/v2/FPgbOkDCgG8t0ppZ6TwZXLucr1wl_us4"`
RPCRateLimit int `env:"RPC_RATE_LIMIT" envDefault:"15"`
RPCRateBurst int `env:"RPC_RATE_BURST" envDefault:"5"`
FromBlockNumber int64 `env:"FROM_BLOCK_NUMBER" envDefault:"6034337"`
Expand Down
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func main() {
handler.Run(sCtx)
log.Info("listener running...\n")
router := gin.Default()
disputeGameHandler := api.NewDisputeGameHandler(sCtx.DB)
disputeGameHandler := api.NewDisputeGameHandler(sCtx.DB, sCtx.L1RPC, sCtx.L2RPC)
docs.SwaggerInfo.Title = "Dispute Game Swagger API"
docs.SwaggerInfo.Description = "This is a dispute-explorer server."
docs.SwaggerInfo.BasePath = "/"
Expand All @@ -39,6 +39,8 @@ func main() {
router.GET("/disputegames/statistics/bond/inprogress", disputeGameHandler.GetBondInProgressPerDays)
router.GET("/disputegames/daylycount", disputeGameHandler.GetCountDisputeGameGroupByStatus)
router.GET("/disputegames/events", disputeGameHandler.ListGameEvents)
router.GET("/disputegames/claimroot/:blockNumber", disputeGameHandler.GetClaimRoot)
router.POST("/disputegames/calculate/claim", disputeGameHandler.GetGamesClaimByPosition)

router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

Expand Down
Loading

0 comments on commit f23443b

Please sign in to comment.