Skip to content

Commit d8e0319

Browse files
committed
Implement Holder HTLC claim chunking for 0FC channels
Otherwise, we could hit the max 10_000vB size limit on V3 transactions (BIP 431 rule 4). Also introduce a `max_tx_weight` parameter to `select_confirmed_utxos`. This constraint makes sure anchor and HTLC transactions in 0FC channels satisfy the `TRUC_MAX_WEIGHT` and the `TRUC_CHILD_MAX_WEIGHT` maximums. Expand the coin-selection algorithm provided for any `T: WalletSource` to satisfy this new constraint.
1 parent 19a9dbd commit d8e0319

File tree

12 files changed

+791
-176
lines changed

12 files changed

+791
-176
lines changed

lightning/src/chain/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use bitcoin::block::{Block, Header};
1313
use bitcoin::constants::genesis_block;
1414
use bitcoin::hash_types::{BlockHash, Txid};
15+
use bitcoin::hashes::sha256::Hash as Sha256;
16+
use bitcoin::hashes::{Hash, HashEngine};
1517
use bitcoin::network::Network;
1618
use bitcoin::script::{Script, ScriptBuf};
1719
use bitcoin::secp256k1::PublicKey;
@@ -21,6 +23,7 @@ use crate::chain::transaction::{OutPoint, TransactionData};
2123
use crate::impl_writeable_tlv_based;
2224
use crate::ln::types::ChannelId;
2325
use crate::sign::ecdsa::EcdsaChannelSigner;
26+
use crate::sign::HTLCDescriptor;
2427

2528
#[allow(unused_imports)]
2629
use crate::prelude::*;
@@ -442,3 +445,20 @@ where
442445
/// This is not exported to bindings users as we just use [u8; 32] directly.
443446
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
444447
pub struct ClaimId(pub [u8; 32]);
448+
449+
impl ClaimId {
450+
pub(crate) fn from_htlcs(htlcs: &[HTLCDescriptor]) -> ClaimId {
451+
let mut engine = Sha256::engine();
452+
for htlc in htlcs {
453+
engine.input(&htlc.commitment_txid.to_byte_array());
454+
engine.input(&htlc.htlc.transaction_output_index.unwrap().to_be_bytes());
455+
}
456+
ClaimId(Sha256::from_engine(engine).to_byte_array())
457+
}
458+
pub(crate) fn step_with_bytes(&self, bytes: &[u8]) -> ClaimId {
459+
let mut engine = Sha256::engine();
460+
engine.input(&self.0);
461+
engine.input(bytes);
462+
ClaimId(Sha256::from_engine(engine).to_byte_array())
463+
}
464+
}

lightning/src/chain/onchaintx.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
1515
use bitcoin::amount::Amount;
1616
use bitcoin::hash_types::{BlockHash, Txid};
17-
use bitcoin::hashes::sha256::Hash as Sha256;
18-
use bitcoin::hashes::{Hash, HashEngine};
17+
use bitcoin::hashes::Hash;
1918
use bitcoin::locktime::absolute::LockTime;
2019
use bitcoin::script::{Script, ScriptBuf};
2120
use bitcoin::secp256k1;
@@ -882,12 +881,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
882881
// claim, which will always be unique per request. Once a claim ID
883882
// is generated, it is assigned and remains unchanged, even if the
884883
// underlying set of HTLCs changes.
885-
let mut engine = Sha256::engine();
886-
for htlc in htlcs {
887-
engine.input(&htlc.commitment_txid.to_byte_array());
888-
engine.input(&htlc.htlc.transaction_output_index.unwrap().to_be_bytes());
889-
}
890-
ClaimId(Sha256::from_engine(engine).to_byte_array())
884+
ClaimId::from_htlcs(htlcs)
891885
},
892886
};
893887
debug_assert!(self.pending_claim_requests.get(&claim_id).is_none());

lightning/src/events/bump_transaction/mod.rs

Lines changed: 326 additions & 152 deletions
Large diffs are not rendered by default.

