Skip to content

Commit 189c467

Browse files
committed
Add anchors coverage to test_revoked_counterparty_aggregated_claims
1 parent d2f149c commit 189c467

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
@@ -1672,6 +1672,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
16721672
do_test_revoked_counterparty_htlc_tx_balances(true);
16731673
}
16741674

1675+
fn do_test_revoked_counterparty_aggregated_claims(anchors: bool) {
16751676
// Tests `get_claimable_balances` for revoked counterparty commitment transactions when
16761677
// claiming with an aggregated claim transaction.
16771678
let mut chanmon_cfgs = create_chanmon_cfgs(2);
@@ -1681,9 +1682,25 @@ fn test_revoked_counterparty_htlc_tx_balances() {
16811682
// transaction which, from the point of view of our keys_manager, is revoked.
16821683
chanmon_cfgs[1].keys_manager.disable_revocation_policy_check = true;
16831684
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1684-
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1685+
let mut user_config = test_default_channel_config();
1686+
if anchors {
1687+
user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
1688+
user_config.manually_accept_inbound_channels = true;
1689+
}
1690+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(user_config), Some(user_config)]);
16851691
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
16861692

1693+
let coinbase_tx = Transaction {
1694+
version: 2,
1695+
lock_time: PackedLockTime::ZERO,
1696+
input: vec![TxIn { ..Default::default() }],
1697+
output: vec![TxOut {
1698+
value: Amount::ONE_BTC.to_sat(),
1699+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
1700+
}],
1701+
};
1702+
nodes[0].wallet_source.add_utxo(bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 0 }, coinbase_tx.output[0].value);
1703+
16871704
let (_, _, chan_id, funding_tx) =
16881705
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 100_000_000);
16891706
let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 };
@@ -1706,9 +1723,11 @@ fn test_revoked_counterparty_htlc_tx_balances() {
17061723
// Now get the latest commitment transaction from A and then update the fee to revoke it
17071724
let as_revoked_txn = get_local_commitment_txn!(nodes[0], chan_id);
17081725

1709-
assert_eq!(as_revoked_txn.len(), 2);
1726+
assert_eq!(as_revoked_txn.len(), if anchors { 1 } else { 2 });
17101727
check_spends!(as_revoked_txn[0], funding_tx);
1711-
check_spends!(as_revoked_txn[1], as_revoked_txn[0]); // The HTLC-Claim transaction
1728+
if !anchors {
1729+
check_spends!(as_revoked_txn[1], as_revoked_txn[0]); // The HTLC-Claim transaction
1730+
}
17121731

17131732
let channel_type_features = get_channel_type_features!(nodes[0], nodes[1], chan_id);
17141733
let chan_feerate = get_feerate!(nodes[0], nodes[1], chan_id) as u64;
@@ -1747,51 +1766,93 @@ fn test_revoked_counterparty_htlc_tx_balances() {
17471766
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed, [nodes[0].node.get_our_node_id()], 1000000);
17481767
check_added_monitors!(nodes[1], 1);
17491768

1750-
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();
1751-
// Currently the revoked commitment outputs are all claimed in one aggregated transaction
1752-
assert_eq!(claim_txn.len(), 1);
1753-
assert_eq!(claim_txn[0].input.len(), 3);
1754-
check_spends!(claim_txn[0], as_revoked_txn[0]);
1769+
let mut claim_txn = nodes[1].tx_broadcaster.txn_broadcast();
1770+
assert_eq!(claim_txn.len(), if anchors { 2 } else { 1 });
1771+
let revoked_to_self_claim = if anchors {
1772+
assert_eq!(claim_txn[0].input.len(), 1);
1773+
assert_eq!(claim_txn[0].input[0].previous_output.vout, 5); // Separate to_remote claim
1774+
check_spends!(claim_txn[0], as_revoked_txn[0]);
1775+
assert_eq!(claim_txn[1].input.len(), 2);
1776+
assert_eq!(claim_txn[1].input[0].previous_output.vout, 2);
1777+
assert_eq!(claim_txn[1].input[1].previous_output.vout, 3);
1778+
check_spends!(claim_txn[1], as_revoked_txn[0]);
1779+
Some(claim_txn.remove(0))
1780+
} else {
1781+
assert_eq!(claim_txn[0].input.len(), 3);
1782+
assert_eq!(claim_txn[0].input[0].previous_output.vout, 3);
1783+
assert_eq!(claim_txn[0].input[1].previous_output.vout, 0);
1784+
assert_eq!(claim_txn[0].input[2].previous_output.vout, 1);
1785+
check_spends!(claim_txn[0], as_revoked_txn[0]);
1786+
None
1787+
};
17551788

17561789
let to_remote_maturity = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
17571790

1791+
let commitment_tx_fee = chan_feerate *
1792+
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
1793+
let anchor_outputs_value = if anchors { channel::ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 };
17581794
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
17591795
// to_remote output in A's revoked commitment
17601796
amount_satoshis: 100_000 - 4_000 - 3_000,
17611797
confirmation_height: to_remote_maturity,
17621798
}, Balance::CounterpartyRevokedOutputClaimable {
17631799
// to_self output in A's revoked commitment
1764-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1765-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1800+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
17661801
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
17671802
amount_satoshis: 4_000,
17681803
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
17691804
amount_satoshis: 3_000,
17701805
}]),
17711806
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
17721807

