use tokio::sync::broadcast::{
self,
error::{RecvError, TryRecvError},
};
use tower::{timeout::Timeout, Service, ServiceExt};
use zebra_network::MAX_TX_INV_IN_SENT_MESSAGE;
use zebra_network as zn;
use zebra_node_services::mempool::MempoolChange;
use crate::{
components::sync::{PEER_GOSSIP_DELAY, TIPS_RESPONSE_TIMEOUT},
BoxError,
};
pub const MAX_CHANGES_BEFORE_SEND: usize = 10;
pub async fn gossip_mempool_transaction_id<ZN>(
mut receiver: broadcast::Receiver<MempoolChange>,
broadcast_network: ZN,
) -> Result<(), BoxError>
where
ZN: Service<zn::Request, Response = zn::Response, Error = BoxError> + Send + Clone + 'static,
ZN::Future: Send,
{
let max_tx_inv_in_message: usize = MAX_TX_INV_IN_SENT_MESSAGE
.try_into()
.expect("constant fits in usize");
info!("initializing transaction gossip task");
let mut broadcast_network = Timeout::new(broadcast_network, TIPS_RESPONSE_TIMEOUT);
loop {
let mut combined_changes = 1;
let mut txs = loop {
match receiver.recv().await {
Ok(mempool_change) if mempool_change.is_added() => {
break mempool_change.into_tx_ids()
}
Ok(_) => {
continue;
}
Err(RecvError::Lagged(skip_count)) => info!(
?skip_count,
"dropped transactions before gossiping due to heavy mempool or network load"
),
Err(closed @ RecvError::Closed) => Err(closed)?,
}
};
while combined_changes <= MAX_CHANGES_BEFORE_SEND && txs.len() < max_tx_inv_in_message {
match receiver.try_recv() {
Ok(mempool_change) if mempool_change.is_added() => {
txs.extend(mempool_change.into_tx_ids())
}
Ok(_) => {
continue;
}
Err(TryRecvError::Empty) => break,
Err(TryRecvError::Lagged(skip_count)) => info!(
?skip_count,
"dropped transactions before gossiping due to heavy mempool or network load"
),
Err(closed @ TryRecvError::Closed) => Err(closed)?,
}
combined_changes += 1;
}
let txs_len = txs.len();
let request = zn::Request::AdvertiseTransactionIds(txs);
info!(%request, changes = %combined_changes, "sending mempool transaction broadcast");
debug!(
?request,
changes = ?combined_changes,
"full list of mempool transactions in broadcast"
);
let _ = broadcast_network.ready().await?.call(request).await;
metrics::counter!("mempool.gossiped.transactions.total").increment(txs_len as u64);
tokio::time::sleep(PEER_GOSSIP_DELAY).await;
}
}