@@ -1365,15 +1365,39 @@ fn test_revoked_counterparty_commitment_balances() {
1365
1365
do_test_revoked_counterparty_commitment_balances ( true , false ) ;
1366
1366
}
1367
1367
1368
- #[ test]
1369
- fn test_revoked_counterparty_htlc_tx_balances ( ) {
1368
+ fn do_test_revoked_counterparty_htlc_tx_balances ( anchors : bool ) {
1370
1369
// Tests `get_claimable_balances` for revocation spends of HTLC transactions.
1371
1370
let mut chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
1372
1371
chanmon_cfgs[ 1 ] . keys_manager . disable_revocation_policy_check = true ;
1373
1372
let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
1374
- let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
1373
+ let mut user_config = test_default_channel_config ( ) ;
1374
+ if anchors {
1375
+ user_config. channel_handshake_config . negotiate_anchors_zero_fee_htlc_tx = true ;
1376
+ user_config. manually_accept_inbound_channels = true ;
1377
+ }
1378
+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ Some ( user_config) , Some ( user_config) ] ) ;
1375
1379
let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
1376
1380
1381
+ let coinbase_tx = Transaction {
1382
+ version : 2 ,
1383
+ lock_time : PackedLockTime :: ZERO ,
1384
+ input : vec ! [ TxIn { ..Default :: default ( ) } ] ,
1385
+ output : vec ! [
1386
+ TxOut {
1387
+ value: Amount :: ONE_BTC . to_sat( ) ,
1388
+ script_pubkey: nodes[ 0 ] . wallet_source. get_change_script( ) . unwrap( ) ,
1389
+ } ,
1390
+ TxOut {
1391
+ value: Amount :: ONE_BTC . to_sat( ) ,
1392
+ script_pubkey: nodes[ 1 ] . wallet_source. get_change_script( ) . unwrap( ) ,
1393
+ } ,
1394
+ ] ,
1395
+ } ;
1396
+ if anchors {
1397
+ nodes[ 0 ] . wallet_source . add_utxo ( bitcoin:: OutPoint { txid : coinbase_tx. txid ( ) , vout : 0 } , coinbase_tx. output [ 0 ] . value ) ;
1398
+ nodes[ 1 ] . wallet_source . add_utxo ( bitcoin:: OutPoint { txid : coinbase_tx. txid ( ) , vout : 1 } , coinbase_tx. output [ 1 ] . value ) ;
1399
+ }
1400
+
1377
1401
// Create some initial channels
1378
1402
let ( _, _, chan_id, funding_tx) =
1379
1403
create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 11_000_000 ) ;
@@ -1385,9 +1409,15 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1385
1409
let revoked_local_txn = get_local_commitment_txn ! ( nodes[ 1 ] , chan_id) ;
1386
1410
assert_eq ! ( revoked_local_txn[ 0 ] . input. len( ) , 1 ) ;
1387
1411
assert_eq ! ( revoked_local_txn[ 0 ] . input[ 0 ] . previous_output. txid, funding_tx. txid( ) ) ;
1412
+ if anchors {
1413
+ assert_eq ! ( revoked_local_txn[ 0 ] . output[ 4 ] . value, 10000 ) ; // to_self output
1414
+ } else {
1415
+ assert_eq ! ( revoked_local_txn[ 0 ] . output[ 2 ] . value, 10000 ) ; // to_self output
1416
+ }
1388
1417
1389
- // The to-be-revoked commitment tx should have two HTLCs and an output for both sides
1390
- assert_eq ! ( revoked_local_txn[ 0 ] . output. len( ) , 4 ) ;
1418
+ // The to-be-revoked commitment tx should have two HTLCs, an output for each side, and an
1419
+ // anchor output for each side if enabled.
1420
+ assert_eq ! ( revoked_local_txn[ 0 ] . output. len( ) , if anchors { 6 } else { 4 } ) ;
1391
1421
1392
1422
claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] ] , payment_preimage) ;
1393
1423
@@ -1399,16 +1429,25 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1399
1429
check_closed_broadcast ! ( nodes[ 1 ] , true ) ;
1400
1430
check_added_monitors ! ( nodes[ 1 ] , 1 ) ;
1401
1431
check_closed_event ! ( nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed , [ nodes[ 0 ] . node. get_our_node_id( ) ] , 1000000 ) ;
1432
+ if anchors {
1433
+ handle_bump_htlc_event ( & nodes[ 1 ] , 1 ) ;
1434
+ }
1402
1435
let revoked_htlc_success = {
1403
1436
let mut txn = nodes[ 1 ] . tx_broadcaster . txn_broadcast ( ) ;
1404
1437
assert_eq ! ( txn. len( ) , 1 ) ;
1405
- assert_eq ! ( txn[ 0 ] . input. len( ) , 1 ) ;
1406
- assert_eq ! ( txn[ 0 ] . input[ 0 ] . witness. last( ) . unwrap( ) . len( ) , ACCEPTED_HTLC_SCRIPT_WEIGHT ) ;
1407
- check_spends ! ( txn[ 0 ] , revoked_local_txn[ 0 ] ) ;
1438
+ assert_eq ! ( txn[ 0 ] . input. len( ) , if anchors { 2 } else { 1 } ) ;
1439
+ assert_eq ! ( txn[ 0 ] . input[ 0 ] . previous_output. vout, if anchors { 3 } else { 1 } ) ;
1440
+ assert_eq ! ( txn[ 0 ] . input[ 0 ] . witness. last( ) . unwrap( ) . len( ) ,
1441
+ if anchors { ACCEPTED_HTLC_SCRIPT_WEIGHT_ANCHORS } else { ACCEPTED_HTLC_SCRIPT_WEIGHT } ) ;
1442
+ check_spends ! ( txn[ 0 ] , revoked_local_txn[ 0 ] , coinbase_tx) ;
1408
1443
txn. pop ( ) . unwrap ( )
1409
1444
} ;
1445
+ let revoked_htlc_success_fee = chan_feerate * revoked_htlc_success. weight ( ) as u64 / 1000 ;
1410
1446
1411
1447
connect_blocks ( & nodes[ 1 ] , TEST_FINAL_CLTV ) ;
1448
+ if anchors {
1449
+ handle_bump_htlc_event ( & nodes[ 1 ] , 2 ) ;
1450
+ }
1412
1451
let revoked_htlc_timeout = {
1413
1452
let mut txn = nodes[ 1 ] . tx_broadcaster . unique_txn_broadcast ( ) ;
1414
1453
assert_eq ! ( txn. len( ) , 2 ) ;
@@ -1418,7 +1457,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1418
1457
txn. remove ( 0 )
1419
1458
}
1420
1459
} ;
1421
- check_spends ! ( revoked_htlc_timeout, revoked_local_txn[ 0 ] ) ;
1460
+ check_spends ! ( revoked_htlc_timeout, revoked_local_txn[ 0 ] , coinbase_tx ) ;
1422
1461
assert_ne ! ( revoked_htlc_success. input[ 0 ] . previous_output, revoked_htlc_timeout. input[ 0 ] . previous_output) ;
1423
1462
assert_eq ! ( revoked_htlc_success. lock_time. 0 , 0 ) ;
1424
1463
assert_ne ! ( revoked_htlc_timeout. lock_time. 0 , 0 ) ;
@@ -1430,17 +1469,37 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1430
1469
check_closed_event ! ( nodes[ 0 ] , 1 , ClosureReason :: CommitmentTxConfirmed , [ nodes[ 1 ] . node. get_our_node_id( ) ] , 1000000 ) ;
1431
1470
let to_remote_conf_height = nodes[ 0 ] . best_block_info ( ) . 1 + ANTI_REORG_DELAY - 1 ;
1432
1471
1433
- let as_commitment_claim_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) . split_off ( 0 ) ;
1434
- assert_eq ! ( as_commitment_claim_txn. len( ) , 1 ) ;
1435
- check_spends ! ( as_commitment_claim_txn[ 0 ] , revoked_local_txn[ 0 ] ) ;
1472
+ let revoked_to_self_claim = {
1473
+ let mut as_commitment_claim_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcast ( ) ;
1474
+ assert_eq ! ( as_commitment_claim_txn. len( ) , if anchors { 2 } else { 1 } ) ;
1475
+ if anchors {
1476
+ assert_eq ! ( as_commitment_claim_txn[ 0 ] . input. len( ) , 1 ) ;
1477
+ assert_eq ! ( as_commitment_claim_txn[ 0 ] . input[ 0 ] . previous_output. vout, 4 ) ; // Separate to_remote claim
1478
+ check_spends ! ( as_commitment_claim_txn[ 0 ] , revoked_local_txn[ 0 ] ) ;
1479
+ assert_eq ! ( as_commitment_claim_txn[ 1 ] . input. len( ) , 2 ) ;
1480
+ assert_eq ! ( as_commitment_claim_txn[ 1 ] . input[ 0 ] . previous_output. vout, 2 ) ;
1481
+ assert_eq ! ( as_commitment_claim_txn[ 1 ] . input[ 1 ] . previous_output. vout, 3 ) ;
1482
+ check_spends ! ( as_commitment_claim_txn[ 1 ] , revoked_local_txn[ 0 ] ) ;
1483
+ Some ( as_commitment_claim_txn. remove ( 0 ) )
1484
+ } else {
1485
+ assert_eq ! ( as_commitment_claim_txn[ 0 ] . input. len( ) , 3 ) ;
1486
+ assert_eq ! ( as_commitment_claim_txn[ 0 ] . input[ 0 ] . previous_output. vout, 2 ) ;
1487
+ assert_eq ! ( as_commitment_claim_txn[ 0 ] . input[ 1 ] . previous_output. vout, 0 ) ;
1488
+ assert_eq ! ( as_commitment_claim_txn[ 0 ] . input[ 2 ] . previous_output. vout, 1 ) ;
1489
+ check_spends ! ( as_commitment_claim_txn[ 0 ] , revoked_local_txn[ 0 ] ) ;
1490
+ None
1491
+ }
1492
+ } ;
1436
1493
1437
1494
// The next two checks have the same balance set for A - even though we confirm a revoked HTLC
1438
1495
// transaction our balance tracking doesn't use the on-chain value so the
1439
1496
// `CounterpartyRevokedOutputClaimable` entry doesn't change.
1497
+ let commitment_tx_fee = chan_feerate *
1498
+ ( channel:: commitment_tx_base_weight ( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ;
1499
+ let anchor_outputs_value = if anchors { channel:: ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 } ;
1440
1500
let as_balances = sorted_vec ( vec ! [ Balance :: ClaimableAwaitingConfirmations {
1441
1501
// to_remote output in B's revoked commitment
1442
- amount_satoshis: 1_000_000 - 11_000 - 3_000 - chan_feerate *
1443
- ( channel:: commitment_tx_base_weight( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1502
+ amount_satoshis: 1_000_000 - 11_000 - 3_000 - commitment_tx_fee - anchor_outputs_value,
1444
1503
confirmation_height: to_remote_conf_height,
1445
1504
} , Balance :: CounterpartyRevokedOutputClaimable {
1446
1505
// to_self output in B's revoked commitment
@@ -1456,22 +1515,36 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1456
1515
mine_transaction ( & nodes[ 0 ] , & revoked_htlc_success) ;
1457
1516
let as_htlc_claim_tx = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) . split_off ( 0 ) ;
1458
1517
assert_eq ! ( as_htlc_claim_tx. len( ) , 2 ) ;
1518
+ assert_eq ! ( as_htlc_claim_tx[ 0 ] . input. len( ) , 1 ) ;
1459
1519
check_spends ! ( as_htlc_claim_tx[ 0 ] , revoked_htlc_success) ;
1460
- check_spends ! ( as_htlc_claim_tx[ 1 ] , revoked_local_txn[ 0 ] ) ; // A has to generate a new claim for the remaining revoked
1461
- // outputs (which no longer includes the spent HTLC output)
1520
+ // A has to generate a new claim for the remaining revoked outputs (which no longer includes the
1521
+ // spent HTLC output)
1522
+ assert_eq ! ( as_htlc_claim_tx[ 1 ] . input. len( ) , if anchors { 1 } else { 2 } ) ;
1523
+ assert_eq ! ( as_htlc_claim_tx[ 1 ] . input[ 0 ] . previous_output. vout, 2 ) ;
1524
+ if !anchors {
1525
+ assert_eq ! ( as_htlc_claim_tx[ 1 ] . input[ 1 ] . previous_output. vout, 0 ) ;
1526
+ }
1527
+ check_spends ! ( as_htlc_claim_tx[ 1 ] , revoked_local_txn[ 0 ] ) ;
1462
1528
1463
1529
assert_eq ! ( as_balances,
1464
1530
sorted_vec( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1465
1531
1466
1532
assert_eq ! ( as_htlc_claim_tx[ 0 ] . output. len( ) , 1 ) ;
1467
- fuzzy_assert_eq ( as_htlc_claim_tx[ 0 ] . output [ 0 ] . value ,
1468
- 3_000 - chan_feerate * ( revoked_htlc_success. weight ( ) + as_htlc_claim_tx[ 0 ] . weight ( ) ) as u64 / 1000 ) ;
1533
+ let as_revoked_htlc_success_claim_fee = chan_feerate * as_htlc_claim_tx[ 0 ] . weight ( ) as u64 / 1000 ;
1534
+ if anchors {
1535
+ // With anchors, B can pay for revoked_htlc_success's fee with additional inputs, rather
1536
+ // than with the HTLC itself.
1537
+ fuzzy_assert_eq ( as_htlc_claim_tx[ 0 ] . output [ 0 ] . value ,
1538
+ 3_000 - as_revoked_htlc_success_claim_fee) ;
1539
+ } else {
1540
+ fuzzy_assert_eq ( as_htlc_claim_tx[ 0 ] . output [ 0 ] . value ,
1541
+ 3_000 - revoked_htlc_success_fee - as_revoked_htlc_success_claim_fee) ;
1542
+ }
1469
1543
1470
1544
mine_transaction ( & nodes[ 0 ] , & as_htlc_claim_tx[ 0 ] ) ;
1471
1545
assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableAwaitingConfirmations {
1472
1546
// to_remote output in B's revoked commitment
1473
- amount_satoshis: 1_000_000 - 11_000 - 3_000 - chan_feerate *
1474
- ( channel:: commitment_tx_base_weight( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1547
+ amount_satoshis: 1_000_000 - 11_000 - 3_000 - commitment_tx_fee - anchor_outputs_value,
1475
1548
confirmation_height: to_remote_conf_height,
1476
1549
} , Balance :: CounterpartyRevokedOutputClaimable {
1477
1550
// to_self output in B's revoked commitment
@@ -1525,11 +1598,24 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1525
1598
}
1526
1599
1527
1600
mine_transaction ( & nodes[ 0 ] , & revoked_htlc_timeout) ;
1528
- let as_second_htlc_claim_tx = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) . split_off ( 0 ) ;
1529
- assert_eq ! ( as_second_htlc_claim_tx. len( ) , 2 ) ;
1530
-
1531
- check_spends ! ( as_second_htlc_claim_tx[ 0 ] , revoked_htlc_timeout) ;
1532
- check_spends ! ( as_second_htlc_claim_tx[ 1 ] , revoked_local_txn[ 0 ] ) ;
1601
+ let ( revoked_htlc_timeout_claim, revoked_to_self_claim) = {
1602
+ let mut as_second_htlc_claim_tx = nodes[ 0 ] . tx_broadcaster . txn_broadcast ( ) ;
1603
+ assert_eq ! ( as_second_htlc_claim_tx. len( ) , if anchors { 1 } else { 2 } ) ;
1604
+ if anchors {
1605
+ assert_eq ! ( as_second_htlc_claim_tx[ 0 ] . input. len( ) , 1 ) ;
1606
+ assert_eq ! ( as_second_htlc_claim_tx[ 0 ] . input[ 0 ] . previous_output. vout, 0 ) ;
1607
+ check_spends ! ( as_second_htlc_claim_tx[ 0 ] , revoked_htlc_timeout) ;
1608
+ ( as_second_htlc_claim_tx. remove ( 0 ) , revoked_to_self_claim. unwrap ( ) )
1609
+ } else {
1610
+ assert_eq ! ( as_second_htlc_claim_tx[ 0 ] . input. len( ) , 1 ) ;
1611
+ assert_eq ! ( as_second_htlc_claim_tx[ 0 ] . input[ 0 ] . previous_output. vout, 0 ) ;
1612
+ check_spends ! ( as_second_htlc_claim_tx[ 0 ] , revoked_htlc_timeout) ;
1613
+ assert_eq ! ( as_second_htlc_claim_tx[ 1 ] . input. len( ) , 1 ) ;
1614
+ assert_eq ! ( as_second_htlc_claim_tx[ 1 ] . input[ 0 ] . previous_output. vout, 2 ) ;
1615
+ check_spends ! ( as_second_htlc_claim_tx[ 1 ] , revoked_local_txn[ 0 ] ) ;
1616
+ ( as_second_htlc_claim_tx. remove ( 0 ) , as_second_htlc_claim_tx. remove ( 0 ) )
1617
+ }
1618
+ } ;
1533
1619
1534
1620
// Connect blocks to finalize the HTLC resolution with the HTLC-Timeout transaction. In a
1535
1621
// previous iteration of the revoked balance handling this would result in us "forgetting" that
@@ -1543,31 +1629,31 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1543
1629
} ] ) ,
1544
1630
sorted_vec( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1545
1631
1546
- mine_transaction ( & nodes[ 0 ] , & as_second_htlc_claim_tx [ 0 ] ) ;
1632
+ mine_transaction ( & nodes[ 0 ] , & revoked_htlc_timeout_claim ) ;
1547
1633
assert_eq ! ( sorted_vec( vec![ Balance :: CounterpartyRevokedOutputClaimable {
1548
1634
// to_self output in B's revoked commitment
1549
1635
amount_satoshis: 10_000 ,
1550
1636
} , Balance :: ClaimableAwaitingConfirmations {
1551
- amount_satoshis: as_second_htlc_claim_tx [ 0 ] . output[ 0 ] . value,
1637
+ amount_satoshis: revoked_htlc_timeout_claim . output[ 0 ] . value,
1552
1638
confirmation_height: nodes[ 0 ] . best_block_info( ) . 1 + ANTI_REORG_DELAY - 1 ,
1553
1639
} ] ) ,
1554
1640
sorted_vec( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1555
1641
1556
- mine_transaction ( & nodes[ 0 ] , & as_second_htlc_claim_tx [ 1 ] ) ;
1642
+ mine_transaction ( & nodes[ 0 ] , & revoked_to_self_claim ) ;
1557
1643
assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableAwaitingConfirmations {
1558
1644
// to_self output in B's revoked commitment
1559
- amount_satoshis: as_second_htlc_claim_tx [ 1 ] . output[ 0 ] . value,
1645
+ amount_satoshis: revoked_to_self_claim . output[ 0 ] . value,
1560
1646
confirmation_height: nodes[ 0 ] . best_block_info( ) . 1 + ANTI_REORG_DELAY - 1 ,
1561
1647
} , Balance :: ClaimableAwaitingConfirmations {
1562
- amount_satoshis: as_second_htlc_claim_tx [ 0 ] . output[ 0 ] . value,
1648
+ amount_satoshis: revoked_htlc_timeout_claim . output[ 0 ] . value,
1563
1649
confirmation_height: nodes[ 0 ] . best_block_info( ) . 1 + ANTI_REORG_DELAY - 2 ,
1564
1650
} ] ) ,
1565
1651
sorted_vec( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1566
1652
1567
1653
connect_blocks ( & nodes[ 0 ] , ANTI_REORG_DELAY - 2 ) ;
1568
- test_spendable_output ( & nodes[ 0 ] , & as_second_htlc_claim_tx [ 0 ] , false ) ;
1654
+ test_spendable_output ( & nodes[ 0 ] , & revoked_htlc_timeout_claim , false ) ;
1569
1655
connect_blocks ( & nodes[ 0 ] , 1 ) ;
1570
- test_spendable_output ( & nodes[ 0 ] , & as_second_htlc_claim_tx [ 1 ] , false ) ;
1656
+ test_spendable_output ( & nodes[ 0 ] , & revoked_to_self_claim , false ) ;
1571
1657
1572
1658
assert_eq ! ( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) , Vec :: new( ) ) ;
1573
1659
@@ -1581,7 +1667,11 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1581
1667
}
1582
1668
1583
1669
#[ test]
1584
- fn test_revoked_counterparty_aggregated_claims ( ) {
1670
+ fn test_revoked_counterparty_htlc_tx_balances ( ) {
1671
+ do_test_revoked_counterparty_htlc_tx_balances ( false ) ;
1672
+ do_test_revoked_counterparty_htlc_tx_balances ( true ) ;
1673
+ }
1674
+
1585
1675
// Tests `get_claimable_balances` for revoked counterparty commitment transactions when
1586
1676
// claiming with an aggregated claim transaction.
1587
1677
let mut chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
0 commit comments