Skip to content

Commit 7f9eac1

Browse files
committed
Add anchors coverage to test_revoked_counterparty_aggregated_claims
1 parent aaadd79 commit 7f9eac1

File tree

1 file changed

+130
-36
lines changed

1 file changed

+130
-36
lines changed

lightning/src/ln/monitor_tests.rs

+130-36
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
16461646
do_test_revoked_counterparty_htlc_tx_balances(true);
16471647
}
16481648

1649+
fn do_test_revoked_counterparty_aggregated_claims(anchors: bool) {
16491650
// Tests `get_claimable_balances` for revoked counterparty commitment transactions when
16501651
// claiming with an aggregated claim transaction.
16511652
let mut chanmon_cfgs = create_chanmon_cfgs(2);
@@ -1655,9 +1656,25 @@ fn test_revoked_counterparty_htlc_tx_balances() {
16551656
// transaction which, from the point of view of our keys_manager, is revoked.
16561657
chanmon_cfgs[1].keys_manager.disable_revocation_policy_check = true;
16571658
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1658-
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1659+
let mut user_config = test_default_channel_config();
1660+
if anchors {
1661+
user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
1662+
user_config.manually_accept_inbound_channels = true;
1663+
}
1664+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(user_config), Some(user_config)]);
16591665
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
16601666

1667+
let coinbase_tx = Transaction {
1668+
version: 2,
1669+
lock_time: PackedLockTime::ZERO,
1670+
input: vec![TxIn { ..Default::default() }],
1671+
output: vec![TxOut {
1672+
value: Amount::ONE_BTC.to_sat(),
1673+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
1674+
}],
1675+
};
1676+
nodes[0].wallet_source.add_utxo(bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 0 }, coinbase_tx.output[0].value);
1677+
16611678
let (_, _, chan_id, funding_tx) =
16621679
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 100_000_000);
16631680
let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 };
@@ -1680,9 +1697,11 @@ fn test_revoked_counterparty_htlc_tx_balances() {
16801697
// Now get the latest commitment transaction from A and then update the fee to revoke it
16811698
let as_revoked_txn = get_local_commitment_txn!(nodes[0], chan_id);
16821699

1683-
assert_eq!(as_revoked_txn.len(), 2);
1700+
assert_eq!(as_revoked_txn.len(), if anchors { 1 } else { 2 });
16841701
check_spends!(as_revoked_txn[0], funding_tx);
1685-
check_spends!(as_revoked_txn[1], as_revoked_txn[0]); // The HTLC-Claim transaction
1702+
if !anchors {
1703+
check_spends!(as_revoked_txn[1], as_revoked_txn[0]); // The HTLC-Claim transaction
1704+
}
16861705

16871706
let channel_type_features = get_channel_type_features!(nodes[0], nodes[1], chan_id);
16881707
let chan_feerate = get_feerate!(nodes[0], nodes[1], chan_id) as u64;
@@ -1721,51 +1740,93 @@ fn test_revoked_counterparty_htlc_tx_balances() {
17211740
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed, [nodes[0].node.get_our_node_id()], 1000000);
17221741
check_added_monitors!(nodes[1], 1);
17231742

1724-
let mut claim_txn: Vec<_> = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().drain(..).filter(|tx| tx.input.iter().any(|inp| inp.previous_output.txid == as_revoked_txn[0].txid())).collect();
1725-
// Currently the revoked commitment outputs are all claimed in one aggregated transaction
1726-
assert_eq!(claim_txn.len(), 1);
1727-
assert_eq!(claim_txn[0].input.len(), 3);
1728-
check_spends!(claim_txn[0], as_revoked_txn[0]);
1743+
let mut claim_txn = nodes[1].tx_broadcaster.txn_broadcast();
1744+
assert_eq!(claim_txn.len(), if anchors { 2 } else { 1 });
1745+
let revoked_to_self_claim = if anchors {
1746+
assert_eq!(claim_txn[0].input.len(), 1);
1747+
assert_eq!(claim_txn[0].input[0].previous_output.vout, 5); // Separate to_remote claim
1748+
check_spends!(claim_txn[0], as_revoked_txn[0]);
1749+
assert_eq!(claim_txn[1].input.len(), 2);
1750+
assert_eq!(claim_txn[1].input[0].previous_output.vout, 2);
1751+
assert_eq!(claim_txn[1].input[1].previous_output.vout, 3);
1752+
check_spends!(claim_txn[1], as_revoked_txn[0]);
1753+
Some(claim_txn.remove(0))
1754+
} else {
1755+
assert_eq!(claim_txn[0].input.len(), 3);
1756+
assert_eq!(claim_txn[0].input[0].previous_output.vout, 3);
1757+
assert_eq!(claim_txn[0].input[1].previous_output.vout, 0);
1758+
assert_eq!(claim_txn[0].input[2].previous_output.vout, 1);
1759+
check_spends!(claim_txn[0], as_revoked_txn[0]);
1760+
None
1761+
};
17291762

17301763
let to_remote_maturity = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
17311764

1765+
let commitment_tx_fee = chan_feerate *
1766+
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
1767+
let anchor_outputs_value = if anchors { channel::ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 };
17321768
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
17331769
// to_remote output in A's revoked commitment
17341770
amount_satoshis: 100_000 - 4_000 - 3_000,
17351771
confirmation_height: to_remote_maturity,
17361772
}, Balance::CounterpartyRevokedOutputClaimable {
17371773
// to_self output in A's revoked commitment
1738-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1739-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1774+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
17401775
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
17411776
amount_satoshis: 4_000,
17421777
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
17431778
amount_satoshis: 3_000,
17441779
}]),
17451780
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
17461781

