Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extra info to remaining transaction value errors #2585

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions zebra-chain/src/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,26 @@ pub enum Error {
},
}

impl Error {
/// Returns the invalid value for this error.
///
/// This value may be an initial input value, partially calculated value,
/// or an overflowing or underflowing value.
pub fn invalid_value(&self) -> i128 {
use Error::*;

match self.clone() {
Constraint { value, .. } => value.into(),
Convert { value, .. } => value,
MultiplicationOverflow {
overflowing_result, ..
} => overflowing_result,
DivideByZero { amount } => amount.into(),
SumOverflow { partial_sum, .. } => partial_sum.into(),
}
}
}

/// Marker type for `Amount` that allows negative values.
///
/// ```
Expand Down
2 changes: 1 addition & 1 deletion zebra-chain/src/orchard/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ impl<'de> serde::Deserialize<'de> for Node {
}

#[allow(dead_code, missing_docs)]
#[derive(Error, Debug, PartialEq, Eq)]
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum NoteCommitmentTreeError {
#[error("The note commitment tree is full")]
FullTree,
Expand Down
2 changes: 1 addition & 1 deletion zebra-chain/src/sapling/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl<'de> serde::Deserialize<'de> for Node {
}

#[allow(dead_code, missing_docs)]
#[derive(Error, Debug, PartialEq, Eq)]
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum NoteCommitmentTreeError {
#[error("The note commitment tree is full")]
FullTree,
Expand Down
45 changes: 39 additions & 6 deletions zebra-state/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use chrono::{DateTime, Utc};
use thiserror::Error;

use zebra_chain::{
block, orchard, sapling, sprout, transparent, work::difficulty::CompactDifficulty,
amount, block, orchard, sapling, sprout, transparent, value_balance::ValueBalanceError,
work::difficulty::CompactDifficulty,
};

use crate::constants::MIN_TRANSPARENT_COINBASE_MATURITY;
Expand Down Expand Up @@ -35,12 +36,12 @@ impl From<BoxError> for CloneError {
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;

/// An error describing the reason a block could not be committed to the state.
#[derive(Debug, Error, PartialEq, Eq)]
#[derive(Debug, Error, Clone, PartialEq, Eq)]
#[error("block is not contextually valid")]
pub struct CommitBlockError(#[from] ValidateContextError);

/// An error describing why a block failed contextual validation.
#[derive(Debug, Error, PartialEq, Eq)]
#[derive(Debug, Error, Clone, PartialEq, Eq)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum ValidateContextError {
Expand Down Expand Up @@ -140,11 +141,43 @@ pub enum ValidateContextError {
in_finalized_state: bool,
},

#[error("remaining value in the transparent transaction value pool MUST be nonnegative: {transaction_hash:?}, in finalized state: {in_finalized_state:?}")]
#[error(
"the remaining value in the transparent transaction value pool MUST be nonnegative: \
{amount_error:?}, {height:?}, index in block: {tx_index_in_block:?}, \
{transaction_hash:?}"
)]
#[non_exhaustive]
InvalidRemainingTransparentValue {
NegativeRemainingTransactionValue {
amount_error: amount::Error,
height: block::Height,
tx_index_in_block: usize,
transaction_hash: zebra_chain::transaction::Hash,
},

#[error(
"error calculating the remaining value in the transaction value pool: \
{amount_error:?}, {height:?}, index in block: {tx_index_in_block:?}, \
{transaction_hash:?}"
)]
#[non_exhaustive]
CalculateRemainingTransactionValue {
amount_error: amount::Error,
height: block::Height,
tx_index_in_block: usize,
transaction_hash: zebra_chain::transaction::Hash,
},

#[error(
"error calculating value balances for the remaining value in the transaction value pool: \
{value_balance_error:?}, {height:?}, index in block: {tx_index_in_block:?}, \
{transaction_hash:?}"
)]
#[non_exhaustive]
CalculateTransactionValueBalances {
value_balance_error: ValueBalanceError,
height: block::Height,
tx_index_in_block: usize,
transaction_hash: zebra_chain::transaction::Hash,
in_finalized_state: bool,
},

#[error("error in Sapling note commitment tree")]
Expand Down
40 changes: 29 additions & 11 deletions zebra-state/src/service/check/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::collections::{HashMap, HashSet};

use zebra_chain::{
block,
amount, block,
transparent::{self, CoinbaseSpendRestriction::*},
};

Expand Down Expand Up @@ -225,8 +225,8 @@ pub fn remaining_transaction_value(
prepared: &PreparedBlock,
utxos: &HashMap<transparent::OutPoint, transparent::Utxo>,
) -> Result<(), ValidateContextError> {
for transaction in prepared.block.transactions.iter() {
// This rule does not apply to coinbase transactions.
for (tx_index_in_block, transaction) in prepared.block.transactions.iter().enumerate() {
// TODO: check coinbase transaction remaining value (#338, #1162)
if transaction.is_coinbase() {
continue;
}
Expand All @@ -236,15 +236,33 @@ pub fn remaining_transaction_value(
match value_balance {
Ok(vb) => match vb.remaining_transaction_value() {
Ok(_) => Ok(()),
Err(_) => Err(ValidateContextError::InvalidRemainingTransparentValue {
transaction_hash: transaction.hash(),
in_finalized_state: false,
}),
Err(amount_error @ amount::Error::Constraint { .. })
if amount_error.invalid_value() < 0 =>
{
Err(ValidateContextError::NegativeRemainingTransactionValue {
amount_error,
height: prepared.height,
tx_index_in_block,
transaction_hash: prepared.transaction_hashes[tx_index_in_block],
})
}
Err(amount_error) => {
Err(ValidateContextError::CalculateRemainingTransactionValue {
amount_error,
height: prepared.height,
tx_index_in_block,
transaction_hash: prepared.transaction_hashes[tx_index_in_block],
})
}
},
Err(_) => Err(ValidateContextError::InvalidRemainingTransparentValue {
transaction_hash: transaction.hash(),
in_finalized_state: false,
}),
Err(value_balance_error) => {
Err(ValidateContextError::CalculateTransactionValueBalances {
value_balance_error,
height: prepared.height,
tx_index_in_block,
transaction_hash: prepared.transaction_hashes[tx_index_in_block],
})
}
}?
}

Expand Down