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};
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")),
160 pub(crate) mod tests {
162 use bitcoin::blockdata::constants::genesis_block;
163 use bitcoin::consensus::encode;
164 use bitcoin::network::constants::Network;
166 /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
167 impl From<BlockHeaderData> for serde_json::Value {
168 fn from(data: BlockHeaderData) -> Self {
169 let BlockHeaderData { chainwork, height, header } = data;
171 "chainwork": chainwork.to_string()["0x".len()..],
173 "version": header.version,
174 "merkleroot": header.merkle_root.to_hex(),
176 "nonce": header.nonce,
177 "bits": header.bits.to_hex(),
178 "previousblockhash": header.prev_blockhash.to_hex(),
184 fn into_block_header_from_json_response_with_unexpected_type() {
185 let response = JsonResponse(serde_json::json!(42));
186 match TryInto::<BlockHeaderData>::try_into(response) {
188 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
189 assert_eq!(e.get_ref().unwrap().to_string(), "unexpected JSON type");
191 Ok(_) => panic!("Expected error"),
196 fn into_block_header_from_json_response_with_unexpected_header_type() {
197 let response = JsonResponse(serde_json::json!([42]));
198 match TryInto::<BlockHeaderData>::try_into(response) {
200 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
201 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
203 Ok(_) => panic!("Expected error"),
208 fn into_block_header_from_json_response_with_invalid_header_response() {
209 let block = genesis_block(Network::Bitcoin);
210 let mut response = JsonResponse(BlockHeaderData {
211 chainwork: block.header.work(),
215 response.0["chainwork"].take();
217 match TryInto::<BlockHeaderData>::try_into(response) {
219 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
220 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header response");
222 Ok(_) => panic!("Expected error"),
227 fn into_block_header_from_json_response_with_invalid_header_data() {
228 let block = genesis_block(Network::Bitcoin);
229 let mut response = JsonResponse(BlockHeaderData {
230 chainwork: block.header.work(),
234 response.0["chainwork"] = serde_json::json!("foobar");
236 match TryInto::<BlockHeaderData>::try_into(response) {
238 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
239 assert_eq!(e.get_ref().unwrap().to_string(), "invalid header data");
241 Ok(_) => panic!("Expected error"),
246 fn into_block_header_from_json_response_with_valid_header() {
247 let block = genesis_block(Network::Bitcoin);
248 let response = JsonResponse(BlockHeaderData {
249 chainwork: block.header.work(),
254 match TryInto::<BlockHeaderData>::try_into(response) {
255 Err(e) => panic!("Unexpected error: {:?}", e),
257 assert_eq!(data.chainwork, block.header.work());
258 assert_eq!(data.height, 0);
259 assert_eq!(data.header, block.header);
265 fn into_block_header_from_json_response_with_valid_header_array() {
266 let genesis_block = genesis_block(Network::Bitcoin);
267 let best_block_header = BlockHeader {
268 prev_blockhash: genesis_block.block_hash(),
269 ..genesis_block.header
271 let chainwork = genesis_block.header.work() + best_block_header.work();
272 let response = JsonResponse(serde_json::json!([
273 serde_json::Value::from(BlockHeaderData {
274 chainwork, height: 1, header: best_block_header,
276 serde_json::Value::from(BlockHeaderData {
277 chainwork: genesis_block.header.work(), height: 0, header: genesis_block.header,
281 match TryInto::<BlockHeaderData>::try_into(response) {
282 Err(e) => panic!("Unexpected error: {:?}", e),
284 assert_eq!(data.chainwork, chainwork);
285 assert_eq!(data.height, 1);
286 assert_eq!(data.header, best_block_header);
292 fn into_block_header_from_json_response_without_previous_block_hash() {
293 let block = genesis_block(Network::Bitcoin);
294 let mut response = JsonResponse(BlockHeaderData {
295 chainwork: block.header.work(),
299 response.0.as_object_mut().unwrap().remove("previousblockhash");
301 match TryInto::<BlockHeaderData>::try_into(response) {
302 Err(e) => panic!("Unexpected error: {:?}", e),
303 Ok(BlockHeaderData { chainwork: _, height: _, header }) => {
304 assert_eq!(header, block.header);
310 fn into_block_from_invalid_binary_response() {
311 let response = BinaryResponse(b"foo".to_vec());
312 match TryInto::<Block>::try_into(response) {
314 Ok(_) => panic!("Expected error"),
319 fn into_block_from_valid_binary_response() {
320 let genesis_block = genesis_block(Network::Bitcoin);
321 let response = BinaryResponse(encode::serialize(&genesis_block));
322 match TryInto::<Block>::try_into(response) {
323 Err(e) => panic!("Unexpected error: {:?}", e),
324 Ok(block) => assert_eq!(block, genesis_block),
329 fn into_block_from_json_response_with_unexpected_type() {
330 let response = JsonResponse(serde_json::json!({ "result": "foo" }));
331 match TryInto::<Block>::try_into(response) {
333 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
334 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
336 Ok(_) => panic!("Expected error"),
341 fn into_block_from_json_response_with_invalid_hex_data() {
342 let response = JsonResponse(serde_json::json!("foobar"));
343 match TryInto::<Block>::try_into(response) {
345 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
346 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
348 Ok(_) => panic!("Expected error"),
353 fn into_block_from_json_response_with_invalid_block_data() {
354 let response = JsonResponse(serde_json::json!("abcd"));
355 match TryInto::<Block>::try_into(response) {
357 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
358 assert_eq!(e.get_ref().unwrap().to_string(), "invalid block data");
360 Ok(_) => panic!("Expected error"),
365 fn into_block_from_json_response_with_valid_block_data() {
366 let genesis_block = genesis_block(Network::Bitcoin);
367 let response = JsonResponse(serde_json::json!(encode::serialize_hex(&genesis_block)));
368 match TryInto::<Block>::try_into(response) {
369 Err(e) => panic!("Unexpected error: {:?}", e),
370 Ok(block) => assert_eq!(block, genesis_block),
375 fn into_block_hash_from_json_response_with_unexpected_type() {
376 let response = JsonResponse(serde_json::json!("foo"));
377 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
379 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
380 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON object");
382 Ok(_) => panic!("Expected error"),
387 fn into_block_hash_from_json_response_with_unexpected_bestblockhash_type() {
388 let response = JsonResponse(serde_json::json!({ "bestblockhash": 42 }));
389 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
391 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
392 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
394 Ok(_) => panic!("Expected error"),
399 fn into_block_hash_from_json_response_with_invalid_hex_data() {
400 let response = JsonResponse(serde_json::json!({ "bestblockhash": "foobar"} ));
401 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
403 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
404 assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
406 Ok(_) => panic!("Expected error"),
411 fn into_block_hash_from_json_response_without_height() {
412 let block = genesis_block(Network::Bitcoin);
413 let response = JsonResponse(serde_json::json!({
414 "bestblockhash": block.block_hash().to_hex(),
416 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
417 Err(e) => panic!("Unexpected error: {:?}", e),
418 Ok((hash, height)) => {
419 assert_eq!(hash, block.block_hash());
420 assert!(height.is_none());
426 fn into_block_hash_from_json_response_with_unexpected_blocks_type() {
427 let block = genesis_block(Network::Bitcoin);
428 let response = JsonResponse(serde_json::json!({
429 "bestblockhash": block.block_hash().to_hex(),
432 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
434 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
435 assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON number");
437 Ok(_) => panic!("Expected error"),
442 fn into_block_hash_from_json_response_with_invalid_height() {
443 let block = genesis_block(Network::Bitcoin);
444 let response = JsonResponse(serde_json::json!({
445 "bestblockhash": block.block_hash().to_hex(),
446 "blocks": std::u64::MAX,
448 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
450 assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
451 assert_eq!(e.get_ref().unwrap().to_string(), "invalid height");
453 Ok(_) => panic!("Expected error"),
458 fn into_block_hash_from_json_response_with_height() {
459 let block = genesis_block(Network::Bitcoin);
460 let response = JsonResponse(serde_json::json!({
461 "bestblockhash": block.block_hash().to_hex(),
464 match TryInto::<(BlockHash, Option<u32>)>::try_into(response) {
465 Err(e) => panic!("Unexpected error: {:?}", e),
466 Ok((hash, height)) => {
467 assert_eq!(hash, block.block_hash());
468 assert_eq!(height.unwrap(), 1);