Skip to content

Commit eb97a75

Browse files
committed
Make ChannelMonitor sign local transactions (at broadcast time)
1 parent 200e898 commit eb97a75

File tree

6 files changed

+292
-170
lines changed

6 files changed

+292
-170
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ pub fn do_test(data: &[u8]) {
212212
monitor.latest_good_update.lock().unwrap().insert(outpoint, monitor_ser);
213213
}
214214
let mut monitor_refs = HashMap::new();
215-
for (outpoint, monitor) in monitors.iter() {
215+
for (outpoint, monitor) in monitors.iter_mut() {
216216
monitor_refs.insert(*outpoint, monitor);
217217
}
218218

@@ -223,7 +223,7 @@ pub fn do_test(data: &[u8]) {
223223
tx_broadcaster: broadcast.clone(),
224224
logger,
225225
default_config: config,
226-
channel_monitors: &monitor_refs,
226+
channel_monitors: &mut monitor_refs,
227227
};
228228

229229
let res = (<(Sha256d, ChannelManager<EnforcingChannelKeys>)>::read(&mut Cursor::new(&$ser.0), read_args).expect("Failed to read manager").1, monitor);

lightning/src/ln/chan_utils.rs

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
55
use bitcoin::blockdata::script::{Script,Builder};
66
use bitcoin::blockdata::opcodes;
7-
use bitcoin::blockdata::transaction::{TxIn,TxOut,OutPoint,Transaction};
7+
use bitcoin::blockdata::transaction::{TxIn,TxOut,OutPoint,Transaction, SigHashType};
8+
use bitcoin::consensus::encode::{self, Decodable, Encodable};
9+
use bitcoin::util::bip143;
810

911
use bitcoin_hashes::{Hash, HashEngine};
1012
use bitcoin_hashes::sha256::Hash as Sha256;
@@ -13,9 +15,11 @@ use bitcoin_hashes::hash160::Hash as Hash160;
1315
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
1416

1517
use ln::channelmanager::PaymentHash;
18+
use ln::msgs::DecodeError;
19+
use util::ser::{Readable, Writeable, Writer, WriterWriteAdaptor};
1620

17-
use secp256k1::key::{PublicKey,SecretKey};
18-
use secp256k1::Secp256k1;
21+
use secp256k1::key::{SecretKey,PublicKey};
22+
use secp256k1::{Secp256k1, Signature};
1923
use secp256k1;
2024

2125
pub(super) const HTLC_SUCCESS_TX_WEIGHT: u64 = 703;
@@ -281,3 +285,116 @@ pub fn build_htlc_transaction(prev_hash: &Sha256dHash, feerate_per_kw: u64, to_s
281285
output: txouts,
282286
}
283287
}
288+
289+
#[derive(Clone)]
290+
/// We use this to track local commitment transactions and put off signing them until we are ready
291+
/// to broadcast. Eventually this will require a signer which is possibly external, but for now we
292+
/// just pass in the SecretKeys required.
293+
pub(crate) struct LocalCommitmentTransaction {
294+
tx: Transaction
295+
}
296+
impl LocalCommitmentTransaction {
297+
#[cfg(test)]
298+
pub fn dummy() -> Self {
299+
Self { tx: Transaction {
300+
version: 2,
301+
input: Vec::new(),
302+
output: Vec::new(),
303+
lock_time: 0,
304+
} }
305+
}
306+
307+
pub fn new_missing_local_sig(mut tx: Transaction, their_sig: &Signature, our_funding_key: &PublicKey, their_funding_key: &PublicKey) -> LocalCommitmentTransaction {
308+
if tx.input.len() != 1 { panic!("Tried to store a commitment transaction that had input count != 1!"); }
309+
if tx.input[0].witness.len() != 0 { panic!("Tried to store a signed commitment transaction?"); }
310+
311+
tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
312+
313+
if our_funding_key.serialize()[..] < their_funding_key.serialize()[..] {
314+
tx.input[0].witness.push(Vec::new());
315+
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
316+
tx.input[0].witness[2].push(SigHashType::All as u8);
317+
} else {
318+
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
319+
tx.input[0].witness[1].push(SigHashType::All as u8);
320+
tx.input[0].witness.push(Vec::new());
321+
}
322+
323+
Self { tx }
324+
}
325+
326+
pub fn txid(&self) -> Sha256dHash {
327+
self.tx.txid()
328+
}
329+
330+
pub fn has_local_sig(&self) -> bool {
331+
if self.tx.input.len() != 1 { panic!("Commitment transactions must have input count == 1!"); }
332+
if self.tx.input[0].witness.len() == 4 {
333+
assert!(!self.tx.input[0].witness[1].is_empty());
334+
assert!(!self.tx.input[0].witness[2].is_empty());
335+
true
336+
} else {
337+
assert_eq!(self.tx.input[0].witness.len(), 3);
338+
assert!(self.tx.input[0].witness[0].is_empty());
339+
assert!(self.tx.input[0].witness[1].is_empty() || self.tx.input[0].witness[2].is_empty());
340+
false
341+
}
342+
}
343+
344+
pub fn add_local_sig<T: secp256k1::Signing>(&mut self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) {
345+
if self.has_local_sig() { return; }
346+
let sighash = hash_to_message!(&bip143::SighashComponents::new(&self.tx)
347+
.sighash_all(&self.tx.input[0], funding_redeemscript, channel_value_satoshis)[..]);
348+
let our_sig = secp_ctx.sign(&sighash, funding_key);
349+
350+
if self.tx.input[0].witness[1].is_empty() {
351+
self.tx.input[0].witness[1] = our_sig.serialize_der().to_vec();
352+
self.tx.input[0].witness[1].push(SigHashType::All as u8);
353+
} else {
354+
self.tx.input[0].witness[2] = our_sig.serialize_der().to_vec();
355+
self.tx.input[0].witness[2].push(SigHashType::All as u8);
356+
}
357+
358+
self.tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
359+
}
360+
361+
pub fn without_valid_witness(&self) -> &Transaction { &self.tx }
362+
pub fn with_valid_witness(&self) -> &Transaction {
363+
assert!(self.has_local_sig());
364+
&self.tx
365+
}
366+
}
367+
impl PartialEq for LocalCommitmentTransaction {
368+
// We dont care whether we are signed in equality comparison
369+
fn eq(&self, o: &Self) -> bool {
370+
self.txid() == o.txid()
371+
}
372+
}
373+
impl Writeable for LocalCommitmentTransaction {
374+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
375+
if let Err(e) = self.tx.consensus_encode(&mut WriterWriteAdaptor(writer)) {
376+
match e {
377+
encode::Error::Io(e) => return Err(e),
378+
_ => panic!("local tx must have been well-formed!"),
379+
}
380+
}
381+
Ok(())
382+
}
383+
}
384+
impl<R: ::std::io::Read> Readable<R> for LocalCommitmentTransaction {
385+
fn read(reader: &mut R) -> Result<Self, DecodeError> {
386+
let tx = match Transaction::consensus_decode(reader.by_ref()) {
387+
Ok(tx) => tx,
388+
Err(e) => match e {
389+
encode::Error::Io(ioe) => return Err(DecodeError::Io(ioe)),
390+
_ => return Err(DecodeError::InvalidValue),
391+
},
392+
};
393+
394+
if tx.input.len() != 1 {
395+
// Ensure tx didn't hit the 0-input ambiguity case.
396+
return Err(DecodeError::InvalidValue);
397+
}
398+
Ok(Self { tx })
399+
}
400+
}

