Skip to content

Commit 072a6ff

Browse files
authored
Merge pull request #2555 from tnull/2023-08-have-get-route-take-params
Have `get_route` and `Route` take `RouteParameters`
2 parents eb44d99 + 543674f commit 072a6ff

14 files changed

+602
-409
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p
376376
fee_msat: amt,
377377
cltv_expiry_delta: 200,
378378
}], blinded_tail: None }],
379-
payment_params: None,
379+
route_params: None,
380380
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {
381381
check_payment_err(err, amt > max_value_sendable || amt < min_value_sendable);
382382
false
@@ -409,15 +409,15 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
409409
channel_features: middle.channel_features(),
410410
fee_msat: first_hop_fee,
411411
cltv_expiry_delta: 100,
412-
},RouteHop {
412+
}, RouteHop {
413413
pubkey: dest.get_our_node_id(),
414414
node_features: dest.node_features(),
415415
short_channel_id: dest_chan_id,
416416
channel_features: dest.channel_features(),
417417
fee_msat: amt,
418418
cltv_expiry_delta: 200,
419419
}], blinded_tail: None }],
420-
payment_params: None,
420+
route_params: None,
421421
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {
422422
let sent_amt = amt + first_hop_fee;
423423
check_payment_err(err, sent_amt < min_value_sendable || sent_amt > max_value_sendable);

fuzz/src/full_stack.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -527,10 +527,8 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
527527
4 => {
528528
let final_value_msat = slice_to_be24(get_slice!(3)) as u64;
529529
let payment_params = PaymentParameters::from_node_id(get_pubkey!(), 42);
530-
let params = RouteParameters {
531-
payment_params,
532-
final_value_msat,
533-
};
530+
let params = RouteParameters::from_payment_params_and_value(
531+
payment_params, final_value_msat);
534532
let mut payment_hash = PaymentHash([0; 32]);
535533
payment_hash.0[0..8].copy_from_slice(&be64_to_array(payments_sent));
536534
let mut sha = Sha256::engine();
@@ -548,10 +546,8 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
548546
15 => {
549547
let final_value_msat = slice_to_be24(get_slice!(3)) as u64;
550548
let payment_params = PaymentParameters::from_node_id(get_pubkey!(), 42);
551-
let params = RouteParameters {
552-
payment_params,
553-
final_value_msat,
554-
};
549+
let params = RouteParameters::from_payment_params_and_value(
550+
payment_params, final_value_msat);
555551
let mut payment_hash = PaymentHash([0; 32]);
556552
payment_hash.0[0..8].copy_from_slice(&be64_to_array(payments_sent));
557553
let mut sha = Sha256::engine();

fuzz/src/router.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,10 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
326326
let mut last_hops = Vec::new();
327327
last_hops!(last_hops);
328328
find_routes!(first_hops, node_pks.iter(), |final_amt, final_delta, target: &PublicKey| {
329-
RouteParameters {
330-
payment_params: PaymentParameters::from_node_id(*target, final_delta)
329+
RouteParameters::from_payment_params_and_value(
330+
PaymentParameters::from_node_id(*target, final_delta)
331331
.with_route_hints(last_hops.clone()).unwrap(),
332-
final_value_msat: final_amt,
333-
}
332+
final_amt)
334333
});
335334
},
336335
x => {
@@ -366,11 +365,9 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
366365
let mut features = Bolt12InvoiceFeatures::empty();
367366
features.set_basic_mpp_optional();
368367
find_routes!(first_hops, vec![dummy_pk].iter(), |final_amt, _, _| {
369-
RouteParameters {
370-
payment_params: PaymentParameters::blinded(last_hops.clone())
371-
.with_bolt12_features(features.clone()).unwrap(),
372-
final_value_msat: final_amt,
373-
}
368+
RouteParameters::from_payment_params_and_value(PaymentParameters::blinded(last_hops.clone())
369+
.with_bolt12_features(features.clone()).unwrap(),
370+
final_amt)
374371
});
375372
}
376373
}

lightning-invoice/src/utils.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -869,10 +869,8 @@ mod test {
869869
invoice.min_final_cltv_expiry_delta() as u32)
870870
.with_bolt11_features(invoice.features().unwrap().clone()).unwrap()
871871
.with_route_hints(invoice.route_hints()).unwrap();
872-
let route_params = RouteParameters {
873-
payment_params,
874-
final_value_msat: invoice.amount_milli_satoshis().unwrap(),
875-
};
872+
let route_params = RouteParameters::from_payment_params_and_value(
873+
payment_params, invoice.amount_milli_satoshis().unwrap());
876874
let payment_event = {
877875
let mut payment_hash = PaymentHash([0; 32]);
878876
payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
@@ -1326,10 +1324,8 @@ mod test {
13261324
invoice.min_final_cltv_expiry_delta() as u32)
13271325
.with_bolt11_features(invoice.features().unwrap().clone()).unwrap()
13281326
.with_route_hints(invoice.route_hints()).unwrap();
1329-
let params = RouteParameters {
1330-
payment_params,
1331-
final_value_msat: invoice.amount_milli_satoshis().unwrap(),
1332-
};
1327+
let params = RouteParameters::from_payment_params_and_value(
1328+
payment_params, invoice.amount_milli_satoshis().unwrap());
13331329
let (payment_event, fwd_idx) = {
13341330
let mut payment_hash = PaymentHash([0; 32]);
13351331
payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);

lightning/src/ln/channelmanager.rs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9655,10 +9655,9 @@ mod tests {
96559655
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &expected_route, 100_000);
96569656

96579657
// Next, attempt a keysend payment and make sure it fails.
9658-
let route_params = RouteParameters {
9659-
payment_params: PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV, false),
9660-
final_value_msat: 100_000,
9661-
};
9658+
let route_params = RouteParameters::from_payment_params_and_value(
9659+
PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(),
9660+
TEST_FINAL_CLTV, false), 100_000);
96629661
let route = find_route(
96639662
&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph,
96649663
None, nodes[0].logger, &scorer, &(), &random_seed_bytes
@@ -9746,10 +9745,10 @@ mod tests {
97469745
pass_along_path(&nodes[0], &path, 100_000, payment_hash, None, event, true, Some(payment_preimage));
97479746

97489747
// Next, attempt a keysend payment and make sure it fails.
9749-
let route_params = RouteParameters {
9750-
payment_params: PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV, false),
9751-
final_value_msat: 100_000,
9752-
};
9748+
let route_params = RouteParameters::from_payment_params_and_value(
9749+
PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV, false),
9750+
100_000
9751+
);
97539752
let route = find_route(
97549753
&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph,
97559754
None, nodes[0].logger, &scorer, &(), &random_seed_bytes
@@ -9795,10 +9794,8 @@ mod tests {
97959794
let payee_pubkey = nodes[1].node.get_our_node_id();
97969795

97979796
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1]);
9798-
let route_params = RouteParameters {
9799-
payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false),
9800-
final_value_msat: 10_000,
9801-
};
9797+
let route_params = RouteParameters::from_payment_params_and_value(
9798+
PaymentParameters::for_keysend(payee_pubkey, 40, false), 10_000);
98029799
let network_graph = nodes[0].network_graph.clone();
98039800
let first_hops = nodes[0].node.list_usable_channels();
98049801
let scorer = test_utils::TestScorer::new();
@@ -9842,10 +9839,8 @@ mod tests {
98429839
let payee_pubkey = nodes[1].node.get_our_node_id();
98439840

98449841
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1]);
9845-
let route_params = RouteParameters {
9846-
payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false),
9847-
final_value_msat: 10_000,
9848-
};
9842+
let route_params = RouteParameters::from_payment_params_and_value(
9843+
PaymentParameters::for_keysend(payee_pubkey, 40, false), 10_000);
98499844
let network_graph = nodes[0].network_graph.clone();
98509845
let first_hops = nodes[0].node.list_usable_channels();
98519846
let scorer = test_utils::TestScorer::new();
@@ -10742,9 +10737,9 @@ pub mod bench {
1074210737
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None).unwrap();
1074310738

