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