@@ -2217,13 +2217,6 @@ impl ChannelMonitor {
2217
2217
}
2218
2218
}
2219
2219
}
2220
- for claim in pending_claims {
2221
- log_trace ! ( self , "Outpoint {}:{} is under claiming process, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , ( claim. 0 ) . vout, ( claim. 0 ) . txid, ( claim. 1 ) . 0 ) ;
2222
- match self . our_claim_txn_waiting_first_conf . entry ( claim. 0 ) {
2223
- hash_map:: Entry :: Occupied ( _) => { } ,
2224
- hash_map:: Entry :: Vacant ( entry) => { entry. insert ( claim. 1 ) ; }
2225
- }
2226
- }
2227
2220
if let Some ( events) = self . onchain_events_waiting_threshold_conf . remove ( & height) {
2228
2221
for ev in events {
2229
2222
match ev {
@@ -2238,12 +2231,22 @@ impl ChannelMonitor {
2238
2231
}
2239
2232
}
2240
2233
}
2241
- {
2242
- for ( claimed_txid, cached_claim_datas) in self . our_claim_txn_waiting_first_conf . iter_mut ( ) {
2243
- if cached_claim_datas. 0 == height {
2244
- }
2234
+ let mut bump_candidates = Vec :: new ( ) ;
2235
+ for ( claimed_txid, ref mut cached_claim_datas) in self . our_claim_txn_waiting_first_conf . iter_mut ( ) {
2236
+ if cached_claim_datas. 0 == height {
2237
+ bump_candidates. push ( ( claimed_txid. clone ( ) , cached_claim_datas. clone ( ) ) ) ;
2238
+ }
2239
+ }
2240
+ for ( claimed_txid, cached_claim_datas) in bump_candidates. iter_mut ( ) {
2241
+ if let Some ( ( new_timer, new_feerate, bump_tx) ) = self . bump_claim_tx ( height, & claimed_txid, & cached_claim_datas, fee_estimator) {
2242
+ cached_claim_datas. 0 = new_timer;
2243
+ cached_claim_datas. 2 = new_feerate;
2244
+ broadcaster. broadcast_transaction ( & bump_tx) ;
2245
2245
}
2246
2246
}
2247
+ for ( claimed_txid, cached_claim_datas) in bump_candidates. drain ( ..) {
2248
+ self . our_claim_txn_waiting_first_conf . insert ( claimed_txid, cached_claim_datas) ;
2249
+ }
2247
2250
self . last_block_hash = block_hash. clone ( ) ;
2248
2251
( watch_outputs, spendable_outputs, htlc_updated)
2249
2252
}
@@ -2457,6 +2460,122 @@ impl ChannelMonitor {
2457
2460
}
2458
2461
htlc_updated
2459
2462
}
2463
+
2464
+ /// 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
2465
+ /// (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.
2466
+ 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 ) > {
2467
+ let mut inputs = Vec :: new ( ) ;
2468
+ for vout in cached_claim_datas. 4 . keys ( ) {
2469
+ inputs. push ( TxIn {
2470
+ previous_output : BitcoinOutPoint {
2471
+ txid : txid. clone ( ) ,
2472
+ vout : * vout,
2473
+ } ,
2474
+ script_sig : Script :: new ( ) ,
2475
+ sequence : 0xfffffffd ,
2476
+ witness : Vec :: new ( ) ,
2477
+ } ) ;
2478
+ }
2479
+ let mut bumped_tx = Transaction {
2480
+ version : 2 ,
2481
+ lock_time : 0 ,
2482
+ input : inputs,
2483
+ output : vec ! [ TxOut {
2484
+ script_pubkey: self . destination_script. clone( ) ,
2485
+ value: 0
2486
+ } ] ,
2487
+ } ;
2488
+
2489
+ macro_rules! RBF_bump {
2490
+ ( $amount: expr, $old_feerate: expr, $fee_estimator: expr, $predicted_weight: expr, $txid: expr) => {
2491
+ {
2492
+ let mut used_feerate;
2493
+ // If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
2494
+ let new_fee = if $old_feerate < $fee_estimator. get_est_sat_per_1000_weight( ConfirmationTarget :: HighPriority ) {
2495
+ let mut value = $amount;
2496
+ if subtract_high_prio_fee!( self , $fee_estimator, value, $predicted_weight, txid, used_feerate) {
2497
+ $amount - value
2498
+ } else {
2499
+ log_trace!( self , "Can't new-estimation bump claiming on {}, amount {} is too small" , $txid, $amount) ;
2500
+ return None ;
2501
+ }
2502
+ // ...else just increase the previous feerate by 25% (because that's a nice number)
2503
+ } else {
2504
+ let fee = $old_feerate * $predicted_weight / 750 ;
2505
+ if $amount <= fee {
2506
+ log_trace!( self , "Can't 25% bump claiming on {}, amount {} is too small" , $txid, $amount) ;
2507
+ return None ;
2508
+ }
2509
+ fee
2510
+ } ;
2511
+
2512
+ let previous_fee = $old_feerate * $predicted_weight / 1000 ;
2513
+ let min_relay_fee = $fee_estimator. get_min_relay_sat_per_1000_weight( ) * $predicted_weight / 1000 ;
2514
+ // BIP 125 Opt-in Full Replace-by-Fee Signaling
2515
+ // * 3. The replacement transaction pays an absolute fee of at least the sum paid by the original transactions.
2516
+ // * 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.
2517
+ let new_fee = if new_fee < previous_fee + min_relay_fee {
2518
+ new_fee + previous_fee + min_relay_fee - new_fee
2519
+ } else {
2520
+ new_fee
2521
+ } ;
2522
+ Some ( ( new_fee, new_fee * 1000 / $predicted_weight) )
2523
+ }
2524
+ }
2525
+ }
2526
+
2527
+ let new_timer = Self :: get_height_timer ( height, cached_claim_datas. 3 ) ;
2528
+ let mut inputs_witnesses_weight = 0 ;
2529
+ let mut amt = 0 ;
2530
+ for per_outp_material in cached_claim_datas. 4 . values ( ) {
2531
+ match per_outp_material {
2532
+ & TxMaterial :: Revoked { ref script, ref is_htlc, ref amount, .. } => {
2533
+ 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 { & [ ] } ) ;
2534
+ amt = * amount;
2535
+ } ,
2536
+ & TxMaterial :: RemoteHTLC { .. } => { } ,
2537
+ & TxMaterial :: LocalHTLC { .. } => { } ,
2538
+ }
2539
+ }
2540
+ assert ! ( amt != 0 ) ;
2541
+
2542
+ let predicted_weight = bumped_tx. get_weight ( ) + inputs_witnesses_weight;
2543
+ let mut new_feerate = 0 ;
2544
+ if let Some ( ( new_fee, feerate) ) = RBF_bump ! ( amt, cached_claim_datas. 2 , fee_estimator, predicted_weight, txid) {
2545
+ bumped_tx. output [ 0 ] . value = amt - new_fee;
2546
+ new_feerate = feerate;
2547
+ } else {
2548
+ return None ;
2549
+ }
2550
+
2551
+ for ( i, ( vout, per_outp_material) ) in cached_claim_datas. 4 . iter ( ) . enumerate ( ) {
2552
+ match per_outp_material {
2553
+ & TxMaterial :: Revoked { ref script, ref pubkey, ref key, ref is_htlc, ref amount } => {
2554
+ let sighash_parts = bip143:: SighashComponents :: new ( & bumped_tx) ;
2555
+ let sighash = hash_to_message ! ( & sighash_parts. sighash_all( & bumped_tx. input[ i] , & script, * amount) [ ..] ) ;
2556
+ let sig = self . secp_ctx . sign ( & sighash, & key) ;
2557
+ bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
2558
+ bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
2559
+ if * is_htlc {
2560
+ bumped_tx. input [ i] . witness . push ( pubkey. unwrap ( ) . clone ( ) . serialize ( ) . to_vec ( ) ) ;
2561
+ } else {
2562
+ bumped_tx. input [ i] . witness . push ( vec ! ( 1 ) ) ;
2563
+ }
2564
+ bumped_tx. input [ i] . witness . push ( script. clone ( ) . into_bytes ( ) ) ;
2565
+ 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) ;
2566
+ } ,
2567
+ & TxMaterial :: RemoteHTLC { .. } => { } ,
2568
+ & TxMaterial :: LocalHTLC { .. } => {
2569
+ //TODO : Given that Local Commitment Transaction and HTLC-Timeout/HTLC-Success are counter-signed by peer, we can't
2570
+ // RBF them. Need a Lightning specs change and package relay modification :
2571
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
2572
+ return None ;
2573
+ }
2574
+ }
2575
+ }
2576
+ assert ! ( predicted_weight >= bumped_tx. get_weight( ) ) ;
2577
+ Some ( ( new_timer, new_feerate, bumped_tx) )
2578
+ }
2460
2579
}
2461
2580
2462
2581
const MAX_ALLOC_SIZE : usize = 64 * 1024 ;
0 commit comments