Repository: mintlayer/mintlayer-core
Pull request #809 (2023-04-04 08:28:02)

Generalize error handling in tx-verifier-related traits
Make error handling of some traits more general. This is to prepare the traits to be callable from a different subsystem where more error states are possible.

Sadly this resulted in loss of error information granularity in some places (see `TODO`s in the code), as a temporary workaround to keep the scope creep of the PR in check. Could be addressed in subsequent development.


Patch:
diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs
index f025b6e33..5116bacce 100644
--- a/chainstate/src/detail/ban_score.rs
+++ b/chainstate/src/detail/ban_score.rs
@@ -114,6 +114,8 @@ impl BanScore for ConnectTransactionError {
             ConnectTransactionError::SpendStakeError(_) => 100,
             ConnectTransactionError::InvalidOutputTypeInReward(_) => 100,
             ConnectTransactionError::PoolDataNotFound(_) => 0,
+            ConnectTransactionError::UndoFetchFailure => 0,
+            ConnectTransactionError::TxVerifierStorage => 0,
         }
     }
 }
@@ -295,6 +297,7 @@ impl BanScore for ConsensusPoSError {
             ConsensusPoSError::TargetConversionError(_) => 100,
             ConsensusPoSError::InvalidTargetBlockTime => 100,
             ConsensusPoSError::InvariantBrokenNotMonotonicBlockTime => 100,
+            ConsensusPoSError::FailedToFetchUtxo => 0,
         }
     }
 }
@@ -318,7 +321,8 @@ impl BanScore for utxo::Error {
             utxo::Error::NoBlockchainHeightFound => 0,
             utxo::Error::MissingBlockRewardUndo(_) => 0,
             utxo::Error::InvalidBlockRewardOutputType(_) => 100,
-            utxo::Error::DBError(_) => 0,
+            utxo::Error::ViewRead => 0,
+            utxo::Error::StorageWrite => 0,
         }
     }
 }
@@ -359,6 +363,7 @@ impl BanScore for pos_accounting::Error {
             E::PledgeValueToSignedError => 100,
             E::InvariantErrorDelegationUndoFailedDataNotFound => 100,
             E::DuplicatesInDeltaAndUndo => 100,
+            E::ViewFail => 0,
         }
     }
 }
diff --git a/chainstate/src/detail/chainstateref/mod.rs b/chainstate/src/detail/chainstateref/mod.rs
index f6fb61c74..986e51ac6 100644
--- a/chainstate/src/detail/chainstateref/mod.rs
+++ b/chainstate/src/detail/chainstateref/mod.rs
@@ -143,7 +143,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
     }
 
     // TODO: When the mempool incorporates the transaction-verifier, this won't be needed anymore
-    pub fn make_utxo_view(&self) -> impl UtxosView + '_ {
+    pub fn make_utxo_view(&self) -> impl UtxosView<Error = S::Error> + '_ {
         UtxosDB::new(&self.db_tx)
     }
 
