Skip to content

Commit a1eb3a6

Browse files
committed
Generate ClaimEvent for HolderFundingOutput inputs from anchor channels
1 parent ce27c26 commit a1eb3a6

File tree

2 files changed

+155
-17
lines changed

2 files changed

+155
-17
lines changed

lightning/src/chain/onchaintx.rs

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ use bitcoin::secp256k1;
2323

2424
use ln::msgs::DecodeError;
2525
use ln::PaymentPreimage;
26+
use ln::chan_utils;
2627
use ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransaction};
27-
use chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
28+
use chain::chaininterface::{ConfirmationTarget, FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
2829
use chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
2930
use chain::keysinterface::{Sign, KeysInterface};
30-
use chain::package::PackageTemplate;
31+
use chain::package::{PackageSolvingData, PackageTemplate};
3132
use util::logger::Logger;
3233
use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, VecWriter};
3334
use util::byte_utils;
@@ -36,6 +37,7 @@ use io;
3637
use prelude::*;
3738
use alloc::collections::BTreeMap;
3839
use core::cmp;
40+
use core::convert::TryInto;
3941
use core::ops::Deref;
4042
use core::mem::replace;
4143
use bitcoin::hashes::Hash;
@@ -162,8 +164,17 @@ impl Writeable for Option<Vec<Option<(usize, Signature)>>> {
162164
}
163165
}
164166

167+
pub(crate) enum ClaimEvent {
168+
BumpCommitment {
169+
target_feerate_sat_per_1000_weight: u32,
170+
commitment_tx: Transaction,
171+
anchor_output_idx: u32,
172+
},
173+
}
174+
165175
pub(crate) enum OnchainClaim {
166176
Tx(Transaction),
177+
Event(ClaimEvent),
167178
}
168179

169180
/// OnchainTxHandler receives claiming requests, aggregates them if it's sound, broadcast and
@@ -196,6 +207,7 @@ pub struct OnchainTxHandler<ChannelSigner: Sign> {
196207
pub(crate) pending_claim_requests: HashMap<Txid, PackageTemplate>,
197208
#[cfg(not(test))]
198209
pending_claim_requests: HashMap<Txid, PackageTemplate>,
210+
pending_claim_events: HashMap<Txid, ClaimEvent>,
199211

200212
// Used to link outpoints claimed in a connected block to a pending claim request.
201213
// Key is outpoint than monitor parsing has detected we have keys/scripts to claim
@@ -345,6 +357,7 @@ impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler<K::Signer> {
345357
locktimed_packages,
346358
pending_claim_requests,
347359
onchain_events_awaiting_threshold_conf,
360+
pending_claim_events: HashMap::new(),
348361
secp_ctx,
349362
})
350363
}
@@ -364,6 +377,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
364377
claimable_outpoints: HashMap::new(),
365378
locktimed_packages: BTreeMap::new(),
366379
onchain_events_awaiting_threshold_conf: Vec::new(),
380+
pending_claim_events: HashMap::new(),
367381

