[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:
agodnic 2023-01-27 12:58:37 -03:00 committed by GitHub
parent 215a00ccc8
commit e929104ed9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 434 additions and 321 deletions

View File

@ -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)

View File

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

View File

@ -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))

View File

@ -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
}

View File

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

View File

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

View File

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

View File

@ -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}
}

View File

@ -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")),

View File

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

View File

@ -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