From a11c914da7371016df7ddd148f777d8054cfe9c3 Mon Sep 17 00:00:00 2001 From: Jose Storopoli Date: Tue, 11 Feb 2025 08:28:18 -0300 Subject: [PATCH] feat(btcio): refactor and Clone (#659) * fix!(btcio): move submit_package to BroadcastRpc from ReaderRpc * feat(btcio): make BitcoinClient Clone --- crates/btcio/src/rpc/client.rs | 27 +++++++++++++++--------- crates/btcio/src/rpc/traits.rs | 24 ++++++++++----------- crates/btcio/src/test_utils.rs | 38 +++++++++++++++++----------------- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/crates/btcio/src/rpc/client.rs b/crates/btcio/src/rpc/client.rs index 5a430b9c4..685bb5d36 100644 --- a/crates/btcio/src/rpc/client.rs +++ b/crates/btcio/src/rpc/client.rs @@ -1,7 +1,10 @@ use std::{ env::var, fmt, - sync::atomic::{AtomicUsize, Ordering}, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, time::Duration, }; @@ -54,14 +57,18 @@ where } /// An `async` client for interacting with a `bitcoind` instance. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct BitcoinClient { /// The URL of the `bitcoind` instance. url: String, /// The underlying `async` HTTP client. client: Client, /// The ID of the current request. - id: AtomicUsize, + /// + /// # Implementation Details + /// + /// Using an [`Arc`] so that [`BitcoinClient`] is [`Clone`]. + id: Arc, /// The maximum number of retries for a request. max_retries: u8, /// Interval between retries for a request in ms. @@ -107,7 +114,7 @@ impl BitcoinClient { .build() .map_err(|e| ClientError::Other(format!("Could not create client: {e}")))?; - let id = AtomicUsize::new(0); + let id = Arc::new(AtomicUsize::new(0)); let max_retries = max_retries.unwrap_or(DEFAULT_MAX_RETRIES); let retry_interval = retry_interval.unwrap_or(DEFAULT_RETRY_INTERVAL_MS); @@ -301,12 +308,6 @@ impl ReaderRpc for BitcoinClient { .await } - async fn submit_package(&self, txs: &[Transaction]) -> ClientResult { - let txstrs: Vec = txs.iter().map(serialize_hex).collect(); - self.call::("submitpackage", &[to_value(txstrs)?]) - .await - } - async fn network(&self) -> ClientResult { Ok(self .call::("getblockchaininfo", &[]) @@ -345,6 +346,12 @@ impl BroadcasterRpc for BitcoinClient { self.call::>("testmempoolaccept", &[to_value([txstr])?]) .await } + + async fn submit_package(&self, txs: &[Transaction]) -> ClientResult { + let txstrs: Vec = txs.iter().map(serialize_hex).collect(); + self.call::("submitpackage", &[to_value(txstrs)?]) + .await + } } #[async_trait] diff --git a/crates/btcio/src/rpc/traits.rs b/crates/btcio/src/rpc/traits.rs index 66a47b115..df1b0ef93 100644 --- a/crates/btcio/src/rpc/traits.rs +++ b/crates/btcio/src/rpc/traits.rs @@ -80,18 +80,6 @@ pub trait ReaderRpc { include_mempool: bool, ) -> ClientResult; - /// Submit a package of raw transactions (serialized, hex-encoded) to local node. - /// - /// The package will be validated according to consensus and mempool policy rules. If any - /// transaction passes, it will be accepted to mempool. This RPC is experimental and the - /// interface may be unstable. Refer to doc/policy/packages.md for documentation on package - /// policies. - /// - /// # Warning - /// - /// Successful submission does not mean the transactions will propagate throughout the network. - async fn submit_package(&self, txs: &[Transaction]) -> ClientResult; - /// Gets the underlying [`Network`] information. async fn network(&self) -> ClientResult; } @@ -119,6 +107,18 @@ pub trait BroadcasterRpc { /// Tests if a raw transaction is valid. async fn test_mempool_accept(&self, tx: &Transaction) -> ClientResult>; + + /// Submit a package of raw transactions (serialized, hex-encoded) to local node. + /// + /// The package will be validated according to consensus and mempool policy rules. If any + /// transaction passes, it will be accepted to mempool. This RPC is experimental and the + /// interface may be unstable. Refer to doc/policy/packages.md for documentation on package + /// policies. + /// + /// # Warning + /// + /// Successful submission does not mean the transactions will propagate throughout the network. + async fn submit_package(&self, txs: &[Transaction]) -> ClientResult; } /// Wallet functionality that any Bitcoin client **without private keys** that diff --git a/crates/btcio/src/test_utils.rs b/crates/btcio/src/test_utils.rs index 16d6b403a..c2f1e5fff 100644 --- a/crates/btcio/src/test_utils.rs +++ b/crates/btcio/src/test_utils.rs @@ -140,6 +140,25 @@ impl ReaderRpc for TestBitcoinClient { }) } + async fn network(&self) -> ClientResult { + Ok(Network::Regtest) + } +} + +#[async_trait] +impl BroadcasterRpc for TestBitcoinClient { + // send_raw_transaction sends a raw transaction to the network + async fn send_raw_transaction(&self, _tx: &Transaction) -> ClientResult { + Ok(Txid::from_slice(&[1u8; 32]).unwrap()) + } + async fn test_mempool_accept(&self, _tx: &Transaction) -> ClientResult> { + let some_tx: Transaction = consensus::encode::deserialize_hex(SOME_TX).unwrap(); + Ok(vec![TestMempoolAccept { + txid: some_tx.compute_txid(), + reject_reason: None, + }]) + } + async fn submit_package(&self, _txs: &[Transaction]) -> ClientResult { let some_tx: Transaction = consensus::encode::deserialize_hex(SOME_TX).unwrap(); let wtxid = some_tx.compute_wtxid(); @@ -160,25 +179,6 @@ impl ReaderRpc for TestBitcoinClient { replaced_transactions: vec![], }) } - - async fn network(&self) -> ClientResult { - Ok(Network::Regtest) - } -} - -#[async_trait] -impl BroadcasterRpc for TestBitcoinClient { - // send_raw_transaction sends a raw transaction to the network - async fn send_raw_transaction(&self, _tx: &Transaction) -> ClientResult { - Ok(Txid::from_slice(&[1u8; 32]).unwrap()) - } - async fn test_mempool_accept(&self, _tx: &Transaction) -> ClientResult> { - let some_tx: Transaction = consensus::encode::deserialize_hex(SOME_TX).unwrap(); - Ok(vec![TestMempoolAccept { - txid: some_tx.compute_txid(), - reject_reason: None, - }]) - } } #[async_trait]