1747-
// Confirm A's HTLC-Success tranasction which presumably raced B's claim, causing B to create a
1782+
// Confirm A's HTLC-Success transaction which presumably raced B's claim, causing B to create a
17481783
// new claim.
1749-
mine_transaction(&nodes[1], &as_revoked_txn[1]);
1784+
if anchors {
1785+
mine_transaction(&nodes[0], &as_revoked_txn[0]);
1786+
check_closed_broadcast(&nodes[0], 1, true);
1787+
check_added_monitors(&nodes[0], 1);
1788+
check_closed_event!(&nodes[0], 1, ClosureReason::CommitmentTxConfirmed, false, [nodes[1].node.get_our_node_id()], 1_000_000);
1789+
handle_bump_htlc_event(&nodes[0], 1);
1790+
}
1791+
let htlc_success_claim = if anchors {
1792+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
1793+
assert_eq!(txn.len(), 1);
1794+
check_spends!(txn[0], as_revoked_txn[0], coinbase_tx);
1795+
txn.pop().unwrap()
1796+
} else {
1797+
as_revoked_txn[1].clone()
1798+
};
1799+
mine_transaction(&nodes[1], &htlc_success_claim);
17501800
expect_payment_sent(&nodes[1], claimed_payment_preimage, None, true, false);
1751-
let mut claim_txn_2: Vec<_> = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
1752-
claim_txn_2.sort_unstable_by_key(|tx| if tx.input.iter().any(|inp| inp.previous_output.txid == as_revoked_txn[0].txid()) { 0 } else { 1 });
1801+
1802+
let mut claim_txn_2 = nodes[1].tx_broadcaster.txn_broadcast();
17531803
// Once B sees the HTLC-Success transaction it splits its claim transaction into two, though in
17541804
// theory it could re-aggregate the claims as well.
17551805
assert_eq!(claim_txn_2.len(), 2);
1756-
assert_eq!(claim_txn_2[0].input.len(), 2);
1757-
check_spends!(claim_txn_2[0], as_revoked_txn[0]);
1758-
assert_eq!(claim_txn_2[1].input.len(), 1);
1759-
check_spends!(claim_txn_2[1], as_revoked_txn[1]);
1806+
if anchors {
1807+
assert_eq!(claim_txn_2[0].input.len(), 1);
1808+
assert_eq!(claim_txn_2[0].input[0].previous_output.vout, 0);
1809+
check_spends!(claim_txn_2[0], &htlc_success_claim);
1810+
assert_eq!(claim_txn_2[1].input.len(), 1);
1811+
assert_eq!(claim_txn_2[1].input[0].previous_output.vout, 3);
1812+
check_spends!(claim_txn_2[1], as_revoked_txn[0]);
1813+
} else {
1814+
assert_eq!(claim_txn_2[0].input.len(), 1);
1815+
assert_eq!(claim_txn_2[0].input[0].previous_output.vout, 0);
1816+
check_spends!(claim_txn_2[0], as_revoked_txn[1]);
1817+
assert_eq!(claim_txn_2[1].input.len(), 2);
1818+
assert_eq!(claim_txn_2[1].input[0].previous_output.vout, 3);
1819+
assert_eq!(claim_txn_2[1].input[1].previous_output.vout, 1);
1820+
check_spends!(claim_txn_2[1], as_revoked_txn[0]);
1821+
}
17601822