368382
secp_ctx,
369383
}
@@ -402,12 +416,63 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
402416
return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)))
403417
}
404418
} else {
405-
// Note: Currently, amounts of holder outputs spending witnesses aren't used
406-
// as we can't malleate spending package to increase their feerate. This
407-
// should change with the remaining anchor output patchset.
408-
if let Some(transaction) = cached_request.finalize_untractable_package(self, logger) {
409-
return Some((None, 0, OnchainClaim::Tx(transaction)));
419+
// Untractable packages cannot have their fees bumped through Replace-By-Fee. Some
420+
// packages may support fee bumping through Child-Pays-For-Parent, indicated by those
421+
// which require external funding.
422+
let inputs = cached_request.inputs();
423+
debug_assert_eq!(inputs.len(), 1);
424+
let tx = match cached_request.finalize_untractable_package(self, logger) {
425+
Some(tx) => tx,
426+
None => return None,
427+
};
428+
if !cached_request.requires_external_funding() {
429+
return Some((None, 0, OnchainClaim::Tx(tx)));
410430
}
431+
return inputs.iter().find_map(|input| match input.solving_data {
432+
// Commitment inputs with anchors support are the only untractable inputs supported
433+
// thus far that require external funding.
434+
PackageSolvingData::HolderFundingOutput(..) => {
435+
// We'll locate an anchor output we can spend within the commitment transaction.
436+
let commitment_tx = tx.clone();
437+
let anchor_script = chan_utils::get_anchor_redeemscript(
438+
&self.channel_transaction_parameters.holder_pubkeys.funding_pubkey,
439+
).to_v0_p2wsh();
440+
match commitment_tx.output.iter().enumerate()
441+
.find(|(_, txout)| txout.script_pubkey == anchor_script)
442+
.map(|(idx, _)| idx as u32)
443+
{
444+
// An anchor output was found, so we should yield a funding event externally.
445+
Some(anchor_output_idx) => {
446+
let target_feerate_sat_per_1000_weight: u32 = match cached_request
447+
.compute_package_child_feerate(&tx, fee_estimator, ConfirmationTarget::HighPriority)
448+
.map(|f| f.try_into().ok())
449+
.flatten()
450+
{
451+
Some(f) => f,
452+
_ => return None,
453+
};
454+
Some((
455+
new_timer,
456+
target_feerate_sat_per_1000_weight as u64,
457+
OnchainClaim::Event(ClaimEvent::BumpCommitment {
458+
target_feerate_sat_per_1000_weight,
459+
commitment_tx,
460+
anchor_output_idx,
461+
}),
462+
))
463+
},
464+
// An anchor output was not found. There's nothing we can do other than
465+
// attempt to broadcast the transaction with its current fee rate and hope
466+
// it confirms. This is essentially the same behavior as a commitment
467+
// transaction without anchor outputs.
468+
None => Some((None, 0, OnchainClaim::Tx(commitment_tx))),
469+
}
470+
},
471+
_ => {
472+
debug_assert!(false, "Only HolderFundingOutput inputs should be untractable and require external funding");
473+
None
474+
},
475+
});
411476
}
412477
None
413478
}
@@ -481,18 +546,25 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
481546
if let Some((new_timer, new_feerate, claim)) = self.generate_claim(cur_height, &req, &*fee_estimator, &*logger) {
482547
req.set_timer(new_timer);
483548
req.set_feerate(new_feerate);
484-
match claim {
549+
let txid = match claim {
485550
OnchainClaim::Tx(tx) => {
486-
let txid = tx.txid();
487-
for k in req.outpoints() {
488-
log_info!(logger, "Registering claiming request for {}:{}", k.txid, k.vout);
489-
self.claimable_outpoints.insert(k.clone(), (txid, conf_height));
490-
}
491-
self.pending_claim_requests.insert(txid, req);
492551
log_info!(logger, "Broadcasting onchain {}", log_tx!(tx));
493552
broadcaster.broadcast_transaction(&tx);
553+
tx.txid()
554+
},
555+
OnchainClaim::Event(claim_event) => {
556+
let txid = match claim_event {
557+
ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid(),
558+
};
559+
self.pending_claim_events.insert(txid, claim_event);
560+
txid
494561
},
562+
};
563+
for k in req.outpoints() {
564+
log_info!(logger, "Registering claiming request for {}:{}", k.txid, k.vout);
565+
self.claimable_outpoints.insert(k.clone(), (txid, conf_height));
495566
}
567+
self.pending_claim_requests.insert(txid, req);
496568
}
497569
}
498570

@@ -584,6 +656,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
584656
for outpoint in request.outpoints() {
585657
log_debug!(logger, "Removing claim tracking for {} due to maturation of claim tx {}.", outpoint, claim_request);
586658
self.claimable_outpoints.remove(&outpoint);
659+
self.pending_claim_events.remove(&claim_request);
587660
}
588661
}
589662
},
@@ -616,6 +689,9 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
616689
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
617690
broadcaster.broadcast_transaction(&bump_tx);
618691
},
692+
OnchainClaim::Event(claim_event) => {
693+
self.pending_claim_events.insert(*first_claim_txid, claim_event);
694+
},
619695
}
620696
if let Some(request) = self.pending_claim_requests.get_mut(first_claim_txid) {
621697
request.set_timer(new_timer);
@@ -678,7 +754,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
678754
self.onchain_events_awaiting_threshold_conf.push(entry);
679755
}
680756
}
681-
for (_, request) in bump_candidates.iter_mut() {
757+
for (first_claim_txid_height, request) in bump_candidates.iter_mut() {
682758
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(height, &request, fee_estimator, &&*logger) {
683759
request.set_timer(new_timer);
684760
request.set_feerate(new_feerate);
@@ -687,6 +763,9 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
687763
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
688764
broadcaster.broadcast_transaction(&bump_tx);
689765
},
766+
OnchainClaim::Event(claim_event) => {
767+
self.pending_claim_events.insert(first_claim_txid_height.0, claim_event);
768+
},
690769
}
691770
}
692771
}

