Skip to content

Commit 2c57878

Browse files
authored
Merge pull request #1856 from tnull/2022-10-expose-channel-id
Expose the channel via which we received a payment
2 parents 64b9e83 + b1b3666 commit 2c57878

File tree

10 files changed

+107
-31
lines changed

10 files changed

+107
-31
lines changed

fuzz/src/router.rs

+1
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
225225
user_channel_id: 0, inbound_capacity_msat: 0,
226226
unspendable_punishment_reserve: None,
227227
confirmations_required: None,
228+
confirmations: None,
228229
force_close_spend_delay: None,
229230
is_outbound: true, is_channel_ready: true,
230231
is_usable: true, is_public: true,

lightning/src/ln/chanmon_update_fail_tests.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,11 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) {
200200
let events_3 = nodes[1].node.get_and_clear_pending_events();
201201
assert_eq!(events_3.len(), 1);
202202
match events_3[0] {
203-
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => {
203+
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
204204
assert_eq!(payment_hash_1, *payment_hash);
205205
assert_eq!(amount_msat, 1_000_000);
206206
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
207+
assert_eq!(via_channel_id, Some(channel_id));
207208
match &purpose {
208209
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
209210
assert!(payment_preimage.is_none());
@@ -568,10 +569,11 @@ fn do_test_monitor_temporary_update_fail(disconnect_count: usize) {
568569
let events_5 = nodes[1].node.get_and_clear_pending_events();
569570
assert_eq!(events_5.len(), 1);
570571
match events_5[0] {
571-
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => {
572+
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
572573
assert_eq!(payment_hash_2, *payment_hash);
573574
assert_eq!(amount_msat, 1_000_000);
574575
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
576+
assert_eq!(via_channel_id, Some(channel_id));
575577
match &purpose {
576578
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
577579
assert!(payment_preimage.is_none());
@@ -684,10 +686,11 @@ fn test_monitor_update_fail_cs() {
684686
let events = nodes[1].node.get_and_clear_pending_events();
685687
assert_eq!(events.len(), 1);
686688
match events[0] {
687-
Event::PaymentReceived { payment_hash, ref purpose, amount_msat, receiver_node_id } => {
689+
Event::PaymentReceived { payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
688690
assert_eq!(payment_hash, our_payment_hash);
689691
assert_eq!(amount_msat, 1_000_000);
690692
assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
693+
assert_eq!(via_channel_id, Some(channel_id));
691694
match &purpose {
692695
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
693696
assert!(payment_preimage.is_none());
@@ -1627,7 +1630,8 @@ fn test_monitor_update_fail_claim() {
16271630
commitment_signed_dance!(nodes[1], nodes[2], payment_event.commitment_msg, false, true);
16281631

16291632
// Now restore monitor updating on the 0<->1 channel and claim the funds on B.
1630-
let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_1.2).unwrap().clone();
1633+
let channel_id = chan_1.2;
1634+
let (outpoint, latest_update, _) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
16311635
nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
16321636
check_added_monitors!(nodes[1], 0);
16331637

@@ -1648,10 +1652,12 @@ fn test_monitor_update_fail_claim() {
16481652
let events = nodes[0].node.get_and_clear_pending_events();
16491653
assert_eq!(events.len(), 2);
16501654
match events[0] {
1651-
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => {
1655+
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id } => {
16521656
assert_eq!(payment_hash_2, *payment_hash);
16531657
assert_eq!(1_000_000, amount_msat);
16541658
assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id());
1659+
assert_eq!(via_channel_id, Some(channel_id));
1660+
assert_eq!(via_user_channel_id, Some(42));
16551661
match &purpose {
16561662
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
16571663
assert!(payment_preimage.is_none());
@@ -1663,10 +1669,11 @@ fn test_monitor_update_fail_claim() {
16631669
_ => panic!("Unexpected event"),
16641670
}
16651671
match events[1] {
1666-
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => {
1672+
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id, via_user_channel_id: _ } => {
16671673
assert_eq!(payment_hash_3, *payment_hash);
16681674
assert_eq!(1_000_000, amount_msat);
16691675
assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id());
1676+
assert_eq!(via_channel_id, Some(channel_id));
16701677
match &purpose {
16711678
PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
16721679
assert!(payment_preimage.is_none());

lightning/src/ln/channel.rs

+10
Original file line numberDiff line numberDiff line change
@@ -4495,6 +4495,16 @@ impl<Signer: Sign> Channel<Signer> {
44954495
self.funding_tx_confirmed_in
44964496
}
44974497

4498+
/// Returns the current number of confirmations on the funding transaction.
4499+
pub fn get_funding_tx_confirmations(&self, height: u32) -> u32 {
4500+
if self.funding_tx_confirmation_height == 0 {
4501+
// We either haven't seen any confirmation yet, or observed a reorg.
4502+
return 0;
4503+
}
4504+
4505+
height.checked_sub(self.funding_tx_confirmation_height).map_or(0, |c| c + 1)
4506+
}
4507+
44984508
fn get_holder_selected_contest_delay(&self) -> u16 {
44994509
self.channel_transaction_parameters.holder_selected_contest_delay
45004510
}

lightning/src/ln/channelmanager.rs

+33-15
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ pub(super) struct PendingAddHTLCInfo {
142142
prev_short_channel_id: u64,
143143
prev_htlc_id: u64,
144144
prev_funding_outpoint: OutPoint,
145+
prev_user_channel_id: u128,
145146
}
146147

147148
pub(super) enum HTLCForwardInfo {
@@ -1145,6 +1146,10 @@ pub struct ChannelDetails {
11451146
/// [`ChannelHandshakeConfig::minimum_depth`]: crate::util::config::ChannelHandshakeConfig::minimum_depth
11461147
/// [`ChannelHandshakeLimits::max_minimum_depth`]: crate::util::config::ChannelHandshakeLimits::max_minimum_depth
11471148
pub confirmations_required: Option<u32>,
1149+
/// The current number of confirmations on the funding transaction.
1150+
///
1151+
/// This value will be `None` for objects serialized with LDK versions prior to 0.0.113.
1152+
pub confirmations: Option<u32>,
11481153
/// The number of blocks (after our commitment transaction confirms) that we will need to wait
11491154
/// until we can claim our funds after we force-close the channel. During this time our
11501155
/// counterparty is allowed to punish us if we broadcasted a stale state. If our counterparty
@@ -1693,6 +1698,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
16931698
let mut res = Vec::new();
16941699
{
16951700
let channel_state = self.channel_state.lock().unwrap();
1701+
let best_block_height = self.best_block.read().unwrap().height();
16961702
res.reserve(channel_state.by_id.len());
16971703
for (channel_id, channel) in channel_state.by_id.iter().filter(f) {
16981704
let balance = channel.get_available_balances();
@@ -1729,6 +1735,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
17291735
next_outbound_htlc_limit_msat: balance.next_outbound_htlc_limit_msat,
17301736
user_channel_id: channel.get_user_id(),
17311737
confirmations_required: channel.minimum_depth(),
1738+
confirmations: Some(channel.get_funding_tx_confirmations(best_block_height)),
17321739
force_close_spend_delay: channel.get_counterparty_selected_contest_delay(),
17331740
is_outbound: channel.is_outbound(),
17341741
is_channel_ready: channel.is_usable(),
@@ -3025,7 +3032,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
30253032

30263033
let mut new_events = Vec::new();
30273034
let mut failed_forwards = Vec::new();
3028-
let mut phantom_receives: Vec<(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
3035+
let mut phantom_receives: Vec<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
30293036
let mut handle_errors = Vec::new();
30303037
{
30313038
let mut forward_htlcs = HashMap::new();
@@ -3038,7 +3045,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
30383045
for forward_info in pending_forwards.drain(..) {
30393046
match forward_info {
30403047
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
3041-
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint,
3048+
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
30423049
forward_info: PendingHTLCInfo {
30433050
routing, incoming_shared_secret, payment_hash, outgoing_amt_msat,
30443051
outgoing_cltv_value, incoming_amt_msat: _
@@ -3104,7 +3111,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
31043111
match next_hop {
31053112
onion_utils::Hop::Receive(hop_data) => {
31063113
match self.construct_recv_pending_htlc_info(hop_data, incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value, Some(phantom_shared_secret)) {
3107-
Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, vec![(info, prev_htlc_id)])),
3114+
Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![(info, prev_htlc_id)])),
31083115
Err(ReceiveError { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret))
31093116
}
31103117
},
@@ -3147,7 +3154,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
31473154
for forward_info in pending_forwards.drain(..) {
31483155
match forward_info {
31493156
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
3150-
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint ,
3157+
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id: _,
31513158
forward_info: PendingHTLCInfo {
31523159
incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value,
31533160
routing: PendingHTLCRouting::Forward { onion_packet, .. }, incoming_amt_msat: _,
@@ -3274,7 +3281,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
32743281
for forward_info in pending_forwards.drain(..) {
32753282
match forward_info {
32763283
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
3277-
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint,
3284+
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
32783285
forward_info: PendingHTLCInfo {
32793286
routing, incoming_shared_secret, payment_hash, outgoing_amt_msat, ..
32803287
}
@@ -3369,12 +3376,15 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
33693376
log_bytes!(payment_hash.0), total_value, $payment_data.total_msat);
33703377
fail_htlc!(claimable_htlc, payment_hash);
33713378
} else if total_value == $payment_data.total_msat {
3379+
let prev_channel_id = prev_funding_outpoint.to_channel_id();
33723380
htlcs.push(claimable_htlc);
33733381
new_events.push(events::Event::PaymentReceived {
33743382
receiver_node_id: Some(receiver_node_id),
33753383
payment_hash,
33763384
purpose: purpose(),
33773385
amount_msat: total_value,
3386+
via_channel_id: Some(prev_channel_id),
3387+
via_user_channel_id: Some(prev_user_channel_id),
33783388
});
33793389
payment_received_generated = true;
33803390
} else {
@@ -3413,11 +3423,14 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
34133423
hash_map::Entry::Vacant(e) => {
34143424
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
34153425
e.insert((purpose.clone(), vec![claimable_htlc]));
3426+
let prev_channel_id = prev_funding_outpoint.to_channel_id();
34163427
new_events.push(events::Event::PaymentReceived {
34173428
receiver_node_id: Some(receiver_node_id),
34183429
payment_hash,
34193430
amount_msat: outgoing_amt_msat,
34203431
purpose,
3432+
via_channel_id: Some(prev_channel_id),
3433+
via_user_channel_id: Some(prev_user_channel_id),
34213434
});
34223435
},
34233436
hash_map::Entry::Occupied(_) => {
@@ -4389,13 +4402,13 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
43894402
commitment_update: Option<msgs::CommitmentUpdate>, order: RAACommitmentOrder,
43904403
pending_forwards: Vec<(PendingHTLCInfo, u64)>, funding_broadcastable: Option<Transaction>,
43914404
channel_ready: Option<msgs::ChannelReady>, announcement_sigs: Option<msgs::AnnouncementSignatures>)
4392-
-> Option<(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)> {
4405+
-> Option<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> {
43934406
let mut htlc_forwards = None;
43944407

43954408
let counterparty_node_id = channel.get_counterparty_node_id();
43964409
if !pending_forwards.is_empty() {
43974410
htlc_forwards = Some((channel.get_short_channel_id().unwrap_or(channel.outbound_scid_alias()),
4398-
channel.get_funding_txo().unwrap(), pending_forwards));
4411+
channel.get_funding_txo().unwrap(), channel.get_user_id(), pending_forwards));
43994412
}
44004413

44014414
if let Some(msg) = channel_ready {
@@ -5056,8 +5069,8 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
50565069
}
50575070

50585071
#[inline]
5059-
fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)]) {
5060-
for &mut (prev_short_channel_id, prev_funding_outpoint, ref mut pending_forwards) in per_source_pending_forwards {
5072+
fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
5073+
for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
50615074
let mut forward_event = None;
50625075
if !pending_forwards.is_empty() {
50635076
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
@@ -5072,11 +5085,11 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
50725085
}) {
50735086
hash_map::Entry::Occupied(mut entry) => {
50745087
entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
5075-
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info }));
5088+
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }));
50765089
},
50775090
hash_map::Entry::Vacant(entry) => {
50785091
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
5079-
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info })));
5092+
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
50805093
}
50815094
}
50825095
}
@@ -5135,21 +5148,22 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
51355148
raa_updates.finalized_claimed_htlcs,
51365149
chan.get().get_short_channel_id()
51375150
.unwrap_or(chan.get().outbound_scid_alias()),
5138-
chan.get().get_funding_txo().unwrap()))
5151+
chan.get().get_funding_txo().unwrap(),
5152+
chan.get().get_user_id()))
51395153
},
51405154
hash_map::Entry::Vacant(_) => break Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id))
51415155
}
51425156
};
51435157
self.fail_holding_cell_htlcs(htlcs_to_fail, msg.channel_id, counterparty_node_id);
51445158
match res {
51455159
Ok((pending_forwards, mut pending_failures, finalized_claim_htlcs,
5146-
short_channel_id, channel_outpoint)) =>
5160+
short_channel_id, channel_outpoint, user_channel_id)) =>
51475161
{
51485162
for failure in pending_failures.drain(..) {
51495163
let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: channel_outpoint.to_channel_id() };
51505164
self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver);
51515165
}
5152-
self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, pending_forwards)]);
5166+
self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, user_channel_id, pending_forwards)]);
51535167
self.finalize_claims(finalized_claim_htlcs);
51545168
Ok(())
51555169
},
@@ -6127,7 +6141,7 @@ where
61276141
}
61286142
}
61296143

