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;
19 /// Conversion from `std::io::Error` into `BlockSourceError`.
20 impl From<std::io::Error> for BlockSourceError {
21 fn from(e: std::io::Error) -> BlockSourceError {
23 std::io::ErrorKind::InvalidData => BlockSourceError::persistent(e),
24 std::io::ErrorKind::InvalidInput => BlockSourceError::persistent(e),
25 _ => BlockSourceError::transient(e),
30 /// Parses binary data as a block.
31 impl TryInto<Block> for BinaryResponse {
32 type Error = std::io::Error;
34 fn try_into(self) -> std::io::Result<Block> {
35 match encode::deserialize(&self.0) {
36 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
37 Ok(block) => Ok(block),
42 /// Converts a JSON value into block header data. The JSON value may be an object representing a
43 /// block header or an array of such objects. In the latter case, the first object is converted.
44 impl TryInto<BlockHeaderData> for JsonResponse {
45 type Error = std::io::Error;
47 fn try_into(self) -> std::io::Result<BlockHeaderData> {
48 let mut header = match self.0 {
49 serde_json::Value::Array(mut array) if !array.is_empty() => array.drain(..).next().unwrap(),
50 serde_json::Value::Object(_) => self.0,
51 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "unexpected JSON type")),
54 if !header.is_object() {
55 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
58 // Add an empty previousblockhash for the genesis block.
59 if let None = header.get("previousblockhash") {
60 let hash: BlockHash = Default::default();
61 header.as_object_mut().unwrap().insert("previousblockhash".to_string(), serde_json::json!(hash.to_hex()));
64 match serde_json::from_value::<GetHeaderResponse>(header) {
65 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header response")),
66 Ok(response) => match response.try_into() {
67 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header data")),
68 Ok(header) => Ok(header),
74 /// Response data from `getblockheader` RPC and `headers` REST requests.
75 #[derive(Deserialize)]
76 struct GetHeaderResponse {
78 pub merkleroot: String,
82 pub previousblockhash: String,
84 pub chainwork: String,
88 /// Converts from `GetHeaderResponse` to `BlockHeaderData`.
89 impl TryFrom<GetHeaderResponse> for BlockHeaderData {
90 type Error = bitcoin::hashes::hex::Error;
92 fn try_from(response: GetHeaderResponse) -> Result<Self, bitcoin::hashes::hex::Error> {
95 version: response.version,
96 prev_blockhash: BlockHash::from_hex(&response.previousblockhash)?,
97 merkle_root: TxMerkleNode::from_hex(&response.merkleroot)?,
99 bits: u32::from_be_bytes(<[u8; 4]>::from_hex(&response.bits)?),
100 nonce: response.nonce,
102 chainwork: hex_to_uint256(&response.chainwork)?,
103 height: response.height,
108 /// Converts a JSON value into a block. Assumes the block is hex-encoded in a JSON string.
109 impl TryInto<Block> for JsonResponse {
110 type Error = std::io::Error;
112 fn try_into(self) -> std::io::Result<Block> {
113 match self.0.as_str() {
114 None => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
115 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
116 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
117 Ok(block_data) => match encode::deserialize(&block_data) {
118 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid block data")),
119 Ok(block) => Ok(block),
126 /// Converts a JSON value into the best block hash and optional height.
127 impl TryInto<(BlockHash, Option<u32>)> for JsonResponse {
128 type Error = std::io::Error;
130 fn try_into(self) -> std::io::Result<(BlockHash, Option<u32>)> {
131 if !self.0.is_object() {
132 return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object"));
135 let hash = match &self.0["bestblockhash"] {
136 serde_json::Value::String(hex_data) => match BlockHash::from_hex(&hex_data) {
137 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid hex data")),
138 Ok(block_hash) => block_hash,
140 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON string")),
143 let height = match &self.0["blocks"] {
144 serde_json::Value::Null => None,
145 serde_json::Value::Number(height) => match height.as_u64() {
146 None => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
147 Some(height) => match height.try_into() {
148 Err(_) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid height")),
149 Ok(height) => Some(height),
152 _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON number")),
159 impl TryInto<Txid> for JsonResponse {
160 type Error = std::io::Error;
161 fn try_into(self) -> std::io::Result<Txid> {
162 match self.0.as_str() {
163 None => Err(std::io::Error::new(
164 std::io::ErrorKind::InvalidData,
165 "expected JSON string",
167 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
168 Err(_) => Err(std::io::Error::new(
169 std::io::ErrorKind::InvalidData,
172 Ok(txid_data) => match encode::deserialize(&txid_data) {
173 Err(_) => Err(std::io::Error::new(
174 std::io::ErrorKind::InvalidData,
177 Ok(txid) => Ok(txid),
184 /// Converts a JSON value into a transaction. WATCH OUT! this cannot be used for zero-input transactions
185 /// (e.g. createrawtransaction). See <https://github.com/rust-bitcoin/rust-bitcoincore-rpc/issues/197>
186 impl TryInto<Transaction> for JsonResponse {
187 type Error = std::io::Error;
188 fn try_into(self) -> std::io::Result<Transaction> {
189 let hex_tx = if self.0.is_object() {
190 // result is json encoded
191 match &self.0["hex"] {
192 // result has hex field
193 serde_json::Value::String(hex_data) => match self.0["complete"] {
194 // result may or may not be signed (e.g. signrawtransactionwithwallet)
195 serde_json::Value::Bool(x) => {
197 let reason = match &self.0["errors"][0]["error"] {
198 serde_json::Value::String(x) => x.as_str(),
199 _ => "Unknown error",
202 return Err(std::io::Error::new(
203 std::io::ErrorKind::InvalidData,
204 format!("transaction couldn't be signed. {}", reason),
210 // result is a complete transaction (e.g. getrawtranaction verbose)
213 _ => return Err(std::io::Error::new(
214 std::io::ErrorKind::InvalidData,
215 "expected JSON string",
219 // result is plain text (e.g. getrawtransaction no verbose)
220 match self.0.as_str() {
221 Some(hex_tx) => hex_tx,
223 return Err(std::io::Error::new(
224 std::io::ErrorKind::InvalidData,
225 "expected JSON string",
231 match Vec::<u8>::from_hex(hex_tx) {
232 Err(_) => Err(std::io::Error::new(
233 std::io::ErrorKind::InvalidData,
236 Ok(tx_data) => match encode::deserialize(&tx_data) {
237 Err(_) => Err(std::io::Error::new(
238 std::io::ErrorKind::InvalidData,
239 "invalid transaction",
248 pub(crate) mod tests {
250 use bitcoin::blockdata::constants::genesis_block;
251 use bitcoin::hashes::Hash;
252 use bitcoin::network::constants::Network;
253 use serde_json::value::Number;
254 use serde_json::Value;
256 /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
257 impl From<BlockHeaderData> for serde_json::Value {
258 fn from(data: BlockHeaderData) -> Self {
259 let BlockHeaderData { chainwork, height, header } = data;
261 "chainwork": chainwork.to_string()["0x".len()..],
263 "version": header.version,
264 "merkleroot": header.merkle_root.to_hex(),
266 "nonce": header.nonce,
267 "bits": header.bits.to_hex(),
268 "previousblockhash": header.prev_blockhash.to_hex(),
274 fn into_block_header_from_json_response_with_unexpected_type() {
275 let response = JsonResponse(serde_json::json!(42));
276 match TryInto::<BlockHeaderData>::try_into(response) {
278 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
279 assert_eq!(e.get_ref().unwrap().to_string(), "unexpected JSON type");
281 Ok(_) => panic!("Expected error"),
286 fn into_block_header_from_json_response_with_unexpected_header_type() {
287 let response = JsonResponse(serde_json::json!([42]));
288 match TryInto::<BlockHeaderData>::try_into(response) {
290 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
291 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
293 Ok(_) => panic!("Expected error"),
298 fn into_block_header_from_json_response_with_invalid_header_response() {
299 let block = genesis_block(Network::Bitcoin);
300 let mut response = JsonResponse(BlockHeaderData {
301 chainwork: block.header.work(),
305 response.0["chainwork"].take();
307 match TryInto::<BlockHeaderData>::try_into(response) {
309 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
310 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header response");
312 Ok(_) => panic!("Expected error"),
317 fn into_block_header_from_json_response_with_invalid_header_data() {
318 let block = genesis_block(Network::Bitcoin);
319 let mut response = JsonResponse(BlockHeaderData {
320 chainwork: block.header.work(),
324 response.0["chainwork"] = serde_json::json!("foobar");
326 match TryInto::<BlockHeaderData>::try_into(response) {
328 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
329 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
331 Ok(_) => panic!("Expected error"),
336 fn into_block_header_from_json_response_with_valid_header() {
337 let block = genesis_block(Network::Bitcoin);
338 let response = JsonResponse(BlockHeaderData {
339 chainwork: block.header.work(),
344 match TryInto::<BlockHeaderData>::try_into(response) {
345 Err(e) => panic!("Unexpected error: {:?}", e),
347 assert_eq!(data.chainwork, block.header.work());
348 assert_eq!(data.height, 0);
349 assert_eq!(data.header, block.header);
355 fn into_block_header_from_json_response_with_valid_header_array() {
356 let genesis_block = genesis_block(Network::Bitcoin);
357 let best_block_header = BlockHeader {
358 prev_blockhash: genesis_block.block_hash(),
359 ..genesis_block.header
361 let chainwork = genesis_block.header.work() + best_block_header.work();
362 let response = JsonResponse(serde_json::json!([
363 serde_json::Value::from(BlockHeaderData {
364 chainwork, height: 1, header: best_block_header,
366 serde_json::Value::from(BlockHeaderData {
367 chainwork: genesis_block.header.work(), height: 0, header: genesis_block.header,
371 match TryInto::<BlockHeaderData>::try_into(response) {
372 Err(e) => panic!("Unexpected error: {:?}", e),
374 assert_eq!(data.chainwork, chainwork);
375 assert_eq!(data.height, 1);
376 assert_eq!(data.header, best_block_header);
382 fn into_block_header_from_json_response_without_previous_block_hash() {
383 let block = genesis_block(Network::Bitcoin);
384 let mut response = JsonResponse(BlockHeaderData {
385 chainwork: block.header.work(),
389 response.0.as_object_mut().unwrap().remove("previousblockhash");
391 match TryInto::<BlockHeaderData>::try_into(response) {
392 Err(e) => panic!("Unexpected error: {:?}", e),
393 Ok(BlockHeaderData { chainwork: _, height: _, header }) => {
394 assert_eq!(header, block.header);
400 fn into_block_from_invalid_binary_response() {
401 let response = BinaryResponse(b"foo".to_vec());
402 match TryInto::<Block>::try_into(response) {
404 Ok(_) => panic!("Expected error"),
409 fn into_block_from_valid_binary_response() {
410 let genesis_block = genesis_block(Network::Bitcoin);
411 let response = BinaryResponse(encode::serialize(&genesis_block));
412 match TryInto::<Block>::try_into(response) {
413 Err(e) => panic!("Unexpected error: {:?}", e),
414 Ok(block) => assert_eq!(block, genesis_block),
419 fn into_block_from_json_response_with_unexpected_type() {
420 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
421 match TryInto::<Block>::try_into(response) {
423 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
424 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
426 Ok(_) => panic!("Expected error"),
431 fn into_block_from_json_response_with_invalid_hex_data() {
432 let response = JsonResponse(serde_json::json!("foobar"));
433 match TryInto::<Block>::try_into(response) {
435 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
436 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
438 Ok(_) => panic!("Expected error"),
443 fn into_block_from_json_response_with_invalid_block_data() {
444 let response = JsonResponse(serde_json::json!("abcd"));
445 match TryInto::<Block>::try_into(response) {
447 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
448 assert_eq!(e.get_ref().unwrap().to_string(), "invalid block data");
450 Ok(_) => panic!("Expected error"),
455 fn into_block_from_json_response_with_valid_block_data() {
456 let genesis_block = genesis_block(Network::Bitcoin);
457 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&genesis_block)));
458 match TryInto::<Block>::try_into(response) {
459 Err(e) => panic!("Unexpected error: {:?}", e),
460 Ok(block) => assert_eq!(block, genesis_block),
465 fn into_block_hash_from_json_response_with_unexpected_type() {
466 let response = JsonResponse(serde_json::json!("foo"));
467 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
469 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
470 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
472 Ok(_) => panic!("Expected error"),
477 fn into_block_hash_from_json_response_with_unexpected_bestblockhash_type() {
478 let response = JsonResponse(serde_json::json!({ "bestblockhash": 42 }));
479 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
481 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
482 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
484 Ok(_) => panic!("Expected error"),
489 fn into_block_hash_from_json_response_with_invalid_hex_data() {
490 let response = JsonResponse(serde_json::json!({ "bestblockhash": "foobar"} ));
491 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
493 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
494 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
496 Ok(_) => panic!("Expected error"),
501 fn into_block_hash_from_json_response_without_height() {
502 let block = genesis_block(Network::Bitcoin);
503 let response = JsonResponse(serde_json::json!({
504 "bestblockhash": block.block_hash().to_hex(),
506 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
507 Err(e) => panic!("Unexpected error: {:?}", e),
508 Ok((hash, height)) => {
509 assert_eq!(hash, block.block_hash());
510 assert!(height.is_none());
516 fn into_block_hash_from_json_response_with_unexpected_blocks_type() {
517 let block = genesis_block(Network::Bitcoin);
518 let response = JsonResponse(serde_json::json!({
519 "bestblockhash": block.block_hash().to_hex(),
522 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
524 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
525 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON number");
527 Ok(_) => panic!("Expected error"),
532 fn into_block_hash_from_json_response_with_invalid_height() {
533 let block = genesis_block(Network::Bitcoin);
534 let response = JsonResponse(serde_json::json!({
535 "bestblockhash": block.block_hash().to_hex(),
536 "blocks": std::u64::MAX,
538 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
540 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
541 assert_eq!(e.get_ref().unwrap().to_string(), "invalid height");
543 Ok(_) => panic!("Expected error"),
548 fn into_block_hash_from_json_response_with_height() {
549 let block = genesis_block(Network::Bitcoin);
550 let response = JsonResponse(serde_json::json!({
551 "bestblockhash": block.block_hash().to_hex(),
554 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
555 Err(e) => panic!("Unexpected error: {:?}", e),
556 Ok((hash, height)) => {
557 assert_eq!(hash, block.block_hash());
558 assert_eq!(height.unwrap(), 1);
564 fn into_txid_from_json_response_with_unexpected_type() {
565 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
566 match TryInto::<Txid>::try_into(response) {
568 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
569 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
571 Ok(_) => panic!("Expected error"),
576 fn into_txid_from_json_response_with_invalid_hex_data() {
577 let response = JsonResponse(serde_json::json!("foobar"));
578 match TryInto::<Txid>::try_into(response) {
580 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
581 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
583 Ok(_) => panic!("Expected error"),
588 fn into_txid_from_json_response_with_invalid_txid_data() {
589 let response = JsonResponse(serde_json::json!("abcd"));
590 match TryInto::<Txid>::try_into(response) {
592 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
593 assert_eq!(e.get_ref().unwrap().to_string(), "invalid txid");
595 Ok(_) => panic!("Expected error"),
600 fn into_txid_from_json_response_with_valid_txid_data() {
601 let target_txid = Txid::from_slice(&[1; 32]).unwrap();
602 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_txid)));
603 match TryInto::<Txid>::try_into(response) {
604 Err(e) => panic!("Unexpected error: {:?}", e),
605 Ok(txid) => assert_eq!(txid, target_txid),
609 // TryInto<Transaction> can be used in two ways, first with plain hex response where data is
610 // the hex encoded transaction (e.g. as a result of getrawtransaction) or as a JSON object
611 // where the hex encoded transaction can be found in the hex field of the object (if present)
612 // (e.g. as a result of signrawtransactionwithwallet).
614 // plain hex transaction
617 fn into_tx_from_json_response_with_invalid_hex_data() {
618 let response = JsonResponse(serde_json::json!("foobar"));
619 match TryInto::<Transaction>::try_into(response) {
621 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
622 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
624 Ok(_) => panic!("Expected error"),
629 fn into_tx_from_json_response_with_invalid_data_type() {
630 let response = JsonResponse(Value::Number(Number::from_f64(1.0).unwrap()));
631 match TryInto::<Transaction>::try_into(response) {
633 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
634 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
636 Ok(_) => panic!("Expected error"),
641 fn into_tx_from_json_response_with_invalid_tx_data() {
642 let response = JsonResponse(serde_json::json!("abcd"));
643 match TryInto::<Transaction>::try_into(response) {
645 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
646 assert_eq!(e.get_ref().unwrap().to_string(), "invalid transaction");
648 Ok(_) => panic!("Expected error"),
653 fn into_tx_from_json_response_with_valid_tx_data_plain() {
654 let genesis_block = genesis_block(Network::Bitcoin);
655 let target_tx = genesis_block.txdata.get(0).unwrap();
656 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_tx)));
657 match TryInto::<Transaction>::try_into(response) {
658 Err(e) => panic!("Unexpected error: {:?}", e),
659 Ok(tx) => assert_eq!(&tx, target_tx),
664 fn into_tx_from_json_response_with_valid_tx_data_hex_field() {
665 let genesis_block = genesis_block(Network::Bitcoin);
666 let target_tx = genesis_block.txdata.get(0).unwrap();
667 let response = JsonResponse(serde_json::json!({"hex": encode::serialize_hex(&target_tx)}));
668 match TryInto::<Transaction>::try_into(response) {
669 Err(e) => panic!("Unexpected error: {:?}", e),
670 Ok(tx) => assert_eq!(&tx, target_tx),
674 // transaction in hex field of JSON object
677 fn into_tx_from_json_response_with_no_hex_field() {
678 let response = JsonResponse(serde_json::json!({ "error": "foo" }));
679 match TryInto::<Transaction>::try_into(response) {
681 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
683 e.get_ref().unwrap().to_string(),
684 "expected JSON string"
687 Ok(_) => panic!("Expected error"),
692 fn into_tx_from_json_response_not_signed() {
693 let response = JsonResponse(serde_json::json!({ "hex": "foo", "complete": false }));
694 match TryInto::<Transaction>::try_into(response) {
696 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
698 e.get_ref().unwrap().to_string().contains(
699 "transaction couldn't be signed")
702 Ok(_) => panic!("Expected error"),