@@ -280,11 +280,9 @@ impl PackageTemplate {
280
280
pub ( crate ) fn outpoints ( & self ) -> Vec < & BitcoinOutPoint > {
281
281
match self {
282
282
PackageTemplate :: MalleableJusticeTx { ref inputs } => {
283
- assert_ne ! ( inputs. len( ) , 0 ) ;
284
283
inputs. keys ( ) . collect ( )
285
284
} ,
286
285
PackageTemplate :: RemoteHTLCTx { ref inputs } => {
287
- assert_ne ! ( inputs. len( ) , 0 ) ;
288
286
inputs. keys ( ) . collect ( )
289
287
} ,
290
288
PackageTemplate :: LocalHTLCTx { ref input } => {
@@ -299,29 +297,37 @@ impl PackageTemplate {
299
297
} ,
300
298
}
301
299
}
302
- pub ( crate ) fn package_split ( & mut self , outp : & BitcoinOutPoint ) -> PackageTemplate {
303
- let package = match self {
300
+ pub ( crate ) fn package_split ( & mut self , outp : & BitcoinOutPoint ) -> Option < PackageTemplate > {
301
+ match self {
304
302
PackageTemplate :: MalleableJusticeTx { ref mut inputs } => {
305
- assert_ne ! ( inputs. len ( ) , 0 ) ;
306
- let removed = inputs . remove ( outp ) . unwrap ( ) ;
307
- let mut input_splitted = HashMap :: with_capacity ( 1 ) ;
308
- input_splitted . insert ( * outp , removed ) ;
309
- PackageTemplate :: MalleableJusticeTx {
310
- inputs : input_splitted ,
303
+ if let Some ( removed ) = inputs. remove ( outp ) {
304
+ let mut input_splitted = HashMap :: with_capacity ( 1 ) ;
305
+ input_splitted. insert ( * outp , removed ) ;
306
+ return Some ( PackageTemplate :: MalleableJusticeTx {
307
+ inputs : input_splitted ,
308
+ } ) ;
311
309
}
310
+ None
312
311
} ,
313
312
PackageTemplate :: RemoteHTLCTx { ref mut inputs } => {
314
- assert_ne ! ( inputs. len ( ) , 0 ) ;
315
- let removed = inputs . remove ( outp ) . unwrap ( ) ;
316
- let mut input_splitted = HashMap :: with_capacity ( 1 ) ;
317
- input_splitted . insert ( * outp , removed ) ;
318
- PackageTemplate :: RemoteHTLCTx {
319
- inputs : input_splitted ,
313
+ if let Some ( removed ) = inputs. remove ( outp ) {
314
+ let mut input_splitted = HashMap :: with_capacity ( 1 ) ;
315
+ input_splitted. insert ( * outp , removed ) ;
316
+ return Some ( PackageTemplate :: RemoteHTLCTx {
317
+ inputs : input_splitted ,
318
+ } ) ;
320
319
}
321
- } ,
322
- _ => panic ! ( "Removing outpoints from non-malleable packages" )
323
- } ;
324
- package
320
+ None
321
+ } ,
322
+ _ => {
323
+ // Note, we may try to split on remote transaction for
324
+ // which we don't have a competing one (HTLC-Success before
325
+ // timelock expiration). This explain we don't panic!.
326
+ // We should refactor OnchainTxHandler::block_connected to
327
+ // only test equality on competing claims.
328
+ return None ;
329
+ }
330
+ }
325
331
}
326
332
pub ( crate ) fn package_merge ( & mut self , mut template : PackageTemplate ) {
327
333
match self {
@@ -413,7 +419,7 @@ impl PackageTemplate {
413
419
} ;
414
420
bumped_tx. get_weight ( ) + witnesses_weight
415
421
}
416
- pub ( crate ) fn package_finalize < L : Deref , ChanSigner : ChannelKeys > ( & self , onchain_handler : & mut OnchainTxHandler < ChanSigner > , amount : u64 , destination_script : Script , logger : & L ) -> Option < Transaction >
422
+ pub ( crate ) fn package_finalize < L : Deref , ChanSigner : ChannelKeys > ( & self , onchain_handler : & mut OnchainTxHandler < ChanSigner > , value : u64 , destination_script : Script , logger : & L ) -> Option < Transaction >
417
423
where L :: Target : Logger ,
418
424
{
419
425
let mut bumped_tx = Transaction {
@@ -422,7 +428,7 @@ impl PackageTemplate {
422
428
input : vec ! [ ] ,
423
429
output : vec ! [ TxOut {
424
430
script_pubkey: destination_script,
425
- value: 0
431
+ value,
426
432
} ] ,
427
433
} ;
428
434
match self {
@@ -444,7 +450,7 @@ impl PackageTemplate {
444
450
chan_utils:: get_revokeable_redeemscript ( & chan_keys. revocation_key , revk. on_remote_tx_csv , & chan_keys. local_delayed_payment_key )
445
451
} ;
446
452
447
- if let Ok ( sig) = onchain_handler. key_storage . sign_justice_transaction ( & bumped_tx, i, amount, & revk. per_commitment_key , & revk. htlc , revk. on_remote_tx_csv , & onchain_handler. secp_ctx ) {
453
+ if let Ok ( sig) = onchain_handler. key_storage . sign_justice_transaction ( & bumped_tx, i, revk . amount , & revk. per_commitment_key , & revk. htlc , revk. on_remote_tx_csv , & onchain_handler. secp_ctx ) {
448
454
bumped_tx. input [ i] . witness . push ( sig. serialize_der ( ) . to_vec ( ) ) ;
449
455
bumped_tx. input [ i] . witness [ 0 ] . push ( SigHashType :: All as u8 ) ;
450
456
if revk. htlc . is_some ( ) {
@@ -558,6 +564,14 @@ impl PackageTemplate {
558
564
}
559
565
}
560
566
567
+ impl Default for PackageTemplate {
568
+ fn default ( ) -> Self {
569
+ PackageTemplate :: MalleableJusticeTx {
570
+ inputs : HashMap :: new ( ) ,
571
+ }
572
+ }
573
+ }
574
+
561
575
impl Writeable for PackageTemplate {
562
576
fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , :: std:: io:: Error > {
563
577
match self {
@@ -638,3 +652,152 @@ impl Readable for PackageTemplate {
638
652
Ok ( package)
639
653
}
640
654
}
655
+
656
+ /// BumpStrategy is a basic enum to encode a fee-committing strategy. We
657
+ /// may extend it in the future with other stategies like BYOF-input.
658
+ #[ derive( PartialEq , Clone ) ]
659
+ pub ( crate ) enum BumpStrategy {
660
+ RBF ,
661
+ CPFP
662
+ }
663
+
664
+ impl Writeable for BumpStrategy {
665
+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , :: std:: io:: Error > {
666
+ match self {
667
+ BumpStrategy :: RBF => {
668
+ writer. write_all ( & [ 0 ; 1 ] ) ?;
669
+ } ,
670
+ BumpStrategy :: CPFP => {
671
+ writer. write_all ( & [ 1 ; 1 ] ) ?;
672
+ }
673
+ }
674
+ Ok ( ( ) )
675
+ }
676
+ }
677
+
678
+ impl Readable for BumpStrategy {
679
+ fn read < R : :: std:: io:: Read > ( reader : & mut R ) -> Result < Self , DecodeError > {
680
+ let bump_strategy = match <u8 as Readable >:: read ( reader) ? {
681
+ 0 => {
682
+ BumpStrategy :: RBF
683
+ } ,
684
+ 1 => {
685
+ BumpStrategy :: CPFP
686
+ } ,
687
+ _ => return Err ( DecodeError :: InvalidValue ) ,
688
+ } ;
689
+ Ok ( bump_strategy)
690
+ }
691
+ }
692
+
693
+ /// A structure to describe a claim content and its metadatas which is generated
694
+ /// by ChannelMonitor and used by OnchainTxHandler to generate feerate-competive
695
+ /// transactions.
696
+ ///
697
+ /// Metadatas are related to multiple fields playing a role in packet lifetime.
698
+ /// Once issued, it may be aggregated with other requests if it's judged safe
699
+ /// and feerate opportunistic.
700
+ /// Current LN fees model, pre-committed fees with update_fee adjustement, means
701
+ /// that counter-signed transactions must be CPFP to be dynamically confirmed as a
702
+ /// bumping strategy. If transactions aren't lockdown (i.e justice transactions) we
703
+ /// may RBF them.
704
+ /// Feerate previous will serve as a feerate floor between different bumping attempts.
705
+ /// Height timer clocks these different bumping attempts.
706
+ /// Absolute timelock defines the block barrier at which claiming isn't exclusive
707
+ /// to us anymore and thus we MUST have get it solved before.
708
+ /// Height original serves as a packet timestamps to prune out claim in case of reorg.
709
+ /// Content embeds transactions elements to generate transaction. See PackageTemplate.
710
+ #[ derive( PartialEq , Clone ) ]
711
+ pub struct OnchainRequest {
712
+ // Timeout tx must have nLocktime set which means aggregating multiple
713
+ // ones must take the higher nLocktime among them to satisfy all of them.
714
+ // Sadly it has few pitfalls, a) it takes longuer to get fund back b) CLTV_DELTA
715
+ // of a sooner-HTLC could be swallowed by the highest nLocktime of the HTLC set.
716
+ // Do simplify we mark them as non-aggregable.
717
+ pub ( crate ) aggregation : bool ,
718
+ // Content may lockdown with counter-signature of our counterparty
719
+ // or fully-malleable by our own. Depending on this bumping strategy
720
+ // must be adapted.
721
+ pub ( crate ) bump_strategy : BumpStrategy ,
722
+ // Based feerate of previous broadcast. If resources available (either
723
+ // output value or utxo bumping).
724
+ pub ( crate ) feerate_previous : u64 ,
725
+ // At every block tick, used to check if pending claiming tx is taking too
726
+ // much time for confirmation and we need to bump it.
727
+ pub ( crate ) height_timer : Option < u32 > ,
728
+ // Block height before which claiming is exclusive to one party,
729
+ // after reaching it, claiming may be contentious.
730
+ pub ( crate ) absolute_timelock : u32 ,
731
+ // Tracked in case of reorg to wipe out now-superflous request.
732
+ pub ( crate ) height_original : u32 ,
733
+ // Content of request.
734
+ pub ( crate ) content : PackageTemplate ,
735
+ }
736
+
737
+ impl OnchainRequest {
738
+ pub ( crate ) fn request_merge ( & mut self , req : OnchainRequest ) {
739
+ // We init default onchain request with first merge content
740
+ if self . absolute_timelock == :: std:: u32:: MAX {
741
+ println ! ( "Init merging {}" , req. height_original) ;
742
+ self . height_original = req. height_original ;
743
+ self . content = req. content ;
744
+ self . absolute_timelock = req. absolute_timelock ;
745
+ return ;
746
+ }
747
+ assert_eq ! ( self . height_original, req. height_original) ;
748
+ if self . absolute_timelock > req. absolute_timelock {
749
+ self . absolute_timelock = req. absolute_timelock ;
750
+ }
751
+ self . content . package_merge ( req. content ) ;
752
+ }
753
+ }
754
+
755
+ impl Default for OnchainRequest {
756
+ fn default ( ) -> Self {
757
+ OnchainRequest {
758
+ aggregation : true ,
759
+ bump_strategy : BumpStrategy :: RBF ,
760
+ feerate_previous : 0 ,
761
+ height_timer : None ,
762
+ absolute_timelock : :: std:: u32:: MAX ,
763
+ height_original : 0 ,
764
+ content : PackageTemplate :: default ( )
765
+ }
766
+ }
767
+ }
768
+
769
+ impl Writeable for OnchainRequest {
770
+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , :: std:: io:: Error > {
771
+ self . aggregation . write ( writer) ?;
772
+ self . bump_strategy . write ( writer) ?;
773
+ self . feerate_previous . write ( writer) ?;
774
+ self . height_timer . write ( writer) ?;
775
+ self . absolute_timelock . write ( writer) ?;
776
+ self . height_original . write ( writer) ?;
777
+ self . content . write ( writer) ?;
778
+
779
+ Ok ( ( ) )
780
+ }
781
+ }
782
+
783
+ impl Readable for OnchainRequest {
784
+ fn read < R : :: std:: io:: Read > ( reader : & mut R ) -> Result < Self , DecodeError > {
785
+ let aggregation = Readable :: read ( reader) ?;
786
+ let bump_strategy = Readable :: read ( reader) ?;
787
+ let feerate_previous = Readable :: read ( reader) ?;
788
+ let height_timer = Readable :: read ( reader) ?;
789
+ let absolute_timelock = Readable :: read ( reader) ?;
790
+ let height_original = Readable :: read ( reader) ?;
791
+ let content = Readable :: read ( reader) ?;
792
+
793
+ Ok ( OnchainRequest {
794
+ aggregation,
795
+ bump_strategy,
796
+ feerate_previous,
797
+ height_timer,
798
+ absolute_timelock,
799
+ height_original,
800
+ content
801
+ } )
802
+ }
803
+ }
0 commit comments