From b2629afd88b0874ca1ede986a9c789eea20c6be8 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 1 Feb 2022 17:37:16 +0000 Subject: [PATCH] Track SCID aliases from our counterparty and use them in invoices New `funding_locked` messages can include SCID aliases which our counterparty will recognize as "ours" for the purposes of relaying transactions to us. This avoids telling the world about our on-chain transactions every time we want to receive a payment, and will allow for receiving payments before the funding transaction appears on-chain. Here we store the new SCID aliases and use them in invoices instead of he "standard" SCIDs. --- fuzz/src/router.rs | 1 + lightning-invoice/src/utils.rs | 11 +++++++++-- lightning/src/ln/channel.rs | 30 ++++++++++++++++++++++++++++++ lightning/src/ln/channelmanager.rs | 24 ++++++++++++++++++++++++ lightning/src/routing/router.rs | 2 ++ 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index 095059c51..20ca39944 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 fc82541a5..73f013882 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 9935d3c01..99fb345be 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -695,6 +695,13 @@ 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, } #[cfg(any(test, fuzzing))] @@ -947,6 +954,8 @@ impl Channel { workaround_lnd_bug_4006: None, + latest_inbound_scid_alias: None, + #[cfg(any(test, fuzzing))] historical_inbound_htlc_fulfills: HashSet::new(), @@ -1252,6 +1261,8 @@ impl Channel { workaround_lnd_bug_4006: None, + latest_inbound_scid_alias: None, + #[cfg(any(test, fuzzing))] historical_inbound_htlc_fulfills: HashSet::new(), @@ -2151,6 +2162,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 { @@ -4198,6 +4218,11 @@ 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 + } + /// 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 { @@ -5769,6 +5794,7 @@ 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), }); Ok(()) @@ -6024,6 +6050,7 @@ 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; read_tlv_fields!(reader, { (0, announcement_sigs, option), @@ -6039,6 +6066,7 @@ 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), }); if let Some(preimages) = preimages_opt { @@ -6173,6 +6201,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel workaround_lnd_bug_4006: None, + latest_inbound_scid_alias, + #[cfg(any(test, fuzzing))] historical_inbound_htlc_fulfills, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 6d067c24a..46bad1f56 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -1188,7 +1188,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 +1287,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. @@ -1833,6 +1855,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, @@ -5973,6 +5996,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), diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index c45ce59ac..870b1e126 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, -- 2.39.5