Skip to content

Commit af2bb1a

Browse files
committed
Make ChannelMonitor round-trip tests more robust
During testsing, we check that a `ChannelMonitor` will round-trip through serialization exactly. However, we recently added a fix to change a value in `PackageTemplate` on reload to fix some issues in the field in 0.1. This can cause the round-trip tests to fail as a field is modified during read. We fix it here by simply exempting the field from the equality test in the condition where it would be updated on read. We also make the `ChannelMonitor` `PartialEq` trait implementation non-public as weird workarounds like this make clear that such a comparison is a britle API at best.
1 parent 5d0db4b commit af2bb1a

File tree

2 files changed

+61
-5
lines changed

2 files changed

+61
-5
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ use crate::util::ser::{
7777
use crate::prelude::*;
7878

7979
use crate::io::{self, Error};
80-
use crate::sync::{LockTestExt, Mutex};
80+
use crate::sync::Mutex;
8181
use core::ops::Deref;
8282
use core::{cmp, mem};
8383

@@ -1350,18 +1350,30 @@ macro_rules! holder_commitment_htlcs {
13501350
/// Transaction outputs to watch for on-chain spends.
13511351
pub type TransactionOutputs = (Txid, Vec<(u32, TxOut)>);
13521352

1353+
// Because we have weird workarounds for `ChannelMonitor` equality checks in `OnchainTxHandler` and
1354+
// `PackageTemplate` the equality implementation isn't really fit for public consumption. Instead,
1355+
// we only expose it during tests.
1356+
#[cfg(any(feature = "_test_utils", test))]
13531357
impl<Signer: EcdsaChannelSigner> PartialEq for ChannelMonitor<Signer>
13541358
where
13551359
Signer: PartialEq,
13561360
{
1357-
#[rustfmt::skip]
13581361
fn eq(&self, other: &Self) -> bool {
1362+
use crate::sync::LockTestExt;
13591363
// We need some kind of total lockorder. Absent a better idea, we sort by position in
13601364
// memory and take locks in that order (assuming that we can't move within memory while a
13611365
// lock is held).
13621366
let ord = ((self as *const _) as usize) < ((other as *const _) as usize);
1363-
let a = if ord { self.inner.unsafe_well_ordered_double_lock_self() } else { other.inner.unsafe_well_ordered_double_lock_self() };
1364-
let b = if ord { other.inner.unsafe_well_ordered_double_lock_self() } else { self.inner.unsafe_well_ordered_double_lock_self() };
1367+
let a = if ord {
1368+
self.inner.unsafe_well_ordered_double_lock_self()
1369+
} else {
1370+
other.inner.unsafe_well_ordered_double_lock_self()
1371+
};
1372+
let b = if ord {
1373+
other.inner.unsafe_well_ordered_double_lock_self()
1374+
} else {
1375+
self.inner.unsafe_well_ordered_double_lock_self()
1376+
};
13651377
a.eq(&b)
13661378
}
13671379
}

lightning/src/chain/package.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,7 @@ enum PackageMalleability {
10931093
///
10941094
/// As packages are time-sensitive, we fee-bump and rebroadcast them at scheduled intervals.
10951095
/// Failing to confirm a package translate as a loss of funds for the user.
1096-
#[derive(Clone, Debug, PartialEq, Eq)]
1096+
#[derive(Clone, Debug, Eq)]
10971097
pub struct PackageTemplate {
10981098
// List of onchain outputs and solving data to generate satisfying witnesses.
10991099
inputs: Vec<(BitcoinOutPoint, PackageSolvingData)>,
@@ -1122,6 +1122,50 @@ pub struct PackageTemplate {
11221122
height_timer: u32,
11231123
}
11241124

1125+
impl PartialEq for PackageTemplate {
1126+
fn eq(&self, o: &Self) -> bool {
1127+
if self.inputs != o.inputs
1128+
|| self.malleability != o.malleability
1129+
|| self.feerate_previous != o.feerate_previous
1130+
|| self.height_timer != o.height_timer
1131+
{
1132+
return false;
1133+
}
1134+
#[cfg(test)]
1135+
{
1136+
// In some cases we may reset `counterparty_spendable_height` to zero on reload, which
1137+
// can cause our test assertions that ChannelMonitors round-trip exactly to trip. Here
1138+
// we allow exactly the same case as we tweak in the `PackageTemplate` `Readable`
1139+
// implementation.
1140+
if self.counterparty_spendable_height == 0 {
1141+
for (_, input) in self.inputs.iter() {
1142+
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput {
1143+
htlc, ..
1144+
}) = input
1145+
{
1146+
if !htlc.offered && htlc.cltv_expiry != 0 {
1147+
return true;
1148+
}
1149+
}
1150+
}
1151+
}
1152+
if o.counterparty_spendable_height == 0 {
1153+
for (_, input) in o.inputs.iter() {
1154+
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput {
1155+
htlc, ..
1156+
}) = input
1157+
{
1158+
if !htlc.offered && htlc.cltv_expiry != 0 {
1159+
return true;
1160+
}
1161+
}
1162+
}
1163+
}
1164+
}
1165+
self.counterparty_spendable_height == o.counterparty_spendable_height
1166+
}
1167+
}
1168+
11251169
impl PackageTemplate {
11261170
#[rustfmt::skip]
11271171
pub(crate) fn can_merge_with(&self, other: &PackageTemplate, cur_height: u32) -> bool {

0 commit comments

Comments
 (0)