lightning/src/ln/channel.rs

Lines changed: 23 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use ln::msgs;
1919
use ln::msgs::{DecodeError, OptionalField, LocalFeatures, DataLossProtect};
2020
use ln::channelmonitor::ChannelMonitor;
2121
use ln::channelmanager::{PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingForwardHTLCInfo, RAACommitmentOrder, PaymentPreimage, PaymentHash, BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT};
22-
use ln::chan_utils::{TxCreationKeys,HTLCOutputInCommitment,HTLC_SUCCESS_TX_WEIGHT,HTLC_TIMEOUT_TX_WEIGHT};
22+
use ln::chan_utils::{LocalCommitmentTransaction,TxCreationKeys,HTLCOutputInCommitment,HTLC_SUCCESS_TX_WEIGHT,HTLC_TIMEOUT_TX_WEIGHT};
2323
use ln::chan_utils;
2424
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
2525
use chain::transaction::OutPoint;
@@ -450,7 +450,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
450450
let feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
451451

452452
let secp_ctx = Secp256k1::new();
453-
let channel_monitor = ChannelMonitor::new(chan_keys.revocation_base_key(), chan_keys.delayed_payment_base_key(),
453+
let channel_monitor = ChannelMonitor::new(chan_keys.funding_key(), chan_keys.revocation_base_key(), chan_keys.delayed_payment_base_key(),
454454
chan_keys.htlc_base_key(), chan_keys.payment_base_key(), &keys_provider.get_shutdown_pubkey(), config.own_channel_config.our_to_self_delay,
455455
keys_provider.get_destination_script(), logger.clone());
456456

@@ -644,7 +644,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
644644
}
645645

