wormhole-explorer/api/routes/guardian/heartbeats/controller.go

126 lines
4.1 KiB
Go

package heartbeats
import (
"strconv"
"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 *heartbeats.Service
logger *zap.Logger
gs guardian.GuardianSet
}
// NewController create a new controler.
func NewController(srv *heartbeats.Service, logger *zap.Logger, p2pNetwork string) *Controller {
return &Controller{
srv: srv,
logger: logger.With(zap.String("module", "HeartbeatsController")),
gs: guardian.GetByEnv(p2pNetwork),
}
}
// HeartbeatsResponse response.
type HeartbeatsResponse struct {
Heartbeats []*HeartbeatResponse `json:"entries"`
}
type HeartbeatResponse struct {
VerifiedGuardianAddr string `json:"verifiedGuardianAddr"`
P2PNodeAddr string `json:"p2pNodeAddr"`
RawHeartbeat *RawHeartbeat `json:"rawHeartbeat"`
}
type RawHeartbeat struct {
NodeName string `json:"nodeName"`
Counter string `json:"counter"`
Timestamp string `json:"timestamp"`
Networks []*HeartbeatNetworkResponse `json:"networks"`
Version string `json:"version"`
GuardianAddr string `json:"guardianAddr"`
BootTimestamp string `json:"bootTimestamp"`
Features []string `json:"features"`
}
// HeartbeatNetwork definition.
type HeartbeatNetworkResponse struct {
ID int64 `bson:"id" json:"id"`
Height string `bson:"height" json:"height"`
ContractAddress string `bson:"contractaddress" json:"contractAddress"`
ErrorCount string `bson:"errorcount" json:"errorCount"`
}
// GetGuardianSet godoc
// @Description Get heartbeats for guardians
// @Tags Guardian
// @ID guardians-hearbeats
// @Success 200 {object} HeartbeatsResponse
// @Failure 400
// @Failure 500
// @Router /v1/heartbeats [get]
func (c *Controller) GetLastHeartbeats(ctx *fiber.Ctx) error {
// check guardianSet exists.
if len(c.gs.GstByIndex) == 0 {
return response.NewApiError(ctx, fiber.StatusServiceUnavailable, response.Unavailable,
"guardian set not fetched from chain yet", nil)
}
// get lasted guardianSet.
guardianSet := c.gs.GetLatest()
guardianAddresses := guardianSet.KeysAsHexStrings()
// get last heartbeats by ids.
heartbeats, err := c.srv.GetHeartbeatsByIds(ctx.Context(), guardianAddresses)
if err != nil {
return err
}
// build heartbeats response compatible with grpc api response.
response := buildHeartbeatResponse(heartbeats)
return ctx.Status(fiber.StatusOK).JSON(response)
}
func buildHeartbeatResponse(heartbeats []*heartbeats.HeartbeatDoc) *HeartbeatsResponse {
if heartbeats == nil {
return nil
}
heartbeatResponses := make([]*HeartbeatResponse, 0, len(heartbeats))
for _, heartbeat := range heartbeats {
networkResponses := make([]*HeartbeatNetworkResponse, 0, len(heartbeat.Networks))
for _, network := range heartbeat.Networks {
networkResponse := &HeartbeatNetworkResponse{
ID: network.ID,
Height: strconv.Itoa(int(network.Height)),
ContractAddress: network.ContractAddress,
ErrorCount: strconv.Itoa(int(network.ErrorCount)),
}
networkResponses = append(networkResponses, networkResponse)
}
hr := HeartbeatResponse{
VerifiedGuardianAddr: heartbeat.ID,
P2PNodeAddr: "", // not exists in heartbeats mongo collection.
RawHeartbeat: &RawHeartbeat{
NodeName: heartbeat.NodeName,
Counter: strconv.Itoa(int(heartbeat.Counter)),
Timestamp: strconv.Itoa(int(heartbeat.Timestamp)),
Networks: networkResponses,
Version: heartbeat.Version,
GuardianAddr: heartbeat.GuardianAddr,
BootTimestamp: strconv.Itoa(int(heartbeat.BootTimestamp)),
Features: heartbeat.Features,
},
}
heartbeatResponses = append(heartbeatResponses, &hr)
}
return &HeartbeatsResponse{
Heartbeats: heartbeatResponses,
}
}