X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fmonitor_tests.rs;h=60caed4dbdc8c0fe4974137a233bc733e8886603;hb=103180df8f3ab77d1d5282bc8546b3072aaa55ec;hp=0399ed38251ba7bc155af80323add37b86c9eef2;hpb=20f287f4392e7bdb5e83793d4024f4dc5a29ce58;p=rust-lightning diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index 0399ed38..60caed4d 100644 --- a/lightning/src/ln/monitor_tests.rs +++ b/lightning/src/ln/monitor_tests.rs @@ -1400,7 +1400,7 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) { // Create some initial channels let (_, _, chan_id, funding_tx) = - create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 11_000_000); + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 12_000_000); let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 }; assert_eq!(funding_outpoint.to_channel_id(), chan_id); @@ -1410,9 +1410,9 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) { assert_eq!(revoked_local_txn[0].input.len(), 1); assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, funding_tx.txid()); if anchors { - assert_eq!(revoked_local_txn[0].output[4].value, 10000); // to_self output + assert_eq!(revoked_local_txn[0].output[4].value, 11000); // to_self output } else { - assert_eq!(revoked_local_txn[0].output[2].value, 10000); // to_self output + assert_eq!(revoked_local_txn[0].output[2].value, 11000); // to_self output } // The to-be-revoked commitment tx should have two HTLCs, an output for each side, and an @@ -1499,11 +1499,11 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) { let anchor_outputs_value = if anchors { channel::ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 }; let as_balances = sorted_vec(vec![Balance::ClaimableAwaitingConfirmations { // to_remote output in B's revoked commitment - amount_satoshis: 1_000_000 - 11_000 - 3_000 - commitment_tx_fee - anchor_outputs_value, + amount_satoshis: 1_000_000 - 12_000 - 3_000 - commitment_tx_fee - anchor_outputs_value, confirmation_height: to_remote_conf_height, }, Balance::CounterpartyRevokedOutputClaimable { // to_self output in B's revoked commitment - amount_satoshis: 10_000, + amount_satoshis: 11_000, }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1 amount_satoshis: 3_000, }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2 @@ -1544,11 +1544,11 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) { mine_transaction(&nodes[0], &as_htlc_claim_tx[0]); assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations { // to_remote output in B's revoked commitment - amount_satoshis: 1_000_000 - 11_000 - 3_000 - commitment_tx_fee - anchor_outputs_value, + amount_satoshis: 1_000_000 - 12_000 - 3_000 - commitment_tx_fee - anchor_outputs_value, confirmation_height: to_remote_conf_height, }, Balance::CounterpartyRevokedOutputClaimable { // to_self output in B's revoked commitment - amount_satoshis: 10_000, + amount_satoshis: 11_000, }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2 amount_satoshis: 1_000, }, Balance::ClaimableAwaitingConfirmations { @@ -1561,7 +1561,7 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) { test_spendable_output(&nodes[0], &revoked_local_txn[0], false); assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable { // to_self output to B - amount_satoshis: 10_000, + amount_satoshis: 11_000, }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2 amount_satoshis: 1_000, }, Balance::ClaimableAwaitingConfirmations { @@ -1574,7 +1574,7 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) { test_spendable_output(&nodes[0], &as_htlc_claim_tx[0], false); assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable { // to_self output in B's revoked commitment - amount_satoshis: 10_000, + amount_satoshis: 11_000, }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2 amount_satoshis: 1_000, }]), @@ -1623,7 +1623,7 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) { connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1); assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable { // to_self output in B's revoked commitment - amount_satoshis: 10_000, + amount_satoshis: 11_000, }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2 amount_satoshis: 1_000, }]), @@ -1632,7 +1632,7 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) { mine_transaction(&nodes[0], &revoked_htlc_timeout_claim); assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable { // to_self output in B's revoked commitment - amount_satoshis: 10_000, + amount_satoshis: 11_000, }, Balance::ClaimableAwaitingConfirmations { amount_satoshis: revoked_htlc_timeout_claim.output[0].value, confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1, @@ -1680,7 +1680,7 @@ fn do_test_revoked_counterparty_aggregated_claims(anchors: bool) { // secret to the counterparty. However, because we always immediately take the revocation // secret from the keys_manager, we would panic at broadcast as we're trying to sign a // transaction which, from the point of view of our keys_manager, is revoked. - chanmon_cfgs[1].keys_manager.disable_revocation_policy_check = true; + chanmon_cfgs[0].keys_manager.disable_revocation_policy_check = true; let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let mut user_config = test_default_channel_config(); if anchors { @@ -2715,3 +2715,113 @@ fn test_anchors_monitor_fixes_counterparty_payment_script_on_reload() { do_test_anchors_monitor_fixes_counterparty_payment_script_on_reload(false); do_test_anchors_monitor_fixes_counterparty_payment_script_on_reload(true); } + +#[cfg(not(feature = "_test_vectors"))] +fn do_test_monitor_claims_with_random_signatures(anchors: bool, confirm_counterparty_commitment: bool) { + // Tests that our monitor claims will always use fresh random signatures (ensuring a unique + // wtxid) to prevent certain classes of transaction replacement at the bitcoin P2P layer. + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let mut user_config = test_default_channel_config(); + if anchors { + user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + user_config.manually_accept_inbound_channels = true; + } + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(user_config), Some(user_config)]); + let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let coinbase_tx = Transaction { + version: 2, + lock_time: PackedLockTime::ZERO, + input: vec![TxIn { ..Default::default() }], + output: vec![ + TxOut { + value: Amount::ONE_BTC.to_sat(), + script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(), + }, + ], + }; + if anchors { + nodes[0].wallet_source.add_utxo(bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 0 }, coinbase_tx.output[0].value); + } + + // Open a channel and route a payment. We'll let it timeout to claim it. + let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); + route_payment(&nodes[0], &[&nodes[1]], 1_000_000); + + let (closing_node, other_node) = if confirm_counterparty_commitment { + (&nodes[1], &nodes[0]) + } else { + (&nodes[0], &nodes[1]) + }; + + closing_node.node.force_close_broadcasting_latest_txn(&chan_id, &other_node.node.get_our_node_id()).unwrap(); + + // The commitment transaction comes first. + let commitment_tx = { + let mut txn = closing_node.tx_broadcaster.unique_txn_broadcast(); + assert_eq!(txn.len(), 1); + check_spends!(txn[0], funding_tx); + txn.pop().unwrap() + }; + + mine_transaction(closing_node, &commitment_tx); + check_added_monitors!(closing_node, 1); + check_closed_broadcast!(closing_node, true); + check_closed_event!(closing_node, 1, ClosureReason::HolderForceClosed, [other_node.node.get_our_node_id()], 1_000_000); + + mine_transaction(other_node, &commitment_tx); + check_added_monitors!(other_node, 1); + check_closed_broadcast!(other_node, true); + check_closed_event!(other_node, 1, ClosureReason::CommitmentTxConfirmed, [closing_node.node.get_our_node_id()], 1_000_000); + + // If we update the best block to the new height before providing the confirmed transactions, + // we'll see another broadcast of the commitment transaction. + if anchors && !confirm_counterparty_commitment && nodes[0].connect_style.borrow().updates_best_block_first() { + let _ = nodes[0].tx_broadcaster.txn_broadcast(); + } + + // Then comes the HTLC timeout transaction. + if confirm_counterparty_commitment { + connect_blocks(&nodes[0], 5); + test_spendable_output(&nodes[0], &commitment_tx, false); + connect_blocks(&nodes[0], TEST_FINAL_CLTV - 5); + } else { + connect_blocks(&nodes[0], TEST_FINAL_CLTV); + } + if anchors && !confirm_counterparty_commitment { + handle_bump_htlc_event(&nodes[0], 1); + } + let htlc_timeout_tx = { + let mut txn = nodes[0].tx_broadcaster.txn_broadcast(); + assert_eq!(txn.len(), 1); + let tx = if txn[0].input[0].previous_output.txid == commitment_tx.txid() { + txn[0].clone() + } else { + txn[1].clone() + }; + check_spends!(tx, commitment_tx, coinbase_tx); + tx + }; + + // Check we rebroadcast it with a different wtxid. + nodes[0].chain_monitor.chain_monitor.rebroadcast_pending_claims(); + if anchors && !confirm_counterparty_commitment { + handle_bump_htlc_event(&nodes[0], 1); + } + { + let mut txn = nodes[0].tx_broadcaster.txn_broadcast(); + assert_eq!(txn.len(), 1); + assert_eq!(txn[0].txid(), htlc_timeout_tx.txid()); + assert_ne!(txn[0].wtxid(), htlc_timeout_tx.wtxid()); + } +} + +#[cfg(not(feature = "_test_vectors"))] +#[test] +fn test_monitor_claims_with_random_signatures() { + do_test_monitor_claims_with_random_signatures(false, false); + do_test_monitor_claims_with_random_signatures(false, true); + do_test_monitor_claims_with_random_signatures(true, false); + do_test_monitor_claims_with_random_signatures(true, true); +}