Skip to content

Commit aaadd79

Browse files
committed
Add anchors coverage to test_revoked_counterparty_htlc_tx_balances
1 parent d97236e commit aaadd79

File tree

2 files changed

+125
-34
lines changed

2 files changed

+125
-34
lines changed

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2795,7 +2795,8 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
27952795
}
27962796

27972797
// Note that the following only works for CLTV values up to 128
2798-
pub const ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 137; //Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
2798+
pub const ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 137; // Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
2799+
pub const ACCEPTED_HTLC_SCRIPT_WEIGHT_ANCHORS: usize = 140; // Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
27992800

28002801
#[derive(PartialEq)]
28012802
pub enum HTLCType { NONE, TIMEOUT, SUCCESS }

lightning/src/ln/monitor_tests.rs

Lines changed: 123 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,15 +1339,39 @@ fn test_revoked_counterparty_commitment_balances() {
13391339
do_test_revoked_counterparty_commitment_balances(true, false);
13401340
}
13411341

1342-
#[test]
1343-
fn test_revoked_counterparty_htlc_tx_balances() {
1342+
fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) {
13441343
// Tests `get_claimable_balances` for revocation spends of HTLC transactions.
13451344
let mut chanmon_cfgs = create_chanmon_cfgs(2);
13461345
chanmon_cfgs[1].keys_manager.disable_revocation_policy_check = true;
13471346
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1348-
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1347+
let mut user_config = test_default_channel_config();
1348+
if anchors {
1349+
user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
1350+
user_config.manually_accept_inbound_channels = true;
1351+
}
1352+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(user_config), Some(user_config)]);
13491353
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
13501354

1355+
let coinbase_tx = Transaction {
1356+
version: 2,
1357+
lock_time: PackedLockTime::ZERO,
1358+
input: vec![TxIn { ..Default::default() }],
1359+
output: vec![
1360+
TxOut {
1361+
value: Amount::ONE_BTC.to_sat(),
1362+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
1363+
},
1364+
TxOut {
1365+
value: Amount::ONE_BTC.to_sat(),
1366+
script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(),
1367+
},
1368+
],
1369+
};
1370+
if anchors {
1371+
nodes[0].wallet_source.add_utxo(bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 0 }, coinbase_tx.output[0].value);
1372+
nodes[1].wallet_source.add_utxo(bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 1 }, coinbase_tx.output[1].value);
1373+
}
1374+
13511375
// Create some initial channels
13521376
let (_, _, chan_id, funding_tx) =
13531377
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 11_000_000);
@@ -1359,9 +1383,15 @@ fn test_revoked_counterparty_htlc_tx_balances() {
13591383
let revoked_local_txn = get_local_commitment_txn!(nodes[1], chan_id);
13601384
assert_eq!(revoked_local_txn[0].input.len(), 1);
13611385
assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, funding_tx.txid());
1386+
if anchors {
1387+
assert_eq!(revoked_local_txn[0].output[4].value, 10000); // to_self output
1388+
} else {
1389+
assert_eq!(revoked_local_txn[0].output[2].value, 10000); // to_self output
1390+
}
13621391

1363-
// The to-be-revoked commitment tx should have two HTLCs and an output for both sides
1364-
assert_eq!(revoked_local_txn[0].output.len(), 4);
1392+
// The to-be-revoked commitment tx should have two HTLCs, an output for each side, and an
1393+
// anchor output for each side if enabled.
1394+
assert_eq!(revoked_local_txn[0].output.len(), if anchors { 6 } else { 4 });
13651395

13661396
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage);
13671397

