diff --git a/chronik/chronik-indexer/src/query/blocks.rs b/chronik/chronik-indexer/src/query/blocks.rs --- a/chronik/chronik-indexer/src/query/blocks.rs +++ b/chronik/chronik-indexer/src/query/blocks.rs @@ -5,7 +5,6 @@ //! Module for [`QueryBlocks`], to query blocks. use abc_rust_error::Result; -use bitcoinsuite_core::block::BlockHash; use chronik_db::{ db::Db, io::{BlockHeight, BlockReader, BlockStats, BlockStatsReader, DbBlock}, @@ -13,7 +12,7 @@ use chronik_proto::proto; use thiserror::Error; -use crate::avalanche::Avalanche; +use crate::{avalanche::Avalanche, query::HashOrHeight}; const MAX_BLOCKS_PAGE_SIZE: usize = 500; @@ -29,10 +28,6 @@ /// Errors indicating something went wrong with querying blocks. #[derive(Debug, Error, PartialEq)] pub enum QueryBlockError { - /// Query is neither a hex hash nor an integer string - #[error("400: Not a hash or height: {0}")] - NotHashOrHeight(String), - /// Block not found in DB #[error("404: Block not found: {0}")] BlockNotFound(String), @@ -70,16 +65,9 @@ ) -> Result { let db_blocks = BlockReader::new(self.db)?; let block_stats_reader = BlockStatsReader::new(self.db)?; - let db_block = if let Ok(hash) = hash_or_height.parse::() { - db_blocks.by_hash(&hash)? - } else { - let height = match hash_or_height.parse::() { - // disallow leading zeros - Ok(0) if hash_or_height.len() == 1 => 0, - Ok(height) if !hash_or_height.starts_with('0') => height, - _ => return Err(NotHashOrHeight(hash_or_height).into()), - }; - db_blocks.by_height(height)? + let db_block = match hash_or_height.parse::()? { + HashOrHeight::Hash(hash) => db_blocks.by_hash(&hash)?, + HashOrHeight::Height(height) => db_blocks.by_height(height)?, }; let db_block = db_block.ok_or(BlockNotFound(hash_or_height))?; let block_stats = block_stats_reader diff --git a/chronik/chronik-indexer/src/query/util.rs b/chronik/chronik-indexer/src/query/util.rs --- a/chronik/chronik-indexer/src/query/util.rs +++ b/chronik/chronik-indexer/src/query/util.rs @@ -2,14 +2,20 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -use std::collections::{hash_map::Entry, BTreeMap, HashMap}; +use std::{ + collections::{hash_map::Entry, BTreeMap, HashMap}, + str::FromStr, +}; -use abc_rust_error::Result; +use abc_rust_error::{Report, Result}; use bitcoinsuite_core::{ + block::BlockHash, ser::BitcoinSer, tx::{OutPoint, SpentBy, Tx, TxId}, }; -use chronik_db::io::{DbBlock, SpentByEntry, SpentByReader, TxNum, TxReader}; +use chronik_db::io::{ + BlockHeight, DbBlock, SpentByEntry, SpentByReader, TxNum, TxReader, +}; use chronik_proto::proto; use thiserror::Error; @@ -29,6 +35,10 @@ /// The offending entry in the DB that references an unknown tx. entry: SpentByEntry, }, + + /// Query is neither a hex hash nor an integer string + #[error("400: Not a hash or height: {0}")] + NotHashOrHeight(String), } use self::QueryUtilError::*; @@ -103,6 +113,33 @@ } } +pub(crate) enum HashOrHeight { + Hash(BlockHash), + Height(BlockHeight), +} + +impl FromStr for HashOrHeight { + type Err = Report; + + fn from_str(hash_or_height: &str) -> Result { + if let Ok(hash) = hash_or_height.parse::() { + Ok(HashOrHeight::Hash(hash)) + } else { + let height = match hash_or_height.parse::() { + // disallow leading zeros + Ok(0) if hash_or_height.len() == 1 => 0, + Ok(height) if !hash_or_height.starts_with('0') => height, + _ => { + return Err( + NotHashOrHeight(hash_or_height.to_string()).into() + ); + } + }; + Ok(HashOrHeight::Height(height)) + } + } +} + /// Helper struct for querying which tx outputs have been spent by DB or mempool /// txs. pub(crate) struct OutputsSpent<'a> {