spark_rust/spark_test_utils/
bitcoind.rs1use bitcoin::{
2 key::{Keypair, Secp256k1},
3 Address, BlockHash, Network, Transaction, Txid,
4};
5use bitcoincore_rpc::{
6 json::{GetBlockResult, GetRawTransactionResult},
7 Auth, Client, RpcApi,
8};
9use rand::thread_rng;
10
11use crate::{
12 error::{NetworkError, SparkSdkError, ValidationError},
13 wallet::utils::bitcoin::p2tr_address_from_public_key,
14};
15
16pub struct BitcoindClient {
17 client: Client,
18}
19
20#[allow(dead_code)]
21impl BitcoindClient {
22 pub fn new(
23 host: String,
24 port: u16,
25 username: String,
26 password: String,
27 ) -> Result<Self, SparkSdkError> {
28 Client::new(
29 format!("http://{}:{}", host, port).as_str(),
30 Auth::UserPass(username, password),
31 )
32 .map(|client| Self { client })
33 .map_err(|err| SparkSdkError::from(NetworkError::BitcoinRpc(err.to_string())))
34 }
35
36 pub fn new_from_env() -> Result<Self, SparkSdkError> {
37 let host = std::env::var("BITCOIN_RPC_HOST").unwrap_or("127.0.0.1".to_string());
38 let port = match std::env::var("BITCOIN_RPC_PORT") {
39 Ok(port) => port.parse::<u16>().map_err(|err| {
40 SparkSdkError::from(ValidationError::InvalidInput {
41 field: format!("Failed to parse port: {err}"),
42 })
43 }),
44 Err(_) => Ok(18443),
45 }?;
46
47 let username = std::env::var("BITCOIN_RPC_USERNAME").unwrap_or("admin1".to_string());
48 let password = std::env::var("BITCOIN_RPC_PASSWORD").unwrap_or("123".to_string());
49
50 Self::new(host, port, username, password)
51 }
52
53 pub fn generate_to_address(
54 &self,
55 blocks: u64,
56 address: &Address,
57 ) -> Result<Vec<BlockHash>, SparkSdkError> {
58 self.client
59 .generate_to_address(blocks, address)
60 .map_err(|err| SparkSdkError::from(NetworkError::BitcoinRpc(err.to_string())))
61 }
62
63 pub fn get_block_info(&self, block_hash: &BlockHash) -> Result<GetBlockResult, SparkSdkError> {
64 self.client
65 .get_block_info(block_hash)
66 .map_err(|err| SparkSdkError::from(NetworkError::BitcoinRpc(err.to_string())))
67 }
68
69 pub fn get_raw_transaction_info(
70 &self,
71 txid: &Txid,
72 block_hash: Option<&BlockHash>,
73 ) -> Result<GetRawTransactionResult, SparkSdkError> {
74 self.client
75 .get_raw_transaction_info(txid, block_hash)
76 .map_err(|err| SparkSdkError::from(NetworkError::BitcoinRpc(err.to_string())))
77 }
78
79 pub fn send_raw_transaction(&self, tx: &Transaction) -> Result<Txid, SparkSdkError> {
80 self.client
81 .send_raw_transaction(tx)
82 .map_err(|err| SparkSdkError::from(NetworkError::BitcoinRpc(err.to_string())))
83 }
84
85 pub fn mine_blocks(&self, blocks: u64) -> Result<(), SparkSdkError> {
86 let secp = Secp256k1::new();
87
88 let throwaway_keypair = Keypair::new(&secp, &mut thread_rng());
89 let throwaway_address =
90 p2tr_address_from_public_key(&throwaway_keypair.public_key(), Network::Regtest);
91
92 let _ = self
93 .client
94 .generate_to_address(blocks, &throwaway_address)
95 .map_err(|err| {
96 SparkSdkError::from(NetworkError::BitcoinRpc(format!(
97 "Failed to generate to address: {}",
98 err
99 )))
100 })?;
101
102 Ok(())
103 }
104}