Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ use lightning::chain::{
chainmonitor, channelmonitor, BestBlock, ChannelMonitorUpdateStatus, Confirm, Watch,
};
use lightning::events;
use lightning::events::bump_transaction::sync::{WalletSourceSync, WalletSync};
use lightning::ln::channel::{
FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS,
};
Expand Down Expand Up @@ -81,6 +80,7 @@ use lightning::util::logger::Logger;
use lightning::util::ser::{LengthReadable, ReadableArgs, Writeable, Writer};
use lightning::util::test_channel_signer::{EnforcementState, TestChannelSigner};
use lightning::util::test_utils::TestWalletSource;
use lightning::util::wallet_utils::{WalletSourceSync, WalletSync};

use lightning_invoice::RawBolt11Invoice;

Expand Down
2 changes: 1 addition & 1 deletion fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ use lightning::chain::chaininterface::{
use lightning::chain::chainmonitor;
use lightning::chain::transaction::OutPoint;
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen};
use lightning::events::bump_transaction::sync::{WalletSourceSync, WalletSync};
use lightning::events::Event;
use lightning::ln::channel_state::ChannelDetails;
use lightning::ln::channelmanager::{ChainParameters, ChannelManager, InterceptId, PaymentId};
Expand Down Expand Up @@ -71,6 +70,7 @@ use lightning::util::logger::Logger;
use lightning::util::ser::{Readable, Writeable};
use lightning::util::test_channel_signer::{EnforcementState, TestChannelSigner};
use lightning::util::test_utils::TestWalletSource;
use lightning::util::wallet_utils::{WalletSourceSync, WalletSync};

use lightning_invoice::RawBolt11Invoice;

Expand Down
2 changes: 1 addition & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::bump_transaction::sync::WalletSourceSync;
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs::BaseMessageHandler as _;
Expand All @@ -55,6 +54,7 @@ use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::sign::OutputSpender;
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

Expand Down
538 changes: 15 additions & 523 deletions lightning/src/events/bump_transaction/mod.rs

Large diffs are not rendered by default.

252 changes: 3 additions & 249 deletions lightning/src/events/bump_transaction/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,258 +15,12 @@ use core::pin::pin;
use core::task;

use crate::chain::chaininterface::BroadcasterInterface;
use crate::chain::ClaimId;
use crate::prelude::*;
use crate::sign::SignerProvider;
use crate::util::async_poll::{dummy_waker, MaybeSend, MaybeSync};
use crate::util::async_poll::dummy_waker;
use crate::util::logger::Logger;
use crate::util::wallet_utils::{CoinSelectionSourceSync, CoinSelectionSourceSyncWrapper};

use bitcoin::{OutPoint, Psbt, ScriptBuf, Transaction, TxOut};

use super::BumpTransactionEvent;
use super::{
BumpTransactionEventHandler, CoinSelection, CoinSelectionSource, Input, Utxo, Wallet,
WalletSource,
};

/// An alternative to [`CoinSelectionSourceSync`] that can be implemented and used along
/// [`WalletSync`] to provide a default implementation to [`CoinSelectionSourceSync`].
///
/// For an asynchronous version of this trait, see [`WalletSource`].
// Note that updates to documentation on this trait should be copied to the asynchronous version.
pub trait WalletSourceSync {
/// Returns all UTXOs, with at least 1 confirmation each, that are available to spend.
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>;

/// Returns the previous transaction containing the UTXO referenced by the outpoint.
fn get_prevtx(&self, outpoint: OutPoint) -> Result<Transaction, ()>;

/// Returns a script to use for change above dust resulting from a successful coin selection
/// attempt.
fn get_change_script(&self) -> Result<ScriptBuf, ()>;

/// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
/// the transaction known to the wallet (i.e., any provided via
/// [`WalletSource::list_confirmed_utxos`]).
///
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
/// unsigned transaction and then sign it with your wallet.
///
/// [`TxIn::script_sig`]: bitcoin::TxIn::script_sig
/// [`TxIn::witness`]: bitcoin::TxIn::witness
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
}

