check_spends!(claim_txn[3], claim_txn[2]);
assert_eq!(htlc_pair.0.input.len(), 1);
assert_eq!(htlc_pair.0.input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT); // HTLC 1 <--> 0, preimage tx
- check_spends!(htlc_pair.0, remote_txn[0].clone());
+ check_spends!(htlc_pair.0, remote_txn[0]);
assert_eq!(htlc_pair.1.input.len(), 1);
assert_eq!(htlc_pair.1.input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT); // HTLC 0 <--> 1, timeout tx
- check_spends!(htlc_pair.1, remote_txn[0].clone());
+ check_spends!(htlc_pair.1, remote_txn[0]);
let events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 2);
assert_eq!(node_txn.len(), 2); // ChannelMonitor: penalty tx, ChannelManager: local commitment tx
assert_eq!(node_txn[0].input.len(), 2); // We should claim the revoked output and the HTLC output
- check_spends!(node_txn[0], revoked_local_txn[0].clone());
+ check_spends!(node_txn[0], revoked_local_txn[0]);
node_txn.swap_remove(0);
node_txn.truncate(1);
}
test_txn_broadcast(&nodes[1], &chan_5, None, HTLCType::NONE);
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
+ // Verify broadcast of revoked HTLC-timeout
let node_txn = test_txn_broadcast(&nodes[0], &chan_5, Some(revoked_local_txn[0].clone()), HTLCType::TIMEOUT);
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ // Broadcast revoked HTLC-timeout on node 1
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
test_revoked_htlc_claim_txn_broadcast(&nodes[1], node_txn[1].clone(), revoked_local_txn[0].clone());
}
assert_eq!(node_txn.len(), 2); //ChannelMonitor: penalty tx, ChannelManager: local commitment tx
assert_eq!(node_txn[0].input.len(), 1); // We claim the received HTLC output
- check_spends!(node_txn[0], revoked_local_txn[0].clone());
+ check_spends!(node_txn[0], revoked_local_txn[0]);
node_txn.swap_remove(0);
}
test_txn_broadcast(&nodes[0], &chan_6, None, HTLCType::NONE);
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 2); // ChannelMonitor: justice tx against revoked to_local output, ChannelManager: local commitment tx
- check_spends!(node_txn[0], revoked_local_txn[0].clone());
- check_spends!(node_txn[1], chan_1.3.clone());
+ check_spends!(node_txn[0], revoked_local_txn[0]);
+ check_spends!(node_txn[1], chan_1.3);
// Inform nodes[0] that a watchtower cheated on its behalf, so it will force-close the chan
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
assert_eq!(revoked_local_txn[1].input.len(), 1);
assert_eq!(revoked_local_txn[1].input[0].previous_output.txid, revoked_local_txn[0].txid());
assert_eq!(revoked_local_txn[1].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT); // HTLC-Timeout
- check_spends!(revoked_local_txn[1], revoked_local_txn[0].clone());
+ check_spends!(revoked_local_txn[1], revoked_local_txn[0]);
//Revoke the old state
claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_1, 3_000_000);
assert_eq!(node_txn.len(), 3); // ChannelMonitor: penalty tx, ChannelManager: local commitment + HTLC-timeout
assert_eq!(node_txn[0].input.len(), 3); // Claim the revoked output + both revoked HTLC outputs
- check_spends!(node_txn[0], revoked_local_txn[0].clone());
+ check_spends!(node_txn[0], revoked_local_txn[0]);
let mut witness_lens = BTreeSet::new();
witness_lens.insert(node_txn[0].input[0].witness.last().unwrap().len());
// Check the pair local commitment and HTLC-timeout broadcast due to HTLC expiration and present 8 times (rebroadcast at every block from 200 to 206)
assert_eq!(node_txn[0].input.len(), 1);
- check_spends!(node_txn[0], chan_1.3.clone());
+ check_spends!(node_txn[0], chan_1.3);
assert_eq!(node_txn[1].input.len(), 1);
let witness_script = node_txn[1].input[0].witness.last().unwrap();
assert_eq!(witness_script.len(), OFFERED_HTLC_SCRIPT_WEIGHT); //Spending an offered htlc output
- check_spends!(node_txn[1], node_txn[0].clone());
+ check_spends!(node_txn[1], node_txn[0]);
// Justice transactions are indices 2-3-4
assert_eq!(node_txn[2].input.len(), 1);
assert_eq!(node_txn[3].input.len(), 1);
assert_eq!(node_txn[4].input.len(), 1);
- fn get_txout(out_point: &BitcoinOutPoint, tx: &Transaction) -> Option<TxOut> {
- if out_point.txid == tx.txid() {
- tx.output.get(out_point.vout as usize).cloned()
- } else {
- None
- }
- }
- node_txn[2].verify(|out|get_txout(out, &revoked_local_txn[0])).unwrap();
- node_txn[3].verify(|out|get_txout(out, &revoked_local_txn[0])).unwrap();
- node_txn[4].verify(|out|get_txout(out, &revoked_local_txn[0])).unwrap();
+ check_spends!(node_txn[2], revoked_local_txn[0]);
+ check_spends!(node_txn[3], revoked_local_txn[0]);
+ check_spends!(node_txn[4], revoked_local_txn[0]);
let mut witness_lens = BTreeSet::new();
witness_lens.insert(node_txn[2].input[0].witness.last().unwrap().len());
// Broadcast HTLC Success transaction by C on received output from C's commitment tx on B's chain
let commitment_tx = nodes[2].node.channel_state.lock().unwrap().by_id.get_mut(&chan_2.2).unwrap().channel_monitor().get_latest_local_commitment_txn();
assert_eq!(commitment_tx.len(), 1);
- check_spends!(commitment_tx[0], chan_2.3.clone());
+ check_spends!(commitment_tx[0], chan_2.3);
nodes[2].node.claim_funds(our_payment_preimage, 3_000_000);
nodes[2].node.claim_funds(our_payment_preimage_2, 3_000_000);
check_added_monitors!(nodes[2], 2);
assert_eq!(node_txn[0], node_txn[5]);
assert_eq!(node_txn[1], node_txn[6]);
assert_eq!(node_txn[2], commitment_tx[0]);
- check_spends!(node_txn[0], commitment_tx[0].clone());
- check_spends!(node_txn[1], commitment_tx[0].clone());
+ check_spends!(node_txn[0], commitment_tx[0]);
+ check_spends!(node_txn[1], commitment_tx[0]);
assert_eq!(node_txn[0].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
assert_eq!(node_txn[1].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
assert_eq!(node_txn.len(), if $htlc_offered { 7 } else { 5 });
// Node[1]: ChannelManager: 3 (commitment tx, 2*HTLC-Timeout tx), ChannelMonitor: 2 (timeout tx)
// Node[0]: ChannelManager: 3 (commtiemtn tx, 2*HTLC-Timeout tx), ChannelMonitor: 2 HTLC-timeout * 2 (block-rescan)
- check_spends!(node_txn[0], $commitment_tx.clone());
- check_spends!(node_txn[1], $commitment_tx.clone());
+ check_spends!(node_txn[0], $commitment_tx);
+ check_spends!(node_txn[1], $commitment_tx);
if $htlc_offered {
assert_eq!(node_txn[0], node_txn[5]);
assert_eq!(node_txn[1], node_txn[6]);
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
assert!(node_txn[1].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
}
- check_spends!(node_txn[2], $chan_tx.clone());
- check_spends!(node_txn[3], node_txn[2].clone());
- check_spends!(node_txn[4], node_txn[2].clone());
+ check_spends!(node_txn[2], $chan_tx);
+ check_spends!(node_txn[3], node_txn[2]);
+ check_spends!(node_txn[4], node_txn[2]);
assert_eq!(node_txn[2].input[0].witness.last().unwrap().len(), 71);
assert_eq!(node_txn[3].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
assert_eq!(node_txn[4].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
// Broadcast legit commitment tx from A on B's chain
// Broadcast preimage tx by B on offered output from A commitment tx on A's chain
let commitment_tx = nodes[0].node.channel_state.lock().unwrap().by_id.get_mut(&chan_1.2).unwrap().channel_monitor().get_latest_local_commitment_txn();
- check_spends!(commitment_tx[0], chan_1.3.clone());
+ check_spends!(commitment_tx[0], chan_1.3);
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![commitment_tx[0].clone()]}, 1);
check_closed_broadcast!(nodes[1], false);
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); // ChannelManager : 3 (commitment tx + HTLC-Sucess * 2), ChannelMonitor : 1 (HTLC-Success)
assert_eq!(node_txn.len(), 4);
- check_spends!(node_txn[0], commitment_tx[0].clone());
+ check_spends!(node_txn[0], commitment_tx[0]);
assert_eq!(node_txn[0].input.len(), 2);
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
assert_eq!(node_txn[0].input[1].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
assert_eq!(node_txn[0].lock_time, 0);
assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
- check_spends!(node_txn[1], chan_1.3.clone());
+ check_spends!(node_txn[1], chan_1.3);
assert_eq!(node_txn[1].input[0].witness.clone().last().unwrap().len(), 71);
check_spends!(node_txn[2], node_txn[1]);
check_spends!(node_txn[3], node_txn[1]);
// Broadcast legit commitment tx from C on B's chain
let commitment_tx = nodes[2].node.channel_state.lock().unwrap().by_id.get_mut(&chan_2.2).unwrap().channel_monitor().get_latest_local_commitment_txn();
- check_spends!(commitment_tx[0], chan_2.3.clone());
+ check_spends!(commitment_tx[0], chan_2.3);
nodes[2].node.fail_htlc_backwards(&payment_hash);
check_added_monitors!(nodes[2], 0);
expect_pending_htlcs_forwardable!(nodes[2]);
check_closed_broadcast!(nodes[2], false);
let node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); // ChannelManager : 1 (commitment tx)
assert_eq!(node_txn.len(), 1);
- check_spends!(node_txn[0], chan_2.3.clone());
+ check_spends!(node_txn[0], chan_2.3);
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), 71);
// Broadcast timeout transaction by B on received output from C's commitment tx on B's chain
assert_eq!(node_txn[1], node_txn[4]);
assert_eq!(node_txn[1], node_txn[6]);
- check_spends!(node_txn[2], commitment_tx[0].clone());
+ check_spends!(node_txn[2], commitment_tx[0]);
assert_eq!(node_txn[2].clone().input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
- check_spends!(node_txn[0], chan_2.3.clone());
- check_spends!(node_txn[1], node_txn[0].clone());
+ check_spends!(node_txn[0], chan_2.3);
+ check_spends!(node_txn[1], node_txn[0]);
assert_eq!(node_txn[0].clone().input[0].witness.last().unwrap().len(), 71);
assert_eq!(node_txn[1].clone().input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
// Broadcast legit commitment tx from B on A's chain
let commitment_tx = nodes[1].node.channel_state.lock().unwrap().by_id.get_mut(&chan_1.2).unwrap().channel_monitor().get_latest_local_commitment_txn();
- check_spends!(commitment_tx[0], chan_1.3.clone());
+ check_spends!(commitment_tx[0], chan_1.3);
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![commitment_tx[0].clone()]}, 200);
check_closed_broadcast!(nodes[0], false);
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); // ChannelManager : 2 (commitment tx, HTLC-Timeout tx), ChannelMonitor : 1 timeout tx
assert_eq!(node_txn.len(), 3);
- check_spends!(node_txn[0], commitment_tx[0].clone());
+ check_spends!(node_txn[0], commitment_tx[0]);
assert_eq!(node_txn[0].clone().input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
- check_spends!(node_txn[1], chan_1.3.clone());
- check_spends!(node_txn[2], node_txn[1].clone());
+ check_spends!(node_txn[1], chan_1.3);
+ check_spends!(node_txn[2], node_txn[1]);
assert_eq!(node_txn[1].clone().input[0].witness.last().unwrap().len(), 71);
assert_eq!(node_txn[2].clone().input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
}
check_closed_broadcast!(nodes[1], false);
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
- check_spends!(node_txn[0], chan.3.clone());
+ check_spends!(node_txn[0], chan.3);
assert_eq!(node_txn[0].output.len(), 2); // We can't force trimming of to_remote output as channel_reserve_satoshis block us to do so at channel opening
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()] }, 0);
let spend_txn = check_spendable_outputs!(nodes[1], 1);
assert_eq!(spend_txn.len(), 1);
- check_spends!(spend_txn[0], node_txn[0].clone());
+ check_spends!(spend_txn[0], node_txn[0]);
}
#[test]
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
- check_spends!(node_txn[0], chan.3.clone());
+ check_spends!(node_txn[0], chan.3);
assert_eq!(node_txn[0].output.len(), 2); // We can't force trimming of to_remote output as channel_reserve_satoshis block us to do so at channel opening
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
let spend_txn = check_spendable_outputs!(nodes[1], 1);
assert_eq!(spend_txn.len(), 2);
assert_eq!(spend_txn[0], spend_txn[1]);
- check_spends!(spend_txn[0], node_txn[0].clone());
+ check_spends!(spend_txn[0], node_txn[0]);
}
#[test]
let spend_txn = check_spendable_outputs!(nodes[1], 1);
assert_eq!(spend_txn.len(), 3);
assert_eq!(spend_txn[0], spend_txn[2]); // to_remote output on revoked remote commitment_tx
- check_spends!(spend_txn[0], revoked_local_txn[0].clone());
- check_spends!(spend_txn[1], node_txn[0].clone());
+ check_spends!(spend_txn[0], revoked_local_txn[0]);
+ check_spends!(spend_txn[1], node_txn[0]);
}
#[test]
// Check B's monitor was able to send back output descriptor event for preimage tx on A's commitment tx
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); // ChannelManager : 2 (local commitment tx + HTLC-Success), ChannelMonitor: preimage tx
assert_eq!(node_txn.len(), 3);
- check_spends!(node_txn[0], commitment_tx[0].clone());
+ check_spends!(node_txn[0], commitment_tx[0]);
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
eprintln!("{:?}", node_txn[1]);
- check_spends!(node_txn[1], chan_1.3.clone());
+ check_spends!(node_txn[1], chan_1.3);
check_spends!(node_txn[2], node_txn[1]);
let spend_txn = check_spendable_outputs!(nodes[1], 1); // , 0, 0, 1, 1);
assert_eq!(spend_txn.len(), 1);
- check_spends!(spend_txn[0], node_txn[0].clone());
+ check_spends!(spend_txn[0], node_txn[0]);
}
#[test]
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 2);
assert_eq!(node_txn[0].input.len(), 2);
- check_spends!(node_txn[0], revoked_local_txn[0].clone());
+ check_spends!(node_txn[0], revoked_local_txn[0]);
let spend_txn = check_spendable_outputs!(nodes[1], 1);
assert_eq!(spend_txn.len(), 1);
- check_spends!(spend_txn[0], node_txn[0].clone());
+ check_spends!(spend_txn[0], node_txn[0]);
}
#[test]
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(), OFFERED_HTLC_SCRIPT_WEIGHT);
- check_spends!(revoked_htlc_txn[0], revoked_local_txn[0].clone());
- check_spends!(revoked_htlc_txn[1], chan_1.3.clone());
+ check_spends!(revoked_htlc_txn[0], revoked_local_txn[0]);
+ check_spends!(revoked_htlc_txn[1], chan_1.3);
// B will generate justice tx from A's revoked commitment/HTLC tx
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] }, 1);
check_closed_broadcast!(nodes[1], false);
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert_eq!(node_txn.len(), 4 ); // ChannelMonitor: justice tx on revoked commitment, justice tx on revoked HTLC-timeout, adjusted justice tx, ChannelManager: local commitment tx
+ assert_eq!(node_txn.len(), 4); // ChannelMonitor: justice tx on revoked commitment, justice tx on revoked HTLC-timeout, adjusted justice tx, ChannelManager: local commitment tx
+ assert_eq!(node_txn[0].input.len(), 2);
+ check_spends!(node_txn[0], revoked_local_txn[0]);
+ check_spends!(node_txn[1], chan_1.3);
assert_eq!(node_txn[2].input.len(), 1);
- check_spends!(node_txn[2], revoked_htlc_txn[0].clone());
+ check_spends!(node_txn[2], revoked_htlc_txn[0]);
+ assert_eq!(node_txn[3].input.len(), 1);
+ check_spends!(node_txn[3], revoked_local_txn[0]);
// Check B's ChannelMonitor was able to generate the right spendable output descriptor
let spend_txn = check_spendable_outputs!(nodes[1], 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());
+ check_spends!(spend_txn[0], node_txn[0]);
+ check_spends!(spend_txn[1], node_txn[2]);
}
#[test]
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(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
- check_spends!(revoked_htlc_txn[0], revoked_local_txn[0].clone());
+ check_spends!(revoked_htlc_txn[0], revoked_local_txn[0]);
// A will generate justice tx from B's revoked commitment/HTLC tx
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone()] }, 1);
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 3); // ChannelMonitor: justice tx on revoked commitment, justice tx on revoked HTLC-success, ChannelManager: local commitment tx
assert_eq!(node_txn[2].input.len(), 1);
- check_spends!(node_txn[2], revoked_htlc_txn[0].clone());
+ check_spends!(node_txn[2], revoked_htlc_txn[0]);
// Check A's ChannelMonitor was able to generate the right spendable output descriptor
let spend_txn = check_spendable_outputs!(nodes[0], 1);
- assert_eq!(spend_txn.len(), 4);
+ assert_eq!(spend_txn.len(), 5); // Duplicated SpendableOutput due to block rescan after revoked htlc output tracking
assert_eq!(spend_txn[0], spend_txn[2]);
- check_spends!(spend_txn[0], revoked_local_txn[0].clone()); // spending to_remote output from revoked local tx
- check_spends!(spend_txn[1], node_txn[0].clone()); // spending justice tx output from revoked local tx htlc received output
- check_spends!(spend_txn[3], node_txn[2].clone()); // spending justice tx output on htlc success tx
+ check_spends!(spend_txn[0], revoked_local_txn[0]); // spending to_remote output from revoked local tx
+ check_spends!(spend_txn[1], node_txn[0]); // spending justice tx output from revoked local tx htlc received output
+ check_spends!(spend_txn[3], node_txn[2]); // spending justice tx output on htlc success tx
}
#[test]
let (payment_preimage, _payment_hash) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2]), 3000000);
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
let commitment_tx = nodes[2].node.channel_state.lock().unwrap().by_id.get_mut(&chan_2.2).unwrap().channel_monitor().get_latest_local_commitment_txn();
- check_spends!(commitment_tx[0], chan_2.3.clone());
+ check_spends!(commitment_tx[0], chan_2.3);
nodes[2].node.claim_funds(payment_preimage, 3_000_000);
check_added_monitors!(nodes[2], 1);
let updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
assert_eq!(c_txn[0], c_txn[2]);
assert_eq!(c_txn[0], c_txn[3]);
assert_eq!(commitment_tx[0], c_txn[1]);
- check_spends!(c_txn[1], chan_2.3.clone());
- check_spends!(c_txn[2], c_txn[1].clone());
+ check_spends!(c_txn[1], chan_2.3);
+ check_spends!(c_txn[2], c_txn[1]);
assert_eq!(c_txn[1].input[0].witness.clone().last().unwrap().len(), 71);
assert_eq!(c_txn[2].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
assert!(c_txn[0].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
// ChannelMonitor: claim tx, ChannelManager: local commitment tx + HTLC-timeout tx
assert_eq!(b_txn.len(), 3);
check_spends!(b_txn[1], chan_2.3); // B local commitment tx, issued by ChannelManager
- check_spends!(b_txn[2], b_txn[1].clone()); // HTLC-Timeout on B local commitment tx, issued by ChannelManager
+ check_spends!(b_txn[2], b_txn[1]); // HTLC-Timeout on B local commitment tx, issued by ChannelManager
assert_eq!(b_txn[2].input[0].witness.clone().last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
assert!(b_txn[2].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
assert_ne!(b_txn[2].lock_time, 0); // Timeout tx
- check_spends!(b_txn[0], c_txn[1].clone()); // timeout tx on C remote commitment tx, issued by ChannelMonitor, * 2 due to block rescan
+ check_spends!(b_txn[0], c_txn[1]); // timeout tx on C remote commitment tx, issued by ChannelMonitor, * 2 due to block rescan
assert_eq!(b_txn[0].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
assert!(b_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
assert_ne!(b_txn[2].lock_time, 0); // Timeout tx
// ChannelMonitor: HTLC-Success tx, ChannelManager: local commitment tx + HTLC-Success tx
assert_eq!(b_txn.len(), 3);
check_spends!(b_txn[1], chan_1.3);
- check_spends!(b_txn[2], b_txn[1].clone());
- check_spends!(b_txn[0], commitment_tx[0].clone());
+ check_spends!(b_txn[2], b_txn[1]);
+ check_spends!(b_txn[0], commitment_tx[0]);
assert_eq!(b_txn[0].input[0].witness.clone().last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
assert!(b_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
assert_eq!(b_txn[0].lock_time, 0); // Success tx
let commitment_txn = nodes[2].node.channel_state.lock().unwrap().by_id.get_mut(&chan_2.2).unwrap().channel_monitor().get_latest_local_commitment_txn();
assert_eq!(commitment_txn[0].input.len(), 1);
- check_spends!(commitment_txn[0], chan_2.3.clone());
+ check_spends!(commitment_txn[0], chan_2.3);
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![commitment_txn[0].clone()] }, 1);
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
// ChannelMonitor: timeout tx * 2, ChannelManager: local commitment tx + HTLC-timeout * 2
assert_eq!(node_txn.len(), 5);
- check_spends!(node_txn[0], commitment_txn[0].clone());
+ check_spends!(node_txn[0], commitment_txn[0]);
assert_eq!(node_txn[0].input.len(), 1);
- check_spends!(node_txn[1], commitment_txn[0].clone());
+ check_spends!(node_txn[1], commitment_txn[0]);
assert_eq!(node_txn[1].input.len(), 1);
assert_ne!(node_txn[0].input[0], node_txn[1].input[0]);
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
assert_eq!(node_txn[1].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
- check_spends!(node_txn[2], chan_2.3.clone());
- check_spends!(node_txn[3], node_txn[2].clone());
- check_spends!(node_txn[4], node_txn[2].clone());
+ check_spends!(node_txn[2], chan_2.3);
+ check_spends!(node_txn[3], node_txn[2]);
+ check_spends!(node_txn[4], node_txn[2]);
htlc_timeout_tx = node_txn[1].clone();
}
}
let htlc_success_txn: Vec<_> = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
assert_eq!(htlc_success_txn.len(), 7);
- check_spends!(htlc_success_txn[2], chan_2.3.clone());
+ check_spends!(htlc_success_txn[2], chan_2.3);
check_spends!(htlc_success_txn[3], htlc_success_txn[2]);
check_spends!(htlc_success_txn[4], htlc_success_txn[2]);
assert_eq!(htlc_success_txn[0], htlc_success_txn[5]);
assert_eq!(htlc_success_txn[1].input.len(), 1);
assert_eq!(htlc_success_txn[1].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
assert_ne!(htlc_success_txn[0].input[0], htlc_success_txn[1].input[0]);
- check_spends!(htlc_success_txn[0], commitment_txn[0].clone());
- check_spends!(htlc_success_txn[1], commitment_txn[0].clone());
+ check_spends!(htlc_success_txn[0], commitment_txn[0]);
+ check_spends!(htlc_success_txn[1], commitment_txn[0]);
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![htlc_timeout_tx] }, 200);
connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 200, true, header.bitcoin_hash());
let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 9000000).0;
let local_txn = nodes[1].node.channel_state.lock().unwrap().by_id.get_mut(&chan_1.2).unwrap().channel_monitor().get_latest_local_commitment_txn();
assert_eq!(local_txn[0].input.len(), 1);
- check_spends!(local_txn[0], chan_1.3.clone());
+ check_spends!(local_txn[0], chan_1.3);
// Give B knowledge of preimage to be able to generate a local HTLC-Success Tx
nodes[1].node.claim_funds(payment_preimage, 9_000_000);
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn[0].input.len(), 1);
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
- check_spends!(node_txn[0], local_txn[0].clone());
+ check_spends!(node_txn[0], local_txn[0]);
// 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(), 2);
- check_spends!(spend_txn[0], node_txn[0].clone());
- check_spends!(spend_txn[1], node_txn[2].clone());
+ check_spends!(spend_txn[0], node_txn[0]);
+ check_spends!(spend_txn[1], node_txn[2]);
}
fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, announce_latest: bool) {
route_payment(&nodes[0], &vec!(&nodes[1])[..], 9000000).0;
let local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get_mut(&chan_1.2).unwrap().channel_monitor().get_latest_local_commitment_txn();
assert_eq!(local_txn[0].input.len(), 1);
- check_spends!(local_txn[0], chan_1.3.clone());
+ check_spends!(local_txn[0], chan_1.3);
// Timeout HTLC on A's chain and so it can generate a HTLC-Timeout tx
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn[0].input.len(), 1);
assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
- check_spends!(node_txn[0], local_txn[0].clone());
+ check_spends!(node_txn[0], local_txn[0]);
// 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[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());
+ check_spends!(spend_txn[0], local_txn[0]);
+ check_spends!(spend_txn[1], node_txn[0]);
}
#[test]
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![closing_tx.clone()] }, 1);
let spend_txn = check_spendable_outputs!(nodes[0], 2);
assert_eq!(spend_txn.len(), 1);
- check_spends!(spend_txn[0], closing_tx.clone());
+ check_spends!(spend_txn[0], closing_tx);
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![closing_tx.clone()] }, 1);
let spend_txn = check_spendable_outputs!(nodes[1], 2);
#[test]
fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() {
- //BOLT2 Requirement: MUST offer amount_msat greater than 0.
//BOLT2 Requirement: MUST NOT offer amount_msat below the receiving node's htlc_minimum_msat (same validation check catches both of these)
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let mut route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
- route.hops[0].fee_msat = 0;
+ route.hops[0].fee_msat = 100;
let err = nodes[0].node.send_payment(route, our_payment_hash);
nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send less than their minimum HTLC value".to_string(), 1);
}
+#[test]
+fn test_update_add_htlc_bolt2_sender_zero_value_msat() {
+ //BOLT2 Requirement: MUST offer amount_msat greater than 0.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+ let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000, InitFeatures::supported(), InitFeatures::supported());
+ let mut route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
+ let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
+
+ route.hops[0].fee_msat = 0;
+
+ let err = nodes[0].node.send_payment(route, our_payment_hash);
+
+ if let Err(APIError::ChannelUnavailable{err}) = err {
+ assert_eq!(err, "Cannot send 0-msat HTLC");
+ } else {
+ assert!(false);
+ }
+ assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
+ nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send 0-msat HTLC".to_string(), 1);
+}
+
+#[test]
+fn test_update_add_htlc_bolt2_receiver_zero_value_msat() {
+ //BOLT2 Requirement: MUST offer amount_msat greater than 0.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+ let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000, InitFeatures::supported(), InitFeatures::supported());
+ let route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
+ let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
+
+ nodes[0].node.send_payment(route, our_payment_hash).unwrap();
+ check_added_monitors!(nodes[0], 1);
+ let mut updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ updates.update_add_htlcs[0].amount_msat = 0;
+
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]);
+ nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Remote side tried to send a 0-msat HTLC".to_string(), 1);
+ check_closed_broadcast!(nodes[1], true).unwrap();
+}
+
#[test]
fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() {
//BOLT 2 Requirement: MUST set cltv_expiry less than 500000000.
// Check A is able to claim to_remote output
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
assert_eq!(node_txn.len(), 1);
- check_spends!(node_txn[0], chan.3.clone());
+ check_spends!(node_txn[0], chan.3);
assert_eq!(node_txn[0].output.len(), 2);
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[0].clone()]}, 1);
let spend_txn = check_spendable_outputs!(nodes[0], 1);
assert_eq!(spend_txn.len(), 1);
- check_spends!(spend_txn[0], node_txn[0].clone());
+ check_spends!(spend_txn[0], node_txn[0]);
}
#[test]
assert_eq!(node_txn.len(), 3); // justice tx (broadcasted from ChannelMonitor) + local commitment tx + local HTLC-timeout (broadcasted from ChannelManager)
assert_eq!(node_txn[0].input.len(), 3); // Penalty txn claims to_local, offered_htlc and received_htlc outputs
assert_eq!(node_txn[0].output.len(), 1);
- check_spends!(node_txn[0], revoked_txn[0].clone());
+ check_spends!(node_txn[0], revoked_txn[0]);
let fee_1 = penalty_sum - node_txn[0].output[0].value;
feerate_1 = fee_1 * 1000 / node_txn[0].get_weight() as u64;
penalty_1 = node_txn[0].txid();
if node_txn[0].input[0].previous_output.txid == revoked_txid {
assert_eq!(node_txn[0].input.len(), 3); // Penalty txn claims to_local, offered_htlc and received_htlc outputs
assert_eq!(node_txn[0].output.len(), 1);
- check_spends!(node_txn[0], revoked_txn[0].clone());
+ check_spends!(node_txn[0], revoked_txn[0]);
penalty_2 = node_txn[0].txid();
// Verify new bumped tx is different from last claiming transaction, we don't want spurrious rebroadcast
assert_ne!(penalty_2, penalty_1);
if node_txn[0].input[0].previous_output.txid == revoked_txid {
assert_eq!(node_txn[0].input.len(), 3); // Penalty txn claims to_local, offered_htlc and received_htlc outputs
assert_eq!(node_txn[0].output.len(), 1);
- check_spends!(node_txn[0], revoked_txn[0].clone());
+ check_spends!(node_txn[0], revoked_txn[0]);
penalty_3 = node_txn[0].txid();
// Verify new bumped tx is different from last claiming transaction, we don't want spurrious rebroadcast
assert_ne!(penalty_3, penalty_2);
assert_eq!(revoked_htlc_txn.len(), 6);
if revoked_htlc_txn[0].input[0].witness.last().unwrap().len() == ACCEPTED_HTLC_SCRIPT_WEIGHT {
assert_eq!(revoked_htlc_txn[0].input.len(), 1);
- check_spends!(revoked_htlc_txn[0], revoked_local_txn[0].clone());
+ check_spends!(revoked_htlc_txn[0], revoked_local_txn[0]);
assert_eq!(revoked_htlc_txn[1].input.len(), 1);
assert_eq!(revoked_htlc_txn[1].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
- check_spends!(revoked_htlc_txn[1], revoked_local_txn[0].clone());
+ check_spends!(revoked_htlc_txn[1], revoked_local_txn[0]);
} else if revoked_htlc_txn[1].input[0].witness.last().unwrap().len() == ACCEPTED_HTLC_SCRIPT_WEIGHT {
assert_eq!(revoked_htlc_txn[1].input.len(), 1);
- check_spends!(revoked_htlc_txn[1], revoked_local_txn[0].clone());
+ check_spends!(revoked_htlc_txn[1], revoked_local_txn[0]);
assert_eq!(revoked_htlc_txn[0].input.len(), 1);
assert_eq!(revoked_htlc_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
- check_spends!(revoked_htlc_txn[0], revoked_local_txn[0].clone());
+ check_spends!(revoked_htlc_txn[0], revoked_local_txn[0]);
}
// Broadcast set of revoked txn on A
// Verify claim tx are spending revoked HTLC txn
assert_eq!(node_txn[4].input.len(), 2);
assert_eq!(node_txn[4].output.len(), 1);
- if node_txn[4].input[0].previous_output.txid == revoked_htlc_txn[0].txid() {
- assert_eq!(node_txn[4].input[1].previous_output.txid, revoked_htlc_txn[1].txid());
- } else if node_txn[4].input[0].previous_output.txid == revoked_htlc_txn[1].txid() {
- assert_eq!(node_txn[4].input[1].previous_output.txid, revoked_htlc_txn[0].txid());
- } else {
- panic!();
- }
+ check_spends!(node_txn[4], revoked_htlc_txn[0], revoked_htlc_txn[1]);
first = node_txn[4].txid();
// Store both feerates for later comparison
let fee_1 = revoked_htlc_txn[0].output[0].value + revoked_htlc_txn[1].output[0].value - node_txn[4].output[0].value;
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 2); // 2 bumped penalty txn on revoked commitment tx
- check_spends!(node_txn[0], revoked_local_txn[0].clone());
- check_spends!(node_txn[1], revoked_local_txn[0].clone());
+ check_spends!(node_txn[0], revoked_local_txn[0]);
+ check_spends!(node_txn[1], revoked_local_txn[0]);
node_txn.clear();
};
// Few more blocks to confirm penalty txn
let header_135 = connect_blocks(&nodes[0].block_notifier, 5, 130, true, header_130.bitcoin_hash());
- {
- let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert_eq!(node_txn.len(), 0);
- node_txn.clear();
- }
+ assert!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().is_empty());
let header_144 = connect_blocks(&nodes[0].block_notifier, 9, 135, true, header_135);
let node_txn = {
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
assert_eq!(node_txn[0].input.len(), 2);
- if node_txn[0].input[0].previous_output.txid == revoked_htlc_txn[0].txid() {
- assert_eq!(node_txn[0].input[1].previous_output.txid, revoked_htlc_txn[1].txid());
- } else if node_txn[0].input[0].previous_output.txid == revoked_htlc_txn[1].txid() {
- assert_eq!(node_txn[0].input[1].previous_output.txid, revoked_htlc_txn[0].txid());
- } else {
- panic!();
- }
- //// Verify bumped tx is different and 25% bump heuristic
+ check_spends!(node_txn[0], revoked_htlc_txn[0], revoked_htlc_txn[1]);
+ // Verify bumped tx is different and 25% bump heuristic
assert_ne!(first, node_txn[0].txid());
let fee_2 = revoked_htlc_txn[0].output[0].value + revoked_htlc_txn[1].output[0].value - node_txn[0].output[0].value;
let feerate_2 = fee_2 * 1000 / node_txn[0].get_weight() as u64;
connect_blocks(&nodes[0].block_notifier, 20, 145, true, header_145.bitcoin_hash());
{
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert_eq!(node_txn.len(), 1); //TODO: fix check_spend_remote_htlc lack of watch output
+ // We verify than no new transaction has been broadcast because previously
+ // we were buggy on this exact behavior by not tracking for monitoring remote HTLC outputs (see #411)
+ // which means we wouldn't see a spend of them by a justice tx and bumped justice tx
+ // were generated forever instead of safe cleaning after confirmation and ANTI_REORG_SAFE_DELAY blocks.
+ // Enforce spending of revoked htlc output by claiming transaction remove request as expected and dry
+ // up bumped justice generation.
+ assert_eq!(node_txn.len(), 0);
node_txn.clear();
}
check_closed_broadcast!(nodes[0], false);
assert_eq!(node_txn.len(), 5); // 2 * claim tx (broadcasted from ChannelMonitor) + local commitment tx + local HTLC-timeout + local HTLC-success (broadcasted from ChannelManager)
assert_eq!(node_txn[0].input.len(), 1);
assert_eq!(node_txn[1].input.len(), 1);
- check_spends!(node_txn[0], remote_txn[0].clone());
- check_spends!(node_txn[1], remote_txn[0].clone());
+ check_spends!(node_txn[0], remote_txn[0]);
+ check_spends!(node_txn[1], remote_txn[0]);
check_spends!(node_txn[2], chan.3);
check_spends!(node_txn[3], node_txn[2]);
check_spends!(node_txn[4], node_txn[2]);
assert_eq!(node_txn.len(), 2);
assert_eq!(node_txn[0].input.len(), 1);
assert_eq!(node_txn[1].input.len(), 1);
- check_spends!(node_txn[0], remote_txn[0].clone());
- check_spends!(node_txn[1], remote_txn[0].clone());
+ check_spends!(node_txn[0], remote_txn[0]);
+ check_spends!(node_txn[1], remote_txn[0]);
if node_txn[0].input[0].witness.last().unwrap().len() == ACCEPTED_HTLC_SCRIPT_WEIGHT {
let index = node_txn[0].input[0].previous_output.vout;
let fee = remote_txn[0].output[index as usize].value - node_txn[0].output[0].value;
assert_eq!(remote_txn[0].output.len(), 4);
assert_eq!(remote_txn[0].input.len(), 1);
assert_eq!(remote_txn[0].input[0].previous_output.txid, chan.3.txid());
- check_spends!(remote_txn[1], remote_txn[0].clone());
- check_spends!(remote_txn[2], remote_txn[0].clone());
+ check_spends!(remote_txn[1], remote_txn[0]);
+ check_spends!(remote_txn[2], remote_txn[0]);
// Connect blocks on node A to advance height towards TEST_FINAL_CLTV
let prev_header_100 = connect_blocks(&nodes[1].block_notifier, 100, 0, false, Default::default());
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
// ChannelMonitor: claim tx, ChannelManager: local commitment tx + HTLC-Success*2
assert_eq!(node_txn.len(), 4);
- check_spends!(node_txn[0], remote_txn[0].clone());
- check_spends!(node_txn[1], chan.3.clone());
+ check_spends!(node_txn[0], remote_txn[0]);
+ check_spends!(node_txn[1], chan.3);
check_spends!(node_txn[2], node_txn[1]);
check_spends!(node_txn[3], node_txn[1]);
assert_eq!(node_txn[0].input.len(), 2);
let partial_claim_tx = {
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 3);
- check_spends!(node_txn[1], node_txn[0].clone());
- check_spends!(node_txn[2], node_txn[0].clone());
+ check_spends!(node_txn[1], node_txn[0]);
+ check_spends!(node_txn[2], node_txn[0]);
assert_eq!(node_txn[1].input.len(), 1);
assert_eq!(node_txn[2].input.len(), 1);
node_txn[1].clone()
{
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
- check_spends!(node_txn[0], remote_txn[0].clone());
+ check_spends!(node_txn[0], remote_txn[0]);
assert_eq!(node_txn[0].input.len(), 1); //dropped HTLC
node_txn.clear();
}
{
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 1);
- check_spends!(node_txn[0], remote_txn[0].clone());
+ check_spends!(node_txn[0], remote_txn[0]);
assert_eq!(node_txn[0].input.len(), 2); //resurrected HTLC
node_txn.clear();
}
let penalty_txn = {
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
assert_eq!(node_txn.len(), 4); //ChannelMonitor: justice txn * 3, ChannelManager: local commitment tx
- check_spends!(node_txn[0], revoked_local_txn[0].clone());
- check_spends!(node_txn[1], revoked_local_txn[0].clone());
- check_spends!(node_txn[2], revoked_local_txn[0].clone());
+ check_spends!(node_txn[0], revoked_local_txn[0]);
+ check_spends!(node_txn[1], revoked_local_txn[0]);
+ check_spends!(node_txn[2], revoked_local_txn[0]);
let penalty_txn = vec![node_txn[0].clone(), node_txn[1].clone(), node_txn[2].clone()];
node_txn.clear();
penalty_txn
assert_eq!(res.channel_flags, 0);
assert_eq!(res.to_self_delay, 200);
}
+
+#[test]
+fn test_override_0msat_htlc_minimum() {
+ let mut zero_config = UserConfig::default();
+ zero_config.own_channel_config.our_htlc_minimum_msat = 0;
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(zero_config.clone())]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 16_000_000, 12_000_000, 42, Some(zero_config)).unwrap();
+ let res = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+ assert_eq!(res.htlc_minimum_msat, 1);
+
+ nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::supported(), &res);
+ let res = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
+ assert_eq!(res.htlc_minimum_msat, 1);
+}