2020-10-09 01:37:24 -07:00
use std ::sync ::Arc ;
2020-12-02 20:48:34 -08:00
use chrono ::{ DateTime , Utc } ;
2020-10-09 01:37:24 -07:00
use thiserror ::Error ;
2021-07-22 16:40:15 -07:00
use zebra_chain ::{
block , orchard , sapling , sprout , transparent , work ::difficulty ::CompactDifficulty ,
} ;
2020-12-02 20:48:34 -08:00
2021-07-28 21:23:50 -07:00
use crate ::constants ::MIN_TRANSPARENT_COINBASE_MATURITY ;
2020-10-09 01:37:24 -07:00
/// A wrapper for type erased errors that is itself clonable and implements the
/// Error trait
#[ derive(Debug, Error, Clone) ]
#[ error(transparent) ]
pub struct CloneError {
source : Arc < dyn std ::error ::Error + Send + Sync + 'static > ,
}
impl From < CommitBlockError > for CloneError {
fn from ( source : CommitBlockError ) -> Self {
let source = Arc ::new ( source ) ;
Self { source }
}
}
impl From < BoxError > for CloneError {
fn from ( source : BoxError ) -> Self {
let source = Arc ::from ( source ) ;
Self { source }
}
}
/// A boxed [`std::error::Error`].
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.
2021-07-14 05:06:43 -07:00
#[ derive(Debug, Error, PartialEq, Eq) ]
2020-10-09 01:37:24 -07:00
#[ error( " block is not contextually valid " ) ]
pub struct CommitBlockError ( #[ from ] ValidateContextError ) ;
/// An error describing why a block failed contextual validation.
2021-07-14 05:06:43 -07:00
#[ derive(Debug, Error, PartialEq, Eq) ]
2020-10-09 01:37:24 -07:00
#[ non_exhaustive ]
2020-12-02 20:48:34 -08:00
#[ allow(missing_docs) ]
2020-10-09 01:37:24 -07:00
pub enum ValidateContextError {
2020-12-03 15:59:54 -08:00
#[ error( " block height {candidate_height:?} is lower than the current finalized height {finalized_tip_height:?} " ) ]
2020-10-09 01:37:24 -07:00
#[ non_exhaustive ]
2020-12-02 20:48:34 -08:00
OrphanedBlock {
candidate_height : block ::Height ,
finalized_tip_height : block ::Height ,
} ,
2020-11-12 20:26:16 -08:00
2020-12-03 15:59:54 -08:00
#[ error( " block height {candidate_height:?} is not one greater than its parent block's height {parent_height:?} " ) ]
2020-11-12 20:26:16 -08:00
#[ non_exhaustive ]
2020-12-02 20:48:34 -08:00
NonSequentialBlock {
candidate_height : block ::Height ,
parent_height : block ::Height ,
} ,
2020-11-19 00:05:48 -08:00
2020-12-03 15:59:54 -08:00
#[ error( " block time {candidate_time:?} is less than or equal to the median-time-past for the block {median_time_past:?} " ) ]
2020-12-02 20:28:42 -08:00
#[ non_exhaustive ]
2020-12-02 20:48:34 -08:00
TimeTooEarly {
candidate_time : DateTime < Utc > ,
median_time_past : DateTime < Utc > ,
} ,
2020-12-02 20:28:42 -08:00
2020-12-03 15:59:54 -08:00
#[ error( " block time {candidate_time:?} is greater than the median-time-past for the block plus 90 minutes {block_time_max:?} " ) ]
2020-12-02 20:28:42 -08:00
#[ non_exhaustive ]
2020-12-02 20:48:34 -08:00
TimeTooLate {
candidate_time : DateTime < Utc > ,
block_time_max : DateTime < Utc > ,
} ,
2020-12-02 20:28:42 -08:00
2020-12-03 15:59:54 -08:00
#[ error( " block difficulty threshold {difficulty_threshold:?} is not equal to the expected difficulty for the block {expected_difficulty:?} " ) ]
2020-11-19 00:05:48 -08:00
#[ non_exhaustive ]
2020-12-02 20:48:34 -08:00
InvalidDifficultyThreshold {
difficulty_threshold : CompactDifficulty ,
expected_difficulty : CompactDifficulty ,
} ,
2021-07-14 05:06:43 -07:00
2021-07-22 16:40:15 -07:00
#[ error( " transparent double-spend: {outpoint:?} is spent twice in {location:?} " ) ]
#[ non_exhaustive ]
DuplicateTransparentSpend {
outpoint : transparent ::OutPoint ,
location : & 'static str ,
} ,
#[ error( " missing transparent output: possible double-spend of {outpoint:?} in {location:?} " ) ]
#[ non_exhaustive ]
MissingTransparentOutput {
outpoint : transparent ::OutPoint ,
location : & 'static str ,
} ,
#[ error( " out-of-order transparent spend: {outpoint:?} is created by a later transaction in the same block " ) ]
#[ non_exhaustive ]
EarlyTransparentSpend { outpoint : transparent ::OutPoint } ,
2021-07-28 21:23:50 -07:00
#[ error(
" unshielded transparent coinbase spend: {outpoint:?} \
must be spent in a transaction which only has shielded outputs "
) ]
#[ non_exhaustive ]
UnshieldedTransparentCoinbaseSpend { outpoint : transparent ::OutPoint } ,
#[ error(
" immature transparent coinbase spend: \
attempt to spend { outpoint :? } at { spend_height :? } , \
but spends are invalid before { min_spend_height :? } , \
which is { MIN_TRANSPARENT_COINBASE_MATURITY :? } blocks \
after it was created at { created_height :? } "
) ]
#[ non_exhaustive ]
ImmatureTransparentCoinbaseSpend {
outpoint : transparent ::OutPoint ,
spend_height : block ::Height ,
min_spend_height : block ::Height ,
created_height : block ::Height ,
} ,
2021-07-14 05:06:43 -07:00
#[ error( " sprout double-spend: duplicate nullifier: {nullifier:?}, in finalized state: {in_finalized_state:?} " ) ]
#[ non_exhaustive ]
DuplicateSproutNullifier {
nullifier : sprout ::Nullifier ,
in_finalized_state : bool ,
} ,
Reject duplicate Sapling and Orchard nullifiers (#2497)
* Add sapling and orchard duplicate nullifier errors
* Reject duplicate finalized sapling and orchard nullifiers
Reject duplicate sapling and orchard nullifiers in a new block,
when the block is added to a non-finalized chain,
and the duplicate nullifier is already in the finalized state.
* Reject duplicate non-finalized sapling and orchard nullifiers
Reject duplicate sapling and orchard nullifiers in a new block,
when the block is added to a non-finalized chain,
and the duplicate nullifier is in:
* the same shielded data,
* the same transaction,
* the same block, or
* an earlier block in the non-finalized chain.
* Refactor sprout nullifier tests to remove common code
* Add sapling nullifier tests
Test that the state rejects duplicate sapling nullifiers in a new block,
when the block is added to a non-finalized chain,
and the duplicate nullifier is in:
* the same shielded data,
* the same transaction,
* the same block,
* an earlier block in the non-finalized chain, or
* the finalized state.
* Add orchard nullifier tests
Test that the state rejects duplicate orchard nullifiers in a new block,
when the block is added to a non-finalized chain,
and the duplicate nullifier is in:
* the same shielded data,
* the same transaction,
* the same block,
* an earlier block in the non-finalized chain, or
* the finalized state.
* Check for specific nullifiers in the state in tests
* Replace slices with vectors in arguments
* Remove redundant code and variables
* Simplify sapling TransferData tests
Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
* Remove an extra :
* Remove redundant vec!
Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
2021-07-19 17:39:05 -07:00
#[ error( " sapling double-spend: duplicate nullifier: {nullifier:?}, in finalized state: {in_finalized_state:?} " ) ]
#[ non_exhaustive ]
DuplicateSaplingNullifier {
nullifier : sapling ::Nullifier ,
in_finalized_state : bool ,
} ,
#[ error( " orchard double-spend: duplicate nullifier: {nullifier:?}, in finalized state: {in_finalized_state:?} " ) ]
#[ non_exhaustive ]
DuplicateOrchardNullifier {
nullifier : orchard ::Nullifier ,
in_finalized_state : bool ,
} ,
2021-07-28 20:49:36 -07:00
#[ error( " remaining value in the transparent transaction value pool MUST be nonnegative: {transaction_hash:?}, in finalized state: {in_finalized_state:?} " ) ]
#[ non_exhaustive ]
InvalidRemainingTransparentValue {
transaction_hash : zebra_chain ::transaction ::Hash ,
in_finalized_state : bool ,
} ,
2021-07-14 05:06:43 -07:00
}
/// Trait for creating the corresponding duplicate nullifier error from a nullifier.
pub ( crate ) trait DuplicateNullifierError {
/// Returns the corresponding duplicate nullifier error for `self`.
fn duplicate_nullifier_error ( & self , in_finalized_state : bool ) -> ValidateContextError ;
}
impl DuplicateNullifierError for sprout ::Nullifier {
fn duplicate_nullifier_error ( & self , in_finalized_state : bool ) -> ValidateContextError {
ValidateContextError ::DuplicateSproutNullifier {
nullifier : * self ,
in_finalized_state ,
}
}
2020-10-09 01:37:24 -07:00
}
Reject duplicate Sapling and Orchard nullifiers (#2497)
* Add sapling and orchard duplicate nullifier errors
* Reject duplicate finalized sapling and orchard nullifiers
Reject duplicate sapling and orchard nullifiers in a new block,
when the block is added to a non-finalized chain,
and the duplicate nullifier is already in the finalized state.
* Reject duplicate non-finalized sapling and orchard nullifiers
Reject duplicate sapling and orchard nullifiers in a new block,
when the block is added to a non-finalized chain,
and the duplicate nullifier is in:
* the same shielded data,
* the same transaction,
* the same block, or
* an earlier block in the non-finalized chain.
* Refactor sprout nullifier tests to remove common code
* Add sapling nullifier tests
Test that the state rejects duplicate sapling nullifiers in a new block,
when the block is added to a non-finalized chain,
and the duplicate nullifier is in:
* the same shielded data,
* the same transaction,
* the same block,
* an earlier block in the non-finalized chain, or
* the finalized state.
* Add orchard nullifier tests
Test that the state rejects duplicate orchard nullifiers in a new block,
when the block is added to a non-finalized chain,
and the duplicate nullifier is in:
* the same shielded data,
* the same transaction,
* the same block,
* an earlier block in the non-finalized chain, or
* the finalized state.
* Check for specific nullifiers in the state in tests
* Replace slices with vectors in arguments
* Remove redundant code and variables
* Simplify sapling TransferData tests
Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
* Remove an extra :
* Remove redundant vec!
Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
2021-07-19 17:39:05 -07:00
impl DuplicateNullifierError for sapling ::Nullifier {
fn duplicate_nullifier_error ( & self , in_finalized_state : bool ) -> ValidateContextError {
ValidateContextError ::DuplicateSaplingNullifier {
nullifier : * self ,
in_finalized_state ,
}
}
}
impl DuplicateNullifierError for orchard ::Nullifier {
fn duplicate_nullifier_error ( & self , in_finalized_state : bool ) -> ValidateContextError {
ValidateContextError ::DuplicateOrchardNullifier {
nullifier : * self ,
in_finalized_state ,
}
}
}