pub(crate) struct WalletSourceSyncWrapper<T: Deref>(T)
where
T::Target: WalletSourceSync;

// Implement `Deref` directly on WalletSourceSyncWrapper so that it can be used directly
// below, rather than via a wrapper.
impl<T: Deref> Deref for WalletSourceSyncWrapper<T>
where
T::Target: WalletSourceSync,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<T: Deref> WalletSource for WalletSourceSyncWrapper<T>
where
T::Target: WalletSourceSync,
{
fn list_confirmed_utxos<'a>(
&'a self,
) -> impl Future<Output = Result<Vec<Utxo>, ()>> + MaybeSend + 'a {
let utxos = self.0.list_confirmed_utxos();
async move { utxos }
}

fn get_prevtx<'a>(
&'a self, outpoint: OutPoint,
) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a {
let prevtx = self.0.get_prevtx(outpoint);
Box::pin(async move { prevtx })
}

fn get_change_script<'a>(
&'a self,
) -> impl Future<Output = Result<ScriptBuf, ()>> + MaybeSend + 'a {
let script = self.0.get_change_script();
async move { script }
}

fn sign_psbt<'a>(
&'a self, psbt: Psbt,
) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a {
let signed_psbt = self.0.sign_psbt(psbt);
async move { signed_psbt }
}
}

/// A wrapper over [`WalletSourceSync`] that implements [`CoinSelectionSourceSync`] by preferring
/// UTXOs that would avoid conflicting double spends. If not enough UTXOs are available to do so,
/// conflicting double spends may happen.
///
/// For an asynchronous version of this wrapper, see [`Wallet`].
// Note that updates to documentation on this struct should be copied to the asynchronous version.
pub struct WalletSync<W: Deref + MaybeSync + MaybeSend, L: Logger + MaybeSync + MaybeSend>
where
W::Target: WalletSourceSync + MaybeSend,
{
wallet: Wallet<WalletSourceSyncWrapper<W>, L>,
}

impl<W: Deref + MaybeSync + MaybeSend, L: Logger + MaybeSync + MaybeSend> WalletSync<W, L>
where
W::Target: WalletSourceSync + MaybeSend,
{
/// Constructs a new [`WalletSync`] instance.
pub fn new(source: W, logger: L) -> Self {
Self { wallet: Wallet::new(WalletSourceSyncWrapper(source), logger) }
}
}

impl<W: Deref + MaybeSync + MaybeSend, L: Logger + MaybeSync + MaybeSend> CoinSelectionSourceSync
for WalletSync<W, L>
where
W::Target: WalletSourceSync + MaybeSend + MaybeSync,
{
fn select_confirmed_utxos(
&self, claim_id: Option<ClaimId>, must_spend: Vec<Input>, must_pay_to: &[TxOut],
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
) -> Result<CoinSelection, ()> {
let fut = self.wallet.select_confirmed_utxos(
claim_id,
must_spend,
must_pay_to,
target_feerate_sat_per_1000_weight,
max_tx_weight,
);
let mut waker = dummy_waker();
let mut ctx = task::Context::from_waker(&mut waker);
match pin!(fut).poll(&mut ctx) {
task::Poll::Ready(result) => result,
task::Poll::Pending => {
unreachable!(
"Wallet::select_confirmed_utxos should not be pending in a sync context"
);
},
}
}

fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
let fut = self.wallet.sign_psbt(psbt);
let mut waker = dummy_waker();
let mut ctx = task::Context::from_waker(&mut waker);
match pin!(fut).poll(&mut ctx) {
task::Poll::Ready(result) => result,
task::Poll::Pending => {
unreachable!("Wallet::sign_psbt should not be pending in a sync context");
},
}
}
}

