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.
This commit is contained in:
Matt Corallo 2022-02-01 17:37:16 +00:00
parent f54ebf78f6
commit b2629afd88
5 changed files with 66 additions and 2 deletions

View file

@ -216,6 +216,7 @@ pub fn do_test<Out: test_logger::Output>(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,

View file

@ -65,7 +65,7 @@ pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
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());

View file

@ -695,6 +695,13 @@ pub(super) struct Channel<Signer: Sign> {
/// 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<u64>,
}
#[cfg(any(test, fuzzing))]
@ -947,6 +954,8 @@ impl<Signer: Sign> Channel<Signer> {
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<Signer: Sign> Channel<Signer> {
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<Signer: Sign> Channel<Signer> {
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<Signer: Sign> Channel<Signer> {
self.short_channel_id
}
/// Allowed in any state (including after shutdown)
pub fn latest_inbound_scid_alias(&self) -> Option<u64> {
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<OutPoint> {
@ -5769,6 +5794,7 @@ impl<Signer: Sign> Writeable for Channel<Signer> {
(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<Signer>
// 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<Signer>
(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<Signer>
workaround_lnd_bug_4006: None,
latest_inbound_scid_alias,
#[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills,

View file

@ -1188,7 +1188,20 @@ pub struct ChannelDetails {
pub funding_txo: Option<OutPoint>,
/// 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<u64>,
/// 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<u64>,
/// 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<u64> {
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<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> 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),

View file

@ -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,