From fb19645b22292fcbc82350742a16105eb9accd24 Mon Sep 17 00:00:00 2001 From: Alex Pozhylenkov Date: Tue, 16 Apr 2024 13:45:52 +0300 Subject: [PATCH] refactor: Update error handling in `cat-gateway` (#425) * docs: Add CatalystDataGatewayRepository docs (#388) * test: Fix tests after cat-gateway update. * docs: Add `CatalystDataGatewayRepository` usage examples. * docs: Move docs from README to code comment. * test: Update `CatalystDataGatewayRepository` tests. * test: Use Fake instead of Mock. * chore: Fix Markdown errors. * chore: Explicit use of `HttpStatus` codes. * adds one more worker and compression for catgateway logs (#400) * feat: Collect flutter code coverage (#404) * test: Fix tests after cat-gateway update. * docs: Add `CatalystDataGatewayRepository` usage examples. * docs: Move docs from README to code comment. * test: Update `CatalystDataGatewayRepository` tests. * test: Use Fake instead of Mock. * chore: Fix Markdown errors. * chore: Explicit use of `HttpStatus` codes. * chore(deps-dev): bump vite in /utilities/wallet-tester (#397) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.1.6 to 5.1.7. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.1.7/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.1.7/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Apisit Ritreungroj <38898766+apskhem@users.noreply.github.com> * feat: Get slot number by provided date-time endpoint (#387) * fix CardanoStakeAddress error handling * refactor, add sync_state_get endpoint * refactor types * refactor * add block_hash validation * wip * wip * wip * wip * add check_network fn * fix * fix schematisis test * try * wip * try * try * try * try * wip * try * try * fix * update Network * add test_utxo test * try * fix * try * fix * wip * fix * fix docket-compose.yml file * try * try * fix * try * try * try * try * wip * fix * wip * try * try * wip * try * try * revert * wip * wip * wip * fix * fix * fix * remove mithril_snapshot loader * wip * wip * wip * wip * wip * wip * wip * add stake addr bech32 encode utils function * wip * wip * update indexing of the utxo data * fix spelling * wip * wip * finish utxo test * fix deny * fix check * fix * fix * update earthly builder versions * wip * ignore test_utxo.py in CI * dont ignore tests * add date_time_to_slot_number_get endpoint * add sql queries * fix * update slot info, fix follower indexing block time issue * add previous slot info field * fix * refactor * fix sync_state_get * wip * fix check * try * fix * finish slot_info test, fix queries * fix * cleanup * wip * wip * wip * feat: RBAC Documentation Drafting (#332) * chore: wip * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * Update 0005-flutter-app.md * docs(docs): Use latest docs builders and fix concepts page * docs(cips): Start drafting the CIPS for milestone 2 * docs(cips): More text for RBAC metadata draft * docs(cips): WIP updates to draft cip for role registration * docs(cips): define draft specification for a ULID cbor tag * docs(cips): Further WIP edits to RBAC * docs(cips): fix ulid spec binary encoding reference * docs(cips): Add a tag to the epoch time. * docs(cips): Add CBOR tag cip for ED25519-BIP32 Keys, Derivation paths and Signatures * docs(cips): Properly define the field tags to use where known, and clean up Stake Address specification. * docs(cips): Fix nonce so its reliable without needing blockchain data * docs(cips): updates * docs(docs): Add CDDL definition for POC x509 envelope metadata * fix(vscode): update vscode extension recommendations * docs(cips): rbac x509 envelope fix * docs(cips): wip updates to high level docs * docs(cips): Add overview of cardano transaction processign and data * docs(cips): update cardano block to be complete for clarity * docs(cips): fix layout engine * docs(cips): wip cddl for envelope metadata * docs(cips): Add cddl specs and diagrams for x509 rbac registration work * docs(cips): Add full transaction/metadata relationship diagram * refactor(cips): reorganize documentation ready for drafting descriptive prose about the formats and uses * docs(cips): add cip draft for catalyst roles using the x509-rbac standard * docs(cips): Add c509 cddl with restrictions and enhancements for plutus usage * docs(cips): Metadata envelope specification draft complete * Update docs/src/catalyst-standards/draft-cips/c509-plutus-restricted-certificate/c509-cert-plutus-restricted.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/c509-plutus-restricted-certificate/c509-cert-plutus-restricted.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-role-registration-metadata/x509-roles.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-role-registration-metadata/x509-roles.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-envelope-metadata/x509-envelope.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-envelope-metadata/x509-envelope.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/x509-envelope-metadata/x509-envelope.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * Update docs/src/catalyst-standards/draft-cips/c509-plutus-restricted-certificate/c509-cert-plutus-restricted.cddl Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * docs(cips): Fix time and algorithm comments * build(frontend): Relax flutter/dart version requirements to last minor release * docs(cips): wip * fix(cips): rename draft x509 envelope CIP so its easier to identify * docs(cips): WIP updates to x509 roles * fix(cips): rename RBAC definition CIP draft so its easier to identify * docs(cips): x509 certificate registration format fully defined * docs(cips): Document the restricted plutus subset. * docs(cips): Add document detailing how CIP-30 is used to sign the transaction * fix(cips): remove trailing spaces * fix(cips): Fix line lengths * fix(cips): Correct spelling * fix(cips): spelling * fix(frontend): revert changes to flutter/dart versions * fix(frontend): more flutter/dart version corrections * fix(frontend): Revert flutter files to same as main branch * fix(frontend): revert more flutter .yml files to those in main * fix(cips): Fix links between files * docs(cips): Add catalyst specific role registration documentation * docs(spelling): fix spelling --------- Co-authored-by: minikin Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * ci: configure static analysis & code formatting check * style: formatting * fix: revert browser installation scripts * style: format code * ci: optimize directions include in repo-catalyst-voices-all artifact to include only needed ones * refactor: remove empty tests * ci: add melos script to generate test reports * ci: melos script to generate test report * style: revert previously generated files formatting * style: format files * ci: update melos to exclude generated code form code coverage * ci: cleanup build script * ci: generate multiple junit test reports and save them at /test_reports * ci: depend on melos analyze instead of custom command * docs: improve melos docs * ci: remove unused melos scripts * ci: format files in test & integration_test directories * ci: break code to make sure CI will report failure for demonstration purposes * style: fix lint issues * ci: change WORKDIR after creating the user to make sure it will be owned by that user * ci: restore root user * Revert "Merge branch 'main' into feat/collect-flutter-code-coverage" This reverts commit d0f66b2c1e7228141009ea153b35b488bf7922b1, reversing changes made to 39ce4017c3624a807b93116633ac1b249f2d86bd. * style: format code * ci: revert test-unit target name --------- Signed-off-by: dependabot[bot] Co-authored-by: Lucio Baglione Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Apisit Ritreungroj <38898766+apskhem@users.noreply.github.com> Co-authored-by: Alex Pozhylenkov Co-authored-by: Steven Johnson Co-authored-by: minikin Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> * refactor: update schema_validation check (#414) * update schema_validation check * wip * fix * update MismatchedSchema and NoDatabaseUrl error handling * update get_follower_config error handling * update follower queries error handling * fix * update legacy queries * update utxo queries error handling * fix * wip * remove service error type * update registration error handling * wip * fix --------- Signed-off-by: dependabot[bot] Co-authored-by: Lucio Baglione Co-authored-by: Stefano Cunego <93382903+kukkok3@users.noreply.github.com> Co-authored-by: Dominik Toton <166132265+dtscalac@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Apisit Ritreungroj <38898766+apskhem@users.noreply.github.com> Co-authored-by: Steven Johnson Co-authored-by: minikin Co-authored-by: bkioshn <35752733+bkioshn@users.noreply.github.com> --- catalyst-gateway/bin/src/cli.rs | 13 +-- catalyst-gateway/bin/src/event_db/config.rs | 19 ++-- catalyst-gateway/bin/src/event_db/error.rs | 56 +--------- .../bin/src/event_db/follower/mod.rs | 27 ++--- .../event_db/legacy/queries/event/ballot.rs | 10 +- .../src/event_db/legacy/queries/event/mod.rs | 8 +- .../legacy/queries/event/objective.rs | 3 +- .../event_db/legacy/queries/event/proposal.rs | 8 +- .../event_db/legacy/queries/event/review.rs | 5 +- .../event_db/legacy/queries/registration.rs | 17 +-- .../bin/src/event_db/legacy/queries/search.rs | 38 +++---- .../event_db/legacy/queries/vit_ss/fund.rs | 7 +- catalyst-gateway/bin/src/event_db/mod.rs | 10 +- .../bin/src/event_db/schema_check.rs | 19 +++- catalyst-gateway/bin/src/event_db/utxo.rs | 24 ++--- .../src/event_db/voter_registration/mod.rs | 22 ++-- catalyst-gateway/bin/src/follower.rs | 2 +- catalyst-gateway/bin/src/main.rs | 2 +- catalyst-gateway/bin/src/registration/mod.rs | 102 ++++++++++-------- .../cardano/date_time_to_slot_number_get.rs | 39 +++---- .../service/api/cardano/registration_get.rs | 8 +- .../src/service/api/cardano/staked_ada_get.rs | 8 +- .../src/service/api/cardano/sync_state_get.rs | 8 +- .../bin/src/service/api/health/ready_get.rs | 15 +-- .../src/service/common/responses/resp_5xx.rs | 22 ++-- catalyst-gateway/bin/src/service/mod.rs | 13 +-- .../bin/src/service/poem_service.rs | 8 +- catalyst-gateway/bin/src/state/mod.rs | 7 +- 28 files changed, 225 insertions(+), 295 deletions(-) diff --git a/catalyst-gateway/bin/src/cli.rs b/catalyst-gateway/bin/src/cli.rs index b86f25061d9..3531cb0996e 100644 --- a/catalyst-gateway/bin/src/cli.rs +++ b/catalyst-gateway/bin/src/cli.rs @@ -12,17 +12,6 @@ use crate::{ state::State, }; -#[derive(thiserror::Error, Debug)] -/// All service errors -pub(crate) enum Error { - #[error(transparent)] - /// Service oriented errors - Service(#[from] service::Error), - #[error(transparent)] - /// DB oriented errors - EventDb(#[from] crate::event_db::error::Error), -} - #[derive(Parser)] #[clap(rename_all = "kebab-case")] /// Simple service CLI options @@ -77,7 +66,7 @@ impl Cli { match event_db.get_follower_config().await { Ok(config) => break config, - Err(err) => error!("no config {:?}", err), + Err(err) => error!("No follower config found, error: {err}"), } }; diff --git a/catalyst-gateway/bin/src/event_db/config.rs b/catalyst-gateway/bin/src/event_db/config.rs index 09f6e28a4c6..d5e678b861a 100644 --- a/catalyst-gateway/bin/src/event_db/config.rs +++ b/catalyst-gateway/bin/src/event_db/config.rs @@ -4,7 +4,8 @@ use std::str::FromStr; use cardano_chain_follower::Network; use serde::{Deserialize, Serialize}; -use crate::event_db::{Error, EventDB}; +use super::error::NotFoundError; +use crate::event_db::EventDB; /// Representation of the `config` table id fields `id`, `id2`, `id3` enum ConfigId { @@ -51,7 +52,7 @@ pub(crate) struct MithrilSnapshotConfig { impl EventDB { /// Config query - pub(crate) async fn get_follower_config(&self) -> Result, Error> { + pub(crate) async fn get_follower_config(&self) -> anyhow::Result> { let conn = self.pool.get().await?; let id = "cardano"; @@ -66,17 +67,16 @@ impl EventDB { let mut follower_configs = Vec::new(); for row in rows { - let network = Network::from_str(row.try_get::<_, &str>(ConfigId::Id3.as_str())?) - .map_err(|e| Error::Unknown(e.to_string()))?; + let network = Network::from_str(row.try_get::<_, &str>(ConfigId::Id3.as_str())?)?; let config: serde_json::Value = row.try_get("value")?; let relay = config .get("relay") - .ok_or(Error::JsonParseIssue( + .ok_or(anyhow::anyhow!( "Cardano follower config does not have `relay` property".to_string(), ))? .as_str() - .ok_or(Error::JsonParseIssue( + .ok_or(anyhow::anyhow!( "Cardano follower config `relay` not a string type".to_string(), ))? .to_string(); @@ -84,13 +84,12 @@ impl EventDB { let mithril_snapshot = serde_json::from_value( config .get("mithril_snapshot") - .ok_or(Error::JsonParseIssue( + .ok_or(anyhow::anyhow!( "Cardano follower config does not have `mithril_snapshot` property" .to_string(), ))? .clone(), - ) - .map_err(|e| Error::JsonParseIssue(e.to_string()))?; + )?; follower_configs.push(FollowerConfig { network, @@ -100,7 +99,7 @@ impl EventDB { } if follower_configs.is_empty() { - Err(Error::NoConfig) + Err(NotFoundError.into()) } else { Ok(follower_configs) } diff --git a/catalyst-gateway/bin/src/event_db/error.rs b/catalyst-gateway/bin/src/event_db/error.rs index a17bcf1bc2e..2d084344926 100644 --- a/catalyst-gateway/bin/src/event_db/error.rs +++ b/catalyst-gateway/bin/src/event_db/error.rs @@ -1,56 +1,6 @@ //! Database Errors -use bb8::RunError; - -/// Event database errors +/// DB not found error #[derive(thiserror::Error, Debug, PartialEq, Eq)] -pub(crate) enum Error { - /// Schema in database does not match schema supported by the Crate. - #[error(" Schema in database does not match schema supported by the Crate. The current schema version: {was}, the schema version we expected: {expected}")] - MismatchedSchema { - /// The current DB schema version. - was: i32, - /// The expected DB schema version. - expected: i32, - }, - /// No DB URL was provided - #[error("DB URL is undefined")] - NoDatabaseUrl, - /// Cannot find this item - #[error("Cannot find this item")] - NotFound, - /// DB connection timeout - #[error("Connection to DB timed out")] - TimedOut, - /// Unknown error - #[error("error: {0}")] - Unknown(String), - /// No config - #[error("No config")] - NoConfig, - /// JSON Parsing error - #[error("Unable to parse database data: {0}")] - JsonParseIssue(String), - /// Unable to extract policy assets - #[error("Unable parse assets: {0}")] - AssetParsingIssue(String), - /// Unable to extract hashed witnesses - #[allow(dead_code)] - #[error("Unable to extract hashed witnesses: {0}")] - HashedWitnessExtraction(String), -} - -impl From> for Error { - fn from(val: RunError) -> Self { - match val { - RunError::TimedOut => Self::TimedOut, - RunError::User(_) => Self::Unknown(val.to_string()), - } - } -} - -impl From for Error { - fn from(val: tokio_postgres::Error) -> Self { - Self::Unknown(val.to_string()) - } -} +#[error("Cannot find this item")] +pub(crate) struct NotFoundError; diff --git a/catalyst-gateway/bin/src/event_db/follower/mod.rs b/catalyst-gateway/bin/src/event_db/follower/mod.rs index 8c33fa4e489..3c2504e6435 100644 --- a/catalyst-gateway/bin/src/event_db/follower/mod.rs +++ b/catalyst-gateway/bin/src/event_db/follower/mod.rs @@ -3,7 +3,8 @@ use cardano_chain_follower::Network; use handlebars::Handlebars; -use crate::event_db::{Error, EventDB}; +use super::error::NotFoundError; +use crate::event_db::EventDB; /// Block time pub type DateTime = chrono::DateTime; @@ -55,7 +56,7 @@ struct SlotInfoQueryTmplFields { impl SlotInfoQueryType { /// Get SQL query - fn get_sql_query(&self) -> Result { + fn get_sql_query(&self) -> anyhow::Result { let tmpl_fields = match self { SlotInfoQueryType::Previous => { SlotInfoQueryTmplFields { @@ -81,8 +82,7 @@ impl SlotInfoQueryType { // disable default `html_escape` function // which transforms `<`, `>` symbols to `<`, `>` reg.register_escape_fn(|s| s.into()); - reg.render_template(SLOT_INFO_SQL_HBS, &tmpl_fields) - .map_err(|e| Error::Unknown(e.to_string())) + Ok(reg.render_template(SLOT_INFO_SQL_HBS, &tmpl_fields)?) } } @@ -91,7 +91,7 @@ impl EventDB { pub(crate) async fn index_follower_data( &self, slot_no: SlotNumber, network: Network, epoch_no: EpochNumber, block_time: DateTime, block_hash: BlockHash, - ) -> Result<(), Error> { + ) -> anyhow::Result<()> { let conn = self.pool.get().await?; let _rows = conn @@ -110,7 +110,7 @@ impl EventDB { /// Get slot info for the provided date-time and network and query type pub(crate) async fn get_slot_info( &self, date_time: DateTime, network: Network, query_type: SlotInfoQueryType, - ) -> Result<(SlotNumber, BlockHash, DateTime), Error> { + ) -> anyhow::Result<(SlotNumber, BlockHash, DateTime)> { let conn = self.pool.get().await?; let rows = conn @@ -120,9 +120,7 @@ impl EventDB { ]) .await?; - let Some(row) = rows.first() else { - return Err(Error::NotFound); - }; + let row = rows.first().ok_or(NotFoundError)?; let slot_number: SlotNumber = row.try_get(SLOT_NO_COLUMN)?; let block_hash = row.try_get(BLOCK_HASH_COLUMN)?; @@ -134,16 +132,14 @@ impl EventDB { /// Start follower from where previous follower left off. pub(crate) async fn last_updated_metadata( &self, network: Network, - ) -> Result<(SlotNumber, BlockHash, DateTime), Error> { + ) -> anyhow::Result<(SlotNumber, BlockHash, DateTime)> { let conn = self.pool.get().await?; let rows = conn .query(SELECT_UPDATE_STATE_SQL, &[&network.to_string()]) .await?; - let Some(row) = rows.first() else { - return Err(Error::NotFound); - }; + let row = rows.first().ok_or(NotFoundError)?; let slot_no = row.try_get(SLOT_NO_COLUMN)?; let block_hash = row.try_get(BLOCK_HASH_COLUMN)?; @@ -157,7 +153,7 @@ impl EventDB { pub(crate) async fn refresh_last_updated( &self, last_updated: DateTime, slot_no: SlotNumber, block_hash: BlockHash, network: Network, machine_id: &MachineId, - ) -> Result<(), Error> { + ) -> anyhow::Result<()> { let conn = self.pool.get().await?; // Rollback or update @@ -169,8 +165,7 @@ impl EventDB { // All future additions are just updates on ended, slot_no and block_hash let _rows = conn .query(INSERT_UPDATE_STATE_SQL, &[ - &i64::try_from(network_id) - .map_err(|_| Error::Unknown("Network id out of range".to_string()))?, + &i64::try_from(network_id)?, &last_updated, &last_updated, &machine_id, diff --git a/catalyst-gateway/bin/src/event_db/legacy/queries/event/ballot.rs b/catalyst-gateway/bin/src/event_db/legacy/queries/event/ballot.rs index 25588f7bfe7..969209176fd 100644 --- a/catalyst-gateway/bin/src/event_db/legacy/queries/event/ballot.rs +++ b/catalyst-gateway/bin/src/event_db/legacy/queries/event/ballot.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::event_db::{ - error::Error, + error::NotFoundError, legacy::types::{ ballot::{ Ballot, BallotType, GroupVotePlans, ObjectiveBallots, ObjectiveChoices, ProposalBallot, @@ -52,7 +52,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_ballot( &self, event: EventId, objective: ObjectiveId, proposal: ProposalId, - ) -> Result { + ) -> anyhow::Result { let conn = self.pool.get().await?; let rows = conn @@ -62,7 +62,7 @@ impl EventDB { &proposal.0, ]) .await?; - let row = rows.first().ok_or(Error::NotFound)?; + let row = rows.first().ok_or(NotFoundError)?; let choices = row.try_get("objective")?; let rows = conn @@ -95,7 +95,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_objective_ballots( &self, event: EventId, objective: ObjectiveId, - ) -> Result, Error> { + ) -> anyhow::Result> { let conn = self.pool.get().await?; let rows = conn @@ -145,7 +145,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_event_ballots( &self, event: EventId, - ) -> Result, Error> { + ) -> anyhow::Result> { let conn = self.pool.get().await?; let rows = conn diff --git a/catalyst-gateway/bin/src/event_db/legacy/queries/event/mod.rs b/catalyst-gateway/bin/src/event_db/legacy/queries/event/mod.rs index 3305e72963b..9b0e5a1352a 100644 --- a/catalyst-gateway/bin/src/event_db/legacy/queries/event/mod.rs +++ b/catalyst-gateway/bin/src/event_db/legacy/queries/event/mod.rs @@ -2,7 +2,7 @@ use chrono::{NaiveDateTime, Utc}; use crate::event_db::{ - error::Error, + error::NotFoundError, legacy::types::event::{ Event, EventDetails, EventGoal, EventId, EventRegistration, EventSchedule, EventSummary, VotingPowerAlgorithm, VotingPowerSettings, @@ -44,7 +44,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_events( &self, limit: Option, offset: Option, - ) -> Result, Error> { + ) -> anyhow::Result> { let conn = self.pool.get().await?; let rows = conn @@ -76,11 +76,11 @@ impl EventDB { /// Get event query #[allow(dead_code)] - pub(crate) async fn get_event(&self, event: EventId) -> Result { + pub(crate) async fn get_event(&self, event: EventId) -> anyhow::Result { let conn = self.pool.get().await?; let rows = conn.query(Self::EVENT_QUERY, &[&event.0]).await?; - let row = rows.first().ok_or(Error::NotFound)?; + let row = rows.first().ok_or(NotFoundError)?; let ends = row .try_get::<&'static str, Option>("end_time")? diff --git a/catalyst-gateway/bin/src/event_db/legacy/queries/event/objective.rs b/catalyst-gateway/bin/src/event_db/legacy/queries/event/objective.rs index e46db9313d3..aecacb2714b 100644 --- a/catalyst-gateway/bin/src/event_db/legacy/queries/event/objective.rs +++ b/catalyst-gateway/bin/src/event_db/legacy/queries/event/objective.rs @@ -1,6 +1,5 @@ //! Objective Queries use crate::event_db::{ - error::Error, legacy::types::{ event::EventId, objective::{ @@ -33,7 +32,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_objectives( &self, event: EventId, limit: Option, offset: Option, - ) -> Result, Error> { + ) -> anyhow::Result> { let conn = self.pool.get().await?; let rows = conn diff --git a/catalyst-gateway/bin/src/event_db/legacy/queries/event/proposal.rs b/catalyst-gateway/bin/src/event_db/legacy/queries/event/proposal.rs index ad1f87d91aa..fb0340ceb07 100644 --- a/catalyst-gateway/bin/src/event_db/legacy/queries/event/proposal.rs +++ b/catalyst-gateway/bin/src/event_db/legacy/queries/event/proposal.rs @@ -1,6 +1,6 @@ //! Proposal Queries use crate::event_db::{ - error::Error, + error::NotFoundError, legacy::types::{ event::EventId, objective::ObjectiveId, @@ -32,7 +32,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_proposal( &self, event: EventId, objective: ObjectiveId, proposal: ProposalId, - ) -> Result { + ) -> anyhow::Result { let conn: bb8::PooledConnection< bb8_postgres::PostgresConnectionManager, > = self.pool.get().await?; @@ -40,7 +40,7 @@ impl EventDB { let rows = conn .query(Self::PROPOSAL_QUERY, &[&event.0, &objective.0, &proposal.0]) .await?; - let row = rows.first().ok_or(Error::NotFound)?; + let row = rows.first().ok_or(NotFoundError)?; let proposer = vec![ProposerDetails { name: row.try_get("proposer_name")?, @@ -71,7 +71,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_proposals( &self, event: EventId, objective: ObjectiveId, limit: Option, offset: Option, - ) -> Result, Error> { + ) -> anyhow::Result> { let conn = self.pool.get().await?; let rows = conn diff --git a/catalyst-gateway/bin/src/event_db/legacy/queries/event/review.rs b/catalyst-gateway/bin/src/event_db/legacy/queries/event/review.rs index cbcb03ca8c1..9ad6cb529b3 100644 --- a/catalyst-gateway/bin/src/event_db/legacy/queries/event/review.rs +++ b/catalyst-gateway/bin/src/event_db/legacy/queries/event/review.rs @@ -1,6 +1,5 @@ //! Review Queries use crate::event_db::{ - error::Error, legacy::types::{ event::EventId, objective::ObjectiveId, @@ -41,7 +40,7 @@ impl EventDB { pub(crate) async fn get_reviews( &self, event: EventId, objective: ObjectiveId, proposal: ProposalId, limit: Option, offset: Option, - ) -> Result, Error> { + ) -> anyhow::Result> { let conn = self.pool.get().await?; let rows = conn @@ -81,7 +80,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_review_types( &self, event: EventId, objective: ObjectiveId, limit: Option, offset: Option, - ) -> Result, Error> { + ) -> anyhow::Result> { let conn = self.pool.get().await?; let rows = conn diff --git a/catalyst-gateway/bin/src/event_db/legacy/queries/registration.rs b/catalyst-gateway/bin/src/event_db/legacy/queries/registration.rs index a88aee46bd0..07bd85c82b1 100644 --- a/catalyst-gateway/bin/src/event_db/legacy/queries/registration.rs +++ b/catalyst-gateway/bin/src/event_db/legacy/queries/registration.rs @@ -2,11 +2,12 @@ use chrono::{NaiveDateTime, Utc}; use crate::event_db::{ + error::NotFoundError, legacy::types::{ event::EventId, registration::{Delegation, Delegator, RewardAddress, Voter, VoterGroupId, VoterInfo}, }, - Error, EventDB, + EventDB, }; impl EventDB { @@ -74,7 +75,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_voter( &self, event: &Option, voting_key: String, with_delegations: bool, - ) -> Result { + ) -> anyhow::Result { let conn = self.pool.get().await?; let rows = if let Some(event) = event { @@ -84,7 +85,7 @@ impl EventDB { conn.query(Self::VOTER_BY_LAST_EVENT_QUERY, &[&voting_key]) .await? }; - let voter = rows.first().ok_or(Error::NotFound)?; + let voter = rows.first().ok_or(NotFoundError)?; let voting_group = VoterGroupId(voter.try_get("voting_group")?); let voting_power = voter.try_get("voting_power")?; @@ -102,7 +103,7 @@ impl EventDB { let total_voting_power_per_group: i64 = rows .first() - .ok_or(Error::NotFound)? + .ok_or(NotFoundError)? .try_get("total_voting_power")?; let voting_power_saturation = if total_voting_power_per_group == 0 { @@ -163,7 +164,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn get_delegator( &self, event: &Option, stake_public_key: String, - ) -> Result { + ) -> anyhow::Result { let conn = self.pool.get().await?; let rows = if let Some(event) = event { conn.query(Self::DELEGATOR_SNAPSHOT_INFO_BY_EVENT_QUERY, &[&event.0]) @@ -172,7 +173,7 @@ impl EventDB { conn.query(Self::DELEGATOR_SNAPSHOT_INFO_BY_LAST_EVENT_QUERY, &[]) .await? }; - let delegator_snapshot_info = rows.first().ok_or(Error::NotFound)?; + let delegator_snapshot_info = rows.first().ok_or(NotFoundError)?; let delegation_rows = if let Some(event) = event { conn.query(Self::DELEGATIONS_BY_EVENT_QUERY, &[ @@ -188,7 +189,7 @@ impl EventDB { .await? }; if delegation_rows.is_empty() { - return Err(Error::NotFound); + return Err(NotFoundError.into()); } let mut delegations = Vec::new(); @@ -210,7 +211,7 @@ impl EventDB { }; let total_power: i64 = rows .first() - .ok_or(Error::NotFound)? + .ok_or(NotFoundError)? .try_get("total_voting_power")?; #[allow(clippy::indexing_slicing)] // delegation_rows already checked to be not empty. diff --git a/catalyst-gateway/bin/src/event_db/legacy/queries/search.rs b/catalyst-gateway/bin/src/event_db/legacy/queries/search.rs index 0e9356e4fc5..e21e15c2fe7 100644 --- a/catalyst-gateway/bin/src/event_db/legacy/queries/search.rs +++ b/catalyst-gateway/bin/src/event_db/legacy/queries/search.rs @@ -2,6 +2,7 @@ use chrono::{NaiveDateTime, Utc}; use crate::event_db::{ + error::NotFoundError, legacy::types::{ event::{EventId, EventSummary}, objective::{ObjectiveId, ObjectiveSummary, ObjectiveType}, @@ -10,7 +11,7 @@ use crate::event_db::{ SearchConstraint, SearchOrderBy, SearchQuery, SearchResult, SearchTable, ValueResults, }, }, - Error, EventDB, + EventDB, }; impl EventDB { @@ -121,7 +122,7 @@ impl EventDB { /// Search for a total. async fn search_total( &self, search_query: SearchQuery, limit: Option, offset: Option, - ) -> Result { + ) -> anyhow::Result { let conn = self.pool.get().await?; let rows: Vec = conn @@ -130,8 +131,8 @@ impl EventDB { &offset.unwrap_or(0), ]) .await - .map_err(|_| Error::NotFound)?; - let row = rows.first().ok_or(Error::NotFound)?; + .map_err(|_| NotFoundError)?; + let row = rows.first().ok_or(NotFoundError)?; Ok(SearchResult { total: row.try_get("total")?, @@ -142,7 +143,7 @@ impl EventDB { /// Search for events async fn search_events( &self, search_query: SearchQuery, limit: Option, offset: Option, - ) -> Result { + ) -> anyhow::Result { let conn = self.pool.get().await?; let rows: Vec = conn .query(&Self::construct_query(&search_query), &[ @@ -150,7 +151,7 @@ impl EventDB { &offset.unwrap_or(0), ]) .await - .map_err(|_| Error::NotFound)?; + .map_err(|_| NotFoundError)?; let mut events = Vec::new(); for row in rows { @@ -172,10 +173,7 @@ impl EventDB { }); } - let total: i64 = events - .len() - .try_into() - .map_err(|_| Error::Unknown("Cannot convert to i64".to_string()))?; + let total: i64 = events.len().try_into()?; Ok(SearchResult { total, @@ -186,7 +184,7 @@ impl EventDB { /// Search for objectives async fn search_objectives( &self, search_query: SearchQuery, limit: Option, offset: Option, - ) -> Result { + ) -> anyhow::Result { let conn = self.pool.get().await?; let rows: Vec = conn .query(&Self::construct_query(&search_query), &[ @@ -194,7 +192,7 @@ impl EventDB { &offset.unwrap_or(0), ]) .await - .map_err(|_| Error::NotFound)?; + .map_err(|_| NotFoundError)?; let mut objectives = Vec::new(); for row in rows { @@ -211,10 +209,7 @@ impl EventDB { objectives.push(objective); } - let total: i64 = objectives - .len() - .try_into() - .map_err(|_| Error::Unknown("Cannot convert to i64".to_string()))?; + let total: i64 = objectives.len().try_into()?; Ok(SearchResult { total, @@ -225,7 +220,7 @@ impl EventDB { /// Search for proposals async fn search_proposals( &self, search_query: SearchQuery, limit: Option, offset: Option, - ) -> Result { + ) -> anyhow::Result { let conn = self.pool.get().await?; let rows: Vec = conn @@ -234,7 +229,7 @@ impl EventDB { &offset.unwrap_or(0), ]) .await - .map_err(|_| Error::NotFound)?; + .map_err(|_| NotFoundError)?; let mut proposals = Vec::new(); for row in rows { @@ -248,10 +243,7 @@ impl EventDB { proposals.push(summary); } - let total: i64 = proposals - .len() - .try_into() - .map_err(|_| Error::Unknown("Cannot convert to i64".to_string()))?; + let total: i64 = proposals.len().try_into()?; Ok(SearchResult { total, @@ -265,7 +257,7 @@ impl EventDB { #[allow(dead_code)] pub(crate) async fn search( &self, search_query: SearchQuery, total: bool, limit: Option, offset: Option, - ) -> Result { + ) -> anyhow::Result { if total { self.search_total(search_query, limit, offset).await } else { diff --git a/catalyst-gateway/bin/src/event_db/legacy/queries/vit_ss/fund.rs b/catalyst-gateway/bin/src/event_db/legacy/queries/vit_ss/fund.rs index 7b6e88b9bb8..723abc42e60 100644 --- a/catalyst-gateway/bin/src/event_db/legacy/queries/vit_ss/fund.rs +++ b/catalyst-gateway/bin/src/event_db/legacy/queries/vit_ss/fund.rs @@ -1,6 +1,7 @@ use chrono::{NaiveDateTime, Utc}; use crate::event_db::{ + error::NotFoundError, legacy::types::vit_ss::{ challenge::{Challenge, ChallengeHighlights}, fund::{Fund, FundNextInfo, FundStageDates, FundWithNext}, @@ -8,7 +9,7 @@ use crate::event_db::{ group::Group, vote_plan::Voteplan, }, - Error, EventDB, + EventDB, }; impl EventDB { @@ -107,11 +108,11 @@ impl EventDB { /// Get fund query // TODO(stevenj): https://github.com/input-output-hk/catalyst-voices/issues/68 #[allow(dead_code, clippy::too_many_lines)] - pub(crate) async fn get_fund(&self) -> Result { + pub(crate) async fn get_fund(&self) -> anyhow::Result { let conn = self.pool.get().await?; let rows = conn.query(Self::FUND_QUERY, &[]).await?; - let row = rows.first().ok_or(Error::NotFound)?; + let row = rows.first().ok_or(NotFoundError)?; let fund_id = row.try_get("id")?; diff --git a/catalyst-gateway/bin/src/event_db/mod.rs b/catalyst-gateway/bin/src/event_db/mod.rs index 5c1b23bb17d..e516e8151d3 100644 --- a/catalyst-gateway/bin/src/event_db/mod.rs +++ b/catalyst-gateway/bin/src/event_db/mod.rs @@ -4,7 +4,6 @@ use std::str::FromStr; use bb8::Pool; use bb8_postgres::PostgresConnectionManager; use dotenvy::dotenv; -use error::Error; use tokio_postgres::NoTls; pub(crate) mod config; @@ -32,6 +31,11 @@ pub(crate) struct EventDB { pool: Pool>, } +/// No DB URL was provided +#[derive(thiserror::Error, Debug, PartialEq, Eq)] +#[error("DB URL is undefined")] +pub(crate) struct NoDatabaseUrlError; + /// Establish a connection to the database, and check the schema is up-to-date. /// /// # Parameters @@ -54,14 +58,14 @@ pub(crate) struct EventDB { /// /// The env var "`DATABASE_URL`" can be set directly as an anv var, or in a /// `.env` file. -pub(crate) async fn establish_connection(url: Option) -> Result { +pub(crate) async fn establish_connection(url: Option) -> anyhow::Result { // Support env vars in a `.env` file, doesn't need to exist. dotenv().ok(); let database_url = match url { Some(url) => url, // If the Database connection URL is not supplied, try and get from the env var. - None => std::env::var(DATABASE_URL_ENVVAR).map_err(|_| Error::NoDatabaseUrl)?, + None => std::env::var(DATABASE_URL_ENVVAR).map_err(|_| NoDatabaseUrlError)?, }; let config = tokio_postgres::config::Config::from_str(&database_url)?; diff --git a/catalyst-gateway/bin/src/event_db/schema_check.rs b/catalyst-gateway/bin/src/event_db/schema_check.rs index b4b2bf1589a..cf2a17e815f 100644 --- a/catalyst-gateway/bin/src/event_db/schema_check.rs +++ b/catalyst-gateway/bin/src/event_db/schema_check.rs @@ -1,12 +1,22 @@ //! Check if the schema is up-to-date. -use crate::event_db::{Error, EventDB, DATABASE_SCHEMA_VERSION}; +use crate::event_db::{EventDB, DATABASE_SCHEMA_VERSION}; + +/// Schema in database does not match schema supported by the Crate. +#[derive(thiserror::Error, Debug, PartialEq, Eq)] +#[error(" Schema in database does not match schema supported by the Crate. The current schema version: {was}, the schema version we expected: {expected}")] +pub(crate) struct MismatchedSchemaError { + /// The current DB schema version. + was: i32, + /// The expected DB schema version. + expected: i32, +} impl EventDB { /// Check the schema version. /// return the current schema version if its current. /// Otherwise return an error. - pub(crate) async fn schema_version_check(&self) -> Result { + pub(crate) async fn schema_version_check(&self) -> anyhow::Result { let conn = self.pool.get().await?; let schema_check = conn @@ -21,10 +31,11 @@ impl EventDB { if current_ver == DATABASE_SCHEMA_VERSION { Ok(current_ver) } else { - Err(Error::MismatchedSchema { + Err(MismatchedSchemaError { was: current_ver, expected: DATABASE_SCHEMA_VERSION, - }) + } + .into()) } } } diff --git a/catalyst-gateway/bin/src/event_db/utxo.rs b/catalyst-gateway/bin/src/event_db/utxo.rs index 5826a7da04b..01ebe21c18f 100644 --- a/catalyst-gateway/bin/src/event_db/utxo.rs +++ b/catalyst-gateway/bin/src/event_db/utxo.rs @@ -5,7 +5,7 @@ use pallas::ledger::{addresses::Address, traverse::MultiEraTx}; use super::{follower::SlotNumber, voter_registration::StakeCredential}; use crate::{ - event_db::{Error, EventDB}, + event_db::{error::NotFoundError, EventDB}, util::parse_policy_assets, }; @@ -14,7 +14,7 @@ pub(crate) type StakeAmount = i64; impl EventDB { /// Index utxo data - pub(crate) async fn index_utxo_data(&self, tx: &MultiEraTx<'_>) -> Result<(), Error> { + pub(crate) async fn index_utxo_data(&self, tx: &MultiEraTx<'_>) -> anyhow::Result<()> { let conn = self.pool.get().await?; let tx_hash = tx.hash(); @@ -23,12 +23,9 @@ impl EventDB { for (index, tx_out) in tx.outputs().iter().enumerate() { // extract assets let assets = serde_json::to_value(parse_policy_assets(&tx_out.non_ada_assets())) - .map_err(|e| Error::AssetParsingIssue(format!("Asset parsing issue {e}")))?; + .map_err(|e| anyhow::anyhow!(format!("Asset parsing issue {e}")))?; - let stake_address = match tx_out - .address() - .map_err(|e| Error::Unknown(format!("Address issue {e}")))? - { + let stake_address = match tx_out.address()? { Address::Shelley(address) => address.try_into().ok(), Address::Stake(stake_address) => Some(stake_address), Address::Byron(_) => None, @@ -39,10 +36,9 @@ impl EventDB { .query( include_str!("../../../event-db/queries/utxo/insert_utxo.sql"), &[ - &i32::try_from(index).map_err(|e| Error::Unknown(e.to_string()))?, + &i32::try_from(index)?, &tx_hash.as_slice(), - &i64::try_from(tx_out.lovelace_amount()) - .map_err(|e| Error::Unknown(e.to_string()))?, + &i64::try_from(tx_out.lovelace_amount())?, &stake_credential, &assets, ], @@ -61,7 +57,7 @@ impl EventDB { &[ &tx_hash.as_slice(), &output_tx_hash.as_slice(), - &i32::try_from(out_index).map_err(|e| Error::Unknown(e.to_string()))?, + &i32::try_from(out_index)?, ], ) .await?; @@ -73,7 +69,7 @@ impl EventDB { /// Index txn metadata pub(crate) async fn index_txn_data( &self, tx_id: &[u8], slot_no: SlotNumber, network: Network, - ) -> Result<(), Error> { + ) -> anyhow::Result<()> { let conn = self.pool.get().await?; let _rows = conn @@ -89,7 +85,7 @@ impl EventDB { /// Get total utxo amount pub(crate) async fn total_utxo_amount( &self, stake_credential: StakeCredential, network: Network, slot_num: SlotNumber, - ) -> Result<(StakeAmount, SlotNumber), Error> { + ) -> anyhow::Result<(StakeAmount, SlotNumber)> { let conn = self.pool.get().await?; let row = conn @@ -107,7 +103,7 @@ impl EventDB { Ok((amount, slot_number)) } else { - Err(Error::NotFound) + Err(NotFoundError.into()) } } } diff --git a/catalyst-gateway/bin/src/event_db/voter_registration/mod.rs b/catalyst-gateway/bin/src/event_db/voter_registration/mod.rs index f2f5fbd2671..49401e6982d 100644 --- a/catalyst-gateway/bin/src/event_db/voter_registration/mod.rs +++ b/catalyst-gateway/bin/src/event_db/voter_registration/mod.rs @@ -4,7 +4,7 @@ use cardano_chain_follower::Network; use pallas::ledger::traverse::MultiEraTx; use serde_json::json; -use super::{follower::SlotNumber, Error, EventDB}; +use super::{error::NotFoundError, follower::SlotNumber, EventDB}; use crate::registration::{ parse_registrations_from_metadata, validate_reg_cddl, CddlConfig, ErrorReport, VotingInfo, }; @@ -45,7 +45,7 @@ impl EventDB { &self, tx_id: TxId, stake_credential: Option, public_voting_key: Option, payment_address: Option, metadata_cip36: Option, nonce: Option, errors_report: ErrorReport, - ) -> Result<(), Error> { + ) -> anyhow::Result<()> { let conn = self.pool.get().await?; // for the catalyst we dont support multiple delegations @@ -60,7 +60,7 @@ impl EventDB { let encoded_voting_key = if let Some(voting_key) = public_voting_key { Some( serde_json::to_string(&voting_key) - .map_err(|_| Error::Unknown("Cannot encode voting key".to_string()))? + .map_err(|_| anyhow::anyhow!("Cannot encode voting key".to_string()))? .as_bytes() .to_vec(), ) @@ -95,7 +95,7 @@ impl EventDB { /// Index registration data pub(crate) async fn index_registration_data( &self, tx: &MultiEraTx<'_>, network: Network, - ) -> Result<(), Error> { + ) -> anyhow::Result<()> { let cddl = CddlConfig::new(); if !tx.metadata().is_empty() { @@ -125,9 +125,7 @@ impl EventDB { } let nonce = if let Some(nonce) = registration.nonce { - Some(nonce.0.try_into().map_err(|_| { - Error::Unknown("Cannot cast registration nonce from u64 to i64".to_string()) - })?) + Some(nonce.0.try_into()?) } else { None }; @@ -151,7 +149,7 @@ impl EventDB { /// Get registration info pub(crate) async fn get_registration_info( &self, stake_credential: StakeCredential, network: Network, slot_num: SlotNumber, - ) -> Result<(TxId, PaymentAddress, PublicVotingInfo, Nonce), Error> { + ) -> anyhow::Result<(TxId, PaymentAddress, PublicVotingInfo, Nonce)> { let conn = self.pool.get().await?; let rows = conn @@ -162,18 +160,16 @@ impl EventDB { ]) .await?; - let Some(row) = rows.first() else { - return Err(Error::NotFound); - }; + let row = rows.first().ok_or(NotFoundError)?; let tx_id = row.try_get(TX_ID_COLUMN)?; let payment_address = row.try_get(PAYMENT_ADDRESS_COLUMN)?; let nonce = row.try_get(NONCE_COLUMN)?; let public_voting_info = serde_json::from_str( &String::from_utf8(row.try_get(PUBLIC_VOTING_KEY_COLUMN)?) - .map_err(|_| Error::Unknown("Cannot parse public voting key".to_string()))?, + .map_err(|_| anyhow::anyhow!("Cannot parse public voting key".to_string()))?, ) - .map_err(|_| Error::Unknown("Cannot parse public voting key".to_string()))?; + .map_err(|_| anyhow::anyhow!("Cannot parse public voting key".to_string()))?; Ok((tx_id, payment_address, public_voting_info, nonce)) } diff --git a/catalyst-gateway/bin/src/follower.rs b/catalyst-gateway/bin/src/follower.rs index f289ef513fb..174199a138e 100644 --- a/catalyst-gateway/bin/src/follower.rs +++ b/catalyst-gateway/bin/src/follower.rs @@ -51,7 +51,7 @@ pub(crate) async fn start_followers( } }, Err(err) => { - error!("No config {:?}", err); + error!("Get follower config error: {err}"); break None; }, } diff --git a/catalyst-gateway/bin/src/main.rs b/catalyst-gateway/bin/src/main.rs index c658d782236..2ed906d380b 100644 --- a/catalyst-gateway/bin/src/main.rs +++ b/catalyst-gateway/bin/src/main.rs @@ -12,7 +12,7 @@ mod state; mod util; #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> anyhow::Result<()> { cli::Cli::parse().exec().await?; Ok(()) } diff --git a/catalyst-gateway/bin/src/registration/mod.rs b/catalyst-gateway/bin/src/registration/mod.rs index 0216aac7ae8..1ff5a817482 100644 --- a/catalyst-gateway/bin/src/registration/mod.rs +++ b/catalyst-gateway/bin/src/registration/mod.rs @@ -1,6 +1,6 @@ //! Verify registration TXs -use std::{error::Error, io::Cursor}; +use std::io::Cursor; use cardano_chain_follower::Network; use ciborium::Value; @@ -187,7 +187,7 @@ impl Default for VotingInfo { /// # Errors /// /// Failure will occur if parsed keys do not match CDDL spec -pub fn validate_reg_cddl(bin_reg: &[u8], cddl_config: &CddlConfig) -> Result<(), Box> { +pub fn validate_reg_cddl(bin_reg: &[u8], cddl_config: &CddlConfig) -> anyhow::Result<()> { cddl::validate_cbor_from_slice(&cddl_config.cip_36, bin_reg, None)?; Ok(()) @@ -226,23 +226,26 @@ pub fn is_valid_rewards_address(rewards_address_prefix: u8, network: Network) -> } /// Convert raw 61285 cbor to witness signature -pub fn raw_sig_conversion(raw_cbor: &[u8]) -> Result> { +pub fn raw_sig_conversion(raw_cbor: &[u8]) -> anyhow::Result { let decoded: ciborium::value::Value = ciborium::de::from_reader(Cursor::new(&raw_cbor))?; let signature_61285 = match decoded { Value::Map(m) => m.iter().map(|entry| entry.1.clone()).collect::>(), - _ => return Err(format!("Invalid signature {decoded:?}").into()), + _ => return Err(anyhow::anyhow!("Invalid signature {decoded:?}")), }; - let sig = signature_61285.first().ok_or("no 61285 key")?.clone(); + let sig = signature_61285 + .first() + .ok_or(anyhow::anyhow!("no 61285 key"))? + .clone(); let sig_bytes: [u8; 64] = match sig.into_bytes() { Ok(s) => { match s.try_into() { Ok(sig) => sig, - Err(err) => return Err(format!("Invalid signature length {err:?}").into()), + Err(err) => return Err(anyhow::anyhow!("Invalid signature length {err:?}")), } }, - Err(err) => return Err(format!("Invalid signature parsing {err:?}").into()), + Err(err) => return Err(anyhow::anyhow!("Invalid signature parsing {err:?}")), }; Ok(Signature::from_bytes(&sig_bytes)) @@ -250,18 +253,19 @@ pub fn raw_sig_conversion(raw_cbor: &[u8]) -> Result> #[allow(clippy::manual_let_else)] /// Parse cip36 registration tx -pub fn inspect_metamap_reg(spec_61284: &[Value]) -> Result<&Vec<(Value, Value)>, Box> { +pub fn inspect_metamap_reg(spec_61284: &[Value]) -> anyhow::Result<&Vec<(Value, Value)>> { let metamap = match &spec_61284 .get(KEY_61284) - .ok_or("Issue parsing 61284 parent key")? + .ok_or(anyhow::anyhow!("Issue parsing 61284 parent key"))? { Value::Map(metamap) => metamap, _ => { - return Err(format!( + return Err(anyhow::anyhow!( "Invalid metamap {:?}", - spec_61284.get(KEY_61284).ok_or("Issue parsing metamap")? - ) - .into()) + spec_61284 + .get(KEY_61284) + .ok_or(anyhow::anyhow!("Issue parsing metamap"))? + )) }, }; Ok(metamap) @@ -269,10 +273,10 @@ pub fn inspect_metamap_reg(spec_61284: &[Value]) -> Result<&Vec<(Value, Value)>, #[allow(clippy::manual_let_else)] /// Extract voting key -pub fn inspect_voting_key(metamap: &[(Value, Value)]) -> Result> { +pub fn inspect_voting_key(metamap: &[(Value, Value)]) -> anyhow::Result { let voting_key = match &metamap .get(DELEGATIONS_OR_DIRECT) - .ok_or("Issue with voting key 61284 cbor parsing")? + .ok_or(anyhow::anyhow!("Issue with voting key 61284 cbor parsing"))? { (Value::Integer(_one), Value::Bytes(direct)) => VotingInfo::Direct(PubKey(direct.clone())), (Value::Integer(_one), Value::Array(delegations)) => { @@ -282,54 +286,52 @@ pub fn inspect_voting_key(metamap: &[(Value, Value)]) -> Result { let voting_key = match delegations .get(VOTE_KEY) - .ok_or("Issue parsing delegations")? + .ok_or(anyhow::anyhow!("Issue parsing delegations"))? .as_bytes() { Some(key) => key, - None => return Err("Invalid voting key".to_string().into()), + None => return Err(anyhow::anyhow!("Invalid voting key")), }; let weight = match delegations .get(WEIGHT) - .ok_or("Issue parsing weight")? + .ok_or(anyhow::anyhow!("Issue parsing weight"))? .as_integer() { Some(weight) => { match weight.try_into() { Ok(weight) => weight, Err(_err) => { - return Err("Invalid weight in delegation" - .to_string() - .into()) + return Err(anyhow::anyhow!("Invalid weight in delegation")) }, } }, - None => return Err("Invalid delegation".to_string().into()), + None => return Err(anyhow::anyhow!("Invalid delegation")), }; delegations_map.push(((PubKey(voting_key.clone())), weight)); }, - _ => return Err("Invalid voting key".to_string().into()), + _ => return Err(anyhow::anyhow!("Invalid voting key")), } } VotingInfo::Delegated(delegations_map) }, - _ => return Err("Invalid signature".to_string().into()), + _ => return Err(anyhow::anyhow!("Invalid signature")), }; Ok(voting_key) } /// Extract stake key -pub fn inspect_stake_key(metamap: &[(Value, Value)]) -> Result> { +pub fn inspect_stake_key(metamap: &[(Value, Value)]) -> anyhow::Result { let stake_key = match &metamap .get(STAKE_ADDRESS) - .ok_or("Issue with stake key parsing")? + .ok_or(anyhow::anyhow!("Issue with stake key parsing"))? { (Value::Integer(_two), Value::Bytes(stake_addr)) => PubKey(stake_addr.clone()), - _ => return Err("Invalid stake key".to_string().into()), + _ => return Err(anyhow::anyhow!("Invalid stake key")), }; Ok(stake_key) } @@ -337,38 +339,44 @@ pub fn inspect_stake_key(metamap: &[(Value, Value)]) -> Result Result<&Vec, Box> { +) -> anyhow::Result<&Vec> { let (Value::Integer(_three), Value::Bytes(rewards_address)) = &metamap .get(PAYMENT_ADDRESS) - .ok_or("Issue with rewards address parsing")? + .ok_or(anyhow::anyhow!("Issue with rewards address parsing"))? else { - return Err("Invalid rewards address".to_string().into()); + return Err(anyhow::anyhow!("Invalid rewards address")); }; - if !is_valid_rewards_address(*rewards_address.get(NETWORK_ID).ok_or("err")?, network_id) { - return Err("Invalid reward address".to_string().into()); + if !is_valid_rewards_address( + *rewards_address + .get(NETWORK_ID) + .ok_or(anyhow::anyhow!("Cannot get network id byte"))?, + network_id, + ) { + return Err(anyhow::anyhow!("Invalid reward address")); } Ok(rewards_address) } /// Extract Nonce -pub fn inspect_nonce(metamap: &[(Value, Value)]) -> Result> { - let nonce: i128 = match metamap.get(NONCE).ok_or("Issue with nonce parsing")? { +pub fn inspect_nonce(metamap: &[(Value, Value)]) -> anyhow::Result { + let nonce: i128 = match metamap + .get(NONCE) + .ok_or(anyhow::anyhow!("Issue with nonce parsing"))? + { (Value::Integer(_four), Value::Integer(nonce)) => i128::from(*nonce), - _ => return Err("Invalid nonce".to_string().into()), + _ => return Err(anyhow::anyhow!("Invalid nonce")), }; Ok(Nonce(nonce.try_into()?)) } /// Extract optional voting purpose -pub fn inspect_voting_purpose( - metamap: &[(Value, Value)], -) -> Result, Box> { +pub fn inspect_voting_purpose(metamap: &[(Value, Value)]) -> anyhow::Result> { if metamap.len() == 5 { match metamap .get(VOTE_PURPOSE) - .ok_or("Issue with voting purpose parsing")? + .ok_or(anyhow::anyhow!("Issue with voting purpose parsing"))? { (Value::Integer(_five), Value::Integer(purpose)) => { Ok(Some(VotingPurpose(i128::from(*purpose).try_into()?))) @@ -384,7 +392,7 @@ pub fn inspect_voting_purpose( /// Collect secondary errors for granular json error report pub fn parse_registrations_from_metadata( meta: &MultiEraMeta, network: Network, -) -> Result<(Registration, ErrorReport), Box> { +) -> anyhow::Result<(Registration, ErrorReport)> { let mut voting_key: Option = None; let mut stake_key: Option = None; let mut voting_purpose: Option = None; @@ -398,7 +406,7 @@ pub fn parse_registrations_from_metadata( if let pallas::ledger::traverse::MultiEraMeta::AlonzoCompatible(meta) = meta { for (key, cip36_registration) in meta.iter() { if *key == u64::try_from(CIP36_61284)? { - let raw_cbor = meta.encode_fragment()?; + let raw_cbor = meta.encode_fragment().map_err(|e| anyhow::anyhow!("{e}"))?; raw_cbor_cip36 = Some(raw_cbor.clone()); let decoded: ciborium::value::Value = @@ -475,9 +483,15 @@ pub fn parse_registrations_from_metadata( }; } else if *key == u64::try_from(CIP36_61285)? { // Validate 61285 signature - let raw_cbor = cip36_registration.encode_fragment()?; - - match raw_sig_conversion(&cip36_registration.encode_fragment()?) { + let raw_cbor = cip36_registration + .encode_fragment() + .map_err(|e| anyhow::anyhow!("{e}"))?; + + match raw_sig_conversion( + &cip36_registration + .encode_fragment() + .map_err(|e| anyhow::anyhow!("{e}"))?, + ) { Ok(signature) => { sig = Some(signature); }, diff --git a/catalyst-gateway/bin/src/service/api/cardano/date_time_to_slot_number_get.rs b/catalyst-gateway/bin/src/service/api/cardano/date_time_to_slot_number_get.rs index 387c8a4a31f..2de58338b28 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/date_time_to_slot_number_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/date_time_to_slot_number_get.rs @@ -5,8 +5,8 @@ use poem_openapi::payload::Json; use crate::{ event_db::{ - error::Error as DBError, - follower::{DateTime, SlotInfoQueryType}, + error::NotFoundError, + follower::{BlockHash, DateTime, SlotInfoQueryType, SlotNumber}, }, service::common::{ objects::cardano::{ @@ -16,7 +16,7 @@ use crate::{ responses::{ resp_2xx::OK, resp_4xx::ApiValidationError, - resp_5xx::{server_error_response, ServerError, ServiceUnavailable}, + resp_5xx::{handle_5xx_response, ServerError, ServiceUnavailable}, }, }, state::State, @@ -54,31 +54,32 @@ pub(crate) async fn endpoint( event_db.get_slot_info(date_time, network.into(), SlotInfoQueryType::Next) ); - let process_slot_info_result = |slot_info_result| { - match slot_info_result { - Ok((slot_number, block_hash, block_time)) => { - Ok(Some(Slot { - slot_number, - block_hash: From::from(block_hash), - block_time, - })) - }, - Err(DBError::NotFound) => Ok(None), - Err(err) => Err(err), - } - }; + let process_slot_info_result = + |slot_info_result: anyhow::Result<(SlotNumber, BlockHash, DateTime)>| { + match slot_info_result { + Ok((slot_number, block_hash, block_time)) => { + Ok(Some(Slot { + slot_number, + block_hash: From::from(block_hash), + block_time, + })) + }, + Err(err) if err.is::() => Ok(None), + Err(err) => Err(err), + } + }; let current = match process_slot_info_result(current) { Ok(current) => current, - Err(err) => return server_error_response!("{err}"), + Err(err) => return handle_5xx_response!(err), }; let previous = match process_slot_info_result(previous) { Ok(current) => current, - Err(err) => return server_error_response!("{err}"), + Err(err) => return handle_5xx_response!(err), }; let next = match process_slot_info_result(next) { Ok(current) => current, - Err(err) => return server_error_response!("{err}"), + Err(err) => return handle_5xx_response!(err), }; T200(OK(Json(SlotInfo { diff --git a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs index 25214462768..facda154bea 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs @@ -7,7 +7,7 @@ use poem_extensions::{ use poem_openapi::payload::Json; use crate::{ - event_db::{error::Error as DBError, follower::SlotNumber}, + event_db::{error::NotFoundError, follower::SlotNumber}, service::{ common::{ objects::cardano::{ @@ -16,7 +16,7 @@ use crate::{ responses::{ resp_2xx::OK, resp_4xx::{ApiValidationError, NotFound}, - resp_5xx::{server_error_response, ServerError, ServiceUnavailable}, + resp_5xx::{handle_5xx_response, ServerError, ServiceUnavailable}, }, }, utilities::check_network, @@ -61,7 +61,7 @@ pub(crate) async fn endpoint( nonce, )))) }, - Err(DBError::NotFound) => T404(NotFound), - Err(err) => server_error_response!("{err}"), + Err(err) if err.is::() => T404(NotFound), + Err(err) => handle_5xx_response!(err), } } diff --git a/catalyst-gateway/bin/src/service/api/cardano/staked_ada_get.rs b/catalyst-gateway/bin/src/service/api/cardano/staked_ada_get.rs index 94c61ed075d..611dce50abc 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/staked_ada_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/staked_ada_get.rs @@ -7,7 +7,7 @@ use poem_extensions::{ use poem_openapi::payload::Json; use crate::{ - event_db::{error::Error as DBError, follower::SlotNumber}, + event_db::{error::NotFoundError, follower::SlotNumber}, service::{ common::{ objects::cardano::{ @@ -16,7 +16,7 @@ use crate::{ responses::{ resp_2xx::OK, resp_4xx::{ApiValidationError, NotFound}, - resp_5xx::{server_error_response, ServerError, ServiceUnavailable}, + resp_5xx::{handle_5xx_response, ServerError, ServiceUnavailable}, }, }, utilities::check_network, @@ -59,7 +59,7 @@ pub(crate) async fn endpoint( slot_number, }))) }, - Err(DBError::NotFound) => T404(NotFound), - Err(err) => server_error_response!("{err}"), + Err(err) if err.is::() => T404(NotFound), + Err(err) => handle_5xx_response!(err), } } diff --git a/catalyst-gateway/bin/src/service/api/cardano/sync_state_get.rs b/catalyst-gateway/bin/src/service/api/cardano/sync_state_get.rs index 0b677b2f422..1636ebc0b28 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/sync_state_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/sync_state_get.rs @@ -7,13 +7,13 @@ use poem_extensions::{ use poem_openapi::payload::Json; use crate::{ - event_db::error::Error as DBError, + event_db::error::NotFoundError, service::common::{ objects::cardano::{network::Network, sync_state::SyncState}, responses::{ resp_2xx::OK, resp_4xx::{ApiValidationError, NotFound}, - resp_5xx::{server_error_response, ServerError, ServiceUnavailable}, + resp_5xx::{handle_5xx_response, ServerError, ServiceUnavailable}, }, }, state::State, @@ -43,7 +43,7 @@ pub(crate) async fn endpoint(state: &State, network: Option) -> AllResp last_updated, }))) }, - Err(DBError::NotFound) => T404(NotFound), - Err(err) => server_error_response!("{err}"), + Err(err) if err.is::() => T404(NotFound), + Err(err) => handle_5xx_response!(err), } } diff --git a/catalyst-gateway/bin/src/service/api/health/ready_get.rs b/catalyst-gateway/bin/src/service/api/health/ready_get.rs index ad309288c44..99abdbeeb7b 100644 --- a/catalyst-gateway/bin/src/service/api/health/ready_get.rs +++ b/catalyst-gateway/bin/src/service/api/health/ready_get.rs @@ -9,11 +9,11 @@ use poem_extensions::{ }; use crate::{ - event_db::error::Error as DBError, + event_db::schema_check::MismatchedSchemaError, service::common::responses::{ resp_2xx::NoContent, resp_4xx::ApiValidationError, - resp_5xx::{server_error_response, ServerError, ServiceUnavailable}, + resp_5xx::{handle_5xx_response, ServerError, ServiceUnavailable}, }, state::State, }; @@ -57,15 +57,10 @@ pub(crate) async fn endpoint(state: Data<&Arc>) -> AllResponses { tracing::debug!("DB schema version status ok"); T204(NoContent) }, - Err(DBError::MismatchedSchema { was, expected }) => { - tracing::error!( - expected = expected, - current = was, - "DB schema version status mismatch" - ); + Err(err) if err.is::() => { + tracing::error!("{err}"); T503(ServiceUnavailable) }, - Err(DBError::TimedOut) => T503(ServiceUnavailable), - Err(err) => server_error_response!("{err}"), + Err(err) => handle_5xx_response!(err), } } diff --git a/catalyst-gateway/bin/src/service/common/responses/resp_5xx.rs b/catalyst-gateway/bin/src/service/common/responses/resp_5xx.rs index 39a15054f98..29d1310c0f1 100644 --- a/catalyst-gateway/bin/src/service/common/responses/resp_5xx.rs +++ b/catalyst-gateway/bin/src/service/common/responses/resp_5xx.rs @@ -9,17 +9,23 @@ use uuid::Uuid; /// probably want to place this in your crate root use crate::settings::generate_github_issue_url; -/// Create a new Server Error Response. +/// Handle a 5xx response. +/// Returns a Server Error or a Service Unavailable response. /// Logging error message. -macro_rules! server_error_response { - ($($t:tt)*) => {{ - let error = crate::service::common::responses::resp_5xx::ServerError::new(None); - let id = error.id(); - tracing::error!(id = format!("{id}") ,$($t)*); - poem_extensions::UniResponse::T500(error) +/// Argument must be `anyhow::Error` type. +macro_rules! handle_5xx_response { + ($err:ident) => {{ + if $err.is::>() { + poem_extensions::UniResponse::T503(ServiceUnavailable) + } else { + let error = crate::service::common::responses::resp_5xx::ServerError::new(None); + let id = error.id(); + tracing::error!(id = format!("{id}"), "{}", $err); + poem_extensions::UniResponse::T500(error) + } }}; } -pub(crate) use server_error_response; +pub(crate) use handle_5xx_response; #[derive(Debug, Object)] #[oai(example, skip_serializing_if_is_none)] diff --git a/catalyst-gateway/bin/src/service/mod.rs b/catalyst-gateway/bin/src/service/mod.rs index 6ce731a3fa9..85d172a603a 100644 --- a/catalyst-gateway/bin/src/service/mod.rs +++ b/catalyst-gateway/bin/src/service/mod.rs @@ -14,17 +14,6 @@ mod utilities; pub(crate) use poem_service::get_app_docs; -/// Service level errors -#[derive(thiserror::Error, Debug)] -pub(crate) enum Error { - /// An error with the `EventDB` - #[error(transparent)] - EventDb(#[from] crate::event_db::error::Error), - /// An IO error has occurred - #[error(transparent)] - Io(#[from] std::io::Error), -} - /// # Run Catalyst Gateway Service. /// /// Runs the Poem based API. @@ -39,6 +28,6 @@ pub(crate) enum Error { /// `Error::CannotRunService` - cannot run the service /// `Error::EventDbError` - cannot connect to the event db /// `Error::IoError` - An IO error has occurred. -pub(crate) async fn run(settings: &DocsSettings, state: Arc) -> Result<(), Error> { +pub(crate) async fn run(settings: &DocsSettings, state: Arc) -> anyhow::Result<()> { poem_service::run(settings, state).await } diff --git a/catalyst-gateway/bin/src/service/poem_service.rs b/catalyst-gateway/bin/src/service/poem_service.rs index f06e87c0487..41067f31aa6 100644 --- a/catalyst-gateway/bin/src/service/poem_service.rs +++ b/catalyst-gateway/bin/src/service/poem_service.rs @@ -20,7 +20,6 @@ use crate::{ catch_panic::{set_panic_hook, ServicePanicHandler}, middleware::tracing_mw::{init_prometheus, Tracing}, }, - Error, }, settings::{get_api_host_names, DocsSettings, API_URL_PREFIX}, state::State, @@ -72,7 +71,7 @@ pub(crate) fn get_app_docs(setting: &DocsSettings) -> String { /// * `Error::CannotRunService` - cannot run the service /// * `Error::EventDbError` - cannot connect to the event db /// * `Error::IoError` - An IO error has occurred. -pub(crate) async fn run(settings: &DocsSettings, state: Arc) -> Result<(), Error> { +pub(crate) async fn run(settings: &DocsSettings, state: Arc) -> anyhow::Result<()> { // The address to listen on let addr = settings.address; tracing::info!("Starting Poem Service ..."); @@ -88,10 +87,7 @@ pub(crate) async fn run(settings: &DocsSettings, state: Arc) -> Result<() let app = mk_app(hosts, None, &state, settings); - poem::Server::new(TcpListener::bind(addr)) - .run(app) - .await - .map_err(Error::Io) + Ok(poem::Server::new(TcpListener::bind(addr)).run(app).await?) } #[cfg(test)] diff --git a/catalyst-gateway/bin/src/state/mod.rs b/catalyst-gateway/bin/src/state/mod.rs index 4457d1b26c9..55b9a9d0db8 100644 --- a/catalyst-gateway/bin/src/state/mod.rs +++ b/catalyst-gateway/bin/src/state/mod.rs @@ -1,10 +1,7 @@ //! Shared state used by all endpoints. use std::sync::Arc; -use crate::{ - cli::Error, - event_db::{establish_connection, EventDB}, -}; +use crate::event_db::{establish_connection, EventDB}; /// Global State of the service pub(crate) struct State { @@ -20,7 +17,7 @@ pub(crate) struct State { impl State { /// Create a new global [`State`] - pub(crate) async fn new(database_url: Option) -> Result { + pub(crate) async fn new(database_url: Option) -> anyhow::Result { // Get a configured pool to the Database, runs schema version check internally. let event_db = Arc::new(establish_connection(database_url).await?);