bf9e9577619a3fd0a1a7fecc3d79cd070bd706e1
[rust-lightning] / lightning-block-sync / src / convert.rs
1 use crate::http::{BinaryResponse, JsonResponse};
2 use crate::utils::hex_to_uint256;
3 use crate::{BlockHeaderData, BlockSourceError};
4
5 use bitcoin::blockdata::block::{Block, BlockHeader};
6 use bitcoin::consensus::encode;
7 use bitcoin::hash_types::{BlockHash, TxMerkleNode, Txid};
8 use bitcoin::hashes::hex::FromHex;
9 use bitcoin::Transaction;
10
11 use serde_json;
12
13 use std::convert::From;
14 use std::convert::TryFrom;
15 use std::convert::TryInto;
16 use std::str::FromStr;
17 use bitcoin::hashes::Hash;
18
19 impl TryInto<serde_json::Value> for JsonResponse {
20         type Error = std::io::Error;
21         fn try_into(self) -> Result<serde_json::Value, std::io::Error> { Ok(self.0) }
22 }
23
24 /// Conversion from `std::io::Error` into `BlockSourceError`.
25 impl From<std::io::Error> for BlockSourceError {
26         fn from(e: std::io::Error) -> BlockSourceError {
27                 match e.kind() {
28                         std::io::ErrorKind::InvalidData => BlockSourceError::persistent(e),
29                         std::io::ErrorKind::InvalidInput => BlockSourceError::persistent(e),
30                         _ => BlockSourceError::transient(e),
31                 }
32         }
33 }
34
35 /// Parses binary data as a block.
36 impl TryInto<Block> for BinaryResponse {
37         type Error = std::io::Error;
38
39         fn try_into(self) -> std::io::Result<Block> {
40                 match encode::deserialize(&self.0) {
41                         Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
42                         Ok(block) => Ok(block),
43                 }
44         }
45 }
46
47 /// Parses binary data as a block hash.
48 impl TryInto<BlockHash> for BinaryResponse {
49         type Error = std::io::Error;
50
51         fn try_into(self) -> std::io::Result<BlockHash> {
52                 BlockHash::from_slice(&self.0).map_err(|_|
53                         std::io::Error::new(std::io::ErrorKind::InvalidData, "bad block hash length")
54                 )
55         }
56 }
57
58 /// Converts a JSON value into block header data. The JSON value may be an object representing a
59 /// block header or an array of such objects. In the latter case, the first object is converted.
60 impl TryInto<BlockHeaderData> for JsonResponse {
61         type Error = std::io::Error;
62
63         fn try_into(self) -> std::io::Result<BlockHeaderData> {
64                 let header = match self.0 {
65                         serde_json::Value::Array(mut array) if !array.is_empty() => array.drain(..).next().unwrap(),
66                         serde_json::Value::Object(_) => self.0,
67                         _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unexpected JSON type")),
68                 };
69
70                 if !header.is_object() {
71                         return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
72                 }
73
74                 // Add an empty previousblockhash for the genesis block.
75                 match header.try_into() {
76                         Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header data")),
77                         Ok(header) => Ok(header),
78                 }
79         }
80 }
81
82 impl TryFrom<serde_json::Value> for BlockHeaderData {
83         type Error = ();
84
85         fn try_from(response: serde_json::Value) -> Result<Self, ()> {
86                 macro_rules! get_field { ($name: expr, $ty_access: tt) => {
87                         response.get($name).ok_or(())?.$ty_access().ok_or(())?
88                 } }
89
90                 Ok(BlockHeaderData {
91                         header: BlockHeader {
92                                 version: get_field!("version", as_i64).try_into().map_err(|_| ())?,
93                                 prev_blockhash: if let Some(hash_str) = response.get("previousblockhash") {
94                                                 BlockHash::from_hex(hash_str.as_str().ok_or(())?).map_err(|_| ())?
95                                         } else { BlockHash::all_zeros() },
96                                 merkle_root: TxMerkleNode::from_hex(get_field!("merkleroot", as_str)).map_err(|_| ())?,
97                                 time: get_field!("time", as_u64).try_into().map_err(|_| ())?,
98                                 bits: u32::from_be_bytes(<[u8; 4]>::from_hex(get_field!("bits", as_str)).map_err(|_| ())?),
99                                 nonce: get_field!("nonce", as_u64).try_into().map_err(|_| ())?,
100                         },
101                         chainwork: hex_to_uint256(get_field!("chainwork", as_str)).map_err(|_| ())?,
102                         height: get_field!("height", as_u64).try_into().map_err(|_| ())?,
103                 })
104         }
105 }
106
107 /// Converts a JSON value into a block. Assumes the block is hex-encoded in a JSON string.
108 impl TryInto<Block> for JsonResponse {
109         type Error = std::io::Error;
110
111         fn try_into(self) -> std::io::Result<Block> {
112                 match self.0.as_str() {
113                         None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
114                         Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
115                                 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
116                                 Ok(block_data) => match encode::deserialize(&block_data) {
117                                         Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
118                                         Ok(block) => Ok(block),
119                                 },
120                         },
121                 }
122         }
123 }
124
125 /// Converts a JSON value into the best block hash and optional height.
126 impl TryInto<(BlockHash, Option<u32>)> for JsonResponse {
127         type Error = std::io::Error;
128
129         fn try_into(self) -> std::io::Result<(BlockHash, Option<u32>)> {
130                 if !self.0.is_object() {
131                         return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
132                 }
133
134                 let hash = match &self.0["bestblockhash"] {
135                         serde_json::Value::String(hex_data) => match BlockHash::from_hex(&hex_data) {
136                                 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
137                                 Ok(block_hash) => block_hash,
138                         },
139                         _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
140                 };
141
142                 let height = match &self.0["blocks"] {
143                         serde_json::Value::Null => None,
144                         serde_json::Value::Number(height) => match height.as_u64() {
145                                 None => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
146                                 Some(height) => match height.try_into() {
147                                         Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
148                                         Ok(height) => Some(height),
149                                 }
150                         },
151                         _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON number")),
152                 };
153
154                 Ok((hash, height))
155         }
156 }
157
158 impl TryInto<Txid> for JsonResponse {
159         type Error = std::io::Error;
160         fn try_into(self) -> std::io::Result<Txid> {
161                 match self.0.as_str() {
162                         None => Err(std::io::Error::new(
163                                 std::io::ErrorKind::InvalidData,
164                                 "expected JSON string",
165                         )),
166                         Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
167                                 Err(_) => Err(std::io::Error::new(
168                                         std::io::ErrorKind::InvalidData,
169                                         "invalid hex data",
170                                 )),
171                                 Ok(txid_data) => match encode::deserialize(&txid_data) {
172                                         Err(_) => Err(std::io::Error::new(
173                                                 std::io::ErrorKind::InvalidData,
174                                                 "invalid txid",
175                                         )),
176                                         Ok(txid) => Ok(txid),
177                                 },
178                         },
179                 }
180         }
181 }
182
183 /// Converts a JSON value into a transaction. WATCH OUT! this cannot be used for zero-input transactions
184 /// (e.g. createrawtransaction). See <https://github.com/rust-bitcoin/rust-bitcoincore-rpc/issues/197>
185 impl TryInto<Transaction> for JsonResponse {
186         type Error = std::io::Error;
187         fn try_into(self) -> std::io::Result<Transaction> {
188                 let hex_tx = if self.0.is_object() {
189                         // result is json encoded
190                         match &self.0["hex"] {
191                                 // result has hex field
192                                 serde_json::Value::String(hex_data) => match self.0["complete"] {
193                                         // result may or may not be signed (e.g. signrawtransactionwithwallet)
194                                         serde_json::Value::Bool(x) => {
195                                                 if x == false {
196                                                         let reason = match &self.0["errors"][0]["error"] {
197                                                                 serde_json::Value::String(x) => x.as_str(),
198                                                                 _ => "Unknown error",
199                                                         };
200
201                                                         return Err(std::io::Error::new(
202                                                                 std::io::ErrorKind::InvalidData,
203                                                                 format!("transaction couldn't be signed. {}", reason),
204                                                         ));
205                                                 } else {
206                                                         hex_data
207                                                 }
208                                         }
209                                         // result is a complete transaction (e.g. getrawtranaction verbose)
210                                         _ => hex_data,
211                                 },
212                                 _ => return Err(std::io::Error::new(
213                                                         std::io::ErrorKind::InvalidData,
214                                                         "expected JSON string",
215                                         )),
216                         }
217                 } else {
218                         // result is plain text (e.g. getrawtransaction no verbose)
219                         match self.0.as_str() {
220                                 Some(hex_tx) => hex_tx,
221                                 None => {
222                                         return Err(std::io::Error::new(
223                                                 std::io::ErrorKind::InvalidData,
224                                                 "expected JSON string",
225                                         ))
226                                 }
227                         }
228                 };
229
230                 match Vec::<u8>::from_hex(hex_tx) {
231                         Err(_) => Err(std::io::Error::new(
232                                 std::io::ErrorKind::InvalidData,
233                                 "invalid hex data",
234                         )),
235                         Ok(tx_data) => match encode::deserialize(&tx_data) {
236                                 Err(_) => Err(std::io::Error::new(
237                                         std::io::ErrorKind::InvalidData,
238                                         "invalid transaction",
239                                 )),
240                                 Ok(tx) => Ok(tx),
241                         },
242                 }
243         }
244 }
245
246 impl TryInto<BlockHash> for JsonResponse {
247         type Error = std::io::Error;
248
249         fn try_into(self) -> std::io::Result<BlockHash> {
250                 match self.0.as_str() {
251                         None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
252                         Some(hex_data) if hex_data.len() != 64 =>
253                                 Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hash length")),
254                         Some(hex_data) => BlockHash::from_str(hex_data)
255                                 .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
256                 }
257         }
258 }
259
260 /// The REST `getutxos` endpoint retuns a whole pile of data we don't care about and one bit we do
261 /// - whether the `hit bitmap` field had any entries. Thus we condense the result down into only
262 /// that.
263 pub(crate) struct GetUtxosResponse {
264         pub(crate) hit_bitmap_nonempty: bool
265 }
266
267 impl TryInto<GetUtxosResponse> for JsonResponse {
268         type Error = std::io::Error;
269
270         fn try_into(self) -> std::io::Result<GetUtxosResponse> {
271                 let bitmap_str =
272                         self.0.as_object().ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected an object"))?
273                         .get("bitmap").ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "missing bitmap field"))?
274                         .as_str().ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "bitmap should be an str"))?;
275                         let mut hit_bitmap_nonempty = false;
276                         for c in bitmap_str.chars() {
277                                 if c < '0' || c > '9' {
278                                         return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid byte"));
279                                 }
280                                 if c > '0' { hit_bitmap_nonempty = true; }
281                         }
282                         Ok(GetUtxosResponse { hit_bitmap_nonempty })
283         }
284 }
285
286 #[cfg(test)]
287 pub(crate) mod tests {
288         use super::*;
289         use bitcoin::blockdata::constants::genesis_block;
290         use bitcoin::hashes::Hash;
291         use bitcoin::hashes::hex::ToHex;
292         use bitcoin::network::constants::Network;
293         use serde_json::value::Number;
294         use serde_json::Value;
295
296         /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
297         impl From<BlockHeaderData> for serde_json::Value {
298                 fn from(data: BlockHeaderData) -> Self {
299                         let BlockHeaderData { chainwork, height, header } = data;
300                         serde_json::json!({
301                                 "chainwork": chainwork.to_string()["0x".len()..],
302                                 "height": height,
303                                 "version": header.version,
304                                 "merkleroot": header.merkle_root.to_hex(),
305                                 "time": header.time,
306                                 "nonce": header.nonce,
307                                 "bits": header.bits.to_hex(),
308                                 "previousblockhash": header.prev_blockhash.to_hex(),
309                         })
310                 }
311         }
312
313         #[test]
314         fn into_block_header_from_json_response_with_unexpected_type() {
315                 let response = JsonResponse(serde_json::json!(42));
316                 match TryInto::<BlockHeaderData>::try_into(response) {
317                         Err(e) => {
318                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
319                                 assert_eq!(e.get_ref().unwrap().to_string(), "unexpected JSON type");
320                         },
321                         Ok(_) => panic!("Expected error"),
322                 }
323         }
324
325         #[test]
326         fn into_block_header_from_json_response_with_unexpected_header_type() {
327                 let response = JsonResponse(serde_json::json!([42]));
328                 match TryInto::<BlockHeaderData>::try_into(response) {
329                         Err(e) => {
330                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
331                                 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
332                         },
333                         Ok(_) => panic!("Expected error"),
334                 }
335         }
336
337         #[test]
338         fn into_block_header_from_json_response_with_invalid_header_response() {
339                 let block = genesis_block(Network::Bitcoin);
340                 let mut response = JsonResponse(BlockHeaderData {
341                         chainwork: block.header.work(),
342                         height: 0,
343                         header: block.header
344                 }.into());
345                 response.0["chainwork"].take();
346
347                 match TryInto::<BlockHeaderData>::try_into(response) {
348                         Err(e) => {
349                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
350                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
351                         },
352                         Ok(_) => panic!("Expected error"),
353                 }
354         }
355
356         #[test]
357         fn into_block_header_from_json_response_with_invalid_header_data() {
358                 let block = genesis_block(Network::Bitcoin);
359                 let mut response = JsonResponse(BlockHeaderData {
360                         chainwork: block.header.work(),
361                         height: 0,
362                         header: block.header
363                 }.into());
364                 response.0["chainwork"] = serde_json::json!("foobar");
365
366                 match TryInto::<BlockHeaderData>::try_into(response) {
367                         Err(e) => {
368                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
369                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
370                         },
371                         Ok(_) => panic!("Expected error"),
372                 }
373         }
374
375         #[test]
376         fn into_block_header_from_json_response_with_valid_header() {
377                 let block = genesis_block(Network::Bitcoin);
378                 let response = JsonResponse(BlockHeaderData {
379                         chainwork: block.header.work(),
380                         height: 0,
381                         header: block.header
382                 }.into());
383
384                 match TryInto::<BlockHeaderData>::try_into(response) {
385                         Err(e) => panic!("Unexpected error: {:?}", e),
386                         Ok(data) => {
387                                 assert_eq!(data.chainwork, block.header.work());
388                                 assert_eq!(data.height, 0);
389                                 assert_eq!(data.header, block.header);
390                         },
391                 }
392         }
393
394         #[test]
395         fn into_block_header_from_json_response_with_valid_header_array() {
396                 let genesis_block = genesis_block(Network::Bitcoin);
397                 let best_block_header = BlockHeader {
398                         prev_blockhash: genesis_block.block_hash(),
399                         ..genesis_block.header
400                 };
401                 let chainwork = genesis_block.header.work() + best_block_header.work();
402                 let response = JsonResponse(serde_json::json!([
403                                 serde_json::Value::from(BlockHeaderData {
404                                         chainwork, height: 1, header: best_block_header,
405                                 }),
406                                 serde_json::Value::from(BlockHeaderData {
407                                         chainwork: genesis_block.header.work(), height: 0, header: genesis_block.header,
408                                 }),
409                 ]));
410
411                 match TryInto::<BlockHeaderData>::try_into(response) {
412                         Err(e) => panic!("Unexpected error: {:?}", e),
413                         Ok(data) => {
414                                 assert_eq!(data.chainwork, chainwork);
415                                 assert_eq!(data.height, 1);
416                                 assert_eq!(data.header, best_block_header);
417                         },
418                 }
419         }
420
421         #[test]
422         fn into_block_header_from_json_response_without_previous_block_hash() {
423                 let block = genesis_block(Network::Bitcoin);
424                 let mut response = JsonResponse(BlockHeaderData {
425                         chainwork: block.header.work(),
426                         height: 0,
427                         header: block.header
428                 }.into());
429                 response.0.as_object_mut().unwrap().remove("previousblockhash");
430
431                 match TryInto::<BlockHeaderData>::try_into(response) {
432                         Err(e) => panic!("Unexpected error: {:?}", e),
433                         Ok(BlockHeaderData { chainwork: _, height: _, header }) => {
434                                 assert_eq!(header, block.header);
435                         },
436                 }
437         }
438
439         #[test]
440         fn into_block_from_invalid_binary_response() {
441                 let response = BinaryResponse(b"foo".to_vec());
442                 match TryInto::<Block>::try_into(response) {
443                         Err(_) => {},
444                         Ok(_) => panic!("Expected error"),
445                 }
446         }
447
448         #[test]
449         fn into_block_from_valid_binary_response() {
450                 let genesis_block = genesis_block(Network::Bitcoin);
451                 let response = BinaryResponse(encode::serialize(&genesis_block));
452                 match TryInto::<Block>::try_into(response) {
453                         Err(e) => panic!("Unexpected error: {:?}", e),
454                         Ok(block) => assert_eq!(block, genesis_block),
455                 }
456         }
457
458         #[test]
459         fn into_block_from_json_response_with_unexpected_type() {
460                 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
461                 match TryInto::<Block>::try_into(response) {
462                         Err(e) => {
463                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
464                                 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
465                         },
466                         Ok(_) => panic!("Expected error"),
467                 }
468         }
469
470         #[test]
471         fn into_block_from_json_response_with_invalid_hex_data() {
472                 let response = JsonResponse(serde_json::json!("foobar"));
473                 match TryInto::<Block>::try_into(response) {
474                         Err(e) => {
475                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
476                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
477                         },
478                         Ok(_) => panic!("Expected error"),
479                 }
480         }
481
482         #[test]
483         fn into_block_from_json_response_with_invalid_block_data() {
484                 let response = JsonResponse(serde_json::json!("abcd"));
485                 match TryInto::<Block>::try_into(response) {
486                         Err(e) => {
487                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
488                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid block data");
489                         },
490                         Ok(_) => panic!("Expected error"),
491                 }
492         }
493
494         #[test]
495         fn into_block_from_json_response_with_valid_block_data() {
496                 let genesis_block = genesis_block(Network::Bitcoin);
497                 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&genesis_block)));
498                 match TryInto::<Block>::try_into(response) {
499                         Err(e) => panic!("Unexpected error: {:?}", e),
500                         Ok(block) => assert_eq!(block, genesis_block),
501                 }
502         }
503
504         #[test]
505         fn into_block_hash_from_json_response_with_unexpected_type() {
506                 let response = JsonResponse(serde_json::json!("foo"));
507                 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
508                         Err(e) => {
509                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
510                                 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
511                         },
512                         Ok(_) => panic!("Expected error"),
513                 }
514         }
515
516         #[test]
517         fn into_block_hash_from_json_response_with_unexpected_bestblockhash_type() {
518                 let response = JsonResponse(serde_json::json!({ "bestblockhash": 42 }));
519                 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
520                         Err(e) => {
521                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
522                                 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
523                         },
524                         Ok(_) => panic!("Expected error"),
525                 }
526         }
527
528         #[test]
529         fn into_block_hash_from_json_response_with_invalid_hex_data() {
530                 let response = JsonResponse(serde_json::json!({ "bestblockhash": "foobar"} ));
531                 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
532                         Err(e) => {
533                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
534                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
535                         },
536                         Ok(_) => panic!("Expected error"),
537                 }
538         }
539
540         #[test]
541         fn into_block_hash_from_json_response_without_height() {
542                 let block = genesis_block(Network::Bitcoin);
543                 let response = JsonResponse(serde_json::json!({
544                         "bestblockhash": block.block_hash().to_hex(),
545                 }));
546                 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
547                         Err(e) => panic!("Unexpected error: {:?}", e),
548                         Ok((hash, height)) => {
549                                 assert_eq!(hash, block.block_hash());
550                                 assert!(height.is_none());
551                         },
552                 }
553         }
554
555         #[test]
556         fn into_block_hash_from_json_response_with_unexpected_blocks_type() {
557                 let block = genesis_block(Network::Bitcoin);
558                 let response = JsonResponse(serde_json::json!({
559                         "bestblockhash": block.block_hash().to_hex(),
560                         "blocks": "foo",
561                 }));
562                 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
563                         Err(e) => {
564                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
565                                 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON number");
566                         },
567                         Ok(_) => panic!("Expected error"),
568                 }
569         }
570
571         #[test]
572         fn into_block_hash_from_json_response_with_invalid_height() {
573                 let block = genesis_block(Network::Bitcoin);
574                 let response = JsonResponse(serde_json::json!({
575                         "bestblockhash": block.block_hash().to_hex(),
576                         "blocks": std::u64::MAX,
577                 }));
578                 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
579                         Err(e) => {
580                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
581                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid height");
582                         },
583                         Ok(_) => panic!("Expected error"),
584                 }
585         }
586
587         #[test]
588         fn into_block_hash_from_json_response_with_height() {
589                 let block = genesis_block(Network::Bitcoin);
590                 let response = JsonResponse(serde_json::json!({
591                         "bestblockhash": block.block_hash().to_hex(),
592                         "blocks": 1,
593                 }));
594                 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
595                         Err(e) => panic!("Unexpected error: {:?}", e),
596                         Ok((hash, height)) => {
597                                 assert_eq!(hash, block.block_hash());
598                                 assert_eq!(height.unwrap(), 1);
599                         },
600                 }
601         }
602
603         #[test]
604         fn into_txid_from_json_response_with_unexpected_type() {
605                 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
606                 match TryInto::<Txid>::try_into(response) {
607                         Err(e) => {
608                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
609                                 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
610                         }
611                         Ok(_) => panic!("Expected error"),
612                 }
613         }
614
615         #[test]
616         fn into_txid_from_json_response_with_invalid_hex_data() {
617                 let response = JsonResponse(serde_json::json!("foobar"));
618                 match TryInto::<Txid>::try_into(response) {
619                         Err(e) => {
620                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
621                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
622                         }
623                         Ok(_) => panic!("Expected error"),
624                 }
625         }
626
627         #[test]
628         fn into_txid_from_json_response_with_invalid_txid_data() {
629                 let response = JsonResponse(serde_json::json!("abcd"));
630                 match TryInto::<Txid>::try_into(response) {
631                         Err(e) => {
632                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
633                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid txid");
634                         }
635                         Ok(_) => panic!("Expected error"),
636                 }
637         }
638
639         #[test]
640         fn into_txid_from_json_response_with_valid_txid_data() {
641                 let target_txid = Txid::from_slice(&[1; 32]).unwrap();
642                 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_txid)));
643                 match TryInto::<Txid>::try_into(response) {
644                         Err(e) => panic!("Unexpected error: {:?}", e),
645                         Ok(txid) => assert_eq!(txid, target_txid),
646                 }
647         }
648
649         // TryInto<Transaction> can be used in two ways, first with plain hex response where data is
650         // the hex encoded transaction (e.g. as a result of getrawtransaction) or as a JSON object
651         // where the hex encoded transaction can be found in the hex field of the object (if present)
652         // (e.g. as a result of signrawtransactionwithwallet).
653
654         // plain hex transaction
655
656         #[test]
657         fn into_tx_from_json_response_with_invalid_hex_data() {
658                 let response = JsonResponse(serde_json::json!("foobar"));
659                 match TryInto::<Transaction>::try_into(response) {
660                         Err(e) => {
661                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
662                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
663                         }
664                         Ok(_) => panic!("Expected error"),
665                 }
666         }
667
668         #[test]
669         fn into_tx_from_json_response_with_invalid_data_type() {
670                 let response = JsonResponse(Value::Number(Number::from_f64(1.0).unwrap()));
671                 match TryInto::<Transaction>::try_into(response) {
672                         Err(e) => {
673                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
674                                 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
675                         }
676                         Ok(_) => panic!("Expected error"),
677                 }
678         }
679
680         #[test]
681         fn into_tx_from_json_response_with_invalid_tx_data() {
682                 let response = JsonResponse(serde_json::json!("abcd"));
683                 match TryInto::<Transaction>::try_into(response) {
684                         Err(e) => {
685                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
686                                 assert_eq!(e.get_ref().unwrap().to_string(), "invalid transaction");
687                         }
688                         Ok(_) => panic!("Expected error"),
689                 }
690         }
691
692         #[test]
693         fn into_tx_from_json_response_with_valid_tx_data_plain() {
694                 let genesis_block = genesis_block(Network::Bitcoin);
695                 let target_tx = genesis_block.txdata.get(0).unwrap();
696                 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_tx)));
697                 match TryInto::<Transaction>::try_into(response) {
698                         Err(e) => panic!("Unexpected error: {:?}", e),
699                         Ok(tx) => assert_eq!(&tx, target_tx),
700                 }
701         }
702
703         #[test]
704         fn into_tx_from_json_response_with_valid_tx_data_hex_field() {
705                 let genesis_block = genesis_block(Network::Bitcoin);
706                 let target_tx = genesis_block.txdata.get(0).unwrap();
707                 let response = JsonResponse(serde_json::json!({"hex": encode::serialize_hex(&target_tx)}));
708                 match TryInto::<Transaction>::try_into(response) {
709                         Err(e) => panic!("Unexpected error: {:?}", e),
710                         Ok(tx) => assert_eq!(&tx, target_tx),
711                 }
712         }
713
714         // transaction in hex field of JSON object
715
716         #[test]
717         fn into_tx_from_json_response_with_no_hex_field() {
718                 let response = JsonResponse(serde_json::json!({ "error": "foo" }));
719                 match TryInto::<Transaction>::try_into(response) {
720                         Err(e) => {
721                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
722                                 assert_eq!(
723                                         e.get_ref().unwrap().to_string(),
724                                         "expected JSON string"
725                                 );
726                         }
727                         Ok(_) => panic!("Expected error"),
728                 }
729         }
730
731         #[test]
732         fn into_tx_from_json_response_not_signed() {
733                 let response = JsonResponse(serde_json::json!({ "hex": "foo", "complete": false }));
734                 match TryInto::<Transaction>::try_into(response) {
735                         Err(e) => {
736                                 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
737                                 assert!(
738                                         e.get_ref().unwrap().to_string().contains(
739                                         "transaction couldn't be signed")
740                                 );
741                         }
742                         Ok(_) => panic!("Expected error"),
743                 }
744         }
745 }