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, ToHex};
9 use bitcoin::Transaction;
11 use serde::Deserialize;
15 use std::convert::From;
16 use std::convert::TryFrom;
17 use std::convert::TryInto;
18 use bitcoin::hashes::Hash;
20 /// Conversion from `std::io::Error` into `BlockSourceError`.
21 impl From<std::io::Error> for BlockSourceError {
22 fn from(e: std::io::Error) -> BlockSourceError {
24 std::io::ErrorKind::InvalidData => BlockSourceError::persistent(e),
25 std::io::ErrorKind::InvalidInput => BlockSourceError::persistent(e),
26 _ => BlockSourceError::transient(e),
31 /// Parses binary data as a block.
32 impl TryInto<Block> for BinaryResponse {
33 type Error = std::io::Error;
35 fn try_into(self) -> std::io::Result<Block> {
36 match encode::deserialize(&self.0) {
37 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
38 Ok(block) => Ok(block),
43 /// Converts a JSON value into block header data. The JSON value may be an object representing a
44 /// block header or an array of such objects. In the latter case, the first object is converted.
45 impl TryInto<BlockHeaderData> for JsonResponse {
46 type Error = std::io::Error;
48 fn try_into(self) -> std::io::Result<BlockHeaderData> {
49 let mut header = match self.0 {
50 serde_json::Value::Array(mut array) if !array.is_empty() => array.drain(..).next().unwrap(),
51 serde_json::Value::Object(_) => self.0,
52 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unexpected JSON type")),
55 if !header.is_object() {
56 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
59 // Add an empty previousblockhash for the genesis block.
60 if let None = header.get("previousblockhash") {
61 let hash: BlockHash = BlockHash::all_zeros();
62 header.as_object_mut().unwrap().insert("previousblockhash".to_string(), serde_json::json!(hash.to_hex()));
65 match serde_json::from_value::<GetHeaderResponse>(header) {
66 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header response")),
67 Ok(response) => match response.try_into() {
68 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header data")),
69 Ok(header) => Ok(header),
75 /// Response data from `getblockheader` RPC and `headers` REST requests.
76 #[derive(Deserialize)]
77 struct GetHeaderResponse {
79 pub merkleroot: String,
83 pub previousblockhash: String,
85 pub chainwork: String,
89 /// Converts from `GetHeaderResponse` to `BlockHeaderData`.
90 impl TryFrom<GetHeaderResponse> for BlockHeaderData {
91 type Error = bitcoin::hashes::hex::Error;
93 fn try_from(response: GetHeaderResponse) -> Result<Self, bitcoin::hashes::hex::Error> {
96 version: response.version,
97 prev_blockhash: BlockHash::from_hex(&response.previousblockhash)?,
98 merkle_root: TxMerkleNode::from_hex(&response.merkleroot)?,
100 bits: u32::from_be_bytes(<[u8; 4]>::from_hex(&response.bits)?),
101 nonce: response.nonce,
103 chainwork: hex_to_uint256(&response.chainwork)?,
104 height: response.height,
109 /// Converts a JSON value into a block. Assumes the block is hex-encoded in a JSON string.
110 impl TryInto<Block> for JsonResponse {
111 type Error = std::io::Error;
113 fn try_into(self) -> std::io::Result<Block> {
114 match self.0.as_str() {
115 None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
116 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
117 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
118 Ok(block_data) => match encode::deserialize(&block_data) {
119 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
120 Ok(block) => Ok(block),
127 /// Converts a JSON value into the best block hash and optional height.
128 impl TryInto<(BlockHash, Option<u32>)> for JsonResponse {
129 type Error = std::io::Error;
131 fn try_into(self) -> std::io::Result<(BlockHash, Option<u32>)> {
132 if !self.0.is_object() {
133 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
136 let hash = match &self.0["bestblockhash"] {
137 serde_json::Value::String(hex_data) => match BlockHash::from_hex(&hex_data) {
138 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
139 Ok(block_hash) => block_hash,
141 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
144 let height = match &self.0["blocks"] {
145 serde_json::Value::Null => None,
146 serde_json::Value::Number(height) => match height.as_u64() {
147 None => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
148 Some(height) => match height.try_into() {
149 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
150 Ok(height) => Some(height),
153 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON number")),
160 impl TryInto<Txid> for JsonResponse {
161 type Error = std::io::Error;
162 fn try_into(self) -> std::io::Result<Txid> {
163 match self.0.as_str() {
164 None => Err(std::io::Error::new(
165 std::io::ErrorKind::InvalidData,
166 "expected JSON string",
168 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
169 Err(_) => Err(std::io::Error::new(
170 std::io::ErrorKind::InvalidData,
173 Ok(txid_data) => match encode::deserialize(&txid_data) {
174 Err(_) => Err(std::io::Error::new(
175 std::io::ErrorKind::InvalidData,
178 Ok(txid) => Ok(txid),
185 /// Converts a JSON value into a transaction. WATCH OUT! this cannot be used for zero-input transactions
186 /// (e.g. createrawtransaction). See <https://github.com/rust-bitcoin/rust-bitcoincore-rpc/issues/197>
187 impl TryInto<Transaction> for JsonResponse {
188 type Error = std::io::Error;
189 fn try_into(self) -> std::io::Result<Transaction> {
190 let hex_tx = if self.0.is_object() {
191 // result is json encoded
192 match &self.0["hex"] {
193 // result has hex field
194 serde_json::Value::String(hex_data) => match self.0["complete"] {
195 // result may or may not be signed (e.g. signrawtransactionwithwallet)
196 serde_json::Value::Bool(x) => {
198 let reason = match &self.0["errors"][0]["error"] {
199 serde_json::Value::String(x) => x.as_str(),
200 _ => "Unknown error",
203 return Err(std::io::Error::new(
204 std::io::ErrorKind::InvalidData,
205 format!("transaction couldn't be signed. {}", reason),
211 // result is a complete transaction (e.g. getrawtranaction verbose)
214 _ => return Err(std::io::Error::new(
215 std::io::ErrorKind::InvalidData,
216 "expected JSON string",
220 // result is plain text (e.g. getrawtransaction no verbose)
221 match self.0.as_str() {
222 Some(hex_tx) => hex_tx,
224 return Err(std::io::Error::new(
225 std::io::ErrorKind::InvalidData,
226 "expected JSON string",
232 match Vec::<u8>::from_hex(hex_tx) {
233 Err(_) => Err(std::io::Error::new(
234 std::io::ErrorKind::InvalidData,
237 Ok(tx_data) => match encode::deserialize(&tx_data) {
238 Err(_) => Err(std::io::Error::new(
239 std::io::ErrorKind::InvalidData,
240 "invalid transaction",
249 pub(crate) mod tests {
251 use bitcoin::blockdata::constants::genesis_block;
252 use bitcoin::hashes::Hash;
253 use bitcoin::network::constants::Network;
254 use serde_json::value::Number;
255 use serde_json::Value;
257 /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
258 impl From<BlockHeaderData> for serde_json::Value {
259 fn from(data: BlockHeaderData) -> Self {
260 let BlockHeaderData { chainwork, height, header } = data;
262 "chainwork": chainwork.to_string()["0x".len()..],
264 "version": header.version,
265 "merkleroot": header.merkle_root.to_hex(),
267 "nonce": header.nonce,
268 "bits": header.bits.to_hex(),
269 "previousblockhash": header.prev_blockhash.to_hex(),
275 fn into_block_header_from_json_response_with_unexpected_type() {
276 let response = JsonResponse(serde_json::json!(42));
277 match TryInto::<BlockHeaderData>::try_into(response) {
279 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
280 assert_eq!(e.get_ref().unwrap().to_string(), "unexpected JSON type");
282 Ok(_) => panic!("Expected error"),
287 fn into_block_header_from_json_response_with_unexpected_header_type() {
288 let response = JsonResponse(serde_json::json!([42]));
289 match TryInto::<BlockHeaderData>::try_into(response) {
291 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
292 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
294 Ok(_) => panic!("Expected error"),
299 fn into_block_header_from_json_response_with_invalid_header_response() {
300 let block = genesis_block(Network::Bitcoin);
301 let mut response = JsonResponse(BlockHeaderData {
302 chainwork: block.header.work(),
306 response.0["chainwork"].take();
308 match TryInto::<BlockHeaderData>::try_into(response) {
310 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
311 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header response");
313 Ok(_) => panic!("Expected error"),
318 fn into_block_header_from_json_response_with_invalid_header_data() {
319 let block = genesis_block(Network::Bitcoin);
320 let mut response = JsonResponse(BlockHeaderData {
321 chainwork: block.header.work(),
325 response.0["chainwork"] = serde_json::json!("foobar");
327 match TryInto::<BlockHeaderData>::try_into(response) {
329 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
330 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
332 Ok(_) => panic!("Expected error"),
337 fn into_block_header_from_json_response_with_valid_header() {
338 let block = genesis_block(Network::Bitcoin);
339 let response = JsonResponse(BlockHeaderData {
340 chainwork: block.header.work(),
345 match TryInto::<BlockHeaderData>::try_into(response) {
346 Err(e) => panic!("Unexpected error: {:?}", e),
348 assert_eq!(data.chainwork, block.header.work());
349 assert_eq!(data.height, 0);
350 assert_eq!(data.header, block.header);
356 fn into_block_header_from_json_response_with_valid_header_array() {
357 let genesis_block = genesis_block(Network::Bitcoin);
358 let best_block_header = BlockHeader {
359 prev_blockhash: genesis_block.block_hash(),
360 ..genesis_block.header
362 let chainwork = genesis_block.header.work() + best_block_header.work();
363 let response = JsonResponse(serde_json::json!([
364 serde_json::Value::from(BlockHeaderData {
365 chainwork, height: 1, header: best_block_header,
367 serde_json::Value::from(BlockHeaderData {
368 chainwork: genesis_block.header.work(), height: 0, header: genesis_block.header,
372 match TryInto::<BlockHeaderData>::try_into(response) {
373 Err(e) => panic!("Unexpected error: {:?}", e),
375 assert_eq!(data.chainwork, chainwork);
376 assert_eq!(data.height, 1);
377 assert_eq!(data.header, best_block_header);
383 fn into_block_header_from_json_response_without_previous_block_hash() {
384 let block = genesis_block(Network::Bitcoin);
385 let mut response = JsonResponse(BlockHeaderData {
386 chainwork: block.header.work(),
390 response.0.as_object_mut().unwrap().remove("previousblockhash");
392 match TryInto::<BlockHeaderData>::try_into(response) {
393 Err(e) => panic!("Unexpected error: {:?}", e),
394 Ok(BlockHeaderData { chainwork: _, height: _, header }) => {
395 assert_eq!(header, block.header);
401 fn into_block_from_invalid_binary_response() {
402 let response = BinaryResponse(b"foo".to_vec());
403 match TryInto::<Block>::try_into(response) {
405 Ok(_) => panic!("Expected error"),
410 fn into_block_from_valid_binary_response() {
411 let genesis_block = genesis_block(Network::Bitcoin);
412 let response = BinaryResponse(encode::serialize(&genesis_block));
413 match TryInto::<Block>::try_into(response) {
414 Err(e) => panic!("Unexpected error: {:?}", e),
415 Ok(block) => assert_eq!(block, genesis_block),
420 fn into_block_from_json_response_with_unexpected_type() {
421 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
422 match TryInto::<Block>::try_into(response) {
424 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
425 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
427 Ok(_) => panic!("Expected error"),
432 fn into_block_from_json_response_with_invalid_hex_data() {
433 let response = JsonResponse(serde_json::json!("foobar"));
434 match TryInto::<Block>::try_into(response) {
436 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
437 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
439 Ok(_) => panic!("Expected error"),
444 fn into_block_from_json_response_with_invalid_block_data() {
445 let response = JsonResponse(serde_json::json!("abcd"));
446 match TryInto::<Block>::try_into(response) {
448 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
449 assert_eq!(e.get_ref().unwrap().to_string(), "invalid block data");
451 Ok(_) => panic!("Expected error"),
456 fn into_block_from_json_response_with_valid_block_data() {
457 let genesis_block = genesis_block(Network::Bitcoin);
458 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&genesis_block)));
459 match TryInto::<Block>::try_into(response) {
460 Err(e) => panic!("Unexpected error: {:?}", e),
461 Ok(block) => assert_eq!(block, genesis_block),
466 fn into_block_hash_from_json_response_with_unexpected_type() {
467 let response = JsonResponse(serde_json::json!("foo"));
468 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
470 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
471 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
473 Ok(_) => panic!("Expected error"),
478 fn into_block_hash_from_json_response_with_unexpected_bestblockhash_type() {
479 let response = JsonResponse(serde_json::json!({ "bestblockhash": 42 }));
480 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
482 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
483 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
485 Ok(_) => panic!("Expected error"),
490 fn into_block_hash_from_json_response_with_invalid_hex_data() {
491 let response = JsonResponse(serde_json::json!({ "bestblockhash": "foobar"} ));
492 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
494 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
495 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
497 Ok(_) => panic!("Expected error"),
502 fn into_block_hash_from_json_response_without_height() {
503 let block = genesis_block(Network::Bitcoin);
504 let response = JsonResponse(serde_json::json!({
505 "bestblockhash": block.block_hash().to_hex(),
507 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
508 Err(e) => panic!("Unexpected error: {:?}", e),
509 Ok((hash, height)) => {
510 assert_eq!(hash, block.block_hash());
511 assert!(height.is_none());
517 fn into_block_hash_from_json_response_with_unexpected_blocks_type() {
518 let block = genesis_block(Network::Bitcoin);
519 let response = JsonResponse(serde_json::json!({
520 "bestblockhash": block.block_hash().to_hex(),
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 number");
528 Ok(_) => panic!("Expected error"),
533 fn into_block_hash_from_json_response_with_invalid_height() {
534 let block = genesis_block(Network::Bitcoin);
535 let response = JsonResponse(serde_json::json!({
536 "bestblockhash": block.block_hash().to_hex(),
537 "blocks": std::u64::MAX,
539 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
541 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
542 assert_eq!(e.get_ref().unwrap().to_string(), "invalid height");
544 Ok(_) => panic!("Expected error"),
549 fn into_block_hash_from_json_response_with_height() {
550 let block = genesis_block(Network::Bitcoin);
551 let response = JsonResponse(serde_json::json!({
552 "bestblockhash": block.block_hash().to_hex(),
555 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
556 Err(e) => panic!("Unexpected error: {:?}", e),
557 Ok((hash, height)) => {
558 assert_eq!(hash, block.block_hash());
559 assert_eq!(height.unwrap(), 1);
565 fn into_txid_from_json_response_with_unexpected_type() {
566 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
567 match TryInto::<Txid>::try_into(response) {
569 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
570 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
572 Ok(_) => panic!("Expected error"),
577 fn into_txid_from_json_response_with_invalid_hex_data() {
578 let response = JsonResponse(serde_json::json!("foobar"));
579 match TryInto::<Txid>::try_into(response) {
581 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
582 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
584 Ok(_) => panic!("Expected error"),
589 fn into_txid_from_json_response_with_invalid_txid_data() {
590 let response = JsonResponse(serde_json::json!("abcd"));
591 match TryInto::<Txid>::try_into(response) {
593 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
594 assert_eq!(e.get_ref().unwrap().to_string(), "invalid txid");
596 Ok(_) => panic!("Expected error"),
601 fn into_txid_from_json_response_with_valid_txid_data() {
602 let target_txid = Txid::from_slice(&[1; 32]).unwrap();
603 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_txid)));
604 match TryInto::<Txid>::try_into(response) {
605 Err(e) => panic!("Unexpected error: {:?}", e),
606 Ok(txid) => assert_eq!(txid, target_txid),
610 // TryInto<Transaction> can be used in two ways, first with plain hex response where data is
611 // the hex encoded transaction (e.g. as a result of getrawtransaction) or as a JSON object
612 // where the hex encoded transaction can be found in the hex field of the object (if present)
613 // (e.g. as a result of signrawtransactionwithwallet).
615 // plain hex transaction
618 fn into_tx_from_json_response_with_invalid_hex_data() {
619 let response = JsonResponse(serde_json::json!("foobar"));
620 match TryInto::<Transaction>::try_into(response) {
622 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
623 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
625 Ok(_) => panic!("Expected error"),
630 fn into_tx_from_json_response_with_invalid_data_type() {
631 let response = JsonResponse(Value::Number(Number::from_f64(1.0).unwrap()));
632 match TryInto::<Transaction>::try_into(response) {
634 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
635 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
637 Ok(_) => panic!("Expected error"),
642 fn into_tx_from_json_response_with_invalid_tx_data() {
643 let response = JsonResponse(serde_json::json!("abcd"));
644 match TryInto::<Transaction>::try_into(response) {
646 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
647 assert_eq!(e.get_ref().unwrap().to_string(), "invalid transaction");
649 Ok(_) => panic!("Expected error"),
654 fn into_tx_from_json_response_with_valid_tx_data_plain() {
655 let genesis_block = genesis_block(Network::Bitcoin);
656 let target_tx = genesis_block.txdata.get(0).unwrap();
657 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_tx)));
658 match TryInto::<Transaction>::try_into(response) {
659 Err(e) => panic!("Unexpected error: {:?}", e),
660 Ok(tx) => assert_eq!(&tx, target_tx),
665 fn into_tx_from_json_response_with_valid_tx_data_hex_field() {
666 let genesis_block = genesis_block(Network::Bitcoin);
667 let target_tx = genesis_block.txdata.get(0).unwrap();
668 let response = JsonResponse(serde_json::json!({"hex": encode::serialize_hex(&target_tx)}));
669 match TryInto::<Transaction>::try_into(response) {
670 Err(e) => panic!("Unexpected error: {:?}", e),
671 Ok(tx) => assert_eq!(&tx, target_tx),
675 // transaction in hex field of JSON object
678 fn into_tx_from_json_response_with_no_hex_field() {
679 let response = JsonResponse(serde_json::json!({ "error": "foo" }));
680 match TryInto::<Transaction>::try_into(response) {
682 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
684 e.get_ref().unwrap().to_string(),
685 "expected JSON string"
688 Ok(_) => panic!("Expected error"),
693 fn into_tx_from_json_response_not_signed() {
694 let response = JsonResponse(serde_json::json!({ "hex": "foo", "complete": false }));
695 match TryInto::<Transaction>::try_into(response) {
697 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
699 e.get_ref().unwrap().to_string().contains(
700 "transaction couldn't be signed")
703 Ok(_) => panic!("Expected error"),