github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/cmd/rs-af/litrpc.rs (about) 1 #![allow(unused)] 2 #![allow(non_snake_case)] 3 4 #![feature(concat_idents)] 5 6 use std::net::TcpStream; 7 8 use std::fmt; 9 10 use reqwest; 11 12 use serde::{Serialize, Deserialize}; 13 use serde::de::DeserializeOwned; 14 15 use serde_json; 16 17 #[derive(Clone, Debug, Serialize)] 18 struct RpcReqest<P> where P: Serialize { 19 method: String, 20 params: Vec<P>, 21 jsonrpc: String, 22 id: u64 23 } 24 25 #[derive(Clone, Debug, Deserialize)] 26 struct RpcResponse<R> { 27 //#[serde(default="Option::None")] 28 result: Option<R>, 29 30 //#[serde(default="Option::None")] 31 error: Option<RpcError>, 32 33 id: u64 34 } 35 36 #[derive(Clone, Debug, Deserialize)] 37 pub struct RpcError { 38 code: i64, 39 message: String, 40 data: String 41 } 42 43 pub struct LitRpcClient { 44 next_msg_id: u64, 45 url: String, 46 client: reqwest::Client 47 } 48 49 #[derive(Debug)] 50 pub enum LitRpcError { 51 RpcError(RpcError), 52 RpcInvalidResponse, 53 SerdeJsonError(serde_json::Error), 54 HttpError(reqwest::Error), 55 } 56 57 impl From<reqwest::Error> for LitRpcError { 58 fn from(from: reqwest::Error) -> Self { 59 LitRpcError::HttpError(from) 60 } 61 } 62 63 impl From<serde_json::Error> for LitRpcError { 64 fn from(from: serde_json::Error) -> Self { 65 LitRpcError::SerdeJsonError(from) 66 } 67 } 68 69 impl LitRpcClient { 70 pub fn new(addr: &str, port: u16) -> LitRpcClient { 71 LitRpcClient { 72 next_msg_id: 0, 73 url: format!("http://{}:{}/oneoff", addr, port), 74 client: reqwest::Client::new() 75 } 76 } 77 78 pub fn call<P: Serialize, R: DeserializeOwned>(&mut self, name: &str, params: P) -> Result<R, LitRpcError> { 79 80 // Construct the request object. 81 let req = RpcReqest { 82 method: String::from(name), 83 params: vec![params], 84 jsonrpc: String::from("2.0"), // required by the standard 85 id: self.next_msg_id 86 }; 87 88 // Increment the "next" value to not confuse request IDs. 89 self.next_msg_id += 1; 90 91 // Serialize the request. 92 let req_body = serde_json::to_string(&req)?; 93 eprintln!("request: {}", req_body); 94 95 // Send it off and get a response. 96 let mut res_json = self.client.post(self.url.as_str()) 97 .body(req_body) 98 .send()?; 99 let text = res_json.text()?; 100 eprintln!("reponse: {}", text); 101 102 // Deserialize... 103 let res: RpcResponse<R> = serde_json::from_str(text.as_ref())?; 104 if res.id != req.id { 105 // Ok this makes no sense but we should fail out anyways. 106 return Err(LitRpcError::RpcInvalidResponse) 107 } 108 109 // And then match on the response part thingies to figure out what to do. 110 match (res.result, res.error) { 111 (Some(r), None) => Ok(r), 112 (None, Some(e)) => Err(LitRpcError::RpcError(e)), 113 _ => Err(LitRpcError::RpcInvalidResponse) 114 } 115 116 } 117 } 118 119 macro_rules! rpc_call { 120 {$fname:ident, $method:ident, { $($ii:ident: $it:ty),* } => $oname:ident { $($oi:ident: $ot:ty),* }} => { 121 122 #[derive(Clone, Deserialize)] 123 #[allow(non_snake_case)] 124 pub struct $oname { 125 $( pub $oi: $ot ),* 126 } 127 128 impl LitRpcClient { 129 130 #[allow(non_snake_case)] 131 pub fn $fname(&mut self, $($ii: $it),*) -> Result<$oname, LitRpcError> { 132 133 #[derive(Clone, Debug, Serialize)] 134 #[allow(non_snake_case)] 135 struct Params { 136 $( $ii: $it ),* 137 }; 138 139 let arg = Params { 140 $($ii: $ii),* 141 }; 142 143 self.call(concat!("LitRPC.", stringify!($method)), arg) 144 } 145 } 146 147 }; 148 {$fname:ident, $method:ident, { $($ii:ident: $it:ty),* } => $oname:ident} => { 149 150 impl LitRpcClient { 151 152 #[allow(non_snake_case)] 153 pub fn $fname(&mut self, $($ii: $it),*) -> Result<$oname, LitRpcError> { 154 155 #[derive(Clone, Debug, Serialize)] 156 #[allow(non_snake_case)] 157 struct Params { 158 $( $ii: $it ),* 159 }; 160 161 let arg = Params { 162 $($ii: $ii),* 163 }; 164 165 self.call(concat!("LitRPC.", stringify!($method)), arg) 166 } 167 } 168 169 }; 170 } 171 172 #[derive(Clone, Deserialize)] 173 pub struct StatusReply { 174 Status: String 175 } 176 177 impl fmt::Debug for StatusReply { 178 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 179 f.write_str(format!("status: {}", self.Status).as_ref()) 180 } 181 } 182 183 // netcmds 184 185 #[derive(Clone, Deserialize)] 186 pub struct ListeningPortsReply { 187 LisIpPorts: Vec<String>, 188 Adr: String 189 } 190 191 rpc_call! { 192 call_lis, Listen, 193 { Port: String } => ListeningPortsReply 194 } 195 196 rpc_call! { 197 call_connect, Connect, 198 { LNAddr: String } => StatusReply 199 } 200 201 rpc_call! { 202 call_assign_nickname, AssignNickname, 203 { Peer: u32, Nickname: String } => StatusReply 204 } 205 206 #[derive(Clone, Deserialize)] 207 pub struct ConInfo { 208 PeerNumber: u32, 209 RemoteHost: String 210 } 211 212 rpc_call! { 213 call_list_connections, ListConnections, 214 {} => ListConnectionsReply { Connections: Vec<ConInfo>, MyPKH: String } 215 } 216 217 rpc_call! { 218 call_get_listening_ports, GetListeningPorts, 219 {} => ListeningPortsReply 220 } 221 222 rpc_call! { call_get_messages, GetMessages, {} => StatusReply } 223 rpc_call! { call_say, Say, {} => StatusReply } 224 rpc_call! { call_stop, Stop, {} => StatusReply } 225 226 rpc_call! { 227 call_get_chan_map, GetChannelMap, 228 {} => ChanGraphReply { Graph: String } 229 } 230 231 // chancmds 232 233 #[derive(Clone, Debug, Deserialize)] 234 pub struct ChanInfo { 235 pub OutPoint: String, 236 pub CoinType: u32, 237 pub Closed: bool, 238 pub Capacity: i64, 239 pub MyBalance: i64, 240 pub Height: i32, 241 pub StateNum: i64, 242 pub PeerIdx: u32, 243 pub CIdx: u32, 244 pub Data: [u8; 32], 245 pub Pkh: [u8; 20], 246 pub LastUpdate: u64 247 } 248 249 rpc_call! { 250 call_chan_list, ChannelList, 251 { ChanIdx: u32 } => ChanListReply { Channels: Vec<ChanInfo> } 252 } 253 254 rpc_call! { 255 call_fund, FundChannel, 256 { 257 Peer: u32, 258 CoinType: u32, 259 Capacity: i64, 260 Roundup: i64, 261 InitialSend: i64, 262 Data: [u8; 32] 263 } => StatusReply 264 } 265 266 rpc_call! { 267 call_dual_fund, DualFundChannel, 268 { 269 Peer: i32, 270 CoinType: i32, 271 OurAmount: i64, 272 TheirAmount: i64 273 } => StatusReply 274 } 275 276 // TODO DualFundDecline, DualFundAccept, etc. 277 278 rpc_call! { 279 call_get_pending_dual_fund, PendingDualFund, 280 {} => PendingDualFundReply { 281 Pending: bool, 282 PeerIdx: u32, 283 CoinType: u32, 284 TheirAmount: i64, 285 RequestedAmount: i64 286 } 287 } 288 289 /* TODO No auto-Deserialize for [u8; 64], do something about this later. 290 #[derive(Clone, Deserialize)] 291 pub struct JusticeTx { 292 Sig: [u8; 64], 293 Txid: [u8; 16], 294 Amt: i64, 295 Data: [u8; 32], 296 Pkh: [u8; 20], 297 Idx: u64 298 } 299 300 rpc_call! { 301 call_get_state_dump, StateDump, 302 {} => StateDumpReply { 303 Txs: Vec<JusticeTx> 304 } 305 } 306 */ 307 308 rpc_call! { 309 call_push, Push, 310 { 311 ChanIdx: u32, 312 Amt: i64, 313 Data: [u8; 32] 314 } => PushReply { 315 StateIdx: u64 316 } 317 } 318 319 rpc_call! { 320 call_close_chan, CloseChannel, 321 { ChanIdx: u32 } => StatusReply 322 } 323 324 rpc_call! { 325 call_break_chan, BreakChannel, 326 { ChanIdx: u32 } => StatusReply 327 } 328 329 #[derive(Clone, Deserialize)] 330 pub struct PrivInfo { 331 pub OutPoint: String, 332 pub Amt: i64, 333 pub Height: i32, 334 pub Delay: i32, 335 pub CoinType: String, 336 pub Witty: bool, 337 pub PairKey: String, 338 pub WIF: String 339 } 340 341 rpc_call! { 342 call_dump_privs, DumpPrivs, 343 {} => DumpPrivsReply { Privs: Vec<PrivInfo> } 344 } 345 346 // walletcmds 347 348 #[derive(Clone, Deserialize)] 349 pub struct CoinBalInfo { 350 pub CoinType: u32, 351 pub SyncHeight: i32, 352 pub ChanTotal: i64, 353 pub TxoTotal: i64, 354 pub MatureWitty: i64, 355 pub FeeRate: i64 356 } 357 358 rpc_call! { 359 call_bal, Balance, 360 {} => BalanceReply { Balances: Vec<CoinBalInfo> } 361 } 362 363 #[derive(Clone, Debug, Deserialize)] 364 pub struct TxoInfo { 365 pub OutPoint: String, 366 pub Amt: i64, 367 pub Height: i32, 368 pub Delay: i32, 369 pub CoinType: String, 370 pub Witty: bool, 371 pub KeyPath: String 372 } 373 374 rpc_call! { 375 call_get_txo_list, TxoList, 376 {} => TxoListReply { Txos: Vec<TxoInfo> } 377 } 378 379 #[derive(Clone, Deserialize)] 380 pub struct TxidsReply { 381 pub Txids: Vec<String> 382 } 383 384 rpc_call! { 385 call_send, Send, // oh no it's already an identifier whoops 386 { 387 DestAddrs: Vec<String>, 388 Amts: Vec<i64> 389 } => TxidsReply 390 } 391 392 rpc_call! { 393 call_sweep, Sweep, 394 { 395 DestAdr: String, 396 NumTx: u32, 397 Drop: bool // oh no it's an identifier too, whoops! 398 } => TxidsReply 399 } 400 401 rpc_call! { 402 call_fanout, Fanout, 403 { 404 DestAdr: String, 405 NumOutputs: u32, 406 AmtPerOutput: i64 407 } => TxidsReply 408 } 409 410 #[derive(Clone, Deserialize)] 411 pub struct FeeReply { 412 pub CurrentFee: i64 413 } 414 415 rpc_call! { 416 call_set_fee, SetFee, 417 { Fee: i64, CoinType: u32 } => FeeReply 418 } 419 420 rpc_call! { 421 call_get_fee, GetFee, 422 { CoinType: u32 } => FeeReply 423 } 424 425 #[derive(Clone, Deserialize)] 426 pub struct AddressReply { 427 pub CoinTypes: Vec<u32>, 428 pub WitAddresses: Vec<String>, 429 pub LegacyAddresses: Vec<String> 430 } 431 432 rpc_call! { 433 call_gen_address, Address, 434 { 435 NumToMake: u32, 436 CoinType: u32 437 } => AddressReply 438 } 439 440 rpc_call! { 441 call_get_addresses, GetAddresses, 442 {} => AddressReply 443 } 444 445 // TODO make RPC call definitions