@@ -3,11 +3,15 @@ use bitcoin::secp256k1::PublicKey;
3
3
use clap:: { builder:: TypedValueParser , Parser } ;
4
4
use log:: LevelFilter ;
5
5
use serde:: { Deserialize , Serialize } ;
6
+ use simln_lib:: sim_node:: {
7
+ ln_node_from_graph, populate_network_graph, ChannelPolicy , SimGraph , SimulatedChannel ,
8
+ } ;
6
9
use simln_lib:: {
7
10
cln, cln:: ClnNode , eclair, eclair:: EclairNode , lnd, lnd:: LndNode , serializers,
8
11
ActivityDefinition , Amount , Interval , LightningError , LightningNode , NodeId , NodeInfo ,
9
12
Simulation , SimulationCfg , WriteResults ,
10
13
} ;
14
+ use simln_lib:: { ShortChannelID , SimulationError } ;
11
15
use std:: collections:: HashMap ;
12
16
use std:: fs;
13
17
use std:: ops:: AsyncFn ;
@@ -81,9 +85,33 @@ pub struct Cli {
81
85
pub fix_seed : Option < u64 > ,
82
86
}
83
87
88
+ impl Cli {
89
+ pub fn validate ( & self , sim_params : & SimParams ) -> Result < ( ) , anyhow:: Error > {
90
+ // Validate that nodes and sim_graph are exclusively set
91
+ if !sim_params. nodes . is_empty ( ) && !sim_params. sim_network . is_empty ( ) {
92
+ return Err ( anyhow ! (
93
+ "Simulation file cannot contain {} nodes and {} sim_graph entries,
94
+ simulation can only be run with real or simulated nodes not both." ,
95
+ sim_params. nodes. len( ) ,
96
+ sim_params. sim_network. len( )
97
+ ) ) ;
98
+ }
99
+ if sim_params. nodes . is_empty ( ) && sim_params. sim_network . is_empty ( ) {
100
+ return Err ( anyhow ! (
101
+ "Simulation file must contain nodes to run with real lightning
102
+ nodes or sim_graph to run with simulated nodes"
103
+ ) ) ;
104
+ }
105
+ Ok ( ( ) )
106
+ }
107
+ }
108
+
84
109
#[ derive( Debug , Serialize , Deserialize , Clone ) ]
85
110
pub struct SimParams {
86
- pub nodes : Vec < NodeConnection > ,
111
+ #[ serde( default ) ]
112
+ nodes : Vec < NodeConnection > ,
113
+ #[ serde( default ) ]
114
+ pub sim_network : Vec < NetworkParser > ,
87
115
#[ serde( default ) ]
88
116
pub activity : Vec < ActivityParser > ,
89
117
}
@@ -96,6 +124,27 @@ pub enum NodeConnection {
96
124
Eclair ( eclair:: EclairConnection ) ,
97
125
}
98
126
127
+ /// Data structure that is used to parse information from the simulation file. It is used to
128
+ /// create a mocked network.
129
+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
130
+ pub struct NetworkParser {
131
+ pub scid : ShortChannelID ,
132
+ pub capacity_msat : u64 ,
133
+ pub node_1 : ChannelPolicy ,
134
+ pub node_2 : ChannelPolicy ,
135
+ }
136
+
137
+ impl From < NetworkParser > for SimulatedChannel {
138
+ fn from ( network_parser : NetworkParser ) -> Self {
139
+ SimulatedChannel :: new (
140
+ network_parser. capacity_msat ,
141
+ network_parser. scid ,
142
+ network_parser. node_1 ,
143
+ network_parser. node_2 ,
144
+ )
145
+ }
146
+ }
147
+
99
148
/// Data structure used to parse information from the simulation file. It allows source and destination to be
100
149
/// [NodeId], which enables the use of public keys and aliases in the simulation description.
101
150
#[ derive( Debug , Clone , Serialize , Deserialize ) ]
@@ -140,23 +189,87 @@ impl TryFrom<&Cli> for SimulationCfg {
140
189
}
141
190
}
142
191
192
+ struct NodeMapping {
193
+ pk_node_map : HashMap < PublicKey , NodeInfo > ,
194
+ alias_node_map : HashMap < String , NodeInfo > ,
195
+ }
196
+
197
+ pub async fn create_simulation_with_network (
198
+ cli : & Cli ,
199
+ sim_params : & SimParams ,
200
+ tasks : TaskTracker ,
201
+ ) -> Result < ( Simulation , Vec < ActivityDefinition > ) , anyhow:: Error > {
202
+ let cfg: SimulationCfg = SimulationCfg :: try_from ( cli) ?;
203
+ let SimParams {
204
+ nodes : _,
205
+ sim_network,
206
+ activity : _activity,
207
+ } = sim_params;
208
+
209
+ // Convert nodes representation for parsing to SimulatedChannel
210
+ let channels = sim_network
211
+ . clone ( )
212
+ . into_iter ( )
213
+ . map ( SimulatedChannel :: from)
214
+ . collect :: < Vec < SimulatedChannel > > ( ) ;
215
+
216
+ let mut nodes_info = HashMap :: new ( ) ;
217
+ for channel in & channels {
218
+ let ( node_1_info, node_2_info) = channel. create_simulated_nodes ( ) ;
219
+ nodes_info. insert ( node_1_info. pubkey , node_1_info) ;
220
+ nodes_info. insert ( node_2_info. pubkey , node_2_info) ;
221
+ }
222
+
223
+ let ( shutdown_trigger, shutdown_listener) = triggered:: trigger ( ) ;
224
+
225
+ // Setup a simulation graph that will handle propagation of payments through the network
226
+ let simulation_graph = Arc :: new ( Mutex :: new (
227
+ SimGraph :: new ( channels. clone ( ) , tasks. clone ( ) , shutdown_trigger. clone ( ) )
228
+ . map_err ( |e| SimulationError :: SimulatedNetworkError ( format ! ( "{:?}" , e) ) ) ?,
229
+ ) ) ;
230
+
231
+ // Copy all simulated channels into a read-only routing graph, allowing to pathfind for
232
+ // individual payments without locking th simulation graph (this is a duplication of the channels,
233
+ // but the performance tradeoff is worthwhile for concurrent pathfinding).
234
+ let routing_graph = Arc :: new (
235
+ populate_network_graph ( channels)
236
+ . map_err ( |e| SimulationError :: SimulatedNetworkError ( format ! ( "{:?}" , e) ) ) ?,
237
+ ) ;
238
+
239
+ let nodes = ln_node_from_graph ( simulation_graph. clone ( ) , routing_graph) . await ;
240
+ let validated_activities =
241
+ get_validated_activities ( & nodes, nodes_info, sim_params. activity . clone ( ) ) . await ?;
242
+
243
+ Ok ( (
244
+ Simulation :: new ( cfg, nodes, tasks, shutdown_trigger, shutdown_listener) ,
245
+ validated_activities,
246
+ ) )
247
+ }
248
+
143
249
/// Parses the cli options provided and creates a simulation to be run, connecting to lightning nodes and validating
144
250
/// any activity described in the simulation file.
145
251
pub async fn create_simulation (
146
252
cli : & Cli ,
147
253
sim_params : & SimParams ,
148
- ) -> Result < ( Simulation , HashMap < PublicKey , NodeInfo > ) , anyhow:: Error > {
254
+ tasks : TaskTracker ,
255
+ ) -> Result < ( Simulation , Vec < ActivityDefinition > ) , anyhow:: Error > {
149
256
let cfg: SimulationCfg = SimulationCfg :: try_from ( cli) ?;
150
-
151
257
let SimParams {
152
258
nodes,
259
+ sim_network : _sim_network,
153
260
activity : _activity,
154
261
} = sim_params;
155
262
156
263
let ( clients, clients_info) = get_clients ( nodes. to_vec ( ) ) . await ?;
157
- let tasks = TaskTracker :: new ( ) ;
264
+ let ( shutdown_trigger , shutdown_listener ) = triggered :: trigger ( ) ;
158
265
159
- Ok ( ( Simulation :: new ( cfg, clients, tasks) , clients_info) )
266
+ let validated_activities =
267
+ get_validated_activities ( & clients, clients_info, sim_params. activity . clone ( ) ) . await ?;
268
+
269
+ Ok ( (
270
+ Simulation :: new ( cfg, clients, tasks, shutdown_trigger, shutdown_listener) ,
271
+ validated_activities,
272
+ ) )
160
273
}
161
274
162
275
/// Connects to the set of nodes providing, returning a map of node public keys to LightningNode implementations and
@@ -194,9 +307,7 @@ async fn get_clients(
194
307
195
308
/// Adds a lightning node to a client map and tracking maps used to lookup node pubkeys and aliases for activity
196
309
/// validation.
197
- async fn add_node_to_maps (
198
- nodes : & HashMap < PublicKey , NodeInfo > ,
199
- ) -> Result < ( HashMap < PublicKey , NodeInfo > , HashMap < String , NodeInfo > ) , LightningError > {
310
+ fn add_node_to_maps ( nodes : & HashMap < PublicKey , NodeInfo > ) -> Result < NodeMapping , LightningError > {
200
311
let mut pk_node_map = HashMap :: new ( ) ;
201
312
let mut alias_node_map = HashMap :: new ( ) ;
202
313
@@ -228,7 +339,10 @@ async fn add_node_to_maps(
228
339
pk_node_map. insert ( node_info. pubkey , node_info. clone ( ) ) ;
229
340
}
230
341
231
- Ok ( ( pk_node_map, alias_node_map) )
342
+ Ok ( NodeMapping {
343
+ pk_node_map,
344
+ alias_node_map,
345
+ } )
232
346
}
233
347
234
348
/// Validates a set of defined activities, cross-checking aliases and public keys against the set of clients that
@@ -372,8 +486,10 @@ pub async fn get_validated_activities(
372
486
"no nodes for query" . to_string ( ) ,
373
487
) )
374
488
} ;
375
- let ( pk_node_map, alias_node_map) = add_node_to_maps ( & nodes_info) . await ?;
376
- let validated_activities =
377
- validate_activities ( activity. to_vec ( ) , pk_node_map, alias_node_map, get_node) . await ?;
378
- Ok ( validated_activities)
489
+ let NodeMapping {
490
+ pk_node_map,
491
+ alias_node_map,
492
+ } = add_node_to_maps ( & nodes_info) ?;
493
+
494
+ validate_activities ( activity. to_vec ( ) , pk_node_map, alias_node_map, get_node) . await
379
495
}
0 commit comments