From 68fdcf49382514a2d73be0968b2e52ac86f180cf Mon Sep 17 00:00:00 2001 From: Acha Bill <57879913+acha-bill@users.noreply.github.com> Date: Thu, 25 Jun 2020 11:43:22 +0100 Subject: [PATCH] Make singleton instances on plugins only accessible through getters (#477) * refactor plugins * fix fmt * fix compile err * fix errors * expose singletons via sync.once * re-disable remotelog and webauth plugins * re-enable autopeering plugin * Apply suggestions from code review * Apply suggestions from code review * add descriptive comments for getters Co-authored-by: Luca Moser <moser.luca@gmail.com> --- dapps/networkdelay/dapp.go | 16 ++- dapps/networkdelay/webapi.go | 2 +- dapps/valuetransfers/dapp.go | 77 +++++++---- dapps/valuetransfers/fpc.go | 6 +- pluginmgr/core/plugins.go | 34 ++--- pluginmgr/research/plugins.go | 10 +- pluginmgr/ui/plugins.go | 2 +- pluginmgr/webapi/plugins.go | 20 +-- plugins/analysis/client/plugin.go | 21 ++- plugins/analysis/dashboard/fpc_livefeed.go | 8 +- plugins/analysis/dashboard/fpc_storage.go | 6 +- plugins/analysis/dashboard/plugin.go | 17 ++- plugins/analysis/dashboard/routes.go | 4 +- plugins/analysis/packet/fpc_heartbeat.go | 24 ++-- plugins/analysis/packet/heartbeat.go | 26 ++-- plugins/analysis/packet/packet.go | 20 ++- plugins/analysis/server/plugin.go | 18 ++- plugins/autopeering/autopeering.go | 15 ++- plugins/autopeering/local/local.go | 6 +- plugins/autopeering/plugin.go | 14 +- plugins/banner/plugin.go | 16 ++- plugins/bootstrap/plugin.go | 16 ++- plugins/cli/plugin.go | 18 ++- plugins/config/plugin.go | 46 +++++-- plugins/dashboard/explorer_routes.go | 4 +- plugins/dashboard/livefeed.go | 4 +- plugins/dashboard/plugin.go | 24 +++- plugins/dashboard/routes.go | 4 +- plugins/dashboard/visualizer.go | 16 +-- plugins/dashboard/ws.go | 2 +- plugins/database/plugin.go | 19 ++- plugins/drng/drng.go | 8 +- plugins/drng/plugin.go | 16 ++- plugins/gossip/gossip.go | 6 +- plugins/gossip/plugin.go | 21 ++- plugins/gracefulshutdown/plugin.go | 74 ++++++----- plugins/issuer/plugin.go | 17 ++- plugins/logger/plugin.go | 25 +++- plugins/messagelayer/plugin.go | 122 +++++++++++++----- plugins/metrics/message.go | 2 +- plugins/metrics/message_test.go | 3 +- plugins/metrics/plugin.go | 29 +++-- plugins/portcheck/plugin.go | 16 ++- plugins/pow/plugin.go | 6 +- plugins/pow/pow.go | 6 +- plugins/profiling/plugin.go | 16 ++- plugins/prometheus/db_size.go | 2 +- plugins/prometheus/plugin.go | 12 +- plugins/remotelog/plugin.go | 17 ++- plugins/sync/plugin.go | 33 +++-- plugins/webapi/autopeering/plugin.go | 18 ++- plugins/webapi/data/plugin.go | 16 ++- plugins/webapi/drng/plugin.go | 20 ++- plugins/webapi/healthz/plugin.go | 17 ++- plugins/webapi/info/plugin.go | 18 ++- plugins/webapi/message/plugin.go | 22 +++- plugins/webapi/plugin.go | 42 ++++-- plugins/webapi/spammer/plugin.go | 19 ++- plugins/webapi/value/attachments/handler.go | 6 +- .../value/gettransactionbyid/handler.go | 4 +- plugins/webapi/value/plugin.go | 24 +++- .../webapi/value/unspentoutputs/handler.go | 4 +- plugins/webauth/webauth.go | 24 +++- tools/relay-checker/config.go | 20 +-- 64 files changed, 825 insertions(+), 375 deletions(-) diff --git a/dapps/networkdelay/dapp.go b/dapps/networkdelay/dapp.go index 36611d18..9e45e4a8 100644 --- a/dapps/networkdelay/dapp.go +++ b/dapps/networkdelay/dapp.go @@ -2,6 +2,7 @@ package networkdelay import ( "fmt" + "sync" "time" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" @@ -29,7 +30,8 @@ const ( var ( // App is the "plugin" instance of the network delay application. - App = node.NewPlugin(PluginName, node.Disabled, configure) + app *node.Plugin + once sync.Once // log holds a reference to the logger used by this app. log *logger.Logger @@ -41,6 +43,14 @@ var ( originPublicKey ed25519.PublicKey ) +// App gets the plugin instance. +func App() *node.Plugin { + once.Do(func () { + app = node.NewPlugin(PluginName, node.Disabled, configure) + }) + return app +} + func configure(_ *node.Plugin) { // configure logger log = logger.NewLogger(PluginName) @@ -53,7 +63,7 @@ func configure(_ *node.Plugin) { } // get origin public key from config - bytes, err := base58.Decode(config.Node.GetString(CfgNetworkDelayOriginPublicKey)) + bytes, err := base58.Decode(config.Node().GetString(CfgNetworkDelayOriginPublicKey)) if err != nil { log.Fatalf("could not parse %s config entry as base58. %v", CfgNetworkDelayOriginPublicKey, err) } @@ -65,7 +75,7 @@ func configure(_ *node.Plugin) { configureWebAPI() // subscribe to message-layer - messagelayer.Tangle.Events.MessageSolid.Attach(events.NewClosure(onReceiveMessageFromMessageLayer)) + messagelayer.Tangle().Events.MessageSolid.Attach(events.NewClosure(onReceiveMessageFromMessageLayer)) } func onReceiveMessageFromMessageLayer(cachedMessage *message.CachedMessage, cachedMessageMetadata *messageTangle.CachedMessageMetadata) { diff --git a/dapps/networkdelay/webapi.go b/dapps/networkdelay/webapi.go index b7ecf54f..53f72ea6 100644 --- a/dapps/networkdelay/webapi.go +++ b/dapps/networkdelay/webapi.go @@ -11,7 +11,7 @@ import ( ) func configureWebAPI() { - webapi.Server.POST("networkdelay", broadcastNetworkDelayObject) + webapi.Server().POST("networkdelay", broadcastNetworkDelayObject) } // broadcastNetworkDelayObject creates a message with a network delay object and diff --git a/dapps/valuetransfers/dapp.go b/dapps/valuetransfers/dapp.go index 9015d18a..88be707b 100644 --- a/dapps/valuetransfers/dapp.go +++ b/dapps/valuetransfers/dapp.go @@ -44,17 +44,19 @@ func init() { } var ( - // App is the "plugin" instance of the value-transfers application. - App = node.NewPlugin(PluginName, node.Enabled, configure, run) + // app is the "plugin" instance of the value-transfers application. + app *node.Plugin + appOnce sync.Once - // Tangle represents the value tangle that is used to express votes on value transactions. - Tangle *tangle.Tangle + // _tangle represents the value tangle that is used to express votes on value transactions. + _tangle *tangle.Tangle + tangleOnce sync.Once - // FCOB contains the fcob consensus logic. - FCOB *consensus.FCOB + // fcob contains the fcob consensus logic. + fcob *consensus.FCOB - // LedgerState represents the ledger state, that keeps track of the liked branches and offers an API to access funds. - LedgerState *tangle.LedgerState + // ledgerState represents the ledger state, that keeps track of the liked branches and offers an API to access funds. + ledgerState *tangle.LedgerState // log holds a reference to the logger used by this app. log *logger.Logger @@ -66,15 +68,44 @@ var ( valueObjectFactoryOnce sync.Once ) +// App gets the plugin instance. +func App() *node.Plugin { + appOnce.Do(func() { + app = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return app +} + +// Tangle gets the tangle instance. +// tangle represents the value tangle that is used to express votes on value transactions. +func Tangle() *tangle.Tangle { + tangleOnce.Do(func() { + _tangle = tangle.New(database.Store()) + }) + return _tangle +} + +// FCOB gets the fcob instance. +// fcob contains the fcob consensus logic. +func FCOB() *consensus.FCOB { + return fcob +} + +// LedgerState gets the ledgerState instance. +// ledgerState represents the ledger state, that keeps track of the liked branches and offers an API to access funds. +func LedgerState() *tangle.LedgerState { + return ledgerState +} + func configure(_ *node.Plugin) { // configure logger log = logger.NewLogger(PluginName) // configure Tangle - Tangle = tangle.New(database.Store()) + _tangle = Tangle() // read snapshot file - snapshotFilePath := config.Node.GetString(CfgValueLayerSnapshotFile) + snapshotFilePath := config.Node().GetString(CfgValueLayerSnapshotFile) if len(snapshotFilePath) != 0 { snapshot := tangle.Snapshot{} f, err := os.Open(snapshotFilePath) @@ -84,11 +115,11 @@ func configure(_ *node.Plugin) { if _, err := snapshot.ReadFrom(f); err != nil { log.Panic("could not read snapshot file:", err) } - Tangle.LoadSnapshot(snapshot) + _tangle.LoadSnapshot(snapshot) log.Infof("read snapshot from %s", snapshotFilePath) } - Tangle.Events.Error.Attach(events.NewClosure(func(err error) { + _tangle.Events.Error.Attach(events.NewClosure(func(err error) { log.Error(err) })) @@ -96,31 +127,31 @@ func configure(_ *node.Plugin) { tipManager = TipManager() valueObjectFactory = ValueObjectFactory() - Tangle.Events.PayloadLiked.Attach(events.NewClosure(func(cachedPayload *payload.CachedPayload, cachedMetadata *tangle.CachedPayloadMetadata) { + _tangle.Events.PayloadLiked.Attach(events.NewClosure(func(cachedPayload *payload.CachedPayload, cachedMetadata *tangle.CachedPayloadMetadata) { cachedMetadata.Release() cachedPayload.Consume(tipManager.AddTip) })) - Tangle.Events.PayloadDisliked.Attach(events.NewClosure(func(cachedPayload *payload.CachedPayload, cachedMetadata *tangle.CachedPayloadMetadata) { + _tangle.Events.PayloadDisliked.Attach(events.NewClosure(func(cachedPayload *payload.CachedPayload, cachedMetadata *tangle.CachedPayloadMetadata) { cachedMetadata.Release() cachedPayload.Consume(tipManager.RemoveTip) })) // configure FCOB consensus rules - cfgAvgNetworkDelay := config.Node.GetInt(CfgValueLayerFCOBAverageNetworkDelay) + cfgAvgNetworkDelay := config.Node().GetInt(CfgValueLayerFCOBAverageNetworkDelay) log.Infof("avg. network delay configured to %d seconds", cfgAvgNetworkDelay) - FCOB = consensus.NewFCOB(Tangle, time.Duration(cfgAvgNetworkDelay)*time.Second) - FCOB.Events.Vote.Attach(events.NewClosure(func(id string, initOpn vote.Opinion) { + fcob = consensus.NewFCOB(_tangle, time.Duration(cfgAvgNetworkDelay)*time.Second) + fcob.Events.Vote.Attach(events.NewClosure(func(id string, initOpn vote.Opinion) { if err := voter.Vote(id, initOpn); err != nil { log.Warnf("FPC vote: %s", err) } })) - FCOB.Events.Error.Attach(events.NewClosure(func(err error) { + fcob.Events.Error.Attach(events.NewClosure(func(err error) { log.Errorf("FCOB error: %s", err) })) // configure FPC + link to consensus configureFPC() - voter.Events().Finalized.Attach(events.NewClosure(FCOB.ProcessVoteResult)) + voter.Events().Finalized.Attach(events.NewClosure(fcob.ProcessVoteResult)) voter.Events().Finalized.Attach(events.NewClosure(func(ev *vote.OpinionEvent) { log.Infof("FPC finalized for transaction with id '%s' - final opinion: '%s'", ev.ID, ev.Opinion) })) @@ -129,16 +160,16 @@ func configure(_ *node.Plugin) { })) // register SignatureFilter in Parser - messagelayer.MessageParser.AddMessageFilter(tangle.NewSignatureFilter()) + messagelayer.MessageParser().AddMessageFilter(tangle.NewSignatureFilter()) // subscribe to message-layer - messagelayer.Tangle.Events.MessageSolid.Attach(events.NewClosure(onReceiveMessageFromMessageLayer)) + messagelayer.Tangle().Events.MessageSolid.Attach(events.NewClosure(onReceiveMessageFromMessageLayer)) } func run(*node.Plugin) { if err := daemon.BackgroundWorker("ValueTangle", func(shutdownSignal <-chan struct{}) { <-shutdownSignal - Tangle.Shutdown() + _tangle.Shutdown() }, shutdown.PriorityTangle); err != nil { log.Panicf("Failed to start as daemon: %s", err) } @@ -169,7 +200,7 @@ func onReceiveMessageFromMessageLayer(cachedMessage *message.CachedMessage, cach return } - Tangle.AttachPayload(valuePayload) + _tangle.AttachPayload(valuePayload) } // TipManager returns the TipManager singleton. diff --git a/dapps/valuetransfers/fpc.go b/dapps/valuetransfers/fpc.go index 4f975b21..23d271f7 100644 --- a/dapps/valuetransfers/fpc.go +++ b/dapps/valuetransfers/fpc.go @@ -79,7 +79,7 @@ func configureFPC() { log = logger.NewLogger(FpcPluginName) lPeer := local.GetInstance() - bindAddr := config.Node.GetString(CfgFPCBindAddress) + bindAddr := config.Node().GetString(CfgFPCBindAddress) _, portStr, err := net.SplitHostPort(bindAddr) if err != nil { log.Fatalf("FPC bind address '%s' is invalid: %s", bindAddr, err) @@ -104,7 +104,7 @@ func runFPC() { const ServerWorkerName = "FPCVoterServer" if err := daemon.BackgroundWorker(ServerWorkerName, func(shutdownSignal <-chan struct{}) { stopped := make(chan struct{}) - bindAddr := config.Node.GetString(CfgFPCBindAddress) + bindAddr := config.Node().GetString(CfgFPCBindAddress) voterServer = votenet.New(Voter(), func(id string) vote.Opinion { branchID, err := branchmanager.BranchIDFromBase58(id) if err != nil { @@ -113,7 +113,7 @@ func runFPC() { return vote.Unknown } - cachedBranch := Tangle.BranchManager().Branch(branchID) + cachedBranch := _tangle.BranchManager().Branch(branchID) defer cachedBranch.Release() branch := cachedBranch.Unwrap() diff --git a/pluginmgr/core/plugins.go b/pluginmgr/core/plugins.go index 7bdae01c..13e02886 100644 --- a/pluginmgr/core/plugins.go +++ b/pluginmgr/core/plugins.go @@ -24,22 +24,22 @@ import ( ) var PLUGINS = node.Plugins( - banner.Plugin, - config.Plugin, - logger.Plugin, - cli.Plugin, - portcheck.Plugin, - profiling.Plugin, - database.Plugin, - autopeering.Plugin, + banner.Plugin(), + config.Plugin(), + logger.Plugin(), + cli.Plugin(), + portcheck.Plugin(), + profiling.Plugin(), + database.Plugin(), + autopeering.Plugin(), pow.Plugin, - messagelayer.Plugin, - gossip.Plugin, - issuer.Plugin, - bootstrap.Plugin, - sync.Plugin, - gracefulshutdown.Plugin, - metrics.Plugin, - drng.Plugin, - valuetransfers.App, + messagelayer.Plugin(), + gossip.Plugin(), + issuer.Plugin(), + bootstrap.Plugin(), + sync.Plugin(), + gracefulshutdown.Plugin(), + metrics.Plugin(), + drng.Plugin(), + valuetransfers.App(), ) diff --git a/pluginmgr/research/plugins.go b/pluginmgr/research/plugins.go index 1275898c..66e4037f 100644 --- a/pluginmgr/research/plugins.go +++ b/pluginmgr/research/plugins.go @@ -11,10 +11,10 @@ import ( ) var PLUGINS = node.Plugins( - remotelog.Plugin, - analysisserver.Plugin, - analysisclient.Plugin, - analysisdashboard.Plugin, + remotelog.Plugin(), + analysisserver.Plugin(), + analysisclient.Plugin(), + analysisdashboard.Plugin(), prometheus.Plugin, - networkdelay.App, + networkdelay.App(), ) diff --git a/pluginmgr/ui/plugins.go b/pluginmgr/ui/plugins.go index 0cb1418a..e3a2dc1a 100644 --- a/pluginmgr/ui/plugins.go +++ b/pluginmgr/ui/plugins.go @@ -6,5 +6,5 @@ import ( ) var PLUGINS = node.Plugins( - dashboard.Plugin, + dashboard.Plugin(), ) diff --git a/pluginmgr/webapi/plugins.go b/pluginmgr/webapi/plugins.go index 11df33b2..807a01e7 100644 --- a/pluginmgr/webapi/plugins.go +++ b/pluginmgr/webapi/plugins.go @@ -15,14 +15,14 @@ import ( ) var PLUGINS = node.Plugins( - webapi.Plugin, - webauth.Plugin, - spammer.Plugin, - data.Plugin, - drng.Plugin, - healthz.Plugin, - message.Plugin, - autopeering.Plugin, - info.Plugin, - value.Plugin, + webapi.Plugin(), + webauth.Plugin(), + spammer.Plugin(), + data.Plugin(), + drng.Plugin(), + healthz.Plugin(), + message.Plugin(), + autopeering.Plugin(), + info.Plugin(), + value.Plugin(), ) diff --git a/plugins/analysis/client/plugin.go b/plugins/analysis/client/plugin.go index 7108fd4b..734e3507 100644 --- a/plugins/analysis/client/plugin.go +++ b/plugins/analysis/client/plugin.go @@ -1,6 +1,7 @@ package client import ( + "sync" "time" "github.com/iotaledger/goshimmer/dapps/valuetransfers" @@ -30,16 +31,26 @@ func init() { } var ( - // Plugin is the plugin instance of the analysis client plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, run) - conn *Connector - log *logger.Logger + // plugin is the plugin instance of the analysis client plugin. + plugin *node.Plugin + once sync.Once + log *logger.Logger + conn *Connector + connLock sync.Mutex ) +// Plugin gets the plugin instance +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, run) + }) + return plugin +} + func run(_ *node.Plugin) { finalized = make(map[string]vote.Opinion) log = logger.NewLogger(PluginName) - conn = NewConnector("tcp", config.Node.GetString(CfgServerAddress)) + conn = NewConnector("tcp", config.Node().GetString(CfgServerAddress)) if err := daemon.BackgroundWorker(PluginName, func(shutdownSignal <-chan struct{}) { conn.Start() diff --git a/plugins/analysis/dashboard/fpc_livefeed.go b/plugins/analysis/dashboard/fpc_livefeed.go index 73160c6d..00ee50a8 100644 --- a/plugins/analysis/dashboard/fpc_livefeed.go +++ b/plugins/analysis/dashboard/fpc_livefeed.go @@ -41,7 +41,7 @@ type FPCUpdate struct { func configureFPCLiveFeed() { - if config.Node.GetBool(CfgMongoDBEnabled) { + if config.Node().GetBool(CfgMongoDBEnabled) { mongoDB() } @@ -51,7 +51,7 @@ func configureFPCLiveFeed() { task.Return(nil) }, workerpool.WorkerCount(fpcLiveFeedWorkerCount), workerpool.QueueSize(fpcLiveFeedWorkerQueueSize)) - if config.Node.GetBool(CfgMongoDBEnabled) { + if config.Node().GetBool(CfgMongoDBEnabled) { fpcStoreFinalizedWorkerPool = workerpool.New(func(task workerpool.Task) { storeFinalizedVoteContext(task.Param(0).(FPCRecords)) task.Return(nil) @@ -69,7 +69,7 @@ func runFPCLiveFeed() { fpcLiveFeedWorkerPool.Start() defer fpcLiveFeedWorkerPool.Stop() - if config.Node.GetBool(CfgMongoDBEnabled) { + if config.Node().GetBool(CfgMongoDBEnabled) { fpcStoreFinalizedWorkerPool.Start() defer fpcStoreFinalizedWorkerPool.Stop() } @@ -150,7 +150,7 @@ func createFPCUpdate(hb *packet.FPCHeartbeat) *FPCUpdate { }) } - if config.Node.GetBool(CfgMongoDBEnabled) { + if config.Node().GetBool(CfgMongoDBEnabled) { fpcStoreFinalizedWorkerPool.TrySubmit(finalizedConflicts) } diff --git a/plugins/analysis/dashboard/fpc_storage.go b/plugins/analysis/dashboard/fpc_storage.go index 47607b62..66506742 100644 --- a/plugins/analysis/dashboard/fpc_storage.go +++ b/plugins/analysis/dashboard/fpc_storage.go @@ -56,9 +56,9 @@ func mongoDB() *mongo.Database { } func newMongoDB() (*mongo.Client, error) { - username := config.Node.GetString(CfgMongoDBUsername) - password := config.Node.GetString(CfgMongoDBPassword) - hostAddr := config.Node.GetString(CfgMongoDBHostAddress) + username := config.Node().GetString(CfgMongoDBUsername) + password := config.Node().GetString(CfgMongoDBPassword) + hostAddr := config.Node().GetString(CfgMongoDBHostAddress) client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://" + username + ":" + password + "@" + hostAddr)) if err != nil { diff --git a/plugins/analysis/dashboard/plugin.go b/plugins/analysis/dashboard/plugin.go index 0d3817b5..da6ab6e0 100644 --- a/plugins/analysis/dashboard/plugin.go +++ b/plugins/analysis/dashboard/plugin.go @@ -19,13 +19,18 @@ import ( const PluginName = "Analysis-Dashboard" var ( - // Plugin is the plugin instance of the dashboard plugin. - Plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + // plugin is the plugin instance of the dashboard plugin. + plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) log *logger.Logger server *echo.Echo ) +// Plugin gets the plugin instance +func Plugin() *node.Plugin { + return plugin +} + func configure(plugin *node.Plugin) { log = logger.NewLogger(plugin.Name) configureFPCLiveFeed() @@ -40,10 +45,10 @@ func configureServer() { server.HidePort = true server.Use(middleware.Recover()) - if config.Node.GetBool(CfgBasicAuthEnabled) { + if config.Node().GetBool(CfgBasicAuthEnabled) { server.Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) { - if username == config.Node.GetString(CfgBasicAuthUsername) && - password == config.Node.GetString(CfgBasicAuthPassword) { + if username == config.Node().GetString(CfgBasicAuthUsername) && + password == config.Node().GetString(CfgBasicAuthPassword) { return true, nil } return false, nil @@ -71,7 +76,7 @@ func worker(shutdownSignal <-chan struct{}) { defer log.Infof("Stopping %s ... done", PluginName) stopped := make(chan struct{}) - bindAddr := config.Node.GetString(CfgBindAddress) + bindAddr := config.Node().GetString(CfgBindAddress) go func() { log.Infof("%s started, bind-address=%s", PluginName, bindAddr) if err := server.Start(bindAddr); err != nil { diff --git a/plugins/analysis/dashboard/routes.go b/plugins/analysis/dashboard/routes.go index 1ffc810b..442af15b 100644 --- a/plugins/analysis/dashboard/routes.go +++ b/plugins/analysis/dashboard/routes.go @@ -28,7 +28,7 @@ var appBox = packr.New("AnalysisDashboard_App", "./frontend/build") var assetsBox = packr.New("AnalysisDashboard_Assets", "./frontend/src/assets") func indexRoute(e echo.Context) error { - if config.Node.GetBool(CfgDev) { + if config.Node().GetBool(CfgDev) { res, err := http.Get("http://127.0.0.1:9090/") if err != nil { return err @@ -48,7 +48,7 @@ func indexRoute(e echo.Context) error { func setupRoutes(e *echo.Echo) { - if config.Node.GetBool("analysis.dashboard.dev") { + if config.Node().GetBool("analysis.dashboard.dev") { e.Static("/assets", "./plugins/analysis/dashboard/frontend/src/assets") } else { diff --git a/plugins/analysis/packet/fpc_heartbeat.go b/plugins/analysis/packet/fpc_heartbeat.go index 2746abe2..3c64aafc 100644 --- a/plugins/analysis/packet/fpc_heartbeat.go +++ b/plugins/analysis/packet/fpc_heartbeat.go @@ -5,7 +5,6 @@ import ( "encoding/binary" "encoding/gob" "errors" - "github.com/iotaledger/goshimmer/packages/vote" "github.com/iotaledger/hive.go/protocol/message" "github.com/iotaledger/hive.go/protocol/tlv" @@ -16,15 +15,6 @@ var ( ErrInvalidFPCHeartbeat = errors.New("invalid FPC heartbeat") ) -var ( - // FPCHeartbeatMessageDefinition defines a heartbeat message's format. - FPCHeartbeatMessageDefinition = &message.Definition{ - ID: MessageTypeFPCHeartbeat, - MaxBytesLength: 65535, - VariableLength: true, - } -) - // FPCHeartbeat represents a heartbeat packet. type FPCHeartbeat struct { // The ID of the node who sent the heartbeat. @@ -36,6 +26,20 @@ type FPCHeartbeat struct { Finalized map[string]vote.Opinion } +// FPCHeartbeatMessageDefinition gets the fpcHeartbeatMessageDefinition. +func FPCHeartbeatMessageDefinition() *message.Definition { + // fpcHeartbeatMessageDefinition defines a heartbeat message's format. + var fpcHeartbeatMessageDefinition *message.Definition + fpcHeartBeatOnce.Do(func(){ + fpcHeartbeatMessageDefinition = &message.Definition{ + ID: MessageTypeFPCHeartbeat, + MaxBytesLength: 65535, + VariableLength: true, + } + }) + return fpcHeartbeatMessageDefinition +} + // ParseFPCHeartbeat parses a slice of bytes (serialized packet) into a FPC heartbeat. func ParseFPCHeartbeat(data []byte) (*FPCHeartbeat, error) { hb := &FPCHeartbeat{} diff --git a/plugins/analysis/packet/heartbeat.go b/plugins/analysis/packet/heartbeat.go index 41cfd39b..65f75e56 100644 --- a/plugins/analysis/packet/heartbeat.go +++ b/plugins/analysis/packet/heartbeat.go @@ -6,7 +6,6 @@ import ( "encoding/binary" "errors" "fmt" - "github.com/iotaledger/hive.go/protocol/message" "github.com/iotaledger/hive.go/protocol/tlv" ) @@ -25,9 +24,6 @@ const ( HeartbeatPacketPeerIDSize = sha256.Size // HeartbeatPacketOutboundIDCountSize is the byte size of the counter indicating the amount of outbound IDs. HeartbeatPacketOutboundIDCountSize = 1 -) - -var ( // HeartbeatPacketMinSize is the minimum byte size of a heartbeat packet. HeartbeatPacketMinSize = HeartbeatPacketPeerIDSize + HeartbeatPacketOutboundIDCountSize // HeartbeatPacketMaxSize is the maximum size a heartbeat packet can have. @@ -35,14 +31,6 @@ var ( HeartbeatMaxOutboundPeersCount*sha256.Size + HeartbeatMaxInboundPeersCount*sha256.Size ) -var ( - // HeartbeatMessageDefinition defines a heartbeat message's format. - HeartbeatMessageDefinition = &message.Definition{ - ID: MessageTypeHeartbeat, - MaxBytesLength: uint16(HeartbeatPacketMaxSize), - VariableLength: true, - } -) // Heartbeat represents a heartbeat packet. type Heartbeat struct { @@ -57,6 +45,20 @@ type Heartbeat struct { InboundIDs [][]byte } +// HeartBeatMessageDefinition gets the heartbeatMessageDefinition. +func HeartBeatMessageDefinition() *message.Definition{ + // heartbeatMessageDefinition defines a heartbeat message's format. + var heartbeatMessageDefinition *message.Definition + heartBeatOnce.Do(func(){ + heartbeatMessageDefinition = &message.Definition{ + ID: MessageTypeHeartbeat, + MaxBytesLength: uint16(HeartbeatPacketMaxSize), + VariableLength: true, + } + }) + return heartbeatMessageDefinition +} + // ParseHeartbeat parses a slice of bytes (serialized packet) into a heartbeat. func ParseHeartbeat(data []byte) (*Heartbeat, error) { // check minimum size diff --git a/plugins/analysis/packet/packet.go b/plugins/analysis/packet/packet.go index d95c72d3..cf5a9740 100644 --- a/plugins/analysis/packet/packet.go +++ b/plugins/analysis/packet/packet.go @@ -2,6 +2,7 @@ package packet import ( "errors" + "sync" "github.com/iotaledger/hive.go/protocol/message" "github.com/iotaledger/hive.go/protocol/tlv" @@ -12,16 +13,25 @@ var ( ErrMalformedPacket = errors.New("malformed packet") ) -// AnalysisMsgRegistry holds all message definitions for analysis server related messages -var AnalysisMsgRegistry *message.Registry +var ( + // analysisMsgRegistry holds all message definitions for analysis server related messages + analysisMsgRegistry *message.Registry + fpcHeartBeatOnce sync.Once + heartBeatOnce sync.Once +) func init() { // message definitions to be registered in registry definitions := []*message.Definition{ tlv.HeaderMessageDefinition, - HeartbeatMessageDefinition, - FPCHeartbeatMessageDefinition, + HeartBeatMessageDefinition(), + FPCHeartbeatMessageDefinition(), MetricHeartbeatMessageDefinition, } - AnalysisMsgRegistry = message.NewRegistry(definitions) + analysisMsgRegistry = message.NewRegistry(definitions) +} + +// AnalysisMsgRegistry gets the analysisMsgRegistry. +func AnalysisMsgRegistry() *message.Registry { + return analysisMsgRegistry } diff --git a/plugins/analysis/server/plugin.go b/plugins/analysis/server/plugin.go index 603fd5c4..6c7173fc 100644 --- a/plugins/analysis/server/plugin.go +++ b/plugins/analysis/server/plugin.go @@ -5,6 +5,7 @@ import ( "net" "strconv" "strings" + "sync" "time" "github.com/iotaledger/goshimmer/packages/shutdown" @@ -36,13 +37,22 @@ func init() { } var ( - // Plugin is the plugin instance of the analysis server plugin. - Plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + // plugin is the plugin instance of the analysis server plugin. + plugin *node.Plugin + once sync.Once server *tcp.TCPServer prot *protocol.Protocol log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + }) + return plugin +} + func configure(_ *node.Plugin) { log = logger.NewLogger(PluginName) server = tcp.NewServer() @@ -54,7 +64,7 @@ func configure(_ *node.Plugin) { } func run(_ *node.Plugin) { - bindAddr := config.Node.GetString(CfgAnalysisServerBindAddress) + bindAddr := config.Node().GetString(CfgAnalysisServerBindAddress) addr, portStr, err := net.SplitHostPort(bindAddr) if err != nil { log.Fatal("invalid bind address in %s: %s", CfgAnalysisServerBindAddress, err) @@ -69,7 +79,7 @@ func run(_ *node.Plugin) { defer log.Infof("Stopping %s ... done", PluginName) // connect protocol events to processors - prot = protocol.New(packet.AnalysisMsgRegistry) + prot = protocol.New(packet.AnalysisMsgRegistry()) wireUp(prot) go server.Listen(addr, port) diff --git a/plugins/autopeering/autopeering.go b/plugins/autopeering/autopeering.go index 8f2b7f26..7b33f422 100644 --- a/plugins/autopeering/autopeering.go +++ b/plugins/autopeering/autopeering.go @@ -33,8 +33,8 @@ var ( // ErrParsingMasterNode is returned for an invalid master node. ErrParsingMasterNode = errors.New("cannot parse master node") - // NetworkID specifies the autopeering network identifier. - NetworkID = hash32([]byte(banner.AppVersion + NetworkVersion)) + // networkID specifies the autopeering network identifier. + networkID = hash32([]byte(banner.AppVersion + NetworkVersion)) // Conn contains the network connection. Conn *NetConnMetric @@ -56,6 +56,11 @@ var ( }{c: make(chan *server.Server, 1)} ) +// NetworkID gets the networkID. +func NetworkID() uint32 { + return networkID +} + // Discovery returns the peer discovery instance. func Discovery() *discover.Protocol { peerDiscOnce.Do(createPeerDisc) @@ -71,7 +76,7 @@ func Selection() *selection.Protocol { // BindAddress returns the string form of the autopeering bind address. func BindAddress() string { peering := local.GetInstance().Services().Get(service.PeeringKey) - host := config.Node.GetString(local.CfgBind) + host := config.Node().GetString(local.CfgBind) port := strconv.Itoa(peering.Port()) return net.JoinHostPort(host, port) } @@ -97,7 +102,7 @@ func createPeerDisc() { } log.Debugf("Master peers: %v", masterPeers) - peerDisc = discover.New(local.GetInstance(), ProtocolVersion, NetworkID, + peerDisc = discover.New(local.GetInstance(), ProtocolVersion, networkID, discover.Logger(log), discover.MasterPeers(masterPeers), ) @@ -177,7 +182,7 @@ func hash32(b []byte) uint32 { } func parseEntryNodes() (result []*peer.Peer, err error) { - for _, entryNodeDefinition := range config.Node.GetStringSlice(CfgEntryNodes) { + for _, entryNodeDefinition := range config.Node().GetStringSlice(CfgEntryNodes) { if entryNodeDefinition == "" { continue } diff --git a/plugins/autopeering/local/local.go b/plugins/autopeering/local/local.go index b2a211c8..d3fbdf92 100644 --- a/plugins/autopeering/local/local.go +++ b/plugins/autopeering/local/local.go @@ -26,7 +26,7 @@ func configureLocal() *peer.Local { log := logger.NewLogger("Local") var peeringIP net.IP - if str := config.Node.GetString(CfgExternal); strings.ToLower(str) == "auto" { + if str := config.Node().GetString(CfgExternal); strings.ToLower(str) == "auto" { // let the autopeering discover the IP peeringIP = net.IPv4zero } else { @@ -39,7 +39,7 @@ func configureLocal() *peer.Local { } } - peeringPort := config.Node.GetInt(CfgPort) + peeringPort := config.Node().GetInt(CfgPort) if 0 > peeringPort || peeringPort > 65535 { log.Fatalf("Invalid port number (%s): %d", CfgPort, peeringPort) } @@ -50,7 +50,7 @@ func configureLocal() *peer.Local { // set the private key from the seed provided in the config var seed [][]byte - if str := config.Node.GetString(CfgSeed); str != "" { + if str := config.Node().GetString(CfgSeed); str != "" { var bytes []byte var err error diff --git a/plugins/autopeering/plugin.go b/plugins/autopeering/plugin.go index 74aa171c..c100b976 100644 --- a/plugins/autopeering/plugin.go +++ b/plugins/autopeering/plugin.go @@ -1,6 +1,7 @@ package autopeering import ( + "sync" "time" "github.com/iotaledger/goshimmer/packages/shutdown" @@ -16,12 +17,21 @@ import ( const PluginName = "Autopeering" var ( - // Plugin is the plugin instance of the autopeering plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + // plugin is the plugin instance of the autopeering plugin. + plugin *node.Plugin + once sync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + func configure(*node.Plugin) { log = logger.NewLogger(PluginName) diff --git a/plugins/banner/plugin.go b/plugins/banner/plugin.go index 1f0936a6..f71fab08 100644 --- a/plugins/banner/plugin.go +++ b/plugins/banner/plugin.go @@ -2,6 +2,7 @@ package banner import ( "fmt" + "sync" "github.com/iotaledger/hive.go/node" ) @@ -9,8 +10,11 @@ import ( // PluginName is the name of the banner plugin. const PluginName = "Banner" -// Plugin is the plugin instance of the banner plugin. -var Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) +var ( + // plugin is the plugin instance of the banner plugin. + plugin *node.Plugin + once sync.Once +) const ( // AppVersion version number @@ -20,6 +24,14 @@ const ( AppName = "GoShimmer" ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + }) + return plugin +} + func configure(ctx *node.Plugin) { fmt.Printf(` _____ ____ _____ _ _ _____ __ __ __ __ ______ _____ diff --git a/plugins/bootstrap/plugin.go b/plugins/bootstrap/plugin.go index 1ebfd520..c1aab9dd 100644 --- a/plugins/bootstrap/plugin.go +++ b/plugins/bootstrap/plugin.go @@ -1,6 +1,7 @@ package bootstrap import ( + goSync "sync" "time" "github.com/iotaledger/goshimmer/packages/binary/spammer" @@ -32,11 +33,20 @@ func init() { } var ( - // Plugin is the plugin instance of the bootstrap plugin. - Plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + // plugin is the plugin instance of the bootstrap plugin. + plugin *node.Plugin + once goSync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + }) + return plugin +} + func configure(_ *node.Plugin) { log = logger.NewLogger(PluginName) // we're auto. synced if we start in bootstrapping mode @@ -47,7 +57,7 @@ func configure(_ *node.Plugin) { func run(_ *node.Plugin) { messageSpammer := spammer.New(issuer.IssuePayload) - issuancePeriodSec := config.Node.GetInt(CfgBootstrapInitialIssuanceTimePeriodSec) + issuancePeriodSec := config.Node().GetInt(CfgBootstrapInitialIssuanceTimePeriodSec) issuancePeriod := time.Duration(issuancePeriodSec) * time.Second // issue messages on top of the genesis diff --git a/plugins/cli/plugin.go b/plugins/cli/plugin.go index 99bb6368..8946d22b 100644 --- a/plugins/cli/plugin.go +++ b/plugins/cli/plugin.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "os" + "sync" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/node" @@ -15,12 +16,23 @@ import ( const PluginName = "CLI" var ( - // Plugin is the plugin instance of the CLI plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled) + // plugin is the plugin instance of the CLI plugin. + plugin *node.Plugin + once sync.Once version = flag.BoolP("version", "v", false, "Prints the GoShimmer version") ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled) + }) + return plugin +} + func init() { + plugin = Plugin() + for name, plugin := range node.GetPlugins() { onAddPlugin(name, plugin.Status) } @@ -29,7 +41,7 @@ func init() { flag.Usage = printUsage - Plugin.Events.Init.Attach(events.NewClosure(onInit)) + plugin.Events.Init.Attach(events.NewClosure(onInit)) } func onAddPlugin(name string, status int) { diff --git a/plugins/config/plugin.go b/plugins/config/plugin.go index da882233..151ed747 100644 --- a/plugins/config/plugin.go +++ b/plugins/config/plugin.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "strings" + "sync" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/node" @@ -16,8 +17,9 @@ import ( const PluginName = "Config" var ( - // Plugin is the plugin instance of the config plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled) + // plugin is the plugin instance of the config plugin. + plugin *node.Plugin + pluginOnce sync.Once // flags configName = flag.StringP("config", "c", "config", "Filename of the config file without the file extension") @@ -25,19 +27,38 @@ var ( skipConfigAvailable = flag.Bool("skip-config", false, "Skip config file availability check") // Node is viper - Node *viper.Viper + _node *viper.Viper + nodeOnce sync.Once ) // Init triggers the Init event. func Init() { - Plugin.Events.Init.Trigger(Plugin) + plugin.Events.Init.Trigger(plugin) +} + +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + pluginOnce.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled) + }) + return plugin +} + +// Node gets the node. +func Node() *viper.Viper { + nodeOnce.Do(func() { + _node = viper.New() + }) + return _node } func init() { // set the default logger config - Node = viper.New() + _node = Node() + plugin = Plugin() + - Plugin.Events.Init.Attach(events.NewClosure(func(*node.Plugin) { + plugin.Events.Init.Attach(events.NewClosure(func(*node.Plugin) { if err := fetch(false); err != nil { if !*skipConfigAvailable { // we wanted a config file but it was not present @@ -59,24 +80,25 @@ func init() { func fetch(printConfig bool, ignoreSettingsAtPrint ...[]string) error { // replace dots with underscores in env dotReplacer := strings.NewReplacer(".", "_") - Node.SetEnvKeyReplacer(dotReplacer) + _node.SetEnvKeyReplacer(dotReplacer) + // read in ENV variables // read in ENV variables - Node.AutomaticEnv() + _node.AutomaticEnv() flag.Parse() - err := parameter.LoadConfigFile(Node, *configDirPath, *configName, true, *skipConfigAvailable) + err := parameter.LoadConfigFile(_node, *configDirPath, *configName, true, *skipConfigAvailable) if err != nil { return err } if printConfig { - parameter.PrintConfig(Node, ignoreSettingsAtPrint...) + parameter.PrintConfig(_node, ignoreSettingsAtPrint...) } - for _, pluginName := range Node.GetStringSlice(node.CFG_DISABLE_PLUGINS) { + for _, pluginName := range _node.GetStringSlice(node.CFG_DISABLE_PLUGINS) { node.DisabledPlugins[node.GetPluginIdentifier(pluginName)] = true } - for _, pluginName := range Node.GetStringSlice(node.CFG_ENABLE_PLUGINS) { + for _, pluginName := range _node.GetStringSlice(node.CFG_ENABLE_PLUGINS) { node.EnabledPlugins[node.GetPluginIdentifier(pluginName)] = true } diff --git a/plugins/dashboard/explorer_routes.go b/plugins/dashboard/explorer_routes.go index 2f346354..5dc6bf4a 100644 --- a/plugins/dashboard/explorer_routes.go +++ b/plugins/dashboard/explorer_routes.go @@ -30,7 +30,7 @@ type ExplorerMessage struct { func createExplorerMessage(msg *message.Message) (*ExplorerMessage, error) { messageID := msg.Id() - messageMetadata := messagelayer.Tangle.MessageMetadata(messageID) + messageMetadata := messagelayer.Tangle().MessageMetadata(messageID) t := &ExplorerMessage{ ID: messageID.String(), Timestamp: 0, @@ -119,7 +119,7 @@ func setupExplorerRoutes(routeGroup *echo.Group) { } func findMessage(messageID message.Id) (explorerMsg *ExplorerMessage, err error) { - if !messagelayer.Tangle.Message(messageID).Consume(func(msg *message.Message) { + if !messagelayer.Tangle().Message(messageID).Consume(func(msg *message.Message) { explorerMsg, err = createExplorerMessage(msg) }) { err = fmt.Errorf("%w: message %s", ErrNotFound, messageID.String()) diff --git a/plugins/dashboard/livefeed.go b/plugins/dashboard/livefeed.go index f0bebaa6..8e491bec 100644 --- a/plugins/dashboard/livefeed.go +++ b/plugins/dashboard/livefeed.go @@ -40,11 +40,11 @@ func runLiveFeed() { }) if err := daemon.BackgroundWorker("Dashboard[MsgUpdater]", func(shutdownSignal <-chan struct{}) { - messagelayer.Tangle.Events.MessageAttached.Attach(notifyNewMsg) + messagelayer.Tangle().Events.MessageAttached.Attach(notifyNewMsg) liveFeedWorkerPool.Start() <-shutdownSignal log.Info("Stopping Dashboard[MsgUpdater] ...") - messagelayer.Tangle.Events.MessageAttached.Detach(notifyNewMsg) + messagelayer.Tangle().Events.MessageAttached.Detach(notifyNewMsg) newMsgRateLimiter.Stop() liveFeedWorkerPool.Stop() log.Info("Stopping Dashboard[MsgUpdater] ... done") diff --git a/plugins/dashboard/plugin.go b/plugins/dashboard/plugin.go index f0f033aa..b19aaf9a 100644 --- a/plugins/dashboard/plugin.go +++ b/plugins/dashboard/plugin.go @@ -7,6 +7,7 @@ import ( "net/http" "runtime" "strconv" + "sync" "time" "github.com/iotaledger/goshimmer/packages/shutdown" @@ -30,8 +31,9 @@ import ( const PluginName = "Dashboard" var ( - // Plugin is the plugin instance of the dashboard plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + // plugin is the plugin instance of the dashboard plugin. + plugin *node.Plugin + once sync.Once log *logger.Logger server *echo.Echo @@ -39,6 +41,14 @@ var ( nodeStartAt = time.Now() ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + func configure(plugin *node.Plugin) { log = logger.NewLogger(plugin.Name) configureWebSocketWorkerPool() @@ -54,10 +64,10 @@ func configureServer() { server.HidePort = true server.Use(middleware.Recover()) - if config.Node.GetBool(CfgBasicAuthEnabled) { + if config.Node().GetBool(CfgBasicAuthEnabled) { server.Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) { - if username == config.Node.GetString(CfgBasicAuthUsername) && - password == config.Node.GetString(CfgBasicAuthPassword) { + if username == config.Node().GetString(CfgBasicAuthUsername) && + password == config.Node().GetString(CfgBasicAuthPassword) { return true, nil } return false, nil @@ -75,7 +85,7 @@ func run(*node.Plugin) { // run the visualizer vertex feed runVisualizer() // run dRNG live feed if dRNG plugin is enabled - if !node.IsSkipped(drng.Plugin) { + if !node.IsSkipped(drng.Plugin()) { runDrngLiveFeed() } @@ -98,7 +108,7 @@ func worker(shutdownSignal <-chan struct{}) { defer metrics.Events.ReceivedMPSUpdated.Detach(notifyStatus) stopped := make(chan struct{}) - bindAddr := config.Node.GetString(CfgBindAddress) + bindAddr := config.Node().GetString(CfgBindAddress) go func() { log.Infof("%s started, bind-address=%s", PluginName, bindAddr) if err := server.Start(bindAddr); err != nil { diff --git a/plugins/dashboard/routes.go b/plugins/dashboard/routes.go index 63084bab..5ae146bf 100644 --- a/plugins/dashboard/routes.go +++ b/plugins/dashboard/routes.go @@ -28,7 +28,7 @@ var appBox = packr.New("Dashboard_App", "./frontend/build") var assetsBox = packr.New("Dashboard_Assets", "./frontend/src/assets") func indexRoute(e echo.Context) error { - if config.Node.GetBool(CfgDev) { + if config.Node().GetBool(CfgDev) { res, err := http.Get("http://127.0.0.1:9090/") if err != nil { return err @@ -48,7 +48,7 @@ func indexRoute(e echo.Context) error { func setupRoutes(e *echo.Echo) { - if config.Node.GetBool("dashboard.dev") { + if config.Node().GetBool("dashboard.dev") { e.Static("/assets", "./plugins/dashboard/frontend/src/assets") } else { diff --git a/plugins/dashboard/visualizer.go b/plugins/dashboard/visualizer.go index 2725f40d..fab0c7c5 100644 --- a/plugins/dashboard/visualizer.go +++ b/plugins/dashboard/visualizer.go @@ -80,14 +80,14 @@ func runVisualizer() { }) if err := daemon.BackgroundWorker("Dashboard[Visualizer]", func(shutdownSignal <-chan struct{}) { - messagelayer.Tangle.Events.MessageAttached.Attach(notifyNewMsg) - defer messagelayer.Tangle.Events.MessageAttached.Detach(notifyNewMsg) - messagelayer.Tangle.Events.MessageSolid.Attach(notifyNewMsg) - defer messagelayer.Tangle.Events.MessageSolid.Detach(notifyNewMsg) - messagelayer.TipSelector.Events.TipAdded.Attach(notifyNewTip) - defer messagelayer.TipSelector.Events.TipAdded.Detach(notifyNewTip) - messagelayer.TipSelector.Events.TipRemoved.Attach(notifyDeletedTip) - defer messagelayer.TipSelector.Events.TipRemoved.Detach(notifyDeletedTip) + messagelayer.Tangle().Events.MessageAttached.Attach(notifyNewMsg) + defer messagelayer.Tangle().Events.MessageAttached.Detach(notifyNewMsg) + messagelayer.Tangle().Events.MessageSolid.Attach(notifyNewMsg) + defer messagelayer.Tangle().Events.MessageSolid.Detach(notifyNewMsg) + messagelayer.TipSelector().Events.TipAdded.Attach(notifyNewTip) + defer messagelayer.TipSelector().Events.TipAdded.Detach(notifyNewTip) + messagelayer.TipSelector().Events.TipRemoved.Attach(notifyDeletedTip) + defer messagelayer.TipSelector().Events.TipRemoved.Detach(notifyDeletedTip) visualizerWorkerPool.Start() <-shutdownSignal log.Info("Stopping Dashboard[Visualizer] ...") diff --git a/plugins/dashboard/ws.go b/plugins/dashboard/ws.go index 7092edfc..3288d77d 100644 --- a/plugins/dashboard/ws.go +++ b/plugins/dashboard/ws.go @@ -48,7 +48,7 @@ func configureWebSocketWorkerPool() { broadcastWsMessage(&wsmsg{MsgTypeMPSMetric, task.Param(0).(uint64)}) broadcastWsMessage(&wsmsg{MsgTypeNodeStatus, currentNodeStatus()}) broadcastWsMessage(&wsmsg{MsgTypeNeighborMetric, neighborMetrics()}) - broadcastWsMessage(&wsmsg{MsgTypeTipsMetric, messagelayer.TipSelector.TipCount()}) + broadcastWsMessage(&wsmsg{MsgTypeTipsMetric, messagelayer.TipSelector().TipCount()}) task.Return(nil) }, workerpool.WorkerCount(wsSendWorkerCount), workerpool.QueueSize(wsSendWorkerQueueSize)) } diff --git a/plugins/database/plugin.go b/plugins/database/plugin.go index 1886ddb2..a3f4d25c 100644 --- a/plugins/database/plugin.go +++ b/plugins/database/plugin.go @@ -19,15 +19,24 @@ import ( const PluginName = "Database" var ( - // Plugin is the plugin instance of the database plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure) - log *logger.Logger + // plugin is the plugin instance of the database plugin. + plugin *node.Plugin + pluginOnce sync.Once + log *logger.Logger db database.DB store kvstore.KVStore storeOnce sync.Once ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + pluginOnce.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin +} + // Store returns the KVStore instance. func Store() kvstore.KVStore { storeOnce.Do(createStore) @@ -43,10 +52,10 @@ func createStore() { log = logger.NewLogger(PluginName) var err error - if config.Node.GetBool(CfgDatabaseInMemory) { + if config.Node().GetBool(CfgDatabaseInMemory) { db, err = database.NewMemDB() } else { - dbDir := config.Node.GetString(CfgDatabaseDir) + dbDir := config.Node().GetString(CfgDatabaseDir) db, err = database.NewDB(dbDir) } if err != nil { diff --git a/plugins/drng/drng.go b/plugins/drng/drng.go index 0d377b27..6bfebe7c 100644 --- a/plugins/drng/drng.go +++ b/plugins/drng/drng.go @@ -29,7 +29,7 @@ func configureDRNG() *drng.DRNG { // parse distributed public key of the committee var dpk []byte - if str := config.Node.GetString(CfgDRNGDistributedPubKey); str != "" { + if str := config.Node().GetString(CfgDRNGDistributedPubKey); str != "" { bytes, err := hex.DecodeString(str) if err != nil { log.Warnf("Invalid %s: %s", CfgDRNGDistributedPubKey, err) @@ -42,8 +42,8 @@ func configureDRNG() *drng.DRNG { // configure committee committeeConf := &state.Committee{ - InstanceID: config.Node.GetUint32(CfgDRNGInstanceID), - Threshold: uint8(config.Node.GetUint32(CfgDRNGThreshold)), + InstanceID: config.Node().GetUint32(CfgDRNGInstanceID), + Threshold: uint8(config.Node().GetUint32(CfgDRNGThreshold)), DistributedPK: dpk, Identities: committeeMembers, } @@ -58,7 +58,7 @@ func Instance() *drng.DRNG { } func parseCommitteeMembers() (result []ed25519.PublicKey, err error) { - for _, committeeMember := range config.Node.GetStringSlice(CfgDRNGCommitteeMembers) { + for _, committeeMember := range config.Node().GetStringSlice(CfgDRNGCommitteeMembers) { if committeeMember == "" { continue } diff --git a/plugins/drng/plugin.go b/plugins/drng/plugin.go index 294f564d..041c1d5d 100644 --- a/plugins/drng/plugin.go +++ b/plugins/drng/plugin.go @@ -19,13 +19,23 @@ import ( const PluginName = "DRNG" var ( - // Plugin is the plugin instance of the DRNG plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + // plugin is the plugin instance of the DRNG plugin. + plugin *node.Plugin + pluginOnce sync.Once instance *drng.DRNG once sync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + pluginOnce.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + + func configure(_ *node.Plugin) { configureEvents() } @@ -34,7 +44,7 @@ func run(*node.Plugin) {} func configureEvents() { instance := Instance() - messagelayer.Tangle.Events.MessageSolid.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { + messagelayer.Tangle().Events.MessageSolid.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { cachedMessageMetadata.Release() cachedMessage.Consume(func(msg *message.Message) { diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go index 06162245..d08d8e61 100644 --- a/plugins/gossip/gossip.go +++ b/plugins/gossip/gossip.go @@ -39,7 +39,7 @@ func createManager() { log := logger.NewLogger(PluginName) // announce the gossip service - gossipPort := config.Node.GetInt(CfgGossipPort) + gossipPort := config.Node().GetInt(CfgGossipPort) if !netutil.IsValidPort(gossipPort) { log.Fatalf("Invalid port number (%s): %d", CfgGossipPort, gossipPort) } @@ -60,7 +60,7 @@ func start(shutdownSignal <-chan struct{}) { gossipEndpoint := lPeer.Services().Get(service.GossipKey) // resolve the bind address - address := net.JoinHostPort(config.Node.GetString(local.CfgBind), strconv.Itoa(gossipEndpoint.Port())) + address := net.JoinHostPort(config.Node().GetString(local.CfgBind), strconv.Itoa(gossipEndpoint.Port())) localAddr, err := net.ResolveTCPAddr(gossipEndpoint.Network(), address) if err != nil { log.Fatalf("Error resolving %s: %v", local.CfgBind, err) @@ -92,7 +92,7 @@ func start(shutdownSignal <-chan struct{}) { // loads the given message from the message layer or an error if not found. func loadMessage(messageID message.Id) (bytes []byte, err error) { - if !messagelayer.Tangle.Message(messageID).Consume(func(message *message.Message) { + if !messagelayer.Tangle().Message(messageID).Consume(func(message *message.Message) { bytes = message.Bytes() }) { err = ErrMessageNotFound diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go index dfaaa6ad..4e62dc9a 100644 --- a/plugins/gossip/plugin.go +++ b/plugins/gossip/plugin.go @@ -13,18 +13,29 @@ import ( "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" + "sync" ) // PluginName is the name of the gossip plugin. const PluginName = "Gossip" var ( - // Plugin is the plugin instance of the gossip plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + // plugin is the plugin instance of the gossip plugin. + plugin *node.Plugin + once sync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + + func configure(*node.Plugin) { log = logger.NewLogger(PluginName) @@ -104,11 +115,11 @@ func configureMessageLayer() { // configure flow of incoming messages mgr.Events().MessageReceived.Attach(events.NewClosure(func(event *gossip.MessageReceivedEvent) { - messagelayer.MessageParser.Parse(event.Data, event.Peer) + messagelayer.MessageParser().Parse(event.Data, event.Peer) })) // configure flow of outgoing messages (gossip on solidification) - messagelayer.Tangle.Events.MessageSolid.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { + messagelayer.Tangle().Events.MessageSolid.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { cachedMessageMetadata.Release() cachedMessage.Consume(func(msg *message.Message) { mgr.SendMessage(msg.Bytes()) @@ -116,7 +127,7 @@ func configureMessageLayer() { })) // request missing messages - messagelayer.MessageRequester.Events.SendRequest.Attach(events.NewClosure(func(messageId message.Id) { + messagelayer.MessageRequester().Events.SendRequest.Attach(events.NewClosure(func(messageId message.Id) { mgr.RequestMessage(messageId[:]) })) } diff --git a/plugins/gracefulshutdown/plugin.go b/plugins/gracefulshutdown/plugin.go index 4caf4913..efa54649 100644 --- a/plugins/gracefulshutdown/plugin.go +++ b/plugins/gracefulshutdown/plugin.go @@ -5,6 +5,7 @@ import ( "os/signal" "sort" "strings" + "sync" "syscall" "time" @@ -20,45 +21,56 @@ const PluginName = "Graceful Shutdown" // After that the process is killed. const WaitToKillTimeInSeconds = 60 -var log *logger.Logger -var gracefulStop chan os.Signal +var ( + // plugin is the plugin instance of the graceful shutdown plugin. + plugin *node.Plugin + once sync.Once + log *logger.Logger + gracefulStop chan os.Signal +) -// Plugin is the plugin instance of the graceful shutdown plugin. -var Plugin = node.NewPlugin(PluginName, node.Enabled, func(plugin *node.Plugin) { - log = logger.NewLogger(PluginName) - gracefulStop = make(chan os.Signal) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, func(plugin *node.Plugin) { + log = logger.NewLogger(PluginName) + gracefulStop = make(chan os.Signal) - signal.Notify(gracefulStop, syscall.SIGTERM) - signal.Notify(gracefulStop, syscall.SIGINT) + signal.Notify(gracefulStop, syscall.SIGTERM) + signal.Notify(gracefulStop, syscall.SIGINT) - go func() { - <-gracefulStop + go func() { + <-gracefulStop - log.Warnf("Received shutdown request - waiting (max %d) to finish processing ...", WaitToKillTimeInSeconds) + log.Warnf("Received shutdown request - waiting (max %d) to finish processing ...", WaitToKillTimeInSeconds) - go func() { - start := time.Now() - for x := range time.Tick(1 * time.Second) { - secondsSinceStart := x.Sub(start).Seconds() + go func() { + start := time.Now() + for x := range time.Tick(1 * time.Second) { + secondsSinceStart := x.Sub(start).Seconds() - if secondsSinceStart <= WaitToKillTimeInSeconds { - processList := "" - runningBackgroundWorkers := daemon.GetRunningBackgroundWorkers() - if len(runningBackgroundWorkers) >= 1 { - sort.Strings(runningBackgroundWorkers) - processList = "(" + strings.Join(runningBackgroundWorkers, ", ") + ") " + if secondsSinceStart <= WaitToKillTimeInSeconds { + processList := "" + runningBackgroundWorkers := daemon.GetRunningBackgroundWorkers() + if len(runningBackgroundWorkers) >= 1 { + sort.Strings(runningBackgroundWorkers) + processList = "(" + strings.Join(runningBackgroundWorkers, ", ") + ") " + } + log.Warnf("Received shutdown request - waiting (max %d seconds) to finish processing %s...", WaitToKillTimeInSeconds-int(secondsSinceStart), processList) + } else { + log.Error("Background processes did not terminate in time! Forcing shutdown ...") + os.Exit(1) + } } - log.Warnf("Received shutdown request - waiting (max %d seconds) to finish processing %s...", WaitToKillTimeInSeconds-int(secondsSinceStart), processList) - } else { - log.Error("Background processes did not terminate in time! Forcing shutdown ...") - os.Exit(1) - } - } - }() + }() + + daemon.Shutdown() + }() + }) + }) + return plugin +} - daemon.Shutdown() - }() -}) // ShutdownWithError prints out an error message and shuts down the default daemon instance. func ShutdownWithError(err error) { diff --git a/plugins/issuer/plugin.go b/plugins/issuer/plugin.go index d34e6f36..8bc87008 100644 --- a/plugins/issuer/plugin.go +++ b/plugins/issuer/plugin.go @@ -2,6 +2,7 @@ package issuer import ( "fmt" + goSync "sync" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" @@ -14,10 +15,20 @@ import ( const PluginName = "Issuer" var ( - // Plugin is the plugin instance of the issuer plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure) + // plugin is the plugin instance of the issuer plugin. + plugin *node.Plugin + once goSync.Once + ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin +} + func configure(_ *node.Plugin) {} // IssuePayload issues a payload to the message layer. @@ -26,5 +37,5 @@ func IssuePayload(payload payload.Payload) (*message.Message, error) { if !sync.Synced() { return nil, fmt.Errorf("can't issue payload: %w", sync.ErrNodeNotSynchronized) } - return messagelayer.MessageFactory.IssuePayload(payload), nil + return messagelayer.MessageFactory().IssuePayload(payload), nil } diff --git a/plugins/logger/plugin.go b/plugins/logger/plugin.go index ebe14309..5ab85c84 100644 --- a/plugins/logger/plugin.go +++ b/plugins/logger/plugin.go @@ -5,24 +5,39 @@ import ( "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" + "sync" ) // PluginName is the name of the logger plugin. const PluginName = "Logger" -// Plugin is the plugin instance of the logger plugin. -var Plugin = node.NewPlugin(PluginName, node.Enabled) +var ( + // plugin is the plugin instance of the logger plugin. + plugin *node.Plugin + once sync.Once +) + +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled) + }) + return plugin +} + // Init triggers the Init event. func Init() { - Plugin.Events.Init.Trigger(Plugin) + plugin.Events.Init.Trigger(plugin) } func init() { + plugin = Plugin() + initFlags() - Plugin.Events.Init.Attach(events.NewClosure(func(*node.Plugin) { - if err := logger.InitGlobalLogger(config.Node); err != nil { + plugin.Events.Init.Attach(events.NewClosure(func(*node.Plugin) { + if err := logger.InitGlobalLogger(config.Node()); err != nil { panic(err) } })) diff --git a/plugins/messagelayer/plugin.go b/plugins/messagelayer/plugin.go index ca1fb0e5..d43792ed 100644 --- a/plugins/messagelayer/plugin.go +++ b/plugins/messagelayer/plugin.go @@ -1,6 +1,8 @@ package messagelayer import ( + "sync" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/messageparser" @@ -23,68 +25,122 @@ const ( ) var ( - // Plugin is the plugin instance of the message layer plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) - MessageParser *messageparser.MessageParser - MessageRequester *messagerequester.MessageRequester - TipSelector *tipselector.TipSelector - Tangle *tangle.Tangle - MessageFactory *messagefactory.MessageFactory + // plugin is the plugin instance of the message layer plugin. + plugin *node.Plugin + pluginOnce sync.Once + messageParser *messageparser.MessageParser + msgParserOnce sync.Once + messageRequester *messagerequester.MessageRequester + msgReqOnce sync.Once + tipSelector *tipselector.TipSelector + tipSelectorOnce sync.Once + _tangle *tangle.Tangle + tangleOnce sync.Once + messageFactory *messagefactory.MessageFactory + msgFactoryOnce sync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + pluginOnce.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + +// MessageParser gets the messageParser instance. +func MessageParser() *messageparser.MessageParser { + msgParserOnce.Do(func() { + messageParser = messageparser.New() + }) + return messageParser +} + +// TipSelector gets the tipSelector instance. +func TipSelector() *tipselector.TipSelector { + tipSelectorOnce.Do(func() { + tipSelector = tipselector.New() + }) + return tipSelector +} + +// Tangle gets the tangle instance. +func Tangle() *tangle.Tangle { + tangleOnce.Do(func() { + store := database.Store() + _tangle = tangle.New(store) + }) + return _tangle +} + +// MessageFactory gets the messageFactory instance. +func MessageFactory() *messagefactory.MessageFactory { + msgFactoryOnce.Do(func() { + messageFactory = messagefactory.New(database.Store(), []byte(DBSequenceNumber), local.GetInstance().LocalIdentity(), TipSelector()) + }) + return messageFactory +} + +// MessageRequester gets the messageRequester instance. +func MessageRequester() *messagerequester.MessageRequester { + msgReqOnce.Do(func() { + messageRequester = messagerequester.New() + }) + return messageRequester +} + func configure(*node.Plugin) { log = logger.NewLogger(PluginName) - store := database.Store() // create instances - MessageParser = messageparser.New() - MessageRequester = messagerequester.New() - TipSelector = tipselector.New() - Tangle = tangle.New(store) - - // Setup MessageFactory (behavior + logging)) - MessageFactory = messagefactory.New(database.Store(), []byte(DBSequenceNumber), local.GetInstance().LocalIdentity(), TipSelector) - MessageFactory.Events.MessageConstructed.Attach(events.NewClosure(Tangle.AttachMessage)) - MessageFactory.Events.Error.Attach(events.NewClosure(func(err error) { + messageParser = MessageParser() + messageRequester = MessageRequester() + tipSelector = TipSelector() + _tangle = Tangle() + + // Setup messageFactory (behavior + logging)) + messageFactory = MessageFactory() + messageFactory.Events.MessageConstructed.Attach(events.NewClosure(_tangle.AttachMessage)) + messageFactory.Events.Error.Attach(events.NewClosure(func(err error) { log.Errorf("internal error in message factory: %v", err) })) - // setup MessageParser - MessageParser.Events.MessageParsed.Attach(events.NewClosure(func(msg *message.Message, peer *peer.Peer) { + // setup messageParser + messageParser.Events.MessageParsed.Attach(events.NewClosure(func(msg *message.Message, peer *peer.Peer) { // TODO: ADD PEER - Tangle.AttachMessage(msg) + _tangle.AttachMessage(msg) })) - // setup MessageRequester - Tangle.Events.MessageMissing.Attach(events.NewClosure(MessageRequester.ScheduleRequest)) - Tangle.Events.MissingMessageReceived.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { + // setup messageRequester + _tangle.Events.MessageMissing.Attach(events.NewClosure(messageRequester.ScheduleRequest)) + _tangle.Events.MissingMessageReceived.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { cachedMessageMetadata.Release() cachedMessage.Consume(func(msg *message.Message) { - MessageRequester.StopRequest(msg.Id()) + messageRequester.StopRequest(msg.Id()) }) })) - // setup TipSelector - Tangle.Events.MessageSolid.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { + // setup tipSelector + _tangle.Events.MessageSolid.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { cachedMessageMetadata.Release() - cachedMessage.Consume(TipSelector.AddTip) + cachedMessage.Consume(tipSelector.AddTip) })) } func run(*node.Plugin) { - if err := daemon.BackgroundWorker("Tangle[MissingMessagesMonitor]", func(shutdownSignal <-chan struct{}) { - Tangle.MonitorMissingMessages(shutdownSignal) + if err := daemon.BackgroundWorker("_tangle[MissingMessagesMonitor]", func(shutdownSignal <-chan struct{}) { + _tangle.MonitorMissingMessages(shutdownSignal) }, shutdown.PriorityMissingMessagesMonitoring); err != nil { log.Panicf("Failed to start as daemon: %s", err) } - if err := daemon.BackgroundWorker("Tangle", func(shutdownSignal <-chan struct{}) { + if err := daemon.BackgroundWorker("_tangle", func(shutdownSignal <-chan struct{}) { <-shutdownSignal - MessageFactory.Shutdown() - MessageParser.Shutdown() - Tangle.Shutdown() + messageFactory.Shutdown() + messageParser.Shutdown() + _tangle.Shutdown() }, shutdown.PriorityTangle); err != nil { log.Panicf("Failed to start as daemon: %s", err) } diff --git a/plugins/metrics/message.go b/plugins/metrics/message.go index 569ba349..927ce519 100644 --- a/plugins/metrics/message.go +++ b/plugins/metrics/message.go @@ -66,7 +66,7 @@ func increasePerPayloadCounter(p payload.Type) { } func measureMessageTips() { - metrics.Events().MessageTips.Trigger((uint64)(messagelayer.TipSelector.TipCount())) + metrics.Events().MessageTips.Trigger((uint64)(messagelayer.TipSelector().TipCount())) } // ReceivedMessagesPerSecond retrieves the current messages per second number. diff --git a/plugins/metrics/message_test.go b/plugins/metrics/message_test.go index 3d1eb537..bdf7925a 100644 --- a/plugins/metrics/message_test.go +++ b/plugins/metrics/message_test.go @@ -7,7 +7,6 @@ import ( valuepayload "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload" drngpayload "github.com/iotaledger/goshimmer/packages/binary/drng/payload" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" - "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" "github.com/iotaledger/goshimmer/packages/metrics" "github.com/iotaledger/goshimmer/plugins/messagelayer" "github.com/iotaledger/hive.go/events" @@ -34,7 +33,7 @@ func TestMessageCountPerPayload(t *testing.T) { func TestMessageTips(t *testing.T) { var wg sync.WaitGroup // messagelayer TipSelector not configured here, so to avoid nil pointer panic, we instantiate it - messagelayer.TipSelector = tipselector.New() + messagelayer.TipSelector() metrics.Events().MessageTips.Attach(events.NewClosure(func(tips uint64) { messageTips.Store(tips) wg.Done() diff --git a/plugins/metrics/plugin.go b/plugins/metrics/plugin.go index c7c295f1..a60b7be6 100644 --- a/plugins/metrics/plugin.go +++ b/plugins/metrics/plugin.go @@ -1,6 +1,7 @@ package metrics import ( + "sync" "time" "github.com/iotaledger/goshimmer/dapps/valuetransfers" @@ -27,30 +28,38 @@ import ( const PluginName = "Metrics" var ( - // Plugin is the plugin instance of the metrics plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) - - log *logger.Logger + // plugin is the plugin instance of the metrics plugin. + plugin *node.Plugin + once sync.Once + log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + func configure(_ *node.Plugin) { log = logger.NewLogger(PluginName) } func run(_ *node.Plugin) { - if config.Node.GetBool(CfgMetricsLocal) { + if config.Node().GetBool(CfgMetricsLocal) { registerLocalMetrics() } // Events from analysis server - if config.Node.GetBool(CfgMetricsGlobal) { + if config.Node().GetBool(CfgMetricsGlobal) { server.Events.MetricHeartbeat.Attach(onMetricHeartbeatReceived) } // create a background worker that update the metrics every second if err := daemon.BackgroundWorker("Metrics Updater", func(shutdownSignal <-chan struct{}) { - if config.Node.GetBool(CfgMetricsLocal) { + if config.Node().GetBool(CfgMetricsLocal) { timeutil.Ticker(func() { measureCPUUsage() measureMemUsage() @@ -65,7 +74,7 @@ func run(_ *node.Plugin) { gossipCurrentTx.Store(uint64(g.BytesWritten)) }, 1*time.Second, shutdownSignal) } - if config.Node.GetBool(CfgMetricsGlobal) { + if config.Node().GetBool(CfgMetricsGlobal) { timeutil.Ticker(calculateNetworkDiameter, 1*time.Minute, shutdownSignal) } @@ -78,7 +87,7 @@ func registerLocalMetrics() { //// Events declared in other packages which we want to listen to here //// // increase received MPS counter whenever we attached a message - messagelayer.Tangle.Events.MessageAttached.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { + messagelayer.Tangle().Events.MessageAttached.Attach(events.NewClosure(func(cachedMessage *message.CachedMessage, cachedMessageMetadata *tangle.CachedMessageMetadata) { _payloadType := cachedMessage.Unwrap().Payload().Type() cachedMessage.Release() cachedMessageMetadata.Release() @@ -87,7 +96,7 @@ func registerLocalMetrics() { })) // Value payload attached - valuetransfers.Tangle.Events.PayloadAttached.Attach(events.NewClosure(func(cachedPayload *payload.CachedPayload, cachedPayloadMetadata *valuetangle.CachedPayloadMetadata) { + valuetransfers.Tangle().Events.PayloadAttached.Attach(events.NewClosure(func(cachedPayload *payload.CachedPayload, cachedPayloadMetadata *valuetangle.CachedPayloadMetadata) { cachedPayload.Release() cachedPayloadMetadata.Release() valueTransactionCounter.Inc() diff --git a/plugins/portcheck/plugin.go b/plugins/portcheck/plugin.go index 85ac7a6d..e1b6845b 100644 --- a/plugins/portcheck/plugin.go +++ b/plugins/portcheck/plugin.go @@ -2,6 +2,7 @@ package portcheck import ( "net" + "sync" "github.com/iotaledger/goshimmer/plugins/autopeering" "github.com/iotaledger/goshimmer/plugins/autopeering/local" @@ -17,11 +18,20 @@ import ( const PluginName = "PortCheck" var ( - // Plugin is the plugin instance of the port check plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + // plugin is the plugin instance of the port check plugin. + plugin *node.Plugin + once sync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + func configure(*node.Plugin) { log = logger.NewLogger(PluginName) } @@ -49,7 +59,7 @@ func checkAutopeeringConnection() { defer conn.Close() // create a new discovery server for the port check - disc := discover.New(local.GetInstance(), autopeering.ProtocolVersion, autopeering.NetworkID, discover.Logger(log)) + disc := discover.New(local.GetInstance(), autopeering.ProtocolVersion, autopeering.NetworkID(), discover.Logger(log)) srv := server.Serve(local.GetInstance(), conn, log, disc) defer srv.Close() diff --git a/plugins/pow/plugin.go b/plugins/pow/plugin.go index 1660eecb..90b76e0c 100644 --- a/plugins/pow/plugin.go +++ b/plugins/pow/plugin.go @@ -20,7 +20,7 @@ func run(*node.Plugin) { // assure that the logger is available log := logger.NewLogger(PluginName) - if node.IsSkipped(messagelayer.Plugin) { + if node.IsSkipped(messagelayer.Plugin()) { log.Infof("%s is disabled; skipping %s\n", messagelayer.PluginName, PluginName) return } @@ -28,6 +28,6 @@ func run(*node.Plugin) { // assure that the PoW worker is initialized worker := Worker() - messagelayer.MessageParser.AddBytesFilter(builtinfilters.NewPowFilter(worker, difficulty)) - messagelayer.MessageFactory.SetWorker(messagefactory.WorkerFunc(DoPOW)) + messagelayer.MessageParser().AddBytesFilter(builtinfilters.NewPowFilter(worker, difficulty)) + messagelayer.MessageFactory().SetWorker(messagefactory.WorkerFunc(DoPOW)) } diff --git a/plugins/pow/pow.go b/plugins/pow/pow.go index c547d4ae..69207f5b 100644 --- a/plugins/pow/pow.go +++ b/plugins/pow/pow.go @@ -41,9 +41,9 @@ func Worker() *pow.Worker { workerOnce.Do(func() { log = logger.NewLogger(PluginName) // load the parameters - difficulty = config.Node.GetInt(CfgPOWDifficulty) - numWorkers = config.Node.GetInt(CfgPOWNumThreads) - timeout = config.Node.GetDuration(CfgPOWTimeout) + difficulty = config.Node().GetInt(CfgPOWDifficulty) + numWorkers = config.Node().GetInt(CfgPOWNumThreads) + timeout = config.Node().GetDuration(CfgPOWTimeout) // create the worker worker = pow.New(hash, numWorkers) }) diff --git a/plugins/profiling/plugin.go b/plugins/profiling/plugin.go index 388da645..4ab416d6 100644 --- a/plugins/profiling/plugin.go +++ b/plugins/profiling/plugin.go @@ -3,6 +3,7 @@ package profiling import ( "net/http" "runtime" + "sync" // import required to profile _ "net/http/pprof" @@ -17,14 +18,23 @@ import ( const PluginName = "Profiling" var ( - // Plugin is the profiling plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + // plugin is the profiling plugin. + plugin *node.Plugin + once sync.Once log *logger.Logger ) // CfgProfilingBindAddress defines the config flag of the profiling binding address. const CfgProfilingBindAddress = "profiling.bindAddress" +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + func init() { flag.String(CfgProfilingBindAddress, "127.0.0.1:6061", "bind address for the pprof server") } @@ -34,7 +44,7 @@ func configure(_ *node.Plugin) { } func run(_ *node.Plugin) { - bindAddr := config.Node.GetString(CfgProfilingBindAddress) + bindAddr := config.Node().GetString(CfgProfilingBindAddress) runtime.SetMutexProfileFraction(5) runtime.SetBlockProfileRate(5) diff --git a/plugins/prometheus/db_size.go b/plugins/prometheus/db_size.go index 99408a8e..df6e1372 100644 --- a/plugins/prometheus/db_size.go +++ b/plugins/prometheus/db_size.go @@ -27,7 +27,7 @@ func registerDBMetrics() { } func collectDBSize() { - size, err := directorySize(config.Node.GetString(database.CfgDatabaseDir)) + size, err := directorySize(config.Node().GetString(database.CfgDatabaseDir)) if err == nil { dbSize.Set(float64(size)) } diff --git a/plugins/prometheus/plugin.go b/plugins/prometheus/plugin.go index 8aa2f765..5f32a260 100644 --- a/plugins/prometheus/plugin.go +++ b/plugins/prometheus/plugin.go @@ -29,14 +29,14 @@ var ( func configure(plugin *node.Plugin) { log = logger.NewLogger(plugin.Name) - if config.Node.GetBool(CfgPrometheusGoMetrics) { + if config.Node().GetBool(CfgPrometheusGoMetrics) { registry.MustRegister(prometheus.NewGoCollector()) } - if config.Node.GetBool(CfgPrometheusProcessMetrics) { + if config.Node().GetBool(CfgPrometheusProcessMetrics) { registry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{})) } - if config.Node.GetBool(metrics.CfgMetricsLocal) { + if config.Node().GetBool(metrics.CfgMetricsLocal) { registerAutopeeringMetrics() registerDBMetrics() registerFPCMetrics() @@ -46,7 +46,7 @@ func configure(plugin *node.Plugin) { registerTangleMetrics() } - if config.Node.GetBool(metrics.CfgMetricsGlobal) { + if config.Node().GetBool(metrics.CfgMetricsGlobal) { registerClientsMetrics() } } @@ -73,13 +73,13 @@ func run(plugin *node.Plugin) { EnableOpenMetrics: true, }, ) - if config.Node.GetBool(CfgPrometheusPromhttpMetrics) { + if config.Node().GetBool(CfgPrometheusPromhttpMetrics) { handler = promhttp.InstrumentMetricHandler(registry, handler) } handler.ServeHTTP(c.Writer, c.Request) }) - bindAddr := config.Node.GetString(CfgPrometheusBindAddress) + bindAddr := config.Node().GetString(CfgPrometheusBindAddress) server = &http.Server{Addr: bindAddr, Handler: engine} go func() { diff --git a/plugins/remotelog/plugin.go b/plugins/remotelog/plugin.go index 298038d3..6d4e6f9a 100644 --- a/plugins/remotelog/plugin.go +++ b/plugins/remotelog/plugin.go @@ -36,8 +36,9 @@ const ( ) var ( - // Plugin is the plugin instance of the remote plugin instance. - Plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + // plugin is the plugin instance of the remote plugin instance. + plugin *node.Plugin + pluginOnce sync.Once log *logger.Logger myID string myGitHead string @@ -48,6 +49,14 @@ var ( remoteLoggerOnce sync.Once ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + pluginOnce.Do(func() { + plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + }) + return plugin +} + func init() { flag.String(CfgLoggerRemotelogServerAddress, "remotelog.goshimmer.iota.cafe:5213", "RemoteLog server address") } @@ -55,7 +64,7 @@ func init() { func configure(plugin *node.Plugin) { log = logger.NewLogger(PluginName) - if config.Node.GetBool(CfgDisableEvents) { + if config.Node().GetBool(CfgDisableEvents) { log.Fatalf("%s in config.json needs to be false so that events can be captured!", CfgDisableEvents) return } @@ -151,7 +160,7 @@ func getGitDir() string { // RemoteLogger represents a connection to our remote log server. func RemoteLogger() *RemoteLoggerConn { remoteLoggerOnce.Do(func() { - r, err := newRemoteLoggerConn(config.Node.GetString(CfgLoggerRemotelogServerAddress)) + r, err := newRemoteLoggerConn(config.Node().GetString(CfgLoggerRemotelogServerAddress)) if err != nil { log.Fatal(err) return diff --git a/plugins/sync/plugin.go b/plugins/sync/plugin.go index 5a083448..738285a9 100644 --- a/plugins/sync/plugin.go +++ b/plugins/sync/plugin.go @@ -49,8 +49,9 @@ func init() { } var ( - // Plugin is the plugin instance of the sync plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + // plugin is the plugin instance of the sync plugin. + plugin *node.Plugin + once sync.Once // ErrNodeNotSynchronized is returned when an operation can't be executed because // the node is not synchronized. ErrNodeNotSynchronized = errors.New("node is not synchronized") @@ -59,6 +60,14 @@ var ( log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + // Synced tells whether the node is in a state we consider synchronized, meaning // it has the relevant past and present message data. func Synced() bool { @@ -133,10 +142,10 @@ func monitorForDesynchronization() { if err := daemon.BackgroundWorker("Desync-Monitor", func(shutdownSignal <-chan struct{}) { gossip.Manager().Events().NeighborRemoved.Attach(monitorPeerCountClosure) defer gossip.Manager().Events().NeighborRemoved.Detach(monitorPeerCountClosure) - messagelayer.Tangle.Events.MessageAttached.Attach(monitorMessageInflowClosure) - defer messagelayer.Tangle.Events.MessageAttached.Detach(monitorMessageInflowClosure) + messagelayer.Tangle().Events.MessageAttached.Attach(monitorMessageInflowClosure) + defer messagelayer.Tangle().Events.MessageAttached.Detach(monitorMessageInflowClosure) - desyncedIfNoMessageInSec := config.Node.GetDuration(CfgSyncDesyncedIfNoMessageAfterSec) * time.Second + desyncedIfNoMessageInSec := config.Node().GetDuration(CfgSyncDesyncedIfNoMessageAfterSec) * time.Second timer := time.NewTimer(desyncedIfNoMessageInSec) for { select { @@ -171,7 +180,7 @@ func monitorForDesynchronization() { // starts a background worker and event handlers to check whether the node is synchronized by first collecting // a set of newly received messages and then waiting for them to become solid. func monitorForSynchronization() { - wantedAnchorPointsCount := config.Node.GetInt(CfgSyncAnchorPointsCount) + wantedAnchorPointsCount := config.Node().GetInt(CfgSyncAnchorPointsCount) anchorPoints := newAnchorPoints(wantedAnchorPointsCount) log.Infof("monitoring for synchronization, awaiting %d anchor point messages to become solid", wantedAnchorPointsCount) @@ -203,13 +212,13 @@ func monitorForSynchronization() { }) if err := daemon.BackgroundWorker("Sync-Monitor", func(shutdownSignal <-chan struct{}) { - messagelayer.Tangle.Events.MessageAttached.Attach(initAnchorPointClosure) - defer messagelayer.Tangle.Events.MessageAttached.Detach(initAnchorPointClosure) - messagelayer.Tangle.Events.MessageSolid.Attach(checkAnchorPointSolidityClosure) - defer messagelayer.Tangle.Events.MessageSolid.Detach(checkAnchorPointSolidityClosure) + messagelayer.Tangle().Events.MessageAttached.Attach(initAnchorPointClosure) + defer messagelayer.Tangle().Events.MessageAttached.Detach(initAnchorPointClosure) + messagelayer.Tangle().Events.MessageSolid.Attach(checkAnchorPointSolidityClosure) + defer messagelayer.Tangle().Events.MessageSolid.Detach(checkAnchorPointSolidityClosure) - cleanupDelta := config.Node.GetDuration(CfgSyncAnchorPointsCleanupAfterSec) * time.Second - ticker := time.NewTimer(config.Node.GetDuration(CfgSyncAnchorPointsCleanupIntervalSec) * time.Second) + cleanupDelta := config.Node().GetDuration(CfgSyncAnchorPointsCleanupAfterSec) * time.Second + ticker := time.NewTimer(config.Node().GetDuration(CfgSyncAnchorPointsCleanupIntervalSec) * time.Second) defer ticker.Stop() for { select { diff --git a/plugins/webapi/autopeering/plugin.go b/plugins/webapi/autopeering/plugin.go index 0267831b..9b046602 100644 --- a/plugins/webapi/autopeering/plugin.go +++ b/plugins/webapi/autopeering/plugin.go @@ -4,6 +4,7 @@ import ( "net" "net/http" "strconv" + "sync" "github.com/iotaledger/goshimmer/plugins/autopeering" "github.com/iotaledger/goshimmer/plugins/webapi" @@ -16,11 +17,22 @@ import ( // PluginName is the name of the web API autopeering endpoint plugin. const PluginName = "WebAPI autopeering Endpoint" -// Plugin is the plugin instance of the web API autopeering endpoint plugin. -var Plugin = node.NewPlugin(PluginName, node.Enabled, configure) +var ( + // plugin is the plugin instance of the web API autopeering endpoint plugin. + plugin *node.Plugin + once sync.Once +) func configure(plugin *node.Plugin) { - webapi.Server.GET("autopeering/neighbors", getNeighbors) + webapi.Server().GET("autopeering/neighbors", getNeighbors) +} + +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin } // getNeighbors returns the chosen and accepted neighbors of the node diff --git a/plugins/webapi/data/plugin.go b/plugins/webapi/data/plugin.go index 83d86d26..91f683f5 100644 --- a/plugins/webapi/data/plugin.go +++ b/plugins/webapi/data/plugin.go @@ -2,6 +2,7 @@ package data import ( "net/http" + "sync" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" "github.com/iotaledger/goshimmer/plugins/issuer" @@ -15,14 +16,23 @@ import ( const PluginName = "WebAPI data Endpoint" var ( - // Plugin is the plugin instance of the web API data endpoint plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure) + // plugin is the plugin instance of the web API data endpoint plugin. + plugin *node.Plugin + once sync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin +} + func configure(plugin *node.Plugin) { log = logger.NewLogger(PluginName) - webapi.Server.POST("data", broadcastData) + webapi.Server().POST("data", broadcastData) } // broadcastData creates a message of the given payload and diff --git a/plugins/webapi/drng/plugin.go b/plugins/webapi/drng/plugin.go index b9e542c6..d2a89ec6 100644 --- a/plugins/webapi/drng/plugin.go +++ b/plugins/webapi/drng/plugin.go @@ -6,18 +6,28 @@ import ( "github.com/iotaledger/goshimmer/plugins/webapi/drng/info/committee" "github.com/iotaledger/goshimmer/plugins/webapi/drng/info/randomness" "github.com/iotaledger/hive.go/node" + "sync" ) // PluginName is the name of the web API DRNG endpoint plugin. const PluginName = "WebAPI DRNG Endpoint" var ( - // Plugin is the plugin instance of the web API DRNG endpoint plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure) + // plugin is the plugin instance of the web API DRNG endpoint plugin. + plugin *node.Plugin + once sync.Once ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin +} + func configure(_ *node.Plugin) { - webapi.Server.POST("drng/collectiveBeacon", collectivebeacon.Handler) - webapi.Server.GET("drng/info/committee", committee.Handler) - webapi.Server.GET("drng/info/randomness", randomness.Handler) + webapi.Server().POST("drng/collectiveBeacon", collectivebeacon.Handler) + webapi.Server().GET("drng/info/committee", committee.Handler) + webapi.Server().GET("drng/info/randomness", randomness.Handler) } diff --git a/plugins/webapi/healthz/plugin.go b/plugins/webapi/healthz/plugin.go index 29af56fd..d0c859d4 100644 --- a/plugins/webapi/healthz/plugin.go +++ b/plugins/webapi/healthz/plugin.go @@ -2,6 +2,7 @@ package healthz import ( "net/http" + goSync "sync" "github.com/iotaledger/goshimmer/plugins/gossip" "github.com/iotaledger/goshimmer/plugins/sync" @@ -13,11 +14,21 @@ import ( // PluginName is the name of the web API healthz endpoint plugin. const PluginName = "WebAPI healthz Endpoint" -// Plugin is the plugin instance of the web API info endpoint plugin. -var Plugin = node.NewPlugin(PluginName, node.Enabled, configure) +var ( + // plugin is the plugin instance of the web API info endpoint plugin. + plugin *node.Plugin + once goSync.Once +) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin +} func configure(_ *node.Plugin) { - webapi.Server.GET("healthz", getHealthz) + webapi.Server().GET("healthz", getHealthz) } func getHealthz(c echo.Context) error { diff --git a/plugins/webapi/info/plugin.go b/plugins/webapi/info/plugin.go index 6dc44a26..255343b6 100644 --- a/plugins/webapi/info/plugin.go +++ b/plugins/webapi/info/plugin.go @@ -3,6 +3,7 @@ package info import ( "net/http" "sort" + goSync "sync" "github.com/iotaledger/goshimmer/plugins/autopeering/local" "github.com/iotaledger/goshimmer/plugins/banner" @@ -15,11 +16,22 @@ import ( // PluginName is the name of the web API info endpoint plugin. const PluginName = "WebAPI info Endpoint" -// Plugin is the plugin instance of the web API info endpoint plugin. -var Plugin = node.NewPlugin(PluginName, node.Enabled, configure) +var ( + // plugin is the plugin instance of the web API info endpoint plugin. + plugin *node.Plugin + once goSync.Once +) + +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin +} func configure(_ *node.Plugin) { - webapi.Server.GET("info", getInfo) + webapi.Server().GET("info", getInfo) } // getInfo returns the info of the node diff --git a/plugins/webapi/message/plugin.go b/plugins/webapi/message/plugin.go index 7e29b872..015a69e3 100644 --- a/plugins/webapi/message/plugin.go +++ b/plugins/webapi/message/plugin.go @@ -2,6 +2,7 @@ package message import ( "net/http" + "sync" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/plugins/messagelayer" @@ -15,15 +16,24 @@ import ( const PluginName = "WebAPI message Endpoint" var ( - // Plugin is the plugin instance of the web API message endpoint plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure) + // plugin is the plugin instance of the web API message endpoint plugin. + plugin *node.Plugin + once sync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin +} + func configure(plugin *node.Plugin) { log = logger.NewLogger(PluginName) - webapi.Server.POST("message/findById", findMessageByID) - webapi.Server.POST("message/sendPayload", sendPayload) + webapi.Server().POST("message/findById", findMessageByID) + webapi.Server().POST("message/sendPayload", sendPayload) } // findMessageByID returns the array of messages for the @@ -48,8 +58,8 @@ func findMessageByID(c echo.Context) error { return c.JSON(http.StatusBadRequest, Response{Error: err.Error()}) } - msgObject := messagelayer.Tangle.Message(msgID) - msgMetadataObject := messagelayer.Tangle.MessageMetadata(msgID) + msgObject := messagelayer.Tangle().Message(msgID) + msgMetadataObject := messagelayer.Tangle().MessageMetadata(msgID) if !msgObject.Exists() || !msgMetadataObject.Exists() { result = append(result, Message{}) diff --git a/plugins/webapi/plugin.go b/plugins/webapi/plugin.go index f97f3db9..73482f88 100644 --- a/plugins/webapi/plugin.go +++ b/plugins/webapi/plugin.go @@ -4,6 +4,7 @@ import ( "context" "errors" "net/http" + "sync" "time" "github.com/iotaledger/goshimmer/packages/shutdown" @@ -18,25 +19,44 @@ import ( const PluginName = "WebAPI" var ( - // Plugin is the plugin instance of the web API plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) - // Server is the web API server. - Server = echo.New() + // plugin is the plugin instance of the web API plugin. + plugin *node.Plugin + pluginOnce sync.Once + // server is the web API server. + server *echo.Echo + serverOnce sync.Once log *logger.Logger ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + pluginOnce.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) + }) + return plugin +} + +// Server gets the server instance. +func Server() *echo.Echo { + serverOnce.Do(func() { + server = echo.New() + }) + return server +} + func configure(*node.Plugin) { + server = Server() log = logger.NewLogger(PluginName) // configure the server - Server.HideBanner = true - Server.HidePort = true - Server.GET("/", IndexRequest) + server.HideBanner = true + server.HidePort = true + server.GET("/", IndexRequest) } func run(*node.Plugin) { log.Infof("Starting %s ...", PluginName) - if err := daemon.BackgroundWorker("WebAPI Server", worker, shutdown.PriorityWebAPI); err != nil { + if err := daemon.BackgroundWorker("WebAPI server", worker, shutdown.PriorityWebAPI); err != nil { log.Panicf("Failed to start as daemon: %s", err) } } @@ -45,10 +65,10 @@ func worker(shutdownSignal <-chan struct{}) { defer log.Infof("Stopping %s ... done", PluginName) stopped := make(chan struct{}) - bindAddr := config.Node.GetString(CfgBindAddress) + bindAddr := config.Node().GetString(CfgBindAddress) go func() { log.Infof("%s started, bind-address=%s", PluginName, bindAddr) - if err := Server.Start(bindAddr); err != nil { + if err := server.Start(bindAddr); err != nil { if !errors.Is(err, http.ErrServerClosed) { log.Errorf("Error serving: %s", err) } @@ -65,7 +85,7 @@ func worker(shutdownSignal <-chan struct{}) { log.Infof("Stopping %s ...", PluginName) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - if err := Server.Shutdown(ctx); err != nil { + if err := server.Shutdown(ctx); err != nil { log.Errorf("Error stopping: %s", err) } } diff --git a/plugins/webapi/spammer/plugin.go b/plugins/webapi/spammer/plugin.go index f4de8c78..248338f6 100644 --- a/plugins/webapi/spammer/plugin.go +++ b/plugins/webapi/spammer/plugin.go @@ -8,6 +8,7 @@ import ( "github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" + "sync" ) var messageSpammer *spammer.Spammer @@ -15,15 +16,25 @@ var messageSpammer *spammer.Spammer // PluginName is the name of the spammer plugin. const PluginName = "Spammer" -// Plugin is the plugin instance of the spammer plugin. -var Plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) +var ( + // plugin is the plugin instance of the spammer plugin. + plugin *node.Plugin + once sync.Once + log *logger.Logger +) -var log *logger.Logger +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) + }) + return plugin +} func configure(plugin *node.Plugin) { log = logger.NewLogger(PluginName) messageSpammer = spammer.New(issuer.IssuePayload) - webapi.Server.GET("spammer", handleRequest) + webapi.Server().GET("spammer", handleRequest) } func run(*node.Plugin) { diff --git a/plugins/webapi/value/attachments/handler.go b/plugins/webapi/value/attachments/handler.go index 9996feec..a54d517e 100644 --- a/plugins/webapi/value/attachments/handler.go +++ b/plugins/webapi/value/attachments/handler.go @@ -21,7 +21,7 @@ func Handler(c echo.Context) error { var valueObjs []ValueObject // get txn by txn id - txnObj := valuetransfers.Tangle.Transaction(txnID) + txnObj := valuetransfers.Tangle().Transaction(txnID) defer txnObj.Release() if !txnObj.Exists() { return c.JSON(http.StatusNotFound, Response{Error: "Transaction not found"}) @@ -29,7 +29,7 @@ func Handler(c echo.Context) error { txn := utils.ParseTransaction(txnObj.Unwrap()) // get attachements by txn id - for _, attachmentObj := range valuetransfers.Tangle.Attachments(txnID) { + for _, attachmentObj := range valuetransfers.Tangle().Attachments(txnID) { defer attachmentObj.Release() if !attachmentObj.Exists() { continue @@ -37,7 +37,7 @@ func Handler(c echo.Context) error { attachment := attachmentObj.Unwrap() // get payload by payload id - payloadObj := valuetransfers.Tangle.Payload(attachment.PayloadID()) + payloadObj := valuetransfers.Tangle().Payload(attachment.PayloadID()) defer payloadObj.Release() if !payloadObj.Exists() { continue diff --git a/plugins/webapi/value/gettransactionbyid/handler.go b/plugins/webapi/value/gettransactionbyid/handler.go index b4bee704..0f8ebaaa 100644 --- a/plugins/webapi/value/gettransactionbyid/handler.go +++ b/plugins/webapi/value/gettransactionbyid/handler.go @@ -17,12 +17,12 @@ func Handler(c echo.Context) error { } // get txn by txn id - cachedTxnMetaObj := valuetransfers.Tangle.TransactionMetadata(txnID) + cachedTxnMetaObj := valuetransfers.Tangle().TransactionMetadata(txnID) defer cachedTxnMetaObj.Release() if !cachedTxnMetaObj.Exists() { return c.JSON(http.StatusNotFound, Response{Error: "Transaction not found"}) } - cachedTxnObj := valuetransfers.Tangle.Transaction(txnID) + cachedTxnObj := valuetransfers.Tangle().Transaction(txnID) defer cachedTxnObj.Release() if !cachedTxnObj.Exists() { return c.JSON(http.StatusNotFound, Response{Error: "Transaction not found"}) diff --git a/plugins/webapi/value/plugin.go b/plugins/webapi/value/plugin.go index 72facce9..4fcdf40e 100644 --- a/plugins/webapi/value/plugin.go +++ b/plugins/webapi/value/plugin.go @@ -8,20 +8,30 @@ import ( "github.com/iotaledger/goshimmer/plugins/webapi/value/testsendtxn" "github.com/iotaledger/goshimmer/plugins/webapi/value/unspentoutputs" "github.com/iotaledger/hive.go/node" + "sync" ) // PluginName is the name of the web API DRNG endpoint plugin. const PluginName = "WebAPI Value Endpoint" var ( - // Plugin is the plugin instance of the web API DRNG endpoint plugin. - Plugin = node.NewPlugin(PluginName, node.Enabled, configure) + // plugin is the plugin instance of the web API DRNG endpoint plugin. + plugin *node.Plugin + once sync.Once ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Enabled, configure) + }) + return plugin +} + func configure(_ *node.Plugin) { - webapi.Server.GET("value/attachments", attachments.Handler) - webapi.Server.POST("value/unspentOutputs", unspentoutputs.Handler) - webapi.Server.POST("value/sendTransaction", sendtransaction.Handler) - webapi.Server.POST("value/testSendTxn", testsendtxn.Handler) - webapi.Server.GET("value/transactionByID", gettransactionbyid.Handler) + webapi.Server().GET("value/attachments", attachments.Handler) + webapi.Server().POST("value/unspentOutputs", unspentoutputs.Handler) + webapi.Server().POST("value/sendTransaction", sendtransaction.Handler) + webapi.Server().POST("value/testSendTxn", testsendtxn.Handler) + webapi.Server().GET("value/transactionByID", gettransactionbyid.Handler) } diff --git a/plugins/webapi/value/unspentoutputs/handler.go b/plugins/webapi/value/unspentoutputs/handler.go index 05be4eb5..22dc4f0e 100644 --- a/plugins/webapi/value/unspentoutputs/handler.go +++ b/plugins/webapi/value/unspentoutputs/handler.go @@ -28,11 +28,11 @@ func Handler(c echo.Context) error { outputids := make([]OutputID, 0) // get outputids by address - for id, cachedOutput := range valuetransfers.Tangle.OutputsOnAddress(address) { + for id, cachedOutput := range valuetransfers.Tangle().OutputsOnAddress(address) { // TODO: don't do this in a for defer cachedOutput.Release() output := cachedOutput.Unwrap() - cachedTxMeta := valuetransfers.Tangle.TransactionMetadata(output.TransactionID()) + cachedTxMeta := valuetransfers.Tangle().TransactionMetadata(output.TransactionID()) // TODO: don't do this in a for defer cachedTxMeta.Release() diff --git a/plugins/webauth/webauth.go b/plugins/webauth/webauth.go index 946f0c8b..a337d7ba 100644 --- a/plugins/webauth/webauth.go +++ b/plugins/webauth/webauth.go @@ -3,6 +3,7 @@ package webauth import ( "net/http" "strings" + "sync" "time" "github.com/dgrijalva/jwt-go" @@ -18,20 +19,29 @@ import ( const PluginName = "WebAPI Auth" var ( - // Plugin is the plugin instance of the web API auth plugin. - Plugin = node.NewPlugin(PluginName, node.Disabled, configure) + // plugin is the plugin instance of the web API auth plugin. + plugin *node.Plugin + once sync.Once log *logger.Logger privateKey string ) +// Plugin gets the plugin instance. +func Plugin() *node.Plugin { + once.Do(func() { + plugin = node.NewPlugin(PluginName, node.Disabled, configure) + }) + return plugin +} + func configure(plugin *node.Plugin) { log = logger.NewLogger(PluginName) - privateKey = config.Node.GetString(CfgWebAPIAuthPrivateKey) + privateKey = config.Node().GetString(CfgWebAPIAuthPrivateKey) if len(privateKey) == 0 { panic("") } - webapi.Server.Use(middleware.JWTWithConfig(middleware.JWTConfig{ + webapi.Server().Use(middleware.JWTWithConfig(middleware.JWTConfig{ SigningKey: []byte(privateKey), Skipper: func(c echo.Context) bool { if strings.HasPrefix(c.Path(), "/ui") || c.Path() == "/login" { @@ -41,7 +51,7 @@ func configure(plugin *node.Plugin) { }, })) - webapi.Server.POST("/login", Handler) + webapi.Server().POST("/login", Handler) log.Info("WebAPI is now secured through JWT authentication") } @@ -66,8 +76,8 @@ func Handler(c echo.Context) error { return echo.ErrBadRequest } - if login.Username != config.Node.GetString(CfgWebAPIAuthUsername) || - login.Password != config.Node.GetString(CfgWebAPIAuthPassword) { + if login.Username != config.Node().GetString(CfgWebAPIAuthUsername) || + login.Password != config.Node().GetString(CfgWebAPIAuthPassword) { return echo.ErrUnauthorized } diff --git a/tools/relay-checker/config.go b/tools/relay-checker/config.go index eceecbcb..5e8d2ed7 100644 --- a/tools/relay-checker/config.go +++ b/tools/relay-checker/config.go @@ -11,24 +11,24 @@ var ( ) func initConfig() { - if config.Node.GetString(CfgTargetNode) == "" { + if config.Node().GetString(CfgTargetNode) == "" { panic("Set the target node address\n") } - target = config.Node.GetString(CfgTargetNode) + target = config.Node().GetString(CfgTargetNode) - if len(config.Node.GetStringSlice(CfgTestNodes)) == 0 { + if len(config.Node().GetStringSlice(CfgTestNodes)) == 0 { panic("Set node addresses\n") } - nodes = append(nodes, config.Node.GetStringSlice(CfgTestNodes)...) + nodes = append(nodes, config.Node().GetStringSlice(CfgTestNodes)...) // optional settings - if config.Node.GetString(CfgData) != "" { - msgData = config.Node.GetString(CfgData) + if config.Node().GetString(CfgData) != "" { + msgData = config.Node().GetString(CfgData) } - if config.Node.GetInt(CfgCooldownTime) > 0 { - cooldownTime = config.Node.GetInt(CfgCooldownTime) + if config.Node().GetInt(CfgCooldownTime) > 0 { + cooldownTime = config.Node().GetInt(CfgCooldownTime) } - if config.Node.GetInt(CfgRepeat) > 0 { - repeat = config.Node.GetInt(CfgRepeat) + if config.Node().GetInt(CfgRepeat) > 0 { + repeat = config.Node().GetInt(CfgRepeat) } } -- GitLab