beta,
# 1.41.1 is MSRV for Rust-Lightning, lightning-invoice, and lightning-persister
1.41.1,
- # 1.45.2 is MSRV for lightning-net-tokio, lightning-block-sync, and coverage generation
+ # 1.45.2 is MSRV for lightning-net-tokio, lightning-block-sync, lightning-background-processor, and coverage generation
1.45.2,
# 1.47.0 will be the MSRV for no-std builds using hashbrown once core2 is updated
1.47.0]
- toolchain: stable
build-net-tokio: true
build-no-std: true
+ build-futures: true
- toolchain: stable
platform: macos-latest
build-net-tokio: true
build-no-std: true
+ build-futures: true
- toolchain: beta
platform: macos-latest
build-net-tokio: true
build-no-std: true
+ build-futures: true
- toolchain: stable
platform: windows-latest
build-net-tokio: true
build-no-std: true
+ build-futures: true
- toolchain: beta
platform: windows-latest
build-net-tokio: true
build-no-std: true
+ build-futures: true
- toolchain: beta
build-net-tokio: true
build-no-std: true
+ build-futures: true
- toolchain: 1.41.1
build-no-std: false
test-log-variants: true
+ build-futures: false
- toolchain: 1.45.2
build-net-old-tokio: true
build-net-tokio: true
build-no-std: false
+ build-futures: true
coverage: true
- toolchain: 1.47.0
+ build-futures: true
build-no-std: true
runs-on: ${{ matrix.platform }}
steps:
- name: Test on Rust ${{ matrix.toolchain }} with net-tokio and full code-linking for coverage generation
if: matrix.coverage
run: RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always
- - name: Test on no-std bullds Rust ${{ matrix.toolchain }}
+ - name: Test no-std builds on Rust ${{ matrix.toolchain }}
if: "matrix.build-no-std && !matrix.coverage"
shell: bash # Default on Winblows is powershell
run: |
run: |
cd lightning
RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --no-default-features --features no-std
- cd ..
+ - name: Test futures builds on Rust ${{ matrix.toolchain }}
+ if: "matrix.build-futures && !matrix.coverage"
+ shell: bash # Default on Winblows is powershell
+ run: |
+ cd lightning-background-processor
+ cargo test --verbose --color always --no-default-features --features futures
+ - name: Test futures builds on Rust ${{ matrix.toolchain }} and full code-linking for coverage generation
+ if: "matrix.build-futures && matrix.coverage"
+ shell: bash # Default on Winblows is powershell
+ run: |
+ cd lightning-background-processor
+ RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --no-default-features --features futures
- name: Test on Rust ${{ matrix.toolchain }}
if: "! matrix.build-net-tokio"
run: |
- cargo test --verbose --color always -p lightning
- cargo test --verbose --color always -p lightning-invoice
- cargo test --verbose --color always -p lightning-rapid-gossip-sync
- cargo build --verbose --color always -p lightning-persister
- cargo build --verbose --color always -p lightning-background-processor
+ cargo test --verbose --color always -p lightning
+ cargo test --verbose --color always -p lightning-invoice
+ cargo test --verbose --color always -p lightning-rapid-gossip-sync
+ cargo test --verbose --color always -p lightning-persister
+ cargo test --verbose --color always -p lightning-background-processor
- name: Test C Bindings Modifications on Rust ${{ matrix.toolchain }}
if: "! matrix.build-net-tokio"
run: |
run: |
cargo check --release
cargo check --no-default-features --features=no-std --release
+ cargo check --no-default-features --features=futures --release
cargo doc --release
fuzz:
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
+[features]
+futures = [ "futures-util" ]
+
[dependencies]
bitcoin = "0.29.0"
lightning = { version = "0.0.111", path = "../lightning", features = ["std"] }
lightning-rapid-gossip-sync = { version = "0.0.111", path = "../lightning-rapid-gossip-sync" }
-futures = { version = "0.3", optional = true }
+futures-util = { version = "0.3", default-features = false, features = ["async-await-macro"], optional = true }
[dev-dependencies]
lightning = { version = "0.0.111", path = "../lightning", features = ["_test_utils"] }
use std::ops::Deref;
#[cfg(feature = "futures")]
-use futures::{select, future::FutureExt};
+use futures_util::{select_biased, future::FutureExt};
/// `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
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
+ OMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
- PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
+ 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>,
L::Target: 'static + Logger,
P::Target: 'static + Persist<Signer>,
CMH::Target: 'static + ChannelMessageHandler,
+ OMH::Target: 'static + OnionMessageHandler,
RMH::Target: 'static + RoutingMessageHandler,
UMH::Target: 'static + CustomMessageHandler,
PS::Target: 'static + Persister<'a, Signer, CW, T, K, F, L, SC>,
let mut should_continue = true;
define_run_body!(persister, event_handler, chain_monitor, channel_manager,
gossip_sync, peer_manager, logger, scorer, should_continue, {
- select! {
+ select_biased! {
_ = channel_manager.get_persistable_update_future().fuse() => true,
cont = sleeper(Duration::from_millis(100)).fuse() => {
should_continue = cont;
const EVENT_DEADLINE: u64 = 5 * FRESHNESS_TIMER;
- #[derive(Clone, Eq, Hash, PartialEq)]
+ #[derive(Clone, Hash, PartialEq, Eq)]
struct TestDescriptor{}
impl SocketDescriptor for TestDescriptor {
fn send_data(&mut self, _data: &[u8], _resume_read: bool) -> usize {
[dependencies]
bitcoin = "0.29.0"
lightning = { version = "0.0.111", path = "../lightning" }
-futures = { version = "0.3" }
+futures-util = { version = "0.3" }
tokio = { version = "1.0", features = [ "io-util", "net", "time" ], optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
}
/// The kind of `BlockSourceError`, either persistent or transient.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BlockSourceErrorKind {
/// Indicates an error that won't resolve when retrying a request (e.g., invalid data).
Persistent,
/// A block header and some associated data. This information should be available from most block
/// sources (and, notably, is available in Bitcoin Core's RPC and REST interfaces).
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BlockHeaderData {
/// The block header itself.
pub header: BlockHeader,
use bitcoin::hash_types::BlockHash;
use bitcoin::network::constants::Network;
+use lightning::chain::BestBlock;
use std::ops::Deref;
}
/// A chain tip relative to another chain tip in terms of block hash and chainwork.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ChainTip {
/// A chain tip with the same hash as another chain's tip.
Common,
}
/// A block header with validated proof of work and corresponding block hash.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ValidatedBlockHeader {
pub(crate) block_hash: BlockHash,
inner: BlockHeaderData,
Ok(())
}
+
+ /// Returns the [`BestBlock`] corresponding to this validated block header, which can be passed
+ /// into [`ChannelManager::new`] as part of its [`ChainParameters`]. Useful for ensuring that
+ /// the [`SpvClient`] and [`ChannelManager`] are initialized to the same block during a fresh
+ /// start.
+ ///
+ /// [`SpvClient`]: crate::SpvClient
+ /// [`ChainParameters`]: lightning::ln::channelmanager::ChainParameters
+ /// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager
+ /// [`ChannelManager::new`]: lightning::ln::channelmanager::ChannelManager::new
+ pub fn to_best_block(&self) -> BestBlock {
+ BestBlock::new(self.block_hash, self.inner.height)
+ }
}
/// A block with validated data against its transaction list and corresponding block hash.
use bitcoin::hash_types::BlockHash;
use bitcoin::hashes::hex::ToHex;
-use futures::lock::Mutex;
+use futures_util::lock::Mutex;
use std::convert::TryFrom;
use std::convert::TryInto;
use bitcoin::hash_types::BlockHash;
use bitcoin::hashes::hex::ToHex;
-use futures::lock::Mutex;
+use futures_util::lock::Mutex;
use serde_json;
/// Errors that indicate what is wrong with the invoice. They have some granularity for debug
/// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
#[allow(missing_docs)]
-#[derive(PartialEq, Debug, Clone)]
+#[derive(PartialEq, Eq, Debug, Clone)]
pub enum ParseError {
Bech32Error(bech32::Error),
ParseAmountError(ParseIntError),
/// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
/// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
/// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
-#[derive(PartialEq, Debug, Clone)]
+#[derive(PartialEq, Eq, Debug, Clone)]
pub enum ParseOrSemanticError {
/// The invoice couldn't be decoded
ParseError(ParseError),
L::Target: Logger,
P::Target: Persist<ChannelSigner>,
{
+ #[cfg(not(anchors))]
/// Processes [`SpendableOutputs`] events produced from each [`ChannelMonitor`] upon maturity.
///
/// An [`EventHandler`] may safely call back to the provider, though this shouldn't be needed in
handler.handle_event(&event);
}
}
+ #[cfg(anchors)]
+ /// Processes [`SpendableOutputs`] events produced from each [`ChannelMonitor`] upon maturity.
+ ///
+ /// For channels featuring anchor outputs, this method will also process [`BumpTransaction`]
+ /// events produced from each [`ChannelMonitor`] while there is a balance to claim onchain
+ /// within each channel. As the confirmation of a commitment transaction may be critical to the
+ /// safety of funds, this method must be invoked frequently, ideally once for every chain tip
+ /// update (block connected or disconnected).
+ ///
+ /// An [`EventHandler`] may safely call back to the provider, though this shouldn't be needed in
+ /// order to handle these events.
+ ///
+ /// [`SpendableOutputs`]: events::Event::SpendableOutputs
+ /// [`BumpTransaction`]: events::Event::BumpTransaction
+ fn process_pending_events<H: Deref>(&self, handler: H) where H::Target: EventHandler {
+ let mut pending_events = Vec::new();
+ for monitor_state in self.monitors.read().unwrap().values() {
+ pending_events.append(&mut monitor_state.monitor.get_and_clear_pending_events());
+ }
+ for event in pending_events.drain(..) {
+ handler.handle_event(&event);
+ }
+ }
}
#[cfg(test)]
//! ChannelMonitors to get out of the HSM and onto monitoring devices.
use bitcoin::blockdata::block::BlockHeader;
-use bitcoin::blockdata::transaction::{TxOut,Transaction};
-use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
+use bitcoin::blockdata::transaction::{OutPoint as BitcoinOutPoint, TxOut, Transaction};
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
use chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
use chain::transaction::{OutPoint, TransactionData};
use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, Sign, KeysInterface};
+#[cfg(anchors)]
+use chain::onchaintx::ClaimEvent;
use chain::onchaintx::OnchainTxHandler;
use chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
use chain::Filter;
use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48, OptionDeserWrapper};
use util::byte_utils;
use util::events::Event;
+#[cfg(anchors)]
+use util::events::{AnchorDescriptor, BumpTransactionEvent};
use prelude::*;
use core::{cmp, mem};
/// much smaller than a full [`ChannelMonitor`]. However, for large single commitment transaction
/// updates (e.g. ones during which there are hundreds of HTLCs pending on the commitment
/// transaction), a single update may reach upwards of 1 MiB in serialized size.
-#[cfg_attr(any(test, fuzzing, feature = "_test_utils"), derive(PartialEq))]
+#[cfg_attr(any(test, fuzzing, feature = "_test_utils"), derive(PartialEq, Eq))]
#[derive(Clone)]
#[must_use]
pub struct ChannelMonitorUpdate {
}
/// An event to be processed by the ChannelManager.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub enum MonitorEvent {
/// A monitor event containing an HTLCUpdate.
HTLCEvent(HTLCUpdate),
/// Simple structure sent back by `chain::Watch` when an HTLC from a forward channel is detected on
/// chain. Used to update the corresponding HTLC in the backward channel. Failing to pass the
/// preimage claim backward will lead to loss of funds.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub struct HTLCUpdate {
pub(crate) payment_hash: PaymentHash,
pub(crate) payment_preimage: Option<PaymentPreimage>,
pub(crate) const HTLC_FAIL_BACK_BUFFER: u32 = CLTV_CLAIM_BUFFER + LATENCY_GRACE_PERIOD_BLOCKS;
// TODO(devrandom) replace this with HolderCommitmentTransaction
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
struct HolderSignedTx {
/// txid of the transaction in tx, just used to make comparison faster
txid: Txid,
(14, htlc_outputs, vec_type)
});
+#[cfg(anchors)]
+impl HolderSignedTx {
+ fn non_dust_htlcs(&self) -> Vec<HTLCOutputInCommitment> {
+ self.htlc_outputs.iter().filter_map(|(htlc, _, _)| {
+ if let Some(_) = htlc.transaction_output_index {
+ Some(htlc.clone())
+ } else {
+ None
+ }
+ })
+ .collect()
+ }
+}
+
/// We use this to track static counterparty commitment transaction data and to generate any
/// justice or 2nd-stage preimage/timeout transactions.
-#[derive(PartialEq)]
+#[derive(PartialEq, Eq)]
struct CounterpartyCommitmentParameters {
counterparty_delayed_payment_base_key: PublicKey,
counterparty_htlc_base_key: PublicKey,
/// transaction causing it.
///
/// Used to determine when the on-chain event can be considered safe from a chain reorganization.
-#[derive(PartialEq)]
+#[derive(PartialEq, Eq)]
struct OnchainEventEntry {
txid: Txid,
height: u32,
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
-#[derive(PartialEq)]
+#[derive(PartialEq, Eq)]
enum OnchainEvent {
/// An outbound HTLC failing after a transaction is confirmed. Used
/// * when an outbound HTLC output is spent by us after the HTLC timed out
);
-#[cfg_attr(any(test, fuzzing, feature = "_test_utils"), derive(PartialEq))]
+#[cfg_attr(any(test, fuzzing, feature = "_test_utils"), derive(PartialEq, Eq))]
#[derive(Clone)]
pub(crate) enum ChannelMonitorUpdateStep {
LatestHolderCommitmentTXInfo {
}
/// An HTLC which has been irrevocably resolved on-chain, and has reached ANTI_REORG_DELAY.
-#[derive(PartialEq)]
+#[derive(PartialEq, Eq)]
struct IrrevocablyResolvedHTLC {
commitment_tx_output_idx: Option<u32>,
/// The txid of the transaction which resolved the HTLC, this may be a commitment (if the HTLC
B::Target: BroadcasterInterface,
L::Target: Logger,
{
- self.inner.lock().unwrap().broadcast_latest_holder_commitment_txn(broadcaster, logger)
+ self.inner.lock().unwrap().broadcast_latest_holder_commitment_txn(broadcaster, logger);
}
/// Updates a ChannelMonitor on the basis of some new information provided by the Channel
panic!("Attempted to apply ChannelMonitorUpdates out of order, check the update_id before passing an update to update_monitor!");
}
let mut ret = Ok(());
+ let bounded_fee_estimator = LowerBoundedFeeEstimator::new(&*fee_estimator);
for update in updates.updates.iter() {
match update {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs } => {
},
ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage } => {
log_trace!(logger, "Updating ChannelMonitor with payment preimage");
- let bounded_fee_estimator = LowerBoundedFeeEstimator::new(&*fee_estimator);
self.provide_payment_preimage(&PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()), &payment_preimage, broadcaster, &bounded_fee_estimator, logger)
},
ChannelMonitorUpdateStep::CommitmentSecret { idx, secret } => {
self.lockdown_from_offchain = true;
if *should_broadcast {
self.broadcast_latest_holder_commitment_txn(broadcaster, logger);
+ // If the channel supports anchor outputs, we'll need to emit an external
+ // event to be consumed such that a child transaction is broadcast with a
+ // high enough feerate for the parent commitment transaction to confirm.
+ if self.onchain_tx_handler.opt_anchors() {
+ let funding_output = HolderFundingOutput::build(
+ self.funding_redeemscript.clone(), self.channel_value_satoshis,
+ self.onchain_tx_handler.opt_anchors(),
+ );
+ let best_block_height = self.best_block.height();
+ let commitment_package = PackageTemplate::build_package(
+ self.funding_info.0.txid.clone(), self.funding_info.0.index as u32,
+ PackageSolvingData::HolderFundingOutput(funding_output),
+ best_block_height, false, best_block_height,
+ );
+ self.onchain_tx_handler.update_claims_view(
+ &[], vec![commitment_package], best_block_height, best_block_height,
+ broadcaster, &bounded_fee_estimator, logger,
+ );
+ }
} else if !self.holder_tx_signed {
log_error!(logger, "WARNING: You have a potentially-unsafe holder commitment transaction available to broadcast");
log_error!(logger, " in channel monitor for channel {}!", log_bytes!(self.funding_info.0.to_channel_id()));
pub fn get_and_clear_pending_events(&mut self) -> Vec<Event> {
let mut ret = Vec::new();
mem::swap(&mut ret, &mut self.pending_events);
+ #[cfg(anchors)]
+ for claim_event in self.onchain_tx_handler.get_and_clear_pending_claim_events().drain(..) {
+ match claim_event {
+ ClaimEvent::BumpCommitment {
+ package_target_feerate_sat_per_1000_weight, commitment_tx, anchor_output_idx,
+ } => {
+ let commitment_txid = commitment_tx.txid();
+ debug_assert_eq!(self.current_holder_commitment_tx.txid, commitment_txid);
+ let pending_htlcs = self.current_holder_commitment_tx.non_dust_htlcs();
+ let commitment_tx_fee_satoshis = self.channel_value_satoshis -
+ commitment_tx.output.iter().fold(0u64, |sum, output| sum + output.value);
+ ret.push(Event::BumpTransaction(BumpTransactionEvent::ChannelClose {
+ package_target_feerate_sat_per_1000_weight,
+ commitment_tx,
+ commitment_tx_fee_satoshis,
+ anchor_descriptor: AnchorDescriptor {
+ channel_keys_id: self.channel_keys_id,
+ channel_value_satoshis: self.channel_value_satoshis,
+ outpoint: BitcoinOutPoint {
+ txid: commitment_txid,
+ vout: anchor_output_idx,
+ },
+ },
+ pending_htlcs,
+ }));
+ },
+ }
+ }
ret
}
CounterpartyOfferedHTLCOutput::build(*per_commitment_point,
self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
self.counterparty_commitment_params.counterparty_htlc_base_key,
- preimage.unwrap(), htlc.clone()))
+ preimage.unwrap(), htlc.clone(), self.onchain_tx_handler.opt_anchors()))
} else {
PackageSolvingData::CounterpartyReceivedHTLCOutput(
CounterpartyReceivedHTLCOutput::build(*per_commitment_point,
self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
self.counterparty_commitment_params.counterparty_htlc_base_key,
- htlc.clone()))
+ htlc.clone(), self.onchain_tx_handler.opt_anchors()))
};
let aggregation = if !htlc.offered { false } else { true };
let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry,aggregation, 0);
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
txid,
transaction: Some((*tx).clone()),
- height: height,
+ height,
event: OnchainEvent::FundingSpendConfirmation {
on_local_output_csv: balance_spendable_csv,
commitment_tx_to_counterparty_output,
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
if should_broadcast {
- let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone());
+ let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone(), self.channel_value_satoshis, self.onchain_tx_handler.opt_anchors());
let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), false, self.best_block.height());
claimable_outpoints.push(commitment_package);
self.pending_monitor_events.push(MonitorEvent::CommitmentTxConfirmed(self.funding_info.0));
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
self.holder_tx_signed = true;
- // Because we're broadcasting a commitment transaction, we should construct the package
- // assuming it gets confirmed in the next block. Sadly, we have code which considers
- // "not yet confirmed" things as discardable, so we cannot do that here.
- let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, self.best_block.height());
- let new_outputs = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, &commitment_tx);
- if !new_outputs.is_empty() {
- watch_outputs.push((self.current_holder_commitment_tx.txid.clone(), new_outputs));
+ // We can't broadcast our HTLC transactions while the commitment transaction is
+ // unconfirmed. We'll delay doing so until we detect the confirmed commitment in
+ // `transactions_confirmed`.
+ if !self.onchain_tx_handler.opt_anchors() {
+ // Because we're broadcasting a commitment transaction, we should construct the package
+ // assuming it gets confirmed in the next block. Sadly, we have code which considers
+ // "not yet confirmed" things as discardable, so we cannot do that here.
+ let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, self.best_block.height());
+ let new_outputs = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, &commitment_tx);
+ if !new_outputs.is_empty() {
+ watch_outputs.push((self.current_holder_commitment_tx.txid.clone(), new_outputs));
+ }
+ claimable_outpoints.append(&mut new_outpoints);
}
- claimable_outpoints.append(&mut new_outpoints);
}
// Find which on-chain events have reached their confirmation threshold.
let entry = OnchainEventEntry {
txid: tx.txid(),
transaction: Some(tx.clone()),
- height: height,
+ height,
event: OnchainEvent::MaturingOutput { descriptor: spendable_output.clone() },
};
log_info!(logger, "Received spendable output {}, spendable at height {}", log_spendable!(spendable_output), entry.confirmation_threshold());
use util::ser::{Writeable, Writer, Readable, ReadableArgs};
use chain::transaction::OutPoint;
+use ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
use ln::{chan_utils, PaymentPreimage};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
use ln::msgs::UnsignedChannelAnnouncement;
/// Information about a spendable output to a P2WSH script. See
/// SpendableOutputDescriptor::DelayedPaymentOutput for more details on how to spend this.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DelayedPaymentOutputDescriptor {
/// The outpoint which is spendable
pub outpoint: OutPoint,
/// Information about a spendable output to our "payment key". See
/// SpendableOutputDescriptor::StaticPaymentOutput for more details on how to spend this.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StaticPaymentOutputDescriptor {
/// The outpoint which is spendable
pub outpoint: OutPoint,
/// spend on-chain. The information needed to do this is provided in this enum, including the
/// outpoint describing which txid and output index is available, the full output which exists at
/// that txid/index, and any keys or other information required to sign.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SpendableOutputDescriptor {
/// An output to a script which was provided via KeysInterface directly, either from
/// `get_destination_script()` or `get_shutdown_scriptpubkey()`, thus you should already know
/// chosen to forgo their output as dust.
fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
+ /// Computes the signature for a commitment transaction's anchor output used as an
+ /// input within `anchor_tx`, which spends the commitment transaction, at index `input`.
+ fn sign_holder_anchor_input(
+ &self, anchor_tx: &mut Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
+ ) -> Result<Signature, ()>;
+
/// Signs a channel announcement message with our funding key and our node secret key (aka
/// node_id or network_key), proving it comes from one of the channel participants.
///
witness.push(witness_script.clone().into_bytes());
Ok(witness)
}
+
}
impl BaseSign for InMemorySigner {
Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}
+ fn sign_holder_anchor_input(
+ &self, anchor_tx: &mut Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
+ ) -> Result<Signature, ()> {
+ let witness_script = chan_utils::get_anchor_redeemscript(&self.holder_channel_pubkeys.funding_pubkey);
+ let sighash = sighash::SighashCache::new(&*anchor_tx).segwit_signature_hash(
+ input, &witness_script, ANCHOR_OUTPUT_VALUE_SATOSHI, EcdsaSighashType::All,
+ ).unwrap();
+ Ok(sign(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key))
+ }
+
fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
-> Result<(Signature, Signature), ()> {
let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
pub(crate) mod package;
/// The best known block as identified by its hash and height.
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, Copy, PartialEq, Eq)]
pub struct BestBlock {
block_hash: BlockHash,
height: u32,
}
/// An enum representing the status of a channel monitor update persistence.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelMonitorUpdateStatus {
/// The update has been durably persisted and all copies of the relevant [`ChannelMonitor`]
/// have been updated.
///
/// [`ChannelMonitor`]: channelmonitor::ChannelMonitor
/// [`ChannelMonitor::block_connected`]: channelmonitor::ChannelMonitor::block_connected
-#[derive(Clone, PartialEq, Hash)]
+#[derive(Clone, PartialEq, Eq, Hash)]
pub struct WatchedOutput {
/// First block where the transaction output may have been spent.
pub block_hash: Option<BlockHash>,
use ln::msgs::DecodeError;
use ln::PaymentPreimage;
+#[cfg(anchors)]
+use ln::chan_utils;
use ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransaction};
+#[cfg(anchors)]
+use chain::chaininterface::ConfirmationTarget;
use chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
use chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
use chain::keysinterface::{Sign, KeysInterface};
+#[cfg(anchors)]
+use chain::package::PackageSolvingData;
use chain::package::PackageTemplate;
use util::logger::Logger;
use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, VecWriter};
use core::cmp;
use core::ops::Deref;
use core::mem::replace;
+#[cfg(anchors)]
+use core::mem::swap;
use bitcoin::hashes::Hash;
const MAX_ALLOC_SIZE: usize = 64*1024;
/// transaction causing it.
///
/// Used to determine when the on-chain event can be considered safe from a chain reorganization.
-#[derive(PartialEq)]
+#[derive(PartialEq, Eq)]
struct OnchainEventEntry {
txid: Txid,
height: u32,
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
-#[derive(PartialEq)]
+#[derive(PartialEq, Eq)]
enum OnchainEvent {
/// Outpoint under claim process by our own tx, once this one get enough confirmations, we remove it from
/// bump-txn candidate buffer.
}
}
+// Represents the different types of claims for which events are yielded externally to satisfy said
+// claims.
+#[cfg(anchors)]
+pub(crate) enum ClaimEvent {
+ /// Event yielded to signal that the commitment transaction fee must be bumped to claim any
+ /// encumbered funds and proceed to HTLC resolution, if any HTLCs exist.
+ BumpCommitment {
+ package_target_feerate_sat_per_1000_weight: u32,
+ commitment_tx: Transaction,
+ anchor_output_idx: u32,
+ },
+}
+
+/// Represents the different ways an output can be claimed (i.e., spent to an address under our
+/// control) onchain.
+pub(crate) enum OnchainClaim {
+ /// A finalized transaction pending confirmation spending the output to claim.
+ Tx(Transaction),
+ #[cfg(anchors)]
+ /// An event yielded externally to signal additional inputs must be added to a transaction
+ /// pending confirmation spending the output to claim.
+ Event(ClaimEvent),
+}
/// OnchainTxHandler receives claiming requests, aggregates them if it's sound, broadcast and
/// do RBF bumping if possible.
pub(crate) pending_claim_requests: HashMap<Txid, PackageTemplate>,
#[cfg(not(test))]
pending_claim_requests: HashMap<Txid, PackageTemplate>,
+ #[cfg(anchors)]
+ pending_claim_events: HashMap<Txid, ClaimEvent>,
// Used to link outpoints claimed in a connected block to a pending claim request.
// Key is outpoint than monitor parsing has detected we have keys/scripts to claim
locktimed_packages,
pending_claim_requests,
onchain_events_awaiting_threshold_conf,
+ #[cfg(anchors)]
+ pending_claim_events: HashMap::new(),
secp_ctx,
})
}
claimable_outpoints: HashMap::new(),
locktimed_packages: BTreeMap::new(),
onchain_events_awaiting_threshold_conf: Vec::new(),
+ #[cfg(anchors)]
+ pending_claim_events: HashMap::new(),
secp_ctx,
}
self.holder_commitment.to_broadcaster_value_sat()
}
- /// Lightning security model (i.e being able to redeem/timeout HTLC or penalize coutnerparty onchain) lays on the assumption of claim transactions getting confirmed before timelock expiration
- /// (CSV or CLTV following cases). In case of high-fee spikes, claim tx may stuck in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or Child-Pay-For-Parent.
- /// Panics if there are signing errors, because signing operations in reaction to on-chain events
- /// are not expected to fail, and if they do, we may lose funds.
- fn generate_claim_tx<F: Deref, L: Deref>(&mut self, cur_height: u32, cached_request: &PackageTemplate, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(Option<u32>, u64, Transaction)>
+ #[cfg(anchors)]
+ pub(crate) fn get_and_clear_pending_claim_events(&mut self) -> Vec<ClaimEvent> {
+ let mut ret = HashMap::new();
+ swap(&mut ret, &mut self.pending_claim_events);
+ ret.into_iter().map(|(_, event)| event).collect::<Vec<_>>()
+ }
+
+ /// Lightning security model (i.e being able to redeem/timeout HTLC or penalize counterparty
+ /// onchain) lays on the assumption of claim transactions getting confirmed before timelock
+ /// expiration (CSV or CLTV following cases). In case of high-fee spikes, claim tx may get stuck
+ /// in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or
+ /// Child-Pay-For-Parent.
+ ///
+ /// Panics if there are signing errors, because signing operations in reaction to on-chain
+ /// events are not expected to fail, and if they do, we may lose funds.
+ fn generate_claim<F: Deref, L: Deref>(&mut self, cur_height: u32, cached_request: &PackageTemplate, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(Option<u32>, u64, OnchainClaim)>
where F::Target: FeeEstimator,
L::Target: Logger,
{
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
let new_timer = Some(cached_request.get_height_timer(cur_height));
if cached_request.is_malleable() {
- let predicted_weight = cached_request.package_weight(&self.destination_script, self.channel_transaction_parameters.opt_anchors.is_some());
+ let predicted_weight = cached_request.package_weight(&self.destination_script);
if let Some((output_value, new_feerate)) =
cached_request.compute_package_output(predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger) {
assert!(new_feerate != 0);
- let transaction = cached_request.finalize_package(self, output_value, self.destination_script.clone(), logger).unwrap();
+ let transaction = cached_request.finalize_malleable_package(self, output_value, self.destination_script.clone(), logger).unwrap();
log_trace!(logger, "...with timer {} and feerate {}", new_timer.unwrap(), new_feerate);
assert!(predicted_weight >= transaction.weight());
- return Some((new_timer, new_feerate, transaction))
+ return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)))
}
} else {
- // Note: Currently, amounts of holder outputs spending witnesses aren't used
- // as we can't malleate spending package to increase their feerate. This
- // should change with the remaining anchor output patchset.
- if let Some(transaction) = cached_request.finalize_package(self, 0, self.destination_script.clone(), logger) {
- return Some((None, 0, transaction));
+ // Untractable packages cannot have their fees bumped through Replace-By-Fee. Some
+ // packages may support fee bumping through Child-Pays-For-Parent, indicated by those
+ // which require external funding.
+ #[cfg(not(anchors))]
+ let inputs = cached_request.inputs();
+ #[cfg(anchors)]
+ let mut inputs = cached_request.inputs();
+ debug_assert_eq!(inputs.len(), 1);
+ let tx = match cached_request.finalize_untractable_package(self, logger) {
+ Some(tx) => tx,
+ None => return None,
+ };
+ if !cached_request.requires_external_funding() {
+ return Some((None, 0, OnchainClaim::Tx(tx)));
}
+ #[cfg(anchors)]
+ return inputs.find_map(|input| match input {
+ // Commitment inputs with anchors support are the only untractable inputs supported
+ // thus far that require external funding.
+ PackageSolvingData::HolderFundingOutput(..) => {
+ debug_assert_eq!(tx.txid(), self.holder_commitment.trust().txid(),
+ "Holder commitment transaction mismatch");
+ // We'll locate an anchor output we can spend within the commitment transaction.
+ let funding_pubkey = &self.channel_transaction_parameters.holder_pubkeys.funding_pubkey;
+ match chan_utils::get_anchor_output(&tx, funding_pubkey) {
+ // An anchor output was found, so we should yield a funding event externally.
+ Some((idx, _)) => {
+ // TODO: Use a lower confirmation target when both our and the
+ // counterparty's latest commitment don't have any HTLCs present.
+ let conf_target = ConfirmationTarget::HighPriority;
+ let package_target_feerate_sat_per_1000_weight = cached_request
+ .compute_package_feerate(fee_estimator, conf_target);
+ Some((
+ new_timer,
+ package_target_feerate_sat_per_1000_weight as u64,
+ OnchainClaim::Event(ClaimEvent::BumpCommitment {
+ package_target_feerate_sat_per_1000_weight,
+ commitment_tx: tx.clone(),
+ anchor_output_idx: idx,
+ }),
+ ))
+ },
+ // An anchor output was not found. There's nothing we can do other than
+ // attempt to broadcast the transaction with its current fee rate and hope
+ // it confirms. This is essentially the same behavior as a commitment
+ // transaction without anchor outputs.
+ None => Some((None, 0, OnchainClaim::Tx(tx.clone()))),
+ }
+ },
+ _ => {
+ debug_assert!(false, "Only HolderFundingOutput inputs should be untractable and require external funding");
+ None
+ },
+ });
}
None
}
// Generate claim transactions and track them to bump if necessary at
// height timer expiration (i.e in how many blocks we're going to take action).
for mut req in preprocessed_requests {
- if let Some((new_timer, new_feerate, tx)) = self.generate_claim_tx(cur_height, &req, &*fee_estimator, &*logger) {
+ if let Some((new_timer, new_feerate, claim)) = self.generate_claim(cur_height, &req, &*fee_estimator, &*logger) {
req.set_timer(new_timer);
req.set_feerate(new_feerate);
- let txid = tx.txid();
+ let txid = match claim {
+ OnchainClaim::Tx(tx) => {
+ log_info!(logger, "Broadcasting onchain {}", log_tx!(tx));
+ broadcaster.broadcast_transaction(&tx);
+ tx.txid()
+ },
+ #[cfg(anchors)]
+ OnchainClaim::Event(claim_event) => {
+ log_info!(logger, "Yielding onchain event to spend inputs {:?}", req.outpoints());
+ let txid = match claim_event {
+ ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid(),
+ };
+ self.pending_claim_events.insert(txid, claim_event);
+ txid
+ },
+ };
for k in req.outpoints() {
log_info!(logger, "Registering claiming request for {}:{}", k.txid, k.vout);
self.claimable_outpoints.insert(k.clone(), (txid, conf_height));
}
self.pending_claim_requests.insert(txid, req);
- log_info!(logger, "Broadcasting onchain {}", log_tx!(tx));
- broadcaster.broadcast_transaction(&tx);
}
}
for outpoint in request.outpoints() {
log_debug!(logger, "Removing claim tracking for {} due to maturation of claim tx {}.", outpoint, claim_request);
self.claimable_outpoints.remove(&outpoint);
+ #[cfg(anchors)]
+ self.pending_claim_events.remove(&claim_request);
}
}
},
// Build, bump and rebroadcast tx accordingly
log_trace!(logger, "Bumping {} candidates", bump_candidates.len());
for (first_claim_txid, request) in bump_candidates.iter() {
- if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(cur_height, &request, &*fee_estimator, &*logger) {
- log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
- broadcaster.broadcast_transaction(&bump_tx);
+ if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(cur_height, &request, &*fee_estimator, &*logger) {
+ match bump_claim {
+ OnchainClaim::Tx(bump_tx) => {
+ log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
+ broadcaster.broadcast_transaction(&bump_tx);
+ },
+ #[cfg(anchors)]
+ OnchainClaim::Event(claim_event) => {
+ log_info!(logger, "Yielding RBF-bumped onchain event to spend inputs {:?}", request.outpoints());
+ self.pending_claim_events.insert(*first_claim_txid, claim_event);
+ },
+ }
if let Some(request) = self.pending_claim_requests.get_mut(first_claim_txid) {
request.set_timer(new_timer);
request.set_feerate(new_feerate);
self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
- for (_, request) in bump_candidates.iter_mut() {
- if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, &request, fee_estimator, &&*logger) {
+ for (_first_claim_txid_height, request) in bump_candidates.iter_mut() {
+ if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(height, &request, fee_estimator, &&*logger) {
request.set_timer(new_timer);
request.set_feerate(new_feerate);
- log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
- broadcaster.broadcast_transaction(&bump_tx);
+ match bump_claim {
+ OnchainClaim::Tx(bump_tx) => {
+ log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
+ broadcaster.broadcast_transaction(&bump_tx);
+ },
+ #[cfg(anchors)]
+ OnchainClaim::Event(claim_event) => {
+ log_info!(logger, "Yielding onchain event after reorg to spend inputs {:?}", request.outpoints());
+ self.pending_claim_events.insert(_first_claim_txid_height.0, claim_event);
+ },
+ }
}
}
for (ancestor_claim_txid, request) in bump_candidates.drain() {
use io;
use prelude::*;
use core::cmp;
+#[cfg(anchors)]
+use core::convert::TryInto;
use core::mem;
use core::ops::Deref;
use bitcoin::{PackedLockTime, Sequence, Witness};
///
/// CSV and pubkeys are used as part of a witnessScript redeeming a balance output, amount is used
/// as part of the signature hash and revocation secret to generate a satisfying witness.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub(crate) struct RevokedOutput {
per_commitment_point: PublicKey,
counterparty_delayed_payment_base_key: PublicKey,
///
/// CSV is used as part of a witnessScript redeeming a balance output, amount is used as part
/// of the signature hash and revocation secret to generate a satisfying witness.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub(crate) struct RevokedHTLCOutput {
per_commitment_point: PublicKey,
counterparty_delayed_payment_base_key: PublicKey,
/// witnessScript.
///
/// The preimage is used as part of the witness.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub(crate) struct CounterpartyOfferedHTLCOutput {
per_commitment_point: PublicKey,
counterparty_delayed_payment_base_key: PublicKey,
counterparty_htlc_base_key: PublicKey,
preimage: PaymentPreimage,
- htlc: HTLCOutputInCommitment
+ htlc: HTLCOutputInCommitment,
+ opt_anchors: Option<()>,
}
impl CounterpartyOfferedHTLCOutput {
- pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, preimage: PaymentPreimage, htlc: HTLCOutputInCommitment) -> Self {
+ pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, preimage: PaymentPreimage, htlc: HTLCOutputInCommitment, opt_anchors: bool) -> Self {
CounterpartyOfferedHTLCOutput {
per_commitment_point,
counterparty_delayed_payment_base_key,
counterparty_htlc_base_key,
preimage,
- htlc
+ htlc,
+ opt_anchors: if opt_anchors { Some(()) } else { None },
}
}
+
+ fn opt_anchors(&self) -> bool {
+ self.opt_anchors.is_some()
+ }
}
impl_writeable_tlv_based!(CounterpartyOfferedHTLCOutput, {
(0, per_commitment_point, required),
+ (1, opt_anchors, option),
(2, counterparty_delayed_payment_base_key, required),
(4, counterparty_htlc_base_key, required),
(6, preimage, required),
///
/// HTLCOutputInCommitment (hash, timelock, directon) and pubkeys are used to generate a suitable
/// witnessScript.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub(crate) struct CounterpartyReceivedHTLCOutput {
per_commitment_point: PublicKey,
counterparty_delayed_payment_base_key: PublicKey,
counterparty_htlc_base_key: PublicKey,
- htlc: HTLCOutputInCommitment
+ htlc: HTLCOutputInCommitment,
+ opt_anchors: Option<()>,
}
impl CounterpartyReceivedHTLCOutput {
- pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, htlc: HTLCOutputInCommitment) -> Self {
+ pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, htlc: HTLCOutputInCommitment, opt_anchors: bool) -> Self {
CounterpartyReceivedHTLCOutput {
per_commitment_point,
counterparty_delayed_payment_base_key,
counterparty_htlc_base_key,
- htlc
+ htlc,
+ opt_anchors: if opt_anchors { Some(()) } else { None },
}
}
+
+ fn opt_anchors(&self) -> bool {
+ self.opt_anchors.is_some()
+ }
}
impl_writeable_tlv_based!(CounterpartyReceivedHTLCOutput, {
(0, per_commitment_point, required),
+ (1, opt_anchors, option),
(2, counterparty_delayed_payment_base_key, required),
(4, counterparty_htlc_base_key, required),
(6, htlc, required),
///
/// Either offered or received, the amount is always used as part of the bip143 sighash.
/// Preimage is only included as part of the witness in former case.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub(crate) struct HolderHTLCOutput {
preimage: Option<PaymentPreimage>,
amount: u64,
/// A struct to describe the channel output on the funding transaction.
///
/// witnessScript is used as part of the witness redeeming the funding utxo.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub(crate) struct HolderFundingOutput {
funding_redeemscript: Script,
+ funding_amount: Option<u64>,
+ opt_anchors: Option<()>,
}
+
impl HolderFundingOutput {
- pub(crate) fn build(funding_redeemscript: Script) -> Self {
+ pub(crate) fn build(funding_redeemscript: Script, funding_amount: u64, opt_anchors: bool) -> Self {
HolderFundingOutput {
funding_redeemscript,
+ funding_amount: Some(funding_amount),
+ opt_anchors: if opt_anchors { Some(()) } else { None },
}
}
+
+ fn opt_anchors(&self) -> bool {
+ self.opt_anchors.is_some()
+ }
}
impl_writeable_tlv_based!(HolderFundingOutput, {
(0, funding_redeemscript, required),
+ (1, opt_anchors, option),
+ (3, funding_amount, option),
});
/// A wrapper encapsulating all in-protocol differing outputs types.
///
/// The generic API offers access to an outputs common attributes or allow transformation such as
/// finalizing an input claiming the output.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub(crate) enum PackageSolvingData {
RevokedOutput(RevokedOutput),
RevokedHTLCOutput(RevokedHTLCOutput),
impl PackageSolvingData {
fn amount(&self) -> u64 {
let amt = match self {
- PackageSolvingData::RevokedOutput(ref outp) => { outp.amount },
- PackageSolvingData::RevokedHTLCOutput(ref outp) => { outp.amount },
- PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => { outp.htlc.amount_msat / 1000 },
- PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => { outp.htlc.amount_msat / 1000 },
+ PackageSolvingData::RevokedOutput(ref outp) => outp.amount,
+ PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.amount,
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000,
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000,
// Note: Currently, amounts of holder outputs spending witnesses aren't used
// as we can't malleate spending package to increase their feerate. This
// should change with the remaining anchor output patchset.
- PackageSolvingData::HolderHTLCOutput(..) => { unreachable!() },
- PackageSolvingData::HolderFundingOutput(..) => { unreachable!() },
+ PackageSolvingData::HolderHTLCOutput(..) => unreachable!(),
+ PackageSolvingData::HolderFundingOutput(ref outp) => {
+ debug_assert!(outp.opt_anchors());
+ outp.funding_amount.unwrap()
+ }
};
amt
}
- fn weight(&self, opt_anchors: bool) -> usize {
+ fn weight(&self) -> usize {
let weight = match self {
PackageSolvingData::RevokedOutput(ref outp) => { outp.weight as usize },
PackageSolvingData::RevokedHTLCOutput(ref outp) => { outp.weight as usize },
- PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { weight_offered_htlc(opt_anchors) as usize },
- PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { weight_received_htlc(opt_anchors) as usize },
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => { weight_offered_htlc(outp.opt_anchors()) as usize },
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => { weight_received_htlc(outp.opt_anchors()) as usize },
// Note: Currently, weights of holder outputs spending witnesses aren't used
// as we can't malleate spending package to increase their feerate. This
// should change with the remaining anchor output patchset.
/// A malleable package might be aggregated with other packages to save on fees.
/// A untractable package has been counter-signed and aggregable will break cached counterparty
/// signatures.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub(crate) enum PackageMalleability {
Malleable,
Untractable,
///
/// As packages are time-sensitive, we fee-bump and rebroadcast them at scheduled intervals.
/// Failing to confirm a package translate as a loss of funds for the user.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub struct PackageTemplate {
// List of onchain outputs and solving data to generate satisfying witnesses.
inputs: Vec<(BitcoinOutPoint, PackageSolvingData)>,
pub(crate) fn outpoints(&self) -> Vec<&BitcoinOutPoint> {
self.inputs.iter().map(|(o, _)| o).collect()
}
+ pub(crate) fn inputs(&self) -> impl ExactSizeIterator<Item = &PackageSolvingData> {
+ self.inputs.iter().map(|(_, i)| i)
+ }
pub(crate) fn split_package(&mut self, split_outp: &BitcoinOutPoint) -> Option<PackageTemplate> {
match self.malleability {
PackageMalleability::Malleable => {
}
/// Gets the amount of all outptus being spent by this package, only valid for malleable
/// packages.
- fn package_amount(&self) -> u64 {
+ pub(crate) fn package_amount(&self) -> u64 {
let mut amounts = 0;
for (_, outp) in self.inputs.iter() {
amounts += outp.amount();
self.inputs.iter().map(|(_, outp)| outp.absolute_tx_timelock(self.height_original))
.max().expect("There must always be at least one output to spend in a PackageTemplate")
}
- pub(crate) fn package_weight(&self, destination_script: &Script, opt_anchors: bool) -> usize {
+ pub(crate) fn package_weight(&self, destination_script: &Script) -> usize {
let mut inputs_weight = 0;
let mut witnesses_weight = 2; // count segwit flags
for (_, outp) in self.inputs.iter() {
// previous_out_point: 36 bytes ; var_int: 1 byte ; sequence: 4 bytes
inputs_weight += 41 * WITNESS_SCALE_FACTOR;
- witnesses_weight += outp.weight(opt_anchors);
+ witnesses_weight += outp.weight();
}
// version: 4 bytes ; count_tx_in: 1 byte ; count_tx_out: 1 byte ; lock_time: 4 bytes
let transaction_weight = 10 * WITNESS_SCALE_FACTOR;
let output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR;
inputs_weight + witnesses_weight + transaction_weight + output_weight
}
- pub(crate) fn finalize_package<L: Deref, Signer: Sign>(&self, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64, destination_script: Script, logger: &L) -> Option<Transaction>
- where L::Target: Logger,
- {
- match self.malleability {
- PackageMalleability::Malleable => {
- let mut bumped_tx = Transaction {
- version: 2,
- lock_time: PackedLockTime::ZERO,
- input: vec![],
- output: vec![TxOut {
- script_pubkey: destination_script,
- value,
- }],
- };
- for (outpoint, _) in self.inputs.iter() {
- bumped_tx.input.push(TxIn {
- previous_output: *outpoint,
- script_sig: Script::new(),
- sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
- witness: Witness::new(),
- });
- }
- for (i, (outpoint, out)) in self.inputs.iter().enumerate() {
- log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout);
- if !out.finalize_input(&mut bumped_tx, i, onchain_handler) { return None; }
- }
- log_debug!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid());
- return Some(bumped_tx);
- },
- PackageMalleability::Untractable => {
- debug_assert_eq!(value, 0, "value is ignored for non-malleable packages, should be zero to ensure callsites are correct");
- if let Some((outpoint, outp)) = self.inputs.first() {
- if let Some(final_tx) = outp.get_finalized_tx(outpoint, onchain_handler) {
- log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout);
- log_debug!(logger, "Finalized transaction {} ready to broadcast", final_tx.txid());
- return Some(final_tx);
- }
- return None;
- } else { panic!("API Error: Package must not be inputs empty"); }
- },
+ pub(crate) fn finalize_malleable_package<L: Deref, Signer: Sign>(
+ &self, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64, destination_script: Script, logger: &L
+ ) -> Option<Transaction> where L::Target: Logger {
+ debug_assert!(self.is_malleable());
+ let mut bumped_tx = Transaction {
+ version: 2,
+ lock_time: PackedLockTime::ZERO,
+ input: vec![],
+ output: vec![TxOut {
+ script_pubkey: destination_script,
+ value,
+ }],
+ };
+ for (outpoint, _) in self.inputs.iter() {
+ bumped_tx.input.push(TxIn {
+ previous_output: *outpoint,
+ script_sig: Script::new(),
+ sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
+ witness: Witness::new(),
+ });
+ }
+ for (i, (outpoint, out)) in self.inputs.iter().enumerate() {
+ log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout);
+ if !out.finalize_input(&mut bumped_tx, i, onchain_handler) { return None; }
}
+ log_debug!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid());
+ Some(bumped_tx)
+ }
+ pub(crate) fn finalize_untractable_package<L: Deref, Signer: Sign>(
+ &self, onchain_handler: &mut OnchainTxHandler<Signer>, logger: &L,
+ ) -> Option<Transaction> where L::Target: Logger {
+ debug_assert!(!self.is_malleable());
+ if let Some((outpoint, outp)) = self.inputs.first() {
+ if let Some(final_tx) = outp.get_finalized_tx(outpoint, onchain_handler) {
+ log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout);
+ log_debug!(logger, "Finalized transaction {} ready to broadcast", final_tx.txid());
+ return Some(final_tx);
+ }
+ return None;
+ } else { panic!("API Error: Package must not be inputs empty"); }
}
/// In LN, output claimed are time-sensitive, which means we have to spend them before reaching some timelock expiration. At in-channel
/// output detection, we generate a first version of a claim tx and associate to it a height timer. A height timer is an absolute block
}
None
}
+
+ #[cfg(anchors)]
+ /// Computes a feerate based on the given confirmation target. If a previous feerate was used,
+ /// and the new feerate is below it, we'll use a 25% increase of the previous feerate instead of
+ /// the new one.
+ pub(crate) fn compute_package_feerate<F: Deref>(
+ &self, fee_estimator: &LowerBoundedFeeEstimator<F>, conf_target: ConfirmationTarget,
+ ) -> u32 where F::Target: FeeEstimator {
+ let feerate_estimate = fee_estimator.bounded_sat_per_1000_weight(conf_target);
+ if self.feerate_previous != 0 {
+ // If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
+ if feerate_estimate as u64 > self.feerate_previous {
+ feerate_estimate
+ } else {
+ // ...else just increase the previous feerate by 25% (because that's a nice number)
+ (self.feerate_previous + (self.feerate_previous / 4)).try_into().unwrap_or(u32::max_value())
+ }
+ } else {
+ feerate_estimate
+ }
+ }
+
+ /// Determines whether a package contains an input which must have additional external inputs
+ /// attached to help the spending transaction reach confirmation.
+ pub(crate) fn requires_external_funding(&self) -> bool {
+ self.inputs.iter().find(|input| match input.1 {
+ PackageSolvingData::HolderFundingOutput(ref outp) => outp.opt_anchors(),
+ _ => false,
+ }).is_some()
+ }
+
pub (crate) fn build_package(txid: Txid, vout: u32, input_solving_data: PackageSolvingData, soonest_conf_deadline: u32, aggregable: bool, height_original: u32) -> Self {
let malleability = match input_solving_data {
- PackageSolvingData::RevokedOutput(..) => { PackageMalleability::Malleable },
- PackageSolvingData::RevokedHTLCOutput(..) => { PackageMalleability::Malleable },
- PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { PackageMalleability::Malleable },
- PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { PackageMalleability::Malleable },
- PackageSolvingData::HolderHTLCOutput(..) => { PackageMalleability::Untractable },
- PackageSolvingData::HolderFundingOutput(..) => { PackageMalleability::Untractable },
+ PackageSolvingData::RevokedOutput(..) => PackageMalleability::Malleable,
+ PackageSolvingData::RevokedHTLCOutput(..) => PackageMalleability::Malleable,
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => PackageMalleability::Malleable,
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => PackageMalleability::Malleable,
+ PackageSolvingData::HolderHTLCOutput(..) => PackageMalleability::Untractable,
+ PackageSolvingData::HolderFundingOutput(..) => PackageMalleability::Untractable,
};
let mut inputs = Vec::with_capacity(1);
inputs.push((BitcoinOutPoint { txid, vout }, input_solving_data));
}
macro_rules! dumb_counterparty_output {
- ($secp_ctx: expr, $amt: expr) => {
+ ($secp_ctx: expr, $amt: expr, $opt_anchors: expr) => {
{
let dumb_scalar = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap();
let dumb_point = PublicKey::from_secret_key(&$secp_ctx, &dumb_scalar);
let hash = PaymentHash([1; 32]);
let htlc = HTLCOutputInCommitment { offered: true, amount_msat: $amt, cltv_expiry: 0, payment_hash: hash, transaction_output_index: None };
- PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(dumb_point, dumb_point, dumb_point, htlc))
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(dumb_point, dumb_point, dumb_point, htlc, $opt_anchors))
}
}
}
macro_rules! dumb_counterparty_offered_output {
- ($secp_ctx: expr, $amt: expr) => {
+ ($secp_ctx: expr, $amt: expr, $opt_anchors: expr) => {
{
let dumb_scalar = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap();
let dumb_point = PublicKey::from_secret_key(&$secp_ctx, &dumb_scalar);
let hash = PaymentHash([1; 32]);
let preimage = PaymentPreimage([2;32]);
let htlc = HTLCOutputInCommitment { offered: false, amount_msat: $amt, cltv_expiry: 1000, payment_hash: hash, transaction_output_index: None };
- PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(dumb_point, dumb_point, dumb_point, preimage, htlc))
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(dumb_point, dumb_point, dumb_point, preimage, htlc, $opt_anchors))
}
}
}
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
let revk_outp = dumb_revk_output!(secp_ctx);
- let counterparty_outp = dumb_counterparty_output!(secp_ctx, 0);
+ let counterparty_outp = dumb_counterparty_output!(secp_ctx, 0, false);
let mut revoked_package = PackageTemplate::build_package(txid, 0, revk_outp, 1000, true, 100);
let counterparty_package = PackageTemplate::build_package(txid, 1, counterparty_outp, 1000, true, 100);
fn test_package_amounts() {
let txid = Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap();
let secp_ctx = Secp256k1::new();
- let counterparty_outp = dumb_counterparty_output!(secp_ctx, 1_000_000);
+ let counterparty_outp = dumb_counterparty_output!(secp_ctx, 1_000_000, false);
let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, true, 100);
assert_eq!(package.package_amount(), 1000);
{
let revk_outp = dumb_revk_output!(secp_ctx);
let package = PackageTemplate::build_package(txid, 0, revk_outp, 0, true, 100);
- for &opt_anchors in [false, true].iter() {
- assert_eq!(package.package_weight(&Script::new(), opt_anchors), weight_sans_output + WEIGHT_REVOKED_OUTPUT as usize);
- }
+ assert_eq!(package.package_weight(&Script::new()), weight_sans_output + WEIGHT_REVOKED_OUTPUT as usize);
}
{
- let counterparty_outp = dumb_counterparty_output!(secp_ctx, 1_000_000);
- let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, true, 100);
for &opt_anchors in [false, true].iter() {
- assert_eq!(package.package_weight(&Script::new(), opt_anchors), weight_sans_output + weight_received_htlc(opt_anchors) as usize);
+ let counterparty_outp = dumb_counterparty_output!(secp_ctx, 1_000_000, opt_anchors);
+ let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, true, 100);
+ assert_eq!(package.package_weight(&Script::new()), weight_sans_output + weight_received_htlc(opt_anchors) as usize);
}
}
{
- let counterparty_outp = dumb_counterparty_offered_output!(secp_ctx, 1_000_000);
- let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, true, 100);
for &opt_anchors in [false, true].iter() {
- assert_eq!(package.package_weight(&Script::new(), opt_anchors), weight_sans_output + weight_offered_htlc(opt_anchors) as usize);
+ let counterparty_outp = dumb_counterparty_offered_output!(secp_ctx, 1_000_000, opt_anchors);
+ let package = PackageTemplate::build_package(txid, 0, counterparty_outp, 1000, true, 100);
+ assert_eq!(package.package_weight(&Script::new()), weight_sans_output + weight_offered_htlc(opt_anchors) as usize);
}
}
}
if opt_anchors { HTLC_TIMEOUT_ANCHOR_TX_WEIGHT } else { HTLC_TIMEOUT_TX_WEIGHT }
}
-#[derive(PartialEq)]
+#[derive(PartialEq, Eq)]
pub(crate) enum HTLCClaim {
OfferedTimeout,
OfferedPreimage,
old_secrets: [([u8; 32], u64); 49],
}
+impl Eq for CounterpartyCommitmentSecrets {}
impl PartialEq for CounterpartyCommitmentSecrets {
fn eq(&self, other: &Self) -> bool {
for (&(ref secret, ref idx), &(ref o_secret, ref o_idx)) in self.old_secrets.iter().zip(other.old_secrets.iter()) {
/// channel basepoints via the new function, or they were obtained via
/// CommitmentTransaction.trust().keys() because we trusted the source of the
/// pre-calculated keys.
-#[derive(PartialEq, Clone)]
+#[derive(PartialEq, Eq, Clone)]
pub struct TxCreationKeys {
/// The broadcaster's per-commitment public key which was used to derive the other keys.
pub per_commitment_point: PublicKey,
});
/// One counterparty's public keys which do not change over the life of a channel.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub struct ChannelPublicKeys {
/// The public key which is used to sign all commitment transactions, as it appears in the
/// on-chain channel lock-in 2-of-2 multisig output.
res
}
-#[derive(Clone, PartialEq)]
/// Information about an HTLC as it appears in a commitment transaction
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HTLCOutputInCommitment {
/// Whether the HTLC was "offered" (ie outbound in relation to this commitment transaction).
/// Note that this is not the same as whether it is ountbound *from us*. To determine that you
.into_script()
}
+#[cfg(anchors)]
+/// Locates the output with an anchor script paying to `funding_pubkey` within `commitment_tx`.
+pub(crate) fn get_anchor_output<'a>(commitment_tx: &'a Transaction, funding_pubkey: &PublicKey) -> Option<(u32, &'a TxOut)> {
+ let anchor_script = chan_utils::get_anchor_redeemscript(funding_pubkey).to_v0_p2wsh();
+ commitment_tx.output.iter().enumerate()
+ .find(|(_, txout)| txout.script_pubkey == anchor_script)
+ .map(|(idx, txout)| (idx as u32, txout))
+}
+
+/// Returns the witness required to satisfy and spend an anchor input.
+pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signature) -> Witness {
+ let anchor_redeem_script = chan_utils::get_anchor_redeemscript(funding_key);
+ let mut funding_sig = funding_sig.serialize_der().to_vec();
+ funding_sig.push(EcdsaSighashType::All as u8);
+ Witness::from_vec(vec![funding_sig, anchor_redeem_script.to_bytes()])
+}
+
/// Per-channel data used to build transactions in conjunction with the per-commitment data (CommitmentTransaction).
/// The fields are organized by holder/counterparty.
///
fn deref(&self) -> &Self::Target { &self.inner }
}
+impl Eq for HolderCommitmentTransaction {}
impl PartialEq for HolderCommitmentTransaction {
// We dont care whether we are signed in equality comparison
fn eq(&self, o: &Self) -> bool {
///
/// This class can be used inside a signer implementation to generate a signature given the relevant
/// secret key.
-#[derive(Clone, Hash, PartialEq)]
+#[derive(Clone, Hash, PartialEq, Eq)]
pub struct ClosingTransaction {
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
built: BuiltCommitmentTransaction,
}
+impl Eq for CommitmentTransaction {}
impl PartialEq for CommitmentTransaction {
fn eq(&self, o: &Self) -> bool {
let eq = self.commitment_number == o.commitment_number &&
log_debug!(self.logger, "Finishing force-closure of channel with {} HTLCs to fail", failed_htlcs.len());
for htlc_source in failed_htlcs.drain(..) {
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
- let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id: channel_id };
+ let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
self.fail_htlc_backwards_internal(source, &payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
}
if let Some((funding_txo, monitor_update)) = monitor_update_option {
}
} else {
events::Event::ProbeFailed {
- payment_id: payment_id,
+ payment_id,
payment_hash: payment_hash.clone(),
path: path.clone(),
short_channel_id,
if self.payment_is_probe(payment_hash, &payment_id) {
events::Event::ProbeFailed {
- payment_id: payment_id,
+ payment_id,
payment_hash: payment_hash.clone(),
path: path.clone(),
short_channel_id: Some(scid),
}
Ok(HTLCSource::OutboundRoute {
session_priv: session_priv.0.unwrap(),
- first_hop_htlc_msat: first_hop_htlc_msat,
+ first_hop_htlc_msat,
path: path.unwrap(),
payment_id: payment_id.unwrap(),
payment_secret,
assert!(updates.update_fail_htlcs.is_empty());
assert!(updates.update_fail_malformed_htlcs.is_empty());
assert!(updates.update_fee.is_none());
- SendEvent { node_id: node_id, msgs: updates.update_add_htlcs, commitment_msg: updates.commitment_signed }
+ SendEvent { node_id, msgs: updates.update_add_htlcs, commitment_msg: updates.commitment_signed }
}
pub fn from_event(event: MessageSendEvent) -> SendEvent {
pub(crate) const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
/// An error in decoding a message or struct.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DecodeError {
/// A version byte specified something we don't know how to handle.
/// Includes unknown realm byte in an OnionHopData packet
}
/// An init message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Init {
/// The relevant features which the sender supports
pub features: InitFeatures,
}
/// An error message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ErrorMessage {
/// The channel ID involved in the error.
///
}
/// A warning message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WarningMessage {
/// The channel ID involved in the warning.
///
}
/// A ping message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ping {
/// The desired response length
pub ponglen: u16,
}
/// A pong message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Pong {
/// The pong packet size.
/// This field is not sent on the wire. byteslen zeros are sent.
}
/// An open_channel message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpenChannel {
/// The genesis hash of the blockchain where the channel is to be opened
pub chain_hash: BlockHash,
}
/// An accept_channel message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AcceptChannel {
/// A temporary channel ID, until the funding outpoint is announced
pub temporary_channel_id: [u8; 32],
}
/// A funding_created message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FundingCreated {
/// A temporary channel ID, until the funding is established
pub temporary_channel_id: [u8; 32],
}
/// A funding_signed message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FundingSigned {
/// The channel ID
pub channel_id: [u8; 32],
}
/// A channel_ready message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelReady {
/// The channel ID
pub channel_id: [u8; 32],
}
/// A shutdown message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Shutdown {
/// The channel ID
pub channel_id: [u8; 32],
/// The minimum and maximum fees which the sender is willing to place on the closing transaction.
/// This is provided in [`ClosingSigned`] by both sides to indicate the fee range they are willing
/// to use.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClosingSignedFeeRange {
/// The minimum absolute fee, in satoshis, which the sender is willing to place on the closing
/// transaction.
}
/// A closing_signed message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClosingSigned {
/// The channel ID
pub channel_id: [u8; 32],
}
/// An update_add_htlc message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateAddHTLC {
/// The channel ID
pub channel_id: [u8; 32],
}
/// An onion message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OnionMessage {
/// Used in decrypting the onion packet's payload.
pub blinding_point: PublicKey,
}
/// An update_fulfill_htlc message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateFulfillHTLC {
/// The channel ID
pub channel_id: [u8; 32],
}
/// An update_fail_htlc message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateFailHTLC {
/// The channel ID
pub channel_id: [u8; 32],
}
/// An update_fail_malformed_htlc message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateFailMalformedHTLC {
/// The channel ID
pub channel_id: [u8; 32],
}
/// A commitment_signed message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommitmentSigned {
/// The channel ID
pub channel_id: [u8; 32],
}
/// A revoke_and_ack message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RevokeAndACK {
/// The channel ID
pub channel_id: [u8; 32],
}
/// An update_fee message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateFee {
/// The channel ID
pub channel_id: [u8; 32],
pub feerate_per_kw: u32,
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
/// Proof that the sender knows the per-commitment secret of the previous commitment transaction.
/// This is used to convince the recipient that the channel is at a certain commitment
/// number even if they lost that data due to a local failure. Of course, the peer may lie
}
/// A channel_reestablish message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelReestablish {
/// The channel ID
pub channel_id: [u8; 32],
}
/// An announcement_signatures message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AnnouncementSignatures {
/// The channel ID
pub channel_id: [u8; 32],
}
/// An address which can be used to connect to a remote peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NetAddress {
/// An IPv4 address/port on which the peer is listening.
IPv4 {
/// The unsigned part of a node_announcement
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnsignedNodeAnnouncement {
/// The advertised features
pub features: NodeFeatures,
pub(crate) excess_address_data: Vec<u8>,
pub(crate) excess_data: Vec<u8>,
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
/// A node_announcement message to be sent or received from a peer
pub struct NodeAnnouncement {
/// The signature by the node key
}
/// The unsigned part of a channel_announcement
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnsignedChannelAnnouncement {
/// The advertised channel features
pub features: ChannelFeatures,
pub(crate) excess_data: Vec<u8>,
}
/// A channel_announcement message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelAnnouncement {
/// Authentication of the announcement by the first public node
pub node_signature_1: Signature,
}
/// The unsigned part of a channel_update
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnsignedChannelUpdate {
/// The genesis hash of the blockchain where the channel is to be opened
pub chain_hash: BlockHash,
pub excess_data: Vec<u8>,
}
/// A channel_update message to be sent or received from a peer
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelUpdate {
/// A signature of the channel update
pub signature: Signature,
/// UTXOs in a range of blocks. The recipient of a query makes a best
/// effort to reply to the query using one or more reply_channel_range
/// messages.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryChannelRange {
/// The genesis hash of the blockchain being queried
pub chain_hash: BlockHash,
/// not be a perfect view of the network. The short_channel_ids in the
/// reply are encoded. We only support encoding_type=0 uncompressed
/// serialization and do not support encoding_type=1 zlib serialization.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReplyChannelRange {
/// The genesis hash of the blockchain being queried
pub chain_hash: BlockHash,
/// reply_short_channel_ids_end message. The short_channel_ids sent in
/// this query are encoded. We only support encoding_type=0 uncompressed
/// serialization and do not support encoding_type=1 zlib serialization.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryShortChannelIds {
/// The genesis hash of the blockchain being queried
pub chain_hash: BlockHash,
/// query_short_channel_ids message. The query recipient makes a best
/// effort to respond based on their local network view which may not be
/// a perfect view of the network.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReplyShortChannelIdsEnd {
/// The genesis hash of the blockchain that was queried
pub chain_hash: BlockHash,
/// A gossip_timestamp_filter message is used by a node to request
/// gossip relay for messages in the requested time range when the
/// gossip_queries feature has been negotiated.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GossipTimestampFilter {
/// The genesis hash of the blockchain for channel and node information
pub chain_hash: BlockHash,
/// Struct used to return values from revoke_and_ack messages, containing a bunch of commitment
/// transaction updates if they were pending.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommitmentUpdate {
/// update_add_htlc messages which should be sent
pub update_add_htlcs: Vec<UpdateAddHTLC>,
/// OptionalFeild simply gets Present if there are enough bytes to read into it), we have a
/// separate enum type for them.
/// (C-not exported) due to a free generic in T
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OptionalField<T> {
/// Optional field is included in message
Present(T),
}
}
+impl Eq for OnionPacket { }
impl PartialEq for OnionPacket {
fn eq(&self, other: &OnionPacket) -> bool {
for (i, j) in self.hop_data.iter().zip(other.hop_data.iter()) {
}
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct OnionErrorPacket {
// This really should be a constant size slice, but the spec lets these things be up to 128KB?
// (TODO) We limit it in decode to much lower...
/// A script pubkey for shutting down a channel as defined by [BOLT #2].
///
/// [BOLT #2]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub struct ShutdownScript(ShutdownScriptImpl);
/// An error occurring when converting from [`Script`] to [`ShutdownScript`].
pub script: Script
}
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
enum ShutdownScriptImpl {
/// [`PublicKey`] used to form a P2WPKH script pubkey. Used to support backward-compatible
/// serialization.
/// Errors that may occur when [sending an onion message].
///
/// [sending an onion message]: OnionMessenger::send_onion_message
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Eq)]
pub enum SendError {
/// Errored computing onion message packet keys.
Secp256k1(secp256k1::Error),
pub(super) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300;
pub(super) const BIG_PACKET_HOP_DATA_LEN: usize = 32768;
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Packet {
pub(super) version: u8,
pub(super) public_key: PublicKey,
/// return packet by a node along the route. See [BOLT #4] for details.
///
/// [BOLT #4]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NetworkUpdate {
/// An error indicating a `channel_update` messages should be applied via
/// [`NetworkGraph::update_channel`].
}
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
/// Details about one direction of a channel as received within a [`ChannelUpdate`].
pub struct ChannelUpdateInfo {
/// When the last update to the channel direction was issued.
}
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
/// Details about a channel (both directions).
/// Received within a channel announcement.
pub struct ChannelInfo {
(2, proportional_millionths, required)
});
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
/// Information received in the latest node_announcement from this node.
pub struct NodeAnnouncementInfo {
/// Protocol features the node announced support for
///
/// 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)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NodeAlias(pub [u8; 32]);
impl fmt::Display for NodeAlias {
}
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
/// Details about a node in the network, known from the network announcement.
pub struct NodeInfo {
/// All valid channels a node has announced
}
}
+impl<L: Deref> Eq for NetworkGraph<L> where L::Target: Logger {}
impl<L: Deref> PartialEq for NetworkGraph<L> where L::Target: Logger {
fn eq(&self, other: &Self) -> bool {
self.genesis_hash == other.genesis_hash &&
lowest_fee_to_peer_through_node: total_fee_msat,
lowest_fee_to_node: $next_hops_fee_msat as u64 + hop_use_fee_msat,
total_cltv_delta: hop_total_cltv_delta,
- value_contribution_msat: value_contribution_msat,
+ value_contribution_msat,
path_htlc_minimum_msat,
path_penalty_msat,
path_length_to_node,
/// [`Score`] implementation using channel success probability distributions.
///
-/// Based on *Optimally Reliable & Cheap Payment Flows on the Lightning Network* by Rene Pickhardt
-/// and Stefan Richter [[1]]. Given the uncertainty of channel liquidity balances, probability
-/// distributions are defined based on knowledge learned from successful and unsuccessful attempts.
-/// Then the negative `log10` of the success probability is used to determine the cost of routing a
-/// specific HTLC amount through a channel.
+/// Channels are tracked with upper and lower liquidity bounds - when an HTLC fails at a channel,
+/// we learn that the upper-bound on the available liquidity is lower than the amount of the HTLC.
+/// When a payment is forwarded through a channel (but fails later in the route), we learn the
+/// lower-bound on the channel's available liquidity must be at least the value of the HTLC.
///
-/// Knowledge about channel liquidity balances takes the form of upper and lower bounds on the
-/// possible liquidity. Certainty of the bounds is decreased over time using a decay function. See
-/// [`ProbabilisticScoringParameters`] for details.
+/// These bounds are then used to determine a success probability using the formula from
+/// *Optimally Reliable & Cheap Payment Flows on the Lightning Network* by Rene Pickhardt
+/// and Stefan Richter [[1]] (i.e. `(upper_bound - payment_amount) / (upper_bound - lower_bound)`).
///
-/// Since the scorer aims to learn the current channel liquidity balances, it works best for nodes
-/// with high payment volume or that actively probe the [`NetworkGraph`]. Nodes with low payment
-/// volume are more likely to experience failed payment paths, which would need to be retried.
+/// This probability is combined with the [`liquidity_penalty_multiplier_msat`] and
+/// [`liquidity_penalty_amount_multiplier_msat`] parameters to calculate a concrete penalty in
+/// milli-satoshis. The penalties, when added across all hops, have the property of being linear in
+/// terms of the entire path's success probability. This allows the router to directly compare
+/// penalties for different paths. See the documentation of those parameters for the exact formulas.
+///
+/// The liquidity bounds are decayed by halving them every [`liquidity_offset_half_life`].
+///
+/// Further, we track the history of our upper and lower liquidity bounds for each channel,
+/// allowing us to assign a second penalty (using [`historical_liquidity_penalty_multiplier_msat`]
+/// and [`historical_liquidity_penalty_amount_multiplier_msat`]) based on the same probability
+/// formula, but using the history of a channel rather than our latest estimates for the liquidity
+/// bounds.
///
/// # Note
///
/// behavior.
///
/// [1]: https://arxiv.org/abs/2107.05322
+/// [`liquidity_penalty_multiplier_msat`]: ProbabilisticScoringParameters::liquidity_penalty_multiplier_msat
+/// [`liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringParameters::liquidity_penalty_amount_multiplier_msat
+/// [`liquidity_offset_half_life`]: ProbabilisticScoringParameters::liquidity_offset_half_life
+/// [`historical_liquidity_penalty_multiplier_msat`]: ProbabilisticScoringParameters::historical_liquidity_penalty_multiplier_msat
+/// [`historical_liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringParameters::historical_liquidity_penalty_amount_multiplier_msat
pub type ProbabilisticScorer<G, L> = ProbabilisticScorerUsingTime::<G, L, ConfiguredTime>;
/// Probabilistic [`Score`] implementation.
/// uncertainty bounds of the channel liquidity balance. Amounts above the upper bound will
/// result in a `u64::max_value` penalty, however.
///
+ /// `-log10(success_probability) * liquidity_penalty_multiplier_msat`
+ ///
/// Default value: 30,000 msat
///
/// [`liquidity_offset_half_life`]: Self::liquidity_offset_half_life
pub liquidity_penalty_multiplier_msat: u64,
- /// The time required to elapse before any knowledge learned about channel liquidity balances is
- /// cut in half.
+ /// Whenever this amount of time elapses since the last update to a channel's liquidity bounds,
+ /// the distance from the bounds to "zero" is cut in half. In other words, the lower-bound on
+ /// the available liquidity is halved and the upper-bound moves half-way to the channel's total
+ /// capacity.
+ ///
+ /// Because halving the liquidity bounds grows the uncertainty on the channel's liquidity,
+ /// the penalty for an amount within the new bounds may change. See the [`ProbabilisticScorer`]
+ /// struct documentation for more info on the way the liquidity bounds are used.
///
- /// The bounds are defined in terms of offsets and are initially zero. Increasing the offsets
- /// gives tighter bounds on the channel liquidity balance. Thus, halving the offsets decreases
- /// the certainty of the channel liquidity balance.
+ /// For example, if the channel's capacity is 1 million sats, and the current upper and lower
+ /// liquidity bounds are 200,000 sats and 600,000 sats, after this amount of time the upper
+ /// and lower liquidity bounds will be decayed to 100,000 and 800,000 sats.
///
- /// Default value: 1 hour
+ /// Default value: 6 hours
///
/// # Note
///
base_penalty_msat: 0,
base_penalty_amount_multiplier_msat: 0,
liquidity_penalty_multiplier_msat: 0,
- liquidity_offset_half_life: Duration::from_secs(3600),
+ liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
liquidity_penalty_amount_multiplier_msat: 0,
historical_liquidity_penalty_multiplier_msat: 0,
historical_liquidity_penalty_amount_multiplier_msat: 0,
base_penalty_msat: 500,
base_penalty_amount_multiplier_msat: 8192,
liquidity_penalty_multiplier_msat: 30_000,
- liquidity_offset_half_life: Duration::from_secs(3600),
+ liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
liquidity_penalty_amount_multiplier_msat: 192,
historical_liquidity_penalty_multiplier_msat: 10_000,
historical_liquidity_penalty_amount_multiplier_msat: 64,
use util::ser::{self, FixedLengthReader, LengthReadableArgs, Writeable};
// Used for for testing various lengths of serialization.
- #[derive(Debug, PartialEq)]
+ #[derive(Debug, PartialEq, Eq)]
struct TestWriteable {
field1: Vec<u8>,
field2: Vec<u8>,
/// Options which apply on a per-channel basis and may change at runtime or based on negotiation
/// with our counterparty.
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ChannelConfig {
/// Amount (in millionths of a satoshi) charged per satoshi for payments forwarded outbound
/// over the channel.
// You may not use this file except in accordance with one or both of these
// licenses.
+use ln::channel::{ANCHOR_OUTPUT_VALUE_SATOSHI, MIN_CHAN_DUST_LIMIT_SATOSHIS};
use ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction, ClosingTransaction};
use ln::{chan_utils, msgs, PaymentPreimage};
use chain::keysinterface::{Sign, InMemorySigner, BaseSign};
Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap())
}
+ fn sign_holder_anchor_input(
+ &self, anchor_tx: &mut Transaction, input: usize, secp_ctx: &Secp256k1<secp256k1::All>,
+ ) -> Result<Signature, ()> {
+ debug_assert!(MIN_CHAN_DUST_LIMIT_SATOSHIS > ANCHOR_OUTPUT_VALUE_SATOSHI);
+ // As long as our minimum dust limit is enforced and is greater than our anchor output
+ // value, an anchor output can only have an index within [0, 1].
+ assert!(anchor_tx.input[input].previous_output.vout == 0 || anchor_tx.input[input].previous_output.vout == 1);
+ self.inner.sign_holder_anchor_input(anchor_tx, input, secp_ctx)
+ }
+
fn sign_channel_announcement(&self, msg: &msgs::UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
-> Result<(Signature, Signature), ()> {
self.inner.sign_channel_announcement(msg, secp_ctx)
/// Indicates an error on the client's part (usually some variant of attempting to use too-low or
/// too-high values)
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub enum APIError {
/// Indicates the API was wholly misused (see err for more). Cases where these can be returned
/// are documented, but generally indicates some precondition of a function was violated.
//! few other things.
use chain::keysinterface::SpendableOutputDescriptor;
+use ln::chan_utils::HTLCOutputInCommitment;
use ln::channelmanager::PaymentId;
use ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
use ln::features::ChannelTypeFeatures;
use util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, VecReadWrapper, VecWriteWrapper};
use routing::router::{RouteHop, RouteParameters};
-use bitcoin::{PackedLockTime, Transaction};
+use bitcoin::{PackedLockTime, Transaction, OutPoint};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
(2, SpontaneousPayment)
);
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
/// The reason the channel was closed. See individual variants more details.
pub enum ClosureReason {
/// Closure generated from receiving a peer error message.
);
/// Intended destination of a failed HTLC as indicated in [`Event::HTLCHandlingFailed`].
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HTLCDestination {
/// We tried forwarding to a channel but failed to do so. An example of such an instance is when
/// there is insufficient capacity in our outbound channel.
}
);
+/// A descriptor used to sign for a commitment transaction's anchor output.
+#[derive(Clone, Debug)]
+pub struct AnchorDescriptor {
+ /// A unique identifier used along with `channel_value_satoshis` to re-derive the
+ /// [`InMemorySigner`] required to sign `input`.
+ ///
+ /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ pub channel_keys_id: [u8; 32],
+ /// The value in satoshis of the channel we're attempting to spend the anchor output of. This is
+ /// used along with `channel_keys_id` to re-derive the [`InMemorySigner`] required to sign
+ /// `input`.
+ ///
+ /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ pub channel_value_satoshis: u64,
+ /// The transaction input's outpoint corresponding to the commitment transaction's anchor
+ /// output.
+ pub outpoint: OutPoint,
+}
+
+/// Represents the different types of transactions, originating from LDK, to be bumped.
+#[derive(Clone, Debug)]
+pub enum BumpTransactionEvent {
+ /// Indicates that a channel featuring anchor outputs is to be closed by broadcasting the local
+ /// commitment transaction. Since commitment transactions have a static feerate pre-agreed upon,
+ /// they may need additional fees to be attached through a child transaction using the popular
+ /// [Child-Pays-For-Parent](https://bitcoinops.org/en/topics/cpfp) fee bumping technique. This
+ /// child transaction must include the anchor input described within `anchor_descriptor` along
+ /// with additional inputs to meet the target feerate. Failure to meet the target feerate
+ /// decreases the confirmation odds of the transaction package (which includes the commitment
+ /// and child anchor transactions), possibly resulting in a loss of funds. Once the transaction
+ /// is constructed, it must be fully signed for and broadcasted by the consumer of the event
+ /// along with the `commitment_tx` enclosed. Note that the `commitment_tx` must always be
+ /// broadcast first, as the child anchor transaction depends on it.
+ ///
+ /// The consumer should be able to sign for any of the additional inputs included within the
+ /// child anchor transaction. To sign its anchor input, an [`InMemorySigner`] should be
+ /// re-derived through [`KeysManager::derive_channel_keys`] with the help of
+ /// [`AnchorDescriptor::channel_keys_id`] and [`AnchorDescriptor::channel_value_satoshis`].
+ ///
+ /// It is possible to receive more than one instance of this event if a valid child anchor
+ /// transaction is never broadcast or is but not with a sufficient fee to be mined. Care should
+ /// be taken by the consumer of the event to ensure any future iterations of the child anchor
+ /// transaction adhere to the [Replace-By-Fee
+ /// rules](https://github.com/bitcoin/bitcoin/blob/master/doc/policy/mempool-replacements.md)
+ /// for fee bumps to be accepted into the mempool, and eventually the chain. As the frequency of
+ /// these events is not user-controlled, users may ignore/drop the event if they are no longer
+ /// able to commit external confirmed funds to the child anchor transaction.
+ ///
+ /// The set of `pending_htlcs` on the commitment transaction to be broadcast can be inspected to
+ /// determine whether a significant portion of the channel's funds are allocated to HTLCs,
+ /// enabling users to make their own decisions regarding the importance of the commitment
+ /// transaction's confirmation. Note that this is not required, but simply exists as an option
+ /// for users to override LDK's behavior. On commitments with no HTLCs (indicated by those with
+ /// an empty `pending_htlcs`), confirmation of the commitment transaction can be considered to
+ /// be not urgent.
+ ///
+ /// [`InMemorySigner`]: crate::chain::keysinterface::InMemorySigner
+ /// [`KeysManager::derive_channel_keys`]: crate::chain::keysinterface::KeysManager::derive_channel_keys
+ ChannelClose {
+ /// The target feerate that the transaction package, which consists of the commitment
+ /// transaction and the to-be-crafted child anchor transaction, must meet.
+ package_target_feerate_sat_per_1000_weight: u32,
+ /// The channel's commitment transaction to bump the fee of. This transaction should be
+ /// broadcast along with the anchor transaction constructed as a result of consuming this
+ /// event.
+ commitment_tx: Transaction,
+ /// The absolute fee in satoshis of the commitment transaction. This can be used along the
+ /// with weight of the commitment transaction to determine its feerate.
+ commitment_tx_fee_satoshis: u64,
+ /// The descriptor to sign the anchor input of the anchor transaction constructed as a
+ /// result of consuming this event.
+ anchor_descriptor: AnchorDescriptor,
+ /// The set of pending HTLCs on the commitment transaction that need to be resolved once the
+ /// commitment transaction confirms.
+ pending_htlcs: Vec<HTLCOutputInCommitment>,
+ },
+}
+
/// 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
/// Destination of the HTLC that failed to be processed.
failed_next_destination: HTLCDestination,
},
+ #[cfg(anchors)]
+ /// Indicates that a transaction originating from LDK needs to have its fee bumped. This event
+ /// requires confirmed external funds to be readily available to spend.
+ ///
+ /// LDK does not currently generate this event. It is limited to the scope of channels with
+ /// anchor outputs, which will be introduced in a future release.
+ BumpTransaction(BumpTransactionEvent),
}
impl Writeable for Event {
(2, failed_next_destination, required),
})
},
+ #[cfg(anchors)]
+ &Event::BumpTransaction(ref event)=> {
+ 27u8.write(writer)?;
+ match event {
+ // We never write the ChannelClose events as they'll be replayed upon restarting
+ // anyway if the commitment transaction remains unconfirmed.
+ BumpTransactionEvent::ChannelClose { .. } => {}
+ }
+ }
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
pub const MAX_SCID_VOUT_INDEX: u64 = 0xffff;
/// A `short_channel_id` construction error
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Eq)]
pub enum ShortChannelIdError {
BlockOverflow,
TxIndexOverflow,
/// 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.
-#[cfg_attr(test, derive(PartialEq, Debug))]
+#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
pub(crate) struct HighZeroBytesDroppedBigSize<T>(pub T);
macro_rules! impl_writeable_primitive {
/// The character set consists of ASCII alphanumeric characters, hyphens, and periods.
/// Its length is guaranteed to be representable by a single byte.
/// This serialization is used by BOLT 7 hostnames.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Hostname(String);
impl Hostname {
/// Returns the length of the hostname.
--- /dev/null
+disable_all_formatting = true
\ No newline at end of file