diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index 095059c5183..20ca399449c 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -216,6 +216,7 @@ pub fn do_test(data: &[u8], out: Out) { }, funding_txo: Some(OutPoint { txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), index: 0 }), short_channel_id: Some(scid), + inbound_scid_alias: None, channel_value_satoshis: slice_to_be64(get_slice!(8)), user_channel_id: 0, inbound_capacity_msat: 0, unspendable_punishment_reserve: None, diff --git a/lightning-invoice/src/utils.rs b/lightning-invoice/src/utils.rs index fc82541a564..73f0138824d 100644 --- a/lightning-invoice/src/utils.rs +++ b/lightning-invoice/src/utils.rs @@ -65,7 +65,7 @@ pub fn create_phantom_invoice( for hint in phantom_route_hints { for channel in &hint.channels { - let short_channel_id = match channel.short_channel_id { + let short_channel_id = match channel.get_inbound_payment_scid() { Some(id) => id, None => continue, }; @@ -162,7 +162,7 @@ where let our_channels = channelmanager.list_usable_channels(); let mut route_hints = vec![]; for channel in our_channels { - let short_channel_id = match channel.short_channel_id { + let short_channel_id = match channel.get_inbound_payment_scid() { Some(id) => id, None => continue, }; @@ -313,6 +313,13 @@ mod test { assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64); assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string()))); + // Invoice SCIDs should always use inbound SCID aliases over the real channel ID, if one is + // available. + assert_eq!(invoice.route_hints().len(), 1); + assert_eq!(invoice.route_hints()[0].0.len(), 1); + assert_eq!(invoice.route_hints()[0].0[0].short_channel_id, + nodes[1].node.list_usable_channels()[0].inbound_scid_alias.unwrap()); + let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key()) .with_features(invoice.features().unwrap().clone()) .with_route_hints(invoice.route_hints()); diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index dd9fcbea9a8..71e0ea121a9 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -695,6 +695,19 @@ pub(super) struct Channel { /// This channel's type, as negotiated during channel open channel_type: ChannelTypeFeatures, + + // Our counterparty can offer us SCID aliases which they will map to this channel when routing + // outbound payments. These can be used in invoice route hints to avoid explicitly revealing + // the channel's funding UTXO. + // We only bother storing the most recent SCID alias at any time, though our counterparty has + // to store all of them. + latest_inbound_scid_alias: Option, + + // We always offer our counterparty a static SCID alias, which we recognize as for this channel + // if we see it in HTLC forwarding instructions. We don't bother rotating the alias given we + // don't currently support node id aliases and eventually privacy should be provided with + // blinded paths instead of simple scid+node_id aliases. + outbound_scid_alias: u64, } #[cfg(any(test, fuzzing))] @@ -800,7 +813,8 @@ impl Channel { // Constructors: pub fn new_outbound( fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, their_features: &InitFeatures, - channel_value_satoshis: u64, push_msat: u64, user_id: u64, config: &UserConfig, current_chain_height: u32 + channel_value_satoshis: u64, push_msat: u64, user_id: u64, config: &UserConfig, current_chain_height: u32, + outbound_scid_alias: u64 ) -> Result, APIError> where K::Target: KeysInterface, F::Target: FeeEstimator, @@ -947,6 +961,9 @@ impl Channel { workaround_lnd_bug_4006: None, + latest_inbound_scid_alias: None, + outbound_scid_alias, + #[cfg(any(test, fuzzing))] historical_inbound_htlc_fulfills: HashSet::new(), @@ -984,7 +1001,8 @@ impl Channel { /// Assumes chain_hash has already been checked and corresponds with what we expect! pub fn new_from_req( fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, their_features: &InitFeatures, - msg: &msgs::OpenChannel, user_id: u64, config: &UserConfig, current_chain_height: u32, logger: &L + msg: &msgs::OpenChannel, user_id: u64, config: &UserConfig, current_chain_height: u32, logger: &L, + outbound_scid_alias: u64 ) -> Result, ChannelError> where K::Target: KeysInterface, F::Target: FeeEstimator, @@ -1252,6 +1270,9 @@ impl Channel { workaround_lnd_bug_4006: None, + latest_inbound_scid_alias: None, + outbound_scid_alias, + #[cfg(any(test, fuzzing))] historical_inbound_htlc_fulfills: HashSet::new(), @@ -2151,6 +2172,15 @@ impl Channel { return Err(ChannelError::Ignore("Peer sent funding_locked when we needed a channel_reestablish. The peer is likely lnd, see https://github.com/lightningnetwork/lnd/issues/4006".to_owned())); } + if let Some(scid_alias) = msg.short_channel_id_alias { + if Some(scid_alias) != self.short_channel_id { + // The scid alias provided can be used to route payments *from* our counterparty, + // i.e. can be used for inbound payments and provided in invoices, but is not used + // when routing outbound payments. + self.latest_inbound_scid_alias = Some(scid_alias); + } + } + let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); if non_shutdown_state == ChannelState::FundingSent as u32 { @@ -2158,17 +2188,28 @@ impl Channel { } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) { self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & MULTI_STATE_FLAGS); self.update_time_counter += 1; - } else if (self.channel_state & (ChannelState::ChannelFunded as u32) != 0 && - // Note that funding_signed/funding_created will have decremented both by 1! - self.cur_holder_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 && - self.cur_counterparty_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1) || - // If we reconnected before sending our funding locked they may still resend theirs: - (self.channel_state & (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) == - (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32)) { - if self.counterparty_cur_commitment_point != Some(msg.next_per_commitment_point) { + } else if self.channel_state & (ChannelState::ChannelFunded as u32) != 0 || + // If we reconnected before sending our funding locked they may still resend theirs: + (self.channel_state & (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) == + (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32)) + { + // They probably disconnected/reconnected and re-sent the funding_locked, which is + // required, or they're sending a fresh SCID alias. + let expected_point = + if self.cur_counterparty_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 { + // If they haven't ever sent an updated point, the point they send should match + // the current one. + self.counterparty_cur_commitment_point + } else { + // If they have sent updated points, funding_locked is always supposed to match + // their "first" point, which we re-derive here. + Some(PublicKey::from_secret_key(&self.secp_ctx, &SecretKey::from_slice( + &self.commitment_secrets.get_secret(INITIAL_COMMITMENT_NUMBER - 1).expect("We should have all prev secrets available") + ).expect("We already advanced, so previous secret keys should have been validated already"))) + }; + if expected_point != Some(msg.next_per_commitment_point) { return Err(ChannelError::Close("Peer sent a reconnect funding_locked with a different point".to_owned())); } - // They probably disconnected/reconnected and re-sent the funding_locked, which is required return Ok(None); } else { return Err(ChannelError::Close("Peer sent a funding_locked at a strange time".to_owned())); @@ -3457,6 +3498,7 @@ impl Channel { Some(msgs::FundingLocked { channel_id: self.channel_id(), next_per_commitment_point, + short_channel_id_alias: Some(self.outbound_scid_alias), }) } else { None }; @@ -3678,6 +3720,7 @@ impl Channel { funding_locked: Some(msgs::FundingLocked { channel_id: self.channel_id(), next_per_commitment_point, + short_channel_id_alias: Some(self.outbound_scid_alias), }), raa: None, commitment_update: None, mon_update: None, order: RAACommitmentOrder::CommitmentFirst, @@ -3713,6 +3756,7 @@ impl Channel { Some(msgs::FundingLocked { channel_id: self.channel_id(), next_per_commitment_point, + short_channel_id_alias: Some(self.outbound_scid_alias), }) } else { None }; @@ -4195,6 +4239,22 @@ impl Channel { self.short_channel_id } + /// Allowed in any state (including after shutdown) + pub fn latest_inbound_scid_alias(&self) -> Option { + self.latest_inbound_scid_alias + } + + /// Allowed in any state (including after shutdown) + pub fn outbound_scid_alias(&self) -> u64 { + self.outbound_scid_alias + } + /// Only allowed immediately after deserialization if get_outbound_scid_alias returns 0, + /// indicating we were written by LDK prior to 0.0.106 which did not set outbound SCID aliases. + pub fn set_outbound_scid_alias(&mut self, outbound_scid_alias: u64) { + assert_eq!(self.outbound_scid_alias, 0); + self.outbound_scid_alias = outbound_scid_alias; + } + /// Returns the funding_txo we either got from our peer, or were given by /// get_outbound_funding_created. pub fn get_funding_txo(&self) -> Option { @@ -4443,10 +4503,12 @@ impl Channel { if need_commitment_update { if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) == 0 { if self.channel_state & (ChannelState::PeerDisconnected as u32) == 0 { - let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + let next_per_commitment_point = + self.holder_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &self.secp_ctx); return Some(msgs::FundingLocked { channel_id: self.channel_id, next_per_commitment_point, + short_channel_id_alias: Some(self.outbound_scid_alias), }); } } else { @@ -5765,6 +5827,8 @@ impl Writeable for Channel { (13, self.channel_creation_height, required), (15, preimages, vec_type), (17, self.announcement_sigs_state, required), + (19, self.latest_inbound_scid_alias, option), + (21, self.outbound_scid_alias, required), }); Ok(()) @@ -6020,6 +6084,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel // If we read an old Channel, for simplicity we just treat it as "we never sent an // AnnouncementSignatures" which implies we'll re-send it on reconnect, but that's fine. let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent); + let mut latest_inbound_scid_alias = None; + let mut outbound_scid_alias = None; read_tlv_fields!(reader, { (0, announcement_sigs, option), @@ -6035,6 +6101,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel (13, channel_creation_height, option), (15, preimages_opt, vec_type), (17, announcement_sigs_state, option), + (19, latest_inbound_scid_alias, option), + (21, outbound_scid_alias, option), }); if let Some(preimages) = preimages_opt { @@ -6169,6 +6237,10 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel workaround_lnd_bug_4006: None, + latest_inbound_scid_alias, + // Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing + outbound_scid_alias: outbound_scid_alias.unwrap_or(0), + #[cfg(any(test, fuzzing))] historical_inbound_htlc_fulfills, @@ -6291,7 +6363,7 @@ mod tests { let secp_ctx = Secp256k1::new(); let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - match Channel::::new_outbound(&&fee_estimator, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0) { + match Channel::::new_outbound(&&fee_estimator, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42) { Err(APIError::IncompatibleShutdownScript { script }) => { assert_eq!(script.into_inner(), non_v0_segwit_shutdown_script.into_inner()); }, @@ -6313,7 +6385,7 @@ mod tests { let node_a_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - let node_a_chan = Channel::::new_outbound(&&fee_est, &&keys_provider, node_a_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap(); + let node_a_chan = Channel::::new_outbound(&&fee_est, &&keys_provider, node_a_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap(); // Now change the fee so we can check that the fee in the open_channel message is the // same as the old fee. @@ -6339,13 +6411,13 @@ mod tests { // Create Node A's channel pointing to Node B's pubkey let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap(); + let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap(); // Create Node B's channel by receiving Node A's open_channel message // Make sure A's dust limit is as we expect. let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash()); let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); - let mut node_b_chan = Channel::::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger).unwrap(); + let mut node_b_chan = Channel::::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger, 42).unwrap(); // Node B --> Node A: accept channel, explicitly setting B's dust limit. let mut accept_channel_msg = node_b_chan.accept_inbound_channel(); @@ -6409,7 +6481,7 @@ mod tests { let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - let mut chan = Channel::::new_outbound(&&fee_est, &&keys_provider, node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap(); + let mut chan = Channel::::new_outbound(&&fee_est, &&keys_provider, node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap(); let commitment_tx_fee_0_htlcs = Channel::::commit_tx_fee_msat(chan.feerate_per_kw, 0, chan.opt_anchors()); let commitment_tx_fee_1_htlc = Channel::::commit_tx_fee_msat(chan.feerate_per_kw, 1, chan.opt_anchors()); @@ -6458,12 +6530,12 @@ mod tests { // Create Node A's channel pointing to Node B's pubkey let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap(); + let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap(); // Create Node B's channel by receiving Node A's open_channel message let open_channel_msg = node_a_chan.get_open_channel(chain_hash); let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); - let mut node_b_chan = Channel::::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger).unwrap(); + let mut node_b_chan = Channel::::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger, 42).unwrap(); // Node B --> Node A: accept channel let accept_channel_msg = node_b_chan.accept_inbound_channel(); @@ -6520,7 +6592,7 @@ mod tests { // Create a channel. let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap(); + let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap(); assert!(node_a_chan.counterparty_forwarding_info.is_none()); assert_eq!(node_a_chan.holder_htlc_minimum_msat, 1); // the default assert!(node_a_chan.counterparty_forwarding_info().is_none()); @@ -6585,7 +6657,7 @@ mod tests { let counterparty_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let mut config = UserConfig::default(); config.channel_options.announced_channel = false; - let mut chan = Channel::::new_outbound(&&feeest, &&keys_provider, counterparty_node_id, &InitFeatures::known(), 10_000_000, 100000, 42, &config, 0).unwrap(); // Nothing uses their network key in this test + let mut chan = Channel::::new_outbound(&&feeest, &&keys_provider, counterparty_node_id, &InitFeatures::known(), 10_000_000, 100000, 42, &config, 0, 42).unwrap(); // Nothing uses their network key in this test chan.holder_dust_limit_satoshis = 546; chan.counterparty_selected_channel_reserve_satoshis = Some(0); // Filled in in accept_channel diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d206dee4950..699941586e6 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -660,8 +660,16 @@ pub(super) enum RAACommitmentOrder { // Note this is only exposed in cfg(test): pub(super) struct ChannelHolder { pub(super) by_id: HashMap<[u8; 32], Channel>, + /// SCIDs (and outbound SCID aliases) to the real channel id. Outbound SCID aliases are added + /// here once the channel is available for normal use, with SCIDs being added once the funding + /// transaction is confirmed at the channel's required confirmation depth. pub(super) short_to_id: HashMap, - /// short channel id -> forward infos. Key of 0 means payments received + /// SCID/SCID Alias -> forward infos. Key of 0 means payments received. + /// + /// Note that because we may have an SCID Alias as the key we can have two entries per channel, + /// though in practice we probably won't be receiving HTLCs for a channel both via the alias + /// and via the classic SCID. + /// /// Note that while this is held in the same mutex as the channels themselves, no consistency /// guarantees are made about the existence of a channel with the short id here, nor the short /// ids in the PendingHTLCInfo! @@ -971,6 +979,12 @@ pub struct ChannelManager>, + /// The set of outbound SCID aliases across all our channels, including unconfirmed channels + /// and some closed channels which reached a usable state prior to being closed. This is used + /// only to avoid duplicates, and is not persisted explicitly to disk, but rebuilt from the + /// active channel list on load. + outbound_scid_aliases: Mutex>, + our_network_key: SecretKey, our_network_pubkey: PublicKey, @@ -1188,7 +1202,20 @@ pub struct ChannelDetails { pub funding_txo: Option, /// The position of the funding transaction in the chain. None if the funding transaction has /// not yet been confirmed and the channel fully opened. + /// + /// Note that if [`inbound_scid_alias`] is set, it must be used for invoices and inbound + /// payments instead of this. See [`get_inbound_payment_scid`]. + /// + /// [`inbound_scid_alias`]: Self::inbound_scid_alias + /// [`get_inbound_payment_scid`]: Self::get_inbound_payment_scid pub short_channel_id: Option, + /// An optional [`short_channel_id`] alias for this channel, randomly generated by our + /// counterparty and usable in place of [`short_channel_id`] in invoice route hints. Our + /// counterparty will recognize the alias provided here in place of the [`short_channel_id`] + /// when they see a payment to be routed to us. + /// + /// [`short_channel_id`]: Self::short_channel_id + pub inbound_scid_alias: Option, /// The value, in satoshis, of this channel as appears in the funding output pub channel_value_satoshis: u64, /// The value, in satoshis, that must always be held in the channel for us. This value ensures @@ -1274,6 +1301,15 @@ pub struct ChannelDetails { pub is_public: bool, } +impl ChannelDetails { + /// Gets the SCID which should be used to identify this channel for inbound payments. This + /// should be used for providing invoice hints or in any other context where our counterparty + /// will forward a payment to us. + pub fn get_inbound_payment_scid(&self) -> Option { + self.inbound_scid_alias.or(self.short_channel_id) + } +} + /// If a payment fails to send, it can be in one of several states. This enum is returned as the /// Err() type describing which state the payment is in, see the description of individual enum /// states for more. @@ -1383,6 +1419,24 @@ macro_rules! handle_error { } } +macro_rules! update_maps_on_chan_removal { + ($self: expr, $short_to_id: expr, $channel: expr) => { + if let Some(short_id) = $channel.get_short_channel_id() { + $short_to_id.remove(&short_id); + } else { + // If the channel was never confirmed on-chain prior to its closure, remove the + // outbound SCID alias we used for it from the collision-prevention set. While we + // generally want to avoid ever re-using an outbound SCID alias across all channels, we + // also don't want a counterparty to be able to trivially cause a memory leak by simply + // opening a million channels with us which are closed before we ever reach the funding + // stage. + let alias_removed = $self.outbound_scid_aliases.lock().unwrap().remove(&$channel.outbound_scid_alias()); + debug_assert!(alias_removed); + } + $short_to_id.remove(&$channel.outbound_scid_alias()); + } +} + /// Returns (boolean indicating if we should remove the Channel object from memory, a mapped error) macro_rules! convert_chan_err { ($self: ident, $err: expr, $short_to_id: expr, $channel: expr, $channel_id: expr) => { @@ -1395,18 +1449,14 @@ macro_rules! convert_chan_err { }, ChannelError::Close(msg) => { log_error!($self.logger, "Closing channel {} due to close-required error: {}", log_bytes!($channel_id[..]), msg); - if let Some(short_id) = $channel.get_short_channel_id() { - $short_to_id.remove(&short_id); - } + update_maps_on_chan_removal!($self, $short_to_id, $channel); let shutdown_res = $channel.force_shutdown(true); (true, MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, $channel.get_user_id(), shutdown_res, $self.get_channel_update_for_broadcast(&$channel).ok())) }, ChannelError::CloseDelayBroadcast(msg) => { log_error!($self.logger, "Channel {} need to be shutdown but closing transactions not broadcast due to {}", log_bytes!($channel_id[..]), msg); - if let Some(short_id) = $channel.get_short_channel_id() { - $short_to_id.remove(&short_id); - } + update_maps_on_chan_removal!($self, $short_to_id, $channel); let shutdown_res = $channel.force_shutdown(false); (true, MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, $channel.get_user_id(), shutdown_res, $self.get_channel_update_for_broadcast(&$channel).ok())) @@ -1446,28 +1496,21 @@ macro_rules! try_chan_entry { } macro_rules! remove_channel { - ($channel_state: expr, $entry: expr) => { + ($self: expr, $channel_state: expr, $entry: expr) => { { let channel = $entry.remove_entry().1; - if let Some(short_id) = channel.get_short_channel_id() { - $channel_state.short_to_id.remove(&short_id); - } + update_maps_on_chan_removal!($self, $channel_state.short_to_id, channel); channel } } } macro_rules! handle_monitor_err { - ($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr) => { - handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, $resend_raa, $resend_commitment, Vec::new(), Vec::new()) - }; ($self: ident, $err: expr, $short_to_id: expr, $chan: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr, $failed_forwards: expr, $failed_fails: expr, $failed_finalized_fulfills: expr, $chan_id: expr) => { match $err { ChannelMonitorUpdateErr::PermanentFailure => { log_error!($self.logger, "Closing channel {} due to monitor update ChannelMonitorUpdateErr::PermanentFailure", log_bytes!($chan_id[..])); - if let Some(short_id) = $chan.get_short_channel_id() { - $short_to_id.remove(&short_id); - } + update_maps_on_chan_removal!($self, $short_to_id, $chan); // TODO: $failed_fails is dropped here, which will cause other channels to hit the // chain in a confused state! We need to move them into the ChannelMonitor which // will be responsible for failing backwards once things confirm on-chain. @@ -1513,9 +1556,19 @@ macro_rules! handle_monitor_err { } res } }; + ($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $chan_id: expr, COMMITMENT_UPDATE_ONLY) => { { + debug_assert!($action_type == RAACommitmentOrder::CommitmentFirst); + handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, false, true, Vec::new(), Vec::new(), Vec::new(), $chan_id) + } }; + ($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $chan_id: expr, NO_UPDATE) => { + handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, false, false, Vec::new(), Vec::new(), Vec::new(), $chan_id) + }; + ($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr) => { + handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, $resend_raa, $resend_commitment, Vec::new(), Vec::new(), Vec::new()) + }; ($self: ident, $err: expr, $channel_state: expr, $entry: expr, $action_type: path, $resend_raa: expr, $resend_commitment: expr, $failed_forwards: expr, $failed_fails: expr) => { handle_monitor_err!($self, $err, $channel_state, $entry, $action_type, $resend_raa, $resend_commitment, $failed_forwards, $failed_fails, Vec::new()) - } + }; } macro_rules! return_monitor_err { @@ -1539,15 +1592,34 @@ macro_rules! maybe_break_monitor_err { } } +macro_rules! send_funding_locked { + ($short_to_id: expr, $pending_msg_events: expr, $channel: expr, $funding_locked_msg: expr) => { + $pending_msg_events.push(events::MessageSendEvent::SendFundingLocked { + node_id: $channel.get_counterparty_node_id(), + msg: $funding_locked_msg, + }); + // Note that we may send a funding locked multiple times for a channel if we reconnect, so + // we allow collisions, but we shouldn't ever be updating the channel ID pointed to. + let outbound_alias_insert = $short_to_id.insert($channel.outbound_scid_alias(), $channel.channel_id()); + assert!(outbound_alias_insert.is_none() || outbound_alias_insert.unwrap() == $channel.channel_id(), + "SCIDs should never collide - ensure you weren't behind the chain tip by a full month when creating channels"); + if let Some(real_scid) = $channel.get_short_channel_id() { + let scid_insert = $short_to_id.insert(real_scid, $channel.channel_id()); + assert!(scid_insert.is_none() || scid_insert.unwrap() == $channel.channel_id(), + "SCIDs should never collide - ensure you weren't behind the chain tip by a full month when creating channels"); + } + } +} + macro_rules! handle_chan_restoration_locked { ($self: ident, $channel_lock: expr, $channel_state: expr, $channel_entry: expr, $raa: expr, $commitment_update: expr, $order: expr, $chanmon_update: expr, $pending_forwards: expr, $funding_broadcastable: expr, $funding_locked: expr, $announcement_sigs: expr) => { { let mut htlc_forwards = None; - let counterparty_node_id = $channel_entry.get().get_counterparty_node_id(); let chanmon_update: Option = $chanmon_update; // Force type-checking to resolve let chanmon_update_is_none = chanmon_update.is_none(); + let counterparty_node_id = $channel_entry.get().get_counterparty_node_id(); let res = loop { let forwards: Vec<(PendingHTLCInfo, u64)> = $pending_forwards; // Force type-checking to resolve if !forwards.is_empty() { @@ -1573,11 +1645,7 @@ macro_rules! handle_chan_restoration_locked { // Similar to the above, this implies that we're letting the funding_locked fly // before it should be allowed to. assert!(chanmon_update.is_none()); - $channel_state.pending_msg_events.push(events::MessageSendEvent::SendFundingLocked { - node_id: counterparty_node_id, - msg, - }); - $channel_state.short_to_id.insert($channel_entry.get().get_short_channel_id().unwrap(), $channel_entry.get().channel_id()); + send_funding_locked!($channel_state.short_to_id, $channel_state.pending_msg_events, $channel_entry.get(), msg); } if let Some(msg) = $announcement_sigs { $channel_state.pending_msg_events.push(events::MessageSendEvent::SendAnnouncementSignatures { @@ -1706,6 +1774,7 @@ impl ChannelMana claimable_htlcs: HashMap::new(), pending_msg_events: Vec::new(), }), + outbound_scid_aliases: Mutex::new(HashSet::new()), pending_inbound_payments: Mutex::new(HashMap::new()), pending_outbound_payments: Mutex::new(HashMap::new()), @@ -1737,6 +1806,25 @@ impl ChannelMana &self.default_configuration } + fn create_and_insert_outbound_scid_alias(&self) -> u64 { + let height = self.best_block.read().unwrap().height(); + let mut outbound_scid_alias = 0; + let mut i = 0; + loop { + if cfg!(fuzzing) { // fuzzing chacha20 doesn't use the key at all so we always get the same alias + outbound_scid_alias += 1; + } else { + outbound_scid_alias = fake_scid::Namespace::OutboundAlias.get_fake_scid(height, &self.genesis_hash, &self.fake_scid_rand_bytes, &self.keys_manager); + } + if outbound_scid_alias != 0 && self.outbound_scid_aliases.lock().unwrap().insert(outbound_scid_alias) { + break; + } + i += 1; + if i > 1_000_000 { panic!("Your RNG is busted or we ran out of possible outbound SCID aliases (which should never happen before we run out of memory to store channels"); } + } + outbound_scid_alias + } + /// Creates a new outbound channel to the given remote node and with the given value. /// /// `user_channel_id` will be provided back as in @@ -1772,11 +1860,20 @@ impl ChannelMana let per_peer_state = self.per_peer_state.read().unwrap(); match per_peer_state.get(&their_network_key) { Some(peer_state) => { + let outbound_scid_alias = self.create_and_insert_outbound_scid_alias(); let peer_state = peer_state.lock().unwrap(); let their_features = &peer_state.latest_features; let config = if override_config.is_some() { override_config.as_ref().unwrap() } else { &self.default_configuration }; - Channel::new_outbound(&self.fee_estimator, &self.keys_manager, their_network_key, their_features, - channel_value_satoshis, push_msat, user_channel_id, config, self.best_block.read().unwrap().height())? + match Channel::new_outbound(&self.fee_estimator, &self.keys_manager, their_network_key, + their_features, channel_value_satoshis, push_msat, user_channel_id, config, + self.best_block.read().unwrap().height(), outbound_scid_alias) + { + Ok(res) => res, + Err(e) => { + self.outbound_scid_aliases.lock().unwrap().remove(&outbound_scid_alias); + return Err(e); + }, + } }, None => return Err(APIError::ChannelUnavailable { err: format!("Not connected to node: {}", their_network_key) }), } @@ -1826,6 +1923,7 @@ impl ChannelMana }, funding_txo: channel.get_funding_txo(), short_channel_id: channel.get_short_channel_id(), + inbound_scid_alias: channel.latest_inbound_scid_alias(), channel_value_satoshis: channel.get_value_satoshis(), unspendable_punishment_reserve: to_self_reserve_satoshis, balance_msat, @@ -1911,9 +2009,9 @@ impl ChannelMana if let Some(monitor_update) = monitor_update { if let Err(e) = self.chain_monitor.update_channel(chan_entry.get().get_funding_txo().unwrap(), monitor_update) { let (result, is_permanent) = - handle_monitor_err!(self, e, channel_state.short_to_id, chan_entry.get_mut(), RAACommitmentOrder::CommitmentFirst, false, false, Vec::new(), Vec::new(), Vec::new(), chan_entry.key()); + handle_monitor_err!(self, e, channel_state.short_to_id, chan_entry.get_mut(), RAACommitmentOrder::CommitmentFirst, chan_entry.key(), NO_UPDATE); if is_permanent { - remove_channel!(channel_state, chan_entry); + remove_channel!(self, channel_state, chan_entry); break result; } } @@ -1925,7 +2023,7 @@ impl ChannelMana }); if chan_entry.get().is_shutdown() { - let channel = remove_channel!(channel_state, chan_entry); + let channel = remove_channel!(self, channel_state, chan_entry); if let Ok(channel_update) = self.get_channel_update_for_broadcast(&channel) { channel_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate { msg: channel_update @@ -2019,9 +2117,6 @@ impl ChannelMana return Err(APIError::ChannelUnavailable{err: "No such channel".to_owned()}); } } - if let Some(short_id) = chan.get().get_short_channel_id() { - channel_state.short_to_id.remove(&short_id); - } if peer_node_id.is_some() { if let Some(peer_msg) = peer_msg { self.issue_channel_close_events(chan.get(),ClosureReason::CounterpartyForceClosed { peer_msg: peer_msg.to_string() }); @@ -2029,7 +2124,7 @@ impl ChannelMana } else { self.issue_channel_close_events(chan.get(),ClosureReason::HolderForceClosed); } - chan.remove_entry().1 + remove_channel!(self, channel_state, chan) } else { return Err(APIError::ChannelUnavailable{err: "No such channel".to_owned()}); } @@ -3176,12 +3271,9 @@ impl ChannelMana } ChannelError::Close(msg) => { log_trace!(self.logger, "Closing channel {} due to Close-required error: {}", log_bytes!(chan.key()[..]), msg); - let (channel_id, mut channel) = chan.remove_entry(); - if let Some(short_id) = channel.get_short_channel_id() { - channel_state.short_to_id.remove(&short_id); - } + let mut channel = remove_channel!(self, channel_state, chan); // ChannelClosed event is generated by handle_error for us. - Err(MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, channel.get_user_id(), channel.force_shutdown(true), self.get_channel_update_for_broadcast(&channel).ok())) + Err(MsgHandleErrInternal::from_finish_shutdown(msg, channel.channel_id(), channel.get_user_id(), channel.force_shutdown(true), self.get_channel_update_for_broadcast(&channel).ok())) }, ChannelError::CloseDelayBroadcast(_) => { panic!("Wait is only generated on receipt of channel_reestablish, which is handled by try_chan_entry, we don't bother to support it here"); } }; @@ -3455,7 +3547,7 @@ impl ChannelMana let ret_err = match res { Ok(Some((update_fee, commitment_signed, monitor_update))) => { if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) { - let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), Vec::new(), chan_id); + let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, chan_id, COMMITMENT_UPDATE_ONLY); if drop { retain_channel = false; } res } else { @@ -4191,13 +4283,24 @@ impl ChannelMana return Err(MsgHandleErrInternal::send_err_msg_no_close("No inbound channels accepted".to_owned(), msg.temporary_channel_id.clone())); } - let mut channel = Channel::new_from_req(&self.fee_estimator, &self.keys_manager, counterparty_node_id.clone(), - &their_features, msg, 0, &self.default_configuration, self.best_block.read().unwrap().height(), &self.logger) - .map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id))?; + let outbound_scid_alias = self.create_and_insert_outbound_scid_alias(); + let mut channel = match Channel::new_from_req(&self.fee_estimator, &self.keys_manager, + counterparty_node_id.clone(), &their_features, msg, 0, &self.default_configuration, + self.best_block.read().unwrap().height(), &self.logger, outbound_scid_alias) + { + Err(e) => { + self.outbound_scid_aliases.lock().unwrap().remove(&outbound_scid_alias); + return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)); + }, + Ok(res) => res + }; let mut channel_state_lock = self.channel_state.lock().unwrap(); let channel_state = &mut *channel_state_lock; match channel_state.by_id.entry(channel.channel_id()) { - hash_map::Entry::Occupied(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("temporary_channel_id collision!".to_owned(), msg.temporary_channel_id.clone())), + hash_map::Entry::Occupied(_) => { + self.outbound_scid_aliases.lock().unwrap().remove(&outbound_scid_alias); + return Err(MsgHandleErrInternal::send_err_msg_no_close("temporary_channel_id collision!".to_owned(), msg.temporary_channel_id.clone())) + }, hash_map::Entry::Vacant(entry) => { if !self.default_configuration.manually_accept_inbound_channels { channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel { @@ -4399,9 +4502,9 @@ impl ChannelMana if let Some(monitor_update) = monitor_update { if let Err(e) = self.chain_monitor.update_channel(chan_entry.get().get_funding_txo().unwrap(), monitor_update) { let (result, is_permanent) = - handle_monitor_err!(self, e, channel_state.short_to_id, chan_entry.get_mut(), RAACommitmentOrder::CommitmentFirst, false, false, Vec::new(), Vec::new(), Vec::new(), chan_entry.key()); + handle_monitor_err!(self, e, channel_state.short_to_id, chan_entry.get_mut(), RAACommitmentOrder::CommitmentFirst, chan_entry.key(), NO_UPDATE); if is_permanent { - remove_channel!(channel_state, chan_entry); + remove_channel!(self, channel_state, chan_entry); break result; } } @@ -4449,10 +4552,7 @@ impl ChannelMana // also implies there are no pending HTLCs left on the channel, so we can // fully delete it from tracking (the channel monitor is still around to // watch for old state broadcasts)! - if let Some(short_id) = chan_entry.get().get_short_channel_id() { - channel_state.short_to_id.remove(&short_id); - } - (tx, Some(chan_entry.remove_entry().1)) + (tx, Some(remove_channel!(self, channel_state, chan_entry))) } else { (tx, None) } }, hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id)) @@ -4890,12 +4990,9 @@ impl ChannelMana let mut channel_lock = self.channel_state.lock().unwrap(); let channel_state = &mut *channel_lock; let by_id = &mut channel_state.by_id; - let short_to_id = &mut channel_state.short_to_id; let pending_msg_events = &mut channel_state.pending_msg_events; - if let Some(mut chan) = by_id.remove(&funding_outpoint.to_channel_id()) { - if let Some(short_id) = chan.get_short_channel_id() { - short_to_id.remove(&short_id); - } + if let hash_map::Entry::Occupied(chan_entry) = by_id.entry(funding_outpoint.to_channel_id()) { + let mut chan = remove_channel!(self, channel_state, chan_entry); failed_channels.push(chan.force_shutdown(false)); if let Ok(update) = self.get_channel_update_for_broadcast(&chan) { pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate { @@ -4965,7 +5062,7 @@ impl ChannelMana if let Some((commitment_update, monitor_update)) = commitment_opt { if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) { has_monitor_update = true; - let (res, close_channel) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, true, Vec::new(), Vec::new(), Vec::new(), channel_id); + let (res, close_channel) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, channel_id, COMMITMENT_UPDATE_ONLY); handle_errors.push((chan.get_counterparty_node_id(), res)); if close_channel { return false; } } else { @@ -5024,10 +5121,6 @@ impl ChannelMana if let Some(tx) = tx_opt { // We're done with this channel. We got a closing_signed and sent back // a closing_signed with a closing transaction to broadcast. - if let Some(short_id) = chan.get_short_channel_id() { - short_to_id.remove(&short_id); - } - if let Ok(update) = self.get_channel_update_for_broadcast(&chan) { pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate { msg: update @@ -5038,6 +5131,7 @@ impl ChannelMana log_info!(self.logger, "Broadcasting {}", log_tx!(tx)); self.tx_broadcaster.broadcast_transaction(&tx); + update_maps_on_chan_removal!(self, short_to_id, chan); false } else { true } }, @@ -5234,7 +5328,7 @@ impl ChannelMana let mut channel_state = self.channel_state.lock().unwrap(); let best_block = self.best_block.read().unwrap(); loop { - let scid_candidate = fake_scid::get_phantom_scid(&self.fake_scid_rand_bytes, best_block.height(), &self.genesis_hash, &self.keys_manager); + let scid_candidate = fake_scid::Namespace::Phantom.get_fake_scid(best_block.height(), &self.genesis_hash, &self.fake_scid_rand_bytes, &self.keys_manager); // Ensure the generated scid doesn't conflict with a real channel. match channel_state.short_to_id.entry(scid_candidate) { hash_map::Entry::Occupied(_) => continue, @@ -5522,10 +5616,7 @@ where })); } if let Some(funding_locked) = funding_locked_opt { - pending_msg_events.push(events::MessageSendEvent::SendFundingLocked { - node_id: channel.get_counterparty_node_id(), - msg: funding_locked, - }); + send_funding_locked!(short_to_id, pending_msg_events, channel, funding_locked); if channel.is_usable() { log_trace!(self.logger, "Sending funding_locked with private initial channel_update for our counterparty on channel {}", log_bytes!(channel.channel_id())); pending_msg_events.push(events::MessageSendEvent::SendChannelUpdate { @@ -5535,7 +5626,6 @@ where } else { log_trace!(self.logger, "Sending funding_locked WITHOUT channel_update for {}", log_bytes!(channel.channel_id())); } - short_to_id.insert(channel.get_short_channel_id().unwrap(), channel.channel_id()); } if let Some(announcement_sigs) = announcement_sigs { log_trace!(self.logger, "Sending announcement_signatures for channel {}", log_bytes!(channel.channel_id())); @@ -5555,9 +5645,7 @@ where } } } else if let Err(reason) = res { - if let Some(short_id) = channel.get_short_channel_id() { - short_to_id.remove(&short_id); - } + update_maps_on_chan_removal!(self, short_to_id, channel); // It looks like our counterparty went on-chain or funding transaction was // reorged out of the main chain. Close the channel. failed_channels.push(channel.force_shutdown(true)); @@ -5747,15 +5835,13 @@ impl { let mut channel_state_lock = self.channel_state.lock().unwrap(); let channel_state = &mut *channel_state_lock; - let short_to_id = &mut channel_state.short_to_id; let pending_msg_events = &mut channel_state.pending_msg_events; + let short_to_id = &mut channel_state.short_to_id; if no_connection_possible { log_debug!(self.logger, "Failing all channels with {} due to no_connection_possible", log_pubkey!(counterparty_node_id)); channel_state.by_id.retain(|_, chan| { if chan.get_counterparty_node_id() == *counterparty_node_id { - if let Some(short_id) = chan.get_short_channel_id() { - short_to_id.remove(&short_id); - } + update_maps_on_chan_removal!(self, short_to_id, chan); failed_channels.push(chan.force_shutdown(true)); if let Ok(update) = self.get_channel_update_for_broadcast(&chan) { pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate { @@ -5774,9 +5860,7 @@ impl if chan.get_counterparty_node_id() == *counterparty_node_id { chan.remove_uncommitted_htlcs_and_mark_paused(&self.logger); if chan.is_shutdown() { - if let Some(short_id) = chan.get_short_channel_id() { - short_to_id.remove(&short_id); - } + update_maps_on_chan_removal!(self, short_to_id, chan); self.issue_channel_close_events(chan, ClosureReason::DisconnectedPeer); return false; } else { @@ -5966,6 +6050,7 @@ impl_writeable_tlv_based!(ChannelCounterparty, { }); impl_writeable_tlv_based!(ChannelDetails, { + (1, inbound_scid_alias, option), (2, channel_id, required), (4, counterparty, required), (6, funding_txo, option), @@ -6775,6 +6860,32 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> } } + let mut outbound_scid_aliases = HashSet::new(); + for (chan_id, chan) in by_id.iter_mut() { + if chan.outbound_scid_alias() == 0 { + let mut outbound_scid_alias; + loop { + outbound_scid_alias = fake_scid::Namespace::OutboundAlias + .get_fake_scid(best_block_height, &genesis_hash, fake_scid_rand_bytes.as_ref().unwrap(), &args.keys_manager); + if outbound_scid_aliases.insert(outbound_scid_alias) { break; } + } + chan.set_outbound_scid_alias(outbound_scid_alias); + } else if !outbound_scid_aliases.insert(chan.outbound_scid_alias()) { + // Note that in rare cases its possible to hit this while reading an older + // channel if we just happened to pick a colliding outbound alias above. + log_error!(args.logger, "Got duplicate outbound SCID alias; {}", chan.outbound_scid_alias()); + return Err(DecodeError::InvalidValue); + } + if chan.is_usable() { + if short_to_id.insert(chan.outbound_scid_alias(), *chan_id).is_some() { + // Note that in rare cases its possible to hit this while reading an older + // channel if we just happened to pick a colliding outbound alias above. + log_error!(args.logger, "Got duplicate outbound SCID alias; {}", chan.outbound_scid_alias()); + return Err(DecodeError::InvalidValue); + } + } + } + let inbound_pmt_key_material = args.keys_manager.get_inbound_payment_key_material(); let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material); let channel_manager = ChannelManager { @@ -6795,6 +6906,8 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> inbound_payment_key: expanded_inbound_key, pending_inbound_payments: Mutex::new(pending_inbound_payments), pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()), + + outbound_scid_aliases: Mutex::new(outbound_scid_aliases), fake_scid_rand_bytes: fake_scid_rand_bytes.unwrap(), our_network_key, diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index a908cd41658..7a3a269316e 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -693,6 +693,61 @@ pub fn create_announced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: &'a (chan_announcement.1, chan_announcement.2, chan_announcement.3, chan_announcement.4) } +pub fn create_unannounced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: &'a Vec>, a: usize, b: usize, channel_value: u64, push_msat: u64, a_flags: InitFeatures, b_flags: InitFeatures) -> (msgs::FundingLocked, Transaction) { + let mut no_announce_cfg = test_default_channel_config(); + no_announce_cfg.channel_options.announced_channel = false; + nodes[a].node.create_channel(nodes[b].node.get_our_node_id(), channel_value, push_msat, 42, Some(no_announce_cfg)).unwrap(); + let open_channel = get_event_msg!(nodes[a], MessageSendEvent::SendOpenChannel, nodes[b].node.get_our_node_id()); + nodes[b].node.handle_open_channel(&nodes[a].node.get_our_node_id(), a_flags, &open_channel); + let accept_channel = get_event_msg!(nodes[b], MessageSendEvent::SendAcceptChannel, nodes[a].node.get_our_node_id()); + nodes[a].node.handle_accept_channel(&nodes[b].node.get_our_node_id(), b_flags, &accept_channel); + + let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[a], channel_value, 42); + nodes[a].node.funding_transaction_generated(&temporary_channel_id, tx.clone()).unwrap(); + nodes[b].node.handle_funding_created(&nodes[a].node.get_our_node_id(), &get_event_msg!(nodes[a], MessageSendEvent::SendFundingCreated, nodes[b].node.get_our_node_id())); + check_added_monitors!(nodes[b], 1); + + let cs_funding_signed = get_event_msg!(nodes[b], MessageSendEvent::SendFundingSigned, nodes[a].node.get_our_node_id()); + nodes[a].node.handle_funding_signed(&nodes[b].node.get_our_node_id(), &cs_funding_signed); + check_added_monitors!(nodes[a], 1); + + let conf_height = core::cmp::max(nodes[a].best_block_info().1 + 1, nodes[b].best_block_info().1 + 1); + confirm_transaction_at(&nodes[a], &tx, conf_height); + connect_blocks(&nodes[a], CHAN_CONFIRM_DEPTH - 1); + confirm_transaction_at(&nodes[b], &tx, conf_height); + connect_blocks(&nodes[b], CHAN_CONFIRM_DEPTH - 1); + let as_funding_locked = get_event_msg!(nodes[a], MessageSendEvent::SendFundingLocked, nodes[b].node.get_our_node_id()); + nodes[a].node.handle_funding_locked(&nodes[b].node.get_our_node_id(), &get_event_msg!(nodes[b], MessageSendEvent::SendFundingLocked, nodes[a].node.get_our_node_id())); + let as_update = get_event_msg!(nodes[a], MessageSendEvent::SendChannelUpdate, nodes[b].node.get_our_node_id()); + nodes[b].node.handle_funding_locked(&nodes[a].node.get_our_node_id(), &as_funding_locked); + let bs_update = get_event_msg!(nodes[b], MessageSendEvent::SendChannelUpdate, nodes[a].node.get_our_node_id()); + + nodes[a].node.handle_channel_update(&nodes[b].node.get_our_node_id(), &bs_update); + nodes[b].node.handle_channel_update(&nodes[a].node.get_our_node_id(), &as_update); + + let mut found_a = false; + for chan in nodes[a].node.list_usable_channels() { + if chan.channel_id == as_funding_locked.channel_id { + assert!(!found_a); + found_a = true; + assert!(!chan.is_public); + } + } + assert!(found_a); + + let mut found_b = false; + for chan in nodes[b].node.list_usable_channels() { + if chan.channel_id == as_funding_locked.channel_id { + assert!(!found_b); + found_b = true; + assert!(!chan.is_public); + } + } + assert!(found_b); + + (as_funding_locked, tx) +} + pub fn update_nodes_with_chan_announce<'a, 'b, 'c, 'd>(nodes: &'a Vec>, a: usize, b: usize, ann: &msgs::ChannelAnnouncement, upd_1: &msgs::ChannelUpdate, upd_2: &msgs::ChannelUpdate) { nodes[a].node.broadcast_node_announcement([0, 0, 0], [0; 32], Vec::new()); let a_events = nodes[a].node.get_and_clear_pending_msg_events(); @@ -748,6 +803,11 @@ pub fn update_nodes_with_chan_announce<'a, 'b, 'c, 'd>(nodes: &'a Vec BREAKDOWN_TIMEOUT is enforced in Channel::new_outbound() - if let Err(error) = Channel::new_outbound(&&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &InitFeatures::known(), 1000000, 1000000, 0, &low_our_to_self_config, 0) { + if let Err(error) = Channel::new_outbound(&&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }, + &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &InitFeatures::known(), 1000000, 1000000, 0, + &low_our_to_self_config, 0, 42) + { match error { APIError::APIMisuseError { err } => { assert!(regex::Regex::new(r"Configured with an unreasonable our_to_self_delay \(\d+\) putting user funds at risks").unwrap().is_match(err.as_str())); }, _ => panic!("Unexpected event"), @@ -7273,7 +7194,10 @@ fn test_user_configurable_csv_delay() { nodes[1].node.create_channel(nodes[0].node.get_our_node_id(), 1000000, 1000000, 42, None).unwrap(); let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id()); open_channel.to_self_delay = 200; - if let Err(error) = Channel::new_from_req(&&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &InitFeatures::known(), &open_channel, 0, &low_our_to_self_config, 0, &nodes[0].logger) { + if let Err(error) = Channel::new_from_req(&&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }, + &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &InitFeatures::known(), &open_channel, 0, + &low_our_to_self_config, 0, &nodes[0].logger, 42) + { match error { ChannelError::Close(err) => { assert!(regex::Regex::new(r"Configured with an unreasonable our_to_self_delay \(\d+\) putting user funds at risks").unwrap().is_match(err.as_str())); }, _ => panic!("Unexpected event"), @@ -7302,7 +7226,10 @@ fn test_user_configurable_csv_delay() { nodes[1].node.create_channel(nodes[0].node.get_our_node_id(), 1000000, 1000000, 42, None).unwrap(); let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id()); open_channel.to_self_delay = 200; - if let Err(error) = Channel::new_from_req(&&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &InitFeatures::known(), &open_channel, 0, &high_their_to_self_config, 0, &nodes[0].logger) { + if let Err(error) = Channel::new_from_req(&&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }, + &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &InitFeatures::known(), &open_channel, 0, + &high_their_to_self_config, 0, &nodes[0].logger, 42) + { match error { ChannelError::Close(err) => { assert!(regex::Regex::new(r"They wanted our payments to be delayed by a needlessly long period\. Upper limit: \d+\. Actual: \d+").unwrap().is_match(err.as_str())); }, _ => panic!("Unexpected event"), @@ -7569,162 +7496,6 @@ fn test_announce_disable_channels() { assert!(chans_disabled.is_empty()); } -#[test] -fn test_priv_forwarding_rejection() { - // If we have a private channel with outbound liquidity, and - // UserConfig::accept_forwards_to_priv_channels is set to false, we should reject any attempts - // to forward through that channel. - let chanmon_cfgs = create_chanmon_cfgs(3); - let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); - let mut no_announce_cfg = test_default_channel_config(); - no_announce_cfg.channel_options.announced_channel = false; - no_announce_cfg.accept_forwards_to_priv_channels = false; - let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(no_announce_cfg), None]); - let persister: test_utils::TestPersister; - let new_chain_monitor: test_utils::TestChainMonitor; - let nodes_1_deserialized: ChannelManager; - let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs); - - let chan_id_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 500_000_000, InitFeatures::known(), InitFeatures::known()).2; - - // Note that the create_*_chan functions in utils requires announcement_signatures, which we do - // not send for private channels. - nodes[1].node.create_channel(nodes[2].node.get_our_node_id(), 1_000_000, 500_000_000, 42, None).unwrap(); - let open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[2].node.get_our_node_id()); - nodes[2].node.handle_open_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &open_channel); - let accept_channel = get_event_msg!(nodes[2], MessageSendEvent::SendAcceptChannel, nodes[1].node.get_our_node_id()); - nodes[1].node.handle_accept_channel(&nodes[2].node.get_our_node_id(), InitFeatures::known(), &accept_channel); - - let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[1], 1_000_000, 42); - nodes[1].node.funding_transaction_generated(&temporary_channel_id, tx.clone()).unwrap(); - nodes[2].node.handle_funding_created(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendFundingCreated, nodes[2].node.get_our_node_id())); - check_added_monitors!(nodes[2], 1); - - let cs_funding_signed = get_event_msg!(nodes[2], MessageSendEvent::SendFundingSigned, nodes[1].node.get_our_node_id()); - nodes[1].node.handle_funding_signed(&nodes[2].node.get_our_node_id(), &cs_funding_signed); - check_added_monitors!(nodes[1], 1); - - let conf_height = core::cmp::max(nodes[1].best_block_info().1 + 1, nodes[2].best_block_info().1 + 1); - confirm_transaction_at(&nodes[1], &tx, conf_height); - connect_blocks(&nodes[1], CHAN_CONFIRM_DEPTH - 1); - confirm_transaction_at(&nodes[2], &tx, conf_height); - connect_blocks(&nodes[2], CHAN_CONFIRM_DEPTH - 1); - let as_funding_locked = get_event_msg!(nodes[1], MessageSendEvent::SendFundingLocked, nodes[2].node.get_our_node_id()); - nodes[1].node.handle_funding_locked(&nodes[2].node.get_our_node_id(), &get_event_msg!(nodes[2], MessageSendEvent::SendFundingLocked, nodes[1].node.get_our_node_id())); - get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[2].node.get_our_node_id()); - nodes[2].node.handle_funding_locked(&nodes[1].node.get_our_node_id(), &as_funding_locked); - get_event_msg!(nodes[2], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id()); - - assert!(nodes[0].node.list_usable_channels()[0].is_public); - assert_eq!(nodes[1].node.list_usable_channels().len(), 2); - assert!(!nodes[2].node.list_usable_channels()[0].is_public); - - // We should always be able to forward through nodes[1] as long as its out through a public - // channel: - send_payment(&nodes[2], &[&nodes[1], &nodes[0]], 10_000); - - // ... however, if we send to nodes[2], we will have to pass the private channel from nodes[1] - // to nodes[2], which should be rejected: - let route_hint = RouteHint(vec![RouteHintHop { - src_node_id: nodes[1].node.get_our_node_id(), - short_channel_id: nodes[2].node.list_channels()[0].short_channel_id.unwrap(), - fees: RoutingFees { base_msat: 1000, proportional_millionths: 0 }, - cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA, - htlc_minimum_msat: None, - htlc_maximum_msat: None, - }]); - let last_hops = vec![route_hint]; - let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], last_hops, 10_000, TEST_FINAL_CLTV); - - nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap(); - check_added_monitors!(nodes[0], 1); - let payment_event = SendEvent::from_event(nodes[0].node.get_and_clear_pending_msg_events().remove(0)); - nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]); - commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false, true); - - let htlc_fail_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id()); - assert!(htlc_fail_updates.update_add_htlcs.is_empty()); - assert_eq!(htlc_fail_updates.update_fail_htlcs.len(), 1); - assert!(htlc_fail_updates.update_fail_malformed_htlcs.is_empty()); - assert!(htlc_fail_updates.update_fee.is_none()); - - nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_fail_updates.update_fail_htlcs[0]); - commitment_signed_dance!(nodes[0], nodes[1], htlc_fail_updates.commitment_signed, true, true); - expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, nodes[2].node.list_channels()[0].short_channel_id.unwrap(), true); - - // Now disconnect nodes[1] from its peers and restart with accept_forwards_to_priv_channels set - // to true. Sadly there is currently no way to change it at runtime. - - nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false); - nodes[2].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false); - - let nodes_1_serialized = nodes[1].node.encode(); - let mut monitor_a_serialized = test_utils::TestVecWriter(Vec::new()); - let mut monitor_b_serialized = test_utils::TestVecWriter(Vec::new()); - get_monitor!(nodes[1], chan_id_1).write(&mut monitor_a_serialized).unwrap(); - get_monitor!(nodes[1], cs_funding_signed.channel_id).write(&mut monitor_b_serialized).unwrap(); - - persister = test_utils::TestPersister::new(); - let keys_manager = &chanmon_cfgs[1].keys_manager; - new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[1].chain_source), nodes[1].tx_broadcaster.clone(), nodes[1].logger, node_cfgs[1].fee_estimator, &persister, keys_manager); - nodes[1].chain_monitor = &new_chain_monitor; - - let mut monitor_a_read = &monitor_a_serialized.0[..]; - let mut monitor_b_read = &monitor_b_serialized.0[..]; - let (_, mut monitor_a) = <(BlockHash, ChannelMonitor)>::read(&mut monitor_a_read, keys_manager).unwrap(); - let (_, mut monitor_b) = <(BlockHash, ChannelMonitor)>::read(&mut monitor_b_read, keys_manager).unwrap(); - assert!(monitor_a_read.is_empty()); - assert!(monitor_b_read.is_empty()); - - no_announce_cfg.accept_forwards_to_priv_channels = true; - - let mut nodes_1_read = &nodes_1_serialized[..]; - let (_, nodes_1_deserialized_tmp) = { - let mut channel_monitors = HashMap::new(); - channel_monitors.insert(monitor_a.get_funding_txo().0, &mut monitor_a); - channel_monitors.insert(monitor_b.get_funding_txo().0, &mut monitor_b); - <(BlockHash, ChannelManager)>::read(&mut nodes_1_read, ChannelManagerReadArgs { - default_config: no_announce_cfg, - keys_manager, - fee_estimator: node_cfgs[1].fee_estimator, - chain_monitor: nodes[1].chain_monitor, - tx_broadcaster: nodes[1].tx_broadcaster.clone(), - logger: nodes[1].logger, - channel_monitors, - }).unwrap() - }; - assert!(nodes_1_read.is_empty()); - nodes_1_deserialized = nodes_1_deserialized_tmp; - - assert!(nodes[1].chain_monitor.watch_channel(monitor_a.get_funding_txo().0, monitor_a).is_ok()); - assert!(nodes[1].chain_monitor.watch_channel(monitor_b.get_funding_txo().0, monitor_b).is_ok()); - check_added_monitors!(nodes[1], 2); - nodes[1].node = &nodes_1_deserialized; - - nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known() }); - nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() }); - let as_reestablish = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id()); - let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id()); - nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &as_reestablish); - nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish); - get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id()); - get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[0].node.get_our_node_id()); - - nodes[1].node.peer_connected(&nodes[2].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known() }); - nodes[2].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() }); - let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[2].node.get_our_node_id()); - let cs_reestablish = get_event_msg!(nodes[2], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id()); - nodes[2].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish); - nodes[1].node.handle_channel_reestablish(&nodes[2].node.get_our_node_id(), &cs_reestablish); - get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[2].node.get_our_node_id()); - get_event_msg!(nodes[2], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id()); - - nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap(); - check_added_monitors!(nodes[0], 1); - pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], 10_000, our_payment_hash, our_payment_secret); - claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], our_payment_preimage); -} - #[test] fn test_bump_penalty_txn_on_revoked_commitment() { // In case of penalty txn with too low feerates for getting into mempools, RBF-bump them to be sure diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index 44704ae5040..444c9685fdd 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -54,6 +54,9 @@ mod functional_tests; mod payment_tests; #[cfg(test)] #[allow(unused_mut)] +mod priv_short_conf_tests; +#[cfg(test)] +#[allow(unused_mut)] mod chanmon_update_fail_tests; #[cfg(test)] #[allow(unused_mut)] diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 7295c40d544..fab396e3c71 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -241,6 +241,9 @@ pub struct FundingLocked { pub channel_id: [u8; 32], /// The per-commitment point of the second commitment transaction pub next_per_commitment_point: PublicKey, + /// If set, provides a short_channel_id alias for this channel. The sender will accept payments + /// to be forwarded over this SCID and forward them to this messages' recipient. + pub short_channel_id_alias: Option, } /// A shutdown message to be sent or received from a peer @@ -1155,7 +1158,9 @@ impl_writeable_msg!(FundingSigned, { impl_writeable_msg!(FundingLocked, { channel_id, next_per_commitment_point, -}, {}); +}, { + (1, short_channel_id_alias, option), +}); impl Writeable for Init { fn write(&self, w: &mut W) -> Result<(), io::Error> { @@ -2246,6 +2251,7 @@ mod tests { let funding_locked = msgs::FundingLocked { channel_id: [2; 32], next_per_commitment_point: pubkey_1, + short_channel_id_alias: None, }; let encoded_value = funding_locked.encode(); let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap(); diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index d67abeefdad..af82a275d9a 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -319,10 +319,10 @@ fn test_onion_failure() { let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(config), Some(config), Some(node_2_cfg)]); let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs); + let channels = [create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()), create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known())]; for node in nodes.iter() { *node.keys_manager.override_random_bytes.lock().unwrap() = Some([3; 32]); } - let channels = [create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()), create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known())]; let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 40000); // positive case send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 40000); diff --git a/lightning/src/ln/priv_short_conf_tests.rs b/lightning/src/ln/priv_short_conf_tests.rs new file mode 100644 index 00000000000..a6f5a1b2ef7 --- /dev/null +++ b/lightning/src/ln/priv_short_conf_tests.rs @@ -0,0 +1,286 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! Tests that test ChannelManager behavior with fewer confirmations required than the default and +//! other behavior that exists only on private channels or with a semi-trusted counterparty (eg +//! LSP). + +use chain::Watch; +use chain::channelmonitor::ChannelMonitor; +use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, MIN_CLTV_EXPIRY_DELTA}; +use routing::network_graph::RoutingFees; +use routing::router::{RouteHint, RouteHintHop}; +use ln::features::InitFeatures; +use ln::msgs; +use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler}; +use util::enforcing_trait_impls::EnforcingSigner; +use util::events::{Event, MessageSendEvent, MessageSendEventsProvider}; +use util::config::UserConfig; +use util::ser::{Writeable, ReadableArgs}; +use util::test_utils; + +use prelude::*; +use core::default::Default; + +use ln::functional_test_utils::*; + +use bitcoin::hash_types::BlockHash; + +#[test] +fn test_priv_forwarding_rejection() { + // If we have a private channel with outbound liquidity, and + // UserConfig::accept_forwards_to_priv_channels is set to false, we should reject any attempts + // to forward through that channel. + let chanmon_cfgs = create_chanmon_cfgs(3); + let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); + let mut no_announce_cfg = test_default_channel_config(); + no_announce_cfg.accept_forwards_to_priv_channels = false; + let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(no_announce_cfg), None]); + let persister: test_utils::TestPersister; + let new_chain_monitor: test_utils::TestChainMonitor; + let nodes_1_deserialized: ChannelManager; + let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs); + + let chan_id_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 500_000_000, InitFeatures::known(), InitFeatures::known()).2; + let chan_id_2 = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 500_000_000, InitFeatures::known(), InitFeatures::known()).0.channel_id; + + // We should always be able to forward through nodes[1] as long as its out through a public + // channel: + send_payment(&nodes[2], &[&nodes[1], &nodes[0]], 10_000); + + // ... however, if we send to nodes[2], we will have to pass the private channel from nodes[1] + // to nodes[2], which should be rejected: + let route_hint = RouteHint(vec![RouteHintHop { + src_node_id: nodes[1].node.get_our_node_id(), + short_channel_id: nodes[2].node.list_channels()[0].short_channel_id.unwrap(), + fees: RoutingFees { base_msat: 1000, proportional_millionths: 0 }, + cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA, + htlc_minimum_msat: None, + htlc_maximum_msat: None, + }]); + let last_hops = vec![route_hint]; + let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], last_hops, 10_000, TEST_FINAL_CLTV); + + nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap(); + check_added_monitors!(nodes[0], 1); + let payment_event = SendEvent::from_event(nodes[0].node.get_and_clear_pending_msg_events().remove(0)); + nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]); + commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false, true); + + let htlc_fail_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id()); + assert!(htlc_fail_updates.update_add_htlcs.is_empty()); + assert_eq!(htlc_fail_updates.update_fail_htlcs.len(), 1); + assert!(htlc_fail_updates.update_fail_malformed_htlcs.is_empty()); + assert!(htlc_fail_updates.update_fee.is_none()); + + nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_fail_updates.update_fail_htlcs[0]); + commitment_signed_dance!(nodes[0], nodes[1], htlc_fail_updates.commitment_signed, true, true); + expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, nodes[2].node.list_channels()[0].short_channel_id.unwrap(), true); + + // Now disconnect nodes[1] from its peers and restart with accept_forwards_to_priv_channels set + // to true. Sadly there is currently no way to change it at runtime. + + nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false); + nodes[2].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false); + + let nodes_1_serialized = nodes[1].node.encode(); + let mut monitor_a_serialized = test_utils::TestVecWriter(Vec::new()); + let mut monitor_b_serialized = test_utils::TestVecWriter(Vec::new()); + get_monitor!(nodes[1], chan_id_1).write(&mut monitor_a_serialized).unwrap(); + get_monitor!(nodes[1], chan_id_2).write(&mut monitor_b_serialized).unwrap(); + + persister = test_utils::TestPersister::new(); + let keys_manager = &chanmon_cfgs[1].keys_manager; + new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[1].chain_source), nodes[1].tx_broadcaster.clone(), nodes[1].logger, node_cfgs[1].fee_estimator, &persister, keys_manager); + nodes[1].chain_monitor = &new_chain_monitor; + + let mut monitor_a_read = &monitor_a_serialized.0[..]; + let mut monitor_b_read = &monitor_b_serialized.0[..]; + let (_, mut monitor_a) = <(BlockHash, ChannelMonitor)>::read(&mut monitor_a_read, keys_manager).unwrap(); + let (_, mut monitor_b) = <(BlockHash, ChannelMonitor)>::read(&mut monitor_b_read, keys_manager).unwrap(); + assert!(monitor_a_read.is_empty()); + assert!(monitor_b_read.is_empty()); + + no_announce_cfg.accept_forwards_to_priv_channels = true; + + let mut nodes_1_read = &nodes_1_serialized[..]; + let (_, nodes_1_deserialized_tmp) = { + let mut channel_monitors = HashMap::new(); + channel_monitors.insert(monitor_a.get_funding_txo().0, &mut monitor_a); + channel_monitors.insert(monitor_b.get_funding_txo().0, &mut monitor_b); + <(BlockHash, ChannelManager)>::read(&mut nodes_1_read, ChannelManagerReadArgs { + default_config: no_announce_cfg, + keys_manager, + fee_estimator: node_cfgs[1].fee_estimator, + chain_monitor: nodes[1].chain_monitor, + tx_broadcaster: nodes[1].tx_broadcaster.clone(), + logger: nodes[1].logger, + channel_monitors, + }).unwrap() + }; + assert!(nodes_1_read.is_empty()); + nodes_1_deserialized = nodes_1_deserialized_tmp; + + assert!(nodes[1].chain_monitor.watch_channel(monitor_a.get_funding_txo().0, monitor_a).is_ok()); + assert!(nodes[1].chain_monitor.watch_channel(monitor_b.get_funding_txo().0, monitor_b).is_ok()); + check_added_monitors!(nodes[1], 2); + nodes[1].node = &nodes_1_deserialized; + + nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known() }); + nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() }); + let as_reestablish = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id()); + let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id()); + nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &as_reestablish); + nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish); + get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id()); + get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[0].node.get_our_node_id()); + + nodes[1].node.peer_connected(&nodes[2].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known() }); + nodes[2].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() }); + let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[2].node.get_our_node_id()); + let cs_reestablish = get_event_msg!(nodes[2], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id()); + nodes[2].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish); + nodes[1].node.handle_channel_reestablish(&nodes[2].node.get_our_node_id(), &cs_reestablish); + get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[2].node.get_our_node_id()); + get_event_msg!(nodes[2], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id()); + + nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap(); + check_added_monitors!(nodes[0], 1); + pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], 10_000, our_payment_hash, our_payment_secret); + claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], our_payment_preimage); +} + +fn do_test_1_conf_open(connect_style: ConnectStyle) { + // Previously, if the minium_depth config was set to 1, we'd never send a funding_locked. This + // tests that we properly send one in that case. + let mut alice_config = UserConfig::default(); + alice_config.own_channel_config.minimum_depth = 1; + alice_config.channel_options.announced_channel = true; + alice_config.peer_channel_config_limits.force_announced_channel_preference = false; + let mut bob_config = UserConfig::default(); + bob_config.own_channel_config.minimum_depth = 1; + bob_config.channel_options.announced_channel = true; + bob_config.peer_channel_config_limits.force_announced_channel_preference = false; + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(alice_config), Some(bob_config)]); + let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); + *nodes[0].connect_style.borrow_mut() = connect_style; + + let tx = create_chan_between_nodes_with_value_init(&nodes[0], &nodes[1], 100000, 10001, InitFeatures::known(), InitFeatures::known()); + mine_transaction(&nodes[1], &tx); + nodes[0].node.handle_funding_locked(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendFundingLocked, nodes[0].node.get_our_node_id())); + assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); + + mine_transaction(&nodes[0], &tx); + let as_msg_events = nodes[0].node.get_and_clear_pending_msg_events(); + assert_eq!(as_msg_events.len(), 2); + let as_funding_locked = if let MessageSendEvent::SendFundingLocked { ref node_id, ref msg } = as_msg_events[0] { + assert_eq!(*node_id, nodes[1].node.get_our_node_id()); + msg.clone() + } else { panic!("Unexpected event"); }; + if let MessageSendEvent::SendChannelUpdate { ref node_id, msg: _ } = as_msg_events[1] { + assert_eq!(*node_id, nodes[1].node.get_our_node_id()); + } else { panic!("Unexpected event"); } + + nodes[1].node.handle_funding_locked(&nodes[0].node.get_our_node_id(), &as_funding_locked); + let bs_msg_events = nodes[1].node.get_and_clear_pending_msg_events(); + assert_eq!(bs_msg_events.len(), 1); + if let MessageSendEvent::SendChannelUpdate { ref node_id, msg: _ } = bs_msg_events[0] { + assert_eq!(*node_id, nodes[0].node.get_our_node_id()); + } else { panic!("Unexpected event"); } + + send_payment(&nodes[0], &[&nodes[1]], 100_000); + + // After 6 confirmations, as required by the spec, we'll send announcement_signatures and + // broadcast the channel_announcement (but not before exactly 6 confirmations). + connect_blocks(&nodes[0], 4); + assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty()); + connect_blocks(&nodes[0], 1); + nodes[1].node.handle_announcement_signatures(&nodes[0].node.get_our_node_id(), &get_event_msg!(nodes[0], MessageSendEvent::SendAnnouncementSignatures, nodes[1].node.get_our_node_id())); + assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty()); + + connect_blocks(&nodes[1], 5); + let bs_announce_events = nodes[1].node.get_and_clear_pending_msg_events(); + assert_eq!(bs_announce_events.len(), 2); + let bs_announcement_sigs = if let MessageSendEvent::SendAnnouncementSignatures { ref node_id, ref msg } = bs_announce_events[0] { + assert_eq!(*node_id, nodes[0].node.get_our_node_id()); + msg.clone() + } else { panic!("Unexpected event"); }; + let (bs_announcement, bs_update) = if let MessageSendEvent::BroadcastChannelAnnouncement { ref msg, ref update_msg } = bs_announce_events[1] { + (msg.clone(), update_msg.clone()) + } else { panic!("Unexpected event"); }; + + nodes[0].node.handle_announcement_signatures(&nodes[1].node.get_our_node_id(), &bs_announcement_sigs); + let as_announce_events = nodes[0].node.get_and_clear_pending_msg_events(); + assert_eq!(as_announce_events.len(), 1); + let (announcement, as_update) = if let MessageSendEvent::BroadcastChannelAnnouncement { ref msg, ref update_msg } = as_announce_events[0] { + (msg.clone(), update_msg.clone()) + } else { panic!("Unexpected event"); }; + assert_eq!(announcement, bs_announcement); + + for node in nodes { + assert!(node.net_graph_msg_handler.handle_channel_announcement(&announcement).unwrap()); + node.net_graph_msg_handler.handle_channel_update(&as_update).unwrap(); + node.net_graph_msg_handler.handle_channel_update(&bs_update).unwrap(); + } +} +#[test] +fn test_1_conf_open() { + do_test_1_conf_open(ConnectStyle::BestBlockFirst); + do_test_1_conf_open(ConnectStyle::TransactionsFirst); + do_test_1_conf_open(ConnectStyle::FullBlockViaListen); +} + +#[test] +fn test_routed_scid_alias() { + // Trivially test sending a payment which is routed through an SCID alias. + let chanmon_cfgs = create_chanmon_cfgs(3); + let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); + let mut no_announce_cfg = test_default_channel_config(); + no_announce_cfg.accept_forwards_to_priv_channels = true; + let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(no_announce_cfg), None]); + let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs); + + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 500_000_000, InitFeatures::known(), InitFeatures::known()).2; + let mut as_funding_locked = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 500_000_000, InitFeatures::known(), InitFeatures::known()).0; + + let last_hop = nodes[2].node.list_usable_channels(); + let hop_hints = vec![RouteHint(vec![RouteHintHop { + src_node_id: nodes[1].node.get_our_node_id(), + short_channel_id: last_hop[0].inbound_scid_alias.unwrap(), + fees: RoutingFees { + base_msat: last_hop[0].counterparty.forwarding_info.as_ref().unwrap().fee_base_msat, + proportional_millionths: last_hop[0].counterparty.forwarding_info.as_ref().unwrap().fee_proportional_millionths, + }, + cltv_expiry_delta: last_hop[0].counterparty.forwarding_info.as_ref().unwrap().cltv_expiry_delta, + htlc_maximum_msat: None, + htlc_minimum_msat: None, + }])]; + let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], hop_hints, 100_000, 42); + assert_eq!(route.paths[0][1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap()); + nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap(); + check_added_monitors!(nodes[0], 1); + + pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], 100_000, payment_hash, payment_secret); + claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage); + + // Now test that if a peer sends us a second funding_locked after the channel is operational we + // will use the new alias. + as_funding_locked.short_channel_id_alias = Some(0xdeadbeef); + nodes[2].node.handle_funding_locked(&nodes[1].node.get_our_node_id(), &as_funding_locked); + // Note that we always respond to a funding_locked with a channel_update. Not a lot of reason + // to bother updating that code, so just drop the message here. + get_event_msg!(nodes[2], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id()); + let updated_channel_info = nodes[2].node.list_usable_channels(); + assert_eq!(updated_channel_info.len(), 1); + assert_eq!(updated_channel_info[0].inbound_scid_alias.unwrap(), 0xdeadbeef); + // Note that because we never send a duplicate funding_locked we can't send a payment through + // the 0xdeadbeef SCID alias. +} diff --git a/lightning/src/ln/reorg_tests.rs b/lightning/src/ln/reorg_tests.rs index 6c39efb87b5..8eb39cfe92a 100644 --- a/lightning/src/ln/reorg_tests.rs +++ b/lightning/src/ln/reorg_tests.rs @@ -201,7 +201,7 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_ let channel_state = nodes[0].node.channel_state.lock().unwrap(); assert_eq!(channel_state.by_id.len(), 1); - assert_eq!(channel_state.short_to_id.len(), 1); + assert_eq!(channel_state.short_to_id.len(), 2); mem::drop(channel_state); if !reorg_after_reload { diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index c45ce59ac13..870b1e1261f 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -1578,6 +1578,7 @@ mod tests { }, funding_txo: Some(OutPoint { txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), index: 0 }), short_channel_id, + inbound_scid_alias: None, channel_value_satoshis: 0, user_channel_id: 0, balance_msat: 0, @@ -5101,6 +5102,7 @@ mod benches { txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), index: 0 }), short_channel_id: Some(1), + inbound_scid_alias: None, channel_value_satoshis: 10_000_000, user_channel_id: 0, balance_msat: 10_000_000, diff --git a/lightning/src/util/scid_utils.rs b/lightning/src/util/scid_utils.rs index 11b8d0c95e0..8552358c35a 100644 --- a/lightning/src/util/scid_utils.rs +++ b/lightning/src/util/scid_utils.rs @@ -61,7 +61,7 @@ pub fn scid_from_parts(block: u64, tx_index: u64, vout_index: u64) -> Result(&self, highest_seen_blockheight: u32, genesis_hash: &BlockHash, fake_scid_rand_bytes: &[u8; 32], keys_manager: &K) -> u64 + pub(crate) fn get_fake_scid(&self, highest_seen_blockheight: u32, genesis_hash: &BlockHash, fake_scid_rand_bytes: &[u8; 32], keys_manager: &K) -> u64 where K::Target: KeysInterface, { // Ensure we haven't created a namespace that doesn't fit into the 3 bits we've allocated for @@ -138,13 +138,6 @@ pub(crate) mod fake_scid { } } - pub fn get_phantom_scid(fake_scid_rand_bytes: &[u8; 32], highest_seen_blockheight: u32, genesis_hash: &BlockHash, keys_manager: &K) -> u64 - where K::Target: KeysInterface, - { - let namespace = Namespace::Phantom; - namespace.get_fake_scid(highest_seen_blockheight, genesis_hash, fake_scid_rand_bytes, keys_manager) - } - fn segwit_activation_height(genesis: &BlockHash) -> u32 { const MAINNET_GENESIS_STR: &'static str = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"; if BlockHash::from_hex(MAINNET_GENESIS_STR).unwrap() == *genesis {