2022-09-07 11:43:05 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-11-21 06:18:33 -08:00
|
|
|
"flag"
|
2023-07-24 07:24:39 -07:00
|
|
|
"log"
|
2023-08-07 12:05:08 -07:00
|
|
|
"time"
|
2022-11-08 11:03:43 -08:00
|
|
|
|
2022-09-07 11:43:05 -07:00
|
|
|
"fmt"
|
2022-11-08 06:53:12 -08:00
|
|
|
"os"
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
healthcheck "github.com/wormhole-foundation/wormhole-explorer/common/health"
|
2023-03-23 11:36:50 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/common/logger"
|
2024-02-06 16:00:45 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/builder"
|
2023-01-31 06:38:17 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/config"
|
2024-02-06 16:00:45 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/gossip"
|
2022-11-16 10:48:16 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/guardiansets"
|
2023-02-13 12:28:34 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/internal/health"
|
2022-11-16 10:48:16 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/migration"
|
2022-11-21 06:18:33 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/processor"
|
2023-10-18 07:18:32 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/producer"
|
2022-12-05 12:41:37 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/server"
|
2022-11-16 10:48:16 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/fly/storage"
|
|
|
|
|
2022-09-07 11:43:05 -07:00
|
|
|
"github.com/certusone/wormhole/node/pkg/common"
|
|
|
|
"github.com/certusone/wormhole/node/pkg/p2p"
|
|
|
|
"github.com/certusone/wormhole/node/pkg/supervisor"
|
2023-12-20 10:10:30 -08:00
|
|
|
crypto2 "github.com/ethereum/go-ethereum/crypto"
|
|
|
|
"github.com/libp2p/go-libp2p/core/crypto"
|
2022-09-07 11:43:05 -07:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
func main() {
|
2023-10-18 07:18:32 -07:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// Node's main lifecycle context.
|
|
|
|
rootCtx, rootCtxCancel := context.WithCancel(context.Background())
|
|
|
|
defer rootCtxCancel()
|
2023-10-18 07:18:32 -07:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
isLocal := flag.Bool("local", false, "a bool")
|
|
|
|
flag.Parse()
|
2023-03-06 09:36:14 -08:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// Load configuration
|
|
|
|
cfg, err := config.New(rootCtx, isLocal)
|
2023-07-24 07:24:39 -07:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error creating config", err)
|
|
|
|
}
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// Get p2p values to connect p2p network
|
|
|
|
p2pNetworkConfig, err := cfg.GetP2pNetwork()
|
2023-01-30 10:51:13 -08:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
nodeKeyPath := "/tmp/node.key"
|
2022-09-07 11:43:05 -07:00
|
|
|
common.SetRestrictiveUmask()
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
logger := logger.New("wormhole-fly", logger.WithLevel(cfg.LogLevel))
|
2022-11-21 06:18:33 -08:00
|
|
|
|
2022-09-07 11:43:05 -07:00
|
|
|
// Verify flags
|
|
|
|
if nodeKeyPath == "" {
|
|
|
|
logger.Fatal("Please specify --nodeKey")
|
|
|
|
}
|
2023-01-30 10:51:13 -08:00
|
|
|
if p2pNetworkConfig.P2pBootstrap == "" {
|
2022-09-07 11:43:05 -07:00
|
|
|
logger.Fatal("Please specify --bootstrap")
|
|
|
|
}
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// New alert client
|
|
|
|
alertClient, err := builder.NewAlertClient(cfg)
|
2023-06-22 14:59:23 -07:00
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("could not create alert client", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// New metrics client
|
|
|
|
metrics := builder.NewMetrics(cfg)
|
2022-09-07 11:43:05 -07:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// New database session
|
|
|
|
db, err := builder.NewDatabase(rootCtx, cfg, logger)
|
2022-11-07 09:47:10 -08:00
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("could not connect to DB", zap.Error(err))
|
|
|
|
}
|
2022-11-08 09:58:22 -08:00
|
|
|
|
2022-11-21 06:18:33 -08:00
|
|
|
// Run the database migration.
|
2023-08-07 12:05:08 -07:00
|
|
|
err = migration.Run(db.Database)
|
2022-11-08 09:58:22 -08:00
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("error running migration", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2023-10-18 07:18:32 -07:00
|
|
|
// Creates a callback to publish VAA messages to a redis pubsub
|
2024-02-06 16:00:45 -08:00
|
|
|
vaaRedisProducerFunc, err := builder.NewVAARedisProducerFunc(cfg, logger)
|
2023-10-18 07:18:32 -07:00
|
|
|
if err != nil {
|
2024-02-06 16:00:45 -08:00
|
|
|
logger.Fatal("could not create vaa redis producer", zap.Error(err))
|
2023-10-18 07:18:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a composite callback to publish VAA messages to a redis pubsub
|
|
|
|
producerFunc := producer.NewComposite(vaaRedisProducerFunc)
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
txHashStore, err := builder.NewTxHashStore(rootCtx, cfg, metrics, db.Database, logger)
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("could not create tx hash store", zap.Error(err))
|
|
|
|
}
|
|
|
|
repository := storage.NewRepository(alertClient, metrics, db.Database, producerFunc, txHashStore, logger)
|
2022-09-07 11:43:05 -07:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
deduplicator, err := builder.NewDeduplicator(logger)
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("could not create deduplicator", zap.Error(err))
|
|
|
|
}
|
2022-09-07 11:43:05 -07:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
channels := builder.NewGossipChannels(cfg)
|
2022-11-08 06:53:12 -08:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
gst := common.NewGuardianSetState(channels.HeartbeatChannel)
|
2023-01-31 06:38:17 -08:00
|
|
|
|
2022-09-07 11:43:05 -07:00
|
|
|
// Bootstrap guardian set, otherwise heartbeats would be skipped
|
2022-09-11 13:57:01 -07:00
|
|
|
// TODO: fetch this and probably figure out how to update it live
|
2023-07-25 11:39:27 -07:00
|
|
|
guardianSetHistory := guardiansets.GetByEnv(p2pNetworkConfig.Enviroment, alertClient)
|
2023-02-02 05:17:42 -08:00
|
|
|
gsLastet := guardianSetHistory.GetLatest()
|
2023-01-31 06:38:17 -08:00
|
|
|
gst.Set(&gsLastet)
|
2022-09-07 11:43:05 -07:00
|
|
|
|
2022-10-22 17:58:38 -07:00
|
|
|
// Ignore observation requests
|
|
|
|
// Note: without this, the whole program hangs on observation requests
|
2024-02-06 16:00:45 -08:00
|
|
|
discardMessages(rootCtx, channels.ObsvReqChannel)
|
|
|
|
guardianCheck := health.NewGuardianCheck(cfg.MaxHealthTimeSeconds)
|
2022-10-22 17:58:38 -07:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
healthObservations, observationQueueConsume, observationPublish := builder.NewObservationConsumePublish(rootCtx, cfg, logger)
|
2024-02-07 12:02:57 -08:00
|
|
|
observationGossipConsumer := processor.NewObservationGossipConsumer(observationPublish, gst, p2pNetworkConfig.Enviroment,
|
|
|
|
cfg.ObservationsChannelSize, cfg.ObservationsWorkersSize, metrics, txHashStore, repository, logger)
|
2024-02-06 16:00:45 -08:00
|
|
|
observationQueueConsumer := processor.NewObservationQueueConsumer(observationQueueConsume, repository, metrics, logger)
|
|
|
|
observationGossipConsumer.Start(rootCtx)
|
|
|
|
observationQueueConsumer.Start(rootCtx)
|
2023-12-18 05:41:07 -08:00
|
|
|
|
2022-09-15 12:17:28 -07:00
|
|
|
// Log observations
|
2024-02-06 16:00:45 -08:00
|
|
|
observationHandler := gossip.NewObservationHandler(channels.ObsvChannel, observationGossipConsumer.Push, guardianCheck, metrics)
|
|
|
|
observationHandler.Start(rootCtx)
|
2022-09-07 11:43:05 -07:00
|
|
|
|
|
|
|
// Log signed VAAs
|
2022-11-21 06:18:33 -08:00
|
|
|
// Creates two callbacks
|
2024-02-06 16:00:45 -08:00
|
|
|
healthVaas, vaaQueueConsume, nonPythVaaPublish := builder.NewVAAConsumePublish(rootCtx, cfg, logger)
|
2023-01-05 11:40:24 -08:00
|
|
|
// Create a vaa notifier
|
2024-02-06 16:00:45 -08:00
|
|
|
notifierFunc := builder.NewVAANotifierFunc(cfg, logger)
|
2022-11-21 06:18:33 -08:00
|
|
|
// Creates a instance to consume VAA messages from Gossip network and handle the messages
|
|
|
|
// When recive a message, the message filter by deduplicator
|
|
|
|
// if VAA is from pyhnet should be saved directly to repository
|
|
|
|
// if VAA is from non pyhnet should be publish with nonPythVaaPublish
|
2023-06-26 08:47:22 -07:00
|
|
|
vaaGossipConsumer := processor.NewVAAGossipConsumer(&guardianSetHistory, deduplicator, nonPythVaaPublish, repository.UpsertVaa, metrics, logger)
|
2022-11-21 06:18:33 -08:00
|
|
|
// Creates a instance to consume VAA messages (non pyth) from a queue and store in a storage
|
2023-06-26 08:47:22 -07:00
|
|
|
vaaQueueConsumer := processor.NewVAAQueueConsumer(vaaQueueConsume, repository, notifierFunc, metrics, logger)
|
2022-11-21 06:18:33 -08:00
|
|
|
// Creates a wrapper that splits the incoming VAAs into 2 channels (pyth to non pyth) in order
|
|
|
|
// to be able to process them in a differentiated way
|
2024-02-06 16:00:45 -08:00
|
|
|
vaaGossipConsumerSplitter := processor.NewVAAGossipSplitterConsumer(vaaGossipConsumer.Push, cfg.VaasWorkersSize, logger, processor.WithSize(cfg.VaasChannelSize))
|
2022-11-21 06:18:33 -08:00
|
|
|
vaaQueueConsumer.Start(rootCtx)
|
|
|
|
vaaGossipConsumerSplitter.Start(rootCtx)
|
|
|
|
|
2022-12-05 12:41:37 -08:00
|
|
|
// start fly http server.
|
2024-02-06 16:00:45 -08:00
|
|
|
healthChecks := []healthcheck.Check{healthObservations, healthVaas, builder.CheckGuardian(guardianCheck)}
|
|
|
|
pprofEnabled := cfg.PprofEnabled
|
|
|
|
server := server.NewServer(cfg.ApiPort, guardianCheck, logger, repository, pprofEnabled, alertClient, healthChecks...)
|
2022-12-05 12:41:37 -08:00
|
|
|
server.Start()
|
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// VAA handler
|
|
|
|
vaaHandler := gossip.NewVaaHandler(p2pNetworkConfig, metrics, channels.SignedInChannel, vaaGossipConsumerSplitter.Push, guardianCheck, logger)
|
|
|
|
vaaHandler.Start(rootCtx)
|
2022-09-07 11:43:05 -07:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// Heartbeats handler
|
|
|
|
hearbeatsHandler := gossip.NewHeartbeatsHandler(channels.HeartbeatChannel, repository, guardianCheck, metrics, logger)
|
|
|
|
hearbeatsHandler.Start(rootCtx)
|
2022-09-07 11:43:05 -07:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// Governor config handler
|
|
|
|
governorConfigHandler := gossip.NewGovernorConfigHandler(channels.GovConfigChannel, repository, guardianCheck, metrics, logger)
|
|
|
|
governorConfigHandler.Start(rootCtx)
|
2022-11-08 06:53:12 -08:00
|
|
|
|
2024-02-06 16:00:45 -08:00
|
|
|
// Governor status handler
|
|
|
|
governorStatusHandler := gossip.NewGovernorStatusHandler(channels.GovStatusChannel, repository, guardianCheck, metrics, logger)
|
|
|
|
governorStatusHandler.Start(rootCtx)
|
2022-11-08 06:53:12 -08:00
|
|
|
|
2022-09-07 11:43:05 -07:00
|
|
|
// Load p2p private key
|
|
|
|
var priv crypto.PrivKey
|
|
|
|
priv, err = common.GetOrCreateNodeKey(logger, nodeKeyPath)
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("Failed to load node key", zap.Error(err))
|
|
|
|
}
|
2023-12-20 10:10:30 -08:00
|
|
|
keyBytes, err := priv.Raw()
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("failed to deserialize raw private key", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
gk, err := crypto2.ToECDSA(keyBytes[:32])
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("failed to deserialize raw key data", zap.Error(err))
|
|
|
|
}
|
2022-09-07 11:43:05 -07:00
|
|
|
|
|
|
|
// Run supervisor.
|
|
|
|
supervisor.New(rootCtx, logger, func(ctx context.Context) error {
|
2023-08-23 06:19:16 -07:00
|
|
|
components := p2p.DefaultComponents()
|
|
|
|
components.Port = cfg.P2pPort
|
2024-02-06 16:00:45 -08:00
|
|
|
components.WarnChannelOverflow = true
|
2023-01-23 11:13:59 -08:00
|
|
|
if err := supervisor.Run(ctx, "p2p",
|
2023-12-20 10:10:30 -08:00
|
|
|
p2p.Run(
|
2024-02-06 16:00:45 -08:00
|
|
|
channels.ObsvChannel,
|
|
|
|
channels.ObsvReqChannel,
|
2023-12-20 10:10:30 -08:00
|
|
|
nil,
|
2024-02-06 16:00:45 -08:00
|
|
|
channels.SendChannel,
|
|
|
|
channels.SignedInChannel,
|
2023-12-20 10:10:30 -08:00
|
|
|
priv,
|
|
|
|
gk,
|
|
|
|
gst,
|
|
|
|
p2pNetworkConfig.P2pNetworkID,
|
|
|
|
p2pNetworkConfig.P2pBootstrap,
|
|
|
|
"",
|
|
|
|
false,
|
|
|
|
rootCtxCancel,
|
|
|
|
nil,
|
|
|
|
nil,
|
2024-02-06 16:00:45 -08:00
|
|
|
channels.GovConfigChannel,
|
|
|
|
channels.GovStatusChannel,
|
2023-12-20 10:10:30 -08:00
|
|
|
components,
|
|
|
|
nil, // ibc feature string
|
|
|
|
false, // gateway relayer enabled
|
|
|
|
false, // ccqEnabled
|
|
|
|
nil, // query requests
|
|
|
|
nil, // query responses
|
|
|
|
"", // query bootstrap peers
|
|
|
|
0, // query port
|
|
|
|
"", // query allow list
|
|
|
|
)); err != nil {
|
2022-09-07 11:43:05 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Info("Started internal services")
|
|
|
|
|
|
|
|
<-ctx.Done()
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
// It's safer to crash and restart the process in case we encounter a panic,
|
|
|
|
// rather than attempting to reschedule the runnable.
|
|
|
|
supervisor.WithPropagatePanic)
|
|
|
|
|
|
|
|
<-rootCtx.Done()
|
2023-08-07 12:05:08 -07:00
|
|
|
|
2022-09-07 11:43:05 -07:00
|
|
|
// TODO: wait for things to shut down gracefully
|
2022-11-21 06:18:33 -08:00
|
|
|
vaaGossipConsumerSplitter.Close()
|
2024-02-06 16:00:45 -08:00
|
|
|
observationGossipConsumer.Close()
|
2022-12-05 12:41:37 -08:00
|
|
|
server.Stop()
|
2023-08-07 12:05:08 -07:00
|
|
|
|
|
|
|
logger.Info("Closing MongoDB connection...")
|
|
|
|
db.DisconnectWithTimeout(10 * time.Second)
|
2022-09-07 11:43:05 -07:00
|
|
|
}
|
2022-10-22 17:58:38 -07:00
|
|
|
|
|
|
|
func discardMessages[T any](ctx context.Context, obsvReqC chan T) {
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
case <-obsvReqC:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|