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