1074410739
$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret),
10745-
PaymentId(payment_hash.0), RouteParameters {
10746-
payment_params, final_value_msat: 10_000,
10747-
}, Retry::Attempts(0)).unwrap();
10740+
PaymentId(payment_hash.0),
10741+
RouteParameters::from_payment_params_and_value(payment_params, 10_000),
10742+
Retry::Attempts(0)).unwrap();
1074810743
let payment_event = SendEvent::from_event($node_a.get_and_clear_pending_msg_events().pop().unwrap());
1074910744
$node_b.handle_update_add_htlc(&$node_a.get_our_node_id(), &payment_event.msgs[0]);
1075010745
$node_b.handle_commitment_signed(&$node_a.get_our_node_id(), &payment_event.commitment_msg);

lightning/src/ln/functional_test_utils.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::events::bump_transaction::{BumpTransactionEventHandler, Wallet, Walle
1919
use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
2020
use crate::ln::channelmanager::{self, AChannelManager, ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, PaymentId, MIN_CLTV_EXPIRY_DELTA};
2121
use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
22-
use crate::routing::router::{self, PaymentParameters, Route};
22+
use crate::routing::router::{self, PaymentParameters, Route, RouteParameters};
2323
use crate::ln::features::InitFeatures;
2424
use crate::ln::msgs;
2525
use crate::ln::msgs::{ChannelMessageHandler,RoutingMessageHandler};
@@ -1864,14 +1864,14 @@ macro_rules! get_payment_preimage_hash {
18641864
}
18651865

18661866
/// Gets a route from the given sender to the node described in `payment_params`.
1867-
pub fn get_route(send_node: &Node, payment_params: &PaymentParameters, recv_value: u64) -> Result<Route, msgs::LightningError> {
1867+
pub fn get_route(send_node: &Node, route_params: &RouteParameters) -> Result<Route, msgs::LightningError> {
18681868
let scorer = TestScorer::new();
18691869
let keys_manager = TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet);
18701870
let random_seed_bytes = keys_manager.get_secure_random_bytes();
18711871
router::get_route(
1872-
&send_node.node.get_our_node_id(), payment_params, &send_node.network_graph.read_only(),
1872+
&send_node.node.get_our_node_id(), route_params, &send_node.network_graph.read_only(),
18731873
Some(&send_node.node.list_usable_channels().iter().collect::<Vec<_>>()),
1874-
recv_value, send_node.logger, &scorer, &(), &random_seed_bytes
1874+
send_node.logger, &scorer, &(), &random_seed_bytes
18751875
)
18761876
}
18771877

@@ -1880,9 +1880,10 @@ pub fn get_route(send_node: &Node, payment_params: &PaymentParameters, recv_valu
18801880
/// Don't use this, use the identically-named function instead.
18811881
#[macro_export]
18821882
macro_rules! get_route {
1883-
($send_node: expr, $payment_params: expr, $recv_value: expr) => {
1884-
$crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value)
1885-
}
1883+
($send_node: expr, $payment_params: expr, $recv_value: expr) => {{
1884+
let route_params = $crate::routing::router::RouteParameters::from_payment_params_and_value($payment_params, $recv_value);
1885+
$crate::ln::functional_test_utils::get_route(&$send_node, &route_params)
1886+
}}
18861887
}
18871888

18881889
#[cfg(test)]
@@ -1894,9 +1895,10 @@ macro_rules! get_route_and_payment_hash {
18941895
$crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value)
18951896
}};
18961897
($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr) => {{
1898+
let route_params = $crate::routing::router::RouteParameters::from_payment_params_and_value($payment_params, $recv_value);
18971899
let (payment_preimage, payment_hash, payment_secret) =
18981900
$crate::ln::functional_test_utils::get_payment_preimage_hash(&$recv_node, Some($recv_value), None);
1899-
let route = $crate::ln::functional_test_utils::get_route(&$send_node, &$payment_params, $recv_value);
1901+
let route = $crate::ln::functional_test_utils::get_route(&$send_node, &route_params);
19001902
(route.unwrap(), payment_hash, payment_preimage, payment_secret)
19011903
}}
19021904
}
@@ -2490,7 +2492,8 @@ pub const TEST_FINAL_CLTV: u32 = 70;
24902492
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) {
24912493
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
24922494
.with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
2493-
let route = get_route(origin_node, &payment_params, recv_value).unwrap();
2495+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, recv_value);
2496+
let route = get_route(origin_node, &route_params).unwrap();
24942497
assert_eq!(route.paths.len(), 1);
24952498
assert_eq!(route.paths[0].hops.len(), expected_route.len());
24962499
for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) {
@@ -2504,14 +2507,14 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route:
25042507
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
25052508
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
25062509
.with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
2510+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, recv_value);
25072511
let network_graph = origin_node.network_graph.read_only();
25082512
let scorer = test_utils::TestScorer::new();
25092513
let seed = [0u8; 32];
25102514
let keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet);
25112515
let random_seed_bytes = keys_manager.get_secure_random_bytes();
2512-
let route = router::get_route(
2513-
&origin_node.node.get_our_node_id(), &payment_params, &network_graph,
2514-
None, recv_value, origin_node.logger, &scorer, &(), &random_seed_bytes).unwrap();
2516+
let route = router::get_route(&origin_node.node.get_our_node_id(), &route_params, &network_graph,
2517+
None, origin_node.logger, &scorer, &(), &random_seed_bytes).unwrap();
25152518
assert_eq!(route.paths.len(), 1);
25162519
assert_eq!(route.paths[0].hops.len(), expected_route.len());
25172520
for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) {

lightning/src/ln/functional_tests.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::ln::channel::{DISCONNECT_PEER_AWAITING_RESPONSE_TICKS, ChannelError};
2626
use crate::ln::{chan_utils, onion_utils};
2727
use crate::ln::chan_utils::{OFFERED_HTLC_SCRIPT_WEIGHT, htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment};
2828
use crate::routing::gossip::{NetworkGraph, NetworkUpdate};
29-
use crate::routing::router::{Path, PaymentParameters, Route, RouteHop, get_route};
29+
use crate::routing::router::{Path, PaymentParameters, Route, RouteHop, get_route, RouteParameters};
3030
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
3131
use crate::ln::msgs;
3232
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
@@ -1049,7 +1049,9 @@ fn fake_network_test() {
10491049
});
10501050
hops[1].fee_msat = chan_4.1.contents.fee_base_msat as u64 + chan_4.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
10511051
hops[0].fee_msat = chan_3.0.contents.fee_base_msat as u64 + chan_3.0.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
1052-
let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![Path { hops, blinded_tail: None }], payment_params: None }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
1052+
let payment_preimage_1 = send_along_route(&nodes[1],
1053+
Route { paths: vec![Path { hops, blinded_tail: None }], route_params: None },
1054+
&vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
10531055

10541056
let mut hops = Vec::with_capacity(3);
10551057
hops.push(RouteHop {
@@ -1078,7 +1080,9 @@ fn fake_network_test() {
10781080
});
10791081
hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
10801082
hops[0].fee_msat = chan_3.1.contents.fee_base_msat as u64 + chan_3.1.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
1081-
let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![Path { hops, blinded_tail: None }], payment_params: None }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
1083+
let payment_hash_2 = send_along_route(&nodes[1],
1084+
Route { paths: vec![Path { hops, blinded_tail: None }], route_params: None },
1085+
&vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
10821086

10831087
// Claim the rebalances...
10841088
fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2);
@@ -7137,8 +7141,11 @@ fn test_check_htlc_underpaying() {
71377141

71387142
let scorer = test_utils::TestScorer::new();
71397143
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
7140-
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
7141-
let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None, 10_000, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
7144+
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(),
7145+
TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
7146+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 10_000);
7147+
let route = get_route(&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph.read_only(),
7148+
None, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
71427149
let (_, our_payment_hash, _) = get_payment_preimage_hash!(nodes[0]);
71437150
let our_payment_secret = nodes[1].node.create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, None).unwrap();
71447151
nodes[0].node.send_payment_with_route(&route, our_payment_hash,
@@ -7394,12 +7401,14 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
73947401
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
73957402
let scorer = test_utils::TestScorer::new();
73967403
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
7397-
let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None,
7398-
3_000_000, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
7404+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 3_000_000);
7405+
let route = get_route(&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph.read_only(), None,
7406+
nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
73997407
let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
74007408
let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
7401-
let route = get_route(&nodes[1].node.get_our_node_id(), &payment_params, &nodes[1].network_graph.read_only(), None,
7402-
3_000_000, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
7409+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 3_000_000);
7410+
let route = get_route(&nodes[1].node.get_our_node_id(), &route_params, &nodes[1].network_graph.read_only(), None,
7411+
nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();
74037412
send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000);
74047413

74057414
let revoked_local_txn = get_local_commitment_txn!(nodes[1], chan.2);

0 commit comments

Comments
 (0)