1 use crate::http::{BinaryResponse, JsonResponse};
2 use crate::utils::hex_to_uint256;
3 use crate::{BlockHeaderData, BlockSourceError};
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;
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: get_field!("version", as_i64).try_into().map_err(|_| ())?,
93 prev_blockhash: if let Some(hash_str) = response.get("previousblockhash") {
94 BlockHash::from_hex(hash_str.as_str().ok_or(())?).map_err(|_| ())?
95 } else { BlockHash::all_zeros() },
96 merkle_root: TxMerkleNode::from_hex(get_field!("merkleroot", as_str)).map_err(|_| ())?,
97 time: get_field!("time", as_u64).try_into().map_err(|_| ())?,
98 bits: u32::from_be_bytes(<[u8; 4]>::from_hex(get_field!("bits", as_str)).map_err(|_| ())?),
99 nonce: get_field!("nonce", as_u64).try_into().map_err(|_| ())?,
101 chainwork: hex_to_uint256(get_field!("chainwork", as_str)).map_err(|_| ())?,
102 height: get_field!("height", as_u64).try_into().map_err(|_| ())?,
107 /// Converts a JSON value into a block. Assumes the block is hex-encoded in a JSON string.
108 impl TryInto<Block> for JsonResponse {
109 type Error = std::io::Error;
111 fn try_into(self) -> std::io::Result<Block> {
112 match self.0.as_str() {
113 None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
114 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
115 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
116 Ok(block_data) => match encode::deserialize(&block_data) {
117 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
118 Ok(block) => Ok(block),
125 /// Converts a JSON value into the best block hash and optional height.
126 impl TryInto<(BlockHash, Option<u32>)> for JsonResponse {
127 type Error = std::io::Error;
129 fn try_into(self) -> std::io::Result<(BlockHash, Option<u32>)> {
130 if !self.0.is_object() {
131 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
134 let hash = match &self.0["bestblockhash"] {
135 serde_json::Value::String(hex_data) => match BlockHash::from_hex(&hex_data) {
136 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
137 Ok(block_hash) => block_hash,
139 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
142 let height = match &self.0["blocks"] {
143 serde_json::Value::Null => None,
144 serde_json::Value::Number(height) => match height.as_u64() {
145 None => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
146 Some(height) => match height.try_into() {
147 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
148 Ok(height) => Some(height),
151 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON number")),
158 impl TryInto<Txid> for JsonResponse {
159 type Error = std::io::Error;
160 fn try_into(self) -> std::io::Result<Txid> {
161 match self.0.as_str() {
162 None => Err(std::io::Error::new(
163 std::io::ErrorKind::InvalidData,
164 "expected JSON string",
166 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
167 Err(_) => Err(std::io::Error::new(
168 std::io::ErrorKind::InvalidData,
171 Ok(txid_data) => match encode::deserialize(&txid_data) {
172 Err(_) => Err(std::io::Error::new(
173 std::io::ErrorKind::InvalidData,
176 Ok(txid) => Ok(txid),
183 /// Converts a JSON value into a transaction. WATCH OUT! this cannot be used for zero-input transactions
184 /// (e.g. createrawtransaction). See <https://github.com/rust-bitcoin/rust-bitcoincore-rpc/issues/197>
185 impl TryInto<Transaction> for JsonResponse {
186 type Error = std::io::Error;
187 fn try_into(self) -> std::io::Result<Transaction> {
188 let hex_tx = if self.0.is_object() {
189 // result is json encoded
190 match &self.0["hex"] {
191 // result has hex field
192 serde_json::Value::String(hex_data) => match self.0["complete"] {
193 // result may or may not be signed (e.g. signrawtransactionwithwallet)
194 serde_json::Value::Bool(x) => {
196 let reason = match &self.0["errors"][0]["error"] {
197 serde_json::Value::String(x) => x.as_str(),
198 _ => "Unknown error",
201 return Err(std::io::Error::new(
202 std::io::ErrorKind::InvalidData,
203 format!("transaction couldn't be signed. {}", reason),
209 // result is a complete transaction (e.g. getrawtranaction verbose)
212 _ => return Err(std::io::Error::new(
213 std::io::ErrorKind::InvalidData,
214 "expected JSON string",
218 // result is plain text (e.g. getrawtransaction no verbose)
219 match self.0.as_str() {
220 Some(hex_tx) => hex_tx,
222 return Err(std::io::Error::new(
223 std::io::ErrorKind::InvalidData,
224 "expected JSON string",
230 match Vec::<u8>::from_hex(hex_tx) {
231 Err(_) => Err(std::io::Error::new(
232 std::io::ErrorKind::InvalidData,
235 Ok(tx_data) => match encode::deserialize(&tx_data) {
236 Err(_) => Err(std::io::Error::new(
237 std::io::ErrorKind::InvalidData,
238 "invalid transaction",
246 impl TryInto<BlockHash> for JsonResponse {
247 type Error = std::io::Error;
249 fn try_into(self) -> std::io::Result<BlockHash> {
250 match self.0.as_str() {
251 None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
252 Some(hex_data) if hex_data.len() != 64 =>
253 Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hash length")),
254 Some(hex_data) => BlockHash::from_str(hex_data)
255 .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
260 /// The REST `getutxos` endpoint retuns a whole pile of data we don't care about and one bit we do
261 /// - whether the `hit bitmap` field had any entries. Thus we condense the result down into only
263 pub(crate) struct GetUtxosResponse {
264 pub(crate) hit_bitmap_nonempty: bool
267 impl TryInto<GetUtxosResponse> for JsonResponse {
268 type Error = std::io::Error;
270 fn try_into(self) -> std::io::Result<GetUtxosResponse> {
272 self.0.as_object().ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected an object"))?
273 .get("bitmap").ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "missing bitmap field"))?
274 .as_str().ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "bitmap should be an str"))?;
275 let mut hit_bitmap_nonempty = false;
276 for c in bitmap_str.chars() {
277 if c < '0' || c > '9' {
278 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid byte"));
280 if c > '0' { hit_bitmap_nonempty = true; }
282 Ok(GetUtxosResponse { hit_bitmap_nonempty })
287 pub(crate) mod tests {
289 use bitcoin::blockdata::constants::genesis_block;
290 use bitcoin::hashes::Hash;
291 use bitcoin::hashes::hex::ToHex;
292 use bitcoin::network::constants::Network;
293 use serde_json::value::Number;
294 use serde_json::Value;
296 /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
297 impl From<BlockHeaderData> for serde_json::Value {
298 fn from(data: BlockHeaderData) -> Self {
299 let BlockHeaderData { chainwork, height, header } = data;
301 "chainwork": chainwork.to_string()["0x".len()..],
303 "version": header.version,
304 "merkleroot": header.merkle_root.to_hex(),
306 "nonce": header.nonce,
307 "bits": header.bits.to_hex(),
308 "previousblockhash": header.prev_blockhash.to_hex(),
314 fn into_block_header_from_json_response_with_unexpected_type() {
315 let response = JsonResponse(serde_json::json!(42));
316 match TryInto::<BlockHeaderData>::try_into(response) {
318 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
319 assert_eq!(e.get_ref().unwrap().to_string(), "unexpected JSON type");
321 Ok(_) => panic!("Expected error"),
326 fn into_block_header_from_json_response_with_unexpected_header_type() {
327 let response = JsonResponse(serde_json::json!([42]));
328 match TryInto::<BlockHeaderData>::try_into(response) {
330 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
331 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
333 Ok(_) => panic!("Expected error"),
338 fn into_block_header_from_json_response_with_invalid_header_response() {
339 let block = genesis_block(Network::Bitcoin);
340 let mut response = JsonResponse(BlockHeaderData {
341 chainwork: block.header.work(),
345 response.0["chainwork"].take();
347 match TryInto::<BlockHeaderData>::try_into(response) {
349 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
350 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
352 Ok(_) => panic!("Expected error"),
357 fn into_block_header_from_json_response_with_invalid_header_data() {
358 let block = genesis_block(Network::Bitcoin);
359 let mut response = JsonResponse(BlockHeaderData {
360 chainwork: block.header.work(),
364 response.0["chainwork"] = serde_json::json!("foobar");
366 match TryInto::<BlockHeaderData>::try_into(response) {
368 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
369 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
371 Ok(_) => panic!("Expected error"),
376 fn into_block_header_from_json_response_with_valid_header() {
377 let block = genesis_block(Network::Bitcoin);
378 let response = JsonResponse(BlockHeaderData {
379 chainwork: block.header.work(),
384 match TryInto::<BlockHeaderData>::try_into(response) {
385 Err(e) => panic!("Unexpected error: {:?}", e),
387 assert_eq!(data.chainwork, block.header.work());
388 assert_eq!(data.height, 0);
389 assert_eq!(data.header, block.header);
395 fn into_block_header_from_json_response_with_valid_header_array() {
396 let genesis_block = genesis_block(Network::Bitcoin);
397 let best_block_header = BlockHeader {
398 prev_blockhash: genesis_block.block_hash(),
399 ..genesis_block.header
401 let chainwork = genesis_block.header.work() + best_block_header.work();
402 let response = JsonResponse(serde_json::json!([
403 serde_json::Value::from(BlockHeaderData {
404 chainwork, height: 1, header: best_block_header,
406 serde_json::Value::from(BlockHeaderData {
407 chainwork: genesis_block.header.work(), height: 0, header: genesis_block.header,
411 match TryInto::<BlockHeaderData>::try_into(response) {
412 Err(e) => panic!("Unexpected error: {:?}", e),
414 assert_eq!(data.chainwork, chainwork);
415 assert_eq!(data.height, 1);
416 assert_eq!(data.header, best_block_header);
422 fn into_block_header_from_json_response_without_previous_block_hash() {
423 let block = genesis_block(Network::Bitcoin);
424 let mut response = JsonResponse(BlockHeaderData {
425 chainwork: block.header.work(),
429 response.0.as_object_mut().unwrap().remove("previousblockhash");
431 match TryInto::<BlockHeaderData>::try_into(response) {
432 Err(e) => panic!("Unexpected error: {:?}", e),
433 Ok(BlockHeaderData { chainwork: _, height: _, header }) => {
434 assert_eq!(header, block.header);
440 fn into_block_from_invalid_binary_response() {
441 let response = BinaryResponse(b"foo".to_vec());
442 match TryInto::<Block>::try_into(response) {
444 Ok(_) => panic!("Expected error"),
449 fn into_block_from_valid_binary_response() {
450 let genesis_block = genesis_block(Network::Bitcoin);
451 let response = BinaryResponse(encode::serialize(&genesis_block));
452 match TryInto::<Block>::try_into(response) {
453 Err(e) => panic!("Unexpected error: {:?}", e),
454 Ok(block) => assert_eq!(block, genesis_block),
459 fn into_block_from_json_response_with_unexpected_type() {
460 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
461 match TryInto::<Block>::try_into(response) {
463 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
464 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
466 Ok(_) => panic!("Expected error"),
471 fn into_block_from_json_response_with_invalid_hex_data() {
472 let response = JsonResponse(serde_json::json!("foobar"));
473 match TryInto::<Block>::try_into(response) {
475 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
476 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
478 Ok(_) => panic!("Expected error"),
483 fn into_block_from_json_response_with_invalid_block_data() {
484 let response = JsonResponse(serde_json::json!("abcd"));
485 match TryInto::<Block>::try_into(response) {
487 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
488 assert_eq!(e.get_ref().unwrap().to_string(), "invalid block data");
490 Ok(_) => panic!("Expected error"),
495 fn into_block_from_json_response_with_valid_block_data() {
496 let genesis_block = genesis_block(Network::Bitcoin);
497 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&genesis_block)));
498 match TryInto::<Block>::try_into(response) {
499 Err(e) => panic!("Unexpected error: {:?}", e),
500 Ok(block) => assert_eq!(block, genesis_block),
505 fn into_block_hash_from_json_response_with_unexpected_type() {
506 let response = JsonResponse(serde_json::json!("foo"));
507 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
509 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
510 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
512 Ok(_) => panic!("Expected error"),
517 fn into_block_hash_from_json_response_with_unexpected_bestblockhash_type() {
518 let response = JsonResponse(serde_json::json!({ "bestblockhash": 42 }));
519 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
521 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
522 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
524 Ok(_) => panic!("Expected error"),
529 fn into_block_hash_from_json_response_with_invalid_hex_data() {
530 let response = JsonResponse(serde_json::json!({ "bestblockhash": "foobar"} ));
531 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
533 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
534 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
536 Ok(_) => panic!("Expected error"),
541 fn into_block_hash_from_json_response_without_height() {
542 let block = genesis_block(Network::Bitcoin);
543 let response = JsonResponse(serde_json::json!({
544 "bestblockhash": block.block_hash().to_hex(),
546 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
547 Err(e) => panic!("Unexpected error: {:?}", e),
548 Ok((hash, height)) => {
549 assert_eq!(hash, block.block_hash());
550 assert!(height.is_none());
556 fn into_block_hash_from_json_response_with_unexpected_blocks_type() {
557 let block = genesis_block(Network::Bitcoin);
558 let response = JsonResponse(serde_json::json!({
559 "bestblockhash": block.block_hash().to_hex(),
562 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
564 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
565 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON number");
567 Ok(_) => panic!("Expected error"),
572 fn into_block_hash_from_json_response_with_invalid_height() {
573 let block = genesis_block(Network::Bitcoin);
574 let response = JsonResponse(serde_json::json!({
575 "bestblockhash": block.block_hash().to_hex(),
576 "blocks": std::u64::MAX,
578 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
580 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
581 assert_eq!(e.get_ref().unwrap().to_string(), "invalid height");
583 Ok(_) => panic!("Expected error"),
588 fn into_block_hash_from_json_response_with_height() {
589 let block = genesis_block(Network::Bitcoin);
590 let response = JsonResponse(serde_json::json!({
591 "bestblockhash": block.block_hash().to_hex(),
594 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
595 Err(e) => panic!("Unexpected error: {:?}", e),
596 Ok((hash, height)) => {
597 assert_eq!(hash, block.block_hash());
598 assert_eq!(height.unwrap(), 1);
604 fn into_txid_from_json_response_with_unexpected_type() {
605 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
606 match TryInto::<Txid>::try_into(response) {
608 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
609 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
611 Ok(_) => panic!("Expected error"),
616 fn into_txid_from_json_response_with_invalid_hex_data() {
617 let response = JsonResponse(serde_json::json!("foobar"));
618 match TryInto::<Txid>::try_into(response) {
620 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
621 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
623 Ok(_) => panic!("Expected error"),
628 fn into_txid_from_json_response_with_invalid_txid_data() {
629 let response = JsonResponse(serde_json::json!("abcd"));
630 match TryInto::<Txid>::try_into(response) {
632 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
633 assert_eq!(e.get_ref().unwrap().to_string(), "invalid txid");
635 Ok(_) => panic!("Expected error"),
640 fn into_txid_from_json_response_with_valid_txid_data() {
641 let target_txid = Txid::from_slice(&[1; 32]).unwrap();
642 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_txid)));
643 match TryInto::<Txid>::try_into(response) {
644 Err(e) => panic!("Unexpected error: {:?}", e),
645 Ok(txid) => assert_eq!(txid, target_txid),
649 // TryInto<Transaction> can be used in two ways, first with plain hex response where data is
650 // the hex encoded transaction (e.g. as a result of getrawtransaction) or as a JSON object
651 // where the hex encoded transaction can be found in the hex field of the object (if present)
652 // (e.g. as a result of signrawtransactionwithwallet).
654 // plain hex transaction
657 fn into_tx_from_json_response_with_invalid_hex_data() {
658 let response = JsonResponse(serde_json::json!("foobar"));
659 match TryInto::<Transaction>::try_into(response) {
661 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
662 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
664 Ok(_) => panic!("Expected error"),
669 fn into_tx_from_json_response_with_invalid_data_type() {
670 let response = JsonResponse(Value::Number(Number::from_f64(1.0).unwrap()));
671 match TryInto::<Transaction>::try_into(response) {
673 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
674 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
676 Ok(_) => panic!("Expected error"),
681 fn into_tx_from_json_response_with_invalid_tx_data() {
682 let response = JsonResponse(serde_json::json!("abcd"));
683 match TryInto::<Transaction>::try_into(response) {
685 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
686 assert_eq!(e.get_ref().unwrap().to_string(), "invalid transaction");
688 Ok(_) => panic!("Expected error"),
693 fn into_tx_from_json_response_with_valid_tx_data_plain() {
694 let genesis_block = genesis_block(Network::Bitcoin);
695 let target_tx = genesis_block.txdata.get(0).unwrap();
696 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_tx)));
697 match TryInto::<Transaction>::try_into(response) {
698 Err(e) => panic!("Unexpected error: {:?}", e),
699 Ok(tx) => assert_eq!(&tx, target_tx),
704 fn into_tx_from_json_response_with_valid_tx_data_hex_field() {
705 let genesis_block = genesis_block(Network::Bitcoin);
706 let target_tx = genesis_block.txdata.get(0).unwrap();
707 let response = JsonResponse(serde_json::json!({"hex": encode::serialize_hex(&target_tx)}));
708 match TryInto::<Transaction>::try_into(response) {
709 Err(e) => panic!("Unexpected error: {:?}", e),
710 Ok(tx) => assert_eq!(&tx, target_tx),
714 // transaction in hex field of JSON object
717 fn into_tx_from_json_response_with_no_hex_field() {
718 let response = JsonResponse(serde_json::json!({ "error": "foo" }));
719 match TryInto::<Transaction>::try_into(response) {
721 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
723 e.get_ref().unwrap().to_string(),
724 "expected JSON string"
727 Ok(_) => panic!("Expected error"),
732 fn into_tx_from_json_response_not_signed() {
733 let response = JsonResponse(serde_json::json!({ "hex": "foo", "complete": false }));
734 match TryInto::<Transaction>::try_into(response) {
736 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
738 e.get_ref().unwrap().to_string().contains(
739 "transaction couldn't be signed")
742 Ok(_) => panic!("Expected error"),