Skip to content

Commit 7473080

Browse files
authored
Merge pull request #147 from TheBlueMatt/2018-09-channelmanager-err-macro
Refactor ChannelManager handle functions into a Channel-closing macro
2 parents cd9d680 + a71abac commit 7473080

File tree

3 files changed

+169
-80
lines changed

3 files changed

+169
-80
lines changed

fuzz/fuzz_targets/channel_target.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ pub fn do_test(data: &[u8]) {
230230
Ok(chan) => chan,
231231
Err(_) => return,
232232
};
233-
chan.get_accept_channel().unwrap();
233+
chan.get_accept_channel();
234234

235235
tx.output.push(TxOut{ value: open_chan.funding_satoshis, script_pubkey: chan.get_funding_redeemscript().to_v0_p2wsh() });
236236
let funding_output = OutPoint::new(Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);

src/ln/channel.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -525,10 +525,10 @@ impl Channel {
525525

526526
let their_announce = if (msg.channel_flags & 1) == 1 { true } else { false };
527527
if require_announce && !their_announce {
528-
return Err(HandleError{err: "Peer tried to open unannounced channel, but we require public ones", action: Some(msgs::ErrorAction::IgnoreError) });
528+
return_error_message!("Peer tried to open unannounced channel, but we require public ones");
529529
}
530530
if !allow_announce && their_announce {
531-
return Err(HandleError{err: "Peer tried to open announced channel, but we require private ones", action: Some(msgs::ErrorAction::IgnoreError) });
531+
return_error_message!("Peer tried to open announced channel, but we require private ones");
532532
}
533533

534534
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
@@ -2200,7 +2200,7 @@ impl Channel {
22002200
})
22012201
}
22022202

