X-Git-Url: http://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=blobdiff_plain;f=src%2Fbitcoind_client.rs;h=f18cd407622eb2c2a1f54c5a9d11fe0b4b5ce5f8;hp=71e6272fbce7969ad6f3583738333b4356659c39;hb=d9e9c01d33f291cf4846c0406a3e00c9ecbd543d;hpb=80916829ec20eb8a510462e765a03c3ed051f2e0 diff --git a/src/bitcoind_client.rs b/src/bitcoind_client.rs index 71e6272..f18cd40 100644 --- a/src/bitcoind_client.rs +++ b/src/bitcoind_client.rs @@ -1,8 +1,8 @@ +use crate::convert::{BlockchainInfo, FeeResponse, FundedTx, NewAddress, RawTx, SignedTx}; use base64; use bitcoin::blockdata::transaction::Transaction; use bitcoin::consensus::encode; use bitcoin::util::address::Address; -use crate::convert::{BlockchainInfo, FeeResponse, FundedTx, NewAddress, RawTx, SignedTx}; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; use lightning_block_sync::http::HttpEndpoint; use lightning_block_sync::rpc::RpcClient; @@ -13,137 +13,150 @@ use std::sync::Mutex; use tokio::runtime::{Handle, Runtime}; pub struct BitcoindClient { - bitcoind_rpc_client: Mutex, - host: String, - port: u16, - rpc_user: String, - rpc_password: String, - runtime: Mutex, + bitcoind_rpc_client: Mutex, + host: String, + port: u16, + rpc_user: String, + rpc_password: String, + runtime: Mutex, } impl BitcoindClient { - pub fn new(host: String, port: u16, rpc_user: String, rpc_password: String) -> - std::io::Result - { - let http_endpoint = HttpEndpoint::for_host(host.clone()).with_port(port); - let rpc_credentials = base64::encode(format!("{}:{}", rpc_user.clone(), - rpc_password.clone())); - let bitcoind_rpc_client = RpcClient::new(&rpc_credentials, http_endpoint)?; - let client = Self { - bitcoind_rpc_client: Mutex::new(bitcoind_rpc_client), - host, - port, - rpc_user, - rpc_password, - runtime: Mutex::new(Runtime::new().unwrap()), - }; - Ok(client) - } - - pub fn get_new_rpc_client(&self) -> std::io::Result { - let http_endpoint = HttpEndpoint::for_host(self.host.clone()).with_port(self.port); - let rpc_credentials = base64::encode(format!("{}:{}", - self.rpc_user.clone(), - self.rpc_password.clone())); - RpcClient::new(&rpc_credentials, http_endpoint) - } - - pub fn create_raw_transaction(&self, outputs: Vec>) -> RawTx { - let runtime = self.runtime.lock().unwrap(); - let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); - - let outputs_json = serde_json::json!(outputs); - runtime.block_on(rpc.call_method::("createrawtransaction", &vec![serde_json::json!([]), outputs_json])).unwrap() - } - - pub fn fund_raw_transaction(&self, raw_tx: RawTx) -> FundedTx { - let runtime = self.runtime.lock().unwrap(); - let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); - - let raw_tx_json = serde_json::json!(raw_tx.0); - runtime.block_on(rpc.call_method("fundrawtransaction", &[raw_tx_json])).unwrap() - } - - pub fn sign_raw_transaction_with_wallet(&self, tx_hex: String) -> SignedTx { - let runtime = self.runtime.lock().unwrap(); - let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); - - let tx_hex_json = serde_json::json!(tx_hex); - runtime.block_on(rpc.call_method("signrawtransactionwithwallet", - &vec![tx_hex_json])).unwrap() - } - - pub fn get_new_address(&self) -> Address { - let runtime = self.runtime.lock().unwrap(); - let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); - - let addr_args = vec![serde_json::json!("LDK output address")]; - let addr = runtime.block_on(rpc.call_method::("getnewaddress", &addr_args)).unwrap(); - Address::from_str(addr.0.as_str()).unwrap() - } - - pub fn get_blockchain_info(&self) -> BlockchainInfo { - let runtime = self.runtime.lock().unwrap(); - let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); - - runtime.block_on(rpc.call_method::("getblockchaininfo", - &vec![])).unwrap() - } + pub fn new( + host: String, port: u16, rpc_user: String, rpc_password: String, + ) -> std::io::Result { + let http_endpoint = HttpEndpoint::for_host(host.clone()).with_port(port); + let rpc_credentials = + base64::encode(format!("{}:{}", rpc_user.clone(), rpc_password.clone())); + let bitcoind_rpc_client = RpcClient::new(&rpc_credentials, http_endpoint)?; + let client = Self { + bitcoind_rpc_client: Mutex::new(bitcoind_rpc_client), + host, + port, + rpc_user, + rpc_password, + runtime: Mutex::new(Runtime::new().unwrap()), + }; + Ok(client) + } + + pub fn get_new_rpc_client(&self) -> std::io::Result { + let http_endpoint = HttpEndpoint::for_host(self.host.clone()).with_port(self.port); + let rpc_credentials = + base64::encode(format!("{}:{}", self.rpc_user.clone(), self.rpc_password.clone())); + RpcClient::new(&rpc_credentials, http_endpoint) + } + + pub fn create_raw_transaction(&self, outputs: Vec>) -> RawTx { + let runtime = self.runtime.lock().unwrap(); + let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); + + let outputs_json = serde_json::json!(outputs); + runtime + .block_on(rpc.call_method::( + "createrawtransaction", + &vec![serde_json::json!([]), outputs_json], + )) + .unwrap() + } + + pub fn fund_raw_transaction(&self, raw_tx: RawTx) -> FundedTx { + let runtime = self.runtime.lock().unwrap(); + let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); + + let raw_tx_json = serde_json::json!(raw_tx.0); + runtime.block_on(rpc.call_method("fundrawtransaction", &[raw_tx_json])).unwrap() + } + + pub fn sign_raw_transaction_with_wallet(&self, tx_hex: String) -> SignedTx { + let runtime = self.runtime.lock().unwrap(); + let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); + + let tx_hex_json = serde_json::json!(tx_hex); + runtime + .block_on(rpc.call_method("signrawtransactionwithwallet", &vec![tx_hex_json])) + .unwrap() + } + + pub fn get_new_address(&self) -> Address { + let runtime = self.runtime.lock().unwrap(); + let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); + + let addr_args = vec![serde_json::json!("LDK output address")]; + let addr = + runtime.block_on(rpc.call_method::("getnewaddress", &addr_args)).unwrap(); + Address::from_str(addr.0.as_str()).unwrap() + } + + pub fn get_blockchain_info(&self) -> BlockchainInfo { + let runtime = self.runtime.lock().unwrap(); + let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); + + runtime.block_on(rpc.call_method::("getblockchaininfo", &vec![])).unwrap() + } } impl FeeEstimator for BitcoindClient { - fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { - let runtime = self.runtime.lock().unwrap(); - let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); - - let (conf_target, estimate_mode, default) = match confirmation_target { - ConfirmationTarget::Background => (144, "ECONOMICAL", 253), - ConfirmationTarget::Normal => (18, "ECONOMICAL", 20000), - ConfirmationTarget::HighPriority => (6, "ECONOMICAL", 50000), - }; - - // This function may be called from a tokio runtime, or not. So we need to check before - // making the call to avoid the error "cannot run a tokio runtime from within a tokio runtime". - let conf_target_json = serde_json::json!(conf_target); - let estimate_mode_json = serde_json::json!(estimate_mode); - let resp = match Handle::try_current() { - Ok(_) => { - tokio::task::block_in_place(|| { - runtime.block_on(rpc.call_method::("estimatesmartfee", - &vec![conf_target_json, - estimate_mode_json])).unwrap() - }) - }, - _ => runtime.block_on(rpc.call_method::("estimatesmartfee", - &vec![conf_target_json, - estimate_mode_json])).unwrap() - }; - if resp.errored { - return default - } - resp.feerate.unwrap() - } + fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { + let runtime = self.runtime.lock().unwrap(); + let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); + + let (conf_target, estimate_mode, default) = match confirmation_target { + ConfirmationTarget::Background => (144, "ECONOMICAL", 253), + ConfirmationTarget::Normal => (18, "ECONOMICAL", 20000), + ConfirmationTarget::HighPriority => (6, "ECONOMICAL", 50000), + }; + + // This function may be called from a tokio runtime, or not. So we need to check before + // making the call to avoid the error "cannot run a tokio runtime from within a tokio runtime". + let conf_target_json = serde_json::json!(conf_target); + let estimate_mode_json = serde_json::json!(estimate_mode); + let resp = match Handle::try_current() { + Ok(_) => tokio::task::block_in_place(|| { + runtime + .block_on(rpc.call_method::( + "estimatesmartfee", + &vec![conf_target_json, estimate_mode_json], + )) + .unwrap() + }), + _ => runtime + .block_on(rpc.call_method::( + "estimatesmartfee", + &vec![conf_target_json, estimate_mode_json], + )) + .unwrap(), + }; + if resp.errored { + return default; + } + resp.feerate.unwrap() + } } impl BroadcasterInterface for BitcoindClient { - fn broadcast_transaction(&self, tx: &Transaction) { - let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); - let runtime = self.runtime.lock().unwrap(); - - let tx_serialized = serde_json::json!(encode::serialize_hex(tx)); - // This function may be called from a tokio runtime, or not. So we need to check before - // making the call to avoid the error "cannot run a tokio runtime from within a tokio runtime". - match Handle::try_current() { - Ok(_) => { - tokio::task::block_in_place(|| { - runtime.block_on(rpc.call_method::("sendrawtransaction", - &vec![tx_serialized])).unwrap(); - }); - }, - _ => { - runtime.block_on(rpc.call_method::("sendrawtransaction", - &vec![tx_serialized])).unwrap(); - } - } - } + fn broadcast_transaction(&self, tx: &Transaction) { + let mut rpc = self.bitcoind_rpc_client.lock().unwrap(); + let runtime = self.runtime.lock().unwrap(); + + let tx_serialized = serde_json::json!(encode::serialize_hex(tx)); + // This function may be called from a tokio runtime, or not. So we need to check before + // making the call to avoid the error "cannot run a tokio runtime from within a tokio runtime". + match Handle::try_current() { + Ok(_) => { + tokio::task::block_in_place(|| { + runtime + .block_on( + rpc.call_method::("sendrawtransaction", &vec![tx_serialized]), + ) + .unwrap(); + }); + } + _ => { + runtime + .block_on(rpc.call_method::("sendrawtransaction", &vec![tx_serialized])) + .unwrap(); + } + } + } }