Skip to content

Commit

Permalink
feat(client): request the broadcast minimum FeeRate
Browse files Browse the repository at this point in the history
  • Loading branch information
rustaceanrob committed Jan 10, 2025
1 parent e59e378 commit ba0f182
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 4 deletions.
3 changes: 3 additions & 0 deletions example/signet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ async fn main() {
Event::Synced(update) => {
tracing::info!("Synced chain up to block {}",update.tip().height);
tracing::info!("Chain tip: {}",update.tip().hash);
// Request information from the node
let fee = sender.broadcast_min_feerate().await.unwrap();
tracing::info!("Minimum transaction broadcast fee rate: {}", fee);
break;
},
Event::Block(indexed_block) => {
Expand Down
18 changes: 16 additions & 2 deletions src/core/client.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use bitcoin::block::Header;
#[cfg(feature = "filter-control")]
use bitcoin::BlockHash;
#[cfg(not(feature = "filter-control"))]
use bitcoin::ScriptBuf;
use bitcoin::Transaction;
use bitcoin::{block::Header, FeeRate};
use std::{collections::BTreeMap, ops::Range, time::Duration};
use tokio::sync::mpsc;
pub use tokio::sync::mpsc::Receiver;
Expand All @@ -15,7 +15,7 @@ use crate::{Event, Log, TrustedPeer, TxBroadcast};
#[cfg(feature = "filter-control")]
use super::{error::FetchBlockError, messages::BlockRequest, BlockReceiver, IndexedBlock};
use super::{
error::{ClientError, FetchHeaderError},
error::{ClientError, FetchFeeRateError, FetchHeaderError},
messages::{BatchHeaderRequest, ClientMessage, HeaderRequest},
};

Expand Down Expand Up @@ -133,6 +133,20 @@ impl EventSender {
.map_err(|_| ClientError::SendError)
}

/// A connection has a minimum transaction fee requirement to enter its mempool. For proper transaction propagation,
/// transactions should have a fee rate at least as high as the maximum fee filter received.
/// This method returns the maximum fee rate requirement of all connected peers.
///
/// For more information, refer to BIP133
pub async fn broadcast_min_feerate(&self) -> Result<FeeRate, FetchFeeRateError> {
let (tx, rx) = tokio::sync::oneshot::channel::<FeeRate>();
self.ntx
.send(ClientMessage::GetBroadcastMinFeeRate(tx))
.await
.map_err(|_| FetchFeeRateError::SendError)?;
rx.await.map_err(|_| FetchFeeRateError::RecvError)
}

/// Add more Bitcoin [`ScriptBuf`] to watch for. Does not rescan the filters.
/// If the script was already present in the node's collection, no change will occur.
///
Expand Down
26 changes: 26 additions & 0 deletions src/core/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,29 @@ impl core::fmt::Display for FetchBlockError {
}

impl_sourceless_error!(FetchBlockError);

/// Errors that occur when fetching the minimum fee rate to broadcast a transaction.
#[derive(Debug)]
pub enum FetchFeeRateError {
/// The channel to the node was likely closed and dropped from memory.
/// This implies the node is not running.
SendError,
/// The channel to the client was likely closed by the node and dropped from memory.
RecvError,
}

impl core::fmt::Display for FetchFeeRateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FetchFeeRateError::SendError => {
write!(f, "the receiver of this message was dropped from memory.")
}
FetchFeeRateError::RecvError => write!(
f,
"the channel to the client was likely closed by the node and dropped from memory."
),
}
}
}

impl_sourceless_error!(FetchFeeRateError);
6 changes: 5 additions & 1 deletion src/core/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{collections::BTreeMap, ops::Range, time::Duration};

#[cfg(feature = "filter-control")]
use bitcoin::BlockHash;
use bitcoin::{block::Header, p2p::message_network::RejectReason, ScriptBuf, Txid};
use bitcoin::{block::Header, p2p::message_network::RejectReason, FeeRate, ScriptBuf, Txid};

#[cfg(feature = "filter-control")]
use crate::IndexedFilter;
Expand Down Expand Up @@ -171,6 +171,8 @@ pub(crate) enum ClientMessage {
GetHeader(HeaderRequest),
/// Request a range of headers.
GetHeaderBatch(BatchHeaderRequest),
/// Request the broadcast minimum fee rate.
GetBroadcastMinFeeRate(FeeRateSender),
}

type HeaderSender = tokio::sync::oneshot::Sender<Result<Header, FetchHeaderError>>;
Expand Down Expand Up @@ -204,6 +206,8 @@ impl BatchHeaderRequest {

pub(crate) type BlockSender = tokio::sync::oneshot::Sender<Result<IndexedBlock, FetchBlockError>>;

pub(crate) type FeeRateSender = tokio::sync::oneshot::Sender<FeeRate>;

#[cfg(feature = "filter-control")]
#[derive(Debug)]
pub(crate) struct BlockRequest {
Expand Down
8 changes: 8 additions & 0 deletions src/core/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,14 @@ impl<H: HeaderStore, P: PeerStore> Node<H, P> {
if send_result.is_err() {
self.dialog.send_warning(Warning::ChannelDropped).await
};
},
ClientMessage::GetBroadcastMinFeeRate(request) => {
let peer_map = self.peer_map.lock().await;
let fee_rate = peer_map.broadcast_min();
let send_result = request.send(fee_rate);
if send_result.is_err() {
self.dialog.send_warning(Warning::ChannelDropped).await
};
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion tests/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ async fn test_mine_after_reorg() {
}

#[tokio::test]
async fn test_long_chain() {
async fn test_various_client_methods() {
let rpc_result = start_bitcoind(false);
// If we can't fetch the genesis block then bitcoind is not running. Just exit.
if rpc_result.is_err() {
Expand All @@ -275,6 +275,7 @@ async fn test_long_chain() {
} = client;
sync_assert(&best, &mut channel, &mut log).await;
let batch = sender.get_header_range(10_000..10_002).await.unwrap();
let _ = sender.broadcast_min_feerate().await.unwrap();
assert!(batch.is_empty());
sender.shutdown().await.unwrap();
rpc.stop().unwrap();
Expand Down

0 comments on commit ba0f182

Please sign in to comment.