/// An abstraction over a bitcoin wallet that can perform coin selection over a set of UTXOs and can
/// sign for them. The coin selection method aims to mimic Bitcoin Core's `fundrawtransaction` RPC,
/// which most wallets should be able to satisfy. Otherwise, consider implementing
/// [`WalletSourceSync`], which can provide a default implementation of this trait when used with
/// [`WalletSync`].
///
/// For an asynchronous version of this trait, see [`CoinSelectionSource`].
// Note that updates to documentation on this trait should be copied to the asynchronous version.
pub trait CoinSelectionSourceSync {
/// Performs coin selection of a set of UTXOs, with at least 1 confirmation each, that are
/// available to spend. Implementations are free to pick their coin selection algorithm of
/// choice, as long as the following requirements are met:
///
/// 1. `must_spend` contains a set of [`Input`]s that must be included in the transaction
/// throughout coin selection, but must not be returned as part of the result.
/// 2. `must_pay_to` contains a set of [`TxOut`]s that must be included in the transaction
/// throughout coin selection. In some cases, like when funding an anchor transaction, this
/// set is empty. Implementations should ensure they handle this correctly on their end,
/// e.g., Bitcoin Core's `fundrawtransaction` RPC requires at least one output to be
/// provided, in which case a zero-value empty OP_RETURN output can be used instead.
/// 3. Enough inputs must be selected/contributed for the resulting transaction (including the
/// inputs and outputs noted above) to meet `target_feerate_sat_per_1000_weight`.
/// 4. The final transaction must have a weight smaller than `max_tx_weight`; if this
/// constraint can't be met, return an `Err`. In the case of counterparty-signed HTLC
/// transactions, we will remove a chunk of HTLCs and try your algorithm again. As for
/// anchor transactions, we will try your coin selection again with the same input-output
/// set when you call [`ChannelMonitor::rebroadcast_pending_claims`], as anchor transactions
/// cannot be downsized.
///
/// Implementations must take note that [`Input::satisfaction_weight`] only tracks the weight of
/// the input's `script_sig` and `witness`. Some wallets, like Bitcoin Core's, may require
/// providing the full input weight. Failing to do so may lead to underestimating fee bumps and
/// delaying block inclusion.
///
/// The `claim_id` must map to the set of external UTXOs assigned to the claim, such that they
/// can be re-used within new fee-bumped iterations of the original claiming transaction,
/// ensuring that claims don't double spend each other. If a specific `claim_id` has never had a
/// transaction associated with it, and all of the available UTXOs have already been assigned to
/// other claims, implementations must be willing to double spend their UTXOs. The choice of
/// which UTXOs to double spend is left to the implementation, but it must strive to keep the
/// set of other claims being double spent to a minimum.
///
/// [`ChannelMonitor::rebroadcast_pending_claims`]: crate::chain::channelmonitor::ChannelMonitor::rebroadcast_pending_claims
fn select_confirmed_utxos(
&self, claim_id: Option<ClaimId>, must_spend: Vec<Input>, must_pay_to: &[TxOut],
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
) -> Result<CoinSelection, ()>;

/// Signs and provides the full witness for all inputs within the transaction known to the
/// trait (i.e., any provided via [`CoinSelectionSourceSync::select_confirmed_utxos`]).
///
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
/// unsigned transaction and then sign it with your wallet.
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
}

struct CoinSelectionSourceSyncWrapper<T: Deref>(T)
where
T::Target: CoinSelectionSourceSync;

// Implement `Deref` directly on CoinSelectionSourceSyncWrapper so that it can be used directly
// below, rather than via a wrapper.
impl<T: Deref> Deref for CoinSelectionSourceSyncWrapper<T>
where
T::Target: CoinSelectionSourceSync,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<T: Deref> CoinSelectionSource for CoinSelectionSourceSyncWrapper<T>
where
T::Target: CoinSelectionSourceSync,
{
fn select_confirmed_utxos<'a>(
&'a self, claim_id: Option<ClaimId>, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
) -> impl Future<Output = Result<CoinSelection, ()>> + MaybeSend + 'a {
let coins = self.0.select_confirmed_utxos(
claim_id,
must_spend,
must_pay_to,
target_feerate_sat_per_1000_weight,
max_tx_weight,
);
async move { coins }
}

