Skip to content

Anchor-outputs (2/3): Add anchors and support Commitment CPFP bumping #725

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
16 changes: 10 additions & 6 deletions lightning/src/chain/onchaintx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,13 +345,17 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
/// (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, U: Deref>(&mut self, height: u32, cached_request: &PackageTemplate, fee_estimator: &F, logger: &L, utxo_pool: &U) -> Option<(Option<u32>, u64, Transaction)>
fn generate_claim_tx<F: Deref, L: Deref, U: Deref>(&mut self, height: u32, cached_request: &mut PackageTemplate, fee_estimator: &F, logger: &L, utxo_pool: &U) -> Option<(Option<u32>, u64, Transaction)>
where F::Target: FeeEstimator,
L::Target: Logger,
U::Target: UtxoPool,
{
if cached_request.outpoints().len() == 0 { return None } // But don't prune pending claiming request yet, we may have to resurrect HTLCs

if !cached_request.is_malleable() {
cached_request.set_bumping_utxo(utxo_pool);
}

// Compute new height timer to decide when we need to regenerate a new bumped version of the claim tx (if we
// 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(height));
Expand Down Expand Up @@ -440,7 +444,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
// 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(height, &req, &*fee_estimator, &*logger, &*utxo_pool) {
if let Some((new_timer, new_feerate, tx)) = self.generate_claim_tx(height, &mut req, &*fee_estimator, &*logger, &*utxo_pool) {
req.set_timer(new_timer);
req.set_feerate(new_feerate);
let txid = tx.txid();
Expand Down Expand Up @@ -564,8 +568,8 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {

// 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(height, &request, &*fee_estimator, &*logger, &*utxo_pool) {
for (first_claim_txid, ref mut request) in bump_candidates.iter_mut() {
if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, request, &*fee_estimator, &*logger, &*utxo_pool) {
log_trace!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
broadcaster.broadcast_transaction(&bump_tx);
if let Some(request) = self.pending_claim_requests.get_mut(first_claim_txid) {
Expand Down Expand Up @@ -632,8 +636,8 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
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, &&*utxo_pool) {
for (_, ref mut request) in bump_candidates.iter_mut() {
if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, request, &&*fee_estimator, &&*logger, &&*utxo_pool) {
request.set_timer(new_timer);
request.set_feerate(new_feerate);
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
Expand Down
25 changes: 24 additions & 1 deletion lightning/src/chain/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use ln::chan_utils;
use ln::msgs::DecodeError;
use chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
use chain::keysinterface::Sign;
use chain::utxointerface::UtxoPool;
use chain::onchaintx::OnchainTxHandler;
use util::byte_utils;
use util::logger::Logger;
Expand Down Expand Up @@ -248,19 +249,23 @@ impl_writeable_tlv_based!(HolderHTLCOutput, {
#[derive(Clone, PartialEq)]
pub(crate) struct HolderFundingOutput {
funding_redeemscript: Script,
utxo_input: Option<(BitcoinOutPoint, BumpingOutput)>
}

impl HolderFundingOutput {
pub(crate) fn build(funding_redeemscript: Script) -> Self {
HolderFundingOutput {
funding_redeemscript,
utxo_input: None
}
}
}

impl_writeable_tlv_based!(HolderFundingOutput, {
(0, funding_redeemscript),
}, {}, {});
}, {
(2, utxo_input),
}, {});

/// A wrapper encapsulating all in-protocol differing outputs types.
///
Expand Down Expand Up @@ -492,6 +497,24 @@ impl PackageTemplate {
pub(crate) fn outpoints(&self) -> Vec<&BitcoinOutPoint> {
self.inputs.iter().map(|(o, _)| o).collect()
}
pub(crate) fn set_bumping_utxo<U: Deref>(&mut self, utxo_pool: &U)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this take the required fee to pass to allocate_utxo?

where U::Target: UtxoPool,
{
// CPFP'ed package can't spent more than one input (for now).
assert_eq!(self.inputs.len(), 1);
for (_, input_solving_data) in self.inputs.iter_mut() {
match input_solving_data {
PackageSolvingData::HolderFundingOutput(data) => {
if data.utxo_input.is_some() { return; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the previously set UTXO is no longer sufficient to cover the necessary fees?

data.utxo_input = utxo_pool.allocate_utxo(0);
},
PackageSolvingData::HolderHTLCOutput(..) => {
return; //TODO: Should we anchor output HTLC-txn?
},
_ => panic!("Malleable package should be bumped through RBF")
}
}
}
pub(crate) fn split_package(&mut self, split_outp: &BitcoinOutPoint) -> Option<PackageTemplate> {
match self.malleability {
PackageMalleability::Malleable => {
Expand Down