chia_consensus/
validation_error.rs

1use clvmr::allocator::{Allocator, Atom, NodePtr, SExp};
2use clvmr::error::EvalErr;
3use thiserror::Error;
4
5#[cfg(feature = "py-bindings")]
6use pyo3::PyErr;
7
8#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub enum ErrorCode {
10    #[default]
11    Unknown,
12    InvalidBlockSolution,
13    InvalidCoinSolution,
14    DuplicateOutput,
15    DoubleSpend,
16    UnknownUnspent,
17    BadAggregateSignature,
18    WrongPuzzleHash,
19    BadFarmerCoinAmount,
20    InvalidCondition,
21    InvalidConditionOpcode,
22    InvalidParentId,
23    InvalidPuzzleHash,
24    InvalidPublicKey,
25    InvalidMessage,
26    InvalidCoinAmount,
27    InvalidCoinAnnouncement,
28    InvalidPuzzleAnnouncement,
29    AssertMyCoinIdFailed,
30    AssertPuzzleAnnouncementFailed,
31    AssertCoinAnnouncementFailed,
32    AssertHeightRelativeFailed,
33    AssertHeightAbsoluteFailed,
34    AssertSecondsAbsoluteFailed,
35    CoinAmountExceedsMaximum,
36    SexpError,
37    InvalidFeeLowFee,
38    MempoolConflict,
39    MintingCoin,
40    ExtendsUnknownBlock,
41    CoinbaseNotYetSpendable,
42    /// Renamed from "BlockCostExceedsMax" since it's more generic than that.
43    CostExceeded,
44    BadAdditionRoot,
45    BadRemovalRoot,
46    InvalidPospaceHash,
47    InvalidCoinbaseSignature,
48    InvalidPlotSignature,
49    TimestampTooFarInPast,
50    TimestampTooFarInFuture,
51    InvalidTransactionsFilterHash,
52    InvalidPospaceChallenge,
53    InvalidPospace,
54    InvalidHeight,
55    InvalidCoinbaseAmount,
56    InvalidMerkleRoot,
57    InvalidBlockFeeAmount,
58    InvalidWeight,
59    InvalidTotalIters,
60    BlockIsNotFinished,
61    InvalidNumIterations,
62    InvalidPot,
63    InvalidPotChallenge,
64    InvalidTransactionsGeneratorHash,
65    InvalidPoolTarget,
66    InvalidCoinbaseParent,
67    InvalidFeesCoinParent,
68    ReserveFeeConditionFailed,
69    NotBlockButHasData,
70    IsTransactionBlockButNoData,
71    InvalidPrevBlockHash,
72    InvalidTransactionsInfoHash,
73    InvalidFoliageBlockHash,
74    InvalidRewardCoins,
75    InvalidBlockCost,
76    NoEndOfSlotInfo,
77    InvalidPrevChallengeSlotHash,
78    InvalidSubEpochSummaryHash,
79    NoSubEpochSummaryHash,
80    ShouldNotMakeChallengeBlock,
81    ShouldMakeChallengeBlock,
82    InvalidChallengeChainData,
83    InvalidCcEosVdf,
84    InvalidRcEosVdf,
85    InvalidChallengeSlotHashRc,
86    InvalidPriorPointRc,
87    InvalidDeficit,
88    InvalidSubEpochSummary,
89    InvalidPrevSubEpochSummaryHash,
90    InvalidRewardChainHash,
91    InvalidSubEpochOverflow,
92    InvalidNewDifficulty,
93    InvalidNewSubSlotIters,
94    InvalidCcSpVdf,
95    InvalidRcSpVdf,
96    InvalidCcSignature,
97    InvalidRcSignature,
98    CannotMakeCcBlock,
99    InvalidRcSpPrevIp,
100    InvalidRcIpPrevIp,
101    InvalidIsTransactionBlock,
102    InvalidUrsbHash,
103    OldPoolTarget,
104    InvalidPoolSignature,
105    InvalidFoliageBlockPresence,
106    InvalidCcIpVdf,
107    InvalidRcIpVdf,
108    IpShouldBeNone,
109    InvalidRewardBlockHash,
110    InvalidMadeNonOverflowInfusions,
111    NoOverflowsInFirstSubSlotNewEpoch,
112    MempoolNotInitialized,
113    ShouldNotHaveIcc,
114    ShouldHaveIcc,
115    InvalidIccVdf,
116    InvalidIccHashCc,
117    InvalidIccHashRc,
118    InvalidIccEosVdf,
119    InvalidSpIndex,
120    TooManyBlocks,
121    InvalidCcChallenge,
122    InvalidPrefarm,
123    AssertSecondsRelativeFailed,
124    BadCoinbaseSignature,
125    // InitialTransactionFreeze (removed in `chia-blockchain` as well)
126    NoTransactionsWhileSyncing,
127    AlreadyIncludingTransaction,
128    IncompatibleNetworkId,
129    PreSoftForkMaxGeneratorSize,
130    InvalidRequiredIters,
131    TooManyGeneratorRefs,
132    AssertMyParentIdFailed,
133    AssertMyPuzzleHashFailed,
134    AssertMyAmountFailed,
135    GeneratorRuntimeError,
136    InvalidCostResult,
137    InvalidTransactionsGeneratorRefsRoot,
138    FutureGeneratorRefs,
139    GeneratorRefHasNoGenerator,
140    DoubleSpendInFork,
141    InvalidFeeTooCloseToZero,
142    CoinAmountNegative,
143    InternalProtocolError,
144    InvalidSpendBundle,
145    FailedGettingGeneratorMultiprocessing,
146    AssertBeforeSecondsAbsoluteFailed,
147    AssertBeforeSecondsRelativeFailed,
148    AssertBeforeHeightAbsoluteFailed,
149    AssertBeforeHeightRelativeFailed,
150    AssertConcurrentSpendFailed,
151    AssertConcurrentPuzzleFailed,
152    ImpossibleSecondsRelativeConstraints,
153    ImpossibleSecondsAbsoluteConstraints,
154    ImpossibleHeightRelativeConstraints,
155    ImpossibleHeightAbsoluteConstraints,
156    AssertMyBirthSecondsFailed,
157    AssertMyBirthHeightFailed,
158    AssertEphemeralFailed,
159    EphemeralRelativeCondition,
160    InvalidSoftforkCondition,
161    InvalidSoftforkCost,
162    TooManyAnnouncements,
163    InvalidMessageMode,
164    InvalidCoinId,
165    MessageNotSentOrReceived,
166    ComplexGeneratorReceived,
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
170#[error("validation error: {1:?}")]
171pub struct ValidationErr(pub NodePtr, pub ErrorCode);
172
173impl From<EvalErr> for ValidationErr {
174    fn from(v: EvalErr) -> Self {
175        match v {
176            EvalErr::CostExceeded => ValidationErr(v.node_ptr(), ErrorCode::CostExceeded),
177            _ => ValidationErr(v.node_ptr(), ErrorCode::GeneratorRuntimeError),
178        }
179    }
180}
181
182impl From<std::io::Error> for ValidationErr {
183    fn from(_: std::io::Error) -> Self {
184        ValidationErr(NodePtr::NIL, ErrorCode::GeneratorRuntimeError)
185    }
186}
187
188#[cfg(feature = "py-bindings")]
189impl From<ValidationErr> for PyErr {
190    fn from(err: ValidationErr) -> PyErr {
191        pyo3::exceptions::PyValueError::new_err(("ValidationError", u32::from(err.1)))
192    }
193}
194
195// helper functions that fail with ValidationErr
196pub fn first(a: &Allocator, n: NodePtr) -> Result<NodePtr, ValidationErr> {
197    match a.sexp(n) {
198        SExp::Pair(left, _) => Ok(left),
199        SExp::Atom => Err(ValidationErr(n, ErrorCode::InvalidCondition)),
200    }
201}
202
203// from chia-blockchain/chia/util/errors.py
204impl From<ErrorCode> for u32 {
205    fn from(err: ErrorCode) -> u32 {
206        match err {
207            ErrorCode::Unknown => 1,
208            ErrorCode::InvalidBlockSolution => 2,
209            ErrorCode::InvalidCoinSolution => 3,
210            ErrorCode::DuplicateOutput => 4,
211            ErrorCode::DoubleSpend => 5,
212            ErrorCode::UnknownUnspent => 6,
213            ErrorCode::BadAggregateSignature => 7,
214            ErrorCode::WrongPuzzleHash => 8,
215            ErrorCode::BadFarmerCoinAmount => 9,
216            ErrorCode::InvalidCondition
217            | ErrorCode::InvalidConditionOpcode
218            | ErrorCode::InvalidParentId
219            | ErrorCode::InvalidPuzzleHash
220            | ErrorCode::InvalidPublicKey
221            | ErrorCode::InvalidMessage
222            | ErrorCode::InvalidCoinAmount
223            | ErrorCode::InvalidCoinAnnouncement
224            | ErrorCode::InvalidPuzzleAnnouncement => 10,
225            ErrorCode::AssertMyCoinIdFailed => 11,
226            ErrorCode::AssertPuzzleAnnouncementFailed | ErrorCode::AssertCoinAnnouncementFailed => {
227                12
228            }
229            ErrorCode::AssertHeightRelativeFailed => 13,
230            ErrorCode::AssertHeightAbsoluteFailed => 14,
231            ErrorCode::AssertSecondsAbsoluteFailed => 15,
232            ErrorCode::CoinAmountExceedsMaximum => 16,
233            ErrorCode::SexpError => 17,
234            ErrorCode::InvalidFeeLowFee => 18,
235            ErrorCode::MempoolConflict => 19,
236            ErrorCode::MintingCoin => 20,
237            ErrorCode::ExtendsUnknownBlock => 21,
238            ErrorCode::CoinbaseNotYetSpendable => 22,
239            ErrorCode::CostExceeded => 23,
240            ErrorCode::BadAdditionRoot => 24,
241            ErrorCode::BadRemovalRoot => 25,
242            ErrorCode::InvalidPospaceHash => 26,
243            ErrorCode::InvalidCoinbaseSignature => 27,
244            ErrorCode::InvalidPlotSignature => 28,
245            ErrorCode::TimestampTooFarInPast => 29,
246            ErrorCode::TimestampTooFarInFuture => 30,
247            ErrorCode::InvalidTransactionsFilterHash => 31,
248            ErrorCode::InvalidPospaceChallenge => 32,
249            ErrorCode::InvalidPospace => 33,
250            ErrorCode::InvalidHeight => 34,
251            ErrorCode::InvalidCoinbaseAmount => 35,
252            ErrorCode::InvalidMerkleRoot => 36,
253            ErrorCode::InvalidBlockFeeAmount => 37,
254            ErrorCode::InvalidWeight => 38,
255            ErrorCode::InvalidTotalIters => 39,
256            ErrorCode::BlockIsNotFinished => 40,
257            ErrorCode::InvalidNumIterations => 41,
258            ErrorCode::InvalidPot => 42,
259            ErrorCode::InvalidPotChallenge => 43,
260            ErrorCode::InvalidTransactionsGeneratorHash => 44,
261            ErrorCode::InvalidPoolTarget => 45,
262            ErrorCode::InvalidCoinbaseParent => 46,
263            ErrorCode::InvalidFeesCoinParent => 47,
264            ErrorCode::ReserveFeeConditionFailed => 48,
265            ErrorCode::NotBlockButHasData => 49,
266            ErrorCode::IsTransactionBlockButNoData => 50,
267            ErrorCode::InvalidPrevBlockHash => 51,
268            ErrorCode::InvalidTransactionsInfoHash => 52,
269            ErrorCode::InvalidFoliageBlockHash => 53,
270            ErrorCode::InvalidRewardCoins => 54,
271            ErrorCode::InvalidBlockCost => 55,
272            ErrorCode::NoEndOfSlotInfo => 56,
273            ErrorCode::InvalidPrevChallengeSlotHash => 57,
274            ErrorCode::InvalidSubEpochSummaryHash => 58,
275            ErrorCode::NoSubEpochSummaryHash => 59,
276            ErrorCode::ShouldNotMakeChallengeBlock => 60,
277            ErrorCode::ShouldMakeChallengeBlock => 61,
278            ErrorCode::InvalidChallengeChainData => 62,
279            ErrorCode::InvalidCcEosVdf => 65,
280            ErrorCode::InvalidRcEosVdf => 66,
281            ErrorCode::InvalidChallengeSlotHashRc => 67,
282            ErrorCode::InvalidPriorPointRc => 68,
283            ErrorCode::InvalidDeficit => 69,
284            ErrorCode::InvalidSubEpochSummary => 70,
285            ErrorCode::InvalidPrevSubEpochSummaryHash => 71,
286            ErrorCode::InvalidRewardChainHash => 72,
287            ErrorCode::InvalidSubEpochOverflow => 73,
288            ErrorCode::InvalidNewDifficulty => 74,
289            ErrorCode::InvalidNewSubSlotIters => 75,
290            ErrorCode::InvalidCcSpVdf => 76,
291            ErrorCode::InvalidRcSpVdf => 77,
292            ErrorCode::InvalidCcSignature => 78,
293            ErrorCode::InvalidRcSignature => 79,
294            ErrorCode::CannotMakeCcBlock => 80,
295            ErrorCode::InvalidRcSpPrevIp => 81,
296            ErrorCode::InvalidRcIpPrevIp => 82,
297            ErrorCode::InvalidIsTransactionBlock => 83,
298            ErrorCode::InvalidUrsbHash => 84,
299            ErrorCode::OldPoolTarget => 85,
300            ErrorCode::InvalidPoolSignature => 86,
301            ErrorCode::InvalidFoliageBlockPresence => 87,
302            ErrorCode::InvalidCcIpVdf => 88,
303            ErrorCode::InvalidRcIpVdf => 89,
304            ErrorCode::IpShouldBeNone => 90,
305            ErrorCode::InvalidRewardBlockHash => 91,
306            ErrorCode::InvalidMadeNonOverflowInfusions => 92,
307            ErrorCode::NoOverflowsInFirstSubSlotNewEpoch => 93,
308            ErrorCode::MempoolNotInitialized => 94,
309            ErrorCode::ShouldNotHaveIcc => 95,
310            ErrorCode::ShouldHaveIcc => 96,
311            ErrorCode::InvalidIccVdf => 97,
312            ErrorCode::InvalidIccHashCc => 98,
313            ErrorCode::InvalidIccHashRc => 99,
314            ErrorCode::InvalidIccEosVdf => 100,
315            ErrorCode::InvalidSpIndex => 101,
316            ErrorCode::TooManyBlocks => 102,
317            ErrorCode::InvalidCcChallenge => 103,
318            ErrorCode::InvalidPrefarm => 104,
319            ErrorCode::AssertSecondsRelativeFailed => 105,
320            ErrorCode::BadCoinbaseSignature => 106,
321            // ErrorCode::InitialTransactionFreeze => 107 (removed in `chia-blockchain`` as well)
322            ErrorCode::NoTransactionsWhileSyncing => 108,
323            ErrorCode::AlreadyIncludingTransaction => 109,
324            ErrorCode::IncompatibleNetworkId => 110,
325            ErrorCode::PreSoftForkMaxGeneratorSize => 111,
326            ErrorCode::InvalidRequiredIters => 112,
327            ErrorCode::TooManyGeneratorRefs => 113,
328            ErrorCode::AssertMyParentIdFailed => 114,
329            ErrorCode::AssertMyPuzzleHashFailed => 115,
330            ErrorCode::AssertMyAmountFailed => 116,
331            ErrorCode::GeneratorRuntimeError => 117,
332            ErrorCode::InvalidCostResult => 118,
333            ErrorCode::InvalidTransactionsGeneratorRefsRoot => 119,
334            ErrorCode::FutureGeneratorRefs => 120,
335            ErrorCode::GeneratorRefHasNoGenerator => 121,
336            ErrorCode::DoubleSpendInFork => 122,
337            ErrorCode::InvalidFeeTooCloseToZero => 123,
338            ErrorCode::CoinAmountNegative => 124,
339            ErrorCode::InternalProtocolError => 125,
340            ErrorCode::InvalidSpendBundle => 126,
341            ErrorCode::FailedGettingGeneratorMultiprocessing => 127,
342            ErrorCode::AssertBeforeSecondsAbsoluteFailed => 128,
343            ErrorCode::AssertBeforeSecondsRelativeFailed => 129,
344            ErrorCode::AssertBeforeHeightAbsoluteFailed => 130,
345            ErrorCode::AssertBeforeHeightRelativeFailed => 131,
346            ErrorCode::AssertConcurrentSpendFailed => 132,
347            ErrorCode::AssertConcurrentPuzzleFailed => 133,
348            ErrorCode::ImpossibleSecondsRelativeConstraints => 134,
349            ErrorCode::ImpossibleSecondsAbsoluteConstraints => 135,
350            ErrorCode::ImpossibleHeightRelativeConstraints => 136,
351            ErrorCode::ImpossibleHeightAbsoluteConstraints => 137,
352            ErrorCode::AssertMyBirthSecondsFailed => 138,
353            ErrorCode::AssertMyBirthHeightFailed => 139,
354            ErrorCode::AssertEphemeralFailed => 140,
355            ErrorCode::EphemeralRelativeCondition => 141,
356            ErrorCode::InvalidSoftforkCondition => 142,
357            ErrorCode::InvalidSoftforkCost => 143,
358            ErrorCode::TooManyAnnouncements => 144,
359            ErrorCode::InvalidMessageMode => 145,
360            ErrorCode::InvalidCoinId => 146,
361            ErrorCode::MessageNotSentOrReceived => 147,
362            ErrorCode::ComplexGeneratorReceived => 148,
363        }
364    }
365}
366
367pub fn rest(a: &Allocator, n: NodePtr) -> Result<NodePtr, ValidationErr> {
368    match a.sexp(n) {
369        SExp::Pair(_, right) => Ok(right),
370        SExp::Atom => Err(ValidationErr(n, ErrorCode::InvalidCondition)),
371    }
372}
373
374pub fn next(a: &Allocator, n: NodePtr) -> Result<Option<(NodePtr, NodePtr)>, ValidationErr> {
375    match a.sexp(n) {
376        SExp::Pair(left, right) => Ok(Some((left, right))),
377        SExp::Atom => {
378            // this is expected to be a valid list terminator
379            if a.atom_len(n) == 0 {
380                Ok(None)
381            } else {
382                Err(ValidationErr(n, ErrorCode::InvalidCondition))
383            }
384        }
385    }
386}
387
388pub fn atom(a: &Allocator, n: NodePtr, code: ErrorCode) -> Result<Atom<'_>, ValidationErr> {
389    match a.sexp(n) {
390        SExp::Atom => Ok(a.atom(n)),
391        SExp::Pair(..) => Err(ValidationErr(n, code)),
392    }
393}
394
395pub fn check_nil(a: &Allocator, n: NodePtr) -> Result<(), ValidationErr> {
396    if atom(a, n, ErrorCode::InvalidCondition)?.as_ref().is_empty() {
397        Ok(())
398    } else {
399        Err(ValidationErr(n, ErrorCode::InvalidCondition))
400    }
401}