lightning/src/events/bump_transaction/sync.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,14 @@ where
102102
{
103103
fn select_confirmed_utxos(
104104
&self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
105-
target_feerate_sat_per_1000_weight: u32,
105+
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
106106
) -> Result<CoinSelection, ()> {
107107
let mut fut = self.wallet.select_confirmed_utxos(
108108
claim_id,
109109
must_spend,
110110
must_pay_to,
111111
target_feerate_sat_per_1000_weight,
112+
max_tx_weight,
112113
);
113114
let mut waker = dummy_waker();
114115
let mut ctx = task::Context::from_waker(&mut waker);
@@ -140,7 +141,7 @@ pub trait CoinSelectionSourceSync {
140141
/// A synchronous version of [`CoinSelectionSource::select_confirmed_utxos`].
141142
fn select_confirmed_utxos(
142143
&self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &[TxOut],
143-
target_feerate_sat_per_1000_weight: u32,
144+
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
144145
) -> Result<CoinSelection, ()>;
145146

146147
/// A synchronous version of [`CoinSelectionSource::sign_psbt`].
@@ -169,13 +170,14 @@ where
169170
{
170171
fn select_confirmed_utxos<'a>(
171172
&'a self, claim_id: ClaimId, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
172-
target_feerate_sat_per_1000_weight: u32,
173+
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
173174
) -> AsyncResult<'a, CoinSelection> {
174175
let coins = self.0.select_confirmed_utxos(
175176
claim_id,
176177
must_spend,
177178
must_pay_to,
178179
target_feerate_sat_per_1000_weight,
180+
max_tx_weight,
179181
);
180182
Box::pin(async move { coins })
181183
}

lightning/src/events/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,15 +1604,18 @@ pub enum Event {
16041604
/// Indicates that a transaction originating from LDK needs to have its fee bumped. This event
16051605
/// requires confirmed external funds to be readily available to spend.
16061606
///
1607-
/// LDK does not currently generate this event unless the
1608-
/// [`ChannelHandshakeConfig::negotiate_anchors_zero_fee_htlc_tx`] config flag is set to true.
1607+
/// LDK does not currently generate this event unless either the
1608+
/// [`ChannelHandshakeConfig::negotiate_anchors_zero_fee_htlc_tx`] or the
1609+
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`] config flags are set to
1610+
/// true.
16091611
/// It is limited to the scope of channels with anchor outputs.
16101612
///
16111613
/// # Failure Behavior and Persistence
16121614
/// This event will eventually be replayed after failures-to-handle (i.e., the event handler
16131615
/// returning `Err(ReplayEvent ())`), but will only be regenerated as needed after restarts.
16141616
///
16151617
/// [`ChannelHandshakeConfig::negotiate_anchors_zero_fee_htlc_tx`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchors_zero_fee_htlc_tx
1618+
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
16161619
BumpTransaction(BumpTransactionEvent),
16171620
/// We received an onion message that is intended to be forwarded to a peer
16181621
/// that is currently offline. This event will only be generated if the

lightning/src/ln/chan_utils.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//! largely of interest for those implementing the traits on [`crate::sign`] by hand.
1212
1313
use bitcoin::amount::Amount;
14+
use bitcoin::constants::WITNESS_SCALE_FACTOR;
1415
use bitcoin::opcodes;
1516
use bitcoin::script::{Builder, Script, ScriptBuf};
1617
use bitcoin::sighash;
@@ -89,12 +90,18 @@ pub const ANCHOR_INPUT_WITNESS_WEIGHT: u64 = 114;
8990
#[cfg(not(feature = "grind_signatures"))]
9091
pub const ANCHOR_INPUT_WITNESS_WEIGHT: u64 = 115;
9192

92-
/// The weight of a P2A anchor witness.
93-
pub const P2A_ANCHOR_INPUT_WITNESS_WEIGHT: u64 = 1;
93+
/// The weight of an empty witness; used to spend a P2A output.
94+
pub const EMPTY_WITNESS_WEIGHT: u64 = 1;
9495

9596
/// The maximum value of a P2A anchor.
9697
pub const P2A_MAX_VALUE: u64 = 240;
9798

99+
/// The maximum weight of a TRUC transaction, see BIP431.
100+
pub const TRUC_MAX_WEIGHT: u64 = 10_000 * WITNESS_SCALE_FACTOR as u64;
101+
102+
/// The maximum weight of a TRUC transaction with an unconfirmed TRUC ancestor, see BIP431.
103+
pub const TRUC_CHILD_MAX_WEIGHT: u64 = 1000 * WITNESS_SCALE_FACTOR as u64;
104+
98105
/// The upper bound weight of an HTLC timeout input from a commitment transaction with keyed anchor outputs.
99106
pub const HTLC_TIMEOUT_INPUT_KEYED_ANCHOR_WITNESS_WEIGHT: u64 = 288;
100107
/// The upper bound weight of an HTLC timeout input from a commitment transaction with a p2a anchor output.
@@ -125,6 +132,15 @@ pub const FUNDING_TRANSACTION_WITNESS_WEIGHT: u64 = 1 + // number_of_witness_ele
125132
1 + // witness_script_length
126133
MULTISIG_SCRIPT_SIZE;
127134

135+
pub(crate) const BASE_TX_SIZE: u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */;
136+
pub(crate) const SEGWIT_MARKER_FLAG_WEIGHT: u64 = 2;
137+
pub(crate) const EMPTY_SCRIPT_SIG_WEIGHT: u64 =
138+
1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64;
139+
pub(crate) const BASE_INPUT_SIZE: u64 = 32 /* txid */ + 4 /* vout */ + 4 /* sequence */;
140+
pub(crate) const BASE_INPUT_WEIGHT: u64 = BASE_INPUT_SIZE * WITNESS_SCALE_FACTOR as u64;
141+
pub(crate) const P2WSH_TXOUT_WEIGHT: u64 =
142+
(8 /* value */ + 1 /* var_int */ + 34/* p2wsh spk */) * WITNESS_SCALE_FACTOR as u64;
143+
128144
/// Gets the weight for an HTLC-Success transaction.
129145
#[inline]
130146
#[rustfmt::skip]
@@ -134,6 +150,18 @@ pub fn htlc_success_tx_weight(channel_type_features: &ChannelTypeFeatures) -> u6
134150
if channel_type_features.supports_anchors_zero_fee_htlc_tx() { HTLC_SUCCESS_ANCHOR_TX_WEIGHT } else { HTLC_SUCCESS_TX_WEIGHT }
135151
}
136152

153+
/// Gets the weight of a single input-output pair in externally funded HTLC-success transactions
154+
pub fn aggregated_htlc_success_input_output_pair_weight(
155+
channel_type_features: &ChannelTypeFeatures,
156+
) -> u64 {
157+
let satisfaction_weight = if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
158+
EMPTY_SCRIPT_SIG_WEIGHT + HTLC_SUCCESS_INPUT_KEYED_ANCHOR_WITNESS_WEIGHT
159+
} else {
160+
EMPTY_SCRIPT_SIG_WEIGHT + HTLC_SUCCESS_INPUT_P2A_ANCHOR_WITNESS_WEIGHT
161+
};
162+
BASE_INPUT_WEIGHT + P2WSH_TXOUT_WEIGHT + satisfaction_weight
163+
}
164+
137165
/// Gets the weight for an HTLC-Timeout transaction.
138166
#[inline]
139167
#[rustfmt::skip]
@@ -143,6 +171,18 @@ pub fn htlc_timeout_tx_weight(channel_type_features: &ChannelTypeFeatures) -> u6
143171
if channel_type_features.supports_anchors_zero_fee_htlc_tx() { HTLC_TIMEOUT_ANCHOR_TX_WEIGHT } else { HTLC_TIMEOUT_TX_WEIGHT }
144172
}
145173

174+
/// Gets the weight of a single input-output pair in externally funded HTLC-timeout transactions
175+
pub fn aggregated_htlc_timeout_input_output_pair_weight(
176+
channel_type_features: &ChannelTypeFeatures,
177+
) -> u64 {
178+
let satisfaction_weight = if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
179+
EMPTY_SCRIPT_SIG_WEIGHT + HTLC_TIMEOUT_INPUT_KEYED_ANCHOR_WITNESS_WEIGHT
180+
} else {
181+
EMPTY_SCRIPT_SIG_WEIGHT + HTLC_TIMEOUT_INPUT_P2A_ANCHOR_WITNESS_WEIGHT
182+
};
183+
BASE_INPUT_WEIGHT + P2WSH_TXOUT_WEIGHT + satisfaction_weight
184+
}
185+
146186
/// Describes the type of HTLC claim as determined by analyzing the witness.
147187
#[derive(PartialEq, Eq)]
148188
pub enum HTLCClaim {

lightning/src/ln/channel.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,14 @@ use crate::chain::channelmonitor::{
3636
};
3737
use crate::chain::transaction::{OutPoint, TransactionData};
3838
use crate::chain::BestBlock;
39-
use crate::events::bump_transaction::{BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT};
4039
use crate::events::{ClosureReason, FundingInfo};
4140
use crate::ln::chan_utils;
4241
use crate::ln::chan_utils::{
4342
get_commitment_transaction_number_obscure_factor, max_htlcs, second_stage_tx_fees_sat,
4443
selected_commitment_sat_per_1000_weight, ChannelPublicKeys, ChannelTransactionParameters,
4544
ClosingTransaction, CommitmentTransaction, CounterpartyChannelTransactionParameters,
4645
CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HolderCommitmentTransaction,
47-
FUNDING_TRANSACTION_WITNESS_WEIGHT,
46+
BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT, FUNDING_TRANSACTION_WITNESS_WEIGHT,
4847
};
4948
use crate::ln::channel_state::{
5049
ChannelShutdownState, CounterpartyForwardingInfo, InboundHTLCDetails, InboundHTLCStateDetails,

lightning/src/ln/functional_test_utils.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use crate::events::{
2121
ClaimedHTLC, ClosureReason, Event, HTLCHandlingFailureType, PaidBolt12Invoice, PathFailure,
2222
PaymentFailureReason, PaymentPurpose,
2323
};
24-
use crate::ln::chan_utils::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC};
24+
use crate::ln::chan_utils::{
25+
commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, TRUC_MAX_WEIGHT,
26+
};
2527
use crate::ln::channelmanager::{
2628
AChannelManager, ChainParameters, ChannelManager, ChannelManagerReadArgs, PaymentId,
2729
RAACommitmentOrder, RecipientOnionFields, MIN_CLTV_EXPIRY_DELTA,
@@ -58,6 +60,7 @@ use bitcoin::hashes::sha256::Hash as Sha256;
5860
use bitcoin::hashes::Hash as _;
5961
use bitcoin::locktime::absolute::{LockTime, LOCK_TIME_THRESHOLD};
6062
use bitcoin::network::Network;
63+
use bitcoin::policy::MAX_STANDARD_TX_WEIGHT;
6164
use bitcoin::pow::CompactTarget;
6265
use bitcoin::script::ScriptBuf;
6366
use bitcoin::secp256k1::{PublicKey, SecretKey};
@@ -421,6 +424,30 @@ pub fn provide_anchor_reserves<'a, 'b, 'c>(nodes: &[Node<'a, 'b, 'c>]) -> Transa
421424
tx
422425
}
423426

427+
pub fn provide_anchor_utxo_reserves<'a, 'b, 'c>(
428+
nodes: &[Node<'a, 'b, 'c>], utxos: usize, amount: Amount,
429+
) -> Transaction {
430+
let mut output = Vec::with_capacity(nodes.len());
431+
for node in nodes {
432+
let script_pubkey = node.wallet_source.get_change_script().unwrap();
433+
for _ in 0..utxos {
434+
output.push(TxOut { value: amount, script_pubkey: script_pubkey.clone() });
435+
}
436+
}
437+
let tx = Transaction {
438+
version: TxVersion::TWO,
439+
lock_time: LockTime::from_height(nodes[0].best_block_info().1).unwrap(),
440+
input: vec![TxIn { ..Default::default() }],
441+
output,
442+
};
443+
let height = nodes[0].best_block_info().1 + 1;
444+
let block = create_dummy_block(nodes[0].best_block_hash(), height, vec![tx.clone()]);
445+
for node in nodes {
446+
do_connect_block_with_consistency_checks(node, block.clone(), false);
447+
}
448+
tx
449+
}
450+
424451
pub fn disconnect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, count: u32) {
425452
call_claimable_balances(node);
426453
eprintln!(
@@ -1941,6 +1968,11 @@ pub fn update_nodes_with_chan_announce<'a, 'b, 'c, 'd>(
19411968
pub fn do_check_spends<F: Fn(&bitcoin::transaction::OutPoint) -> Option<TxOut>>(
19421969
tx: &Transaction, get_output: F,
19431970
) {
1971+
if tx.version == TxVersion::non_standard(3) {
1972+
assert!(tx.weight().to_wu() <= TRUC_MAX_WEIGHT);
1973+
} else {
1974+
assert!(tx.weight().to_wu() <= MAX_STANDARD_TX_WEIGHT as u64);
1975+
}
19441976
let mut p2a_output_below_dust = false;
19451977
let mut has_p2a_output = false;
19461978
for outp in tx.output.iter() {

lightning/src/ln/funding.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
use bitcoin::{Amount, ScriptBuf, SignedAmount, TxOut};
1313
use bitcoin::{Script, Sequence, Transaction, Weight};
1414

15-
use crate::events::bump_transaction::{Utxo, EMPTY_SCRIPT_SIG_WEIGHT};
15+
use crate::events::bump_transaction::Utxo;
16+
use crate::ln::chan_utils::EMPTY_SCRIPT_SIG_WEIGHT;
1617
use crate::prelude::Vec;
1718
use crate::sign::{P2TR_KEY_PATH_WITNESS_WEIGHT, P2WPKH_WITNESS_WEIGHT};
1819

lightning/src/ln/interactivetxs.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ use bitcoin::{
2626
};
2727

2828
use crate::chain::chaininterface::fee_for_weight;
29-
use crate::events::bump_transaction::{BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT};
30-
use crate::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
29+
use crate::ln::chan_utils::{
30+
BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT, FUNDING_TRANSACTION_WITNESS_WEIGHT,
31+
};
3132
use crate::ln::channel::{FundingNegotiationContext, TOTAL_BITCOIN_SUPPLY_SATOSHIS};
3233
use crate::ln::funding::FundingTxInput;
3334
use crate::ln::msgs;

0 commit comments

Comments
 (0)