@@ -2285,6 +2285,22 @@ impl ChannelMonitor {
2285
2285
}
2286
2286
}
2287
2287
}
2288
+ let mut bump_candidates = Vec :: new ( ) ;
2289
+ for ( claimed_txid, ref mut cached_claim_datas) in self . our_claim_txn_waiting_first_conf . iter_mut ( ) {
2290
+ if cached_claim_datas. height_timer == height {
2291
+ bump_candidates. push ( ( claimed_txid. clone ( ) , cached_claim_datas. clone ( ) ) ) ;
2292
+ }
2293
+ }
2294
+ for & mut ( claimed_txid, ref mut cached_claim_datas) in bump_candidates. iter_mut ( ) {
2295
+ if let Some ( ( new_timer, new_feerate, bump_tx) ) = self . bump_claim_tx ( height, & claimed_txid, & cached_claim_datas, fee_estimator) {
2296
+ cached_claim_datas. height_timer = new_timer;
2297
+ cached_claim_datas. feerate_previous = new_feerate;
2298
+ broadcaster. broadcast_transaction ( & bump_tx) ;
2299
+ }
2300
+ }
2301
+ for ( claimed_txid, cached_claim_datas) in bump_candidates. drain ( ..) {
2302
+ self . our_claim_txn_waiting_first_conf . insert ( claimed_txid, cached_claim_datas) ;
2303
+ }
2288
2304
self . last_block_hash = block_hash. clone ( ) ;
2289
2305
( watch_outputs, spendable_outputs, htlc_updated)
2290
2306
}
@@ -2498,6 +2514,123 @@ impl ChannelMonitor {
2498
2514
}
2499
2515
htlc_updated
2500
2516
}
2517
+
2518
+ /// 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
2519
+ /// (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.
2520
+ fn bump_claim_tx ( & self , height : u32 , txid : & Sha256dHash , cached_claim_datas : & ClaimTxBumpMaterial , fee_estimator : & FeeEstimator ) -> Option < ( u32 , u64 , Transaction ) > {
2521
+ let mut inputs = Vec :: new ( ) ;
2522
+ for vout in cached_claim_datas. per_input_material . keys ( ) {
2523
+ inputs. push ( TxIn {
2524
+ previous_output : BitcoinOutPoint {
2525
+ txid : txid. clone ( ) ,
2526
+ vout : * vout,
2527
+ } ,
2528
+ script_sig : Script :: new ( ) ,
2529
+ sequence : 0xfffffffd ,
2530
+ witness : Vec :: new ( ) ,
2531
+ } ) ;
2532
+ }
2533
+ let mut bumped_tx = Transaction {
2534
+ version : 2 ,
2535
+ lock_time : 0 ,
2536
+ input : inputs,
2537
+ output : vec ! [ TxOut {
2538
+ script_pubkey: self . destination_script. clone( ) ,
2539
+ value: 0
2540
+ } ] ,
2541
+ } ;
2542
+
2543
+ macro_rules! RBF_bump {
2544
+ ( $amount: expr, $old_feerate: expr, $fee_estimator: expr, $predicted_weight: expr, $txid: expr) => {
2545
+ {
2546
+ let mut used_feerate;
2547
+ // If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
2548
+ let new_fee = if $old_feerate < $fee_estimator. get_est_sat_per_1000_weight( ConfirmationTarget :: HighPriority ) {
2549
+ let mut value = $amount;
2550
+ if subtract_high_prio_fee!( self , $fee_estimator, value, $predicted_weight, txid, used_feerate) {
2551
+ $amount - value
2552
+ } else {
2553
+ log_trace!( self , "Can't new-estimation bump claiming on {}, amount {} is too small" , $txid, $amount) ;
2554
+ return None ;
2555
+ }
2556
+ // ...else just increase the previous feerate by 25% (because that's a nice number)
2557
+ } else {
2558
+ let fee = $old_feerate * $predicted_weight / 750 ;
2559
+ if $amount <= fee {
2560
+ log_trace!( self , "Can't 25% bump claiming on {}, amount {} is too small" , $txid, $amount) ;
2561
+ return None ;
2562
+ }
2563
+ fee
2564
+ } ;
2565
+
2566
+ let previous_fee = $old_feerate * $predicted_weight / 1000 ;
2567
+ let min_relay_fee = $fee_estimator. get_min_relay_sat_per_1000_weight( ) * $predicted_weight / 1000 ;
2568
+ // BIP 125 Opt-in Full Replace-by-Fee Signaling
2569
+ // * 3. The replacement transaction pays an absolute fee of at least the sum paid by the original transactions.
2570
+ // * 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.
2571
+ let new_fee = if new_fee < previous_fee + min_relay_fee {
2572
+ new_fee + previous_fee + min_relay_fee - new_fee
2573
+ } else {
2574
+ new_fee
2575
+ } ;
2576
+ Some ( ( new_fee, new_fee * 1000 / $predicted_weight) )
2577
+ }
2578
+ }
2579
+ }
2580
+
2581
+ let new_timer = Self :: get_height_timer ( height, cached_claim_datas. soonest_timelock ) ;
2582
+ let mut inputs_witnesses_weight = 0 ;
2583
+ let mut amt = 0 ;
2584
+ for per_outp_material in cached_claim_datas. per_input_material . values ( ) {
2585
+ match per_outp_material {
2586
+ & InputMaterial :: Revoked { ref script, ref is_htlc, ref amount, .. } => {
2587
+ 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 { & [ ] } ) ;
2588
+ amt += * amount;
2589
+ } ,
2590
+ & InputMaterial :: RemoteHTLC { .. } => { return None ; } ,
2591
+ & InputMaterial :: LocalHTLC { .. } => { return None ; }
2592
+ }
2593
+ }
2594
+ assert ! ( amt != 0 ) ;
2595
+
2596
+ let predicted_weight = bumped_tx. get_weight ( ) + inputs_witnesses_weight;
2597
+ let new_feerate;
2598
+ if let Some ( ( new_fee, feerate) ) = RBF_bump ! ( amt, cached_claim_datas. feerate_previous, fee_estimator, predicted_weight as u64 , txid) {
2599
+ bumped_tx. output [ 0 ] . value = amt - new_fee;
2600
+ new_feerate = feerate;
2601
+ } else {
2602
+ return None ;
2603
+ }
2604
+ assert ! ( new_feerate != 0 ) ;
2605
+
2606
+ for ( i, ( vout, per_outp_material) ) in cached_claim_datas. per_input_material . iter ( ) . enumerate ( ) {
2607
+ match per_outp_material {
2608
+ & InputMaterial :: Revoked { ref script, ref pubkey, ref key, ref is_htlc, ref amount } => {
2609
+ let sighash_parts = bip143:: SighashComponents :: new ( & bumped_tx) ;
2610
+ let sighash = hash_to_message ! ( & sighash_parts. sighash_all( & bumped_tx. input[ i] , & script, * amount) [ ..] ) ;
2611
+ let sig = self . secp_ctx . sign ( & sighash, & key) ;
2612
+ bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
2613
+ bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
2614
+ if * is_htlc {
2615
+ bumped_tx. input [ i] . witness . push ( pubkey. unwrap ( ) . clone ( ) . serialize ( ) . to_vec ( ) ) ;
2616
+ } else {
2617
+ bumped_tx. input [ i] . witness . push ( vec ! ( 1 ) ) ;
2618
+ }
2619
+ bumped_tx. input [ i] . witness . push ( script. clone ( ) . into_bytes ( ) ) ;
2620
+ 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) ;
2621
+ } ,
2622
+ & InputMaterial :: RemoteHTLC { .. } => { } ,
2623
+ & InputMaterial :: LocalHTLC { .. } => {
2624
+ //TODO : Given that Local Commitment Transaction and HTLC-Timeout/HTLC-Success are counter-signed by peer, we can't
2625
+ // RBF them. Need a Lightning specs change and package relay modification :
2626
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
2627
+ return None ;
2628
+ }
2629
+ }
2630
+ }
2631
+ assert ! ( predicted_weight >= bumped_tx. get_weight( ) ) ;
2632
+ Some ( ( new_timer, new_feerate, bumped_tx) )
2633
+ }
2501
2634
}
2502
2635
2503
2636
const MAX_ALLOC_SIZE : usize = 64 * 1024 ;
0 commit comments