corepc_client/client_sync/
mod.rs1mod error;
6pub mod v17;
7pub mod v18;
8pub mod v19;
9pub mod v20;
10pub mod v21;
11pub mod v22;
12pub mod v23;
13pub mod v24;
14pub mod v25;
15pub mod v26;
16pub mod v27;
17pub mod v28;
18
19use std::fs::File;
20use std::io::{BufRead, BufReader};
21use std::path::PathBuf;
22
23use bitcoin::Txid;
24use serde::{Deserialize, Serialize};
25
26pub use crate::client_sync::error::Error;
27
28pub type Result<T> = std::result::Result<T, Error>;
32
33#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
35pub enum Auth {
36 None,
37 UserPass(String, String),
38 CookieFile(PathBuf),
39}
40
41impl Auth {
42 pub fn get_user_pass(self) -> Result<(Option<String>, Option<String>)> {
44 match self {
45 Auth::None => Ok((None, None)),
46 Auth::UserPass(u, p) => Ok((Some(u), Some(p))),
47 Auth::CookieFile(path) => {
48 let line = BufReader::new(File::open(path)?)
49 .lines()
50 .next()
51 .ok_or(Error::InvalidCookieFile)??;
52 let colon = line.find(':').ok_or(Error::InvalidCookieFile)?;
53 Ok((Some(line[..colon].into()), Some(line[colon + 1..].into())))
54 }
55 }
56 }
57}
58
59#[macro_export]
61macro_rules! define_jsonrpc_minreq_client {
62 ($version:literal) => {
63 use std::fmt;
64
65 use $crate::client_sync::{log_response, Auth, Result};
66 use $crate::client_sync::error::Error;
67
68 pub struct Client {
70 inner: jsonrpc::client::Client,
71 }
72
73 impl fmt::Debug for Client {
74 fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
75 write!(
76 f,
77 "corepc_client::client_sync::{}::Client({:?})", $version, self.inner
78 )
79 }
80 }
81
82 impl Client {
83 pub fn new(url: &str) -> Self {
85 let transport = jsonrpc::http::minreq_http::Builder::new()
86 .url(url)
87 .expect("jsonrpc v0.18, this function does not error")
88 .build();
89 let inner = jsonrpc::client::Client::with_transport(transport);
90
91 Self { inner }
92 }
93
94 pub fn new_with_auth(url: &str, auth: Auth) -> Result<Self> {
96 if matches!(auth, Auth::None) {
97 return Err(Error::MissingUserPassword);
98 }
99 let (user, pass) = auth.get_user_pass()?;
100
101 let transport = jsonrpc::http::minreq_http::Builder::new()
102 .url(url)
103 .expect("jsonrpc v0.18, this function does not error")
104 .basic_auth(user.unwrap(), pass)
105 .build();
106 let inner = jsonrpc::client::Client::with_transport(transport);
107
108 Ok(Self { inner })
109 }
110
111 pub fn call<T: for<'a> serde::de::Deserialize<'a>>(
113 &self,
114 method: &str,
115 args: &[serde_json::Value],
116 ) -> Result<T> {
117 let raw = serde_json::value::to_raw_value(args)?;
118 let req = self.inner.build_request(&method, Some(&*raw));
119 if log::log_enabled!(log::Level::Debug) {
120 log::debug!(target: "corepc", "request: {} {}", method, serde_json::Value::from(args));
121 }
122
123 let resp = self.inner.send_request(req).map_err(Error::from);
124 log_response(method, &resp);
125 Ok(resp?.result()?)
126 }
127 }
128 }
129}
130
131#[macro_export]
140macro_rules! impl_client_check_expected_server_version {
141 ($expected_versions:expr) => {
142 impl Client {
143 pub fn check_expected_server_version(&self) -> Result<()> {
145 let server_version = self.server_version()?;
146 if !$expected_versions.contains(&server_version) {
147 return Err($crate::client_sync::error::UnexpectedServerVersionError {
148 got: server_version,
149 expected: $expected_versions.to_vec(),
150 })?;
151 }
152 Ok(())
153 }
154 }
155 };
156}
157
158fn into_json<T>(val: T) -> Result<serde_json::Value>
160where
161 T: serde::ser::Serialize,
162{
163 Ok(serde_json::to_value(val)?)
164}
165
166#[allow(dead_code)] fn opt_into_json<T>(opt: Option<T>) -> Result<serde_json::Value>
169where
170 T: serde::ser::Serialize,
171{
172 match opt {
173 Some(val) => Ok(into_json(val)?),
174 None => Ok(serde_json::Value::Null),
175 }
176}
177
178#[allow(dead_code)] fn null() -> serde_json::Value { serde_json::Value::Null }
181
182#[allow(dead_code)] fn empty_arr() -> serde_json::Value { serde_json::Value::Array(vec![]) }
185
186#[allow(dead_code)] fn empty_obj() -> serde_json::Value { serde_json::Value::Object(Default::default()) }
189
190#[allow(dead_code)] fn opt_result<T: for<'a> serde::de::Deserialize<'a>>(
193 result: serde_json::Value,
194) -> Result<Option<T>> {
195 if result == serde_json::Value::Null {
196 Ok(None)
197 } else {
198 Ok(serde_json::from_value(result)?)
199 }
200}
201
202fn log_response(method: &str, resp: &Result<jsonrpc::Response>) {
204 use log::Level::{Debug, Trace, Warn};
205
206 if log::log_enabled!(Warn) || log::log_enabled!(Debug) || log::log_enabled!(Trace) {
207 match resp {
208 Err(ref e) =>
209 if log::log_enabled!(Debug) {
210 log::debug!(target: "corepc", "error: {}: {:?}", method, e);
211 },
212 Ok(ref resp) =>
213 if let Some(ref e) = resp.error {
214 if log::log_enabled!(Debug) {
215 log::debug!(target: "corepc", "response error for {}: {:?}", method, e);
216 }
217 } else if log::log_enabled!(Trace) {
218 let def =
219 serde_json::value::to_raw_value(&serde_json::value::Value::Null).unwrap();
220 let result = resp.result.as_ref().unwrap_or(&def);
221 log::trace!(target: "corepc", "response for {}: {}", method, result);
222 },
223 }
224 }
225}
226
227#[derive(Debug, Serialize)]
229pub struct Input {
230 pub txid: bitcoin::Txid,
232 pub vout: u64,
234 pub sequence: Option<bitcoin::Sequence>,
236}
237
238#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
240pub struct WalletCreateFundedPsbtInput {
241 txid: Txid,
242 vout: u32,
243}
244
245#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
247pub struct TemplateRequest {
248 pub rules: Vec<TemplateRules>,
250}
251
252#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
254#[serde(rename_all = "lowercase")]
255pub enum TemplateRules {
256 Segwit,
258 Signet,
260 Csv,
262 Taproot,
264}