-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnode.go
More file actions
348 lines (294 loc) · 10.6 KB
/
Copy pathnode.go
File metadata and controls
348 lines (294 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
package node
import (
"context"
"github.com/unpackdev/fdb/pkg/types"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/unpackdev/fdb/pkg/accounts"
"github.com/unpackdev/fdb/pkg/config"
"github.com/unpackdev/fdb/pkg/db"
"github.com/unpackdev/fdb/pkg/logger"
"github.com/unpackdev/fdb/pkg/metrics"
"github.com/unpackdev/fdb/pkg/networking"
"github.com/unpackdev/fdb/pkg/observability"
"github.com/unpackdev/fdb/pkg/rbac"
"github.com/unpackdev/fdb/pkg/share"
"github.com/unpackdev/fdb/pkg/state"
"github.com/unpackdev/fdb/pkg/topology"
"time"
"github.com/pkg/errors"
"go.uber.org/zap"
)
// Node encapsulates all components of a PeerDNS node.
type Node struct {
logger logger.Logger
ctx context.Context
cancel context.CancelFunc
cfg config.Config
obs *observability.Observability
rbacMgr *rbac.Manager
store *accounts.Store
account *accounts.Account
network *networking.Network
collector *metrics.Collector
pm *metrics.PerformanceMonitor
stateMgr *state.StateManager
actors *share.ActorSet
topology *topology.Topology
dbM *db.Manager
batchWriter *db.BatchWriter
distributor *P2PDistributor // P2P record distribution component
}
// NewNode initializes and returns a new Node.
func NewNode(
ctx context.Context, config config.Config, rbacMgr *rbac.Manager, logger logger.Logger,
store *accounts.Store, obs *observability.Observability, stateMgr *state.StateManager,
dbM *db.Manager, batchWriter *db.BatchWriter,
) (*Node, error) {
nodeCtx, cancel := context.WithCancel(ctx)
stateMgr.SetState(NodeStateType, state.Uninitialized)
if config.Networking.PeerID == "" {
logger.Error("PeerID not provided in the networking configuration")
cancel()
return nil, errors.New("peerId must be defined in the networking configuration")
}
// Attempt to load the identity from the manager using the PeerID
account, err := store.GetByPeerID(config.Networking.PeerID)
if err != nil {
logger.Error("Failed to load account from identity manager", zap.String("peer_id", config.Networking.PeerID.String()), zap.Error(err))
cancel()
return nil, errors.Wrapf(err, "failed to load account from identity manager with PeerID: %s", config.Networking.PeerID)
}
logger.Debug("Loaded node identity", zap.String("PeerID", account.PeerID().String()))
// Initialize the Consensus-Related ActorSet
actors := share.NewActorSet(logger)
//var cAccount *accounts.Account
//var caErr error
// Discover current node account keys...
// At this moment RSV is first, RV second, RS third...
// You need to tweak the node roles configuration if you wish to change the role.
// This configuration is only for the sequencers and validators. Main master account is defined above
// in this function in the beginning.
//if rbac.HasRole(config.Node.Roles, rbac.RoleSequencerValidator) {
// cAccount, caErr = store.GetByRole(rbac.RoleSequencerValidator)
// if caErr != nil {
// cancel()
// return nil, errors.Wrap(caErr, "failed to load sequencer+validator account for node - did you create node key?")
// }
//} else if rbac.HasRole(config.Node.Roles, rbac.RoleValidator) {
// cAccount, caErr = store.GetByRole(rbac.RoleValidator)
// if caErr != nil {
// cancel()
// return nil, errors.Wrap(caErr, "failed to load validator account for node - did you create node key?")
// }
//} else if rbac.HasRole(config.Node.Roles, rbac.RoleSequencer) {
// cAccount, caErr = store.GetByRole(rbac.RoleSequencer)
// if caErr != nil {
// cancel()
// return nil, errors.Wrap(caErr, "failed to load sequencer account for node - did you create node key?")
// }
//}
// Initialize the DiscoveryService
bootstrapAddrs, err := config.Networking.BootstrapPeersAsAddrs()
if err != nil {
logger.Error("Failed to parse bootstrap peers", zap.Error(err))
cancel()
return nil, err
}
// Initialize networking
ntwrk, err := networking.NewNetwork(nodeCtx, config.Networking, account, bootstrapAddrs, logger, obs, stateMgr)
if err != nil {
logger.Error("Failed to initialize P2P network", zap.Error(err))
cancel()
return nil, err
}
// Initialize metrics collector with default weights
// @TODO: These weights severely needs to be researched later on
weights := share.Metrics{
BandwidthUsage: 0.0,
Computational: 0.0,
Storage: 0.0,
Uptime: 1.0,
Responsiveness: 0.0,
Reliability: 0.0,
}
emaAlpha := 0.2 // Smoothing factor for EMA (0 < emaAlpha <= 1)
maxRespScore := 10.0 // Maximum cap for Responsiveness
collector := metrics.NewCollector(nodeCtx, logger, weights, emaAlpha, maxRespScore)
// Initialize PerformanceMonitor (do not start it yet)
performanceMonitor := metrics.NewPerformanceMonitor(
nodeCtx,
ntwrk.Host(),
logger,
obs,
collector,
1*time.Second,
100,
1*time.Second,
10*time.Second,
10*time.Second,
1*time.Second,
)
// At this moment we have all blockers satisfied to create a network topology management system
topologyManager, tErr := topology.NewTopology(ctx, logger, account, ntwrk, rbacMgr, collector, actors)
if tErr != nil {
cancel()
return nil, errors.Wrap(tErr, "failure to create new topology management system")
}
// Create a node instance with all components
node := &Node{
logger: logger,
ctx: nodeCtx,
cancel: cancel,
cfg: config,
obs: obs,
rbacMgr: rbacMgr,
store: store,
account: account,
network: ntwrk,
collector: collector,
pm: performanceMonitor,
stateMgr: stateMgr,
actors: actors,
topology: topologyManager,
dbM: dbM,
batchWriter: batchWriter,
}
// Initialize the P2P distributor with a reasonable batch size
node.distributor = NewP2PDistributor(node, 2048)
// Set the state to Initialized after successful node creation.
stateMgr.SetState(NodeStateType, state.Initialized)
return node, nil
}
// Store returns the node's account store.
func (n *Node) Store() *accounts.Store {
return n.store
}
// Account returns the node's account.
func (n *Node) Account() *accounts.Account {
return n.account
}
// StateManager returns the node's state manager.
func (n *Node) StateManager() *state.StateManager {
return n.stateMgr
}
// Discovery returns the node's discovery service.
func (n *Node) Discovery() *networking.DiscoveryService {
return n.network.Discovery()
}
// Collector returns the node's metrics collector.
func (n *Node) Collector() *metrics.Collector {
return n.collector
}
// PerformanceMonitor returns the node's performance monitor.
func (n *Node) PerformanceMonitor() *metrics.PerformanceMonitor {
return n.pm
}
// Network returns the node's network.
func (n *Node) Network() *networking.Network {
return n.network
}
// ActorSet returns the node's actor set.
func (n *Node) ActorSet() *share.ActorSet {
return n.actors
}
// Topology returns the node's topology.
func (n *Node) Topology() *topology.Topology {
return n.topology
}
// Observability returns the node's observability.
func (n *Node) Observability() *observability.Observability {
return n.obs
}
// RbacManager returns the node's RBAC manager.
func (n *Node) RbacManager() *rbac.Manager {
return n.rbacMgr
}
// Distributor returns the node's P2P distributor component
func (n *Node) Distributor() *P2PDistributor {
return n.distributor
}
// DistributeRecord adds a record to be distributed across the P2P network
func (n *Node) DistributeRecord(key [32]byte, value []byte) error {
return n.distributor.DistributeRecord(key, value, types.PriorityNormal, types.TargetAll)
}
// DistributeRecordWithPriority adds a record with specified priority and target
func (n *Node) DistributeRecordWithPriority(key [32]byte, value []byte, priority types.Priority, target types.Target) error {
return n.distributor.DistributeRecord(key, value, priority, target)
}
// DistributeRecordToPeer sends a record directly to a specific peer
func (n *Node) DistributeRecordToPeer(key [32]byte, value []byte, peerID peer.ID) error {
return n.distributor.DistributeRecordToPeer(key, value, peerID, types.PriorityNormal)
}
func (n *Node) Start() error {
n.logger.Info("Starting node")
n.stateMgr.SetState(NodeStateType, state.Starting)
if err := n.network.Start(); err != nil {
n.stateMgr.SetState(NodeStateType, state.Failed)
return errors.Wrap(err, "failed to start network")
}
// Start the P2P distributor
n.distributor.Start()
// Start a goroutine to continuously advertise the node until its advertised
// Genesis node when started won't have any peers to connect.
// Sequencers and validators do not need this...
// Regular nodes do need this process.
// For the time being, leaving this advertisement for all the nodes.
// TODO
go func() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
err := n.network.Discovery().Advertise("node", n.cfg.Networking.BootstrapNode)
if err != nil {
n.logger.Warn("Failed to advertise node service", zap.Error(err))
} else {
n.logger.Info("Successfully advertised node service")
return
}
case <-n.ctx.Done():
return
}
}
}()
// Start the performance monitor
n.pm.Start()
// Set the state to Started after successfully starting all components.
n.stateMgr.SetState(NodeStateType, state.Started)
return nil
}
// Shutdown gracefully shuts down the node.
func (n *Node) Shutdown() error {
n.stateMgr.SetState(NodeStateType, state.Stopping)
// Shut down txpool as fast as possible. No more transaction should be accepted...
// RPC as well should be notified of this to start rejecting any in-flight transactions...
//n.txpool.Shutdown()
// Now we're going to send system-wide signal to shut down any remaining operations.
// Cancelling sequencer at the top like this would potentially result in very bad situation of
// block production being screwed up including actual database screwups.
// As well we want txpool to be shutdown immediately and not through the context to ensure nothing
// is coming in any more as fast as possible.
n.cancel()
// Stop the PerformanceMonitor
if n.pm != nil {
n.pm.Stop()
}
if err := n.network.Shutdown(); err != nil && !errors.Is(err, context.Canceled) {
n.stateMgr.SetState(NodeStateType, state.Failed)
return err
}
// Stop the P2P distributor
if n.distributor != nil {
n.distributor.Stop()
}
if err := n.stateMgr.WaitForState(P2PDistributorStateType, state.Stopped, 10*time.Second); err != nil {
n.logger.Error("Failed to stop P2P distributor", zap.Error(err))
n.stateMgr.SetState(NodeStateType, state.Failed)
return err
}
n.logger.Info("Node shutdown complete")
// Set the state to Stopped after shutting down.
n.stateMgr.SetState(NodeStateType, state.Stopped)
return nil
}