Skip to content

Commit 00a95f1

Browse files
committed
add channel <-> channel monitor consistency check
1 parent 982b9ac commit 00a95f1

File tree

5 files changed

+304
-7
lines changed

5 files changed

+304
-7
lines changed

ci/ci-tests.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ cargo test -p lightning --verbose --color always --features dnssec
4646
cargo check -p lightning --verbose --color always --features dnssec
4747
cargo doc -p lightning --document-private-items --features dnssec
4848

49+
echo -e "\n\nChecking and testing lightning with safe_channels"
50+
cargo test -p lightning --verbose --color always --features safe_channels
51+
cargo check -p lightning --verbose --color always --features safe_channels
52+
cargo doc -p lightning --document-private-items --features safe_channels
53+
4954
echo -e "\n\nChecking and testing Block Sync Clients with features"
5055

5156
cargo test -p lightning-block-sync --verbose --color always --features rest-client

lightning/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ _externalize_tests = ["inventory", "_test_utils"]
2323
# Allow signing of local transactions that may have been revoked or will be revoked, for functional testing (e.g. justice tx handling).
2424
# This is unsafe to use in production because it may result in the counterparty publishing taking our funds.
2525
unsafe_revoked_tx_signing = []
26+
safe_channels = []
2627

2728
std = []
2829

lightning/src/chain/channelmonitor.rs

Lines changed: 166 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ use crate::ln::chan_utils::{
5050
self, ChannelTransactionParameters, CommitmentTransaction, CounterpartyCommitmentSecrets,
5151
HTLCClaim, HTLCOutputInCommitment, HolderCommitmentTransaction,
5252
};
53+
#[cfg(feature = "safe_channels")]
54+
use crate::ln::channel::read_check_data;
5355
use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
5456
use crate::ln::channel_keys::{
5557
DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey, RevocationBasepoint,
@@ -111,6 +113,10 @@ pub struct ChannelMonitorUpdate {
111113
/// Will be `None` for `ChannelMonitorUpdate`s constructed on LDK versions prior to 0.0.121 and
112114
/// always `Some` otherwise.
113115
pub channel_id: Option<ChannelId>,
116+
117+
/// The encoded channel data associated with this ChannelMonitor, if any.
118+
#[cfg(feature = "safe_channels")]
119+
pub encoded_channel: Option<Vec<u8>>,
114120
}
115121

116122
impl ChannelMonitorUpdate {
@@ -156,9 +162,16 @@ impl Writeable for ChannelMonitorUpdate {
156162
for update_step in self.updates.iter() {
157163
update_step.write(w)?;
158164
}
165+
#[cfg(not(feature = "safe_channels"))]
166+
write_tlv_fields!(w, {
167+
// 1 was previously used to store `counterparty_node_id`
168+
(3, self.channel_id, option),
169+
});
170+
#[cfg(feature = "safe_channels")]
159171
write_tlv_fields!(w, {
160172
// 1 was previously used to store `counterparty_node_id`
161173
(3, self.channel_id, option),
174+
(5, self.encoded_channel, option)
162175
});
163176
Ok(())
164177
}
@@ -176,11 +189,24 @@ impl Readable for ChannelMonitorUpdate {
176189
}
177190
}
178191
let mut channel_id = None;
192+
#[cfg(not(feature = "safe_channels"))]
179193
read_tlv_fields!(r, {
180194
// 1 was previously used to store `counterparty_node_id`
181195
(3, channel_id, option),
182196
});
183-
Ok(Self { update_id, updates, channel_id })
197+
#[cfg(feature = "safe_channels")]
198+
let mut encoded_channel = None;
199+
#[cfg(feature = "safe_channels")]
200+
read_tlv_fields!(r, {
201+
// 1 was previously used to store `counterparty_node_id`
202+
(3, channel_id, option),
203+
(5, encoded_channel, option)
204+
});
205+
Ok(Self {
206+
update_id, updates, channel_id,
207+
#[cfg(feature = "safe_channels")]
208+
encoded_channel
209+
})
184210
}
185211
}
186212

@@ -1402,6 +1428,11 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
14021428
/// make deciding whether to do so simple, here we track whether this monitor was last written
14031429
/// prior to 0.1.
14041430
written_by_0_1_or_later: bool,
1431+
1432+
/// The serialized channel state as provided via the last `ChannelMonitorUpdate` or via a call to
1433+
/// [`ChannelMonitor::update_encoded_channel`].
1434+
#[cfg(feature = "safe_channels")]
1435+
encoded_channel: Option<Vec<u8>>,
14051436
}
14061437

