1use std::sync::Arc;
4
5use chrono::{DateTime, Utc};
6use thiserror::Error;
7
8use zebra_chain::{
9 amount::{self, NegativeAllowed, NonNegative},
10 block,
11 history_tree::HistoryTreeError,
12 orchard, sapling, sprout, transaction, transparent,
13 value_balance::{ValueBalance, ValueBalanceError},
14 work::difficulty::CompactDifficulty,
15};
16
17use crate::constants::MIN_TRANSPARENT_COINBASE_MATURITY;
18
19#[derive(Debug, Error, Clone)]
22#[error(transparent)]
23pub struct CloneError {
24 source: Arc<dyn std::error::Error + Send + Sync + 'static>,
25}
26
27impl From<CommitSemanticallyVerifiedError> for CloneError {
28 fn from(source: CommitSemanticallyVerifiedError) -> Self {
29 let source = Arc::new(source);
30 Self { source }
31 }
32}
33
34impl From<BoxError> for CloneError {
35 fn from(source: BoxError) -> Self {
36 let source = Arc::from(source);
37 Self { source }
38 }
39}
40
41pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
43
44#[derive(Debug, Error, PartialEq, Eq)]
46#[error("block is not contextually valid: {}", .0)]
47pub struct CommitSemanticallyVerifiedError(#[from] ValidateContextError);
48
49#[derive(Debug, Error)]
52pub enum ReconsiderError {
53 #[error("Block with hash {0} was not previously invalidated")]
54 MissingInvalidatedBlock(block::Hash),
55
56 #[error("Parent chain not found for block {0}")]
57 ParentChainNotFound(block::Hash),
58
59 #[error("Invalidated blocks list is empty when it should contain at least one block")]
60 InvalidatedBlocksEmpty,
61
62 #[error("{0}")]
63 ValidationError(#[from] ValidateContextError),
64}
65
66#[derive(Debug, Error, Clone, PartialEq, Eq)]
68#[non_exhaustive]
69#[allow(missing_docs)]
70pub enum ValidateContextError {
71 #[error("block hash {block_hash} was previously invalidated")]
72 #[non_exhaustive]
73 BlockPreviouslyInvalidated { block_hash: block::Hash },
74
75 #[error("block parent not found in any chain, or not enough blocks in chain")]
76 #[non_exhaustive]
77 NotReadyToBeCommitted,
78
79 #[error("block height {candidate_height:?} is lower than the current finalized height {finalized_tip_height:?}")]
80 #[non_exhaustive]
81 OrphanedBlock {
82 candidate_height: block::Height,
83 finalized_tip_height: block::Height,
84 },
85
86 #[error("block height {candidate_height:?} is not one greater than its parent block's height {parent_height:?}")]
87 #[non_exhaustive]
88 NonSequentialBlock {
89 candidate_height: block::Height,
90 parent_height: block::Height,
91 },
92
93 #[error("block time {candidate_time:?} is less than or equal to the median-time-past for the block {median_time_past:?}")]
94 #[non_exhaustive]
95 TimeTooEarly {
96 candidate_time: DateTime<Utc>,
97 median_time_past: DateTime<Utc>,
98 },
99
100 #[error("block time {candidate_time:?} is greater than the median-time-past for the block plus 90 minutes {block_time_max:?}")]
101 #[non_exhaustive]
102 TimeTooLate {
103 candidate_time: DateTime<Utc>,
104 block_time_max: DateTime<Utc>,
105 },
106
107 #[error("block difficulty threshold {difficulty_threshold:?} is not equal to the expected difficulty for the block {expected_difficulty:?}")]
108 #[non_exhaustive]
109 InvalidDifficultyThreshold {
110 difficulty_threshold: CompactDifficulty,
111 expected_difficulty: CompactDifficulty,
112 },
113
114 #[error("transparent double-spend: {outpoint:?} is spent twice in {location:?}")]
115 #[non_exhaustive]
116 DuplicateTransparentSpend {
117 outpoint: transparent::OutPoint,
118 location: &'static str,
119 },
120
121 #[error("missing transparent output: possible double-spend of {outpoint:?} in {location:?}")]
122 #[non_exhaustive]
123 MissingTransparentOutput {
124 outpoint: transparent::OutPoint,
125 location: &'static str,
126 },
127
128 #[error("out-of-order transparent spend: {outpoint:?} is created by a later transaction in the same block")]
129 #[non_exhaustive]
130 EarlyTransparentSpend { outpoint: transparent::OutPoint },
131
132 #[error(
133 "unshielded transparent coinbase spend: {outpoint:?} \
134 must be spent in a transaction which only has shielded outputs"
135 )]
136 #[non_exhaustive]
137 UnshieldedTransparentCoinbaseSpend { outpoint: transparent::OutPoint },
138
139 #[error(
140 "immature transparent coinbase spend: \
141 attempt to spend {outpoint:?} at {spend_height:?}, \
142 but spends are invalid before {min_spend_height:?}, \
143 which is {MIN_TRANSPARENT_COINBASE_MATURITY:?} blocks \
144 after it was created at {created_height:?}"
145 )]
146 #[non_exhaustive]
147 ImmatureTransparentCoinbaseSpend {
148 outpoint: transparent::OutPoint,
149 spend_height: block::Height,
150 min_spend_height: block::Height,
151 created_height: block::Height,
152 },
153
154 #[error("sprout double-spend: duplicate nullifier: {nullifier:?}, in finalized state: {in_finalized_state:?}")]
155 #[non_exhaustive]
156 DuplicateSproutNullifier {
157 nullifier: sprout::Nullifier,
158 in_finalized_state: bool,
159 },
160
161 #[error("sapling double-spend: duplicate nullifier: {nullifier:?}, in finalized state: {in_finalized_state:?}")]
162 #[non_exhaustive]
163 DuplicateSaplingNullifier {
164 nullifier: sapling::Nullifier,
165 in_finalized_state: bool,
166 },
167
168 #[error("orchard double-spend: duplicate nullifier: {nullifier:?}, in finalized state: {in_finalized_state:?}")]
169 #[non_exhaustive]
170 DuplicateOrchardNullifier {
171 nullifier: orchard::Nullifier,
172 in_finalized_state: bool,
173 },
174
175 #[error(
176 "the remaining value in the transparent transaction value pool MUST be nonnegative:\n\
177 {amount_error:?},\n\
178 {height:?}, index in block: {tx_index_in_block:?}, {transaction_hash:?}"
179 )]
180 #[non_exhaustive]
181 NegativeRemainingTransactionValue {
182 amount_error: amount::Error,
183 height: block::Height,
184 tx_index_in_block: usize,
185 transaction_hash: transaction::Hash,
186 },
187
188 #[error(
189 "error calculating the remaining value in the transaction value pool:\n\
190 {amount_error:?},\n\
191 {height:?}, index in block: {tx_index_in_block:?}, {transaction_hash:?}"
192 )]
193 #[non_exhaustive]
194 CalculateRemainingTransactionValue {
195 amount_error: amount::Error,
196 height: block::Height,
197 tx_index_in_block: usize,
198 transaction_hash: transaction::Hash,
199 },
200
201 #[error(
202 "error calculating value balances for the remaining value in the transaction value pool:\n\
203 {value_balance_error:?},\n\
204 {height:?}, index in block: {tx_index_in_block:?}, {transaction_hash:?}"
205 )]
206 #[non_exhaustive]
207 CalculateTransactionValueBalances {
208 value_balance_error: ValueBalanceError,
209 height: block::Height,
210 tx_index_in_block: usize,
211 transaction_hash: transaction::Hash,
212 },
213
214 #[error(
215 "error calculating the block chain value pool change:\n\
216 {value_balance_error:?},\n\
217 {height:?}, {block_hash:?},\n\
218 transactions: {transaction_count:?}, spent UTXOs: {spent_utxo_count:?}"
219 )]
220 #[non_exhaustive]
221 CalculateBlockChainValueChange {
222 value_balance_error: ValueBalanceError,
223 height: block::Height,
224 block_hash: block::Hash,
225 transaction_count: usize,
226 spent_utxo_count: usize,
227 },
228
229 #[error(
230 "error adding value balances to the chain value pool:\n\
231 {value_balance_error:?},\n\
232 {chain_value_pools:?},\n\
233 {block_value_pool_change:?},\n\
234 {height:?}"
235 )]
236 #[non_exhaustive]
237 AddValuePool {
238 value_balance_error: ValueBalanceError,
239 chain_value_pools: ValueBalance<NonNegative>,
240 block_value_pool_change: ValueBalance<NegativeAllowed>,
241 height: Option<block::Height>,
242 },
243
244 #[error("error updating a note commitment tree: {0}")]
245 NoteCommitmentTreeError(#[from] zebra_chain::parallel::tree::NoteCommitmentTreeError),
246
247 #[error("error building the history tree: {0}")]
248 HistoryTreeError(#[from] Arc<HistoryTreeError>),
249
250 #[error("block contains an invalid commitment: {0}")]
251 InvalidBlockCommitment(#[from] block::CommitmentError),
252
253 #[error(
254 "unknown Sprout anchor: {anchor:?},\n\
255 {height:?}, index in block: {tx_index_in_block:?}, {transaction_hash:?}"
256 )]
257 #[non_exhaustive]
258 UnknownSproutAnchor {
259 anchor: sprout::tree::Root,
260 height: Option<block::Height>,
261 tx_index_in_block: Option<usize>,
262 transaction_hash: transaction::Hash,
263 },
264
265 #[error(
266 "unknown Sapling anchor: {anchor:?},\n\
267 {height:?}, index in block: {tx_index_in_block:?}, {transaction_hash:?}"
268 )]
269 #[non_exhaustive]
270 UnknownSaplingAnchor {
271 anchor: sapling::tree::Root,
272 height: Option<block::Height>,
273 tx_index_in_block: Option<usize>,
274 transaction_hash: transaction::Hash,
275 },
276
277 #[error(
278 "unknown Orchard anchor: {anchor:?},\n\
279 {height:?}, index in block: {tx_index_in_block:?}, {transaction_hash:?}"
280 )]
281 #[non_exhaustive]
282 UnknownOrchardAnchor {
283 anchor: orchard::tree::Root,
284 height: Option<block::Height>,
285 tx_index_in_block: Option<usize>,
286 transaction_hash: transaction::Hash,
287 },
288}
289
290pub trait DuplicateNullifierError {
292 fn duplicate_nullifier_error(&self, in_finalized_state: bool) -> ValidateContextError;
294}
295
296impl DuplicateNullifierError for sprout::Nullifier {
297 fn duplicate_nullifier_error(&self, in_finalized_state: bool) -> ValidateContextError {
298 ValidateContextError::DuplicateSproutNullifier {
299 nullifier: *self,
300 in_finalized_state,
301 }
302 }
303}
304
305impl DuplicateNullifierError for sapling::Nullifier {
306 fn duplicate_nullifier_error(&self, in_finalized_state: bool) -> ValidateContextError {
307 ValidateContextError::DuplicateSaplingNullifier {
308 nullifier: *self,
309 in_finalized_state,
310 }
311 }
312}
313
314impl DuplicateNullifierError for orchard::Nullifier {
315 fn duplicate_nullifier_error(&self, in_finalized_state: bool) -> ValidateContextError {
316 ValidateContextError::DuplicateOrchardNullifier {
317 nullifier: *self,
318 in_finalized_state,
319 }
320 }
321}