lightning/src/chain/package.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ impl_writeable_tlv_based_enum!(PackageSolvingData, ;
316316

317317
#[derive(Clone, PartialEq)]
318318
pub(crate) struct Input {
319-
solving_data: PackageSolvingData,
319+
pub(crate) solving_data: PackageSolvingData,
320320
opt_anchors: bool,
321321
}
322322

@@ -557,6 +557,9 @@ impl PackageTemplate {
557557
pub(crate) fn outpoints(&self) -> Vec<&BitcoinOutPoint> {
558558
self.inputs.iter().map(|(o, _)| o).collect()
559559
}
560+
pub(crate) fn inputs(&self) -> Vec<&Input> {
561+
self.inputs.iter().map(|(_, i)| i).collect()
562+
}
560563
pub(crate) fn split_package(&mut self, split_outp: &BitcoinOutPoint) -> Option<PackageTemplate> {
561564
match self.malleability {
562565
PackageMalleability::Malleable => {
@@ -620,7 +623,7 @@ impl PackageTemplate {
620623
}
621624
/// Gets the amount of all outptus being spent by this package, only valid for malleable
622625
/// packages.
623-
fn package_amount(&self) -> u64 {
626+
pub(crate) fn package_amount(&self) -> u64 {
624627
let mut amounts = 0;
625628
for (_, outp) in self.inputs.iter() {
626629
amounts += outp.amount();
@@ -733,6 +736,62 @@ impl PackageTemplate {
733736
}
734737
None
735738
}
739+
740+
pub(crate) fn requires_external_funding(&self) -> bool {
741+
self.inputs.iter().find(|input| match input.1.solving_data {
742+
PackageSolvingData::HolderFundingOutput(..) => input.1.opt_anchors(),
743+
_ => false,
744+
}).is_some()
745+
}
746+
747+
/// Computes a feerate based on the given confirmation target. If a previous feerate was used,
748+
/// and the new feerate is below it, we'll use a 25% increase of the previous feerate instead of
749+
/// the new one.
750+
fn compute_feerate<F: Deref>(
751+
previous_feerate: u64, fee_estimator: &LowerBoundedFeeEstimator<F>, conf_target: ConfirmationTarget,
752+
) -> u64 where F::Target: FeeEstimator {
753+
let feerate_estimate = fee_estimator.bounded_sat_per_1000_weight(conf_target);
754+
if previous_feerate != 0 {
755+
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
756+
if feerate_estimate as u64 > previous_feerate {
757+
feerate_estimate as u64
758+
} else {
759+
// ...else just increase the previous feerate by 25% (because that's a nice number)
760+
previous_feerate + (previous_feerate / 4)
761+
}
762+
} else {
763+
feerate_estimate as u64
764+
}
765+
}
766+
767+
/// Computes the feerate for a child transaction such that its parent transaction meets a
768+
/// feerate based on the given confirmation target.
769+
pub(crate) fn compute_package_child_feerate<F: Deref>(
770+
&self, parent: &Transaction, fee_estimator: &LowerBoundedFeeEstimator<F>, conf_target: ConfirmationTarget,
771+
) -> Option<u64> where F::Target: FeeEstimator {
772+
debug_assert!(!self.is_malleable());
773+
let parent_feerate = {
774+
let input_amount = self.package_amount();
775+
let weight = self.package_weight(
776+
&parent.output.iter().map(|outp| &outp.script_pubkey).collect::<Vec<_>>(),
777+
);
778+
let output_amount = parent.output.iter().fold(0, |acc, outp| acc + outp.value);
779+
let fee = input_amount - output_amount;
780+
fee / (weight as u64 * 1000)
781+
};
782+
// The previous feerate tracks the previous child feerate used, so we'll need to map it back
783+
// to the overall package feerate.
784+
let previous_package_feerate = if self.feerate_previous != 0 {
785+
(parent_feerate + self.feerate_previous) / 2
786+
} else {
787+
0
788+
};
789+
let package_feerate = Self::compute_feerate(previous_package_feerate, fee_estimator, conf_target);
790+
// With the new package feerate obtained, compute the feerate the child transaction must
791+
// meet such that the package feerate is met.
792+
(package_feerate as u64).checked_mul(2).map(|f| f.checked_sub(parent_feerate)).flatten()
793+
}
794+
736795
pub (crate) fn build_package(txid: Txid, vout: u32, input: Input, soonest_conf_deadline: u32, aggregable: bool, height_original: u32) -> Self {
737796
let malleability = match input.solving_data {
738797
PackageSolvingData::RevokedOutput(..) => PackageMalleability::Malleable,

0 commit comments

Comments
 (0)