1 use crate::http::{BinaryResponse, JsonResponse};
2 use crate::utils::hex_to_work;
3 use crate::{BlockHeaderData, BlockSourceError};
5 use bitcoin::blockdata::block::{Block, Header};
6 use bitcoin::consensus::encode;
7 use bitcoin::hash_types::{BlockHash, TxMerkleNode, Txid};
8 use bitcoin::hashes::hex::FromHex;
9 use bitcoin::Transaction;
13 use std::convert::From;
14 use std::convert::TryFrom;
15 use std::convert::TryInto;
16 use std::str::FromStr;
17 use bitcoin::hashes::Hash;
19 impl TryInto<serde_json::Value> for JsonResponse {
20 type Error = std::io::Error;
21 fn try_into(self) -> Result<serde_json::Value, std::io::Error> { Ok(self.0) }
24 /// Conversion from `std::io::Error` into `BlockSourceError`.
25 impl From<std::io::Error> for BlockSourceError {
26 fn from(e: std::io::Error) -> BlockSourceError {
28 std::io::ErrorKind::InvalidData => BlockSourceError::persistent(e),
29 std::io::ErrorKind::InvalidInput => BlockSourceError::persistent(e),
30 _ => BlockSourceError::transient(e),
35 /// Parses binary data as a block.
36 impl TryInto<Block> for BinaryResponse {
37 type Error = std::io::Error;
39 fn try_into(self) -> std::io::Result<Block> {
40 match encode::deserialize(&self.0) {
41 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
42 Ok(block) => Ok(block),
47 /// Parses binary data as a block hash.
48 impl TryInto<BlockHash> for BinaryResponse {
49 type Error = std::io::Error;
51 fn try_into(self) -> std::io::Result<BlockHash> {
52 BlockHash::from_slice(&self.0).map_err(|_|
53 std::io::Error::new(std::io::ErrorKind::InvalidData, "bad block hash length")
58 /// Converts a JSON value into block header data. The JSON value may be an object representing a
59 /// block header or an array of such objects. In the latter case, the first object is converted.
60 impl TryInto<BlockHeaderData> for JsonResponse {
61 type Error = std::io::Error;
63 fn try_into(self) -> std::io::Result<BlockHeaderData> {
64 let header = match self.0 {
65 serde_json::Value::Array(mut array) if !array.is_empty() => array.drain(..).next().unwrap(),
66 serde_json::Value::Object(_) => self.0,
67 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unexpected JSON type")),
70 if !header.is_object() {
71 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
74 // Add an empty previousblockhash for the genesis block.
75 match header.try_into() {
76 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header data")),
77 Ok(header) => Ok(header),
82 impl TryFrom<serde_json::Value> for BlockHeaderData {
85 fn try_from(response: serde_json::Value) -> Result<Self, ()> {
86 macro_rules! get_field { ($name: expr, $ty_access: tt) => {
87 response.get($name).ok_or(())?.$ty_access().ok_or(())?
92 version: bitcoin::blockdata::block::Version::from_consensus(
93 get_field!("version", as_i64).try_into().map_err(|_| ())?
95 prev_blockhash: if let Some(hash_str) = response.get("previousblockhash") {
96 BlockHash::from_str(hash_str.as_str().ok_or(())?).map_err(|_| ())?
97 } else { BlockHash::all_zeros() },
98 merkle_root: TxMerkleNode::from_str(get_field!("merkleroot", as_str)).map_err(|_| ())?,
99 time: get_field!("time", as_u64).try_into().map_err(|_| ())?,
100 bits: bitcoin::CompactTarget::from_consensus(
101 u32::from_be_bytes(<[u8; 4]>::from_hex(get_field!("bits", as_str)).map_err(|_| ())?)
103 nonce: get_field!("nonce", as_u64).try_into().map_err(|_| ())?,
105 chainwork: hex_to_work(get_field!("chainwork", as_str)).map_err(|_| ())?,
106 height: get_field!("height", as_u64).try_into().map_err(|_| ())?,
111 /// Converts a JSON value into a block. Assumes the block is hex-encoded in a JSON string.
112 impl TryInto<Block> for JsonResponse {
113 type Error = std::io::Error;
115 fn try_into(self) -> std::io::Result<Block> {
116 match self.0.as_str() {
117 None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
118 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
119 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
120 Ok(block_data) => match encode::deserialize(&block_data) {
121 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
122 Ok(block) => Ok(block),
129 /// Converts a JSON value into the best block hash and optional height.
130 impl TryInto<(BlockHash, Option<u32>)> for JsonResponse {
131 type Error = std::io::Error;
133 fn try_into(self) -> std::io::Result<(BlockHash, Option<u32>)> {
134 if !self.0.is_object() {
135 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
138 let hash = match &self.0["bestblockhash"] {
139 serde_json::Value::String(hex_data) => match BlockHash::from_str(&hex_data) {
140 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
141 Ok(block_hash) => block_hash,
143 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
146 let height = match &self.0["blocks"] {
147 serde_json::Value::Null => None,
148 serde_json::Value::Number(height) => match height.as_u64() {
149 None => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
150 Some(height) => match height.try_into() {
151 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
152 Ok(height) => Some(height),
155 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON number")),
162 impl TryInto<Txid> for JsonResponse {
163 type Error = std::io::Error;
164 fn try_into(self) -> std::io::Result<Txid> {
165 match self.0.as_str() {
166 None => Err(std::io::Error::new(
167 std::io::ErrorKind::InvalidData,
168 "expected JSON string",
170 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
171 Err(_) => Err(std::io::Error::new(
172 std::io::ErrorKind::InvalidData,
175 Ok(txid_data) => match encode::deserialize(&txid_data) {
176 Err(_) => Err(std::io::Error::new(
177 std::io::ErrorKind::InvalidData,
180 Ok(txid) => Ok(txid),
187 /// Converts a JSON value into a transaction. WATCH OUT! this cannot be used for zero-input transactions
188 /// (e.g. createrawtransaction). See <https://github.com/rust-bitcoin/rust-bitcoincore-rpc/issues/197>
189 impl TryInto<Transaction> for JsonResponse {
190 type Error = std::io::Error;
191 fn try_into(self) -> std::io::Result<Transaction> {
192 let hex_tx = if self.0.is_object() {
193 // result is json encoded
194 match &self.0["hex"] {
195 // result has hex field
196 serde_json::Value::String(hex_data) => match self.0["complete"] {
197 // result may or may not be signed (e.g. signrawtransactionwithwallet)
198 serde_json::Value::Bool(x) => {
200 let reason = match &self.0["errors"][0]["error"] {
201 serde_json::Value::String(x) => x.as_str(),
202 _ => "Unknown error",
205 return Err(std::io::Error::new(
206 std::io::ErrorKind::InvalidData,
207 format!("transaction couldn't be signed. {}", reason),
213 // result is a complete transaction (e.g. getrawtranaction verbose)
216 _ => return Err(std::io::Error::new(
217 std::io::ErrorKind::InvalidData,
218 "expected JSON string",
222 // result is plain text (e.g. getrawtransaction no verbose)
223 match self.0.as_str() {
224 Some(hex_tx) => hex_tx,
226 return Err(std::io::Error::new(
227 std::io::ErrorKind::InvalidData,
228 "expected JSON string",
234 match Vec::<u8>::from_hex(hex_tx) {
235 Err(_) => Err(std::io::Error::new(
236 std::io::ErrorKind::InvalidData,
239 Ok(tx_data) => match encode::deserialize(&tx_data) {
240 Err(_) => Err(std::io::Error::new(
241 std::io::ErrorKind::InvalidData,
242 "invalid transaction",
250 impl TryInto<BlockHash> for JsonResponse {
251 type Error = std::io::Error;
253 fn try_into(self) -> std::io::Result<BlockHash> {
254 match self.0.as_str() {
255 None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
256 Some(hex_data) if hex_data.len() != 64 =>
257 Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hash length")),
258 Some(hex_data) => BlockHash::from_str(hex_data)
259 .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
264 /// The REST `getutxos` endpoint retuns a whole pile of data we don't care about and one bit we do
265 /// - whether the `hit bitmap` field had any entries. Thus we condense the result down into only
267 pub(crate) struct GetUtxosResponse {
268 pub(crate) hit_bitmap_nonempty: bool
271 impl TryInto<GetUtxosResponse> for JsonResponse {
272 type Error = std::io::Error;
274 fn try_into(self) -> std::io::Result<GetUtxosResponse> {
276 self.0.as_object().ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected an object"))?
277 .get("bitmap").ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "missing bitmap field"))?
278 .as_str().ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "bitmap should be an str"))?;
279 let mut hit_bitmap_nonempty = false;
280 for c in bitmap_str.chars() {
281 if c < '0' || c > '9' {
282 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid byte"));
284 if c > '0' { hit_bitmap_nonempty = true; }
286 Ok(GetUtxosResponse { hit_bitmap_nonempty })
291 pub(crate) mod tests {
293 use bitcoin::blockdata::constants::genesis_block;
294 use bitcoin::hashes::Hash;
295 use bitcoin::network::constants::Network;
297 use serde_json::value::Number;
298 use serde_json::Value;
300 /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
301 impl From<BlockHeaderData> for serde_json::Value {
302 fn from(data: BlockHeaderData) -> Self {
303 let BlockHeaderData { chainwork, height, header } = data;
305 "chainwork": chainwork.to_be_bytes().as_hex().to_string(),
307 "version": header.version.to_consensus(),
308 "merkleroot": header.merkle_root.to_string(),
310 "nonce": header.nonce,
311 "bits": header.bits.to_consensus().to_be_bytes().as_hex().to_string(),
312 "previousblockhash": header.prev_blockhash.to_string(),
318 fn into_block_header_from_json_response_with_unexpected_type() {
319 let response = JsonResponse(serde_json::json!(42));
320 match TryInto::<BlockHeaderData>::try_into(response) {
322 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
323 assert_eq!(e.get_ref().unwrap().to_string(), "unexpected JSON type");
325 Ok(_) => panic!("Expected error"),
330 fn into_block_header_from_json_response_with_unexpected_header_type() {
331 let response = JsonResponse(serde_json::json!([42]));
332 match TryInto::<BlockHeaderData>::try_into(response) {
334 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
335 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
337 Ok(_) => panic!("Expected error"),
342 fn into_block_header_from_json_response_with_invalid_header_response() {
343 let block = genesis_block(Network::Bitcoin);
344 let mut response = JsonResponse(BlockHeaderData {
345 chainwork: block.header.work(),
349 response.0["chainwork"].take();
351 match TryInto::<BlockHeaderData>::try_into(response) {
353 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
354 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
356 Ok(_) => panic!("Expected error"),
361 fn into_block_header_from_json_response_with_invalid_header_data() {
362 let block = genesis_block(Network::Bitcoin);
363 let mut response = JsonResponse(BlockHeaderData {
364 chainwork: block.header.work(),
368 response.0["chainwork"] = serde_json::json!("foobar");
370 match TryInto::<BlockHeaderData>::try_into(response) {
372 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
373 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
375 Ok(_) => panic!("Expected error"),
380 fn into_block_header_from_json_response_with_valid_header() {
381 let block = genesis_block(Network::Bitcoin);
382 let response = JsonResponse(BlockHeaderData {
383 chainwork: block.header.work(),
388 match TryInto::<BlockHeaderData>::try_into(response) {
389 Err(e) => panic!("Unexpected error: {:?}", e),
391 assert_eq!(data.chainwork, block.header.work());
392 assert_eq!(data.height, 0);
393 assert_eq!(data.header, block.header);
399 fn into_block_header_from_json_response_with_valid_header_array() {
400 let genesis_block = genesis_block(Network::Bitcoin);
401 let best_block_header = Header {
402 prev_blockhash: genesis_block.block_hash(),
403 ..genesis_block.header
405 let chainwork = genesis_block.header.work() + best_block_header.work();
406 let response = JsonResponse(serde_json::json!([
407 serde_json::Value::from(BlockHeaderData {
408 chainwork, height: 1, header: best_block_header,
410 serde_json::Value::from(BlockHeaderData {
411 chainwork: genesis_block.header.work(), height: 0, header: genesis_block.header,
415 match TryInto::<BlockHeaderData>::try_into(response) {
416 Err(e) => panic!("Unexpected error: {:?}", e),
418 assert_eq!(data.chainwork, chainwork);
419 assert_eq!(data.height, 1);
420 assert_eq!(data.header, best_block_header);
426 fn into_block_header_from_json_response_without_previous_block_hash() {
427 let block = genesis_block(Network::Bitcoin);
428 let mut response = JsonResponse(BlockHeaderData {
429 chainwork: block.header.work(),
433 response.0.as_object_mut().unwrap().remove("previousblockhash");
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);
444 fn into_block_from_invalid_binary_response() {
445 let response = BinaryResponse(b"foo".to_vec());
446 match TryInto::<Block>::try_into(response) {
448 Ok(_) => panic!("Expected error"),
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),
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) {
467 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
468 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
470 Ok(_) => panic!("Expected error"),
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) {
479 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
480 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
482 Ok(_) => panic!("Expected error"),
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) {
491 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
492 assert_eq!(e.get_ref().unwrap().to_string(), "invalid block data");
494 Ok(_) => panic!("Expected error"),
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),
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) {
513 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
514 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
516 Ok(_) => panic!("Expected error"),
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) {
525 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
526 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
528 Ok(_) => panic!("Expected error"),
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) {
537 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
538 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
540 Ok(_) => panic!("Expected error"),
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(),
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());
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(),
566 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
568 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
569 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON number");
571 Ok(_) => panic!("Expected error"),
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,
582 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
584 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
585 assert_eq!(e.get_ref().unwrap().to_string(), "invalid height");
587 Ok(_) => panic!("Expected error"),
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(),
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);
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) {
612 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
613 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
615 Ok(_) => panic!("Expected error"),
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) {
624 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
625 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
627 Ok(_) => panic!("Expected error"),
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) {
636 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
637 assert_eq!(e.get_ref().unwrap().to_string(), "invalid txid");
639 Ok(_) => panic!("Expected error"),
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),
653 // TryInto<Transaction> can be used in two ways, first with plain hex response where data is
654 // the hex encoded transaction (e.g. as a result of getrawtransaction) or as a JSON object
655 // where the hex encoded transaction can be found in the hex field of the object (if present)
656 // (e.g. as a result of signrawtransactionwithwallet).
658 // plain hex transaction
661 fn into_tx_from_json_response_with_invalid_hex_data() {
662 let response = JsonResponse(serde_json::json!("foobar"));
663 match TryInto::<Transaction>::try_into(response) {
665 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
666 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
668 Ok(_) => panic!("Expected error"),
673 fn into_tx_from_json_response_with_invalid_data_type() {
674 let response = JsonResponse(Value::Number(Number::from_f64(1.0).unwrap()));
675 match TryInto::<Transaction>::try_into(response) {
677 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
678 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
680 Ok(_) => panic!("Expected error"),
685 fn into_tx_from_json_response_with_invalid_tx_data() {
686 let response = JsonResponse(serde_json::json!("abcd"));
687 match TryInto::<Transaction>::try_into(response) {
689 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
690 assert_eq!(e.get_ref().unwrap().to_string(), "invalid transaction");
692 Ok(_) => panic!("Expected error"),
697 fn into_tx_from_json_response_with_valid_tx_data_plain() {
698 let genesis_block = genesis_block(Network::Bitcoin);
699 let target_tx = genesis_block.txdata.get(0).unwrap();
700 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_tx)));
701 match TryInto::<Transaction>::try_into(response) {
702 Err(e) => panic!("Unexpected error: {:?}", e),
703 Ok(tx) => assert_eq!(&tx, target_tx),
708 fn into_tx_from_json_response_with_valid_tx_data_hex_field() {
709 let genesis_block = genesis_block(Network::Bitcoin);
710 let target_tx = genesis_block.txdata.get(0).unwrap();
711 let response = JsonResponse(serde_json::json!({"hex": encode::serialize_hex(&target_tx)}));
712 match TryInto::<Transaction>::try_into(response) {
713 Err(e) => panic!("Unexpected error: {:?}", e),
714 Ok(tx) => assert_eq!(&tx, target_tx),
718 // transaction in hex field of JSON object
721 fn into_tx_from_json_response_with_no_hex_field() {
722 let response = JsonResponse(serde_json::json!({ "error": "foo" }));
723 match TryInto::<Transaction>::try_into(response) {
725 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
727 e.get_ref().unwrap().to_string(),
728 "expected JSON string"
731 Ok(_) => panic!("Expected error"),
736 fn into_tx_from_json_response_not_signed() {
737 let response = JsonResponse(serde_json::json!({ "hex": "foo", "complete": false }));
738 match TryInto::<Transaction>::try_into(response) {
740 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
742 e.get_ref().unwrap().to_string().contains(
743 "transaction couldn't be signed")
746 Ok(_) => panic!("Expected error"),