Skip to content

Commit ef16642

Browse files
author
Antoine Riard
committed
Add pruning of preimages no longer needed + tests
1 parent d8474c9 commit ef16642

File tree

2 files changed

+206
-9
lines changed

2 files changed

+206
-9
lines changed

src/ln/channel.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,7 @@ impl Channel {
11621162

11631163
// Now that we're past error-generating stuff, update our local state:
11641164

1165-
self.channel_monitor.provide_latest_remote_commitment_tx_info(&remote_initial_commitment_tx, Vec::new());
1165+
self.channel_monitor.provide_latest_remote_commitment_tx_info(&remote_initial_commitment_tx, Vec::new(), self.cur_remote_commitment_transaction_number);
11661166
self.channel_state = ChannelState::FundingSent as u32;
11671167
let funding_txo = self.channel_monitor.get_funding_txo().unwrap();
11681168
self.channel_id = funding_txo.0.into_be() ^ Uint256::from_u64(funding_txo.1 as u64).unwrap(); //TODO: or le?
@@ -2067,7 +2067,7 @@ impl Channel {
20672067
let temporary_channel_id = self.channel_id;
20682068

20692069
// Now that we're past error-generating stuff, update our local state:
2070-
self.channel_monitor.provide_latest_remote_commitment_tx_info(&commitment_tx, Vec::new());
2070+
self.channel_monitor.provide_latest_remote_commitment_tx_info(&commitment_tx, Vec::new(), self.cur_remote_commitment_transaction_number);
20712071
self.channel_state = ChannelState::FundingCreated as u32;
20722072
let funding_txo = self.channel_monitor.get_funding_txo().unwrap();
20732073
self.channel_id = funding_txo.0.into_be() ^ Uint256::from_u64(funding_txo.1 as u64).unwrap(); //TODO: or le?
@@ -2241,7 +2241,7 @@ impl Channel {
22412241
}
22422242

22432243
// Update state now that we've passed all the can-fail calls...
2244-
self.channel_monitor.provide_latest_remote_commitment_tx_info(&remote_commitment_tx.0, remote_commitment_tx.1);
2244+
self.channel_monitor.provide_latest_remote_commitment_tx_info(&remote_commitment_tx.0, remote_commitment_tx.1, self.cur_remote_commitment_transaction_number);
22452245
self.channel_state |= ChannelState::AwaitingRemoteRevoke as u32;
22462246

22472247
Ok((msgs::CommitmentSigned {

src/ln/channelmonitor.rs

Lines changed: 203 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ pub struct ChannelMonitor {
155155
old_secrets: [([u8; 32], u64); 49],
156156
remote_claimable_outpoints: HashMap<Sha256dHash, Vec<HTLCOutputInCommitment>>,
157157
remote_htlc_outputs_on_chain: Mutex<HashMap<Sha256dHash, u64>>,
158+
//hash to commitment number mapping use to determine the state of transaction owning it
159+
// (revoked/non-revoked) and so lightnen pruning
160+
remote_hash_commitment_number: HashMap<[u8; 32], u64>,
158161

159162
// We store two local commitment transactions to avoid any race conditions where we may update
160163
// some monitors (potentially on watchtowers) but then fail to update others, resulting in the
@@ -185,6 +188,7 @@ impl Clone for ChannelMonitor {
185188
old_secrets: self.old_secrets.clone(),
186189
remote_claimable_outpoints: self.remote_claimable_outpoints.clone(),
187190
remote_htlc_outputs_on_chain: Mutex::new((*self.remote_htlc_outputs_on_chain.lock().unwrap()).clone()),
191+
remote_hash_commitment_number: self.remote_hash_commitment_number.clone(),
188192

189193
prev_local_signed_commitment_tx: self.prev_local_signed_commitment_tx.clone(),
190194
current_local_signed_commitment_tx: self.current_local_signed_commitment_tx.clone(),
@@ -217,6 +221,7 @@ impl ChannelMonitor {
217221
old_secrets: [([0; 32], 1 << 48); 49],
218222
remote_claimable_outpoints: HashMap::new(),
219223
remote_htlc_outputs_on_chain: Mutex::new(HashMap::new()),
224+
remote_hash_commitment_number: HashMap::new(),
220225

221226
prev_local_signed_commitment_tx: None,
222227
current_local_signed_commitment_tx: None,
@@ -255,7 +260,9 @@ impl ChannelMonitor {
255260

256261
/// Inserts a revocation secret into this channel monitor. Also optionally tracks the next
257262
/// revocation point which may be required to claim HTLC outputs which we know the preimage of
258-
/// in case the remote end force-closes using their latest state.
263+
/// in case the remote end force-closes using their latest state. Prunes old preimages if neither
264+
/// needed by local commitment transactions HTCLs nor by remote ones. Unless we haven't already seen remote
265+
/// commitment transaction's secret, they are de facto pruned (we can use revocation key).
259266
pub fn provide_secret(&mut self, idx: u64, secret: [u8; 32], their_next_revocation_point: Option<(u64, PublicKey)>) -> Result<(), HandleError> {
260267
let pos = ChannelMonitor::place_secret(idx);
261268
for i in 0..pos {
@@ -286,20 +293,46 @@ impl ChannelMonitor {
286293
}
287294
}
288295
}
289-
// TODO: Prune payment_preimages no longer needed by the revocation (just have to check
290-
// that non-revoked remote commitment tx(n) do not need it, and our latest local commitment
291-
// tx does not need it.
296+
297+
let mut waste_hash_state : Vec<[u8;32]> = Vec::new();
298+
{
299+
let local_signed_commitment_tx = &self.current_local_signed_commitment_tx;
300+
let remote_hash_commitment_number = &self.remote_hash_commitment_number;
301+
let min_idx = self.get_min_seen_secret();
302+
self.payment_preimages.retain(|&k, _| {
303+
for &(ref htlc, _s1, _s2) in &local_signed_commitment_tx.as_ref().expect("Channel needs at least an initial commitment tx !").htlc_outputs {
304+
if k == htlc.payment_hash {
305+
return true
306+
}
307+
}
308+
if let Some(cn) = remote_hash_commitment_number.get(&k) {
309+
if *cn < min_idx {
310+
return true
311+
}
312+
}
313+
waste_hash_state.push(k);
314+
false
315+
});
316+
}
317+
for h in waste_hash_state {
318+
self.remote_hash_commitment_number.remove(&h);
319+
}
320+
292321
Ok(())
293322
}
294323

295324
/// Informs this monitor of the latest remote (ie non-broadcastable) commitment transaction.
296325
/// The monitor watches for it to be broadcasted and then uses the HTLC information (and
297326
/// possibly future revocation/preimage information) to claim outputs where possible.
298-
pub fn provide_latest_remote_commitment_tx_info(&mut self, unsigned_commitment_tx: &Transaction, htlc_outputs: Vec<HTLCOutputInCommitment>) {
327+
/// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers.
328+
pub fn provide_latest_remote_commitment_tx_info(&mut self, unsigned_commitment_tx: &Transaction, htlc_outputs: Vec<HTLCOutputInCommitment>, commitment_number: u64) {
299329
// TODO: Encrypt the htlc_outputs data with the single-hash of the commitment transaction
300330
// so that a remote monitor doesn't learn anything unless there is a malicious close.
301331
// (only maybe, sadly we cant do the same for local info, as we need to be aware of
302332
// timeouts)
333+
for htlc in &htlc_outputs {
334+
self.remote_hash_commitment_number.insert(htlc.payment_hash, commitment_number);
335+
}
303336
self.remote_claimable_outpoints.insert(unsigned_commitment_tx.txid(), htlc_outputs);
304337
}
305338

@@ -796,9 +829,15 @@ impl ChannelMonitor {
796829
mod tests {
797830
use bitcoin::util::misc::hex_bytes;
798831
use bitcoin::blockdata::script::Script;
832+
use bitcoin::util::hash::{Hash160,Sha256dHash};
833+
use bitcoin::blockdata::transaction::Transaction;
799834
use ln::channelmonitor::ChannelMonitor;
835+
use ln::channelmonitor::LocalSignedTx;
836+
use ln::chan_utils::HTLCOutputInCommitment;
800837
use secp256k1::key::{SecretKey,PublicKey};
801-
use secp256k1::Secp256k1;
838+
use secp256k1::{Secp256k1, Signature};
839+
use rand::{thread_rng,Rng};
840+
use std::collections::HashMap;
802841

803842
#[test]
804843
fn test_per_commitment_storage() {
@@ -1154,5 +1193,163 @@ mod tests {
11541193
}
11551194
}
11561195

1196+
macro_rules! gen_local_tx {
1197+
($hex : expr, $monitor : expr, $htlcs : expr, $rng : expr, $preimage : expr, $hash : expr) => {
1198+
{
1199+
1200+
let mut htlcs = Vec::new();
1201+
1202+
for _i in 0..$htlcs {
1203+
$rng.fill_bytes(&mut $preimage);
1204+
$hash[0..20].clone_from_slice(&Hash160::from_data(&$preimage)[0..20]);
1205+
$monitor.provide_payment_preimage(&$hash, &$preimage);
1206+
htlcs.push((HTLCOutputInCommitment {
1207+
offered : true,
1208+
amount_msat : 0,
1209+
cltv_expiry : 0,
1210+
payment_hash : $hash.clone(),
1211+
transaction_output_index : 0,
1212+
}, Signature::from_der(&Secp256k1::new(), $hex).unwrap(),
1213+
Signature::from_der(&Secp256k1::new(), $hex).unwrap()))
1214+
}
1215+
1216+
Some(LocalSignedTx {
1217+
txid: Sha256dHash::from_data(&[]),
1218+
tx: Transaction {
1219+
version: 0,
1220+
lock_time: 0,
1221+
input: Vec::new(),
1222+
output: Vec::new(),
1223+
},
1224+
revocation_key: PublicKey::new(),
1225+
a_htlc_key: PublicKey::new(),
1226+
b_htlc_key: PublicKey::new(),
1227+
delayed_payment_key: PublicKey::new(),
1228+
feerate_per_kw: 0,
1229+
htlc_outputs: htlcs,
1230+
})
1231+
}
1232+
}
1233+
}
1234+
1235+
macro_rules! gen_remote_outpoints {
1236+
($monitor : expr, $tx : expr, $htlcs : expr, $rng : expr, $preimage : expr, $hash: expr, $number : expr) => {
1237+
{
1238+
let mut commitment_number = $number;
1239+
1240+
for i in 0..$tx {
1241+
1242+
let tx_zero = Transaction {
1243+
version : 0,
1244+
lock_time : i,
1245+
input : Vec::new(),
1246+
output: Vec::new(),
1247+
};
1248+
1249+
let mut htlcs = Vec::new();
1250+
1251+
for _i in 0..$htlcs {
1252+
$rng.fill_bytes(&mut $preimage);
1253+
$hash[0..20].clone_from_slice(&Hash160::from_data(&$preimage)[0..20]);
1254+
$monitor.provide_payment_preimage(&$hash, &$preimage);
1255+
htlcs.push(HTLCOutputInCommitment {
1256+
offered : true,
1257+
amount_msat : 0,
1258+
cltv_expiry : 0,
1259+
payment_hash : $hash.clone(),
1260+
transaction_output_index : 0,
1261+
});
1262+
}
1263+
commitment_number -= 1;
1264+
$monitor.provide_latest_remote_commitment_tx_info(&tx_zero, htlcs, commitment_number);
1265+
}
1266+
}
1267+
}
1268+
}
1269+
1270+
#[test]
1271+
fn test_prune_preimages() {
1272+
1273+
let mut monitor: ChannelMonitor;
1274+
let mut secrets: Vec<[u8; 32]> = Vec::new();
1275+
let secp_ctx = Secp256k1::new();
1276+
let mut preimage: [u8;32] = [0;32];
1277+
let mut hash: [u8;32] = [0;32];
1278+
let mut rng = thread_rng();
1279+
1280+
{
1281+
// insert 30 random hash, 10 from local, 10 from remote, prune 30/50
1282+
monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &PublicKey::new(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), 0, Script::new());
1283+
1284+
for _i in 0..30 {
1285+
rng.fill_bytes(&mut preimage);
1286+
hash[0..20].clone_from_slice(&Hash160::from_data(&preimage)[0..20]);
1287+
monitor.provide_payment_preimage(&hash, &preimage);
1288+
}
1289+
monitor.current_local_signed_commitment_tx = gen_local_tx!(&hex_bytes("3045022100fa86fa9a36a8cd6a7bb8f06a541787d51371d067951a9461d5404de6b928782e02201c8b7c334c10aed8976a3a465be9a28abff4cb23acbf00022295b378ce1fa3cd").unwrap()[..], monitor, 10, rng, preimage, hash);
1290+
gen_remote_outpoints!(monitor, 1, 10, rng, preimage, hash, 281474976710654);
1291+
secrets.clear();
1292+
secrets.push([0; 32]);
1293+
secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex_bytes("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
1294+
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone(), None);
1295+
assert_eq!(monitor.payment_preimages.len(), 20);
1296+
}
1297+
1298+
1299+
{
1300+
// insert 30 random hash, prune 30/30
1301+
monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &PublicKey::new(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), 0, Script::new());
1302+
1303+
for _i in 0..30 {
1304+
rng.fill_bytes(&mut preimage);
1305+
hash[0..20].clone_from_slice(&Hash160::from_data(&preimage)[0..20]);
1306+
monitor.provide_payment_preimage(&hash, &preimage);
1307+
}
1308+
monitor.current_local_signed_commitment_tx = gen_local_tx!(&hex_bytes("3045022100fa86fa9a36a8cd6a7bb8f06a541787d51371d067951a9461d5404de6b928782e02201c8b7c334c10aed8976a3a465be9a28abff4cb23acbf00022295b378ce1fa3cd").unwrap()[..], monitor, 0, rng, preimage, hash);
1309+
gen_remote_outpoints!(monitor, 0, 0, rng, preimage, hash, 281474976710655);
1310+
secrets.clear();
1311+
secrets.push([0; 32]);
1312+
secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex_bytes("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
1313+
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone(), None);
1314+
assert_eq!(monitor.payment_preimages.len(), 0);
1315+
}
1316+
1317+
{
1318+
// insert 30 random hash, 25 on 5 remotes, prune 30/55
1319+
monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &PublicKey::new(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), 0, Script::new());
1320+
1321+
for _i in 0..30 {
1322+
rng.fill_bytes(&mut preimage);
1323+
hash[0..20].clone_from_slice(&Hash160::from_data(&preimage)[0..20]);
1324+
monitor.provide_payment_preimage(&hash, &preimage);
1325+
}
1326+
monitor.current_local_signed_commitment_tx = gen_local_tx!(&hex_bytes("3045022100fa86fa9a36a8cd6a7bb8f06a541787d51371d067951a9461d5404de6b928782e02201c8b7c334c10aed8976a3a465be9a28abff4cb23acbf00022295b378ce1fa3cd").unwrap()[..], monitor, 0, rng, preimage, hash);
1327+
gen_remote_outpoints!(monitor, 5, 5, rng, preimage, hash, 281474976710654);
1328+
secrets.clear();
1329+
secrets.push([0; 32]);
1330+
secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex_bytes("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
1331+
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone(), None);
1332+
assert_eq!(monitor.payment_preimages.len(), 25);
1333+
}
1334+
1335+
{
1336+
// insert 30 random hash, 25 from local, prune 30/55
1337+
monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &PublicKey::new(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), 0, Script::new());
1338+
1339+
for _i in 0..30 {
1340+
rng.fill_bytes(&mut preimage);
1341+
hash[0..20].clone_from_slice(&Hash160::from_data(&preimage)[0..20]);
1342+
monitor.provide_payment_preimage(&hash, &preimage);
1343+
}
1344+
monitor.current_local_signed_commitment_tx = gen_local_tx!(&hex_bytes("3045022100fa86fa9a36a8cd6a7bb8f06a541787d51371d067951a9461d5404de6b928782e02201c8b7c334c10aed8976a3a465be9a28abff4cb23acbf00022295b378ce1fa3cd").unwrap()[..], monitor, 25, rng, preimage, hash);
1345+
gen_remote_outpoints!(monitor, 0, 0, rng, preimage, hash, 281474976710655);
1346+
secrets.clear();
1347+
secrets.push([0; 32]);
1348+
secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex_bytes("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
1349+
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone(), None);
1350+
assert_eq!(monitor.payment_preimages.len(), 25);
1351+
}
1352+
}
1353+
11571354
// Further testing is done in the ChannelManager integration tests.
11581355
}

0 commit comments

Comments
 (0)