name: Continuous Integration Checks
-on: [push, pull_request]
+on:
+ push:
+ branches-ignore:
+ - master
+ pull_request:
+ branches-ignore:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
jobs:
build:
RUSTFLAGS="--cfg=c_bindings" cargo test --verbose --color always --no-default-features --features=no-std
popd
done
+# This one only works for lightning-invoice
+pushd lightning-invoice
+# check that compile with no-std and serde works in lightning-invoice
+cargo test --verbose --color always --no-default-features --features no-std --features serde
+popd
echo -e "\n\nTesting no-std build on a downstream no-std crate"
# check no-std compatibility across dependencies
];
$nodes[0].rapid_gossip_sync.update_network_graph_no_std(&initialization_input[..], Some(1642291930)).unwrap();
- // this should have added two channels
- assert_eq!($nodes[0].network_graph.read_only().channels().len(), 3);
+ // this should have added two channels and pruned the previous one.
+ assert_eq!($nodes[0].network_graph.read_only().channels().len(), 2);
$receive.expect("Network graph not pruned within deadline");
Signet,
}
+impl From<Network> for Currency {
+ fn from(network: Network) -> Self {
+ match network {
+ Network::Bitcoin => Currency::Bitcoin,
+ Network::Testnet => Currency::BitcoinTestnet,
+ Network::Regtest => Currency::Regtest,
+ Network::Signet => Currency::Signet,
+ }
+ }
+}
+
+impl From<Currency> for Network {
+ fn from(currency: Currency) -> Self {
+ match currency {
+ Currency::Bitcoin => Network::Bitcoin,
+ Currency::BitcoinTestnet => Network::Testnet,
+ Currency::Regtest => Network::Regtest,
+ Currency::Simnet => Network::Regtest,
+ Currency::Signet => Network::Signet,
+ }
+ }
+}
+
/// Tagged field which may have an unknown tag
///
/// This is not exported to bindings users as we don't currently support TaggedField
/// Returns a list of all fallback addresses as [`Address`]es
pub fn fallback_addresses(&self) -> Vec<Address> {
self.fallbacks().iter().map(|fallback| {
- let network = match self.currency() {
- Currency::Bitcoin => Network::Bitcoin,
- Currency::BitcoinTestnet => Network::Testnet,
- Currency::Regtest => Network::Regtest,
- Currency::Simnet => Network::Regtest,
- Currency::Signet => Network::Signet,
- };
-
let payload = match fallback {
Fallback::SegWitProgram { version, program } => {
Payload::WitnessProgram { version: *version, program: program.to_vec() }
}
};
- Address { payload, network }
+ Address { payload, network: self.network() }
}).collect()
}
self.signed_invoice.currency()
}
+ /// Returns the network for which the invoice was issued
+ ///
+ /// This is not exported to bindings users, see [`Self::currency`] instead.
+ pub fn network(&self) -> Network {
+ self.signed_invoice.currency().into()
+ }
+
/// Returns the amount if specified in the invoice as millisatoshis.
pub fn amount_milli_satoshis(&self) -> Option<u64> {
self.signed_invoice.amount_pico_btc().map(|v| v / 10)
fn deserialize<D>(deserializer: D) -> Result<Invoice, D::Error> where D: Deserializer<'de> {
let bolt11 = String::deserialize(deserializer)?
.parse::<Invoice>()
- .map_err(|e| D::Error::custom(format!("{:?}", e)))?;
+ .map_err(|e| D::Error::custom(alloc::format!("{:?}", e)))?;
Ok(bolt11)
}
//! let network_graph = NetworkGraph::new(Network::Bitcoin, &logger);
//! let rapid_sync = RapidGossipSync::new(&network_graph, &logger);
//! let snapshot_contents: &[u8] = &[0; 0];
-//! let new_last_sync_timestamp_result = rapid_sync.update_network_graph(snapshot_contents);
+//! // In no-std you need to provide the current time in unix epoch seconds
+//! // otherwise you can use update_network_graph
+//! let current_time_unix = 0;
+//! let new_last_sync_timestamp_result = rapid_sync.update_network_graph_no_std(snapshot_contents, Some(current_time_unix));
//! ```
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
/// Returns the last sync timestamp to be used the next time rapid sync data is queried.
///
/// `update_data`: `&[u8]` binary stream that comprises the update data
+ #[cfg(feature = "std")]
pub fn update_network_graph(&self, update_data: &[u8]) -> Result<u32, GraphSyncError> {
let mut read_cursor = io::Cursor::new(update_data);
self.update_network_graph_from_byte_stream(&mut read_cursor)
const STALE_RGS_UPDATE_AGE_LIMIT_SECS: u64 = 60 * 60 * 24 * 14;
impl<NG: Deref<Target=NetworkGraph<L>>, L: Deref> RapidGossipSync<NG, L> where L::Target: Logger {
+ #[cfg(feature = "std")]
pub(crate) fn update_network_graph_from_byte_stream<R: io::Read>(
&self,
read_cursor: &mut R,
) -> Result<u32, GraphSyncError> {
#[allow(unused_mut, unused_assignments)]
let mut current_time_unix = None;
- #[cfg(all(feature = "std", not(test)))]
+ #[cfg(not(test))]
{
// Note that many tests rely on being able to set arbitrarily old timestamps, thus we
// disable this check during tests!
}
self.network_graph.set_last_rapid_gossip_sync_timestamp(latest_seen_timestamp);
+
+ if let Some(time) = current_time_unix {
+ self.network_graph.remove_stale_channels_and_tracking_with_time(time)
+ }
+
self.is_initial_sync_complete.store(true, Ordering::Release);
log_trace!(self.logger, "Done processing RGS data from {}", latest_seen_timestamp);
Ok(latest_seen_timestamp)
mod tests {
use bitcoin::Network;
+ #[cfg(feature = "std")]
use lightning::ln::msgs::DecodeError;
+
use lightning::routing::gossip::NetworkGraph;
use lightning::util::test_utils::TestLogger;
const VALID_BINARY_TIMESTAMP: u64 = 1642291930;
#[test]
+ #[cfg(feature = "std")]
fn network_graph_fails_to_update_from_clipped_input() {
let logger = TestLogger::new();
let network_graph = NetworkGraph::new(Network::Bitcoin, &logger);
}
#[test]
+ #[cfg(feature = "std")]
fn incremental_only_update_ignores_missing_channel() {
let incremental_update_input = vec![
76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247,
}
#[test]
+ #[cfg(feature = "std")]
fn incremental_only_update_fails_without_prior_updates() {
let announced_update_input = vec![
76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247,
}
#[test]
+ #[cfg(feature = "std")]
fn incremental_only_update_fails_without_prior_same_direction_updates() {
let initialization_input = vec![
76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247,
}
#[test]
+ #[cfg(feature = "std")]
fn incremental_update_succeeds_with_prior_announcements_and_full_updates() {
let initialization_input = vec![
76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247,
}
#[test]
+ #[cfg(feature = "std")]
fn update_succeeds_when_duplicate_gossip_is_applied() {
let initialization_input = vec![
76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247,
}
#[test]
+ #[cfg(feature = "std")]
fn full_update_succeeds() {
let logger = TestLogger::new();
let network_graph = NetworkGraph::new(Network::Bitcoin, &logger);
assert_eq!(network_graph.read_only().channels().len(), 2);
}
+ #[test]
+ fn prunes_after_update() {
+ // this is the timestamp encoded in the binary data of valid_input below
+ let logger = TestLogger::new();
+
+ let latest_nonpruning_time = VALID_BINARY_TIMESTAMP + 60 * 60 * 24 * 7;
+
+ {
+ let network_graph = NetworkGraph::new(Network::Bitcoin, &logger);
+ assert_eq!(network_graph.read_only().channels().len(), 0);
+
+ let rapid_sync = RapidGossipSync::new(&network_graph, &logger);
+ let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(latest_nonpruning_time));
+ assert!(update_result.is_ok());
+ assert_eq!(network_graph.read_only().channels().len(), 2);
+ }
+
+ {
+ let network_graph = NetworkGraph::new(Network::Bitcoin, &logger);
+ assert_eq!(network_graph.read_only().channels().len(), 0);
+
+ let rapid_sync = RapidGossipSync::new(&network_graph, &logger);
+ let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(latest_nonpruning_time + 1));
+ assert!(update_result.is_ok());
+ assert_eq!(network_graph.read_only().channels().len(), 0);
+ }
+ }
+
#[test]
fn timestamp_edge_cases_are_handled_correctly() {
// this is the timestamp encoded in the binary data of valid_input below
let rapid_sync = RapidGossipSync::new(&network_graph, &logger);
let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(latest_succeeding_time));
assert!(update_result.is_ok());
- assert_eq!(network_graph.read_only().channels().len(), 2);
+ assert_eq!(network_graph.read_only().channels().len(), 0);
}
{
}
#[test]
+ #[cfg(feature = "std")]
pub fn update_fails_with_unknown_version() {
let unknown_version_input = vec![
76, 68, 75, 2, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247,
};
);
+/// The reason the payment failed. Used in [`Event::PaymentFailed`].
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum PaymentFailureReason {
+ /// The intended recipient rejected our payment.
+ RecipientRejected,
+ /// The user chose to abandon this payment by calling [`ChannelManager::abandon_payment`].
+ ///
+ /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
+ UserAbandoned,
+ /// We exhausted all of our retry attempts while trying to send the payment, or we
+ /// exhausted the [`Retry::Timeout`] if the user set one. If at any point a retry
+ /// attempt failed while being forwarded along the path, an [`Event::PaymentPathFailed`] will
+ /// have come before this.
+ ///
+ /// [`Retry::Timeout`]: crate::ln::channelmanager::Retry::Timeout
+ RetriesExhausted,
+ /// The payment expired while retrying, based on the provided
+ /// [`PaymentParameters::expiry_time`].
+ ///
+ /// [`PaymentParameters::expiry_time`]: crate::routing::router::PaymentParameters::expiry_time
+ PaymentExpired,
+ /// We failed to find a route while retrying the payment.
+ RouteNotFound,
+ /// This error should generally never happen. This likely means that there is a problem with
+ /// your router.
+ UnexpectedError,
+}
+
+impl_writeable_tlv_based_enum!(PaymentFailureReason,
+ (0, RecipientRejected) => {},
+ (2, UserAbandoned) => {},
+ (4, RetriesExhausted) => {},
+ (6, PaymentExpired) => {},
+ (8, RouteNotFound) => {},
+ (10, UnexpectedError) => {}, ;
+);
+
/// An Event which you should probably take some action in response to.
///
/// Note that while Writeable and Readable are implemented for Event, you probably shouldn't use
///
/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment
payment_hash: PaymentHash,
+ /// The reason the payment failed. This is only `None` for events generated or serialized
+ /// by versions prior to 0.0.115.
+ reason: Option<PaymentFailureReason>,
},
/// Indicates that a path for an outbound payment was successful.
///
(4, *path, vec_type)
})
},
- &Event::PaymentFailed { ref payment_id, ref payment_hash } => {
+ &Event::PaymentFailed { ref payment_id, ref payment_hash, ref reason } => {
15u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_id, required),
+ (1, reason, option),
(2, payment_hash, required),
})
},
let f = || {
let mut payment_hash = PaymentHash([0; 32]);
let mut payment_id = PaymentId([0; 32]);
+ let mut reason = None;
read_tlv_fields!(reader, {
(0, payment_id, required),
+ (1, reason, upgradable_option),
(2, payment_hash, required),
});
Ok(Some(Event::PaymentFailed {
payment_id,
payment_hash,
+ reason,
}))
};
f()
/// We've announced the channel as enabled and are connected to our peer.
Enabled,
/// Our channel is no longer live, but we haven't announced the channel as disabled yet.
- DisabledStaged,
+ DisabledStaged(u8),
/// Our channel is live again, but we haven't announced the channel as enabled yet.
- EnabledStaged,
+ EnabledStaged(u8),
/// We've announced the channel as disabled.
Disabled,
}
pub counterparty_max_accepted_htlcs: u16,
#[cfg(not(test))]
counterparty_max_accepted_htlcs: u16,
- //implied by OUR_MAX_HTLCS: max_accepted_htlcs: u16,
+ holder_max_accepted_htlcs: u16,
minimum_depth: Option<u32>,
counterparty_forwarding_info: Option<CounterpartyForwardingInfo>,
feerate: u32,
}
-pub const OUR_MAX_HTLCS: u16 = 50; //TODO
+pub const DEFAULT_MAX_HTLCS: u16 = 50;
pub(crate) fn commitment_tx_base_weight(opt_anchors: bool) -> u64 {
const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
counterparty_htlc_minimum_msat: 0,
holder_htlc_minimum_msat: if config.channel_handshake_config.our_htlc_minimum_msat == 0 { 1 } else { config.channel_handshake_config.our_htlc_minimum_msat },
counterparty_max_accepted_htlcs: 0,
+ holder_max_accepted_htlcs: cmp::min(config.channel_handshake_config.our_max_accepted_htlcs, MAX_HTLCS),
minimum_depth: None, // Filled in in accept_channel
counterparty_forwarding_info: None,
counterparty_htlc_minimum_msat: msg.htlc_minimum_msat,
holder_htlc_minimum_msat: if config.channel_handshake_config.our_htlc_minimum_msat == 0 { 1 } else { config.channel_handshake_config.our_htlc_minimum_msat },
counterparty_max_accepted_htlcs: msg.max_accepted_htlcs,
+ holder_max_accepted_htlcs: cmp::min(config.channel_handshake_config.our_max_accepted_htlcs, MAX_HTLCS),
minimum_depth: Some(cmp::max(config.channel_handshake_config.minimum_depth, 1)),
counterparty_forwarding_info: None,
let inbound_stats = self.get_inbound_pending_htlc_stats(None);
let outbound_stats = self.get_outbound_pending_htlc_stats(None);
- if inbound_stats.pending_htlcs + 1 > OUR_MAX_HTLCS as u32 {
- return Err(ChannelError::Close(format!("Remote tried to push more than our max accepted HTLCs ({})", OUR_MAX_HTLCS)));
+ if inbound_stats.pending_htlcs + 1 > self.holder_max_accepted_htlcs as u32 {
+ return Err(ChannelError::Close(format!("Remote tried to push more than our max accepted HTLCs ({})", self.holder_max_accepted_htlcs)));
}
if inbound_stats.pending_htlcs_value_msat + msg.amount_msat > self.holder_max_htlc_value_in_flight_msat {
return Err(ChannelError::Close(format!("Remote HTLC add would put them over our max HTLC value ({})", self.holder_max_htlc_value_in_flight_msat)));
htlc_minimum_msat: self.holder_htlc_minimum_msat,
feerate_per_kw: self.feerate_per_kw as u32,
to_self_delay: self.get_holder_selected_contest_delay(),
- max_accepted_htlcs: OUR_MAX_HTLCS,
+ max_accepted_htlcs: self.holder_max_accepted_htlcs,
funding_pubkey: keys.funding_pubkey,
revocation_basepoint: keys.revocation_basepoint,
payment_point: keys.payment_point,
htlc_minimum_msat: self.holder_htlc_minimum_msat,
minimum_depth: self.minimum_depth.unwrap(),
to_self_delay: self.get_holder_selected_contest_delay(),
- max_accepted_htlcs: OUR_MAX_HTLCS,
+ max_accepted_htlcs: self.holder_max_accepted_htlcs,
funding_pubkey: keys.funding_pubkey,
revocation_basepoint: keys.revocation_basepoint,
payment_point: keys.payment_point,
// channel as enabled, so we write 0. For EnabledStaged, we similarly write a 1.
match self {
ChannelUpdateStatus::Enabled => 0u8.write(writer)?,
- ChannelUpdateStatus::DisabledStaged => 0u8.write(writer)?,
- ChannelUpdateStatus::EnabledStaged => 1u8.write(writer)?,
+ ChannelUpdateStatus::DisabledStaged(_) => 0u8.write(writer)?,
+ ChannelUpdateStatus::EnabledStaged(_) => 1u8.write(writer)?,
ChannelUpdateStatus::Disabled => 1u8.write(writer)?,
}
Ok(())
// we write the high bytes as an option here.
let user_id_high_opt = Some((self.user_id >> 64) as u64);
+ let holder_max_accepted_htlcs = if self.holder_max_accepted_htlcs == DEFAULT_MAX_HTLCS { None } else { Some(self.holder_max_accepted_htlcs) };
+
write_tlv_fields!(writer, {
(0, self.announcement_sigs, option),
// minimum_depth and counterparty_selected_channel_reserve_satoshis used to have a
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
+ (28, holder_max_accepted_htlcs, option),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});
let value_to_self_msat = Readable::read(reader)?;
let pending_inbound_htlc_count: u64 = Readable::read(reader)?;
- let mut pending_inbound_htlcs = Vec::with_capacity(cmp::min(pending_inbound_htlc_count as usize, OUR_MAX_HTLCS as usize));
+
+ let mut pending_inbound_htlcs = Vec::with_capacity(cmp::min(pending_inbound_htlc_count as usize, DEFAULT_MAX_HTLCS as usize));
for _ in 0..pending_inbound_htlc_count {
pending_inbound_htlcs.push(InboundHTLCOutput {
htlc_id: Readable::read(reader)?,
}
let pending_outbound_htlc_count: u64 = Readable::read(reader)?;
- let mut pending_outbound_htlcs = Vec::with_capacity(cmp::min(pending_outbound_htlc_count as usize, OUR_MAX_HTLCS as usize));
+ let mut pending_outbound_htlcs = Vec::with_capacity(cmp::min(pending_outbound_htlc_count as usize, DEFAULT_MAX_HTLCS as usize));
for _ in 0..pending_outbound_htlc_count {
pending_outbound_htlcs.push(OutboundHTLCOutput {
htlc_id: Readable::read(reader)?,
}
let holding_cell_htlc_update_count: u64 = Readable::read(reader)?;
- let mut holding_cell_htlc_updates = Vec::with_capacity(cmp::min(holding_cell_htlc_update_count as usize, OUR_MAX_HTLCS as usize*2));
+ let mut holding_cell_htlc_updates = Vec::with_capacity(cmp::min(holding_cell_htlc_update_count as usize, DEFAULT_MAX_HTLCS as usize*2));
for _ in 0..holding_cell_htlc_update_count {
holding_cell_htlc_updates.push(match <u8 as Readable>::read(reader)? {
0 => HTLCUpdateAwaitingACK::AddHTLC {
let monitor_pending_commitment_signed = Readable::read(reader)?;
let monitor_pending_forwards_count: u64 = Readable::read(reader)?;
- let mut monitor_pending_forwards = Vec::with_capacity(cmp::min(monitor_pending_forwards_count as usize, OUR_MAX_HTLCS as usize));
+ let mut monitor_pending_forwards = Vec::with_capacity(cmp::min(monitor_pending_forwards_count as usize, DEFAULT_MAX_HTLCS as usize));
for _ in 0..monitor_pending_forwards_count {
monitor_pending_forwards.push((Readable::read(reader)?, Readable::read(reader)?));
}
let monitor_pending_failures_count: u64 = Readable::read(reader)?;
- let mut monitor_pending_failures = Vec::with_capacity(cmp::min(monitor_pending_failures_count as usize, OUR_MAX_HTLCS as usize));
+ let mut monitor_pending_failures = Vec::with_capacity(cmp::min(monitor_pending_failures_count as usize, DEFAULT_MAX_HTLCS as usize));
for _ in 0..monitor_pending_failures_count {
monitor_pending_failures.push((Readable::read(reader)?, Readable::read(reader)?, Readable::read(reader)?));
}
let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;
+ let mut holder_max_accepted_htlcs: Option<u16> = None;
read_tlv_fields!(reader, {
(0, announcement_sigs, option),
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
+ (28, holder_max_accepted_htlcs, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});
// separate u64 values.
let user_id = user_id_low as u128 + ((user_id_high_opt.unwrap_or(0) as u128) << 64);
+ let holder_max_accepted_htlcs = holder_max_accepted_htlcs.unwrap_or(DEFAULT_MAX_HTLCS);
+
Ok(Channel {
user_id,
cur_counterparty_commitment_transaction_number,
value_to_self_msat,
+ holder_max_accepted_htlcs,
pending_inbound_htlcs,
pending_outbound_htlcs,
holding_cell_htlc_updates,
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, HTLC_FAIL_BACK_BUFFER, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY, MonitorEvent, CLOSED_CHANNEL_UPDATE_ID};
use crate::chain::transaction::{OutPoint, TransactionData};
use crate::events;
-use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider, ClosureReason, HTLCDestination};
+use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider, ClosureReason, HTLCDestination, PaymentFailureReason};
// Since this struct is returned in `list_channels` methods, expose it here in case users want to
// construct one themselves.
use crate::ln::{inbound_payment, PaymentHash, PaymentPreimage, PaymentSecret};
/// This is not exported to bindings users as Arcs don't make sense in bindings
pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L> = ChannelManager<&'a M, &'b T, &'c KeysManager, &'c KeysManager, &'c KeysManager, &'d F, &'e DefaultRouter<&'f NetworkGraph<&'g L>, &'g L, &'h Mutex<ProbabilisticScorer<&'f NetworkGraph<&'g L>, &'g L>>>, &'g L>;
+/// A trivial trait which describes any [`ChannelManager`] used in testing.
+#[cfg(any(test, feature = "_test_utils"))]
+pub trait AChannelManager {
+ type Watch: chain::Watch<Self::Signer>;
+ type M: Deref<Target = Self::Watch>;
+ type Broadcaster: BroadcasterInterface;
+ type T: Deref<Target = Self::Broadcaster>;
+ type EntropySource: EntropySource;
+ type ES: Deref<Target = Self::EntropySource>;
+ type NodeSigner: NodeSigner;
+ type NS: Deref<Target = Self::NodeSigner>;
+ type Signer: WriteableEcdsaChannelSigner;
+ type SignerProvider: SignerProvider<Signer = Self::Signer>;
+ type SP: Deref<Target = Self::SignerProvider>;
+ type FeeEstimator: FeeEstimator;
+ type F: Deref<Target = Self::FeeEstimator>;
+ type Router: Router;
+ type R: Deref<Target = Self::Router>;
+ type Logger: Logger;
+ type L: Deref<Target = Self::Logger>;
+ fn get_cm(&self) -> &ChannelManager<Self::M, Self::T, Self::ES, Self::NS, Self::SP, Self::F, Self::R, Self::L>;
+}
+#[cfg(any(test, feature = "_test_utils"))]
+impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref> AChannelManager
+for ChannelManager<M, T, ES, NS, SP, F, R, L>
+where
+ M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer> + Sized,
+ T::Target: BroadcasterInterface + Sized,
+ ES::Target: EntropySource + Sized,
+ NS::Target: NodeSigner + Sized,
+ SP::Target: SignerProvider + Sized,
+ F::Target: FeeEstimator + Sized,
+ R::Target: Router + Sized,
+ L::Target: Logger + Sized,
+{
+ type Watch = M::Target;
+ type M = M;
+ type Broadcaster = T::Target;
+ type T = T;
+ type EntropySource = ES::Target;
+ type ES = ES;
+ type NodeSigner = NS::Target;
+ type NS = NS;
+ type Signer = <SP::Target as SignerProvider>::Signer;
+ type SignerProvider = SP::Target;
+ type SP = SP;
+ type FeeEstimator = F::Target;
+ type F = F;
+ type Router = R::Target;
+ type R = R;
+ type Logger = L::Target;
+ type L = L;
+ fn get_cm(&self) -> &ChannelManager<M, T, ES, NS, SP, F, R, L> { self }
+}
+
/// Manager which keeps track of a number of channels and sends messages to the appropriate
/// channel, also tracking HTLC preimages and forwarding onion packets appropriately.
///
/// [`OutboundPayments::remove_stale_resolved_payments`].
pub(crate) const IDEMPOTENCY_TIMEOUT_TICKS: u8 = 7;
+/// The number of ticks of [`ChannelManager::timer_tick_occurred`] where a peer is disconnected
+/// until we mark the channel disabled and gossip the update.
+pub(crate) const DISABLE_GOSSIP_TICKS: u8 = 10;
+
+/// The number of ticks of [`ChannelManager::timer_tick_occurred`] where a peer is connected until
+/// we mark the channel enabled and gossip the update.
+pub(crate) const ENABLE_GOSSIP_TICKS: u8 = 5;
+
/// The maximum number of unfunded channels we can have per-peer before we start rejecting new
/// (inbound) ones. The number of peers with unfunded channels is limited separately in
/// [`MAX_UNFUNDED_CHANNEL_PEERS`].
}
macro_rules! handle_error {
- ($self: ident, $internal: expr, $counterparty_node_id: expr) => {
+ ($self: ident, $internal: expr, $counterparty_node_id: expr) => { {
+ // In testing, ensure there are no deadlocks where the lock is already held upon
+ // entering the macro.
+ debug_assert_ne!($self.pending_events.held_by_thread(), LockHeldState::HeldByThread);
+ debug_assert_ne!($self.per_peer_state.held_by_thread(), LockHeldState::HeldByThread);
+
match $internal {
Ok(msg) => Ok(msg),
Err(MsgHandleErrInternal { err, chan_id, shutdown_finish }) => {
- // In testing, ensure there are no deadlocks where the lock is already held upon
- // entering the macro.
- debug_assert_ne!($self.pending_events.held_by_thread(), LockHeldState::HeldByThread);
- debug_assert_ne!($self.per_peer_state.held_by_thread(), LockHeldState::HeldByThread);
-
let mut msg_events = Vec::with_capacity(2);
if let Some((shutdown_res, update_option)) = shutdown_finish {
Err(err)
},
}
- }
+ } }
}
macro_rules! update_maps_on_chan_removal {
($self: ident, $update_res: expr, $update_id: expr, $peer_state_lock: expr, $peer_state: expr, $per_peer_state_lock: expr, $chan: expr, MANUALLY_REMOVING, $remove: expr) => { {
// update_maps_on_chan_removal needs to be able to take id_to_peer, so make sure we can in
// any case so that it won't deadlock.
- debug_assert!($self.id_to_peer.try_lock().is_ok());
+ debug_assert_ne!($self.id_to_peer.held_by_thread(), LockHeldState::HeldByThread);
match $update_res {
ChannelMonitorUpdateStatus::InProgress => {
log_debug!($self.logger, "ChannelMonitor update for {} in flight, holding messages until the update completes.",
}
}
+macro_rules! process_events_body {
+ ($self: expr, $event_to_handle: expr, $handle_event: expr) => {
+ // We'll acquire our total consistency lock until the returned future completes so that
+ // we can be sure no other persists happen while processing events.
+ let _read_guard = $self.total_consistency_lock.read().unwrap();
+
+ let mut result = NotifyOption::SkipPersist;
+
+ // TODO: This behavior should be documented. It's unintuitive that we query
+ // ChannelMonitors when clearing other events.
+ if $self.process_pending_monitor_events() {
+ result = NotifyOption::DoPersist;
+ }
+
+ let pending_events = mem::replace(&mut *$self.pending_events.lock().unwrap(), vec![]);
+ if !pending_events.is_empty() {
+ result = NotifyOption::DoPersist;
+ }
+
+ for event in pending_events {
+ $event_to_handle = event;
+ $handle_event;
+ }
+
+ if result == NotifyOption::DoPersist {
+ $self.persistence_notifier.notify();
+ }
+ }
+}
+
impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref> ChannelManager<M, T, ES, NS, SP, F, R, L>
where
M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
// hopefully an attacker trying to path-trace payments cannot make this occur
// on a small/per-node/per-channel scale.
if !chan.is_live() { // channel_disabled
- break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, chan_update_opt));
+ // If the channel_update we're going to return is disabled (i.e. the
+ // peer has been disabled for some time), return `channel_disabled`,
+ // otherwise return `temporary_channel_failure`.
+ if chan_update_opt.as_ref().map(|u| u.contents.flags & 2 == 2).unwrap_or(false) {
+ break Some(("Forwarding channel has been disconnected for some time.", 0x1000 | 20, chan_update_opt));
+ } else {
+ break Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, chan_update_opt));
+ }
}
if *outgoing_amt_msat < chan.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
log_trace!(self.logger, "Generating channel update for channel {}", log_bytes!(chan.channel_id()));
let were_node_one = self.our_network_pubkey.serialize()[..] < chan.get_counterparty_node_id().serialize()[..];
+ let enabled = chan.is_usable() && match chan.channel_update_status() {
+ ChannelUpdateStatus::Enabled => true,
+ ChannelUpdateStatus::DisabledStaged(_) => true,
+ ChannelUpdateStatus::Disabled => false,
+ ChannelUpdateStatus::EnabledStaged(_) => false,
+ };
+
let unsigned = msgs::UnsignedChannelUpdate {
chain_hash: self.genesis_hash,
short_channel_id,
timestamp: chan.get_update_time_counter(),
- flags: (!were_node_one) as u8 | ((!chan.is_live() as u8) << 1),
+ flags: (!were_node_one) as u8 | ((!enabled as u8) << 1),
cltv_expiry_delta: chan.get_cltv_expiry_delta(),
htlc_minimum_msat: chan.get_counterparty_htlc_minimum_msat(),
htlc_maximum_msat: chan.get_announced_htlc_max_msat(),
/// [`Event::PaymentSent`]: events::Event::PaymentSent
pub fn abandon_payment(&self, payment_id: PaymentId) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
- self.pending_outbound_payments.abandon_payment(payment_id, &self.pending_events);
+ self.pending_outbound_payments.abandon_payment(payment_id, PaymentFailureReason::UserAbandoned, &self.pending_events);
}
/// Send a spontaneous payment, which is a payment that does not require the recipient to have
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
- let (chan, msg) = {
- let (res, chan) = {
- match peer_state.channel_by_id.remove(temporary_channel_id) {
- Some(mut chan) => {
- let funding_txo = find_funding_output(&chan, &funding_transaction)?;
-
- (chan.get_outbound_funding_created(funding_transaction, funding_txo, &self.logger)
- .map_err(|e| if let ChannelError::Close(msg) = e {
- MsgHandleErrInternal::from_finish_shutdown(msg, chan.channel_id(), chan.get_user_id(), chan.force_shutdown(true), None)
- } else { unreachable!(); })
- , chan)
+ let (msg, chan) = match peer_state.channel_by_id.remove(temporary_channel_id) {
+ Some(mut chan) => {
+ let funding_txo = find_funding_output(&chan, &funding_transaction)?;
+
+ let funding_res = chan.get_outbound_funding_created(funding_transaction, funding_txo, &self.logger)
+ .map_err(|e| if let ChannelError::Close(msg) = e {
+ MsgHandleErrInternal::from_finish_shutdown(msg, chan.channel_id(), chan.get_user_id(), chan.force_shutdown(true), None)
+ } else { unreachable!(); });
+ match funding_res {
+ Ok(funding_msg) => (funding_msg, chan),
+ Err(_) => {
+ mem::drop(peer_state_lock);
+ mem::drop(per_peer_state);
+
+ let _ = handle_error!(self, funding_res, chan.get_counterparty_node_id());
+ return Err(APIError::ChannelUnavailable {
+ err: "Signer refused to sign the initial commitment transaction".to_owned()
+ });
},
- None => { return Err(APIError::ChannelUnavailable { err: format!("Channel with id {} not found for the passed counterparty node_id {}", log_bytes!(*temporary_channel_id), counterparty_node_id) }) },
}
- };
- match handle_error!(self, res, chan.get_counterparty_node_id()) {
- Ok(funding_msg) => {
- (chan, funding_msg)
- },
- Err(_) => { return Err(APIError::ChannelUnavailable {
- err: "Signer refused to sign the initial commitment transaction".to_owned()
- }) },
- }
+ },
+ None => {
+ return Err(APIError::ChannelUnavailable {
+ err: format!(
+ "Channel with id {} not found for the passed counterparty node_id {}",
+ log_bytes!(*temporary_channel_id), counterparty_node_id),
+ })
+ },
};
peer_state.pending_msg_events.push(events::MessageSendEvent::SendFundingCreated {
}
match chan.channel_update_status() {
- ChannelUpdateStatus::Enabled if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::DisabledStaged),
- ChannelUpdateStatus::Disabled if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::EnabledStaged),
- ChannelUpdateStatus::DisabledStaged if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Enabled),
- ChannelUpdateStatus::EnabledStaged if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Disabled),
- ChannelUpdateStatus::DisabledStaged if !chan.is_live() => {
- if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
- pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
- msg: update
- });
+ ChannelUpdateStatus::Enabled if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::DisabledStaged(0)),
+ ChannelUpdateStatus::Disabled if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::EnabledStaged(0)),
+ ChannelUpdateStatus::DisabledStaged(_) if chan.is_live()
+ => chan.set_channel_update_status(ChannelUpdateStatus::Enabled),
+ ChannelUpdateStatus::EnabledStaged(_) if !chan.is_live()
+ => chan.set_channel_update_status(ChannelUpdateStatus::Disabled),
+ ChannelUpdateStatus::DisabledStaged(mut n) if !chan.is_live() => {
+ n += 1;
+ if n >= DISABLE_GOSSIP_TICKS {
+ chan.set_channel_update_status(ChannelUpdateStatus::Disabled);
+ if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
+ pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+ msg: update
+ });
+ }
+ should_persist = NotifyOption::DoPersist;
+ } else {
+ chan.set_channel_update_status(ChannelUpdateStatus::DisabledStaged(n));
}
- should_persist = NotifyOption::DoPersist;
- chan.set_channel_update_status(ChannelUpdateStatus::Disabled);
},
- ChannelUpdateStatus::EnabledStaged if chan.is_live() => {
- if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
- pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
- msg: update
- });
+ ChannelUpdateStatus::EnabledStaged(mut n) if chan.is_live() => {
+ n += 1;
+ if n >= ENABLE_GOSSIP_TICKS {
+ chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
+ if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
+ pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+ msg: update
+ });
+ }
+ should_persist = NotifyOption::DoPersist;
+ } else {
+ chan.set_channel_update_status(ChannelUpdateStatus::EnabledStaged(n));
}
- should_persist = NotifyOption::DoPersist;
- chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
},
_ => {},
}
pub async fn process_pending_events_async<Future: core::future::Future, H: Fn(Event) -> Future>(
&self, handler: H
) {
- // We'll acquire our total consistency lock until the returned future completes so that
- // we can be sure no other persists happen while processing events.
- let _read_guard = self.total_consistency_lock.read().unwrap();
-
- let mut result = NotifyOption::SkipPersist;
-
- // TODO: This behavior should be documented. It's unintuitive that we query
- // ChannelMonitors when clearing other events.
- if self.process_pending_monitor_events() {
- result = NotifyOption::DoPersist;
- }
-
- let pending_events = mem::replace(&mut *self.pending_events.lock().unwrap(), vec![]);
- if !pending_events.is_empty() {
- result = NotifyOption::DoPersist;
- }
-
- for event in pending_events {
- handler(event).await;
- }
-
- if result == NotifyOption::DoPersist {
- self.persistence_notifier.notify();
- }
+ let mut ev;
+ process_events_body!(self, ev, { handler(ev).await });
}
}
/// An [`EventHandler`] may safely call back to the provider in order to handle an event.
/// However, it must not call [`Writeable::write`] as doing so would result in a deadlock.
fn process_pending_events<H: Deref>(&self, handler: H) where H::Target: EventHandler {
- PersistenceNotifierGuard::optionally_notify(&self.total_consistency_lock, &self.persistence_notifier, || {
- let mut result = NotifyOption::SkipPersist;
-
- // TODO: This behavior should be documented. It's unintuitive that we query
- // ChannelMonitors when clearing other events.
- if self.process_pending_monitor_events() {
- result = NotifyOption::DoPersist;
- }
-
- let pending_events = mem::replace(&mut *self.pending_events.lock().unwrap(), vec![]);
- if !pending_events.is_empty() {
- result = NotifyOption::DoPersist;
- }
-
- for event in pending_events {
- handler.handle_event(event);
- }
-
- result
- });
+ let mut ev;
+ process_events_body!(self, ev, handler.handle_event(ev));
}
}
use test::Bencher;
- struct NodeHolder<'a, P: Persist<InMemorySigner>> {
- node: &'a ChannelManager<
- &'a ChainMonitor<InMemorySigner, &'a test_utils::TestChainSource,
- &'a test_utils::TestBroadcaster, &'a test_utils::TestFeeEstimator,
- &'a test_utils::TestLogger, &'a P>,
- &'a test_utils::TestBroadcaster, &'a KeysManager, &'a KeysManager, &'a KeysManager,
- &'a test_utils::TestFeeEstimator, &'a test_utils::TestRouter<'a>,
- &'a test_utils::TestLogger>,
+ type Manager<'a, P> = ChannelManager<
+ &'a ChainMonitor<InMemorySigner, &'a test_utils::TestChainSource,
+ &'a test_utils::TestBroadcaster, &'a test_utils::TestFeeEstimator,
+ &'a test_utils::TestLogger, &'a P>,
+ &'a test_utils::TestBroadcaster, &'a KeysManager, &'a KeysManager, &'a KeysManager,
+ &'a test_utils::TestFeeEstimator, &'a test_utils::TestRouter<'a>,
+ &'a test_utils::TestLogger>;
+
+ struct ANodeHolder<'a, P: Persist<InMemorySigner>> {
+ node: &'a Manager<'a, P>,
+ }
+ impl<'a, P: Persist<InMemorySigner>> NodeHolder for ANodeHolder<'a, P> {
+ type CM = Manager<'a, P>;
+ #[inline]
+ fn node(&self) -> &Manager<'a, P> { self.node }
+ #[inline]
+ fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor> { None }
}
#[cfg(test)]
network,
best_block: BestBlock::from_network(network),
});
- let node_a_holder = NodeHolder { node: &node_a };
+ let node_a_holder = ANodeHolder { node: &node_a };
let logger_b = test_utils::TestLogger::with_id("node a".to_owned());
let chain_monitor_b = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_b);
network,
best_block: BestBlock::from_network(network),
});
- let node_b_holder = NodeHolder { node: &node_b };
+ let node_b_holder = ANodeHolder { node: &node_b };
node_a.peer_connected(&node_b.get_our_node_id(), &Init { features: node_b.init_features(), remote_network_address: None }, true).unwrap();
node_b.peer_connected(&node_a.get_our_node_id(), &Init { features: node_a.init_features(), remote_network_address: None }, false).unwrap();
let payment_event = SendEvent::from_event($node_a.get_and_clear_pending_msg_events().pop().unwrap());
$node_b.handle_update_add_htlc(&$node_a.get_our_node_id(), &payment_event.msgs[0]);
$node_b.handle_commitment_signed(&$node_a.get_our_node_id(), &payment_event.commitment_msg);
- let (raa, cs) = do_get_revoke_commit_msgs!(NodeHolder { node: &$node_b }, &$node_a.get_our_node_id());
+ let (raa, cs) = get_revoke_commit_msgs(&ANodeHolder { node: &$node_b }, &$node_a.get_our_node_id());
$node_a.handle_revoke_and_ack(&$node_b.get_our_node_id(), &raa);
$node_a.handle_commitment_signed(&$node_b.get_our_node_id(), &cs);
- $node_b.handle_revoke_and_ack(&$node_a.get_our_node_id(), &get_event_msg!(NodeHolder { node: &$node_a }, MessageSendEvent::SendRevokeAndACK, $node_b.get_our_node_id()));
+ $node_b.handle_revoke_and_ack(&$node_a.get_our_node_id(), &get_event_msg!(ANodeHolder { node: &$node_a }, MessageSendEvent::SendRevokeAndACK, $node_b.get_our_node_id()));
- expect_pending_htlcs_forwardable!(NodeHolder { node: &$node_b });
- expect_payment_claimable!(NodeHolder { node: &$node_b }, payment_hash, payment_secret, 10_000);
+ expect_pending_htlcs_forwardable!(ANodeHolder { node: &$node_b });
+ expect_payment_claimable!(ANodeHolder { node: &$node_b }, payment_hash, payment_secret, 10_000);
$node_b.claim_funds(payment_preimage);
- expect_payment_claimed!(NodeHolder { node: &$node_b }, payment_hash, 10_000);
+ expect_payment_claimed!(ANodeHolder { node: &$node_b }, payment_hash, 10_000);
match $node_b.get_and_clear_pending_msg_events().pop().unwrap() {
MessageSendEvent::UpdateHTLCs { node_id, updates } => {
_ => panic!("Failed to generate claim event"),
}
- let (raa, cs) = do_get_revoke_commit_msgs!(NodeHolder { node: &$node_a }, &$node_b.get_our_node_id());
+ let (raa, cs) = get_revoke_commit_msgs(&ANodeHolder { node: &$node_a }, &$node_b.get_our_node_id());
$node_b.handle_revoke_and_ack(&$node_a.get_our_node_id(), &raa);
$node_b.handle_commitment_signed(&$node_a.get_our_node_id(), &cs);
- $node_a.handle_revoke_and_ack(&$node_b.get_our_node_id(), &get_event_msg!(NodeHolder { node: &$node_b }, MessageSendEvent::SendRevokeAndACK, $node_a.get_our_node_id()));
+ $node_a.handle_revoke_and_ack(&$node_b.get_our_node_id(), &get_event_msg!(ANodeHolder { node: &$node_b }, MessageSendEvent::SendRevokeAndACK, $node_a.get_our_node_id()));
- expect_payment_sent!(NodeHolder { node: &$node_a }, payment_preimage);
+ expect_payment_sent!(ANodeHolder { node: &$node_a }, payment_preimage);
}
}
use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch, keysinterface::EntropySource};
use crate::chain::channelmonitor::ChannelMonitor;
use crate::chain::transaction::OutPoint;
-use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose};
+use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, PaymentFailureReason};
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-use crate::ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, PaymentId, MIN_CLTV_EXPIRY_DELTA};
+use crate::ln::channelmanager::{AChannelManager, ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, PaymentId, MIN_CLTV_EXPIRY_DELTA};
use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
use crate::routing::router::{self, PaymentParameters, Route};
use crate::ln::features::InitFeatures;
pub override_init_features: Rc<RefCell<Option<InitFeatures>>>,
}
+type TestChannelManager<'a, 'b, 'c> = ChannelManager<&'b TestChainMonitor<'c>, &'c test_utils::TestBroadcaster, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'c test_utils::TestFeeEstimator, &'b test_utils::TestRouter<'c>, &'c test_utils::TestLogger>;
+
pub struct Node<'a, 'b: 'a, 'c: 'b> {
pub chain_source: &'c test_utils::TestChainSource,
pub tx_broadcaster: &'c test_utils::TestBroadcaster,
pub router: &'b test_utils::TestRouter<'c>,
pub chain_monitor: &'b test_utils::TestChainMonitor<'c>,
pub keys_manager: &'b test_utils::TestKeysInterface,
- pub node: &'a ChannelManager<&'b TestChainMonitor<'c>, &'c test_utils::TestBroadcaster, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'b test_utils::TestKeysInterface, &'c test_utils::TestFeeEstimator, &'b test_utils::TestRouter<'c>, &'c test_utils::TestLogger>,
+ pub node: &'a TestChannelManager<'a, 'b, 'c>,
pub network_graph: &'a NetworkGraph<&'c test_utils::TestLogger>,
pub gossip_sync: P2PGossipSync<&'b NetworkGraph<&'c test_utils::TestLogger>, &'c test_utils::TestChainSource, &'c test_utils::TestLogger>,
pub node_seed: [u8; 32],
unsafe impl Send for NodePtr {}
unsafe impl Sync for NodePtr {}
+
+pub trait NodeHolder {
+ type CM: AChannelManager;
+ fn node(&self) -> &ChannelManager<
+ <Self::CM as AChannelManager>::M,
+ <Self::CM as AChannelManager>::T,
+ <Self::CM as AChannelManager>::ES,
+ <Self::CM as AChannelManager>::NS,
+ <Self::CM as AChannelManager>::SP,
+ <Self::CM as AChannelManager>::F,
+ <Self::CM as AChannelManager>::R,
+ <Self::CM as AChannelManager>::L>;
+ fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor>;
+}
+impl<H: NodeHolder> NodeHolder for &H {
+ type CM = H::CM;
+ fn node(&self) -> &ChannelManager<
+ <Self::CM as AChannelManager>::M,
+ <Self::CM as AChannelManager>::T,
+ <Self::CM as AChannelManager>::ES,
+ <Self::CM as AChannelManager>::NS,
+ <Self::CM as AChannelManager>::SP,
+ <Self::CM as AChannelManager>::F,
+ <Self::CM as AChannelManager>::R,
+ <Self::CM as AChannelManager>::L> { (*self).node() }
+ fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor> { (*self).chain_monitor() }
+}
+impl<'a, 'b: 'a, 'c: 'b> NodeHolder for Node<'a, 'b, 'c> {
+ type CM = TestChannelManager<'a, 'b, 'c>;
+ fn node(&self) -> &TestChannelManager<'a, 'b, 'c> { &self.node }
+ fn chain_monitor(&self) -> Option<&test_utils::TestChainMonitor> { Some(self.chain_monitor) }
+}
+
impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
fn drop(&mut self) {
if !panicking() {
}
/// Gets an RAA and CS which were sent in response to a commitment update
-///
-/// Should only be used directly when the `$node` is not actually a [`Node`].
-macro_rules! do_get_revoke_commit_msgs {
- ($node: expr, $recipient: expr) => { {
- let events = $node.node.get_and_clear_pending_msg_events();
- assert_eq!(events.len(), 2);
- (match events[0] {
- MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
- assert_eq!(node_id, $recipient);
- (*msg).clone()
- },
- _ => panic!("Unexpected event"),
- }, match events[1] {
- MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => {
- assert_eq!(node_id, $recipient);
- assert!(updates.update_add_htlcs.is_empty());
- assert!(updates.update_fulfill_htlcs.is_empty());
- assert!(updates.update_fail_htlcs.is_empty());
- assert!(updates.update_fail_malformed_htlcs.is_empty());
- assert!(updates.update_fee.is_none());
- updates.commitment_signed.clone()
- },
- _ => panic!("Unexpected event"),
- })
- } }
-}
-
-/// Gets an RAA and CS which were sent in response to a commitment update
-pub fn get_revoke_commit_msgs(node: &Node, recipient: &PublicKey) -> (msgs::RevokeAndACK, msgs::CommitmentSigned) {
- do_get_revoke_commit_msgs!(node, recipient)
+pub fn get_revoke_commit_msgs<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H, recipient: &PublicKey) -> (msgs::RevokeAndACK, msgs::CommitmentSigned) {
+ let events = node.node().get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 2);
+ (match events[0] {
+ MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
+ assert_eq!(node_id, recipient);
+ (*msg).clone()
+ },
+ _ => panic!("Unexpected event"),
+ }, match events[1] {
+ MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => {
+ assert_eq!(node_id, recipient);
+ assert!(updates.update_add_htlcs.is_empty());
+ assert!(updates.update_fulfill_htlcs.is_empty());
+ assert!(updates.update_fail_htlcs.is_empty());
+ assert!(updates.update_fail_malformed_htlcs.is_empty());
+ assert!(updates.update_fee.is_none());
+ updates.commitment_signed.clone()
+ },
+ _ => panic!("Unexpected event"),
+ })
}
#[macro_export]
}
/// Check whether N channel monitor(s) have been added.
-pub fn check_added_monitors(node: &Node, count: usize) {
- let mut added_monitors = node.chain_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), count);
- added_monitors.clear();
+pub fn check_added_monitors<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H, count: usize) {
+ if let Some(chain_monitor) = node.chain_monitor() {
+ let mut added_monitors = chain_monitor.added_monitors.lock().unwrap();
+ assert_eq!(added_monitors.len(), count);
+ added_monitors.clear();
+ }
}
/// Check whether N channel monitor(s) have been added.
bs_revoke_and_ack
}
};
- ($node_a: expr, $node_b: expr, (), $fail_backwards: expr, true /* skip last step */, true /* return extra message */) => {
- {
- let (extra_msg_option, bs_revoke_and_ack) = $crate::ln::functional_test_utils::do_main_commitment_signed_dance(&$node_a, &$node_b, $fail_backwards);
- $node_a.node.handle_revoke_and_ack(&$node_b.node.get_our_node_id(), &bs_revoke_and_ack);
- $crate::ln::functional_test_utils::check_added_monitors(&$node_a, 1);
- extra_msg_option
- }
- };
($node_a: expr, $node_b: expr, (), $fail_backwards: expr, true /* skip last step */, false /* no extra message */) => {
- assert!(commitment_signed_dance!($node_a, $node_b, (), $fail_backwards, true, true).is_none());
+ assert!($crate::ln::functional_test_utils::commitment_signed_dance_through_cp_raa(&$node_a, &$node_b, $fail_backwards).is_none());
};
($node_a: expr, $node_b: expr, $commitment_signed: expr, $fail_backwards: expr) => {
$crate::ln::functional_test_utils::do_commitment_signed_dance(&$node_a, &$node_b, &$commitment_signed, $fail_backwards, false);
}
}
-
+/// Runs the commitment_signed dance after the initial commitment_signed is delivered through to
+/// the initiator's `revoke_and_ack` response. i.e. [`do_main_commitment_signed_dance`] plus the
+/// `revoke_and_ack` response to it.
+///
+/// Returns any additional message `node_b` generated in addition to the `revoke_and_ack` response.
+pub fn commitment_signed_dance_through_cp_raa(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, '_, '_>, fail_backwards: bool) -> Option<MessageSendEvent> {
+ let (extra_msg_option, bs_revoke_and_ack) = do_main_commitment_signed_dance(node_a, node_b, fail_backwards);
+ node_a.node.handle_revoke_and_ack(&node_b.node.get_our_node_id(), &bs_revoke_and_ack);
+ check_added_monitors(node_a, 1);
+ extra_msg_option
+}
+
+/// Does the main logic in the commitment_signed dance. After the first `commitment_signed` has
+/// been delivered, this method picks up and delivers the response `revoke_and_ack` and
+/// `commitment_signed`, returning the recipient's `revoke_and_ack` and any extra message it may
+/// have included.
pub fn do_main_commitment_signed_dance(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, '_, '_>, fail_backwards: bool) -> (Option<MessageSendEvent>, msgs::RevokeAndACK) {
let (as_revoke_and_ack, as_commitment_signed) = get_revoke_commit_msgs!(node_a, node_b.node.get_our_node_id());
check_added_monitors!(node_b, 0);
(extra_msg_option, bs_revoke_and_ack)
}
+/// Runs a full commitment_signed dance, delivering a commitment_signed, the responding
+/// `revoke_and_ack` and `commitment_signed`, and then the final `revoke_and_ack` response.
+///
+/// If `skip_last_step` is unset, also checks for the payment failure update for the previous hop
+/// on failure or that no new messages are left over on success.
pub fn do_commitment_signed_dance(node_a: &Node<'_, '_, '_>, node_b: &Node<'_, '_, '_>, commitment_signed: &msgs::CommitmentSigned, fail_backwards: bool, skip_last_step: bool) {
check_added_monitors!(node_a, 0);
assert!(node_a.node.get_and_clear_pending_msg_events().is_empty());
}
}
+pub fn expect_payment_sent<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H,
+ expected_payment_preimage: PaymentPreimage, expected_fee_msat_opt: Option<Option<u64>>,
+ expect_per_path_claims: bool,
+) {
+ let events = node.node().get_and_clear_pending_events();
+ let expected_payment_hash = PaymentHash(
+ bitcoin::hashes::sha256::Hash::hash(&expected_payment_preimage.0).into_inner());
+ if expect_per_path_claims {
+ assert!(events.len() > 1);
+ } else {
+ assert_eq!(events.len(), 1);
+ }
+ let expected_payment_id = match events[0] {
+ Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
+ assert_eq!(expected_payment_preimage, *payment_preimage);
+ assert_eq!(expected_payment_hash, *payment_hash);
+ if let Some(expected_fee_msat) = expected_fee_msat_opt {
+ assert_eq!(*fee_paid_msat, expected_fee_msat);
+ } else {
+ assert!(fee_paid_msat.is_some());
+ }
+ payment_id.unwrap()
+ },
+ _ => panic!("Unexpected event"),
+ };
+ if expect_per_path_claims {
+ for i in 1..events.len() {
+ match events[i] {
+ Event::PaymentPathSuccessful { payment_id, payment_hash, .. } => {
+ assert_eq!(payment_id, expected_payment_id);
+ assert_eq!(payment_hash, Some(expected_payment_hash));
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+ }
+}
+
#[cfg(test)]
#[macro_export]
macro_rules! expect_payment_sent_without_paths {
($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr) => {
$crate::expect_payment_sent!($node, $expected_payment_preimage, $expected_fee_msat_opt, true);
};
- ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr, $expect_paths: expr) => { {
- use bitcoin::hashes::Hash as _;
- let events = $node.node.get_and_clear_pending_events();
- let expected_payment_hash = $crate::ln::PaymentHash(
- bitcoin::hashes::sha256::Hash::hash(&$expected_payment_preimage.0).into_inner());
- if $expect_paths {
- assert!(events.len() > 1);
- } else {
- assert_eq!(events.len(), 1);
- }
- let expected_payment_id = match events[0] {
- $crate::events::Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
- assert_eq!($expected_payment_preimage, *payment_preimage);
- assert_eq!(expected_payment_hash, *payment_hash);
- assert!(fee_paid_msat.is_some());
- if $expected_fee_msat_opt.is_some() {
- assert_eq!(*fee_paid_msat, $expected_fee_msat_opt);
- }
- payment_id.unwrap()
- },
- _ => panic!("Unexpected event"),
- };
- if $expect_paths {
- for i in 1..events.len() {
- match events[i] {
- $crate::events::Event::PaymentPathSuccessful { payment_id, payment_hash, .. } => {
- assert_eq!(payment_id, expected_payment_id);
- assert_eq!(payment_hash, Some(expected_payment_hash));
- },
- _ => panic!("Unexpected event"),
- }
- }
- }
- } }
+ ($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr, $expect_paths: expr) => {
+ $crate::ln::functional_test_utils::expect_payment_sent(&$node, $expected_payment_preimage,
+ $expected_fee_msat_opt.map(|o| Some(o)), $expect_paths);
+ }
}
#[cfg(test)]
};
if !conditions.expected_mpp_parts_remain {
match &payment_failed_events[1] {
- Event::PaymentFailed { ref payment_hash, ref payment_id } => {
+ Event::PaymentFailed { ref payment_hash, ref payment_id, ref reason } => {
assert_eq!(*payment_hash, expected_payment_hash, "unexpected second payment_hash");
assert_eq!(*payment_id, expected_payment_id);
+ assert_eq!(reason.unwrap(), if expected_payment_failed_permanently {
+ PaymentFailureReason::RecipientRejected
+ } else {
+ PaymentFailureReason::RetriesExhausted
+ });
}
_ => panic!("Unexpected second event"),
}
let expected_destinations: Vec<HTLCDestination> = repeat(HTLCDestination::FailedPayment { payment_hash: our_payment_hash }).take(expected_paths.len()).collect();
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(expected_paths[0].last().unwrap(), expected_destinations);
- pass_failed_payment_back(origin_node, expected_paths, skip_last, our_payment_hash);
+ pass_failed_payment_back(origin_node, expected_paths, skip_last, our_payment_hash, PaymentFailureReason::RecipientRejected);
}
-pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) {
+pub fn pass_failed_payment_back<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash, expected_fail_reason: PaymentFailureReason) {
let mut expected_paths: Vec<_> = expected_paths_slice.iter().collect();
check_added_monitors!(expected_paths[0].last().unwrap(), expected_paths.len());
};
if i == expected_paths.len() - 1 {
match events[1] {
- Event::PaymentFailed { ref payment_hash, ref payment_id } => {
+ Event::PaymentFailed { ref payment_hash, ref payment_id, ref reason } => {
assert_eq!(*payment_hash, our_payment_hash, "unexpected second payment_hash");
assert_eq!(*payment_id, expected_payment_id);
+ assert_eq!(reason.unwrap(), expected_fail_reason);
}
_ => panic!("Unexpected second event"),
}
}
let mut had_channel_update = false; // ChannelUpdate may be now or later, but not both
- if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, ref msg }) = msg_events.get(idx) {
+ if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, .. }) = msg_events.get(idx) {
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
idx += 1;
- assert_eq!(msg.contents.flags & 2, 0); // "disabled" flag must not be set as we just reconnected.
had_channel_update = true;
}
}
}
- if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, ref msg }) = msg_events.get(idx) {
+ if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, .. }) = msg_events.get(idx) {
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
idx += 1;
- assert_eq!(msg.contents.flags & 2, 0); // "disabled" flag must not be set as we just reconnected.
assert!(!had_channel_update);
}
use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
use crate::chain::transaction::OutPoint;
use crate::chain::keysinterface::{ChannelSigner, EcdsaChannelSigner, EntropySource};
-use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination};
+use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
use crate::ln::{PaymentPreimage, PaymentSecret, PaymentHash};
use crate::ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT};
-use crate::ln::channelmanager::{self, PaymentId, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA};
+use crate::ln::channelmanager::{self, PaymentId, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, BREAKDOWN_TIMEOUT, ENABLE_GOSSIP_TICKS, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA};
use crate::ln::channel::{Channel, ChannelError};
use crate::ln::{chan_utils, onion_utils};
use crate::ln::chan_utils::{OFFERED_HTLC_SCRIPT_WEIGHT, htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment};
let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2);
let mut payments = Vec::new();
- for _ in 0..crate::ln::channel::OUR_MAX_HTLCS {
+ for _ in 0..50 {
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[2], 100000);
nodes[1].node.send_payment_with_route(&route, payment_hash,
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fulfill_htlcs[0]);
commitment_signed_dance!(nodes[0], nodes[1], &updates.commitment_signed, false);
-
- let events = nodes[0].node.get_and_clear_pending_events();
- match events[0] {
- Event::PaymentSent { ref payment_preimage, ref payment_hash, .. } => {
- assert_eq!(*payment_preimage, our_payment_preimage);
- assert_eq!(*payment_hash, duplicate_payment_hash);
- }
- _ => panic!("Unexpected event"),
- }
+ expect_payment_sent(&nodes[0], our_payment_preimage, None, true);
}
#[test]
onion_routing_packet: onion_packet.clone(),
};
- for i in 0..super::channel::OUR_MAX_HTLCS {
+ for i in 0..50 {
msg.htlc_id = i as u64;
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
}
- msg.htlc_id = (super::channel::OUR_MAX_HTLCS) as u64;
+ msg.htlc_id = (50) as u64;
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
assert!(nodes[1].node.list_channels().is_empty());
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
- nodes[0].node.timer_tick_occurred(); // Enabled -> DisabledStaged
- nodes[0].node.timer_tick_occurred(); // DisabledStaged -> Disabled
+ for _ in 0..DISABLE_GOSSIP_TICKS + 1 {
+ nodes[0].node.timer_tick_occurred();
+ }
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(msg_events.len(), 3);
let mut chans_disabled = HashMap::new();
nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &reestablish_1[2]);
handle_chan_reestablish_msgs!(nodes[1], nodes[0]);
- nodes[0].node.timer_tick_occurred();
+ for _ in 0..ENABLE_GOSSIP_TICKS {
+ nodes[0].node.timer_tick_occurred();
+ }
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
nodes[0].node.timer_tick_occurred();
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 15_000_000, our_payment_hash, Some(our_payment_secret), events.pop().unwrap(), true, None);
do_claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, our_payment_preimage);
- let events = nodes[0].node.get_and_clear_pending_events();
- assert_eq!(events.len(), 3);
- match events[0] {
- Event::PaymentSent { payment_hash, .. } => { // The payment was abandoned earlier, so the fee paid will be None
- assert_eq!(payment_hash, our_payment_hash);
- },
- _ => panic!("Unexpected event")
- }
- match events[1] {
- Event::PaymentPathSuccessful { payment_hash, .. } => {
- assert_eq!(payment_hash.unwrap(), our_payment_hash);
- },
- _ => panic!("Unexpected event")
- }
- match events[2] {
- Event::PaymentPathSuccessful { payment_hash, .. } => {
- assert_eq!(payment_hash.unwrap(), our_payment_hash);
- },
- _ => panic!("Unexpected event")
- }
+ expect_payment_sent(&nodes[0], our_payment_preimage, Some(None), true);
}
#[test]
];
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[3], failed_destinations);
- pass_failed_payment_back(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_hash);
+ pass_failed_payment_back(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_hash, PaymentFailureReason::RecipientRejected);
// nodes[1] now retries one of the two paths...
nodes[0].node.send_payment_with_route(&route, payment_hash,
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-use crate::routing::gossip::NodeId;
+use crate::routing::gossip::{NodeAlias, NodeId};
/// 21 million * 10^8 * 1000
pub(crate) const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
/// An alias, for UI purposes.
///
/// This should be sanitized before use. There is no guarantee of uniqueness.
- pub alias: [u8; 32],
+ pub alias: NodeAlias,
/// List of addresses on which this node is reachable
pub addresses: Vec<NetAddress>,
pub(crate) excess_address_data: Vec<u8>,
let node_id: NodeId = Readable::read(r)?;
let mut rgb = [0; 3];
r.read_exact(&mut rgb)?;
- let alias: [u8; 32] = Readable::read(r)?;
+ let alias: NodeAlias = Readable::read(r)?;
let addr_len: u16 = Readable::read(r)?;
let mut addresses: Vec<NetAddress> = Vec::new();
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
use crate::ln::msgs;
use crate::ln::msgs::{FinalOnionHopData, OptionalField, OnionErrorPacket, OnionHopDataFormat};
- use crate::routing::gossip::NodeId;
+ use crate::routing::gossip::{NodeAlias, NodeId};
use crate::util::ser::{Writeable, Readable, Hostname};
use bitcoin::hashes::hex::FromHex;
timestamp: 20190119,
node_id: NodeId::from_pubkey(&pubkey_1),
rgb: [32; 3],
- alias: [16;32],
+ alias: NodeAlias([16;32]),
addresses,
excess_address_data: if excess_address_data { vec![33, 108, 40, 11, 83, 149, 162, 84, 110, 126, 75, 38, 99, 224, 79, 129, 22, 34, 241, 90, 79, 146, 232, 58, 162, 233, 43, 162, 165, 115, 193, 57, 20, 44, 84, 174, 99, 7, 42, 30, 193, 238, 125, 192, 192, 75, 222, 92, 132, 120, 6, 23, 42, 160, 92, 146, 194, 42, 232, 227, 8, 209, 210, 105] } else { Vec::new() },
excess_data: if excess_data { vec![59, 18, 204, 25, 92, 224, 162, 209, 189, 166, 168, 139, 239, 161, 159, 160, 127, 81, 202, 167, 92, 232, 56, 55, 242, 137, 101, 96, 11, 138, 172, 171, 8, 85, 255, 176, 231, 65, 236, 95, 124, 65, 66, 30, 152, 41, 169, 212, 134, 17, 200, 200, 49, 247, 27, 229, 234, 115, 230, 101, 148, 151, 127, 253] } else { Vec::new() },
use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
-use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure};
+use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason};
use crate::ln::{PaymentHash, PaymentSecret};
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
-use crate::ln::channelmanager::{HTLCForwardInfo, FailureCode, CLTV_FAR_FAR_AWAY, MIN_CLTV_EXPIRY_DELTA, PendingAddHTLCInfo, PendingHTLCInfo, PendingHTLCRouting, PaymentId, RecipientOnionFields};
+use crate::ln::channelmanager::{HTLCForwardInfo, FailureCode, CLTV_FAR_FAR_AWAY, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA, PendingAddHTLCInfo, PendingHTLCInfo, PendingHTLCRouting, PaymentId, RecipientOnionFields};
use crate::ln::onion_utils;
use crate::routing::gossip::{NetworkUpdate, RoutingFees};
use crate::routing::router::{get_route, PaymentParameters, Route, RouteHint, RouteHintHop};
panic!("Unexpected event");
}
match events[1] {
- Event::PaymentFailed { payment_hash: ev_payment_hash, payment_id: ev_payment_id } => {
+ Event::PaymentFailed { payment_hash: ev_payment_hash, payment_id: ev_payment_id, reason: ref ev_reason } => {
assert_eq!(*payment_hash, ev_payment_hash);
assert_eq!(payment_id, ev_payment_id);
+ assert_eq!(if expected_retryable {
+ PaymentFailureReason::RetriesExhausted
+ } else {
+ PaymentFailureReason::RecipientRejected
+ }, ev_reason.unwrap());
}
_ => panic!("Unexpected second event"),
}
// disconnect event to the channel between nodes[1] ~ nodes[2]
nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id());
nodes[2].node.peer_disconnected(&nodes[1].node.get_our_node_id());
+ }, true, Some(UPDATE|7), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id));
+ run_onion_failure_test("channel_disabled", 0, &nodes, &route, &payment_hash, &payment_secret, |_| {}, || {
+ // disconnect event to the channel between nodes[1] ~ nodes[2]
+ for _ in 0..DISABLE_GOSSIP_TICKS + 1 {
+ nodes[1].node.timer_tick_occurred();
+ nodes[2].node.timer_tick_occurred();
+ }
+ nodes[1].node.get_and_clear_pending_msg_events();
+ nodes[2].node.get_and_clear_pending_msg_events();
}, true, Some(UPDATE|20), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id));
reconnect_nodes(&nodes[1], &nodes[2], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
-use crate::events;
+use crate::events::{self, PaymentFailureReason};
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
use crate::ln::onion_utils::HTLCFailReason;
Abandoned {
session_privs: HashSet<[u8; 32]>,
payment_hash: PaymentHash,
+ /// Will be `None` if the payment was serialized before 0.0.115.
+ reason: Option<PaymentFailureReason>,
},
}
*self = PendingOutboundPayment::Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs: 0 };
}
- fn mark_abandoned(&mut self) -> Result<(), ()> {
- let mut session_privs = HashSet::new();
- let our_payment_hash;
- core::mem::swap(&mut session_privs, match self {
- PendingOutboundPayment::Legacy { .. } |
- PendingOutboundPayment::Fulfilled { .. } =>
- return Err(()),
- PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } |
- PendingOutboundPayment::Abandoned { session_privs, payment_hash, .. } => {
- our_payment_hash = *payment_hash;
- session_privs
- },
- });
- *self = PendingOutboundPayment::Abandoned { session_privs, payment_hash: our_payment_hash };
- Ok(())
+ fn mark_abandoned(&mut self, reason: PaymentFailureReason) {
+ if let PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } = self {
+ let mut our_session_privs = HashSet::new();
+ core::mem::swap(&mut our_session_privs, session_privs);
+ *self = PendingOutboundPayment::Abandoned {
+ session_privs: our_session_privs,
+ payment_hash: *payment_hash,
+ reason: Some(reason)
+ };
+ }
}
/// panics if path is None and !self.is_fulfilled
outbounds.retain(|pmt_id, pmt| {
let mut retain = true;
if !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 {
- if pmt.mark_abandoned().is_ok() {
+ pmt.mark_abandoned(PaymentFailureReason::RetriesExhausted);
+ if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. } = pmt {
pending_events.lock().unwrap().push(events::Event::PaymentFailed {
payment_id: *pmt_id,
- payment_hash: pmt.payment_hash().expect("PendingOutboundPayments::Retryable always has a payment hash set"),
+ payment_hash: *payment_hash,
+ reason: *reason,
});
retain = false;
}
#[cfg(feature = "std")] {
if has_expired(&route_params) {
log_error!(logger, "Payment params expired on retry, abandoning payment {}", log_bytes!(payment_id.0));
- self.abandon_payment(payment_id, pending_events);
+ self.abandon_payment(payment_id, PaymentFailureReason::PaymentExpired, pending_events);
return
}
}
Ok(route) => route,
Err(e) => {
log_error!(logger, "Failed to find a route on retry, abandoning payment {}: {:#?}", log_bytes!(payment_id.0), e);
- self.abandon_payment(payment_id, pending_events);
+ self.abandon_payment(payment_id, PaymentFailureReason::RouteNotFound, pending_events);
return
}
};
for path in route.paths.iter() {
if path.len() == 0 {
log_error!(logger, "length-0 path in route");
- self.abandon_payment(payment_id, pending_events);
+ self.abandon_payment(payment_id, PaymentFailureReason::UnexpectedError, pending_events);
return
}
}
}
macro_rules! abandon_with_entry {
- ($payment: expr) => {
- if $payment.get_mut().mark_abandoned().is_ok() && $payment.get().remaining_parts() == 0 {
- pending_events.lock().unwrap().push(events::Event::PaymentFailed {
- payment_id,
- payment_hash,
- });
- $payment.remove();
+ ($payment: expr, $reason: expr) => {
+ $payment.get_mut().mark_abandoned($reason);
+ if let PendingOutboundPayment::Abandoned { reason, .. } = $payment.get() {
+ if $payment.get().remaining_parts() == 0 {
+ pending_events.lock().unwrap().push(events::Event::PaymentFailed {
+ payment_id,
+ payment_hash,
+ reason: *reason,
+ });
+ $payment.remove();
+ }
}
}
}
let retry_amt_msat: u64 = route.paths.iter().map(|path| path.last().unwrap().fee_msat).sum();
if retry_amt_msat + *pending_amt_msat > *total_msat * (100 + RETRY_OVERFLOW_PERCENTAGE) / 100 {
log_error!(logger, "retry_amt_msat of {} will put pending_amt_msat (currently: {}) more than 10% over total_payment_amt_msat of {}", retry_amt_msat, pending_amt_msat, total_msat);
- abandon_with_entry!(payment);
+ abandon_with_entry!(payment, PaymentFailureReason::UnexpectedError);
return
}
(*total_msat, RecipientOnionFields {
};
if !payment.get().is_retryable_now() {
log_error!(logger, "Retries exhausted for payment id {}", log_bytes!(payment_id.0));
- abandon_with_entry!(payment);
+ abandon_with_entry!(payment, PaymentFailureReason::RetriesExhausted);
return
}
payment.get_mut().increment_attempts();
{
match err {
PaymentSendFailure::AllFailedResendSafe(errs) => {
- Self::push_path_failed_evs_and_scids(payment_id, payment_hash, &mut route_params, route.paths, errs.into_iter().map(|e| Err(e)), pending_events);
+ Self::push_path_failed_evs_and_scids(payment_id, payment_hash, &mut route_params, route.paths, errs.into_iter().map(|e| Err(e)), logger, pending_events);
self.retry_payment_internal(payment_hash, payment_id, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path);
},
PaymentSendFailure::PartialFailure { failed_paths_retry: Some(mut retry), results, .. } => {
- Self::push_path_failed_evs_and_scids(payment_id, payment_hash, &mut retry, route.paths, results.into_iter(), pending_events);
+ Self::push_path_failed_evs_and_scids(payment_id, payment_hash, &mut retry, route.paths, results.into_iter(), logger, pending_events);
// Some paths were sent, even if we failed to send the full MPP value our recipient may
// misbehave and claim the funds, at which point we have to consider the payment sent, so
// return `Ok()` here, ignoring any retry errors.
// initial HTLC-Add messages yet.
},
PaymentSendFailure::PathParameterError(results) => {
- Self::push_path_failed_evs_and_scids(payment_id, payment_hash, &mut route_params, route.paths, results.into_iter(), pending_events);
- self.abandon_payment(payment_id, pending_events);
+ log_error!(logger, "Failed to send to route due to parameter error in a single path. Your router is buggy");
+ Self::push_path_failed_evs_and_scids(payment_id, payment_hash, &mut route_params, route.paths, results.into_iter(), logger, pending_events);
+ self.abandon_payment(payment_id, PaymentFailureReason::UnexpectedError, pending_events);
},
PaymentSendFailure::ParameterError(e) => {
log_error!(logger, "Failed to send to route due to parameter error: {:?}. Your router is buggy", e);
- self.abandon_payment(payment_id, pending_events);
+ self.abandon_payment(payment_id, PaymentFailureReason::UnexpectedError, pending_events);
},
PaymentSendFailure::DuplicatePayment => debug_assert!(false), // unreachable
}
}
- fn push_path_failed_evs_and_scids<I: ExactSizeIterator + Iterator<Item = Result<(), APIError>>>(
+ fn push_path_failed_evs_and_scids<I: ExactSizeIterator + Iterator<Item = Result<(), APIError>>, L: Deref>(
payment_id: PaymentId, payment_hash: PaymentHash, route_params: &mut RouteParameters,
- paths: Vec<Vec<RouteHop>>, path_results: I, pending_events: &Mutex<Vec<events::Event>>
- ) {
+ paths: Vec<Vec<RouteHop>>, path_results: I, logger: &L, pending_events: &Mutex<Vec<events::Event>>
+ ) where L::Target: Logger {
let mut events = pending_events.lock().unwrap();
debug_assert_eq!(paths.len(), path_results.len());
for (path, path_res) in paths.into_iter().zip(path_results) {
if let Err(e) = path_res {
if let APIError::MonitorUpdateInProgress = e { continue }
+ log_error!(logger, "Failed to send along path due to error: {:?}", e);
let mut failed_scid = None;
if let APIError::ChannelUnavailable { .. } = e {
let scid = path[0].short_channel_id;
}
if payment_is_probe || !is_retryable_now || !payment_retryable {
- let _ = payment.get_mut().mark_abandoned(); // we'll only Err if it's a legacy payment
+ let reason = if !payment_retryable {
+ PaymentFailureReason::RecipientRejected
+ } else {
+ PaymentFailureReason::RetriesExhausted
+ };
+ payment.get_mut().mark_abandoned(reason);
is_retryable_now = false;
}
if payment.get().remaining_parts() == 0 {
- if payment.get().abandoned() {
+ if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. }= payment.get() {
if !payment_is_probe {
full_failure_ev = Some(events::Event::PaymentFailed {
payment_id: *payment_id,
- payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
+ payment_hash: *payment_hash,
+ reason: *reason,
});
}
payment.remove();
}
pub(super) fn abandon_payment(
- &self, payment_id: PaymentId, pending_events: &Mutex<Vec<events::Event>>
+ &self, payment_id: PaymentId, reason: PaymentFailureReason, pending_events: &Mutex<Vec<events::Event>>
) {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
- if let Ok(()) = payment.get_mut().mark_abandoned() {
+ payment.get_mut().mark_abandoned(reason);
+ if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. } = payment.get() {
if payment.get().remaining_parts() == 0 {
pending_events.lock().unwrap().push(events::Event::PaymentFailed {
payment_id,
- payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
+ payment_hash: *payment_hash,
+ reason: *reason,
});
payment.remove();
}
},
(3, Abandoned) => {
(0, session_privs, required),
+ (1, reason, option),
(2, payment_hash, required),
},
);
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
- use crate::events::{Event, PathFailure};
+ use crate::events::{Event, PathFailure, PaymentFailureReason};
use crate::ln::PaymentHash;
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
use crate::ln::features::{ChannelFeatures, NodeFeatures};
&pending_events, &|_, _, _, _, _, _, _, _| Ok(()));
let events = pending_events.lock().unwrap();
assert_eq!(events.len(), 1);
- if let Event::PaymentFailed { .. } = events[0] { } else { panic!("Unexpected event"); }
+ if let Event::PaymentFailed { ref reason, .. } = events[0] {
+ assert_eq!(reason.unwrap(), PaymentFailureReason::PaymentExpired);
+ } else { panic!("Unexpected event"); }
} else {
let err = outbound_payments.send_payment(
PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]),
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
use crate::chain::keysinterface::EntropySource;
use crate::chain::transaction::OutPoint;
-use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure};
+use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason};
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS, RecentPaymentDetails, RecipientOnionFields};
use crate::ln::features::InvoiceFeatures;
}
check_send_rejected!();
- pass_failed_payment_back(&nodes[0], &[&[&nodes[1]]], false, first_payment_hash);
+ pass_failed_payment_back(&nodes[0], &[&[&nodes[1]]], false, first_payment_hash, PaymentFailureReason::RecipientRejected);
// However, we can reuse the PaymentId immediately after we `abandon_payment` upon passing the
// failed payment back.
let mut events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id } => {
+ Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id, reason: ref ev_reason } => {
assert_eq!(payment_hash, *ev_payment_hash);
assert_eq!(PaymentId(payment_hash.0), *ev_payment_id);
+ assert_eq!(PaymentFailureReason::RetriesExhausted, ev_reason.unwrap());
},
_ => panic!("Unexpected event"),
}
let mut events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id } => {
+ Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id, reason: ref ev_reason } => {
assert_eq!(payment_hash, *ev_payment_hash);
assert_eq!(PaymentId(payment_hash.0), *ev_payment_id);
+ assert_eq!(PaymentFailureReason::RetriesExhausted, ev_reason.unwrap());
},
_ => panic!("Unexpected event"),
}
let mut events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id } => {
+ Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id, reason: ref ev_reason } => {
assert_eq!(payment_hash, *ev_payment_hash);
assert_eq!(PaymentId(payment_hash.0), *ev_payment_id);
+ assert_eq!(PaymentFailureReason::RouteNotFound, ev_reason.unwrap());
},
_ => panic!("Unexpected event"),
}
nodes[1].node.fail_htlc_backwards(&payment_hash);
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], [HTLCDestination::FailedPayment { payment_hash }]);
- pass_failed_payment_back(&nodes[0], &[&[&nodes[1]]], false, payment_hash);
+ pass_failed_payment_back(&nodes[0], &[&[&nodes[1]]], false, payment_hash, PaymentFailureReason::RecipientRejected);
}
#[test]
_ => panic!("Unexpected event"),
}
match events[1] {
- Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id } => {
+ Event::PaymentFailed { payment_hash: ref ev_payment_hash, payment_id: ref ev_payment_id, reason: ref ev_reason } => {
assert_eq!(payment_hash, *ev_payment_hash);
assert_eq!(PaymentId(payment_hash.0), *ev_payment_id);
+ assert_eq!(PaymentFailureReason::RetriesExhausted, ev_reason.unwrap());
},
_ => panic!("Unexpected event"),
}
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[3], [reason.clone()]);
connect_blocks(&nodes[3], 4);
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[3], [reason]);
- pass_failed_payment_back(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_hash);
+ pass_failed_payment_back(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_hash, PaymentFailureReason::RecipientRejected);
} else {
nodes[1].node.force_close_broadcasting_latest_txn(&chan_bd, &nodes[3].node.get_our_node_id()).unwrap();
check_closed_event(&nodes[1], 1, ClosureReason::HolderForceClosed, false);
use crate::ln::wire;
use crate::ln::wire::Encode;
use crate::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
-use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId};
+use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, NodeAlias};
use crate::util::atomic_counter::AtomicCounter;
use crate::util::logger::Logger;
features,
timestamp: self.last_node_announcement_serial.fetch_add(1, Ordering::AcqRel),
node_id: NodeId::from_pubkey(&self.node_signer.get_node_id(Recipient::Node).unwrap()),
- rgb, alias, addresses,
+ rgb,
+ alias: NodeAlias(alias),
+ addresses,
excess_address_data: Vec::new(),
excess_data: Vec::new(),
};
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash;
+use bitcoin::hashes::hex::FromHex;
use bitcoin::hash_types::BlockHash;
use bitcoin::network::constants::Network;
use crate::io_extras::{copy, sink};
use crate::prelude::*;
use core::{cmp, fmt};
+use core::convert::TryFrom;
use crate::sync::{RwLock, RwLockReadGuard};
#[cfg(feature = "std")]
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::sync::Mutex;
use core::ops::{Bound, Deref};
+use core::str::FromStr;
#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};
pub fn as_slice(&self) -> &[u8] {
&self.0
}
+
+ /// Get the public key from this NodeId
+ pub fn as_pubkey(&self) -> Result<PublicKey, secp256k1::Error> {
+ PublicKey::from_slice(&self.0)
+ }
}
impl fmt::Debug for NodeId {
}
}
+impl From<PublicKey> for NodeId {
+ fn from(pubkey: PublicKey) -> Self {
+ Self::from_pubkey(&pubkey)
+ }
+}
+
+impl TryFrom<NodeId> for PublicKey {
+ type Error = secp256k1::Error;
+
+ fn try_from(node_id: NodeId) -> Result<Self, Self::Error> {
+ node_id.as_pubkey()
+ }
+}
+
+impl FromStr for NodeId {
+ type Err = bitcoin::hashes::hex::Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let data: [u8; PUBLIC_KEY_SIZE] = FromHex::from_hex(s)?;
+ Ok(NodeId(data))
+ }
+}
+
/// Represents the network as nodes and channels between them
pub struct NetworkGraph<L: Deref> where L::Target: Logger {
secp_ctx: Secp256k1<secp256k1::VerifyOnly>,
///
/// Since node aliases are provided by third parties, they are a potential avenue for injection
/// attacks. Care must be taken when processing.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NodeAlias(pub [u8; 32]);
impl fmt::Display for NodeAlias {
features: msg.features.clone(),
last_update: msg.timestamp,
rgb: msg.rgb,
- alias: NodeAlias(msg.alias),
+ alias: msg.alias,
announcement_message: if should_relay { full_msg.cloned() } else { None },
});
timestamp: 100,
node_id,
rgb: [0; 3],
- alias: [0; 32],
+ alias: NodeAlias([0; 32]),
addresses: Vec::new(),
excess_address_data: Vec::new(),
excess_data: Vec::new(),
// You may not use this file except in accordance with one or both of these
// licenses.
-use crate::routing::gossip::{NetworkGraph, P2PGossipSync};
+use crate::routing::gossip::{NetworkGraph, NodeAlias, P2PGossipSync};
use crate::ln::features::{ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
NodeAnnouncement, UnsignedNodeAnnouncement, ChannelUpdate, UnsignedChannelUpdate, MAX_VALUE_MSAT};
timestamp,
node_id,
rgb: [0; 3],
- alias: [0; 32],
+ alias: NodeAlias([0; 32]),
addresses: Vec::new(),
excess_address_data: Vec::new(),
excess_data: Vec::new(),
/// [`DecodeError::InvalidValue`]: crate::ln::msgs::DecodeError::InvalidValue
/// [`SIGHASH_SINGLE + update_fee Considered Harmful`]: https://lists.linuxfoundation.org/pipermail/lightning-dev/2020-September/002796.html
pub negotiate_anchors_zero_fee_htlc_tx: bool,
+
+ /// The maximum number of HTLCs in-flight from our counterparty towards us at the same time.
+ ///
+ /// Increasing the value can help improve liquidity and stability in
+ /// routing at the cost of higher long term disk / DB usage.
+ ///
+ /// Note: Versions of LDK earlier than v0.0.115 will fail to read channels with a configuration
+ /// other than the default value.
+ ///
+ /// Default value: 50
+ /// Maximum value: 483, any values larger will be treated as 483.
+ /// This is the BOLT #2 spec limit on `max_accepted_htlcs`.
+ pub our_max_accepted_htlcs: u16,
}
impl Default for ChannelHandshakeConfig {
their_channel_reserve_proportional_millionths: 10_000,
#[cfg(anchors)]
negotiate_anchors_zero_fee_htlc_tx: false,
+ our_max_accepted_htlcs: 50,
}
}
}
r.read_exact(&mut buf)?;
let mut res = [0u16; 8];
for (idx, v) in res.iter_mut().enumerate() {
- *v = (buf[idx] as u16) << 8 | (buf[idx + 1] as u16)
+ *v = (buf[idx*2] as u16) << 8 | (buf[idx*2 + 1] as u16)
}
Ok(res)
}