2203-
pub fn get_accept_channel(&self) -> Result<msgs::AcceptChannel, HandleError> {
2203+
pub fn get_accept_channel(&self) -> msgs::AcceptChannel {
22042204
if self.channel_outbound {
22052205
panic!("Tried to send accept_channel for an outbound channel?");
22062206
}
@@ -2213,7 +2213,7 @@ impl Channel {
22132213

22142214
let local_commitment_secret = self.build_local_commitment_secret(self.cur_local_commitment_transaction_number);
22152215

2216-
Ok(msgs::AcceptChannel {
2216+
msgs::AcceptChannel {
22172217
temporary_channel_id: self.channel_id,
22182218
dust_limit_satoshis: self.our_dust_limit_satoshis,
22192219
max_htlc_value_in_flight_msat: Channel::get_our_max_htlc_value_in_flight_msat(self.channel_value_satoshis),
@@ -2229,7 +2229,7 @@ impl Channel {
22292229
htlc_basepoint: PublicKey::from_secret_key(&self.secp_ctx, &self.local_keys.htlc_base_key),
22302230
first_per_commitment_point: PublicKey::from_secret_key(&self.secp_ctx, &local_commitment_secret),
22312231
shutdown_scriptpubkey: None,
2232-
})
2232+
}
22332233
}
22342234

22352235
fn get_outbound_funding_created_signature(&mut self) -> Result<(Signature, Transaction), HandleError> {
@@ -2298,13 +2298,13 @@ impl Channel {
22982298
/// https://github.com/lightningnetwork/lightning-rfc/issues/468
22992299
pub fn get_channel_announcement(&self, our_node_id: PublicKey, chain_hash: Sha256dHash) -> Result<(msgs::UnsignedChannelAnnouncement, Signature), HandleError> {
23002300
if !self.announce_publicly {
2301-
return Err(HandleError{err: "Channel is not available for public announcements", action: None});
2301+
return Err(HandleError{err: "Channel is not available for public announcements", action: Some(msgs::ErrorAction::IgnoreError)});
23022302
}
23032303
if self.channel_state & (ChannelState::ChannelFunded as u32) == 0 {
2304-
return Err(HandleError{err: "Cannot get a ChannelAnnouncement until the channel funding has been locked", action: None});
2304+
return Err(HandleError{err: "Cannot get a ChannelAnnouncement until the channel funding has been locked", action: Some(msgs::ErrorAction::IgnoreError)});
23052305
}
23062306
if (self.channel_state & (ChannelState::LocalShutdownSent as u32 | ChannelState::ShutdownComplete as u32)) != 0 {
2307-
return Err(HandleError{err: "Cannot get a ChannelAnnouncement once the channel is closing", action: None});
2307+
return Err(HandleError{err: "Cannot get a ChannelAnnouncement once the channel is closing", action: Some(msgs::ErrorAction::IgnoreError)});
23082308
}
23092309

23102310
let were_node_one = our_node_id.serialize()[..] < self.their_node_id.serialize()[..];

src/ln/channelmanager.rs

Lines changed: 160 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,51 @@ enum PendingOutboundHTLC {
120120
}
121121
}
122122

123+
struct MsgHandleErrInternal {
124+
err: msgs::HandleError,
125+
needs_channel_force_close: bool,
126+
}
127+
impl MsgHandleErrInternal {
128+
#[inline]
129+
fn send_err_msg_no_close(err: &'static str, channel_id: [u8; 32]) -> Self {
130+
Self {
131+
err: HandleError {
132+
err,
133+
action: Some(msgs::ErrorAction::SendErrorMessage {
134+
msg: msgs::ErrorMessage {
135+
channel_id,
136+
data: err.to_string()
137+
},
138+
}),
139+
},
140+
needs_channel_force_close: false,
141+
}
142+
}
143+
#[inline]
144+
fn send_err_msg_close_chan(err: &'static str, channel_id: [u8; 32]) -> Self {
145+
Self {
146+
err: HandleError {
147+
err,
148+
action: Some(msgs::ErrorAction::SendErrorMessage {
149+
msg: msgs::ErrorMessage {
150+
channel_id,
151+
data: err.to_string()
152+
},
153+
}),
154+
},
155+
needs_channel_force_close: true,
156+
}
157+
}
158+
#[inline]
159+
fn from_maybe_close(err: msgs::HandleError) -> Self {
160+
Self { err, needs_channel_force_close: true }
161+
}
162+
#[inline]
163+
fn from_no_close(err: msgs::HandleError) -> Self {
164+
Self { err, needs_channel_force_close: false }
165+
}
166+
}
167+
123168
/// We hold back HTLCs we intend to relay for a random interval in the range (this, 5*this). This
124169
/// provides some limited amount of privacy. Ideally this would range from somewhere like 1 second
125170
/// to 30 seconds, but people expect lightning to be, you know, kinda fast, sadly. We could
@@ -189,10 +234,10 @@ pub struct ChannelManager {
189234
const CLTV_EXPIRY_DELTA: u16 = 6 * 24 * 2; //TODO?
190235

191236
macro_rules! secp_call {
192-
( $res: expr, $err_msg: expr, $action: expr ) => {
237+
( $res: expr, $err: expr ) => {
193238
match $res {
194239
Ok(key) => key,
195-
Err(_) => return Err(HandleError{err: $err_msg, action: Some($action)})
240+
Err(_) => return Err($err),
196241
}
197242
};
198243
}
@@ -914,7 +959,8 @@ impl ChannelManager {
914959

915960
//TODO: This should return something other than HandleError, that's really intended for
916961
//p2p-returns only.
917-
let onion_keys = secp_call!(ChannelManager::construct_onion_keys(&self.secp_ctx, &route, &session_priv), "Pubkey along hop was maliciously selected", msgs::ErrorAction::IgnoreError);
962+
let onion_keys = secp_call!(ChannelManager::construct_onion_keys(&self.secp_ctx, &route, &session_priv),
963+
HandleError{err: "Pubkey along hop was maliciously selected", action: Some(msgs::ErrorAction::IgnoreError)});
918964
let (onion_payloads, htlc_msat, htlc_cltv) = ChannelManager::build_onion_payloads(&route, cur_height)?;
919965
let onion_packet = ChannelManager::construct_onion_packet(onion_payloads, onion_keys, &payment_hash)?;
920966

@@ -1357,6 +1403,83 @@ impl ChannelManager {
13571403
pub fn test_restore_channel_monitor(&self) {
13581404
unimplemented!();
13591405
}
1406+
1407+
fn internal_open_channel(&self, their_node_id: &PublicKey, msg: &msgs::OpenChannel) -> Result<msgs::AcceptChannel, MsgHandleErrInternal> {
1408+
if msg.chain_hash != self.genesis_hash {
1409+
return Err(MsgHandleErrInternal::send_err_msg_no_close("Unknown genesis block hash", msg.temporary_channel_id.clone()));
1410+
}
1411+
let mut channel_state = self.channel_state.lock().unwrap();
1412+
if channel_state.by_id.contains_key(&msg.temporary_channel_id) {
1413+
return Err(MsgHandleErrInternal::send_err_msg_no_close("temporary_channel_id collision!", msg.temporary_channel_id.clone()));
1414+
}
1415+
1416+
let chan_keys = if cfg!(feature = "fuzztarget") {
1417+
ChannelKeys {
1418+
funding_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]).unwrap(),
1419+
revocation_base_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0]).unwrap(),
1420+
payment_base_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0]).unwrap(),
1421+
delayed_payment_base_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0]).unwrap(),
1422+
htlc_base_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0]).unwrap(),
1423+
channel_close_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0]).unwrap(),
1424+
channel_monitor_claim_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0]).unwrap(),
1425+
commitment_seed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1426+
}
1427+
} else {
1428+
let mut key_seed = [0u8; 32];
1429+
rng::fill_bytes(&mut key_seed);
1430+
match ChannelKeys::new_from_seed(&key_seed) {
1431+
Ok(key) => key,
1432+
Err(_) => panic!("RNG is busted!")
1433+
}
1434+
};
1435+
1436+
let channel = Channel::new_from_req(&*self.fee_estimator, chan_keys, their_node_id.clone(), msg, 0, false, self.announce_channels_publicly, Arc::clone(&self.logger)).map_err(|e| MsgHandleErrInternal::from_no_close(e))?;
1437+
let accept_msg = channel.get_accept_channel();
1438+
channel_state.by_id.insert(channel.channel_id(), channel);
1439+
Ok(accept_msg)
1440+
}
1441+
1442+
fn internal_announcement_signatures(&self, their_node_id: &PublicKey, msg: &msgs::AnnouncementSignatures) -> Result<(), MsgHandleErrInternal> {
1443+
let (chan_announcement, chan_update) = {
1444+
let mut channel_state = self.channel_state.lock().unwrap();
1445+
match channel_state.by_id.get_mut(&msg.channel_id) {
1446+
Some(chan) => {
1447+
if chan.get_their_node_id() != *their_node_id {
1448+
return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id));
1449+
}
1450+
if !chan.is_usable() {
1451+
return Err(MsgHandleErrInternal::from_no_close(HandleError{err: "Got an announcement_signatures before we were ready for it", action: Some(msgs::ErrorAction::IgnoreError)}));
1452+
}
1453+
1454+
let our_node_id = self.get_our_node_id();
1455+
let (announcement, our_bitcoin_sig) = chan.get_channel_announcement(our_node_id.clone(), self.genesis_hash.clone())
1456+
.map_err(|e| MsgHandleErrInternal::from_maybe_close(e))?;
1457+
1458+
let were_node_one = announcement.node_id_1 == our_node_id;
1459+
let msghash = Message::from_slice(&Sha256dHash::from_data(&announcement.encode()[..])[..]).unwrap();
1460+
let bad_sig_action = MsgHandleErrInternal::send_err_msg_close_chan("Bad announcement_signatures node_signature", msg.channel_id);
1461+
secp_call!(self.secp_ctx.verify(&msghash, &msg.node_signature, if were_node_one { &announcement.node_id_2 } else { &announcement.node_id_1 }), bad_sig_action);
1462+
secp_call!(self.secp_ctx.verify(&msghash, &msg.bitcoin_signature, if were_node_one { &announcement.bitcoin_key_2 } else { &announcement.bitcoin_key_1 }), bad_sig_action);
1463+
1464+
let our_node_sig = self.secp_ctx.sign(&msghash, &self.our_network_key);
1465+
1466+
(msgs::ChannelAnnouncement {
1467+
node_signature_1: if were_node_one { our_node_sig } else { msg.node_signature },
1468+
node_signature_2: if were_node_one { msg.node_signature } else { our_node_sig },
1469+
bitcoin_signature_1: if were_node_one { our_bitcoin_sig } else { msg.bitcoin_signature },
1470+
bitcoin_signature_2: if were_node_one { msg.bitcoin_signature } else { our_bitcoin_sig },
1471+
contents: announcement,
1472+
}, self.get_channel_update(chan).unwrap()) // can only fail if we're not in a ready state
1473+
},
1474+
None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
1475+
}
1476+
};
1477+
let mut pending_events = self.pending_events.lock().unwrap();
1478+
pending_events.push(events::Event::BroadcastChannelAnnouncement { msg: chan_announcement, update_msg: chan_update });
1479+
Ok(())
1480+
}
1481+
1482+
13601483
}
13611484

