1 use crate::convert::{BlockchainInfo, FeeResponse, FundedTx, NewAddress, RawTx, SignedTx};
3 use bitcoin::blockdata::transaction::Transaction;
4 use bitcoin::consensus::encode;
5 use bitcoin::util::address::Address;
6 use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
7 use lightning_block_sync::http::HttpEndpoint;
8 use lightning_block_sync::rpc::RpcClient;
10 use std::collections::HashMap;
11 use std::str::FromStr;
13 use tokio::runtime::{Handle, Runtime};
15 pub struct BitcoindClient {
16 bitcoind_rpc_client: Mutex<RpcClient>,
21 runtime: Mutex<Runtime>,
26 host: String, port: u16, rpc_user: String, rpc_password: String,
27 ) -> std::io::Result<Self> {
28 let http_endpoint = HttpEndpoint::for_host(host.clone()).with_port(port);
30 base64::encode(format!("{}:{}", rpc_user.clone(), rpc_password.clone()));
31 let bitcoind_rpc_client = RpcClient::new(&rpc_credentials, http_endpoint)?;
33 bitcoind_rpc_client: Mutex::new(bitcoind_rpc_client),
38 runtime: Mutex::new(Runtime::new().unwrap()),
43 pub fn get_new_rpc_client(&self) -> std::io::Result<RpcClient> {
44 let http_endpoint = HttpEndpoint::for_host(self.host.clone()).with_port(self.port);
46 base64::encode(format!("{}:{}", self.rpc_user.clone(), self.rpc_password.clone()));
47 RpcClient::new(&rpc_credentials, http_endpoint)
50 pub fn create_raw_transaction(&self, outputs: Vec<HashMap<String, f64>>) -> RawTx {
51 let runtime = self.runtime.lock().unwrap();
52 let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
54 let outputs_json = serde_json::json!(outputs);
56 .block_on(rpc.call_method::<RawTx>(
57 "createrawtransaction",
58 &vec![serde_json::json!([]), outputs_json],
63 pub fn fund_raw_transaction(&self, raw_tx: RawTx) -> FundedTx {
64 let runtime = self.runtime.lock().unwrap();
65 let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
67 let raw_tx_json = serde_json::json!(raw_tx.0);
68 runtime.block_on(rpc.call_method("fundrawtransaction", &[raw_tx_json])).unwrap()
71 pub fn sign_raw_transaction_with_wallet(&self, tx_hex: String) -> SignedTx {
72 let runtime = self.runtime.lock().unwrap();
73 let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
75 let tx_hex_json = serde_json::json!(tx_hex);
77 .block_on(rpc.call_method("signrawtransactionwithwallet", &vec![tx_hex_json]))
81 pub fn get_new_address(&self) -> Address {
82 let runtime = self.runtime.lock().unwrap();
83 let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
85 let addr_args = vec![serde_json::json!("LDK output address")];
87 runtime.block_on(rpc.call_method::<NewAddress>("getnewaddress", &addr_args)).unwrap();
88 Address::from_str(addr.0.as_str()).unwrap()
91 pub fn get_blockchain_info(&self) -> BlockchainInfo {
92 let runtime = self.runtime.lock().unwrap();
93 let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
95 runtime.block_on(rpc.call_method::<BlockchainInfo>("getblockchaininfo", &vec![])).unwrap()
99 impl FeeEstimator for BitcoindClient {
100 fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 {
101 let runtime = self.runtime.lock().unwrap();
102 let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
104 let (conf_target, estimate_mode, default) = match confirmation_target {
105 ConfirmationTarget::Background => (144, "ECONOMICAL", 253),
106 ConfirmationTarget::Normal => (18, "ECONOMICAL", 20000),
107 ConfirmationTarget::HighPriority => (6, "ECONOMICAL", 50000),
110 // This function may be called from a tokio runtime, or not. So we need to check before
111 // making the call to avoid the error "cannot run a tokio runtime from within a tokio runtime".
112 let conf_target_json = serde_json::json!(conf_target);
113 let estimate_mode_json = serde_json::json!(estimate_mode);
114 let resp = match Handle::try_current() {
115 Ok(_) => tokio::task::block_in_place(|| {
117 .block_on(rpc.call_method::<FeeResponse>(
119 &vec![conf_target_json, estimate_mode_json],
124 .block_on(rpc.call_method::<FeeResponse>(
126 &vec![conf_target_json, estimate_mode_json],
133 resp.feerate.unwrap()
137 impl BroadcasterInterface for BitcoindClient {
138 fn broadcast_transaction(&self, tx: &Transaction) {
139 let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
140 let runtime = self.runtime.lock().unwrap();
142 let tx_serialized = serde_json::json!(encode::serialize_hex(tx));
143 // This function may be called from a tokio runtime, or not. So we need to check before
144 // making the call to avoid the error "cannot run a tokio runtime from within a tokio runtime".
145 match Handle::try_current() {
147 tokio::task::block_in_place(|| {
150 rpc.call_method::<RawTx>("sendrawtransaction", &vec![tx_serialized]),
157 .block_on(rpc.call_method::<RawTx>("sendrawtransaction", &vec![tx_serialized]))