1773-
// Confirm A's HTLC-Success tranasction which presumably raced B's claim, causing B to create a
1808+
// Confirm A's HTLC-Success transaction which presumably raced B's claim, causing B to create a
17741809
// new claim.
1775-
mine_transaction(&nodes[1], &as_revoked_txn[1]);
1810+
if anchors {
1811+
mine_transaction(&nodes[0], &as_revoked_txn[0]);
1812+
check_closed_broadcast(&nodes[0], 1, true);
1813+
check_added_monitors(&nodes[0], 1);
1814+
check_closed_event!(&nodes[0], 1, ClosureReason::CommitmentTxConfirmed, false, [nodes[1].node.get_our_node_id()], 1_000_000);
1815+
handle_bump_htlc_event(&nodes[0], 1);
1816+
}
1817+
let htlc_success_claim = if anchors {
1818+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
1819+
assert_eq!(txn.len(), 1);
1820+
check_spends!(txn[0], as_revoked_txn[0], coinbase_tx);
1821+
txn.pop().unwrap()
1822+
} else {
1823+
as_revoked_txn[1].clone()
1824+
};
1825+
mine_transaction(&nodes[1], &htlc_success_claim);
17761826
expect_payment_sent(&nodes[1], claimed_payment_preimage, None, true, false);
1777-
let mut claim_txn_2: Vec<_> = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
1778-
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 });
1827+
1828+
let mut claim_txn_2 = nodes[1].tx_broadcaster.txn_broadcast();
17791829
// Once B sees the HTLC-Success transaction it splits its claim transaction into two, though in
17801830
// theory it could re-aggregate the claims as well.
17811831
assert_eq!(claim_txn_2.len(), 2);
1782-
assert_eq!(claim_txn_2[0].input.len(), 2);
1783-
check_spends!(claim_txn_2[0], as_revoked_txn[0]);
1784-
assert_eq!(claim_txn_2[1].input.len(), 1);
1785-
check_spends!(claim_txn_2[1], as_revoked_txn[1]);
1832+
if anchors {
1833+
assert_eq!(claim_txn_2[0].input.len(), 1);
1834+
assert_eq!(claim_txn_2[0].input[0].previous_output.vout, 0);
1835+
check_spends!(claim_txn_2[0], &htlc_success_claim);
1836+
assert_eq!(claim_txn_2[1].input.len(), 1);
1837+
assert_eq!(claim_txn_2[1].input[0].previous_output.vout, 3);
1838+
check_spends!(claim_txn_2[1], as_revoked_txn[0]);
1839+
} else {
1840+
assert_eq!(claim_txn_2[0].input.len(), 1);
1841+
assert_eq!(claim_txn_2[0].input[0].previous_output.vout, 0);
1842+
check_spends!(claim_txn_2[0], as_revoked_txn[1]);
1843+
assert_eq!(claim_txn_2[1].input.len(), 2);
1844+
assert_eq!(claim_txn_2[1].input[0].previous_output.vout, 3);
1845+
assert_eq!(claim_txn_2[1].input[1].previous_output.vout, 1);
1846+
check_spends!(claim_txn_2[1], as_revoked_txn[0]);
1847+
}
17861848

17871849
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
17881850
// to_remote output in A's revoked commitment
17891851
amount_satoshis: 100_000 - 4_000 - 3_000,
17901852
confirmation_height: to_remote_maturity,
17911853
}, Balance::CounterpartyRevokedOutputClaimable {
17921854
// to_self output in A's revoked commitment
1793-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1794-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1855+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
17951856
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
17961857
amount_satoshis: 4_000,
17971858
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
@@ -1807,8 +1868,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
18071868

18081869
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
18091870
// to_self output in A's revoked commitment
1810-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1811-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1871+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
18121872
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
18131873
amount_satoshis: 4_000,
18141874
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
@@ -1819,47 +1879,75 @@ fn test_revoked_counterparty_htlc_tx_balances() {
18191879
}]),
18201880
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
18211881

