@@ -1339,15 +1339,39 @@ fn test_revoked_counterparty_commitment_balances() {
1339
1339
do_test_revoked_counterparty_commitment_balances ( true , false ) ;
1340
1340
}
1341
1341
1342
- #[ test]
1343
- fn test_revoked_counterparty_htlc_tx_balances ( ) {
1342
+ fn do_test_revoked_counterparty_htlc_tx_balances ( anchors : bool ) {
1344
1343
// Tests `get_claimable_balances` for revocation spends of HTLC transactions.
1345
1344
let mut chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
1346
1345
chanmon_cfgs[ 1 ] . keys_manager . disable_revocation_policy_check = true ;
1347
1346
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) ] ) ;
1349
1353
let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
1350
1354
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
+
1351
1375
// Create some initial channels
1352
1376
let ( _, _, chan_id, funding_tx) =
1353
1377
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() {
1359
1383
let revoked_local_txn = get_local_commitment_txn ! ( nodes[ 1 ] , chan_id) ;
1360
1384
assert_eq ! ( revoked_local_txn[ 0 ] . input. len( ) , 1 ) ;
1361
1385
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
+ }
1362
1391
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 } ) ;
1365
1395
1366
1396
claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] ] , payment_preimage) ;
1367
1397
@@ -1373,16 +1403,25 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1373
1403
check_closed_broadcast ! ( nodes[ 1 ] , true ) ;
1374
1404
check_added_monitors ! ( nodes[ 1 ] , 1 ) ;
1375
1405
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
+ }
1376
1409
let revoked_htlc_success = {
1377
1410
let mut txn = nodes[ 1 ] . tx_broadcaster . txn_broadcast ( ) ;
1378
1411
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) ;
1382
1417
txn. pop ( ) . unwrap ( )
1383
1418
} ;
1419
+ let revoked_htlc_success_fee = chan_feerate * revoked_htlc_success. weight ( ) as u64 / 1000 ;
1384
1420
1385
1421
connect_blocks ( & nodes[ 1 ] , TEST_FINAL_CLTV ) ;
1422
+ if anchors {
1423
+ handle_bump_htlc_event ( & nodes[ 1 ] , 2 ) ;
1424
+ }
1386
1425
let revoked_htlc_timeout = {
1387
1426
let mut txn = nodes[ 1 ] . tx_broadcaster . unique_txn_broadcast ( ) ;
1388
1427
assert_eq ! ( txn. len( ) , 2 ) ;
@@ -1392,7 +1431,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1392
1431
txn. remove ( 0 )
1393
1432
}
1394
1433
} ;
1395
- check_spends ! ( revoked_htlc_timeout, revoked_local_txn[ 0 ] ) ;
1434
+ check_spends ! ( revoked_htlc_timeout, revoked_local_txn[ 0 ] , coinbase_tx ) ;
1396
1435
assert_ne ! ( revoked_htlc_success. input[ 0 ] . previous_output, revoked_htlc_timeout. input[ 0 ] . previous_output) ;
1397
1436
assert_eq ! ( revoked_htlc_success. lock_time. 0 , 0 ) ;
1398
1437
assert_ne ! ( revoked_htlc_timeout. lock_time. 0 , 0 ) ;
@@ -1404,17 +1443,37 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1404
1443
check_closed_event ! ( nodes[ 0 ] , 1 , ClosureReason :: CommitmentTxConfirmed , [ nodes[ 1 ] . node. get_our_node_id( ) ] , 1000000 ) ;
1405
1444
let to_remote_conf_height = nodes[ 0 ] . best_block_info ( ) . 1 + ANTI_REORG_DELAY - 1 ;
1406
1445
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
+ } ;
1410
1467
1411
1468
// The next two checks have the same balance set for A - even though we confirm a revoked HTLC
1412
1469
// transaction our balance tracking doesn't use the on-chain value so the
1413
1470
// `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 } ;
1414
1474
let as_balances = sorted_vec ( vec ! [ Balance :: ClaimableAwaitingConfirmations {
1415
1475
// 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,
1418
1477
confirmation_height: to_remote_conf_height,
1419
1478
} , Balance :: CounterpartyRevokedOutputClaimable {
1420
1479
// to_self output in B's revoked commitment
@@ -1430,22 +1489,36 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1430
1489
mine_transaction ( & nodes[ 0 ] , & revoked_htlc_success) ;
1431
1490
let as_htlc_claim_tx = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) . split_off ( 0 ) ;
1432
1491
assert_eq ! ( as_htlc_claim_tx. len( ) , 2 ) ;
1492
+ assert_eq ! ( as_htlc_claim_tx[ 0 ] . input. len( ) , 1 ) ;
1433
1493
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 ] ) ;
1436
1502
1437
1503
assert_eq ! ( as_balances,
1438
1504
sorted_vec( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1439
1505
1440
1506
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
+ }
1443
1517
1444
1518
mine_transaction ( & nodes[ 0 ] , & as_htlc_claim_tx[ 0 ] ) ;
1445
1519
assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableAwaitingConfirmations {
1446
1520
// 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,
1449
1522
confirmation_height: to_remote_conf_height,
1450
1523
} , Balance :: CounterpartyRevokedOutputClaimable {
1451
1524
// to_self output in B's revoked commitment
@@ -1499,11 +1572,24 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1499
1572
}
1500
1573
1501
1574
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
+ } ;
1507
1593
1508
1594
// Connect blocks to finalize the HTLC resolution with the HTLC-Timeout transaction. In a
1509
1595
// 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() {
1517
1603
} ] ) ,
1518
1604
sorted_vec( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1519
1605
1520
- mine_transaction ( & nodes[ 0 ] , & as_second_htlc_claim_tx [ 0 ] ) ;
1606
+ mine_transaction ( & nodes[ 0 ] , & revoked_htlc_timeout_claim ) ;
1521
1607
assert_eq ! ( sorted_vec( vec![ Balance :: CounterpartyRevokedOutputClaimable {
1522
1608
// to_self output in B's revoked commitment
1523
1609
amount_satoshis: 10_000 ,
1524
1610
} , Balance :: ClaimableAwaitingConfirmations {
1525
- amount_satoshis: as_second_htlc_claim_tx [ 0 ] . output[ 0 ] . value,
1611
+ amount_satoshis: revoked_htlc_timeout_claim . output[ 0 ] . value,
1526
1612
confirmation_height: nodes[ 0 ] . best_block_info( ) . 1 + ANTI_REORG_DELAY - 1 ,
1527
1613
} ] ) ,
1528
1614
sorted_vec( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1529
1615
1530
- mine_transaction ( & nodes[ 0 ] , & as_second_htlc_claim_tx [ 1 ] ) ;
1616
+ mine_transaction ( & nodes[ 0 ] , & revoked_to_self_claim ) ;
1531
1617
assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableAwaitingConfirmations {
1532
1618
// 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,
1534
1620
confirmation_height: nodes[ 0 ] . best_block_info( ) . 1 + ANTI_REORG_DELAY - 1 ,
1535
1621
} , Balance :: ClaimableAwaitingConfirmations {
1536
- amount_satoshis: as_second_htlc_claim_tx [ 0 ] . output[ 0 ] . value,
1622
+ amount_satoshis: revoked_htlc_timeout_claim . output[ 0 ] . value,
1537
1623
confirmation_height: nodes[ 0 ] . best_block_info( ) . 1 + ANTI_REORG_DELAY - 2 ,
1538
1624
} ] ) ,
1539
1625
sorted_vec( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1540
1626
1541
1627
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 ) ;
1543
1629
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 ) ;
1545
1631
1546
1632
assert_eq ! ( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) , Vec :: new( ) ) ;
1547
1633
@@ -1555,7 +1641,11 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1555
1641
}
1556
1642
1557
1643
#[ 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
+
1559
1649
// Tests `get_claimable_balances` for revoked counterparty commitment transactions when
1560
1650
// claiming with an aggregated claim transaction.
1561
1651
let mut chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
0 commit comments