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