6130-
impl<M: Deref , T: Deref , K: Deref , F: Deref , L: Deref >
6144+
impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref >
61316145
ChannelMessageHandler for ChannelManager<M, T, K, F, L>
61326146
where M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
61336147
T::Target: BroadcasterInterface,
@@ -6459,6 +6473,7 @@ impl Writeable for ChannelDetails {
64596473
(6, self.funding_txo, option),
64606474
(7, self.config, option),
64616475
(8, self.short_channel_id, option),
6476+
(9, self.confirmations, option),
64626477
(10, self.channel_value_satoshis, required),
64636478
(12, self.unspendable_punishment_reserve, option),
64646479
(14, user_channel_id_low, required),
@@ -6493,6 +6508,7 @@ impl Readable for ChannelDetails {
64936508
(6, funding_txo, option),
64946509
(7, config, option),
64956510
(8, short_channel_id, option),
6511+
(9, confirmations, option),
64966512
(10, channel_value_satoshis, required),
64976513
(12, unspendable_punishment_reserve, option),
64986514
(14, user_channel_id_low, required),
@@ -6536,6 +6552,7 @@ impl Readable for ChannelDetails {
65366552
next_outbound_htlc_limit_msat: next_outbound_htlc_limit_msat.0.unwrap(),
65376553
inbound_capacity_msat: inbound_capacity_msat.0.unwrap(),
65386554
confirmations_required,
6555+
confirmations,
65396556
force_close_spend_delay,
65406557
is_outbound: is_outbound.0.unwrap(),
65416558
is_channel_ready: is_channel_ready.0.unwrap(),
@@ -6799,6 +6816,7 @@ impl_writeable_tlv_based_enum!(HTLCFailReason,
67996816

68006817
impl_writeable_tlv_based!(PendingAddHTLCInfo, {
68016818
(0, forward_info, required),
6819+
(1, prev_user_channel_id, (default_value, 0)),
68026820
(2, prev_short_channel_id, required),
68036821
(4, prev_htlc_id, required),
68046822
(6, prev_funding_outpoint, required),

lightning/src/ln/functional_test_utils.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1479,7 +1479,7 @@ macro_rules! expect_payment_received {
14791479
let events = $node.node.get_and_clear_pending_events();
14801480
assert_eq!(events.len(), 1);
14811481
match events[0] {
1482-
$crate::util::events::Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => {
1482+
$crate::util::events::Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, via_channel_id: _, via_user_channel_id: _ } => {
14831483
assert_eq!($expected_payment_hash, *payment_hash);
14841484
assert_eq!($expected_recv_value, amount_msat);
14851485
assert_eq!($expected_receiver_node_id, receiver_node_id.unwrap());
@@ -1774,7 +1774,7 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p
17741774
if payment_received_expected {
17751775
assert_eq!(events_2.len(), 1);
17761776
match events_2[0] {
1777-
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id } => {
1777+
Event::PaymentReceived { ref payment_hash, ref purpose, amount_msat, receiver_node_id, ref via_channel_id, ref via_user_channel_id } => {
17781778
assert_eq!(our_payment_hash, *payment_hash);
17791779
assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap());
17801780
match &purpose {
@@ -1788,6 +1788,8 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p
17881788
},
17891789
}
17901790
assert_eq!(amount_msat, recv_value);
1791+
assert!(node.node.list_channels().iter().any(|details| details.channel_id == via_channel_id.unwrap()));
1792+
assert!(node.node.list_channels().iter().any(|details| details.user_channel_id == via_user_channel_id.unwrap()));
17911793
},
17921794
_ => panic!("Unexpected event"),
17931795
}

0 commit comments

Comments
 (0)