[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"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ansrivas/fiberprometheus/v2"
|
"github.com/ansrivas/fiberprometheus/v2"
|
||||||
"github.com/gofiber/adaptor/v2"
|
"github.com/gofiber/adaptor/v2"
|
||||||
"github.com/gofiber/fiber/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/cors"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
"github.com/gofiber/fiber/v2/middleware/requestid"
|
"github.com/gofiber/fiber/v2/middleware/requestid"
|
||||||
|
@ -20,7 +18,6 @@ import (
|
||||||
|
|
||||||
ipfslog "github.com/ipfs/go-log/v2"
|
ipfslog "github.com/ipfs/go-log/v2"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/governor"
|
"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/heartbeats"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/infrastructure"
|
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/infrastructure"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/observations"
|
"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/internal/db"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
"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"
|
rpcApi "github.com/wormhole-foundation/wormhole-explorer/api/rpc"
|
||||||
"go.uber.org/zap"
|
"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
|
//go:embed docs/swagger.json
|
||||||
var swagger []byte
|
var swagger []byte
|
||||||
|
|
||||||
|
@ -116,111 +101,40 @@ func main() {
|
||||||
// Get cache get function
|
// Get cache get function
|
||||||
cacheGetFunc := NewCache(cfg, rootLogger)
|
cacheGetFunc := NewCache(cfg, rootLogger)
|
||||||
|
|
||||||
// Setup repositories
|
// Set up repositories
|
||||||
vaaRepo := vaa.NewRepository(db, rootLogger)
|
vaaRepo := vaa.NewRepository(db, rootLogger)
|
||||||
obsRepo := observations.NewRepository(db, rootLogger)
|
obsRepo := observations.NewRepository(db, rootLogger)
|
||||||
governorRepo := governor.NewRepository(db, rootLogger)
|
governorRepo := governor.NewRepository(db, rootLogger)
|
||||||
infrastructureRepo := infrastructure.NewRepository(db, rootLogger)
|
infrastructureRepo := infrastructure.NewRepository(db, rootLogger)
|
||||||
heartbeatsRepo := heartbeats.NewRepository(db, rootLogger)
|
heartbeatsRepo := heartbeats.NewRepository(db, rootLogger)
|
||||||
|
|
||||||
// Setup services
|
// Set up services
|
||||||
vaaService := vaa.NewService(vaaRepo, cacheGetFunc, rootLogger)
|
vaaService := vaa.NewService(vaaRepo, cacheGetFunc, rootLogger)
|
||||||
obsService := observations.NewService(obsRepo, rootLogger)
|
obsService := observations.NewService(obsRepo, rootLogger)
|
||||||
governorService := governor.NewService(governorRepo, rootLogger)
|
governorService := governor.NewService(governorRepo, rootLogger)
|
||||||
infrastructureService := infrastructure.NewService(infrastructureRepo, rootLogger)
|
infrastructureService := infrastructure.NewService(infrastructureRepo, rootLogger)
|
||||||
heartbeatsService := heartbeats.NewService(heartbeatsRepo, rootLogger)
|
heartbeatsService := heartbeats.NewService(heartbeatsRepo, rootLogger)
|
||||||
|
|
||||||
// Setup controllers
|
// Set up a custom error handler
|
||||||
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.
|
|
||||||
response.SetEnableStackTrace(*cfg)
|
response.SetEnableStackTrace(*cfg)
|
||||||
app := fiber.New(fiber.Config{ErrorHandler: middleware.ErrorHandler})
|
app := fiber.New(fiber.Config{ErrorHandler: middleware.ErrorHandler})
|
||||||
|
|
||||||
// Middleware
|
// Configure middleware
|
||||||
prometheus := fiberprometheus.New("wormscan")
|
prometheus := fiberprometheus.New("wormscan")
|
||||||
prometheus.RegisterAt(app, "/metrics")
|
prometheus.RegisterAt(app, "/metrics")
|
||||||
app.Use(prometheus.Middleware)
|
app.Use(prometheus.Middleware)
|
||||||
|
|
||||||
app.Use(cors.New())
|
app.Use(cors.New())
|
||||||
app.Use(requestid.New())
|
app.Use(requestid.New())
|
||||||
app.Use(logger.New(logger.Config{
|
app.Use(logger.New(logger.Config{
|
||||||
Format: "level=info timestamp=${time} method=${method} path=${path} status${status} request_id=${locals:requestid}\n",
|
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)
|
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")
|
// Set up gRPC handlers
|
||||||
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)
|
|
||||||
|
|
||||||
handler := rpcApi.NewHandler(vaaService, heartbeatsService, governorService, rootLogger)
|
handler := rpcApi.NewHandler(vaaService, heartbeatsService, governorService, rootLogger)
|
||||||
grpcServer := rpcApi.NewServer(handler, rootLogger)
|
grpcServer := rpcApi.NewServer(handler, rootLogger)
|
||||||
grpcWebServer := grpcweb.WrapServer(grpcServer)
|
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 (
|
import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/guardian"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -37,12 +38,12 @@ type GuardianSet struct {
|
||||||
// @Router /v1/guardianset/current [get]
|
// @Router /v1/guardianset/current [get]
|
||||||
func (c *Controller) GetGuardianSet(ctx *fiber.Ctx) error {
|
func (c *Controller) GetGuardianSet(ctx *fiber.Ctx) error {
|
||||||
// check guardianSet exists.
|
// check guardianSet exists.
|
||||||
if len(ByIndex) == 0 {
|
if len(guardian.ByIndex) == 0 {
|
||||||
return response.NewApiError(ctx, fiber.StatusServiceUnavailable, response.Unavailable,
|
return response.NewApiError(ctx, fiber.StatusServiceUnavailable, response.Unavailable,
|
||||||
"guardian set not fetched from chain yet", nil)
|
"guardian set not fetched from chain yet", nil)
|
||||||
}
|
}
|
||||||
// get lasted guardianSet.
|
// get lasted guardianSet.
|
||||||
guardinSet := GetLatest()
|
guardinSet := guardian.GetLatest()
|
||||||
|
|
||||||
// get guardian addresses.
|
// get guardian addresses.
|
||||||
addresses := make([]string, len(guardinSet.Keys))
|
addresses := make([]string, len(guardinSet.Keys))
|
|
@ -5,18 +5,19 @@ import (
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/guardian"
|
"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"
|
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Controller definition.
|
// Controller definition.
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
srv *Service
|
srv *heartbeats.Service
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewController create a new controler.
|
// NewController create a new controler.
|
||||||
func NewController(srv *Service, logger *zap.Logger) *Controller {
|
func NewController(srv *heartbeats.Service, logger *zap.Logger) *Controller {
|
||||||
return &Controller{
|
return &Controller{
|
||||||
srv: srv,
|
srv: srv,
|
||||||
logger: logger.With(zap.String("module", "HeartbeatsController")),
|
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)
|
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildHeartbeatResponse(heartbeats []*HeartbeatDoc) *HeartbeatsResponse {
|
func buildHeartbeatResponse(heartbeats []*heartbeats.HeartbeatDoc) *HeartbeatsResponse {
|
||||||
if heartbeats == nil {
|
if heartbeats == nil {
|
||||||
return 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
|
package governor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"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/middleware"
|
||||||
_ "github.com/wormhole-foundation/wormhole-explorer/api/response" // needed by swaggo docs
|
_ "github.com/wormhole-foundation/wormhole-explorer/api/response" // needed by swaggo docs
|
||||||
"github.com/wormhole-foundation/wormhole/sdk/vaa"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Controller definition.
|
// Controller definition.
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
srv *Service
|
srv *governor.Service
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewController create a new controler.
|
// 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"))}
|
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)
|
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
|
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.
|
// Controller definition.
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
srv *Service
|
srv *infrastructure.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewController creates a Controller instance.
|
// NewController creates a Controller instance.
|
||||||
func NewController(serv *Service) *Controller {
|
func NewController(serv *infrastructure.Service) *Controller {
|
||||||
return &Controller{srv: serv}
|
return &Controller{srv: serv}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,19 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/observations"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Controller definition.
|
// Controller definition.
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
srv *Service
|
srv *observations.Service
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewController create a new controler.
|
// NewController create a new controler.
|
||||||
func NewController(srv *Service, logger *zap.Logger) *Controller {
|
func NewController(srv *observations.Service, logger *zap.Logger) *Controller {
|
||||||
return &Controller{
|
return &Controller{
|
||||||
srv: srv,
|
srv: srv,
|
||||||
logger: logger.With(zap.String("module", "ObservationsController")),
|
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"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"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/middleware"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Controller definition.
|
// Controller definition.
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
srv *Service
|
srv *vaa.Service
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewController create a new controler.
|
// 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"))}
|
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)
|
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
|
// GetVaaCount godoc
|
||||||
// @Description Returns the total number of VAAs emitted for each blockchain.
|
// @Description Returns the total number of VAAs emitted for each blockchain.
|
||||||
// @Tags Wormscan
|
// @Tags Wormscan
|
Loading…
Reference in New Issue