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