Skip to content

Commit 8c339e3

Browse files
author
Antoine Riard
committed
Add transaction index in watched_outputs
Previously, outputs were monitored based on txid and an index yelled from an enumeration over the returned selected outputs by monitoring code. This is always have been broken but was only discovered while introducing anchor outputs as those ones rank always first per BIP69. We didn't have test cases where a HTLC was bigger than a party balance on a holder commitment and thus not ranking first. Next commit introduce test coverage.
1 parent f35a5ce commit 8c339e3

File tree

2 files changed

+32
-26
lines changed

2 files changed

+32
-26
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ impl<ChanSigner: ChannelKeys, C: Deref, T: Deref, F: Deref, L: Deref> ChainMonit
9595

9696
if let Some(ref chain_source) = self.chain_source {
9797
for (txid, outputs) in txn_outputs.drain(..) {
98-
for (idx, output) in outputs.iter().enumerate() {
99-
chain_source.register_output(&OutPoint { txid, index: idx as u16 }, &output.script_pubkey);
98+
for (idx, output) in outputs.iter() {
99+
chain_source.register_output(&OutPoint { txid, index: *idx as u16 }, &output.script_pubkey);
100100
}
101101
}
102102
}
@@ -152,8 +152,8 @@ impl<ChanSigner: ChannelKeys, C: Deref, T: Deref, F: Deref, L: Deref> ChainMonit
152152
if let Some(ref chain_source) = self.chain_source {
153153
chain_source.register_tx(&funding_txo.0.txid, &funding_txo.1);
154154
for (txid, outputs) in monitor.get_outputs_to_watch().iter() {
155-
for (idx, script_pubkey) in outputs.iter().enumerate() {
156-
chain_source.register_output(&OutPoint { txid: *txid, index: idx as u16 }, &script_pubkey);
155+
for (idx, script_pubkey) in outputs.iter() {
156+
chain_source.register_output(&OutPoint { txid: *txid, index: *idx as u16 }, script_pubkey);
157157
}
158158
}
159159
}

lightning/src/chain/channelmonitor.rs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ pub struct ChannelMonitor<ChanSigner: ChannelKeys> {
666666
// interface knows about the TXOs that we want to be notified of spends of. We could probably
667667
// be smart and derive them from the above storage fields, but its much simpler and more
668668
// Obviously Correct (tm) if we just keep track of them explicitly.
669-
outputs_to_watch: HashMap<Txid, Vec<Script>>,
669+
outputs_to_watch: HashMap<Txid, Vec<(u32, Script)>>,
670670

671671
#[cfg(test)]
672672
pub onchain_tx_handler: OnchainTxHandler<ChanSigner>,
@@ -914,10 +914,11 @@ impl<ChanSigner: ChannelKeys + Writeable> ChannelMonitor<ChanSigner> {
914914
}
915915

916916
(self.outputs_to_watch.len() as u64).write(writer)?;
917-
for (txid, output_scripts) in self.outputs_to_watch.iter() {
917+
for (txid, idx_scripts) in self.outputs_to_watch.iter() {
918918
txid.write(writer)?;
919-
(output_scripts.len() as u64).write(writer)?;
920-
for script in output_scripts.iter() {
919+
(idx_scripts.len() as u64).write(writer)?;
920+
for (idx, script) in idx_scripts.iter() {
921+
idx.write(writer)?;
921922
script.write(writer)?;
922923
}
923924
}
@@ -963,7 +964,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
963964
onchain_tx_handler.provide_latest_holder_tx(initial_holder_commitment_tx);
964965

965966
let mut outputs_to_watch = HashMap::new();
966-
outputs_to_watch.insert(funding_info.0.txid, vec![funding_info.1.clone()]);
967+
outputs_to_watch.insert(funding_info.0.txid, vec![(funding_info.0.index as u32, funding_info.1.clone())]);
967968

968969
ChannelMonitor {
969970
latest_update_id: 0,
@@ -1209,7 +1210,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
12091210
/// transaction), which we must learn about spends of via block_connected().
12101211
///
12111212
/// (C-not exported) because we have no HashMap bindings
1212-
pub fn get_outputs_to_watch(&self) -> &HashMap<Txid, Vec<Script>> {
1213+
pub fn get_outputs_to_watch(&self) -> &HashMap<Txid, Vec<(u32, Script)>> {
12131214
// If we've detected a counterparty commitment tx on chain, we must include it in the set
12141215
// of outputs to watch for spends of, otherwise we're likely to lose user funds. Because
12151216
// its trivial to do, double-check that here.
@@ -1264,7 +1265,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
12641265
/// HTLC-Success/HTLC-Timeout transactions.
12651266
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
12661267
/// revoked counterparty commitment tx
1267-
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, (Txid, Vec<TxOut>)) where L::Target: Logger {
1268+
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, (Txid, Vec<(u32, TxOut)>)) where L::Target: Logger {
12681269
// Most secp and related errors trying to create keys means we have no hope of constructing
12691270
// a spend transaction...so we return no transactions to broadcast
12701271
let mut claimable_outpoints = Vec::new();
@@ -1319,7 +1320,9 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
13191320
if !claimable_outpoints.is_empty() || per_commitment_option.is_some() { // ie we're confident this is actually ours
13201321
// We're definitely a counterparty commitment transaction!
13211322
log_trace!(logger, "Got broadcast of revoked counterparty commitment transaction, going to generate general spend tx with {} inputs", claimable_outpoints.len());
1322-
watch_outputs.append(&mut tx.output.clone());
1323+
for (idx, outp) in tx.output.iter().enumerate() {
1324+
watch_outputs.push((idx as u32, outp.clone()));
1325+
}
13231326
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
13241327

13251328
macro_rules! check_htlc_fails {
@@ -1366,7 +1369,9 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
13661369
// already processed the block, resulting in the counterparty_commitment_txn_on_chain entry
13671370
// not being generated by the above conditional. Thus, to be safe, we go ahead and
13681371
// insert it here.
1369-
watch_outputs.append(&mut tx.output.clone());
1372+
for (idx, outp) in tx.output.iter().enumerate() {
1373+
watch_outputs.push((idx as u32, outp.clone()));
1374+
}
13701375
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
13711376

13721377
log_trace!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid);
@@ -1456,7 +1461,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
14561461
}
14571462

14581463
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
1459-
fn check_spend_counterparty_htlc<L: Deref>(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec<ClaimRequest>, Option<(Txid, Vec<TxOut>)>) where L::Target: Logger {
1464+
fn check_spend_counterparty_htlc<L: Deref>(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec<ClaimRequest>, Option<(Txid, Vec<(u32, TxOut)>)>) where L::Target: Logger {
14601465
let htlc_txid = tx.txid();
14611466
if tx.input.len() != 1 || tx.output.len() != 1 || tx.input[0].witness.len() != 5 {
14621467
return (Vec::new(), None)
@@ -1478,10 +1483,11 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
14781483
log_trace!(logger, "Counterparty HTLC broadcast {}:{}", htlc_txid, 0);
14791484
let witness_data = InputMaterial::Revoked { per_commitment_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, input_descriptor: InputDescriptors::RevokedOutput, amount: tx.output[0].value, htlc: None, on_counterparty_tx_csv: self.counterparty_tx_cache.on_counterparty_tx_csv };
14801485
let claimable_outpoints = vec!(ClaimRequest { absolute_timelock: height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: htlc_txid, vout: 0}, witness_data });
1481-
(claimable_outpoints, Some((htlc_txid, tx.output.clone())))
1486+
let outputs = vec![(0, tx.output[0].clone())];
1487+
(claimable_outpoints, Some((htlc_txid, outputs)))
14821488
}
14831489

1484-
fn broadcast_by_holder_state(&self, commitment_tx: &Transaction, holder_tx: &HolderSignedTx) -> (Vec<ClaimRequest>, Vec<TxOut>, Option<(Script, PublicKey, PublicKey)>) {
1490+
fn broadcast_by_holder_state(&self, commitment_tx: &Transaction, holder_tx: &HolderSignedTx) -> (Vec<ClaimRequest>, Vec<(u32, TxOut)>, Option<(Script, PublicKey, PublicKey)>) {
14851491
let mut claim_requests = Vec::with_capacity(holder_tx.htlc_outputs.len());
14861492
let mut watch_outputs = Vec::with_capacity(holder_tx.htlc_outputs.len());
14871493

@@ -1502,7 +1508,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
15021508
} else { None },
15031509
amount: htlc.amount_msat,
15041510
}});
1505-
watch_outputs.push(commitment_tx.output[transaction_output_index as usize].clone());
1511+
watch_outputs.push((transaction_output_index, commitment_tx.output[transaction_output_index as usize].clone()));
15061512
}
15071513
}
15081514

@@ -1512,7 +1518,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
15121518
/// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet)
15131519
/// revoked using data in holder_claimable_outpoints.
15141520
/// Should not be used if check_spend_revoked_transaction succeeds.
1515-
fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, (Txid, Vec<TxOut>)) where L::Target: Logger {
1521+
fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, (Txid, Vec<(u32, TxOut)>)) where L::Target: Logger {
15161522
let commitment_txid = tx.txid();
15171523
let mut claim_requests = Vec::new();
15181524
let mut watch_outputs = Vec::new();
@@ -1662,7 +1668,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
16621668
/// [`get_outputs_to_watch`].
16631669
///
16641670
/// [`get_outputs_to_watch`]: #method.get_outputs_to_watch
1665-
pub fn block_connected<B: Deref, F: Deref, L: Deref>(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L)-> Vec<(Txid, Vec<TxOut>)>
1671+
pub fn block_connected<B: Deref, F: Deref, L: Deref>(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L)-> Vec<(Txid, Vec<(u32, TxOut)>)>
16661672
where B::Target: BroadcasterInterface,
16671673
F::Target: FeeEstimator,
16681674
L::Target: Logger,
@@ -1763,8 +1769,8 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
17631769
// Determine new outputs to watch by comparing against previously known outputs to watch,
17641770
// updating the latter in the process.
17651771
watch_outputs.retain(|&(ref txid, ref txouts)| {
1766-
let output_scripts = txouts.iter().map(|o| o.script_pubkey.clone()).collect();
1767-
self.outputs_to_watch.insert(txid.clone(), output_scripts).is_none()
1772+
let idx_and_scripts = txouts.iter().map(|o| (o.0, o.1.script_pubkey.clone())).collect();
1773+
self.outputs_to_watch.insert(txid.clone(), idx_and_scripts).is_none()
17681774
});
17691775
watch_outputs
17701776
}
@@ -1813,8 +1819,8 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
18131819
fn spends_watched_output(&self, tx: &Transaction) -> bool {
18141820
for input in tx.input.iter() {
18151821
if let Some(outputs) = self.get_outputs_to_watch().get(&input.previous_output.txid) {
1816-
for (idx, _script_pubkey) in outputs.iter().enumerate() {
1817-
if idx == input.previous_output.vout as usize {
1822+
for (idx, _script_pubkey) in outputs.iter() {
1823+
if *idx == input.previous_output.vout {
18181824
return true;
18191825
}
18201826
}
@@ -2316,13 +2322,13 @@ impl<ChanSigner: ChannelKeys + Readable> Readable for (BlockHash, ChannelMonitor
23162322
}
23172323

23182324
let outputs_to_watch_len: u64 = Readable::read(reader)?;
2319-
let mut outputs_to_watch = HashMap::with_capacity(cmp::min(outputs_to_watch_len as usize, MAX_ALLOC_SIZE / (mem::size_of::<Txid>() + mem::size_of::<Vec<Script>>())));
2325+
let mut outputs_to_watch = HashMap::with_capacity(cmp::min(outputs_to_watch_len as usize, MAX_ALLOC_SIZE / (mem::size_of::<Txid>() + mem::size_of::<u32>() + mem::size_of::<Vec<Script>>())));
23202326
for _ in 0..outputs_to_watch_len {
23212327
let txid = Readable::read(reader)?;
23222328
let outputs_len: u64 = Readable::read(reader)?;
2323-
let mut outputs = Vec::with_capacity(cmp::min(outputs_len as usize, MAX_ALLOC_SIZE / mem::size_of::<Script>()));
2329+
let mut outputs = Vec::with_capacity(cmp::min(outputs_len as usize, MAX_ALLOC_SIZE / (mem::size_of::<u32>() + mem::size_of::<Script>())));
23242330
for _ in 0..outputs_len {
2325-
outputs.push(Readable::read(reader)?);
2331+
outputs.push((Readable::read(reader)?, Readable::read(reader)?));
23262332
}
23272333
if let Some(_) = outputs_to_watch.insert(txid, outputs) {
23282334
return Err(DecodeError::InvalidValue);

0 commit comments

Comments
 (0)