X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fmonitor_tests.rs;h=1f9a2cf3a74e3b9555e8a64c6e6ed9f143324697;hb=e61b128967e4f056a0220a002a377d4f03d23015;hp=f8508a829bcc98e43e88fcab917a27fccc8af31f;hpb=84da915a12912d031a7fef4cb232627fae0b74fc;p=rust-lightning diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index f8508a82..1f9a2cf3 100644 --- a/lightning/src/ln/monitor_tests.rs +++ b/lightning/src/ln/monitor_tests.rs @@ -10,7 +10,7 @@ //! Further functional tests which test blockchain reorganizations. #[cfg(anchors)] -use crate::chain::keysinterface::{ChannelSigner, EcdsaChannelSigner}; +use crate::sign::{ChannelSigner, EcdsaChannelSigner}; #[cfg(anchors)] use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS; use crate::chain::channelmonitor::{ANTI_REORG_DELAY, Balance}; @@ -301,26 +301,34 @@ fn do_test_claim_value_force_close(prev_commitment_tx: bool) { let sent_htlc_balance = Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 3_000, claimable_height: htlc_cltv_timeout, + payment_hash, }; let sent_htlc_timeout_balance = Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 4_000, claimable_height: htlc_cltv_timeout, + payment_hash: timeout_payment_hash, }; let received_htlc_balance = Balance::MaybePreimageClaimableHTLC { claimable_amount_satoshis: 3_000, expiry_height: htlc_cltv_timeout, + payment_hash, }; let received_htlc_timeout_balance = Balance::MaybePreimageClaimableHTLC { claimable_amount_satoshis: 4_000, expiry_height: htlc_cltv_timeout, + payment_hash: timeout_payment_hash, }; let received_htlc_claiming_balance = Balance::ContentiousClaimable { claimable_amount_satoshis: 3_000, timeout_height: htlc_cltv_timeout, + payment_hash, + payment_preimage, }; let received_htlc_timeout_claiming_balance = Balance::ContentiousClaimable { claimable_amount_satoshis: 4_000, timeout_height: htlc_cltv_timeout, + payment_hash: timeout_payment_hash, + payment_preimage: timeout_payment_preimage, }; // Before B receives the payment preimage, it only suggests the push_msat value of 1_000 sats @@ -471,7 +479,7 @@ fn do_test_claim_value_force_close(prev_commitment_tx: bool) { nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()); // When the HTLC timeout output is spendable in the next block, A should broadcast it - connect_blocks(&nodes[0], htlc_cltv_timeout - nodes[0].best_block_info().1 - 1); + connect_blocks(&nodes[0], htlc_cltv_timeout - nodes[0].best_block_info().1); let a_broadcast_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0); assert_eq!(a_broadcast_txn.len(), 2); assert_eq!(a_broadcast_txn[0].input.len(), 1); @@ -634,10 +642,12 @@ fn test_balances_on_local_commitment_htlcs() { let htlc_balance_known_preimage = Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 10_000, claimable_height: htlc_cltv_timeout, + payment_hash, }; let htlc_balance_unknown_preimage = Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 20_000, claimable_height: htlc_cltv_timeout, + payment_hash: payment_hash_2, }; assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations { @@ -762,18 +772,22 @@ fn test_no_preimage_inbound_htlc_balances() { let a_sent_htlc_balance = Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 10_000, claimable_height: htlc_cltv_timeout, + payment_hash: to_b_failed_payment_hash, }; let a_received_htlc_balance = Balance::MaybePreimageClaimableHTLC { claimable_amount_satoshis: 20_000, expiry_height: htlc_cltv_timeout, + payment_hash: to_a_failed_payment_hash, }; let b_received_htlc_balance = Balance::MaybePreimageClaimableHTLC { claimable_amount_satoshis: 10_000, expiry_height: htlc_cltv_timeout, + payment_hash: to_b_failed_payment_hash, }; let b_sent_htlc_balance = Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 20_000, claimable_height: htlc_cltv_timeout, + payment_hash: to_a_failed_payment_hash, }; // Both A and B will have an HTLC that's claimable on timeout and one that's claimable if they @@ -833,7 +847,7 @@ fn test_no_preimage_inbound_htlc_balances() { // HTLC has been spent, even after the HTLC expires. We'll also fail the inbound HTLC, but it // won't do anything as the channel is already closed. - connect_blocks(&nodes[0], TEST_FINAL_CLTV - 1); + connect_blocks(&nodes[0], TEST_FINAL_CLTV); let as_htlc_timeout_claim = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0); assert_eq!(as_htlc_timeout_claim.len(), 1); check_spends!(as_htlc_timeout_claim[0], as_txn[0]); @@ -854,7 +868,7 @@ fn test_no_preimage_inbound_htlc_balances() { // The next few blocks for B look the same as for A, though for the opposite HTLC nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clear(); - connect_blocks(&nodes[1], TEST_FINAL_CLTV - (ANTI_REORG_DELAY - 1) - 1); + connect_blocks(&nodes[1], TEST_FINAL_CLTV - (ANTI_REORG_DELAY - 1)); expect_pending_htlcs_forwardable_conditions!(nodes[1], [HTLCDestination::FailedPayment { payment_hash: to_b_failed_payment_hash }]); let bs_htlc_timeout_claim = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0); @@ -1064,12 +1078,15 @@ fn do_test_revoked_counterparty_commitment_balances(confirm_htlc_spend_first: bo }, Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 2_000, claimable_height: missing_htlc_cltv_timeout, + payment_hash: missing_htlc_payment_hash, }, Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 4_000, claimable_height: htlc_cltv_timeout, + payment_hash: timeout_payment_hash, }, Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 5_000, claimable_height: live_htlc_cltv_timeout, + payment_hash: live_payment_hash, }]), sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances())); @@ -1498,9 +1515,11 @@ fn test_revoked_counterparty_aggregated_claims() { }, Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 4_000, claimable_height: htlc_cltv_timeout, + payment_hash: revoked_payment_hash, }, Balance::MaybeTimeoutClaimableHTLC { claimable_amount_satoshis: 3_000, claimable_height: htlc_cltv_timeout, + payment_hash: claimed_payment_hash, }]), sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances())); @@ -1665,7 +1684,7 @@ fn do_test_restored_packages_retry(check_old_monitor_retries_after_upgrade: bool mine_transaction(&nodes[0], &commitment_tx); // Connect blocks until the HTLC's expiration is met, expecting a transaction broadcast. - connect_blocks(&nodes[0], TEST_FINAL_CLTV - 1); + connect_blocks(&nodes[0], TEST_FINAL_CLTV); let htlc_timeout_tx = { let mut txn = nodes[0].tx_broadcaster.txn_broadcast(); assert_eq!(txn.len(), 1); @@ -1709,6 +1728,157 @@ fn test_restored_packages_retry() { do_test_restored_packages_retry(true); } +fn do_test_monitor_rebroadcast_pending_claims(anchors: bool) { + // Test that we will retry broadcasting pending claims for a force-closed channel on every + // `ChainMonitor::rebroadcast_pending_claims` call. + if anchors { + assert!(cfg!(anchors)); + } + let mut chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let mut config = test_default_channel_config(); + if anchors { + #[cfg(anchors)] { + config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + } + } + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), Some(config)]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let (_, _, _, chan_id, funding_tx) = create_chan_between_nodes_with_value( + &nodes[0], &nodes[1], 1_000_000, 500_000_000 + ); + const HTLC_AMT_MSAT: u64 = 1_000_000; + const HTLC_AMT_SAT: u64 = HTLC_AMT_MSAT / 1000; + route_payment(&nodes[0], &[&nodes[1]], HTLC_AMT_MSAT); + + let htlc_expiry = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + 1; + + let commitment_txn = get_local_commitment_txn!(&nodes[0], &chan_id); + assert_eq!(commitment_txn.len(), if anchors { 1 /* commitment tx only */} else { 2 /* commitment and htlc timeout tx */ }); + check_spends!(&commitment_txn[0], &funding_tx); + mine_transaction(&nodes[0], &commitment_txn[0]); + check_closed_broadcast!(&nodes[0], true); + check_closed_event(&nodes[0], 1, ClosureReason::CommitmentTxConfirmed, false); + check_added_monitors(&nodes[0], 1); + + // Set up a helper closure we'll use throughout our test. We should only expect retries without + // bumps if fees have not increased after a block has been connected (assuming the height timer + // re-evaluates at every block) or after `ChainMonitor::rebroadcast_pending_claims` is called. + let mut prev_htlc_tx_feerate = None; + let mut check_htlc_retry = |should_retry: bool, should_bump: bool| -> Option { + let (htlc_tx, htlc_tx_feerate) = if anchors { + assert!(nodes[0].tx_broadcaster.txn_broadcast().is_empty()); + let mut events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events(); + assert_eq!(events.len(), if should_retry { 1 } else { 0 }); + if !should_retry { + return None; + } + #[allow(unused_assignments)] + let mut tx = Transaction { + version: 2, + lock_time: bitcoin::PackedLockTime::ZERO, + input: vec![], + output: vec![], + }; + #[allow(unused_assignments)] + let mut feerate = 0; + #[cfg(anchors)] { + feerate = if let Event::BumpTransaction(BumpTransactionEvent::HTLCResolution { + target_feerate_sat_per_1000_weight, mut htlc_descriptors, tx_lock_time, + }) = events.pop().unwrap() { + let secp = Secp256k1::new(); + assert_eq!(htlc_descriptors.len(), 1); + let descriptor = htlc_descriptors.pop().unwrap(); + assert_eq!(descriptor.commitment_txid, commitment_txn[0].txid()); + let htlc_output_idx = descriptor.htlc.transaction_output_index.unwrap() as usize; + assert!(htlc_output_idx < commitment_txn[0].output.len()); + tx.lock_time = tx_lock_time; + // Note that we don't care about actually making the HTLC transaction meet the + // feerate for the test, we just want to make sure the feerates we receive from + // the events never decrease. + tx.input.push(descriptor.unsigned_tx_input()); + let signer = nodes[0].keys_manager.derive_channel_keys( + descriptor.channel_value_satoshis, &descriptor.channel_keys_id, + ); + let per_commitment_point = signer.get_per_commitment_point( + descriptor.per_commitment_number, &secp + ); + tx.output.push(descriptor.tx_output(&per_commitment_point, &secp)); + let our_sig = signer.sign_holder_htlc_transaction(&mut tx, 0, &descriptor, &secp).unwrap(); + let witness_script = descriptor.witness_script(&per_commitment_point, &secp); + tx.input[0].witness = descriptor.tx_input_witness(&our_sig, &witness_script); + target_feerate_sat_per_1000_weight as u64 + } else { panic!("unexpected event"); }; + } + (tx, feerate) + } else { + assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty()); + let mut txn = nodes[0].tx_broadcaster.txn_broadcast(); + assert_eq!(txn.len(), if should_retry { 1 } else { 0 }); + if !should_retry { + return None; + } + let htlc_tx = txn.pop().unwrap(); + check_spends!(htlc_tx, commitment_txn[0]); + let htlc_tx_fee = HTLC_AMT_SAT - htlc_tx.output[0].value; + let htlc_tx_feerate = htlc_tx_fee * 1000 / htlc_tx.weight() as u64; + (htlc_tx, htlc_tx_feerate) + }; + if should_bump { + assert!(htlc_tx_feerate > prev_htlc_tx_feerate.take().unwrap()); + } else if let Some(prev_feerate) = prev_htlc_tx_feerate.take() { + assert_eq!(htlc_tx_feerate, prev_feerate); + } + prev_htlc_tx_feerate = Some(htlc_tx_feerate); + Some(htlc_tx) + }; + + // Connect blocks up to one before the HTLC expires. This should not result in a claim/retry. + connect_blocks(&nodes[0], htlc_expiry - nodes[0].best_block_info().1 - 1); + check_htlc_retry(false, false); + + // Connect one more block, producing our first claim. + connect_blocks(&nodes[0], 1); + check_htlc_retry(true, false); + + // Connect one more block, expecting a retry with a fee bump. Unfortunately, we cannot bump HTLC + // transactions pre-anchors. + connect_blocks(&nodes[0], 1); + check_htlc_retry(true, anchors); + + // Trigger a call and we should have another retry, but without a bump. + nodes[0].chain_monitor.chain_monitor.rebroadcast_pending_claims(); + check_htlc_retry(true, false); + + // Double the feerate and trigger a call, expecting a fee-bumped retry. + *nodes[0].fee_estimator.sat_per_kw.lock().unwrap() *= 2; + nodes[0].chain_monitor.chain_monitor.rebroadcast_pending_claims(); + check_htlc_retry(true, anchors); + + // Connect one more block, expecting a retry with a fee bump. Unfortunately, we cannot bump HTLC + // transactions pre-anchors. + connect_blocks(&nodes[0], 1); + let htlc_tx = check_htlc_retry(true, anchors).unwrap(); + + // Mine the HTLC transaction to ensure we don't retry claims while they're confirmed. + mine_transaction(&nodes[0], &htlc_tx); + // If we have a `ConnectStyle` that advertises the new block first without the transasctions, + // we'll receive an extra bumped claim. + if nodes[0].connect_style.borrow().updates_best_block_first() { + check_htlc_retry(true, anchors); + } + nodes[0].chain_monitor.chain_monitor.rebroadcast_pending_claims(); + check_htlc_retry(false, false); +} + +#[test] +fn test_monitor_timer_based_claim() { + do_test_monitor_rebroadcast_pending_claims(false); + #[cfg(anchors)] + do_test_monitor_rebroadcast_pending_claims(true); +} + #[cfg(anchors)] #[test] fn test_yield_anchors_events() {