cargo test --verbose --color always --no-default-features --features no-std
# check if there is a conflict between no-std and the default std feature
cargo test --verbose --color always --features no-std
+ # check that things still pass without grind_signatures
+ # note that outbound_commitment_test only runs in this mode, because of hardcoded signature values
+ cargo test --verbose --color always --no-default-features --features std
# check if there is a conflict between no-std and the c_bindings cfg
RUSTFLAGS="--cfg=c_bindings" cargo test --verbose --color always --no-default-features --features=no-std
cd ..
profile: minimal
- name: Cache routing graph snapshot
id: cache-graph
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: lightning/net_graph-2021-05-31.bin
key: ldk-net_graph-v0.0.15-2021-05-31.bin
check_commits:
runs-on: ubuntu-latest
env:
- # rustc 1.53 regressed and panics when building our (perfectly fine) docs.
- # See https://github.com/rust-lang/rust/issues/84738
- TOOLCHAIN: 1.52.1
+ TOOLCHAIN: 1.57.0
steps:
- name: Checkout source code
uses: actions/checkout@v3
let mut channel_txn = Vec::new();
macro_rules! make_channel {
($source: expr, $dest: expr, $chan_id: expr) => { {
- $source.peer_connected(&$dest.get_our_node_id(), &Init { features: InitFeatures::known() });
- $dest.peer_connected(&$source.get_our_node_id(), &Init { features: InitFeatures::known() });
+ $source.peer_connected(&$dest.get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
+ $dest.peer_connected(&$source.get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
$source.create_channel($dest.get_our_node_id(), 100_000, 42, 0, None).unwrap();
let open_channel = {
},
0x0e => {
if chan_a_disconnected {
- nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known() });
- nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: InitFeatures::known() });
+ nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
chan_a_disconnected = false;
}
},
0x0f => {
if chan_b_disconnected {
- nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: InitFeatures::known() });
- nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known() });
+ nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
chan_b_disconnected = false;
}
},
// Next, make sure peers are all connected to each other
if chan_a_disconnected {
- nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known() });
- nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: InitFeatures::known() });
+ nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
chan_a_disconnected = false;
}
if chan_b_disconnected {
- nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: InitFeatures::known() });
- nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known() });
+ nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
chan_b_disconnected = false;
}
}
}
if new_id == 0 { return; }
- loss_detector.handler.new_outbound_connection(get_pubkey!(), Peer{id: (new_id - 1) as u8, peers_connected: &peers}).unwrap();
+ loss_detector.handler.new_outbound_connection(get_pubkey!(), Peer{id: (new_id - 1) as u8, peers_connected: &peers}, None).unwrap();
peers.borrow_mut()[new_id - 1] = true;
},
1 => {
}
}
if new_id == 0 { return; }
- loss_detector.handler.new_inbound_connection(Peer{id: (new_id - 1) as u8, peers_connected: &peers}).unwrap();
+ loss_detector.handler.new_inbound_connection(Peer{id: (new_id - 1) as u8, peers_connected: &peers}, None).unwrap();
peers.borrow_mut()[new_id - 1] = true;
},
2 => {
forwarding_info: None,
},
funding_txo: Some(OutPoint { txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), index: 0 }),
+ channel_type: None,
short_channel_id: Some(scid),
inbound_scid_alias: None,
channel_value_satoshis: slice_to_be64(get_slice!(8)),
for i in 0..num_nodes {
for j in (i+1)..num_nodes {
- nodes[i].node.peer_connected(&nodes[j].node.get_our_node_id(), &Init { features: InitFeatures::known() });
- nodes[j].node.peer_connected(&nodes[i].node.get_our_node_id(), &Init { features: InitFeatures::known() });
+ nodes[i].node.peer_connected(&nodes[j].node.get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[j].node.peer_connected(&nodes[i].node.get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
}
}
use {CreationError, Currency, DEFAULT_EXPIRY_TIME, Invoice, InvoiceBuilder, SignOrCreationError};
use payment::{Payer, Router};
+use crate::{prelude::*, Description, InvoiceDescription, Sha256};
use bech32::ToBase32;
use bitcoin_hashes::{Hash, sha256};
-use crate::prelude::*;
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use lightning::chain::keysinterface::{Recipient, KeysInterface, Sign};
/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
- amt_msat: Option<u64>, description: String, payment_hash: PaymentHash, payment_secret:
- PaymentSecret, phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency
+ amt_msat: Option<u64>, description: String, payment_hash: PaymentHash, payment_secret: PaymentSecret,
+ phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency,
) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface {
+ let description = Description::new(description).map_err(SignOrCreationError::CreationError)?;
+ let description = InvoiceDescription::Direct(&description,);
+ _create_phantom_invoice::<Signer, K>(
+ amt_msat, description, payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
+ )
+}
+
+#[cfg(feature = "std")]
+/// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
+/// See [`PhantomKeysManager`] for more information on phantom node payments.
+///
+/// `phantom_route_hints` parameter:
+/// * Contains channel info for all nodes participating in the phantom invoice
+/// * Entries are retrieved from a call to [`ChannelManager::get_phantom_route_hints`] on each
+/// participating node
+/// * It is fine to cache `phantom_route_hints` and reuse it across invoices, as long as the data is
+/// updated when a channel becomes disabled or closes
+/// * Note that if too many channels are included in [`PhantomRouteHints::channels`], the invoice
+/// may be too long for QR code scanning. To fix this, `PhantomRouteHints::channels` may be pared
+/// down
+///
+/// `description_hash` is a SHA-256 hash of the description text
+///
+/// `payment_hash` and `payment_secret` come from [`ChannelManager::create_inbound_payment`] or
+/// [`ChannelManager::create_inbound_payment_for_hash`]. These values can be retrieved from any
+/// participating node.
+///
+/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
+/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
+/// requirement).
+///
+/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
+/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
+/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
+pub fn create_phantom_invoice_with_description_hash<Signer: Sign, K: Deref>(
+ amt_msat: Option<u64>, description_hash: Sha256, payment_hash: PaymentHash,
+ payment_secret: PaymentSecret, phantom_route_hints: Vec<PhantomRouteHints>,
+ keys_manager: K, network: Currency,
+) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface
+{
+
+ _create_phantom_invoice::<Signer, K>(
+ amt_msat,
+ InvoiceDescription::Hash(&description_hash),
+ payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
+ )
+}
+
+#[cfg(feature = "std")]
+fn _create_phantom_invoice<Signer: Sign, K: Deref>(
+ amt_msat: Option<u64>, description: InvoiceDescription, payment_hash: PaymentHash,
+ payment_secret: PaymentSecret, phantom_route_hints: Vec<PhantomRouteHints>,
+ keys_manager: K, network: Currency,
+) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface
+{
if phantom_route_hints.len() == 0 {
- return Err(SignOrCreationError::CreationError(CreationError::MissingRouteHints))
+ return Err(SignOrCreationError::CreationError(
+ CreationError::MissingRouteHints,
+ ));
}
- let mut invoice = InvoiceBuilder::new(network)
- .description(description)
+ let invoice = match description {
+ InvoiceDescription::Direct(description) => {
+ InvoiceBuilder::new(network).description(description.0.clone())
+ }
+ InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
+ };
+
+ let mut invoice = invoice
.current_timestamp()
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
.payment_secret(payment_secret)
let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.expect("for the foreseeable future this shouldn't happen");
create_invoice_from_channelmanager_and_duration_since_epoch(
- channelmanager,
- keys_manager,
- network,
- amt_msat,
- description,
- duration
+ channelmanager, keys_manager, network, amt_msat, description, duration
+ )
+}
+
+#[cfg(feature = "std")]
+/// Utility to construct an invoice. Generally, unless you want to do something like a custom
+/// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
+/// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
+/// doesn't have to store preimage/payment secret information and (b) `ChannelManager` can verify
+/// that the payment secret is valid when the invoice is paid.
+/// Use this variant if you want to pass the `description_hash` to the invoice.
+pub fn create_invoice_from_channelmanager_with_description_hash<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
+ channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
+ amt_msat: Option<u64>, description_hash: Sha256,
+) -> Result<Invoice, SignOrCreationError<()>>
+where
+ M::Target: chain::Watch<Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface<Signer = Signer>,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+{
+ use std::time::SystemTime;
+
+ let duration = SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .expect("for the foreseeable future this shouldn't happen");
+
+ create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch(
+ channelmanager, keys_manager, network, amt_msat, description_hash, duration,
+ )
+}
+
+/// See [`create_invoice_from_channelmanager_with_description_hash`]
+/// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
+/// available and the current time is supplied by the caller.
+pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
+ channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
+ amt_msat: Option<u64>, description_hash: Sha256, duration_since_epoch: Duration,
+) -> Result<Invoice, SignOrCreationError<()>>
+where
+ M::Target: chain::Watch<Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface<Signer = Signer>,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+{
+ _create_invoice_from_channelmanager_and_duration_since_epoch(
+ channelmanager, keys_manager, network, amt_msat,
+ InvoiceDescription::Hash(&description_hash),
+ duration_since_epoch,
)
}
channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
) -> Result<Invoice, SignOrCreationError<()>>
+where
+ M::Target: chain::Watch<Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface<Signer = Signer>,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+{
+ _create_invoice_from_channelmanager_and_duration_since_epoch(
+ channelmanager, keys_manager, network, amt_msat,
+ InvoiceDescription::Direct(
+ &Description::new(description).map_err(SignOrCreationError::CreationError)?,
+ ),
+ duration_since_epoch,
+ )
+}
+
+fn _create_invoice_from_channelmanager_and_duration_since_epoch<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
+ channelmanager: &ChannelManager<Signer, M, T, K, F, L>, keys_manager: K, network: Currency,
+ amt_msat: Option<u64>, description: InvoiceDescription, duration_since_epoch: Duration,
+) -> Result<Invoice, SignOrCreationError<()>>
where
M::Target: chain::Watch<Signer>,
T::Target: BroadcasterInterface,
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
// supply.
- let (payment_hash, payment_secret) = channelmanager.create_inbound_payment(
- amt_msat, DEFAULT_EXPIRY_TIME.try_into().unwrap())
+ let (payment_hash, payment_secret) = channelmanager
+ .create_inbound_payment(amt_msat, DEFAULT_EXPIRY_TIME.try_into().unwrap())
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
let our_node_pubkey = channelmanager.get_our_node_id();
- let mut invoice = InvoiceBuilder::new(network)
- .description(description)
+
+ let invoice = match description {
+ InvoiceDescription::Direct(description) => {
+ InvoiceBuilder::new(network).description(description.0.clone())
+ }
+ InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
+ };
+
+ let mut invoice = invoice
.duration_since_epoch(duration_since_epoch)
.payee_pub_key(our_node_pubkey)
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
assert_eq!(events.len(), 2);
}
+ #[test]
+ fn test_create_invoice_with_description_hash() {
+ 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, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+ let description_hash = crate::Sha256(Hash::hash("Testing description_hash".as_bytes()));
+ let invoice = ::utils::create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch(
+ &nodes[1].node, nodes[1].keys_manager, Currency::BitcoinTestnet, Some(10_000),
+ description_hash, Duration::from_secs(1234567),
+ ).unwrap();
+ assert_eq!(invoice.amount_pico_btc(), Some(100_000));
+ assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
+ assert_eq!(invoice.description(), InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Testing description_hash".as_bytes()))));
+ }
+
#[test]
fn test_hints_includes_single_channels_to_nodes() {
let chanmon_cfgs = create_chanmon_cfgs(3);
}
}
+ #[test]
+ #[cfg(feature = "std")]
+ fn create_phantom_invoice_with_description_hash() {
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ let payment_amt = 20_000;
+ let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
+ let route_hints = vec![
+ nodes[1].node.get_phantom_route_hints(),
+ nodes[2].node.get_phantom_route_hints(),
+ ];
+
+ let description_hash = crate::Sha256(Hash::hash("Description hash phantom invoice".as_bytes()));
+ let invoice = ::utils::create_phantom_invoice_with_description_hash::<EnforcingSigner,&test_utils::TestKeysInterface>(Some(payment_amt), description_hash, payment_hash, payment_secret, route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet).unwrap();
+
+ assert_eq!(invoice.amount_pico_btc(), Some(200_000));
+ assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
+ assert_eq!(invoice.description(), InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Description hash phantom invoice".as_bytes()))));
+ }
+
#[test]
#[cfg(feature = "std")]
fn test_multi_node_hints_includes_single_channels_to_participating_nodes() {
use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;
use lightning::ln::peer_handler::CustomMessageHandler;
-use lightning::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler};
+use lightning::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, NetAddress};
use lightning::util::logger::Logger;
use std::task;
+use std::net::IpAddr;
use std::net::SocketAddr;
use std::net::TcpStream as StdTcpStream;
use std::sync::{Arc, Mutex};
RMH: RoutingMessageHandler + 'static + Send + Sync,
L: Logger + 'static + ?Sized + Send + Sync,
UMH: CustomMessageHandler + 'static + Send + Sync {
+ let ip_addr = stream.peer_addr().unwrap();
let (reader, write_receiver, read_receiver, us) = Connection::new(stream);
#[cfg(debug_assertions)]
let last_us = Arc::clone(&us);
- let handle_opt = if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone())) {
+ let handle_opt = if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone()), match ip_addr.ip() {
+ IpAddr::V4(ip) => Some(NetAddress::IPv4 {
+ addr: ip.octets(),
+ port: ip_addr.port(),
+ }),
+ IpAddr::V6(ip) => Some(NetAddress::IPv6 {
+ addr: ip.octets(),
+ port: ip_addr.port(),
+ }),
+ }) {
Some(tokio::spawn(Connection::schedule_read(peer_manager, us, reader, read_receiver, write_receiver)))
} else {
// Note that we will skip socket_disconnected here, in accordance with the PeerManager
RMH: RoutingMessageHandler + 'static + Send + Sync,
L: Logger + 'static + ?Sized + Send + Sync,
UMH: CustomMessageHandler + 'static + Send + Sync {
+ let ip_addr = stream.peer_addr().unwrap();
let (reader, mut write_receiver, read_receiver, us) = Connection::new(stream);
#[cfg(debug_assertions)]
let last_us = Arc::clone(&us);
-
- let handle_opt = if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone())) {
+ let handle_opt = if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone()), match ip_addr.ip() {
+ IpAddr::V4(ip) => Some(NetAddress::IPv4 {
+ addr: ip.octets(),
+ port: ip_addr.port(),
+ }),
+ IpAddr::V6(ip) => Some(NetAddress::IPv6 {
+ addr: ip.octets(),
+ port: ip_addr.port(),
+ }),
+ }) {
Some(tokio::spawn(async move {
// We should essentially always have enough room in a TCP socket buffer to send the
// initial 10s of bytes. However, tokio running in single-threaded mode will always
fn handle_channel_update(&self, _msg: &ChannelUpdate) -> Result<bool, LightningError> { Ok(false) }
fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)> { Vec::new() }
fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec<NodeAnnouncement> { Vec::new() }
- fn sync_routing_table(&self, _their_node_id: &PublicKey, _init_msg: &Init) { }
+ fn peer_connected(&self, _their_node_id: &PublicKey, _init_msg: &Init) { }
fn handle_reply_channel_range(&self, _their_node_id: &PublicKey, _msg: ReplyChannelRange) -> Result<(), LightningError> { Ok(()) }
fn handle_reply_short_channel_ids_end(&self, _their_node_id: &PublicKey, _msg: ReplyShortChannelIdsEnd) -> Result<(), LightningError> { Ok(()) }
fn handle_query_channel_range(&self, _their_node_id: &PublicKey, _msg: QueryChannelRange) -> Result<(), LightningError> { Ok(()) }
no-std = ["hashbrown", "bitcoin/no-std", "core2/alloc"]
std = ["bitcoin/std"]
-default = ["std"]
+# Generates low-r bitcoin signatures, which saves 1 byte in 50% of the cases
+grind_signatures = []
+
+default = ["std", "grind_signatures"]
[dependencies]
bitcoin = { version = "0.27", default-features = false, features = ["secp-recovery"] }
use bitcoin::secp256k1;
use util::{byte_utils, transaction_utils};
-use util::crypto::hkdf_extract_expand_twice;
+use util::crypto::{hkdf_extract_expand_twice, sign};
use util::ser::{Writeable, Writer, Readable, ReadableArgs};
use chain::transaction::OutPoint;
let remotepubkey = self.pubkeys().payment_point;
let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
- let remotesig = secp_ctx.sign(&sighash, &self.payment_key);
+ let remotesig = sign(secp_ctx, &sighash, &self.payment_key);
let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey();
if payment_script != descriptor.output.script_pubkey { return Err(()); }
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
- let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
+ let local_delayedsig = sign(secp_ctx, &sighash, &delayed_payment_key);
let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();
if descriptor.output.script_pubkey != payment_script { return Err(()); }
let htlc_sighashtype = if self.opt_anchors() { SigHashType::SinglePlusAnyoneCanPay } else { SigHashType::All };
let htlc_sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype)[..]);
let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key).map_err(|_| ())?;
- htlc_sigs.push(secp_ctx.sign(&htlc_sighash, &holder_htlc_key));
+ htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key));
}
Ok((commitment_sig, htlc_sigs))
};
let mut sighash_parts = bip143::SigHashCache::new(justice_tx);
let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]);
- return Ok(secp_ctx.sign(&sighash, &revocation_key))
+ return Ok(sign(secp_ctx, &sighash, &revocation_key))
}
fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
};
let mut sighash_parts = bip143::SigHashCache::new(justice_tx);
let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]);
- return Ok(secp_ctx.sign(&sighash, &revocation_key))
+ return Ok(sign(secp_ctx, &sighash, &revocation_key))
}
fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
} else { return Err(()) };
let mut sighash_parts = bip143::SigHashCache::new(htlc_tx);
let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]);
- return Ok(secp_ctx.sign(&sighash, &htlc_key))
+ return Ok(sign(secp_ctx, &sighash, &htlc_key))
}
Err(())
}
fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
-> Result<(Signature, Signature), ()> {
let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
- Ok((secp_ctx.sign(&msghash, &self.node_secret), secp_ctx.sign(&msghash, &self.funding_key)))
+ Ok((sign(secp_ctx, &msghash, &self.node_secret), sign(secp_ctx, &msghash, &self.funding_key)))
}
fn ready_channel(&mut self, channel_parameters: &ChannelTransactionParameters) {
if payment_script != output.script_pubkey { return Err(()); };
let sighash = hash_to_message!(&bip143::SigHashCache::new(&spend_tx).signature_hash(input_idx, &witness_script, output.value, SigHashType::All)[..]);
- let sig = secp_ctx.sign(&sighash, &secret.private_key.key);
+ let sig = sign(secp_ctx, &sighash, &secret.private_key.key);
spend_tx.input[input_idx].witness.push(sig.serialize_der().to_vec());
spend_tx.input[input_idx].witness[0].push(SigHashType::All as u8);
spend_tx.input[input_idx].witness.push(pubkey.key.serialize().to_vec());
}
thread_local! {
- /// We track the set of locks currently held by a reference to their `MutexMetadata`
- static MUTEXES_HELD: RefCell<HashSet<Arc<MutexMetadata>>> = RefCell::new(HashSet::new());
+ /// We track the set of locks currently held by a reference to their `LockMetadata`
+ static LOCKS_HELD: RefCell<HashSet<Arc<LockMetadata>>> = RefCell::new(HashSet::new());
}
-static MUTEX_IDX: AtomicUsize = AtomicUsize::new(0);
+static LOCK_IDX: AtomicUsize = AtomicUsize::new(0);
-/// Metadata about a single mutex, by id, the set of things locked-before it, and the backtrace of
+/// Metadata about a single lock, by id, the set of things locked-before it, and the backtrace of
/// when the Mutex itself was constructed.
-struct MutexMetadata {
- mutex_idx: u64,
- locked_before: StdMutex<HashSet<Arc<MutexMetadata>>>,
+struct LockMetadata {
+ lock_idx: u64,
+ locked_before: StdMutex<HashSet<Arc<LockMetadata>>>,
#[cfg(feature = "backtrace")]
- mutex_construction_bt: Backtrace,
+ lock_construction_bt: Backtrace,
}
-impl PartialEq for MutexMetadata {
- fn eq(&self, o: &MutexMetadata) -> bool { self.mutex_idx == o.mutex_idx }
+impl PartialEq for LockMetadata {
+ fn eq(&self, o: &LockMetadata) -> bool { self.lock_idx == o.lock_idx }
}
-impl Eq for MutexMetadata {}
-impl std::hash::Hash for MutexMetadata {
- fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) { hasher.write_u64(self.mutex_idx); }
+impl Eq for LockMetadata {}
+impl std::hash::Hash for LockMetadata {
+ fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) { hasher.write_u64(self.lock_idx); }
+}
+
+impl LockMetadata {
+ fn new() -> LockMetadata {
+ LockMetadata {
+ locked_before: StdMutex::new(HashSet::new()),
+ lock_idx: LOCK_IDX.fetch_add(1, Ordering::Relaxed) as u64,
+ #[cfg(feature = "backtrace")]
+ lock_construction_bt: Backtrace::new(),
+ }
+ }
+
+ // Returns whether we were a recursive lock (only relevant for read)
+ fn _pre_lock(this: &Arc<LockMetadata>, read: bool) -> bool {
+ let mut inserted = false;
+ LOCKS_HELD.with(|held| {
+ // For each lock which is currently locked, check that no lock's locked-before
+ // set includes the lock we're about to lock, which would imply a lockorder
+ // inversion.
+ for locked in held.borrow().iter() {
+ if read && *locked == *this {
+ // Recursive read locks are explicitly allowed
+ return;
+ }
+ }
+ for locked in held.borrow().iter() {
+ if !read && *locked == *this {
+ panic!("Tried to lock a lock while it was held!");
+ }
+ for locked_dep in locked.locked_before.lock().unwrap().iter() {
+ if *locked_dep == *this {
+ #[cfg(feature = "backtrace")]
+ panic!("Tried to violate existing lockorder.\nMutex that should be locked after the current lock was created at the following backtrace.\nNote that to get a backtrace for the lockorder violation, you should set RUST_BACKTRACE=1\n{:?}", locked.lock_construction_bt);
+ #[cfg(not(feature = "backtrace"))]
+ panic!("Tried to violate existing lockorder. Build with the backtrace feature for more info.");
+ }
+ }
+ // Insert any already-held locks in our locked-before set.
+ this.locked_before.lock().unwrap().insert(Arc::clone(locked));
+ }
+ held.borrow_mut().insert(Arc::clone(this));
+ inserted = true;
+ });
+ inserted
+ }
+
+ fn pre_lock(this: &Arc<LockMetadata>) { Self::_pre_lock(this, false); }
+ fn pre_read_lock(this: &Arc<LockMetadata>) -> bool { Self::_pre_lock(this, true) }
+
+ fn try_locked(this: &Arc<LockMetadata>) {
+ LOCKS_HELD.with(|held| {
+ // Since a try-lock will simply fail if the lock is held already, we do not
+ // consider try-locks to ever generate lockorder inversions. However, if a try-lock
+ // succeeds, we do consider it to have created lockorder dependencies.
+ for locked in held.borrow().iter() {
+ this.locked_before.lock().unwrap().insert(Arc::clone(locked));
+ }
+ held.borrow_mut().insert(Arc::clone(this));
+ });
+ }
}
pub struct Mutex<T: Sized> {
inner: StdMutex<T>,
- deps: Arc<MutexMetadata>,
+ deps: Arc<LockMetadata>,
}
#[must_use = "if unused the Mutex will immediately unlock"]
impl<T: Sized> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
- MUTEXES_HELD.with(|held| {
+ LOCKS_HELD.with(|held| {
held.borrow_mut().remove(&self.mutex.deps);
});
}
impl<T> Mutex<T> {
pub fn new(inner: T) -> Mutex<T> {
- Mutex {
- inner: StdMutex::new(inner),
- deps: Arc::new(MutexMetadata {
- locked_before: StdMutex::new(HashSet::new()),
- mutex_idx: MUTEX_IDX.fetch_add(1, Ordering::Relaxed) as u64,
- #[cfg(feature = "backtrace")]
- mutex_construction_bt: Backtrace::new(),
- }),
- }
+ Mutex { inner: StdMutex::new(inner), deps: Arc::new(LockMetadata::new()) }
}
pub fn lock<'a>(&'a self) -> LockResult<MutexGuard<'a, T>> {
- MUTEXES_HELD.with(|held| {
- // For each mutex which is currently locked, check that no mutex's locked-before
- // set includes the mutex we're about to lock, which would imply a lockorder
- // inversion.
- for locked in held.borrow().iter() {
- for locked_dep in locked.locked_before.lock().unwrap().iter() {
- if *locked_dep == self.deps {
- #[cfg(feature = "backtrace")]
- panic!("Tried to violate existing lockorder.\nMutex that should be locked after the current lock was created at the following backtrace.\nNote that to get a backtrace for the lockorder violation, you should set RUST_BACKTRACE=1\n{:?}", locked.mutex_construction_bt);
- #[cfg(not(feature = "backtrace"))]
- panic!("Tried to violate existing lockorder. Build with the backtrace feature for more info.");
- }
- }
- // Insert any already-held mutexes in our locked-before set.
- self.deps.locked_before.lock().unwrap().insert(Arc::clone(locked));
- }
- held.borrow_mut().insert(Arc::clone(&self.deps));
- });
+ LockMetadata::pre_lock(&self.deps);
self.inner.lock().map(|lock| MutexGuard { mutex: self, lock }).map_err(|_| ())
}
pub fn try_lock<'a>(&'a self) -> LockResult<MutexGuard<'a, T>> {
let res = self.inner.try_lock().map(|lock| MutexGuard { mutex: self, lock }).map_err(|_| ());
if res.is_ok() {
- MUTEXES_HELD.with(|held| {
- // Since a try-lock will simply fail if the lock is held already, we do not
- // consider try-locks to ever generate lockorder inversions. However, if a try-lock
- // succeeds, we do consider it to have created lockorder dependencies.
- for locked in held.borrow().iter() {
- self.deps.locked_before.lock().unwrap().insert(Arc::clone(locked));
- }
- held.borrow_mut().insert(Arc::clone(&self.deps));
- });
+ LockMetadata::try_locked(&self.deps);
}
res
}
}
-pub struct RwLock<T: ?Sized> {
- inner: StdRwLock<T>
+pub struct RwLock<T: Sized> {
+ inner: StdRwLock<T>,
+ deps: Arc<LockMetadata>,
}
-pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
- lock: StdRwLockReadGuard<'a, T>,
+pub struct RwLockReadGuard<'a, T: Sized + 'a> {
+ lock: &'a RwLock<T>,
+ first_lock: bool,
+ guard: StdRwLockReadGuard<'a, T>,
}
-pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
- lock: StdRwLockWriteGuard<'a, T>,
+pub struct RwLockWriteGuard<'a, T: Sized + 'a> {
+ lock: &'a RwLock<T>,
+ guard: StdRwLockWriteGuard<'a, T>,
}
-impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
+impl<T: Sized> Deref for RwLockReadGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
- &self.lock.deref()
+ &self.guard.deref()
}
}
-impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> {
+impl<T: Sized> Drop for RwLockReadGuard<'_, T> {
+ fn drop(&mut self) {
+ if !self.first_lock {
+ // Note that its not strictly true that the first taken read lock will get unlocked
+ // last, but in practice our locks are always taken as RAII, so it should basically
+ // always be true.
+ return;
+ }
+ LOCKS_HELD.with(|held| {
+ held.borrow_mut().remove(&self.lock.deps);
+ });
+ }
+}
+
+impl<T: Sized> Deref for RwLockWriteGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
- &self.lock.deref()
+ &self.guard.deref()
+ }
+}
+
+impl<T: Sized> Drop for RwLockWriteGuard<'_, T> {
+ fn drop(&mut self) {
+ LOCKS_HELD.with(|held| {
+ held.borrow_mut().remove(&self.lock.deps);
+ });
}
}
-impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
+impl<T: Sized> DerefMut for RwLockWriteGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
- self.lock.deref_mut()
+ self.guard.deref_mut()
}
}
impl<T> RwLock<T> {
pub fn new(inner: T) -> RwLock<T> {
- RwLock { inner: StdRwLock::new(inner) }
+ RwLock { inner: StdRwLock::new(inner), deps: Arc::new(LockMetadata::new()) }
}
pub fn read<'a>(&'a self) -> LockResult<RwLockReadGuard<'a, T>> {
- self.inner.read().map(|lock| RwLockReadGuard { lock }).map_err(|_| ())
+ let first_lock = LockMetadata::pre_read_lock(&self.deps);
+ self.inner.read().map(|guard| RwLockReadGuard { lock: self, guard, first_lock }).map_err(|_| ())
}
pub fn write<'a>(&'a self) -> LockResult<RwLockWriteGuard<'a, T>> {
- self.inner.write().map(|lock| RwLockWriteGuard { lock }).map_err(|_| ())
+ LockMetadata::pre_lock(&self.deps);
+ self.inner.write().map(|guard| RwLockWriteGuard { lock: self, guard }).map_err(|_| ())
}
pub fn try_write<'a>(&'a self) -> LockResult<RwLockWriteGuard<'a, T>> {
- self.inner.try_write().map(|lock| RwLockWriteGuard { lock }).map_err(|_| ())
+ let res = self.inner.try_write().map(|guard| RwLockWriteGuard { lock: self, guard }).map_err(|_| ());
+ if res.is_ok() {
+ LockMetadata::try_locked(&self.deps);
+ }
+ res
+ }
+}
+
+#[test]
+#[should_panic]
+fn recursive_lock_fail() {
+ let mutex = Mutex::new(());
+ let _a = mutex.lock().unwrap();
+ let _b = mutex.lock().unwrap();
+}
+
+#[test]
+fn recursive_read() {
+ let lock = RwLock::new(());
+ let _a = lock.read().unwrap();
+ let _b = lock.read().unwrap();
+}
+
+#[test]
+#[should_panic]
+fn lockorder_fail() {
+ let a = Mutex::new(());
+ let b = Mutex::new(());
+ {
+ let _a = a.lock().unwrap();
+ let _b = b.lock().unwrap();
+ }
+ {
+ let _b = b.lock().unwrap();
+ let _a = a.lock().unwrap();
+ }
+}
+
+#[test]
+#[should_panic]
+fn write_lockorder_fail() {
+ let a = RwLock::new(());
+ let b = RwLock::new(());
+ {
+ let _a = a.write().unwrap();
+ let _b = b.write().unwrap();
+ }
+ {
+ let _b = b.write().unwrap();
+ let _a = a.write().unwrap();
+ }
+}
+
+#[test]
+#[should_panic]
+fn read_lockorder_fail() {
+ let a = RwLock::new(());
+ let b = RwLock::new(());
+ {
+ let _a = a.read().unwrap();
+ let _b = b.read().unwrap();
+ }
+ {
+ let _b = b.read().unwrap();
+ let _a = a.read().unwrap();
+ }
+}
+
+#[test]
+fn read_recurisve_no_lockorder() {
+ // Like the above, but note that no lockorder is implied when we recursively read-lock a
+ // RwLock, causing this to pass just fine.
+ let a = RwLock::new(());
+ let b = RwLock::new(());
+ let _outer = a.read().unwrap();
+ {
+ let _a = a.read().unwrap();
+ let _b = b.read().unwrap();
+ }
+ {
+ let _b = b.read().unwrap();
+ let _a = a.read().unwrap();
+ }
+}
+
+#[test]
+#[should_panic]
+fn read_write_lockorder_fail() {
+ let a = RwLock::new(());
+ let b = RwLock::new(());
+ {
+ let _a = a.write().unwrap();
+ let _b = b.read().unwrap();
+ }
+ {
+ let _b = b.read().unwrap();
+ let _a = a.write().unwrap();
}
}
use ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI};
use core::ops::Deref;
use chain;
+use util::crypto::sign;
pub(crate) const MAX_HTLCS: u16 = 483;
pub fn dummy() -> Self {
let secp_ctx = Secp256k1::new();
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
- let dummy_sig = secp_ctx.sign(&secp256k1::Message::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[42; 32]).unwrap());
+ let dummy_sig = sign(&secp_ctx, &secp256k1::Message::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[42; 32]).unwrap());
let keys = TxCreationKeys {
per_commitment_point: dummy_key.clone(),
/// because we are about to broadcast a holder transaction.
pub fn sign<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
- secp_ctx.sign(&sighash, funding_key)
+ sign(secp_ctx, &sighash, funding_key)
}
}
/// because we are about to broadcast a holder transaction.
pub fn sign<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
- secp_ctx.sign(&sighash, funding_key)
+ sign(secp_ctx, &sighash, funding_key)
}
}
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, self.opt_anchors(), &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, this_htlc.amount_msat / 1000, SigHashType::All)[..]);
- ret.push(secp_ctx.sign(&sighash, &holder_htlc_key));
+ ret.push(sign(secp_ctx, &sighash, &holder_htlc_key));
}
Ok(ret)
}
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::hash_types::BlockHash;
use bitcoin::network::constants::Network;
-use chain::channelmonitor::ChannelMonitor;
+use chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor};
use chain::transaction::OutPoint;
use chain::{ChannelMonitorUpdateErr, Listen, Watch};
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure};
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 1);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 1);
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 1);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 1);
commitment_signed_dance!(nodes[1], nodes[2], updates.commitment_signed, false);
chanmon_cfgs[1].persister.set_update_ret(Err(ChannelMonitorUpdateErr::TemporaryFailure));
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
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.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
assert!(as_reestablish == get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id()));
assert!(bs_reestablish == get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id()));
assert!(nodes[1].node.claim_funds(payment_preimage_1));
check_added_monitors!(nodes[1], 1);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let as_reconnect = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
let bs_reconnect = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let as_reconnect = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
let bs_reconnect = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
let as_connect_msg = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
- nodes[1].node.peer_connected(&nodes[0].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::known(), remote_network_address: None });
let bs_connect_msg = 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_connect_msg);
claim_payment(&nodes[1], &[&nodes[0]], payment_preimage);
}
+#[test]
+fn test_fail_htlc_on_broadcast_after_claim() {
+ // In an earlier version of 7e78fa660cec8a73286c94c1073ee588140e7a01 we'd also fail the inbound
+ // channel backwards if we received an HTLC failure after a HTLC fulfillment. Here we test a
+ // specific case of that by having the HTLC failure come from the ChannelMonitor after a dust
+ // HTLC was not included in a confirmed commitment transaction.
+ //
+ // We first forward a payment, then claim it with an update_fulfill_htlc message, closing the
+ // channel immediately before commitment occurs. After the commitment transaction reaches
+ // ANTI_REORG_DELAY confirmations, will will try to fail the HTLC which was already fulfilled.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+ let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
+ let chan_id_2 = create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known()).2;
+
+ let payment_preimage = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 2000).0;
+
+ let bs_txn = get_local_commitment_txn!(nodes[2], chan_id_2);
+ assert_eq!(bs_txn.len(), 1);
+
+ nodes[2].node.claim_funds(payment_preimage);
+ check_added_monitors!(nodes[2], 1);
+ let cs_updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_update_fulfill_htlc(&nodes[2].node.get_our_node_id(), &cs_updates.update_fulfill_htlcs[0]);
+ let bs_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ check_added_monitors!(nodes[1], 1);
+ expect_payment_forwarded!(nodes[1], Some(1000), false);
+
+ mine_transaction(&nodes[1], &bs_txn[0]);
+ check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
+ check_closed_broadcast!(nodes[1], true);
+ connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
+ check_added_monitors!(nodes[1], 1);
+ expect_pending_htlcs_forwardable!(nodes[1]);
+
+ nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.update_fulfill_htlcs[0]);
+ expect_payment_sent_without_paths!(nodes[0], payment_preimage);
+ commitment_signed_dance!(nodes[0], nodes[1], bs_updates.commitment_signed, true, true);
+ expect_payment_path_successful!(nodes[0]);
+}
+
fn do_update_fee_resend_test(deliver_update: bool, parallel_updates: bool) {
// In early versions we did not handle resending of update_fee on reconnect correctly. The
// chanmon_consistency fuzz target, of course, immediately found it, but we test a few cases
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
let as_connect_msg = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
- nodes[1].node.peer_connected(&nodes[0].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::known(), remote_network_address: None });
let bs_connect_msg = 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_connect_msg);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
// Now reconnect the two
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 1);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 1);
self.channel_transaction_parameters.opt_anchors.is_some()
}
+ fn get_initial_channel_type(config: &UserConfig) -> ChannelTypeFeatures {
+ // The default channel type (ie the first one we try) depends on whether the channel is
+ // public - if it is, we just go with `only_static_remotekey` as it's the only option
+ // available. If it's private, we first try `scid_privacy` as it provides better privacy
+ // with no other changes, and fall back to `only_static_remotekey`
+ let mut ret = ChannelTypeFeatures::only_static_remote_key();
+ if !config.channel_options.announced_channel && config.own_channel_config.negotiate_scid_privacy {
+ ret.set_scid_privacy_required();
+ }
+ ret
+ }
+
+ /// If we receive an error message, it may only be a rejection of the channel type we tried,
+ /// not of our ability to open any channel at all. Thus, on error, we should first call this
+ /// and see if we get a new `OpenChannel` message, otherwise the channel is failed.
+ pub(crate) fn maybe_handle_error_without_close(&mut self, chain_hash: BlockHash) -> Result<msgs::OpenChannel, ()> {
+ if !self.is_outbound() || self.channel_state != ChannelState::OurInitSent as u32 { return Err(()); }
+ if self.channel_type == ChannelTypeFeatures::only_static_remote_key() {
+ // We've exhausted our options
+ return Err(());
+ }
+ self.channel_type = ChannelTypeFeatures::only_static_remote_key(); // We only currently support two types
+ Ok(self.get_open_channel(chain_hash))
+ }
+
// Constructors:
pub fn new_outbound<K: Deref, F: Deref>(
fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, their_features: &InitFeatures,
#[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills: HashSet::new(),
- // We currently only actually support one channel type, so don't retry with new types
- // on error messages. When we support more we'll need fallback support (assuming we
- // want to support old types).
- channel_type: ChannelTypeFeatures::only_static_remote_key(),
+ channel_type: Self::get_initial_channel_type(&config),
})
}
L::Target: Logger,
{
let opt_anchors = false; // TODO - should be based on features
+ let announced_channel = if (msg.channel_flags & 1) == 1 { true } else { false };
// First check the channel type is known, failing before we do anything else if we don't
// support this channel type.
if channel_type.supports_any_optional_bits() {
return Err(ChannelError::Close("Channel Type field contained optional bits - this is not allowed".to_owned()));
}
- if *channel_type != ChannelTypeFeatures::only_static_remote_key() {
- return Err(ChannelError::Close("Channel Type was not understood".to_owned()));
+ // We currently only allow two channel types, so write it all out here - we allow
+ // `only_static_remote_key` in all contexts, and further allow
+ // `static_remote_key|scid_privacy` if the channel is not publicly announced.
+ let mut allowed_type = ChannelTypeFeatures::only_static_remote_key();
+ if *channel_type != allowed_type {
+ allowed_type.set_scid_privacy_required();
+ if *channel_type != allowed_type {
+ return Err(ChannelError::Close("Channel Type was not understood".to_owned()));
+ }
+ if announced_channel {
+ return Err(ChannelError::Close("SCID Alias/Privacy Channel Type cannot be set on a public channel".to_owned()));
+ }
}
channel_type.clone()
} else {
// Convert things into internal flags and prep our state:
- let announce = if (msg.channel_flags & 1) == 1 { true } else { false };
if config.peer_channel_config_limits.force_announced_channel_preference {
- if local_config.announced_channel != announce {
+ if local_config.announced_channel != announced_channel {
return Err(ChannelError::Close("Peer tried to open channel but their announcement preference is different from ours".to_owned()));
}
}
// we either accept their preference or the preferences match
- local_config.announced_channel = announce;
+ local_config.announced_channel = announced_channel;
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis);
if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
self.user_id
}
+ /// Gets the channel's type
+ pub fn get_channel_type(&self) -> &ChannelTypeFeatures {
+ &self.channel_type
+ }
+
/// Guaranteed to be Some after both FundingLocked messages have been exchanged (and, thus,
/// is_usable() returns true).
/// Allowed in any state (including after shutdown)
/// should be sent back to the counterparty node.
///
/// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
- pub fn accept_inbound_channel(&mut self) -> msgs::AcceptChannel {
+ pub fn accept_inbound_channel(&mut self, user_id: u64) -> msgs::AcceptChannel {
if self.is_outbound() {
panic!("Tried to send accept_channel for an outbound channel?");
}
panic!("The inbound channel has already been accepted");
}
+ self.user_id = user_id;
self.inbound_awaiting_accept = false;
self.generate_accept_channel_message()
let mut node_b_chan = Channel::<EnforcingSigner>::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();
+ let mut accept_channel_msg = node_b_chan.accept_inbound_channel(0);
accept_channel_msg.dust_limit_satoshis = 546;
node_a_chan.accept_channel(&accept_channel_msg, &config.peer_channel_config_limits, &InitFeatures::known()).unwrap();
node_a_chan.holder_dust_limit_satoshis = 1560;
let mut node_b_chan = Channel::<EnforcingSigner>::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();
+ let accept_channel_msg = node_b_chan.accept_inbound_channel(0);
node_a_chan.accept_channel(&accept_channel_msg, &config.peer_channel_config_limits, &InitFeatures::known()).unwrap();
// Node A --> Node B: funding created
}
}
+ #[cfg(not(feature = "grind_signatures"))]
#[test]
fn outbound_commitment_test() {
// Test vectors from BOLT 3 Appendices C and F (anchors):
// construct one themselves.
use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use ln::channel::{Channel, ChannelError, ChannelUpdateStatus, UpdateFulfillCommitFetch};
-use ln::features::{InitFeatures, NodeFeatures};
+use ln::features::{ChannelTypeFeatures, InitFeatures, NodeFeatures};
use routing::router::{PaymentParameters, Route, RouteHop, RoutePath, RouteParameters};
use ln::msgs;
use ln::msgs::NetAddress;
use util::events::{EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
use util::{byte_utils, events};
use util::scid_utils::fake_scid;
-use util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer};
+use util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter};
use util::logger::{Level, Logger};
use util::errors::APIError;
#[cfg(any(test, feature = "std"))]
use std::time::Instant;
+use util::crypto::sign;
mod inbound_payment {
use alloc::string::ToString;
cltv_expiry: u32,
value: u64,
onion_payload: OnionPayload,
+ timer_ticks: u8,
}
/// A payment identifier used to uniquely identify a payment to LDK.
/// issues such as overly long function definitions. Note that the ChannelManager can take any
/// type that implements KeysInterface for its keys manager, but this type alias chooses the
/// concrete type of the KeysManager.
+///
+/// (C-not exported) as Arcs don't make sense in bindings
pub type SimpleArcChannelManager<M, T, F, L> = ChannelManager<InMemorySigner, Arc<M>, Arc<T>, Arc<KeysManager>, Arc<F>, Arc<L>>;
/// SimpleRefChannelManager is a type alias for a ChannelManager reference, and is the reference
/// helps with issues such as long function definitions. Note that the ChannelManager can take any
/// type that implements KeysInterface for its keys manager, but this type alias chooses the
/// concrete type of the KeysManager.
+///
+/// (C-not exported) as Arcs don't make sense in bindings
pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, M, T, F, L> = ChannelManager<InMemorySigner, &'a M, &'b T, &'c KeysManager, &'d F, &'e L>;
/// Manager which keeps track of a number of channels and sends messages to the appropriate
/// pending HTLCs in flight.
pub(crate) const PAYMENT_EXPIRY_BLOCKS: u32 = 3;
+/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until expiry of incomplete MPPs
+pub(crate) const MPP_TIMEOUT_TICKS: u8 = 3;
+
/// Information needed for constructing an invoice route hint for this channel.
#[derive(Clone, Debug, PartialEq)]
pub struct CounterpartyForwardingInfo {
/// Note that, if this has been set, `channel_id` will be equivalent to
/// `funding_txo.unwrap().to_channel_id()`.
pub funding_txo: Option<OutPoint>,
+ /// The features which this channel operates with. See individual features for more info.
+ ///
+ /// `None` until negotiation completes and the channel type is finalized.
+ pub channel_type: Option<ChannelTypeFeatures>,
/// The position of the funding transaction in the chain. None if the funding transaction has
/// not yet been confirmed and the channel fully opened.
///
/// counterparty will recognize the alias provided here in place of the [`short_channel_id`]
/// when they see a payment to be routed to us.
///
+ /// Our counterparty may choose to rotate this value at any time, though will always recognize
+ /// previous values for inbound payment forwarding.
+ ///
/// [`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
}
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.
+ /// Gets the current 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.
+ ///
+ /// This is either the [`ChannelDetails::inbound_scid_alias`], if set, or the
+ /// [`ChannelDetails::short_channel_id`]. See those for more information.
pub fn get_inbound_payment_scid(&self) -> Option<u64> {
self.inbound_scid_alias.or(self.short_channel_id)
}
forwarding_info: channel.counterparty_forwarding_info(),
},
funding_txo: channel.get_funding_txo(),
+ // Note that accept_channel (or open_channel) is always the first message, so
+ // `have_received_message` indicates that type negotiation has completed.
+ channel_type: if channel.have_received_message() { Some(channel.get_channel_type().clone()) } else { None },
short_channel_id: channel.get_short_channel_id(),
inbound_scid_alias: channel.latest_inbound_scid_alias(),
channel_value_satoshis: channel.get_value_satoshis(),
};
let (chan_update_opt, forwardee_cltv_expiry_delta) = if let Some(forwarding_id) = forwarding_id_opt {
let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
- // Leave channel updates as None for private channels.
- let chan_update_opt = if chan.should_announce() {
- Some(self.get_channel_update_for_unicast(chan).unwrap()) } else { None };
if !chan.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
// Note that the behavior here should be identical to the above block - we
// should NOT reveal the existence or non-existence of a private channel if
// we don't allow forwards outbound over them.
- break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
+ break Some(("Refusing to forward to a private channel based on our config.", 0x4000 | 10, None));
+ }
+ if chan.get_channel_type().supports_scid_privacy() && *short_channel_id != chan.outbound_scid_alias() {
+ // `option_scid_alias` (referred to in LDK as `scid_privacy`) means
+ // "refuse to forward unless the SCID alias was used", so we pretend
+ // we don't have the channel here.
+ break Some(("Refusing to forward over real channel SCID as our counterparty requested.", 0x4000 | 10, None));
}
+ let chan_update_opt = self.get_channel_update_for_onion(*short_channel_id, chan).ok();
// Note that we could technically not return an error yet here and just hope
// that the connection is reestablished or monitor updated by the time we get
break None;
}
{
- let mut res = Vec::with_capacity(8 + 128);
+ let mut res = VecWriter(Vec::with_capacity(chan_update.serialized_length() + 8 + 2));
if let Some(chan_update) = chan_update {
if code == 0x1000 | 11 || code == 0x1000 | 12 {
- res.extend_from_slice(&byte_utils::be64_to_array(msg.amount_msat));
+ msg.amount_msat.write(&mut res).expect("Writes cannot fail");
}
else if code == 0x1000 | 13 {
- res.extend_from_slice(&byte_utils::be32_to_array(msg.cltv_expiry));
+ msg.cltv_expiry.write(&mut res).expect("Writes cannot fail");
}
else if code == 0x1000 | 20 {
// TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
+ 0u16.write(&mut res).expect("Writes cannot fail");
}
- res.extend_from_slice(&chan_update.encode_with_len()[..]);
+ (chan_update.serialized_length() as u16).write(&mut res).expect("Writes cannot fail");
+ chan_update.write(&mut res).expect("Writes cannot fail");
}
- return_err!(err, code, &res[..]);
+ return_err!(err, code, &res.0[..]);
}
}
}
Some(id) => id,
};
+ self.get_channel_update_for_onion(short_channel_id, chan)
+ }
+ fn get_channel_update_for_onion(&self, short_channel_id: u64, chan: &Channel<Signer>) -> Result<msgs::ChannelUpdate, LightningError> {
+ log_trace!(self.logger, "Generating channel update for channel {}", log_bytes!(chan.channel_id()));
let were_node_one = PublicKey::from_secret_key(&self.secp_ctx, &self.our_network_key).serialize()[..] < chan.get_counterparty_node_id().serialize()[..];
let unsigned = msgs::UnsignedChannelUpdate {
excess_data: Vec::new(),
};
let msghash = hash_to_message!(&Sha256dHash::hash(&announcement.encode()[..])[..]);
- let node_announce_sig = self.secp_ctx.sign(&msghash, &self.our_network_key);
+ let node_announce_sig = sign(&self.secp_ctx, &msghash, &self.our_network_key);
let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_state_lock;
} else {
panic!("Stated return value requirements in send_htlc() were not met");
}
- let chan_update = self.get_channel_update_for_unicast(chan.get()).unwrap();
+ let (failure_code, data) = self.get_htlc_temp_fail_err_and_data(0x1000|7, short_chan_id, chan.get());
failed_forwards.push((htlc_source, payment_hash,
- HTLCFailReason::Reason { failure_code: 0x1000 | 7, data: chan_update.encode_with_len() }
+ HTLCFailReason::Reason { failure_code, data }
));
continue;
},
phantom_shared_secret,
},
value: amt_to_forward,
+ timer_ticks: 0,
cltv_expiry,
onion_payload,
};
let new_feerate = self.fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
let mut handle_errors = Vec::new();
+ let mut timed_out_mpp_htlcs = Vec::new();
{
let mut channel_state_lock = self.channel_state.lock().unwrap();
let channel_state = &mut *channel_state_lock;
true
});
+
+ channel_state.claimable_htlcs.retain(|payment_hash, htlcs| {
+ if htlcs.is_empty() {
+ // This should be unreachable
+ debug_assert!(false);
+ return false;
+ }
+ if let OnionPayload::Invoice(ref final_hop_data) = htlcs[0].onion_payload {
+ // Check if we've received all the parts we need for an MPP (the value of the parts adds to total_msat).
+ // In this case we're not going to handle any timeouts of the parts here.
+ if final_hop_data.total_msat == htlcs.iter().fold(0, |total, htlc| total + htlc.value) {
+ return true;
+ } else if htlcs.into_iter().any(|htlc| {
+ htlc.timer_ticks += 1;
+ return htlc.timer_ticks >= MPP_TIMEOUT_TICKS
+ }) {
+ timed_out_mpp_htlcs.extend(htlcs.into_iter().map(|htlc| (htlc.prev_hop.clone(), payment_hash.clone())));
+ return false;
+ }
+ }
+ true
+ });
+ }
+
+ for htlc_source in timed_out_mpp_htlcs.drain(..) {
+ self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), HTLCSource::PreviousHopData(htlc_source.0), &htlc_source.1, HTLCFailReason::Reason { failure_code: 23, data: Vec::new() });
}
for (err, counterparty_node_id) in handle_errors.drain(..) {
} else { false }
}
+ /// Gets an HTLC onion failure code and error data for an `UPDATE` error, given the error code
+ /// that we want to return and a channel.
+ ///
+ /// This is for failures on the channel on which the HTLC was *received*, not failures
+ /// forwarding
+ fn get_htlc_inbound_temp_fail_err_and_data(&self, desired_err_code: u16, chan: &Channel<Signer>) -> (u16, Vec<u8>) {
+ // We can't be sure what SCID was used when relaying inbound towards us, so we have to
+ // guess somewhat. If its a public channel, we figure best to just use the real SCID (as
+ // we're not leaking that we have a channel with the counterparty), otherwise we try to use
+ // an inbound SCID alias before the real SCID.
+ let scid_pref = if chan.should_announce() {
+ chan.get_short_channel_id().or(chan.latest_inbound_scid_alias())
+ } else {
+ chan.latest_inbound_scid_alias().or(chan.get_short_channel_id())
+ };
+ if let Some(scid) = scid_pref {
+ self.get_htlc_temp_fail_err_and_data(desired_err_code, scid, chan)
+ } else {
+ (0x4000|10, Vec::new())
+ }
+ }
+
+
+ /// Gets an HTLC onion failure code and error data for an `UPDATE` error, given the error code
+ /// that we want to return and a channel.
+ fn get_htlc_temp_fail_err_and_data(&self, desired_err_code: u16, scid: u64, chan: &Channel<Signer>) -> (u16, Vec<u8>) {
+ debug_assert_eq!(desired_err_code & 0x1000, 0x1000);
+ if let Ok(upd) = self.get_channel_update_for_onion(scid, chan) {
+ let mut enc = VecWriter(Vec::with_capacity(upd.serialized_length() + 4));
+ if desired_err_code == 0x1000 | 20 {
+ // TODO: underspecified, follow https://github.com/lightning/bolts/issues/791
+ 0u16.write(&mut enc).expect("Writes cannot fail");
+ }
+ (upd.serialized_length() as u16).write(&mut enc).expect("Writes cannot fail");
+ upd.write(&mut enc).expect("Writes cannot fail");
+ (desired_err_code, enc.0)
+ } else {
+ // If we fail to get a unicast channel_update, it implies we don't yet have an SCID,
+ // which means we really shouldn't have gotten a payment to be forwarded over this
+ // channel yet, or if we did it's from a route hint. Either way, returning an error of
+ // PERM|no_such_channel should be fine.
+ (0x4000|10, Vec::new())
+ }
+ }
+
// Fail a list of HTLCs that were just freed from the holding cell. The HTLCs need to be
// failed backwards or, if they were one of our outgoing HTLCs, then their failure needs to
// be surfaced to the user.
let (failure_code, onion_failure_data) =
match self.channel_state.lock().unwrap().by_id.entry(channel_id) {
hash_map::Entry::Occupied(chan_entry) => {
- if let Ok(upd) = self.get_channel_update_for_unicast(&chan_entry.get()) {
- (0x1000|7, upd.encode_with_len())
- } else {
- (0x4000|10, Vec::new())
- }
+ self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
},
hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
};
// channel_update later through the announcement_signatures process for public
// channels, but there's no reason not to just inform our counterparty of our fees
// now.
- Some(events::MessageSendEvent::SendChannelUpdate {
- node_id: channel.get().get_counterparty_node_id(),
- msg: self.get_channel_update_for_unicast(channel.get()).unwrap(),
- })
+ if let Ok(msg) = self.get_channel_update_for_unicast(channel.get()) {
+ Some(events::MessageSendEvent::SendChannelUpdate {
+ node_id: channel.get().get_counterparty_node_id(),
+ msg,
+ })
+ } else { None }
} else { None };
chan_restoration_res = handle_chan_restoration_locked!(self, channel_lock, channel_state, channel, updates.raa, updates.commitment_update, updates.order, None, updates.accepted_htlcs, updates.funding_broadcastable, updates.funding_locked, updates.announcement_sigs);
if let Some(upd) = channel_update {
///
/// The `temporary_channel_id` parameter indicates which inbound channel should be accepted.
///
- /// [`Event::OpenChannelRequest`]: crate::util::events::Event::OpenChannelRequest
- pub fn accept_inbound_channel(&self, temporary_channel_id: &[u8; 32]) -> Result<(), APIError> {
+ /// For inbound channels, the `user_channel_id` parameter will be provided back in
+ /// [`Event::ChannelClosed::user_channel_id`] to allow tracking of which events correspond
+ /// with which `accept_inbound_channel` call.
+ ///
+ /// [`Event::OpenChannelRequest`]: events::Event::OpenChannelRequest
+ /// [`Event::ChannelClosed::user_channel_id`]: events::Event::ChannelClosed::user_channel_id
+ pub fn accept_inbound_channel(&self, temporary_channel_id: &[u8; 32], user_channel_id: u64) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let mut channel_state_lock = self.channel_state.lock().unwrap();
}
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
node_id: channel.get().get_counterparty_node_id(),
- msg: channel.get_mut().accept_inbound_channel(),
+ msg: channel.get_mut().accept_inbound_channel(user_channel_id),
});
}
hash_map::Entry::Vacant(_) => {
if !self.default_configuration.manually_accept_inbound_channels {
channel_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
node_id: counterparty_node_id.clone(),
- msg: channel.accept_inbound_channel(),
+ msg: channel.accept_inbound_channel(0),
});
} else {
let mut pending_events = self.pending_events.lock().unwrap();
counterparty_node_id: counterparty_node_id.clone(),
funding_satoshis: msg.funding_satoshis,
push_msat: msg.push_msat,
+ channel_type: channel.get_channel_type().clone(),
}
);
}
// channel_update here if the channel is not public, i.e. we're not sending an
// announcement_signatures.
log_trace!(self.logger, "Sending private initial channel_update for our counterparty on channel {}", log_bytes!(chan.get().channel_id()));
- channel_state.pending_msg_events.push(events::MessageSendEvent::SendChannelUpdate {
- node_id: counterparty_node_id.clone(),
- msg: self.get_channel_update_for_unicast(chan.get()).unwrap(),
- });
+ if let Ok(msg) = self.get_channel_update_for_unicast(chan.get()) {
+ channel_state.pending_msg_events.push(events::MessageSendEvent::SendChannelUpdate {
+ node_id: counterparty_node_id.clone(),
+ msg,
+ });
+ }
}
Ok(())
},
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
let reason = if (error_code & 0x1000) != 0 {
- if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- if error_code == 0x1000 | 20 {
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- }
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
- } else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (because no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
- }
+ let (real_code, error_data) = self.get_htlc_inbound_temp_fail_err_and_data(error_code, chan);
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, real_code, &error_data)
} else {
onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
// If the channel is in a usable state (ie the channel is not being shut
// down), send a unicast channel_update to our counterparty to make sure
// they have the latest channel parameters.
- channel_update = Some(events::MessageSendEvent::SendChannelUpdate {
- node_id: chan.get().get_counterparty_node_id(),
- msg: self.get_channel_update_for_unicast(chan.get()).unwrap(),
- });
+ if let Ok(msg) = self.get_channel_update_for_unicast(chan.get()) {
+ channel_update = Some(events::MessageSendEvent::SendChannelUpdate {
+ node_id: chan.get().get_counterparty_node_id(),
+ msg,
+ });
+ }
}
let need_lnd_workaround = chan.get_mut().workaround_lnd_bug_4006.take();
chan_restoration_res = handle_chan_restoration_locked!(
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
self.do_chain_event(Some(height), |channel| channel.transactions_confirmed(&block_hash, height, txdata, self.genesis_hash.clone(), self.get_our_node_id(), &self.logger)
.map(|(a, b)| (a, Vec::new(), b)));
+
+ let last_best_block_height = self.best_block.read().unwrap().height();
+ if height < last_best_block_height {
+ let timestamp = self.highest_seen_timestamp.load(Ordering::Acquire);
+ self.do_chain_event(Some(last_best_block_height), |channel| channel.best_block_updated(last_best_block_height, timestamp as u32, self.genesis_hash.clone(), self.get_our_node_id(), &self.logger));
+ }
}
fn best_block_updated(&self, header: &BlockHeader, height: u32) {
let res = f(channel);
if let Ok((funding_locked_opt, mut timed_out_pending_htlcs, announcement_sigs)) = res {
for (source, payment_hash) in timed_out_pending_htlcs.drain(..) {
- let chan_update = self.get_channel_update_for_unicast(&channel).map(|u| u.encode_with_len()).unwrap(); // Cannot add/recv HTLCs before we have a short_id so unwrap is safe
- timed_out_htlcs.push((source, payment_hash, HTLCFailReason::Reason {
- failure_code: 0x1000 | 14, // expiry_too_soon, or at least it is now
- data: chan_update,
+ let (failure_code, data) = self.get_htlc_inbound_temp_fail_err_and_data(0x1000|14 /* expiry_too_soon */, &channel);
+ timed_out_htlcs.push((source, payment_hash, HTLCFailReason::Reason {
+ failure_code, data,
}));
}
if let Some(funding_locked) = funding_locked_opt {
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 {
- node_id: channel.get_counterparty_node_id(),
- msg: self.get_channel_update_for_unicast(channel).unwrap(),
- });
+ if let Ok(msg) = self.get_channel_update_for_unicast(channel) {
+ pending_msg_events.push(events::MessageSendEvent::SendChannelUpdate {
+ node_id: channel.get_counterparty_node_id(),
+ msg,
+ });
+ }
} else {
log_trace!(self.logger, "Sending funding_locked WITHOUT channel_update for {}", log_bytes!(channel.channel_id()));
}
&events::MessageSendEvent::SendChannelRangeQuery { .. } => false,
&events::MessageSendEvent::SendShortIdsQuery { .. } => false,
&events::MessageSendEvent::SendReplyChannelRange { .. } => false,
+ &events::MessageSendEvent::SendGossipTimestampFilter { .. } => false,
}
});
}
}
}
} else {
+ {
+ // First check if we can advance the channel type and try again.
+ let mut channel_state = self.channel_state.lock().unwrap();
+ if let Some(chan) = channel_state.by_id.get_mut(&msg.channel_id) {
+ if chan.get_counterparty_node_id() != *counterparty_node_id {
+ return;
+ }
+ if let Ok(msg) = chan.maybe_handle_error_without_close(self.genesis_hash) {
+ channel_state.pending_msg_events.push(events::MessageSendEvent::SendOpenChannel {
+ node_id: *counterparty_node_id,
+ msg,
+ });
+ return;
+ }
+ }
+ }
+
// Untrusted messages from peer, we throw away the error if id points to a non-existent channel
let _ = self.force_close_channel_with_peer(&msg.channel_id, Some(counterparty_node_id), Some(&msg.data));
}
impl_writeable_tlv_based!(ChannelDetails, {
(1, inbound_scid_alias, option),
(2, channel_id, required),
+ (3, channel_type, option),
(4, counterparty, required),
(6, funding_txo, option),
(8, short_channel_id, option),
};
Ok(Self {
prev_hop: prev_hop.0.unwrap(),
+ timer_ticks: 0,
value,
onion_payload,
cltv_expiry,
let payer_pubkey = nodes[0].node.get_our_node_id();
let payee_pubkey = nodes[1].node.get_our_node_id();
- nodes[0].node.peer_connected(&payee_pubkey, &msgs::Init { features: InitFeatures::known() });
- nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
+ nodes[0].node.peer_connected(&payee_pubkey, &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
let route_params = RouteParameters {
let payer_pubkey = nodes[0].node.get_our_node_id();
let payee_pubkey = nodes[1].node.get_our_node_id();
- nodes[0].node.peer_connected(&payee_pubkey, &msgs::Init { features: InitFeatures::known() });
- nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
+ nodes[0].node.peer_connected(&payee_pubkey, &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
let route_params = RouteParameters {
});
let node_b_holder = NodeHolder { node: &node_b };
- node_a.peer_connected(&node_b.get_our_node_id(), &Init { features: InitFeatures::known() });
- node_b.peer_connected(&node_a.get_our_node_id(), &Init { features: InitFeatures::known() });
+ node_a.peer_connected(&node_b.get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
+ node_b.peer_connected(&node_a.get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None });
node_a.create_channel(node_b.get_our_node_id(), 8_000_000, 100_000_000, 42, None).unwrap();
node_b.handle_open_channel(&node_a.get_our_node_id(), InitFeatures::known(), &get_event_msg!(node_a_holder, MessageSendEvent::SendOpenChannel, node_b.get_our_node_id()));
node_a.handle_accept_channel(&node_b.get_our_node_id(), InitFeatures::known(), &get_event_msg!(node_b_holder, MessageSendEvent::SendAcceptChannel, node_a.get_our_node_id()));
// Byte 4
,
// Byte 5
- ChannelType,
+ ChannelType | SCIDPrivacy,
],
});
define_context!(NodeContext {
// Byte 4
,
// Byte 5
- ChannelType,
+ ChannelType | SCIDPrivacy,
// Byte 6
Keysend,
],
,
// Byte 3
,
+ // Byte 4
+ ,
+ // Byte 5
+ SCIDPrivacy,
],
optional_features: [
// Byte 0
,
// Byte 3
,
+ // Byte 4
+ ,
+ // Byte 5
+ ,
],
});
define_feature!(45, ChannelType, [InitContext, NodeContext],
"Feature flags for `option_channel_type`.", set_channel_type_optional,
set_channel_type_required, supports_channel_type, requires_channel_type);
+ define_feature!(47, SCIDPrivacy, [InitContext, NodeContext, ChannelTypeContext],
+ "Feature flags for only forwarding with SCID aliasing. Called `option_scid_alias` in the BOLTs",
+ set_scid_privacy_optional, set_scid_privacy_required, supports_scid_privacy, requires_scid_privacy);
+
define_feature!(55, Keysend, [NodeContext],
"Feature flags for keysend payments.", set_keysend_optional, set_keysend_required,
supports_keysend, requires_keysend);
assert!(InitFeatures::known().supports_shutdown_anysegwit());
assert!(NodeFeatures::known().supports_shutdown_anysegwit());
+ assert!(InitFeatures::known().supports_scid_privacy());
+ assert!(NodeFeatures::known().supports_scid_privacy());
+ assert!(!InitFeatures::known().requires_scid_privacy());
+ assert!(!NodeFeatures::known().requires_scid_privacy());
+
let mut init_features = InitFeatures::known();
assert!(init_features.initial_routing_sync());
init_features.clear_initial_routing_sync();
// - basic_mpp
// - opt_shutdown_anysegwit
// -
- // - option_channel_type
+ // - option_channel_type | option_scid_alias
assert_eq!(node_features.flags.len(), 6);
assert_eq!(node_features.flags[0], 0b00000010);
assert_eq!(node_features.flags[1], 0b01010001);
assert_eq!(node_features.flags[2], 0b00000010);
assert_eq!(node_features.flags[3], 0b00001000);
assert_eq!(node_features.flags[4], 0b00000000);
- assert_eq!(node_features.flags[5], 0b00100000);
+ assert_eq!(node_features.flags[5], 0b10100000);
}
// Check that cleared flags are kept blank when converting back:
use chain::channelmonitor::ChannelMonitor;
use chain::transaction::OutPoint;
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, PaymentId};
+use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, PaymentId, MIN_CLTV_EXPIRY_DELTA};
use routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
use routing::router::{PaymentParameters, Route, get_route};
use ln::features::{InitFeatures, InvoiceFeatures};
}
}
+/// Get an error message from the pending events queue.
+#[macro_export]
+macro_rules! get_err_msg {
+ ($node: expr, $node_id: expr) => {
+ {
+ let events = $node.node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ $crate::util::events::MessageSendEvent::HandleError {
+ action: $crate::ln::msgs::ErrorAction::SendErrorMessage { ref msg }, ref node_id
+ } => {
+ assert_eq!(*node_id, $node_id);
+ (*msg).clone()
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+ }
+}
+
/// Get a specific event from the pending events queue.
#[macro_export]
macro_rules! get_event {
let mut default_config = UserConfig::default();
// Set cltv_expiry_delta slightly lower to keep the final CLTV values inside one byte in our
// tests so that our script-length checks don't fail (see ACCEPTED_HTLC_SCRIPT_WEIGHT).
- default_config.channel_options.cltv_expiry_delta = 6*6;
+ default_config.channel_options.cltv_expiry_delta = MIN_CLTV_EXPIRY_DELTA;
default_config.channel_options.announced_channel = true;
default_config.peer_channel_config_limits.force_announced_channel_preference = false;
// When most of our tests were written, the default HTLC minimum was fixed at 1000.
for i in 0..node_count {
for j in (i+1)..node_count {
- nodes[i].node.peer_connected(&nodes[j].node.get_our_node_id(), &msgs::Init { features: cfgs[j].features.clone() });
- nodes[j].node.peer_connected(&nodes[i].node.get_our_node_id(), &msgs::Init { features: cfgs[i].features.clone() });
+ nodes[i].node.peer_connected(&nodes[j].node.get_our_node_id(), &msgs::Init { features: cfgs[j].features.clone(), remote_network_address: None });
+ nodes[j].node.peer_connected(&nodes[i].node.get_our_node_id(), &msgs::Init { features: cfgs[i].features.clone(), remote_network_address: None });
}
}
/// pending_htlc_adds includes both the holding cell and in-flight update_add_htlcs, whereas
/// for claims/fails they are separated out.
pub fn reconnect_nodes<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, send_funding_locked: (bool, bool), pending_htlc_adds: (i64, i64), pending_htlc_claims: (usize, usize), pending_htlc_fails: (usize, usize), pending_cell_htlc_claims: (usize, usize), pending_cell_htlc_fails: (usize, usize), pending_raa: (bool, bool)) {
- node_a.node.peer_connected(&node_b.node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ node_a.node.peer_connected(&node_b.node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(node_a, node_b);
- node_b.node.peer_connected(&node_a.node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ node_b.node.peer_connected(&node_a.node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(node_b, node_a);
if send_funding_locked.0 {
let events_2 = nodes[1].node.get_and_clear_pending_msg_events();
assert!(events_2.is_empty());
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let as_reestablish = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
// nodes[0] hasn't yet received a funding_locked, so it only sends that on reconnect.
assert!(found_announcement);
}
+#[test]
+fn test_funding_locked_without_best_block_updated() {
+ // Previously, if we were offline when a funding transaction was locked in, and then we came
+ // back online, calling best_block_updated once followed by transactions_confirmed, we'd not
+ // generate a funding_locked until a later best_block_updated. This tests that we generate the
+ // funding_locked immediately instead.
+ 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, &[None, None]);
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+ *nodes[0].connect_style.borrow_mut() = ConnectStyle::BestBlockFirstSkippingBlocks;
+
+ let funding_tx = create_chan_between_nodes_with_value_init(&nodes[0], &nodes[1], 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
+
+ let conf_height = nodes[0].best_block_info().1 + 1;
+ connect_blocks(&nodes[0], CHAN_CONFIRM_DEPTH);
+ let block_txn = [funding_tx];
+ let conf_txn: Vec<_> = block_txn.iter().enumerate().collect();
+ let conf_block_header = nodes[0].get_block_header(conf_height);
+ nodes[0].node.transactions_confirmed(&conf_block_header, &conf_txn[..], conf_height);
+
+ // Ensure nodes[0] generates a funding_locked after the transactions_confirmed
+ let as_funding_locked = get_event_msg!(nodes[0], MessageSendEvent::SendFundingLocked, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_funding_locked(&nodes[0].node.get_our_node_id(), &as_funding_locked);
+}
+
#[test]
fn test_drop_messages_peer_disconnect_dual_htlc() {
// Test that we can handle reconnecting when both sides of a channel have pending
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 1);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 1);
assert_eq!(nodes[0].node.list_channels().len(), 1);
check_added_monitors!(nodes[0], 1);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &reestablish_1[0]);
assert_eq!(nodes[0].node.list_channels().len(), 1);
check_added_monitors!(nodes[0], 1);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &reestablish_1[0]);
//... and we can even still claim the payment!
claim_payment(&nodes[2], &[&nodes[0], &nodes[1]], our_payment_preimage);
- nodes[3].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[3].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish = get_event_msg!(nodes[3], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
- nodes[0].node.peer_connected(&nodes[3].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[3].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
nodes[0].node.handle_channel_reestablish(&nodes[3].node.get_our_node_id(), &reestablish);
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(msg_events.len(), 1);
//Disconnect and Reconnect
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 1);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 1);
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &reestablish_2[0]);
check_added_monitors!(nodes[0], 1);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_0 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
}
}
// Reconnect peers
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
assert_eq!(reestablish_1.len(), 3);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
assert_eq!(reestablish_2.len(), 3);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(manually_accept_conf.clone())]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
- nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, Some(manually_accept_conf)).unwrap();
+ let temp_channel_id = nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, Some(manually_accept_conf)).unwrap();
let res = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &res);
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
- nodes[1].node.accept_inbound_channel(&temporary_channel_id).unwrap();
+ nodes[1].node.accept_inbound_channel(&temporary_channel_id, 23).unwrap();
}
_ => panic!("Unexpected event"),
}
}
_ => panic!("Unexpected event"),
}
+
+ nodes[1].node.force_close_channel(&temp_channel_id).unwrap();
+
+ let close_msg_ev = nodes[1].node.get_and_clear_pending_msg_events();
+ assert_eq!(close_msg_ev.len(), 1);
+
+ let events = nodes[1].node.get_and_clear_pending_events();
+ match events[0] {
+ Event::ChannelClosed { user_channel_id, .. } => {
+ assert_eq!(user_channel_id, 23);
+ }
+ _ => panic!("Unexpected event"),
+ }
}
#[test]
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
- nodes[1].node.accept_inbound_channel(&temporary_channel_id).unwrap();
- let api_res = nodes[1].node.accept_inbound_channel(&temporary_channel_id);
+ nodes[1].node.accept_inbound_channel(&temporary_channel_id, 0).unwrap();
+ let api_res = nodes[1].node.accept_inbound_channel(&temporary_channel_id, 0);
match api_res {
Err(APIError::APIMisuseError { err }) => {
assert_eq!(err, "The channel isn't currently awaiting to be accepted.");
let node = create_network(1, &node_cfg, &node_chanmgr)[0].node;
let unknown_channel_id = [0; 32];
- let api_res = node.accept_inbound_channel(&unknown_channel_id);
+ let api_res = node.accept_inbound_channel(&unknown_channel_id, 0);
match api_res {
Err(APIError::ChannelUnavailable { err }) => {
assert_eq!(err, "Can't accept a channel that doesn't exist");
let payer_pubkey = nodes[0].node.get_our_node_id();
let payee_pubkey = nodes[1].node.get_our_node_id();
- nodes[0].node.peer_connected(&payee_pubkey, &msgs::Init { features: InitFeatures::known() });
- nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
+ nodes[0].node.peer_connected(&payee_pubkey, &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
let route_params = RouteParameters {
pub struct Init {
/// The relevant features which the sender supports
pub features: InitFeatures,
+ /// The receipient's network address. This adds the option to report a remote IP address
+ /// back to a connecting peer using the init message. A node can decide to use that information
+ /// to discover a potential update to its public IPv4 address (NAT) and use
+ /// that for a node_announcement update message containing the new address.
+ pub remote_network_address: Option<NetAddress>,
}
/// An error message to be sent or received from a peer
/// Called when a connection is established with a peer. This can be used to
/// perform routing table synchronization using a strategy defined by the
/// implementor.
- fn sync_routing_table(&self, their_node_id: &PublicKey, init: &Init);
+ fn peer_connected(&self, their_node_id: &PublicKey, init: &Init);
/// Handles the reply of a query we initiated to learn about channels
/// for a given range of blocks. We can expect to receive one or more
/// replies to a single query.
// global_features gets the bottom 13 bits of our features, and local_features gets all of
// our relevant feature bits. This keeps us compatible with old nodes.
self.features.write_up_to_13(w)?;
- self.features.write(w)
+ self.features.write(w)?;
+ encode_tlv_stream!(w, {
+ (3, self.remote_network_address, option)
+ });
+ Ok(())
}
}
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let global_features: InitFeatures = Readable::read(r)?;
let features: InitFeatures = Readable::read(r)?;
+ let mut remote_network_address: Option<NetAddress> = None;
+ decode_tlv_stream!(r, {
+ (3, remote_network_address, option)
+ });
Ok(Init {
features: features.or(global_features),
+ remote_network_address,
})
}
}
fn encoding_init() {
assert_eq!(msgs::Init {
features: InitFeatures::from_le_bytes(vec![0xFF, 0xFF, 0xFF]),
+ remote_network_address: None,
}.encode(), hex::decode("00023fff0003ffffff").unwrap());
assert_eq!(msgs::Init {
features: InitFeatures::from_le_bytes(vec![0xFF]),
+ remote_network_address: None,
}.encode(), hex::decode("0001ff0001ff").unwrap());
assert_eq!(msgs::Init {
features: InitFeatures::from_le_bytes(vec![]),
+ remote_network_address: None,
}.encode(), hex::decode("00000000").unwrap());
+
+ let init_msg = msgs::Init { features: InitFeatures::from_le_bytes(vec![]),
+ remote_network_address: Some(msgs::NetAddress::IPv4 {
+ addr: [127, 0, 0, 1],
+ port: 1000,
+ }),
+ };
+ let encoded_value = init_msg.encode();
+ let target_value = hex::decode("000000000307017f00000103e8").unwrap();
+ assert_eq!(encoded_value, target_value);
+ assert_eq!(msgs::Init::read(&mut Cursor::new(&target_value)).unwrap(), init_msg);
}
#[test]
.expected_htlc_error_data(0x4000 | 15, &error_data);
expect_payment_failed_conditions!(nodes[0], payment_hash, true, fail_conditions);
}
-
use chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS};
use chain::transaction::OutPoint;
use chain::keysinterface::KeysInterface;
-use ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, PaymentId, PaymentSendFailure};
+use ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, MPP_TIMEOUT_TICKS, PaymentId, PaymentSendFailure};
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::msgs;
use ln::msgs::ChannelMessageHandler;
claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
}
+fn do_mpp_receive_timeout(send_partial_mpp: bool) {
+ let chanmon_cfgs = create_chanmon_cfgs(4);
+ let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+ let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+
+ let chan_1_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
+ let chan_2_id = create_announced_chan_between_nodes(&nodes, 0, 2, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
+ let chan_3_id = create_announced_chan_between_nodes(&nodes, 1, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
+ let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
+
+ let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[3], 100_000);
+ let path = route.paths[0].clone();
+ route.paths.push(path);
+ route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
+ route.paths[0][0].short_channel_id = chan_1_id;
+ route.paths[0][1].short_channel_id = chan_3_id;
+ route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
+ route.paths[1][0].short_channel_id = chan_2_id;
+ route.paths[1][1].short_channel_id = chan_4_id;
+
+ // Initiate the MPP payment.
+ let _ = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ check_added_monitors!(nodes[0], 2); // one monitor per path
+ let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 2);
+
+ // Pass half of the payment along the first path.
+ pass_along_path(&nodes[0], &[&nodes[1], &nodes[3]], 200_000, payment_hash, Some(payment_secret), events.remove(0), false, None);
+
+ if send_partial_mpp {
+ // Time out the partial MPP
+ for _ in 0..MPP_TIMEOUT_TICKS {
+ nodes[3].node.timer_tick_occurred();
+ }
+
+ // Failed HTLC from node 3 -> 1
+ expect_pending_htlcs_forwardable!(nodes[3]);
+ let htlc_fail_updates_3_1 = get_htlc_update_msgs!(nodes[3], nodes[1].node.get_our_node_id());
+ assert_eq!(htlc_fail_updates_3_1.update_fail_htlcs.len(), 1);
+ nodes[1].node.handle_update_fail_htlc(&nodes[3].node.get_our_node_id(), &htlc_fail_updates_3_1.update_fail_htlcs[0]);
+ check_added_monitors!(nodes[3], 1);
+ commitment_signed_dance!(nodes[1], nodes[3], htlc_fail_updates_3_1.commitment_signed, false);
+
+ // Failed HTLC from node 1 -> 0
+ expect_pending_htlcs_forwardable!(nodes[1]);
+ let htlc_fail_updates_1_0 = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ assert_eq!(htlc_fail_updates_1_0.update_fail_htlcs.len(), 1);
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_fail_updates_1_0.update_fail_htlcs[0]);
+ check_added_monitors!(nodes[1], 1);
+ commitment_signed_dance!(nodes[0], nodes[1], htlc_fail_updates_1_0.commitment_signed, false);
+
+ expect_payment_failed_conditions!(nodes[0], payment_hash, false, PaymentFailedConditions::new().mpp_parts_remain().expected_htlc_error_data(23, &[][..]));
+ } else {
+ // Pass half of the payment along the second path.
+ pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 200_000, payment_hash, Some(payment_secret), events.remove(0), true, None);
+
+ // Even after MPP_TIMEOUT_TICKS we should not timeout the MPP if we have all the parts
+ for _ in 0..MPP_TIMEOUT_TICKS {
+ nodes[3].node.timer_tick_occurred();
+ }
+
+ claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
+ }
+}
+
+#[test]
+fn mpp_receive_timeout() {
+ do_mpp_receive_timeout(true);
+ do_mpp_receive_timeout(false);
+}
+
#[test]
fn retry_expired_payment() {
let chanmon_cfgs = create_chanmon_cfgs(3);
assert_eq!(as_broadcasted_txn[0], as_commitment_tx);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known()});
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
// Now nodes[1] should send a channel reestablish, which nodes[0] will respond to with an
// error, as the channel has hit the chain.
- nodes[1].node.peer_connected(&nodes[0].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::known(), remote_network_address: None });
let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish);
let as_err = nodes[0].node.get_and_clear_pending_msg_events();
use ln::features::InitFeatures;
use ln::msgs;
-use ln::msgs::{ChannelMessageHandler, LightningError, RoutingMessageHandler};
+use ln::msgs::{ChannelMessageHandler, LightningError, NetAddress, RoutingMessageHandler};
use ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager};
use util::ser::{VecWriter, Writeable, Writer};
use ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep};
fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) ->
Vec<(msgs::ChannelAnnouncement, Option<msgs::ChannelUpdate>, Option<msgs::ChannelUpdate>)> { Vec::new() }
fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec<msgs::NodeAnnouncement> { Vec::new() }
- fn sync_routing_table(&self, _their_node_id: &PublicKey, _init: &msgs::Init) {}
+ fn peer_connected(&self, _their_node_id: &PublicKey, _init: &msgs::Init) {}
fn handle_reply_channel_range(&self, _their_node_id: &PublicKey, _msg: msgs::ReplyChannelRange) -> Result<(), LightningError> { Ok(()) }
fn handle_reply_short_channel_ids_end(&self, _their_node_id: &PublicKey, _msg: msgs::ReplyShortChannelIdsEnd) -> Result<(), LightningError> { Ok(()) }
fn handle_query_channel_range(&self, _their_node_id: &PublicKey, _msg: msgs::QueryChannelRange) -> Result<(), LightningError> { Ok(()) }
channel_encryptor: PeerChannelEncryptor,
their_node_id: Option<PublicKey>,
their_features: Option<InitFeatures>,
+ their_net_address: Option<NetAddress>,
pending_outbound_buffer: LinkedList<Vec<u8>>,
pending_outbound_buffer_first_msg_offset: usize,
/// lifetimes). Other times you can afford a reference, which is more efficient, in which case
/// SimpleRefPeerManager is the more appropriate type. Defining these type aliases prevents
/// issues such as overly long function definitions.
+///
+/// (C-not exported) as Arcs don't make sense in bindings
pub type SimpleArcPeerManager<SD, M, T, F, C, L> = PeerManager<SD, Arc<SimpleArcChannelManager<M, T, F, L>>, Arc<NetGraphMsgHandler<Arc<NetworkGraph>, Arc<C>, Arc<L>>>, Arc<L>, Arc<IgnoringMessageHandler>>;
/// SimpleRefPeerManager is a type alias for a PeerManager reference, and is the reference
/// usage of lightning-net-tokio (since tokio::spawn requires parameters with static lifetimes).
/// But if this is not necessary, using a reference is more efficient. Defining these type aliases
/// helps with issues such as long function definitions.
+///
+/// (C-not exported) as Arcs don't make sense in bindings
pub type SimpleRefPeerManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, SD, M, T, F, C, L> = PeerManager<SD, SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, M, T, F, L>, &'e NetGraphMsgHandler<&'g NetworkGraph, &'h C, &'f L>, &'f L, IgnoringMessageHandler>;
/// A PeerManager manages a set of peers, described by their [`SocketDescriptor`] and marshalls
}
}
+/// A function used to filter out local or private addresses
+/// https://www.iana.org./assignments/ipv4-address-space/ipv4-address-space.xhtml
+/// https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
+fn filter_addresses(ip_address: Option<NetAddress>) -> Option<NetAddress> {
+ match ip_address{
+ // For IPv4 range 10.0.0.0 - 10.255.255.255 (10/8)
+ Some(NetAddress::IPv4{addr: [10, _, _, _], port: _}) => None,
+ // For IPv4 range 0.0.0.0 - 0.255.255.255 (0/8)
+ Some(NetAddress::IPv4{addr: [0, _, _, _], port: _}) => None,
+ // For IPv4 range 100.64.0.0 - 100.127.255.255 (100.64/10)
+ Some(NetAddress::IPv4{addr: [100, 64..=127, _, _], port: _}) => None,
+ // For IPv4 range 127.0.0.0 - 127.255.255.255 (127/8)
+ Some(NetAddress::IPv4{addr: [127, _, _, _], port: _}) => None,
+ // For IPv4 range 169.254.0.0 - 169.254.255.255 (169.254/16)
+ Some(NetAddress::IPv4{addr: [169, 254, _, _], port: _}) => None,
+ // For IPv4 range 172.16.0.0 - 172.31.255.255 (172.16/12)
+ Some(NetAddress::IPv4{addr: [172, 16..=31, _, _], port: _}) => None,
+ // For IPv4 range 192.168.0.0 - 192.168.255.255 (192.168/16)
+ Some(NetAddress::IPv4{addr: [192, 168, _, _], port: _}) => None,
+ // For IPv4 range 192.88.99.0 - 192.88.99.255 (192.88.99/24)
+ Some(NetAddress::IPv4{addr: [192, 88, 99, _], port: _}) => None,
+ // For IPv6 range 2000:0000:0000:0000:0000:0000:0000:0000 - 3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff (2000::/3)
+ Some(NetAddress::IPv6{addr: [0x20..=0x3F, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], port: _}) => ip_address,
+ // For remaining addresses
+ Some(NetAddress::IPv6{addr: _, port: _}) => None,
+ Some(..) => ip_address,
+ None => None,
+ }
+}
+
impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, L: Deref, CMH: Deref> PeerManager<Descriptor, CM, RM, L, CMH> where
CM::Target: ChannelMessageHandler,
RM::Target: RoutingMessageHandler,
SecretKey::from_slice(&Sha256::from_engine(ephemeral_hash).into_inner()).expect("You broke SHA-256!")
}
- /// Indicates a new outbound connection has been established to a node with the given node_id.
+ /// Indicates a new outbound connection has been established to a node with the given node_id
+ /// and an optional remote network address.
+ ///
+ /// The remote network address adds the option to report a remote IP address back to a connecting
+ /// peer using the init message.
+ /// The user should pass the remote network address of the host they are connected to.
+ ///
/// Note that if an Err is returned here you MUST NOT call socket_disconnected for the new
/// descriptor but must disconnect the connection immediately.
///
/// [`socket_disconnected()`].
///
/// [`socket_disconnected()`]: PeerManager::socket_disconnected
- pub fn new_outbound_connection(&self, their_node_id: PublicKey, descriptor: Descriptor) -> Result<Vec<u8>, PeerHandleError> {
+ pub fn new_outbound_connection(&self, their_node_id: PublicKey, descriptor: Descriptor, remote_network_address: Option<NetAddress>) -> Result<Vec<u8>, PeerHandleError> {
let mut peer_encryptor = PeerChannelEncryptor::new_outbound(their_node_id.clone(), self.get_ephemeral_key());
let res = peer_encryptor.get_act_one().to_vec();
let pending_read_buffer = [0; 50].to_vec(); // Noise act two is 50 bytes
channel_encryptor: peer_encryptor,
their_node_id: None,
their_features: None,
+ their_net_address: remote_network_address,
pending_outbound_buffer: LinkedList::new(),
pending_outbound_buffer_first_msg_offset: 0,
Ok(res)
}
- /// Indicates a new inbound connection has been established.
+ /// Indicates a new inbound connection has been established to a node with an optional remote
+ /// network address.
+ ///
+ /// The remote network address adds the option to report a remote IP address back to a connecting
+ /// peer using the init message.
+ /// The user should pass the remote network address of the host they are connected to.
///
/// May refuse the connection by returning an Err, but will never write bytes to the remote end
/// (outbound connector always speaks first). Note that if an Err is returned here you MUST NOT
/// [`socket_disconnected()`].
///
/// [`socket_disconnected()`]: PeerManager::socket_disconnected
- pub fn new_inbound_connection(&self, descriptor: Descriptor) -> Result<(), PeerHandleError> {
+ pub fn new_inbound_connection(&self, descriptor: Descriptor, remote_network_address: Option<NetAddress>) -> Result<(), PeerHandleError> {
let peer_encryptor = PeerChannelEncryptor::new_inbound(&self.our_node_secret);
let pending_read_buffer = [0; 50].to_vec(); // Noise act one is 50 bytes
channel_encryptor: peer_encryptor,
their_node_id: None,
their_features: None,
+ their_net_address: remote_network_address,
pending_outbound_buffer: LinkedList::new(),
pending_outbound_buffer_first_msg_offset: 0,
peer.their_node_id = Some(their_node_id);
insert_node_id!();
let features = InitFeatures::known();
- let resp = msgs::Init { features };
+ let resp = msgs::Init { features, remote_network_address: filter_addresses(peer.their_net_address.clone())};
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
},
peer.their_node_id = Some(their_node_id);
insert_node_id!();
let features = InitFeatures::known();
- let resp = msgs::Init { features };
+ let resp = msgs::Init { features, remote_network_address: filter_addresses(peer.their_net_address.clone())};
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
},
return Err(PeerHandleError{ no_connection_possible: true }.into());
}
- self.message_handler.route_handler.sync_routing_table(&peer.their_node_id.unwrap(), &msg);
+ self.message_handler.route_handler.peer_connected(&peer.their_node_id.unwrap(), &msg);
self.message_handler.chan_handler.peer_connected(&peer.their_node_id.unwrap(), &msg);
peer.their_features = Some(msg.features);
msg.sync_complete);
self.enqueue_message(get_peer_for_forwarding!(node_id), msg);
}
+ MessageSendEvent::SendGossipTimestampFilter { ref node_id, ref msg } => {
+ self.enqueue_message(get_peer_for_forwarding!(node_id), msg);
+ }
}
}
#[cfg(test)]
mod tests {
- use ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
+ use ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler, filter_addresses};
use ln::msgs;
+ use ln::msgs::NetAddress;
use util::events;
use util::test_utils;
let a_id = PublicKey::from_secret_key(&secp_ctx, &peer_a.our_node_secret);
let mut fd_a = FileDescriptor { fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())) };
let mut fd_b = FileDescriptor { fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())) };
- let initial_data = peer_b.new_outbound_connection(a_id, fd_b.clone()).unwrap();
- peer_a.new_inbound_connection(fd_a.clone()).unwrap();
+ let initial_data = peer_b.new_outbound_connection(a_id, fd_b.clone(), None).unwrap();
+ peer_a.new_inbound_connection(fd_a.clone(), None).unwrap();
assert_eq!(peer_a.read_event(&mut fd_a, &initial_data).unwrap(), false);
peer_a.process_events();
assert_eq!(peer_b.read_event(&mut fd_b, &fd_a.outbound_data.lock().unwrap().split_off(0)).unwrap(), false);
let a_id = PublicKey::from_secret_key(&secp_ctx, &peers[0].our_node_secret);
let mut fd_a = FileDescriptor { fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())) };
let mut fd_b = FileDescriptor { fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())) };
- let initial_data = peers[1].new_outbound_connection(a_id, fd_b.clone()).unwrap();
- peers[0].new_inbound_connection(fd_a.clone()).unwrap();
+ let initial_data = peers[1].new_outbound_connection(a_id, fd_b.clone(), None).unwrap();
+ peers[0].new_inbound_connection(fd_a.clone(), None).unwrap();
// If we get a single timer tick before completion, that's fine
assert_eq!(peers[0].peers.lock().unwrap().peers.len(), 1);
assert!(peers[0].read_event(&mut fd_a, &fd_b.outbound_data.lock().unwrap().split_off(0)).is_err());
}
+
+ #[test]
+ fn test_filter_addresses(){
+ // Tests the filter_addresses function.
+
+ // For (10/8)
+ let ip_address = NetAddress::IPv4{addr: [10, 0, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [10, 0, 255, 201], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [10, 255, 255, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For (0/8)
+ let ip_address = NetAddress::IPv4{addr: [0, 0, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [0, 0, 255, 187], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [0, 255, 255, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For (100.64/10)
+ let ip_address = NetAddress::IPv4{addr: [100, 64, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [100, 78, 255, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [100, 127, 255, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For (127/8)
+ let ip_address = NetAddress::IPv4{addr: [127, 0, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [127, 65, 73, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [127, 255, 255, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For (169.254/16)
+ let ip_address = NetAddress::IPv4{addr: [169, 254, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [169, 254, 221, 101], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [169, 254, 255, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For (172.16/12)
+ let ip_address = NetAddress::IPv4{addr: [172, 16, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [172, 27, 101, 23], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [172, 31, 255, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For (192.168/16)
+ let ip_address = NetAddress::IPv4{addr: [192, 168, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [192, 168, 205, 159], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [192, 168, 255, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For (192.88.99/24)
+ let ip_address = NetAddress::IPv4{addr: [192, 88, 99, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [192, 88, 99, 140], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv4{addr: [192, 88, 99, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For other IPv4 addresses
+ let ip_address = NetAddress::IPv4{addr: [188, 255, 99, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), Some(ip_address.clone()));
+ let ip_address = NetAddress::IPv4{addr: [123, 8, 129, 14], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), Some(ip_address.clone()));
+ let ip_address = NetAddress::IPv4{addr: [2, 88, 9, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), Some(ip_address.clone()));
+
+ // For (2000::/3)
+ let ip_address = NetAddress::IPv6{addr: [32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), Some(ip_address.clone()));
+ let ip_address = NetAddress::IPv6{addr: [45, 34, 209, 190, 0, 123, 55, 34, 0, 0, 3, 27, 201, 0, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), Some(ip_address.clone()));
+ let ip_address = NetAddress::IPv6{addr: [63, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), Some(ip_address.clone()));
+
+ // For other IPv6 addresses
+ let ip_address = NetAddress::IPv6{addr: [24, 240, 12, 32, 0, 0, 0, 0, 20, 97, 0, 32, 121, 254, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv6{addr: [68, 23, 56, 63, 0, 0, 2, 7, 75, 109, 0, 39, 0, 0, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+ let ip_address = NetAddress::IPv6{addr: [101, 38, 140, 230, 100, 0, 30, 98, 0, 26, 0, 0, 57, 96, 0, 0], port: 1000};
+ assert_eq!(filter_addresses(Some(ip_address.clone())), None);
+
+ // For (None)
+ assert_eq!(filter_addresses(None), None);
+ }
}
use chain::Watch;
use chain::channelmonitor::ChannelMonitor;
+use chain::keysinterface::{Recipient, KeysInterface};
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 ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, OptionalField};
use util::enforcing_trait_impls::EnforcingSigner;
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
use util::config::UserConfig;
use ln::functional_test_utils::*;
+use bitcoin::blockdata::constants::genesis_block;
use bitcoin::hash_types::BlockHash;
+use bitcoin::hashes::Hash;
+use bitcoin::hashes::sha256d::Hash as Sha256dHash;
+use bitcoin::network::constants::Network;
+use bitcoin::secp256k1::Secp256k1;
#[test]
fn test_priv_forwarding_rejection() {
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() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
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);
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() });
+ nodes[1].node.peer_connected(&nodes[2].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
+ nodes[2].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
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);
// Note that because we never send a duplicate funding_locked we can't send a payment through
// the 0xdeadbeef SCID alias.
}
+
+#[test]
+fn test_scid_privacy_on_pub_channel() {
+ // Tests rejecting the scid_privacy feature for public channels and that we don't ever try to
+ // send them.
+ 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, &[None, None]);
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let mut scid_privacy_cfg = test_default_channel_config();
+ scid_privacy_cfg.channel_options.announced_channel = true;
+ scid_privacy_cfg.own_channel_config.negotiate_scid_privacy = true;
+ nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, Some(scid_privacy_cfg)).unwrap();
+ let mut open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+
+ assert!(!open_channel.channel_type.as_ref().unwrap().supports_scid_privacy()); // we ignore `negotiate_scid_privacy` on pub channels
+ open_channel.channel_type.as_mut().unwrap().set_scid_privacy_required();
+ assert_eq!(open_channel.channel_flags & 1, 1); // The `announce_channel` bit is set.
+
+ nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_channel);
+ let err = get_err_msg!(nodes[1], nodes[0].node.get_our_node_id());
+ assert_eq!(err.data, "SCID Alias/Privacy Channel Type cannot be set on a public channel");
+}
+
+#[test]
+fn test_scid_privacy_negotiation() {
+ // Tests of the negotiation of SCID alias and falling back to non-SCID-alias if our
+ // counterparty doesn't support it.
+ 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, &[None, None]);
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let mut scid_privacy_cfg = test_default_channel_config();
+ scid_privacy_cfg.channel_options.announced_channel = false;
+ scid_privacy_cfg.own_channel_config.negotiate_scid_privacy = true;
+ nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, Some(scid_privacy_cfg)).unwrap();
+
+ let init_open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+ assert!(init_open_channel.channel_type.as_ref().unwrap().supports_scid_privacy());
+ assert!(nodes[0].node.list_channels()[0].channel_type.is_none()); // channel_type is none until counterparty accepts
+
+ // now simulate nodes[1] responding with an Error message, indicating it doesn't understand
+ // SCID alias.
+ nodes[0].node.handle_error(&nodes[1].node.get_our_node_id(), &msgs::ErrorMessage {
+ channel_id: init_open_channel.temporary_channel_id,
+ data: "Yo, no SCID aliases, no privacy here!".to_string()
+ });
+ assert!(nodes[0].node.list_channels()[0].channel_type.is_none()); // channel_type is none until counterparty accepts
+
+ let second_open_channel = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+ assert!(!second_open_channel.channel_type.as_ref().unwrap().supports_scid_privacy());
+ nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &second_open_channel);
+ nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()));
+
+ let events = nodes[0].node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ Event::FundingGenerationReady { .. } => {},
+ _ => panic!("Unexpected event"),
+ }
+
+ assert!(!nodes[0].node.list_channels()[0].channel_type.as_ref().unwrap().supports_scid_privacy());
+ assert!(!nodes[1].node.list_channels()[0].channel_type.as_ref().unwrap().supports_scid_privacy());
+}
+
+#[test]
+fn test_inbound_scid_privacy() {
+ // Tests accepting channels with the scid_privacy feature and rejecting forwards using the
+ // channel's real SCID as required by the channel feature.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let mut accept_forward_cfg = test_default_channel_config();
+ accept_forward_cfg.accept_forwards_to_priv_channels = true;
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(accept_forward_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, 0, InitFeatures::known(), InitFeatures::known());
+
+ let mut no_announce_cfg = test_default_channel_config();
+ no_announce_cfg.channel_options.announced_channel = false;
+ no_announce_cfg.own_channel_config.negotiate_scid_privacy = true;
+ nodes[1].node.create_channel(nodes[2].node.get_our_node_id(), 100_000, 10_000, 42, Some(no_announce_cfg)).unwrap();
+ let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[2].node.get_our_node_id());
+
+ assert!(open_channel.channel_type.as_ref().unwrap().requires_scid_privacy());
+
+ 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], 100_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 bs_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()));
+ let bs_update = 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(), &bs_funding_locked);
+ let cs_update = get_event_msg!(nodes[2], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id());
+
+ nodes[1].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &cs_update);
+ nodes[2].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &bs_update);
+
+ // Now we can pay just fine using the SCID alias nodes[2] gave to nodes[1]...
+
+ let last_hop = nodes[2].node.list_usable_channels();
+ let mut 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.clone(), 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);
+
+ // ... but if we try to pay using the real SCID, nodes[1] will just tell us they don't know
+ // what channel we're talking about.
+ hop_hints[0].0[0].short_channel_id = last_hop[0].short_channel_id.unwrap();
+
+ let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], hop_hints, 100_000, 42);
+ assert_eq!(route_2.paths[0][1].short_channel_id, last_hop[0].short_channel_id.unwrap());
+ nodes[0].node.send_payment(&route_2, payment_hash_2, &Some(payment_secret_2)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+
+ let payment_event = SendEvent::from_node(&nodes[0]);
+ assert_eq!(nodes[1].node.get_our_node_id(), payment_event.node_id);
+ 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, true, true);
+
+ nodes[1].logger.assert_log_regex("lightning::ln::channelmanager".to_string(), regex::Regex::new(r"Refusing to forward over real channel SCID as our counterparty requested").unwrap(), 1);
+
+ let mut updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
+ commitment_signed_dance!(nodes[0], nodes[1], updates.commitment_signed, false);
+
+ expect_payment_failed_conditions!(nodes[0], payment_hash_2, false,
+ PaymentFailedConditions::new().blamed_scid(last_hop[0].short_channel_id.unwrap())
+ .blamed_chan_closed(true).expected_htlc_error_data(0x4000|10, &[0; 0]));
+}
+
+#[test]
+fn test_scid_alias_returned() {
+ // Tests that when we fail an HTLC (in this case due to attempting to forward more than the
+ // channel's available balance) we use the correct (in this case the aliased) SCID in the
+ // channel_update which is returned in the onion to the sender.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let mut accept_forward_cfg = test_default_channel_config();
+ accept_forward_cfg.accept_forwards_to_priv_channels = true;
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(accept_forward_cfg), None]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 0, InitFeatures::known(), InitFeatures::known());
+ create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000, 0, InitFeatures::known(), InitFeatures::known());
+
+ let last_hop = nodes[2].node.list_usable_channels();
+ let mut 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 (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], hop_hints, 10_000, 42);
+ assert_eq!(route.paths[0][1].short_channel_id, nodes[2].node.list_usable_channels()[0].inbound_scid_alias.unwrap());
+
+ route.paths[0][1].fee_msat = 10_000_000; // Overshoot the last channel's value
+
+ // Route the HTLC through to the destination.
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+ let as_updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &as_updates.update_add_htlcs[0]);
+ commitment_signed_dance!(nodes[1], nodes[0], &as_updates.commitment_signed, false, true);
+
+ expect_pending_htlcs_forwardable!(nodes[1]);
+ expect_pending_htlcs_forwardable!(nodes[1]);
+ check_added_monitors!(nodes[1], 1);
+
+ let bs_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.update_fail_htlcs[0]);
+ commitment_signed_dance!(nodes[0], nodes[1], bs_updates.commitment_signed, false, true);
+
+ // Build the expected channel update
+ let contents = msgs::UnsignedChannelUpdate {
+ chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ short_channel_id: last_hop[0].inbound_scid_alias.unwrap(),
+ timestamp: 21,
+ flags: 1,
+ cltv_expiry_delta: accept_forward_cfg.channel_options.cltv_expiry_delta,
+ htlc_minimum_msat: 1_000,
+ htlc_maximum_msat: OptionalField::Present(1_000_000), // Defaults to 10% of the channel value
+ fee_base_msat: last_hop[0].counterparty.forwarding_info.as_ref().unwrap().fee_base_msat,
+ fee_proportional_millionths: last_hop[0].counterparty.forwarding_info.as_ref().unwrap().fee_proportional_millionths,
+ excess_data: Vec::new(),
+ };
+ let msg_hash = Sha256dHash::hash(&contents.encode()[..]);
+ let signature = Secp256k1::new().sign(&hash_to_message!(&msg_hash[..]), &nodes[1].keys_manager.get_node_secret(Recipient::Node).unwrap());
+ let msg = msgs::ChannelUpdate { signature, contents };
+
+ expect_payment_failed_conditions!(nodes[0], payment_hash, false,
+ PaymentFailedConditions::new().blamed_scid(last_hop[0].inbound_scid_alias.unwrap())
+ .blamed_chan_closed(false).expected_htlc_error_data(0x1000|7, &msg.encode_with_len()));
+
+ route.paths[0][1].fee_msat = 10_000; // Reset to the correct payment amount
+ route.paths[0][0].fee_msat = 0; // But set fee paid to the middle hop to 0
+
+ // Route the HTLC through to the destination.
+ nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+ let as_updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &as_updates.update_add_htlcs[0]);
+ commitment_signed_dance!(nodes[1], nodes[0], &as_updates.commitment_signed, false, true);
+
+ let bs_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.update_fail_htlcs[0]);
+ commitment_signed_dance!(nodes[0], nodes[1], bs_updates.commitment_signed, false, true);
+
+ let mut err_data = Vec::new();
+ err_data.extend_from_slice(&10_000u64.to_be_bytes());
+ err_data.extend_from_slice(&msg.encode_with_len());
+ expect_payment_failed_conditions!(nodes[0], payment_hash, false,
+ PaymentFailedConditions::new().blamed_scid(last_hop[0].inbound_scid_alias.unwrap())
+ .blamed_chan_closed(false).expected_htlc_error_data(0x1000|12, &err_data));
+}
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let node_0_reestablish = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let node_1_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(), &node_0_reestablish);
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
- nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
let node_1_2nd_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
- nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty() });
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
if recv_count == 0 {
// If all closing_signeds weren't delivered we can just resume where we left off...
let node_0_2nd_reestablish = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
let mut reader = io::Cursor::new(buffer);
let decoded_msg = read(&mut reader, &IgnoringMessageHandler{}).unwrap();
match decoded_msg {
- Message::Init(msgs::Init { features }) => {
+ Message::Init(msgs::Init { features, .. }) => {
assert!(features.supports_variable_length_onion());
assert!(features.supports_upfront_shutdown_script());
assert!(features.supports_gossip_queries());
use chain::Access;
use ln::features::{ChannelFeatures, NodeFeatures};
use ln::msgs::{DecodeError, ErrorAction, Init, LightningError, RoutingMessageHandler, NetAddress, MAX_VALUE_MSAT};
-use ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, OptionalField};
+use ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, OptionalField, GossipTimestampFilter};
use ln::msgs::{QueryChannelRange, ReplyChannelRange, QueryShortChannelIds, ReplyShortChannelIdsEnd};
use ln::msgs;
use util::ser::{Writeable, Readable, Writer};
/// to request gossip messages for each channel. The sync is considered complete
/// when the final reply_scids_end message is received, though we are not
/// tracking this directly.
- fn sync_routing_table(&self, their_node_id: &PublicKey, init_msg: &Init) {
-
+ fn peer_connected(&self, their_node_id: &PublicKey, init_msg: &Init) {
// We will only perform a sync with peers that support gossip_queries.
if !init_msg.features.supports_gossip_queries() {
return ();
}
- // Check if we need to perform a full synchronization with this peer
- if !self.should_request_full_sync(&their_node_id) {
- return ();
+ // The lightning network's gossip sync system is completely broken in numerous ways.
+ //
+ // Given no broadly-available set-reconciliation protocol, the only reasonable approach is
+ // to do a full sync from the first few peers we connect to, and then receive gossip
+ // updates from all our peers normally.
+ //
+ // Originally, we could simply tell a peer to dump us the entire gossip table on startup,
+ // wasting lots of bandwidth but ensuring we have the full network graph. After the initial
+ // dump peers would always send gossip and we'd stay up-to-date with whatever our peer has
+ // seen.
+ //
+ // In order to reduce the bandwidth waste, "gossip queries" were introduced, allowing you
+ // to ask for the SCIDs of all channels in your peer's routing graph, and then only request
+ // channel data which you are missing. Except there was no way at all to identify which
+ // `channel_update`s you were missing, so you still had to request everything, just in a
+ // very complicated way with some queries instead of just getting the dump.
+ //
+ // Later, an option was added to fetch the latest timestamps of the `channel_update`s to
+ // make efficient sync possible, however it has yet to be implemented in lnd, which makes
+ // relying on it useless.
+ //
+ // After gossip queries were introduced, support for receiving a full gossip table dump on
+ // connection was removed from several nodes, making it impossible to get a full sync
+ // without using the "gossip queries" messages.
+ //
+ // Once you opt into "gossip queries" the only way to receive any gossip updates that a
+ // peer receives after you connect, you must send a `gossip_timestamp_filter` message. This
+ // message, as the name implies, tells the peer to not forward any gossip messages with a
+ // timestamp older than a given value (not the time the peer received the filter, but the
+ // timestamp in the update message, which is often hours behind when the peer received the
+ // message).
+ //
+ // Obnoxiously, `gossip_timestamp_filter` isn't *just* a filter, but its also a request for
+ // your peer to send you the full routing graph (subject to the filter). Thus, in order to
+ // tell a peer to send you any updates as it sees them, you have to also ask for the full
+ // routing graph to be synced. If you set a timestamp filter near the current time, peers
+ // will simply not forward any new updates they see to you which were generated some time
+ // ago (which is not uncommon). If you instead set a timestamp filter near 0 (or two weeks
+ // ago), you will always get the full routing graph from all your peers.
+ //
+ // Most lightning nodes today opt to simply turn off receiving gossip data which only
+ // propagated some time after it was generated, and, worse, often disable gossiping with
+ // several peers after their first connection. The second behavior can cause gossip to not
+ // propagate fully if there are cuts in the gossiping subgraph.
+ //
+ // In an attempt to cut a middle ground between always fetching the full graph from all of
+ // our peers and never receiving gossip from peers at all, we send all of our peers a
+ // `gossip_timestamp_filter`, with the filter time set either two weeks ago or an hour ago.
+ //
+ // For no-std builds, we bury our head in the sand and do a full sync on each connection.
+ let should_request_full_sync = self.should_request_full_sync(&their_node_id);
+ #[allow(unused_mut, unused_assignments)]
+ let mut gossip_start_time = 0;
+ #[cfg(feature = "std")]
+ {
+ gossip_start_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs();
+ if should_request_full_sync {
+ gossip_start_time -= 60 * 60 * 24 * 7 * 2; // 2 weeks ago
+ } else {
+ gossip_start_time -= 60 * 60; // an hour ago
+ }
}
- let first_blocknum = 0;
- let number_of_blocks = 0xffffffff;
- log_debug!(self.logger, "Sending query_channel_range peer={}, first_blocknum={}, number_of_blocks={}", log_pubkey!(their_node_id), first_blocknum, number_of_blocks);
let mut pending_events = self.pending_events.lock().unwrap();
- pending_events.push(MessageSendEvent::SendChannelRangeQuery {
+ pending_events.push(MessageSendEvent::SendGossipTimestampFilter {
node_id: their_node_id.clone(),
- msg: QueryChannelRange {
+ msg: GossipTimestampFilter {
chain_hash: self.network_graph.genesis_hash,
- first_blocknum,
- number_of_blocks,
+ first_timestamp: gossip_start_time as u32, // 2106 issue!
+ timestamp_range: u32::max_value(),
},
});
}
- /// Statelessly processes a reply to a channel range query by immediately
- /// sending an SCID query with SCIDs in the reply. To keep this handler
- /// stateless, it does not validate the sequencing of replies for multi-
- /// reply ranges. It does not validate whether the reply(ies) cover the
- /// queried range. It also does not filter SCIDs to only those in the
- /// original query range. We also do not validate that the chain_hash
- /// matches the chain_hash of the NetworkGraph. Any chan_ann message that
- /// does not match our chain_hash will be rejected when the announcement is
- /// processed.
- fn handle_reply_channel_range(&self, their_node_id: &PublicKey, msg: ReplyChannelRange) -> Result<(), LightningError> {
- log_debug!(self.logger, "Handling reply_channel_range peer={}, first_blocknum={}, number_of_blocks={}, sync_complete={}, scids={}", log_pubkey!(their_node_id), msg.first_blocknum, msg.number_of_blocks, msg.sync_complete, msg.short_channel_ids.len(),);
-
- log_debug!(self.logger, "Sending query_short_channel_ids peer={}, batch_size={}", log_pubkey!(their_node_id), msg.short_channel_ids.len());
- let mut pending_events = self.pending_events.lock().unwrap();
- pending_events.push(MessageSendEvent::SendShortIdsQuery {
- node_id: their_node_id.clone(),
- msg: QueryShortChannelIds {
- chain_hash: msg.chain_hash,
- short_channel_ids: msg.short_channel_ids,
- }
- });
-
+ fn handle_reply_channel_range(&self, _their_node_id: &PublicKey, _msg: ReplyChannelRange) -> Result<(), LightningError> {
+ // We don't make queries, so should never receive replies. If, in the future, the set
+ // reconciliation extensions to gossip queries become broadly supported, we should revert
+ // this code to its state pre-0.0.106.
Ok(())
}
- /// When an SCID query is initiated the remote peer will begin streaming
- /// gossip messages. In the event of a failure, we may have received
- /// some channel information. Before trying with another peer, the
- /// caller should update its set of SCIDs that need to be queried.
- fn handle_reply_short_channel_ids_end(&self, their_node_id: &PublicKey, msg: ReplyShortChannelIdsEnd) -> Result<(), LightningError> {
- log_debug!(self.logger, "Handling reply_short_channel_ids_end peer={}, full_information={}", log_pubkey!(their_node_id), msg.full_information);
-
- // If the remote node does not have up-to-date information for the
- // chain_hash they will set full_information=false. We can fail
- // the result and try again with a different peer.
- if !msg.full_information {
- return Err(LightningError {
- err: String::from("Received reply_short_channel_ids_end with no information"),
- action: ErrorAction::IgnoreError
- });
- }
-
+ fn handle_reply_short_channel_ids_end(&self, _their_node_id: &PublicKey, _msg: ReplyShortChannelIdsEnd) -> Result<(), LightningError> {
+ // We don't make queries, so should never receive replies. If, in the future, the set
+ // reconciliation extensions to gossip queries become broadly supported, we should revert
+ // this code to its state pre-0.0.106.
Ok(())
}
}
}
}
- macro_rules! maybe_update_channel_info {
- ( $target: expr, $src_node: expr) => {
+ macro_rules! check_update_latest {
+ ($target: expr) => {
if let Some(existing_chan_info) = $target.as_ref() {
// The timestamp field is somewhat of a misnomer - the BOLTs use it to
// order updates to ensure you always have the latest one, only
} else {
chan_was_enabled = false;
}
+ }
+ }
+ macro_rules! get_new_channel_info {
+ () => { {
let last_update_message = if msg.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY
{ full_msg.cloned() } else { None };
},
last_update_message
};
- $target = Some(updated_channel_update_info);
- }
+ Some(updated_channel_update_info)
+ } }
}
let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
if msg.flags & 1 == 1 {
dest_node_id = channel.node_one.clone();
+ check_update_latest!(channel.two_to_one);
if let Some((sig, ctx)) = sig_info {
secp_verify_sig!(ctx, &msg_hash, &sig, &PublicKey::from_slice(channel.node_two.as_slice()).map_err(|_| LightningError{
err: "Couldn't parse source node pubkey".to_owned(),
action: ErrorAction::IgnoreAndLog(Level::Debug)
})?, "channel_update");
}
- maybe_update_channel_info!(channel.two_to_one, channel.node_two);
+ channel.two_to_one = get_new_channel_info!();
} else {
dest_node_id = channel.node_two.clone();
+ check_update_latest!(channel.one_to_two);
if let Some((sig, ctx)) = sig_info {
secp_verify_sig!(ctx, &msg_hash, &sig, &PublicKey::from_slice(channel.node_one.as_slice()).map_err(|_| LightningError{
err: "Couldn't parse destination node pubkey".to_owned(),
action: ErrorAction::IgnoreAndLog(Level::Debug)
})?, "channel_update");
}
- maybe_update_channel_info!(channel.one_to_two, channel.node_one);
+ channel.one_to_two = get_new_channel_info!();
}
}
}
use routing::network_graph::{NetGraphMsgHandler, NetworkGraph, NetworkUpdate, MAX_EXCESS_BYTES_FOR_RELAY};
use ln::msgs::{Init, OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate,
- ReplyChannelRange, ReplyShortChannelIdsEnd, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
+ ReplyChannelRange, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
use util::test_utils;
use util::logger::Logger;
use util::ser::{Readable, Writeable};
}
#[test]
+ #[cfg(feature = "std")]
fn calling_sync_routing_table() {
+ use std::time::{SystemTime, UNIX_EPOCH};
+
let network_graph = create_network_graph();
let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(&network_graph);
let node_privkey_1 = &SecretKey::from_slice(&[42; 32]).unwrap();
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_privkey_1);
let chain_hash = genesis_block(Network::Testnet).header.block_hash();
- let first_blocknum = 0;
- let number_of_blocks = 0xffff_ffff;
// It should ignore if gossip_queries feature is not enabled
{
- let init_msg = Init { features: InitFeatures::known().clear_gossip_queries() };
- net_graph_msg_handler.sync_routing_table(&node_id_1, &init_msg);
+ let init_msg = Init { features: InitFeatures::known().clear_gossip_queries(), remote_network_address: None };
+ net_graph_msg_handler.peer_connected(&node_id_1, &init_msg);
let events = net_graph_msg_handler.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 0);
}
- // It should send a query_channel_message with the correct information
+ // It should send a gossip_timestamp_filter with the correct information
{
- let init_msg = Init { features: InitFeatures::known() };
- net_graph_msg_handler.sync_routing_table(&node_id_1, &init_msg);
+ let init_msg = Init { features: InitFeatures::known(), remote_network_address: None };
+ net_graph_msg_handler.peer_connected(&node_id_1, &init_msg);
let events = net_graph_msg_handler.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
match &events[0] {
- MessageSendEvent::SendChannelRangeQuery{ node_id, msg } => {
+ MessageSendEvent::SendGossipTimestampFilter{ node_id, msg } => {
assert_eq!(node_id, &node_id_1);
assert_eq!(msg.chain_hash, chain_hash);
- assert_eq!(msg.first_blocknum, first_blocknum);
- assert_eq!(msg.number_of_blocks, number_of_blocks);
+ let expected_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs();
+ assert!((msg.first_timestamp as u64) >= expected_timestamp - 60*60*24*7*2);
+ assert!((msg.first_timestamp as u64) < expected_timestamp - 60*60*24*7*2 + 10);
+ assert_eq!(msg.timestamp_range, u32::max_value());
},
_ => panic!("Expected MessageSendEvent::SendChannelRangeQuery")
};
}
-
- // It should not enqueue a query when should_request_full_sync return false.
- // The initial implementation allows syncing with the first 5 peers after
- // which should_request_full_sync will return false
- {
- let network_graph = create_network_graph();
- let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(&network_graph);
- let init_msg = Init { features: InitFeatures::known() };
- for n in 1..7 {
- let node_privkey = &SecretKey::from_slice(&[n; 32]).unwrap();
- let node_id = PublicKey::from_secret_key(&secp_ctx, node_privkey);
- net_graph_msg_handler.sync_routing_table(&node_id, &init_msg);
- let events = net_graph_msg_handler.get_and_clear_pending_msg_events();
- if n <= 5 {
- assert_eq!(events.len(), 1);
- } else {
- assert_eq!(events.len(), 0);
- }
-
- }
- }
- }
-
- #[test]
- fn handling_reply_channel_range() {
- let network_graph = create_network_graph();
- let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(&network_graph);
- let node_privkey_1 = &SecretKey::from_slice(&[42; 32]).unwrap();
- let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_privkey_1);
-
- let chain_hash = genesis_block(Network::Testnet).header.block_hash();
-
- // Test receipt of a single reply that should enqueue an SCID query
- // matching the SCIDs in the reply
- {
- let result = net_graph_msg_handler.handle_reply_channel_range(&node_id_1, ReplyChannelRange {
- chain_hash,
- sync_complete: true,
- first_blocknum: 0,
- number_of_blocks: 2000,
- short_channel_ids: vec![
- 0x0003e0_000000_0000, // 992x0x0
- 0x0003e8_000000_0000, // 1000x0x0
- 0x0003e9_000000_0000, // 1001x0x0
- 0x0003f0_000000_0000, // 1008x0x0
- 0x00044c_000000_0000, // 1100x0x0
- 0x0006e0_000000_0000, // 1760x0x0
- ],
- });
- assert!(result.is_ok());
-
- // We expect to emit a query_short_channel_ids message with the received scids
- let events = net_graph_msg_handler.get_and_clear_pending_msg_events();
- assert_eq!(events.len(), 1);
- match &events[0] {
- MessageSendEvent::SendShortIdsQuery { node_id, msg } => {
- assert_eq!(node_id, &node_id_1);
- assert_eq!(msg.chain_hash, chain_hash);
- assert_eq!(msg.short_channel_ids, vec![
- 0x0003e0_000000_0000, // 992x0x0
- 0x0003e8_000000_0000, // 1000x0x0
- 0x0003e9_000000_0000, // 1001x0x0
- 0x0003f0_000000_0000, // 1008x0x0
- 0x00044c_000000_0000, // 1100x0x0
- 0x0006e0_000000_0000, // 1760x0x0
- ]);
- },
- _ => panic!("expected MessageSendEvent::SendShortIdsQuery"),
- }
- }
- }
-
- #[test]
- fn handling_reply_short_channel_ids() {
- let network_graph = create_network_graph();
- let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(&network_graph);
- let node_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
- let node_id = PublicKey::from_secret_key(&secp_ctx, node_privkey);
-
- let chain_hash = genesis_block(Network::Testnet).header.block_hash();
-
- // Test receipt of a successful reply
- {
- let result = net_graph_msg_handler.handle_reply_short_channel_ids_end(&node_id, ReplyShortChannelIdsEnd {
- chain_hash,
- full_information: true,
- });
- assert!(result.is_ok());
- }
-
- // Test receipt of a reply that indicates the peer does not maintain up-to-date information
- // for the chain_hash requested in the query.
- {
- let result = net_graph_msg_handler.handle_reply_short_channel_ids_end(&node_id, ReplyShortChannelIdsEnd {
- chain_hash,
- full_information: false,
- });
- assert!(result.is_err());
- assert_eq!(result.err().unwrap().err, "Received reply_short_channel_ids_end with no information");
- }
}
#[test]
impl cmp::Ord for RouteGraphNode {
fn cmp(&self, other: &RouteGraphNode) -> cmp::Ordering {
let other_score = cmp::max(other.lowest_fee_to_peer_through_node, other.path_htlc_minimum_msat)
- .checked_add(other.path_penalty_msat)
- .unwrap_or_else(|| u64::max_value());
+ .saturating_add(other.path_penalty_msat);
let self_score = cmp::max(self.lowest_fee_to_peer_through_node, self.path_htlc_minimum_msat)
- .checked_add(self.path_penalty_msat)
- .unwrap_or_else(|| u64::max_value());
+ .saturating_add(self.path_penalty_msat);
other_score.cmp(&self_score).then_with(|| other.node_id.cmp(&self.node_id))
}
}
self.hops.last().unwrap().0.fee_msat
}
+ fn get_path_penalty_msat(&self) -> u64 {
+ self.hops.first().map(|h| h.0.path_penalty_msat).unwrap_or(u64::max_value())
+ }
+
fn get_total_fee_paid_msat(&self) -> u64 {
if self.hops.len() < 1 {
return 0;
pub(crate) fn get_route<L: Deref, S: Score>(
our_node_pubkey: &PublicKey, payment_params: &PaymentParameters, network_graph: &ReadOnlyNetworkGraph,
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
- logger: L, scorer: &S, _random_seed_bytes: &[u8; 32]
+ logger: L, scorer: &S, random_seed_bytes: &[u8; 32]
) -> Result<Route, LightningError>
where L::Target: Logger {
let payee_node_id = NodeId::from_pubkey(&payment_params.payee_pubkey);
// - when we want to stop looking for new paths.
let mut already_collected_value_msat = 0;
+ for (_, channels) in first_hop_targets.iter_mut() {
+ // Sort the first_hops channels to the same node(s) in priority order of which channel we'd
+ // most like to use.
+ //
+ // First, if channels are below `recommended_value_msat`, sort them in descending order,
+ // preferring larger channels to avoid splitting the payment into more MPP parts than is
+ // required.
+ //
+ // Second, because simply always sorting in descending order would always use our largest
+ // available outbound capacity, needlessly fragmenting our available channel capacities,
+ // sort channels above `recommended_value_msat` in ascending order, preferring channels
+ // which have enough, but not too much, capacity for the payment.
+ channels.sort_unstable_by(|chan_a, chan_b| {
+ if chan_b.outbound_capacity_msat < recommended_value_msat || chan_a.outbound_capacity_msat < recommended_value_msat {
+ // Sort in descending order
+ chan_b.outbound_capacity_msat.cmp(&chan_a.outbound_capacity_msat)
+ } else {
+ // Sort in ascending order
+ chan_a.outbound_capacity_msat.cmp(&chan_b.outbound_capacity_msat)
+ }
+ });
+ }
+
log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payment_params.payee_pubkey, our_node_pubkey, final_value_msat);
macro_rules! add_entry {
.entry(short_channel_id)
.or_insert_with(|| $candidate.effective_capacity().as_msat());
- // It is tricky to substract $next_hops_fee_msat from available liquidity here.
+ // It is tricky to subtract $next_hops_fee_msat from available liquidity here.
// It may be misleading because we might later choose to reduce the value transferred
// over these channels, and the channel which was insufficient might become sufficient.
// Worst case: we drop a good channel here because it can't cover the high following
.checked_sub(2*MEDIAN_HOP_CLTV_EXPIRY_DELTA)
.unwrap_or(payment_params.max_total_cltv_expiry_delta - final_cltv_expiry_delta);
let hop_total_cltv_delta = ($next_hops_cltv_delta as u32)
- .checked_add($candidate.cltv_expiry_delta())
- .unwrap_or(u32::max_value());
+ .saturating_add($candidate.cltv_expiry_delta());
let doesnt_exceed_cltv_delta_limit = hop_total_cltv_delta <= max_total_cltv_expiry_delta;
let value_contribution_msat = cmp::min(available_value_contribution_msat, $next_hops_value_contribution);
}
}
- let path_penalty_msat = $next_hops_path_penalty_msat.checked_add(
- scorer.channel_penalty_msat(short_channel_id, amount_to_transfer_over_msat, *available_liquidity_msat,
- &$src_node_id, &$dest_node_id)).unwrap_or_else(|| u64::max_value());
+ let path_penalty_msat = $next_hops_path_penalty_msat.saturating_add(
+ scorer.channel_penalty_msat(short_channel_id, amount_to_transfer_over_msat,
+ *available_liquidity_msat, &$src_node_id, &$dest_node_id));
let new_graph_node = RouteGraphNode {
node_id: $src_node_id,
lowest_fee_to_peer_through_node: total_fee_msat,
// the fees included in $next_hops_path_htlc_minimum_msat, but also
// can't use something that may decrease on future hops.
let old_cost = cmp::max(old_entry.total_fee_msat, old_entry.path_htlc_minimum_msat)
- .checked_add(old_entry.path_penalty_msat)
- .unwrap_or_else(|| u64::max_value());
+ .saturating_add(old_entry.path_penalty_msat);
let new_cost = cmp::max(total_fee_msat, path_htlc_minimum_msat)
- .checked_add(path_penalty_msat)
- .unwrap_or_else(|| u64::max_value());
+ .saturating_add(path_penalty_msat);
if !old_entry.was_processed && new_cost < old_cost {
targets.push(new_graph_node);
.unwrap_or_else(|| CandidateRouteHop::PrivateHop { hint: hop });
let capacity_msat = candidate.effective_capacity().as_msat();
aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
- .checked_add(scorer.channel_penalty_msat(hop.short_channel_id, final_value_msat, capacity_msat, &source, &target))
- .unwrap_or_else(|| u64::max_value());
+ .saturating_add(scorer.channel_penalty_msat(hop.short_channel_id, final_value_msat, capacity_msat, &source, &target));
aggregate_next_hops_cltv_delta = aggregate_next_hops_cltv_delta
- .checked_add(hop.cltv_expiry_delta as u32)
- .unwrap_or_else(|| u32::max_value());
+ .saturating_add(hop.cltv_expiry_delta as u32);
if !add_entry!(candidate, source, target, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat, aggregate_next_hops_cltv_delta) {
// If this hop was not used then there is no use checking the preceding hops
}
// Sort by total fees and take the best paths.
- payment_paths.sort_by_key(|path| path.get_total_fee_paid_msat());
+ payment_paths.sort_unstable_by_key(|path| path.get_total_fee_paid_msat());
if payment_paths.len() > 50 {
payment_paths.truncate(50);
}
// Draw multiple sufficient routes by randomly combining the selected paths.
let mut drawn_routes = Vec::new();
- for i in 0..payment_paths.len() {
+ let mut prng = ChaCha20::new(random_seed_bytes, &[0u8; 12]);
+ let mut random_index_bytes = [0u8; ::core::mem::size_of::<usize>()];
+
+ let num_permutations = payment_paths.len();
+ for _ in 0..num_permutations {
let mut cur_route = Vec::<PaymentPath>::new();
let mut aggregate_route_value_msat = 0;
// Step (6).
- // TODO: real random shuffle
- // Currently just starts with i_th and goes up to i-1_th in a looped way.
- let cur_payment_paths = [&payment_paths[i..], &payment_paths[..i]].concat();
+ // Do a Fisher-Yates shuffle to create a random permutation of the payment paths
+ for cur_index in (1..payment_paths.len()).rev() {
+ prng.process_in_place(&mut random_index_bytes);
+ let random_index = usize::from_be_bytes(random_index_bytes).wrapping_rem(cur_index+1);
+ payment_paths.swap(cur_index, random_index);
+ }
// Step (7).
- for payment_path in cur_payment_paths {
+ for payment_path in &payment_paths {
cur_route.push(payment_path.clone());
aggregate_route_value_msat += payment_path.get_value_msat();
if aggregate_route_value_msat > final_value_msat {
// also makes routing more reliable.
let mut overpaid_value_msat = aggregate_route_value_msat - final_value_msat;
- // First, drop some expensive low-value paths entirely if possible.
- // Sort by value so that we drop many really-low values first, since
- // fewer paths is better: the payment is less likely to fail.
- // TODO: this could also be optimized by also sorting by feerate_per_sat_routed,
- // so that the sender pays less fees overall. And also htlc_minimum_msat.
- cur_route.sort_by_key(|path| path.get_value_msat());
+ // First, we drop some expensive low-value paths entirely if possible, since fewer
+ // paths is better: the payment is less likely to fail. In order to do so, we sort
+ // by value and fall back to total fees paid, i.e., in case of equal values we
+ // prefer lower cost paths.
+ cur_route.sort_unstable_by(|a, b| {
+ a.get_value_msat().cmp(&b.get_value_msat())
+ // Reverse ordering for fees, so we drop higher-fee paths first
+ .then_with(|| b.get_total_fee_paid_msat().saturating_add(b.get_path_penalty_msat())
+ .cmp(&a.get_total_fee_paid_msat().saturating_add(a.get_path_penalty_msat())))
+ });
+
// We should make sure that at least 1 path left.
let mut paths_left = cur_route.len();
cur_route.retain(|path| {
assert!(cur_route.len() > 0);
// Step (8).
- // Now, substract the overpaid value from the most-expensive path.
+ // Now, subtract the overpaid value from the most-expensive path.
// TODO: this could also be optimized by also sorting by feerate_per_sat_routed,
// so that the sender pays less fees overall. And also htlc_minimum_msat.
- cur_route.sort_by_key(|path| { path.hops.iter().map(|hop| hop.0.candidate.fees().proportional_millionths as u64).sum::<u64>() });
+ cur_route.sort_unstable_by_key(|path| { path.hops.iter().map(|hop| hop.0.candidate.fees().proportional_millionths as u64).sum::<u64>() });
let expensive_payment_path = cur_route.first_mut().unwrap();
- // We already dropped all the small channels above, meaning all the
- // remaining channels are larger than remaining overpaid_value_msat.
+
+ // We already dropped all the small value paths above, meaning all the
+ // remaining paths are larger than remaining overpaid_value_msat.
// Thus, this can't be negative.
let expensive_path_new_value_msat = expensive_payment_path.get_value_msat() - overpaid_value_msat;
expensive_payment_path.update_value_and_recompute_fees(expensive_path_new_value_msat);
// Step (9).
// Select the best route by lowest total fee.
- drawn_routes.sort_by_key(|paths| paths.iter().map(|path| path.get_total_fee_paid_msat()).sum::<u64>());
+ drawn_routes.sort_unstable_by_key(|paths| paths.iter().map(|path| path.get_total_fee_paid_msat()).sum::<u64>());
let mut selected_paths = Vec::<Vec<Result<RouteHop, LightningError>>>::new();
for payment_path in drawn_routes.first().unwrap() {
let mut path = payment_path.hops.iter().map(|(payment_hop, node_features)| {
for path in route.paths.iter_mut() {
let mut shadow_ctlv_expiry_delta_offset: u32 = 0;
- // Choose the last publicly known node as the starting point for the random walk
- if let Some(starting_hop) = path.iter().rev().find(|h| network_nodes.contains_key(&NodeId::from_pubkey(&h.pubkey))) {
- let mut cur_node_id = NodeId::from_pubkey(&starting_hop.pubkey);
+ // Remember the last three nodes of the random walk and avoid looping back on them.
+ // Init with the last three nodes from the actual path, if possible.
+ let mut nodes_to_avoid: [NodeId; 3] = [NodeId::from_pubkey(&path.last().unwrap().pubkey),
+ NodeId::from_pubkey(&path.get(path.len().saturating_sub(2)).unwrap().pubkey),
+ NodeId::from_pubkey(&path.get(path.len().saturating_sub(3)).unwrap().pubkey)];
+
+ // Choose the last publicly known node as the starting point for the random walk.
+ let mut cur_hop: Option<NodeId> = None;
+ let mut path_nonce = [0u8; 12];
+ if let Some(starting_hop) = path.iter().rev()
+ .find(|h| network_nodes.contains_key(&NodeId::from_pubkey(&h.pubkey))) {
+ cur_hop = Some(NodeId::from_pubkey(&starting_hop.pubkey));
+ path_nonce.copy_from_slice(&cur_hop.unwrap().as_slice()[..12]);
+ }
+
+ // Init PRNG with the path-dependant nonce, which is static for private paths.
+ let mut prng = ChaCha20::new(random_seed_bytes, &path_nonce);
+ let mut random_path_bytes = [0u8; ::core::mem::size_of::<usize>()];
- // Init PRNG with path nonce
- let mut path_nonce = [0u8; 12];
- path_nonce.copy_from_slice(&cur_node_id.as_slice()[..12]);
- let mut prng = ChaCha20::new(random_seed_bytes, &path_nonce);
- let mut random_path_bytes = [0u8; ::core::mem::size_of::<usize>()];
+ // Pick a random path length in [1 .. 3]
+ prng.process_in_place(&mut random_path_bytes);
+ let random_walk_length = usize::from_be_bytes(random_path_bytes).wrapping_rem(3).wrapping_add(1);
- // Pick a random path length in [1 .. 3]
- prng.process_in_place(&mut random_path_bytes);
- let random_walk_length = usize::from_be_bytes(random_path_bytes).wrapping_rem(3).wrapping_add(1);
+ for random_hop in 0..random_walk_length {
+ // If we don't find a suitable offset in the public network graph, we default to
+ // MEDIAN_HOP_CLTV_EXPIRY_DELTA.
+ let mut random_hop_offset = MEDIAN_HOP_CLTV_EXPIRY_DELTA;
- for _random_hop in 0..random_walk_length {
+ if let Some(cur_node_id) = cur_hop {
if let Some(cur_node) = network_nodes.get(&cur_node_id) {
- // Randomly choose the next hop
+ // Randomly choose the next unvisited hop.
prng.process_in_place(&mut random_path_bytes);
- if let Some(random_channel) = usize::from_be_bytes(random_path_bytes).checked_rem(cur_node.channels.len())
+ if let Some(random_channel) = usize::from_be_bytes(random_path_bytes)
+ .checked_rem(cur_node.channels.len())
.and_then(|index| cur_node.channels.get(index))
.and_then(|id| network_channels.get(id)) {
random_channel.as_directed_from(&cur_node_id).map(|(dir_info, next_id)| {
- dir_info.direction().map(|channel_update_info|
- shadow_ctlv_expiry_delta_offset = shadow_ctlv_expiry_delta_offset
- .checked_add(channel_update_info.cltv_expiry_delta.into())
- .unwrap_or(shadow_ctlv_expiry_delta_offset));
- cur_node_id = *next_id;
+ if !nodes_to_avoid.iter().any(|x| x == next_id) {
+ nodes_to_avoid[random_hop] = *next_id;
+ dir_info.direction().map(|channel_update_info| {
+ random_hop_offset = channel_update_info.cltv_expiry_delta.into();
+ cur_hop = Some(*next_id);
+ });
+ }
});
}
}
}
- } else {
- // If the entire path is private, choose a random offset from multiples of
- // MEDIAN_HOP_CLTV_EXPIRY_DELTA
- let mut prng = ChaCha20::new(random_seed_bytes, &[0u8; 8]);
- let mut random_bytes = [0u8; 4];
- prng.process_in_place(&mut random_bytes);
- let random_walk_length = u32::from_be_bytes(random_bytes).wrapping_rem(3).wrapping_add(1);
- shadow_ctlv_expiry_delta_offset = random_walk_length * MEDIAN_HOP_CLTV_EXPIRY_DELTA;
+
+ shadow_ctlv_expiry_delta_offset = shadow_ctlv_expiry_delta_offset
+ .checked_add(random_hop_offset)
+ .unwrap_or(shadow_ctlv_expiry_delta_offset);
}
// Limit the total offset to reduce the worst-case locked liquidity timevalue
forwarding_info: None,
},
funding_txo: Some(OutPoint { txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), index: 0 }),
+ channel_type: None,
short_channel_id,
inbound_scid_alias: None,
channel_value_satoshis: 0,
assert_eq!(route.paths[1][0].short_channel_id, 2);
assert_eq!(route.paths[1][0].fee_msat, 50_000);
}
+
+ {
+ // If we have a bunch of outbound channels to the same node, where most are not
+ // sufficient to pay the full payment, but one is, we should default to just using the
+ // one single channel that has sufficient balance, avoiding MPP.
+ //
+ // If we have several options above the 3xpayment value threshold, we should pick the
+ // smallest of them, avoiding further fragmenting our available outbound balance to
+ // this node.
+ let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&[
+ &get_channel_details(Some(2), nodes[0], InitFeatures::known(), 50_000),
+ &get_channel_details(Some(3), nodes[0], InitFeatures::known(), 50_000),
+ &get_channel_details(Some(5), nodes[0], InitFeatures::known(), 50_000),
+ &get_channel_details(Some(6), nodes[0], InitFeatures::known(), 300_000),
+ &get_channel_details(Some(7), nodes[0], InitFeatures::known(), 50_000),
+ &get_channel_details(Some(8), nodes[0], InitFeatures::known(), 50_000),
+ &get_channel_details(Some(9), nodes[0], InitFeatures::known(), 50_000),
+ &get_channel_details(Some(4), nodes[0], InitFeatures::known(), 1_000_000),
+ ]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
+ assert_eq!(route.paths.len(), 1);
+ assert_eq!(route.paths[0].len(), 1);
+
+ assert_eq!(route.paths[0][0].pubkey, nodes[0]);
+ assert_eq!(route.paths[0][0].short_channel_id, 6);
+ assert_eq!(route.paths[0][0].fee_msat, 100_000);
+ }
}
#[test]
funding_txo: Some(OutPoint {
txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), index: 0
}),
+ channel_type: None,
short_channel_id: Some(1),
inbound_scid_alias: None,
channel_value_satoshis: 10_000_000,
penalty_msat: u64,
}
-impl_writeable_tlv_based!(FixedPenaltyScorer, {
- (0, penalty_msat, required),
-});
-
impl FixedPenaltyScorer {
/// Creates a new scorer using `penalty_msat`.
pub fn with_penalty(penalty_msat: u64) -> Self {
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
}
+impl Writeable for FixedPenaltyScorer {
+ #[inline]
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ write_tlv_fields!(w, {});
+ Ok(())
+ }
+}
+
+impl ReadableArgs<u64> for FixedPenaltyScorer {
+ #[inline]
+ fn read<R: Read>(r: &mut R, penalty_msat: u64) -> Result<Self, DecodeError> {
+ read_tlv_fields!(r, {});
+ Ok(Self { penalty_msat })
+ }
+}
+
/// [`Score`] implementation that provides reasonable default behavior.
///
/// Used to apply a fixed penalty to each channel, thus avoiding long paths when shorter paths with
}
/// Parameters for configuring [`ProbabilisticScorer`].
+///
+/// Used to configure a base penalty and a liquidity penalty, the sum of which is the channel
+/// penalty (i.e., the amount in msats willing to be paid to avoid routing through the channel).
#[derive(Clone, Copy)]
pub struct ProbabilisticScoringParameters {
- /// A multiplier used to determine the amount in msats willing to be paid to avoid routing
- /// through a channel, as per multiplying by the negative `log10` of the channel's success
- /// probability for a payment.
+ /// A fixed penalty in msats to apply to each channel.
///
- /// The success probability is determined by the effective channel capacity, the payment amount,
- /// and knowledge learned from prior successful and unsuccessful payments. The lower bound of
- /// the success probability is 0.01, effectively limiting the penalty to the range
- /// `0..=2*liquidity_penalty_multiplier_msat`. The knowledge learned is decayed over time based
- /// on [`liquidity_offset_half_life`].
+ /// Default value: 500 msat
+ pub base_penalty_msat: u64,
+
+ /// A multiplier used in conjunction with the negative `log10` of the channel's success
+ /// probability for a payment to determine the liquidity penalty.
+ ///
+ /// The penalty is based in part by the knowledge learned from prior successful and unsuccessful
+ /// payments. This knowledge is decayed over time based on [`liquidity_offset_half_life`]. The
+ /// penalty is effectively limited to `2 * liquidity_penalty_multiplier_msat`.
///
- /// Default value: 10,000 msat
+ /// Default value: 40,000 msat
///
/// [`liquidity_offset_half_life`]: Self::liquidity_offset_half_life
pub liquidity_penalty_multiplier_msat: u64,
pub liquidity_offset_half_life: Duration,
}
-impl_writeable_tlv_based!(ProbabilisticScoringParameters, {
- (0, liquidity_penalty_multiplier_msat, required),
- (2, liquidity_offset_half_life, required),
-});
-
/// Accounting for channel liquidity balance uncertainty.
///
/// Direction is defined in terms of [`NodeId`] partial ordering, where the source node is the
impl Default for ProbabilisticScoringParameters {
fn default() -> Self {
Self {
- liquidity_penalty_multiplier_msat: 10_000,
+ base_penalty_msat: 500,
+ liquidity_penalty_multiplier_msat: 40_000,
liquidity_offset_half_life: Duration::from_secs(3600),
}
}
/// Returns a penalty for routing the given HTLC `amount_msat` through the channel in this
/// direction.
fn penalty_msat(&self, amount_msat: u64, liquidity_penalty_multiplier_msat: u64) -> u64 {
+ let max_penalty_msat = liquidity_penalty_multiplier_msat.saturating_mul(2);
let max_liquidity_msat = self.max_liquidity_msat();
let min_liquidity_msat = core::cmp::min(self.min_liquidity_msat(), max_liquidity_msat);
if amount_msat > max_liquidity_msat {
- u64::max_value()
+ max_penalty_msat
} else if amount_msat <= min_liquidity_msat {
0
} else {
- let numerator = max_liquidity_msat + 1 - amount_msat;
- let denominator = max_liquidity_msat + 1 - min_liquidity_msat;
- approx::negative_log10_times_1024(numerator, denominator)
- .saturating_mul(liquidity_penalty_multiplier_msat) / 1024
+ let numerator = (max_liquidity_msat - amount_msat).saturating_add(1);
+ let denominator = (max_liquidity_msat - min_liquidity_msat).saturating_add(1);
+ let penalty_msat = approx::negative_log10_times_1024(numerator, denominator)
+ .saturating_mul(liquidity_penalty_multiplier_msat) / 1024;
+ // Upper bound the penalty to ensure some channel is selected.
+ penalty_msat.min(max_penalty_msat)
}
- // Upper bound the penalty to ensure some channel is selected.
- .min(2 * liquidity_penalty_multiplier_msat)
}
/// Returns the lower bound of the channel liquidity balance in this direction.
.unwrap_or(&ChannelLiquidity::new())
.as_directed(source, target, capacity_msat, liquidity_offset_half_life)
.penalty_msat(amount_msat, liquidity_penalty_multiplier_msat)
+ .saturating_add(self.params.base_penalty_msat)
}
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
}
}
-impl<G, T> ReadableArgs<(ProbabilisticScoringParameters, G)> for ProbabilisticScorerUsingTime<G, T>
-where
- G: Deref<Target = NetworkGraph>,
- T: Time,
-{
+impl<G: Deref<Target = NetworkGraph>, T: Time>
+ReadableArgs<(ProbabilisticScoringParameters, G)> for ProbabilisticScorerUsingTime<G, T> {
#[inline]
fn read<R: Read>(
r: &mut R, args: (ProbabilisticScoringParameters, G)
fn increased_penalty_nearing_liquidity_upper_bound() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
- liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
+ base_penalty_msat: 0, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
};
let scorer = ProbabilisticScorer::new(params, &network_graph);
let source = source_node_id();
let last_updated = SinceEpoch::now();
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
- liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
+ base_penalty_msat: 0, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
};
let scorer = ProbabilisticScorer::new(params, &network_graph)
.with_channel(42,
fn does_not_further_penalize_own_channel() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
- liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
+ base_penalty_msat: 0, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
};
let mut scorer = ProbabilisticScorer::new(params, &network_graph);
let sender = sender_node_id();
fn sets_liquidity_lower_bound_on_downstream_failure() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
- liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
+ base_penalty_msat: 0, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
};
let mut scorer = ProbabilisticScorer::new(params, &network_graph);
let source = source_node_id();
fn sets_liquidity_upper_bound_on_failure() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
- liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
+ base_penalty_msat: 0, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
};
let mut scorer = ProbabilisticScorer::new(params, &network_graph);
let source = source_node_id();
fn reduces_liquidity_upper_bound_along_path_on_success() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
- liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
+ base_penalty_msat: 0, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
};
let mut scorer = ProbabilisticScorer::new(params, &network_graph);
let sender = sender_node_id();
fn decays_liquidity_bounds_over_time() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
+ base_penalty_msat: 0,
liquidity_penalty_multiplier_msat: 1_000,
liquidity_offset_half_life: Duration::from_secs(10),
};
fn decays_liquidity_bounds_without_shift_overflow() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
+ base_penalty_msat: 0,
liquidity_penalty_multiplier_msat: 1_000,
liquidity_offset_half_life: Duration::from_secs(10),
};
fn restricts_liquidity_bounds_after_decay() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
+ base_penalty_msat: 0,
liquidity_penalty_multiplier_msat: 1_000,
liquidity_offset_half_life: Duration::from_secs(10),
};
fn restores_persisted_liquidity_bounds() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
+ base_penalty_msat: 0,
liquidity_penalty_multiplier_msat: 1_000,
liquidity_offset_half_life: Duration::from_secs(10),
};
fn decays_persisted_liquidity_bounds() {
let network_graph = network_graph();
let params = ProbabilisticScoringParameters {
+ base_penalty_msat: 0,
liquidity_penalty_multiplier_msat: 1_000,
liquidity_offset_half_life: Duration::from_secs(10),
};
SinceEpoch::advance(Duration::from_secs(10));
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 371);
}
+
+ #[test]
+ fn adds_base_penalty_to_liquidity_penalty() {
+ let network_graph = network_graph();
+ let source = source_node_id();
+ let target = target_node_id();
+
+ let params = ProbabilisticScoringParameters {
+ base_penalty_msat: 0, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
+ };
+ let scorer = ProbabilisticScorer::new(params, &network_graph);
+ assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 58);
+
+ let params = ProbabilisticScoringParameters {
+ base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
+ };
+ let scorer = ProbabilisticScorer::new(params, &network_graph);
+ assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 558);
+ }
+
+ #[test]
+ fn calculates_log10_without_overflowing_u64_max_value() {
+ let network_graph = network_graph();
+ let source = source_node_id();
+ let target = target_node_id();
+
+ let params = ProbabilisticScoringParameters {
+ base_penalty_msat: 0, ..Default::default()
+ };
+ let scorer = ProbabilisticScorer::new(params, &network_graph);
+ assert_eq!(
+ scorer.channel_penalty_msat(42, u64::max_value(), u64::max_value(), &source, &target),
+ 80_000,
+ );
+ }
}
/// Default value: 1. If the value is less than 1, it is ignored and set to 1, as is required
/// by the protocol.
pub our_htlc_minimum_msat: u64,
+ /// If set, we attempt to negotiate the `scid_privacy` (referred to as `scid_alias` in the
+ /// BOLTs) option for outbound private channels. This provides better privacy by not including
+ /// our real on-chain channel UTXO in each invoice and requiring that our counterparty only
+ /// relay HTLCs to us using the channel's SCID alias.
+ ///
+ /// If this option is set, channels may be created that will not be readable by LDK versions
+ /// prior to 0.0.106, causing [`ChannelManager`]'s read method to return a
+ /// [`DecodeError:InvalidValue`].
+ ///
+ /// Note that setting this to true does *not* prevent us from opening channels with
+ /// counterparties that do not support the `scid_alias` option; we will simply fall back to a
+ /// private channel without that option.
+ ///
+ /// Ignored if the channel is negotiated to be announced, see
+ /// [`ChannelConfig::announced_channel`] and
+ /// [`ChannelHandshakeLimits::force_announced_channel_preference`] for more.
+ ///
+ /// Default value: false. This value is likely to change to true in the future.
+ ///
+ /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+ /// [`DecodeError:InvalidValue`]: crate::ln::msgs::DecodeError::InvalidValue
+ pub negotiate_scid_privacy: bool,
}
impl Default for ChannelHandshakeConfig {
minimum_depth: 6,
our_to_self_delay: BREAKDOWN_TIMEOUT,
our_htlc_minimum_msat: 1,
+ negotiate_scid_privacy: false,
}
}
}
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
+use bitcoin::secp256k1::{Message, Secp256k1, SecretKey, Signature, Signing};
macro_rules! hkdf_extract_expand {
($salt: expr, $ikm: expr) => {{
pub fn hkdf_extract_expand_thrice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 3)
}
+
+#[inline]
+pub fn sign<C: Signing>(ctx: &Secp256k1<C>, msg: &Message, sk: &SecretKey) -> Signature {
+ #[cfg(feature = "grind_signatures")]
+ let sig = ctx.sign_low_r(msg, sk);
+ #[cfg(not(feature = "grind_signatures"))]
+ let sig = ctx.sign(msg, sk);
+ sig
+}
use chain::keysinterface::SpendableOutputDescriptor;
use ln::channelmanager::PaymentId;
use ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
+use ln::features::ChannelTypeFeatures;
use ln::msgs;
use ln::msgs::DecodeError;
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
- /// The `user_channel_id` value passed in to [`ChannelManager::create_channel`], or 0 for
- /// an inbound channel. This will always be zero for objects serialized with LDK versions
- /// prior to 0.0.102.
+ /// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
+ /// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
+ /// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
+ /// `user_channel_id` will be 0 for an inbound channel.
+ /// This will always be zero for objects serialized with LDK versions prior to 0.0.102.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
+ /// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
+ /// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u64,
/// The reason the channel was closed.
reason: ClosureReason
funding_satoshis: u64,
/// Our starting balance in the channel if the request is accepted, in milli-satoshi.
push_msat: u64,
+ /// The features that this channel will operate with. If you reject the channel, a
+ /// well-behaved counterparty may automatically re-attempt the channel with a new set of
+ /// feature flags.
+ ///
+ /// Note that if [`ChannelTypeFeatures::supports_scid_privacy`] returns true on this type,
+ /// the resulting [`ChannelManager`] will not be readable by versions of LDK prior to
+ /// 0.0.106.
+ ///
+ /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+ channel_type: ChannelTypeFeatures,
},
}
node_id: PublicKey,
/// The reply_channel_range which should be sent.
msg: msgs::ReplyChannelRange,
- }
+ },
+ /// Sends a timestamp filter for inbound gossip. This should be sent on each new connection to
+ /// enable receiving gossip messages from the peer.
+ SendGossipTimestampFilter {
+ /// The node_id of this message recipient
+ node_id: PublicKey,
+ /// The gossip_timestamp_filter which should be sent.
+ msg: msgs::GossipTimestampFilter,
+ },
}
/// A trait indicating an object may generate message send events
}
/// Writes self out to a Vec<u8>
+ #[cfg(test)]
fn encode_with_len(&self) -> Vec<u8> {
let mut msg = VecWriter(Vec::new());
0u16.write(&mut msg).unwrap();
Vec::new()
}
- fn sync_routing_table(&self, _their_node_id: &PublicKey, _init_msg: &msgs::Init) {}
+ fn peer_connected(&self, _their_node_id: &PublicKey, _init_msg: &msgs::Init) {}
fn handle_reply_channel_range(&self, _their_node_id: &PublicKey, _msg: msgs::ReplyChannelRange) -> Result<(), msgs::LightningError> {
Ok(())