chik_consensus/
validation_error.rs

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