@@ -1373,16 +1403,25 @@ fn test_revoked_counterparty_htlc_tx_balances() {
13731403
check_closed_broadcast!(nodes[1], true);
13741404
check_added_monitors!(nodes[1], 1);
13751405
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed, [nodes[0].node.get_our_node_id()], 1000000);
1406+
if anchors {
1407+
handle_bump_htlc_event(&nodes[1], 1);
1408+
}
13761409
let revoked_htlc_success = {
13771410
let mut txn = nodes[1].tx_broadcaster.txn_broadcast();
13781411
assert_eq!(txn.len(), 1);
1379-
assert_eq!(txn[0].input.len(), 1);
1380-
assert_eq!(txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
1381-
check_spends!(txn[0], revoked_local_txn[0]);
1412+
assert_eq!(txn[0].input.len(), if anchors { 2 } else { 1 });
1413+
assert_eq!(txn[0].input[0].previous_output.vout, if anchors { 3 } else { 1 });
1414+
assert_eq!(txn[0].input[0].witness.last().unwrap().len(),
1415+
if anchors { ACCEPTED_HTLC_SCRIPT_WEIGHT_ANCHORS } else { ACCEPTED_HTLC_SCRIPT_WEIGHT });
1416+
check_spends!(txn[0], revoked_local_txn[0], coinbase_tx);
13821417
txn.pop().unwrap()
13831418
};
1419+
let revoked_htlc_success_fee = chan_feerate * revoked_htlc_success.weight() as u64 / 1000;
13841420

13851421
connect_blocks(&nodes[1], TEST_FINAL_CLTV);
1422+
if anchors {
1423+
handle_bump_htlc_event(&nodes[1], 2);
1424+
}
13861425
let revoked_htlc_timeout = {
13871426
let mut txn = nodes[1].tx_broadcaster.unique_txn_broadcast();
13881427
assert_eq!(txn.len(), 2);
@@ -1392,7 +1431,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
13921431
txn.remove(0)
13931432
}
13941433
};
1395-
check_spends!(revoked_htlc_timeout, revoked_local_txn[0]);
1434+
check_spends!(revoked_htlc_timeout, revoked_local_txn[0], coinbase_tx);
13961435
assert_ne!(revoked_htlc_success.input[0].previous_output, revoked_htlc_timeout.input[0].previous_output);
13971436
assert_eq!(revoked_htlc_success.lock_time.0, 0);
13981437
assert_ne!(revoked_htlc_timeout.lock_time.0, 0);
@@ -1404,17 +1443,37 @@ fn test_revoked_counterparty_htlc_tx_balances() {
14041443
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed, [nodes[1].node.get_our_node_id()], 1000000);
14051444
let to_remote_conf_height = nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1;
14061445

1407-
let as_commitment_claim_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
1408-
assert_eq!(as_commitment_claim_txn.len(), 1);
1409-
check_spends!(as_commitment_claim_txn[0], revoked_local_txn[0]);
1446+
let revoked_to_self_claim = {
1447+
let mut as_commitment_claim_txn = nodes[0].tx_broadcaster.txn_broadcast();
1448+
assert_eq!(as_commitment_claim_txn.len(), if anchors { 2 } else { 1 });
1449+
if anchors {
1450+
assert_eq!(as_commitment_claim_txn[0].input.len(), 1);
1451+
assert_eq!(as_commitment_claim_txn[0].input[0].previous_output.vout, 4); // Separate to_remote claim
1452+
check_spends!(as_commitment_claim_txn[0], revoked_local_txn[0]);
1453+
assert_eq!(as_commitment_claim_txn[1].input.len(), 2);
1454+
assert_eq!(as_commitment_claim_txn[1].input[0].previous_output.vout, 2);
1455+
assert_eq!(as_commitment_claim_txn[1].input[1].previous_output.vout, 3);
1456+
check_spends!(as_commitment_claim_txn[1], revoked_local_txn[0]);
1457+
Some(as_commitment_claim_txn.remove(0))
1458+
} else {
1459+
assert_eq!(as_commitment_claim_txn[0].input.len(), 3);
1460+
assert_eq!(as_commitment_claim_txn[0].input[0].previous_output.vout, 2);
1461+
assert_eq!(as_commitment_claim_txn[0].input[1].previous_output.vout, 0);
1462+
assert_eq!(as_commitment_claim_txn[0].input[2].previous_output.vout, 1);
1463+
check_spends!(as_commitment_claim_txn[0], revoked_local_txn[0]);
1464+
None
1465+
}
1466+
};
14101467

