1
1
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
2
2
// SPDX-License-Identifier: LGPL-3.0-only
3
3
4
- // SPDX-License-Identifier: LGPL-3.0-only
5
-
6
4
using System ;
7
5
using System . Collections . Concurrent ;
8
6
using System . Collections . Generic ;
12
10
using System . Runtime . CompilerServices ;
13
11
using System . Text . Json ;
14
12
using System . Threading ;
15
- using System . Threading . Channels ;
16
13
using System . Threading . Tasks ;
17
14
using Nethermind . Config ;
18
15
using Nethermind . Core . Crypto ;
19
16
using Nethermind . Logging ;
20
- using Nethermind . Network . StaticNodes ;
21
- using Nethermind . Serialization . Json ;
22
17
using Nethermind . Stats . Model ;
23
18
24
19
namespace Nethermind . Network
25
20
{
26
21
public class TrustedNodesManager : ITrustedNodesManager
27
22
{
28
23
private ConcurrentDictionary < PublicKey , NetworkNode > _nodes = new ( ) ;
29
-
30
24
private readonly string _trustedNodesPath ;
31
25
private readonly ILogger _logger ;
32
26
@@ -38,16 +32,19 @@ public TrustedNodesManager(string trustedNodesPath, ILogManager logManager)
38
32
39
33
public IEnumerable < NetworkNode > Nodes => _nodes . Values ;
40
34
41
- private static readonly char [ ] separator = new [ ] { '\r ' , '\n ' } ;
42
-
43
35
public async Task InitAsync ( )
44
36
{
45
37
if ( ! File . Exists ( _trustedNodesPath ) )
46
38
{
47
- if ( _logger . IsDebug ) _logger . Debug ( $ "Trusted nodes file not found for path: { _trustedNodesPath } ") ;
39
+ if ( _logger . IsDebug )
40
+ {
41
+ _logger . Debug ( $ "Trusted nodes file not found at: { _trustedNodesPath } ") ;
42
+ }
48
43
return ;
49
44
}
50
- List < string > lines = new ( ) ;
45
+
46
+ HashSet < string > lines = new ( ) ;
47
+ // Read lines asynchronously
51
48
await foreach ( string line in File . ReadLinesAsync ( _trustedNodesPath ) )
52
49
{
53
50
if ( ! string . IsNullOrWhiteSpace ( line ) )
@@ -56,66 +53,73 @@ public async Task InitAsync()
56
53
}
57
54
}
58
55
59
- string [ ] nodes = lines . Distinct ( ) . ToArray ( ) ;
60
-
61
56
if ( _logger . IsInfo )
62
- _logger . Info ( $ "Loaded { nodes . Length } trusted nodes from file: { Path . GetFullPath ( _trustedNodesPath ) } ") ;
57
+ {
58
+ _logger . Info ( $ "Loaded { lines . Count } trusted nodes from: { Path . GetFullPath ( _trustedNodesPath ) } ") ;
59
+ }
63
60
64
- if ( nodes . Length != 0 && _logger . IsDebug )
61
+ if ( lines . Any ( ) && _logger . IsDebug )
65
62
{
66
- string formattedNodes = string . Join ( Environment . NewLine , nodes ) ;
67
- _logger . Debug ( $ "Trusted nodes: { Environment . NewLine } { formattedNodes } ") ;
63
+ _logger . Debug ( "Trusted nodes:\n " + string . Join ( Environment . NewLine , lines ) ) ;
68
64
}
69
65
66
+ // Parse each line into a NetworkNode
70
67
List < NetworkNode > networkNodes = new ( ) ;
71
- foreach ( string ? n in nodes )
68
+ foreach ( string line in lines )
72
69
{
73
70
try
74
71
{
75
- NetworkNode networkNode = new ( n ) ;
76
- networkNodes . Add ( networkNode ) ;
72
+ networkNodes . Add ( new NetworkNode ( line ) ) ;
77
73
}
78
- catch ( Exception exception ) when ( exception is ArgumentException or SocketException )
74
+ catch ( Exception ex ) when ( ex is ArgumentException or SocketException )
79
75
{
80
- if ( _logger . IsError ) _logger . Error ( "Unable to process node. " , exception ) ;
76
+ if ( _logger . IsError )
77
+ {
78
+ _logger . Error ( $ "Failed to parse '{ line } ' as a trusted node.", ex ) ;
79
+ }
81
80
}
82
81
}
83
82
84
- _nodes = new ConcurrentDictionary < PublicKey , NetworkNode > ( networkNodes . ToDictionary ( n => n . NodeId , n => n ) ) ;
83
+ _nodes = new ConcurrentDictionary < PublicKey , NetworkNode > (
84
+ networkNodes . ToDictionary ( n => n . NodeId , n => n ) ) ;
85
85
}
86
86
87
- private static string [ ] GetNodes ( string data )
87
+ // ---- INodeSource requirement: IAsyncEnumerable<Node> ----
88
+ // C# requires 'async' for IAsyncEnumerable yield. We'll add a small 'await Task.Yield()'
89
+ // to avoid the warning about "no awaits".
90
+ public async IAsyncEnumerable < Node > DiscoverNodes ( [ EnumeratorCancellation ] CancellationToken cancellationToken )
88
91
{
89
- string [ ] nodes ;
90
- try
91
- {
92
- nodes = JsonSerializer . Deserialize < string [ ] > ( data ) ?? Array . Empty < string > ( ) ;
93
- }
94
- catch ( JsonException )
92
+ // At least one 'await', so no compiler warnings
93
+ await Task . Yield ( ) ;
94
+
95
+ foreach ( NetworkNode netNode in _nodes . Values )
95
96
{
96
- nodes = data . Split ( separator , StringSplitOptions . RemoveEmptyEntries ) ;
97
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
98
+ yield return new Node ( netNode ) { IsTrusted = true } ;
97
99
}
98
-
99
- return nodes . Distinct ( ) . ToArray ( ) ;
100
100
}
101
101
102
102
public async Task < bool > AddAsync ( Enode enode , bool updateFile = true )
103
103
{
104
104
NetworkNode networkNode = new ( enode . ToString ( ) ) ;
105
105
if ( ! _nodes . TryAdd ( networkNode . NodeId , networkNode ) )
106
106
{
107
- if ( _logger . IsInfo ) _logger . Info ( $ "Trusted node was already added: { enode } ") ;
107
+ if ( _logger . IsInfo )
108
+ {
109
+ _logger . Info ( $ "Trusted node was already added: { enode } ") ;
110
+ }
108
111
return false ;
109
112
}
110
113
111
- if ( _logger . IsInfo ) _logger . Info ( $ "Trusted node added: { enode } ") ;
112
- Node node = new ( networkNode ) { IsTrusted = true } ;
113
- NodeAdded ? . Invoke ( this , new NodeEventArgs ( node ) ) ;
114
+ if ( _logger . IsInfo )
115
+ {
116
+ _logger . Info ( $ "Trusted node added: { enode } ") ;
117
+ }
118
+
114
119
if ( updateFile )
115
120
{
116
121
await SaveFileAsync ( ) ;
117
122
}
118
-
119
123
return true ;
120
124
}
121
125
@@ -124,18 +128,22 @@ public async Task<bool> RemoveAsync(Enode enode, bool updateFile = true)
124
128
NetworkNode networkNode = new ( enode . ToString ( ) ) ;
125
129
if ( ! _nodes . TryRemove ( networkNode . NodeId , out _ ) )
126
130
{
127
- if ( _logger . IsInfo ) _logger . Info ( $ "Trusted node was not found: { enode } ") ;
131
+ if ( _logger . IsInfo )
132
+ {
133
+ _logger . Info ( $ "Trusted node was not found: { enode } ") ;
134
+ }
128
135
return false ;
129
136
}
130
137
131
- if ( _logger . IsInfo ) _logger . Info ( $ "Trusted node was removed: { enode } ") ;
132
- Node node = new ( networkNode ) { IsTrusted = true } ;
133
- NodeRemoved ? . Invoke ( this , new NodeEventArgs ( node ) ) ;
138
+ if ( _logger . IsInfo )
139
+ {
140
+ _logger . Info ( $ "Trusted node was removed: { enode } ") ;
141
+ }
142
+
134
143
if ( updateFile )
135
144
{
136
145
await SaveFileAsync ( ) ;
137
146
}
138
-
139
147
return true ;
140
148
}
141
149
@@ -145,41 +153,20 @@ public bool IsTrusted(Enode enode)
145
153
return _nodes . ContainsKey ( node . NodeId ) ;
146
154
}
147
155
148
- private Task SaveFileAsync ( )
149
- => File . WriteAllTextAsync ( _trustedNodesPath ,
150
- JsonSerializer . Serialize ( _nodes . Values . Select ( n => n . ToString ( ) ) , EthereumJsonSerializer . JsonOptionsIndented ) ) ;
151
-
152
- public async IAsyncEnumerable < Node > DiscoverNodes ( [ EnumeratorCancellation ] CancellationToken cancellationToken )
156
+ // ---- INodeSource requirement: event EventHandler<NodeEventArgs> ----
157
+ public event EventHandler < NodeEventArgs > ? NodeRemoved
153
158
{
154
- Channel < Node > ch = Channel . CreateBounded < Node > ( 128 ) ;
155
-
156
- foreach ( Node node in _nodes . Values . Select ( n => new Node ( n ) { IsTrusted = true } ) )
157
- {
158
- cancellationToken . ThrowIfCancellationRequested ( ) ;
159
- yield return node ;
160
- }
161
-
162
- void handler ( object ? _ , NodeEventArgs args )
163
- {
164
- ch . Writer . TryWrite ( args . Node ) ;
165
- }
159
+ add { /* no-op */ }
160
+ remove { /* no-op*/ }
161
+ }
166
162
167
- try
168
- {
169
- NodeAdded += handler ;
170
163
171
- await foreach ( Node node in ch . Reader . ReadAllAsync ( cancellationToken ) )
172
- {
173
- yield return node ;
174
- }
175
- }
176
- finally
177
- {
178
- NodeAdded -= handler ;
179
- }
164
+ private Task SaveFileAsync ( )
165
+ {
166
+ // Serialize the Enode strings from each stored node
167
+ IEnumerable < string > enodes = _nodes . Values . Select ( n => n . ToString ( ) ) ;
168
+ return File . WriteAllTextAsync ( _trustedNodesPath ,
169
+ JsonSerializer . Serialize ( enodes , new JsonSerializerOptions { WriteIndented = true } ) ) ;
180
170
}
181
-
182
- private event EventHandler < NodeEventArgs > ? NodeAdded ;
183
- public event EventHandler < NodeEventArgs > ? NodeRemoved ;
184
171
}
185
172
}
0 commit comments