Skip to content

Commit 3b6000d

Browse files
Ensure build_commitment_tx and next_local/remote_tx_fee agree on the fee
1 parent e5056f2 commit 3b6000d

File tree

1 file changed

+125
-4
lines changed

1 file changed

+125
-4
lines changed

lightning/src/ln/channel.rs

Lines changed: 125 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ use std;
4242
use std::default::Default;
4343
use std::{cmp,mem,fmt};
4444
use std::ops::Deref;
45+
#[cfg(any(test, feature = "fuzztarget"))]
46+
use std::sync::Mutex;
4547
use bitcoin::hashes::hex::ToHex;
4648

4749
#[cfg(test)]
@@ -386,6 +388,24 @@ pub(super) struct Channel<ChanSigner: ChannelKeys> {
386388
commitment_secrets: CounterpartyCommitmentSecrets,
387389

388390
network_sync: UpdateStatus,
391+
392+
// We save these values so we can make sure `next_local_commit_tx_fee_msat` and
393+
// `next_remote_commit_tx_fee_msat` properly predict what the next commitment transaction fee will
394+
// be, by comparing the cached values to the fee of the tranaction generated by
395+
// `build_commitment_transaction`.
396+
#[cfg(any(test, feature = "fuzztarget"))]
397+
next_local_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
398+
#[cfg(any(test, feature = "fuzztarget"))]
399+
next_remote_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
400+
}
401+
402+
#[cfg(any(test, feature = "fuzztarget"))]
403+
struct CommitmentTxInfoCached {
404+
fee: u64,
405+
total_pending_htlcs: usize,
406+
next_holder_htlc_id: u64,
407+
next_counterparty_htlc_id: u64,
408+
feerate: u32,
389409
}
390410

391411
pub const OUR_MAX_HTLCS: u16 = 50; //TODO
@@ -557,6 +577,11 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
557577
commitment_secrets: CounterpartyCommitmentSecrets::new(),
558578

559579
network_sync: UpdateStatus::Fresh,
580+
581+
#[cfg(any(test, feature = "fuzztarget"))]
582+
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
583+
#[cfg(any(test, feature = "fuzztarget"))]
584+
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
560585
})
561586
}
562587

@@ -790,6 +815,11 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
790815
commitment_secrets: CounterpartyCommitmentSecrets::new(),
791816

792817
network_sync: UpdateStatus::Fresh,
818+
819+
#[cfg(any(test, feature = "fuzztarget"))]
820+
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
821+
#[cfg(any(test, feature = "fuzztarget"))]
822+
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
793823
};
794824

795825
Ok(chan)
@@ -1740,7 +1770,30 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17401770
}
17411771
}
17421772

1743-
self.commit_tx_fee_msat(included_htlcs + addl_htlcs)
1773+
let num_htlcs = included_htlcs + addl_htlcs;
1774+
let res = self.commit_tx_fee_msat(num_htlcs);
1775+
#[cfg(any(test, feature = "fuzztarget"))]
1776+
{
1777+
let mut fee = res;
1778+
if fee_spike_buffer_htlc.is_some() {
1779+
fee = self.commit_tx_fee_msat(num_htlcs - 1);
1780+
}
1781+
let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len()
1782+
+ self.holding_cell_htlc_updates.len();
1783+
let commitment_tx_info = CommitmentTxInfoCached {
1784+
fee,
1785+
total_pending_htlcs,
1786+
next_holder_htlc_id: if locally_offered { self.next_holder_htlc_id + 1 } else {
1787+
self.next_holder_htlc_id
1788+
},
1789+
next_counterparty_htlc_id: if locally_offered { self.next_counterparty_htlc_id } else {
1790+
self.next_counterparty_htlc_id + 1
1791+
},
1792+
feerate: self.feerate_per_kw,
1793+
};
1794+
*self.next_local_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
1795+
}
1796+
res
17441797
}
17451798

17461799
// Get the commitment tx fee for the remote's next commitment transaction based on the number of
@@ -1781,11 +1834,34 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17811834
match htlc.state {
17821835
OutboundHTLCState::Committed => included_htlcs += 1,
17831836
OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1,
1837+
OutboundHTLCState::LocalAnnounced { .. } => included_htlcs += 1,
17841838
_ => {},
17851839
}
17861840
}
17871841

1788-
self.commit_tx_fee_msat(included_htlcs + addl_htlcs)
1842+
let num_htlcs = included_htlcs + addl_htlcs;
1843+
let res = self.commit_tx_fee_msat(num_htlcs);
1844+
#[cfg(any(test, feature = "fuzztarget"))]
1845+
{
1846+
let mut fee = res;
1847+
if fee_spike_buffer_htlc.is_some() {
1848+
fee = self.commit_tx_fee_msat(num_htlcs - 1);
1849+
}
1850+
let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len();
1851+
let commitment_tx_info = CommitmentTxInfoCached {
1852+
fee,
1853+
total_pending_htlcs,
1854+
next_holder_htlc_id: if locally_offered { self.next_holder_htlc_id + 1 } else {
1855+
self.next_holder_htlc_id
1856+
},
1857+
next_counterparty_htlc_id: if locally_offered { self.next_counterparty_htlc_id } else {
1858+
self.next_counterparty_htlc_id + 1
1859+
},
1860+
feerate: self.feerate_per_kw,
1861+
};
1862+
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
1863+
}
1864+
res
17891865
}
17901866

