@@ -457,8 +457,10 @@ pub struct ChannelMonitor {
457
457
// Used to track outpoint in the process of being claimed by our transactions. We need to scan all transactions
458
458
// for inputs spending this. If height timer (u32) is expired and claim tx hasn't reached enough confirmations
459
459
// before, use TxMaterial to regenerate a new claim tx with a satoshis-per-1000-weight-units higher than last
460
- // one (u64).
461
- our_claim_txn_waiting_first_conf : HashMap < BitcoinOutPoint , ( u32 , TxMaterial , u64 ) > ,
460
+ // one (u64), if timelock expiration (u32) is near, decrease height timer, the in-between bumps delay.
461
+ // Last field cached (u32) is height of outpoint confirmation, which is needed to flush this tracker
462
+ // in case of reorgs, given block timer are scaled on timer expiration we can't deduce from it original height.
463
+ our_claim_txn_waiting_first_conf : HashMap < BitcoinOutPoint , ( u32 , TxMaterial , u64 , u32 , u32 ) > ,
462
464
463
465
// Used to track onchain events, i.e transactions parts of channels confirmed on chain, on which
464
466
// we have to take actions once they reach enough confs. Key is a block height timer, i.e we enforce
@@ -621,6 +623,15 @@ impl ChannelMonitor {
621
623
tx_weight
622
624
}
623
625
626
+ fn get_height_timer ( current_height : u32 , timelock_expiration : u32 ) -> u32 {
627
+ if timelock_expiration <= current_height || timelock_expiration - current_height <= 3 {
628
+ return current_height + 1
629
+ } else if timelock_expiration - current_height <= 15 {
630
+ return current_height + 3
631
+ }
632
+ current_height + 15
633
+ }
634
+
624
635
#[ inline]
625
636
fn place_secret ( idx : u64 ) -> u8 {
626
637
for i in 0 ..48 {
@@ -1126,6 +1137,8 @@ impl ChannelMonitor {
1126
1137
}
1127
1138
}
1128
1139
writer. write_all ( & byte_utils:: be64_to_array ( claim_tx_data. 2 ) ) ?;
1140
+ writer. write_all ( & byte_utils:: be32_to_array ( claim_tx_data. 3 ) ) ?;
1141
+ writer. write_all ( & byte_utils:: be32_to_array ( claim_tx_data. 4 ) ) ?;
1129
1142
}
1130
1143
1131
1144
writer. write_all ( & byte_utils:: be64_to_array ( self . onchain_events_waiting_threshold_conf . len ( ) as u64 ) ) ?;
@@ -1280,7 +1293,7 @@ impl ChannelMonitor {
1280
1293
witness : Vec :: new ( ) ,
1281
1294
} ) ;
1282
1295
inputs_desc. push ( InputDescriptors :: RevokedOutput ) ;
1283
- inputs_info. push ( ( None , outp. value ) ) ;
1296
+ inputs_info. push ( ( None , outp. value , self . their_to_self_delay . unwrap ( ) as u32 ) ) ; // We can safely unwrap given we are past channel opening
1284
1297
total_value += outp. value ;
1285
1298
} else if Some ( & outp. script_pubkey ) == local_payment_p2wpkh. as_ref ( ) {
1286
1299
spendable_outputs. push ( SpendableOutputDescriptor :: DynamicOutputP2WPKH {
@@ -1344,7 +1357,7 @@ impl ChannelMonitor {
1344
1357
if htlc. cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER {
1345
1358
inputs. push ( input) ;
1346
1359
inputs_desc. push ( if htlc. offered { InputDescriptors :: RevokedOfferedHTLC } else { InputDescriptors :: RevokedReceivedHTLC } ) ;
1347
- inputs_info. push ( ( Some ( idx) , tx. output [ transaction_output_index as usize ] . value ) ) ;
1360
+ inputs_info. push ( ( Some ( idx) , tx. output [ transaction_output_index as usize ] . value , htlc . cltv_expiry ) ) ;
1348
1361
total_value += tx. output [ transaction_output_index as usize ] . value ;
1349
1362
} else {
1350
1363
let mut single_htlc_tx = Transaction {
@@ -1357,14 +1370,15 @@ impl ChannelMonitor {
1357
1370
} ) ,
1358
1371
} ;
1359
1372
let predicted_weight = single_htlc_tx. get_weight ( ) + Self :: get_witnesses_weight ( & [ if htlc. offered { InputDescriptors :: RevokedOfferedHTLC } else { InputDescriptors :: RevokedReceivedHTLC } ] ) ;
1373
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1360
1374
let mut used_feerate;
1361
1375
if subtract_high_prio_fee ! ( self , fee_estimator, single_htlc_tx. output[ 0 ] . value, predicted_weight, tx. txid( ) , used_feerate) {
1362
1376
let sighash_parts = bip143:: SighashComponents :: new ( & single_htlc_tx) ;
1363
1377
let ( redeemscript, revocation_key) = sign_input ! ( sighash_parts, single_htlc_tx. input[ 0 ] , Some ( idx) , htlc. amount_msat / 1000 ) ;
1364
1378
assert ! ( predicted_weight >= single_htlc_tx. get_weight( ) ) ;
1365
1379
match self . our_claim_txn_waiting_first_conf . entry ( single_htlc_tx. input [ 0 ] . previous_output . clone ( ) ) {
1366
1380
hash_map:: Entry :: Occupied ( _) => { } ,
1367
- 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) ) ; }
1381
+ 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 ) ) ; }
1368
1382
}
1369
1383
txn_to_broadcast. push ( single_htlc_tx) ;
1370
1384
}
@@ -1441,9 +1455,10 @@ impl ChannelMonitor {
1441
1455
1442
1456
for ( input, info) in spend_tx. input . iter_mut ( ) . zip ( inputs_info. iter ( ) ) {
1443
1457
let ( redeemscript, revocation_key) = sign_input ! ( sighash_parts, input, info. 0 , info. 1 ) ;
1458
+ let height_timer = Self :: get_height_timer ( height, info. 2 ) ;
1444
1459
match self . our_claim_txn_waiting_first_conf . entry ( input. previous_output . clone ( ) ) {
1445
1460
hash_map:: Entry :: Occupied ( _) => { } ,
1446
- 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) ) ; }
1461
+ 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, info . 2 , height ) ) ; }
1447
1462
}
1448
1463
}
1449
1464
assert ! ( predicted_weight >= spend_tx. get_weight( ) ) ;
@@ -1607,7 +1622,7 @@ impl ChannelMonitor {
1607
1622
if htlc. cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER {
1608
1623
inputs. push ( input) ;
1609
1624
inputs_desc. push ( if htlc. offered { InputDescriptors :: OfferedHTLC } else { InputDescriptors :: ReceivedHTLC } ) ;
1610
- inputs_info. push ( ( payment_preimage, tx. output [ transaction_output_index as usize ] . value ) ) ;
1625
+ inputs_info. push ( ( payment_preimage, tx. output [ transaction_output_index as usize ] . value , htlc . cltv_expiry ) ) ;
1611
1626
total_value += tx. output [ transaction_output_index as usize ] . value ;
1612
1627
} else {
1613
1628
let mut single_htlc_tx = Transaction {
@@ -1620,6 +1635,7 @@ impl ChannelMonitor {
1620
1635
} ) ,
1621
1636
} ;
1622
1637
let predicted_weight = single_htlc_tx. get_weight ( ) + Self :: get_witnesses_weight ( & [ if htlc. offered { InputDescriptors :: OfferedHTLC } else { InputDescriptors :: ReceivedHTLC } ] ) ;
1638
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1623
1639
let mut used_feerate;
1624
1640
if subtract_high_prio_fee ! ( self , fee_estimator, single_htlc_tx. output[ 0 ] . value, predicted_weight, tx. txid( ) , used_feerate) {
1625
1641
let sighash_parts = bip143:: SighashComponents :: new ( & single_htlc_tx) ;
@@ -1631,7 +1647,7 @@ impl ChannelMonitor {
1631
1647
} ) ;
1632
1648
match self . our_claim_txn_waiting_first_conf . entry ( single_htlc_tx. input [ 0 ] . previous_output . clone ( ) ) {
1633
1649
hash_map:: Entry :: Occupied ( _) => { } ,
1634
- 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) ) ; }
1650
+ 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 ) ) ; }
1635
1651
}
1636
1652
txn_to_broadcast. push ( single_htlc_tx) ;
1637
1653
}
@@ -1659,6 +1675,7 @@ impl ChannelMonitor {
1659
1675
} ) ,
1660
1676
} ;
1661
1677
let predicted_weight = timeout_tx. get_weight ( ) + Self :: get_witnesses_weight ( & [ InputDescriptors :: ReceivedHTLC ] ) ;
1678
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1662
1679
let mut used_feerate;
1663
1680
if subtract_high_prio_fee ! ( self , fee_estimator, timeout_tx. output[ 0 ] . value, predicted_weight, tx. txid( ) , used_feerate) {
1664
1681
let sighash_parts = bip143:: SighashComponents :: new ( & timeout_tx) ;
@@ -1667,7 +1684,7 @@ impl ChannelMonitor {
1667
1684
//TODO: track SpendableOutputDescriptor
1668
1685
match self . our_claim_txn_waiting_first_conf . entry ( timeout_tx. input [ 0 ] . previous_output . clone ( ) ) {
1669
1686
hash_map:: Entry :: Occupied ( _) => { } ,
1670
- 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) ) ; }
1687
+ 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 ) ) ; }
1671
1688
}
1672
1689
}
1673
1690
txn_to_broadcast. push ( timeout_tx) ;
@@ -1699,9 +1716,10 @@ impl ChannelMonitor {
1699
1716
1700
1717
for ( input, info) in spend_tx. input . iter_mut ( ) . zip ( inputs_info. iter ( ) ) {
1701
1718
let ( redeemscript, htlc_key) = sign_input ! ( sighash_parts, input, info. 1 , ( info. 0 ) . 0 . to_vec( ) ) ;
1719
+ let height_timer = Self :: get_height_timer ( height, info. 2 ) ;
1702
1720
match self . our_claim_txn_waiting_first_conf . entry ( input. previous_output . clone ( ) ) {
1703
1721
hash_map:: Entry :: Occupied ( _) => { } ,
1704
- 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) ) ; }
1722
+ 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 ) ) ; }
1705
1723
}
1706
1724
}
1707
1725
assert ! ( predicted_weight >= spend_tx. get_weight( ) ) ;
@@ -1805,15 +1823,16 @@ impl ChannelMonitor {
1805
1823
assert ! ( predicted_weight >= spend_tx. get_weight( ) ) ;
1806
1824
let outpoint = BitcoinOutPoint { txid : spend_tx. txid ( ) , vout : 0 } ;
1807
1825
let output = spend_tx. output [ 0 ] . clone ( ) ;
1826
+ 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
1808
1827
match self . our_claim_txn_waiting_first_conf . entry ( spend_tx. input [ 0 ] . previous_output . clone ( ) ) {
1809
1828
hash_map:: Entry :: Occupied ( _) => { } ,
1810
- 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) ) ; }
1829
+ 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, self . their_to_self_delay . unwrap ( ) as u32 , height ) ) ; }
1811
1830
}
1812
1831
( Some ( spend_tx) , Some ( SpendableOutputDescriptor :: StaticOutput { outpoint, output } ) )
1813
1832
} else { ( None , None ) }
1814
1833
}
1815
1834
1816
- 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 ) ) > ) {
1835
+ 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 ) ) > ) {
1817
1836
let mut res = Vec :: with_capacity ( local_tx. htlc_outputs . len ( ) ) ;
1818
1837
let mut spendable_outputs = Vec :: with_capacity ( local_tx. htlc_outputs . len ( ) ) ;
1819
1838
let mut watch_outputs = Vec :: with_capacity ( local_tx. htlc_outputs . len ( ) ) ;
@@ -1866,7 +1885,8 @@ impl ChannelMonitor {
1866
1885
htlc_timeout_tx. input [ 0 ] . witness . push ( htlc_script. clone ( ) . into_bytes ( ) ) ;
1867
1886
1868
1887
add_dynamic_output ! ( htlc_timeout_tx, 0 ) ;
1869
- 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 ) ) ) ;
1888
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1889
+ 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) ) ) ;
1870
1890
res. push ( htlc_timeout_tx) ;
1871
1891
} else {
1872
1892
if let Some ( payment_preimage) = self . payment_preimages . get ( & htlc. payment_hash ) {
@@ -1885,7 +1905,8 @@ impl ChannelMonitor {
1885
1905
htlc_success_tx. input [ 0 ] . witness . push ( htlc_script. clone ( ) . into_bytes ( ) ) ;
1886
1906
1887
1907
add_dynamic_output ! ( htlc_success_tx, 0 ) ;
1888
- 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 ) ) ) ;
1908
+ let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
1909
+ 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) ) ) ;
1889
1910
res. push ( htlc_success_tx) ;
1890
1911
}
1891
1912
}
@@ -2187,7 +2208,7 @@ impl ChannelMonitor {
2187
2208
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
2188
2209
//- our claim tx on a commitment tx output
2189
2210
}
2190
- self . our_claim_txn_waiting_first_conf . retain ( |_, ref mut v| if v. 0 == height + 3 { false } else { true } ) ;
2211
+ self . our_claim_txn_waiting_first_conf . retain ( |_, ref mut v| if v. 3 == height { false } else { true } ) ;
2191
2212
self . last_block_hash = block_hash. clone ( ) ;
2192
2213
}
2193
2214
@@ -2656,7 +2677,9 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
2656
2677
_ => return Err ( DecodeError :: InvalidValue ) ,
2657
2678
} ;
2658
2679
let last_fee = Readable :: read ( reader) ?;
2659
- our_claim_txn_waiting_first_conf. insert ( outpoint, ( height_target, tx_material, last_fee) ) ;
2680
+ let timelock_expiration = Readable :: read ( reader) ?;
2681
+ let height = Readable :: read ( reader) ?;
2682
+ our_claim_txn_waiting_first_conf. insert ( outpoint, ( height_target, tx_material, last_fee, timelock_expiration, height) ) ;
2660
2683
}
2661
2684
2662
2685
let waiting_threshold_conf_len: u64 = Readable :: read ( reader) ?;
0 commit comments