Skip to content

Commit 07a3a63

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 680da4f commit 07a3a63

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

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

1333+
// Because we have weird workarounds for `ChannelMonitor` equality checks in `OnchainTxHandler` and
1334+
// `PackageTemplate` the equality implementation isn't really fit for public consumption. Instead,
1335+
// we only expose it during tests.
1336+
#[cfg(any(feature = "_test_utils", test))]
13331337
impl<Signer: EcdsaChannelSigner> PartialEq for ChannelMonitor<Signer>
13341338
where
13351339
Signer: PartialEq,
13361340
{
1337-
#[rustfmt::skip]
13381341
fn eq(&self, other: &Self) -> bool {
1342+
use crate::sync::LockTestExt;
13391343
// We need some kind of total lockorder. Absent a better idea, we sort by position in
13401344
// memory and take locks in that order (assuming that we can't move within memory while a
13411345
// lock is held).
13421346
let ord = ((self as *const _) as usize) < ((other as *const _) as usize);
1343-
let a = if ord { self.inner.unsafe_well_ordered_double_lock_self() } else { other.inner.unsafe_well_ordered_double_lock_self() };
1344-
let b = if ord { other.inner.unsafe_well_ordered_double_lock_self() } else { self.inner.unsafe_well_ordered_double_lock_self() };
1347+
let a = if ord {
1348+
self.inner.unsafe_well_ordered_double_lock_self()
1349+
} else {
1350+
other.inner.unsafe_well_ordered_double_lock_self()
1351+
};
1352+
let b = if ord {
1353+
other.inner.unsafe_well_ordered_double_lock_self()
1354+
} else {
1355+
self.inner.unsafe_well_ordered_double_lock_self()
1356+
};
13451357
a.eq(&b)
13461358
}
13471359
}

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)