@@ -10,7 +10,7 @@ mod common;
1010use common:: {
1111 do_channel_full_cycle, expect_channel_pending_event, expect_channel_ready_event, expect_event,
1212 expect_payment_claimable_event, expect_payment_received_event, expect_payment_successful_event,
13- generate_blocks_and_wait,
13+ generate_blocks_and_wait, get_transaction ,
1414 logging:: { init_log_logger, validate_log_entry, TestLogWriter } ,
1515 open_channel, premine_and_distribute_funds, random_config, random_listening_addresses,
1616 setup_bitcoind_and_electrsd, setup_builder, setup_node, setup_two_nodes, wait_for_tx,
@@ -33,15 +33,18 @@ use lightning_invoice::{Bolt11InvoiceDescription, Description};
3333use lightning_types:: payment:: { PaymentHash , PaymentPreimage } ;
3434
3535use bitcoin:: address:: NetworkUnchecked ;
36+ use bitcoin:: hashes:: hex:: FromHex ;
3637use bitcoin:: hashes:: sha256:: Hash as Sha256Hash ;
3738use bitcoin:: hashes:: Hash ;
38- use bitcoin:: Address ;
39- use bitcoin:: Amount ;
39+ use bitcoin:: { Address , Amount , ScriptBuf , Sequence , Transaction , Witness } ;
4040use log:: LevelFilter ;
4141
42+ use std:: collections:: HashSet ;
4243use std:: str:: FromStr ;
4344use std:: sync:: Arc ;
4445
46+ use crate :: common:: { distribute_funds_unconfirmed, premine_blocks} ;
47+
4548#[ test]
4649fn channel_full_cycle ( ) {
4750 let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
@@ -670,6 +673,217 @@ fn onchain_wallet_recovery() {
670673 ) ;
671674}
672675
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+
673887#[ test]
674888fn sign_verify_msg ( ) {
675889 let ( _bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
0 commit comments