@@ -3,7 +3,7 @@ use crate::{
33 bitcoind:: {
44 interface:: { BitcoinD , DepositsState , SyncInfo , UnvaultsState , UtxoInfo } ,
55 utils:: {
6- cancel_txids, emer_txid, populate_deposit_cache, populate_unvaults_cache,
6+ cancel_txids, cpfp_package , emer_txid, populate_deposit_cache, populate_unvaults_cache,
77 presigned_transactions, unemer_txid, unvault_txid, unvault_txin_from_deposit,
88 vault_deposit_utxo,
99 } ,
@@ -31,20 +31,16 @@ use crate::{
3131 revaultd:: { BlockchainTip , RevaultD , VaultStatus } ,
3232} ;
3333use revault_tx:: {
34- bitcoin:: { consensus:: encode, secp256k1, Amount , OutPoint , Txid } ,
35- error:: TransactionCreationError ,
34+ bitcoin:: { secp256k1, Amount , OutPoint , Txid } ,
3635 miniscript:: descriptor:: { DescriptorSecretKey , DescriptorXKey , KeyMap , Wildcard } ,
3736 scripts:: CpfpDescriptor ,
38- transactions:: {
39- CpfpTransaction , CpfpableTransaction , RevaultTransaction , SpendTransaction ,
40- UnvaultTransaction ,
41- } ,
37+ transactions:: { CpfpableTransaction , RevaultTransaction , SpendTransaction , UnvaultTransaction } ,
4238 txins:: { CpfpTxIn , RevaultTxIn } ,
43- txouts:: { CpfpTxOut , RevaultTxOut } ,
39+ txouts:: RevaultTxOut ,
4440} ;
4541
4642use std:: {
47- collections:: { HashMap , HashSet } ,
43+ collections:: HashMap ,
4844 path:: { Path , PathBuf } ,
4945 sync:: {
5046 atomic:: { AtomicBool , Ordering } ,
@@ -408,7 +404,7 @@ fn mark_confirmed_emers(
408404 Ok ( ( ) )
409405}
410406
411- enum ToBeCpfped {
407+ pub enum ToBeCpfped {
412408 Spend ( SpendTransaction ) ,
413409 Unvault ( UnvaultTransaction ) ,
414410}
@@ -447,101 +443,12 @@ impl ToBeCpfped {
447443 }
448444}
449445
450- // CPFP a bunch of transactions, bumping their feerate by at least `target_feerate`.
451- // `target_feerate` is expressed in sat/kWU.
452- // All the transactions' feerate MUST be below `target_feerate`.
453- fn cpfp_package (
454- revaultd : & Arc < RwLock < RevaultD > > ,
446+ /// `target_feerate` is in sats/kWU
447+ pub fn should_cpfp (
455448 bitcoind : & BitcoinD ,
456- to_be_cpfped : Vec < ToBeCpfped > ,
449+ tx : & impl CpfpableTransaction ,
457450 target_feerate : u64 ,
458- ) -> Result < ( ) , BitcoindError > {
459- let revaultd = revaultd. read ( ) . unwrap ( ) ;
460- let cpfp_descriptor = & revaultd. cpfp_descriptor ;
461-
462- // First of all, compute all the information we need from the to-be-cpfped transactions.
463- let mut txids = HashSet :: with_capacity ( to_be_cpfped. len ( ) ) ;
464- let mut package_weight = 0 ;
465- let mut package_fees = Amount :: from_sat ( 0 ) ;
466- let mut txins = Vec :: with_capacity ( to_be_cpfped. len ( ) ) ;
467- for tx in to_be_cpfped. iter ( ) {
468- txids. insert ( tx. txid ( ) ) ;
469- package_weight += tx. max_weight ( ) ;
470- package_fees += tx. fees ( ) ;
471- assert ! ( ( ( package_fees. as_sat( ) * 1000 / package_weight) as u64 ) < target_feerate) ;
472- match tx. cpfp_txin ( cpfp_descriptor, & revaultd. secp_ctx ) {
473- Some ( txin) => txins. push ( txin) ,
474- None => {
475- log:: error!( "No CPFP txin for tx '{}'" , tx. txid( ) ) ;
476- return Ok ( ( ) ) ;
477- }
478- }
479- }
480- let tx_feerate = ( package_fees. as_sat ( ) * 1_000 / package_weight) as u64 ; // to sats/kWU
481- assert ! ( tx_feerate < target_feerate) ;
482- let added_feerate = target_feerate - tx_feerate;
483-
484- // Then construct the child PSBT
485- let confirmed_cpfp_utxos: Vec < _ > = bitcoind
486- . list_unspent_cpfp ( ) ?
487- . into_iter ( )
488- . filter_map ( |l| {
489- // Not considering our own outputs nor UTXOs still in mempool
490- if txids. contains ( & l. outpoint . txid ) || l. confirmations < 1 {
491- None
492- } else {
493- let txout = CpfpTxOut :: new (
494- Amount :: from_sat ( l. txo . value ) ,
495- & revaultd. derived_cpfp_descriptor ( l. derivation_index . expect ( "Must be here" ) ) ,
496- ) ;
497- Some ( CpfpTxIn :: new ( l. outpoint , txout) )
498- }
499- } )
500- . collect ( ) ;
501- let psbt = match CpfpTransaction :: from_txins (
502- txins,
503- package_weight,
504- package_fees,
505- added_feerate,
506- confirmed_cpfp_utxos,
507- ) {
508- Ok ( tx) => tx,
509- Err ( TransactionCreationError :: InsufficientFunds ) => {
510- // Well, we're poor.
511- log:: error!(
512- "We wanted to feebump transactions '{:?}', but we don't have enough funds!" ,
513- txids
514- ) ;
515- return Ok ( ( ) ) ;
516- }
517- Err ( e) => {
518- log:: error!( "Error while creating CPFP transaction: '{}'" , e) ;
519- return Ok ( ( ) ) ;
520- }
521- } ;
522-
523- // Finally, sign and (try to) broadcast the CPFP transaction
524- let ( complete, psbt_signed) = bitcoind. sign_psbt ( psbt. psbt ( ) ) ?;
525- if !complete {
526- log:: error!(
527- "Bitcoind returned a non-finalized CPFP PSBT: {}" ,
528- base64:: encode( encode:: serialize( & psbt_signed) )
529- ) ;
530- return Ok ( ( ) ) ;
531- }
532-
533- let final_tx = psbt_signed. extract_tx ( ) ;
534- if let Err ( e) = bitcoind. broadcast_transaction ( & final_tx) {
535- log:: error!( "Error broadcasting '{:?}' CPFP tx: {}" , txids, e) ;
536- } else {
537- log:: info!( "CPFPed transactions with ids '{:?}'" , txids) ;
538- }
539-
540- Ok ( ( ) )
541- }
542-
543- // `target_feerate` is in sats/kWU
544- fn should_cpfp ( bitcoind : & BitcoinD , tx : & impl CpfpableTransaction , target_feerate : u64 ) -> bool {
451+ ) -> bool {
545452 bitcoind
546453 . get_wallet_transaction ( & tx. txid ( ) )
547454 // In the unlikely (actually, shouldn't happen but hey) case where
@@ -599,7 +506,14 @@ fn maybe_cpfp_txs(
599506 // TODO: std transaction max size check and split
600507 // TODO: smarter RBF (especially opportunistically with the fee delta)
601508 if !to_cpfp. is_empty ( ) {
602- cpfp_package ( revaultd, bitcoind, to_cpfp, current_feerate) ?;
509+ match cpfp_package ( revaultd, bitcoind, to_cpfp, current_feerate) {
510+ Err ( e) => {
511+ log:: error!( "Error broadcasting CPFP: {}" , e) ;
512+ }
513+ Ok ( txids) => {
514+ log:: info!( "CPFPed transactions with ids '{:?}'" , txids) ;
515+ }
516+ }
603517 } else {
604518 log:: debug!( "Nothing to CPFP" ) ;
605519 }
0 commit comments