17611823
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
17621824
// to_remote output in A's revoked commitment
17631825
amount_satoshis: 100_000 - 4_000 - 3_000,
17641826
confirmation_height: to_remote_maturity,
17651827
}, Balance::CounterpartyRevokedOutputClaimable {
17661828
// to_self output in A's revoked commitment
1767-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1768-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1829+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
17691830
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
17701831
amount_satoshis: 4_000,
17711832
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
@@ -1781,8 +1842,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
17811842

17821843
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
17831844
// to_self output in A's revoked commitment
1784-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1785-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1845+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
17861846
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
17871847
amount_satoshis: 4_000,
17881848
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
@@ -1793,47 +1853,75 @@ fn test_revoked_counterparty_htlc_tx_balances() {
17931853
}]),
17941854
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
17951855

1796-
mine_transaction(&nodes[1], &claim_txn_2[1]);
1856+
mine_transaction(&nodes[1], &claim_txn_2[0]);
17971857
let htlc_2_claim_maturity = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
17981858

17991859
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
18001860
// to_self output in A's revoked commitment
1801-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1802-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1861+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
18031862
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
18041863
amount_satoshis: 4_000,
18051864
}, Balance::ClaimableAwaitingConfirmations { // HTLC 2
1806-
amount_satoshis: claim_txn_2[1].output[0].value,
1865+
amount_satoshis: claim_txn_2[0].output[0].value,
18071866
confirmation_height: htlc_2_claim_maturity,
18081867
}]),
18091868
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
18101869

18111870
connect_blocks(&nodes[1], 5);
1812-
test_spendable_output(&nodes[1], &claim_txn_2[1], false);
1871+
test_spendable_output(&nodes[1], &claim_txn_2[0], false);
18131872

18141873
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
18151874
// to_self output in A's revoked commitment
1816-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1817-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1875+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
18181876
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
18191877
amount_satoshis: 4_000,
18201878
}]),
18211879
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
18221880

1823-
mine_transaction(&nodes[1], &claim_txn_2[0]);
1881+
if anchors {
1882+
mine_transactions(&nodes[1], &[&claim_txn_2[1], revoked_to_self_claim.as_ref().unwrap()]);
1883+
} else {
1884+
mine_transaction(&nodes[1], &claim_txn_2[1]);
1885+
}
18241886
let rest_claim_maturity = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
18251887

1826-
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
1827-
amount_satoshis: claim_txn_2[0].output[0].value,
1828-
confirmation_height: rest_claim_maturity,
1829-
}],
1830-
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
1888+
if anchors {
1889+
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
1890+
amount_satoshis: claim_txn_2[1].output[0].value,
1891+
confirmation_height: rest_claim_maturity,
1892+
}, Balance::ClaimableAwaitingConfirmations {
1893+
amount_satoshis: revoked_to_self_claim.as_ref().unwrap().output[0].value,
1894+
confirmation_height: rest_claim_maturity,
1895+
}],
1896+
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
1897+
} else {
1898+
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
1899+
amount_satoshis: claim_txn_2[1].output[0].value,
1900+
confirmation_height: rest_claim_maturity,
1901+
}],
1902+
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
1903+
}
18311904

18321905
assert!(nodes[1].node.get_and_clear_pending_events().is_empty()); // We shouldn't fail the payment until we spend the output
18331906

18341907
connect_blocks(&nodes[1], 5);
18351908
expect_payment_failed!(nodes[1], revoked_payment_hash, false);
1836-
test_spendable_output(&nodes[1], &claim_txn_2[0], false);
1909+
if anchors {
1910+
let events = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
1911+
assert_eq!(events.len(), 2);
1912+
for (i, event) in events.into_iter().enumerate() {
1913+
if let Event::SpendableOutputs { outputs, .. } = event {
1914+
assert_eq!(outputs.len(), 1);
1915+
let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(
1916+
&[&outputs[0]], Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
1917+
253, None, &Secp256k1::new()
1918+
).unwrap();
1919+
check_spends!(spend_tx, if i == 0 { &claim_txn_2[1] } else { revoked_to_self_claim.as_ref().unwrap() });
1920+
} else { panic!(); }
1921+
}
1922+
} else {
1923+
test_spendable_output(&nodes[1], &claim_txn_2[1], false);
1924+
}
18371925
assert!(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances().is_empty());
18381926

18391927
// Ensure that even if we connect more blocks, potentially replaying the entire chain if we're
@@ -1845,6 +1933,12 @@ fn test_revoked_counterparty_htlc_tx_balances() {
18451933
assert!(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances().is_empty());
18461934
}
18471935

1936+
#[test]
1937+
fn test_revoked_counterparty_aggregated_claims() {
1938+
do_test_revoked_counterparty_aggregated_claims(false);
1939+
do_test_revoked_counterparty_aggregated_claims(true);
1940+
}
1941+
18481942
fn do_test_restored_packages_retry(check_old_monitor_retries_after_upgrade: bool) {
18491943
// Tests that we'll retry packages that were previously timelocked after we've restored them.
18501944
let chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)