@@ -2277,6 +2277,22 @@ impl ChannelMonitor {
2277
2277
}
2278
2278
}
2279
2279
}
2280
+ let mut bump_candidates = Vec :: new ( ) ;
2281
+ for ( claimed_txid, ref mut cached_claim_datas) in self . our_claim_txn_waiting_first_conf . iter_mut ( ) {
2282
+ if cached_claim_datas. 0 == height {
2283
+ bump_candidates. push ( ( claimed_txid. clone ( ) , cached_claim_datas. clone ( ) ) ) ;
2284
+ }
2285
+ }
2286
+ for ( claimed_txid, cached_claim_datas) in bump_candidates. iter_mut ( ) {
2287
+ if let Some ( ( new_timer, new_feerate, bump_tx) ) = self . bump_claim_tx ( height, & claimed_txid, & cached_claim_datas, fee_estimator) {
2288
+ cached_claim_datas. 0 = new_timer;
2289
+ cached_claim_datas. 2 = new_feerate;
2290
+ broadcaster. broadcast_transaction ( & bump_tx) ;
2291
+ }
2292
+ }
2293
+ for ( claimed_txid, cached_claim_datas) in bump_candidates. drain ( ..) {
2294
+ self . our_claim_txn_waiting_first_conf . insert ( claimed_txid, cached_claim_datas) ;
2295
+ }
2280
2296
self . last_block_hash = block_hash. clone ( ) ;
2281
2297
( watch_outputs, spendable_outputs, htlc_updated)
2282
2298
}
@@ -2490,6 +2506,123 @@ impl ChannelMonitor {
2490
2506
}
2491
2507
htlc_updated
2492
2508
}
2509
+
2510
+ /// Lightning security model (i.e being able to redeem/timeout HTLC or penalize coutnerparty onchain) lays on the assumption of claim transactions getting confirmed before timelock expiration
2511
+ /// (CSV or CLTV following cases). In case of high-fee spikes, claim tx may stuck in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or Child-Pay-For-Parent.
2512
+ fn bump_claim_tx ( & self , height : u32 , txid : & Sha256dHash , cached_claim_datas : & ( u32 , u32 , u64 , u32 , HashMap < u32 , TxMaterial > ) , fee_estimator : & FeeEstimator ) -> Option < ( u32 , u64 , Transaction ) > {
2513
+ let mut inputs = Vec :: new ( ) ;
2514
+ for vout in cached_claim_datas. 4 . keys ( ) {
2515
+ inputs. push ( TxIn {
2516
+ previous_output : BitcoinOutPoint {
2517
+ txid : txid. clone ( ) ,
2518
+ vout : * vout,
2519
+ } ,
2520
+ script_sig : Script :: new ( ) ,
2521
+ sequence : 0xfffffffd ,
2522
+ witness : Vec :: new ( ) ,
2523
+ } ) ;
2524
+ }
2525
+ let mut bumped_tx = Transaction {
2526
+ version : 2 ,
2527
+ lock_time : 0 ,
2528
+ input : inputs,
2529
+ output : vec ! [ TxOut {
2530
+ script_pubkey: self . destination_script. clone( ) ,
2531
+ value: 0
2532
+ } ] ,
2533
+ } ;
2534
+
2535
+ macro_rules! RBF_bump {
2536
+ ( $amount: expr, $old_feerate: expr, $fee_estimator: expr, $predicted_weight: expr, $txid: expr) => {
2537
+ {
2538
+ let mut used_feerate;
2539
+ // If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
2540
+ let new_fee = if $old_feerate < $fee_estimator. get_est_sat_per_1000_weight( ConfirmationTarget :: HighPriority ) {
2541
+ let mut value = $amount;
2542
+ if subtract_high_prio_fee!( self , $fee_estimator, value, $predicted_weight, txid, used_feerate) {
2543
+ $amount - value
2544
+ } else {
2545
+ log_trace!( self , "Can't new-estimation bump claiming on {}, amount {} is too small" , $txid, $amount) ;
2546
+ return None ;
2547
+ }
2548
+ // ...else just increase the previous feerate by 25% (because that's a nice number)
2549
+ } else {
2550
+ let fee = $old_feerate * $predicted_weight / 750 ;
2551
+ if $amount <= fee {
2552
+ log_trace!( self , "Can't 25% bump claiming on {}, amount {} is too small" , $txid, $amount) ;
2553
+ return None ;
2554
+ }
2555
+ fee
2556
+ } ;
2557
+
2558
+ let previous_fee = $old_feerate * $predicted_weight / 1000 ;
2559
+ let min_relay_fee = $fee_estimator. get_min_relay_sat_per_1000_weight( ) * $predicted_weight / 1000 ;
2560
+ // BIP 125 Opt-in Full Replace-by-Fee Signaling
2561
+ // * 3. The replacement transaction pays an absolute fee of at least the sum paid by the original transactions.
2562
+ // * 4. The replacement transaction must also pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting.
2563
+ let new_fee = if new_fee < previous_fee + min_relay_fee {
2564
+ new_fee + previous_fee + min_relay_fee - new_fee
2565
+ } else {
2566
+ new_fee
2567
+ } ;
2568
+ Some ( ( new_fee, new_fee * 1000 / $predicted_weight) )
2569
+ }
2570
+ }
2571
+ }
2572
+
2573
+ let new_timer = Self :: get_height_timer ( height, cached_claim_datas. 3 ) ;
2574
+ let mut inputs_witnesses_weight = 0 ;
2575
+ let mut amt = 0 ;
2576
+ for per_outp_material in cached_claim_datas. 4 . values ( ) {
2577
+ match per_outp_material {
2578
+ & TxMaterial :: Revoked { ref script, ref is_htlc, ref amount, .. } => {
2579
+ inputs_witnesses_weight += Self :: get_witnesses_weight ( if !is_htlc { & [ InputDescriptors :: RevokedOutput ] } else if script. len ( ) == OFFERED_HTLC_SCRIPT_WEIGHT { & [ InputDescriptors :: RevokedOfferedHTLC ] } else if script. len ( ) == ACCEPTED_HTLC_SCRIPT_WEIGHT { & [ InputDescriptors :: RevokedReceivedHTLC ] } else { & [ ] } ) ;
2580
+ amt += * amount;
2581
+ } ,
2582
+ & TxMaterial :: RemoteHTLC { .. } => { return None ; } ,
2583
+ & TxMaterial :: LocalHTLC { .. } => { return None ; }
2584
+ }
2585
+ }
2586
+ assert ! ( amt != 0 ) ;
2587
+
2588
+ let predicted_weight = bumped_tx. get_weight ( ) + inputs_witnesses_weight;
2589
+ let new_feerate;
2590
+ if let Some ( ( new_fee, feerate) ) = RBF_bump ! ( amt, cached_claim_datas. 2 , fee_estimator, predicted_weight as u64 , txid) {
2591
+ bumped_tx. output [ 0 ] . value = amt - new_fee;
2592
+ new_feerate = feerate;
2593
+ } else {
2594
+ return None ;
2595
+ }
2596
+ assert ! ( new_feerate != 0 ) ;
2597
+
2598
+ for ( i, ( vout, per_outp_material) ) in cached_claim_datas. 4 . iter ( ) . enumerate ( ) {
2599
+ match per_outp_material {
2600
+ & TxMaterial :: Revoked { ref script, ref pubkey, ref key, ref is_htlc, ref amount } => {
2601
+ let sighash_parts = bip143:: SighashComponents :: new ( & bumped_tx) ;
2602
+ let sighash = hash_to_message ! ( & sighash_parts. sighash_all( & bumped_tx. input[ i] , & script, * amount) [ ..] ) ;
2603
+ let sig = self . secp_ctx . sign ( & sighash, & key) ;
2604
+ bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
2605
+ bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
2606
+ if * is_htlc {
2607
+ bumped_tx. input [ i] . witness . push ( pubkey. unwrap ( ) . clone ( ) . serialize ( ) . to_vec ( ) ) ;
2608
+ } else {
2609
+ bumped_tx. input [ i] . witness . push ( vec ! ( 1 ) ) ;
2610
+ }
2611
+ bumped_tx. input [ i] . witness . push ( script. clone ( ) . into_bytes ( ) ) ;
2612
+ log_trace ! ( self , "Going to broadcast bumped Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}" , bumped_tx. txid( ) , if !is_htlc { "to_local" } else if script. len( ) == OFFERED_HTLC_SCRIPT_WEIGHT { "offered" } else if script. len( ) == ACCEPTED_HTLC_SCRIPT_WEIGHT { "received" } else { "" } , vout, txid, new_feerate) ;
2613
+ } ,
2614
+ & TxMaterial :: RemoteHTLC { .. } => { } ,
2615
+ & TxMaterial :: LocalHTLC { .. } => {
2616
+ //TODO : Given that Local Commitment Transaction and HTLC-Timeout/HTLC-Success are counter-signed by peer, we can't
2617
+ // RBF them. Need a Lightning specs change and package relay modification :
2618
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
2619
+ return None ;
2620
+ }
2621
+ }
2622
+ }
2623
+ assert ! ( predicted_weight >= bumped_tx. get_weight( ) ) ;
2624
+ Some ( ( new_timer, new_feerate, bumped_tx) )
2625
+ }
2493
2626
}
2494
2627
2495
2628
const MAX_ALLOC_SIZE : usize = 64 * 1024 ;
0 commit comments