use crate::bitcoin::config::BitcoinConfig;
use crate::bitcoin::error::{BitcoinError, BitcoinResult};
use crate::bitcoin::interface::{
AddressType, BitcoinImplementationType, BitcoinInterface, BlockHeader,
};
use async_trait::async_trait;
use bitcoin::secp256k1::{self, XOnlyPublicKey as SecpXOnlyPublicKey};
use bitcoin::{
absolute::LockTime, secp256k1::Secp256k1, Address as BitcoinAddress, Block as BitcoinBlock,
CompressedPublicKey, FeeRate, Network, PrivateKey, PubkeyHash, ScriptBuf,
Transaction as BitcoinTransaction, Txid,
};
use std::collections::HashMap;
use std::str::FromStr;
pub use bitcoin::{Address, Block, Transaction};
#[allow(dead_code)]
pub struct RustBitcoinImplementation {
network: Network,
rpc_client: Option<bitcoincore_rpc::Client>,
wallet: LocalWallet,
tx_cache: HashMap<Txid, BitcoinTransaction>,
block_cache: HashMap<String, BitcoinBlock>,
}
struct LocalWallet {
keys: HashMap<String, PrivateKey>,
addresses: HashMap<String, BitcoinAddress>,
secp: Secp256k1<bitcoin::secp256k1::All>,
}
impl LocalWallet {
fn new() -> Self {
Self {
keys: HashMap::new(),
addresses: HashMap::new(),
secp: Secp256k1::new(),
}
}
fn generate_key(
&mut self,
address_type: AddressType,
) -> Result<(String, BitcoinAddress), BitcoinError> {
let (secret_key, public_key) = self
.secp
.generate_keypair(&mut secp256k1::rand::thread_rng());
let bitcoin_pubkey = bitcoin::PublicKey::new(public_key);
let key_id = format!("key_{bitcoin_pubkey}");
let network = self.network();
let address = match address_type {
AddressType::P2PKH => {
let pubkey_hash = PubkeyHash::from(&bitcoin_pubkey);
BitcoinAddress::p2pkh(pubkey_hash, network)
}
AddressType::P2WPKH => {
let compressed_pubkey = CompressedPublicKey::from_slice(&public_key.serialize())
.map_err(|e| BitcoinError::Other(format!("Compressed pubkey error: {e}")))?;
BitcoinAddress::p2wpkh(&compressed_pubkey, network)
}
AddressType::P2TR => {
let x_only =
SecpXOnlyPublicKey::from_slice(&public_key.x_only_public_key().0.serialize())
.map_err(|_| {
BitcoinError::Other("Failed to create x-only public key".to_string())
})?;
let taproot_spend_info = bitcoin::taproot::TaprootBuilder::new()
.add_leaf(0, ScriptBuf::new())
.map_err(|_| BitcoinError::Other("Failed to create taproot".to_string()))?
.finalize(&self.secp, x_only)
.map_err(|_| BitcoinError::Other("Failed to finalize taproot".to_string()))?;
BitcoinAddress::p2tr(
&self.secp,
x_only,
taproot_spend_info.merkle_root(),
network,
)
}
_ => {
return Err(BitcoinError::Other("Unsupported address type".to_string()));
}
};
let bitcoin_privkey = bitcoin::PrivateKey::new(secret_key, network);
self.keys.insert(key_id.clone(), bitcoin_privkey);
self.addresses.insert(key_id.clone(), address.clone());
Ok((key_id, address))
}
fn network(&self) -> Network {
Network::Bitcoin }
}
impl RustBitcoinImplementation {
pub fn new(config: &BitcoinConfig) -> Result<Self, Box<dyn std::error::Error>> {
let network_str = if config.network.is_empty() {
"testnet".to_string()
} else {
config.network.clone()
};
let network = match network_str.as_str() {
"mainnet" | "bitcoin" => Network::Bitcoin,
"testnet" | "test" => Network::Testnet,
"regtest" => Network::Regtest,
_ => {
return Err(Box::new(BitcoinError::InvalidConfiguration(format!(
"Invalid network: {network_str}"
))))
}
};
Ok(Self {
network,
rpc_client: None,
wallet: LocalWallet::new(),
tx_cache: HashMap::new(),
block_cache: HashMap::new(),
})
}
pub fn new_network(network: Network) -> Self {
Self {
network,
rpc_client: None,
wallet: LocalWallet::new(),
tx_cache: HashMap::new(),
block_cache: HashMap::new(),
}
}
pub fn with_rpc_client(
mut self,
rpc_url: String,
rpc_auth: bitcoincore_rpc::Auth,
) -> Result<Self, BitcoinError> {
let rpc_client = bitcoincore_rpc::Client::new(&rpc_url, rpc_auth)
.map_err(|e| BitcoinError::Other(format!("Failed to create RPC client: {e}")))?;
self.rpc_client = Some(rpc_client);
Ok(self)
}
}
#[async_trait]
impl BitcoinInterface for RustBitcoinImplementation {
async fn get_transaction(&self, txid: &str) -> BitcoinResult<Transaction> {
let txid_hash = Txid::from_str(txid)
.map_err(|_| BitcoinError::InvalidTransaction("Invalid transaction ID".to_string()))?;
if let Some(cached_tx) = self.tx_cache.get(&txid_hash) {
return Ok(cached_tx.clone());
}
if let Some(_client) = &self.rpc_client {
return Err(BitcoinError::TransactionNotFound);
}
Err(BitcoinError::TransactionNotFound)
}
async fn get_block(&self, hash: &str) -> BitcoinResult<Block> {
let _block_hash = bitcoin::BlockHash::from_str(hash)
.map_err(|_| BitcoinError::InvalidTransaction("Invalid block hash".to_string()))?;
if let Some(cached_block) = self.block_cache.get(hash) {
return Ok(cached_block.clone());
}
if let Some(_client) = &self.rpc_client {
return Err(BitcoinError::BlockNotFound);
}
Err(BitcoinError::BlockNotFound)
}
async fn get_block_height(&self) -> BitcoinResult<u32> {
if let Some(_client) = &self.rpc_client {
return Ok(0);
}
Ok(0)
}
async fn generate_address(&self, address_type: AddressType) -> BitcoinResult<Address> {
let mut wallet = LocalWallet::new();
let (_key_id, bitcoin_address) = wallet.generate_key(address_type)?;
Ok(bitcoin_address)
}
async fn create_transaction(
&self,
_outputs: Vec<(String, u64)>,
fee_rate: u64,
) -> BitcoinResult<Transaction> {
let fee_rate = FeeRate::from_sat_per_vb(fee_rate);
if fee_rate.is_none() {
return Err(BitcoinError::Other("Invalid fee rate".to_string()));
}
let fee_rate = fee_rate.unwrap();
let estimated_size = 200; let _fee = fee_rate.fee_vb(estimated_size);
let mut wallet = LocalWallet::new();
let _change_address = wallet.generate_key(AddressType::P2WPKH)?.1;
let bitcoin_tx = BitcoinTransaction {
version: bitcoin::transaction::Version(2),
lock_time: LockTime::ZERO,
input: vec![],
output: vec![],
};
Ok(bitcoin_tx)
}
async fn broadcast_transaction(&self, transaction: &Transaction) -> BitcoinResult<String> {
if let Some(_client) = &self.rpc_client {
return Ok(transaction.compute_txid().to_string());
}
Ok(transaction.compute_txid().to_string())
}
async fn get_block_header(&self, _hash: &str) -> BitcoinResult<BlockHeader> {
if let Some(_client) = &self.rpc_client {
return Err(BitcoinError::BlockNotFound);
}
Err(BitcoinError::BlockNotFound)
}
async fn verify_merkle_proof(
&self,
_tx_hash: &str,
_block_header: &BlockHeader,
) -> BitcoinResult<bool> {
Ok(true)
}
async fn get_balance(&self, _address: &Address) -> BitcoinResult<u64> {
Ok(0)
}
async fn estimate_fee(&self, _target_blocks: u8) -> BitcoinResult<u64> {
Ok(1000) }
async fn send_transaction(&self, tx: &Transaction) -> BitcoinResult<String> {
self.broadcast_transaction(tx).await
}
fn implementation_type(&self) -> BitcoinImplementationType {
BitcoinImplementationType::Rust
}
}