13621485
impl events::EventsProvider for ChannelManager {
@@ -1483,41 +1606,42 @@ impl ChainListener for ChannelManager {
14831606
}
14841607
}
14851608

1609+
macro_rules! handle_error {
1610+
($self: ident, $internal: expr, $their_node_id: expr) => {
1611+
match $internal {
1612+
Ok(msg) => Ok(msg),
1613+
Err(MsgHandleErrInternal { err, needs_channel_force_close }) => {
1614+
if needs_channel_force_close {
1615+
match &err.action {
1616+
&Some(msgs::ErrorAction::DisconnectPeer { msg: Some(ref msg) }) => {
1617+
if msg.channel_id == [0; 32] {
1618+
$self.peer_disconnected(&$their_node_id, true);
1619+
} else {
1620+
$self.force_close_channel(&msg.channel_id);
1621+
}
1622+
},
1623+
&Some(msgs::ErrorAction::DisconnectPeer { msg: None }) => {},
1624+
&Some(msgs::ErrorAction::IgnoreError) => {},
1625+
&Some(msgs::ErrorAction::SendErrorMessage { ref msg }) => {
1626+
if msg.channel_id == [0; 32] {
1627+
$self.peer_disconnected(&$their_node_id, true);
1628+
} else {
1629+
$self.force_close_channel(&msg.channel_id);
1630+
}
1631+
},
1632+
&None => {},
1633+
}
1634+
}
1635+
Err(err)
1636+
},
1637+
}
1638+
}
1639+
}
1640+
14861641
impl ChannelMessageHandler for ChannelManager {
14871642
//TODO: Handle errors and close channel (or so)
14881643
fn handle_open_channel(&self, their_node_id: &PublicKey, msg: &msgs::OpenChannel) -> Result<msgs::AcceptChannel, HandleError> {
1489-
if msg.chain_hash != self.genesis_hash {
1490-
return Err(HandleError{err: "Unknown genesis block hash", action: None});
1491-
}
1492-
let mut channel_state = self.channel_state.lock().unwrap();
1493-
if channel_state.by_id.contains_key(&msg.temporary_channel_id) {
1494-
return Err(HandleError{err: "temporary_channel_id collision!", action: None});
1495-
}
1496-
1497-
let chan_keys = if cfg!(feature = "fuzztarget") {
1498-
ChannelKeys {
1499-
funding_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]).unwrap(),
1500-
revocation_base_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0]).unwrap(),
1501-
payment_base_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0]).unwrap(),
1502-
delayed_payment_base_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0]).unwrap(),
1503-
htlc_base_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0]).unwrap(),
1504-
channel_close_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0]).unwrap(),
1505-
channel_monitor_claim_key: SecretKey::from_slice(&self.secp_ctx, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0]).unwrap(),
1506-
commitment_seed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1507-
}
1508-
} else {
1509-
let mut key_seed = [0u8; 32];
1510-
rng::fill_bytes(&mut key_seed);
1511-
match ChannelKeys::new_from_seed(&key_seed) {
1512-
Ok(key) => key,
1513-
Err(_) => panic!("RNG is busted!")
1514-
}
1515-
};
1516-
1517-
let channel = Channel::new_from_req(&*self.fee_estimator, chan_keys, their_node_id.clone(), msg, 0, false, self.announce_channels_publicly, Arc::clone(&self.logger))?;
1518-
let accept_msg = channel.get_accept_channel()?;
1519-
channel_state.by_id.insert(channel.channel_id(), channel);
1520-
Ok(accept_msg)
1644+
handle_error!(self, self.internal_open_channel(their_node_id, msg), their_node_id)
15211645
}
15221646

