@@ -460,8 +460,10 @@ pub struct ChannelMonitor {
460
460
// Used to track outpoint in the process of being claimed by our transactions. We need to scan all transactions
461
461
// for inputs spending this. If height timer (u32) is expired and claim tx hasn't reached enough confirmations
462
462
// before, use TxMaterial to regenerate a new claim tx with a satoshis-per-1000-weight-units higher than last
463
- // one (u64).
464
- our_claim_txn_waiting_first_conf : HashMap < BitcoinOutPoint , ( u32 , TxMaterial , u64 ) > ,
463
+ // one (u64), if timelock expiration (u32) is near, decrease height timer, the in-between bumps delay.
464
+ // Last field cached (u32) is height of outpoint confirmation, which is needed to flush this tracker
465
+ // in case of reorgs, given block timer are scaled on timer expiration we can't deduce from it original height.
466
+ our_claim_txn_waiting_first_conf : HashMap < BitcoinOutPoint , ( u32 , TxMaterial , u64 , u32 , u32 ) > ,
465
467
466
468
// Used to track onchain events, i.e transactions parts of channels confirmed on chain, on which
467
469
// we have to take actions once they reach enough confs. Key is a block height timer, i.e we enforce
@@ -624,6 +626,15 @@ impl ChannelMonitor {
624
626
tx_weight
625
627
}
626
628
629
+ fn get_height_timer ( current_height : u32 , timelock_expiration : u32 ) -> u32 {
630
+ if timelock_expiration <= current_height || timelock_expiration - current_height <= 3 {
631
+ return current_height + 1
632
+ } else if timelock_expiration - current_height <= 15 {
633
+ return current_height + 3
634
+ }
635
+ current_height + 15
636
+ }
637
+
627
638
#[ inline]
628
639
fn place_secret ( idx : u64 ) -> u8 {
629
640
for i in 0 ..48 {
@@ -1129,6 +1140,8 @@ impl ChannelMonitor {
1129
1140
}
1130
1141
}
1131
1142
writer. write_all ( & byte_utils:: be64_to_array ( claim_tx_data. 2 ) ) ?;
1143
+ writer. write_all ( & byte_utils:: be32_to_array ( claim_tx_data. 3 ) ) ?;
1144
+ writer. write_all ( & byte_utils:: be32_to_array ( claim_tx_data. 4 ) ) ?;
1132
1145
}
1133
1146
1134
1147
writer. write_all ( & byte_utils:: be64_to_array ( self . onchain_events_waiting_threshold_conf . len ( ) as u64 ) ) ?;
@@ -1283,7 +1296,7 @@ impl ChannelMonitor {
1283
1296
witness : Vec :: new ( ) ,
1284
1297
} ) ;
1285
1298
inputs_desc. push ( InputDescriptors :: RevokedOutput ) ;
1286
- inputs_info. push ( ( None , outp. value ) ) ;
1299
+ inputs_info. push ( ( None , outp. value , self . our_to_self_delay as u32 ) ) ;
1287
1300
total_value += outp. value ;
1288
1301
} else if Some ( & outp. script_pubkey ) == local_payment_p2wpkh. as_ref ( ) {
1289
1302
spendable_outputs. push ( SpendableOutputDescriptor :: DynamicOutputP2WPKH {
@@ -1347,7 +1360,7 @@ impl ChannelMonitor {
1347
1360
if htlc. cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER {
1348
1361
inputs. push ( input) ;
1349
1362
inputs_desc. push ( if htlc. offered { InputDescriptors :: RevokedOfferedHTLC } else { InputDescriptors :: RevokedReceivedHTLC } ) ;
1350
- inputs_info. push ( ( Some ( idx) , tx. output [ transaction_output_index as usize ] . value ) ) ;
1363
+ inputs_info. push ( ( Some ( idx) , tx. output [ transaction_output_index as usize ] . value , htlc . cltv_expiry ) ) ;
1351
1364
total_value += tx. output [ transaction_output_index as usize ] . value ;
1352
1365
} else {
1353
1366
let mut single_htlc_tx = Transaction {
@@ -1360,14 +1373,15 @@ impl ChannelMonitor {
1360
1373
} ) ,
1361
1374
} ;
1362
1375
let predicted_weight = single_htlc_tx. get_weight ( ) + Self :: get_witnesses_weight ( & [ if htlc. offered { InputDescriptors :: RevokedOfferedHTLC } else { InputDescriptors :: RevokedReceivedHTLC } ] ) ;
1376
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1363
1377
let mut used_feerate;
1364
1378
if subtract_high_prio_fee ! ( self , fee_estimator, single_htlc_tx. output[ 0 ] . value, predicted_weight, tx. txid( ) , used_feerate) {
1365
1379
let sighash_parts = bip143:: SighashComponents :: new ( & single_htlc_tx) ;
1366
1380
let ( redeemscript, revocation_key) = sign_input ! ( sighash_parts, single_htlc_tx. input[ 0 ] , Some ( idx) , htlc. amount_msat / 1000 ) ;
1367
1381
assert ! ( predicted_weight >= single_htlc_tx. get_weight( ) ) ;
1368
1382
match self . our_claim_txn_waiting_first_conf . entry ( single_htlc_tx. input [ 0 ] . previous_output . clone ( ) ) {
1369
1383
hash_map:: Entry :: Occupied ( _) => { } ,
1370
- hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height + 3 , TxMaterial :: Revoked { script : redeemscript, pubkey : Some ( revocation_pubkey) , key : revocation_key, is_htlc : true , amount : htlc. amount_msat / 1000 } , used_feerate) ) ; }
1384
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height_timer , TxMaterial :: Revoked { script : redeemscript, pubkey : Some ( revocation_pubkey) , key : revocation_key, is_htlc : true , amount : htlc. amount_msat / 1000 } , used_feerate, htlc . cltv_expiry , height ) ) ; }
1371
1385
}
1372
1386
txn_to_broadcast. push ( single_htlc_tx) ;
1373
1387
}
@@ -1444,9 +1458,10 @@ impl ChannelMonitor {
1444
1458
1445
1459
for ( input, info) in spend_tx. input . iter_mut ( ) . zip ( inputs_info. iter ( ) ) {
1446
1460
let ( redeemscript, revocation_key) = sign_input ! ( sighash_parts, input, info. 0 , info. 1 ) ;
1461
+ let height_timer = Self :: get_height_timer ( height, info. 2 ) ;
1447
1462
match self . our_claim_txn_waiting_first_conf . entry ( input. previous_output . clone ( ) ) {
1448
1463
hash_map:: Entry :: Occupied ( _) => { } ,
1449
- hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height + 3 , TxMaterial :: Revoked { script : redeemscript, pubkey : if info. 0 . is_some ( ) { Some ( revocation_pubkey) } else { None } , key : revocation_key, is_htlc : if info. 0 . is_some ( ) { true } else { false } , amount : info. 1 } , used_feerate) ) ; }
1464
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height_timer , TxMaterial :: Revoked { script : redeemscript, pubkey : if info. 0 . is_some ( ) { Some ( revocation_pubkey) } else { None } , key : revocation_key, is_htlc : if info. 0 . is_some ( ) { true } else { false } , amount : info. 1 } , used_feerate, if !info . 0 . is_some ( ) { height + info . 2 } else { info . 2 } , height ) ) ; }
1450
1465
}
1451
1466
}
1452
1467
assert ! ( predicted_weight >= spend_tx. get_weight( ) ) ;
@@ -1610,7 +1625,7 @@ impl ChannelMonitor {
1610
1625
if htlc. cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER {
1611
1626
inputs. push ( input) ;
1612
1627
inputs_desc. push ( if htlc. offered { InputDescriptors :: OfferedHTLC } else { InputDescriptors :: ReceivedHTLC } ) ;
1613
- inputs_info. push ( ( payment_preimage, tx. output [ transaction_output_index as usize ] . value ) ) ;
1628
+ inputs_info. push ( ( payment_preimage, tx. output [ transaction_output_index as usize ] . value , htlc . cltv_expiry ) ) ;
1614
1629
total_value += tx. output [ transaction_output_index as usize ] . value ;
1615
1630
} else {
1616
1631
let mut single_htlc_tx = Transaction {
@@ -1623,6 +1638,7 @@ impl ChannelMonitor {
1623
1638
} ) ,
1624
1639
} ;
1625
1640
let predicted_weight = single_htlc_tx. get_weight ( ) + Self :: get_witnesses_weight ( & [ if htlc. offered { InputDescriptors :: OfferedHTLC } else { InputDescriptors :: ReceivedHTLC } ] ) ;
1641
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1626
1642
let mut used_feerate;
1627
1643
if subtract_high_prio_fee ! ( self , fee_estimator, single_htlc_tx. output[ 0 ] . value, predicted_weight, tx. txid( ) , used_feerate) {
1628
1644
let sighash_parts = bip143:: SighashComponents :: new ( & single_htlc_tx) ;
@@ -1634,7 +1650,7 @@ impl ChannelMonitor {
1634
1650
} ) ;
1635
1651
match self . our_claim_txn_waiting_first_conf . entry ( single_htlc_tx. input [ 0 ] . previous_output . clone ( ) ) {
1636
1652
hash_map:: Entry :: Occupied ( _) => { } ,
1637
- hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height + 3 , TxMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : Some ( * payment_preimage) , amount : htlc. amount_msat / 1000 } , used_feerate) ) ; }
1653
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height_timer , TxMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : Some ( * payment_preimage) , amount : htlc. amount_msat / 1000 } , used_feerate, htlc . cltv_expiry , height ) ) ; }
1638
1654
}
1639
1655
txn_to_broadcast. push ( single_htlc_tx) ;
1640
1656
}
@@ -1662,6 +1678,7 @@ impl ChannelMonitor {
1662
1678
} ) ,
1663
1679
} ;
1664
1680
let predicted_weight = timeout_tx. get_weight ( ) + Self :: get_witnesses_weight ( & [ InputDescriptors :: ReceivedHTLC ] ) ;
1681
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1665
1682
let mut used_feerate;
1666
1683
if subtract_high_prio_fee ! ( self , fee_estimator, timeout_tx. output[ 0 ] . value, predicted_weight, tx. txid( ) , used_feerate) {
1667
1684
let sighash_parts = bip143:: SighashComponents :: new ( & timeout_tx) ;
@@ -1670,7 +1687,7 @@ impl ChannelMonitor {
1670
1687
//TODO: track SpendableOutputDescriptor
1671
1688
match self . our_claim_txn_waiting_first_conf . entry ( timeout_tx. input [ 0 ] . previous_output . clone ( ) ) {
1672
1689
hash_map:: Entry :: Occupied ( _) => { } ,
1673
- hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height + 3 , TxMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : None , amount : htlc. amount_msat / 1000 } , used_feerate) ) ; }
1690
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height_timer , TxMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : None , amount : htlc. amount_msat / 1000 } , used_feerate, htlc . cltv_expiry , height ) ) ; }
1674
1691
}
1675
1692
}
1676
1693
txn_to_broadcast. push ( timeout_tx) ;
@@ -1702,9 +1719,10 @@ impl ChannelMonitor {
1702
1719
1703
1720
for ( input, info) in spend_tx. input . iter_mut ( ) . zip ( inputs_info. iter ( ) ) {
1704
1721
let ( redeemscript, htlc_key) = sign_input ! ( sighash_parts, input, info. 1 , ( info. 0 ) . 0 . to_vec( ) ) ;
1722
+ let height_timer = Self :: get_height_timer ( height, info. 2 ) ;
1705
1723
match self . our_claim_txn_waiting_first_conf . entry ( input. previous_output . clone ( ) ) {
1706
1724
hash_map:: Entry :: Occupied ( _) => { } ,
1707
- hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height + 3 , TxMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : Some ( * ( info. 0 ) ) , amount : info. 1 } , used_feerate) ) ; }
1725
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height_timer , TxMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : Some ( * ( info. 0 ) ) , amount : info. 1 } , used_feerate, info . 2 , height ) ) ; }
1708
1726
}
1709
1727
}
1710
1728
assert ! ( predicted_weight >= spend_tx. get_weight( ) ) ;
@@ -1808,15 +1826,16 @@ impl ChannelMonitor {
1808
1826
assert ! ( predicted_weight >= spend_tx. get_weight( ) ) ;
1809
1827
let outpoint = BitcoinOutPoint { txid : spend_tx. txid ( ) , vout : 0 } ;
1810
1828
let output = spend_tx. output [ 0 ] . clone ( ) ;
1829
+ let height_timer = Self :: get_height_timer ( height, self . their_to_self_delay . unwrap ( ) as u32 ) ; // We can safely unwrap given we are past channel opening
1811
1830
match self . our_claim_txn_waiting_first_conf . entry ( spend_tx. input [ 0 ] . previous_output . clone ( ) ) {
1812
1831
hash_map:: Entry :: Occupied ( _) => { } ,
1813
- hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height + 3 , TxMaterial :: Revoked { script : redeemscript, pubkey : None , key : revocation_key, is_htlc : false , amount : tx. output [ 0 ] . value } , used_feerate) ) ; }
1832
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( height_timer , TxMaterial :: Revoked { script : redeemscript, pubkey : None , key : revocation_key, is_htlc : false , amount : tx. output [ 0 ] . value } , used_feerate, height + self . our_to_self_delay as u32 , height ) ) ; }
1814
1833
}
1815
1834
( Some ( spend_tx) , Some ( SpendableOutputDescriptor :: StaticOutput { outpoint, output } ) )
1816
1835
} else { ( None , None ) }
1817
1836
}
1818
1837
1819
- fn broadcast_by_local_state ( & self , local_tx : & LocalSignedTx , per_commitment_point : & Option < PublicKey > , delayed_payment_base_key : & Option < SecretKey > , height : u32 ) -> ( Vec < Transaction > , Vec < SpendableOutputDescriptor > , Vec < TxOut > , Vec < ( BitcoinOutPoint , ( u32 , TxMaterial , u64 ) ) > ) {
1838
+ fn broadcast_by_local_state ( & self , local_tx : & LocalSignedTx , per_commitment_point : & Option < PublicKey > , delayed_payment_base_key : & Option < SecretKey > , height : u32 ) -> ( Vec < Transaction > , Vec < SpendableOutputDescriptor > , Vec < TxOut > , Vec < ( BitcoinOutPoint , ( u32 , TxMaterial , u64 , u32 , u32 ) ) > ) {
1820
1839
let mut res = Vec :: with_capacity ( local_tx. htlc_outputs . len ( ) ) ;
1821
1840
let mut spendable_outputs = Vec :: with_capacity ( local_tx. htlc_outputs . len ( ) ) ;
1822
1841
let mut watch_outputs = Vec :: with_capacity ( local_tx. htlc_outputs . len ( ) ) ;
@@ -1869,7 +1888,8 @@ impl ChannelMonitor {
1869
1888
htlc_timeout_tx. input [ 0 ] . witness . push ( htlc_script. clone ( ) . into_bytes ( ) ) ;
1870
1889
1871
1890
add_dynamic_output ! ( htlc_timeout_tx, 0 ) ;
1872
- pending_claims. push ( ( htlc_timeout_tx. input [ 0 ] . previous_output . clone ( ) , ( height + 3 , TxMaterial :: LocalHTLC { script : htlc_script, sigs : ( * their_sig, * our_sig) , preimage : None , amount : htlc. amount_msat / 1000 } , 0 ) ) ) ;
1891
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1892
+ pending_claims. push ( ( htlc_timeout_tx. input [ 0 ] . previous_output . clone ( ) , ( height_timer, TxMaterial :: LocalHTLC { script : htlc_script, sigs : ( * their_sig, * our_sig) , preimage : None , amount : htlc. amount_msat / 1000 } , 0 , htlc. cltv_expiry , height) ) ) ;
1873
1893
res. push ( htlc_timeout_tx) ;
1874
1894
} else {
1875
1895
if let Some ( payment_preimage) = self . payment_preimages . get ( & htlc. payment_hash ) {
@@ -1888,7 +1908,8 @@ impl ChannelMonitor {
1888
1908
htlc_success_tx. input [ 0 ] . witness . push ( htlc_script. clone ( ) . into_bytes ( ) ) ;
1889
1909
1890
1910
add_dynamic_output ! ( htlc_success_tx, 0 ) ;
1891
- pending_claims. push ( ( htlc_success_tx. input [ 0 ] . previous_output . clone ( ) , ( height + 3 , TxMaterial :: LocalHTLC { script : htlc_script, sigs : ( * their_sig, * our_sig) , preimage : Some ( * payment_preimage) , amount : htlc. amount_msat / 1000 } , 0 ) ) ) ;
1911
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1912
+ pending_claims. push ( ( htlc_success_tx. input [ 0 ] . previous_output . clone ( ) , ( height_timer, TxMaterial :: LocalHTLC { script : htlc_script, sigs : ( * their_sig, * our_sig) , preimage : Some ( * payment_preimage) , amount : htlc. amount_msat / 1000 } , 0 , htlc. cltv_expiry , height) ) ) ;
1892
1913
res. push ( htlc_success_tx) ;
1893
1914
}
1894
1915
}
@@ -2190,7 +2211,7 @@ impl ChannelMonitor {
2190
2211
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
2191
2212
//- our claim tx on a commitment tx output
2192
2213
}
2193
- self . our_claim_txn_waiting_first_conf . retain ( |_, ref mut v| if v. 0 == height + 3 { false } else { true } ) ;
2214
+ self . our_claim_txn_waiting_first_conf . retain ( |_, ref mut v| if v. 3 == height { false } else { true } ) ;
2194
2215
self . last_block_hash = block_hash. clone ( ) ;
2195
2216
}
2196
2217
@@ -2659,7 +2680,9 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
2659
2680
_ => return Err ( DecodeError :: InvalidValue ) ,
2660
2681
} ;
2661
2682
let last_fee = Readable :: read ( reader) ?;
2662
- our_claim_txn_waiting_first_conf. insert ( outpoint, ( height_target, tx_material, last_fee) ) ;
2683
+ let timelock_expiration = Readable :: read ( reader) ?;
2684
+ let height = Readable :: read ( reader) ?;
2685
+ our_claim_txn_waiting_first_conf. insert ( outpoint, ( height_target, tx_material, last_fee, timelock_expiration, height) ) ;
2663
2686
}
2664
2687
2665
2688
let waiting_threshold_conf_len: u64 = Readable :: read ( reader) ?;
0 commit comments