zebra/zebra-consensus/src/error.rs

177 lines
6.0 KiB
Rust
Raw Normal View History

//! Errors that can occur when checking consensus rules.
//!
//! Each error variant corresponds to a consensus rule, so enumerating
//! all possible verification failures enumerates the consensus rules we
//! implement, and ensures that we don't reject blocks or transactions
//! for a non-enumerated reason.
use thiserror::Error;
use crate::BoxError;
#[derive(Error, Copy, Clone, Debug, PartialEq)]
pub enum SubsidyError {
#[error("no coinbase transaction in block")]
NoCoinbase,
#[error("founders reward output not found")]
FoundersRewardNotFound,
}
#[derive(Error, Clone, Debug, PartialEq)]
pub enum TransactionError {
#[error("first transaction must be coinbase")]
CoinbasePosition,
#[error("coinbase input found in non-coinbase transaction")]
CoinbaseAfterFirst,
#[error("coinbase transaction MUST NOT have any transparent (PrevOut) inputs")]
CoinbaseHasPrevOutInput,
#[error("coinbase transaction MUST NOT have any JoinSplit descriptions")]
CoinbaseHasJoinSplit,
#[error("coinbase transaction MUST NOT have any Spend descriptions")]
CoinbaseHasSpend,
#[error("coinbase transaction MUST NOT have any Output descriptions pre-Heartwood")]
CoinbaseHasOutputPreHeartwood,
#[error("coinbase transaction MUST NOT have the EnableSpendsOrchard flag set")]
CoinbaseHasEnableSpendsOrchard,
#[error("coinbase inputs MUST NOT exist in mempool")]
CoinbaseInMempool,
#[error("coinbase transaction failed subsidy validation")]
Subsidy(#[from] SubsidyError),
#[error("transaction version number MUST be >= 4")]
WrongVersion,
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
#[error("transaction version {0} not supported by the network upgrade {1:?}")]
UnsupportedByNetworkUpgrade(u32, zebra_chain::parameters::NetworkUpgrade),
#[error("must have at least one input: transparent, shielded spend, or joinsplit")]
NoInputs,
#[error("must have at least one output: transparent, shielded output, or joinsplit")]
NoOutputs,
#[error("if there are no Spends or Outputs, the value balance MUST be 0.")]
BadBalance,
2020-10-28 17:22:25 -07:00
#[error("could not verify a transparent script")]
Script(#[from] zebra_script::Error),
#[error("spend description cv and rk MUST NOT be of small order")]
SmallOrder,
// XXX change this when we align groth16 verifier errors with bellman
// and add a from annotation when the error type is more precise
#[error("spend proof MUST be valid given a primary input formed from the other fields except spendAuthSig")]
2020-10-28 17:22:25 -07:00
Groth16,
#[error(
"Sprout joinSplitSig MUST represent a valid signature under joinSplitPubKey of dataToBeSigned"
)]
Ed25519(#[from] zebra_chain::primitives::ed25519::Error),
#[error("Sapling bindingSig MUST represent a valid signature under the transaction binding validating key bvk of SigHash")]
RedJubjub(zebra_chain::primitives::redjubjub::Error),
#[error("Orchard bindingSig MUST represent a valid signature under the transaction binding validating key bvk of SigHash")]
RedPallas(zebra_chain::primitives::redpallas::Error),
// temporary error type until #1186 is fixed
#[error("Downcast from BoxError to redjubjub::Error failed")]
InternalDowncastError(String),
#[error("adding to the sprout pool is disabled after Canopy")]
DisabledAddToSproutPool,
}
impl From<BoxError> for TransactionError {
fn from(mut err: BoxError) -> Self {
// TODO: handle redpallas::Error, ScriptInvalid, InvalidSignature
match err.downcast::<zebra_chain::primitives::redjubjub::Error>() {
Ok(e) => return TransactionError::RedJubjub(*e),
Err(e) => err = e,
}
// buffered transaction verifier service error
match err.downcast::<TransactionError>() {
Ok(e) => return *e,
Err(e) => err = e,
}
TransactionError::InternalDowncastError(format!(
"downcast to known transaction error type failed, original error: {:?}",
err,
))
}
}
impl From<SubsidyError> for BlockError {
fn from(err: SubsidyError) -> BlockError {
BlockError::Transaction(TransactionError::Subsidy(err))
}
}
#[derive(Error, Clone, Debug, PartialEq)]
pub enum BlockError {
#[error("block contains invalid transactions")]
Transaction(#[from] TransactionError),
#[error("block has no transactions")]
NoTransactions,
#[error("block has mismatched merkle root")]
BadMerkleRoot {
actual: zebra_chain::block::merkle::Root,
expected: zebra_chain::block::merkle::Root,
},
#[error("block contains duplicate transactions")]
DuplicateTransaction,
#[error("block {0:?} is already in the chain at depth {1:?}")]
AlreadyInChain(zebra_chain::block::Hash, u32),
#[error("invalid block {0:?}: missing block height")]
MissingHeight(zebra_chain::block::Hash),
#[error("invalid block height {0:?} in {1:?}: greater than the maximum height {2:?}")]
MaxHeight(
zebra_chain::block::Height,
zebra_chain::block::Hash,
zebra_chain::block::Height,
),
#[error("invalid difficulty threshold in block header {0:?} {1:?}")]
InvalidDifficulty(zebra_chain::block::Height, zebra_chain::block::Hash),
2020-10-13 18:35:45 -07:00
#[error("block {0:?} has a difficulty threshold {2:?} that is easier than the {3:?} difficulty limit {4:?}, hash: {1:?}")]
TargetDifficultyLimit(
zebra_chain::block::Height,
zebra_chain::block::Hash,
zebra_chain::work::difficulty::ExpandedDifficulty,
zebra_chain::parameters::Network,
zebra_chain::work::difficulty::ExpandedDifficulty,
),
#[error(
"block {0:?} on {3:?} has a hash {1:?} that is easier than its difficulty threshold {2:?}"
)]
DifficultyFilter(
zebra_chain::block::Height,
zebra_chain::block::Hash,
zebra_chain::work::difficulty::ExpandedDifficulty,
zebra_chain::parameters::Network,
),
#[error("transaction has wrong consensus branch id for block network upgrade")]
WrongTransactionConsensusBranchId,
}