@@ -52,7 +52,7 @@ enum OnchainEvent {
52
52
pub struct ClaimTxBumpMaterial {
53
53
// At every block tick, used to check if pending claiming tx is taking too
54
54
// much time for confirmation and we need to bump it.
55
- height_timer : u32 ,
55
+ height_timer : Option < u32 > ,
56
56
// Tracked in case of reorg to wipe out now-superflous bump material
57
57
feerate_previous : u64 ,
58
58
// Soonest timelocks among set of outpoints claimed, used to compute
@@ -64,7 +64,7 @@ pub struct ClaimTxBumpMaterial {
64
64
65
65
impl Writeable for ClaimTxBumpMaterial {
66
66
fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , :: std:: io:: Error > {
67
- writer . write_all ( & byte_utils :: be32_to_array ( self . height_timer ) ) ?;
67
+ self . height_timer . write ( writer ) ?;
68
68
writer. write_all ( & byte_utils:: be64_to_array ( self . feerate_previous ) ) ?;
69
69
writer. write_all ( & byte_utils:: be32_to_array ( self . soonest_timelock ) ) ?;
70
70
writer. write_all ( & byte_utils:: be64_to_array ( self . per_input_material . len ( ) as u64 ) ) ?;
@@ -357,7 +357,7 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
357
357
358
358
/// 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
359
359
/// (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.
360
- fn generate_claim_tx < F : Deref > ( & self , height : u32 , cached_claim_datas : & ClaimTxBumpMaterial , fee_estimator : F ) -> Option < ( u32 , u64 , Transaction ) >
360
+ fn generate_claim_tx < F : Deref > ( & self , height : u32 , cached_claim_datas : & ClaimTxBumpMaterial , fee_estimator : F ) -> Option < ( Option < u32 > , u64 , Transaction ) >
361
361
where F :: Target : FeeEstimator
362
362
{
363
363
if cached_claim_datas. per_input_material . len ( ) == 0 { return None } // But don't prune pending claiming request yet, we may have to resurrect HTLCs
@@ -422,9 +422,10 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
422
422
423
423
// Compute new height timer to decide when we need to regenerate a new bumped version of the claim tx (if we
424
424
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
425
- let new_timer = Self :: get_height_timer ( height, cached_claim_datas. soonest_timelock ) ;
425
+ let new_timer = Some ( Self :: get_height_timer ( height, cached_claim_datas. soonest_timelock ) ) ;
426
426
let mut inputs_witnesses_weight = 0 ;
427
427
let mut amt = 0 ;
428
+ let mut dynamic_fee = true ;
428
429
for per_outp_material in cached_claim_datas. per_input_material . values ( ) {
429
430
match per_outp_material {
430
431
& InputMaterial :: Revoked { ref witness_script, ref is_htlc, ref amount, .. } => {
@@ -436,71 +437,99 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
436
437
amt += * amount;
437
438
} ,
438
439
& InputMaterial :: LocalHTLC { .. } => { return None ; }
439
- }
440
- }
441
-
442
- let predicted_weight = bumped_tx. get_weight ( ) + inputs_witnesses_weight;
443
- let mut new_feerate;
444
- // If old feerate is 0, first iteration of this claim, use normal fee calculation
445
- if cached_claim_datas. feerate_previous != 0 {
446
- if let Some ( ( new_fee, feerate) ) = RBF_bump ! ( amt, cached_claim_datas. feerate_previous, fee_estimator, predicted_weight as u64 ) {
447
- // If new computed fee is superior at the whole claimable amount burn all in fees
448
- if new_fee > amt {
449
- bumped_tx. output [ 0 ] . value = 0 ;
450
- } else {
451
- bumped_tx. output [ 0 ] . value = amt - new_fee;
440
+ & InputMaterial :: Funding { .. } => {
441
+ dynamic_fee = false ;
452
442
}
453
- new_feerate = feerate;
454
- } else { return None ; }
455
- } else {
456
- if subtract_high_prio_fee ! ( self , fee_estimator, amt, predicted_weight, new_feerate) {
457
- bumped_tx. output [ 0 ] . value = amt;
458
- } else { return None ; }
443
+ }
459
444
}
460
- assert ! ( new_feerate != 0 ) ;
461
445
462
- for ( i, ( outp, per_outp_material) ) in cached_claim_datas. per_input_material . iter ( ) . enumerate ( ) {
463
- match per_outp_material {
464
- & InputMaterial :: Revoked { ref witness_script, ref pubkey, ref key, ref is_htlc, ref amount } => {
465
- let sighash_parts = bip143:: SighashComponents :: new ( & bumped_tx) ;
466
- let sighash = hash_to_message ! ( & sighash_parts. sighash_all( & bumped_tx. input[ i] , & witness_script, * amount) [ ..] ) ;
467
- let sig = self . secp_ctx . sign ( & sighash, & key) ;
468
- bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
469
- bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
470
- if * is_htlc {
471
- bumped_tx. input [ i] . witness . push ( pubkey. unwrap ( ) . clone ( ) . serialize ( ) . to_vec ( ) ) ;
446
+ if dynamic_fee {
447
+ let predicted_weight = bumped_tx. get_weight ( ) + inputs_witnesses_weight;
448
+ let mut new_feerate;
449
+ // If old feerate is 0, first iteration of this claim, use normal fee calculation
450
+ if cached_claim_datas. feerate_previous != 0 {
451
+ if let Some ( ( new_fee, feerate) ) = RBF_bump ! ( amt, cached_claim_datas. feerate_previous, fee_estimator, predicted_weight as u64 ) {
452
+ // If new computed fee is superior at the whole claimable amount burn all in fees
453
+ if new_fee > amt {
454
+ bumped_tx. output [ 0 ] . value = 0 ;
472
455
} else {
473
- bumped_tx. input [ i ] . witness . push ( vec ! ( 1 ) ) ;
456
+ bumped_tx. output [ 0 ] . value = amt - new_fee ;
474
457
}
475
- bumped_tx. input [ i] . witness . push ( witness_script. clone ( ) . into_bytes ( ) ) ;
476
- log_trace ! ( self , "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}..." , bumped_tx. txid( ) , if !is_htlc { "to_local" } else if HTLCType :: scriptlen_to_htlctype( witness_script. len( ) ) == Some ( HTLCType :: OfferedHTLC ) { "offered" } else if HTLCType :: scriptlen_to_htlctype( witness_script. len( ) ) == Some ( HTLCType :: AcceptedHTLC ) { "received" } else { "" } , outp. vout, outp. txid, new_feerate) ;
477
- } ,
478
- & InputMaterial :: RemoteHTLC { ref witness_script, ref key, ref preimage, ref amount, ref locktime } => {
479
- if !preimage. is_some ( ) { bumped_tx. lock_time = * locktime } ; // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation
480
- let sighash_parts = bip143:: SighashComponents :: new ( & bumped_tx) ;
481
- let sighash = hash_to_message ! ( & sighash_parts. sighash_all( & bumped_tx. input[ i] , & witness_script, * amount) [ ..] ) ;
482
- let sig = self . secp_ctx . sign ( & sighash, & key) ;
483
- bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
484
- bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
485
- if let & Some ( preimage) = preimage {
486
- bumped_tx. input [ i] . witness . push ( preimage. clone ( ) . 0 . to_vec ( ) ) ;
487
- } else {
488
- bumped_tx. input [ i] . witness . push ( vec ! [ 0 ] ) ;
458
+ new_feerate = feerate;
459
+ } else { return None ; }
460
+ } else {
461
+ if subtract_high_prio_fee ! ( self , fee_estimator, amt, predicted_weight, new_feerate) {
462
+ bumped_tx. output [ 0 ] . value = amt;
463
+ } else { return None ; }
464
+ }
465
+ assert ! ( new_feerate != 0 ) ;
466
+
467
+ for ( i, ( outp, per_outp_material) ) in cached_claim_datas. per_input_material . iter ( ) . enumerate ( ) {
468
+ match per_outp_material {
469
+ & InputMaterial :: Revoked { ref witness_script, ref pubkey, ref key, ref is_htlc, ref amount } => {
470
+ let sighash_parts = bip143:: SighashComponents :: new ( & bumped_tx) ;
471
+ let sighash = hash_to_message ! ( & sighash_parts. sighash_all( & bumped_tx. input[ i] , & witness_script, * amount) [ ..] ) ;
472
+ let sig = self . secp_ctx . sign ( & sighash, & key) ;
473
+ bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
474
+ bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
475
+ if * is_htlc {
476
+ bumped_tx. input [ i] . witness . push ( pubkey. unwrap ( ) . clone ( ) . serialize ( ) . to_vec ( ) ) ;
477
+ } else {
478
+ bumped_tx. input [ i] . witness . push ( vec ! ( 1 ) ) ;
479
+ }
480
+ bumped_tx. input [ i] . witness . push ( witness_script. clone ( ) . into_bytes ( ) ) ;
481
+ log_trace ! ( self , "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}..." , bumped_tx. txid( ) , if !is_htlc { "to_local" } else if HTLCType :: scriptlen_to_htlctype( witness_script. len( ) ) == Some ( HTLCType :: OfferedHTLC ) { "offered" } else if HTLCType :: scriptlen_to_htlctype( witness_script. len( ) ) == Some ( HTLCType :: AcceptedHTLC ) { "received" } else { "" } , outp. vout, outp. txid, new_feerate) ;
482
+ } ,
483
+ & InputMaterial :: RemoteHTLC { ref witness_script, ref key, ref preimage, ref amount, ref locktime } => {
484
+ if !preimage. is_some ( ) { bumped_tx. lock_time = * locktime } ; // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation
485
+ let sighash_parts = bip143:: SighashComponents :: new ( & bumped_tx) ;
486
+ let sighash = hash_to_message ! ( & sighash_parts. sighash_all( & bumped_tx. input[ i] , & witness_script, * amount) [ ..] ) ;
487
+ let sig = self . secp_ctx . sign ( & sighash, & key) ;
488
+ bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
489
+ bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
490
+ if let & Some ( preimage) = preimage {
491
+ bumped_tx. input [ i] . witness . push ( preimage. clone ( ) . 0 . to_vec ( ) ) ;
492
+ } else {
493
+ bumped_tx. input [ i] . witness . push ( vec ! [ 0 ] ) ;
494
+ }
495
+ bumped_tx. input [ i] . witness . push ( witness_script. clone ( ) . into_bytes ( ) ) ;
496
+ log_trace ! ( self , "Going to broadcast Claim Transaction {} claiming remote {} htlc output {} from {} with new feerate {}..." , bumped_tx. txid( ) , if preimage. is_some( ) { "offered" } else { "received" } , outp. vout, outp. txid, new_feerate) ;
497
+ } ,
498
+ _ => unreachable ! ( )
499
+ }
500
+ }
501
+ log_trace ! ( self , "...with timer {}" , new_timer. unwrap( ) ) ;
502
+ assert ! ( predicted_weight >= bumped_tx. get_weight( ) ) ;
503
+ return Some ( ( new_timer, new_feerate, bumped_tx) )
504
+ } else {
505
+ for ( _, ( outp, per_outp_material) ) in cached_claim_datas. per_input_material . iter ( ) . enumerate ( ) {
506
+ match per_outp_material {
507
+ & InputMaterial :: LocalHTLC { .. } => {
508
+ //TODO : Given that Local Commitment Transaction and HTLC-Timeout/HTLC-Success are counter-signed by peer, we can't
509
+ // RBF them. Need a Lightning specs change and package relay modification :
510
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
511
+ return None ;
512
+ } ,
513
+ & InputMaterial :: Funding { ref channel_value } => {
514
+ if self . local_commitment . is_some ( ) {
515
+ let mut local_commitment = self . local_commitment . clone ( ) . unwrap ( ) ;
516
+ self . key_storage . sign_local_commitment ( & mut local_commitment, & self . funding_redeemscript , * channel_value, & self . secp_ctx ) ;
517
+ let signed_tx = local_commitment. with_valid_witness ( ) . clone ( ) ;
518
+ let mut amt_outputs = 0 ;
519
+ for outp in signed_tx. output . iter ( ) {
520
+ amt_outputs += outp. value ;
521
+ }
522
+ let feerate = ( channel_value - amt_outputs) * 1000 / signed_tx. get_weight ( ) as u64 ;
523
+ // Timer set to $NEVER given we can't bump tx without anchor outputs
524
+ log_trace ! ( self , "Going to broadcast Local Transaction {} claiming funding output {} from {}..." , signed_tx. txid( ) , outp. vout, outp. txid) ;
525
+ return Some ( ( None , feerate, signed_tx) ) ;
526
+ }
489
527
}
490
- bumped_tx. input [ i] . witness . push ( witness_script. clone ( ) . into_bytes ( ) ) ;
491
- log_trace ! ( self , "Going to broadcast Claim Transaction {} claiming remote {} htlc output {} from {} with new feerate {}..." , bumped_tx. txid( ) , if preimage. is_some( ) { "offered" } else { "received" } , outp. vout, outp. txid, new_feerate) ;
492
- } ,
493
- & InputMaterial :: LocalHTLC { .. } => {
494
- //TODO : Given that Local Commitment Transaction and HTLC-Timeout/HTLC-Success are counter-signed by peer, we can't
495
- // RBF them. Need a Lightning specs change and package relay modification :
496
- // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
497
- return None ;
528
+ _ => unreachable ! ( )
498
529
}
499
530
}
500
531
}
501
- log_trace ! ( self , "...with timer {}" , new_timer) ;
502
- assert ! ( predicted_weight >= bumped_tx. get_weight( ) ) ;
503
- Some ( ( new_timer, new_feerate, bumped_tx) )
532
+ None
504
533
}
505
534
506
535
pub ( super ) fn block_connected < B : Deref , F : Deref > ( & mut self , txn_matched : & [ & Transaction ] , claimable_outpoints : Vec < ClaimRequest > , height : u32 , broadcaster : B , fee_estimator : F )
@@ -535,7 +564,7 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
535
564
// Generate claim transactions and track them to bump if necessary at
536
565
// height timer expiration (i.e in how many blocks we're going to take action).
537
566
for claim in new_claims {
538
- let mut claim_material = ClaimTxBumpMaterial { height_timer : 0 , feerate_previous : 0 , soonest_timelock : claim. 0 , per_input_material : claim. 1 . clone ( ) } ;
567
+ let mut claim_material = ClaimTxBumpMaterial { height_timer : None , feerate_previous : 0 , soonest_timelock : claim. 0 , per_input_material : claim. 1 . clone ( ) } ;
539
568
if let Some ( ( new_timer, new_feerate, tx) ) = self . generate_claim_tx ( height, & claim_material, & * fee_estimator) {
540
569
claim_material. height_timer = new_timer;
541
570
claim_material. feerate_previous = new_feerate;
@@ -653,8 +682,10 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
653
682
654
683
// Check if any pending claim request must be rescheduled
655
684
for ( first_claim_txid, ref claim_data) in self . pending_claim_requests . iter ( ) {
656
- if claim_data. height_timer == height {
657
- bump_candidates. insert ( * first_claim_txid) ;
685
+ if let Some ( h) = claim_data. height_timer {
686
+ if h == height {
687
+ bump_candidates. insert ( * first_claim_txid) ;
688
+ }
658
689
}
659
690
}
660
691
0 commit comments