},
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,
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,
};
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,
};
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());
/// 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))]
workaround_lnd_bug_4006: None,
+ latest_inbound_scid_alias: None,
+
#[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills: HashSet::new(),
workaround_lnd_bug_4006: None,
+ latest_inbound_scid_alias: None,
+
#[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills: HashSet::new(),
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 {
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> {
(13, self.channel_creation_height, required),
(15, preimages, vec_type),
(17, self.announcement_sigs_state, required),
+ (19, self.latest_inbound_scid_alias, option),
});
Ok(())
// 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),
(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 {
workaround_lnd_bug_4006: None,
+ latest_inbound_scid_alias,
+
#[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills,
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
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.
},
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,
});
impl_writeable_tlv_based!(ChannelDetails, {
+ (1, inbound_scid_alias, option),
(2, channel_id, required),
(4, counterparty, required),
(6, funding_txo, option),
},
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,
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,