17911867
pub fn update_add_htlc<F, L: Deref>(&mut self, msg: &msgs::UpdateAddHTLC, mut pending_forward_status: PendingHTLCStatus, create_pending_htlc_status: F, logger: &L) -> Result<(), ChannelError>
@@ -2019,15 +2095,31 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
20192095
(commitment_tx.1, htlcs_cloned, commitment_tx.0, commitment_txid)
20202096
};
20212097

2098+
let total_fee = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
20222099
//If channel fee was updated by funder confirm funder can afford the new fee rate when applied to the current local commitment transaction
20232100
if update_fee {
2024-
let total_fee = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
2025-
20262101
let counterparty_reserve_we_require = Channel::<ChanSigner>::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis);
20272102
if self.channel_value_satoshis - self.value_to_self_msat / 1000 < total_fee + counterparty_reserve_we_require {
20282103
return Err((None, ChannelError::Close("Funding remote cannot afford proposed new fee".to_owned())));
20292104
}
20302105
}
2106+
#[cfg(any(test, feature = "fuzztarget"))]
2107+
{
2108+
if self.is_outbound() {
2109+
let projected_commit_tx_info = self.next_local_commitment_tx_fee_info_cached.lock().unwrap().take();
2110+
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
2111+
if let Some(info) = projected_commit_tx_info {
2112+
let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len()
2113+
+ self.holding_cell_htlc_updates.len();
2114+
if info.total_pending_htlcs == total_pending_htlcs
2115+
&& info.next_holder_htlc_id == self.next_holder_htlc_id
2116+
&& info.next_counterparty_htlc_id == self.next_counterparty_htlc_id
2117+
&& info.feerate == self.feerate_per_kw {
2118+
assert_eq!(total_fee, info.fee / 1000);
2119+
}
2120+
}
2121+
}
2122+
}
20312123

20322124
if msg.htlc_signatures.len() != num_htlcs {
20332125
return Err((None, ChannelError::Close(format!("Got wrong number of HTLC signatures ({}) from remote. It must be {}", msg.htlc_signatures.len(), num_htlcs))));
@@ -2293,6 +2385,12 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
22932385
return Err(ChannelError::Close("Received an unexpected revoke_and_ack".to_owned()));
22942386
}
22952387

2388+
#[cfg(any(test, feature = "fuzztarget"))]
2389+
{
2390+
*self.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None;
2391+
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
2392+
}
2393+
22962394
self.commitment_secrets.provide_secret(self.cur_counterparty_commitment_transaction_number + 1, msg.per_commitment_secret)
22972395
.map_err(|_| ChannelError::Close("Previous secrets did not match new one".to_owned()))?;
22982396
self.latest_monitor_update_id += 1;
@@ -3921,6 +4019,24 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
39214019
let counterparty_commitment_txid = counterparty_commitment_tx.0.trust().txid();
39224020
let (signature, htlc_signatures);
39234021

4022+
#[cfg(any(test, feature = "fuzztarget"))]
4023+
{
4024+
if !self.is_outbound() {
4025+
let projected_commit_tx_info = self.next_remote_commitment_tx_fee_info_cached.lock().unwrap().take();
4026+
*self.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None;
4027+
if let Some(info) = projected_commit_tx_info {
4028+
let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len();
4029+
if info.total_pending_htlcs == total_pending_htlcs
4030+
&& info.next_holder_htlc_id == self.next_holder_htlc_id
4031+
&& info.next_counterparty_htlc_id == self.next_counterparty_htlc_id
4032+
&& info.feerate == self.feerate_per_kw {
4033+
let actual_fee = self.commit_tx_fee_msat(counterparty_commitment_tx.1);
4034+
assert_eq!(actual_fee, info.fee);
4035+
}
4036+
}
4037+
}
4038+
}
4039+
39244040
{
39254041
let mut htlcs = Vec::with_capacity(counterparty_commitment_tx.2.len());
39264042
for &(ref htlc, _) in counterparty_commitment_tx.2.iter() {
@@ -4511,6 +4627,11 @@ impl<'a, ChanSigner: ChannelKeys, K: Deref> ReadableArgs<&'a K> for Channel<Chan
45114627
commitment_secrets,
45124628

45134629
network_sync: UpdateStatus::Fresh,
4630+
4631+
#[cfg(any(test, feature = "fuzztarget"))]
4632+
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
4633+
#[cfg(any(test, feature = "fuzztarget"))]
4634+
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
45144635
})
45154636
}
45164637
}

0 commit comments

Comments
 (0)