646646
let secp_ctx = Secp256k1::new();
647-
let channel_monitor = ChannelMonitor::new(chan_keys.revocation_base_key(), chan_keys.delayed_payment_base_key(),
647+
let channel_monitor = ChannelMonitor::new(chan_keys.funding_key(), chan_keys.revocation_base_key(), chan_keys.delayed_payment_base_key(),
648648
chan_keys.htlc_base_key(), chan_keys.payment_base_key(), &keys_provider.get_shutdown_pubkey(), config.own_channel_config.our_to_self_delay,
649649
keys_provider.get_destination_script(), logger.clone());
650650

@@ -1122,38 +1122,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
11221122
}.push_opcode(opcodes::all::OP_PUSHNUM_2).push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script()
11231123
}
11241124

1125-
fn sign_commitment_transaction(&self, tx: &mut Transaction, their_sig: &Signature) -> Signature {
1126-
if tx.input.len() != 1 {
1127-
panic!("Tried to sign commitment transaction that had input count != 1!");
1128-
}
1129-
if tx.input[0].witness.len() != 0 {
1130-
panic!("Tried to re-sign commitment transaction");
1131-
}
1132-
1133-
let funding_redeemscript = self.get_funding_redeemscript();
1134-
1135-
let sighash = hash_to_message!(&bip143::SighashComponents::new(&tx).sighash_all(&tx.input[0], &funding_redeemscript, self.channel_value_satoshis)[..]);
1136-
let our_sig = self.secp_ctx.sign(&sighash, self.local_keys.funding_key());
1137-
1138-
tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
1139-
1140-
let our_funding_key = PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()).serialize();
1141-
let their_funding_key = self.their_funding_pubkey.unwrap().serialize();
1142-
if our_funding_key[..] < their_funding_key[..] {
1143-
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
1144-
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
1145-
} else {
1146-
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
1147-
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
1148-
}
1149-
tx.input[0].witness[1].push(SigHashType::All as u8);
1150-
tx.input[0].witness[2].push(SigHashType::All as u8);
1151-
1152-
tx.input[0].witness.push(funding_redeemscript.into_bytes());
1153-
1154-
our_sig
1155-
}
1156-
11571125
/// Builds the htlc-success or htlc-timeout transaction which spends a given HTLC output
11581126
/// @local is used only to convert relevant internal structures which refer to remote vs local
11591127
/// to decide value of outputs and direction of HTLCs.
@@ -1492,26 +1460,25 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
14921460
Ok(())
14931461
}
14941462

1495-
fn funding_created_signature(&mut self, sig: &Signature) -> Result<(Transaction, Transaction, Signature, TxCreationKeys), ChannelError> {
1463+
fn funding_created_signature(&mut self, sig: &Signature) -> Result<(Transaction, LocalCommitmentTransaction, Signature, TxCreationKeys), ChannelError> {
14961464
let funding_script = self.get_funding_redeemscript();
14971465

14981466
let local_keys = self.build_local_transaction_keys(self.cur_local_commitment_transaction_number)?;
1499-
let mut local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, self.feerate_per_kw).0;
1467+
let local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, self.feerate_per_kw).0;
15001468
let local_sighash = hash_to_message!(&bip143::SighashComponents::new(&local_initial_commitment_tx).sighash_all(&local_initial_commitment_tx.input[0], &funding_script, self.channel_value_satoshis)[..]);
15011469

15021470
// They sign the "local" commitment transaction...
15031471
secp_check!(self.secp_ctx.verify(&local_sighash, &sig, &self.their_funding_pubkey.unwrap()), "Invalid funding_created signature from peer");
15041472

1505-
// ...and we sign it, allowing us to broadcast the tx if we wish
1506-
self.sign_commitment_transaction(&mut local_initial_commitment_tx, sig);
1473+
let localtx = LocalCommitmentTransaction::new_missing_local_sig(local_initial_commitment_tx, sig, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), self.their_funding_pubkey.as_ref().unwrap());
15071474

15081475
let remote_keys = self.build_remote_transaction_keys()?;
15091476
let remote_initial_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, false, self.feerate_per_kw).0;
15101477
let remote_signature = self.local_keys.sign_remote_commitment(self.channel_value_satoshis, &self.get_funding_redeemscript(), self.feerate_per_kw, &remote_initial_commitment_tx, &remote_keys, &Vec::new(), self.our_to_self_delay, &self.secp_ctx)
15111478
.map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed"))?.0;
15121479

