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