1 use crate::{BlockHeaderData, BlockSourceError};
2 use crate::http::{BinaryResponse, JsonResponse};
3 use crate::utils::hex_to_uint256;
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::{ToHex, FromHex};
10 use serde::Deserialize;
14 use std::convert::From;
15 use std::convert::TryFrom;
16 use std::convert::TryInto;
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 mut 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 if let None = header.get("previousblockhash") {
59 let hash: BlockHash = Default::default();
60 header.as_object_mut().unwrap().insert("previousblockhash".to_string(), serde_json::json!(hash.to_hex()));
63 match serde_json::from_value::<GetHeaderResponse>(header) {
64 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header response")),
65 Ok(response) => match response.try_into() {
66 Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid header data")),
67 Ok(header) => Ok(header),
73 /// Response data from `getblockheader` RPC and `headers` REST requests.
74 #[derive(Deserialize)]
75 struct GetHeaderResponse {
77 pub merkleroot: String,
81 pub previousblockhash: String,
83 pub chainwork: String,
87 /// Converts from `GetHeaderResponse` to `BlockHeaderData`.
88 impl TryFrom<GetHeaderResponse> for BlockHeaderData {
89 type Error = bitcoin::hashes::hex::Error;
91 fn try_from(response: GetHeaderResponse) -> Result<Self, bitcoin::hashes::hex::Error> {
94 version: response.version,
95 prev_blockhash: BlockHash::from_hex(&response.previousblockhash)?,
96 merkle_root: TxMerkleNode::from_hex(&response.merkleroot)?,
98 bits: u32::from_be_bytes(<[u8; 4]>::from_hex(&response.bits)?),
99 nonce: response.nonce,
101 chainwork: hex_to_uint256(&response.chainwork)?,
102 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),
185 pub(crate) mod tests {
187 use bitcoin::blockdata::constants::genesis_block;
188 use bitcoin::consensus::encode;
189 use bitcoin::hashes::Hash;
190 use bitcoin::network::constants::Network;
192 /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
193 impl From<BlockHeaderData> for serde_json::Value {
194 fn from(data: BlockHeaderData) -> Self {
195 let BlockHeaderData { chainwork, height, header } = data;
197 "chainwork": chainwork.to_string()["0x".len()..],
199 "version": header.version,
200 "merkleroot": header.merkle_root.to_hex(),
202 "nonce": header.nonce,
203 "bits": header.bits.to_hex(),
204 "previousblockhash": header.prev_blockhash.to_hex(),
210 fn into_block_header_from_json_response_with_unexpected_type() {
211 let response = JsonResponse(serde_json::json!(42));
212 match TryInto::<BlockHeaderData>::try_into(response) {
214 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
215 assert_eq!(e.get_ref().unwrap().to_string(), "unexpected JSON type");
217 Ok(_) => panic!("Expected error"),
222 fn into_block_header_from_json_response_with_unexpected_header_type() {
223 let response = JsonResponse(serde_json::json!([42]));
224 match TryInto::<BlockHeaderData>::try_into(response) {
226 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
227 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
229 Ok(_) => panic!("Expected error"),
234 fn into_block_header_from_json_response_with_invalid_header_response() {
235 let block = genesis_block(Network::Bitcoin);
236 let mut response = JsonResponse(BlockHeaderData {
237 chainwork: block.header.work(),
241 response.0["chainwork"].take();
243 match TryInto::<BlockHeaderData>::try_into(response) {
245 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
246 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header response");
248 Ok(_) => panic!("Expected error"),
253 fn into_block_header_from_json_response_with_invalid_header_data() {
254 let block = genesis_block(Network::Bitcoin);
255 let mut response = JsonResponse(BlockHeaderData {
256 chainwork: block.header.work(),
260 response.0["chainwork"] = serde_json::json!("foobar");
262 match TryInto::<BlockHeaderData>::try_into(response) {
264 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
265 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
267 Ok(_) => panic!("Expected error"),
272 fn into_block_header_from_json_response_with_valid_header() {
273 let block = genesis_block(Network::Bitcoin);
274 let response = JsonResponse(BlockHeaderData {
275 chainwork: block.header.work(),
280 match TryInto::<BlockHeaderData>::try_into(response) {
281 Err(e) => panic!("Unexpected error: {:?}", e),
283 assert_eq!(data.chainwork, block.header.work());
284 assert_eq!(data.height, 0);
285 assert_eq!(data.header, block.header);
291 fn into_block_header_from_json_response_with_valid_header_array() {
292 let genesis_block = genesis_block(Network::Bitcoin);
293 let best_block_header = BlockHeader {
294 prev_blockhash: genesis_block.block_hash(),
295 ..genesis_block.header
297 let chainwork = genesis_block.header.work() + best_block_header.work();
298 let response = JsonResponse(serde_json::json!([
299 serde_json::Value::from(BlockHeaderData {
300 chainwork, height: 1, header: best_block_header,
302 serde_json::Value::from(BlockHeaderData {
303 chainwork: genesis_block.header.work(), height: 0, header: genesis_block.header,
307 match TryInto::<BlockHeaderData>::try_into(response) {
308 Err(e) => panic!("Unexpected error: {:?}", e),
310 assert_eq!(data.chainwork, chainwork);
311 assert_eq!(data.height, 1);
312 assert_eq!(data.header, best_block_header);
318 fn into_block_header_from_json_response_without_previous_block_hash() {
319 let block = genesis_block(Network::Bitcoin);
320 let mut response = JsonResponse(BlockHeaderData {
321 chainwork: block.header.work(),
325 response.0.as_object_mut().unwrap().remove("previousblockhash");
327 match TryInto::<BlockHeaderData>::try_into(response) {
328 Err(e) => panic!("Unexpected error: {:?}", e),
329 Ok(BlockHeaderData { chainwork: _, height: _, header }) => {
330 assert_eq!(header, block.header);
336 fn into_block_from_invalid_binary_response() {
337 let response = BinaryResponse(b"foo".to_vec());
338 match TryInto::<Block>::try_into(response) {
340 Ok(_) => panic!("Expected error"),
345 fn into_block_from_valid_binary_response() {
346 let genesis_block = genesis_block(Network::Bitcoin);
347 let response = BinaryResponse(encode::serialize(&genesis_block));
348 match TryInto::<Block>::try_into(response) {
349 Err(e) => panic!("Unexpected error: {:?}", e),
350 Ok(block) => assert_eq!(block, genesis_block),
355 fn into_block_from_json_response_with_unexpected_type() {
356 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
357 match TryInto::<Block>::try_into(response) {
359 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
360 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
362 Ok(_) => panic!("Expected error"),
367 fn into_block_from_json_response_with_invalid_hex_data() {
368 let response = JsonResponse(serde_json::json!("foobar"));
369 match TryInto::<Block>::try_into(response) {
371 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
372 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
374 Ok(_) => panic!("Expected error"),
379 fn into_block_from_json_response_with_invalid_block_data() {
380 let response = JsonResponse(serde_json::json!("abcd"));
381 match TryInto::<Block>::try_into(response) {
383 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
384 assert_eq!(e.get_ref().unwrap().to_string(), "invalid block data");
386 Ok(_) => panic!("Expected error"),
391 fn into_block_from_json_response_with_valid_block_data() {
392 let genesis_block = genesis_block(Network::Bitcoin);
393 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&genesis_block)));
394 match TryInto::<Block>::try_into(response) {
395 Err(e) => panic!("Unexpected error: {:?}", e),
396 Ok(block) => assert_eq!(block, genesis_block),
401 fn into_block_hash_from_json_response_with_unexpected_type() {
402 let response = JsonResponse(serde_json::json!("foo"));
403 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
405 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
406 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
408 Ok(_) => panic!("Expected error"),
413 fn into_block_hash_from_json_response_with_unexpected_bestblockhash_type() {
414 let response = JsonResponse(serde_json::json!({ "bestblockhash": 42 }));
415 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
417 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
418 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
420 Ok(_) => panic!("Expected error"),
425 fn into_block_hash_from_json_response_with_invalid_hex_data() {
426 let response = JsonResponse(serde_json::json!({ "bestblockhash": "foobar"} ));
427 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
429 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
430 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
432 Ok(_) => panic!("Expected error"),
437 fn into_block_hash_from_json_response_without_height() {
438 let block = genesis_block(Network::Bitcoin);
439 let response = JsonResponse(serde_json::json!({
440 "bestblockhash": block.block_hash().to_hex(),
442 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
443 Err(e) => panic!("Unexpected error: {:?}", e),
444 Ok((hash, height)) => {
445 assert_eq!(hash, block.block_hash());
446 assert!(height.is_none());
452 fn into_block_hash_from_json_response_with_unexpected_blocks_type() {
453 let block = genesis_block(Network::Bitcoin);
454 let response = JsonResponse(serde_json::json!({
455 "bestblockhash": block.block_hash().to_hex(),
458 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
460 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
461 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON number");
463 Ok(_) => panic!("Expected error"),
468 fn into_block_hash_from_json_response_with_invalid_height() {
469 let block = genesis_block(Network::Bitcoin);
470 let response = JsonResponse(serde_json::json!({
471 "bestblockhash": block.block_hash().to_hex(),
472 "blocks": std::u64::MAX,
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 height");
479 Ok(_) => panic!("Expected error"),
484 fn into_block_hash_from_json_response_with_height() {
485 let block = genesis_block(Network::Bitcoin);
486 let response = JsonResponse(serde_json::json!({
487 "bestblockhash": block.block_hash().to_hex(),
490 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
491 Err(e) => panic!("Unexpected error: {:?}", e),
492 Ok((hash, height)) => {
493 assert_eq!(hash, block.block_hash());
494 assert_eq!(height.unwrap(), 1);
500 fn into_txid_from_json_response_with_unexpected_type() {
501 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
502 match TryInto::<Txid>::try_into(response) {
504 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
505 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
507 Ok(_) => panic!("Expected error"),
512 fn into_txid_from_json_response_with_invalid_hex_data() {
513 let response = JsonResponse(serde_json::json!("foobar"));
514 match TryInto::<Txid>::try_into(response) {
516 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
517 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
519 Ok(_) => panic!("Expected error"),
524 fn into_txid_from_json_response_with_invalid_txid_data() {
525 let response = JsonResponse(serde_json::json!("abcd"));
526 match TryInto::<Txid>::try_into(response) {
528 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
529 assert_eq!(e.get_ref().unwrap().to_string(), "invalid txid");
531 Ok(_) => panic!("Expected error"),
536 fn into_txid_from_json_response_with_valid_txid_data() {
537 let target_txid = Txid::from_slice(&[1; 32]).unwrap();
538 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_txid)));
539 match TryInto::<Txid>::try_into(response) {
540 Err(e) => panic!("Unexpected error: {:?}", e),
541 Ok(txid) => assert_eq!(txid, target_txid),