15131480
// We sign the "remote" commitment transaction, allowing them to broadcast the tx if they wish.
1514-
Ok((remote_initial_commitment_tx, local_initial_commitment_tx, remote_signature, local_keys))
1481+
Ok((remote_initial_commitment_tx, localtx, remote_signature, local_keys))
15151482
}
15161483

15171484
pub fn funding_created(&mut self, msg: &msgs::FundingCreated) -> Result<(msgs::FundingSigned, ChannelMonitor), ChannelError> {
@@ -1575,14 +1542,15 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
15751542
let funding_script = self.get_funding_redeemscript();
15761543

15771544
let local_keys = self.build_local_transaction_keys(self.cur_local_commitment_transaction_number)?;
1578-
let mut local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, self.feerate_per_kw).0;
1545+
let local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, self.feerate_per_kw).0;
15791546
let local_sighash = hash_to_message!(&bip143::SighashComponents::new(&local_initial_commitment_tx).sighash_all(&local_initial_commitment_tx.input[0], &funding_script, self.channel_value_satoshis)[..]);
15801547

15811548
// They sign the "local" commitment transaction, allowing us to broadcast the tx if we wish.
15821549
secp_check!(self.secp_ctx.verify(&local_sighash, &msg.signature, &self.their_funding_pubkey.unwrap()), "Invalid funding_signed signature from peer");
15831550

1584-
self.sign_commitment_transaction(&mut local_initial_commitment_tx, &msg.signature);
1585-
self.channel_monitor.provide_latest_local_commitment_tx_info(local_initial_commitment_tx, local_keys, self.feerate_per_kw, Vec::new());
1551+
self.channel_monitor.provide_latest_local_commitment_tx_info(
1552+
LocalCommitmentTransaction::new_missing_local_sig(local_initial_commitment_tx, &msg.signature, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), self.their_funding_pubkey.as_ref().unwrap()),
1553+
local_keys, self.feerate_per_kw, Vec::new());
15861554
self.channel_state = ChannelState::FundingSent as u32 | (self.channel_state & (ChannelState::MonitorUpdateFailed as u32));
15871555
self.cur_local_commitment_transaction_number -= 1;
15881556

@@ -1846,8 +1814,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18461814
return Err(ChannelError::Close("Got wrong number of HTLC signatures from remote"));
18471815
}
18481816

1849-
self.sign_commitment_transaction(&mut local_commitment_tx.0, &msg.signature);
1850-
18511817
let mut htlcs_and_sigs = Vec::with_capacity(local_commitment_tx.2.len());
18521818
for (idx, (htlc, source)) in local_commitment_tx.2.drain(..).enumerate() {
18531819
if let Some(_) = htlc.transaction_output_index {
@@ -1881,7 +1847,10 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18811847
}
18821848
}
18831849

1884-
self.channel_monitor.provide_latest_local_commitment_tx_info(local_commitment_tx.0, local_keys, self.feerate_per_kw, htlcs_and_sigs);
1850+
1851+
self.channel_monitor.provide_latest_local_commitment_tx_info(
1852+
LocalCommitmentTransaction::new_missing_local_sig(local_commitment_tx.0, &msg.signature, &PublicKey::from_secret_key(&self.secp_ctx, self.local_keys.funding_key()), self.their_funding_pubkey.as_ref().unwrap()),
1853+
local_keys, self.feerate_per_kw, htlcs_and_sigs);
18851854

