false
} else { true }
});
- assert_eq!(res.len(), 2);
+ assert!(res.len() == 2 || res.len() == 3);
+ if res.len() == 3 {
+ assert_eq!(res[1], res[2]);
+ }
}
assert!(node_txn.is_empty());
_ => panic!("Unexpected event"),
}
let revoked_htlc_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert_eq!(revoked_htlc_txn.len(), 2);
+ assert_eq!(revoked_htlc_txn.len(), 3);
+ assert_eq!(revoked_htlc_txn[0], revoked_htlc_txn[2]);
assert_eq!(revoked_htlc_txn[0].input.len(), 1);
assert_eq!(revoked_htlc_txn[0].input[0].witness.last().unwrap().len(), 133);
check_spends!(revoked_htlc_txn[0], revoked_local_txn[0].clone());
+ check_spends!(revoked_htlc_txn[1], chan_1.3.clone());
// B will generate justice tx from A's revoked commitment/HTLC tx
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] }, 1);
}
let revoked_htlc_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert_eq!(revoked_htlc_txn.len(), 2);
+ assert_eq!(revoked_htlc_txn.len(), 3);
+ assert_eq!(revoked_htlc_txn[0], revoked_htlc_txn[2]);
assert_eq!(revoked_htlc_txn[0].input.len(), 1);
assert_eq!(revoked_htlc_txn[0].input[0].witness.last().unwrap().len(), 138);
check_spends!(revoked_htlc_txn[0], revoked_local_txn[0].clone());
// Verify that B is able to spend its own HTLC-Success tx thanks to spendable output event given back by its ChannelMonitor
let spend_txn = check_spendable_outputs!(nodes[1], 1);
- assert_eq!(spend_txn.len(), 1);
+ assert_eq!(spend_txn.len(), 2);
check_spends!(spend_txn[0], node_txn[0].clone());
+ check_spends!(spend_txn[1], node_txn[2].clone());
}
#[test]
// Verify that A is able to spend its own HTLC-Timeout tx thanks to spendable output event given back by its ChannelMonitor
let spend_txn = check_spendable_outputs!(nodes[0], 1);
- assert_eq!(spend_txn.len(), 4);
+ assert_eq!(spend_txn.len(), 8);
assert_eq!(spend_txn[0], spend_txn[2]);
+ assert_eq!(spend_txn[0], spend_txn[4]);
+ assert_eq!(spend_txn[0], spend_txn[6]);
assert_eq!(spend_txn[1], spend_txn[3]);
+ assert_eq!(spend_txn[1], spend_txn[5]);
+ assert_eq!(spend_txn[1], spend_txn[7]);
check_spends!(spend_txn[0], local_txn[0].clone());
check_spends!(spend_txn[1], node_txn[0].clone());
}
} else { (None, None) }
}
- fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, per_commitment_point: &Option<PublicKey>, delayed_payment_base_key: &Option<SecretKey>) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>) {
+ fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, per_commitment_point: &Option<PublicKey>, delayed_payment_base_key: &Option<SecretKey>) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, Vec<TxOut>) {
let mut res = Vec::with_capacity(local_tx.htlc_outputs.len());
let mut spendable_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
+ let mut watch_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
macro_rules! add_dynamic_output {
($father_tx: expr, $vout: expr) => {
res.push(htlc_success_tx);
}
}
+ watch_outputs.push(local_tx.tx.output[htlc.transaction_output_index as usize].clone());
}
- (res, spendable_outputs)
+ (res, spendable_outputs, watch_outputs)
}
/// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet)
/// revoked using data in local_claimable_outpoints.
/// Should not be used if check_spend_revoked_transaction succeeds.
- fn check_spend_local_transaction(&self, tx: &Transaction, _height: u32) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>) {
+ fn check_spend_local_transaction(&self, tx: &Transaction, _height: u32) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, (Sha256dHash, Vec<TxOut>)) {
let commitment_txid = tx.txid();
if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
if local_tx.txid == commitment_txid {
match self.key_storage {
Storage::Local { ref delayed_payment_base_key, ref latest_per_commitment_point, .. } => {
- return self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key));
+ let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key));
+ return (local_txn, spendable_outputs, (commitment_txid, watch_outputs));
},
Storage::Watchtower { .. } => {
- return self.broadcast_by_local_state(local_tx, &None, &None);
+ let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, &None, &None);
+ return (local_txn, spendable_outputs, (commitment_txid, watch_outputs));
}
}
}
if local_tx.txid == commitment_txid {
match self.key_storage {
Storage::Local { ref delayed_payment_base_key, ref prev_latest_per_commitment_point, .. } => {
- return self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key));
+ let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key));
+ return (local_txn, spendable_outputs, (commitment_txid, watch_outputs));
},
Storage::Watchtower { .. } => {
- return self.broadcast_by_local_state(local_tx, &None, &None);
+ let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, &None, &None);
+ return (local_txn, spendable_outputs, (commitment_txid, watch_outputs));
}
}
}
}
- (Vec::new(), Vec::new())
+ (Vec::new(), Vec::new(), (commitment_txid, Vec::new()))
}
/// Generate a spendable output event when closing_transaction get registered onchain.
watch_outputs.push(new_outputs);
}
if txn.is_empty() {
- let (remote_txn, mut outputs) = self.check_spend_local_transaction(tx, height);
- spendable_outputs.append(&mut outputs);
- txn = remote_txn;
+ let (local_txn, mut spendable_output, new_outputs) = self.check_spend_local_transaction(tx, height);
+ spendable_outputs.append(&mut spendable_output);
+ txn = local_txn;
+ if !new_outputs.1.is_empty() {
+ watch_outputs.push(new_outputs);
+ }
}
if !funding_txo.is_none() && txn.is_empty() {
if let Some(spendable_output) = self.check_spend_closing_transaction(tx) {
broadcaster.broadcast_transaction(&cur_local_tx.tx);
match self.key_storage {
Storage::Local { ref delayed_payment_base_key, ref latest_per_commitment_point, .. } => {
- let (txs, mut outputs) = self.broadcast_by_local_state(&cur_local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key));
- spendable_outputs.append(&mut outputs);
+ let (txs, mut spendable_output, new_outputs) = self.broadcast_by_local_state(&cur_local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key));
+ spendable_outputs.append(&mut spendable_output);
+ if !new_outputs.is_empty() {
+ watch_outputs.push((cur_local_tx.txid.clone(), new_outputs));
+ }
for tx in txs {
broadcaster.broadcast_transaction(&tx);
}
},
Storage::Watchtower { .. } => {
- let (txs, mut outputs) = self.broadcast_by_local_state(&cur_local_tx, &None, &None);
- spendable_outputs.append(&mut outputs);
+ let (txs, mut spendable_output, new_outputs) = self.broadcast_by_local_state(&cur_local_tx, &None, &None);
+ spendable_outputs.append(&mut spendable_output);
+ if !new_outputs.is_empty() {
+ watch_outputs.push((cur_local_tx.txid.clone(), new_outputs));
+ }
for tx in txs {
broadcaster.broadcast_transaction(&tx);
}