diff --git a/chainstate/src/detail/chainstateref/tx_verifier_storage.rs b/chainstate/src/detail/chainstateref/tx_verifier_storage.rs
index 08f12b3e8..4dd159e13 100644
--- a/chainstate/src/detail/chainstateref/tx_verifier_storage.rs
+++ b/chainstate/src/detail/chainstateref/tx_verifier_storage.rs
@@ -43,6 +43,8 @@ use utxo::{ConsumedUtxoCache, FlushableUtxoView, UtxosBlockUndo, UtxosDB, UtxosS
 impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> TransactionVerifierStorageRef
     for ChainstateRef<'a, S, V>
 {
+    type Error = TransactionVerifierStorageError;
+
     fn get_token_id_from_issuance_tx(
         &self,
         tx_id: Id<Transaction>,
@@ -103,6 +105,8 @@ pub fn gen_block_index_getter<S: BlockchainStorageRead>(
 impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> UtxosStorageRead
     for ChainstateRef<'a, S, V>
 {
+    type Error = storage_result::Error;
+
     fn get_utxo(
         &self,
         outpoint: &common::chain::OutPoint,
@@ -293,6 +297,8 @@ impl<'a, S: BlockchainStorageWrite, V: TransactionVerificationStrategy>
 impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> PoSAccountingView
     for ChainstateRef<'a, S, V>
 {
+    type Error = pos_accounting::Error;
+
     fn pool_exists(&self, pool_id: PoolId) -> Result<bool, pos_accounting::Error> {
         self.get_pool_data(pool_id).map(|v| v.is_some())
     }
diff --git a/chainstate/src/detail/tx_verification_strategy/default_strategy.rs b/chainstate/src/detail/tx_verification_strategy/default_strategy.rs
index 558112a1b..738e3c0fd 100644
--- a/chainstate/src/detail/tx_verification_strategy/default_strategy.rs
+++ b/chainstate/src/detail/tx_verification_strategy/default_strategy.rs
@@ -67,6 +67,7 @@ impl TransactionVerificationStrategy for DefaultTransactionVerificationStrategy
         U: UtxosView,
         A: PoSAccountingView,
         M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         // The comparison for timelock is done with median_time_past based on BIP-113, i.e., the median time instead of the block timestamp
         let median_time_past =
@@ -138,6 +139,7 @@ impl TransactionVerificationStrategy for DefaultTransactionVerificationStrategy
         U: UtxosView,
         A: PoSAccountingView,
         M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         let mut tx_verifier = tx_verifier_maker(storage_backend, chain_config, verifier_config);
 
diff --git a/chainstate/src/detail/tx_verification_strategy/mod.rs b/chainstate/src/detail/tx_verification_strategy/mod.rs
index bb2521d6f..3c34f04d3 100644
--- a/chainstate/src/detail/tx_verification_strategy/mod.rs
+++ b/chainstate/src/detail/tx_verification_strategy/mod.rs
@@ -27,7 +27,9 @@ use common::{
     primitives::id::WithId,
 };
 use tx_verifier::transaction_verifier::{
-    config::TransactionVerifierConfig, storage::TransactionVerifierStorageRef, TransactionVerifier,
+    config::TransactionVerifierConfig,
+    storage::{TransactionVerifierStorageError, TransactionVerifierStorageRef},
+    TransactionVerifier,
 };
 
 // TODO: replace with trait_alias when stabilized
@@ -62,11 +64,12 @@ pub trait TransactionVerificationStrategy: Sized + Send {
     ) -> Result<TransactionVerifier<C, S, U, A>, BlockError>
     where
         H: BlockIndexHandle,
-        S: TransactionVerifierStorageRef,
+        S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
         U: UtxosView,
         C: AsRef<ChainConfig>,
         A: PoSAccountingView,
-        M: TransactionVerifierMakerFn<C, S, U, A>;
+        M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>;
 
     /// Disconnect the transactions given by block and block_index,
     /// and return a TransactionVerifier with an internal state
@@ -84,8 +87,9 @@ pub trait TransactionVerificationStrategy: Sized + Send {
     ) -> Result<TransactionVerifier<C, S, U, A>, BlockError>
     where
         C: AsRef<ChainConfig>,
-        S: TransactionVerifierStorageRef,
+        S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
         U: UtxosView,
         A: PoSAccountingView,
-        M: TransactionVerifierMakerFn<C, S, U, A>;
+        M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>;
 }
diff --git a/chainstate/src/interface/chainstate_interface_impl.rs b/chainstate/src/interface/chainstate_interface_impl.rs
index ef8527dd1..d6a4975ac 100644
--- a/chainstate/src/interface/chainstate_interface_impl.rs
+++ b/chainstate/src/interface/chainstate_interface_impl.rs
@@ -342,12 +342,11 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> ChainstateInterfa
             .make_db_tx_ro()
             .map_err(|e| ChainstateError::from(PropertyQueryError::from(e)))?;
         let utxo_view = chainstate_ref.make_utxo_view();
-        let available_inputs = tx
-            .inputs()
+        tx.inputs()
             .iter()
-            .map(|input| utxo_view.utxo(input.outpoint()).map(|_| input).cloned())
-            .collect();
-        Ok(available_inputs)
+            .map(|input| utxo_view.utxo(input.outpoint()).map(|x| x.map(|_| input).cloned()))
+            .collect::<Result<Vec<_>, _>>()
+            .map_err(|e| ChainstateError::FailedToReadProperty(e.into()))
     }
 
     fn get_inputs_outpoints_values(
@@ -363,7 +362,10 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> ChainstateInterfa
         let outpoint_values = tx.inputs().iter().map(|input| input.outpoint()).try_fold(
             Vec::new(),
             |mut values, outpoint| {
-                if let Some(utxo) = utxo_view.utxo(outpoint) {
+                let opt_utxo = utxo_view
+                    .utxo(outpoint)
+                    .map_err(|e| ChainstateError::FailedToReadProperty(e.into()))?;
+                if let Some(utxo) = opt_utxo {
                     match utxo.output().value() {
                         OutputValue::Coin(amount) => values.push(Some(amount)),
                         _ => {
@@ -444,7 +446,9 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> ChainstateInterfa
             .make_db_tx_ro()
             .map_err(|e| ChainstateError::FailedToReadProperty(e.into()))?;
         let utxo_view = chainstate_ref.make_utxo_view();
-        Ok(utxo_view.utxo(outpoint))
+        utxo_view
+            .utxo(outpoint)
+            .map_err(|e| ChainstateError::FailedToReadProperty(e.into()))
     }
 
     fn is_initial_block_download(&self) -> Result<bool, ChainstateError> {
diff --git a/chainstate/storage/src/internal/mod.rs b/chainstate/storage/src/internal/mod.rs
index 50da83a50..062142283 100644
--- a/chainstate/storage/src/internal/mod.rs
+++ b/chainstate/storage/src/internal/mod.rs
@@ -251,6 +251,7 @@ impl<B: storage::Backend> BlockchainStorageRead for Store<B> {
 }
 
 impl<B: storage::Backend> UtxosStorageRead for Store<B> {
+    type Error = crate::Error;
     delegate_to_transaction! {
         fn get_utxo(&self, outpoint: &OutPoint) -> crate::Result<Option<Utxo>>;
         fn get_best_block_for_utxos(&self) -> crate::Result<Option<Id<GenBlock>>>;
diff --git a/chainstate/storage/src/internal/store_tx.rs b/chainstate/storage/src/internal/store_tx.rs
index 4662984ab..e9c86420d 100644
--- a/chainstate/storage/src/internal/store_tx.rs
+++ b/chainstate/storage/src/internal/store_tx.rs
@@ -204,6 +204,8 @@ macro_rules! impl_read_ops {
         }
 
         impl<'st, B: storage::Backend> UtxosStorageRead for $TxType<'st, B> {
+            type Error = crate::Error;
+
             fn get_utxo(&self, outpoint: &OutPoint) -> crate::Result<Option<Utxo>> {
                 self.read::<db::DBUtxo, _, _>(outpoint)
             }
diff --git a/chainstate/storage/src/lib.rs b/chainstate/storage/src/lib.rs
index 9b36c86e6..50e8b8a6a 100644
--- a/chainstate/storage/src/lib.rs
+++ b/chainstate/storage/src/lib.rs
@@ -54,7 +54,7 @@ impl pos_accounting::StorageTag for SealedStorageTag {}
 
 /// Queries on persistent blockchain data
 pub trait BlockchainStorageRead:
-    UtxosStorageRead
+    UtxosStorageRead<Error = crate::Error>
     + PoSAccountingStorageRead<SealedStorageTag>
     + PoSAccountingStorageRead<TipStorageTag>
 {
diff --git a/chainstate/storage/src/mock/mock_impl.rs b/chainstate/storage/src/mock/mock_impl.rs
index c3f005e1c..d7bdc86fc 100644
--- a/chainstate/storage/src/mock/mock_impl.rs
+++ b/chainstate/storage/src/mock/mock_impl.rs
@@ -90,6 +90,7 @@ mockall::mock! {
     }
 
     impl UtxosStorageRead for Store {
+        type Error = crate::Error;
         fn get_utxo(&self, outpoint: &OutPoint) -> crate::Result<Option<Utxo>>;
         fn get_best_block_for_utxos(&self) -> crate::Result<Option<Id<GenBlock>>>;
         fn get_undo_data(&self, id: Id<Block>) -> crate::Result<Option<UtxosBlockUndo>>;
@@ -328,6 +329,7 @@ mockall::mock! {
     }
 
     impl crate::UtxosStorageRead for StoreTxRo {
+        type Error = crate::Error;
         fn get_utxo(&self, outpoint: &OutPoint) -> crate::Result<Option<Utxo>>;
         fn get_best_block_for_utxos(&self) -> crate::Result<Option<Id<GenBlock>>>;
         fn get_undo_data(&self, id: Id<Block>) -> crate::Result<Option<UtxosBlockUndo>>;
@@ -434,6 +436,7 @@ mockall::mock! {
     }
 
     impl UtxosStorageRead for StoreTxRw {
+        type Error = crate::Error;
         fn get_utxo(&self, outpoint: &OutPoint) -> crate::Result<Option<Utxo>>;
         fn get_best_block_for_utxos(&self) -> crate::Result<Option<Id<GenBlock>>>;
         fn get_undo_data(&self, id: Id<Block>) -> crate::Result<Option<UtxosBlockUndo>>;
diff --git a/chainstate/tx-verifier/src/transaction_verifier/accounting_undo_cache.rs b/chainstate/tx-verifier/src/transaction_verifier/accounting_undo_cache.rs
index 607271150..a8ceee0d0 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/accounting_undo_cache.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/accounting_undo_cache.rs
@@ -15,9 +15,7 @@
 
 use std::collections::{btree_map::Entry, BTreeMap};
 
-use super::{
-    error::ConnectTransactionError, storage::TransactionVerifierStorageError, TransactionSource,
-};
+use super::{error::ConnectTransactionError, TransactionSource};
 use common::{
     chain::{Block, Transaction},
     primitives::Id,
@@ -58,13 +56,14 @@ impl AccountingBlockUndoCache {
         self.data
     }
 
-    pub fn fetch_block_undo<F>(
+    pub fn fetch_block_undo<F, E>(
         &mut self,
         tx_source: &TransactionSource,
         fetcher_func: F,
     ) -> Result<&mut AccountingBlockUndo, ConnectTransactionError>
     where
-        F: Fn(Id<Block>) -> Result<Option<AccountingBlockUndo>, TransactionVerifierStorageError>,
+        F: Fn(Id<Block>) -> Result<Option<AccountingBlockUndo>, E>,
+        ConnectTransactionError: From<E>,
     {
         match self.data.entry(*tx_source) {
             Entry::Occupied(entry) => Ok(&mut entry.into_mut().undo),
@@ -84,14 +83,15 @@ impl AccountingBlockUndoCache {
         }
     }
 
-    pub fn take_tx_undo<F>(
+    pub fn take_tx_undo<F, E>(
         &mut self,
         tx_source: &TransactionSource,
         tx_id: &Id<Transaction>,
         fetcher_func: F,
     ) -> Result<AccountingTxUndo, ConnectTransactionError>
     where
-        F: Fn(Id<Block>) -> Result<Option<AccountingBlockUndo>, TransactionVerifierStorageError>,
+        F: Fn(Id<Block>) -> Result<Option<AccountingBlockUndo>, E>,
+        ConnectTransactionError: From<E>,
     {
         let block_undo = self.fetch_block_undo(tx_source, fetcher_func)?;
 
@@ -100,13 +100,14 @@ impl AccountingBlockUndoCache {
             .ok_or(ConnectTransactionError::MissingPoSAccountingUndo(*tx_id))
     }
 
-    pub fn take_block_reward_undo<F>(
+    pub fn take_block_reward_undo<F, E>(
         &mut self,
         tx_source: &TransactionSource,
         fetcher_func: F,
     ) -> Result<Option<AccountingBlockRewardUndo>, ConnectTransactionError>
     where
-        F: Fn(Id<Block>) -> Result<Option<AccountingBlockUndo>, TransactionVerifierStorageError>,
+        F: Fn(Id<Block>) -> Result<Option<AccountingBlockUndo>, E>,
+        ConnectTransactionError: From<E>,
     {
         Ok(self.fetch_block_undo(tx_source, fetcher_func)?.take_reward_undos())
     }
diff --git a/chainstate/tx-verifier/src/transaction_verifier/error.rs b/chainstate/tx-verifier/src/transaction_verifier/error.rs
index 6089c2e53..1fedc7143 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/error.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/error.rs
@@ -103,6 +103,13 @@ pub enum ConnectTransactionError {
     InvalidOutputTypeInReward(Id<Block>),
     #[error("Data of pool {0} not found")]
     PoolDataNotFound(PoolId),
+
+    // TODO The following should contain more granular inner error information
+    //      https://github.com/mintlayer/mintlayer-core/issues/811
+    #[error("Fetching undo data failed")]
+    UndoFetchFailure,
+    #[error("Some transaction verifier storage error")]
+    TxVerifierStorage,
 }
 
 impl From<chainstate_storage::Error> for ConnectTransactionError {
diff --git a/chainstate/tx-verifier/src/transaction_verifier/flush.rs b/chainstate/tx-verifier/src/transaction_verifier/flush.rs
index 6a7eb1640..47bc971b7 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/flush.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/flush.rs
@@ -73,8 +73,8 @@ fn flush_tokens(
     Ok(())
 }
 
-pub fn flush_to_storage(
-    storage: &mut impl TransactionVerifierStorageMut,
+pub fn flush_to_storage<S: TransactionVerifierStorageMut>(
+    storage: &mut S,
     consumed: TransactionVerifierDelta,
 ) -> Result<(), TransactionVerifierStorageError> {
     for (tx_id, tx_index_op) in consumed.tx_index_cache {
diff --git a/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs b/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs
index 785e7aa7c..858bebe5c 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/hierarchy.rs
@@ -18,7 +18,7 @@ use std::collections::BTreeMap;
 use super::{
     cached_inputs_operation::CachedInputsOperation,
     storage::{
-        TransactionVerifierStorageError, TransactionVerifierStorageMut,
+        HasTxIndexDisabledError, TransactionVerifierStorageError, TransactionVerifierStorageMut,
         TransactionVerifierStorageRef,
     },
     token_issuance_cache::{CachedAuxDataOp, CachedTokenIndexOp},
@@ -41,11 +41,15 @@ use utxo::{ConsumedUtxoCache, FlushableUtxoView, UtxosBlockUndo, UtxosStorageRea
 
 impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView>
     TransactionVerifierStorageRef for TransactionVerifier<C, S, U, A>
+where
+    <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
 {
+    type Error = <S as TransactionVerifierStorageRef>::Error;
+
     fn get_token_id_from_issuance_tx(
         &self,
         tx_id: Id<Transaction>,
-    ) -> Result<Option<TokenId>, TransactionVerifierStorageError> {
+    ) -> Result<Option<TokenId>, <Self as TransactionVerifierStorageRef>::Error> {
         match self.token_issuance_cache.txid_from_issuance().get(&tx_id) {
             Some(v) => match v {
                 CachedTokenIndexOp::Write(id) => Ok(Some(*id)),
@@ -66,11 +70,10 @@ impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView>
     fn get_mainchain_tx_index(
         &self,
         tx_id: &OutPointSourceId,
-    ) -> Result<Option<TxMainChainIndex>, TransactionVerifierStorageError> {
-        let tx_index_cache = self
-            .tx_index_cache
-            .as_ref()
-            .ok_or(TransactionVerifierStorageError::TransactionIndexDisabled)?;
+    ) -> Result<Option<TxMainChainIndex>, <Self as TransactionVerifierStorageRef>::Error> {
+        let tx_index_cache = self.tx_index_cache.as_ref().ok_or_else(|| {
+            <<Self as TransactionVerifierStorageRef>::Error>::tx_index_disabled_error()
+        })?;
         match tx_index_cache.get_from_cached(tx_id) {
             Some(v) => match v {
                 CachedInputsOperation::Write(idx) => Ok(Some(idx.clone())),
@@ -84,7 +87,7 @@ impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView>
     fn get_token_aux_data(
         &self,
         token_id: &TokenId,
-    ) -> Result<Option<TokenAuxiliaryData>, TransactionVerifierStorageError> {
+    ) -> Result<Option<TokenAuxiliaryData>, <Self as TransactionVerifierStorageRef>::Error> {
         match self.token_issuance_cache.data().get(token_id) {
             Some(v) => match v {
                 CachedAuxDataOp::Write(t) => Ok(Some(t.clone())),
@@ -98,7 +101,7 @@ impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView>
     fn get_accounting_undo(
         &self,
         id: Id<Block>,
-    ) -> Result<Option<AccountingBlockUndo>, TransactionVerifierStorageError> {
+    ) -> Result<Option<AccountingBlockUndo>, <Self as TransactionVerifierStorageRef>::Error> {
         match self.accounting_block_undo.data().get(&TransactionSource::Chain(id)) {
             Some(v) => Ok(Some(v.undo.clone())),
             None => self.storage.get_accounting_undo(id),
@@ -106,21 +109,22 @@ impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView>
     }
 }
 
-impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView> UtxosStorageRead
+impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A> UtxosStorageRead
     for TransactionVerifier<C, S, U, A>
+where
+    <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
 {
-    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<utxo::Utxo>, storage_result::Error> {
-        Ok(self.utxo_cache.utxo(outpoint))
+    type Error = <S as utxo::UtxosStorageRead>::Error;
+
+    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<utxo::Utxo>, Self::Error> {
+        self.utxo_cache.utxo(outpoint).map_err(|e| e.into())
     }
 
-    fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>, storage_result::Error> {
+    fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>, Self::Error> {
         Ok(Some(self.best_block))
     }
 
-    fn get_undo_data(
-        &self,
-        id: Id<Block>,
-    ) -> Result<Option<UtxosBlockUndo>, storage_result::Error> {
+    fn get_undo_data(&self, id: Id<Block>) -> Result<Option<UtxosBlockUndo>, Self::Error> {
         match self.utxo_block_undo.data().get(&TransactionSource::Chain(id)) {
             Some(v) => Ok(Some(v.undo.clone())),
             None => self.storage.get_undo_data(id),
@@ -128,8 +132,11 @@ impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView> Ut
     }
 }
 
-impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView>
-    TransactionVerifierStorageMut for TransactionVerifier<C, S, U, A>
+impl<C, S, U: UtxosView, A: PoSAccountingView> TransactionVerifierStorageMut
+    for TransactionVerifier<C, S, U, A>
+where
+    S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
+    <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
 {
     fn set_mainchain_tx_index(
         &mut self,
@@ -258,36 +265,38 @@ impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView> Fl
 impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView> PoSAccountingView
     for TransactionVerifier<C, S, U, A>
 {
-    fn pool_exists(&self, pool_id: PoolId) -> Result<bool, pos_accounting::Error> {
+    type Error = pos_accounting::Error;
+
+    fn pool_exists(&self, pool_id: PoolId) -> Result<bool, Self::Error> {
         self.accounting_delta.pool_exists(pool_id)
     }
 
-    fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, pos_accounting::Error> {
+    fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, Self::Error> {
         self.accounting_delta.get_pool_balance(pool_id)
     }
 
-    fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, pos_accounting::Error> {
+    fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, Self::Error> {
         self.accounting_delta.get_pool_data(pool_id)
     }
 
     fn get_delegation_balance(
         &self,
         delegation_id: DelegationId,
-    ) -> Result<Option<Amount>, pos_accounting::Error> {
+    ) -> Result<Option<Amount>, Self::Error> {
         self.accounting_delta.get_delegation_balance(delegation_id)
     }
 
     fn get_delegation_data(
         &self,
         delegation_id: DelegationId,
-    ) -> Result<Option<DelegationData>, pos_accounting::Error> {
+    ) -> Result<Option<DelegationData>, Self::Error> {
         self.accounting_delta.get_delegation_data(delegation_id)
     }
 
     fn get_pool_delegations_shares(
         &self,
         pool_id: PoolId,
-    ) -> Result<Option<BTreeMap<DelegationId, Amount>>, pos_accounting::Error> {
+    ) -> Result<Option<BTreeMap<DelegationId, Amount>>, Self::Error> {
         self.accounting_delta.get_pool_delegations_shares(pool_id)
     }
 
@@ -295,7 +304,7 @@ impl<C, S: TransactionVerifierStorageRef, U: UtxosView, A: PoSAccountingView> Po
         &self,
         pool_id: PoolId,
         delegation_id: DelegationId,
-    ) -> Result<Option<Amount>, pos_accounting::Error> {
+    ) -> Result<Option<Amount>, Self::Error> {
         self.accounting_delta.get_pool_delegation_share(pool_id, delegation_id)
     }
 }
diff --git a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy.rs b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy.rs
index 37ec7af0c..2db09760e 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/input_output_policy.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/input_output_policy.rs
@@ -60,6 +60,7 @@ fn check_inputs_can_be_used_in_tx(
         .map(|input| {
             utxo_view
                 .utxo(input.outpoint())
+                .map_err(|_| utxo::Error::ViewRead)?
                 .ok_or(ConnectTransactionError::MissingOutputOrSpent)
         })
         .collect::<Result<Vec<_>, _>>()?
diff --git a/chainstate/tx-verifier/src/transaction_verifier/mod.rs b/chainstate/tx-verifier/src/transaction_verifier/mod.rs
index 590729af5..c83def08a 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/mod.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/mod.rs
@@ -194,7 +194,8 @@ pub struct TransactionVerifier<C, S, U, A> {
 impl<C, S: TransactionVerifierStorageRef + ShallowClone> TransactionVerifier<C, S, UtxosDB<S>, S> {
     pub fn new(storage: S, chain_config: C, verifier_config: TransactionVerifierConfig) -> Self {
         let accounting_delta = PoSAccountingDelta::new(S::clone(&storage));
-        let utxo_cache = UtxosCache::new(UtxosDB::new(S::clone(&storage)));
+        let utxo_cache = UtxosCache::new(UtxosDB::new(storage.shallow_clone()))
+            .expect("Utxo cache setup failed");
         let best_block = storage
             .get_best_block_for_utxos()
             .expect("Database error while reading utxos best block")
@@ -239,7 +240,7 @@ where
             best_block,
             tx_index_cache,
             token_issuance_cache: TokenIssuanceCache::new(),
-            utxo_cache: UtxosCache::new(utxos), // TODO: take utxos from handle
+            utxo_cache: UtxosCache::new(utxos).expect("Utxo cache setup failed"),
             utxo_block_undo: UtxosBlockUndoCache::new(),
             accounting_delta: PoSAccountingDelta::new(accounting),
             accounting_block_undo: AccountingBlockUndoCache::new(),
@@ -254,6 +255,7 @@ where
     S: TransactionVerifierStorageRef,
     U: UtxosView,
     A: PoSAccountingView,
+    <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
 {
     pub fn derive_child(
         &self,
@@ -262,7 +264,7 @@ where
             storage: self,
             chain_config: self.chain_config.as_ref(),
             tx_index_cache: OptionalTxIndexCache::new(self.tx_index_cache.enabled()),
-            utxo_cache: UtxosCache::new(&self.utxo_cache),
+            utxo_cache: UtxosCache::new(&self.utxo_cache).expect("construct"),
             utxo_block_undo: UtxosBlockUndoCache::new(),
             token_issuance_cache: TokenIssuanceCache::new(),
             accounting_delta: PoSAccountingDelta::new(&self.accounting_delta),
@@ -283,7 +285,7 @@ where
                     || -> Result<Option<TokenId>, ConnectTransactionError> {
                         // issuance transactions are unique, so we use them to get the token id
                         self.get_token_id_from_issuance_tx(tx_id)
-                            .map_err(ConnectTransactionError::TransactionVerifierError)
+                            .map_err(|_| ConnectTransactionError::TxVerifierStorage)
                     };
                 let (key, amount) = get_input_token_id_and_amount(
                     &utxo.output().value(),
@@ -310,6 +312,7 @@ where
             let utxo = self
                 .utxo_cache
                 .utxo(input.outpoint())
+                .map_err(|_| utxo::Error::ViewRead)?
                 .ok_or(ConnectTransactionError::MissingOutputOrSpent)?;
             self.amount_from_outpoint(input.outpoint().tx_id(), utxo)
         });
@@ -499,6 +502,7 @@ where
                 let outpoint = input.outpoint();
                 self.utxo_cache
                     .utxo(outpoint)
+                    .map_err(|_| utxo::Error::ViewRead)?
                     .ok_or(ConnectTransactionError::MissingOutputOrSpent)
                     .map(|utxo| utxo.take_output())
             })
@@ -509,6 +513,7 @@ where
             let utxo = self
                 .utxo_cache
                 .utxo(outpoint)
+                .map_err(|_| utxo::Error::ViewRead)?
                 .ok_or(ConnectTransactionError::MissingOutputOrSpent)?;
 
             // TODO: see if a different treatment should be done for different output purposes
@@ -596,7 +601,11 @@ where
     ) -> Result<(), ConnectTransactionError> {
         tx.outputs().iter().try_for_each(|output| match output {
             TxOutput::StakePool(_) => {
-                let block_undo_fetcher = |id: Id<Block>| self.storage.get_accounting_undo(id);
+                let block_undo_fetcher = |id: Id<Block>| {
+                    self.storage
+                        .get_accounting_undo(id)
+                        .map_err(|_| ConnectTransactionError::TxVerifierStorage)
+                };
                 self.accounting_block_undo
                     .take_tx_undo(&tx_source, &tx.get_id(), block_undo_fetcher)?
                     .into_inner()
@@ -633,8 +642,14 @@ where
         input_output_policy::check_tx_inputs_outputs_purposes(tx.transaction(), &self.utxo_cache)?;
 
         // pre-cache token ids to check ensure it's not in the db when issuing
-        self.token_issuance_cache
-            .precache_token_issuance(|id| self.storage.get_token_aux_data(id), tx.transaction())?;
+        self.token_issuance_cache.precache_token_issuance(
+            |id| {
+                self.storage
+                    .get_token_aux_data(id)
+                    .map_err(|_| ConnectTransactionError::TxVerifierStorage)
+            },
+            tx.transaction(),
+        )?;
 
         // check for attempted money printing
         let fee = Some(self.check_transferred_amounts_and_get_fee(tx.transaction())?);
@@ -677,7 +692,9 @@ where
                 if let Some(tx_index_cache) = self.tx_index_cache.as_mut() {
                     // pre-cache all inputs
                     tx_index_cache.precache_inputs(tx.inputs(), |tx_id: &OutPointSourceId| {
-                        self.storage.get_mainchain_tx_index(tx_id)
+                        self.storage
+                            .get_mainchain_tx_index(tx_id)
+                            .map_err(|_| ConnectTransactionError::TxVerifierStorage)
                     })?;
 
                     // mark tx index as spent
@@ -701,7 +718,9 @@ where
             // pre-cache all inputs
             if let Some(tx_index_cache) = self.tx_index_cache.as_mut() {
                 tx_index_cache.precache_inputs(inputs, |tx_id: &OutPointSourceId| {
-                    self.storage.get_mainchain_tx_index(tx_id)
+                    self.storage
+                        .get_mainchain_tx_index(tx_id)
+                        .map_err(|_| ConnectTransactionError::TxVerifierStorage)
                 })?;
             }
 
@@ -797,7 +816,11 @@ where
         tx_source: &TransactionSource,
         tx_id: &Id<Transaction>,
     ) -> Result<bool, ConnectTransactionError> {
-        let block_undo_fetcher = |id: Id<Block>| self.storage.get_undo_data(id);
+        let block_undo_fetcher = |id: Id<Block>| {
+            self.storage
+                .get_undo_data(id)
+                .map_err(|_| ConnectTransactionError::UndoFetchFailure)
+        };
         match tx_source {
             TransactionSource::Chain(block_id) => {
                 let current_block_height = self
@@ -836,7 +859,11 @@ where
         tx_source: &TransactionSource,
         tx: &SignedTransaction,
     ) -> Result<(), ConnectTransactionError> {
-        let block_undo_fetcher = |id: Id<Block>| self.storage.get_undo_data(id);
+        let block_undo_fetcher = |id: Id<Block>| {
+            self.storage
+                .get_undo_data(id)
+                .map_err(|_| ConnectTransactionError::UndoFetchFailure)
+        };
         let tx_undo = self.utxo_block_undo.take_tx_undo(
             tx_source,
             &tx.transaction().get_id(),
@@ -845,8 +872,11 @@ where
 
         match tx_source {
             TransactionSource::Chain(_) => {
-                let tx_index_fetcher =
-                    |tx_id: &OutPointSourceId| self.storage.get_mainchain_tx_index(tx_id);
+                let tx_index_fetcher = |tx_id: &OutPointSourceId| {
+                    self.storage
+                        .get_mainchain_tx_index(tx_id)
+                        .map_err(|_| ConnectTransactionError::TxVerifierStorage)
+                };
                 // update tx index only for txs from main chain
                 if let Some(tx_index_cache) = self.tx_index_cache.as_mut() {
                     // pre-cache all inputs
@@ -864,8 +894,14 @@ where
         self.utxo_cache.disconnect_transaction(tx.transaction(), tx_undo)?;
 
         // pre-cache token ids before removing them
-        self.token_issuance_cache
-            .precache_token_issuance(|id| self.storage.get_token_aux_data(id), tx.transaction())?;
+        self.token_issuance_cache.precache_token_issuance(
+            |id| {
+                self.storage
+                    .get_token_aux_data(id)
+                    .map_err(|_| ConnectTransactionError::TxVerifierStorage)
+            },
+            tx.transaction(),
+        )?;
 
         // Remove issued tokens
         self.token_issuance_cache.unregister(tx.transaction())?;
@@ -893,7 +929,11 @@ where
             BlockTransactableRef::BlockReward(block) => {
                 let reward_transactable = block.block_reward_transactable();
 
-                let block_undo_fetcher = |id: Id<Block>| self.storage.get_undo_data(id);
+                let block_undo_fetcher = |id: Id<Block>| {
+                    self.storage
+                        .get_undo_data(id)
+                        .map_err(|_| ConnectTransactionError::UndoFetchFailure)
+                };
                 let reward_undo = self.utxo_block_undo.take_block_reward_undo(
                     &TransactionSource::Chain(block.get_id()),
                     block_undo_fetcher,
@@ -908,8 +948,11 @@ where
                     (reward_transactable.inputs(), self.tx_index_cache.as_mut())
                 {
                     // pre-cache all inputs
-                    let tx_index_fetcher =
-                        |tx_id: &OutPointSourceId| self.storage.get_mainchain_tx_index(tx_id);
+                    let tx_index_fetcher = |tx_id: &OutPointSourceId| {
+                        self.storage
+                            .get_mainchain_tx_index(tx_id)
+                            .map_err(|_| ConnectTransactionError::TxVerifierStorage)
+                    };
                     tx_index_cache.precache_inputs(inputs, tx_index_fetcher)?;
 
                     // unspend inputs
@@ -919,8 +962,11 @@ where
                 match block.header().consensus_data() {
                     ConsensusData::None | ConsensusData::PoW(_) => { /*do nothing*/ }
                     ConsensusData::PoS(_) => {
-                        let block_undo_fetcher =
-                            |id: Id<Block>| self.storage.get_accounting_undo(id);
+                        let block_undo_fetcher = |id: Id<Block>| {
+                            self.storage
+                                .get_accounting_undo(id)
+                                .map_err(|_| ConnectTransactionError::TxVerifierStorage)
+                        };
                         let reward_undo = self.accounting_block_undo.take_block_reward_undo(
                             &TransactionSource::Chain(block.get_id()),
                             block_undo_fetcher,
diff --git a/chainstate/tx-verifier/src/transaction_verifier/storage.rs b/chainstate/tx-verifier/src/transaction_verifier/storage.rs
index 9e913a496..ad6547566 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/storage.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/storage.rs
@@ -60,13 +60,28 @@ pub enum TransactionVerifierStorageError {
     AccountingBlockUndoError(#[from] pos_accounting::AccountingBlockUndoError),
 }
 
+pub trait HasTxIndexDisabledError {
+    fn tx_index_disabled_error() -> Self;
+}
+
+impl HasTxIndexDisabledError for TransactionVerifierStorageError {
+    fn tx_index_disabled_error() -> Self {
+        Self::TransactionIndexDisabled
+    }
+}
+
 // TODO(Gosha): PoSAccountingView should be replaced with PoSAccountingStorageRead in which the
 //              return error type can handle both storage_result::Error and pos_accounting::Error
-pub trait TransactionVerifierStorageRef: UtxosStorageRead + PoSAccountingView {
+pub trait TransactionVerifierStorageRef: UtxosStorageRead + PoSAccountingView
+where
+    <Self as TransactionVerifierStorageRef>::Error: HasTxIndexDisabledError,
+{
+    type Error: std::error::Error;
+
     fn get_token_id_from_issuance_tx(
         &self,
         tx_id: Id<Transaction>,
-    ) -> Result<Option<TokenId>, TransactionVerifierStorageError>;
+    ) -> Result<Option<TokenId>, <Self as TransactionVerifierStorageRef>::Error>;
 
     // TODO: Study whether moving this to a closure on tx_verifier construction is helpful.
     //       The issue here is that looking into history prevents testing the tx_verifier independently
@@ -81,21 +96,23 @@ pub trait TransactionVerifierStorageRef: UtxosStorageRead + PoSAccountingView {
     fn get_mainchain_tx_index(
         &self,
         tx_id: &OutPointSourceId,
-    ) -> Result<Option<TxMainChainIndex>, TransactionVerifierStorageError>;
+    ) -> Result<Option<TxMainChainIndex>, <Self as TransactionVerifierStorageRef>::Error>;
 
     fn get_token_aux_data(
         &self,
         token_id: &TokenId,
-    ) -> Result<Option<TokenAuxiliaryData>, TransactionVerifierStorageError>;
+    ) -> Result<Option<TokenAuxiliaryData>, <Self as TransactionVerifierStorageRef>::Error>;
 
     fn get_accounting_undo(
         &self,
         id: Id<Block>,
-    ) -> Result<Option<AccountingBlockUndo>, TransactionVerifierStorageError>;
+    ) -> Result<Option<AccountingBlockUndo>, <Self as TransactionVerifierStorageRef>::Error>;
 }
 
 pub trait TransactionVerifierStorageMut:
-    TransactionVerifierStorageRef + FlushableUtxoView + FlushablePoSAccountingView
+    TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>
+    + FlushableUtxoView
+    + FlushablePoSAccountingView
 {
     fn set_mainchain_tx_index(
         &mut self,
@@ -163,10 +180,12 @@ impl<T: Deref> TransactionVerifierStorageRef for T
 where
     T::Target: TransactionVerifierStorageRef,
 {
+    type Error = <T::Target as TransactionVerifierStorageRef>::Error;
+
     fn get_token_id_from_issuance_tx(
         &self,
         tx_id: Id<Transaction>,
-    ) -> Result<Option<TokenId>, TransactionVerifierStorageError> {
+    ) -> Result<Option<TokenId>, <Self as TransactionVerifierStorageRef>::Error> {
         self.deref().get_token_id_from_issuance_tx(tx_id)
     }
 
@@ -180,21 +199,21 @@ where
     fn get_mainchain_tx_index(
         &self,
         tx_id: &OutPointSourceId,
-    ) -> Result<Option<TxMainChainIndex>, TransactionVerifierStorageError> {
+    ) -> Result<Option<TxMainChainIndex>, <Self as TransactionVerifierStorageRef>::Error> {
         self.deref().get_mainchain_tx_index(tx_id)
     }
 
     fn get_token_aux_data(
         &self,
         token_id: &TokenId,
-    ) -> Result<Option<TokenAuxiliaryData>, TransactionVerifierStorageError> {
+    ) -> Result<Option<TokenAuxiliaryData>, <Self as TransactionVerifierStorageRef>::Error> {
         self.deref().get_token_aux_data(token_id)
     }
 
     fn get_accounting_undo(
         &self,
         id: Id<Block>,
-    ) -> Result<Option<AccountingBlockUndo>, TransactionVerifierStorageError> {
+    ) -> Result<Option<AccountingBlockUndo>, <Self as TransactionVerifierStorageRef>::Error> {
         self.deref().get_accounting_undo(id)
     }
 }
diff --git a/chainstate/tx-verifier/src/transaction_verifier/timelock_check.rs b/chainstate/tx-verifier/src/transaction_verifier/timelock_check.rs
index 491cba1a4..525e2df6e 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/timelock_check.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/timelock_check.rs
@@ -92,8 +92,10 @@ pub fn check_timelocks<
 
     for input in inputs {
         let outpoint = input.outpoint();
-        let utxo =
-            utxos_view.utxo(outpoint).ok_or(ConnectTransactionError::MissingOutputOrSpent)?;
+        let utxo = utxos_view
+            .utxo(outpoint)
+            .map_err(|_| utxo::Error::ViewRead)?
+            .ok_or(ConnectTransactionError::MissingOutputOrSpent)?;
 
         if utxo.output().has_timelock() {
             let height = match utxo.source() {
diff --git a/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs b/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs
index 8f66ccd22..5656b2fc0 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/token_issuance_cache.rs
@@ -25,7 +25,6 @@ use common::{
 
 use super::{
     error::{ConnectTransactionError, TokensError},
-    storage::TransactionVerifierStorageError,
     CachedOperation,
 };
 
@@ -139,13 +138,14 @@ impl TokenIssuanceCache {
         Ok(())
     }
 
-    pub fn precache_token_issuance<
-        F: Fn(&TokenId) -> Result<Option<TokenAuxiliaryData>, TransactionVerifierStorageError>,
-    >(
+    pub fn precache_token_issuance<E>(
         &mut self,
-        token_data_getter: F,
+        token_data_getter: impl Fn(&TokenId) -> Result<Option<TokenAuxiliaryData>, E>,
         tx: &Transaction,
-    ) -> Result<(), ConnectTransactionError> {
+    ) -> Result<(), ConnectTransactionError>
+    where
+        ConnectTransactionError: From<E>,
+    {
         let has_token_issuance =
             tx.outputs().iter().any(|output| is_tokens_issuance(&output.value()));
         if has_token_issuance {
diff --git a/chainstate/tx-verifier/src/transaction_verifier/tx_index_cache.rs b/chainstate/tx-verifier/src/transaction_verifier/tx_index_cache.rs
index 6c0bf99dc..5cb355a5d 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/tx_index_cache.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/tx_index_cache.rs
@@ -16,9 +16,9 @@
 use std::collections::btree_map::Entry;
 
 use super::{
+    cached_inputs_operation::CachedInputsOperation,
     error::{ConnectTransactionError, TxIndexError},
-    storage::TransactionVerifierStorageError,
-    {cached_inputs_operation::CachedInputsOperation, BlockTransactableRef},
+    BlockTransactableRef,
 };
 use common::{
     chain::{signature::Signable, OutPointSourceId, Spender, TxInput, TxMainChainIndex},
@@ -160,15 +160,14 @@ impl TxIndexCache {
         Ok(())
     }
 
-    pub fn precache_inputs<F>(
+    pub fn precache_inputs<F, E>(
         &mut self,
         inputs: &[TxInput],
         fetcher_func: F,
     ) -> Result<(), ConnectTransactionError>
     where
-        F: Fn(
-            &OutPointSourceId,
-        ) -> Result<Option<TxMainChainIndex>, TransactionVerifierStorageError>,
+        F: Fn(&OutPointSourceId) -> Result<Option<TxMainChainIndex>, E>,
+        ConnectTransactionError: From<E>,
     {
         inputs.iter().try_for_each(|input| {
             let outpoint = input.outpoint();
diff --git a/chainstate/tx-verifier/src/transaction_verifier/utxos_undo_cache.rs b/chainstate/tx-verifier/src/transaction_verifier/utxos_undo_cache.rs
index 78c1ee8da..c4b725cda 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/utxos_undo_cache.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/utxos_undo_cache.rs
@@ -16,7 +16,6 @@
 use std::collections::{btree_map::Entry, BTreeMap};
 
 use super::{error::ConnectTransactionError, TransactionSource};
-use chainstate_types::storage_result;
 use common::{
     chain::{Block, Transaction},
     primitives::Id,
@@ -55,13 +54,14 @@ impl UtxosBlockUndoCache {
         self.data
     }
 
-    pub fn read_block_undo<F>(
+    pub fn read_block_undo<F, E>(
         &self,
         tx_source: &TransactionSource,
         fetcher_func: F,
     ) -> Result<UtxosBlockUndo, ConnectTransactionError>
     where
-        F: Fn(Id<Block>) -> Result<Option<UtxosBlockUndo>, storage_result::Error>,
+        F: Fn(Id<Block>) -> Result<Option<UtxosBlockUndo>, E>,
+        ConnectTransactionError: From<E>,
     {
         match self.data.get(tx_source) {
             Some(entry) => Ok(entry.undo.clone()),
@@ -76,13 +76,14 @@ impl UtxosBlockUndoCache {
         }
     }
 
-    pub fn fetch_block_undo<F>(
+    pub fn fetch_block_undo<F, E>(
         &mut self,
         tx_source: &TransactionSource,
         fetcher_func: F,
     ) -> Result<&mut UtxosBlockUndo, ConnectTransactionError>
     where
-        F: Fn(Id<Block>) -> Result<Option<UtxosBlockUndo>, storage_result::Error>,
+        F: Fn(Id<Block>) -> Result<Option<UtxosBlockUndo>, E>,
+        ConnectTransactionError: From<E>,
     {
         match self.data.entry(*tx_source) {
             Entry::Occupied(entry) => Ok(&mut entry.into_mut().undo),
@@ -102,14 +103,15 @@ impl UtxosBlockUndoCache {
         }
     }
 
-    pub fn take_tx_undo<F>(
+    pub fn take_tx_undo<F, E>(
         &mut self,
         tx_source: &TransactionSource,
         tx_id: &Id<Transaction>,
         fetcher_func: F,
     ) -> Result<UtxosTxUndo, ConnectTransactionError>
     where
-        F: Fn(Id<Block>) -> Result<Option<UtxosBlockUndo>, storage_result::Error>,
+        F: Fn(Id<Block>) -> Result<Option<UtxosBlockUndo>, E>,
+        ConnectTransactionError: From<E>,
     {
         let block_undo = self.fetch_block_undo(tx_source, fetcher_func)?;
 
@@ -122,13 +124,14 @@ impl UtxosBlockUndoCache {
         }
     }
 
-    pub fn take_block_reward_undo<F>(
+    pub fn take_block_reward_undo<F, E>(
         &mut self,
         tx_source: &TransactionSource,
         fetcher_func: F,
     ) -> Result<Option<UtxosBlockRewardUndo>, ConnectTransactionError>
     where
-        F: Fn(Id<Block>) -> Result<Option<UtxosBlockUndo>, storage_result::Error>,
+        F: Fn(Id<Block>) -> Result<Option<UtxosBlockUndo>, E>,
+        ConnectTransactionError: From<E>,
     {
         Ok(self.fetch_block_undo(tx_source, fetcher_func)?.take_block_reward_undo())
     }
diff --git a/chainstate/types/src/storage_result.rs b/chainstate/types/src/storage_result.rs
index 584a058e5..8f3c8bc02 100644
--- a/chainstate/types/src/storage_result.rs
+++ b/chainstate/types/src/storage_result.rs
@@ -26,5 +26,11 @@ impl From<storage::Error> for Error {
     }
 }
 
+impl From<std::convert::Infallible> for Error {
+    fn from(inf: std::convert::Infallible) -> Self {
+        match inf {}
+    }
+}
+
 /// Possibly failing result of blockchain storage query
 pub type Result<T> = core::result::Result<T, Error>;
diff --git a/consensus/src/pos/error.rs b/consensus/src/pos/error.rs
index 1b9e8b544..72181ff72 100644
--- a/consensus/src/pos/error.rs
+++ b/consensus/src/pos/error.rs
@@ -60,4 +60,9 @@ pub enum ConsensusPoSError {
     InvalidTargetBlockTime,
     #[error("CRITICAL: Block time must be monotonic")]
     InvariantBrokenNotMonotonicBlockTime,
+
+    // TODO the following error should include the corresponding error from UtxosView
+    //      https://github.com/mintlayer/mintlayer-core/issues/811
+    #[error("Failed to fetch utxo")]
+    FailedToFetchUtxo,
 }
diff --git a/consensus/src/pos/kernel.rs b/consensus/src/pos/kernel.rs
index 3b4a84a63..7a9f71d87 100644
--- a/consensus/src/pos/kernel.rs
+++ b/consensus/src/pos/kernel.rs
@@ -26,8 +26,10 @@ pub fn get_kernel_output<U: UtxosView>(
         [] => Err(ConsensusPoSError::NoKernel),
         [kernel_input] => {
             let kernel_outpoint = kernel_input.outpoint();
-            let kernel_output =
-                utxos_view.utxo(kernel_outpoint).ok_or(ConsensusPoSError::NoKernel)?;
+            let kernel_output = utxos_view
+                .utxo(kernel_outpoint)
+                .map_err(|_| ConsensusPoSError::FailedToFetchUtxo)?
+                .ok_or(ConsensusPoSError::NoKernel)?;
 
             Ok(kernel_output.output().clone())
         }
diff --git a/consensus/src/pos/mod.rs b/consensus/src/pos/mod.rs
index 52f156348..a2d167272 100644
--- a/consensus/src/pos/mod.rs
+++ b/consensus/src/pos/mod.rs
@@ -108,7 +108,7 @@ pub fn check_proof_of_stake<H, U, P>(
 where
     H: BlockIndexHandle,
     U: UtxosView,
-    P: PoSAccountingView,
+    P: PoSAccountingView<Error = pos_accounting::Error>,
 {
     let target = target::calculate_target_required(
         chain_config,
diff --git a/consensus/src/validator.rs b/consensus/src/validator.rs
index 5a7012268..102efb4b8 100644
--- a/consensus/src/validator.rs
+++ b/consensus/src/validator.rs
@@ -40,7 +40,7 @@ pub fn validate_consensus<H, U, P>(
 where
     H: BlockIndexHandle,
     U: UtxosView,
-    P: PoSAccountingView,
+    P: PoSAccountingView<Error = pos_accounting::Error>,
 {
     let prev_block_id = *header.prev_block_id();
 
@@ -115,7 +115,7 @@ fn validate_pos_consensus<H, U, P>(
 where
     H: BlockIndexHandle,
     U: UtxosView,
-    P: PoSAccountingView,
+    P: PoSAccountingView<Error = pos_accounting::Error>,
 {
     match header.consensus_data() {
         ConsensusData::None | ConsensusData::PoW(_) => {
diff --git a/pos_accounting/src/error.rs b/pos_accounting/src/error.rs
index a35e3af2b..028a12f03 100644
--- a/pos_accounting/src/error.rs
+++ b/pos_accounting/src/error.rs
@@ -79,4 +79,9 @@ pub enum Error {
     InvariantErrorDelegationUndoFailedDataNotFound,
     #[error("Delta reverts merge failed due to duplicates")]
     DuplicatesInDeltaAndUndo,
+
+    // TODO Need a more granular error reporting in the following
+    //      https://github.com/mintlayer/mintlayer-core/issues/811
+    #[error("PoS accounting view query failed")]
+    ViewFail,
 }
diff --git a/pos_accounting/src/pool/delta/operator_impls.rs b/pos_accounting/src/pool/delta/operator_impls.rs
index 44008642a..3a546363f 100644
--- a/pos_accounting/src/pool/delta/operator_impls.rs
+++ b/pos_accounting/src/pool/delta/operator_impls.rs
@@ -49,7 +49,7 @@ impl<P: PoSAccountingView> PoSAccountingOperations for PoSAccountingDelta<P> {
     ) -> Result<(PoolId, PoSAccountingUndo), Error> {
         let pool_id = make_pool_id(input0_outpoint);
 
-        if self.get_pool_balance(pool_id)?.is_some() {
+        if self.get_pool_balance(pool_id).map_err(|_| Error::ViewFail)?.is_some() {
             // This should never happen since it's based on an unspent input
             return Err(Error::InvariantErrorPoolBalanceAlreadyExists);
         }
diff --git a/pos_accounting/src/pool/delta/view_impl.rs b/pos_accounting/src/pool/delta/view_impl.rs
index 445ff91e2..b8ee939c1 100644
--- a/pos_accounting/src/pool/delta/view_impl.rs
+++ b/pos_accounting/src/pool/delta/view_impl.rs
@@ -64,32 +64,37 @@ fn sum_maps<K: Ord + Copy>(
 }
 
 impl<P: PoSAccountingView> PoSAccountingView for PoSAccountingDelta<P> {
-    fn pool_exists(&self, pool_id: PoolId) -> Result<bool, Error> {
+    type Error = Error;
+
+    fn pool_exists(&self, pool_id: PoolId) -> Result<bool, Self::Error> {
         Ok(self
             .get_pool_data(pool_id)?
             .ok_or_else(|| self.parent.get_pool_data(pool_id))
             .is_ok())
     }
 
-    fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, Error> {
-        let parent_balance = self.parent.get_pool_balance(pool_id)?;
+    fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, Self::Error> {
+        let parent_balance = self.parent.get_pool_balance(pool_id).map_err(|_| Error::ViewFail)?;
         let local_delta = self.data.pool_balances.data().get(&pool_id).cloned();
         combine_amount_delta(&parent_balance, &local_delta).map_err(Error::AccountingError)
     }
 
-    fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, Error> {
+    fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, Self::Error> {
         match self.data.pool_data.get_data(&pool_id) {
             accounting::GetDataResult::Present(d) => Ok(Some(d.clone())),
             accounting::GetDataResult::Deleted => Ok(None),
-            accounting::GetDataResult::Missing => self.parent.get_pool_data(pool_id),
+            accounting::GetDataResult::Missing => {
+                Ok(self.parent.get_pool_data(pool_id).map_err(|_| Error::ViewFail)?)
+            }
         }
     }
 
     fn get_pool_delegations_shares(
         &self,
         pool_id: PoolId,
-    ) -> Result<Option<BTreeMap<DelegationId, Amount>>, Error> {
-        let parent_shares = self.parent.get_pool_delegations_shares(pool_id)?;
+    ) -> Result<Option<BTreeMap<DelegationId, Amount>>, Self::Error> {
+        let parent_shares =
+            self.parent.get_pool_delegations_shares(pool_id).map_err(|_| Error::ViewFail)?;
         let local_shares = self.get_cached_delegations_shares(pool_id);
 
         match (parent_shares, local_shares) {
@@ -104,8 +109,12 @@ impl<P: PoSAccountingView> PoSAccountingView for PoSAccountingDelta<P> {
         }
     }
 
-    fn get_delegation_balance(&self, delegation_id: DelegationId) -> Result<Option<Amount>, Error> {
-        let parent_amount = self.parent.get_delegation_balance(delegation_id)?;
+    fn get_delegation_balance(
+        &self,
+        delegation_id: DelegationId,
+    ) -> Result<Option<Amount>, Self::Error> {
+        let parent_amount =
+            self.parent.get_delegation_balance(delegation_id).map_err(|_| Error::ViewFail)?;
         let local_amount = self.data.delegation_balances.data().get(&delegation_id).copied();
         combine_amount_delta(&parent_amount, &local_amount).map_err(Error::AccountingError)
     }
@@ -113,11 +122,13 @@ impl<P: PoSAccountingView> PoSAccountingView for PoSAccountingDelta<P> {
     fn get_delegation_data(
         &self,
         delegation_id: DelegationId,
-    ) -> Result<Option<DelegationData>, Error> {
+    ) -> Result<Option<DelegationData>, Self::Error> {
         match self.data.delegation_data.get_data(&delegation_id) {
             accounting::GetDataResult::Present(d) => Ok(Some(d.clone())),
             accounting::GetDataResult::Deleted => Ok(None),
-            accounting::GetDataResult::Missing => self.parent.get_delegation_data(delegation_id),
+            accounting::GetDataResult::Missing => {
+                Ok(self.parent.get_delegation_data(delegation_id).map_err(|_| Error::ViewFail)?)
+            }
         }
     }
 
@@ -125,8 +136,11 @@ impl<P: PoSAccountingView> PoSAccountingView for PoSAccountingDelta<P> {
         &self,
         pool_id: PoolId,
         delegation_id: DelegationId,
-    ) -> Result<Option<Amount>, Error> {
-        let parent_amount = self.parent.get_pool_delegation_share(pool_id, delegation_id)?;
+    ) -> Result<Option<Amount>, Self::Error> {
+        let parent_amount = self
+            .parent
+            .get_pool_delegation_share(pool_id, delegation_id)
+            .map_err(|_| Error::ViewFail)?;
         let local_amount =
             self.data.pool_delegation_shares.data().get(&(pool_id, delegation_id)).copied();
         combine_amount_delta(&parent_amount, &local_amount).map_err(Error::AccountingError)
diff --git a/pos_accounting/src/pool/storage/view_impls.rs b/pos_accounting/src/pool/storage/view_impls.rs
index 312fd2eb0..60a148b9a 100644
--- a/pos_accounting/src/pool/storage/view_impls.rs
+++ b/pos_accounting/src/pool/storage/view_impls.rs
@@ -33,6 +33,8 @@ use crate::{
 };
 
 impl<S: PoSAccountingStorageRead<T>, T: StorageTag> PoSAccountingView for PoSAccountingDB<S, T> {
+    type Error = Error;
+
     fn pool_exists(&self, pool_id: PoolId) -> Result<bool, Error> {
         self.get_pool_data(pool_id).map(|v| v.is_some())
     }
diff --git a/pos_accounting/src/pool/view.rs b/pos_accounting/src/pool/view.rs
index eb8f93c14..eab65480c 100644
--- a/pos_accounting/src/pool/view.rs
+++ b/pos_accounting/src/pool/view.rs
@@ -25,29 +25,34 @@ use crate::{error::Error, DeltaMergeUndo};
 use super::{delegation::DelegationData, delta::data::PoSAccountingDeltaData, pool_data::PoolData};
 
 pub trait PoSAccountingView {
-    fn pool_exists(&self, pool_id: PoolId) -> Result<bool, Error>;
+    type Error: std::error::Error;
 
-    fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, Error>;
+    fn pool_exists(&self, pool_id: PoolId) -> Result<bool, Self::Error>;
 
-    fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, Error>;
+    fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, Self::Error>;
+
+    fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, Self::Error>;
 
     fn get_pool_delegations_shares(
         &self,
         pool_id: PoolId,
-    ) -> Result<Option<BTreeMap<DelegationId, Amount>>, Error>;
+    ) -> Result<Option<BTreeMap<DelegationId, Amount>>, Self::Error>;
 
-    fn get_delegation_balance(&self, delegation_id: DelegationId) -> Result<Option<Amount>, Error>;
+    fn get_delegation_balance(
+        &self,
+        delegation_id: DelegationId,
+    ) -> Result<Option<Amount>, Self::Error>;
 
     fn get_delegation_data(
         &self,
         delegation_id: DelegationId,
-    ) -> Result<Option<DelegationData>, Error>;
+    ) -> Result<Option<DelegationData>, Self::Error>;
 
     fn get_pool_delegation_share(
         &self,
         pool_id: PoolId,
         delegation_id: DelegationId,
-    ) -> Result<Option<Amount>, Error>;
+    ) -> Result<Option<Amount>, Self::Error>;
 }
 
 pub trait FlushablePoSAccountingView {
@@ -59,33 +64,38 @@ where
     T: Deref,
     <T as Deref>::Target: PoSAccountingView,
 {
-    fn pool_exists(&self, pool_id: PoolId) -> Result<bool, Error> {
+    type Error = <T::Target as PoSAccountingView>::Error;
+
+    fn pool_exists(&self, pool_id: PoolId) -> Result<bool, Self::Error> {
         self.deref().pool_exists(pool_id)
     }
 
-    fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, Error> {
+    fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, Self::Error> {
         self.deref().get_pool_balance(pool_id)
     }
 
-    fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, Error> {
+    fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, Self::Error> {
         self.deref().get_pool_data(pool_id)
     }
 
     fn get_pool_delegations_shares(
         &self,
         pool_id: PoolId,
-    ) -> Result<Option<BTreeMap<DelegationId, Amount>>, Error> {
+    ) -> Result<Option<BTreeMap<DelegationId, Amount>>, Self::Error> {
         self.deref().get_pool_delegations_shares(pool_id)
     }
 
-    fn get_delegation_balance(&self, delegation_id: DelegationId) -> Result<Option<Amount>, Error> {
+    fn get_delegation_balance(
+        &self,
+        delegation_id: DelegationId,
+    ) -> Result<Option<Amount>, Self::Error> {
         self.deref().get_delegation_balance(delegation_id)
     }
 
     fn get_delegation_data(
         &self,
         delegation_id: DelegationId,
-    ) -> Result<Option<DelegationData>, Error> {
+    ) -> Result<Option<DelegationData>, Self::Error> {
         self.deref().get_delegation_data(delegation_id)
     }
 
@@ -93,7 +103,7 @@ where
         &self,
         pool_id: PoolId,
         delegation_id: DelegationId,
-    ) -> Result<Option<Amount>, Error> {
+    ) -> Result<Option<Amount>, Self::Error> {
         self.deref().get_pool_delegation_share(pool_id, delegation_id)
     }
 }
diff --git a/utxo/src/cache.rs b/utxo/src/cache.rs
index 55bc9ad53..cefc0c0f8 100644
--- a/utxo/src/cache.rs
+++ b/utxo/src/cache.rs
@@ -15,10 +15,8 @@
 
 use crate::{
     utxo_entry::{IsDirty, IsFresh, UtxoEntry},
-    {
-        Error, FlushableUtxoView, Utxo, UtxoSource, UtxosBlockRewardUndo, UtxosTxUndo,
-        UtxosTxUndoWithSources, UtxosView,
-    },
+    Error, FlushableUtxoView, Utxo, UtxoSource, UtxosBlockRewardUndo, UtxosTxUndo,
+    UtxosTxUndoWithSources, UtxosView,
 };
 use common::{
     chain::{
@@ -45,15 +43,17 @@ where
     T: Deref,
     <T as Deref>::Target: UtxosView,
 {
-    fn utxo(&self, outpoint: &OutPoint) -> Option<Utxo> {
+    type Error = <T::Target as UtxosView>::Error;
+
+    fn utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error> {
         self.deref().utxo(outpoint)
     }
 
-    fn has_utxo(&self, outpoint: &OutPoint) -> bool {
+    fn has_utxo(&self, outpoint: &OutPoint) -> Result<bool, Self::Error> {
         self.deref().has_utxo(outpoint)
     }
 
-    fn best_block_hash(&self) -> Id<GenBlock> {
+    fn best_block_hash(&self) -> Result<Id<GenBlock>, Self::Error> {
         self.deref().best_block_hash()
     }
 
@@ -76,35 +76,34 @@ impl<P: UtxosView> UtxosCache<P> {
     /// Returns a UtxoEntry, given the outpoint.
     // the reason why it's not a `&UtxoEntry`, is because the flags are bound to change esp.
     // when the utxo was actually retrieved from the parent.
-    fn fetch_utxo_entry(&mut self, outpoint: &OutPoint) -> Option<UtxoEntry> {
+    fn fetch_utxo_entry(&mut self, outpoint: &OutPoint) -> Result<Option<UtxoEntry>, Error> {
         if let Some(res) = self.utxos.get(outpoint) {
-            return Some(res.clone());
+            return Ok(Some(res.clone()));
         }
 
         // since the utxo does not exist in this view, try to check from parent.
-        {
-            // if the utxo exists in parent:
-            // dirty is 'No' because this view does not have the utxo, therefore is different from parent
-            // fresh is 'No' because this view does not have the utxo but the parent has.
-            let entry = self
-                .parent
-                .utxo(outpoint)
-                .map(|utxo| UtxoEntry::new(Some(utxo), IsFresh::No, IsDirty::No));
-            if let Some(entry) = &entry {
-                self.utxos.insert(outpoint.clone(), entry.clone());
-            }
-            entry
+        // if the utxo exists in parent:
+        // dirty is 'No' because this view does not have the utxo, therefore is different from parent
+        // fresh is 'No' because this view does not have the utxo but the parent has.
+        let entry = self
+            .parent
+            .utxo(outpoint)
+            .map_err(|_| Error::ViewRead)?
+            .map(|utxo| UtxoEntry::new(Some(utxo), IsFresh::No, IsDirty::No));
+        if let Some(entry) = &entry {
+            self.utxos.insert(outpoint.clone(), entry.clone());
         }
+        Ok(entry)
     }
 
-    pub fn new(parent: P) -> Self {
-        let current_block_hash = parent.best_block_hash();
-        UtxosCache {
+    pub fn new(parent: P) -> Result<Self, P::Error> {
+        let current_block_hash = parent.best_block_hash()?;
+        Ok(UtxosCache {
             parent,
             current_block_hash,
             utxos: BTreeMap::new(),
             memory_usage: 0,
-        }
+        })
     }
 
     pub fn set_best_block(&mut self, block_hash: Id<GenBlock>) {
@@ -123,7 +122,7 @@ impl<P: UtxosView> UtxosCache<P> {
             let outpoint = OutPoint::new(OutPointSourceId::BlockReward(*block_id), idx as u32);
             // block reward transactions can always be overwritten
             let overwrite = if check_for_overwrite {
-                self.has_utxo(&outpoint)
+                self.has_utxo(&outpoint).map_err(|_| Error::ViewRead)?
             } else {
                 true
             };
@@ -151,7 +150,8 @@ impl<P: UtxosView> UtxosCache<P> {
             .try_for_each(|(idx, output)| {
                 let outpoint = OutPoint::new(id.clone(), idx as u32);
                 // by default no overwrite allowed.
-                let overwrite = check_for_overwrite && self.has_utxo(&outpoint);
+                let has_utxo = self.has_utxo(&outpoint).map_err(|_| Error::ViewRead)?;
+                let overwrite = check_for_overwrite && has_utxo;
                 let utxo = Utxo::new(output.clone(), false, source.clone());
 
                 self.add_utxo(&outpoint, utxo, overwrite)
@@ -309,7 +309,7 @@ impl<P: UtxosView> UtxosCache<P> {
     /// Flags the utxo as "spent", given an outpoint.
     /// Returns the Utxo if an update was performed.
     pub fn spend_utxo(&mut self, outpoint: &OutPoint) -> Result<Utxo, Error> {
-        let entry = self.fetch_utxo_entry(outpoint).ok_or(Error::NoUtxoFound)?;
+        let entry = self.fetch_utxo_entry(outpoint)?.ok_or(Error::NoUtxoFound)?;
         // TODO: update the memory usage
         // self.memory_usage must be deducted from this entry's size
 
@@ -332,9 +332,15 @@ impl<P: UtxosView> UtxosCache<P> {
     }
 
     /// Returns a mutable reference of the utxo, given the outpoint.
-    pub fn get_mut_utxo(&mut self, outpoint: &OutPoint) -> Option<&mut Utxo> {
-        let entry = self.fetch_utxo_entry(outpoint)?;
-        let utxo = entry.utxo()?;
+    pub fn get_mut_utxo(&mut self, outpoint: &OutPoint) -> Result<Option<&mut Utxo>, Error> {
+        let entry = match self.fetch_utxo_entry(outpoint)? {
+            Some(entry) => entry,
+            None => return Ok(None),
+        };
+        let utxo = match entry.utxo() {
+            Some(utxo) => utxo,
+            None => return Ok(None),
+        };
 
         let utxo: &mut UtxoEntry = self.utxos.entry(outpoint.clone()).or_insert_with(|| {
             //TODO: update the memory storage here
@@ -345,7 +351,7 @@ impl<P: UtxosView> UtxosCache<P> {
             )
         });
 
-        utxo.utxo_mut()
+        Ok(utxo.utxo_mut())
     }
 
     /// Removes the utxo from the cache if it's not modified
@@ -381,22 +387,24 @@ impl<P> Debug for UtxosCache<P> {
 }
 
 impl<P: UtxosView> UtxosView for UtxosCache<P> {
-    fn utxo(&self, outpoint: &OutPoint) -> Option<Utxo> {
+    type Error = P::Error;
+
+    fn utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error> {
         let key = outpoint;
         if let Some(res) = self.utxos.get(key) {
-            return res.utxo().cloned();
+            return Ok(res.utxo().cloned());
         }
 
         // if utxo is not found in this view, use parent's `get_utxo`.
         self.parent.utxo(outpoint)
     }
 
-    fn has_utxo(&self, outpoint: &OutPoint) -> bool {
-        self.utxo(outpoint).is_some()
+    fn has_utxo(&self, outpoint: &OutPoint) -> Result<bool, Self::Error> {
+        self.utxo(outpoint).map(|u| u.is_some())
     }
 
-    fn best_block_hash(&self) -> Id<GenBlock> {
-        self.current_block_hash
+    fn best_block_hash(&self) -> Result<Id<GenBlock>, Self::Error> {
+        Ok(self.current_block_hash)
     }
 
     fn estimated_size(&self) -> Option<usize> {
@@ -469,7 +477,9 @@ impl<P> FlushableUtxoView for UtxosCache<P> {
 #[cfg(test)]
 mod unit_test {
     use super::*;
-    use crate::tests::test_helper::{empty_test_utxos_view, insert_single_entry, Presence};
+    use crate::tests::test_helper::{
+        empty_test_utxos_view, insert_single_entry, Presence, UnwrapInfallible,
+    };
     use common::primitives::H256;
     use rstest::rstest;
     use test_utils::random::{make_seedable_rng, Seed};
@@ -482,9 +492,9 @@ mod unit_test {
 
         let expected_best_block_id: Id<GenBlock> = H256::random_using(&mut rng).into();
         let test_view = empty_test_utxos_view(H256::zero().into());
-        let mut cache = UtxosCache::new(&test_view);
+        let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
         cache.set_best_block(expected_best_block_id);
-        assert_eq!(expected_best_block_id, cache.best_block_hash());
+        assert_eq!(Ok(expected_best_block_id), cache.best_block_hash());
     }
 
     #[rstest]
@@ -493,7 +503,7 @@ mod unit_test {
     fn uncache_absent(#[case] seed: Seed) {
         let mut rng = make_seedable_rng(seed);
         let test_view = empty_test_utxos_view(H256::zero().into());
-        let mut cache = UtxosCache::new(&test_view);
+        let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
         // when the outpoint does not exist.
         let (_, outp) = insert_single_entry(&mut rng, &mut cache, Presence::Absent, None, None);
@@ -507,7 +517,7 @@ mod unit_test {
     fn uncache_not_fresh_not_dirty(#[case] seed: Seed) {
         let mut rng = make_seedable_rng(seed);
         let test_view = empty_test_utxos_view(H256::zero().into());
-        let mut cache = UtxosCache::new(&test_view);
+        let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
         // when the entry is not dirty and not fresh
         let (_, outp) = insert_single_entry(
@@ -527,7 +537,7 @@ mod unit_test {
     fn uncache_dirty_not_fresh(#[case] seed: Seed) {
         let mut rng = make_seedable_rng(seed);
         let test_view = empty_test_utxos_view(H256::zero().into());
-        let mut cache = UtxosCache::new(&test_view);
+        let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
         // when the entry is dirty, entry cannot be removed.
         let (_, outp) = insert_single_entry(
@@ -546,7 +556,7 @@ mod unit_test {
     fn uncache_fresh_and_dirty(#[case] seed: Seed) {
         let mut rng = make_seedable_rng(seed);
         let test_view = empty_test_utxos_view(H256::zero().into());
-        let mut cache = UtxosCache::new(&test_view);
+        let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
         // when the entry is both fresh and dirty, entry cannot be removed.
         let (_, outp) = insert_single_entry(
@@ -565,7 +575,7 @@ mod unit_test {
     fn fetch_an_entry(#[case] seed: Seed) {
         let mut rng = make_seedable_rng(seed);
         let test_view = empty_test_utxos_view(H256::zero().into());
-        let mut cache1 = UtxosCache::new(&test_view);
+        let mut cache1 = UtxosCache::new(&test_view).unwrap_infallible();
         let (_, outpoint) = insert_single_entry(
             &mut rng,
             &mut cache1,
@@ -574,12 +584,12 @@ mod unit_test {
             None,
         );
 
-        let mut cache2 = UtxosCache::new(&cache1);
+        let mut cache2 = UtxosCache::new(&cache1).unwrap_infallible();
 
         assert!(cache1.has_utxo_in_cache(&outpoint));
         assert!(!cache2.has_utxo_in_cache(&outpoint));
 
-        cache2.fetch_utxo_entry(&outpoint);
+        cache2.fetch_utxo_entry(&outpoint).unwrap();
 
         assert!(cache1.has_utxo_in_cache(&outpoint));
         assert!(cache2.has_utxo_in_cache(&outpoint));
diff --git a/utxo/src/error.rs b/utxo/src/error.rs
index b382413d7..c86e56f92 100644
--- a/utxo/src/error.rs
+++ b/utxo/src/error.rs
@@ -13,7 +13,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use chainstate_types::storage_result;
 use common::{
     chain::{block::GenBlock, OutPointSourceId},
     primitives::Id,
@@ -38,6 +37,16 @@ pub enum Error {
     MissingBlockRewardUndo(Id<GenBlock>),
     #[error("Block reward type is invalid `{0}`")]
     InvalidBlockRewardOutputType(Id<GenBlock>),
-    #[error("Database error: `{0}`")]
-    DBError(#[from] storage_result::Error),
+
+    // TODO This is a temporary solution. It does not provide much information, the exact utxo
+    //      view error is lost. The concrete error type depends on the UtxoView used. The error
+    //      enum here should be parametrized by the error type rather than hide it, so the error
+    //      type information is available at compilation time and the exact error emitted from
+    //      UtxoView is available to the caller at run time. That, however, leads to many call
+    //      sites having to be updated so it's left for future improvements.
+    //      https://github.com/mintlayer/mintlayer-core/issues/811
+    #[error("UTXO view query failed `for some reason (TM)`")]
+    ViewRead,
+    #[error("UTXO storage write failed `for some reason (TM)`")]
+    StorageWrite,
 }
diff --git a/utxo/src/storage/in_memory.rs b/utxo/src/storage/in_memory.rs
index cf2c4f028..cbc6cbe50 100644
--- a/utxo/src/storage/in_memory.rs
+++ b/utxo/src/storage/in_memory.rs
@@ -15,7 +15,7 @@
 
 use super::{UtxosStorageRead, UtxosStorageWrite};
 use crate::{Utxo, UtxosBlockUndo, UtxosView};
-use chainstate_types::storage_result::Error;
+use chainstate_types::storage_result::{self, Error};
 use common::{
     chain::{Block, GenBlock, OutPoint},
     primitives::Id,
@@ -40,6 +40,8 @@ impl UtxosDBInMemoryImpl {
 }
 
 impl UtxosStorageRead for UtxosDBInMemoryImpl {
+    type Error = storage_result::Error;
+
     fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Error> {
         let res = self.store.get(outpoint);
         Ok(res.cloned())
@@ -81,16 +83,18 @@ impl UtxosStorageWrite for UtxosDBInMemoryImpl {
 }
 
 impl UtxosView for UtxosDBInMemoryImpl {
-    fn utxo(&self, outpoint: &OutPoint) -> Option<Utxo> {
-        self.store.get(outpoint).cloned()
+    type Error = std::convert::Infallible;
+
+    fn utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error> {
+        Ok(self.store.get(outpoint).cloned())
     }
 
-    fn has_utxo(&self, outpoint: &OutPoint) -> bool {
-        self.store.get(outpoint).is_some()
+    fn has_utxo(&self, outpoint: &OutPoint) -> Result<bool, Self::Error> {
+        Ok(self.store.get(outpoint).is_some())
     }
 
-    fn best_block_hash(&self) -> Id<GenBlock> {
-        self.best_block_id
+    fn best_block_hash(&self) -> Result<Id<GenBlock>, Self::Error> {
+        Ok(self.best_block_id)
     }
 
     fn estimated_size(&self) -> Option<usize> {
diff --git a/utxo/src/storage/mod.rs b/utxo/src/storage/mod.rs
index 541cacb5d..b0daea51e 100644
--- a/utxo/src/storage/mod.rs
+++ b/utxo/src/storage/mod.rs
@@ -17,7 +17,6 @@ mod rw_impls;
 mod view_impls;
 
 use crate::{FlushableUtxoView, Utxo, UtxosBlockUndo, UtxosCache};
-use chainstate_types::storage_result;
 use common::{
     chain::{Block, ChainConfig, GenBlock, OutPoint},
     primitives::{BlockHeight, Id},
@@ -25,27 +24,20 @@ use common::{
 use std::ops::{Deref, DerefMut};
 
 pub trait UtxosStorageRead {
-    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, storage_result::Error>;
-    fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>, storage_result::Error>;
-    fn get_undo_data(&self, id: Id<Block>)
-        -> Result<Option<UtxosBlockUndo>, storage_result::Error>;
+    type Error: std::error::Error;
+    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error>;
+    fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>, Self::Error>;
+    fn get_undo_data(&self, id: Id<Block>) -> Result<Option<UtxosBlockUndo>, Self::Error>;
 }
 
 pub trait UtxosStorageWrite: UtxosStorageRead {
-    fn set_utxo(&mut self, outpoint: &OutPoint, entry: Utxo) -> Result<(), storage_result::Error>;
-    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<(), storage_result::Error>;
-
-    fn set_best_block_for_utxos(
-        &mut self,
-        block_id: &Id<GenBlock>,
-    ) -> Result<(), storage_result::Error>;
-
-    fn set_undo_data(
-        &mut self,
-        id: Id<Block>,
-        undo: &UtxosBlockUndo,
-    ) -> Result<(), storage_result::Error>;
-    fn del_undo_data(&mut self, id: Id<Block>) -> Result<(), storage_result::Error>;
+    fn set_utxo(&mut self, outpoint: &OutPoint, entry: Utxo) -> Result<(), Self::Error>;
+    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<(), Self::Error>;
+
+    fn set_best_block_for_utxos(&mut self, block_id: &Id<GenBlock>) -> Result<(), Self::Error>;
+
+    fn set_undo_data(&mut self, id: Id<Block>, undo: &UtxosBlockUndo) -> Result<(), Self::Error>;
+    fn del_undo_data(&mut self, id: Id<Block>) -> Result<(), Self::Error>;
 }
 
 #[must_use]
@@ -75,7 +67,7 @@ impl<S: UtxosStorageWrite> UtxosDB<S> {
         // before deriving cache, there has to be a best block
         utxos_db.set_best_block_for_utxos(&genesis_id).expect("Setting genesis failed");
 
-        let mut utxos_cache = UtxosCache::new(&utxos_db);
+        let mut utxos_cache = UtxosCache::new(&utxos_db).expect("Utxo cache setup failed");
 
         for (index, output) in genesis.utxos().iter().enumerate() {
             utxos_cache
@@ -100,18 +92,17 @@ where
     T: Deref,
     <T as Deref>::Target: UtxosStorageRead,
 {
-    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, storage_result::Error> {
+    type Error = <T::Target as UtxosStorageRead>::Error;
+
+    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error> {
         self.deref().get_utxo(outpoint)
     }
 
-    fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>, storage_result::Error> {
+    fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>, Self::Error> {
         self.deref().get_best_block_for_utxos()
     }
 
-    fn get_undo_data(
-        &self,
-        id: Id<Block>,
-    ) -> Result<Option<UtxosBlockUndo>, storage_result::Error> {
+    fn get_undo_data(&self, id: Id<Block>) -> Result<Option<UtxosBlockUndo>, Self::Error> {
         self.deref().get_undo_data(id)
     }
 }
@@ -121,30 +112,23 @@ where
     T: DerefMut,
     <T as Deref>::Target: UtxosStorageWrite,
 {
-    fn set_utxo(&mut self, outpoint: &OutPoint, entry: Utxo) -> Result<(), storage_result::Error> {
+    fn set_utxo(&mut self, outpoint: &OutPoint, entry: Utxo) -> Result<(), Self::Error> {
         self.deref_mut().set_utxo(outpoint, entry)
     }
 
-    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<(), storage_result::Error> {
+    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<(), Self::Error> {
         self.deref_mut().del_utxo(outpoint)
     }
 
-    fn set_best_block_for_utxos(
-        &mut self,
-        block_id: &Id<GenBlock>,
-    ) -> Result<(), storage_result::Error> {
+    fn set_best_block_for_utxos(&mut self, block_id: &Id<GenBlock>) -> Result<(), Self::Error> {
         self.deref_mut().set_best_block_for_utxos(block_id)
     }
 
-    fn set_undo_data(
-        &mut self,
-        id: Id<Block>,
-        undo: &UtxosBlockUndo,
-    ) -> Result<(), storage_result::Error> {
+    fn set_undo_data(&mut self, id: Id<Block>, undo: &UtxosBlockUndo) -> Result<(), Self::Error> {
         self.deref_mut().set_undo_data(id, undo)
     }
 
-    fn del_undo_data(&mut self, id: Id<Block>) -> Result<(), storage_result::Error> {
+    fn del_undo_data(&mut self, id: Id<Block>) -> Result<(), Self::Error> {
         self.deref_mut().del_undo_data(id)
     }
 }
diff --git a/utxo/src/storage/rw_impls.rs b/utxo/src/storage/rw_impls.rs
index bd065d9e5..ce6051849 100644
--- a/utxo/src/storage/rw_impls.rs
+++ b/utxo/src/storage/rw_impls.rs
@@ -15,43 +15,44 @@
 
 use super::{UtxosDB, UtxosStorageRead, UtxosStorageWrite};
 use crate::{Utxo, UtxosBlockUndo};
-use chainstate_types::storage_result::Error as StorageError;
 use common::{
     chain::{Block, GenBlock, OutPoint},
     primitives::Id,
 };
 
 impl<S: UtxosStorageWrite> UtxosStorageWrite for UtxosDB<S> {
-    fn set_utxo(&mut self, outpoint: &OutPoint, entry: Utxo) -> Result<(), StorageError> {
+    fn set_utxo(&mut self, outpoint: &OutPoint, entry: Utxo) -> Result<(), Self::Error> {
         self.0.set_utxo(outpoint, entry)
     }
 
-    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<(), StorageError> {
+    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<(), Self::Error> {
         self.0.del_utxo(outpoint)
     }
 
-    fn set_best_block_for_utxos(&mut self, block_id: &Id<GenBlock>) -> Result<(), StorageError> {
+    fn set_best_block_for_utxos(&mut self, block_id: &Id<GenBlock>) -> Result<(), Self::Error> {
         self.0.set_best_block_for_utxos(block_id)
     }
-    fn set_undo_data(&mut self, id: Id<Block>, undo: &UtxosBlockUndo) -> Result<(), StorageError> {
+    fn set_undo_data(&mut self, id: Id<Block>, undo: &UtxosBlockUndo) -> Result<(), Self::Error> {
         self.0.set_undo_data(id, undo)
     }
 
-    fn del_undo_data(&mut self, id: Id<Block>) -> Result<(), StorageError> {
+    fn del_undo_data(&mut self, id: Id<Block>) -> Result<(), Self::Error> {
         self.0.del_undo_data(id)
     }
 }
 
 impl<S: UtxosStorageRead> UtxosStorageRead for UtxosDB<S> {
-    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, StorageError> {
+    type Error = S::Error;
+
+    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error> {
         self.0.get_utxo(outpoint)
     }
 
-    fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>, StorageError> {
+    fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>, Self::Error> {
         self.0.get_best_block_for_utxos()
     }
 
-    fn get_undo_data(&self, id: Id<Block>) -> Result<Option<UtxosBlockUndo>, StorageError> {
+    fn get_undo_data(&self, id: Id<Block>) -> Result<Option<UtxosBlockUndo>, Self::Error> {
         self.0.get_undo_data(id)
     }
 }
diff --git a/utxo/src/storage/view_impls.rs b/utxo/src/storage/view_impls.rs
index f7fdc4aa5..9791ac500 100644
--- a/utxo/src/storage/view_impls.rs
+++ b/utxo/src/storage/view_impls.rs
@@ -14,26 +14,25 @@
 // limitations under the License.
 
 use super::{UtxosDB, UtxosStorageRead, UtxosStorageWrite};
-use crate::{ConsumedUtxoCache, FlushableUtxoView, Utxo, UtxosView};
+use crate::{ConsumedUtxoCache, Error, FlushableUtxoView, Utxo, UtxosView};
 use common::{
     chain::{GenBlock, OutPoint},
     primitives::Id,
 };
 
 impl<S: UtxosStorageRead> UtxosView for UtxosDB<S> {
-    fn utxo(&self, outpoint: &OutPoint) -> Option<Utxo> {
+    type Error = S::Error;
+
+    fn utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error> {
         self.get_utxo(outpoint)
-            .expect("Database error while attempting to retrieve utxo")
     }
 
-    fn has_utxo(&self, outpoint: &OutPoint) -> bool {
-        self.utxo(outpoint).is_some()
+    fn has_utxo(&self, outpoint: &OutPoint) -> Result<bool, Self::Error> {
+        self.utxo(outpoint).map(|u| u.is_some())
     }
 
-    fn best_block_hash(&self) -> Id<GenBlock> {
-        self.get_best_block_for_utxos()
-            .expect("Database error while attempting to retrieve utxo set best block hash")
-            .expect("Failed to get best block hash")
+    fn best_block_hash(&self) -> Result<Id<GenBlock>, Self::Error> {
+        Ok(self.get_best_block_for_utxos()?.expect("Failed to get best block hash"))
     }
 
     fn estimated_size(&self) -> Option<usize> {
@@ -42,20 +41,22 @@ impl<S: UtxosStorageRead> UtxosView for UtxosDB<S> {
 }
 
 impl<S: UtxosStorageWrite> FlushableUtxoView for UtxosDB<S> {
-    fn batch_write(&mut self, utxos: ConsumedUtxoCache) -> Result<(), crate::Error> {
+    fn batch_write(&mut self, utxos: ConsumedUtxoCache) -> Result<(), Error> {
         // check each entry if it's dirty. Only then will the db be updated.
         for (key, entry) in utxos.container {
             let outpoint = &key;
             if entry.is_dirty() {
                 if let Some(utxo) = entry.utxo() {
-                    self.0.set_utxo(outpoint, utxo.clone())?;
+                    self.0.set_utxo(outpoint, utxo.clone()).map_err(|_| Error::StorageWrite)?;
                 } else {
                     // entry is spent
-                    self.0.del_utxo(outpoint)?;
+                    self.0.del_utxo(outpoint).map_err(|_| Error::StorageWrite)?;
                 };
             }
         }
-        self.0.set_best_block_for_utxos(&utxos.best_block)?;
+        self.0
+            .set_best_block_for_utxos(&utxos.best_block)
+            .map_err(|_| Error::StorageWrite)?;
         Ok(())
     }
 }
diff --git a/utxo/src/view.rs b/utxo/src/view.rs
index e029a2bdf..ee674412a 100644
--- a/utxo/src/view.rs
+++ b/utxo/src/view.rs
@@ -20,14 +20,17 @@ use common::{
 };
 
 pub trait UtxosView {
+    /// Error that can occur during utxo queries
+    type Error: std::error::Error;
+
     /// Retrieves utxo.
-    fn utxo(&self, outpoint: &OutPoint) -> Option<Utxo>;
+    fn utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error>;
 
     /// Checks whether outpoint is unspent.
-    fn has_utxo(&self, outpoint: &OutPoint) -> bool;
+    fn has_utxo(&self, outpoint: &OutPoint) -> Result<bool, Self::Error>;
 
     /// Retrieves the block hash of the best block in this view
-    fn best_block_hash(&self) -> Id<GenBlock>;
+    fn best_block_hash(&self) -> Result<Id<GenBlock>, Self::Error>;
 
     /// Estimated size of the whole view (None if not implemented)
     fn estimated_size(&self) -> Option<usize>;


Test patch:
diff --git a/chainstate/test-framework/src/tx_verification_strategy/disposable_strategy.rs b/chainstate/test-framework/src/tx_verification_strategy/disposable_strategy.rs
index 94060e922..d2678e476 100644
--- a/chainstate/test-framework/src/tx_verification_strategy/disposable_strategy.rs
+++ b/chainstate/test-framework/src/tx_verification_strategy/disposable_strategy.rs
@@ -19,6 +19,7 @@ use chainstate::{
         construct_reward_tx_indices, construct_tx_indices, take_front_tx_index,
     },
     BlockError, TransactionVerificationStrategy, TransactionVerifierMakerFn,
+    TransactionVerifierStorageError,
 };
 use chainstate_types::{BlockIndex, BlockIndexHandle};
 use common::{
@@ -65,10 +66,11 @@ impl TransactionVerificationStrategy for DisposableTransactionVerificationStrate
     where
         C: AsRef<ChainConfig>,
         H: BlockIndexHandle,
-        S: TransactionVerifierStorageRef,
+        S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
         U: UtxosView,
         A: PoSAccountingView,
         M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         // The comparison for timelock is done with median_time_past based on BIP-113, i.e., the median time instead of the block timestamp
         let median_time_past =
@@ -141,10 +143,11 @@ impl TransactionVerificationStrategy for DisposableTransactionVerificationStrate
     ) -> Result<TransactionVerifier<C, S, U, A>, BlockError>
     where
         C: AsRef<ChainConfig>,
-        S: TransactionVerifierStorageRef,
+        S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
         U: UtxosView,
         A: PoSAccountingView,
         M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         let mut base_tx_verifier =
             tx_verifier_maker(storage_backend, chain_config, verifier_config);
diff --git a/chainstate/test-framework/src/tx_verification_strategy/randomized_strategy.rs b/chainstate/test-framework/src/tx_verification_strategy/randomized_strategy.rs
index ac3d0386f..eef8db4ba 100644
--- a/chainstate/test-framework/src/tx_verification_strategy/randomized_strategy.rs
+++ b/chainstate/test-framework/src/tx_verification_strategy/randomized_strategy.rs
@@ -21,6 +21,7 @@ use chainstate::{
         construct_reward_tx_indices, construct_tx_indices, take_front_tx_index,
     },
     BlockError, TransactionVerificationStrategy, TransactionVerifierMakerFn,
+    TransactionVerifierStorageError,
 };
 use chainstate_types::{BlockIndex, BlockIndexHandle};
 use common::{
@@ -78,10 +79,11 @@ impl TransactionVerificationStrategy for RandomizedTransactionVerificationStrate
     where
         C: AsRef<ChainConfig>,
         H: BlockIndexHandle,
-        S: TransactionVerifierStorageRef,
+        S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
         U: UtxosView,
         A: PoSAccountingView,
         M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         // The comparison for timelock is done with median_time_past based on BIP-113, i.e., the median time instead of the block timestamp
         let median_time_past =
@@ -114,10 +116,11 @@ impl TransactionVerificationStrategy for RandomizedTransactionVerificationStrate
     ) -> Result<TransactionVerifier<C, S, U, A>, BlockError>
     where
         C: AsRef<ChainConfig>,
-        S: TransactionVerifierStorageRef,
+        S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
         U: UtxosView,
         A: PoSAccountingView,
         M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         let mut tx_verifier = self.disconnect_with_base(
             tx_verifier_maker,
@@ -146,10 +149,11 @@ impl RandomizedTransactionVerificationStrategy {
         median_time_past: &BlockTimestamp,
     ) -> Result<TransactionVerifier<C, S, U, A>, ConnectTransactionError>
     where
-        S: TransactionVerifierStorageRef,
+        S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
         U: UtxosView,
         A: PoSAccountingView,
         M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         let block_subsidy =
             chain_config.as_ref().block_subsidy_at_height(&block_index.block_height());
@@ -223,6 +227,7 @@ impl RandomizedTransactionVerificationStrategy {
         U: UtxosView,
         A: PoSAccountingView,
         S: TransactionVerifierStorageRef,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         let mut tx_verifier = base_tx_verifier.derive_child();
         let mut total_fee = Amount::ZERO;
@@ -262,10 +267,11 @@ impl RandomizedTransactionVerificationStrategy {
     ) -> Result<TransactionVerifier<C, S, U, A>, ConnectTransactionError>
     where
         C: AsRef<ChainConfig>,
-        S: TransactionVerifierStorageRef,
+        S: TransactionVerifierStorageRef<Error = TransactionVerifierStorageError>,
         U: UtxosView,
         A: PoSAccountingView,
         M: TransactionVerifierMakerFn<C, S, U, A>,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         let mut tx_verifier = tx_verifier_maker(storage_backend, chain_config, verifier_config);
         let mut tx_num = i32::try_from(block.transactions().len()).unwrap() - 1;
@@ -306,6 +312,7 @@ impl RandomizedTransactionVerificationStrategy {
         U: UtxosView,
         A: PoSAccountingView,
         S: TransactionVerifierStorageRef,
+        <S as utxo::UtxosStorageRead>::Error: From<U::Error>,
     {
         let mut tx_verifier = base_tx_verifier.derive_child();
         while tx_num >= 0 {
diff --git a/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs b/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs
index 188e07954..708e9cf94 100644
--- a/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs
+++ b/chainstate/test-suite/src/tests/helpers/in_memory_storage_wrapper.rs
@@ -46,6 +46,8 @@ impl InMemoryStorageWrapper {
 }
 
 impl TransactionVerifierStorageRef for InMemoryStorageWrapper {
+    type Error = TransactionVerifierStorageError;
+
     fn get_token_id_from_issuance_tx(
         &self,
         tx_id: Id<Transaction>,
@@ -96,6 +98,8 @@ impl TransactionVerifierStorageRef for InMemoryStorageWrapper {
 }
 
 impl UtxosStorageRead for InMemoryStorageWrapper {
+    type Error = storage_result::Error;
+
     fn get_utxo(
         &self,
         outpoint: &common::chain::OutPoint,
@@ -116,6 +120,8 @@ impl UtxosStorageRead for InMemoryStorageWrapper {
 }
 
 impl PoSAccountingView for InMemoryStorageWrapper {
+    type Error = pos_accounting::Error;
+
     fn pool_exists(&self, pool_id: PoolId) -> Result<bool, pos_accounting::Error> {
         self.get_pool_data(pool_id).map(|v| v.is_some())
     }
diff --git a/chainstate/test-suite/src/tests/tx_verifier_among_threads.rs b/chainstate/test-suite/src/tests/tx_verifier_among_threads.rs
index 9c3ec3456..1ecf63752 100644
--- a/chainstate/test-suite/src/tests/tx_verifier_among_threads.rs
+++ b/chainstate/test-suite/src/tests/tx_verifier_among_threads.rs
@@ -27,19 +27,21 @@ use utxo::{Utxo, UtxosView};
 
 use super::helpers::in_memory_storage_wrapper::InMemoryStorageWrapper;
 
-pub struct EmptyUtxosView;
+struct EmptyUtxosView;
 
 impl UtxosView for EmptyUtxosView {
-    fn utxo(&self, _outpoint: &OutPoint) -> Option<Utxo> {
-        None
+    type Error = std::convert::Infallible;
+
+    fn utxo(&self, _outpoint: &OutPoint) -> Result<Option<Utxo>, Self::Error> {
+        Ok(None)
     }
 
-    fn has_utxo(&self, _outpoint: &OutPoint) -> bool {
-        false
+    fn has_utxo(&self, _outpoint: &OutPoint) -> Result<bool, Self::Error> {
+        Ok(false)
     }
 
-    fn best_block_hash(&self) -> Id<common::chain::GenBlock> {
-        H256::zero().into()
+    fn best_block_hash(&self) -> Result<Id<common::chain::GenBlock>, Self::Error> {
+        Ok(H256::zero().into())
     }
 
     fn estimated_size(&self) -> Option<usize> {
@@ -47,9 +49,11 @@ impl UtxosView for EmptyUtxosView {
     }
 }
 
-pub struct EmptyAccountingView;
+struct EmptyAccountingView;
 
 impl PoSAccountingView for EmptyAccountingView {
+    type Error = pos_accounting::Error;
+
     fn pool_exists(&self, _pool_id: PoolId) -> Result<bool, pos_accounting::Error> {
         Ok(false)
     }
diff --git a/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs b/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs
index a6853f7fe..5a5db8f82 100644
--- a/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs
+++ b/chainstate/tx-verifier/src/transaction_verifier/tests/mock.rs
@@ -39,6 +39,8 @@ mockall::mock! {
     pub Store {}
 
     impl TransactionVerifierStorageRef for Store {
+        type Error = TransactionVerifierStorageError;
+
         fn get_token_id_from_issuance_tx(
             &self,
             tx_id: Id<Transaction>,
@@ -121,6 +123,7 @@ mockall::mock! {
     }
 
     impl UtxosStorageRead for Store {
+        type Error = storage_result::Error;
         fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<Utxo>, storage_result::Error>;
         fn get_best_block_for_utxos(&self) -> Result<Option<Id<GenBlock>>,storage_result::Error>;
         fn get_undo_data(&self, id: Id<Block>) -> Result<Option<utxo::UtxosBlockUndo>, storage_result::Error>;
@@ -131,6 +134,7 @@ mockall::mock! {
     }
 
     impl PoSAccountingView for Store {
+        type Error = pos_accounting::Error;
         fn pool_exists(&self, pool_id: PoolId) -> Result<bool, pos_accounting::Error>;
         fn get_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, pos_accounting::Error>;
         fn get_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, pos_accounting::Error>;
diff --git a/utxo/src/storage/test.rs b/utxo/src/storage/test.rs
index 926d88da4..ac1b56896 100644
--- a/utxo/src/storage/test.rs
+++ b/utxo/src/storage/test.rs
@@ -147,7 +147,7 @@ fn utxo_and_undo_test(#[case] seed: Seed) {
         .iter()
         .map(|input| {
             let outpoint = input.outpoint();
-            assert!(db.has_utxo(outpoint));
+            assert!(db.has_utxo(outpoint).unwrap());
 
             db.utxo(outpoint).expect("utxo should exist.")
         })
@@ -156,12 +156,12 @@ fn utxo_and_undo_test(#[case] seed: Seed) {
     // test the spend
     let (block, block_undo) = {
         // create a cache based on the db.
-        let mut cache = UtxosCache::new(&db);
+        let mut cache = UtxosCache::new(&db).unwrap();
 
         // create a new block to spend.
         let block = create_block(
             &mut rng,
-            cache.best_block_hash(),
+            cache.best_block_hash().unwrap(),
             expected_tx_inputs.clone(),
             0,
             num_of_txs,
@@ -193,7 +193,10 @@ fn utxo_and_undo_test(#[case] seed: Seed) {
         {
             block_undo.tx_undos().iter().enumerate().for_each(|(b_idx, (_tx_id, tx_undo))| {
                 tx_undo.inner().iter().enumerate().for_each(|(t_idx, utxo)| {
-                    assert_eq!(Some(utxo), spent_utxos.get(b_idx + t_idx));
+                    assert_eq!(
+                        Some(Some(utxo)),
+                        spent_utxos.get(b_idx + t_idx).map(|x| x.as_ref())
+                    );
                 })
             })
         }
@@ -208,7 +211,7 @@ fn utxo_and_undo_test(#[case] seed: Seed) {
 
     // check that all in tx_inputs do NOT exist
     expected_tx_inputs.iter().for_each(|input| {
-        assert_eq!(db.utxo(input.outpoint()), None);
+        assert_eq!(db.utxo(input.outpoint()), Ok(None));
     });
 
     // save the undo data to the db.
@@ -223,7 +226,7 @@ fn utxo_and_undo_test(#[case] seed: Seed) {
     {
         block.transactions().iter().for_each(|tx| {
             tx.inputs().iter().for_each(|input| {
-                assert_eq!(db.utxo(input.outpoint()), None);
+                assert_eq!(db.utxo(input.outpoint()), Ok(None));
             });
         });
     }
@@ -231,7 +234,7 @@ fn utxo_and_undo_test(#[case] seed: Seed) {
     // let's try to reverse the spending.
     {
         // get the best_block_id
-        let current_best_block_id = db.best_block_hash();
+        let current_best_block_id = db.best_block_hash().unwrap();
 
         // the current best_block_id should be the block id..
         assert_eq!(&current_best_block_id, &block.get_id());
@@ -246,7 +249,7 @@ fn utxo_and_undo_test(#[case] seed: Seed) {
         assert_eq!(block_undo.tx_undos().len(), expected_tx_inputs.len());
 
         // let's create a view.
-        let mut cache = UtxosCache::new(&db);
+        let mut cache = UtxosCache::new(&db).unwrap();
 
         // get the block tx inputs, and add them to the view.
         block.transactions().iter().enumerate().for_each(|(_idx, tx)| {
@@ -275,7 +278,7 @@ fn utxo_and_undo_test(#[case] seed: Seed) {
         let res = db.utxo(input.outpoint());
 
         let expected_utxo = spent_utxos.get(idx);
-        assert_eq!(res.as_ref(), expected_utxo);
+        assert_eq!(res.ok().as_ref(), expected_utxo);
     });
 }
 
@@ -300,11 +303,11 @@ fn try_spend_tx_with_no_outputs(#[case] seed: Seed) {
         })
         .collect();
 
-    let id = db.best_block_hash();
+    let id = db.best_block_hash().unwrap();
 
     // Create a block with 1 tx and 0 outputs in txs
     let block = create_block(&mut rng, id, tx_inputs, 0, num_of_txs as usize);
-    let mut view = UtxosCache::new(&db);
+    let mut view = UtxosCache::new(&db).unwrap();
     let tx = block.transactions().get(0).unwrap();
 
     assert_eq!(
@@ -337,15 +340,15 @@ fn test_batch_write(#[case] seed: Seed) {
     let outpoint = keys[key_index].clone();
 
     // test the get_utxo
-    let utxo_opt = utxo_db.utxo(&outpoint);
+    let utxo_opt = utxo_db.utxo(&outpoint).unwrap();
     let utxo_entry = utxos.container.get(&outpoint).expect("an entry should be found");
     assert_eq!(utxo_entry.utxo(), utxo_opt.as_ref());
 
     // check has_utxo
-    assert!(utxo_db.has_utxo(&outpoint));
+    assert!(utxo_db.has_utxo(&outpoint).unwrap());
 
     //check the best block hash
-    assert_eq!(utxo_db.best_block_hash(), new_best_block_hash);
+    assert_eq!(utxo_db.best_block_hash().unwrap(), new_best_block_hash);
 }
 
 #[rstest]
@@ -370,7 +373,7 @@ fn try_flush_non_dirty_utxo(#[case] seed: Seed) {
 
     utxo_db.batch_write(cache).unwrap();
 
-    assert!(!utxo_db.has_utxo(&outpoint));
+    assert!(!utxo_db.has_utxo(&outpoint).unwrap());
 }
 
 #[rstest]
@@ -398,5 +401,5 @@ fn try_flush_spent_utxo(#[case] seed: Seed) {
 
     utxo_db.batch_write(cache).unwrap();
 
-    assert!(!utxo_db.has_utxo(&outpoint));
+    assert!(!utxo_db.has_utxo(&outpoint).unwrap());
 }
diff --git a/utxo/src/tests/mod.rs b/utxo/src/tests/mod.rs
index 978d1b0e4..14adfd6cc 100644
--- a/utxo/src/tests/mod.rs
+++ b/utxo/src/tests/mod.rs
@@ -22,6 +22,7 @@ use crate::{
     tests::test_helper::{
         create_tx_outputs, empty_test_utxos_view,
         Presence::{self, *},
+        UnwrapInfallible,
     },
     utxo_entry::{IsDirty, IsFresh, UtxoEntry},
     ConsumedUtxoCache,
@@ -47,7 +48,7 @@ use crypto::{
 };
 use itertools::Itertools;
 use rstest::rstest;
-use std::collections::BTreeMap;
+use std::{collections::BTreeMap, convert::Infallible};
 use test_utils::random::{make_seedable_rng, Seed};
 
 fn make_pool_id(rng: &mut impl Rng) -> PoolId {
@@ -70,7 +71,7 @@ fn check_add_utxo(
     op_result: Result<(), Error>,
 ) {
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
     let (_, outpoint) =
         test_helper::insert_single_entry(rng, &mut cache, cache_presence, cache_flags, None);
 
@@ -103,7 +104,8 @@ fn check_spend_utxo(
     result_flags: Option<(IsFresh, IsDirty)>,
 ) {
     // initialize the parent cache.
-    let mut parent = UtxosCache::new(empty_test_utxos_view(H256::zero().into()));
+    let mut parent =
+        UtxosCache::new(empty_test_utxos_view(H256::zero().into())).unwrap_infallible();
     let (_, parent_outpoint) = test_helper::insert_single_entry(
         rng,
         &mut parent,
@@ -117,10 +119,11 @@ fn check_spend_utxo(
     let mut child = match parent_presence {
         Absent => UtxosCache::new(empty_test_utxos_view(H256::zero().into())),
         _ => {
-            let parent: Box<dyn UtxosView> = Box::new(parent);
+            let parent: Box<dyn UtxosView<Error = Infallible>> = Box::new(parent);
             UtxosCache::new(parent)
         }
-    };
+    }
+    .unwrap_infallible();
 
     let (_, child_outpoint) = test_helper::insert_single_entry(
         rng,
@@ -167,7 +170,7 @@ fn check_write_utxo(
 ) {
     //initialize the parent cache
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut parent = UtxosCache::new(&test_view);
+    let mut parent = UtxosCache::new(&test_view).unwrap_infallible();
     let (_, outpoint) =
         test_helper::insert_single_entry(rng, &mut parent, parent_presence, parent_flags, None);
     let key = &outpoint;
@@ -226,7 +229,8 @@ fn check_get_mut_utxo(
     cache_flags: Option<(IsFresh, IsDirty)>,
     result_flags: Option<(IsFresh, IsDirty)>,
 ) {
-    let mut parent = UtxosCache::new(empty_test_utxos_view(H256::zero().into()));
+    let mut parent =
+        UtxosCache::new(empty_test_utxos_view(H256::zero().into())).unwrap_infallible();
     let (parent_utxo, parent_outpoint) = test_helper::insert_single_entry(
         rng,
         &mut parent,
@@ -239,10 +243,11 @@ fn check_get_mut_utxo(
     let mut child = match parent_presence {
         Absent => UtxosCache::new(empty_test_utxos_view(H256::zero().into())),
         _ => {
-            let parent: Box<dyn UtxosView> = Box::new(parent);
+            let parent: Box<dyn UtxosView<Error = Infallible>> = Box::new(parent);
             UtxosCache::new(parent)
         }
-    };
+    }
+    .unwrap_infallible();
     let (child_utxo, child_outpoint) = test_helper::insert_single_entry(
         rng,
         &mut child,
@@ -255,7 +260,7 @@ fn check_get_mut_utxo(
     let mut expected_utxo: Option<Utxo> = None;
     {
         // perform the get_mut_utxo
-        let utxo_opt = child.get_mut_utxo(&child_outpoint);
+        let utxo_opt = child.get_mut_utxo(&child_outpoint).unwrap();
 
         if let Some(utxo) = utxo_opt {
             match cache_presence {
@@ -437,7 +442,7 @@ fn access_utxo_test(#[case] seed: Seed) {
 fn derive_cache_test(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     let (utxo, outpoint_1) = test_helper::create_utxo(&mut rng, 10);
     assert!(cache.add_utxo(&outpoint_1, utxo, false).is_ok());
@@ -445,16 +450,16 @@ fn derive_cache_test(#[case] seed: Seed) {
     let (utxo, outpoint_2) = test_helper::create_utxo(&mut rng, 20);
     assert!(cache.add_utxo(&outpoint_2, utxo, false).is_ok());
 
-    let mut extra_cache = UtxosCache::new(&cache);
+    let mut extra_cache = UtxosCache::new(&cache).unwrap_infallible();
     assert!(extra_cache.utxos.is_empty());
 
-    assert!(extra_cache.has_utxo(&outpoint_1));
-    assert!(extra_cache.has_utxo(&outpoint_2));
+    assert!(extra_cache.has_utxo(&outpoint_1).unwrap_infallible());
+    assert!(extra_cache.has_utxo(&outpoint_2).unwrap_infallible());
 
     let (utxo, outpoint) = test_helper::create_utxo(&mut rng, 30);
     assert!(extra_cache.add_utxo(&outpoint, utxo, true).is_ok());
 
-    assert!(!cache.has_utxo(&outpoint));
+    assert!(!cache.has_utxo(&outpoint).unwrap_infallible());
 }
 
 #[rstest]
@@ -463,7 +468,7 @@ fn derive_cache_test(#[case] seed: Seed) {
 fn blockchain_or_mempool_utxo_test(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     let (utxo, outpoint_1) = test_helper::create_utxo(&mut rng, 10);
     assert!(cache.add_utxo(&outpoint_1, utxo, false).is_ok());
@@ -471,7 +476,7 @@ fn blockchain_or_mempool_utxo_test(#[case] seed: Seed) {
     let (utxo, outpoint_2) = test_helper::create_utxo_for_mempool(&mut rng);
     assert!(cache.add_utxo(&outpoint_2, utxo, false).is_ok());
 
-    let res = cache.utxo(&outpoint_2).expect("should contain utxo");
+    let res = cache.utxo(&outpoint_2).unwrap_infallible().expect("should contain utxo");
     assert!(res.source().is_mempool());
 }
 
@@ -481,7 +486,7 @@ fn blockchain_or_mempool_utxo_test(#[case] seed: Seed) {
 fn multiple_update_utxos_test(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     // let's test `add_utxos`
     let tx = Transaction::new(0x00, vec![], create_tx_outputs(&mut rng, 10), 0x01).unwrap();
@@ -494,7 +499,7 @@ fn multiple_update_utxos_test(#[case] seed: Seed) {
         let id = OutPointSourceId::from(tx.get_id());
         let outpoint = OutPoint::new(id, i as u32);
 
-        let utxo = cache.utxo(&outpoint).expect("utxo should exist");
+        let utxo = cache.utxo(&outpoint).unwrap_infallible().expect("utxo should exist");
         assert_eq!(utxo.output(), x);
     });
 
@@ -524,7 +529,7 @@ fn multiple_update_utxos_test(#[case] seed: Seed) {
 
     // check that the spent utxos should not exist in the cache anymore.
     to_spend.iter().for_each(|input| {
-        assert!(cache.utxo(input.outpoint()).is_none());
+        assert!(cache.utxo(input.outpoint()).unwrap_infallible().is_none());
     });
 }
 
@@ -535,8 +540,8 @@ fn check_best_block_after_flush(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view1 = empty_test_utxos_view(H256::random_using(&mut rng).into());
     let test_view2 = empty_test_utxos_view(H256::random_using(&mut rng).into());
-    let mut cache1 = UtxosCache::new(&test_view1);
-    let cache2 = UtxosCache::new(&test_view2);
+    let mut cache1 = UtxosCache::new(&test_view1).unwrap_infallible();
+    let cache2 = UtxosCache::new(&test_view2).unwrap_infallible();
     assert_ne!(cache1.best_block_hash(), cache2.best_block_hash());
     let expected_hash = cache2.best_block_hash();
     assert!(flush_to_base(cache2, &mut cache1).is_ok());
@@ -549,7 +554,7 @@ fn check_best_block_after_flush(#[case] seed: Seed) {
 fn check_add_utxos_from_block_reward(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     let block_reward = BlockReward::new(create_tx_outputs(&mut rng, 10));
 
@@ -565,7 +570,7 @@ fn check_add_utxos_from_block_reward(#[case] seed: Seed) {
 
     block_reward.outputs().iter().enumerate().for_each(|(i, x)| {
         let outpoint = OutPoint::new(OutPointSourceId::BlockReward(block_id), i as u32);
-        let utxo = cache.utxo(&outpoint).expect("utxo should exist");
+        let utxo = cache.utxo(&outpoint).unwrap_infallible().expect("utxo should exist");
         assert_eq!(utxo.output(), x);
     });
 }
@@ -576,7 +581,7 @@ fn check_add_utxos_from_block_reward(#[case] seed: Seed) {
 fn check_tx_spend_undo_spend(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     // add 1 utxo to the utxo set
     let (utxo, outpoint) = test_helper::create_utxo(&mut rng, 1);
@@ -607,7 +612,7 @@ fn check_tx_spend_undo_spend(#[case] seed: Seed) {
 fn check_burn_spend_undo_spend(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     // add 1 utxo to the utxo set
     let (u, outpoint) = test_helper::create_utxo(&mut rng, 1);
@@ -639,7 +644,7 @@ fn check_burn_spend_undo_spend(#[case] seed: Seed) {
 fn check_pos_reward_spend_undo_spend(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     // add 1 utxo to the utxo set
     let (utxo, outpoint) = test_helper::create_utxo_from_reward(&mut rng, 1);
@@ -697,7 +702,7 @@ fn check_pos_reward_spend_undo_spend(#[case] seed: Seed) {
 fn check_pow_reward_spend_undo_spend(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     let block = Block::new(
         vec![],
@@ -737,7 +742,7 @@ fn check_pow_reward_spend_undo_spend(#[case] seed: Seed) {
 fn check_missing_reward_undo(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     // add 1 utxo to the utxo set
     let (utxo, outpoint) = test_helper::create_utxo_from_reward(&mut rng, 1);
@@ -788,7 +793,7 @@ fn check_missing_reward_undo(#[case] seed: Seed) {
 fn check_burn_output_in_block_reward(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     // add 1 utxo to the utxo set
     let (utxo, outpoint) = test_helper::create_utxo_from_reward(&mut rng, 1);
@@ -835,7 +840,7 @@ fn check_burn_output_in_block_reward(#[case] seed: Seed) {
 fn check_burn_output_indexing(#[case] seed: Seed) {
     let mut rng = make_seedable_rng(seed);
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut cache = UtxosCache::new(&test_view);
+    let mut cache = UtxosCache::new(&test_view).unwrap_infallible();
 
     // add 1 utxo to the utxo set
     let (u, outpoint) = test_helper::create_utxo(&mut rng, 1);
diff --git a/utxo/src/tests/simulation.rs b/utxo/src/tests/simulation.rs
index b8f43f636..2610ff6c7 100644
--- a/utxo/src/tests/simulation.rs
+++ b/utxo/src/tests/simulation.rs
@@ -13,7 +13,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use super::test_helper::{create_utxo, empty_test_utxos_view};
+use std::convert::Infallible;
+
+use super::test_helper::{create_utxo, empty_test_utxos_view, UnwrapInfallible};
 use crate::{ConsumedUtxoCache, FlushableUtxoView, UtxosCache, UtxosView};
 use common::chain::OutPoint;
 use crypto::random::{CryptoRng, Rng};
@@ -35,7 +37,7 @@ fn cache_simulation_test(
     let mut rng = make_seedable_rng(seed);
     let mut result: Vec<OutPoint> = Vec::new();
     let test_view = empty_test_utxos_view(common::primitives::H256::zero().into());
-    let mut base = UtxosCache::new(test_view);
+    let mut base = UtxosCache::new(test_view).unwrap_infallible();
 
     let new_consumed_cache = simulation_step(
         &mut rng,
@@ -48,8 +50,8 @@ fn cache_simulation_test(
     base.batch_write(new_consumed_cache).expect("batch write must succeed");
 
     for outpoint in &result {
-        let has_utxo = base.has_utxo(outpoint);
-        let utxo = base.utxo(outpoint);
+        let has_utxo = base.has_utxo(outpoint).unwrap_infallible();
+        let utxo = base.utxo(outpoint).unwrap_infallible();
         assert_eq!(has_utxo, utxo.is_some());
         if utxo.is_some() {
             assert!(base.has_utxo_in_cache(outpoint));
@@ -65,7 +67,7 @@ fn cache_simulation_test(
 /// 4. Create a child from current cache by calling this function again (recursion)
 /// 5. Flush the child into current cache
 /// 6. Consume the current cache, and return it
-fn simulation_step<P: UtxosView>(
+fn simulation_step<P: UtxosView<Error = Infallible>>(
     rng: &mut (impl Rng + CryptoRng),
     all_outputs: &mut Vec<OutPoint>,
     parent_cache: &UtxosCache<P>,
@@ -77,8 +79,8 @@ fn simulation_step<P: UtxosView>(
     }
 
     // notice that we're using a reference of a reference
-    let parent: &dyn UtxosView = parent_cache;
-    let mut current_cache = UtxosCache::new(&parent);
+    let parent: &dyn UtxosView<Error = Infallible> = parent_cache;
+    let mut current_cache = UtxosCache::new(&parent).unwrap_infallible();
 
     // fill a global list of outputs
     let mut current_cache_outputs =
@@ -105,7 +107,7 @@ fn simulation_step<P: UtxosView>(
 }
 
 // Perform random modification on a cache (add new, spend existing, uncache), tracking the coverage
-fn populate_cache<P: UtxosView>(
+fn populate_cache<P: UtxosView<Error = Infallible>>(
     rng: &mut (impl Rng + CryptoRng),
     cache: &mut UtxosCache<P>,
     iterations_count: usize,
@@ -138,7 +140,7 @@ fn populate_cache<P: UtxosView>(
         };
 
         // spend utxo or add new random one
-        if cache.has_utxo(&outpoint) {
+        if cache.has_utxo(&outpoint).unwrap_infallible() {
             assert!(cache.spend_utxo(&outpoint).is_ok());
             spent_an_entry = true;
         } else if utxo.is_some() {
@@ -162,8 +164,8 @@ fn populate_cache<P: UtxosView>(
         // every 100 iterations check full cache
         if i % 100 == 0 {
             for outpoint in &result {
-                let has_utxo = cache.has_utxo(outpoint);
-                let utxo = cache.utxo(outpoint);
+                let has_utxo = cache.has_utxo(outpoint).unwrap_infallible();
+                let utxo = cache.utxo(outpoint).unwrap_infallible();
                 assert_eq!(has_utxo, utxo.is_some());
                 if utxo.is_some() {
                     assert!(cache.has_utxo_in_cache(outpoint));
diff --git a/utxo/src/tests/simulation_with_undo.rs b/utxo/src/tests/simulation_with_undo.rs
index be5495ee4..914dcec5a 100644
--- a/utxo/src/tests/simulation_with_undo.rs
+++ b/utxo/src/tests/simulation_with_undo.rs
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use super::test_helper::{create_tx_outputs, empty_test_utxos_view};
+use super::test_helper::{create_tx_outputs, empty_test_utxos_view, UnwrapInfallible};
 use crate::{
     ConsumedUtxoCache, FlushableUtxoView, UtxoSource, UtxosCache, UtxosTxUndoWithSources, UtxosView,
 };
@@ -23,7 +23,7 @@ use common::{
 };
 use crypto::random::{CryptoRng, Rng};
 use rstest::rstest;
-use std::collections::BTreeMap;
+use std::{collections::BTreeMap, convert::Infallible};
 use test_utils::random::{make_seedable_rng, Seed};
 
 // Structure to store outpoints of current utxo set and info for undo
@@ -53,7 +53,7 @@ fn cache_simulation_with_undo(
     let mut rng = make_seedable_rng(seed);
     let mut result: ResultWithUndo = Default::default();
     let test_view = empty_test_utxos_view(H256::zero().into());
-    let mut base = UtxosCache::new(test_view);
+    let mut base = UtxosCache::new(test_view).unwrap_infallible();
 
     let new_consumed_cache = simulation_step(
         &mut rng,
@@ -66,8 +66,8 @@ fn cache_simulation_with_undo(
     base.batch_write(new_consumed_cache).expect("batch write must succeed");
 
     for outpoint in &result.utxo_outpoints {
-        let has_utxo = base.has_utxo(outpoint);
-        let utxo = base.utxo(outpoint);
+        let has_utxo = base.has_utxo(outpoint).unwrap_infallible();
+        let utxo = base.utxo(outpoint).unwrap_infallible();
         assert_eq!(has_utxo, utxo.is_some());
         if utxo.is_some() {
             assert!(base.has_utxo_in_cache(outpoint));
@@ -83,7 +83,7 @@ fn cache_simulation_with_undo(
 /// 4. Create a child from current cache by calling this function again (recursion)
 /// 5. Flush the child into current cache
 /// 6. Consume the current cache, and return it
-fn simulation_step<P: UtxosView>(
+fn simulation_step<P: UtxosView<Error = Infallible>>(
     rng: &mut (impl Rng + CryptoRng),
     all_outputs: &mut ResultWithUndo,
     parent_cache: &UtxosCache<P>,
@@ -95,8 +95,8 @@ fn simulation_step<P: UtxosView>(
     }
 
     // notice that we're using a reference of a reference
-    let parent: &dyn UtxosView = parent_cache;
-    let mut current_cache = UtxosCache::new(&parent);
+    let parent: &dyn UtxosView<Error = Infallible> = parent_cache;
+    let mut current_cache = UtxosCache::new(&parent).unwrap_infallible();
 
     // fill a global list of outputs
     let mut current_cache_outputs =
@@ -125,7 +125,7 @@ fn simulation_step<P: UtxosView>(
     Some(consumed_cache)
 }
 
-fn populate_cache_with_undo<P: UtxosView>(
+fn populate_cache_with_undo<P: UtxosView<Error = Infallible>>(
     rng: &mut (impl Rng + CryptoRng),
     cache: &mut UtxosCache<P>,
     iterations_count: usize,
@@ -202,7 +202,7 @@ fn populate_cache_with_undo<P: UtxosView>(
                     .add_utxo(
                         &undo_info.prev_outpoint,
                         undo_info.tx_undo.utxos()[0].clone(),
-                        cache.has_utxo(&undo_info.prev_outpoint),
+                        cache.has_utxo(&undo_info.prev_outpoint).unwrap_infallible(),
                     )
                     .unwrap();
 
@@ -214,8 +214,8 @@ fn populate_cache_with_undo<P: UtxosView>(
         // every 100 iterations check full cache
         if i % 100 == 0 {
             for outpoint in &result.utxo_outpoints {
-                let has_utxo = cache.has_utxo(outpoint);
-                let utxo = cache.utxo(outpoint);
+                let has_utxo = cache.has_utxo(outpoint).unwrap_infallible();
+                let utxo = cache.utxo(outpoint).unwrap_infallible();
                 assert_eq!(has_utxo, utxo.is_some());
             }
         }
diff --git a/utxo/src/tests/test_helper.rs b/utxo/src/tests/test_helper.rs
index ba437763c..d13ad6ff5 100644
--- a/utxo/src/tests/test_helper.rs
+++ b/utxo/src/tests/test_helper.rs
@@ -13,6 +13,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use std::convert::Infallible;
+
 use crate::{
     utxo_entry::{IsDirty, IsFresh, UtxoEntry},
     Utxo, UtxosCache, UtxosView,
@@ -30,21 +32,35 @@ use crypto::{
 };
 use itertools::Itertools;
 
+pub trait UnwrapInfallible {
+    type Output;
+    fn unwrap_infallible(self) -> Self::Output;
+}
+
+impl<T> UnwrapInfallible for Result<T, Infallible> {
+    type Output = T;
+    fn unwrap_infallible(self) -> Self::Output {
+        self.map_or_else(|inf| match inf {}, |x| x)
+    }
+}
+
 struct EmptyUtxosView {
     best_block_hash: Id<GenBlock>,
 }
 
 impl UtxosView for EmptyUtxosView {
-    fn utxo(&self, _outpoint: &OutPoint) -> Option<Utxo> {
-        None
+    type Error = Infallible;
+
+    fn utxo(&self, _outpoint: &OutPoint) -> Result<Option<Utxo>, Infallible> {
+        Ok(None)
     }
 
-    fn has_utxo(&self, _outpoint: &OutPoint) -> bool {
-        false
+    fn has_utxo(&self, _outpoint: &OutPoint) -> Result<bool, Infallible> {
+        Ok(false)
     }
 
-    fn best_block_hash(&self) -> Id<GenBlock> {
-        self.best_block_hash
+    fn best_block_hash(&self) -> Result<Id<GenBlock>, Infallible> {
+        Ok(self.best_block_hash)
     }
 
     fn estimated_size(&self) -> Option<usize> {
@@ -52,7 +68,9 @@ impl UtxosView for EmptyUtxosView {
     }
 }
 
-pub fn empty_test_utxos_view(best_block_hash: Id<GenBlock>) -> Box<dyn UtxosView> {
+pub fn empty_test_utxos_view(
+    best_block_hash: Id<GenBlock>,
+) -> Box<dyn UtxosView<Error = Infallible>> {
     Box::new(EmptyUtxosView { best_block_hash })
 }