18861855
for htlc in self.pending_inbound_htlcs.iter_mut() {
18871856
let new_forward = if let &InboundHTLCState::RemoteAnnounced(ref forward_info) = &htlc.state {
@@ -2859,11 +2828,11 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
28592828
}
28602829

28612830
/// May only be called after funding has been initiated (ie is_funding_initiated() is true)
2862-
pub fn channel_monitor(&self) -> &ChannelMonitor {
2831+
pub fn channel_monitor(&mut self) -> &mut ChannelMonitor {
28632832
if self.channel_state < ChannelState::FundingCreated as u32 {
28642833
panic!("Can't get a channel monitor until funding has been created");
28652834
}
2866-
&self.channel_monitor
2835+
&mut self.channel_monitor
28672836
}
28682837

28692838
/// Guaranteed to be Some after both FundingLocked messages have been exchanged (and, thus,
@@ -4141,6 +4110,7 @@ mod tests {
41414110
use ln::channel::{Channel,ChannelKeys,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,TxCreationKeys};
41424111
use ln::channel::MAX_FUNDING_SATOSHIS;
41434112
use ln::chan_utils;
4113+
use ln::chan_utils::LocalCommitmentTransaction;
41444114
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
41454115
use chain::keysinterface::{InMemoryChannelKeys, KeysInterface};
41464116
use chain::transaction::OutPoint;
@@ -4260,13 +4230,15 @@ mod tests {
42604230
.collect();
42614231
(res.0, htlcs)
42624232
};
4233+
let redeemscript = chan.get_funding_redeemscript();
42634234
let their_signature = Signature::from_der(&hex::decode($their_sig_hex).unwrap()[..]).unwrap();
4264-
let sighash = Message::from_slice(&bip143::SighashComponents::new(&unsigned_tx.0).sighash_all(&unsigned_tx.0.input[0], &chan.get_funding_redeemscript(), chan.channel_value_satoshis)[..]).unwrap();
4235+
let sighash = Message::from_slice(&bip143::SighashComponents::new(&unsigned_tx.0).sighash_all(&unsigned_tx.0.input[0], &redeemscript, chan.channel_value_satoshis)[..]).unwrap();
42654236
secp_ctx.verify(&sighash, &their_signature, &chan.their_funding_pubkey.unwrap()).unwrap();
42664237

4267-
chan.sign_commitment_transaction(&mut unsigned_tx.0, &their_signature);
4238+
let mut localtx = LocalCommitmentTransaction::new_missing_local_sig(unsigned_tx.0.clone(), &their_signature, &PublicKey::from_secret_key(&secp_ctx, chan.local_keys.funding_key()), chan.their_funding_pubkey.as_ref().unwrap());
4239+
localtx.add_local_sig(chan.local_keys.funding_key(), &redeemscript, chan.channel_value_satoshis, &chan.secp_ctx);
42684240

4269-
assert_eq!(serialize(&unsigned_tx.0)[..],
4241+
assert_eq!(serialize(localtx.with_valid_witness())[..],
42704242
hex::decode($tx_hex).unwrap()[..]);
42714243
};
42724244
}

lightning/src/ln/channelmanager.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3216,7 +3216,7 @@ pub struct ChannelManagerReadArgs<'a, ChanSigner: ChannelKeys> {
32163216
///
32173217
/// In such cases the latest local transactions will be sent to the tx_broadcaster included in
32183218
/// this struct.
3219-
pub channel_monitors: &'a HashMap<OutPoint, &'a ChannelMonitor>,
3219+
pub channel_monitors: &'a mut HashMap<OutPoint, &'a mut ChannelMonitor>,
32203220
}
32213221

32223222
impl<'a, R : ::std::io::Read, ChanSigner: ChannelKeys + Readable<R>> ReadableArgs<R, ChannelManagerReadArgs<'a, ChanSigner>> for (Sha256dHash, ChannelManager<ChanSigner>) {
@@ -3245,7 +3245,7 @@ impl<'a, R : ::std::io::Read, ChanSigner: ChannelKeys + Readable<R>> ReadableArg
32453245

32463246
let funding_txo = channel.channel_monitor().get_funding_txo().ok_or(DecodeError::InvalidValue)?;
32473247
funding_txo_set.insert(funding_txo.clone());
3248-
if let Some(monitor) = args.channel_monitors.get(&funding_txo) {
3248+
if let Some(ref mut monitor) = args.channel_monitors.get_mut(&funding_txo) {
32493249
if channel.get_cur_local_commitment_transaction_number() != monitor.get_cur_local_commitment_number() ||
32503250
channel.get_revoked_remote_commitment_transaction_number() != monitor.get_min_seen_secret() ||
32513251
channel.get_cur_remote_commitment_transaction_number() != monitor.get_cur_remote_commitment_number() {
@@ -3263,7 +3263,7 @@ impl<'a, R : ::std::io::Read, ChanSigner: ChannelKeys + Readable<R>> ReadableArg
32633263
}
32643264
}
32653265

3266-
for (ref funding_txo, ref monitor) in args.channel_monitors.iter() {
3266+
for (ref funding_txo, ref mut monitor) in args.channel_monitors.iter_mut() {
32673267
if !funding_txo_set.contains(funding_txo) {
32683268
closed_channels.push((monitor.get_latest_local_commitment_txn(), Vec::new()));
32693269
}

0 commit comments

Comments
 (0)