Skip to content

Commit 2007c48

Browse files
Parameterize ChannelManager by a Router trait
This will be used in upcoming work to fetch routes on-the-fly for trampoline payments.
1 parent edd9358 commit 2007c48

File tree

15 files changed

+209
-129
lines changed

15 files changed

+209
-129
lines changed

lightning-background-processor/src/lib.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use lightning::chain;
1818
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
1919
use lightning::chain::chainmonitor::{ChainMonitor, Persist};
2020
use lightning::chain::keysinterface::{Sign, KeysInterface};
21-
use lightning::ln::channelmanager::ChannelManager;
21+
use lightning::ln::channelmanager::{ChannelManager, Router};
2222
use lightning::ln::msgs::{ChannelMessageHandler, OnionMessageHandler, RoutingMessageHandler};
2323
use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor};
2424
use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
@@ -478,6 +478,7 @@ impl BackgroundProcessor {
478478
T: 'static + Deref + Send + Sync,
479479
K: 'static + Deref + Send + Sync,
480480
F: 'static + Deref + Send + Sync,
481+
R: 'static + Deref + Send + Sync,
481482
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
482483
L: 'static + Deref + Send + Sync,
483484
P: 'static + Deref + Send + Sync,
@@ -488,7 +489,7 @@ impl BackgroundProcessor {
488489
EH: 'static + EventHandler + Send,
489490
PS: 'static + Deref + Send,
490491
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
491-
CM: 'static + Deref<Target = ChannelManager<CW, T, K, F, L>> + Send + Sync,
492+
CM: 'static + Deref<Target = ChannelManager<CW, T, K, F, R, L>> + Send + Sync,
492493
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
493494
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
494495
UMH: 'static + Deref + Send + Sync,
@@ -506,13 +507,14 @@ impl BackgroundProcessor {
506507
T::Target: 'static + BroadcasterInterface,
507508
K::Target: 'static + KeysInterface<Signer = Signer>,
508509
F::Target: 'static + FeeEstimator,
510+
R::Target: 'static + Router,
509511
L::Target: 'static + Logger,
510512
P::Target: 'static + Persist<Signer>,
511513
CMH::Target: 'static + ChannelMessageHandler,
512514
OMH::Target: 'static + OnionMessageHandler,
513515
RMH::Target: 'static + RoutingMessageHandler,
514516
UMH::Target: 'static + CustomMessageHandler,
515-
PS::Target: 'static + Persister<'a, Signer, CW, T, K, F, L, SC>,
517+
PS::Target: 'static + Persister<'a, Signer, CW, T, K, F, R, L, SC>,
516518
{
517519
let stop_thread = Arc::new(AtomicBool::new(false));
518520
let stop_thread_clone = stop_thread.clone();
@@ -625,7 +627,7 @@ mod tests {
625627
type RGS = Arc<RapidGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>>>;
626628

627629
struct Node {
628-
node: Arc<SimpleArcChannelManager<ChainMonitor, test_utils::TestBroadcaster, test_utils::TestFeeEstimator, test_utils::TestLogger>>,
630+
node: Arc<SimpleArcChannelManager<ChainMonitor, test_utils::TestBroadcaster, test_utils::TestFeeEstimator, test_utils::TestRouter, test_utils::TestLogger>>,
629631
p2p_gossip_sync: PGS,
630632
rapid_gossip_sync: RGS,
631633
peer_manager: Arc<PeerManager<TestDescriptor, Arc<test_utils::TestChannelMessageHandler>, Arc<test_utils::TestRoutingMessageHandler>, IgnoringMessageHandler, Arc<test_utils::TestLogger>, IgnoringMessageHandler>>,
@@ -732,6 +734,7 @@ mod tests {
732734
for i in 0..num_nodes {
733735
let tx_broadcaster = Arc::new(test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new()), blocks: Arc::new(Mutex::new(Vec::new()))});
734736
let fee_estimator = Arc::new(test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) });
737+
let router = Arc::new(test_utils::TestRouter { });
735738
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
736739
let logger = Arc::new(test_utils::TestLogger::with_id(format!("node {}", i)));
737740
let persister = Arc::new(FilesystemPersister::new(format!("{}_persister_{}", persist_dir, i)));
@@ -743,7 +746,7 @@ mod tests {
743746
let chain_monitor = Arc::new(chainmonitor::ChainMonitor::new(Some(chain_source.clone()), tx_broadcaster.clone(), logger.clone(), fee_estimator.clone(), persister.clone()));
744747
let best_block = BestBlock::from_genesis(network);
745748
let params = ChainParameters { network, best_block };
746-
let manager = Arc::new(ChannelManager::new(fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster.clone(), logger.clone(), keys_manager.clone(), UserConfig::default(), params));
749+
let manager = Arc::new(ChannelManager::new(fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster.clone(), router.clone(), logger.clone(), keys_manager.clone(), UserConfig::default(), params));
747750
let network_graph = Arc::new(NetworkGraph::new(genesis_block.header.block_hash(), logger.clone()));
748751
let p2p_gossip_sync = Arc::new(P2PGossipSync::new(network_graph.clone(), Some(chain_source.clone()), logger.clone()));
749752
let rapid_gossip_sync = Arc::new(RapidGossipSync::new(network_graph.clone()));

lightning-block-sync/src/init.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ BlockSourceResult<ValidatedBlockHeader> where B::Target: BlockSource {
4949
/// use lightning::chain::chaininterface::FeeEstimator;
5050
/// use lightning::chain::keysinterface;
5151
/// use lightning::chain::keysinterface::KeysInterface;
52-
/// use lightning::ln::channelmanager::ChannelManager;
53-
/// use lightning::ln::channelmanager::ChannelManagerReadArgs;
52+
/// use lightning::ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, Router};
5453
/// use lightning::util::config::UserConfig;
5554
/// use lightning::util::logger::Logger;
5655
/// use lightning::util::ser::ReadableArgs;
@@ -65,6 +64,7 @@ BlockSourceResult<ValidatedBlockHeader> where B::Target: BlockSource {
6564
/// S: keysinterface::Sign,
6665
/// T: BroadcasterInterface,
6766
/// F: FeeEstimator,
67+
/// R: Router,
6868
/// L: Logger,
6969
/// C: chain::Filter,
7070
/// P: chainmonitor::Persist<S>,
@@ -75,6 +75,7 @@ BlockSourceResult<ValidatedBlockHeader> where B::Target: BlockSource {
7575
/// keys_manager: &K,
7676
/// tx_broadcaster: &T,
7777
/// fee_estimator: &F,
78+
/// router: &R,
7879
/// logger: &L,
7980
/// persister: &P,
8081
/// ) {
@@ -91,11 +92,12 @@ BlockSourceResult<ValidatedBlockHeader> where B::Target: BlockSource {
9192
/// fee_estimator,
9293
/// chain_monitor,
9394
/// tx_broadcaster,
95+
/// router,
9496
/// logger,
9597
/// config,
9698
/// vec![&mut monitor],
9799
/// );
98-
/// <(BlockHash, ChannelManager<&ChainMonitor<S, &C, &T, &F, &L, &P>, &T, &K, &F, &L>)>::read(
100+
/// <(BlockHash, ChannelManager<&ChainMonitor<S, &C, &T, &F, &L, &P>, &T, &K, &F, &R, &L>)>::read(
99101
/// &mut Cursor::new(&serialized_manager), read_args).unwrap()
100102
/// };
101103
///

lightning-invoice/src/utils.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,16 @@ where
235235
///
236236
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
237237
/// in excess of the current time.
238-
pub fn create_invoice_from_channelmanager<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
239-
channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
238+
pub fn create_invoice_from_channelmanager<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
239+
channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
240240
network: Currency, amt_msat: Option<u64>, description: String, invoice_expiry_delta_secs: u32
241241
) -> Result<Invoice, SignOrCreationError<()>>
242242
where
243243
M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
244244
T::Target: BroadcasterInterface,
245245
K::Target: KeysInterface,
246246
F::Target: FeeEstimator,
247+
R::Target: Router,
247248
L::Target: Logger,
248249
{
249250
use std::time::SystemTime;
@@ -265,8 +266,8 @@ where
265266
///
266267
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
267268
/// in excess of the current time.
268-
pub fn create_invoice_from_channelmanager_with_description_hash<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
269-
channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
269+
pub fn create_invoice_from_channelmanager_with_description_hash<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
270+
channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
270271
network: Currency, amt_msat: Option<u64>, description_hash: Sha256,
271272
invoice_expiry_delta_secs: u32
272273
) -> Result<Invoice, SignOrCreationError<()>>
@@ -275,6 +276,7 @@ where
275276
T::Target: BroadcasterInterface,
276277
K::Target: KeysInterface,
277278
F::Target: FeeEstimator,
279+
R::Target: Router,
278280
L::Target: Logger,
279281
{
280282
use std::time::SystemTime;
@@ -292,8 +294,8 @@ where
292294
/// See [`create_invoice_from_channelmanager_with_description_hash`]
293295
/// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
294296
/// available and the current time is supplied by the caller.
295-
pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
296-
channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
297+
pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
298+
channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
297299
network: Currency, amt_msat: Option<u64>, description_hash: Sha256,
298300
duration_since_epoch: Duration, invoice_expiry_delta_secs: u32
299301
) -> Result<Invoice, SignOrCreationError<()>>
@@ -302,6 +304,7 @@ where
302304
T::Target: BroadcasterInterface,
303305
K::Target: KeysInterface,
304306
F::Target: FeeEstimator,
307+
R::Target: Router,
305308
L::Target: Logger,
306309
{
307310
_create_invoice_from_channelmanager_and_duration_since_epoch(
@@ -314,8 +317,8 @@ where
314317
/// See [`create_invoice_from_channelmanager`]
315318
/// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
316319
/// available and the current time is supplied by the caller.
317-
pub fn create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
318-
channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
320+
pub fn create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
321+
channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
319322
network: Currency, amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
320323
invoice_expiry_delta_secs: u32
321324
) -> Result<Invoice, SignOrCreationError<()>>
@@ -324,6 +327,7 @@ where
324327
T::Target: BroadcasterInterface,
325328
K::Target: KeysInterface,
326329
F::Target: FeeEstimator,
330+
R::Target: Router,
327331
L::Target: Logger,
328332
{
329333
_create_invoice_from_channelmanager_and_duration_since_epoch(
@@ -335,8 +339,8 @@ where
335339
)
336340
}
337341

338-
fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
339-
channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
342+
fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
343+
channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
340344
network: Currency, amt_msat: Option<u64>, description: InvoiceDescription,
341345
duration_since_epoch: Duration, invoice_expiry_delta_secs: u32
342346
) -> Result<Invoice, SignOrCreationError<()>>
@@ -345,6 +349,7 @@ where
345349
T::Target: BroadcasterInterface,
346350
K::Target: KeysInterface,
347351
F::Target: FeeEstimator,
352+
R::Target: Router,
348353
L::Target: Logger,
349354
{
350355
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
@@ -590,12 +595,13 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> ProbingRouter for D
590595
}
591596
}
592597

593-
impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Payer for ChannelManager<M, T, K, F, L>
598+
impl<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref> Payer for ChannelManager<M, T, K, F, R, L>
594599
where
595600
M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
596601
T::Target: BroadcasterInterface,
597602
K::Target: KeysInterface,
598603
F::Target: FeeEstimator,
604+
R::Target: Router,
599605
L::Target: Logger,
600606
{
601607
fn node_id(&self) -> PublicKey {

lightning-net-tokio/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@
3131
//! // Define concrete types for our high-level objects:
3232
//! type TxBroadcaster = dyn lightning::chain::chaininterface::BroadcasterInterface + Send + Sync;
3333
//! type FeeEstimator = dyn lightning::chain::chaininterface::FeeEstimator + Send + Sync;
34+
//! type Router = dyn lightning::ln::channelmanager::Router + Send + Sync;
3435
//! type Logger = dyn lightning::util::logger::Logger + Send + Sync;
3536
//! type ChainAccess = dyn lightning::chain::Access + Send + Sync;
3637
//! type ChainFilter = dyn lightning::chain::Filter + Send + Sync;
3738
//! type DataPersister = dyn lightning::chain::chainmonitor::Persist<lightning::chain::keysinterface::InMemorySigner> + Send + Sync;
3839
//! type ChainMonitor = lightning::chain::chainmonitor::ChainMonitor<lightning::chain::keysinterface::InMemorySigner, Arc<ChainFilter>, Arc<TxBroadcaster>, Arc<FeeEstimator>, Arc<Logger>, Arc<DataPersister>>;
39-
//! type ChannelManager = Arc<lightning::ln::channelmanager::SimpleArcChannelManager<ChainMonitor, TxBroadcaster, FeeEstimator, Logger>>;
40-
//! type PeerManager = Arc<lightning::ln::peer_handler::SimpleArcPeerManager<lightning_net_tokio::SocketDescriptor, ChainMonitor, TxBroadcaster, FeeEstimator, ChainAccess, Logger>>;
40+
//! type ChannelManager = Arc<lightning::ln::channelmanager::SimpleArcChannelManager<ChainMonitor, TxBroadcaster, FeeEstimator, Router, Logger>>;
41+
//! type PeerManager = Arc<lightning::ln::peer_handler::SimpleArcPeerManager<lightning_net_tokio::SocketDescriptor, ChainMonitor, TxBroadcaster, FeeEstimator, ChainAccess, Router, Logger>>;
4142
//!
4243
//! // Connect to node with pubkey their_node_id at addr:
4344
//! async fn connect_to_node(peer_manager: PeerManager, chain_monitor: Arc<ChainMonitor>, channel_manager: ChannelManager, their_node_id: PublicKey, addr: SocketAddr) {

lightning/src/ln/chanmon_update_fail_tests.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -2231,7 +2231,7 @@ fn do_channel_holding_cell_serialize(disconnect: bool, reload_a: bool) {
22312231
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
22322232
let persister: test_utils::TestPersister;
22332233
let new_chain_monitor: test_utils::TestChainMonitor;
2234-
let nodes_0_deserialized: ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
2234+
let nodes_0_deserialized: ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>;
22352235
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
22362236

22372237
let chan_id = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 15_000_000, 7_000_000_000, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
@@ -2305,10 +2305,11 @@ fn do_channel_holding_cell_serialize(disconnect: bool, reload_a: bool) {
23052305
nodes_0_deserialized = {
23062306
let mut channel_monitors = HashMap::new();
23072307
channel_monitors.insert(chan_0_monitor.get_funding_txo().0, &mut chan_0_monitor);
2308-
<(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>::read(&mut nodes_0_read, ChannelManagerReadArgs {
2308+
<(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>)>::read(&mut nodes_0_read, ChannelManagerReadArgs {
23092309
default_config: config,
23102310
keys_manager,
23112311
fee_estimator: node_cfgs[0].fee_estimator,
2312+
router: &node_cfgs[0].router,
23122313
chain_monitor: nodes[0].chain_monitor,
23132314
tx_broadcaster: nodes[0].tx_broadcaster.clone(),
23142315
logger: nodes[0].logger,
@@ -2756,7 +2757,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
27562757

27572758
let persister: test_utils::TestPersister;
27582759
let new_chain_monitor: test_utils::TestChainMonitor;
2759-
let nodes_0_deserialized: ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
2760+
let nodes_0_deserialized: ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>;
27602761

27612762
let mut chan_config = test_default_channel_config();
27622763
chan_config.manually_accept_inbound_channels = true;
@@ -2834,10 +2835,11 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
28342835
let mut nodes_0_read = &nodes_0_serialized[..];
28352836
let config = UserConfig::default();
28362837
nodes_0_deserialized = {
2837-
<(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>::read(&mut nodes_0_read, ChannelManagerReadArgs {
2838+
<(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>)>::read(&mut nodes_0_read, ChannelManagerReadArgs {
28382839
default_config: config,
28392840
keys_manager,
28402841
fee_estimator: node_cfgs[0].fee_estimator,
2842+
router: &node_cfgs[0].router,
28412843
chain_monitor: nodes[0].chain_monitor,
28422844
tx_broadcaster: nodes[0].tx_broadcaster.clone(),
28432845
logger: nodes[0].logger,
@@ -2866,7 +2868,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo
28662868

28672869
let persister: test_utils::TestPersister;
28682870
let new_chain_monitor: test_utils::TestChainMonitor;
2869-
let nodes_1_deserialized: ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>;
2871+
let nodes_1_deserialized: ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>;
28702872

28712873
let mut chan_config = test_default_channel_config();
28722874
chan_config.manually_accept_inbound_channels = true;
@@ -2940,10 +2942,11 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo
29402942
let mut nodes_1_read = &nodes_1_serialized[..];
29412943
let config = UserConfig::default();
29422944
nodes_1_deserialized = {
2943-
<(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestLogger>)>::read(&mut nodes_1_read, ChannelManagerReadArgs {
2945+
<(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>)>::read(&mut nodes_1_read, ChannelManagerReadArgs {
29442946
default_config: config,
29452947
keys_manager,
29462948
fee_estimator: node_cfgs[1].fee_estimator,
2949+
router: &node_cfgs[1].router,
29472950
chain_monitor: nodes[1].chain_monitor,
29482951
tx_broadcaster: nodes[1].tx_broadcaster.clone(),
29492952
logger: nodes[1].logger,

0 commit comments

Comments
 (0)