@@ -6429,3 +6429,132 @@ fn test_bump_penalty_txn_on_revoked_commitment() {
6429
6429
nodes[ 1 ] . node . get_and_clear_pending_events ( ) ;
6430
6430
nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
6431
6431
}
6432
+
6433
+ #[ test]
6434
+ fn test_bump_penalty_txn_on_revoked_htlcs ( ) {
6435
+ // In case of penalty txn with too low feerates for getting into mempools, RBF-bump them to sure
6436
+ // we're able to claim outputs on revoked HTLC transactions before timelocks expiration
6437
+
6438
+ let nodes = create_network ( 2 , & [ None , None ] ) ;
6439
+
6440
+ let chan = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1000000 , 59000000 , LocalFeatures :: new ( ) , LocalFeatures :: new ( ) ) ;
6441
+ // Lock HTLC in both directions
6442
+ let payment_preimage = route_payment ( & nodes[ 0 ] , & vec ! ( & nodes[ 1 ] ) [ ..] , 3_000_000 ) . 0 ;
6443
+ route_payment ( & nodes[ 1 ] , & vec ! ( & nodes[ 0 ] ) [ ..] , 3_000_000 ) . 0 ;
6444
+
6445
+ let revoked_local_txn = nodes[ 1 ] . node . channel_state . lock ( ) . unwrap ( ) . by_id . get ( & chan. 2 ) . unwrap ( ) . last_local_commitment_txn . clone ( ) ;
6446
+ assert_eq ! ( revoked_local_txn[ 0 ] . input. len( ) , 1 ) ;
6447
+ assert_eq ! ( revoked_local_txn[ 0 ] . input[ 0 ] . previous_output. txid, chan. 3 . txid( ) ) ;
6448
+
6449
+ // Revoke local commitment tx
6450
+ claim_payment ( & nodes[ 0 ] , & vec ! ( & nodes[ 1 ] ) [ ..] , payment_preimage, 3_000_000 ) ;
6451
+
6452
+ let header = BlockHeader { version : 0x20000000 , prev_blockhash : Default :: default ( ) , merkle_root : Default :: default ( ) , time : 42 , bits : 42 , nonce : 42 } ;
6453
+ // B will generate both revoked HTLC-timeout/HTLC-preimage txn from revoked commitment tx
6454
+ nodes[ 1 ] . block_notifier . block_connected ( & Block { header, txdata : vec ! [ revoked_local_txn[ 0 ] . clone( ) ] } , 1 ) ;
6455
+ check_closed_broadcast ! ( nodes[ 1 ] ) ;
6456
+
6457
+ let revoked_htlc_txn = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
6458
+ assert_eq ! ( revoked_htlc_txn. len( ) , 6 ) ;
6459
+ if revoked_htlc_txn[ 0 ] . input [ 0 ] . witness . last ( ) . unwrap ( ) . len ( ) == ACCEPTED_HTLC_SCRIPT_WEIGHT {
6460
+ assert_eq ! ( revoked_htlc_txn[ 0 ] . input. len( ) , 1 ) ;
6461
+ check_spends ! ( revoked_htlc_txn[ 0 ] , revoked_local_txn[ 0 ] . clone( ) ) ;
6462
+ assert_eq ! ( revoked_htlc_txn[ 1 ] . input. len( ) , 1 ) ;
6463
+ assert_eq ! ( revoked_htlc_txn[ 1 ] . input[ 0 ] . witness. last( ) . unwrap( ) . len( ) , OFFERED_HTLC_SCRIPT_WEIGHT ) ;
6464
+ check_spends ! ( revoked_htlc_txn[ 1 ] , revoked_local_txn[ 0 ] . clone( ) ) ;
6465
+ } else {
6466
+ assert_eq ! ( revoked_htlc_txn[ 1 ] . input. len( ) , 1 ) ;
6467
+ check_spends ! ( revoked_htlc_txn[ 1 ] , revoked_local_txn[ 0 ] . clone( ) ) ;
6468
+ assert_eq ! ( revoked_htlc_txn[ 0 ] . input. len( ) , 1 ) ;
6469
+ assert_eq ! ( revoked_htlc_txn[ 0 ] . input[ 0 ] . witness. last( ) . unwrap( ) . len( ) , OFFERED_HTLC_SCRIPT_WEIGHT ) ;
6470
+ check_spends ! ( revoked_htlc_txn[ 0 ] , revoked_local_txn[ 0 ] . clone( ) ) ;
6471
+ }
6472
+
6473
+ // Broadcast set of revoked txn on A
6474
+ let header_129 = connect_blocks ( & nodes[ 0 ] . block_notifier , 128 , 1 , true , header. bitcoin_hash ( ) ) ;
6475
+ let header_130 = BlockHeader { version : 0x20000000 , prev_blockhash : header_129, merkle_root : Default :: default ( ) , time : 42 , bits : 42 , nonce : 42 } ;
6476
+ nodes[ 0 ] . block_notifier . block_connected ( & Block { header : header_130, txdata : vec ! [ revoked_local_txn[ 0 ] . clone( ) , revoked_htlc_txn[ 0 ] . clone( ) , revoked_htlc_txn[ 1 ] . clone( ) ] } , 130 ) ;
6477
+ let first;
6478
+ let second;
6479
+ let feerate_1;
6480
+ let feerate_2;
6481
+ {
6482
+ let mut node_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
6483
+ assert_eq ! ( node_txn. len( ) , 9 ) ; // 3 penalty txn on revoked commitment tx * 2 (block-rescan) + A commitment tx + 2 penalty tnx on revoked HTLC txn
6484
+ // Verify claim tx are spending revoked HTLC txn
6485
+ assert_eq ! ( node_txn[ 7 ] . input. len( ) , 1 ) ;
6486
+ assert_eq ! ( node_txn[ 7 ] . output. len( ) , 1 ) ;
6487
+ check_spends ! ( node_txn[ 7 ] , revoked_htlc_txn[ 0 ] . clone( ) ) ;
6488
+ first = node_txn[ 7 ] . txid ( ) ;
6489
+ assert_eq ! ( node_txn[ 8 ] . input. len( ) , 1 ) ;
6490
+ assert_eq ! ( node_txn[ 8 ] . output. len( ) , 1 ) ;
6491
+ check_spends ! ( node_txn[ 8 ] , revoked_htlc_txn[ 1 ] . clone( ) ) ;
6492
+ second = node_txn[ 8 ] . txid ( ) ;
6493
+ // Store both feerates for later comparison
6494
+ let fee_1 = revoked_htlc_txn[ 0 ] . output [ 0 ] . value - node_txn[ 7 ] . output [ 0 ] . value ;
6495
+ feerate_1 = fee_1 * 1000 / node_txn[ 7 ] . get_weight ( ) as u64 ;
6496
+ let fee_2 = revoked_htlc_txn[ 1 ] . output [ 0 ] . value - node_txn[ 8 ] . output [ 0 ] . value ;
6497
+ feerate_2 = fee_2 * 1000 / node_txn[ 8 ] . get_weight ( ) as u64 ;
6498
+ node_txn. clear ( ) ;
6499
+ }
6500
+
6501
+ // Connect three more block to see if bumped penalty are issued for HTLC txn
6502
+ let header_133 = connect_blocks ( & nodes[ 0 ] . block_notifier , 3 , 130 , true , header_130. bitcoin_hash ( ) ) ;
6503
+ let node_txn = {
6504
+ let mut node_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
6505
+ assert_eq ! ( node_txn. len( ) , 4 ) ; // 2 first tx : bumped claiming txn on Revoked Commitment Tx + 2 last tx : bumped claiming txn on Revoked HTLCs Txn
6506
+ check_spends ! ( node_txn[ 0 ] , revoked_local_txn[ 0 ] . clone( ) ) ;
6507
+ check_spends ! ( node_txn[ 1 ] , revoked_local_txn[ 0 ] . clone( ) ) ;
6508
+ assert_eq ! ( node_txn[ 2 ] . input. len( ) , 1 ) ;
6509
+ assert_eq ! ( node_txn[ 2 ] . output. len( ) , 1 ) ;
6510
+ assert_eq ! ( node_txn[ 3 ] . input. len( ) , 1 ) ;
6511
+ assert_eq ! ( node_txn[ 3 ] . output. len( ) , 1 ) ;
6512
+ // Verify bumped tx is different and 25% bump heuristic
6513
+ if node_txn[ 2 ] . input [ 0 ] . previous_output . txid == revoked_htlc_txn[ 0 ] . txid ( ) {
6514
+ check_spends ! ( node_txn[ 2 ] , revoked_htlc_txn[ 0 ] . clone( ) ) ;
6515
+ assert_ne ! ( first, node_txn[ 2 ] . txid( ) ) ;
6516
+ let fee = revoked_htlc_txn[ 0 ] . output [ 0 ] . value - node_txn[ 2 ] . output [ 0 ] . value ;
6517
+ let new_feerate = fee * 1000 / node_txn[ 2 ] . get_weight ( ) as u64 ;
6518
+ assert ! ( new_feerate * 100 > feerate_1 * 125 ) ;
6519
+
6520
+ check_spends ! ( node_txn[ 3 ] , revoked_htlc_txn[ 1 ] . clone( ) ) ;
6521
+ assert_ne ! ( second, node_txn[ 3 ] . txid( ) ) ;
6522
+ let fee = revoked_htlc_txn[ 1 ] . output [ 0 ] . value - node_txn[ 3 ] . output [ 0 ] . value ;
6523
+ let new_feerate = fee * 1000 / node_txn[ 3 ] . get_weight ( ) as u64 ;
6524
+ assert ! ( new_feerate * 100 > feerate_2 * 125 ) ;
6525
+ } else if node_txn[ 2 ] . input [ 0 ] . previous_output . txid == revoked_htlc_txn[ 1 ] . txid ( ) {
6526
+ check_spends ! ( node_txn[ 2 ] , revoked_htlc_txn[ 1 ] . clone( ) ) ;
6527
+ assert_ne ! ( second, node_txn[ 2 ] . txid( ) ) ;
6528
+ let fee = revoked_htlc_txn[ 1 ] . output [ 0 ] . value - node_txn[ 2 ] . output [ 0 ] . value ;
6529
+ let new_feerate = fee * 1000 / node_txn[ 2 ] . get_weight ( ) as u64 ;
6530
+ assert ! ( new_feerate * 100 > feerate_2 * 125 ) ;
6531
+
6532
+ check_spends ! ( node_txn[ 3 ] , revoked_htlc_txn[ 0 ] . clone( ) ) ;
6533
+ assert_ne ! ( first, node_txn[ 3 ] . txid( ) ) ;
6534
+ let fee = revoked_htlc_txn[ 0 ] . output [ 0 ] . value - node_txn[ 3 ] . output [ 0 ] . value ;
6535
+ let new_feerate = fee * 1000 / node_txn[ 3 ] . get_weight ( ) as u64 ;
6536
+ assert ! ( new_feerate * 100 > feerate_1 * 125 ) ;
6537
+ } else { assert ! ( false ) }
6538
+ let txn = vec ! [ node_txn[ 0 ] . clone( ) , node_txn[ 1 ] . clone( ) , node_txn[ 2 ] . clone( ) , node_txn[ 3 ] . clone( ) ] ;
6539
+ node_txn. clear ( ) ;
6540
+ txn
6541
+ } ;
6542
+ // Broadcast claim txn and confirm blocks to avoid further bumps on this outputs
6543
+ let header_134 = BlockHeader { version : 0x20000000 , prev_blockhash : header_133, merkle_root : Default :: default ( ) , time : 42 , bits : 42 , nonce : 42 } ;
6544
+ nodes[ 0 ] . block_notifier . block_connected ( & Block { header : header_134, txdata : node_txn } , 134 ) ;
6545
+ connect_blocks ( & nodes[ 0 ] . block_notifier , 6 , 134 , true , header_134. bitcoin_hash ( ) ) ;
6546
+ {
6547
+ let mut node_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
6548
+ node_txn. clear ( ) ;
6549
+ }
6550
+
6551
+ // Connect few more blocks and check only penalty transaction for to_local output have been issued
6552
+ connect_blocks ( & nodes[ 0 ] . block_notifier , 5 , 140 , true , header_134. bitcoin_hash ( ) ) ;
6553
+ {
6554
+ let mut node_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
6555
+ assert_eq ! ( node_txn. len( ) , 1 ) ;
6556
+ check_spends ! ( node_txn[ 0 ] , revoked_local_txn[ 0 ] . clone( ) ) ;
6557
+ node_txn. clear ( ) ;
6558
+ }
6559
+ check_closed_broadcast ! ( nodes[ 0 ] ) ;
6560
+ }
0 commit comments