14111468
// The next two checks have the same balance set for A - even though we confirm a revoked HTLC
14121469
// transaction our balance tracking doesn't use the on-chain value so the
14131470
// `CounterpartyRevokedOutputClaimable` entry doesn't change.
1471+
let commitment_tx_fee = chan_feerate *
1472+
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
1473+
let anchor_outputs_value = if anchors { channel::ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 };
14141474
let as_balances = sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
14151475
// to_remote output in B's revoked commitment
1416-
amount_satoshis: 1_000_000 - 11_000 - 3_000 - chan_feerate *
1417-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1476+
amount_satoshis: 1_000_000 - 11_000 - 3_000 - commitment_tx_fee - anchor_outputs_value,
14181477
confirmation_height: to_remote_conf_height,
14191478
}, Balance::CounterpartyRevokedOutputClaimable {
14201479
// to_self output in B's revoked commitment
@@ -1430,22 +1489,36 @@ fn test_revoked_counterparty_htlc_tx_balances() {
14301489
mine_transaction(&nodes[0], &revoked_htlc_success);
14311490
let as_htlc_claim_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
14321491
assert_eq!(as_htlc_claim_tx.len(), 2);
1492+
assert_eq!(as_htlc_claim_tx[0].input.len(), 1);
14331493
check_spends!(as_htlc_claim_tx[0], revoked_htlc_success);
1434-
check_spends!(as_htlc_claim_tx[1], revoked_local_txn[0]); // A has to generate a new claim for the remaining revoked
1435-
// outputs (which no longer includes the spent HTLC output)
1494+
// A has to generate a new claim for the remaining revoked outputs (which no longer includes the
1495+
// spent HTLC output)
1496+
assert_eq!(as_htlc_claim_tx[1].input.len(), if anchors { 1 } else { 2 });
1497+
assert_eq!(as_htlc_claim_tx[1].input[0].previous_output.vout, 2);
1498+
if !anchors {
1499+
assert_eq!(as_htlc_claim_tx[1].input[1].previous_output.vout, 0);
1500+
}
1501+
check_spends!(as_htlc_claim_tx[1], revoked_local_txn[0]);
14361502

14371503
assert_eq!(as_balances,
14381504
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
14391505

14401506
assert_eq!(as_htlc_claim_tx[0].output.len(), 1);
1441-
fuzzy_assert_eq(as_htlc_claim_tx[0].output[0].value,
1442-
3_000 - chan_feerate * (revoked_htlc_success.weight() + as_htlc_claim_tx[0].weight()) as u64 / 1000);
1507+
let as_revoked_htlc_success_claim_fee = chan_feerate * as_htlc_claim_tx[0].weight() as u64 / 1000;
1508+
if anchors {
1509+
// With anchors, B can pay for revoked_htlc_success's fee with additional inputs, rather
1510+
// than with the HTLC itself.
1511+
fuzzy_assert_eq(as_htlc_claim_tx[0].output[0].value,
1512+
3_000 - as_revoked_htlc_success_claim_fee);
1513+
} else {
1514+
fuzzy_assert_eq(as_htlc_claim_tx[0].output[0].value,
1515+
3_000 - revoked_htlc_success_fee - as_revoked_htlc_success_claim_fee);
1516+
}
14431517

14441518
mine_transaction(&nodes[0], &as_htlc_claim_tx[0]);
14451519
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
14461520
// to_remote output in B's revoked commitment
1447-
amount_satoshis: 1_000_000 - 11_000 - 3_000 - chan_feerate *
1448-
(channel::commitment_tx_base_weight(&channel_type_features) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
1521+
amount_satoshis: 1_000_000 - 11_000 - 3_000 - commitment_tx_fee - anchor_outputs_value,
14491522
confirmation_height: to_remote_conf_height,
14501523
}, Balance::CounterpartyRevokedOutputClaimable {
14511524
// to_self output in B's revoked commitment
@@ -1499,11 +1572,24 @@ fn test_revoked_counterparty_htlc_tx_balances() {
14991572
}
15001573

15011574
mine_transaction(&nodes[0], &revoked_htlc_timeout);
1502-
let as_second_htlc_claim_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
1503-
assert_eq!(as_second_htlc_claim_tx.len(), 2);
1504-
1505-
check_spends!(as_second_htlc_claim_tx[0], revoked_htlc_timeout);
1506-
check_spends!(as_second_htlc_claim_tx[1], revoked_local_txn[0]);
1575+
let (revoked_htlc_timeout_claim, revoked_to_self_claim) = {
1576+
let mut as_second_htlc_claim_tx = nodes[0].tx_broadcaster.txn_broadcast();
1577+
assert_eq!(as_second_htlc_claim_tx.len(), if anchors { 1 } else { 2 });
1578+
if anchors {
1579+
assert_eq!(as_second_htlc_claim_tx[0].input.len(), 1);
1580+
assert_eq!(as_second_htlc_claim_tx[0].input[0].previous_output.vout, 0);
1581+
check_spends!(as_second_htlc_claim_tx[0], revoked_htlc_timeout);
1582+
(as_second_htlc_claim_tx.remove(0), revoked_to_self_claim.unwrap())
1583+
} else {
1584+
assert_eq!(as_second_htlc_claim_tx[0].input.len(), 1);
1585+
assert_eq!(as_second_htlc_claim_tx[0].input[0].previous_output.vout, 0);
1586+
check_spends!(as_second_htlc_claim_tx[0], revoked_htlc_timeout);
1587+
assert_eq!(as_second_htlc_claim_tx[1].input.len(), 1);
1588+
assert_eq!(as_second_htlc_claim_tx[1].input[0].previous_output.vout, 2);
1589+
check_spends!(as_second_htlc_claim_tx[1], revoked_local_txn[0]);
1590+
(as_second_htlc_claim_tx.remove(0), as_second_htlc_claim_tx.remove(0))
1591+
}
1592+
};
15071593

15081594
// Connect blocks to finalize the HTLC resolution with the HTLC-Timeout transaction. In a
15091595
// previous iteration of the revoked balance handling this would result in us "forgetting" that
@@ -1517,31 +1603,31 @@ fn test_revoked_counterparty_htlc_tx_balances() {
15171603
}]),
15181604
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
15191605

1520-
mine_transaction(&nodes[0], &as_second_htlc_claim_tx[0]);
1606+
mine_transaction(&nodes[0], &revoked_htlc_timeout_claim);
15211607
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
15221608
// to_self output in B's revoked commitment
15231609
amount_satoshis: 10_000,
15241610
}, Balance::ClaimableAwaitingConfirmations {
1525-
amount_satoshis: as_second_htlc_claim_tx[0].output[0].value,
1611+
amount_satoshis: revoked_htlc_timeout_claim.output[0].value,
15261612
confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
15271613
}]),
15281614
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
15291615

