Next draft. Features: connectpeer, listchannels, sendpayment, autoreconnect to chan...
[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             // runtime: Mutex::new(runtime),
40         };
41         Ok(client)
42     }
43
44     pub fn get_new_rpc_client(&self) -> std::io::Result<RpcClient> {
45         let http_endpoint = HttpEndpoint::for_host(self.host.clone()).with_port(self.port);
46         let rpc_credentials = base64::encode(format!("{}:{}",
47                                                      self.rpc_user.clone(),
48                                                      self.rpc_password.clone()));
49         RpcClient::new(&rpc_credentials, http_endpoint)
50     }
51
52     pub fn create_raw_transaction(&self, outputs: Vec<HashMap<String, f64>>) -> RawTx {
53         let runtime = self.runtime.lock().unwrap();
54         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
55
56         let outputs_json = serde_json::json!(outputs);
57         runtime.block_on(rpc.call_method::<RawTx>("createrawtransaction", &vec![serde_json::json!([]), outputs_json])).unwrap()
58     }
59
60     pub fn fund_raw_transaction(&self, raw_tx: RawTx) -> FundedTx {
61         let runtime = self.runtime.lock().unwrap();
62         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
63
64         let raw_tx_json = serde_json::json!(raw_tx.0);
65         runtime.block_on(rpc.call_method("fundrawtransaction", &[raw_tx_json])).unwrap()
66     }
67
68     pub fn sign_raw_transaction_with_wallet(&self, tx_hex: String) -> SignedTx {
69         let runtime = self.runtime.lock().unwrap();
70         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
71
72         let tx_hex_json = serde_json::json!(tx_hex);
73         runtime.block_on(rpc.call_method("signrawtransactionwithwallet",
74                                          &vec![tx_hex_json])).unwrap()
75     }
76
77     pub fn get_new_address(&self) -> Address {
78         let runtime = self.runtime.lock().unwrap();
79         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
80
81         let addr_args = vec![serde_json::json!("LDK output address")];
82         let addr = runtime.block_on(rpc.call_method::<NewAddress>("getnewaddress", &addr_args)).unwrap();
83         Address::from_str(addr.0.as_str()).unwrap()
84     }
85
86     pub fn get_blockchain_info(&self) -> BlockchainInfo {
87         let runtime = self.runtime.lock().unwrap();
88         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
89
90         runtime.block_on(rpc.call_method::<BlockchainInfo>("getblockchaininfo",
91                                                                            &vec![])).unwrap()
92     }
93 }
94
95 impl FeeEstimator for BitcoindClient {
96     fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 {
97         let runtime = self.runtime.lock().unwrap();
98         let mut rpc = self.bitcoind_rpc_client.lock().unwrap();
99
100         let (conf_target, estimate_mode, default) = match confirmation_target {
101             ConfirmationTarget::Background => (144, "ECONOMICAL", 253),
102             ConfirmationTarget::Normal => (18, "ECONOMICAL", 20000),
103             ConfirmationTarget::HighPriority => (6, "ECONOMICAL", 50000),
104         };
105
106         // If we're already in a tokio runtime, then we need to get out of it before we can broadcast.
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         // If we're already in a tokio runtime, then we need to get out of it before we can broadcast.
135         match Handle::try_current() {
136             Ok(_) => {
137                 tokio::task::block_in_place(|| {
138                     runtime.block_on(rpc.call_method::<RawTx>("sendrawtransaction",
139                                                                           &vec![tx_serialized])).unwrap();
140                 });
141             },
142             _ => {
143                 runtime.block_on(rpc.call_method::<RawTx>("sendrawtransaction",
144                                                                       &vec![tx_serialized])).unwrap();
145             }
146         }
147     }
148 }