pub struct TxSender {
pub(crate) signer: TxSenderSigningKey,
pub(crate) da_signer: TxSenderSigningKey,
pub rpc: ExtendedBitcoinRpc,
pub db: TxSenderDb,
pub(crate) client: TxSenderClient,
pub network: Network,
pub tx_sender_limits: TxSenderLimits,
pub finality_depth: u32,
pub http_client: Client,
pub(crate) mempool_config: MempoolConfig,
pub(crate) include_unsafe: bool,
}Expand description
Manages the process of sending Bitcoin transactions, including handling fee bumping strategies like Replace-By-Fee (RBF) and Child-Pays-For-Parent (CPFP).
It interacts with a Bitcoin Core RPC endpoint (ExtendedBitcoinRpc) to query network state
(like fee rates) and submit transactions. It uses a Database to persist transaction
state, track confirmation status, and manage associated data like fee payer UTXOs.
The Actor provides signing capabilities for transactions controlled by this service.
Fields§
§signer: TxSenderSigningKey§da_signer: TxSenderSigningKey§rpc: ExtendedBitcoinRpc§db: TxSenderDb§client: TxSenderClient§network: Network§tx_sender_limits: TxSenderLimits§finality_depth: u32§http_client: Client§mempool_config: MempoolConfig§include_unsafe: boolWhether to include unsafe UTXOs when funding transactions.
Implementations§
Source§impl TxSender
impl TxSender
Sourcepub fn create_reveal_script(
&self,
transaction_kind: TransactionKind,
body: &[u8],
) -> CitreaSigningData
pub fn create_reveal_script( &self, transaction_kind: TransactionKind, body: &[u8], ) -> CitreaSigningData
Creates a reveal script for a Citrea transaction based on transaction kind and body.
The script structure follows the commit-reveal pattern:
- public_key OP_CHECKSIGVERIFY (verifies the reveal key)
- transaction_kind (2 bytes)
- OP_FALSE OP_IF (start data push)
- [signature and signer_public_key for Complete, SequencerCommitment, Aggregate]
- body (pushed in 520-byte chunks)
- OP_ENDIF
- nonce (fixed to 16) OP_NIP
§Arguments
transaction_kind- The type of Citrea transactionbody- The transaction body bytes
§Returns
A tuple containing:
- The constructed reveal script
- The control block for spending the taproot output
- The commit transaction address (P2TR)
Source§impl TxSender
impl TxSender
Sourcepub async fn sync_citrea_txs(&self, fee_rate: FeeRateKvb) -> Result<(), Report>
pub async fn sync_citrea_txs(&self, fee_rate: FeeRateKvb) -> Result<(), Report>
Syncs citrea transactions, creating commit transactions for txs without it. After creating commit tx, the reveal txs are added to the core txsender queue using insert_try_to_send as RBF txs.
Sourceasync fn check_evicted_commit_txs(&self) -> Result<(), Report>
async fn check_evicted_commit_txs(&self) -> Result<(), Report>
Checks existing commit txids for eviction based on citrea rows whose try_to_send_id is NULL or not seen yet. If evicted (not in mempool and never seen), clear commit_outpoint and delete any reveal RBF entries tied to it. These check is only required because commit_tx creation uses include_unsafe = true, if some unsafe input is spent in another way, commit tx will become invalid.
Sourceasync fn send_aggregate_txs(&self, fee_rate: FeeRateKvb) -> Result<(), Report>
async fn send_aggregate_txs(&self, fee_rate: FeeRateKvb) -> Result<(), Report>
Build and send aggregate reveal transactions once all chunk reveals are confirmed.
Computes the aggregate body from confirmed chunk reveal txids/wtxids (ordered by chunk row id), updates/reset aggregate rows on mismatch or invalid post-reorg ordering, then runs the same commit/reveal flow. Marks aggregate rows as finalized only after the aggregate reveal and all chunk reveals are finalized.
async fn create_commit_outpoints_for_rows( &self, fee_rate: FeeRateKvb, insertion_id: i64, rows_with_scripts: Vec<(CitreaRawTxRow, CitreaSigningData)>, ) -> Result<Option<Txid>, Report>
async fn insert_reveal_try_to_send( &self, row_id: i64, commit_outpoint: OutPoint, signing_data: CitreaSigningData, ) -> Result<u32, Report>
Sourceasync fn reset_citrea_aggregate_and_delete_try_to_send(
&self,
aggregate_row_id: i64,
aggregate_body: &[u8],
try_to_send_id: Option<u32>,
) -> Result<(), Report>
async fn reset_citrea_aggregate_and_delete_try_to_send( &self, aggregate_row_id: i64, aggregate_body: &[u8], try_to_send_id: Option<u32>, ) -> Result<(), Report>
Resets an aggregate row to the supplied body and removes its stale reveal tracking row in one DB transaction.
update_citrea_aggregate_body_and_reset clears the aggregate row’s
foreign-key reference before delete_try_to_send_tx removes the linked
tx-sender rows.
Sourceasync fn collect_confirmed_chunk_reveals(
&self,
chunk_rows: &[CitreaRawTxRow],
rbf_txids_by_id: &HashMap<u32, Vec<Txid>>,
block_tx_position_cache: &mut HashMap<BlockHash, (u32, Vec<Txid>)>,
) -> Result<Option<ConfirmedChunkReveals>, Report>
async fn collect_confirmed_chunk_reveals( &self, chunk_rows: &[CitreaRawTxRow], rbf_txids_by_id: &HashMap<u32, Vec<Txid>>, block_tx_position_cache: &mut HashMap<BlockHash, (u32, Vec<Txid>)>, ) -> Result<Option<ConfirmedChunkReveals>, Report>
Returns confirmed chunk reveal information in chunk row order.
None means the database has enough seen state to consider the chunks,
but the current Bitcoin RPC view does not have a confirmed RBF member for
at least one chunk. That can happen transiently around reorgs or before
confirmation sync catches up.
Sourceasync fn select_confirmed_tx_info(
&self,
txids: &[Txid],
block_tx_position_cache: &mut HashMap<BlockHash, (u32, Vec<Txid>)>,
) -> Result<Option<ConfirmedTxInfo>, Report>
async fn select_confirmed_tx_info( &self, txids: &[Txid], block_tx_position_cache: &mut HashMap<BlockHash, (u32, Vec<Txid>)>, ) -> Result<Option<ConfirmedTxInfo>, Report>
Selects the newest confirmed member from an RBF txid history.
The input is expected in newest-first insertion order, matching the tx-sender RBF query helpers. Confirmed transactions include their wtxid and block position so aggregate bodies can reference chunk reveals and aggregate ordering can be validated after reorgs.
Source§impl TxSender
impl TxSender
Sourcepub async fn sync_transaction_confirmations_via_rpc(
&self,
dbtx: Option<&mut TxSenderTransaction>,
tip_height: u32,
) -> Result<(), BridgeError>
pub async fn sync_transaction_confirmations_via_rpc( &self, dbtx: Option<&mut TxSenderTransaction>, tip_height: u32, ) -> Result<(), BridgeError>
Synchronize tx-sender confirmation/spent tracking using Bitcoin RPC.
This method updates tx-sender tracking tables (e.g. seen_at_height and is_finalized) based on
current chain state, and clears those markers on reorgs for observations that
are still below finality.
Finality is explicitly tracked via is_finalized:
- For txid-based tables: finalized when RPC reports confirmations >= finality_depth
- For outpoint-based tables: finalized when seen_at_height is set and tip_height - seen_at_height + 1 >= finality_depth Once finalized, a row is never reprocessed, avoiding incorrect finality assumptions after downtime.
Sourceasync fn sync_outpoint_observations_via_rpc(
&self,
dbtx: Option<&mut TxSenderTransaction>,
start_tip_height: u32,
) -> Result<(), BridgeError>
async fn sync_outpoint_observations_via_rpc( &self, dbtx: Option<&mut TxSenderTransaction>, start_tip_height: u32, ) -> Result<(), BridgeError>
Synchronize outpoint-based cancellation/activation observations using Bitcoin RPC.
- uses a conservative
observed_tip_heightfor first-seen heights - finalizes once an outpoint has remained spent for at least
finality_depthblocks.
Sourceasync fn get_mempool_outspend(
&self,
outpoint: &OutPoint,
) -> Option<ValidatedOutspend>
async fn get_mempool_outspend( &self, outpoint: &OutPoint, ) -> Option<ValidatedOutspend>
Get the outspend status of an outpoint from the mempool space API. If it errors at some point, it will return None and the caller should fallback to the Bitcoin RPC. If the outspend is confirmed to be spent, it will return a ValidatedOutspend struct with the confirmed status, confirmations and Some(block height).
Source§impl TxSender
impl TxSender
fn anchor_prevout(anchor_sat: Amount) -> TxOut
fn build_and_sign_child_tx( &self, p2a_anchor: OutPoint, anchor_sat: Amount, fee_payer_utxos: Vec<SpendableUtxo>, change_address: Address, required_fee: Amount, ) -> Result<Transaction>
Sourceasync fn create_fee_payer_utxo(
&self,
bumped_id: u32,
dbtx: Option<&mut TxSenderTransaction>,
tx: &Transaction,
fee_rate: FeeRateKvb,
total_fee_payer_amount: Amount,
fee_payer_utxos_len: usize,
) -> Result<()>
async fn create_fee_payer_utxo( &self, bumped_id: u32, dbtx: Option<&mut TxSenderTransaction>, tx: &Transaction, fee_rate: FeeRateKvb, total_fee_payer_amount: Amount, fee_payer_utxos_len: usize, ) -> Result<()>
Creates and broadcasts a new “fee payer” UTXO to be used for CPFP transactions.
This function is called when a CPFP attempt fails due to insufficient funds
in the existing confirmed fee payer UTXOs associated with a transaction (bumped_id).
It calculates the required fee based on the parent transaction (tx) and the current
fee_rate, adding a buffer (2x required fee + dust limit) to handle potential fee spikes.
It then sends funds to the TxSender’s own signer address using the RPC’s
send_to_address and saves the resulting UTXO information (outpoint, amount)
to the database, linking it to the bumped_id.
§Arguments
bumped_id- The database ID of the parent transaction requiring the fee bump.tx- The parent transaction itself.fee_rate- The target fee rate for the CPFP package.total_fee_payer_amount- The sum of amounts in currently available confirmed fee payer UTXOs.fee_payer_utxos_len- The number of currently available confirmed fee payer UTXOs.
Sourceasync fn create_child_tx(
&self,
p2a_anchor: OutPoint,
anchor_sat: Amount,
fee_payer_utxos: Vec<SpendableUtxo>,
parent_tx_size: Weight,
fee_rate: FeeRateKvb,
) -> Result<Transaction>
async fn create_child_tx( &self, p2a_anchor: OutPoint, anchor_sat: Amount, fee_payer_utxos: Vec<SpendableUtxo>, parent_tx_size: Weight, fee_rate: FeeRateKvb, ) -> Result<Transaction>
Creates a Child-Pays-For-Parent (CPFP) child transaction.
This transaction spends:
- The designated “P2A anchor” output of the parent transaction (
p2a_anchor). - One or more confirmed “fee payer” UTXOs (
fee_payer_utxos) controlled by thesigner.
It calculates the total fee required (required_fee) to make the combined parent + child
package attractive to miners at the target fee_rate. The required_fee is paid entirely
by this child transaction.
The remaining value (total input value - required_fee) is sent to the change_address.
§Signing
We sign the input spending the P2A anchor and all fee payer UTXOs.
§Returns
The constructed and partially signed child transaction.
Sourceasync fn create_package(
&self,
tx: Transaction,
fee_rate: FeeRateKvb,
fee_payer_utxos: Vec<SpendableUtxo>,
) -> Result<Vec<Transaction>>
async fn create_package( &self, tx: Transaction, fee_rate: FeeRateKvb, fee_payer_utxos: Vec<SpendableUtxo>, ) -> Result<Vec<Transaction>>
Creates a transaction package for CPFP submission.
Finds the P2A anchor output in the parent transaction (tx), then constructs
the child transaction using create_child_tx.
§Returns
Vec<Transaction>: Parent transaction followed by the child transaction ready for submission via thesubmitpackageRPC.
Sourceasync fn get_confirmed_fee_payer_utxos(
&self,
try_to_send_id: u32,
) -> Result<Vec<SpendableUtxo>>
async fn get_confirmed_fee_payer_utxos( &self, try_to_send_id: u32, ) -> Result<Vec<SpendableUtxo>>
Retrieves confirmed fee payer UTXOs associated with a specific send attempt.
Queries the database for UTXOs linked to try_to_send_id that are marked as confirmed.
These UTXOs are controlled by the TxSender’s signer and are intended to be
spent by a CPFP child transaction.
§Returns
Vec<B::SpendableInput>: [B::SpendableInput]s of the confirmed fee payer UTXOs that are ready to be included as inputs in the CPFP child tx.
Sourcepub async fn bump_fees_of_unconfirmed_fee_payer_txs(
&self,
fee_rate: FeeRateKvb,
) -> Result<()>
pub async fn bump_fees_of_unconfirmed_fee_payer_txs( &self, fee_rate: FeeRateKvb, ) -> Result<()>
Attempts to bump the fees of unconfirmed “fee payer” UTXOs using RBF.
Fee payer UTXOs are created to fund CPFP child transactions. However, these
fee payer creation transactions might themselves get stuck due to low fees.
This function identifies such unconfirmed fee payer transactions associated with
a parent transaction (bumped_id) and attempts to RBF them using the provided fee_rate.
This ensures the fee payer UTXOs confirm quickly, making them available to be spent by the actual CPFP child transaction.
§Arguments
fee_rate- The target fee rate for bumping the fee payer transactions.
Sourcepub async fn send_cpfp_tx(
&self,
try_to_send_id: u32,
tx: Transaction,
tx_metadata: Option<TxMetadata>,
fee_rate: FeeRateKvb,
current_tip_height: u32,
) -> Result<()>
pub async fn send_cpfp_tx( &self, try_to_send_id: u32, tx: Transaction, tx_metadata: Option<TxMetadata>, fee_rate: FeeRateKvb, current_tip_height: u32, ) -> Result<()>
Sends a transaction using the Child-Pays-For-Parent (CPFP) strategy.
§Logic:
- Check Unconfirmed Fee Payers: Ensures no unconfirmed fee payer UTXOs exist
for this
try_to_send_id. If they do, skips this transaction for now as they need to confirm before being spendable by the child. - Get Confirmed Fee Payers: Retrieves the available confirmed fee payer UTXOs.
- Create Package: Calls
create_packageto build thevec![parent_tx, child_tx]. Thechild_txspends the parent’s anchor output and the fee payer UTXOs, paying a fee calculated for the whole package. - Test Mempool Accept (Not implemented right now as testmempoolaccept didn’t support TRUC package submission #1011):
Uses
testmempoolacceptRPC to check if the package is likely to be accepted by the network before submitting. - Submit Package: Uses the
submitpackageRPC to atomically submit the parent and child transactions. Bitcoin Core evaluates the fee rate of the package together. - Handle Results: Checks the
submitpackageresult. If successful or already in mempool, updates the effective fee rate in the database. If failed, returns an error.
§Arguments
try_to_send_id- The database ID tracking this send attempt.tx- The parent transaction requiring the fee bump.tx_metadata- Optional metadata associated with the transaction.fee_rate- The target fee rate for the CPFP package.current_tip_height- The current height of the tip of the chain.
Source§impl TxSender
impl TxSender
Sourcepub fn is_bridge_tx_nonstandard(&self, tx: &Transaction) -> bool
pub fn is_bridge_tx_nonstandard(&self, tx: &Transaction) -> bool
Checks if a bridge transaction is nonstandard. Keep in mind that these are not all cases where a transaction is nonstandard. We only check non-standard types that clementine generates by default in non-standard mode. Currently checks these cases:
- The transaction contains 0 sat non-anchor (only checks our specific anchor address) and non-op return output.
- The transaction weight is bigger than 400k
Arguments:
tx- The transaction to check.
Returns:
trueif the transaction is nonstandard,falseotherwise.
Sourcepub async fn send_testnet4_nonstandard_tx(
&self,
tx: &Transaction,
try_to_send_id: u32,
) -> Result<(), SendTxError>
pub async fn send_testnet4_nonstandard_tx( &self, tx: &Transaction, try_to_send_id: u32, ) -> Result<(), SendTxError>
Sends a nonstandard transaction to testnet4 using the mempool.space accelerator.
Arguments:
tx- The transaction to send.
Returns:
Ok(())if the transaction is sent successfully to the accelerator.Err(SendTxError)if the transaction is not sent successfully to the accelerator.
Note: Mempool.space accelerator doesn’t accept transactions if: - At least one of the transaction’s inputs is signed with either the SIGHASH_NONE or SIGHASH_ANYONECANPAY flag, which may allow a third party to replace the transaction. - The number of signature operations multiplied by 20 exceeds the transaction’s weight. Mempool Space API docs Mempool Space Accelerator FAQ
Source§impl TxSender
impl TxSender
Sourcepub async fn calculate_bump_feerate_if_needed(
&self,
txid: &Txid,
new_feerate: FeeRateKvb,
) -> Result<Option<FeeRateKvb>>
pub async fn calculate_bump_feerate_if_needed( &self, txid: &Txid, new_feerate: FeeRateKvb, ) -> Result<Option<FeeRateKvb>>
Calculates the appropriate fee rate for a Replace-By-Fee (RBF) transaction.
This method determines the effective fee rate needed to successfully replace an existing transaction in the mempool. It follows Bitcoin’s RBF rules by:
- Retrieving the original transaction and calculating its current fee rate
- Ensuring the new fee rate is higher than the original by at least the minimum required incremental relay fee
- Comparing the calculated minimum bump fee rate with the requested target fee rate and selecting the higher of the two
§Arguments
txid- The transaction ID of the original transaction to be replacednew_feerate- The target fee rate requested for the replacement transaction
§Returns
Ok(Some(FeeRateKvb))- The effective fee rate (in satoshis per kvB) to use for the replacementOk(None)- If the original transaction already has a higher fee rate than requestedErr(...)- If there was an error retrieving or analyzing the original transaction
pub async fn fill_in_utxo_info(&self, psbt: &mut String) -> Result<()>
Sourcepub async fn copy_witnesses(
&self,
psbt: String,
initial_tx: &Transaction,
) -> Result<String>
pub async fn copy_witnesses( &self, psbt: String, initial_tx: &Transaction, ) -> Result<String>
Given a PSBT with inputs, fill in the existing witnesses from the original tx This allows us to create a finalized PSBT if the original tx had SinglePlusAnyoneCanPay signatures. If the original tx did not have S+AP, these signatures will be added. The expected behavior is for them to be replaced using RbfSigningInfo.
§Returns
The PSBT as a base64-encoded string.
pub async fn create_funded_psbt( &self, tx: &Transaction, fee_rate: FeeRateKvb, ) -> Result<WalletCreateFundedPsbtResult>
Sourcepub async fn attempt_sign_psbt(
&self,
psbt: String,
rbf_signing_info: &RbfSigningInfo,
cached_leaf_hash: Option<TapLeafHash>,
) -> Result<String>
pub async fn attempt_sign_psbt( &self, psbt: String, rbf_signing_info: &RbfSigningInfo, cached_leaf_hash: Option<TapLeafHash>, ) -> Result<String>
Given a PSBT with inputs that’ve been signed by the wallet except for our new input, we have to sign the input with our secret key.
§Arguments
psbt- The PSBT to sign.rbf_signing_info- The RBF signing info.cached_leaf_hash- The cached leaf hash for script path spends.
§Returns
The signed PSBT as a base64-encoded string.
pub fn handle_err( &self, err_msg: impl AsRef<str>, err_state: impl Into<String>, try_to_send_id: u32, )
Sourcepub fn verify_new_inputs(&self, psbt: &str, original_tx: &Transaction) -> bool
pub fn verify_new_inputs(&self, psbt: &str, original_tx: &Transaction) -> bool
This function verifies that the wallet has added a funding input to the PSBT.
This is required for a transaction to be added to the wallet.
Sourcepub fn reorder_psbt_outputs(
&self,
psbt: &mut Psbt,
original_tx: &Transaction,
) -> Result<()>
pub fn reorder_psbt_outputs( &self, psbt: &mut Psbt, original_tx: &Transaction, ) -> Result<()>
Reorders PSBT outputs so that the original transaction outputs appear first in the same order, followed by any newly added outputs (e.g., change outputs).
This is important for watchtower challenge transactions where the OP_RETURN output is expected to remain at a specific index.
fn extract_final_tx_from_psbt(psbt: &str) -> Result<Transaction>
pub async fn get_tx_fee(&self, tx: &Transaction) -> Result<Amount>
Sourcepub async fn send_rbf_tx(
&self,
try_to_send_id: u32,
tx: Transaction,
tx_metadata: Option<TxMetadata>,
fee_rate: FeeRateKvb,
rbf_signing_info: Option<RbfSigningInfo>,
current_tip_height: u32,
needs_wtxid_grind: bool,
) -> Result<()>
pub async fn send_rbf_tx( &self, try_to_send_id: u32, tx: Transaction, tx_metadata: Option<TxMetadata>, fee_rate: FeeRateKvb, rbf_signing_info: Option<RbfSigningInfo>, current_tip_height: u32, needs_wtxid_grind: bool, ) -> Result<()>
Sends or bumps a transaction using the Replace-By-Fee (RBF) strategy.
It interacts with the database to track the latest RBF attempt (last_rbf_txid).
§Logic:
-
Check for Existing RBF Tx: Retrieves RBF txids for the
try_to_send_idand selects the most recent one still in the mempool. -
Bump Existing Tx: If a mempool tx exists, it calls
rpc.psbt_bump_fee.- This internally uses the Bitcoin Core
psbtbumpfeeRPC. - We then sign the inputs that we can using our Actor and have the wallet sign the rest.
- This internally uses the Bitcoin Core
-
Send Initial RBF Tx: If no RBF tx is found in the mempool:
- It uses
fund_raw_transactionRPC to let the wallet add (potentially) inputs, outputs, set the fee according tofee_rate, and mark the transaction as replaceable. - Uses
sign_raw_transaction_with_walletRPC to sign the funded transaction. - Uses
send_raw_transactionRPC to broadcast the initial RBF transaction. - Saves the resulting
txidto the database as thelast_rbf_txid.
- It uses
§Arguments
try_to_send_id- The database ID tracking this send attempt.tx- The original transaction intended for RBF (used only on the first attempt).tx_metadata- Optional metadata associated with the transaction.fee_rate- The target fee rate for the RBF replacement.
Source§impl TxSender
impl TxSender
pub fn address(&self) -> &Address
pub fn xonly_public_key(&self) -> XOnlyPublicKey
Sourcepub async fn new(tx_sender_config: TxSenderConfig) -> Result<Self, BridgeError>
pub async fn new(tx_sender_config: TxSenderConfig) -> Result<Self, BridgeError>
Creates a new TxSender.
pub async fn get_fee_rate(&self) -> Result<FeeRateKvb, BridgeError>
Sourcepub(crate) fn calculate_required_fee(
parent_tx_weight: Weight,
num_fee_payer_utxos: usize,
fee_rate: FeeRateKvb,
fee_paying_type: FeePayingType,
) -> Result<Amount>
pub(crate) fn calculate_required_fee( parent_tx_weight: Weight, num_fee_payer_utxos: usize, fee_rate: FeeRateKvb, fee_paying_type: FeePayingType, ) -> Result<Amount>
Calculates the total fee required for a transaction package based on the fee bumping strategy.
§Arguments
parent_tx_weight- The weight of the main transaction being bumped.num_fee_payer_utxos- The number of fee payer UTXOs used (relevant for child tx size in CPFP).fee_rate- The target fee rate (sat/kvB).fee_paying_type- The strategy being used (CPFP or RBF).
§Calculation Logic
- CPFP: Calculates the weight of the hypothetical child transaction based on the number of fee payer inputs and standard P2TR output sizes. It then calculates the fee based on the combined virtual size (vbytes) of the parent and child transactions, as miners evaluate the package deal.
- RBF: Calculates the weight of the replacement transaction itself (assuming inputs and potentially outputs change slightly). The fee is calculated based on the weight of this single replacement transaction.
Reference for weight estimates: https://bitcoin.stackexchange.com/a/116959
pub fn is_p2a_anchor(&self, output: &TxOut) -> bool
pub fn find_p2a_vout(&self, tx: &Transaction) -> Result<usize, BridgeError>
Sourcepub(crate) async fn try_to_send_unconfirmed_txs(
&self,
new_fee_rate: FeeRateKvb,
current_tip_height: u32,
is_tip_height_increased: bool,
) -> Result<()>
pub(crate) async fn try_to_send_unconfirmed_txs( &self, new_fee_rate: FeeRateKvb, current_tip_height: u32, is_tip_height_increased: bool, ) -> Result<()>
Fetches transactions that are eligible to be sent or bumped from database based on the given fee rate and tip height. Then, places a send transaction request to the Bitcoin based on the fee strategy.
For each eligible transaction (id):
- Send/Bump Main Tx: Calls
send_txto either perform RBF or CPFP on the main transaction (id) using thenew_fee_rate. - Handle Errors:
SendTxError::UnconfirmedFeePayerUTXOsLeft: Skips the current tx, waiting for fee payers to confirm.SendTxError::InsufficientFeePayerAmount: Callscreate_fee_payer_utxoto provision more funds for a future CPFP attempt.- Other errors are logged.
§Arguments
new_fee_rate- The current target fee rate based on network conditions.current_tip_height- The current blockchain height, used for time-lock checks.is_tip_height_increased- True if the tip height has increased since the last time we sent unconfirmed transactions.
pub fn client(&self) -> TxSenderClient
Sourcepub async fn calculate_target_fee_rate(
&self,
previous_effective_fee_rate: Option<FeeRateKvb>,
new_fee_rate: FeeRateKvb,
last_bump_block_height: Option<u32>,
current_tip_height: u32,
) -> Result<FeeRateKvb>
pub async fn calculate_target_fee_rate( &self, previous_effective_fee_rate: Option<FeeRateKvb>, new_fee_rate: FeeRateKvb, last_bump_block_height: Option<u32>, current_tip_height: u32, ) -> Result<FeeRateKvb>
Calculates the effective fee rate for a transaction, considering previous effective fee rate and minimum incremental fee requirements.
This function implements the logic for fee bumping that ensures:
- If no previous effective fee rate exists, use the new fee rate
- If previous effective fee rate exists, use the maximum of:
- The new fee rate
- Previous effective fee rate + minimum incremental fee rate
§Arguments
previous_effective_fee_rate- The previous effective fee rate (if any)new_fee_rate- The target fee rate for the new attemptlast_bump_block_height- The block height when the last fee bump was done (if any)current_tip_height- The current blockchain tip height
§Returns
The effective fee rate to use (in sat/kvB), capped by the hard cap from config
Sourcepub async fn send_no_funding_tx(
&self,
try_to_send_id: u32,
tx: Transaction,
tx_metadata: Option<TxMetadata>,
) -> Result<()>
pub async fn send_no_funding_tx( &self, try_to_send_id: u32, tx: Transaction, tx_metadata: Option<TxMetadata>, ) -> Result<()>
Sends a transaction that is already fully funded and signed.
This function is used for transactions that do not require fee bumping strategies like RBF or CPFP. The transaction is submitted directly to the Bitcoin network without any modifications.
§Arguments
try_to_send_id- The database ID tracking this send attempt.tx- The fully funded and signed transaction ready for broadcast.tx_metadata- Optional metadata associated with the transaction for debugging.
§Behavior
- Attempts to broadcast the transaction using
send_raw_transactionRPC. - Updates the database with success/failure state for debugging purposes.
- Logs appropriate messages for monitoring and troubleshooting.
§Returns
Ok(())- If the transaction was successfully broadcast.Err(SendTxError)- If the broadcast failed.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for TxSender
impl !RefUnwindSafe for TxSender
impl Send for TxSender
impl Sync for TxSender
impl Unpin for TxSender
impl !UnwindSafe for TxSender
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<D> OwoColorize for D
impl<D> OwoColorize for D
§fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
§fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
§fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
§fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
§fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
§fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
§fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
§fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
§fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
§fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
§fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
§fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
§fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
§fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
§fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
§fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
§fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
§fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
§fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
§fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
§fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
§fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
§fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
§fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
§fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
§fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::fg] or
a color-specific method, such as [OwoColorize::green], Read more§fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::bg] or
a color-specific method, such as [OwoColorize::on_yellow], Read more§fn fg_rgb<const R: u8, const G: u8, const B: u8>(
&self,
) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>
fn fg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>
§fn bg_rgb<const R: u8, const G: u8, const B: u8>(
&self,
) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>
fn bg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>
§fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>
fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>
§fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>
fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.