@@ -34,7 +34,7 @@ use ln::chan_utils;
34
34
use ln:: chan_utils:: HTLCOutputInCommitment ;
35
35
use ln:: channelmanager:: { HTLCSource , PaymentPreimage , PaymentHash } ;
36
36
use ln:: channel:: { ACCEPTED_HTLC_SCRIPT_WEIGHT , OFFERED_HTLC_SCRIPT_WEIGHT } ;
37
- use chain:: chaininterface:: { ChainListener , ChainWatchInterface , BroadcasterInterface , FeeEstimator , ConfirmationTarget } ;
37
+ use chain:: chaininterface:: { ChainListener , ChainWatchInterface , BroadcasterInterface , FeeEstimator , ConfirmationTarget , MIN_RELAY_FEE_SAT_PER_1000_WEIGHT } ;
38
38
use chain:: transaction:: OutPoint ;
39
39
use chain:: keysinterface:: SpendableOutputDescriptor ;
40
40
use util:: logger:: Logger ;
@@ -2297,6 +2297,22 @@ impl ChannelMonitor {
2297
2297
}
2298
2298
}
2299
2299
}
2300
+ let mut bump_candidates = Vec :: new ( ) ;
2301
+ for ( lead_outpoint, ref mut cached_claim_datas) in self . our_claim_txn_waiting_first_conf . iter_mut ( ) {
2302
+ if cached_claim_datas. height_timer == height {
2303
+ bump_candidates. push ( ( lead_outpoint. clone ( ) , cached_claim_datas. clone ( ) ) ) ;
2304
+ }
2305
+ }
2306
+ for & mut ( lead_outpoint, ref mut cached_claim_datas) in bump_candidates. iter_mut ( ) {
2307
+ if let Some ( ( new_timer, new_feerate, bump_tx) ) = self . bump_claim_tx ( height, & lead_outpoint, & cached_claim_datas, fee_estimator) {
2308
+ cached_claim_datas. height_timer = new_timer;
2309
+ cached_claim_datas. feerate_previous = new_feerate;
2310
+ broadcaster. broadcast_transaction ( & bump_tx) ;
2311
+ }
2312
+ }
2313
+ for ( lead_outpoint, cached_claim_datas) in bump_candidates. drain ( ..) {
2314
+ self . our_claim_txn_waiting_first_conf . insert ( lead_outpoint, cached_claim_datas) ;
2315
+ }
2300
2316
self . last_block_hash = block_hash. clone ( ) ;
2301
2317
( watch_outputs, spendable_outputs, htlc_updated)
2302
2318
}
@@ -2510,6 +2526,129 @@ impl ChannelMonitor {
2510
2526
}
2511
2527
htlc_updated
2512
2528
}
2529
+
2530
+ /// 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
2531
+ /// (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.
2532
+ fn bump_claim_tx ( & self , height : u32 , outp : & BitcoinOutPoint , cached_claim_datas : & ClaimTxBumpMaterial , fee_estimator : & FeeEstimator ) -> Option < ( u32 , u64 , Transaction ) > {
2533
+ let mut inputs = Vec :: new ( ) ;
2534
+ for vout in cached_claim_datas. per_input_material . keys ( ) {
2535
+ inputs. push ( TxIn {
2536
+ previous_output : BitcoinOutPoint {
2537
+ txid : outp. txid ,
2538
+ vout : * vout,
2539
+ } ,
2540
+ script_sig : Script :: new ( ) ,
2541
+ sequence : 0xfffffffd ,
2542
+ witness : Vec :: new ( ) ,
2543
+ } ) ;
2544
+ }
2545
+ let mut bumped_tx = Transaction {
2546
+ version : 2 ,
2547
+ lock_time : 0 ,
2548
+ input : inputs,
2549
+ output : vec ! [ TxOut {
2550
+ script_pubkey: self . destination_script. clone( ) ,
2551
+ value: 0
2552
+ } ] ,
2553
+ } ;
2554
+
2555
+ macro_rules! RBF_bump {
2556
+ ( $amount: expr, $old_feerate: expr, $fee_estimator: expr, $predicted_weight: expr, $txid: expr) => {
2557
+ {
2558
+ let mut used_feerate;
2559
+ // If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
2560
+ let new_fee = if $old_feerate < $fee_estimator. get_est_sat_per_1000_weight( ConfirmationTarget :: HighPriority ) {
2561
+ let mut value = $amount;
2562
+ if subtract_high_prio_fee!( self , $fee_estimator, value, $predicted_weight, outp. txid, used_feerate) {
2563
+ // Overflow check is done in subtract_high_prio_fee
2564
+ $amount - value
2565
+ } else {
2566
+ log_trace!( self , "Can't new-estimation bump claiming on {}, amount {} is too small" , $txid, $amount) ;
2567
+ return None ;
2568
+ }
2569
+ // ...else just increase the previous feerate by 25% (because that's a nice number)
2570
+ } else {
2571
+ let fee = $old_feerate * $predicted_weight / 750 ;
2572
+ if $amount <= fee {
2573
+ log_trace!( self , "Can't 25% bump claiming on {}, amount {} is too small" , $txid, $amount) ;
2574
+ return None ;
2575
+ }
2576
+ fee
2577
+ } ;
2578
+
2579
+ let previous_fee = $old_feerate * $predicted_weight / 1000 ;
2580
+ let min_relay_fee = MIN_RELAY_FEE_SAT_PER_1000_WEIGHT * $predicted_weight / 1000 ;
2581
+ // BIP 125 Opt-in Full Replace-by-Fee Signaling
2582
+ // * 3. The replacement transaction pays an absolute fee of at least the sum paid by the original transactions.
2583
+ // * 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.
2584
+ let new_fee = if new_fee < previous_fee + min_relay_fee {
2585
+ new_fee + previous_fee + min_relay_fee - new_fee
2586
+ } else {
2587
+ new_fee
2588
+ } ;
2589
+ Some ( ( new_fee, new_fee * 1000 / $predicted_weight) )
2590
+ }
2591
+ }
2592
+ }
2593
+
2594
+ let new_timer = Self :: get_height_timer ( height, cached_claim_datas. soonest_timelock ) ;
2595
+ let mut inputs_witnesses_weight = 0 ;
2596
+ let mut amt = 0 ;
2597
+ for per_outp_material in cached_claim_datas. per_input_material . values ( ) {
2598
+ match per_outp_material {
2599
+ & InputMaterial :: Revoked { ref script, ref is_htlc, ref amount, .. } => {
2600
+ 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 { & [ ] } ) ;
2601
+ amt += * amount;
2602
+ } ,
2603
+ & InputMaterial :: RemoteHTLC { .. } => { return None ; } ,
2604
+ & InputMaterial :: LocalHTLC { .. } => { return None ; }
2605
+ }
2606
+ }
2607
+ assert ! ( amt != 0 ) ;
2608
+
2609
+ let predicted_weight = bumped_tx. get_weight ( ) + inputs_witnesses_weight;
2610
+ let new_feerate;
2611
+ if let Some ( ( new_fee, feerate) ) = RBF_bump ! ( amt, cached_claim_datas. feerate_previous, fee_estimator, predicted_weight as u64 , outp. txid) {
2612
+ // If new computed fee is superior at the whole claimable amount burn all in fees
2613
+ if new_fee > amt {
2614
+ bumped_tx. output [ 0 ] . value = 0 ;
2615
+ } else {
2616
+ bumped_tx. output [ 0 ] . value = amt - new_fee;
2617
+ }
2618
+ new_feerate = feerate;
2619
+ } else {
2620
+ return None ;
2621
+ }
2622
+ assert ! ( new_feerate != 0 ) ;
2623
+
2624
+ for ( i, ( vout, per_outp_material) ) in cached_claim_datas. per_input_material . iter ( ) . enumerate ( ) {
2625
+ match per_outp_material {
2626
+ & InputMaterial :: Revoked { ref script, ref pubkey, ref key, ref is_htlc, ref amount } => {
2627
+ let sighash_parts = bip143:: SighashComponents :: new ( & bumped_tx) ;
2628
+ let sighash = hash_to_message ! ( & sighash_parts. sighash_all( & bumped_tx. input[ i] , & script, * amount) [ ..] ) ;
2629
+ let sig = self . secp_ctx . sign ( & sighash, & key) ;
2630
+ bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
2631
+ bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
2632
+ if * is_htlc {
2633
+ bumped_tx. input [ i] . witness . push ( pubkey. unwrap ( ) . clone ( ) . serialize ( ) . to_vec ( ) ) ;
2634
+ } else {
2635
+ bumped_tx. input [ i] . witness . push ( vec ! ( 1 ) ) ;
2636
+ }
2637
+ bumped_tx. input [ i] . witness . push ( script. clone ( ) . into_bytes ( ) ) ;
2638
+ 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, outp. txid, new_feerate) ;
2639
+ } ,
2640
+ & InputMaterial :: RemoteHTLC { .. } => { } ,
2641
+ & InputMaterial :: LocalHTLC { .. } => {
2642
+ //TODO : Given that Local Commitment Transaction and HTLC-Timeout/HTLC-Success are counter-signed by peer, we can't
2643
+ // RBF them. Need a Lightning specs change and package relay modification :
2644
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
2645
+ return None ;
2646
+ }
2647
+ }
2648
+ }
2649
+ assert ! ( predicted_weight >= bumped_tx. get_weight( ) ) ;
2650
+ Some ( ( new_timer, new_feerate, bumped_tx) )
2651
+ }
2513
2652
}
2514
2653
2515
2654
const MAX_ALLOC_SIZE : usize = 64 * 1024 ;
0 commit comments