[API] split api/v1 and v1 routing in different files (#101)
### Summary Split `/v1` and `/api/v1` controllers into different packages (respectively `api/routes/guardian` and `api/routes/wormscan`). Still need to move services/repositories/models according to the new package layout.
This commit is contained in:
parent
215a00ccc8
commit
e929104ed9
106
api/main.go
106
api/main.go
|
@ -7,12 +7,10 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ansrivas/fiberprometheus/v2"
|
||||
"github.com/gofiber/adaptor/v2"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cache"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"github.com/gofiber/fiber/v2/middleware/requestid"
|
||||
|
@ -20,7 +18,6 @@ import (
|
|||
|
||||
ipfslog "github.com/ipfs/go-log/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/governor"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/guardian"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/heartbeats"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/infrastructure"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/observations"
|
||||
|
@ -30,24 +27,12 @@ import (
|
|||
"github.com/wormhole-foundation/wormhole-explorer/api/internal/db"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/guardian"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/wormscan"
|
||||
rpcApi "github.com/wormhole-foundation/wormhole-explorer/api/rpc"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var cacheConfig = cache.Config{
|
||||
Next: func(c *fiber.Ctx) bool {
|
||||
return c.Query("refresh") == "true"
|
||||
},
|
||||
Expiration: 1 * time.Second,
|
||||
CacheControl: true,
|
||||
StoreResponseHeaders: true,
|
||||
}
|
||||
|
||||
func healthOk(ctx *fiber.Ctx) error {
|
||||
ctx.Status(200)
|
||||
return ctx.SendString("Ok")
|
||||
}
|
||||
|
||||
//go:embed docs/swagger.json
|
||||
var swagger []byte
|
||||
|
||||
|
@ -116,111 +101,40 @@ func main() {
|
|||
// Get cache get function
|
||||
cacheGetFunc := NewCache(cfg, rootLogger)
|
||||
|
||||
// Setup repositories
|
||||
// Set up repositories
|
||||
vaaRepo := vaa.NewRepository(db, rootLogger)
|
||||
obsRepo := observations.NewRepository(db, rootLogger)
|
||||
governorRepo := governor.NewRepository(db, rootLogger)
|
||||
infrastructureRepo := infrastructure.NewRepository(db, rootLogger)
|
||||
heartbeatsRepo := heartbeats.NewRepository(db, rootLogger)
|
||||
|
||||
// Setup services
|
||||
// Set up services
|
||||
vaaService := vaa.NewService(vaaRepo, cacheGetFunc, rootLogger)
|
||||
obsService := observations.NewService(obsRepo, rootLogger)
|
||||
governorService := governor.NewService(governorRepo, rootLogger)
|
||||
infrastructureService := infrastructure.NewService(infrastructureRepo, rootLogger)
|
||||
heartbeatsService := heartbeats.NewService(heartbeatsRepo, rootLogger)
|
||||
|
||||
// Setup controllers
|
||||
vaaCtrl := vaa.NewController(vaaService, rootLogger)
|
||||
observationsCtrl := observations.NewController(obsService, rootLogger)
|
||||
governorCtrl := governor.NewController(governorService, rootLogger)
|
||||
infrastructureCtrl := infrastructure.NewController(infrastructureService)
|
||||
guardianCtrl := guardian.NewController(rootLogger)
|
||||
heartbeatsCtrl := heartbeats.NewController(heartbeatsService, rootLogger)
|
||||
|
||||
// Setup app with custom error handling.
|
||||
// Set up a custom error handler
|
||||
response.SetEnableStackTrace(*cfg)
|
||||
app := fiber.New(fiber.Config{ErrorHandler: middleware.ErrorHandler})
|
||||
|
||||
// Middleware
|
||||
// Configure middleware
|
||||
prometheus := fiberprometheus.New("wormscan")
|
||||
prometheus.RegisterAt(app, "/metrics")
|
||||
app.Use(prometheus.Middleware)
|
||||
|
||||
app.Use(cors.New())
|
||||
app.Use(requestid.New())
|
||||
app.Use(logger.New(logger.Config{
|
||||
Format: "level=info timestamp=${time} method=${method} path=${path} status${status} request_id=${locals:requestid}\n",
|
||||
}))
|
||||
|
||||
// Set up route handlers
|
||||
app.Get("/swagger.json", GetSwagger)
|
||||
wormscan.RegisterRoutes(app, rootLogger, vaaService, obsService, governorService, infrastructureService)
|
||||
guardian.RegisterRoutes(app, rootLogger, vaaService, governorService, heartbeatsService)
|
||||
|
||||
api := app.Group("/api/v1")
|
||||
api.Use(middleware.ExtractPagination)
|
||||
api.Get("/health", infrastructureCtrl.HealthCheck)
|
||||
api.Get("/ready", infrastructureCtrl.ReadyCheck)
|
||||
|
||||
// vaas resource
|
||||
vaas := api.Group("/vaas")
|
||||
vaas.Use(cache.New(cacheConfig))
|
||||
vaas.Get("/vaa-counts", vaaCtrl.GetVaaCount)
|
||||
vaas.Get("/", vaaCtrl.FindAll)
|
||||
vaas.Get("/:chain", vaaCtrl.FindByChain)
|
||||
vaas.Get("/:chain/:emitter", vaaCtrl.FindByEmitter)
|
||||
vaas.Get("/:chain/:emitter/:sequence", vaaCtrl.FindById)
|
||||
|
||||
// oservations resource
|
||||
observations := api.Group("/observations")
|
||||
observations.Get("/", observationsCtrl.FindAll)
|
||||
observations.Get("/:chain", observationsCtrl.FindAllByChain)
|
||||
observations.Get("/:chain/:emitter", observationsCtrl.FindAllByEmitter)
|
||||
observations.Get("/:chain/:emitter/:sequence", observationsCtrl.FindAllByVAA)
|
||||
observations.Get("/:chain/:emitter/:sequence/:signer/:hash", observationsCtrl.FindOne)
|
||||
|
||||
// governor resources
|
||||
governor := api.Group("/governor")
|
||||
governorLimit := governor.Group("/limit")
|
||||
governorLimit.Get("/", governorCtrl.GetGovernorLimit)
|
||||
|
||||
governorConfigs := governor.Group("/config")
|
||||
governorConfigs.Get("/", governorCtrl.FindGovernorConfigurations)
|
||||
governorConfigs.Get("/:guardian_address", governorCtrl.FindGovernorConfigurationByGuardianAddress)
|
||||
|
||||
governorStatus := governor.Group("/status")
|
||||
governorStatus.Get("/", governorCtrl.FindGovernorStatus)
|
||||
governorStatus.Get("/:guardian_address", governorCtrl.FindGovernorStatusByGuardianAddress)
|
||||
|
||||
governorNotional := governor.Group("/notional")
|
||||
governorNotional.Get("/limit/", governorCtrl.FindNotionalLimit)
|
||||
governorNotional.Get("/limit/:chain", governorCtrl.GetNotionalLimitByChainID)
|
||||
governorNotional.Get("/available/", governorCtrl.GetAvailableNotional)
|
||||
governorNotional.Get("/available/:chain", governorCtrl.GetAvailableNotionalByChainID)
|
||||
governorNotional.Get("/max_available/:chain", governorCtrl.GetMaxNotionalAvailableByChainID)
|
||||
|
||||
enqueueVaas := governor.Group("/enqueued_vaas")
|
||||
enqueueVaas.Get("/", governorCtrl.GetEnqueueVaas)
|
||||
enqueueVaas.Get("/:chain", governorCtrl.GetEnqueuedVaasByChainID)
|
||||
|
||||
// v1 guardian public api.
|
||||
publicAPIV1 := app.Group("/v1")
|
||||
// signedVAA resource.
|
||||
signedVAA := publicAPIV1.Group("/signed_vaa")
|
||||
signedVAA.Get("/:chain/:emitter/:sequence", vaaCtrl.FindSignedVAAByID)
|
||||
signedBatchVAA := publicAPIV1.Group("/signed_batch_vaa")
|
||||
signedBatchVAA.Get("/:chain/:trxID/:nonce", vaaCtrl.FindSignedBatchVAAByID)
|
||||
// guardianSet resource.
|
||||
guardianSet := publicAPIV1.Group("/guardianset")
|
||||
guardianSet.Get("/current", guardianCtrl.GetGuardianSet)
|
||||
// heartbeats resource.
|
||||
heartbeats := publicAPIV1.Group("/heartbeats")
|
||||
heartbeats.Get("", heartbeatsCtrl.GetLastHeartbeats)
|
||||
// governor resource.
|
||||
gov := publicAPIV1.Group("/governor")
|
||||
gov.Get("/available_notional_by_chain", governorCtrl.GetAvailNotionByChain)
|
||||
gov.Get("/enqueued_vaas", governorCtrl.GetEnqueuedVaas)
|
||||
gov.Get("/is_vaa_enqueued/:chain/:emitter/:sequence", governorCtrl.IsVaaEnqueued)
|
||||
gov.Get("/token_list", governorCtrl.GetTokenList)
|
||||
|
||||
// Set up gRPC handlers
|
||||
handler := rpcApi.NewHandler(vaaService, heartbeatsService, governorService, rootLogger)
|
||||
grpcServer := rpcApi.NewServer(handler, rootLogger)
|
||||
grpcWebServer := grpcweb.WrapServer(grpcServer)
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
// Package governor handle the request of governor data from governor endpoint defined in the api.
|
||||
package governor
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/governor"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
_ "github.com/wormhole-foundation/wormhole-explorer/api/response" // needed by swaggo docs
|
||||
"github.com/wormhole-foundation/wormhole/sdk/vaa"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Controller definition.
|
||||
type Controller struct {
|
||||
srv *governor.Service
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewController create a new controler.
|
||||
func NewController(serv *governor.Service, logger *zap.Logger) *Controller {
|
||||
return &Controller{srv: serv, logger: logger.With(zap.String("module", "GovernorController"))}
|
||||
}
|
||||
|
||||
// AvailableNotionalResponse response compatible with grpc api.
|
||||
type AvailableNotionalResponse struct {
|
||||
Entries []*AvailableNotionalItemResponse `json:"entries"`
|
||||
}
|
||||
|
||||
type AvailableNotionalItemResponse struct {
|
||||
ChainID vaa.ChainID `json:"chainId"`
|
||||
AvailableNotional string `json:"remainingAvailableNotional"`
|
||||
NotionalLimit string `json:"notionalLimit"`
|
||||
MaxTransactionSize string `json:"bigTransactionSize"`
|
||||
}
|
||||
|
||||
// GetAvailNotionByChain godoc
|
||||
// @Description Get available notional by chainID
|
||||
// @Description Since from the wormhole-explorer point of view it is not a node, but has the information of all nodes,
|
||||
// @Description in order to build the endpoints it was assumed:
|
||||
// @Description There are N number of remainingAvailableNotional values in the GovernorConfig collection. N = number of guardians
|
||||
// @Description for a chainID. The smallest remainingAvailableNotional value for a chainID is used for the endpoint response.
|
||||
// @Tags Guardian
|
||||
// @ID governor-available-notional-by-chain
|
||||
// @Success 200 {object} AvailableNotionalResponse
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/governor/available_notional_by_chain [get]
|
||||
func (c *Controller) GetAvailNotionByChain(ctx *fiber.Ctx) error {
|
||||
// call service to get available notional by chainID
|
||||
availableNotional, err := c.srv.GetAvailNotionByChain(ctx.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build response compatible with node grpc api.
|
||||
entries := make([]*AvailableNotionalItemResponse, 0, len(availableNotional))
|
||||
for _, v := range availableNotional {
|
||||
r := AvailableNotionalItemResponse{
|
||||
ChainID: v.ChainID,
|
||||
AvailableNotional: v.AvailableNotional.String(),
|
||||
NotionalLimit: v.NotionalLimit.String(),
|
||||
MaxTransactionSize: v.MaxTransactionSize.String(),
|
||||
}
|
||||
entries = append(entries, &r)
|
||||
}
|
||||
response := AvailableNotionalResponse{
|
||||
Entries: entries,
|
||||
}
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
// EnqueuedVaaResponse response compatible with grpc api.
|
||||
type EnqueuedVaaResponse struct {
|
||||
Entries []*EnqueuedVaaItemResponse `json:"entries"`
|
||||
}
|
||||
|
||||
type EnqueuedVaaItemResponse struct {
|
||||
EmitterChain vaa.ChainID `json:"emitterChain"`
|
||||
EmitterAddress string `json:"emitterAddress"`
|
||||
Sequence uint64 `json:"sequence"`
|
||||
ReleaseTime int64 `json:"releaseTime"`
|
||||
NotionalValue string `json:"notionalValue"`
|
||||
TxHash string `json:"txHash"`
|
||||
}
|
||||
|
||||
// GetEnqueuedVaas godoc
|
||||
// @Description Get enqueued VAAs
|
||||
// @Tags Guardian
|
||||
// @ID guardians-enqueued-vaas
|
||||
// @Success 200 {object} EnqueuedVaaResponse
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/governor/enqueued_vaas [get]
|
||||
func (c *Controller) GetEnqueuedVaas(ctx *fiber.Ctx) error {
|
||||
enqueuedVaa, err := c.srv.GetEnqueuedVaas(ctx.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build response compatible with node grpc api.
|
||||
entries := make([]*EnqueuedVaaItemResponse, 0, len(enqueuedVaa))
|
||||
for _, v := range enqueuedVaa {
|
||||
seqUint64, err := strconv.ParseUint(v.Sequence, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r := EnqueuedVaaItemResponse{
|
||||
EmitterChain: v.EmitterChain,
|
||||
EmitterAddress: v.EmitterAddress,
|
||||
Sequence: seqUint64,
|
||||
ReleaseTime: v.ReleaseTime,
|
||||
NotionalValue: v.NotionalValue.String(),
|
||||
TxHash: v.TxHash,
|
||||
}
|
||||
entries = append(entries, &r)
|
||||
}
|
||||
response := EnqueuedVaaResponse{
|
||||
Entries: entries,
|
||||
}
|
||||
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
// IsVaaEnqueued godoc
|
||||
// @Description Check if vaa is enqueued
|
||||
// @Tags Guardian
|
||||
// @ID guardians-is-vaa-enqueued
|
||||
// @Param chain_id path integer true "id of the blockchain"
|
||||
// @Param emitter path string true "address of the emitter"
|
||||
// @Param seq path integer true "sequence of the vaa"
|
||||
// @Success 200 {object} EnqueuedVaaResponse
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/governor/is_vaa_enqueued/{chain_id}/{emitter}/{seq} [get]
|
||||
func (c *Controller) IsVaaEnqueued(ctx *fiber.Ctx) error {
|
||||
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isEnqueued, err := c.srv.IsVaaEnqueued(ctx.Context(), chainID, *emitter, strconv.FormatUint(seq, 10))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build reponse compatible with node grpc api.
|
||||
response := struct {
|
||||
IsEnqueued bool `json:"isEnqueued"`
|
||||
}{
|
||||
IsEnqueued: isEnqueued,
|
||||
}
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
// GetTokenList godoc
|
||||
// @Description Get token list
|
||||
// @Description Since from the wormhole-explorer point of view it is not a node, but has the information of all nodes,
|
||||
// @Description in order to build the endpoints it was assumed:
|
||||
// @Description For tokens with the same originChainId and originAddress and different price values for each node,
|
||||
// @Description the price that has most occurrences in all the nodes for an originChainId and originAddress is returned.
|
||||
// @Tags Guardian
|
||||
// @ID guardians-token-list
|
||||
// @Success 200 {object} []TokenList
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/governor/token_list [get]
|
||||
func (c *Controller) GetTokenList(ctx *fiber.Ctx) error {
|
||||
tokenList, err := c.srv.GetTokenList(ctx.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build reponse compatible with node grpc api.
|
||||
response := struct {
|
||||
Entries []*governor.TokenList `json:"entries"`
|
||||
}{
|
||||
Entries: tokenList,
|
||||
}
|
||||
return ctx.JSON(response)
|
||||
}
|
|
@ -2,6 +2,7 @@ package guardian
|
|||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/guardian"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -37,12 +38,12 @@ type GuardianSet struct {
|
|||
// @Router /v1/guardianset/current [get]
|
||||
func (c *Controller) GetGuardianSet(ctx *fiber.Ctx) error {
|
||||
// check guardianSet exists.
|
||||
if len(ByIndex) == 0 {
|
||||
if len(guardian.ByIndex) == 0 {
|
||||
return response.NewApiError(ctx, fiber.StatusServiceUnavailable, response.Unavailable,
|
||||
"guardian set not fetched from chain yet", nil)
|
||||
}
|
||||
// get lasted guardianSet.
|
||||
guardinSet := GetLatest()
|
||||
guardinSet := guardian.GetLatest()
|
||||
|
||||
// get guardian addresses.
|
||||
addresses := make([]string, len(guardinSet.Keys))
|
|
@ -5,18 +5,19 @@ import (
|
|||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/guardian"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/heartbeats"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Controller definition.
|
||||
type Controller struct {
|
||||
srv *Service
|
||||
srv *heartbeats.Service
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewController create a new controler.
|
||||
func NewController(srv *Service, logger *zap.Logger) *Controller {
|
||||
func NewController(srv *heartbeats.Service, logger *zap.Logger) *Controller {
|
||||
return &Controller{
|
||||
srv: srv,
|
||||
logger: logger.With(zap.String("module", "HeartbeatsController")),
|
||||
|
@ -82,7 +83,7 @@ func (c *Controller) GetLastHeartbeats(ctx *fiber.Ctx) error {
|
|||
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||
}
|
||||
|
||||
func buildHeartbeatResponse(heartbeats []*HeartbeatDoc) *HeartbeatsResponse {
|
||||
func buildHeartbeatResponse(heartbeats []*heartbeats.HeartbeatDoc) *HeartbeatsResponse {
|
||||
if heartbeats == nil {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package guardian
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
govsvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/governor"
|
||||
heartbeatssvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/heartbeats"
|
||||
vaasvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/vaa"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/guardian/governor"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/guardian/guardian"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/guardian/heartbeats"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/guardian/vaa"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// RegisterRoutes sets up the handlers for the Guardian API.
|
||||
func RegisterRoutes(
|
||||
app *fiber.App,
|
||||
rootLogger *zap.Logger,
|
||||
vaaService *vaasvc.Service,
|
||||
governorService *govsvc.Service,
|
||||
heartbeatsService *heartbeatssvc.Service,
|
||||
) {
|
||||
|
||||
// Set up controllers
|
||||
vaaCtrl := vaa.NewController(vaaService, rootLogger)
|
||||
governorCtrl := governor.NewController(governorService, rootLogger)
|
||||
guardianCtrl := guardian.NewController(rootLogger)
|
||||
heartbeatsCtrl := heartbeats.NewController(heartbeatsService, rootLogger)
|
||||
|
||||
// Set up route handlers
|
||||
apiV1 := app.Group("/v1")
|
||||
|
||||
// signedVAA resource
|
||||
signedVAA := apiV1.Group("/signed_vaa")
|
||||
signedVAA.Get("/:chain/:emitter/:sequence", vaaCtrl.FindSignedVAAByID)
|
||||
signedBatchVAA := apiV1.Group("/signed_batch_vaa")
|
||||
signedBatchVAA.Get("/:chain/:trxID/:nonce", vaaCtrl.FindSignedBatchVAAByID)
|
||||
|
||||
// guardianSet resource
|
||||
guardianSet := apiV1.Group("/guardianset")
|
||||
guardianSet.Get("/current", guardianCtrl.GetGuardianSet)
|
||||
|
||||
// heartbeats resource
|
||||
heartbeats := apiV1.Group("/heartbeats")
|
||||
heartbeats.Get("", heartbeatsCtrl.GetLastHeartbeats)
|
||||
|
||||
// governor resource
|
||||
gov := apiV1.Group("/governor")
|
||||
gov.Get("/available_notional_by_chain", governorCtrl.GetAvailNotionByChain)
|
||||
gov.Get("/enqueued_vaas", governorCtrl.GetEnqueuedVaas)
|
||||
gov.Get("/is_vaa_enqueued/:chain/:emitter/:sequence", governorCtrl.IsVaaEnqueued)
|
||||
gov.Get("/token_list", governorCtrl.GetTokenList)
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Package observations handle the request of VAA data from governor endpoint defined in the api.
|
||||
package vaa
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/vaa"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Controller definition.
|
||||
type Controller struct {
|
||||
srv *vaa.Service
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewController create a new controler.
|
||||
func NewController(serv *vaa.Service, logger *zap.Logger) *Controller {
|
||||
return &Controller{srv: serv, logger: logger.With(zap.String("module", "VaaController"))}
|
||||
}
|
||||
|
||||
// FindSignedVAAByID godoc
|
||||
// @Description get a VAA []byte from a chainID, emitter address and sequence.
|
||||
// @Tags Guardian
|
||||
// @ID guardians-find-signed-vaa
|
||||
// @Param chain_id path integer true "id of the blockchain"
|
||||
// @Param emitter path string true "address of the emitter"
|
||||
// @Param seq path integer true "sequence of the VAA"
|
||||
// @Success 200 {object} object{vaaBytes=[]byte}
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/signed_vaa/{chain_id}/{emitter}/{seq} [get]
|
||||
func (c *Controller) FindSignedVAAByID(ctx *fiber.Ctx) error {
|
||||
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO
|
||||
// check chainID is not Pyth. Pyth message are not stored with the other vaa.
|
||||
//if ChainIDPythNet == chainID {
|
||||
// return response.NewApiError(ctx, fiber.StatusBadRequest, response.InvalidParam,
|
||||
// "not supported for PythNet", nil)
|
||||
//}
|
||||
vaa, err := c.srv.FindById(ctx.Context(), chainID, *emitter, strconv.FormatUint(seq, 10))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response := struct {
|
||||
VaaBytes []byte `json:"vaaBytes"`
|
||||
}{
|
||||
VaaBytes: vaa.Data.Vaa,
|
||||
}
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
// FindSignedBatchVAAByID godoc
|
||||
// @Description get a batch of VAA []byte from a chainID, emitter address and sequence.
|
||||
// @Tags Guardian
|
||||
// @ID guardians-find-signed-batch-vaa
|
||||
// @Param chain_id path integer true "id of the blockchain"
|
||||
// @Param emitter path string true "address of the emitter"
|
||||
// @Param seq path integer true "sequence of the VAA"
|
||||
// @Success 200 {object} object{vaaBytes=[]byte}
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/signed_batch_vaa/{chain_id}/{emitter}/sequence/{seq} [get]
|
||||
func (c *Controller) FindSignedBatchVAAByID(ctx *fiber.Ctx) error {
|
||||
return response.NewApiError(ctx, fiber.StatusNotImplemented, response.Unimplemented, "not yet implemented", nil)
|
||||
}
|
|
@ -2,23 +2,21 @@
|
|||
package governor
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/governor"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
_ "github.com/wormhole-foundation/wormhole-explorer/api/response" // needed by swaggo docs
|
||||
"github.com/wormhole-foundation/wormhole/sdk/vaa"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Controller definition.
|
||||
type Controller struct {
|
||||
srv *Service
|
||||
srv *governor.Service
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewController create a new controler.
|
||||
func NewController(serv *Service, logger *zap.Logger) *Controller {
|
||||
func NewController(serv *governor.Service, logger *zap.Logger) *Controller {
|
||||
return &Controller{srv: serv, logger: logger.With(zap.String("module", "GovernorController"))}
|
||||
}
|
||||
|
||||
|
@ -285,160 +283,3 @@ func (c *Controller) GetEnqueuedVaasByChainID(ctx *fiber.Ctx) error {
|
|||
}
|
||||
return ctx.JSON(enqueuedVaas)
|
||||
}
|
||||
|
||||
// AvailableNotionalResponse response compatible with grpc api.
|
||||
type AvailableNotionalResponse struct {
|
||||
Entries []*AvailableNotionalItemResponse `json:"entries"`
|
||||
}
|
||||
|
||||
type AvailableNotionalItemResponse struct {
|
||||
ChainID vaa.ChainID `json:"chainId"`
|
||||
AvailableNotional string `json:"remainingAvailableNotional"`
|
||||
NotionalLimit string `json:"notionalLimit"`
|
||||
MaxTransactionSize string `json:"bigTransactionSize"`
|
||||
}
|
||||
|
||||
// GetAvailNotionByChain godoc
|
||||
// @Description Get available notional by chainID
|
||||
// @Description Since from the wormhole-explorer point of view it is not a node, but has the information of all nodes,
|
||||
// @Description in order to build the endpoints it was assumed:
|
||||
// @Description There are N number of remainingAvailableNotional values in the GovernorConfig collection. N = number of guardians
|
||||
// @Description for a chainID. The smallest remainingAvailableNotional value for a chainID is used for the endpoint response.
|
||||
// @Tags Guardian
|
||||
// @ID governor-available-notional-by-chain
|
||||
// @Success 200 {object} AvailableNotionalResponse
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/governor/available_notional_by_chain [get]
|
||||
func (c *Controller) GetAvailNotionByChain(ctx *fiber.Ctx) error {
|
||||
// call service to get available notional by chainID
|
||||
availableNotional, err := c.srv.GetAvailNotionByChain(ctx.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build response compatible with node grpc api.
|
||||
entries := make([]*AvailableNotionalItemResponse, 0, len(availableNotional))
|
||||
for _, v := range availableNotional {
|
||||
r := AvailableNotionalItemResponse{
|
||||
ChainID: v.ChainID,
|
||||
AvailableNotional: v.AvailableNotional.String(),
|
||||
NotionalLimit: v.NotionalLimit.String(),
|
||||
MaxTransactionSize: v.MaxTransactionSize.String(),
|
||||
}
|
||||
entries = append(entries, &r)
|
||||
}
|
||||
response := AvailableNotionalResponse{
|
||||
Entries: entries,
|
||||
}
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
// AvailableNotionalResponse response compatible with grpc api.
|
||||
type EnqueuedVaaResponse struct {
|
||||
Entries []*EnqueuedVaaItemResponse `json:"entries"`
|
||||
}
|
||||
|
||||
type EnqueuedVaaItemResponse struct {
|
||||
EmitterChain vaa.ChainID `json:"emitterChain"`
|
||||
EmitterAddress string `json:"emitterAddress"`
|
||||
Sequence uint64 `json:"sequence"`
|
||||
ReleaseTime int64 `json:"releaseTime"`
|
||||
NotionalValue string `json:"notionalValue"`
|
||||
TxHash string `json:"txHash"`
|
||||
}
|
||||
|
||||
// GetEnqueuedVaas godoc
|
||||
// @Description Get enqueued VAAs
|
||||
// @Tags Guardian
|
||||
// @ID guardians-enqueued-vaas
|
||||
// @Success 200 {object} EnqueuedVaaResponse
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/governor/enqueued_vaas [get]
|
||||
func (c *Controller) GetEnqueuedVaas(ctx *fiber.Ctx) error {
|
||||
enqueuedVaa, err := c.srv.GetEnqueuedVaas(ctx.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build response compatible with node grpc api.
|
||||
entries := make([]*EnqueuedVaaItemResponse, 0, len(enqueuedVaa))
|
||||
for _, v := range enqueuedVaa {
|
||||
seqUint64, err := strconv.ParseUint(v.Sequence, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r := EnqueuedVaaItemResponse{
|
||||
EmitterChain: v.EmitterChain,
|
||||
EmitterAddress: v.EmitterAddress,
|
||||
Sequence: seqUint64,
|
||||
ReleaseTime: v.ReleaseTime,
|
||||
NotionalValue: v.NotionalValue.String(),
|
||||
TxHash: v.TxHash,
|
||||
}
|
||||
entries = append(entries, &r)
|
||||
}
|
||||
response := EnqueuedVaaResponse{
|
||||
Entries: entries,
|
||||
}
|
||||
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
// IsVaaEnqueued godoc
|
||||
// @Description Check if vaa is enqueued
|
||||
// @Tags Guardian
|
||||
// @ID guardians-is-vaa-enqueued
|
||||
// @Param chain_id path integer true "id of the blockchain"
|
||||
// @Param emitter path string true "address of the emitter"
|
||||
// @Param seq path integer true "sequence of the vaa"
|
||||
// @Success 200 {object} EnqueuedVaaResponse
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/governor/is_vaa_enqueued/{chain_id}/{emitter}/{seq} [get]
|
||||
func (c *Controller) IsVaaEnqueued(ctx *fiber.Ctx) error {
|
||||
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isEnqueued, err := c.srv.IsVaaEnqueued(ctx.Context(), chainID, *emitter, strconv.FormatUint(seq, 10))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build reponse compatible with node grpc api.
|
||||
response := struct {
|
||||
IsEnqueued bool `json:"isEnqueued"`
|
||||
}{
|
||||
IsEnqueued: isEnqueued,
|
||||
}
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
// GetTokenList godoc
|
||||
// @Description Get token list
|
||||
// @Description Since from the wormhole-explorer point of view it is not a node, but has the information of all nodes,
|
||||
// @Description in order to build the endpoints it was assumed:
|
||||
// @Description For tokens with the same originChainId and originAddress and different price values for each node,
|
||||
// @Description the price that has most occurrences in all the nodes for an originChainId and originAddress is returned.
|
||||
// @Tags Guardian
|
||||
// @ID guardians-token-list
|
||||
// @Success 200 {object} []TokenList
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/governor/token_list [get]
|
||||
func (c *Controller) GetTokenList(ctx *fiber.Ctx) error {
|
||||
tokenList, err := c.srv.GetTokenList(ctx.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build reponse compatible with node grpc api.
|
||||
response := struct {
|
||||
Entries []*TokenList `json:"entries"`
|
||||
}{
|
||||
Entries: tokenList,
|
||||
}
|
||||
return ctx.JSON(response)
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
package infrastructure
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/infrastructure"
|
||||
)
|
||||
|
||||
// Controller definition.
|
||||
type Controller struct {
|
||||
srv *Service
|
||||
srv *infrastructure.Service
|
||||
}
|
||||
|
||||
// NewController creates a Controller instance.
|
||||
func NewController(serv *Service) *Controller {
|
||||
func NewController(serv *infrastructure.Service) *Controller {
|
||||
return &Controller{srv: serv}
|
||||
}
|
||||
|
|
@ -5,18 +5,19 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/observations"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Controller definition.
|
||||
type Controller struct {
|
||||
srv *Service
|
||||
srv *observations.Service
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewController create a new controler.
|
||||
func NewController(srv *Service, logger *zap.Logger) *Controller {
|
||||
func NewController(srv *observations.Service, logger *zap.Logger) *Controller {
|
||||
return &Controller{
|
||||
srv: srv,
|
||||
logger: logger.With(zap.String("module", "ObservationsController")),
|
|
@ -0,0 +1,95 @@
|
|||
package wormscan
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cache"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
govsvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/governor"
|
||||
infrasvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/infrastructure"
|
||||
obssvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/observations"
|
||||
vaasvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/vaa"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/wormscan/governor"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/wormscan/infrastructure"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/wormscan/observations"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/wormscan/vaa"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var cacheConfig = cache.Config{
|
||||
Next: func(c *fiber.Ctx) bool {
|
||||
return c.Query("refresh") == "true"
|
||||
},
|
||||
Expiration: 1 * time.Second,
|
||||
CacheControl: true,
|
||||
StoreResponseHeaders: true,
|
||||
}
|
||||
|
||||
// RegisterRoutes sets up the handlers for the Wormscan API.
|
||||
func RegisterRoutes(
|
||||
app *fiber.App,
|
||||
rootLogger *zap.Logger,
|
||||
vaaService *vaasvc.Service,
|
||||
obsService *obssvc.Service,
|
||||
governorService *govsvc.Service,
|
||||
infrastructureService *infrasvc.Service,
|
||||
) {
|
||||
|
||||
// Set up controllers
|
||||
vaaCtrl := vaa.NewController(vaaService, rootLogger)
|
||||
observationsCtrl := observations.NewController(obsService, rootLogger)
|
||||
governorCtrl := governor.NewController(governorService, rootLogger)
|
||||
infrastructureCtrl := infrastructure.NewController(infrastructureService)
|
||||
|
||||
// Set up route handlers
|
||||
api := app.Group("/api/v1")
|
||||
api.Use(cors.New()) // TODO CORS restrictions?
|
||||
api.Use(middleware.ExtractPagination)
|
||||
|
||||
// monitoring
|
||||
api.Get("/health", infrastructureCtrl.HealthCheck)
|
||||
api.Get("/ready", infrastructureCtrl.ReadyCheck)
|
||||
|
||||
// vaas resource
|
||||
vaas := api.Group("/vaas")
|
||||
vaas.Use(cache.New(cacheConfig))
|
||||
vaas.Get("/vaa-counts", vaaCtrl.GetVaaCount)
|
||||
vaas.Get("/", vaaCtrl.FindAll)
|
||||
vaas.Get("/:chain", vaaCtrl.FindByChain)
|
||||
vaas.Get("/:chain/:emitter", vaaCtrl.FindByEmitter)
|
||||
vaas.Get("/:chain/:emitter/:sequence", vaaCtrl.FindById)
|
||||
|
||||
// oservations resource
|
||||
observations := api.Group("/observations")
|
||||
observations.Get("/", observationsCtrl.FindAll)
|
||||
observations.Get("/:chain", observationsCtrl.FindAllByChain)
|
||||
observations.Get("/:chain/:emitter", observationsCtrl.FindAllByEmitter)
|
||||
observations.Get("/:chain/:emitter/:sequence", observationsCtrl.FindAllByVAA)
|
||||
observations.Get("/:chain/:emitter/:sequence/:signer/:hash", observationsCtrl.FindOne)
|
||||
|
||||
// governor resources
|
||||
governor := api.Group("/governor")
|
||||
governorLimit := governor.Group("/limit")
|
||||
governorLimit.Get("/", governorCtrl.GetGovernorLimit)
|
||||
|
||||
governorConfigs := governor.Group("/config")
|
||||
governorConfigs.Get("/", governorCtrl.FindGovernorConfigurations)
|
||||
governorConfigs.Get("/:guardian_address", governorCtrl.FindGovernorConfigurationByGuardianAddress)
|
||||
|
||||
governorStatus := governor.Group("/status")
|
||||
governorStatus.Get("/", governorCtrl.FindGovernorStatus)
|
||||
governorStatus.Get("/:guardian_address", governorCtrl.FindGovernorStatusByGuardianAddress)
|
||||
|
||||
governorNotional := governor.Group("/notional")
|
||||
governorNotional.Get("/limit/", governorCtrl.FindNotionalLimit)
|
||||
governorNotional.Get("/limit/:chain", governorCtrl.GetNotionalLimitByChainID)
|
||||
governorNotional.Get("/available/", governorCtrl.GetAvailableNotional)
|
||||
governorNotional.Get("/available/:chain", governorCtrl.GetAvailableNotionalByChainID)
|
||||
governorNotional.Get("/max_available/:chain", governorCtrl.GetMaxNotionalAvailableByChainID)
|
||||
|
||||
enqueueVaas := governor.Group("/enqueued_vaas")
|
||||
enqueueVaas.Get("/", governorCtrl.GetEnqueueVaas)
|
||||
enqueueVaas.Get("/:chain", governorCtrl.GetEnqueuedVaasByChainID)
|
||||
}
|
|
@ -5,19 +5,19 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/vaa"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Controller definition.
|
||||
type Controller struct {
|
||||
srv *Service
|
||||
srv *vaa.Service
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewController create a new controler.
|
||||
func NewController(serv *Service, logger *zap.Logger) *Controller {
|
||||
func NewController(serv *vaa.Service, logger *zap.Logger) *Controller {
|
||||
return &Controller{srv: serv, logger: logger.With(zap.String("module", "VaaController"))}
|
||||
}
|
||||
|
||||
|
@ -123,56 +123,6 @@ func (c *Controller) FindById(ctx *fiber.Ctx) error {
|
|||
return ctx.JSON(vaa)
|
||||
}
|
||||
|
||||
// FindSignedVAAByID godoc
|
||||
// @Description get a VAA []byte from a chainID, emitter address and sequence.
|
||||
// @Tags Guardian
|
||||
// @ID guardians-find-signed-vaa
|
||||
// @Param chain_id path integer true "id of the blockchain"
|
||||
// @Param emitter path string true "address of the emitter"
|
||||
// @Param seq path integer true "sequence of the VAA"
|
||||
// @Success 200 {object} object{vaaBytes=[]byte}
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/signed_vaa/{chain_id}/{emitter}/{seq} [get]
|
||||
func (c *Controller) FindSignedVAAByID(ctx *fiber.Ctx) error {
|
||||
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO
|
||||
// check chainID is not Pyth. Pyth message are not stored with the other vaa.
|
||||
//if ChainIDPythNet == chainID {
|
||||
// return response.NewApiError(ctx, fiber.StatusBadRequest, response.InvalidParam,
|
||||
// "not supported for PythNet", nil)
|
||||
//}
|
||||
vaa, err := c.srv.FindById(ctx.Context(), chainID, *emitter, strconv.FormatUint(seq, 10))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response := struct {
|
||||
VaaBytes []byte `json:"vaaBytes"`
|
||||
}{
|
||||
VaaBytes: vaa.Data.Vaa,
|
||||
}
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
// FindSignedBatchVAAByID godoc
|
||||
// @Description get a batch of VAA []byte from a chainID, emitter address and sequence.
|
||||
// @Tags Guardian
|
||||
// @ID guardians-find-signed-batch-vaa
|
||||
// @Param chain_id path integer true "id of the blockchain"
|
||||
// @Param emitter path string true "address of the emitter"
|
||||
// @Param seq path integer true "sequence of the VAA"
|
||||
// @Success 200 {object} object{vaaBytes=[]byte}
|
||||
// @Failure 400
|
||||
// @Failure 500
|
||||
// @Router /v1/signed_batch_vaa/{chain_id}/{emitter}/sequence/{seq} [get]
|
||||
func (c *Controller) FindSignedBatchVAAByID(ctx *fiber.Ctx) error {
|
||||
return response.NewApiError(ctx, fiber.StatusNotImplemented, response.Unimplemented, "not yet implemented", nil)
|
||||
}
|
||||
|
||||
// GetVaaCount godoc
|
||||
// @Description Returns the total number of VAAs emitted for each blockchain.
|
||||
// @Tags Wormscan
|
Loading…
Reference in New Issue