14071438
// Returns a `&FundingScope` for the one we are currently observing/handling commitment transactions
@@ -1521,6 +1552,12 @@ const MIN_SERIALIZATION_VERSION: u8 = 1;
15211552
pub(crate) fn write_chanmon_internal<Signer: EcdsaChannelSigner, W: Writer>(
15221553
channel_monitor: &ChannelMonitorImpl<Signer>, _is_stub: bool, writer: &mut W,
15231554
) -> Result<(), Error> {
1555+
// Check that the encoded channel (if present) is consistent with the rest of the monitor. This sets an invariant
1556+
// for the safe_channels feature.
1557+
#[cfg(feature = "safe_channels")]
1558+
if let Some(ref encoded_channel) = channel_monitor.encoded_channel {
1559+
channel_monitor.check_encoded_channel_consistency(encoded_channel);
1560+
}
15241561
write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
15251562

15261563
channel_monitor.latest_update_id.write(writer)?;
@@ -1733,6 +1770,7 @@ pub(crate) fn write_chanmon_internal<Signer: EcdsaChannelSigner, W: Writer>(
17331770
_ => channel_monitor.pending_monitor_events.clone(),
17341771
};
17351772

1773+
#[cfg(not(feature = "safe_channels"))]
17361774
write_tlv_fields!(writer, {
17371775
(1, channel_monitor.funding_spend_confirmed, option),
17381776
(3, channel_monitor.htlcs_resolved_on_chain, required_vec),
@@ -1757,6 +1795,32 @@ pub(crate) fn write_chanmon_internal<Signer: EcdsaChannelSigner, W: Writer>(
17571795
(37, channel_monitor.funding_seen_onchain, required),
17581796
});
17591797

1798+
#[cfg(feature = "safe_channels")]
1799+
write_tlv_fields!(writer, {
1800+
(1, channel_monitor.funding_spend_confirmed, option),
1801+
(3, channel_monitor.htlcs_resolved_on_chain, required_vec),
1802+
(5, pending_monitor_events, required_vec),
1803+
(7, channel_monitor.funding_spend_seen, required),
1804+
(9, channel_monitor.counterparty_node_id, required),
1805+
(11, channel_monitor.confirmed_commitment_tx_counterparty_output, option),
1806+
(13, channel_monitor.spendable_txids_confirmed, required_vec),
1807+
(15, channel_monitor.counterparty_fulfilled_htlcs, required),
1808+
(17, channel_monitor.initial_counterparty_commitment_info, option),
1809+
(19, channel_monitor.channel_id, required),
1810+
(21, channel_monitor.balances_empty_height, option),
1811+
(23, channel_monitor.holder_pays_commitment_tx_fee, option),
1812+
(25, channel_monitor.payment_preimages, required),
1813+
(27, channel_monitor.first_negotiated_funding_txo, required),
1814+
(29, channel_monitor.initial_counterparty_commitment_tx, option),
1815+
(31, channel_monitor.funding.channel_parameters, required),
1816+
(32, channel_monitor.pending_funding, optional_vec),
1817+
(33, channel_monitor.htlcs_resolved_to_user, required),
1818+
(34, channel_monitor.alternative_funding_confirmed, option),
1819+
(35, channel_monitor.is_manual_broadcast, required),
1820+
(37, channel_monitor.funding_seen_onchain, required),
1821+
(39, channel_monitor.encoded_channel, option),
1822+
});
1823+
17601824
Ok(())
17611825
}
17621826

@@ -1994,6 +2058,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
19942058
alternative_funding_confirmed: None,
19952059

19962060
written_by_0_1_or_later: true,
2061+
#[cfg(feature = "safe_channels")]
2062+
encoded_channel: None,
19972063
})
19982064
}
19992065

@@ -2114,6 +2180,19 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
21142180
inner.update_monitor(updates, broadcaster, fee_estimator, &logger)
21152181
}
21162182

2183+
/// Gets the encoded channel data, if any, associated with this ChannelMonitor.
2184+
#[cfg(feature = "safe_channels")]
2185+
pub fn get_encoded_channel(&self) -> Option<Vec<u8>> {
2186+
self.inner.lock().unwrap().encoded_channel.clone()
2187+
}
2188+
2189+
/// Updates the encoded channel data associated with this ChannelMonitor. To clear the encoded channel data (for
2190+
/// example after shut down of a channel), pass an empty vector.
2191+
#[cfg(feature = "safe_channels")]
2192+
pub fn update_encoded_channel(&self, encoded: Vec<u8>) {
2193+
self.inner.lock().unwrap().update_encoded_channel(encoded);
2194+
}
2195+
21172196
/// Gets the update_id from the latest ChannelMonitorUpdate which was applied to this
21182197
/// ChannelMonitor.
21192198
///
@@ -2719,6 +2798,55 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
27192798
}
27202799