15231647
fn handle_accept_channel(&self, their_node_id: &PublicKey, msg: &msgs::AcceptChannel) -> Result<(), HandleError> {
@@ -1978,42 +2102,7 @@ impl ChannelMessageHandler for ChannelManager {
19782102
}
19792103

19802104
fn handle_announcement_signatures(&self, their_node_id: &PublicKey, msg: &msgs::AnnouncementSignatures) -> Result<(), HandleError> {
1981-
let (chan_announcement, chan_update) = {
1982-
let mut channel_state = self.channel_state.lock().unwrap();
1983-
match channel_state.by_id.get_mut(&msg.channel_id) {
1984-
Some(chan) => {
1985-
if chan.get_their_node_id() != *their_node_id {
1986-
return Err(HandleError{err: "Got a message for a channel from the wrong node!", action: Some(msgs::ErrorAction::IgnoreError) })
1987-
}
1988-
if !chan.is_usable() {
1989-
return Err(HandleError{err: "Got an announcement_signatures before we were ready for it", action: Some(msgs::ErrorAction::IgnoreError) });
1990-
}
1991-
1992-
let our_node_id = self.get_our_node_id();
1993-
let (announcement, our_bitcoin_sig) = chan.get_channel_announcement(our_node_id.clone(), self.genesis_hash.clone())?;
1994-
1995-
let were_node_one = announcement.node_id_1 == our_node_id;
1996-
let msghash = Message::from_slice(&Sha256dHash::from_data(&announcement.encode()[..])[..]).unwrap();
1997-
let bad_sig_action = msgs::ErrorAction::SendErrorMessage { msg: msgs::ErrorMessage { channel_id: msg.channel_id.clone(), data: "Invalid signature in announcement_signatures".to_string() } };
1998-
secp_call!(self.secp_ctx.verify(&msghash, &msg.node_signature, if were_node_one { &announcement.node_id_2 } else { &announcement.node_id_1 }), "Bad announcement_signatures node_signature", bad_sig_action);
1999-
secp_call!(self.secp_ctx.verify(&msghash, &msg.bitcoin_signature, if were_node_one { &announcement.bitcoin_key_2 } else { &announcement.bitcoin_key_1 }), "Bad announcement_signatures bitcoin_signature", bad_sig_action);
2000-
2001-
let our_node_sig = self.secp_ctx.sign(&msghash, &self.our_network_key);
2002-
2003-
(msgs::ChannelAnnouncement {
2004-
node_signature_1: if were_node_one { our_node_sig } else { msg.node_signature },
2005-
node_signature_2: if were_node_one { msg.node_signature } else { our_node_sig },
2006-
bitcoin_signature_1: if were_node_one { our_bitcoin_sig } else { msg.bitcoin_signature },
2007-
bitcoin_signature_2: if were_node_one { msg.bitcoin_signature } else { our_bitcoin_sig },
2008-
contents: announcement,
2009-
}, self.get_channel_update(chan).unwrap()) // can only fail if we're not in a ready state
2010-
},
2011-
None => return Err(HandleError{err: "Failed to find corresponding channel", action: Some(msgs::ErrorAction::IgnoreError)})
2012-
}
2013-
};
2014-
let mut pending_events = self.pending_events.lock().unwrap();
2015-
pending_events.push(events::Event::BroadcastChannelAnnouncement { msg: chan_announcement, update_msg: chan_update });
2016-
Ok(())
2105+
handle_error!(self, self.internal_announcement_signatures(their_node_id, msg), their_node_id)
20172106
}
20182107

20192108
fn peer_disconnected(&self, their_node_id: &PublicKey, no_connection_possible: bool) {

0 commit comments

Comments
 (0)