1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
//! Return types //! //! This module contains definitions of all the complex data structures that are returned by calls use std::convert::TryFrom; use std::ops::Deref; use std::sync::Arc; use bitcoin::blockdata::block; use bitcoin::consensus::encode::deserialize; use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::{sha256, Hash}; use bitcoin::{Script, Txid}; use serde::{de, Deserialize, Serialize}; static JSONRPC_2_0: &str = "2.0"; #[derive(Serialize, Clone)] #[serde(untagged)] /// A single parameter of a [`Request`](struct.Request.html) pub enum Param { /// Integer parameter U32(u32), /// Integer parameter Usize(usize), /// String parameter String(String), /// Boolean parameter Bool(bool), /// Bytes array parameter Bytes(Vec<u8>), } #[derive(Serialize, Clone)] /// A request that can be sent to the server pub struct Request<'a> { jsonrpc: &'static str, /// The JSON-RPC request id pub id: usize, /// The request method pub method: &'a str, /// The request parameters pub params: Vec<Param>, } impl<'a> Request<'a> { /// Creates a new request with a default id fn new(method: &'a str, params: Vec<Param>) -> Self { Self { id: 0, jsonrpc: JSONRPC_2_0, method, params, } } /// Creates a new request with a user-specified id pub fn new_id(id: usize, method: &'a str, params: Vec<Param>) -> Self { let mut instance = Self::new(method, params); instance.id = id; instance } } #[doc(hidden)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)] pub struct Hex32Bytes(#[serde(deserialize_with = "from_hex")] [u8; 32]); impl Deref for Hex32Bytes { type Target = [u8; 32]; fn deref(&self) -> &Self::Target { &self.0 } } impl From<[u8; 32]> for Hex32Bytes { fn from(other: [u8; 32]) -> Hex32Bytes { Hex32Bytes(other) } } /// Format used by the Electrum server to identify an address. The reverse sha256 hash of the /// scriptPubKey. Documented [here](https://electrumx.readthedocs.io/en/latest/protocol-basics.html#script-hashes). pub type ScriptHash = Hex32Bytes; /// Binary blob that condenses all the activity of an address. Used to detect changes without /// having to compare potentially long lists of transactions. pub type ScriptStatus = Hex32Bytes; /// Trait used to convert a struct into the Electrum representation of an address pub trait ToElectrumScriptHash { /// Transforms the current struct into a `ScriptHash` fn to_electrum_scripthash(&self) -> ScriptHash; } impl ToElectrumScriptHash for Script { fn to_electrum_scripthash(&self) -> ScriptHash { let mut result = sha256::Hash::hash(self.as_bytes()).into_inner(); result.reverse(); result.into() } } fn from_hex<'de, T, D>(deserializer: D) -> Result<T, D::Error> where T: FromHex, D: de::Deserializer<'de>, { let s = String::deserialize(deserializer)?; T::from_hex(&s).map_err(de::Error::custom) } fn from_hex_array<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error> where T: FromHex + std::fmt::Debug, D: de::Deserializer<'de>, { let arr = Vec::<String>::deserialize(deserializer)?; let results: Vec<Result<T, _>> = arr .into_iter() .map(|s| T::from_hex(&s).map_err(de::Error::custom)) .collect(); let mut answer = Vec::new(); for x in results.into_iter() { answer.push(x?); } Ok(answer) } fn from_hex_header<'de, D>(deserializer: D) -> Result<block::BlockHeader, D::Error> where D: de::Deserializer<'de>, { let vec: Vec<u8> = from_hex(deserializer)?; deserialize(&vec).map_err(de::Error::custom) } /// Response to a [`script_get_history`](../client/struct.Client.html#method.script_get_history) request. #[derive(Debug, Deserialize)] pub struct GetHistoryRes { /// Confirmation height of the transaction. 0 if unconfirmed, -1 if unconfirmed while some of /// its inputs are unconfirmed too. pub height: i32, /// Txid of the transaction. pub tx_hash: Txid, /// Fee of the transaction. pub fee: Option<u64>, } /// Response to a [`script_list_unspent`](../client/struct.Client.html#method.script_list_unspent) request. #[derive(Debug, Deserialize)] pub struct ListUnspentRes { /// Confirmation height of the transaction that created this output. pub height: usize, /// Txid of the transaction pub tx_hash: Txid, /// Index of the output in the transaction. pub tx_pos: usize, /// Value of the output. pub value: u64, } /// Response to a [`server_features`](../client/struct.Client.html#method.server_features) request. #[derive(Debug, Deserialize)] pub struct ServerFeaturesRes { /// Server version reported. pub server_version: String, /// Hash of the genesis block. #[serde(deserialize_with = "from_hex")] pub genesis_hash: [u8; 32], /// Minimum supported version of the protocol. pub protocol_min: String, /// Maximum supported version of the protocol. pub protocol_max: String, /// Hash function used to create the [`ScriptHash`](type.ScriptHash.html). pub hash_function: Option<String>, /// Pruned height of the server. pub pruning: Option<i64>, } /// Response to a [`server_features`](../client/struct.Client.html#method.server_features) request. #[derive(Debug, Deserialize)] pub struct GetHeadersRes { /// Maximum number of headers returned in a single response. pub max: usize, /// Number of headers in this response. pub count: usize, /// Raw headers concatenated. Normally cleared before returning. #[serde(rename(deserialize = "hex"), deserialize_with = "from_hex")] pub raw_headers: Vec<u8>, /// Array of block headers. #[serde(skip)] pub headers: Vec<block::BlockHeader>, } /// Response to a [`script_get_balance`](../client/struct.Client.html#method.script_get_balance) request. #[derive(Debug, Deserialize)] pub struct GetBalanceRes { /// Confirmed balance in Satoshis for the address. pub confirmed: u64, /// Unconfirmed balance in Satoshis for the address. pub unconfirmed: u64, } /// Response to a [`transaction_get_merkle`](../client/struct.Client.html#method.transaction_get_merkle) request. #[derive(Debug, Deserialize)] pub struct GetMerkleRes { /// Height of the block that confirmed the transaction pub block_height: usize, /// Position in the block of the transaction. pub pos: usize, /// The merkle path of the transaction. #[serde(deserialize_with = "from_hex_array")] pub merkle: Vec<[u8; 32]>, } /// Notification of a new block header #[derive(Debug, Deserialize)] pub struct HeaderNotification { /// New block height. pub height: usize, /// Newly added header. #[serde(rename = "hex", deserialize_with = "from_hex_header")] pub header: block::BlockHeader, } /// Notification of a new block header with the header encoded as raw bytes #[derive(Debug, Deserialize)] pub struct RawHeaderNotification { /// New block height. pub height: usize, /// Newly added header. #[serde(rename = "hex", deserialize_with = "from_hex")] pub header: Vec<u8>, } impl TryFrom<RawHeaderNotification> for HeaderNotification { type Error = Error; fn try_from(raw: RawHeaderNotification) -> Result<Self, Self::Error> { Ok(HeaderNotification { height: raw.height, header: deserialize(&raw.header)?, }) } } /// Notification of the new status of a script #[derive(Debug, Deserialize)] pub struct ScriptNotification { /// Address that generated this notification. pub scripthash: ScriptHash, /// The new status of the address. pub status: ScriptStatus, } /// Errors #[derive(Debug)] pub enum Error { /// Wraps `std::io::Error` IOError(std::io::Error), /// Wraps `serde_json::error::Error` JSON(serde_json::error::Error), /// Wraps `bitcoin::hashes::hex::Error` Hex(bitcoin::hashes::hex::Error), /// Error returned by the Electrum server Protocol(serde_json::Value), /// Error during the deserialization of a Bitcoin data structure Bitcoin(bitcoin::consensus::encode::Error), /// Already subscribed to the notifications of an address AlreadySubscribed(ScriptHash), /// Not subscribed to the notifications of an address NotSubscribed(ScriptHash), /// Error during the deserialization of a response from the server InvalidResponse(serde_json::Value), /// Generic error with a message Message(String), /// Invalid domain name for an SSL certificate InvalidDNSNameError(String), /// Missing domain while it was explicitly asked to validate it MissingDomain, /// Made one or multiple attempts, always in Error AllAttemptsErrored(Vec<Error>), /// There was an io error reading the socket, to be shared between threads SharedIOError(Arc<std::io::Error>), /// Setting both a proxy and a timeout in `Config` results in this error BothSocksAndTimeout, /// Setting both a timeout and passing zero or more than one socket addrs is an error WrongAddrsNumberWithTimeout, /// Couldn't take a lock on the reader mutex. This means that there's already another reader /// thread running CouldntLockReader, #[cfg(feature = "use-openssl")] /// Invalid OpenSSL method used InvalidSslMethod(openssl::error::ErrorStack), #[cfg(feature = "use-openssl")] /// SSL Handshake failed with the server SslHandshakeError(openssl::ssl::HandshakeError<std::net::TcpStream>), } macro_rules! impl_error { ( $from:ty, $to:ident ) => { impl std::convert::From<$from> for Error { fn from(err: $from) -> Self { Error::$to(err) } } }; } impl_error!(std::io::Error, IOError); impl_error!(serde_json::Error, JSON); impl_error!(bitcoin::hashes::hex::Error, Hex); impl_error!(bitcoin::consensus::encode::Error, Bitcoin);