27212800
impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
2801+
#[cfg(feature = "safe_channels")]
2802+
fn check_encoded_channel_consistency(&self, encoded: &[u8]) {
2803+
let encoded_channel_reader = &mut &encoded[..];
2804+
let check_res = read_check_data(encoded_channel_reader);
2805+
if let Ok(check_data) = check_res {
2806+
debug_assert!(
2807+
check_data.cur_holder_commitment_transaction_number
2808+
<= self.get_cur_holder_commitment_number(),
2809+
"cur_holder_commitment_transaction_number - channel: {} vs monitor: {}",
2810+
check_data.cur_holder_commitment_transaction_number,
2811+
self.get_cur_holder_commitment_number()
2812+
);
2813+
debug_assert!(
2814+
check_data.revoked_counterparty_commitment_transaction_number
2815+
<= self.get_min_seen_secret(),
2816+
"revoked_counterparty_commitment_transaction_number - channel: {} vs monitor: {}",
2817+
check_data.revoked_counterparty_commitment_transaction_number,
2818+
self.get_min_seen_secret()
2819+
);
2820+
debug_assert!(
2821+
check_data.cur_counterparty_commitment_transaction_number
2822+
<= self.get_cur_counterparty_commitment_number(),
2823+
"cur_counterparty_commitment_transaction_number - channel: {} vs monitor: {}",
2824+
check_data.cur_counterparty_commitment_transaction_number,
2825+
self.get_cur_counterparty_commitment_number()
2826+
);
2827+
debug_assert!(
2828+
check_data.latest_monitor_update_id >= self.get_latest_update_id(),
2829+
"latest_monitor_update_id - channel: {} vs monitor: {}",
2830+
check_data.latest_monitor_update_id,
2831+
self.get_latest_update_id()
2832+
);
2833+
} else {
2834+
debug_assert!(false, "Failed to read check data from encoded channel");
2835+
}
2836+
}
2837+
2838+
#[cfg(feature = "safe_channels")]
2839+
fn update_encoded_channel(&mut self, encoded: Vec<u8>) {
2840+
if encoded.len() > 0 {
2841+
// Check that the encoded channel is consistent with the rest of the monitor. This sets an invariant for the
2842+
// safe_channels feature.
2843+
self.check_encoded_channel_consistency(&encoded);
2844+
self.encoded_channel = Some(encoded);
2845+
} else {
2846+
self.encoded_channel = None;
2847+
}
2848+
}
2849+
27222850
/// Helper for get_claimable_balances which does the work for an individual HTLC, generating up
27232851
/// to one `Balance` for the HTLC.
27242852
#[rustfmt::skip]
@@ -4405,6 +4533,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
44054533
}
44064534
}
44074535

4536+
// Assume that if the update contains no encoded channel, that the channel remained unchanged. We
4537+
// therefore do not update the monitor.
4538+
#[cfg(feature="safe_channels")]
4539+
if let Some(encoded_channel) = updates.encoded_channel.as_ref() {
4540+
self.update_encoded_channel(encoded_channel.clone());
4541+
}
4542+
44084543
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
44094544
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
44104545
Err(())
@@ -6644,6 +6779,33 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
66446779
let mut alternative_funding_confirmed = None;
66456780
let mut is_manual_broadcast = RequiredWrapper(None);
66466781
let mut funding_seen_onchain = RequiredWrapper(None);
6782+
#[cfg(not(feature="safe_channels"))]
6783+
read_tlv_fields!(reader, {
6784+
(1, funding_spend_confirmed, option),
6785+
(3, htlcs_resolved_on_chain, optional_vec),
6786+
(5, pending_monitor_events, optional_vec),
6787+
(7, funding_spend_seen, option),
6788+
(9, counterparty_node_id, option),
6789+
(11, confirmed_commitment_tx_counterparty_output, option),
6790+
(13, spendable_txids_confirmed, optional_vec),
6791+
(15, counterparty_fulfilled_htlcs, option),
6792+
(17, initial_counterparty_commitment_info, option),
6793+
(19, channel_id, option),
6794+
(21, balances_empty_height, option),
6795+
(23, holder_pays_commitment_tx_fee, option),
6796+
(25, payment_preimages_with_info, option),
6797+
(27, first_negotiated_funding_txo, (default_value, outpoint)),
6798+
(29, initial_counterparty_commitment_tx, option),
6799+
(31, channel_parameters, (option: ReadableArgs, None)),
6800+
(32, pending_funding, optional_vec),
6801+
(33, htlcs_resolved_to_user, option),
6802+
(34, alternative_funding_confirmed, option),
6803+
(35, is_manual_broadcast, (default_value, false)),
6804+
(37, funding_seen_onchain, (default_value, true)),
6805+
});
6806+
#[cfg(feature="safe_channels")]
6807+
let mut encoded_channel = None;
6808+
#[cfg(feature="safe_channels")]
66476809
read_tlv_fields!(reader, {
66486810
(1, funding_spend_confirmed, option),
66496811
(3, htlcs_resolved_on_chain, optional_vec),
@@ -6666,6 +6828,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
66666828
(34, alternative_funding_confirmed, option),
66676829
(35, is_manual_broadcast, (default_value, false)),
66686830
(37, funding_seen_onchain, (default_value, true)),
6831+
(39, encoded_channel, option),
66696832
});
66706833
// Note that `payment_preimages_with_info` was added (and is always written) in LDK 0.1, so
66716834
// we can use it to determine if this monitor was last written by LDK 0.1 or later.
@@ -6843,6 +7006,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
68437006
alternative_funding_confirmed,
68447007

68457008
written_by_0_1_or_later,
7009+
#[cfg(feature="safe_channels")]
7010+
encoded_channel,
68467011
});
68477012

68487013
if counterparty_node_id.is_none() {

0 commit comments

Comments
 (0)