fn sign_psbt<'a>(
&'a self, psbt: Psbt,
) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a {
let psbt = self.0.sign_psbt(psbt);
async move { psbt }
}
}
use super::{BumpTransactionEvent, BumpTransactionEventHandler};

/// A handler for [`Event::BumpTransaction`] events that sources confirmed UTXOs from a
/// [`CoinSelectionSourceSync`] to fee bump transactions via Child-Pays-For-Parent (CPFP) or
Expand Down
23 changes: 11 additions & 12 deletions lightning/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ pub enum FundingInfo {
/// The outpoint of the funding
outpoint: transaction::OutPoint,
},
/// The contributions used to for a dual funding or splice funding transaction.
Contribution {
/// UTXOs spent as inputs contributed to the funding transaction.
inputs: Vec<OutPoint>,
/// Outputs contributed to the funding transaction.
outputs: Vec<TxOut>,
},
}

impl_writeable_tlv_based_enum!(FundingInfo,
Expand All @@ -85,6 +92,10 @@ impl_writeable_tlv_based_enum!(FundingInfo,
},
(1, OutPoint) => {
(1, outpoint, required)
},
(2, Contribution) => {
(0, inputs, optional_vec),
(1, outputs, optional_vec),
}
);

Expand Down Expand Up @@ -1561,10 +1572,6 @@ pub enum Event {
abandoned_funding_txo: Option<OutPoint>,
/// The features that this channel will operate with, if available.
channel_type: Option<ChannelTypeFeatures>,
/// UTXOs spent as inputs contributed to the splice transaction.
contributed_inputs: Vec<OutPoint>,
/// Outputs contributed to the splice transaction.
contributed_outputs: Vec<TxOut>,
},
/// Used to indicate to the user that they can abandon the funding transaction and recycle the
/// inputs for another purpose.
Expand Down Expand Up @@ -2326,8 +2333,6 @@ impl Writeable for Event {
ref counterparty_node_id,
ref abandoned_funding_txo,
ref channel_type,
ref contributed_inputs,
ref contributed_outputs,
} => {
52u8.write(writer)?;
write_tlv_fields!(writer, {
Expand All @@ -2336,8 +2341,6 @@ impl Writeable for Event {
(5, user_channel_id, required),
(7, counterparty_node_id, required),
(9, abandoned_funding_txo, option),
(11, *contributed_inputs, optional_vec),
(13, *contributed_outputs, optional_vec),
});
},
// Note that, going forward, all new events must only write data inside of
Expand Down Expand Up @@ -2964,8 +2967,6 @@ impl MaybeReadable for Event {
(5, user_channel_id, required),
(7, counterparty_node_id, required),
(9, abandoned_funding_txo, option),
(11, contributed_inputs, optional_vec),
(13, contributed_outputs, optional_vec),
});

Ok(Some(Event::SpliceFailed {
Expand All @@ -2974,8 +2975,6 @@ impl MaybeReadable for Event {
counterparty_node_id: counterparty_node_id.0.unwrap(),
abandoned_funding_txo,
channel_type,
contributed_inputs: contributed_inputs.unwrap_or_default(),
contributed_outputs: contributed_outputs.unwrap_or_default(),
}))
};
f()
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/async_signer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
//! Tests for asynchronous signing. These tests verify that the channel state machine behaves
//! properly with a signer implementation that asynchronously derives signatures.

use crate::events::bump_transaction::sync::WalletSourceSync;
use crate::ln::splicing_tests::{initiate_splice_out, negotiate_splice_tx};
use crate::prelude::*;
use crate::util::ser::Writeable;
Expand All @@ -31,6 +30,7 @@ use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::SignerProvider;
use crate::util::logger::Logger;
use crate::util::test_channel_signer::SignerOp;
use crate::util::wallet_utils::WalletSourceSync;

#[test]
fn test_open_channel() {
Expand Down
Loading
Loading