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 bitcoin::hashes::Hash;
18 /// Conversion from `std::io::Error` into `BlockSourceError`.
19 impl From<std::io::Error> for BlockSourceError {
20 fn from(e: std::io::Error) -> BlockSourceError {
22 std::io::ErrorKind::InvalidData => BlockSourceError::persistent(e),
23 std::io::ErrorKind::InvalidInput => BlockSourceError::persistent(e),
24 _ => BlockSourceError::transient(e),
29 /// Parses binary data as a block.
30 impl TryInto<Block> for BinaryResponse {
31 type Error = std::io::Error;
33 fn try_into(self) -> std::io::Result<Block> {
34 match encode::deserialize(&self.0) {
35 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
36 Ok(block) => Ok(block),
41 /// Converts a JSON value into block header data. The JSON value may be an object representing a
42 /// block header or an array of such objects. In the latter case, the first object is converted.
43 impl TryInto<BlockHeaderData> for JsonResponse {
44 type Error = std::io::Error;
46 fn try_into(self) -> std::io::Result<BlockHeaderData> {
47 let header = match self.0 {
48 serde_json::Value::Array(mut array) if !array.is_empty() => array.drain(..).next().unwrap(),
49 serde_json::Value::Object(_) => self.0,
50 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unexpected JSON type")),
53 if !header.is_object() {
54 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
57 // Add an empty previousblockhash for the genesis block.
58 match header.try_into() {
59 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header data")),
60 Ok(header) => Ok(header),
65 impl TryFrom<serde_json::Value> for BlockHeaderData {
68 fn try_from(response: serde_json::Value) -> Result<Self, ()> {
69 macro_rules! get_field { ($name: expr, $ty_access: tt) => {
70 response.get($name).ok_or(())?.$ty_access().ok_or(())?
75 version: get_field!("version", as_i64).try_into().map_err(|_| ())?,
76 prev_blockhash: if let Some(hash_str) = response.get("previousblockhash") {
77 BlockHash::from_hex(hash_str.as_str().ok_or(())?).map_err(|_| ())?
78 } else { BlockHash::all_zeros() },
79 merkle_root: TxMerkleNode::from_hex(get_field!("merkleroot", as_str)).map_err(|_| ())?,
80 time: get_field!("time", as_u64).try_into().map_err(|_| ())?,
81 bits: u32::from_be_bytes(<[u8; 4]>::from_hex(get_field!("bits", as_str)).map_err(|_| ())?),
82 nonce: get_field!("nonce", as_u64).try_into().map_err(|_| ())?,
84 chainwork: hex_to_uint256(get_field!("chainwork", as_str)).map_err(|_| ())?,
85 height: get_field!("height", as_u64).try_into().map_err(|_| ())?,
90 /// Converts a JSON value into a block. Assumes the block is hex-encoded in a JSON string.
91 impl TryInto<Block> for JsonResponse {
92 type Error = std::io::Error;
94 fn try_into(self) -> std::io::Result<Block> {
95 match self.0.as_str() {
96 None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
97 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
98 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
99 Ok(block_data) => match encode::deserialize(&block_data) {
100 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
101 Ok(block) => Ok(block),
108 /// Converts a JSON value into the best block hash and optional height.
109 impl TryInto<(BlockHash, Option<u32>)> for JsonResponse {
110 type Error = std::io::Error;
112 fn try_into(self) -> std::io::Result<(BlockHash, Option<u32>)> {
113 if !self.0.is_object() {
114 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
117 let hash = match &self.0["bestblockhash"] {
118 serde_json::Value::String(hex_data) => match BlockHash::from_hex(&hex_data) {
119 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
120 Ok(block_hash) => block_hash,
122 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
125 let height = match &self.0["blocks"] {
126 serde_json::Value::Null => None,
127 serde_json::Value::Number(height) => match height.as_u64() {
128 None => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
129 Some(height) => match height.try_into() {
130 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
131 Ok(height) => Some(height),
134 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON number")),
141 impl TryInto<Txid> for JsonResponse {
142 type Error = std::io::Error;
143 fn try_into(self) -> std::io::Result<Txid> {
144 match self.0.as_str() {
145 None => Err(std::io::Error::new(
146 std::io::ErrorKind::InvalidData,
147 "expected JSON string",
149 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
150 Err(_) => Err(std::io::Error::new(
151 std::io::ErrorKind::InvalidData,
154 Ok(txid_data) => match encode::deserialize(&txid_data) {
155 Err(_) => Err(std::io::Error::new(
156 std::io::ErrorKind::InvalidData,
159 Ok(txid) => Ok(txid),
166 /// Converts a JSON value into a transaction. WATCH OUT! this cannot be used for zero-input transactions
167 /// (e.g. createrawtransaction). See <https://github.com/rust-bitcoin/rust-bitcoincore-rpc/issues/197>
168 impl TryInto<Transaction> for JsonResponse {
169 type Error = std::io::Error;
170 fn try_into(self) -> std::io::Result<Transaction> {
171 let hex_tx = if self.0.is_object() {
172 // result is json encoded
173 match &self.0["hex"] {
174 // result has hex field
175 serde_json::Value::String(hex_data) => match self.0["complete"] {
176 // result may or may not be signed (e.g. signrawtransactionwithwallet)
177 serde_json::Value::Bool(x) => {
179 let reason = match &self.0["errors"][0]["error"] {
180 serde_json::Value::String(x) => x.as_str(),
181 _ => "Unknown error",
184 return Err(std::io::Error::new(
185 std::io::ErrorKind::InvalidData,
186 format!("transaction couldn't be signed. {}", reason),
192 // result is a complete transaction (e.g. getrawtranaction verbose)
195 _ => return Err(std::io::Error::new(
196 std::io::ErrorKind::InvalidData,
197 "expected JSON string",
201 // result is plain text (e.g. getrawtransaction no verbose)
202 match self.0.as_str() {
203 Some(hex_tx) => hex_tx,
205 return Err(std::io::Error::new(
206 std::io::ErrorKind::InvalidData,
207 "expected JSON string",
213 match Vec::<u8>::from_hex(hex_tx) {
214 Err(_) => Err(std::io::Error::new(
215 std::io::ErrorKind::InvalidData,
218 Ok(tx_data) => match encode::deserialize(&tx_data) {
219 Err(_) => Err(std::io::Error::new(
220 std::io::ErrorKind::InvalidData,
221 "invalid transaction",
230 pub(crate) mod tests {
232 use bitcoin::blockdata::constants::genesis_block;
233 use bitcoin::hashes::Hash;
234 use bitcoin::hashes::hex::ToHex;
235 use bitcoin::network::constants::Network;
236 use serde_json::value::Number;
237 use serde_json::Value;
239 /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
240 impl From<BlockHeaderData> for serde_json::Value {
241 fn from(data: BlockHeaderData) -> Self {
242 let BlockHeaderData { chainwork, height, header } = data;
244 "chainwork": chainwork.to_string()["0x".len()..],
246 "version": header.version,
247 "merkleroot": header.merkle_root.to_hex(),
249 "nonce": header.nonce,
250 "bits": header.bits.to_hex(),
251 "previousblockhash": header.prev_blockhash.to_hex(),
257 fn into_block_header_from_json_response_with_unexpected_type() {
258 let response = JsonResponse(serde_json::json!(42));
259 match TryInto::<BlockHeaderData>::try_into(response) {
261 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
262 assert_eq!(e.get_ref().unwrap().to_string(), "unexpected JSON type");
264 Ok(_) => panic!("Expected error"),
269 fn into_block_header_from_json_response_with_unexpected_header_type() {
270 let response = JsonResponse(serde_json::json!([42]));
271 match TryInto::<BlockHeaderData>::try_into(response) {
273 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
274 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
276 Ok(_) => panic!("Expected error"),
281 fn into_block_header_from_json_response_with_invalid_header_response() {
282 let block = genesis_block(Network::Bitcoin);
283 let mut response = JsonResponse(BlockHeaderData {
284 chainwork: block.header.work(),
288 response.0["chainwork"].take();
290 match TryInto::<BlockHeaderData>::try_into(response) {
292 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
293 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
295 Ok(_) => panic!("Expected error"),
300 fn into_block_header_from_json_response_with_invalid_header_data() {
301 let block = genesis_block(Network::Bitcoin);
302 let mut response = JsonResponse(BlockHeaderData {
303 chainwork: block.header.work(),
307 response.0["chainwork"] = serde_json::json!("foobar");
309 match TryInto::<BlockHeaderData>::try_into(response) {
311 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
312 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
314 Ok(_) => panic!("Expected error"),
319 fn into_block_header_from_json_response_with_valid_header() {
320 let block = genesis_block(Network::Bitcoin);
321 let response = JsonResponse(BlockHeaderData {
322 chainwork: block.header.work(),
327 match TryInto::<BlockHeaderData>::try_into(response) {
328 Err(e) => panic!("Unexpected error: {:?}", e),
330 assert_eq!(data.chainwork, block.header.work());
331 assert_eq!(data.height, 0);
332 assert_eq!(data.header, block.header);
338 fn into_block_header_from_json_response_with_valid_header_array() {
339 let genesis_block = genesis_block(Network::Bitcoin);
340 let best_block_header = BlockHeader {
341 prev_blockhash: genesis_block.block_hash(),
342 ..genesis_block.header
344 let chainwork = genesis_block.header.work() + best_block_header.work();
345 let response = JsonResponse(serde_json::json!([
346 serde_json::Value::from(BlockHeaderData {
347 chainwork, height: 1, header: best_block_header,
349 serde_json::Value::from(BlockHeaderData {
350 chainwork: genesis_block.header.work(), height: 0, header: genesis_block.header,
354 match TryInto::<BlockHeaderData>::try_into(response) {
355 Err(e) => panic!("Unexpected error: {:?}", e),
357 assert_eq!(data.chainwork, chainwork);
358 assert_eq!(data.height, 1);
359 assert_eq!(data.header, best_block_header);
365 fn into_block_header_from_json_response_without_previous_block_hash() {
366 let block = genesis_block(Network::Bitcoin);
367 let mut response = JsonResponse(BlockHeaderData {
368 chainwork: block.header.work(),
372 response.0.as_object_mut().unwrap().remove("previousblockhash");
374 match TryInto::<BlockHeaderData>::try_into(response) {
375 Err(e) => panic!("Unexpected error: {:?}", e),
376 Ok(BlockHeaderData { chainwork: _, height: _, header }) => {
377 assert_eq!(header, block.header);
383 fn into_block_from_invalid_binary_response() {
384 let response = BinaryResponse(b"foo".to_vec());
385 match TryInto::<Block>::try_into(response) {
387 Ok(_) => panic!("Expected error"),
392 fn into_block_from_valid_binary_response() {
393 let genesis_block = genesis_block(Network::Bitcoin);
394 let response = BinaryResponse(encode::serialize(&genesis_block));
395 match TryInto::<Block>::try_into(response) {
396 Err(e) => panic!("Unexpected error: {:?}", e),
397 Ok(block) => assert_eq!(block, genesis_block),
402 fn into_block_from_json_response_with_unexpected_type() {
403 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
404 match TryInto::<Block>::try_into(response) {
406 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
407 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
409 Ok(_) => panic!("Expected error"),
414 fn into_block_from_json_response_with_invalid_hex_data() {
415 let response = JsonResponse(serde_json::json!("foobar"));
416 match TryInto::<Block>::try_into(response) {
418 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
419 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
421 Ok(_) => panic!("Expected error"),
426 fn into_block_from_json_response_with_invalid_block_data() {
427 let response = JsonResponse(serde_json::json!("abcd"));
428 match TryInto::<Block>::try_into(response) {
430 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
431 assert_eq!(e.get_ref().unwrap().to_string(), "invalid block data");
433 Ok(_) => panic!("Expected error"),
438 fn into_block_from_json_response_with_valid_block_data() {
439 let genesis_block = genesis_block(Network::Bitcoin);
440 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&genesis_block)));
441 match TryInto::<Block>::try_into(response) {
442 Err(e) => panic!("Unexpected error: {:?}", e),
443 Ok(block) => assert_eq!(block, genesis_block),
448 fn into_block_hash_from_json_response_with_unexpected_type() {
449 let response = JsonResponse(serde_json::json!("foo"));
450 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
452 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
453 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
455 Ok(_) => panic!("Expected error"),
460 fn into_block_hash_from_json_response_with_unexpected_bestblockhash_type() {
461 let response = JsonResponse(serde_json::json!({ "bestblockhash": 42 }));
462 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
464 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
465 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
467 Ok(_) => panic!("Expected error"),
472 fn into_block_hash_from_json_response_with_invalid_hex_data() {
473 let response = JsonResponse(serde_json::json!({ "bestblockhash": "foobar"} ));
474 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
476 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
477 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
479 Ok(_) => panic!("Expected error"),
484 fn into_block_hash_from_json_response_without_height() {
485 let block = genesis_block(Network::Bitcoin);
486 let response = JsonResponse(serde_json::json!({
487 "bestblockhash": block.block_hash().to_hex(),
489 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
490 Err(e) => panic!("Unexpected error: {:?}", e),
491 Ok((hash, height)) => {
492 assert_eq!(hash, block.block_hash());
493 assert!(height.is_none());
499 fn into_block_hash_from_json_response_with_unexpected_blocks_type() {
500 let block = genesis_block(Network::Bitcoin);
501 let response = JsonResponse(serde_json::json!({
502 "bestblockhash": block.block_hash().to_hex(),
505 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
507 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
508 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON number");
510 Ok(_) => panic!("Expected error"),
515 fn into_block_hash_from_json_response_with_invalid_height() {
516 let block = genesis_block(Network::Bitcoin);
517 let response = JsonResponse(serde_json::json!({
518 "bestblockhash": block.block_hash().to_hex(),
519 "blocks": std::u64::MAX,
521 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
523 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
524 assert_eq!(e.get_ref().unwrap().to_string(), "invalid height");
526 Ok(_) => panic!("Expected error"),
531 fn into_block_hash_from_json_response_with_height() {
532 let block = genesis_block(Network::Bitcoin);
533 let response = JsonResponse(serde_json::json!({
534 "bestblockhash": block.block_hash().to_hex(),
537 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
538 Err(e) => panic!("Unexpected error: {:?}", e),
539 Ok((hash, height)) => {
540 assert_eq!(hash, block.block_hash());
541 assert_eq!(height.unwrap(), 1);
547 fn into_txid_from_json_response_with_unexpected_type() {
548 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
549 match TryInto::<Txid>::try_into(response) {
551 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
552 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
554 Ok(_) => panic!("Expected error"),
559 fn into_txid_from_json_response_with_invalid_hex_data() {
560 let response = JsonResponse(serde_json::json!("foobar"));
561 match TryInto::<Txid>::try_into(response) {
563 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
564 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
566 Ok(_) => panic!("Expected error"),
571 fn into_txid_from_json_response_with_invalid_txid_data() {
572 let response = JsonResponse(serde_json::json!("abcd"));
573 match TryInto::<Txid>::try_into(response) {
575 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
576 assert_eq!(e.get_ref().unwrap().to_string(), "invalid txid");
578 Ok(_) => panic!("Expected error"),
583 fn into_txid_from_json_response_with_valid_txid_data() {
584 let target_txid = Txid::from_slice(&[1; 32]).unwrap();
585 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_txid)));
586 match TryInto::<Txid>::try_into(response) {
587 Err(e) => panic!("Unexpected error: {:?}", e),
588 Ok(txid) => assert_eq!(txid, target_txid),
592 // TryInto<Transaction> can be used in two ways, first with plain hex response where data is
593 // the hex encoded transaction (e.g. as a result of getrawtransaction) or as a JSON object
594 // where the hex encoded transaction can be found in the hex field of the object (if present)
595 // (e.g. as a result of signrawtransactionwithwallet).
597 // plain hex transaction
600 fn into_tx_from_json_response_with_invalid_hex_data() {
601 let response = JsonResponse(serde_json::json!("foobar"));
602 match TryInto::<Transaction>::try_into(response) {
604 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
605 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
607 Ok(_) => panic!("Expected error"),
612 fn into_tx_from_json_response_with_invalid_data_type() {
613 let response = JsonResponse(Value::Number(Number::from_f64(1.0).unwrap()));
614 match TryInto::<Transaction>::try_into(response) {
616 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
617 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
619 Ok(_) => panic!("Expected error"),
624 fn into_tx_from_json_response_with_invalid_tx_data() {
625 let response = JsonResponse(serde_json::json!("abcd"));
626 match TryInto::<Transaction>::try_into(response) {
628 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
629 assert_eq!(e.get_ref().unwrap().to_string(), "invalid transaction");
631 Ok(_) => panic!("Expected error"),
636 fn into_tx_from_json_response_with_valid_tx_data_plain() {
637 let genesis_block = genesis_block(Network::Bitcoin);
638 let target_tx = genesis_block.txdata.get(0).unwrap();
639 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_tx)));
640 match TryInto::<Transaction>::try_into(response) {
641 Err(e) => panic!("Unexpected error: {:?}", e),
642 Ok(tx) => assert_eq!(&tx, target_tx),
647 fn into_tx_from_json_response_with_valid_tx_data_hex_field() {
648 let genesis_block = genesis_block(Network::Bitcoin);
649 let target_tx = genesis_block.txdata.get(0).unwrap();
650 let response = JsonResponse(serde_json::json!({"hex": encode::serialize_hex(&target_tx)}));
651 match TryInto::<Transaction>::try_into(response) {
652 Err(e) => panic!("Unexpected error: {:?}", e),
653 Ok(tx) => assert_eq!(&tx, target_tx),
657 // transaction in hex field of JSON object
660 fn into_tx_from_json_response_with_no_hex_field() {
661 let response = JsonResponse(serde_json::json!({ "error": "foo" }));
662 match TryInto::<Transaction>::try_into(response) {
664 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
666 e.get_ref().unwrap().to_string(),
667 "expected JSON string"
670 Ok(_) => panic!("Expected error"),
675 fn into_tx_from_json_response_not_signed() {
676 let response = JsonResponse(serde_json::json!({ "hex": "foo", "complete": false }));
677 match TryInto::<Transaction>::try_into(response) {
679 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
681 e.get_ref().unwrap().to_string().contains(
682 "transaction couldn't be signed")
685 Ok(_) => panic!("Expected error"),