Add readme, fix license and a few other cleanups
[ldk-sample] / src / bitcoind_client.rs
1 use base64;
2 use bitcoin::blockdata::transaction::Transaction;
3 use bitcoin::consensus::encode;
4 use bitcoin::util::address::Address;
5 use crate::convert::{BlockchainInfo, FeeResponse, FundedTx, NewAddress, RawTx, SignedTx};
6 use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
7 use lightning_block_sync::http::HttpEndpoint;
8 use lightning_block_sync::rpc::RpcClient;
9 use serde_json;
10 use std::collections::HashMap;
11 use std::str::FromStr;
12 use std::sync::Mutex;
13 use tokio::runtime::{Handle, Runtime};
14
15 pub struct BitcoindClient {
16     bitcoind_rpc_client: Mutex<RpcClient>,
17     host: String,
18     port: u16,
19     rpc_user: String,
20     rpc_password: String,
21     runtime: Mutex<Runtime>,
22 }
23
24 impl BitcoindClient {
25     pub fn new(host: String, port: u16, rpc_user: String, rpc_password: String) ->
26         std::io::Result<Self>
27     {
28         let http_endpoint = HttpEndpoint::for_host(host.clone()).with_port(port);
29         let rpc_credentials = base64::encode(format!("{}:{}", rpc_user.clone(),
30                                                      rpc_password.clone()));
31         let bitcoind_rpc_client = RpcClient::new(&rpc_credentials, http_endpoint)?;
32         let client = Self {
33             bitcoind_rpc_client: Mutex::new(bitcoind_rpc_client),
34             host,
35             port,
36             rpc_user,
37             rpc_password,
38             runtime: Mutex::new(Runtime::new().unwrap()),
39         };
40         Ok(client)
41     }
42
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);
45         let rpc_credentials = base64::encode(format!("{}:{}",
46                                                      self.rpc_user.clone(),
47                                                      self.rpc_password.clone()));
48         RpcClient::new(&rpc_credentials, http_endpoint)
49     }
50
51     pub fn create_raw_transaction(&self, outputs: Vec<HashMap<String, f64>>) -> RawTx {
52         let runtime = self.runtime.lock().unwrap();
53         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
54
55         let outputs_json = serde_json::json!(outputs);
56         runtime.block_on(rpc.call_method::<RawTx>("createrawtransaction", &vec![serde_json::json!([]), outputs_json])).unwrap()
57     }
58
59     pub fn fund_raw_transaction(&self, raw_tx: RawTx) -> FundedTx {
60         let runtime = self.runtime.lock().unwrap();
61         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
62
63         let raw_tx_json = serde_json::json!(raw_tx.0);
64         runtime.block_on(rpc.call_method("fundrawtransaction", &[raw_tx_json])).unwrap()
65     }
66
67     pub fn sign_raw_transaction_with_wallet(&self, tx_hex: String) -> SignedTx {
68         let runtime = self.runtime.lock().unwrap();
69         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
70
71         let tx_hex_json = serde_json::json!(tx_hex);
72         runtime.block_on(rpc.call_method("signrawtransactionwithwallet",
73                                          &vec![tx_hex_json])).unwrap()
74     }
75
76     pub fn get_new_address(&self) -> Address {
77         let runtime = self.runtime.lock().unwrap();
78         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
79
80         let addr_args = vec![serde_json::json!("LDK output address")];
81         let addr = runtime.block_on(rpc.call_method::<NewAddress>("getnewaddress", &addr_args)).unwrap();
82         Address::from_str(addr.0.as_str()).unwrap()
83     }
84
85     pub fn get_blockchain_info(&self) -> BlockchainInfo {
86         let runtime = self.runtime.lock().unwrap();
87         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
88
89         runtime.block_on(rpc.call_method::<BlockchainInfo>("getblockchaininfo",
90                                                                            &vec![])).unwrap()
91     }
92 }
93
94 impl FeeEstimator for BitcoindClient {
95     fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 {
96         let runtime = self.runtime.lock().unwrap();
97         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
98
99         let (conf_target, estimate_mode, default) = match confirmation_target {
100             ConfirmationTarget::Background => (144, "ECONOMICAL", 253),
101             ConfirmationTarget::Normal => (18, "ECONOMICAL", 20000),
102             ConfirmationTarget::HighPriority => (6, "ECONOMICAL", 50000),
103         };
104
105         // This function may be called from a tokio runtime, or not. So we need to check before
106         // making the call to avoid the error "cannot run a tokio runtime from within a tokio runtime".
107         let conf_target_json = serde_json::json!(conf_target);
108         let estimate_mode_json = serde_json::json!(estimate_mode);
109         let resp = match Handle::try_current() {
110             Ok(_) => {
111                 tokio::task::block_in_place(|| {
112                     runtime.block_on(rpc.call_method::<FeeResponse>("estimatesmartfee",
113                                                                     &vec![conf_target_json,
114                                                                           estimate_mode_json])).unwrap()
115                 })
116             },
117             _ => runtime.block_on(rpc.call_method::<FeeResponse>("estimatesmartfee",
118                                                                  &vec![conf_target_json,
119                                                                        estimate_mode_json])).unwrap()
120         };
121         if resp.errored {
122             return default
123         }
124         resp.feerate.unwrap()
125     }
126 }
127
128 impl BroadcasterInterface for BitcoindClient {
129           fn broadcast_transaction(&self, tx: &Transaction) {
130         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
131         let runtime = self.runtime.lock().unwrap();
132
133         let tx_serialized = serde_json::json!(encode::serialize_hex(tx));
134         // This function may be called from a tokio runtime, or not. So we need to check before
135         // making the call to avoid the error "cannot run a tokio runtime from within a tokio runtime".
136         match Handle::try_current() {
137             Ok(_) => {
138                 tokio::task::block_in_place(|| {
139                     runtime.block_on(rpc.call_method::<RawTx>("sendrawtransaction",
140                                                                           &vec![tx_serialized])).unwrap();
141                 });
142             },
143             _ => {
144                 runtime.block_on(rpc.call_method::<RawTx>("sendrawtransaction",
145                                                                       &vec![tx_serialized])).unwrap();
146             }
147         }
148     }
149 }