Skip to content

Commit e6a3fe3

Browse files
Ensure build_commitment_tx and next_local/remote_tx_fee agree on the fee
1 parent 87fa298 commit e6a3fe3

File tree

1 file changed

+114
-4
lines changed

1 file changed

+114
-4
lines changed

lightning/src/ln/channel.rs

Lines changed: 114 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+
num_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 num_htlcs = num_htlcs;
1778+
let mut fee = res;
1779+
if fee_spike_buffer_htlc.is_some() {
1780+
num_htlcs -= 1;
1781+
fee = self.commit_tx_fee_msat(num_htlcs);
1782+
}
1783+
let commitment_tx_info = CommitmentTxInfoCached {
1784+
fee,
1785+
num_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,35 @@ 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 num_htlcs = num_htlcs;
1847+
let mut fee = res;
1848+
if fee_spike_buffer_htlc.is_some() {
1849+
num_htlcs -= 1;
1850+
fee = self.commit_tx_fee_msat(num_htlcs);
1851+
}
1852+
let commitment_tx_info = CommitmentTxInfoCached {
1853+
fee,
1854+
num_htlcs,
1855+
next_holder_htlc_id: if locally_offered { self.next_holder_htlc_id + 1 } else {
1856+
self.next_holder_htlc_id
1857+
},
1858+
next_counterparty_htlc_id: if locally_offered { self.next_counterparty_htlc_id } else {
1859+
self.next_counterparty_htlc_id + 1
1860+
},
1861+
feerate: self.feerate_per_kw,
1862+
};
1863+
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info);
1864+
}
1865+
res
17891866
}
17901867

17911868
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 +2096,27 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
20192096
(commitment_tx.1, htlcs_cloned, commitment_tx.0, commitment_txid)
20202097
};
20212098

2099+
let total_fee = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
20222100
//If channel fee was updated by funder confirm funder can afford the new fee rate when applied to the current local commitment transaction
20232101
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-
20262102
let counterparty_reserve_we_require = Channel::<ChanSigner>::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis);
20272103
if self.channel_value_satoshis - self.value_to_self_msat / 1000 < total_fee + counterparty_reserve_we_require {
20282104
return Err((None, ChannelError::Close("Funding remote cannot afford proposed new fee".to_owned())));
20292105
}
20302106
}
2107+
#[cfg(any(test, feature = "fuzztarget"))]
2108+
{
2109+
if self.is_outbound() {
2110+
let projected_commit_tx_info = self.next_local_commitment_tx_fee_info_cached.lock().unwrap().take();
2111+
if let Some(info) = projected_commit_tx_info {
2112+
if info.num_htlcs == num_htlcs && info.next_holder_htlc_id == self.next_holder_htlc_id
2113+
&& info.next_counterparty_htlc_id == self.next_counterparty_htlc_id
2114+
&& info.feerate == self.feerate_per_kw {
2115+
assert_eq!(total_fee, info.fee / 1000);
2116+
}
2117+
}
2118+
}
2119+
}
20312120

20322121
if msg.htlc_signatures.len() != num_htlcs {
20332122
return Err((None, ChannelError::Close(format!("Got wrong number of HTLC signatures ({}) from remote. It must be {}", msg.htlc_signatures.len(), num_htlcs))));
@@ -3921,6 +4010,22 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
39214010
let counterparty_commitment_txid = counterparty_commitment_tx.0.trust().txid();
39224011
let (signature, htlc_signatures);
39234012

4013+
#[cfg(any(test, feature = "fuzztarget"))]
4014+
{
4015+
if !self.is_outbound() {
4016+
let projected_commit_tx_info = self.next_remote_commitment_tx_fee_info_cached.lock().unwrap().take();
4017+
if let Some(info) = projected_commit_tx_info {
4018+
if info.num_htlcs == counterparty_commitment_tx.1
4019+
&& info.next_holder_htlc_id == self.next_holder_htlc_id
4020+
&& info.next_counterparty_htlc_id == self.next_counterparty_htlc_id
4021+
&& info.feerate == self.feerate_per_kw {
4022+
let actual_fee = self.commit_tx_fee_msat(counterparty_commitment_tx.1);
4023+
assert_eq!(actual_fee, info.fee);
4024+
}
4025+
}
4026+
}
4027+
}
4028+
39244029
{
39254030
let mut htlcs = Vec::with_capacity(counterparty_commitment_tx.2.len());
39264031
for &(ref htlc, _) in counterparty_commitment_tx.2.iter() {
@@ -4511,6 +4616,11 @@ impl<'a, ChanSigner: ChannelKeys, K: Deref> ReadableArgs<&'a K> for Channel<Chan
45114616
commitment_secrets,
45124617

45134618
network_sync: UpdateStatus::Fresh,
4619+
4620+
#[cfg(any(test, feature = "fuzztarget"))]
4621+
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
4622+
#[cfg(any(test, feature = "fuzztarget"))]
4623+
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
45144624
})
45154625
}
45164626
}

0 commit comments

Comments
 (0)