diff --git a/Cargo.lock b/Cargo.lock index 34ad68265..92731e493 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -647,6 +647,7 @@ dependencies = [ "jiff", "minreq", "serde_json", + "thiserror 2.0.17", "tokio", "vecdb", ] @@ -4189,7 +4190,9 @@ dependencies = [ [[package]] name = "rawdb" -version = "0.4.0" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd96eb8d340052584b01120ce302e283b9176b278b5b5944a5f0fc00493f861" dependencies = [ "libc", "log", @@ -5370,7 +5373,9 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" [[package]] name = "vecdb" -version = "0.4.0" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "371b5a52a650ac0661a56ceae92e8af88e7930448ae9d3bb29c68a71437dc5d4" dependencies = [ "ctrlc", "log", @@ -5388,7 +5393,9 @@ dependencies = [ [[package]] name = "vecdb_derive" -version = "0.4.0" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f035c1c36fae53aeed226bdda0009292294986ea0ed6a932535cacdbec08d73d" dependencies = [ "quote", "syn 2.0.111", diff --git a/Cargo.toml b/Cargo.toml index 58be4b15d..3a9c7eff4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,9 +76,9 @@ serde_derive = "1.0.228" serde_json = { version = "1.0.145", features = ["float_roundtrip"] } smallvec = "1.15.1" tokio = { version = "1.48.0", features = ["rt-multi-thread"] } -vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco"] } +vecdb = { version = "0.4.1", features = ["derive", "serde_json", "pco"] } +# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco"] } # vecdb = { git = "https://github.com/anydb-rs/anydb", features = ["derive", "serde_json", "pco"] } -# vecdb = { version = "0.4.0", features = ["derive", "serde_json", "pco"] } [workspace.metadata.release] shared-version = true diff --git a/crates/brk_bencher/src/lib.rs b/crates/brk_bencher/src/lib.rs index 40a2f3d1c..9eda9ee3d 100644 --- a/crates/brk_bencher/src/lib.rs +++ b/crates/brk_bencher/src/lib.rs @@ -9,7 +9,7 @@ use std::{ time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; -use brk_error::Result; +use brk_error::{Error, Result}; mod disk; mod io; @@ -84,7 +84,7 @@ impl Bencher { current = current .parent() - .ok_or("Workspace root not found")? + .ok_or(Error::NotFound("Workspace root not found".into()))? .to_path_buf(); }; @@ -94,7 +94,7 @@ impl Bencher { /// Start monitoring disk usage and memory footprint pub fn start(&mut self) -> Result<()> { if self.0.monitor_thread.lock().is_some() { - return Err("Bencher already started".into()); + return Err(Error::Internal("Bencher already started")); } let stop_flag = self.0.stop_flag.clone(); @@ -113,7 +113,7 @@ impl Bencher { self.0.stop_flag.store(true, Ordering::Relaxed); if let Some(handle) = self.0.monitor_thread.lock().take() { - handle.join().map_err(|_| "Monitor thread panicked")??; + handle.join().map_err(|_| Error::Internal("Monitor thread panicked"))??; } self.0.progression.flush()?; diff --git a/crates/brk_cli/src/config.rs b/crates/brk_cli/src/config.rs index a4bb7c8ad..8d534e0c8 100644 --- a/crates/brk_cli/src/config.rs +++ b/crates/brk_cli/src/config.rs @@ -218,7 +218,7 @@ Finally, you can run the program with '-h' for help." self.rpcpassword.clone().unwrap(), )) } else { - Err(Error::Str("Failed to find correct auth")) + Err(Error::AuthFailed) } } diff --git a/crates/brk_computer/examples/computer_bench.rs b/crates/brk_computer/examples/computer_bench.rs index 6868f373e..905f8d082 100644 --- a/crates/brk_computer/examples/computer_bench.rs +++ b/crates/brk_computer/examples/computer_bench.rs @@ -9,7 +9,7 @@ use brk_iterator::Blocks; use brk_reader::Reader; use brk_rpc::{Auth, Client}; use log::{debug, info}; -use vecdb::{AnyStoredVec, Exit}; +use vecdb::Exit; pub fn main() -> Result<()> { // Can't increase main thread's stack size, thus we need to use another thread @@ -45,15 +45,6 @@ fn run() -> Result<()> { let mut computer = Computer::forced_import(&outputs_benches_dir, &indexer, Some(fetcher))?; - dbg!( - computer - .indexes - .txinindex_to_txoutindex - .region() - .meta() - .reserved() - ); - let mut bencher = Bencher::from_cargo_env(env!("CARGO_PKG_NAME"), &outputs_dir.join("computed"))?; bencher.start()?; diff --git a/crates/brk_computer/examples/computer_read.rs b/crates/brk_computer/examples/computer_read.rs index 85708afe9..6ab57d6ba 100644 --- a/crates/brk_computer/examples/computer_read.rs +++ b/crates/brk_computer/examples/computer_read.rs @@ -5,7 +5,7 @@ use brk_error::Result; use brk_fetcher::Fetcher; use brk_indexer::Indexer; use brk_types::TxIndex; -use vecdb::{AnyStoredVec, Exit, GenericStoredVec}; +use vecdb::{Exit, GenericStoredVec}; pub fn main() -> Result<()> { // Can't increase main thread's stack size, thus we need to use another thread @@ -61,18 +61,7 @@ fn run() -> Result<()> { .txindex_to_output_count .read_once(txindex)?; dbg!(output_count); - let _ = dbg!( - computer - .indexes - .txinindex_to_txoutindex - .read_once(first_txinindex) - ); - let _ = dbg!( - computer - .indexes - .txinindex_to_txoutindex - .read_once(first_txinindex + 1) - ); + let _ = dbg!(computer.chain.txinindex_to_value.read_once(first_txinindex)); let _ = dbg!( computer @@ -98,13 +87,6 @@ fn run() -> Result<()> { let _ = dbg!(computer.chain.txindex_to_input_value.read_once(txindex)); let _ = dbg!(computer.chain.txindex_to_output_value.read_once(txindex)); // dbg!(computer.indexes.txindex_to_txindex.ge(txindex)); - dbg!( - computer - .indexes - .txinindex_to_txoutindex - .region() - .meta() - .len() - ); + Ok(()) } diff --git a/crates/brk_computer/src/chain.rs b/crates/brk_computer/src/chain.rs index a2053366a..1e0735ef8 100644 --- a/crates/brk_computer/src/chain.rs +++ b/crates/brk_computer/src/chain.rs @@ -853,19 +853,33 @@ impl Vecs { compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v2, TxVersion::TWO)?; compute_indexes_to_tx_vany(&mut self.indexes_to_tx_v3, TxVersion::THREE)?; + // --- + // TxInIndex + // --- + + let txindex_to_first_txoutindex = &indexer.vecs.tx.txindex_to_first_txoutindex; + let txindex_to_first_txoutindex_reader = txindex_to_first_txoutindex.create_reader(); let txoutindex_to_value = &indexer.vecs.txout.txoutindex_to_value; let txoutindex_to_value_reader = indexer.vecs.txout.txoutindex_to_value.create_reader(); self.txinindex_to_value.compute_transform( starting_indexes.txinindex, - &indexes.txinindex_to_txoutindex, - |(txinindex, txoutindex, ..)| { - let value = if txoutindex == TxOutIndex::COINBASE { - Sats::MAX + &indexer.vecs.txin.txinindex_to_outpoint, + |(txinindex, outpoint, ..)| { + if unlikely(outpoint.is_coinbase()) { + return (txinindex, Sats::MAX); + } + let txoutindex = txindex_to_first_txoutindex + .read_unwrap(outpoint.txindex(), &txindex_to_first_txoutindex_reader) + + outpoint.vout(); + + let value = if unlikely(txoutindex == TxOutIndex::COINBASE) { + unreachable!() } else { txoutindex_to_value .unchecked_read(txoutindex, &txoutindex_to_value_reader) .unwrap() }; + (txinindex, value) }, exit, diff --git a/crates/brk_computer/src/grouped/builder_eager.rs b/crates/brk_computer/src/grouped/builder_eager.rs index eae7e618e..669d0a13c 100644 --- a/crates/brk_computer/src/grouped/builder_eager.rs +++ b/crates/brk_computer/src/grouped/builder_eager.rs @@ -159,7 +159,7 @@ where /// Compute percentiles from sorted values (assumes values is already sorted) fn compute_percentiles_from_sorted(&mut self, index: usize, values: &[T]) -> Result<()> { if let Some(max) = self.max.as_mut() { - max.truncate_push_at(index, *values.last().ok_or(Error::Str("expect some"))?)?; + max.truncate_push_at(index, *values.last().ok_or(Error::Internal("Empty values for percentiles"))?)?; } if let Some(pct90) = self.pct90.as_mut() { pct90.truncate_push_at(index, get_percentile(values, 0.90))?; diff --git a/crates/brk_computer/src/indexes.rs b/crates/brk_computer/src/indexes.rs index c57807b2b..c0ff0f271 100644 --- a/crates/brk_computer/src/indexes.rs +++ b/crates/brk_computer/src/indexes.rs @@ -13,8 +13,8 @@ use brk_types::{ YearIndex, }; use vecdb::{ - Database, EagerVec, Exit, GenericStoredVec, ImportableVec, IterableCloneableVec, LazyVecFrom1, - PAGE_SIZE, PcoVec, TypedVecIterator, unlikely, + Database, EagerVec, Exit, ImportableVec, IterableCloneableVec, LazyVecFrom1, PAGE_SIZE, PcoVec, + TypedVecIterator, }; const VERSION: Version = Version::ZERO; @@ -83,7 +83,6 @@ pub struct Vecs { pub txindex_to_output_count: EagerVec>, pub txindex_to_txindex: LazyVecFrom1, pub txinindex_to_txinindex: LazyVecFrom1, - pub txinindex_to_txoutindex: EagerVec>, pub txoutindex_to_txoutindex: LazyVecFrom1, pub unknownoutputindex_to_unknownoutputindex: LazyVecFrom1, @@ -121,7 +120,6 @@ impl Vecs { } let this = Self { - txinindex_to_txoutindex: eager!("txoutindex"), txoutindex_to_txoutindex: lazy!("txoutindex", indexer.vecs.txout.txoutindex_to_value), txinindex_to_txinindex: lazy!("txinindex", indexer.vecs.txin.txinindex_to_outpoint), p2pk33addressindex_to_p2pk33addressindex: lazy!( @@ -247,27 +245,6 @@ impl Vecs { starting_indexes: brk_indexer::Indexes, exit: &Exit, ) -> Result { - // --- - // TxInIndex - // --- - - let txindex_to_first_txoutindex = &indexer.vecs.tx.txindex_to_first_txoutindex; - let txindex_to_first_txoutindex_reader = txindex_to_first_txoutindex.create_reader(); - self.txinindex_to_txoutindex.compute_transform( - starting_indexes.txinindex, - &indexer.vecs.txin.txinindex_to_outpoint, - |(txinindex, outpoint, ..)| { - if unlikely(outpoint.is_coinbase()) { - return (txinindex, TxOutIndex::COINBASE); - } - let txoutindex = txindex_to_first_txoutindex - .read_unwrap(outpoint.txindex(), &txindex_to_first_txoutindex_reader) - + outpoint.vout(); - (txinindex, txoutindex) - }, - exit, - )?; - // --- // TxIndex // --- diff --git a/crates/brk_computer/src/stateful/common/import.rs b/crates/brk_computer/src/stateful/common/import.rs index 33fc6d90c..51973be6b 100644 --- a/crates/brk_computer/src/stateful/common/import.rs +++ b/crates/brk_computer/src/stateful/common/import.rs @@ -821,7 +821,7 @@ impl Vecs { Ok(prev_height.incremented()) } else { - Err(Error::Str("Unset")) + Err(Error::Internal("No previous height to import state from")) } } diff --git a/crates/brk_computer/src/states/price_to_amount.rs b/crates/brk_computer/src/states/price_to_amount.rs index e3a9bfd44..343fb9e89 100644 --- a/crates/brk_computer/src/states/price_to_amount.rs +++ b/crates/brk_computer/src/states/price_to_amount.rs @@ -37,7 +37,7 @@ impl PriceToAmount { let (&height, path) = files .range(..=height) .next_back() - .ok_or(Error::Str("Not found"))?; + .ok_or(Error::NotFound("No price state found at or before height".into()))?; self.state = Some(State::deserialize(&fs::read(path)?)?); Ok(height) } diff --git a/crates/brk_error/Cargo.toml b/crates/brk_error/Cargo.toml index 91a5f9882..6f81c04ea 100644 --- a/crates/brk_error/Cargo.toml +++ b/crates/brk_error/Cargo.toml @@ -15,5 +15,6 @@ fjall = { workspace = true } jiff = { workspace = true } minreq = { workspace = true } serde_json = { workspace = true } +thiserror = "2.0" tokio = { workspace = true } vecdb = { workspace = true } diff --git a/crates/brk_error/src/lib.rs b/crates/brk_error/src/lib.rs index 094385f7c..27a3cf852 100644 --- a/crates/brk_error/src/lib.rs +++ b/crates/brk_error/src/lib.rs @@ -1,210 +1,130 @@ #![doc = include_str!("../README.md")] -use std::{ - fmt::{self, Debug, Display}, - io, result, time, -}; +use std::{io, result, time}; + +use thiserror::Error; pub type Result = result::Result; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum Error { - IO(io::Error), - BitcoinRPC(bitcoincore_rpc::Error), - Jiff(jiff::Error), - Fjall(fjall::Error), - VecDB(vecdb::Error), - RawDB(vecdb::RawDBError), - Minreq(minreq::Error), - SystemTimeError(time::SystemTimeError), - BitcoinConsensusEncode(bitcoin::consensus::encode::Error), - BitcoinBip34Error(bitcoin::block::Bip34Error), - BitcoinHexError(bitcoin::consensus::encode::FromHexError), - BitcoinFromScriptError(bitcoin::address::FromScriptError), - BitcoinHexToArrayError(bitcoin::hex::HexToArrayError), - SerdeJSON(serde_json::Error), - TokioJoin(tokio::task::JoinError), - ZeroCopyError, - Vecs(vecdb::Error), + #[error(transparent)] + IO(#[from] io::Error), + #[error(transparent)] + BitcoinRPC(#[from] bitcoincore_rpc::Error), + + #[error(transparent)] + Jiff(#[from] jiff::Error), + + #[error(transparent)] + Fjall(#[from] fjall::Error), + + #[error(transparent)] + VecDB(#[from] vecdb::Error), + + #[error(transparent)] + RawDB(#[from] vecdb::RawDBError), + + #[error(transparent)] + Minreq(#[from] minreq::Error), + + #[error(transparent)] + SystemTimeError(#[from] time::SystemTimeError), + + #[error(transparent)] + BitcoinConsensusEncode(#[from] bitcoin::consensus::encode::Error), + + #[error(transparent)] + BitcoinBip34Error(#[from] bitcoin::block::Bip34Error), + + #[error(transparent)] + BitcoinHexError(#[from] bitcoin::consensus::encode::FromHexError), + + #[error(transparent)] + BitcoinFromScriptError(#[from] bitcoin::address::FromScriptError), + + #[error(transparent)] + BitcoinHexToArrayError(#[from] bitcoin::hex::HexToArrayError), + + #[error(transparent)] + SerdeJSON(#[from] serde_json::Error), + + #[error(transparent)] + TokioJoin(#[from] tokio::task::JoinError), + + #[error("ZeroCopy error")] + ZeroCopyError, + + #[error("Wrong length, expected: {expected}, received: {received}")] WrongLength { expected: usize, received: usize }, + + #[error("Wrong address type")] WrongAddressType, + + #[error("Date cannot be indexed, must be 2009-01-03, 2009-01-09 or greater")] UnindexableDate, + + #[error("Quick cache error")] QuickCacheError, + #[error("The provided address appears to be invalid")] InvalidAddress, + + #[error("Invalid network")] InvalidNetwork, + + #[error("The provided TXID appears to be invalid")] InvalidTxid, + + #[error("Mempool data is not available")] MempoolNotAvailable, + + #[error("Address not found in the blockchain (no transaction history)")] UnknownAddress, + + #[error("Failed to find the TXID in the blockchain")] UnknownTxid, + + #[error("Unsupported type ({0})")] UnsupportedType(String), - Str(&'static str), - String(String), + // Generic errors with context + #[error("{0}")] + NotFound(String), + + #[error("{0}")] + OutOfRange(String), + + #[error("Parse error: {0}")] + Parse(String), + + #[error("Internal error: {0}")] + Internal(&'static str), + + #[error("Authentication failed")] + AuthFailed, + + // Metric-specific errors + #[error("'{metric}' not found{}", suggestion.as_ref().map(|s| format!(", did you mean '{s}'?")).unwrap_or_default())] + MetricNotFound { + metric: String, + suggestion: Option, + }, + + #[error("'{metric}' doesn't support the requested index. Supported indexes: {supported}")] + MetricUnsupportedIndex { metric: String, supported: String }, + + #[error("No metrics specified")] + NoMetrics, + + #[error("Request weight {requested} exceeds maximum {max}")] + WeightExceeded { requested: usize, max: usize }, + + #[error("Fetch failed after retries: {0}")] + FetchFailed(String), } -impl From for Error { - #[inline] - fn from(value: bitcoin::block::Bip34Error) -> Self { - Self::BitcoinBip34Error(value) - } -} - -impl From for Error { - #[inline] - fn from(value: bitcoin::consensus::encode::Error) -> Self { - Self::BitcoinConsensusEncode(value) - } -} - -impl From for Error { - #[inline] - fn from(value: bitcoin::consensus::encode::FromHexError) -> Self { - Self::BitcoinHexError(value) - } -} - -impl From for Error { - #[inline] - fn from(value: bitcoin::hex::HexToArrayError) -> Self { - Self::BitcoinHexToArrayError(value) - } -} - -impl From for Error { - #[inline] - fn from(value: bitcoin::address::FromScriptError) -> Self { - Self::BitcoinFromScriptError(value) - } -} - -impl From for Error { - #[inline] - fn from(value: time::SystemTimeError) -> Self { - Self::SystemTimeError(value) - } -} - -impl From for Error { - #[inline] - fn from(error: serde_json::Error) -> Self { - Self::SerdeJSON(error) - } -} - -impl From for Error { - #[inline] - fn from(error: tokio::task::JoinError) -> Self { - Self::TokioJoin(error) - } -} - -impl From for Error { - #[inline] - fn from(value: io::Error) -> Self { - Self::IO(value) - } -} - -impl From for Error { - #[inline] - fn from(value: vecdb::Error) -> Self { - Self::VecDB(value) - } -} - -impl From for Error { - #[inline] - fn from(value: vecdb::RawDBError) -> Self { - Self::RawDB(value) - } -} - -impl From for Error { - #[inline] - fn from(value: bitcoincore_rpc::Error) -> Self { - Self::BitcoinRPC(value) - } -} - -impl From for Error { - #[inline] - fn from(value: minreq::Error) -> Self { - Self::Minreq(value) - } -} - -impl From for Error { - #[inline] - fn from(value: jiff::Error) -> Self { - Self::Jiff(value) - } -} - -impl From for Error { - #[inline] - fn from(value: fjall::Error) -> Self { - Self::Fjall(value) - } -} - -impl From<&'static str> for Error { - #[inline] - fn from(value: &'static str) -> Self { - Self::Str(value) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::BitcoinConsensusEncode(error) => Display::fmt(&error, f), - Error::BitcoinBip34Error(error) => Display::fmt(&error, f), - Error::BitcoinFromScriptError(error) => Display::fmt(&error, f), - Error::BitcoinHexError(error) => Display::fmt(&error, f), - Error::BitcoinHexToArrayError(error) => Display::fmt(&error, f), - Error::BitcoinRPC(error) => Display::fmt(&error, f), - Error::Fjall(error) => Display::fmt(&error, f), - Error::IO(error) => Display::fmt(&error, f), - Error::Jiff(error) => Display::fmt(&error, f), - Error::Minreq(error) => Display::fmt(&error, f), - Error::RawDB(error) => Display::fmt(&error, f), - Error::SerdeJSON(error) => Display::fmt(&error, f), - Error::SystemTimeError(error) => Display::fmt(&error, f), - Error::TokioJoin(error) => Display::fmt(&error, f), - Error::VecDB(error) => Display::fmt(&error, f), - Error::Vecs(error) => Display::fmt(&error, f), - Error::ZeroCopyError => write!(f, "ZeroCopy error"), - Error::WrongLength { expected, received } => write!( - f, - "Wrong length, expected: {expected}, received: {received}" - ), - Error::QuickCacheError => write!(f, "Quick cache error"), - Error::WrongAddressType => write!(f, "Wrong address type"), - Error::UnindexableDate => write!( - f, - "Date cannot be indexed, must be 2009-01-03, 2009-01-09 or greater" - ), - - Error::InvalidTxid => write!(f, "The provided TXID appears to be invalid"), - Error::InvalidNetwork => write!(f, "Invalid network"), - Error::InvalidAddress => write!(f, "The provided address appears to be invalid"), - Error::MempoolNotAvailable => write!(f, "Mempool data is not available"), - Error::UnknownAddress => write!( - f, - "Address not found in the blockchain (no transaction history)" - ), - Error::UnknownTxid => write!(f, "Failed to find the TXID in the blockchain"), - Error::UnsupportedType(t) => write!(f, "Unsupported type ({t})"), - - Error::Str(s) => write!(f, "{s}"), - Error::String(s) => write!(f, "{s}"), - } - } -} - -impl std::error::Error for Error {} impl Error { /// Returns true if this network/fetch error indicates a permanent/blocking condition diff --git a/crates/brk_fetcher/src/binance.rs b/crates/brk_fetcher/src/binance.rs index 9fb473a8d..a8f3d29fe 100644 --- a/crates/brk_fetcher/src/binance.rs +++ b/crates/brk_fetcher/src/binance.rs @@ -88,7 +88,7 @@ impl Binance { .unwrap() .get(date) .cloned() - .ok_or(Error::Str("Couldn't find date")) + .ok_or(Error::NotFound("Couldn't find date".into())) } pub fn fetch_1d() -> Result> { @@ -102,7 +102,7 @@ impl Binance { fn read_har(&self) -> Result> { if self.path.is_none() { - return Err(Error::Str("Path missing")); + return Err(Error::NotFound("HAR path not configured".into())); } info!("Reading Binance har file..."); @@ -116,7 +116,7 @@ impl Binance { let file = if let Ok(file) = File::open(path_binance_har) { file } else { - return Err(Error::Str("Missing binance file")); + return Err(Error::NotFound("Binance HAR file not found".into())); }; let reader = BufReader::new(file); @@ -128,13 +128,13 @@ impl Binance { }; json.get("log") - .ok_or(Error::Str("Expect object to have log attribute"))? + .ok_or(Error::Parse("HAR missing 'log' field".into()))? .as_object() - .ok_or(Error::Str("Expect to be an object"))? + .ok_or(Error::Parse("HAR 'log' is not an object".into()))? .get("entries") - .ok_or(Error::Str("Expect object to have entries"))? + .ok_or(Error::Parse("HAR missing 'entries' field".into()))? .as_array() - .ok_or(Error::Str("Expect to be an array"))? + .ok_or(Error::Parse("HAR 'entries' is not an array".into()))? .iter() .filter(|entry| { entry @@ -180,7 +180,7 @@ impl Binance { fn parse_ohlc_array(json: &Value) -> Result> { let result = json .as_array() - .ok_or(Error::Str("Expected JSON array"))? + .ok_or(Error::Parse("Expected JSON array".into()))? .iter() .filter_map(|v| v.as_array()) .map(|arr| { diff --git a/crates/brk_fetcher/src/brk.rs b/crates/brk_fetcher/src/brk.rs index 44b369d09..9293148a3 100644 --- a/crates/brk_fetcher/src/brk.rs +++ b/crates/brk_fetcher/src/brk.rs @@ -34,7 +34,7 @@ impl BRK { .unwrap() .get(usize::from(height.checked_sub(key).unwrap())) .cloned() - .ok_or(Error::Str("Couldn't find height in BRK")) + .ok_or(Error::NotFound("Couldn't find height in BRK".into())) } fn fetch_height_prices(height: Height) -> Result> { @@ -49,7 +49,7 @@ impl BRK { let body: Value = serde_json::from_slice(minreq::get(url).send()?.as_bytes())?; body.as_array() - .ok_or(Error::Str("Expect to be an array"))? + .ok_or(Error::Parse("Expected JSON array".into()))? .iter() .map(Self::value_to_ohlc) .collect::, _>>() @@ -74,7 +74,7 @@ impl BRK { .unwrap() .get(usize::from(dateindex.checked_sub(key).unwrap())) .cloned() - .ok_or(Error::Str("Couldn't find date in BRK")) + .ok_or(Error::NotFound("Couldn't find date in BRK".into())) } fn fetch_date_prices(dateindex: DateIndex) -> Result> { @@ -89,7 +89,7 @@ impl BRK { let body: Value = serde_json::from_slice(minreq::get(url).send()?.as_bytes())?; body.as_array() - .ok_or(Error::Str("Expect to be an array"))? + .ok_or(Error::Parse("Expected JSON array".into()))? .iter() .map(Self::value_to_ohlc) .collect::, _>>() @@ -99,14 +99,14 @@ impl BRK { fn value_to_ohlc(value: &Value) -> Result { let ohlc = value .as_array() - .ok_or(Error::Str("Expect as_array to work"))?; + .ok_or(Error::Parse("Expected OHLC array".into()))?; let get_value = |index: usize| -> Result<_> { Ok(Cents::from(Dollars::from( ohlc.get(index) - .ok_or(Error::Str("Expect index key to work"))? + .ok_or(Error::Parse("Missing OHLC value at index".into()))? .as_f64() - .ok_or(Error::Str("Expect as_f64 to work"))?, + .ok_or(Error::Parse("Invalid OHLC value type".into()))?, ))) }; diff --git a/crates/brk_fetcher/src/kraken.rs b/crates/brk_fetcher/src/kraken.rs index 26abfd2eb..e7fbeada9 100644 --- a/crates/brk_fetcher/src/kraken.rs +++ b/crates/brk_fetcher/src/kraken.rs @@ -53,7 +53,7 @@ impl Kraken { .unwrap() .get(date) .cloned() - .ok_or(Error::Str("Couldn't find date")) + .ok_or(Error::NotFound("Couldn't find date".into())) } pub fn fetch_1d() -> Result> { @@ -71,7 +71,7 @@ impl Kraken { .get("result") .and_then(|r| r.get("XXBTZUSD")) .and_then(|v| v.as_array()) - .ok_or(Error::Str("Invalid Kraken response format"))? + .ok_or(Error::Parse("Invalid Kraken response format".into()))? .iter() .filter_map(|v| v.as_array()) .map(|arr| { diff --git a/crates/brk_fetcher/src/lib.rs b/crates/brk_fetcher/src/lib.rs index 048196c3a..70779333d 100644 --- a/crates/brk_fetcher/src/lib.rs +++ b/crates/brk_fetcher/src/lib.rs @@ -146,7 +146,7 @@ How to fix this: } } - Err(Error::String(error_message())) + Err(Error::FetchFailed(error_message())) } fn clear_caches(&mut self) { diff --git a/crates/brk_fetcher/src/ohlc.rs b/crates/brk_fetcher/src/ohlc.rs index 585ea1ddf..a0f9c77fd 100644 --- a/crates/brk_fetcher/src/ohlc.rs +++ b/crates/brk_fetcher/src/ohlc.rs @@ -38,7 +38,7 @@ pub fn compute_ohlc_from_range( let last_ohlc = tree.get(×tamp); if previous_ohlc.is_none() || last_ohlc.is_none() { - return Err(Error::String(format!( + return Err(Error::NotFound(format!( "Couldn't find timestamp in {source_name}" ))); } diff --git a/crates/brk_fetcher/src/source.rs b/crates/brk_fetcher/src/source.rs index 490c56b3c..5b9899245 100644 --- a/crates/brk_fetcher/src/source.rs +++ b/crates/brk_fetcher/src/source.rs @@ -66,7 +66,7 @@ impl TrackedSource { /// Try to fetch, tracking health state fn try_fetch(&mut self, fetch: impl FnOnce(&mut T) -> Option>) -> Option> { if !self.is_healthy() { - return Some(Err(Error::String(format!( + return Some(Err(Error::FetchFailed(format!( "{} temporarily disabled (recheck in {}s)", self.name(), self.remaining_cooldown() diff --git a/crates/brk_indexer/src/processor.rs b/crates/brk_indexer/src/processor.rs index a5bcd04c1..5192c5b06 100644 --- a/crates/brk_indexer/src/processor.rs +++ b/crates/brk_indexer/src/processor.rs @@ -76,7 +76,7 @@ impl<'a> BlockProcessor<'a> { .is_some_and(|prev_height| *prev_height != height) { error!("BlockHash: {blockhash}"); - return Err(Error::Str("Collision, expect prefix to need be set yet")); + return Err(Error::Internal("BlockHash prefix collision")); } self.indexes.push_if_needed(self.vecs)?; @@ -234,7 +234,7 @@ impl<'a> BlockProcessor<'a> { .tx .txindex_to_first_txoutindex .get_pushed_or_read(prev_txindex, &self.readers.txindex_to_first_txoutindex)? - .ok_or(Error::Str("Expect txoutindex to not be none"))? + .ok_or(Error::Internal("Missing txoutindex"))? + vout; let outpoint = OutPoint::new(prev_txindex, vout); @@ -243,7 +243,7 @@ impl<'a> BlockProcessor<'a> { .txout .txoutindex_to_outputtype .get_pushed_or_read(txoutindex, &self.readers.txoutindex_to_outputtype)? - .ok_or(Error::Str("Expect outputtype to not be none"))?; + .ok_or(Error::Internal("Missing outputtype"))?; let address_info = if outputtype.is_address() { let typeindex = self @@ -251,7 +251,7 @@ impl<'a> BlockProcessor<'a> { .txout .txoutindex_to_typeindex .get_pushed_or_read(txoutindex, &self.readers.txoutindex_to_typeindex)? - .ok_or(Error::Str("Expect typeindex to not be none"))?; + .ok_or(Error::Internal("Missing typeindex"))?; Some((outputtype, typeindex)) } else { None @@ -358,7 +358,7 @@ impl<'a> BlockProcessor<'a> { )?; let prev_addressbytes = prev_addressbytes_opt .as_ref() - .ok_or(Error::Str("Expect to have addressbytes"))?; + .ok_or(Error::Internal("Missing addressbytes"))?; if self .stores @@ -574,7 +574,7 @@ impl<'a> BlockProcessor<'a> { } else { let outputtype_typeindex = same_block_output_info .remove(&outpoint) - .ok_or(Error::Str("should have found addressindex from same block")) + .ok_or(Error::Internal("Same-block addressindex not found")) .inspect_err(|_| { dbg!(&same_block_output_info, txin); })?; @@ -646,7 +646,7 @@ impl<'a> BlockProcessor<'a> { let len = self.vecs.tx.txindex_to_txid.len(); let prev_txid = txindex_to_txid_iter .get(prev_txindex) - .ok_or(Error::Str("To have txid for txindex")) + .ok_or(Error::Internal("Missing txid for txindex")) .inspect_err(|_| { dbg!(ct.txindex, len); })?; @@ -655,7 +655,7 @@ impl<'a> BlockProcessor<'a> { if !is_dup { dbg!(self.height, ct.txindex, prev_txid, prev_txindex); - return Err(Error::Str("Expect none")); + return Err(Error::Internal("Unexpected TXID collision")); } } diff --git a/crates/brk_query/src/impl/address.rs b/crates/brk_query/src/impl/address.rs index 2d358a434..c3ec4283d 100644 --- a/crates/brk_query/src/impl/address.rs +++ b/crates/brk_query/src/impl/address.rs @@ -37,7 +37,7 @@ impl Query { let outputtype = OutputType::from(&script); dbg!(outputtype); let Ok(bytes) = AddressBytes::try_from((&script, outputtype)) else { - return Err(Error::Str("Failed to convert the address to bytes")); + return Err(Error::InvalidAddress); }; let addresstype = outputtype; let hash = AddressHash::from(&bytes); @@ -116,8 +116,8 @@ impl Query { let txindex = stores .txidprefix_to_txindex .get(&after_txid.into()) - .map_err(|_| Error::Str("Failed to look up after_txid"))? - .ok_or(Error::Str("after_txid not found"))? + .map_err(|_| Error::UnknownTxid)? + .ok_or(Error::UnknownTxid)? .into_owned(); Some(txindex) } else { @@ -202,7 +202,7 @@ impl Query { } pub fn address_mempool_txids(&self, address: Address) -> Result> { - let mempool = self.mempool().ok_or(Error::Str("Mempool not available"))?; + let mempool = self.mempool().ok_or(Error::MempoolNotAvailable)?; let bytes = AddressBytes::from_str(&address)?; let addresses = mempool.get_addresses(); diff --git a/crates/brk_query/src/impl/block/info.rs b/crates/brk_query/src/impl/block/info.rs index cddd7d67e..84b7a1d20 100644 --- a/crates/brk_query/src/impl/block/info.rs +++ b/crates/brk_query/src/impl/block/info.rs @@ -17,7 +17,7 @@ impl Query { let max_height = self.max_height(); if height > max_height { - return Err(Error::Str("Block height out of range")); + return Err(Error::OutOfRange("Block height out of range".into())); } let blockhash = indexer.vecs.block.height_to_blockhash.read_once(height)?; @@ -68,7 +68,7 @@ impl Query { .blockhashprefix_to_height .get(&prefix)? .map(|h| *h) - .ok_or(Error::Str("Block not found")) + .ok_or(Error::NotFound("Block not found".into())) } fn max_height(&self) -> Height { diff --git a/crates/brk_query/src/impl/block/raw.rs b/crates/brk_query/src/impl/block/raw.rs index 334f9e322..58b5a13f8 100644 --- a/crates/brk_query/src/impl/block/raw.rs +++ b/crates/brk_query/src/impl/block/raw.rs @@ -24,7 +24,7 @@ impl Query { .saturating_sub(1), ); if height > max_height { - return Err(Error::Str("Block height out of range")); + return Err(Error::OutOfRange("Block height out of range".into())); } let position = computer.blks.height_to_position.read_once(height)?; diff --git a/crates/brk_query/src/impl/block/timestamp.rs b/crates/brk_query/src/impl/block/timestamp.rs index b281ad6b7..a9d8f7d23 100644 --- a/crates/brk_query/src/impl/block/timestamp.rs +++ b/crates/brk_query/src/impl/block/timestamp.rs @@ -14,7 +14,7 @@ impl Query { let max_height_usize: usize = max_height.into(); if max_height_usize == 0 { - return Err(Error::Str("No blocks indexed")); + return Err(Error::NotFound("No blocks indexed".into())); } let target = timestamp; diff --git a/crates/brk_query/src/impl/block/txs.rs b/crates/brk_query/src/impl/block/txs.rs index 1c2c37928..e45231e0e 100644 --- a/crates/brk_query/src/impl/block/txs.rs +++ b/crates/brk_query/src/impl/block/txs.rs @@ -28,7 +28,7 @@ impl Query { let max_height = self.height(); if height > max_height { - return Err(Error::Str("Block height out of range")); + return Err(Error::OutOfRange("Block height out of range".into())); } let first_txindex = indexer.vecs.tx.height_to_first_txindex.read_once(height)?; @@ -64,7 +64,7 @@ impl Query { let max_height = self.height(); if height > max_height { - return Err(Error::Str("Block height out of range")); + return Err(Error::OutOfRange("Block height out of range".into())); } let first_txindex = indexer.vecs.tx.height_to_first_txindex.read_once(height)?; @@ -101,7 +101,7 @@ impl Query { let max_height = self.height(); if height > max_height { - return Err(Error::Str("Block height out of range")); + return Err(Error::OutOfRange("Block height out of range".into())); } let first_txindex = indexer.vecs.tx.height_to_first_txindex.read_once(height)?; @@ -117,7 +117,7 @@ impl Query { let tx_count = next - first; if index >= tx_count { - return Err(Error::Str("Transaction index out of range")); + return Err(Error::OutOfRange("Transaction index out of range".into())); } let txindex = TxIndex::from(first + index); diff --git a/crates/brk_query/src/impl/mempool.rs b/crates/brk_query/src/impl/mempool.rs index 1c2f51cf8..03be4ed3d 100644 --- a/crates/brk_query/src/impl/mempool.rs +++ b/crates/brk_query/src/impl/mempool.rs @@ -5,12 +5,12 @@ use crate::Query; impl Query { pub fn mempool_info(&self) -> Result { - let mempool = self.mempool().ok_or(Error::Str("Mempool not available"))?; + let mempool = self.mempool().ok_or(Error::MempoolNotAvailable)?; Ok(mempool.get_info()) } pub fn mempool_txids(&self) -> Result> { - let mempool = self.mempool().ok_or(Error::Str("Mempool not available"))?; + let mempool = self.mempool().ok_or(Error::MempoolNotAvailable)?; let txs = mempool.get_txs(); Ok(txs.keys().cloned().collect()) } @@ -22,7 +22,7 @@ impl Query { } pub fn mempool_blocks(&self) -> Result> { - let mempool = self.mempool().ok_or(Error::Str("Mempool not available"))?; + let mempool = self.mempool().ok_or(Error::MempoolNotAvailable)?; let block_stats = mempool.get_block_stats(); diff --git a/crates/brk_query/src/impl/metrics.rs b/crates/brk_query/src/impl/metrics.rs index ceb3054ab..a44ed1435 100644 --- a/crates/brk_query/src/impl/metrics.rs +++ b/crates/brk_query/src/impl/metrics.rs @@ -25,17 +25,16 @@ impl Query { // Check if metric exists but with different indexes if let Some(indexes) = self.vecs().metric_to_indexes(metric.clone()) { let index_list: Vec<_> = indexes.iter().map(|i| i.to_string()).collect(); - return Error::String(format!( - "'{metric}' doesn't support the requested index. Supported indexes: {}", - index_list.join(", ") - )); + return Error::MetricUnsupportedIndex { + metric: metric.to_string(), + supported: index_list.join(", "), + }; } // Metric doesn't exist, suggest alternatives - if let Some(first) = self.match_metric(metric, Limit::MIN).first() { - Error::String(format!("Could not find '{metric}', did you mean '{first}'?")) - } else { - Error::String(format!("Could not find '{metric}'.")) + Error::MetricNotFound { + metric: metric.to_string(), + suggestion: self.match_metric(metric, Limit::MIN).first().map(|s| s.to_string()), } } @@ -161,7 +160,7 @@ impl Query { /// Returns error if no metrics requested or any requested metric is not found. pub fn search(&self, params: &MetricSelection) -> Result> { if params.metrics.is_empty() { - return Err(Error::String("No metrics specified".to_string())); + return Err(Error::NoMetrics); } let mut vecs = Vec::with_capacity(params.metrics.len()); for metric in params.metrics.iter() { @@ -195,9 +194,10 @@ impl Query { let weight = Self::weight(&vecs, params.from(), params.to_for_len(metric.len())); if weight > max_weight { - return Err(Error::String(format!( - "Request too heavy: {weight} bytes exceeds limit of {max_weight} bytes" - ))); + return Err(Error::WeightExceeded { + requested: weight, + max: max_weight, + }); } self.format(*metric, ¶ms.range) @@ -219,9 +219,10 @@ impl Query { let min_len = vecs.iter().map(|v| v.len()).min().expect("search guarantees non-empty"); let weight = Self::weight(&vecs, params.from(), params.to_for_len(min_len)); if weight > max_weight { - return Err(Error::String(format!( - "Request too heavy: {weight} bytes exceeds limit of {max_weight} bytes" - ))); + return Err(Error::WeightExceeded { + requested: weight, + max: max_weight, + }); } self.format_bulk(&vecs, ¶ms.range) diff --git a/crates/brk_query/src/impl/metrics_legacy.rs b/crates/brk_query/src/impl/metrics_legacy.rs index 7037291ff..2e1649302 100644 --- a/crates/brk_query/src/impl/metrics_legacy.rs +++ b/crates/brk_query/src/impl/metrics_legacy.rs @@ -64,9 +64,10 @@ impl Query { let min_len = vecs.iter().map(|v| v.len()).min().expect("search guarantees non-empty"); let weight = Self::weight(&vecs, params.from(), params.to_for_len(min_len)); if weight > max_weight { - return Err(Error::String(format!( - "Request too heavy: {weight} bytes exceeds limit of {max_weight} bytes" - ))); + return Err(Error::WeightExceeded { + requested: weight, + max: max_weight, + }); } self.format_legacy(&vecs, ¶ms.range) diff --git a/crates/brk_query/src/impl/mining/block_fee_rates.rs b/crates/brk_query/src/impl/mining/block_fee_rates.rs index 95de7959c..cfeff8848 100644 --- a/crates/brk_query/src/impl/mining/block_fee_rates.rs +++ b/crates/brk_query/src/impl/mining/block_fee_rates.rs @@ -8,10 +8,14 @@ #![allow(dead_code)] use brk_error::Result; -use brk_types::{BlockFeeRatesEntry, FeeRatePercentiles, TimePeriod}; -use vecdb::{IterableVec, VecIndex}; +use brk_types::{ + BlockFeeRatesEntry, + // FeeRatePercentiles, + TimePeriod, +}; +// use vecdb::{IterableVec, VecIndex}; -use super::dateindex_iter::DateIndexIter; +// use super::dateindex_iter::DateIndexIter; use crate::Query; impl Query { diff --git a/crates/brk_query/src/impl/mining/pools.rs b/crates/brk_query/src/impl/mining/pools.rs index 49e6a212b..48e71e20b 100644 --- a/crates/brk_query/src/impl/mining/pools.rs +++ b/crates/brk_query/src/impl/mining/pools.rs @@ -98,7 +98,7 @@ impl Query { .pools .vecs .get(&slug) - .ok_or_else(|| Error::Str("Pool data not found"))?; + .ok_or_else(|| Error::NotFound("Pool data not found".into()))?; let mut cumulative = pool_vecs .indexes_to_blocks_mined diff --git a/crates/brk_query/src/impl/transaction.rs b/crates/brk_query/src/impl/transaction.rs index 302f324ca..6f609edde 100644 --- a/crates/brk_query/src/impl/transaction.rs +++ b/crates/brk_query/src/impl/transaction.rs @@ -213,7 +213,7 @@ impl Query { let buffer = reader.read_raw_bytes(position, *total_size as usize)?; let mut cursor = Cursor::new(buffer); let tx = bitcoin::Transaction::consensus_decode(&mut cursor) - .map_err(|_| Error::Str("Failed to decode transaction"))?; + .map_err(|_| Error::Parse("Failed to decode transaction".into()))?; // For iterating through inputs, we need iterators (multiple lookups) let mut txindex_to_txid_iter = indexer.vecs.tx.txindex_to_txid.iter()?; diff --git a/crates/brk_reader/src/lib.rs b/crates/brk_reader/src/lib.rs index 1ad33efd8..96ed13e3d 100644 --- a/crates/brk_reader/src/lib.rs +++ b/crates/brk_reader/src/lib.rs @@ -14,7 +14,7 @@ use std::{ use bitcoin::{block::Header, consensus::Decodable}; use blk_index_to_blk_path::*; -use brk_error::Result; +use brk_error::{Error, Result}; use brk_rpc::Client; use brk_types::{BlkMetadata, BlkPosition, BlockHash, Height, ReadBlock}; pub use crossbeam::channel::Receiver; @@ -88,7 +88,7 @@ impl ReaderInner { let blk_paths = self.blk_index_to_blk_path(); let blk_path = blk_paths .get(&position.blk_index()) - .ok_or("Blk file not found")?; + .ok_or(Error::NotFound("Blk file not found".into()))?; let mut file = File::open(blk_path)?; file.seek(SeekFrom::Start(position.offset() as u64))?; @@ -361,7 +361,7 @@ impl ReaderInner { loop { if file.read_exact(&mut byte_buffer).is_err() { - return Err("No magic bytes found".into()); + return Err(Error::NotFound("No magic bytes found".into())); } current_4bytes.rotate_left(1); diff --git a/crates/brk_rpc/src/lib.rs b/crates/brk_rpc/src/lib.rs index 752927b3e..83b5a9b56 100644 --- a/crates/brk_rpc/src/lib.rs +++ b/crates/brk_rpc/src/lib.rs @@ -8,7 +8,7 @@ use bitcoincore_rpc::{ json::{GetBlockHeaderResult, GetBlockResult, GetBlockchainInfoResult, GetTxOutResult}, {Client as CoreClient, Error as RpcError, RpcApi}, }; -use brk_error::Result; +use brk_error::{Error, Result}; use brk_types::{ BlockHash, Height, MempoolEntryInfo, Sats, Transaction, TxIn, TxOut, TxStatus, TxWithHex, Txid, Vout, @@ -262,7 +262,7 @@ impl Client { let mut hash = block_info .previous_block_hash .map(BlockHash::from) - .ok_or("Genesis block has no previous block")?; + .ok_or(Error::NotFound("Genesis block has no previous block".into()))?; loop { if self.is_in_main_chain(&hash)? { @@ -274,10 +274,10 @@ impl Client { hash = info .previous_block_hash .map(BlockHash::from) - .ok_or("Reached genesis without finding main chain")?; + .ok_or(Error::NotFound("Reached genesis without finding main chain".into()))?; } } - Err(_) => Err("Block hash not found in blockchain".into()), + Err(_) => Err(Error::NotFound("Block hash not found in blockchain".into())), } } diff --git a/crates/brk_types/src/index.rs b/crates/brk_types/src/index.rs index 697e9b6e8..f712d4c21 100644 --- a/crates/brk_types/src/index.rs +++ b/crates/brk_types/src/index.rs @@ -179,7 +179,7 @@ impl TryFrom<&str> for Index { v if (Self::EmptyAddressIndex).possible_values().contains(&v) => { Self::EmptyAddressIndex } - _ => return Err(Error::Str("Bad index")), + _ => return Err(Error::Parse(format!("Invalid index: {value}"))), }) } } diff --git a/crates/brk_types/src/loadedaddressdata.rs b/crates/brk_types/src/loadedaddressdata.rs index 97ec8d6fe..186998b2b 100644 --- a/crates/brk_types/src/loadedaddressdata.rs +++ b/crates/brk_types/src/loadedaddressdata.rs @@ -78,7 +78,7 @@ impl LoadedAddressData { pub fn send(&mut self, amount: Sats, previous_price: Option) -> Result<()> { if self.balance() < amount { - return Err(Error::Str("Previous_amount smaller than sent amount")); + return Err(Error::Internal("Previous amount smaller than sent amount")); } self.sent += amount; self.spent_txo_count += 1; diff --git a/crates/brk_types/src/outputtype.rs b/crates/brk_types/src/outputtype.rs index d97da204c..f928b289a 100644 --- a/crates/brk_types/src/outputtype.rs +++ b/crates/brk_types/src/outputtype.rs @@ -907,7 +907,7 @@ impl TryFrom for AddressType { OutputType::P2TR => Self::P2tr, OutputType::P2WPKH => Self::P2wpkh, OutputType::P2WSH => Self::P2wsh, - _ => return Err(Error::Str("Bad output format")), + _ => return Err(Error::UnsupportedType(format!("{:?}", value))), }) } }