Skip to content

Commit ce5135f

Browse files
author
Antoine Riard
committed
Implement dynamic height timer for bump candidates txn
We must adapt our delay between two bumps of claim txn in respect to the timelock encumbering the targeted outpoint. If HTLC or revoked output is near to expire, we should try to get our claim in every block. If it's reasonably in the future, we may give us more latency to bump
1 parent 368abda commit ce5135f

File tree

1 file changed

+34
-15
lines changed

1 file changed

+34
-15
lines changed

src/ln/channelmonitor.rs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -455,8 +455,8 @@ pub struct ChannelMonitor {
455455
// Used to track outpoint in the process of being claimed by our transactions. We need to scan all transactions
456456
// for inputs spending this. If height timer (u32) is expired and claim tx hasn't reached enough confirmations
457457
// before, use TxMaterial to regenerate a new claim tx with a satoshis-per-1000-weight-units higher than last
458-
// one (u64).
459-
our_claim_txn_waiting_first_conf: HashMap<BitcoinOutPoint, (u32, TxMaterial, u64)>,
458+
// one (u64), if timelock expiration (u32) is near, decrease height timer, the in-between bumps delay.
459+
our_claim_txn_waiting_first_conf: HashMap<BitcoinOutPoint, (u32, TxMaterial, u64, u32)>,
460460

461461
// Used to track onchain events, i.e transactions parts of channels confirmed on chain, on which
462462
// we have to take actions once they reach enough confs. Key is a block height timer, i.e we enforce
@@ -619,6 +619,15 @@ impl ChannelMonitor {
619619
tx_weight
620620
}
621621

622+
fn get_height_timer(current_height: u32, timelock_expiration: u32) -> u32 {
623+
if timelock_expiration <= current_height || timelock_expiration - current_height <= 3 {
624+
return current_height + 1
625+
} else if timelock_expiration - current_height <= 15 {
626+
return current_height + 3
627+
}
628+
15
629+
}
630+
622631
#[inline]
623632
fn place_secret(idx: u64) -> u8 {
624633
for i in 0..48 {
@@ -1124,6 +1133,7 @@ impl ChannelMonitor {
11241133
}
11251134
}
11261135
writer.write_all(&byte_utils::be64_to_array(claim_tx_data.2))?;
1136+
writer.write_all(&byte_utils::be32_to_array(claim_tx_data.3))?;
11271137
}
11281138

11291139
writer.write_all(&byte_utils::be64_to_array(self.onchain_events_waiting_threshold_conf.len() as u64))?;
@@ -1278,7 +1288,7 @@ impl ChannelMonitor {
12781288
witness: Vec::new(),
12791289
});
12801290
inputs_desc.push(InputDescriptors::RevokedOutput);
1281-
inputs_info.push((None, outp.value));
1291+
inputs_info.push((None, outp.value, self.their_to_self_delay.unwrap() as u32)); // We can safely unwrap given we are past channel opening
12821292
total_value += outp.value;
12831293
} else if Some(&outp.script_pubkey) == local_payment_p2wpkh.as_ref() {
12841294
spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH {
@@ -1342,7 +1352,7 @@ impl ChannelMonitor {
13421352
if htlc.cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER {
13431353
inputs.push(input);
13441354
inputs_desc.push(if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC });
1345-
inputs_info.push((Some(idx), tx.output[transaction_output_index as usize].value));
1355+
inputs_info.push((Some(idx), tx.output[transaction_output_index as usize].value, htlc.cltv_expiry));
13461356
total_value += tx.output[transaction_output_index as usize].value;
13471357
} else {
13481358
let mut single_htlc_tx = Transaction {
@@ -1355,14 +1365,15 @@ impl ChannelMonitor {
13551365
}),
13561366
};
13571367
let predicted_weight = single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }]);
1368+
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
13581369
let mut used_feerate;
13591370
if subtract_high_prio_fee!(self, fee_estimator, single_htlc_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) {
13601371
let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx);
13611372
let (redeemscript, revocation_key) = sign_input!(sighash_parts, single_htlc_tx.input[0], Some(idx), htlc.amount_msat / 1000);
13621373
assert!(predicted_weight >= single_htlc_tx.get_weight());
13631374
match self.our_claim_txn_waiting_first_conf.entry(single_htlc_tx.input[0].previous_output.clone()) {
13641375
hash_map::Entry::Occupied(_) => {},
1365-
hash_map::Entry::Vacant(entry) => { entry.insert((height + 3, TxMaterial::Revoked { script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: true, amount: htlc.amount_msat / 1000 }, used_feerate)); }
1376+
hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::Revoked { script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: true, amount: htlc.amount_msat / 1000 }, used_feerate, htlc.cltv_expiry)); }
13661377
}
13671378
txn_to_broadcast.push(single_htlc_tx);
13681379
}
@@ -1439,9 +1450,10 @@ impl ChannelMonitor {
14391450

14401451
for (input, info) in spend_tx.input.iter_mut().zip(inputs_info.iter()) {
14411452
let (redeemscript, revocation_key) = sign_input!(sighash_parts, input, info.0, info.1);
1453+
let height_timer = Self::get_height_timer(height, info.2);
14421454
match self.our_claim_txn_waiting_first_conf.entry(input.previous_output.clone()) {
14431455
hash_map::Entry::Occupied(_) => {},
1444-
hash_map::Entry::Vacant(entry) => { entry.insert((height + 3, TxMaterial::Revoked { script: redeemscript, pubkey: if info.0.is_some() { Some(revocation_pubkey) } else { None }, key: revocation_key, is_htlc: if info.0.is_some() { true } else { false }, amount: info.1 }, used_feerate)); }
1456+
hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::Revoked { script: redeemscript, pubkey: if info.0.is_some() { Some(revocation_pubkey) } else { None }, key: revocation_key, is_htlc: if info.0.is_some() { true } else { false }, amount: info.1 }, used_feerate, info.2)); }
14451457
}
14461458
}
14471459
assert!(predicted_weight >= spend_tx.get_weight());
@@ -1605,7 +1617,7 @@ impl ChannelMonitor {
16051617
if htlc.cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER {
16061618
inputs.push(input);
16071619
inputs_desc.push(if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC });
1608-
inputs_info.push((payment_preimage, tx.output[transaction_output_index as usize].value));
1620+
inputs_info.push((payment_preimage, tx.output[transaction_output_index as usize].value, htlc.cltv_expiry));
16091621
total_value += tx.output[transaction_output_index as usize].value;
16101622
} else {
16111623
let mut single_htlc_tx = Transaction {
@@ -1618,6 +1630,7 @@ impl ChannelMonitor {
16181630
}),
16191631
};
16201632
let predicted_weight = single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC }]);
1633+
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
16211634
let mut used_feerate;
16221635
if subtract_high_prio_fee!(self, fee_estimator, single_htlc_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) {
16231636
let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx);
@@ -1629,7 +1642,7 @@ impl ChannelMonitor {
16291642
});
16301643
match self.our_claim_txn_waiting_first_conf.entry(single_htlc_tx.input[0].previous_output.clone()) {
16311644
hash_map::Entry::Occupied(_) => {},
1632-
hash_map::Entry::Vacant(entry) => { entry.insert((height + 3, TxMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000 }, used_feerate)); }
1645+
hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000 }, used_feerate, htlc.cltv_expiry)); }
16331646
}
16341647
txn_to_broadcast.push(single_htlc_tx);
16351648
}
@@ -1657,6 +1670,7 @@ impl ChannelMonitor {
16571670
}),
16581671
};
16591672
let predicted_weight = timeout_tx.get_weight() + Self::get_witnesses_weight(&[InputDescriptors::ReceivedHTLC]);
1673+
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
16601674
let mut used_feerate;
16611675
if subtract_high_prio_fee!(self, fee_estimator, timeout_tx.output[0].value, predicted_weight, tx.txid(), used_feerate) {
16621676
let sighash_parts = bip143::SighashComponents::new(&timeout_tx);
@@ -1665,7 +1679,7 @@ impl ChannelMonitor {
16651679
//TODO: track SpendableOutputDescriptor
16661680
match self.our_claim_txn_waiting_first_conf.entry(timeout_tx.input[0].previous_output.clone()) {
16671681
hash_map::Entry::Occupied(_) => {},
1668-
hash_map::Entry::Vacant(entry) => { entry.insert((height + 3, TxMaterial::RemoteHTLC { script : redeemscript, key: htlc_key, preimage: None, amount: htlc.amount_msat / 1000 }, used_feerate)); }
1682+
hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::RemoteHTLC { script : redeemscript, key: htlc_key, preimage: None, amount: htlc.amount_msat / 1000 }, used_feerate, htlc.cltv_expiry)); }
16691683
}
16701684
}
16711685
txn_to_broadcast.push(timeout_tx);
@@ -1697,9 +1711,10 @@ impl ChannelMonitor {
16971711

16981712
for (input, info) in spend_tx.input.iter_mut().zip(inputs_info.iter()) {
16991713
let (redeemscript, htlc_key) = sign_input!(sighash_parts, input, info.1, (info.0).0.to_vec());
1714+
let height_timer = Self::get_height_timer(height, info.2);
17001715
match self.our_claim_txn_waiting_first_conf.entry(input.previous_output.clone()) {
17011716
hash_map::Entry::Occupied(_) => {},
1702-
hash_map::Entry::Vacant(entry) => { entry.insert((height + 3, TxMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*(info.0)), amount: info.1}, used_feerate)); }
1717+
hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*(info.0)), amount: info.1}, used_feerate, info.2)); }
17031718
}
17041719
}
17051720
assert!(predicted_weight >= spend_tx.get_weight());
@@ -1803,15 +1818,16 @@ impl ChannelMonitor {
18031818
assert!(predicted_weight >= spend_tx.get_weight());
18041819
let outpoint = BitcoinOutPoint { txid: spend_tx.txid(), vout: 0 };
18051820
let output = spend_tx.output[0].clone();
1821+
let height_timer = Self::get_height_timer(height, self.their_to_self_delay.unwrap() as u32); // We can safely unwrap given we are past channel opening
18061822
match self.our_claim_txn_waiting_first_conf.entry(spend_tx.input[0].previous_output.clone()) {
18071823
hash_map::Entry::Occupied(_) => {},
1808-
hash_map::Entry::Vacant(entry) => { entry.insert((height + 3, TxMaterial::Revoked { script: redeemscript, pubkey: None, key: revocation_key, is_htlc: false, amount: tx.output[0].value }, used_feerate)); }
1824+
hash_map::Entry::Vacant(entry) => { entry.insert((height_timer, TxMaterial::Revoked { script: redeemscript, pubkey: None, key: revocation_key, is_htlc: false, amount: tx.output[0].value }, used_feerate, self.their_to_self_delay.unwrap() as u32)); }
18091825
}
18101826
(Some(spend_tx), Some(SpendableOutputDescriptor::StaticOutput { outpoint, output }))
18111827
} else { (None, None) }
18121828
}
18131829