1822-
mine_transaction(&nodes[1], &claim_txn_2[1]);
1882+
mine_transaction(&nodes[1], &claim_txn_2[0]);
18231883
let htlc_2_claim_maturity = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
18241884

18251885
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
18261886
// to_self output in A's revoked commitment
1827-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1828-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1887+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
18291888
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
18301889
amount_satoshis: 4_000,
18311890
}, Balance::ClaimableAwaitingConfirmations { // HTLC 2
1832-
amount_satoshis: claim_txn_2[1].output[0].value,
1891+
amount_satoshis: claim_txn_2[0].output[0].value,
18331892
confirmation_height: htlc_2_claim_maturity,
18341893
}]),
18351894
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
18361895

18371896
connect_blocks(&nodes[1], 5);
1838-
test_spendable_output(&nodes[1], &claim_txn_2[1], false);
1897+
test_spendable_output(&nodes[1], &claim_txn_2[0], false);
18391898

18401899
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
18411900
// to_self output in A's revoked commitment
1842-
amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1843-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1901+
amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
18441902
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
18451903
amount_satoshis: 4_000,
18461904
}]),
18471905
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
18481906

1849-
mine_transaction(&nodes[1], &claim_txn_2[0]);
1907+
if anchors {
1908+
mine_transactions(&nodes[1], &[&claim_txn_2[1], revoked_to_self_claim.as_ref().unwrap()]);
1909+
} else {
1910+
mine_transaction(&nodes[1], &claim_txn_2[1]);
1911+
}
18501912
let rest_claim_maturity = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
18511913

1852-
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
1853-
amount_satoshis: claim_txn_2[0].output[0].value,
1854-
confirmation_height: rest_claim_maturity,
1855-
}],
1856-
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
1914+
if anchors {
1915+
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
1916+
amount_satoshis: claim_txn_2[1].output[0].value,
1917+
confirmation_height: rest_claim_maturity,
1918+
}, Balance::ClaimableAwaitingConfirmations {
1919+
amount_satoshis: revoked_to_self_claim.as_ref().unwrap().output[0].value,
1920+
confirmation_height: rest_claim_maturity,
1921+
}],
1922+
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
1923+
} else {
1924+
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
1925+
amount_satoshis: claim_txn_2[1].output[0].value,
1926+
confirmation_height: rest_claim_maturity,
1927+
}],
1928+
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
1929+
}
18571930

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

18601933
connect_blocks(&nodes[1], 5);
18611934
expect_payment_failed!(nodes[1], revoked_payment_hash, false);
1862-
test_spendable_output(&nodes[1], &claim_txn_2[0], false);
1935+
if anchors {
1936+
let events = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
1937+
assert_eq!(events.len(), 2);
1938+
for (i, event) in events.into_iter().enumerate() {
1939+
if let Event::SpendableOutputs { outputs, .. } = event {
1940+
assert_eq!(outputs.len(), 1);
1941+
let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(
1942+
&[&outputs[0]], Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
1943+
253, None, &Secp256k1::new()
1944+
).unwrap();
1945+
check_spends!(spend_tx, if i == 0 { &claim_txn_2[1] } else { revoked_to_self_claim.as_ref().unwrap() });
1946+
} else { panic!(); }
1947+
}
1948+
} else {
1949+
test_spendable_output(&nodes[1], &claim_txn_2[1], false);
1950+
}
18631951
assert!(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances().is_empty());
18641952

18651953
// Ensure that even if we connect more blocks, potentially replaying the entire chain if we're
@@ -1871,6 +1959,12 @@ fn test_revoked_counterparty_htlc_tx_balances() {
18711959
assert!(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances().is_empty());
18721960
}
18731961

1962+
#[test]
1963+
fn test_revoked_counterparty_aggregated_claims() {
1964+
do_test_revoked_counterparty_aggregated_claims(false);
1965+
do_test_revoked_counterparty_aggregated_claims(true);
1966+
}
1967+
18741968
fn do_test_restored_packages_retry(check_old_monitor_retries_after_upgrade: bool) {
18751969
// Tests that we'll retry packages that were previously timelocked after we've restored them.
18761970
let chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)