Skip to content

Commit 597bf03

Browse files
Antoine Riardariard
Antoine Riard
authored andcommitted
Anchor: do not aggregate claim of revoked output
See lightning/bolts#803 This protect the justice claim of counterparty revoked output. As otherwise if the all the revoked outputs claims are batched in a single transaction, low-feerate HTLCs transactions can delay our honest justice claim transaction until BREAKDOWN_TIMEOUT expires.
1 parent 56b0c96 commit 597bf03

File tree

2 files changed

+31
-30
lines changed

2 files changed

+31
-30
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2603,8 +2603,10 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
26032603
// First, process non-htlc outputs (to_holder & to_counterparty)
26042604
for (idx, outp) in tx.output.iter().enumerate() {
26052605
if outp.script_pubkey == revokeable_p2wsh {
2606-
let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv);
2607-
let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height);
2606+
let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv, self.onchain_tx_handler.opt_anchors(), true);
2607+
// Post-anchor, aggregation of outputs of different types is unsafe. See https://github.com/lightning/bolts/pull/803.
2608+
let aggregation = if self.onchain_tx_handler.opt_anchors() { false } else { true };
2609+
let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, aggregation, height);
26082610
claimable_outpoints.push(justice_package);
26092611
to_counterparty_output_info =
26102612
Some((idx.try_into().expect("Txn can't have more than 2^32 outputs"), outp.value));
@@ -2787,7 +2789,8 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
27872789
let revk_outp = RevokedOutput::build(
27882790
per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
27892791
self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key,
2790-
tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv
2792+
tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv,
2793+
self.onchain_tx_handler.opt_anchors(), false
27912794
);
27922795
let justice_package = PackageTemplate::build_package(
27932796
htlc_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp),

lightning/src/chain/package.rs

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -98,30 +98,36 @@ pub(crate) struct RevokedOutput {
9898
weight: u64,
9999
amount: u64,
100100
on_counterparty_tx_csv: u16,
101+
opt_anchors: Option<()>,
102+
is_counterparty_balance_or_non_anchors: Option<()>,
101103
}
102104

103105
impl RevokedOutput {
104-
pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, per_commitment_key: SecretKey, amount: u64, on_counterparty_tx_csv: u16) -> Self {
106+
pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, per_commitment_key: SecretKey, amount: u64, on_counterparty_tx_csv: u16, opt_anchors: bool, is_counterparty_balance: bool) -> Self {
105107
RevokedOutput {
106108
per_commitment_point,
107109
counterparty_delayed_payment_base_key,
108110
counterparty_htlc_base_key,
109111
per_commitment_key,
110112
weight: WEIGHT_REVOKED_OUTPUT,
111113
amount,
112-
on_counterparty_tx_csv
114+
on_counterparty_tx_csv,
115+
opt_anchors: if opt_anchors { Some(()) } else { None },
116+
is_counterparty_balance_or_non_anchors: if (is_counterparty_balance || !opt_anchors) { Some(()) } else { None },
113117
}
114118
}
115119
}
116120

117121
impl_writeable_tlv_based!(RevokedOutput, {
118122
(0, per_commitment_point, required),
123+
(1, is_counterparty_balance_or_non_anchors, required),
119124
(2, counterparty_delayed_payment_base_key, required),
120125
(4, counterparty_htlc_base_key, required),
121126
(6, per_commitment_key, required),
122127
(8, weight, required),
123128
(10, amount, required),
124129
(12, on_counterparty_tx_csv, required),
130+
(14, opt_anchors, option),
125131
});
126132

127133
/// A struct to describe a revoked offered output and corresponding information to generate a
@@ -827,18 +833,7 @@ impl PackageTemplate {
827833
}
828834

829835
pub (crate) fn build_package(txid: Txid, vout: u32, input_solving_data: PackageSolvingData, soonest_conf_deadline: u32, aggregable: bool, height_original: u32) -> Self {
830-
let malleability = match input_solving_data {
831-
PackageSolvingData::RevokedOutput(..) => PackageMalleability::Malleable,
832-
PackageSolvingData::RevokedHTLCOutput(..) => PackageMalleability::Malleable,
833-
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => PackageMalleability::Malleable,
834-
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => PackageMalleability::Malleable,
835-
PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
836-
PackageMalleability::Malleable
837-
} else {
838-
PackageMalleability::Untractable
839-
},
840-
PackageSolvingData::HolderFundingOutput(..) => PackageMalleability::Untractable,
841-
};
836+
let (malleability, aggregable) = Self::map_output_type_flags(&input_solving_data);
842837
let mut inputs = Vec::with_capacity(1);
843838
inputs.push((BitcoinOutPoint { txid, vout }, input_solving_data));
844839
PackageTemplate {
@@ -851,6 +846,20 @@ impl PackageTemplate {
851846
height_original,
852847
}
853848
}
849+
850+
fn map_output_type_flags(input_solving_data: &PackageSolvingData) -> (PackageMalleability, bool) {
851+
let (malleability, aggregable) = match input_solving_data {
852+
PackageSolvingData::RevokedOutput(RevokedOutput { is_counterparty_balance_or_non_anchors: None, .. }) => { (PackageMalleability::Malleable, true) },
853+
PackageSolvingData::RevokedOutput(RevokedOutput { opt_anchors: Some(..), .. }) => { (PackageMalleability::Malleable, false) },
854+
PackageSolvingData::RevokedOutput(RevokedOutput { opt_anchors: None, .. }) => { (PackageMalleability::Malleable, true) },
855+
PackageSolvingData::RevokedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
856+
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
857+
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { (PackageMalleability::Malleable, false) },
858+
PackageSolvingData::HolderHTLCOutput(..) => { (PackageMalleability::Untractable, false) },
859+
PackageSolvingData::HolderFundingOutput(..) => { (PackageMalleability::Untractable, false) },
860+
};
861+
(malleability, aggregable)
862+
}
854863
}
855864

856865
impl Writeable for PackageTemplate {
@@ -880,18 +889,7 @@ impl Readable for PackageTemplate {
880889
inputs.push((outpoint, rev_outp));
881890
}
882891
let (malleability, aggregable) = if let Some((_, lead_input)) = inputs.first() {
883-
match lead_input {
884-
PackageSolvingData::RevokedOutput(..) => { (PackageMalleability::Malleable, true) },
885-
PackageSolvingData::RevokedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
886-
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { (PackageMalleability::Malleable, true) },
887-
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { (PackageMalleability::Malleable, false) },
888-
PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() {
889-
(PackageMalleability::Malleable, outp.preimage.is_some())
890-
} else {
891-
(PackageMalleability::Untractable, false)
892-
},
893-
PackageSolvingData::HolderFundingOutput(..) => { (PackageMalleability::Untractable, false) },
894-
}
892+
Self::map_output_type_flags(&lead_input)
895893
} else { return Err(DecodeError::InvalidValue); };
896894
let mut soonest_conf_deadline = 0;
897895
let mut feerate_previous = 0;
@@ -1033,7 +1031,7 @@ mod tests {
10331031
{
10341032
let dumb_scalar = SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap();
10351033
let dumb_point = PublicKey::from_secret_key(&$secp_ctx, &dumb_scalar);
1036-
PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, dumb_point, dumb_point, dumb_scalar, 0, 0))
1034+
PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, dumb_point, dumb_point, dumb_scalar, 0, 0, false, false))
10371035
}
10381036
}
10391037
}

0 commit comments

Comments
 (0)