1814-
fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, per_commitment_point: &Option<PublicKey>, delayed_payment_base_key: &Option<SecretKey>, height: u32) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, Vec<TxOut>, Vec<(BitcoinOutPoint, (u32, TxMaterial, u64))>) {
1830+
fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, per_commitment_point: &Option<PublicKey>, delayed_payment_base_key: &Option<SecretKey>, height: u32) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, Vec<TxOut>, Vec<(BitcoinOutPoint, (u32, TxMaterial, u64, u32))>) {
18151831
let mut res = Vec::with_capacity(local_tx.htlc_outputs.len());
18161832
let mut spendable_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
18171833
let mut watch_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
@@ -1864,7 +1880,8 @@ impl ChannelMonitor {
18641880
htlc_timeout_tx.input[0].witness.push(htlc_script.clone().into_bytes());
18651881

18661882
add_dynamic_output!(htlc_timeout_tx, 0);
1867-
pending_claims.push((htlc_timeout_tx.input[0].previous_output.clone(), (height + 3, TxMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, *our_sig), preimage: None, amount: htlc.amount_msat / 1000}, 0)));
1883+
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
1884+
pending_claims.push((htlc_timeout_tx.input[0].previous_output.clone(), (height_timer, TxMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, *our_sig), preimage: None, amount: htlc.amount_msat / 1000}, 0, htlc.cltv_expiry)));
18681885
res.push(htlc_timeout_tx);
18691886
} else {
18701887
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
@@ -1883,7 +1900,8 @@ impl ChannelMonitor {
18831900
htlc_success_tx.input[0].witness.push(htlc_script.clone().into_bytes());
18841901

18851902
add_dynamic_output!(htlc_success_tx, 0);
1886-
pending_claims.push((htlc_success_tx.input[0].previous_output.clone(), (height + 3, TxMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, *our_sig), preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000}, 0)));
1903+
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
1904+
pending_claims.push((htlc_success_tx.input[0].previous_output.clone(), (height_timer, TxMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, *our_sig), preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000}, 0, htlc.cltv_expiry)));
18871905
res.push(htlc_success_tx);
18881906
}
18891907
}
@@ -2654,7 +2672,8 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
26542672
_ => return Err(DecodeError::InvalidValue),
26552673
};
26562674
let last_fee = Readable::read(reader)?;
2657-
our_claim_txn_waiting_first_conf.insert(outpoint, (height_target, tx_material, last_fee));
2675+
let timelock_expiration = Readable::read(reader)?;
2676+
our_claim_txn_waiting_first_conf.insert(outpoint, (height_target, tx_material, last_fee, timelock_expiration));
26582677
}
26592678

26602679
let waiting_threshold_conf_len: u64 = Readable::read(reader)?;

0 commit comments

Comments
 (0)