[features]
futures = [ "futures-util" ]
+std = ["lightning/std", "lightning-rapid-gossip-sync/std"]
+
+default = ["std"]
[dependencies]
-bitcoin = "0.29.0"
-lightning = { version = "0.0.113", path = "../lightning", features = ["std"] }
-lightning-rapid-gossip-sync = { version = "0.0.113", path = "../lightning-rapid-gossip-sync" }
+bitcoin = { version = "0.29.0", default-features = false }
+lightning = { version = "0.0.113", path = "../lightning", default-features = false }
+lightning-rapid-gossip-sync = { version = "0.0.113", path = "../lightning-rapid-gossip-sync", default-features = false }
futures-util = { version = "0.3", default-features = false, features = ["async-await-macro"], optional = true }
[dev-dependencies]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
+
+#[cfg(any(test, feature = "std"))]
+extern crate core;
+
#[macro_use] extern crate lightning;
extern crate lightning_rapid_gossip_sync;
use lightning::util::logger::Logger;
use lightning::util::persist::Persister;
use lightning_rapid_gossip_sync::RapidGossipSync;
+use lightning::io;
+
+use core::ops::Deref;
+use core::time::Duration;
+
+#[cfg(feature = "std")]
use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::thread;
-use std::thread::JoinHandle;
-use std::time::{Duration, Instant};
-use std::ops::Deref;
+#[cfg(feature = "std")]
+use core::sync::atomic::{AtomicBool, Ordering};
+#[cfg(feature = "std")]
+use std::thread::{self, JoinHandle};
+#[cfg(feature = "std")]
+use std::time::Instant;
#[cfg(feature = "futures")]
-use futures_util::{select_biased, future::FutureExt};
+use futures_util::{select_biased, future::FutureExt, task};
/// `BackgroundProcessor` takes care of tasks that (1) need to happen periodically to keep
/// Rust-Lightning running properly, and (2) either can or should be run in the background. Its
///
/// [`ChannelMonitor`]: lightning::chain::channelmonitor::ChannelMonitor
/// [`Event`]: lightning::util::events::Event
+#[cfg(feature = "std")]
#[must_use = "BackgroundProcessor will immediately stop on drop. It should be stored until shutdown."]
pub struct BackgroundProcessor {
stop_thread: Arc<AtomicBool>,
($persister: ident, $chain_monitor: ident, $process_chain_monitor_events: expr,
$channel_manager: ident, $process_channel_manager_events: expr,
$gossip_sync: ident, $peer_manager: ident, $logger: ident, $scorer: ident,
- $loop_exit_check: expr, $await: expr)
+ $loop_exit_check: expr, $await: expr, $get_timer: expr, $timer_elapsed: expr)
=> { {
log_trace!($logger, "Calling ChannelManager's timer_tick_occurred on startup");
$channel_manager.timer_tick_occurred();
- let mut last_freshness_call = Instant::now();
- let mut last_ping_call = Instant::now();
- let mut last_prune_call = Instant::now();
- let mut last_scorer_persist_call = Instant::now();
+ let mut last_freshness_call = $get_timer(FRESHNESS_TIMER);
+ let mut last_ping_call = $get_timer(PING_TIMER);
+ let mut last_prune_call = $get_timer(FIRST_NETWORK_PRUNE_TIMER);
+ let mut last_scorer_persist_call = $get_timer(SCORER_PERSIST_TIMER);
let mut have_pruned = false;
loop {
// We wait up to 100ms, but track how long it takes to detect being put to sleep,
// see `await_start`'s use below.
- let await_start = Instant::now();
+ let mut await_start = $get_timer(1);
let updates_available = $await;
- let await_time = await_start.elapsed();
+ let await_slow = $timer_elapsed(&mut await_start, 1);
if updates_available {
log_trace!($logger, "Persisting ChannelManager...");
log_trace!($logger, "Terminating background processor.");
break;
}
- if last_freshness_call.elapsed().as_secs() > FRESHNESS_TIMER {
+ if $timer_elapsed(&mut last_freshness_call, FRESHNESS_TIMER) {
log_trace!($logger, "Calling ChannelManager's timer_tick_occurred");
$channel_manager.timer_tick_occurred();
- last_freshness_call = Instant::now();
+ last_freshness_call = $get_timer(FRESHNESS_TIMER);
}
- if await_time > Duration::from_secs(1) {
+ if await_slow {
// On various platforms, we may be starved of CPU cycles for several reasons.
// E.g. on iOS, if we've been in the background, we will be entirely paused.
// Similarly, if we're on a desktop platform and the device has been asleep, we
// peers.
log_trace!($logger, "100ms sleep took more than a second, disconnecting peers.");
$peer_manager.disconnect_all_peers();
- last_ping_call = Instant::now();
- } else if last_ping_call.elapsed().as_secs() > PING_TIMER {
+ last_ping_call = $get_timer(PING_TIMER);
+ } else if $timer_elapsed(&mut last_ping_call, PING_TIMER) {
log_trace!($logger, "Calling PeerManager's timer_tick_occurred");
$peer_manager.timer_tick_occurred();
- last_ping_call = Instant::now();
+ last_ping_call = $get_timer(PING_TIMER);
}
// Note that we want to run a graph prune once not long after startup before
// falling back to our usual hourly prunes. This avoids short-lived clients never
// pruning their network graph. We run once 60 seconds after startup before
// continuing our normal cadence.
- if last_prune_call.elapsed().as_secs() > if have_pruned { NETWORK_PRUNE_TIMER } else { FIRST_NETWORK_PRUNE_TIMER } {
+ if $timer_elapsed(&mut last_prune_call, if have_pruned { NETWORK_PRUNE_TIMER } else { FIRST_NETWORK_PRUNE_TIMER }) {
// The network graph must not be pruned while rapid sync completion is pending
if let Some(network_graph) = $gossip_sync.prunable_network_graph() {
- log_trace!($logger, "Pruning and persisting network graph.");
- network_graph.remove_stale_channels_and_tracking();
+ #[cfg(feature = "std")] {
+ log_trace!($logger, "Pruning and persisting network graph.");
+ network_graph.remove_stale_channels_and_tracking();
+ }
+ #[cfg(not(feature = "std"))] {
+ log_warn!($logger, "Not pruning network graph, consider enabling `std` or doing so manually with remove_stale_channels_and_tracking_with_time.");
+ log_trace!($logger, "Persisting network graph.");
+ }
if let Err(e) = $persister.persist_graph(network_graph) {
log_error!($logger, "Error: Failed to persist network graph, check your disk and permissions {}", e)
}
- last_prune_call = Instant::now();
+ last_prune_call = $get_timer(NETWORK_PRUNE_TIMER);
have_pruned = true;
}
}
- if last_scorer_persist_call.elapsed().as_secs() > SCORER_PERSIST_TIMER {
+ if $timer_elapsed(&mut last_scorer_persist_call, SCORER_PERSIST_TIMER) {
if let Some(ref scorer) = $scorer {
log_trace!($logger, "Persisting scorer");
if let Err(e) = $persister.persist_scorer(&scorer) {
log_error!($logger, "Error: Failed to persist scorer, check your disk and permissions {}", e)
}
}
- last_scorer_persist_call = Instant::now();
+ last_scorer_persist_call = $get_timer(SCORER_PERSIST_TIMER);
}
}
/// future which outputs true, the loop will exit and this function's future will complete.
///
/// See [`BackgroundProcessor::start`] for information on which actions this handles.
+///
+/// Requires the `futures` feature. Note that while this method is available without the `std`
+/// feature, doing so will skip calling [`NetworkGraph::remove_stale_channels_and_tracking`],
+/// you should call [`NetworkGraph::remove_stale_channels_and_tracking_with_time`] regularly
+/// manually instead.
#[cfg(feature = "futures")]
pub async fn process_events_async<
'a,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, OMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,
- SleepFuture: core::future::Future<Output = bool>,
+ SleepFuture: core::future::Future<Output = bool> + core::marker::Unpin,
Sleeper: Fn(Duration) -> SleepFuture
>(
persister: PS, event_handler: EventHandler, chain_monitor: M, channel_manager: CM,
gossip_sync: GossipSync<PGS, RGS, G, CA, L>, peer_manager: PM, logger: L, scorer: Option<S>,
sleeper: Sleeper,
-) -> Result<(), std::io::Error>
+) -> Result<(), io::Error>
where
CA::Target: 'static + chain::Access,
CF::Target: 'static + chain::Filter,
false
}
}
+ }, |t| sleeper(Duration::from_secs(t)),
+ |fut: &mut SleepFuture, _| {
+ let mut waker = task::noop_waker();
+ let mut ctx = task::Context::from_waker(&mut waker);
+ core::pin::Pin::new(fut).poll(&mut ctx).is_ready()
})
}
+#[cfg(feature = "std")]
impl BackgroundProcessor {
/// Start a background thread that takes care of responsibilities enumerated in the [top-level
/// documentation].
define_run_body!(persister, chain_monitor, chain_monitor.process_pending_events(&event_handler),
channel_manager, channel_manager.process_pending_events(&event_handler),
gossip_sync, peer_manager, logger, scorer, stop_thread.load(Ordering::Acquire),
- channel_manager.await_persistable_update_timeout(Duration::from_millis(100)))
+ channel_manager.await_persistable_update_timeout(Duration::from_millis(100)),
+ |_| Instant::now(), |time: &Instant, dur| time.elapsed().as_secs() > dur)
});
Self { stop_thread: stop_thread_clone, thread_handle: Some(handle) }
}
}
}
+#[cfg(feature = "std")]
impl Drop for BackgroundProcessor {
fn drop(&mut self) {
self.stop_and_join_thread().unwrap();
}
}
-#[cfg(test)]
+#[cfg(all(feature = "std", test))]
mod tests {
use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::constants::genesis_block;
id: u64,
}
impl Connection {
- async fn poll_event_process<CMH, RMH, OMH, L, UMH>(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>>, mut event_receiver: mpsc::Receiver<()>) where
+ async fn poll_event_process<PM, CMH, RMH, OMH, L, UMH>(
+ peer_manager: PM,
+ mut event_receiver: mpsc::Receiver<()>,
+ ) where
+ PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>> + 'static + Send + Sync,
CMH: Deref + 'static + Send + Sync,
RMH: Deref + 'static + Send + Sync,
OMH: Deref + 'static + Send + Sync,
OMH::Target: OnionMessageHandler + Send + Sync,
L::Target: Logger + Send + Sync,
UMH::Target: CustomMessageHandler + Send + Sync,
- {
+ {
loop {
if event_receiver.recv().await.is_none() {
return;
}
}
- async fn schedule_read<CMH, RMH, OMH, L, UMH>(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>>, us: Arc<Mutex<Self>>, mut reader: io::ReadHalf<TcpStream>, mut read_wake_receiver: mpsc::Receiver<()>, mut write_avail_receiver: mpsc::Receiver<()>) where
+ async fn schedule_read<PM, CMH, RMH, OMH, L, UMH>(
+ peer_manager: PM,
+ us: Arc<Mutex<Self>>,
+ mut reader: io::ReadHalf<TcpStream>,
+ mut read_wake_receiver: mpsc::Receiver<()>,
+ mut write_avail_receiver: mpsc::Receiver<()>,
+ ) where
+ PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>> + 'static + Send + Sync + Clone,
CMH: Deref + 'static + Send + Sync,
RMH: Deref + 'static + Send + Sync,
OMH: Deref + 'static + Send + Sync,
OMH::Target: OnionMessageHandler + 'static + Send + Sync,
L::Target: Logger + 'static + Send + Sync,
UMH::Target: CustomMessageHandler + 'static + Send + Sync,
- {
+ {
// Create a waker to wake up poll_event_process, above
let (event_waker, event_receiver) = mpsc::channel(1);
- tokio::spawn(Self::poll_event_process(Arc::clone(&peer_manager), event_receiver));
+ tokio::spawn(Self::poll_event_process(peer_manager.clone(), event_receiver));
// 8KB is nice and big but also should never cause any issues with stack overflowing.
let mut buf = [0; 8192];
/// The returned future will complete when the peer is disconnected and associated handling
/// futures are freed, though, because all processing futures are spawned with tokio::spawn, you do
/// not need to poll the provided future in order to make progress.
-pub fn setup_inbound<CMH, RMH, OMH, L, UMH>(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>>, stream: StdTcpStream) -> impl std::future::Future<Output=()> where
+pub fn setup_inbound<PM, CMH, RMH, OMH, L, UMH>(
+ peer_manager: PM,
+ stream: StdTcpStream,
+) -> impl std::future::Future<Output=()> where
+ PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>> + 'static + Send + Sync + Clone,
CMH: Deref + 'static + Send + Sync,
RMH: Deref + 'static + Send + Sync,
OMH: Deref + 'static + Send + Sync,
/// The returned future will complete when the peer is disconnected and associated handling
/// futures are freed, though, because all processing futures are spawned with tokio::spawn, you do
/// not need to poll the provided future in order to make progress.
-pub fn setup_outbound<CMH, RMH, OMH, L, UMH>(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>>, their_node_id: PublicKey, stream: StdTcpStream) -> impl std::future::Future<Output=()> where
+pub fn setup_outbound<PM, CMH, RMH, OMH, L, UMH>(
+ peer_manager: PM,
+ their_node_id: PublicKey,
+ stream: StdTcpStream,
+) -> impl std::future::Future<Output=()> where
+ PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>> + 'static + Send + Sync + Clone,
CMH: Deref + 'static + Send + Sync,
RMH: Deref + 'static + Send + Sync,
OMH: Deref + 'static + Send + Sync,
/// disconnected and associated handling futures are freed, though, because all processing in said
/// futures are spawned with tokio::spawn, you do not need to poll the second future in order to
/// make progress.
-pub async fn connect_outbound<CMH, RMH, OMH, L, UMH>(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>>, their_node_id: PublicKey, addr: SocketAddr) -> Option<impl std::future::Future<Output=()>> where
+pub async fn connect_outbound<PM, CMH, RMH, OMH, L, UMH>(
+ peer_manager: PM,
+ their_node_id: PublicKey,
+ addr: SocketAddr,
+) -> Option<impl std::future::Future<Output=()>> where
+ PM: Deref<Target = peer_handler::PeerManager<SocketDescriptor, CMH, RMH, OMH, L, UMH>> + 'static + Send + Sync + Clone,
CMH: Deref + 'static + Send + Sync,
RMH: Deref + 'static + Send + Sync,
OMH: Deref + 'static + Send + Sync,
self.channel_transaction_parameters.opt_anchors.is_some()
}
- fn get_initial_channel_type(config: &UserConfig) -> ChannelTypeFeatures {
+ fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> 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`
+ // with no other changes, and fall back to `only_static_remotekey`.
let mut ret = ChannelTypeFeatures::only_static_remote_key();
- if !config.channel_handshake_config.announced_channel && config.channel_handshake_config.negotiate_scid_privacy {
+ if !config.channel_handshake_config.announced_channel &&
+ config.channel_handshake_config.negotiate_scid_privacy &&
+ their_features.supports_scid_privacy() {
ret.set_scid_privacy_required();
}
+
+ // Optionally, if the user would like to negotiate the `anchors_zero_fee_htlc_tx` option, we
+ // set it now. If they don't understand it, we'll fall back to our default of
+ // `only_static_remotekey`.
+ #[cfg(anchors)]
+ { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
+ if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx &&
+ their_features.supports_anchors_zero_fee_htlc_tx() {
+ ret.set_anchors_zero_fee_htlc_tx_required();
+ }
+ }
+
ret
}
// We've exhausted our options
return Err(());
}
- self.channel_type = ChannelTypeFeatures::only_static_remote_key(); // We only currently support two types
+ // We support opening a few different types of channels. Try removing our additional
+ // features one by one until we've either arrived at our default or the counterparty has
+ // accepted one.
+ //
+ // Due to the order below, we may not negotiate `option_anchors_zero_fee_htlc_tx` if the
+ // counterparty doesn't support `option_scid_privacy`. Since `get_initial_channel_type`
+ // checks whether the counterparty supports every feature, this would only happen if the
+ // counterparty is advertising the feature, but rejecting channels proposing the feature for
+ // whatever reason.
+ if self.channel_type.supports_anchors_zero_fee_htlc_tx() {
+ self.channel_type.clear_anchors_zero_fee_htlc_tx();
+ assert!(self.channel_transaction_parameters.opt_non_zero_fee_anchors.is_none());
+ self.channel_transaction_parameters.opt_anchors = None;
+ } else if self.channel_type.supports_scid_privacy() {
+ self.channel_type.clear_scid_privacy();
+ } else {
+ self.channel_type = ChannelTypeFeatures::only_static_remote_key();
+ }
Ok(self.get_open_channel(chain_hash))
}
SP::Target: SignerProvider<Signer = Signer>,
F::Target: FeeEstimator,
{
- let opt_anchors = false; // TODO - should be based on features
-
let holder_selected_contest_delay = config.channel_handshake_config.our_to_self_delay;
let channel_keys_id = signer_provider.generate_channel_keys_id(false, channel_value_satoshis, user_id);
let holder_signer = signer_provider.derive_channel_signer(channel_value_satoshis, channel_keys_id);
return Err(APIError::APIMisuseError { err: format!("Holder selected channel reserve below implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
}
+ let channel_type = Self::get_initial_channel_type(&config, their_features);
+ debug_assert!(channel_type.is_subset(&channelmanager::provided_channel_type_features(&config)));
+
let feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
let value_to_self_msat = channel_value_satoshis * 1000 - push_msat;
- let commitment_tx_fee = Self::commit_tx_fee_msat(feerate, MIN_AFFORDABLE_HTLC_COUNT, opt_anchors);
+ let commitment_tx_fee = Self::commit_tx_fee_msat(feerate, MIN_AFFORDABLE_HTLC_COUNT, channel_type.requires_anchors_zero_fee_htlc_tx());
if value_to_self_msat < commitment_tx_fee {
return Err(APIError::APIMisuseError{ err: format!("Funding amount ({}) can't even pay fee for initial commitment transaction fee of {}.", value_to_self_msat / 1000, commitment_tx_fee / 1000) });
}
is_outbound_from_holder: true,
counterparty_parameters: None,
funding_outpoint: None,
- opt_anchors: if opt_anchors { Some(()) } else { None },
+ opt_anchors: if channel_type.requires_anchors_zero_fee_htlc_tx() { Some(()) } else { None },
opt_non_zero_fee_anchors: None
},
funding_transaction: None,
#[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills: HashSet::new(),
- channel_type: Self::get_initial_channel_type(&config),
+ channel_type,
channel_keys_id,
})
}
/// Creates a new channel from a remote sides' request for one.
/// Assumes chain_hash has already been checked and corresponds with what we expect!
pub fn new_from_req<ES: Deref, SP: Deref, F: Deref, L: Deref>(
- fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP, counterparty_node_id: PublicKey, their_features: &InitFeatures,
- msg: &msgs::OpenChannel, user_id: u128, config: &UserConfig, current_chain_height: u32, logger: &L,
- outbound_scid_alias: u64
+ fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
+ counterparty_node_id: PublicKey, our_supported_features: &ChannelTypeFeatures,
+ their_features: &InitFeatures, msg: &msgs::OpenChannel, user_id: u128, config: &UserConfig,
+ current_chain_height: u32, logger: &L, outbound_scid_alias: u64
) -> Result<Channel<Signer>, ChannelError>
where ES::Target: EntropySource,
SP::Target: SignerProvider<Signer = Signer>,
F::Target: FeeEstimator,
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
return Err(ChannelError::Close("Channel Type field contained optional bits - this is not allowed".to_owned()));
}
- if channel_type.requires_unknown_bits() {
- return Err(ChannelError::Close("Channel Type field contains unknown bits".to_owned()));
+ // We only support the channel types defined by the `ChannelManager` in
+ // `provided_channel_type_features`. The channel type must always support
+ // `static_remote_key`.
+ if !channel_type.requires_static_remote_key() {
+ return Err(ChannelError::Close("Channel Type was not understood - we require static remote key".to_owned()));
}
-
- // We currently only allow four channel types, so write it all out here - we allow
- // `only_static_remote_key` or `static_remote_key | zero_conf` in all contexts, and
- // further allow `static_remote_key | scid_privacy` or
- // `static_remote_key | scid_privacy | zero_conf`, if the channel is not
- // publicly announced.
- if *channel_type != ChannelTypeFeatures::only_static_remote_key() {
- if !channel_type.requires_scid_privacy() && !channel_type.requires_zero_conf() {
- return Err(ChannelError::Close("Channel Type was not understood".to_owned()));
- }
-
- if channel_type.requires_scid_privacy() && announced_channel {
- return Err(ChannelError::Close("SCID Alias/Privacy Channel Type cannot be set on a public channel".to_owned()));
- }
+ // Make sure we support all of the features behind the channel type.
+ if !channel_type.is_subset(our_supported_features) {
+ return Err(ChannelError::Close("Channel Type contains unsupported features".to_owned()));
+ }
+ if channel_type.requires_scid_privacy() && announced_channel {
+ return Err(ChannelError::Close("SCID Alias/Privacy Channel Type cannot be set on a public channel".to_owned()));
}
channel_type.clone()
} else {
- ChannelTypeFeatures::from_counterparty_init(&their_features)
+ let channel_type = ChannelTypeFeatures::from_init(&their_features);
+ if channel_type != ChannelTypeFeatures::only_static_remote_key() {
+ return Err(ChannelError::Close("Only static_remote_key is supported for non-negotiated channel types".to_owned()));
+ }
+ channel_type
};
- if !channel_type.supports_static_remote_key() {
- return Err(ChannelError::Close("Channel Type was not understood - we require static remote key".to_owned()));
- }
+ let opt_anchors = channel_type.supports_anchors_zero_fee_htlc_tx();
let channel_keys_id = signer_provider.generate_channel_keys_id(true, msg.funding_satoshis, user_id);
let holder_signer = signer_provider.derive_channel_signer(msg.funding_satoshis, channel_keys_id);
} else if their_features.supports_channel_type() {
// Assume they've accepted the channel type as they said they understand it.
} else {
- self.channel_type = ChannelTypeFeatures::from_counterparty_init(&their_features)
+ let channel_type = ChannelTypeFeatures::from_init(&their_features);
+ if channel_type != ChannelTypeFeatures::only_static_remote_key() {
+ return Err(ChannelError::Close("Only static_remote_key is supported for non-negotiated channel types".to_owned()));
+ }
+ self.channel_type = channel_type;
}
let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() {
}
const MAX_ALLOC_SIZE: usize = 64*1024;
-impl<'a, 'b, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32)> for Channel<<SP::Target as SignerProvider>::Signer>
+impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c ChannelTypeFeatures)> for Channel<<SP::Target as SignerProvider>::Signer>
where
ES::Target: EntropySource,
SP::Target: SignerProvider
{
- fn read<R : io::Read>(reader: &mut R, args: (&'a ES, &'b SP, u32)) -> Result<Self, DecodeError> {
- let (entropy_source, signer_provider, serialized_height) = args;
+ fn read<R : io::Read>(reader: &mut R, args: (&'a ES, &'b SP, u32, &'c ChannelTypeFeatures)) -> Result<Self, DecodeError> {
+ let (entropy_source, signer_provider, serialized_height, our_supported_features) = args;
let ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
// `user_id` used to be a single u64 value. In order to remain backwards compatible with
}
let chan_features = channel_type.as_ref().unwrap();
- if chan_features.supports_unknown_bits() || chan_features.requires_unknown_bits() {
+ if !chan_features.is_subset(our_supported_features) {
// If the channel was written by a new version and negotiated with features we don't
// understand yet, refuse to read it.
return Err(DecodeError::UnknownRequiredFeature);
}
- if channel_parameters.opt_anchors.is_some() {
- // Relax this check when ChannelTypeFeatures supports anchors.
- return Err(DecodeError::InvalidValue);
- }
-
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
use hex;
use crate::ln::PaymentHash;
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
+ #[cfg(anchors)]
+ use crate::ln::channel::InitFeatures;
use crate::ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS};
use crate::ln::features::ChannelTypeFeatures;
// Make sure A's dust limit is as we expect.
let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
- let mut node_b_chan = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, 42).unwrap();
+ let mut node_b_chan = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &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(0);
// Create Node B's channel by receiving Node A's open_channel message
let open_channel_msg = node_a_chan.get_open_channel(chain_hash);
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
- let mut node_b_chan = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, 42).unwrap();
+ let mut node_b_chan = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &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(0);
// Test that `new_from_req` creates a channel with the correct value for
// `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
// which is set to the lower bound - 1 (2%) of the `channel_value`.
- let chan_3 = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_init_features(&config_2_percent), &chan_1_open_channel_msg, 7, &config_2_percent, 0, &&logger, 42).unwrap();
+ let chan_3 = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_2_percent), &channelmanager::provided_init_features(&config_2_percent), &chan_1_open_channel_msg, 7, &config_2_percent, 0, &&logger, 42).unwrap();
let chan_3_value_msat = chan_3.channel_value_satoshis * 1000;
assert_eq!(chan_3.holder_max_htlc_value_in_flight_msat, (chan_3_value_msat as f64 * 0.02) as u64);
// Test with the upper bound - 1 of valid values (99%).
- let chan_4 = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_init_features(&config_99_percent), &chan_1_open_channel_msg, 7, &config_99_percent, 0, &&logger, 42).unwrap();
+ let chan_4 = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_99_percent), &channelmanager::provided_init_features(&config_99_percent), &chan_1_open_channel_msg, 7, &config_99_percent, 0, &&logger, 42).unwrap();
let chan_4_value_msat = chan_4.channel_value_satoshis * 1000;
assert_eq!(chan_4.holder_max_htlc_value_in_flight_msat, (chan_4_value_msat as f64 * 0.99) as u64);
// Test that `new_from_req` uses the lower bound of the configurable percentage values (1%)
// if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1.
- let chan_7 = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_init_features(&config_0_percent), &chan_1_open_channel_msg, 7, &config_0_percent, 0, &&logger, 42).unwrap();
+ let chan_7 = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_0_percent), &channelmanager::provided_init_features(&config_0_percent), &chan_1_open_channel_msg, 7, &config_0_percent, 0, &&logger, 42).unwrap();
let chan_7_value_msat = chan_7.channel_value_satoshis * 1000;
assert_eq!(chan_7.holder_max_htlc_value_in_flight_msat, (chan_7_value_msat as f64 * 0.01) as u64);
// Test that `new_from_req` uses the upper bound of the configurable percentage values
// (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value
// than 100.
- let chan_8 = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_init_features(&config_101_percent), &chan_1_open_channel_msg, 7, &config_101_percent, 0, &&logger, 42).unwrap();
+ let chan_8 = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&config_101_percent), &channelmanager::provided_init_features(&config_101_percent), &chan_1_open_channel_msg, 7, &config_101_percent, 0, &&logger, 42).unwrap();
let chan_8_value_msat = chan_8.channel_value_satoshis * 1000;
assert_eq!(chan_8.holder_max_htlc_value_in_flight_msat, chan_8_value_msat);
}
inbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (inbound_selected_channel_reserve_perc * 1_000_000.0) as u32;
if outbound_selected_channel_reserve_perc + inbound_selected_channel_reserve_perc < 1.0 {
- let chan_inbound_node = Channel::<EnforcingSigner>::new_from_req(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, 42).unwrap();
+ let chan_inbound_node = Channel::<EnforcingSigner>::new_from_req(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&inbound_node_config), &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, 42).unwrap();
let expected_inbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.channel_value_satoshis as f64 * inbound_selected_channel_reserve_perc) as u64);
assert_eq!(chan_inbound_node.counterparty_selected_channel_reserve_satoshis.unwrap(), expected_outbound_selected_chan_reserve);
} else {
// Channel Negotiations failed
- let result = Channel::<EnforcingSigner>::new_from_req(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, 42);
+ let result = Channel::<EnforcingSigner>::new_from_req(&&fee_est, &&keys_provider, &&keys_provider, inbound_node_id, &channelmanager::provided_channel_type_features(&inbound_node_config), &channelmanager::provided_init_features(&outbound_node_config), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, 42);
assert!(result.is_err());
}
}
open_channel_msg.channel_type = Some(channel_type_features);
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
let res = Channel::<EnforcingSigner>::new_from_req(&feeest, &&keys_provider, &&keys_provider,
- node_b_node_id, &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, 42);
+ node_b_node_id, &channelmanager::provided_channel_type_features(&config),
+ &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, 42);
assert!(res.is_ok());
}
+
+ #[cfg(anchors)]
+ #[test]
+ fn test_supports_anchors_zero_htlc_tx_fee() {
+ // Tests that if both sides support and negotiate `anchors_zero_fee_htlc_tx`, it is the
+ // resulting `channel_type`.
+ let secp_ctx = Secp256k1::new();
+ let fee_estimator = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});
+ let network = Network::Testnet;
+ let keys_provider = test_utils::TestKeysInterface::new(&[42; 32], network);
+ let logger = test_utils::TestLogger::new();
+
+ let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap());
+ let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap());
+
+ let mut config = UserConfig::default();
+ config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
+
+ // It is not enough for just the initiator to signal `option_anchors_zero_fee_htlc_tx`, both
+ // need to signal it.
+ let channel_a = Channel::<EnforcingSigner>::new_outbound(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
+ &channelmanager::provided_init_features(&UserConfig::default()), 10000000, 100000, 42,
+ &config, 0, 42
+ ).unwrap();
+ assert!(!channel_a.channel_type.supports_anchors_zero_fee_htlc_tx());
+
+ let mut expected_channel_type = ChannelTypeFeatures::empty();
+ expected_channel_type.set_static_remote_key_required();
+ expected_channel_type.set_anchors_zero_fee_htlc_tx_required();
+
+ let channel_a = Channel::<EnforcingSigner>::new_outbound(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
+ &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
+ ).unwrap();
+
+ let open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
+ let channel_b = Channel::<EnforcingSigner>::new_from_req(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
+ &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config),
+ &open_channel_msg, 7, &config, 0, &&logger, 42
+ ).unwrap();
+
+ assert_eq!(channel_a.channel_type, expected_channel_type);
+ assert_eq!(channel_b.channel_type, expected_channel_type);
+ }
+
+ #[cfg(anchors)]
+ #[test]
+ fn test_rejects_implicit_simple_anchors() {
+ // Tests that if `option_anchors` is being negotiated implicitly through the intersection of
+ // each side's `InitFeatures`, it is rejected.
+ let secp_ctx = Secp256k1::new();
+ let fee_estimator = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});
+ let network = Network::Testnet;
+ let keys_provider = test_utils::TestKeysInterface::new(&[42; 32], network);
+ let logger = test_utils::TestLogger::new();
+
+ let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap());
+ let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap());
+
+ let config = UserConfig::default();
+
+ // See feature bit assignments: https://github.com/lightning/bolts/blob/master/09-features.md
+ let static_remote_key_required: u64 = 1 << 12;
+ let simple_anchors_required: u64 = 1 << 20;
+ let raw_init_features = static_remote_key_required | simple_anchors_required;
+ let init_features_with_simple_anchors = InitFeatures::from_le_bytes(raw_init_features.to_le_bytes().to_vec());
+
+ let channel_a = Channel::<EnforcingSigner>::new_outbound(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
+ &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
+ ).unwrap();
+
+ // Set `channel_type` to `None` to force the implicit feature negotiation.
+ let mut open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
+ open_channel_msg.channel_type = None;
+
+ // Since A supports both `static_remote_key` and `option_anchors`, but B only accepts
+ // `static_remote_key`, it will fail the channel.
+ let channel_b = Channel::<EnforcingSigner>::new_from_req(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
+ &channelmanager::provided_channel_type_features(&config), &init_features_with_simple_anchors,
+ &open_channel_msg, 7, &config, 0, &&logger, 42
+ );
+ assert!(channel_b.is_err());
+ }
+
+ #[cfg(anchors)]
+ #[test]
+ fn test_rejects_simple_anchors_channel_type() {
+ // Tests that if `option_anchors` is being negotiated through the `channel_type` feature,
+ // it is rejected.
+ let secp_ctx = Secp256k1::new();
+ let fee_estimator = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});
+ let network = Network::Testnet;
+ let keys_provider = test_utils::TestKeysInterface::new(&[42; 32], network);
+ let logger = test_utils::TestLogger::new();
+
+ let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap());
+ let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap());
+
+ let config = UserConfig::default();
+
+ // See feature bit assignments: https://github.com/lightning/bolts/blob/master/09-features.md
+ let static_remote_key_required: u64 = 1 << 12;
+ let simple_anchors_required: u64 = 1 << 20;
+ let simple_anchors_raw_features = static_remote_key_required | simple_anchors_required;
+ let simple_anchors_init = InitFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec());
+ let simple_anchors_channel_type = ChannelTypeFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec());
+ assert!(simple_anchors_init.requires_unknown_bits());
+ assert!(simple_anchors_channel_type.requires_unknown_bits());
+
+ // First, we'll try to open a channel between A and B where A requests a channel type for
+ // the original `option_anchors` feature (non zero fee htlc tx). This should be rejected by
+ // B as it's not supported by LDK.
+ let channel_a = Channel::<EnforcingSigner>::new_outbound(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_b,
+ &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
+ ).unwrap();
+
+ let mut open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
+ open_channel_msg.channel_type = Some(simple_anchors_channel_type.clone());
+
+ let res = Channel::<EnforcingSigner>::new_from_req(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
+ &channelmanager::provided_channel_type_features(&config), &simple_anchors_init,
+ &open_channel_msg, 7, &config, 0, &&logger, 42
+ );
+ assert!(res.is_err());
+
+ // Then, we'll try to open another channel where A requests a channel type for
+ // `anchors_zero_fee_htlc_tx`. B is malicious and tries to downgrade the channel type to the
+ // original `option_anchors` feature, which should be rejected by A as it's not supported by
+ // LDK.
+ let mut channel_a = Channel::<EnforcingSigner>::new_outbound(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, &simple_anchors_init,
+ 10000000, 100000, 42, &config, 0, 42
+ ).unwrap();
+
+ let open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
+
+ let channel_b = Channel::<EnforcingSigner>::new_from_req(
+ &fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
+ &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config),
+ &open_channel_msg, 7, &config, 0, &&logger, 42
+ ).unwrap();
+
+ let mut accept_channel_msg = channel_b.get_accept_channel_message();
+ accept_channel_msg.channel_type = Some(simple_anchors_channel_type.clone());
+
+ let res = channel_a.accept_channel(
+ &accept_channel_msg, &config.channel_handshake_limits, &simple_anchors_init
+ );
+ assert!(res.is_err());
+ }
}
let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
let peer_state = &mut *peer_state_lock;
let mut channel = match Channel::new_from_req(&self.fee_estimator, &self.entropy_source, &self.signer_provider,
- counterparty_node_id.clone(), &peer_state.latest_features, msg, user_channel_id, &self.default_configuration,
+ counterparty_node_id.clone(), &self.channel_type_features(), &peer_state.latest_features, msg, user_channel_id, &self.default_configuration,
self.best_block.read().unwrap().height(), &self.logger, outbound_scid_alias)
{
Err(e) => {
/// Fetches the set of [`ChannelTypeFeatures`] flags which are provided by or required by
/// [`ChannelManager`].
pub(crate) fn provided_channel_type_features(config: &UserConfig) -> ChannelTypeFeatures {
- ChannelTypeFeatures::from_counterparty_init(&provided_init_features(config))
+ ChannelTypeFeatures::from_init(&provided_init_features(config))
}
/// Fetches the set of [`InitFeatures`] flags which are provided by or required by
features.set_channel_type_optional();
features.set_scid_privacy_optional();
features.set_zero_conf_optional();
+ #[cfg(anchors)]
+ { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
+ if _config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx {
+ features.set_anchors_zero_fee_htlc_tx_optional();
+ }
+ }
features
}
let mut short_to_chan_info = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
let mut channel_closures = Vec::new();
for _ in 0..channel_count {
- let mut channel: Channel<<SP::Target as SignerProvider>::Signer> = Channel::read(reader, (&args.entropy_source, &args.signer_provider, best_block_height))?;
+ let mut channel: Channel<<SP::Target as SignerProvider>::Signer> = Channel::read(reader, (
+ &args.entropy_source, &args.signer_provider, best_block_height, &provided_channel_type_features(&args.default_config)
+ ))?;
let funding_txo = channel.get_funding_txo().ok_or(DecodeError::InvalidValue)?;
funding_txo_set.insert(funding_txo.clone());
if let Some(ref mut monitor) = args.channel_monitors.get_mut(&funding_txo) {
nodes[1].node.handle_update_fee(&unkown_public_key, &update_fee_msg);
}
+
+ #[cfg(anchors)]
+ #[test]
+ fn test_anchors_zero_fee_htlc_tx_fallback() {
+ // Tests that if both nodes support anchors, but the remote node does not want to accept
+ // anchor channels at the moment, an error it sent to the local node such that it can retry
+ // the channel without the anchors feature.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let mut anchors_config = test_default_channel_config();
+ anchors_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
+ anchors_config.manually_accept_inbound_channels = true;
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(anchors_config.clone()), Some(anchors_config.clone())]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 0, None).unwrap();
+ let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+ assert!(open_channel_msg.channel_type.as_ref().unwrap().supports_anchors_zero_fee_htlc_tx());
+
+ nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel_msg);
+ let events = nodes[1].node.get_and_clear_pending_events();
+ match events[0] {
+ Event::OpenChannelRequest { temporary_channel_id, .. } => {
+ nodes[1].node.force_close_broadcasting_latest_txn(&temporary_channel_id, &nodes[0].node.get_our_node_id()).unwrap();
+ }
+ _ => panic!("Unexpected event"),
+ }
+
+ let error_msg = get_err_msg!(nodes[1], nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_error(&nodes[1].node.get_our_node_id(), &error_msg);
+
+ let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+ assert!(!open_channel_msg.channel_type.unwrap().supports_anchors_zero_fee_htlc_tx());
+
+ check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed);
+ }
}
#[cfg(all(any(test, feature = "_test_utils"), feature = "_bench_unstable"))]
//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information).
//! - `Keysend` - send funds to a node without an invoice
//! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information).
+//! - `AnchorsZeroFeeHtlcTx` - requires/supports that commitment transactions include anchor outputs
+//! and HTLC transactions are pre-signed with zero fee (see
+//! [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more
+//! information).
//!
//! [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md
//! [messages]: crate::ln::msgs
// Byte 1
VariableLengthOnion | StaticRemoteKey | PaymentSecret,
// Byte 2
- BasicMPP | Wumbo,
+ BasicMPP | Wumbo | AnchorsZeroFeeHtlcTx,
// Byte 3
ShutdownAnySegwit,
// Byte 4
// Byte 1
VariableLengthOnion | StaticRemoteKey | PaymentSecret,
// Byte 2
- BasicMPP | Wumbo,
+ BasicMPP | Wumbo | AnchorsZeroFeeHtlcTx,
// Byte 3
ShutdownAnySegwit,
// Byte 4
// Byte 1
StaticRemoteKey,
// Byte 2
- ,
+ AnchorsZeroFeeHtlcTx,
// Byte 3
,
// Byte 4
define_feature!(19, Wumbo, [InitContext, NodeContext],
"Feature flags for `option_support_large_channel` (aka wumbo channels).", set_wumbo_optional, set_wumbo_required,
supports_wumbo, requires_wumbo);
+ define_feature!(23, AnchorsZeroFeeHtlcTx, [InitContext, NodeContext, ChannelTypeContext],
+ "Feature flags for `option_anchors_zero_fee_htlc_tx`.", set_anchors_zero_fee_htlc_tx_optional,
+ set_anchors_zero_fee_htlc_tx_required, supports_anchors_zero_fee_htlc_tx, requires_anchors_zero_fee_htlc_tx);
define_feature!(27, ShutdownAnySegwit, [InitContext, NodeContext],
"Feature flags for `opt_shutdown_anysegwit`.", set_shutdown_any_segwit_optional,
set_shutdown_any_segwit_required, supports_shutdown_anysegwit, requires_shutdown_anysegwit);
}
impl ChannelTypeFeatures {
- /// Constructs the implicit channel type based on the common supported types between us and our
- /// counterparty
- pub(crate) fn from_counterparty_init(counterparty_init: &InitFeatures) -> Self {
- let mut ret = counterparty_init.to_context_internal();
+ // Maps the relevant `InitFeatures` to `ChannelTypeFeatures`. Any unknown features to
+ // `ChannelTypeFeatures` are not included in the result.
+ pub(crate) fn from_init(init: &InitFeatures) -> Self {
+ let mut ret = init.to_context_internal();
// ChannelTypeFeatures must only contain required bits, so we OR the required forms of all
// optional bits and then AND out the optional ones.
for byte in ret.flags.iter_mut() {
(byte & unknown_features) != 0
})
}
+
+ // Returns true if the features within `self` are a subset of the features within `other`.
+ pub(crate) fn is_subset(&self, other: &Self) -> bool {
+ for (idx, byte) in self.flags.iter().enumerate() {
+ if let Some(other_byte) = other.flags.get(idx) {
+ if byte & other_byte != *byte {
+ // `self` has bits set that `other` doesn't.
+ return false;
+ }
+ } else {
+ if *byte > 0 {
+ // `self` has a non-zero byte that `other` doesn't.
+ return false;
+ }
+ }
+ }
+ true
+ }
}
impl<T: sealed::UpfrontShutdownScript> Features<T> {
}
}
+impl<T: sealed::SCIDPrivacy> Features<T> {
+ pub(crate) fn clear_scid_privacy(&mut self) {
+ <T as sealed::SCIDPrivacy>::clear_bits(&mut self.flags);
+ }
+}
+
+impl<T: sealed::AnchorsZeroFeeHtlcTx> Features<T> {
+ pub(crate) fn clear_anchors_zero_fee_htlc_tx(&mut self) {
+ <T as sealed::AnchorsZeroFeeHtlcTx>::clear_bits(&mut self.flags);
+ }
+}
+
#[cfg(test)]
impl<T: sealed::UnknownFeature> Features<T> {
pub(crate) fn unknown() -> Self {
init_features.set_channel_type_optional();
init_features.set_scid_privacy_optional();
init_features.set_zero_conf_optional();
+ init_features.set_anchors_zero_fee_htlc_tx_optional();
assert!(init_features.initial_routing_sync());
assert!(!init_features.supports_upfront_shutdown_script());
assert_eq!(node_features.flags.len(), 7);
assert_eq!(node_features.flags[0], 0b00000010);
assert_eq!(node_features.flags[1], 0b01010001);
- assert_eq!(node_features.flags[2], 0b00001010);
+ assert_eq!(node_features.flags[2], 0b10001010);
assert_eq!(node_features.flags[3], 0b00001000);
assert_eq!(node_features.flags[4], 0b10000000);
assert_eq!(node_features.flags[5], 0b10100000);
// required-StaticRemoteKey ChannelTypeFeatures.
let mut init_features = InitFeatures::empty();
init_features.set_static_remote_key_optional();
- let converted_features = ChannelTypeFeatures::from_counterparty_init(&init_features);
+ let converted_features = ChannelTypeFeatures::from_init(&init_features);
assert_eq!(converted_features, ChannelTypeFeatures::only_static_remote_key());
assert!(!converted_features.supports_any_optional_bits());
assert!(converted_features.requires_static_remote_key());
connect_blocks(node, CHAN_CONFIRM_DEPTH - 1);
scid
}
-/// Mine a signle block containing the given transaction
+/// Mine a single block containing the given transaction
pub fn mine_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) {
let height = node.best_block_info().1 + 1;
confirm_transaction_at(node, tx, height);
}
+/// Mine a single block containing the given transactions
+pub fn mine_transactions<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, txn: &[&Transaction]) {
+ let height = node.best_block_info().1 + 1;
+ confirm_transactions_at(node, txn, height);
+}
/// Mine the given transaction at the given height, mining blocks as required to build to that
/// height
///
/// Returns the SCID a channel confirmed in the given transaction will have, assuming the funding
/// output is the 1st output in the transaction.
-pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) -> u64 {
+pub fn confirm_transactions_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, txn: &[&Transaction], conf_height: u32) -> u64 {
let first_connect_height = node.best_block_info().1 + 1;
assert!(first_connect_height <= conf_height);
if conf_height > first_connect_height {
for _ in 0..*node.network_chan_count.borrow() { // Make sure we don't end up with channels at the same short id by offsetting by chan_count
block.txdata.push(Transaction { version: 0, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() });
}
- block.txdata.push(tx.clone());
+ for tx in txn {
+ block.txdata.push((*tx).clone());
+ }
connect_block(node, &block);
scid_utils::scid_from_parts(conf_height as u64, block.txdata.len() as u64 - 1, 0).unwrap()
}
+pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) -> u64 {
+ confirm_transactions_at(node, &[tx], conf_height)
+}
/// The possible ways we may notify a ChannelManager of a new block
#[derive(Clone, Copy, Debug, PartialEq)]
let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id());
open_channel.to_self_delay = 200;
if let Err(error) = Channel::new_from_req(&LowerBoundedFeeEstimator::new(&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }),
- &nodes[0].keys_manager, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &nodes[1].node.init_features(), &open_channel, 0,
+ &nodes[0].keys_manager, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &nodes[0].node.channel_type_features(), &nodes[1].node.init_features(), &open_channel, 0,
&low_our_to_self_config, 0, &nodes[0].logger, 42)
{
match error {
let mut open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id());
open_channel.to_self_delay = 200;
if let Err(error) = Channel::new_from_req(&LowerBoundedFeeEstimator::new(&test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }),
- &nodes[0].keys_manager, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &nodes[1].node.init_features(), &open_channel, 0,
+ &nodes[0].keys_manager, &nodes[0].keys_manager, nodes[1].node.get_our_node_id(), &nodes[0].node.channel_type_features(), &nodes[1].node.init_features(), &open_channel, 0,
&high_their_to_self_config, 0, &nodes[0].logger, 42)
{
match error {
//! Further functional tests which test blockchain reorganizations.
+#[cfg(anchors)]
+use crate::chain::keysinterface::BaseSign;
+#[cfg(anchors)]
+use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, Balance};
use crate::chain::transaction::OutPoint;
use crate::chain::chaininterface::LowerBoundedFeeEstimator;
use crate::ln::channel;
+#[cfg(anchors)]
+use crate::ln::chan_utils;
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, PaymentId};
use crate::ln::msgs::ChannelMessageHandler;
+#[cfg(anchors)]
+use crate::util::config::UserConfig;
+#[cfg(anchors)]
+use crate::util::events::BumpTransactionEvent;
use crate::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason, HTLCDestination};
use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::opcodes;
use bitcoin::secp256k1::Secp256k1;
+#[cfg(anchors)]
+use bitcoin::{Amount, Script, TxIn, TxOut, PackedLockTime};
use bitcoin::Transaction;
use crate::prelude::*;
assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
assert!(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances().is_empty());
}
+
+#[cfg(anchors)]
+#[test]
+fn test_yield_anchors_events() {
+ // Tests that two parties supporting anchor outputs can open a channel, route payments over
+ // it, and finalize its resolution uncooperatively. Once the HTLCs are locked in, one side will
+ // force close once the HTLCs expire. The force close should stem from an event emitted by LDK,
+ // allowing the consumer to provide additional fees to the commitment transaction to be
+ // broadcast. Once the commitment transaction confirms, events for the HTLC resolution should be
+ // emitted by LDK, such that the consumer can attach fees to the zero fee HTLC transactions.
+ let secp = Secp256k1::new();
+ let mut chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let mut anchors_config = UserConfig::default();
+ anchors_config.channel_handshake_config.announced_channel = true;
+ anchors_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(anchors_config), Some(anchors_config)]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let chan_id = create_announced_chan_between_nodes_with_value(
+ &nodes, 0, 1, 1_000_000, 500_000_000
+ ).2;
+ route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
+ let (payment_preimage, payment_hash, _) = route_payment(&nodes[1], &[&nodes[0]], 1_000_000);
+
+ assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
+
+ connect_blocks(&nodes[0], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
+ check_closed_broadcast!(&nodes[0], true);
+ assert!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().is_empty());
+
+ get_monitor!(nodes[0], chan_id).provide_payment_preimage(
+ &payment_hash, &payment_preimage, &node_cfgs[0].tx_broadcaster,
+ &LowerBoundedFeeEstimator::new(node_cfgs[0].fee_estimator), &nodes[0].logger
+ );
+
+ let mut holder_events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ assert_eq!(holder_events.len(), 1);
+ let (commitment_tx, anchor_tx) = match holder_events.pop().unwrap() {
+ Event::BumpTransaction(BumpTransactionEvent::ChannelClose { commitment_tx, anchor_descriptor, .. }) => {
+ assert_eq!(commitment_tx.input.len(), 1);
+ assert_eq!(commitment_tx.output.len(), 6);
+ let mut anchor_tx = Transaction {
+ version: 2,
+ lock_time: PackedLockTime::ZERO,
+ input: vec![
+ TxIn { previous_output: anchor_descriptor.outpoint, ..Default::default() },
+ TxIn { ..Default::default() },
+ ],
+ output: vec![TxOut {
+ value: Amount::ONE_BTC.to_sat(),
+ script_pubkey: Script::new_op_return(&[]),
+ }],
+ };
+ let signer = nodes[0].keys_manager.derive_channel_keys(
+ anchor_descriptor.channel_value_satoshis, &anchor_descriptor.channel_keys_id,
+ );
+ let funding_sig = signer.sign_holder_anchor_input(&mut anchor_tx, 0, &secp).unwrap();
+ anchor_tx.input[0].witness = chan_utils::build_anchor_input_witness(
+ &signer.pubkeys().funding_pubkey, &funding_sig
+ );
+ (commitment_tx, anchor_tx)
+ },
+ _ => panic!("Unexpected event"),
+ };
+
+ mine_transactions(&nodes[0], &[&commitment_tx, &anchor_tx]);
+ check_added_monitors!(nodes[0], 1);
+
+ let mut holder_events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ // Certain block `ConnectStyle`s cause an extra `ChannelClose` event to be emitted since the
+ // best block is being updated prior to the confirmed transactions.
+ match *nodes[0].connect_style.borrow() {
+ ConnectStyle::BestBlockFirst|ConnectStyle::BestBlockFirstReorgsOnlyTip|ConnectStyle::BestBlockFirstSkippingBlocks => {
+ assert_eq!(holder_events.len(), 3);
+ if let Event::BumpTransaction(BumpTransactionEvent::ChannelClose { .. }) = holder_events.remove(0) {}
+ else { panic!("unexpected event"); }
+
+ },
+ _ => assert_eq!(holder_events.len(), 2),
+ };
+ let mut htlc_txs = Vec::with_capacity(2);
+ for event in holder_events {
+ match event {
+ Event::BumpTransaction(BumpTransactionEvent::HTLCResolution { htlc_descriptors, .. }) => {
+ assert_eq!(htlc_descriptors.len(), 1);
+ let htlc_descriptor = &htlc_descriptors[0];
+ let signer = nodes[0].keys_manager.derive_channel_keys(
+ htlc_descriptor.channel_value_satoshis, &htlc_descriptor.channel_keys_id
+ );
+ let per_commitment_point = signer.get_per_commitment_point(htlc_descriptor.per_commitment_number, &secp);
+ let mut htlc_tx = Transaction {
+ version: 2,
+ lock_time: if htlc_descriptor.htlc.offered {
+ PackedLockTime(htlc_descriptor.htlc.cltv_expiry)
+ } else {
+ PackedLockTime::ZERO
+ },
+ input: vec![
+ htlc_descriptor.unsigned_tx_input(), // HTLC input
+ TxIn { ..Default::default() } // Fee input
+ ],
+ output: vec![
+ htlc_descriptor.tx_output(&per_commitment_point, &secp), // HTLC output
+ TxOut { // Fee input change
+ value: Amount::ONE_BTC.to_sat(),
+ script_pubkey: Script::new_op_return(&[]),
+ }
+ ]
+ };
+ let our_sig = signer.sign_holder_htlc_transaction(&mut htlc_tx, 0, htlc_descriptor, &secp).unwrap();
+ let witness_script = htlc_descriptor.witness_script(&per_commitment_point, &secp);
+ htlc_tx.input[0].witness = htlc_descriptor.tx_input_witness(&our_sig, &witness_script);
+ htlc_txs.push(htlc_tx);
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+
+ mine_transactions(&nodes[0], &[&htlc_txs[0], &htlc_txs[1]]);
+ connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
+
+ assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
+
+ connect_blocks(&nodes[0], BREAKDOWN_TIMEOUT as u32);
+
+ let holder_events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ assert_eq!(holder_events.len(), 3);
+ for event in holder_events {
+ match event {
+ Event::SpendableOutputs { .. } => {},
+ _ => panic!("Unexpected event"),
+ }
+ }
+
+ // Clear the remaining events as they're not relevant to what we're testing.
+ nodes[0].node.get_and_clear_pending_events();
+}
///
/// [`SignerProvider::get_shutdown_scriptpubkey`]: crate::chain::keysinterface::SignerProvider::get_shutdown_scriptpubkey
pub commit_upfront_shutdown_pubkey: bool,
-
/// The Proportion of the channel value to configure as counterparty's channel reserve,
/// i.e., `their_channel_reserve_satoshis` for both outbound and inbound channels.
///
/// as 1000 sats instead, which is a safe implementation-specific lower bound.
/// Maximum value: 1,000,000, any values larger than 1 Million will be treated as 1 Million (or 100%)
/// instead, although channel negotiations will fail in that case.
- pub their_channel_reserve_proportional_millionths: u32
+ pub their_channel_reserve_proportional_millionths: u32,
+ #[cfg(anchors)]
+ /// If set, we attempt to negotiate the `anchors_zero_fee_htlc_tx`option for outbound channels.
+ ///
+ /// If this option is set, channels may be created that will not be readable by LDK versions
+ /// prior to 0.0.114, 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 `anchors_zero_fee_htlc_tx` option; we will simply
+ /// fall back to a `static_remote_key` channel.
+ ///
+ /// LDK will not support the legacy `option_anchors` commitment version due to a discovered
+ /// vulnerability after its deployment. For more context, see the [`SIGHASH_SINGLE + update_fee
+ /// Considered Harmful`] mailing list post.
+ ///
+ /// 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
+ /// [`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,
}
impl Default for ChannelHandshakeConfig {
announced_channel: false,
commit_upfront_shutdown_pubkey: true,
their_channel_reserve_proportional_millionths: 10_000,
+ #[cfg(anchors)]
+ negotiate_anchors_zero_fee_htlc_tx: false,
}
}
}
/// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
pub channel_value_satoshis: u64,
/// The necessary channel parameters that need to be provided to the re-derived
- /// [`InMemorySigner`] through [`BaseSign::ready_channel`].
+ /// [`InMemorySigner`] through [`BaseSign::provide_channel_parameters`].
///
/// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
- /// [`BaseSign::ready_channel`]: crate::chain::keysinterface::BaseSign::ready_channel
+ /// [`BaseSign::provide_channel_parameters`]: crate::chain::keysinterface::BaseSign::provide_channel_parameters
pub channel_parameters: ChannelTransactionParameters,
/// The txid of the commitment transaction in which the HTLC output lives.
pub commitment_txid: Txid,
}
}
if num_preimage > 0 || num_timeout > 0 || num_revoked > 0 {
- write!(f, "HTLC claim tx ({} preimage, {} timeout, {} revoked)",
+ write!(f, "HTLC claim tx ({} preimage, {} timeout, {} revoked) ",
num_preimage, num_timeout, num_revoked)?;
}
}
use core::convert::TryFrom;
use core::ops::Deref;
+use alloc::collections::BTreeMap;
+
use bitcoin::secp256k1::{PublicKey, SecretKey};
use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE, SCHNORR_SIGNATURE_SIZE};
use bitcoin::secp256k1::ecdsa;
}
}
+/// The lightning protocol uses u16s for lengths in most cases. As our serialization framework
+/// primarily targets that, we must as well. However, because we may serialize objects that have
+/// more than 65K entries, we need to be able to store larger values. Thus, we define a variable
+/// length integer here that is backwards-compatible for values < 0xffff. We treat 0xffff as
+/// "read eight more bytes".
+///
+/// To ensure we only have one valid encoding per value, we add 0xffff to values written as eight
+/// bytes. Thus, 0xfffe is serialized as 0xfffe, whereas 0xffff is serialized as
+/// 0xffff0000000000000000 (i.e. read-eight-bytes then zero).
+struct CollectionLength(pub u64);
+impl Writeable for CollectionLength {
+ #[inline]
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ if self.0 < 0xffff {
+ (self.0 as u16).write(writer)
+ } else {
+ 0xffffu16.write(writer)?;
+ (self.0 - 0xffff).write(writer)
+ }
+ }
+}
+
+impl Readable for CollectionLength {
+ #[inline]
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let mut val: u64 = <u16 as Readable>::read(r)? as u64;
+ if val == 0xffff {
+ val = <u64 as Readable>::read(r)?
+ .checked_add(0xffff).ok_or(DecodeError::InvalidValue)?;
+ }
+ Ok(CollectionLength(val))
+ }
+}
+
/// In TLV we occasionally send fields which only consist of, or potentially end with, a
/// variable-length integer which is simply truncated by skipping high zero bytes. This type
/// encapsulates such integers implementing [`Readable`]/[`Writeable`] for them.
fn from(v: &'a Vec<T>) -> Self { Self(v) }
}
-// HashMap
-impl<K, V> Writeable for HashMap<K, V>
- where K: Writeable + Eq + Hash,
- V: Writeable
-{
- #[inline]
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- (self.len() as u16).write(w)?;
- for (key, value) in self.iter() {
- key.write(w)?;
- value.write(w)?;
+macro_rules! impl_for_map {
+ ($ty: ident, $keybound: ident, $constr: expr) => {
+ impl<K, V> Writeable for $ty<K, V>
+ where K: Writeable + Eq + $keybound, V: Writeable
+ {
+ #[inline]
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ CollectionLength(self.len() as u64).write(w)?;
+ for (key, value) in self.iter() {
+ key.write(w)?;
+ value.write(w)?;
+ }
+ Ok(())
+ }
}
- Ok(())
- }
-}
-impl<K, V> Readable for HashMap<K, V>
- where K: Readable + Eq + Hash,
- V: MaybeReadable
-{
- #[inline]
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let len: u16 = Readable::read(r)?;
- let mut ret = HashMap::with_capacity(len as usize);
- for _ in 0..len {
- let k = K::read(r)?;
- let v_opt = V::read(r)?;
- if let Some(v) = v_opt {
- if ret.insert(k, v).is_some() {
- return Err(DecodeError::InvalidValue);
+ impl<K, V> Readable for $ty<K, V>
+ where K: Readable + Eq + $keybound, V: MaybeReadable
+ {
+ #[inline]
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let len: CollectionLength = Readable::read(r)?;
+ let mut ret = $constr(len.0 as usize);
+ for _ in 0..len.0 {
+ let k = K::read(r)?;
+ let v_opt = V::read(r)?;
+ if let Some(v) = v_opt {
+ if ret.insert(k, v).is_some() {
+ return Err(DecodeError::InvalidValue);
+ }
+ }
}
+ Ok(ret)
}
}
- Ok(ret)
}
}
+impl_for_map!(BTreeMap, Ord, |_| BTreeMap::new());
+impl_for_map!(HashMap, Hash, |len| HashMap::with_capacity(len));
+
// HashSet
impl<T> Writeable for HashSet<T>
where T: Writeable + Eq + Hash
{
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- (self.len() as u16).write(w)?;
+ CollectionLength(self.len() as u64).write(w)?;
for item in self.iter() {
item.write(w)?;
}
{
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let len: u16 = Readable::read(r)?;
- let mut ret = HashSet::with_capacity(len as usize);
- for _ in 0..len {
+ let len: CollectionLength = Readable::read(r)?;
+ let mut ret = HashSet::with_capacity(cmp::min(len.0 as usize, MAX_BUF_SIZE / core::mem::size_of::<T>()));
+ for _ in 0..len.0 {
if !ret.insert(T::read(r)?) {
return Err(DecodeError::InvalidValue)
}
}
// Vectors
-impl Writeable for Vec<u8> {
- #[inline]
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- (self.len() as u16).write(w)?;
- w.write_all(&self)
- }
-}
+macro_rules! impl_for_vec {
+ ($ty: ty $(, $name: ident)*) => {
+ impl<$($name : Writeable),*> Writeable for Vec<$ty> {
+ #[inline]
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ CollectionLength(self.len() as u64).write(w)?;
+ for elem in self.iter() {
+ elem.write(w)?;
+ }
+ Ok(())
+ }
+ }
-impl Readable for Vec<u8> {
- #[inline]
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let len: u16 = Readable::read(r)?;
- let mut ret = Vec::with_capacity(len as usize);
- ret.resize(len as usize, 0);
- r.read_exact(&mut ret)?;
- Ok(ret)
+ impl<$($name : Readable),*> Readable for Vec<$ty> {
+ #[inline]
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let len: CollectionLength = Readable::read(r)?;
+ let mut ret = Vec::with_capacity(cmp::min(len.0 as usize, MAX_BUF_SIZE / core::mem::size_of::<$ty>()));
+ for _ in 0..len.0 {
+ if let Some(val) = MaybeReadable::read(r)? {
+ ret.push(val);
+ }
+ }
+ Ok(ret)
+ }
+ }
}
}
-impl Writeable for Vec<ecdsa::Signature> {
+
+impl Writeable for Vec<u8> {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- (self.len() as u16).write(w)?;
- for e in self.iter() {
- e.write(w)?;
- }
- Ok(())
+ CollectionLength(self.len() as u64).write(w)?;
+ w.write_all(&self)
}
}
-impl Readable for Vec<ecdsa::Signature> {
+impl Readable for Vec<u8> {
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let len: u16 = Readable::read(r)?;
- let byte_size = (len as usize)
- .checked_mul(COMPACT_SIGNATURE_SIZE)
- .ok_or(DecodeError::BadLengthDescriptor)?;
- if byte_size > MAX_BUF_SIZE {
- return Err(DecodeError::BadLengthDescriptor);
+ let mut len: CollectionLength = Readable::read(r)?;
+ let mut ret = Vec::new();
+ while len.0 > 0 {
+ let readamt = cmp::min(len.0 as usize, MAX_BUF_SIZE);
+ let readstart = ret.len();
+ ret.resize(readstart + readamt, 0);
+ r.read_exact(&mut ret[readstart..])?;
+ len.0 -= readamt as u64;
}
- let mut ret = Vec::with_capacity(len as usize);
- for _ in 0..len { ret.push(Readable::read(r)?); }
Ok(ret)
}
}
+impl_for_vec!(ecdsa::Signature);
+impl_for_vec!((A, B), A, B);
+
impl Writeable for Script {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
(self.len() as u16).write(w)?;
impl Writeable for String {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- (self.len() as u16).write(w)?;
+ CollectionLength(self.len() as u64).write(w)?;
w.write_all(self.as_bytes())
}
}
field.write($stream)?;
}
};
+ ($stream: expr, $type: expr, $field: expr, ignorable) => {
+ $crate::_encode_tlv!($stream, $type, $field, required);
+ };
($stream: expr, $type: expr, $field: expr, (option, encoding: ($fieldty: ty, $encoding: ident))) => {
$crate::_encode_tlv!($stream, $type, $field.map(|f| $encoding(f)), option);
};
$len.0 += field_len;
}
};
+ ($len: expr, $type: expr, $field: expr, ignorable) => {
+ $crate::_get_varint_length_prefixed_tlv_length!($len, $type, $field, required);
+ };
}
/// See the documentation of [`write_tlv_fields`].
($field: ident, option) => {
$field
};
+ ($field: ident, ignorable) => {
+ if $field.is_none() { return Ok(None); } else { $field.unwrap() }
+ };
($field: ident, required) => {
$field.0.unwrap()
};
($field: ident, option) => {
let mut $field = None;
};
+ ($field: ident, ignorable) => {
+ let mut $field = None;
+ };
}
/// Equivalent to running [`_init_tlv_field_var`] then [`read_tlv_fields`].
lightning = { path = "../lightning", default-features = false }
lightning-invoice = { path = "../lightning-invoice", default-features = false }
lightning-rapid-gossip-sync = { path = "../lightning-rapid-gossip-sync", default-features = false }
+lightning-background-processor = { path = "../lightning-background-processor", features = ["futures"], default-features = false }