1530-
mine_transaction(&nodes[0], &as_second_htlc_claim_tx[1]);
1616+
mine_transaction(&nodes[0], &revoked_to_self_claim);
15311617
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
15321618
// to_self output in B's revoked commitment
1533-
amount_satoshis: as_second_htlc_claim_tx[1].output[0].value,
1619+
amount_satoshis: revoked_to_self_claim.output[0].value,
15341620
confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
15351621
}, Balance::ClaimableAwaitingConfirmations {
1536-
amount_satoshis: as_second_htlc_claim_tx[0].output[0].value,
1622+
amount_satoshis: revoked_htlc_timeout_claim.output[0].value,
15371623
confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 2,
15381624
}]),
15391625
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
15401626

15411627
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 2);
1542-
test_spendable_output(&nodes[0], &as_second_htlc_claim_tx[0], false);
1628+
test_spendable_output(&nodes[0], &revoked_htlc_timeout_claim, false);
15431629
connect_blocks(&nodes[0], 1);
1544-
test_spendable_output(&nodes[0], &as_second_htlc_claim_tx[1], false);
1630+
test_spendable_output(&nodes[0], &revoked_to_self_claim, false);
15451631

15461632
assert_eq!(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances(), Vec::new());
15471633

@@ -1555,7 +1641,11 @@ fn test_revoked_counterparty_htlc_tx_balances() {
15551641
}
15561642

15571643
#[test]
1558-
fn test_revoked_counterparty_aggregated_claims() {
1644+
fn test_revoked_counterparty_htlc_tx_balances() {
1645+
do_test_revoked_counterparty_htlc_tx_balances(false);
1646+
do_test_revoked_counterparty_htlc_tx_balances(true);
1647+
}
1648+
15591649
// Tests `get_claimable_balances` for revoked counterparty commitment transactions when
15601650
// claiming with an aggregated claim transaction.
15611651
let mut chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)