@@ -10,7 +10,7 @@ mod common;
10
10
use common:: {
11
11
do_channel_full_cycle, expect_channel_pending_event, expect_channel_ready_event, expect_event,
12
12
expect_payment_claimable_event, expect_payment_received_event, expect_payment_successful_event,
13
- generate_blocks_and_wait,
13
+ generate_blocks_and_wait, get_transaction ,
14
14
logging:: { init_log_logger, validate_log_entry, TestLogWriter } ,
15
15
open_channel, premine_and_distribute_funds, random_config, random_listening_addresses,
16
16
setup_bitcoind_and_electrsd, setup_builder, setup_node, setup_two_nodes, wait_for_tx,
@@ -33,15 +33,18 @@ use lightning_invoice::{Bolt11InvoiceDescription, Description};
33
33
use lightning_types:: payment:: { PaymentHash , PaymentPreimage } ;
34
34
35
35
use bitcoin:: address:: NetworkUnchecked ;
36
+ use bitcoin:: hashes:: hex:: FromHex ;
36
37
use bitcoin:: hashes:: sha256:: Hash as Sha256Hash ;
37
38
use bitcoin:: hashes:: Hash ;
38
- use bitcoin:: Address ;
39
- use bitcoin:: Amount ;
39
+ use bitcoin:: { Address , Amount , ScriptBuf , Sequence , Transaction , Witness } ;
40
40
use log:: LevelFilter ;
41
41
42
+ use std:: collections:: HashSet ;
42
43
use std:: str:: FromStr ;
43
44
use std:: sync:: Arc ;
44
45
46
+ use crate :: common:: { distribute_funds_unconfirmed, premine_blocks} ;
47
+
45
48
#[ test]
46
49
fn channel_full_cycle ( ) {
47
50
let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
@@ -670,6 +673,217 @@ fn onchain_wallet_recovery() {
670
673
) ;
671
674
}
672
675
676
+ #[ test]
677
+ fn test_rbf_via_mempool ( ) {
678
+ run_rbf_test ( false ) ;
679
+ }
680
+
681
+ #[ test]
682
+ fn test_rbf_via_direct_block_insertion ( ) {
683
+ run_rbf_test ( true ) ;
684
+ }
685
+
686
+ // `is_insert_block`:
687
+ // - `true`: transaction is mined immediately (no mempool), testing confirmed-Tx handling.
688
+ // - `false`: transaction stays in mempool until confirmation, testing unconfirmed-Tx handling.
689
+ fn run_rbf_test ( is_insert_block : bool ) {
690
+ let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
691
+ let chain_source_bitcoind = TestChainSource :: BitcoindRpcSync ( & bitcoind) ;
692
+ let chain_source_electrsd = TestChainSource :: Electrum ( & electrsd) ;
693
+ let chain_source_esplora = TestChainSource :: Esplora ( & electrsd) ;
694
+
695
+ macro_rules! config_node {
696
+ ( $chain_source: expr, $anchor_channels: expr) => { {
697
+ let config_a = random_config( $anchor_channels) ;
698
+ let node = setup_node( & $chain_source, config_a, None ) ;
699
+ node
700
+ } } ;
701
+ }
702
+ let anchor_channels = false ;
703
+ let nodes = vec ! [
704
+ config_node!( chain_source_electrsd, anchor_channels) ,
705
+ config_node!( chain_source_bitcoind, anchor_channels) ,
706
+ config_node!( chain_source_esplora, anchor_channels) ,
707
+ ] ;
708
+
709
+ let ( bitcoind, electrs) = ( & bitcoind. client , & electrsd. client ) ;
710
+ premine_blocks ( bitcoind, electrs) ;
711
+
712
+ // Helpers declaration before starting the test
713
+ let all_addrs =
714
+ nodes. iter ( ) . map ( |node| node. onchain_payment ( ) . new_address ( ) . unwrap ( ) ) . collect :: < Vec < _ > > ( ) ;
715
+ let amount_sat = 2_100_000 ;
716
+ let mut txid;
717
+ macro_rules! distribute_funds_all_nodes {
718
+ ( ) => {
719
+ txid = distribute_funds_unconfirmed(
720
+ bitcoind,
721
+ electrs,
722
+ all_addrs. clone( ) ,
723
+ Amount :: from_sat( amount_sat) ,
724
+ ) ;
725
+ } ;
726
+ }
727
+
728
+ let mut tx;
729
+ let scripts_buf: HashSet < ScriptBuf > =
730
+ all_addrs. iter ( ) . map ( |addr| addr. script_pubkey ( ) ) . collect ( ) ;
731
+ let mut fee_output_index;
732
+ macro_rules! prepare_rbf {
733
+ ( ) => {
734
+ tx = get_transaction( electrs, txid) ;
735
+
736
+ let mut option_fee_output_index = None ;
737
+ for ( index, output) in tx. output. iter( ) . enumerate( ) {
738
+ if !scripts_buf. contains( & output. script_pubkey) {
739
+ option_fee_output_index = Some ( index) ;
740
+ break ;
741
+ }
742
+ }
743
+ fee_output_index = option_fee_output_index. expect(
744
+ "No output available for fee pumping. Need at least one output not being modified." ,
745
+ ) ;
746
+ } ;
747
+ }
748
+
749
+ let mut bump_fee_amount_sat;
750
+ macro_rules! bump_fee_rbf_and_public_transaction {
751
+ ( ) => {
752
+ bump_fee_amount_sat = tx. vsize( ) as u64 ;
753
+ let attempts = 5 ;
754
+ for _attempt in 0 ..attempts {
755
+ bump_fee!( ) ;
756
+
757
+ match bitcoind. send_raw_transaction( & tx) {
758
+ Ok ( res) => {
759
+ // Mine a block immediately so the transaction is confirmed
760
+ // before any node identifies it as a transaction that was in the mempool.
761
+ if is_insert_block {
762
+ generate_blocks_and_wait( bitcoind, electrs, 1 ) ;
763
+ }
764
+ let new_txid = res. 0 . parse( ) . unwrap( ) ;
765
+ wait_for_tx( electrs, new_txid) ;
766
+ break ;
767
+ } ,
768
+ Err ( _) => {
769
+ if _attempt == attempts - 1 {
770
+ panic!( "Failed to pump fee after {} attempts" , attempts) ;
771
+ }
772
+
773
+ bump_fee_amount_sat += bump_fee_amount_sat * 5 ;
774
+ if tx. output[ fee_output_index] . value. to_sat( ) < bump_fee_amount_sat {
775
+ panic!( "Insufficient funds to increase fee" ) ;
776
+ }
777
+ } ,
778
+ }
779
+ }
780
+ } ;
781
+ }
782
+
783
+ macro_rules! bump_fee {
784
+ ( ) => {
785
+ let fee_output = & mut tx. output[ fee_output_index] ;
786
+ let new_fee_value = fee_output. value. to_sat( ) . saturating_sub( bump_fee_amount_sat) ;
787
+ fee_output. value = Amount :: from_sat( new_fee_value) ;
788
+
789
+ // dust limit
790
+ if new_fee_value < 546 {
791
+ panic!( "Warning: Fee output approaching dust limit ({} sats)" , new_fee_value) ;
792
+ }
793
+
794
+ for input in & mut tx. input {
795
+ input. sequence = Sequence :: ENABLE_RBF_NO_LOCKTIME ;
796
+ input. script_sig = ScriptBuf :: new( ) ;
797
+ input. witness = Witness :: new( ) ;
798
+ }
799
+
800
+ let signed_result = bitcoind. sign_raw_transaction_with_wallet( & tx) . unwrap( ) ;
801
+ assert!( signed_result. complete, "Failed to sign RBF transaction" ) ;
802
+
803
+ let tx_bytes = Vec :: <u8 >:: from_hex( & signed_result. hex) . unwrap( ) ;
804
+ tx = bitcoin:: consensus:: encode:: deserialize:: <Transaction >( & tx_bytes) . unwrap( ) ;
805
+ } ;
806
+ }
807
+
808
+ macro_rules! validate_balances {
809
+ ( $expected_balance_sat: expr, $is_spendable: expr) => {
810
+ let spend_balance = if $is_spendable { $expected_balance_sat } else { 0 } ;
811
+ for node in & nodes {
812
+ node. sync_wallets( ) . unwrap( ) ;
813
+ let balances = node. list_balances( ) ;
814
+ assert_eq!( balances. spendable_onchain_balance_sats, spend_balance) ;
815
+ assert_eq!( balances. total_onchain_balance_sats, $expected_balance_sat) ;
816
+ }
817
+ } ;
818
+ }
819
+
820
+ // Modify the output to the nodes
821
+ distribute_funds_all_nodes ! ( ) ;
822
+ validate_balances ! ( amount_sat, false ) ;
823
+ prepare_rbf ! ( ) ;
824
+ tx. output . iter_mut ( ) . for_each ( |output| {
825
+ if scripts_buf. contains ( & output. script_pubkey ) {
826
+ let new_addr = bitcoind. new_address ( ) . unwrap ( ) ;
827
+ output. script_pubkey = new_addr. script_pubkey ( ) ;
828
+ }
829
+ } ) ;
830
+ bump_fee_rbf_and_public_transaction ! ( ) ;
831
+ validate_balances ! ( 0 , is_insert_block) ;
832
+
833
+ // Not modifying the output scripts, but still bumping the fee.
834
+ distribute_funds_all_nodes ! ( ) ;
835
+ validate_balances ! ( amount_sat, false ) ;
836
+ prepare_rbf ! ( ) ;
837
+ bump_fee_rbf_and_public_transaction ! ( ) ;
838
+ validate_balances ! ( amount_sat, is_insert_block) ;
839
+
840
+ let mut final_amount_sat = amount_sat * 2 ;
841
+ let value_sat = 21_000 ;
842
+
843
+ // Increase the value of the nodes' outputs
844
+ distribute_funds_all_nodes ! ( ) ;
845
+ prepare_rbf ! ( ) ;
846
+ tx. output . iter_mut ( ) . for_each ( |output| {
847
+ if scripts_buf. contains ( & output. script_pubkey ) {
848
+ output. value = Amount :: from_sat ( output. value . to_sat ( ) + value_sat) ;
849
+ }
850
+ } ) ;
851
+ bump_fee_rbf_and_public_transaction ! ( ) ;
852
+ final_amount_sat += value_sat;
853
+ validate_balances ! ( final_amount_sat, is_insert_block) ;
854
+
855
+ // Decreases the value of the nodes' outputs
856
+ distribute_funds_all_nodes ! ( ) ;
857
+ final_amount_sat += amount_sat;
858
+ prepare_rbf ! ( ) ;
859
+ tx. output . iter_mut ( ) . for_each ( |output| {
860
+ if scripts_buf. contains ( & output. script_pubkey ) {
861
+ output. value = Amount :: from_sat ( output. value . to_sat ( ) - value_sat) ;
862
+ }
863
+ } ) ;
864
+ bump_fee_rbf_and_public_transaction ! ( ) ;
865
+ final_amount_sat -= value_sat;
866
+ validate_balances ! ( final_amount_sat, is_insert_block) ;
867
+
868
+ if !is_insert_block {
869
+ generate_blocks_and_wait ( bitcoind, electrs, 1 ) ;
870
+ validate_balances ! ( final_amount_sat, true ) ;
871
+ }
872
+
873
+ // Check if it is possible to send all funds from the node
874
+ let mut txids = Vec :: new ( ) ;
875
+ let addr = bitcoind. new_address ( ) . unwrap ( ) ;
876
+ nodes. iter ( ) . for_each ( |node| {
877
+ let txid = node. onchain_payment ( ) . send_all_to_address ( & addr, true , None ) . unwrap ( ) ;
878
+ txids. push ( txid) ;
879
+ } ) ;
880
+ txids. iter ( ) . for_each ( |txid| {
881
+ wait_for_tx ( electrs, * txid) ;
882
+ } ) ;
883
+ generate_blocks_and_wait ( bitcoind, electrs, 6 ) ;
884
+ validate_balances ! ( 0 , true ) ;
885
+ }
886
+
673
887
#[ test]
674
888
fn sign_verify_msg ( ) {
675
889
let ( _bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
0 commit comments