diff --git a/crates/chia-consensus/fuzz/fuzz_targets/fast-forward.rs b/crates/chia-consensus/fuzz/fuzz_targets/fast-forward.rs index 0c887fbd5..c0521a4d4 100644 --- a/crates/chia-consensus/fuzz/fuzz_targets/fast-forward.rs +++ b/crates/chia-consensus/fuzz/fuzz_targets/fast-forward.rs @@ -6,7 +6,7 @@ use chia_consensus::conditions::{ use chia_consensus::consensus_constants::TEST_CONSTANTS; use chia_consensus::fast_forward::fast_forward_singleton; use chia_consensus::spend_visitor::SpendVisitor; -use chia_consensus::validation_error::{ErrorCode, ValidationErr}; +use chia_consensus::error_code::ErrorCode; use chia_protocol::Bytes32; use chia_protocol::Coin; use chia_protocol::CoinSpend; @@ -73,7 +73,7 @@ fn run_puzzle( solution: &[u8], parent_id: &[u8], amount: u64, -) -> core::result::Result { +) -> core::result::Result { let puzzle = node_from_bytes(a, puzzle)?; let solution = node_from_bytes(a, solution)?; @@ -160,14 +160,16 @@ fn test_ff( // fast-forward. It's OK to fail in different ways before and after, as long // as it's one of these failures let discrepancy_errors = [ - ErrorCode::AssertMyParentIdFailed, - ErrorCode::AssertMyCoinIdFailed, + u32::from(ErrorCode::AssertMyParentIdFailed(NodePtr::NIL)), + u32::from(ErrorCode::AssertMyCoinIdFailed(NodePtr::NIL)), ]; match (conditions1, conditions2) { - (Err(ValidationErr(n1, msg1)), Err(ValidationErr(n2, msg2))) => { - if msg1 != msg2 || node_to_bytes(a, n1).unwrap() != node_to_bytes(a, n2).unwrap() { - assert!(discrepancy_errors.contains(&msg1) || discrepancy_errors.contains(&msg2)); + (Err(msg1), Err(msg2)) => { + let code1 = u32::from(msg1); + let code2 = u32::from(msg2); + if code1 != code2 { + assert!(discrepancy_errors.contains(&code1) || discrepancy_errors.contains(&code2)); } } (Ok(conditions1), Ok(conditions2)) => { @@ -204,21 +206,21 @@ fn test_ff( assert_eq!(spend1.create_coin, spend2.create_coin); assert_eq!(spend1.flags, spend2.flags); } - (Ok(conditions1), Err(ValidationErr(_n2, msg2))) => { + (Ok(conditions1), Err(msg2)) => { // if the spend is valid and becomes invalid when // rebased/fast-forwarded, it should at least not be considered // eligible. assert!((conditions1.spends[0].flags & ELIGIBLE_FOR_FF) == 0); - assert!(discrepancy_errors.contains(&msg2)); + assert!(discrepancy_errors.contains(&u32::from(msg2))); } - (Err(ValidationErr(_n1, msg1)), Ok(conditions2)) => { + (Err(msg1), Ok(conditions2)) => { // if the spend is invalid and becomes valid when // rebased/fast-forwarded, it should not be considered // eligible. This is a bit of a far-fetched scenario, but could // happen if there's an ASSERT_MY_COINID that's only valid after the // fast-forward assert!((conditions2.spends[0].flags & ELIGIBLE_FOR_FF) == 0); - assert!(discrepancy_errors.contains(&msg1)); + assert!(discrepancy_errors.contains(&u32::from(msg1))); } } } diff --git a/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs b/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs index f399beb9f..ecd6110f9 100644 --- a/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs +++ b/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs @@ -3,7 +3,7 @@ use chia_bls::Signature; use chia_consensus::allocator::make_allocator; use chia_consensus::consensus_constants::TEST_CONSTANTS; use chia_consensus::run_block_generator::{run_block_generator, run_block_generator2}; -use chia_consensus::validation_error::{ErrorCode, ValidationErr}; +use chia_consensus::error_code::ErrorCode; use clvmr::chia_dialect::LIMIT_HEAP; use libfuzzer_sys::fuzz_target; @@ -36,7 +36,7 @@ fuzz_target!(|data: &[u8]| { #[allow(clippy::match_same_arms)] match (r1, r2) { - (Err(ValidationErr(_, ErrorCode::CostExceeded)), Ok(_)) => { + (Err(ErrorCode::CostExceeded(_)), Ok(_)) => { // Since run_block_generator2 cost less, it's not a problem if the // original generator runs out of cost while the rust implementation // succeeds. This is part of its features. diff --git a/crates/chia-consensus/fuzz/fuzz_targets/sanitize-uint.rs b/crates/chia-consensus/fuzz/fuzz_targets/sanitize-uint.rs index 6b3f16889..430755e2f 100644 --- a/crates/chia-consensus/fuzz/fuzz_targets/sanitize-uint.rs +++ b/crates/chia-consensus/fuzz/fuzz_targets/sanitize-uint.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; use chia_consensus::sanitize_int::{SanitizedUint, sanitize_uint}; -use chia_consensus::validation_error::{ErrorCode, ValidationErr}; +use chia_consensus::error_code::ErrorCode; use clvmr::allocator::Allocator; fuzz_target!(|data: &[u8]| { @@ -21,9 +21,8 @@ fuzz_target!(|data: &[u8]| { Ok(SanitizedUint::PositiveOverflow) => { assert!(data.len() > 8); } - Err(ValidationErr(n, c)) => { + Err(ErrorCode::InvalidCoinAmount(n)) => { assert!(n == atom); - assert!(c == ErrorCode::InvalidCoinAmount); } } @@ -40,9 +39,8 @@ fuzz_target!(|data: &[u8]| { Ok(SanitizedUint::PositiveOverflow) => { assert!(data.len() > 4); } - Err(ValidationErr(n, c)) => { + Err(ErrorCode::InvalidCoinAmount(n)) => { assert!(n == atom); - assert!(c == ErrorCode::InvalidCoinAmount); } } }); diff --git a/crates/chia-consensus/src/additions_and_removals.rs b/crates/chia-consensus/src/additions_and_removals.rs index 8d196a0ca..2ac7b1753 100644 --- a/crates/chia-consensus/src/additions_and_removals.rs +++ b/crates/chia-consensus/src/additions_and_removals.rs @@ -4,7 +4,7 @@ use chia_protocol::Coin; use crate::allocator::make_allocator; use crate::consensus_constants::ConsensusConstants; -use crate::validation_error::{ErrorCode, ValidationErr, atom, first, next, rest}; +use crate::error_code::{ErrorCode, atom, first, next, rest}; use chia_protocol::{Bytes, Bytes32}; use clvm_traits::FromClvm; use clvm_utils::{TreeCache, tree_hash_cached}; @@ -24,7 +24,7 @@ pub fn additions_and_removals, I: IntoIterator Result<(Vec<(Coin, Option)>, Vec<(Bytes32, Coin)>), ValidationErr> +) -> Result<(Vec<(Coin, Option)>, Vec<(Bytes32, Coin)>), ErrorCode> where ::IntoIter: DoubleEndedIterator, { @@ -57,7 +57,7 @@ where iter = rest; let (_parent_id, (puzzle, _rest)) = <(NodePtr, (NodePtr, NodePtr))>::from_clvm(&a, spend) - .map_err(|_| ValidationErr(spend, ErrorCode::InvalidCondition))?; + .map_err(|_| ErrorCode::InvalidCondition(spend))?; cache.visit_tree(&a, puzzle); } @@ -67,7 +67,7 @@ where // process the spend let (parent_id, (puzzle, (amount, (solution, _spend_level_extra)))) = <(Bytes32, (NodePtr, (u64, (NodePtr, NodePtr))))>::from_clvm(&a, spend) - .map_err(|_| ValidationErr(spend, ErrorCode::InvalidCondition))?; + .map_err(|_| ErrorCode::InvalidCondition(spend))?; let Reduction(clvm_cost, mut iter) = run_program(&mut a, &dialect, puzzle, solution, cost_left)?; @@ -100,7 +100,7 @@ where c = rest(&a, c)?; let (puzzle_hash, (amount, hint)) = <(Bytes32, (u64, NodePtr))>::from_clvm(&a, c) - .map_err(|_| ValidationErr(c, ErrorCode::InvalidCondition))?; + .map_err(|_| ErrorCode::InvalidCondition(c))?; let coin = Coin { parent_coin_info: spend_id, diff --git a/crates/chia-consensus/src/check_time_locks.rs b/crates/chia-consensus/src/check_time_locks.rs index 3db2b499f..90a29d757 100644 --- a/crates/chia-consensus/src/check_time_locks.rs +++ b/crates/chia-consensus/src/check_time_locks.rs @@ -1,9 +1,10 @@ use std::collections::HashMap; use crate::owned_conditions::OwnedSpendBundleConditions; -use crate::validation_error::ErrorCode; +use crate::error_code::ErrorCode; use chia_protocol::Bytes32; use chia_protocol::CoinRecord; +use clvmr::allocator::NodePtr; #[cfg(feature = "py-bindings")] use pyo3::PyResult; #[cfg(feature = "py-bindings")] @@ -16,57 +17,57 @@ pub fn check_time_locks( timestamp: u64, ) -> Result<(), ErrorCode> { if prev_transaction_block_height < bundle_conds.height_absolute { - return Err(ErrorCode::AssertHeightAbsoluteFailed); + return Err(ErrorCode::AssertHeightAbsoluteFailed(NodePtr::NIL)); } if timestamp < bundle_conds.seconds_absolute { - return Err(ErrorCode::AssertSecondsAbsoluteFailed); + return Err(ErrorCode::AssertSecondsAbsoluteFailed(NodePtr::NIL)); } if let Some(before_height_absolute) = bundle_conds.before_height_absolute { if prev_transaction_block_height >= before_height_absolute { - return Err(ErrorCode::AssertBeforeHeightAbsoluteFailed); + return Err(ErrorCode::AssertBeforeHeightAbsoluteFailed(NodePtr::NIL)); } } if let Some(before_seconds_absolute) = bundle_conds.before_seconds_absolute { if timestamp >= before_seconds_absolute { - return Err(ErrorCode::AssertBeforeSecondsAbsoluteFailed); + return Err(ErrorCode::AssertBeforeSecondsAbsoluteFailed(NodePtr::NIL)); } } for spend in &bundle_conds.spends { let Some(unspent) = removal_coin_records.get(&Bytes32::from(spend.coin_id)) else { - return Err(ErrorCode::InvalidCoinId); + return Err(ErrorCode::InvalidCoinId(NodePtr::NIL)); }; if let Some(birth_height) = spend.birth_height { if birth_height != unspent.confirmed_block_index { - return Err(ErrorCode::AssertMyBirthHeightFailed); + return Err(ErrorCode::AssertMyBirthHeightFailed(NodePtr::NIL)); } } if let Some(birth_seconds) = spend.birth_seconds { if birth_seconds != unspent.timestamp { - return Err(ErrorCode::AssertMyBirthSecondsFailed); + return Err(ErrorCode::AssertMyBirthSecondsFailed(NodePtr::NIL)); } } if let Some(height_relative) = spend.height_relative { if prev_transaction_block_height < unspent.confirmed_block_index + height_relative { - return Err(ErrorCode::AssertHeightRelativeFailed); + return Err(ErrorCode::AssertHeightRelativeFailed(NodePtr::NIL)); } } if let Some(seconds_relative) = spend.seconds_relative { if timestamp < unspent.timestamp + seconds_relative { - return Err(ErrorCode::AssertSecondsRelativeFailed); + return Err(ErrorCode::AssertSecondsRelativeFailed(NodePtr::NIL)); } } if let Some(before_height_relative) = spend.before_height_relative { if prev_transaction_block_height >= unspent.confirmed_block_index + before_height_relative { - return Err(ErrorCode::AssertBeforeHeightRelativeFailed); + return Err(ErrorCode::AssertBeforeHeightRelativeFailed(NodePtr::NIL)); } } if let Some(before_seconds_relative) = spend.before_seconds_relative { if timestamp >= unspent.timestamp + before_seconds_relative { - return Err(ErrorCode::AssertBeforeSecondsRelativeFailed); + return Err(ErrorCode::AssertBeforeSecondsRelativeFailed(NodePtr::NIL)); } } } @@ -124,7 +125,7 @@ mod tests { OwnedSpendBundleConditions { height_absolute: 11, ..Default::default() }, 10, 0, - Err(ErrorCode::AssertHeightAbsoluteFailed) + Err(ErrorCode::AssertHeightAbsoluteFailed(NodePtr::NIL)) )] #[case::height_absolute_exact( OwnedSpendBundleConditions { height_absolute: 10, ..Default::default() }, @@ -142,7 +143,7 @@ mod tests { OwnedSpendBundleConditions { seconds_absolute: 1001, ..Default::default() }, 0, 1000, - Err(ErrorCode::AssertSecondsAbsoluteFailed) + Err(ErrorCode::AssertSecondsAbsoluteFailed(NodePtr::NIL)) )] #[case::seconds_absolute_exact( OwnedSpendBundleConditions { seconds_absolute: 1000, ..Default::default() }, @@ -166,13 +167,13 @@ mod tests { OwnedSpendBundleConditions { before_height_absolute: Some(10), ..Default::default() }, 10, 0, - Err(ErrorCode::AssertBeforeHeightAbsoluteFailed) + Err(ErrorCode::AssertBeforeHeightAbsoluteFailed(NodePtr::NIL)) )] #[case::before_height_absolute_over( OwnedSpendBundleConditions { before_height_absolute: Some(10), ..Default::default() }, 11, 0, - Err(ErrorCode::AssertBeforeHeightAbsoluteFailed) + Err(ErrorCode::AssertBeforeHeightAbsoluteFailed(NodePtr::NIL)) )] #[case::before_seconds_absolute_under( OwnedSpendBundleConditions { before_seconds_absolute: Some(1000), ..Default::default() }, @@ -184,13 +185,13 @@ mod tests { OwnedSpendBundleConditions { before_seconds_absolute: Some(1000), ..Default::default() }, 0, 1000, - Err(ErrorCode::AssertBeforeSecondsAbsoluteFailed) + Err(ErrorCode::AssertBeforeSecondsAbsoluteFailed(NodePtr::NIL)) )] #[case::before_seconds_absolute_over( OwnedSpendBundleConditions { before_seconds_absolute: Some(1000), ..Default::default() }, 0, 1001, - Err(ErrorCode::AssertBeforeSecondsAbsoluteFailed) + Err(ErrorCode::AssertBeforeSecondsAbsoluteFailed(NodePtr::NIL)) )] fn test_absolute_constraints( #[case] bundle: OwnedSpendBundleConditions, @@ -211,7 +212,7 @@ mod tests { }, 149, // initial height 50 + 99 2000, - Err(ErrorCode::AssertHeightRelativeFailed) + Err(ErrorCode::AssertHeightRelativeFailed(NodePtr::NIL)) )] #[case::height_relative_exact( OwnedSpendConditions { @@ -238,7 +239,7 @@ mod tests { }, 200, 1999, // 1000 + 999 - Err(ErrorCode::AssertSecondsRelativeFailed) + Err(ErrorCode::AssertSecondsRelativeFailed(NodePtr::NIL)) )] #[case::seconds_relative_exact( OwnedSpendConditions { @@ -274,7 +275,7 @@ mod tests { }, 60, // initial height 50 + 10 1000, - Err(ErrorCode::AssertBeforeHeightRelativeFailed) + Err(ErrorCode::AssertBeforeHeightRelativeFailed(NodePtr::NIL)) )] #[case::before_height_relative_over( OwnedSpendConditions { @@ -283,7 +284,7 @@ mod tests { }, 61, // initial height 50 + 11 1000, - Err(ErrorCode::AssertBeforeHeightRelativeFailed) + Err(ErrorCode::AssertBeforeHeightRelativeFailed(NodePtr::NIL)) )] #[case::before_seconds_relative_under( OwnedSpendConditions { @@ -301,7 +302,7 @@ mod tests { }, 100, 2000, // initial time 1000 + 1000 - Err(ErrorCode::AssertBeforeSecondsRelativeFailed) + Err(ErrorCode::AssertBeforeSecondsRelativeFailed(NodePtr::NIL)) )] #[case::before_seconds_relative_over( OwnedSpendConditions { @@ -310,7 +311,7 @@ mod tests { }, 100, 2001, // initial time 1000 + 2001 - Err(ErrorCode::AssertBeforeSecondsRelativeFailed) + Err(ErrorCode::AssertBeforeSecondsRelativeFailed(NodePtr::NIL)) )] fn test_relative_constraints_failures( #[case] spend: OwnedSpendConditions, @@ -349,7 +350,7 @@ mod tests { ..Default::default() }; let result = check_time_locks(&HashMap::new(), &bundle, 0, 0); - assert_eq!(result, Err(ErrorCode::InvalidCoinId)); + assert_eq!(result, Err(ErrorCode::InvalidCoinId(NodePtr::NIL))); } #[rstest] @@ -403,7 +404,10 @@ mod tests { }; let result = check_time_locks(&map, &bundle, 100, 1000); - assert_eq!(result, Err(ErrorCode::AssertMyBirthHeightFailed)); + assert_eq!( + result, + Err(ErrorCode::AssertMyBirthHeightFailed(NodePtr::NIL)) + ); } #[rstest] @@ -427,7 +431,10 @@ mod tests { }; let result = check_time_locks(&map, &bundle, 100, 1000); - assert_eq!(result, Err(ErrorCode::AssertMyBirthSecondsFailed)); + assert_eq!( + result, + Err(ErrorCode::AssertMyBirthSecondsFailed(NodePtr::NIL)) + ); } #[test] @@ -478,7 +485,10 @@ mod tests { // spend_relative_fail should fail first as 59 is below required 61 height let result = check_time_locks(&map, &bundle, 59, 600); - assert_eq!(result, Err(ErrorCode::AssertHeightRelativeFailed)); + assert_eq!( + result, + Err(ErrorCode::AssertHeightRelativeFailed(NodePtr::NIL)) + ); let mut map = HashMap::new(); map.insert(coin_id_1, coin_record_1); @@ -491,6 +501,9 @@ mod tests { // spend_birth_dail should now fail let result = check_time_locks(&map, &bundle, 59, 600); - assert_eq!(result, Err(ErrorCode::AssertMyBirthHeightFailed)); + assert_eq!( + result, + Err(ErrorCode::AssertMyBirthHeightFailed(NodePtr::NIL)) + ); } } diff --git a/crates/chia-consensus/src/condition_sanitizers.rs b/crates/chia-consensus/src/condition_sanitizers.rs index eb986c843..8e0b65d5f 100644 --- a/crates/chia-consensus/src/condition_sanitizers.rs +++ b/crates/chia-consensus/src/condition_sanitizers.rs @@ -1,27 +1,31 @@ use super::sanitize_int::{SanitizedUint, sanitize_uint}; -use super::validation_error::{ErrorCode, ValidationErr, atom}; +use super::error_code::{ErrorCode, atom}; use clvmr::allocator::{Allocator, NodePtr}; pub fn sanitize_hash( a: &Allocator, n: NodePtr, size: usize, - code: ErrorCode, -) -> Result { + code: impl Fn(NodePtr) -> ErrorCode + Copy, +) -> Result { let buf = atom(a, n, code)?; if buf.as_ref().len() == size { Ok(n) } else { - Err(ValidationErr(n, code)) + Err(code(n)) } } -pub fn parse_amount(a: &Allocator, n: NodePtr, code: ErrorCode) -> Result { +pub fn parse_amount( + a: &Allocator, + n: NodePtr, + code: impl Fn(NodePtr) -> ErrorCode + Copy, +) -> Result { // amounts are not allowed to exceed 2^64. i.e. 8 bytes match sanitize_uint(a, n, 8, code)? { SanitizedUint::NegativeOverflow | SanitizedUint::PositiveOverflow => { - Err(ValidationErr(n, code)) + Err(code(n)) } SanitizedUint::Ok(r) => Ok(r), } @@ -30,24 +34,24 @@ pub fn parse_amount(a: &Allocator, n: NodePtr, code: ErrorCode) -> Result Result { + code: impl Fn(NodePtr) -> ErrorCode + Copy, +) -> Result { let buf = atom(a, n, code)?; if buf.as_ref().len() > 1024 { - Err(ValidationErr(n, code)) + Err(code(n)) } else { Ok(n) } } -pub fn sanitize_message_mode(a: &Allocator, node: NodePtr) -> Result { +pub fn sanitize_message_mode(a: &Allocator, node: NodePtr) -> Result { let Some(mode) = a.small_number(node) else { - return Err(ValidationErr(node, ErrorCode::InvalidMessageMode)); + return Err(ErrorCode::InvalidMessageMode(Some(node))); }; // only 6 bits are allowed to be set if (mode & !0b11_1111) != 0 { - return Err(ValidationErr(node, ErrorCode::InvalidMessageMode)); + return Err(ErrorCode::InvalidMessageMode(Some(node))); } Ok(mode) } @@ -82,7 +86,10 @@ fn test_sanitize_mode(#[case] value: i64, #[case] pass: bool) { if pass { assert_eq!(i64::from(ret.unwrap()), value); } else { - assert_eq!(ret.unwrap_err().1, ErrorCode::InvalidMessageMode); + assert!(matches!( + ret.unwrap_err(), + ErrorCode::InvalidMessageMode(Some(_)) + )); } } @@ -105,7 +112,7 @@ fn test_sanitize_hash() { let short_n = a.new_atom(&short).unwrap(); assert_eq!( sanitize_hash(&a, short_n, 32, ErrorCode::InvalidCondition), - Err(ValidationErr(short_n, ErrorCode::InvalidCondition)) + Err(ErrorCode::InvalidCondition(short_n)) ); let valid_n = a.new_atom(&valid).unwrap(); assert_eq!( @@ -115,13 +122,13 @@ fn test_sanitize_hash() { let long_n = a.new_atom(&long).unwrap(); assert_eq!( sanitize_hash(&a, long_n, 32, ErrorCode::InvalidCondition), - Err(ValidationErr(long_n, ErrorCode::InvalidCondition)) + Err(ErrorCode::InvalidCondition(long_n)) ); let pair = a.new_pair(short_n, long_n).unwrap(); assert_eq!( sanitize_hash(&a, pair, 32, ErrorCode::InvalidCondition), - Err(ValidationErr(pair, ErrorCode::InvalidCondition)) + Err(ErrorCode::InvalidCondition(pair)) ); } @@ -139,18 +146,18 @@ fn test_sanitize_announce_msg() { let long_n = a.new_atom(&long).unwrap(); assert_eq!( sanitize_announce_msg(&a, long_n, ErrorCode::InvalidCondition), - Err(ValidationErr(long_n, ErrorCode::InvalidCondition)) + Err(ErrorCode::InvalidCondition(long_n)) ); let pair = a.new_pair(valid_n, long_n).unwrap(); assert_eq!( sanitize_announce_msg(&a, pair, ErrorCode::InvalidCondition), - Err(ValidationErr(pair, ErrorCode::InvalidCondition)) + Err(ErrorCode::InvalidCondition(pair)) ); } #[cfg(test)] -fn amount_tester(buf: &[u8]) -> Result { +fn amount_tester(buf: &[u8]) -> Result { let mut a = Allocator::new(); let n = a.new_atom(buf).unwrap(); @@ -160,46 +167,44 @@ fn amount_tester(buf: &[u8]) -> Result { #[test] fn test_sanitize_amount() { // negative amounts are not allowed - assert_eq!( - amount_tester(&[0x80]).unwrap_err().1, - ErrorCode::InvalidCoinAmount - ); - assert_eq!( - amount_tester(&[0xff]).unwrap_err().1, - ErrorCode::InvalidCoinAmount - ); - assert_eq!( - amount_tester(&[0xff, 0]).unwrap_err().1, - ErrorCode::InvalidCoinAmount - ); + assert!(matches!( + amount_tester(&[0x80]).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); + assert!(matches!( + amount_tester(&[0xff]).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); + assert!(matches!( + amount_tester(&[0xff, 0]).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); // leading zeros are somtimes necessary to make values positive assert_eq!(amount_tester(&[0, 0xff]), Ok(0xff)); // but are disallowed when they are redundant - assert_eq!( - amount_tester(&[0, 0, 0, 0xff]).unwrap_err().1, - ErrorCode::InvalidCoinAmount - ); - assert_eq!( - amount_tester(&[0, 0, 0, 0x80]).unwrap_err().1, - ErrorCode::InvalidCoinAmount - ); - assert_eq!( - amount_tester(&[0, 0, 0, 0x7f]).unwrap_err().1, - ErrorCode::InvalidCoinAmount - ); - assert_eq!( - amount_tester(&[0, 0, 0]).unwrap_err().1, - ErrorCode::InvalidCoinAmount - ); + assert!(matches!( + amount_tester(&[0, 0, 0, 0xff]).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); + assert!(matches!( + amount_tester(&[0, 0, 0, 0x80]).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); + assert!(matches!( + amount_tester(&[0, 0, 0, 0x7f]).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); + assert!(matches!( + amount_tester(&[0, 0, 0]).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); // amounts aren't allowed to be too big - assert_eq!( - amount_tester(&[0x7f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - .unwrap_err() - .1, - ErrorCode::InvalidCoinAmount - ); + assert!(matches!( + amount_tester(&[0x7f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); // this is small enough though assert_eq!( diff --git a/crates/chia-consensus/src/conditions.rs b/crates/chia-consensus/src/conditions.rs index f4eac5ba9..bc838db32 100644 --- a/crates/chia-consensus/src/conditions.rs +++ b/crates/chia-consensus/src/conditions.rs @@ -16,13 +16,13 @@ use super::opcodes::{ parse_opcode, }; use super::sanitize_int::{SanitizedUint, sanitize_uint}; -use super::validation_error::{ErrorCode, ValidationErr, first, next, rest}; +use super::error_code::{ErrorCode, first, next, rest}; use crate::consensus_constants::ConsensusConstants; use crate::flags::{COST_CONDITIONS, DONT_VALIDATE_SIGNATURE, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT}; use crate::make_aggsig_final_message::u64_to_bytes; use crate::messages::{Message, SpendId}; use crate::spend_visitor::SpendVisitor; -use crate::validation_error::check_nil; +use crate::error_code::check_nil; use chia_bls::{BlsCache, PublicKey, Signature, aggregate_verify}; use chia_protocol::{Bytes, Bytes32, Coin}; use chia_sha2::Sha256; @@ -74,7 +74,7 @@ impl SpendVisitor for EmptyVisitor { _a: &Allocator, _state: &ParseState, _bundle: &mut SpendBundleConditions, - ) -> Result<(), ValidationErr> { + ) -> Result<(), ErrorCode> { Ok(()) } } @@ -195,7 +195,7 @@ impl SpendVisitor for MempoolVisitor { a: &Allocator, state: &ParseState, bundle: &mut SpendBundleConditions, - ) -> Result<(), ValidationErr> { + ) -> Result<(), ErrorCode> { // any spend whose output is being spent in the same bundle is not // eligible for fast forward. Spending an output is a form of // committment to the specific coin. @@ -338,7 +338,7 @@ fn check_agg_sig_unsafe_message( a: &Allocator, msg: NodePtr, constants: &ConsensusConstants, -) -> Result<(), ValidationErr> { +) -> Result<(), ErrorCode> { if a.atom_len(msg) < 32 { return Ok(()); } @@ -353,7 +353,7 @@ fn check_agg_sig_unsafe_message( constants.agg_sig_parent_puzzle_additional_data.as_ref(), ] { if buf.as_ref().ends_with(additional_data) { - return Err(ValidationErr(msg, ErrorCode::InvalidMessage)); + return Err(ErrorCode::InvalidMessage(msg)); } } Ok(()) @@ -363,7 +363,7 @@ fn maybe_check_args_terminator( a: &Allocator, arg: NodePtr, flags: u32, -) -> Result<(), ValidationErr> { +) -> Result<(), ErrorCode> { if (flags & STRICT_ARGS_COUNT) != 0 { check_nil(a, rest(a, arg)?)?; } @@ -375,7 +375,7 @@ pub fn parse_args( mut c: NodePtr, op: ConditionOpcode, flags: u32, -) -> Result { +) -> Result { match op { AGG_SIG_UNSAFE | AGG_SIG_ME @@ -413,10 +413,10 @@ pub fn parse_args( let node = first(a, c)?; let amount = match sanitize_uint(a, node, 8, ErrorCode::InvalidCoinAmount)? { SanitizedUint::PositiveOverflow => { - return Err(ValidationErr(node, ErrorCode::CoinAmountExceedsMaximum)); + return Err(ErrorCode::CoinAmountExceedsMaximum(node)); } SanitizedUint::NegativeOverflow => { - return Err(ValidationErr(node, ErrorCode::CoinAmountNegative)); + return Err(ErrorCode::CoinAmountNegative(node)); } SanitizedUint::Ok(amount) => amount, }; @@ -452,13 +452,13 @@ pub fn parse_args( if (flags & NO_UNKNOWN_CONDS) != 0 { // We don't know of any new softforked-in conditions, so they // are all unknown - Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)) + Err(ErrorCode::InvalidConditionOpcode(c)) } else { match sanitize_uint(a, first(a, c)?, 4, ErrorCode::InvalidSoftforkCost)? { // the first argument represents the cost of the condition. // We scale it by 10000 to make the argument be a bit smaller SanitizedUint::Ok(cost) => Ok(Condition::Softfork(cost * 10000)), - _ => Err(ValidationErr(c, ErrorCode::InvalidSoftforkCost)), + _ => Err(ErrorCode::InvalidSoftforkCost(c)), } } } @@ -466,7 +466,7 @@ pub fn parse_args( // All of these conditions are unknown // but they have costs if (flags & NO_UNKNOWN_CONDS) != 0 { - Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)) + Err(ErrorCode::InvalidConditionOpcode(c)) } else { Ok(Condition::Softfork(compute_unknown_condition_cost(op))) } @@ -537,7 +537,7 @@ pub fn parse_args( let code = ErrorCode::AssertMyBirthSecondsFailed; match sanitize_uint(a, node, 8, code)? { SanitizedUint::PositiveOverflow | SanitizedUint::NegativeOverflow => { - Err(ValidationErr(node, code)) + Err(code(node)) } SanitizedUint::Ok(r) => Ok(Condition::AssertMyBirthSeconds(r)), } @@ -548,7 +548,7 @@ pub fn parse_args( let code = ErrorCode::AssertMyBirthHeightFailed; match sanitize_uint(a, node, 4, code)? { SanitizedUint::PositiveOverflow | SanitizedUint::NegativeOverflow => { - Err(ValidationErr(node, code)) + Err(code(node)) } SanitizedUint::Ok(r) => Ok(Condition::AssertMyBirthHeight(r as u32)), } @@ -565,7 +565,7 @@ pub fn parse_args( let node = first(a, c)?; let code = ErrorCode::AssertSecondsRelativeFailed; match sanitize_uint(a, node, 8, code)? { - SanitizedUint::PositiveOverflow => Err(ValidationErr(node, code)), + SanitizedUint::PositiveOverflow => Err(code(node)), SanitizedUint::NegativeOverflow => Ok(Condition::SkipRelativeCondition), SanitizedUint::Ok(r) => Ok(Condition::AssertSecondsRelative(r)), } @@ -575,7 +575,7 @@ pub fn parse_args( let node = first(a, c)?; let code = ErrorCode::AssertSecondsAbsoluteFailed; match sanitize_uint(a, node, 8, code)? { - SanitizedUint::PositiveOverflow => Err(ValidationErr(node, code)), + SanitizedUint::PositiveOverflow => Err(code(node)), SanitizedUint::NegativeOverflow => Ok(Condition::Skip), SanitizedUint::Ok(r) => Ok(Condition::AssertSecondsAbsolute(r)), } @@ -585,7 +585,7 @@ pub fn parse_args( let node = first(a, c)?; let code = ErrorCode::AssertHeightRelativeFailed; match sanitize_uint(a, node, 4, code)? { - SanitizedUint::PositiveOverflow => Err(ValidationErr(node, code)), + SanitizedUint::PositiveOverflow => Err(code(node)), SanitizedUint::NegativeOverflow => Ok(Condition::SkipRelativeCondition), SanitizedUint::Ok(r) => Ok(Condition::AssertHeightRelative(r as u32)), } @@ -595,7 +595,7 @@ pub fn parse_args( let node = first(a, c)?; let code = ErrorCode::AssertHeightAbsoluteFailed; match sanitize_uint(a, node, 4, code)? { - SanitizedUint::PositiveOverflow => Err(ValidationErr(node, code)), + SanitizedUint::PositiveOverflow => Err(code(node)), SanitizedUint::NegativeOverflow => Ok(Condition::Skip), SanitizedUint::Ok(r) => Ok(Condition::AssertHeightAbsolute(r as u32)), } @@ -606,7 +606,7 @@ pub fn parse_args( let code = ErrorCode::AssertBeforeSecondsRelativeFailed; match sanitize_uint(a, node, 8, code)? { SanitizedUint::PositiveOverflow => Ok(Condition::SkipRelativeCondition), - SanitizedUint::NegativeOverflow => Err(ValidationErr(node, code)), + SanitizedUint::NegativeOverflow => Err(code(node)), SanitizedUint::Ok(r) => Ok(Condition::AssertBeforeSecondsRelative(r)), } } @@ -617,7 +617,7 @@ pub fn parse_args( let code = ErrorCode::AssertBeforeSecondsAbsoluteFailed; match sanitize_uint(a, node, 8, code)? { SanitizedUint::PositiveOverflow => Ok(Condition::Skip), - SanitizedUint::NegativeOverflow => Err(ValidationErr(node, code)), + SanitizedUint::NegativeOverflow => Err(code(node)), SanitizedUint::Ok(r) => Ok(Condition::AssertBeforeSecondsAbsolute(r)), } } @@ -627,7 +627,7 @@ pub fn parse_args( let code = ErrorCode::AssertBeforeHeightRelativeFailed; match sanitize_uint(a, node, 4, code)? { SanitizedUint::PositiveOverflow => Ok(Condition::SkipRelativeCondition), - SanitizedUint::NegativeOverflow => Err(ValidationErr(node, code)), + SanitizedUint::NegativeOverflow => Err(code(node)), SanitizedUint::Ok(r) => Ok(Condition::AssertBeforeHeightRelative(r as u32)), } } @@ -637,7 +637,7 @@ pub fn parse_args( let code = ErrorCode::AssertBeforeHeightAbsoluteFailed; match sanitize_uint(a, node, 4, code)? { SanitizedUint::PositiveOverflow => Ok(Condition::Skip), - SanitizedUint::NegativeOverflow => Err(ValidationErr(node, code)), + SanitizedUint::NegativeOverflow => Err(code(node)), SanitizedUint::Ok(r) => Ok(Condition::AssertBeforeHeightAbsolute(r as u32)), } } @@ -678,7 +678,7 @@ pub fn parse_args( // this condition is always true, we always ignore arguments Ok(Condition::Skip) } - _ => Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)), + _ => Err(ErrorCode::InvalidConditionOpcode(c)), } } @@ -910,7 +910,7 @@ pub struct ParseState { pub(crate) fn parse_single_spend( a: &Allocator, mut spend: NodePtr, -) -> Result<(NodePtr, NodePtr, NodePtr, NodePtr), ValidationErr> { +) -> Result<(NodePtr, NodePtr, NodePtr, NodePtr), ErrorCode> { let parent_id = first(a, spend)?; spend = rest(a, spend)?; let puzzle_hash = first(a, spend)?; @@ -935,7 +935,7 @@ pub fn process_single_spend<'a, V: SpendVisitor>( max_cost: &mut Cost, clvm_cost: Cost, constants: &ConsensusConstants, -) -> Result<&'a mut SpendConditions, ValidationErr> { +) -> Result<&'a mut SpendConditions, ErrorCode> { let parent_id = sanitize_hash(a, parent_id, 32, ErrorCode::InvalidParentId)?; let puzzle_hash = sanitize_hash(a, puzzle_hash, 32, ErrorCode::InvalidPuzzleHash)?; let my_amount = parse_amount(a, amount, ErrorCode::InvalidCoinAmount)?; @@ -955,7 +955,7 @@ pub fn process_single_spend<'a, V: SpendVisitor>( { // if this coin ID has already been added to this set, it's a double // spend - return Err(ValidationErr(parent_id, ErrorCode::DoubleSpend)); + return Err(ErrorCode::DoubleSpend(parent_id)); } state.spent_puzzles.insert(puzzle_hash); @@ -988,20 +988,20 @@ fn assert_not_ephemeral(spend_flags: &mut u32, state: &mut ParseState, idx: usiz *spend_flags |= HAS_RELATIVE_CONDITION; } -fn decrement(cnt: &mut u32, n: NodePtr) -> Result<(), ValidationErr> { +fn decrement(cnt: &mut u32, n: NodePtr) -> Result<(), ErrorCode> { if *cnt == 0 { - Err(ValidationErr(n, ErrorCode::TooManyAnnouncements)) + Err(ErrorCode::TooManyAnnouncements(n)) } else { *cnt -= 1; Ok(()) } } -fn to_key(a: &Allocator, pk: NodePtr) -> Result { +fn to_key(a: &Allocator, pk: NodePtr) -> Result { let key = PublicKey::from_bytes(a.atom(pk).as_ref().try_into().expect("internal error")) - .map_err(|_| ValidationErr(pk, ErrorCode::InvalidPublicKey))?; + .map_err(|_| ErrorCode::InvalidPublicKey(pk))?; if key.is_inf() { - Err(ValidationErr(pk, ErrorCode::InvalidPublicKey)) + Err(ErrorCode::InvalidPublicKey(pk)) } else { Ok(key) } @@ -1018,7 +1018,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( max_cost: &mut Cost, constants: &ConsensusConstants, visitor: &mut V, -) -> Result<&'a mut SpendConditions, ValidationErr> { +) -> Result<&'a mut SpendConditions, ErrorCode> { let mut announce_countdown: u32 = 1024; let mut free_condition_countdown: usize = FREE_CONDITIONS; @@ -1028,7 +1028,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( let Some(op) = parse_opcode(a, first(a, c)?, flags) else { // in strict mode we don't allow unknown conditions if (flags & NO_UNKNOWN_CONDS) != 0 { - return Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)); + return Err(ErrorCode::InvalidConditionOpcode(c)); } // in non-strict mode, we just ignore unknown conditions continue; @@ -1039,7 +1039,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( match op { CREATE_COIN => { if *max_cost < CREATE_COIN_COST { - return Err(ValidationErr(c, ErrorCode::CostExceeded)); + return Err(ErrorCode::CostExceeded(Some(c))); } *max_cost -= CREATE_COIN_COST; ret.condition_cost += CREATE_COIN_COST; @@ -1054,7 +1054,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( | AGG_SIG_PARENT_PUZZLE | AGG_SIG_PARENT_AMOUNT => { if *max_cost < AGG_SIG_COST { - return Err(ValidationErr(c, ErrorCode::CostExceeded)); + return Err(ErrorCode::CostExceeded(Some(c))); } *max_cost -= AGG_SIG_COST; ret.condition_cost += AGG_SIG_COST; @@ -1065,7 +1065,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( if (flags & COST_CONDITIONS) != 0 { if free_condition_countdown == 0 { if *max_cost < GENERIC_CONDITION_COST { - return Err(ValidationErr(c, ErrorCode::CostExceeded)); + return Err(ErrorCode::CostExceeded(Some(c))); } *max_cost -= GENERIC_CONDITION_COST; ret.condition_cost += GENERIC_CONDITION_COST; @@ -1083,7 +1083,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( ret.reserve_fee = ret .reserve_fee .checked_add(limit) - .ok_or(ValidationErr(c, ErrorCode::ReserveFeeConditionFailed))?; + .ok_or(ErrorCode::ReserveFeeConditionFailed(c))?; } Condition::CreateCoin(ph, amount, hint) => { let new_coin = NewCoin { @@ -1092,7 +1092,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( hint, }; if !spend.create_coin.insert(new_coin) { - return Err(ValidationErr(c, ErrorCode::DuplicateOutput)); + return Err(ErrorCode::DuplicateOutput(c)); } ret.addition_amount += amount as u128; } @@ -1108,10 +1108,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( // this spend bundle requres to be spent *before* a // timestamp and also *after* a timestamp that's the // same or later. that's impossible. - return Err(ValidationErr( - c, - ErrorCode::ImpossibleSecondsRelativeConstraints, - )); + return Err(ErrorCode::ImpossibleSecondsRelativeConstraints(c)); } } assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); @@ -1132,10 +1129,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( // this spend bundle requres to be spent *before* a // height and also *after* a height that's the // same or later. that's impossible. - return Err(ValidationErr( - c, - ErrorCode::ImpossibleHeightRelativeConstraints, - )); + return Err(ErrorCode::ImpossibleHeightRelativeConstraints(c)); } } assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); @@ -1156,10 +1150,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( // this spend bundle requres to be spent *before* a // timestamp and also *after* a timestamp that's the // same or later. that's impossible. - return Err(ValidationErr( - c, - ErrorCode::ImpossibleSecondsRelativeConstraints, - )); + return Err(ErrorCode::ImpossibleSecondsRelativeConstraints(c)); } } assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); @@ -1184,10 +1175,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( // this spend bundle requres to be spent *before* a // height and also *after* a height that's the // same or later. that's impossible. - return Err(ValidationErr( - c, - ErrorCode::ImpossibleHeightRelativeConstraints, - )); + return Err(ErrorCode::ImpossibleHeightRelativeConstraints(c)); } } assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); @@ -1202,12 +1190,12 @@ pub fn parse_conditions<'a, V: SpendVisitor>( } Condition::AssertMyCoinId(id) => { if a.atom(id).as_ref() != (*spend.coin_id).as_ref() { - return Err(ValidationErr(c, ErrorCode::AssertMyCoinIdFailed)); + return Err(ErrorCode::AssertMyCoinIdFailed(c)); } } Condition::AssertMyAmount(amount) => { if amount != spend.coin_amount { - return Err(ValidationErr(c, ErrorCode::AssertMyAmountFailed)); + return Err(ErrorCode::AssertMyAmountFailed(c)); } } Condition::AssertMyBirthSeconds(s) => { @@ -1215,7 +1203,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( // error if it's different from the new birth assertion. One of // them must be false if spend.birth_seconds.is_some_and(|v| v != s) { - return Err(ValidationErr(c, ErrorCode::AssertMyBirthSecondsFailed)); + return Err(ErrorCode::AssertMyBirthSecondsFailed(c)); } spend.birth_seconds = Some(s); assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); @@ -1225,7 +1213,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( // error if it's different from the new birth assertion. One of // them must be false if spend.birth_height.is_some_and(|v| v != h) { - return Err(ValidationErr(c, ErrorCode::AssertMyBirthHeightFailed)); + return Err(ErrorCode::AssertMyBirthHeightFailed(c)); } spend.birth_height = Some(h); assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); @@ -1235,12 +1223,12 @@ pub fn parse_conditions<'a, V: SpendVisitor>( } Condition::AssertMyParentId(id) => { if a.atom(id).as_ref() != a.atom(spend.parent_id).as_ref() { - return Err(ValidationErr(c, ErrorCode::AssertMyParentIdFailed)); + return Err(ErrorCode::AssertMyParentIdFailed(c)); } } Condition::AssertMyPuzzlehash(hash) => { if a.atom(hash).as_ref() != a.atom(spend.puzzle_hash).as_ref() { - return Err(ValidationErr(c, ErrorCode::AssertMyPuzzleHashFailed)); + return Err(ErrorCode::AssertMyPuzzleHashFailed(c)); } } Condition::CreateCoinAnnouncement(msg) => { @@ -1358,7 +1346,7 @@ pub fn parse_conditions<'a, V: SpendVisitor>( } Condition::Softfork(cost) => { if *max_cost < cost { - return Err(ValidationErr(c, ErrorCode::CostExceeded)); + return Err(ErrorCode::CostExceeded(Some(c))); } *max_cost -= cost; ret.condition_cost += cost; @@ -1450,7 +1438,7 @@ pub fn parse_spends( aggregate_signature: &Signature, bls_cache: Option<&BlsCache>, constants: &ConsensusConstants, -) -> Result { +) -> Result { let mut ret = SpendBundleConditions::default(); let mut state = ParseState::default(); @@ -1496,16 +1484,16 @@ pub fn validate_conditions( state: &ParseState, spends: NodePtr, _flags: u32, -) -> Result<(), ValidationErr> { +) -> Result<(), ErrorCode> { if ret.removal_amount < ret.addition_amount { // The sum of removal amounts must not be less than the sum of addition // amounts - return Err(ValidationErr(spends, ErrorCode::MintingCoin)); + return Err(ErrorCode::MintingCoin(spends)); } if ret.removal_amount - ret.addition_amount < ret.reserve_fee as u128 { // the actual fee is lower than the reserved fee - return Err(ValidationErr(spends, ErrorCode::ReserveFeeConditionFailed)); + return Err(ErrorCode::ReserveFeeConditionFailed(spends)); } if let Some(bh) = ret.before_height_absolute { @@ -1513,10 +1501,7 @@ pub fn validate_conditions( // this spend bundle requres to be spent *before* a // height and also *after* a height that's the // same or later. that's impossible. - return Err(ValidationErr( - spends, - ErrorCode::ImpossibleHeightAbsoluteConstraints, - )); + return Err(ErrorCode::ImpossibleHeightAbsoluteConstraints(spends)); } } @@ -1525,10 +1510,7 @@ pub fn validate_conditions( // this spend bundle requres to be spent *before* a // timestamp and also *after* a timestamp that's the // same or later. that's impossible. - return Err(ValidationErr( - spends, - ErrorCode::ImpossibleSecondsAbsoluteConstraints, - )); + return Err(ErrorCode::ImpossibleSecondsAbsoluteConstraints(spends)); } } @@ -1538,10 +1520,7 @@ pub fn validate_conditions( .spent_coins .contains_key(&Bytes32::try_from(a.atom(*coin_id).as_ref()).unwrap()) { - return Err(ValidationErr( - *coin_id, - ErrorCode::AssertConcurrentSpendFailed, - )); + return Err(ErrorCode::AssertConcurrentSpendFailed(*coin_id)); } } @@ -1556,10 +1535,7 @@ pub fn validate_conditions( for puzzle_assert in &state.assert_concurrent_puzzle { if !spent_phs.contains(&a.atom(*puzzle_assert).as_ref().try_into().unwrap()) { - return Err(ValidationErr( - *puzzle_assert, - ErrorCode::AssertConcurrentPuzzleFailed, - )); + return Err(ErrorCode::AssertConcurrentPuzzleFailed(*puzzle_assert)); } } } @@ -1579,10 +1555,7 @@ pub fn validate_conditions( for coin_assert in &state.assert_coin { if !announcements.contains(&a.atom(*coin_assert).as_ref().try_into().unwrap()) { - return Err(ValidationErr( - *coin_assert, - ErrorCode::AssertCoinAnnouncementFailed, - )); + return Err(ErrorCode::AssertCoinAnnouncementFailed(*coin_assert)); } } } @@ -1590,9 +1563,8 @@ pub fn validate_conditions( for spend_idx in &state.assert_ephemeral { // make sure this coin was created in this block if !is_ephemeral(a, *spend_idx, &state.spent_coins, &ret.spends) { - return Err(ValidationErr( + return Err(ErrorCode::AssertEphemeralFailed( ret.spends[*spend_idx].parent_id, - ErrorCode::AssertEphemeralFailed, )); } } @@ -1602,9 +1574,8 @@ pub fn validate_conditions( // because consensus rules do not allow relative conditions on // ephemeral spends if is_ephemeral(a, *spend_idx, &state.spent_coins, &ret.spends) { - return Err(ValidationErr( + return Err(ErrorCode::EphemeralRelativeCondition( ret.spends[*spend_idx].parent_id, - ErrorCode::EphemeralRelativeCondition, )); } } @@ -1622,10 +1593,7 @@ pub fn validate_conditions( for puzzle_assert in &state.assert_puzzle { if !announcements.contains(&a.atom(*puzzle_assert).as_ref().try_into().unwrap()) { - return Err(ValidationErr( - *puzzle_assert, - ErrorCode::AssertPuzzleAnnouncementFailed, - )); + return Err(ErrorCode::AssertPuzzleAnnouncementFailed(*puzzle_assert)); } } } @@ -1643,10 +1611,7 @@ pub fn validate_conditions( for count in messages.values() { if *count != 0 { - return Err(ValidationErr( - NodePtr::NIL, - ErrorCode::MessageNotSentOrReceived, - )); + return Err(ErrorCode::MessageNotSentOrReceived); } } } @@ -1663,7 +1628,7 @@ pub fn validate_signature( signature: &Signature, flags: u32, bls_cache: Option<&BlsCache>, -) -> Result<(), ValidationErr> { +) -> Result<(), ErrorCode> { if (flags & DONT_VALIDATE_SIGNATURE) != 0 { return Ok(()); } @@ -1673,19 +1638,13 @@ pub fn validate_signature( state.pkm_pairs.iter().map(|(pk, msg)| (pk, msg.as_slice())), signature, ) { - return Err(ValidationErr( - NodePtr::NIL, - ErrorCode::BadAggregateSignature, - )); + return Err(ErrorCode::BadAggregateSignature); } } else if !aggregate_verify( signature, state.pkm_pairs.iter().map(|(pk, msg)| (pk, msg.as_slice())), ) { - return Err(ValidationErr( - NodePtr::NIL, - ErrorCode::BadAggregateSignature, - )); + return Err(ErrorCode::BadAggregateSignature); } Ok(()) } @@ -1930,7 +1889,7 @@ fn cond_test_cb( callback: Callback, signature: &Signature, bls_cache: Option<&BlsCache>, -) -> Result<(Allocator, SpendBundleConditions), ValidationErr> { +) -> Result<(Allocator, SpendBundleConditions), ErrorCode> { let mut a = Allocator::new(); println!("input: {input}"); @@ -1967,7 +1926,7 @@ use crate::flags::MEMPOOL_MODE; use clvm_traits::ToClvm; #[cfg(test)] -fn cond_test(input: &str) -> Result<(Allocator, SpendBundleConditions), ValidationErr> { +fn cond_test(input: &str) -> Result<(Allocator, SpendBundleConditions), ErrorCode> { // by default, run all tests in strict mempool mode cond_test_cb(input, MEMPOOL_MODE, None, &Signature::default(), None) } @@ -1976,7 +1935,7 @@ fn cond_test(input: &str) -> Result<(Allocator, SpendBundleConditions), Validati fn cond_test_flag( input: &str, flags: u32, -) -> Result<(Allocator, SpendBundleConditions), ValidationErr> { +) -> Result<(Allocator, SpendBundleConditions), ErrorCode> { cond_test_cb(input, flags, None, &Signature::default(), None) } @@ -1986,24 +1945,39 @@ fn cond_test_sig( signature: &Signature, bls_cache: Option<&BlsCache>, flags: u32, -) -> Result<(Allocator, SpendBundleConditions), ValidationErr> { +) -> Result<(Allocator, SpendBundleConditions), ErrorCode> { cond_test_cb(input, flags, None, signature, bls_cache) } +#[cfg(test)] +fn err_code(err: ErrorCode) -> u32 { + u32::from(err) +} + +#[cfg(test)] +fn err_code_node(f: fn(NodePtr) -> ErrorCode) -> u32 { + u32::from(f(NodePtr::NIL)) +} + +#[cfg(test)] +fn err_code_opt(f: fn(Option) -> ErrorCode) -> u32 { + u32::from(f(None)) +} + #[test] fn test_invalid_condition_list1() { - assert_eq!( - cond_test("((({h1} ({h2} (123 (8 )))").unwrap_err().1, - ErrorCode::InvalidCondition - ); + assert!(matches!( + cond_test("((({h1} ({h2} (123 (8 )))").unwrap_err(), + ErrorCode::InvalidCondition(_) + )); } #[test] fn test_invalid_condition_list2() { - assert_eq!( - cond_test("((({h1} ({h2} (123 ((8 ))))").unwrap_err().1, - ErrorCode::InvalidCondition - ); + assert!(matches!( + cond_test("((({h1} ({h2} (123 ((8 ))))").unwrap_err(), + ErrorCode::InvalidCondition(_) + )); } #[test] @@ -2028,10 +2002,11 @@ fn test_invalid_condition_args_terminator_mempool() { // ASSERT_SECONDS_RELATIVE // in mempool mode, the argument list must be properly terminated assert_eq!( - cond_test("((({h1} ({h2} (123 (((80 (50 8 ))))") - .unwrap_err() - .1, - ErrorCode::InvalidCondition + err_code( + cond_test("((({h1} ({h2} (123 (((80 (50 8 ))))") + .unwrap_err() + ), + err_code_node(ErrorCode::InvalidCondition) ); } @@ -2054,10 +2029,11 @@ fn test_invalid_condition_list_terminator() { fn test_invalid_condition_list_terminator_mempool() { // ASSERT_SECONDS_RELATIVE assert_eq!( - cond_test("((({h1} ({h2} (123 (((80 (50 8 ))))") - .unwrap_err() - .1, - ErrorCode::InvalidCondition + err_code( + cond_test("((({h1} ({h2} (123 (((80 (50 8 ))))") + .unwrap_err() + ), + err_code_node(ErrorCode::InvalidCondition) ); } @@ -2065,32 +2041,32 @@ fn test_invalid_condition_list_terminator_mempool() { fn test_invalid_condition_short_list_terminator() { // ASSERT_SECONDS_RELATIVE assert_eq!( - cond_test("((({h1} ({h2} (123 (((80 8 ))))").unwrap_err().1, - ErrorCode::InvalidCondition + err_code(cond_test("((({h1} ({h2} (123 (((80 8 ))))").unwrap_err()), + err_code_node(ErrorCode::InvalidCondition) ); } #[test] fn test_invalid_spend_list1() { assert_eq!( - cond_test("(8 )").unwrap_err().1, - ErrorCode::InvalidCondition + err_code(cond_test("(8 )").unwrap_err()), + err_code_node(ErrorCode::InvalidCondition) ); } #[test] fn test_invalid_spend_list2() { assert_eq!( - cond_test("((8 ))").unwrap_err().1, - ErrorCode::InvalidCondition + err_code(cond_test("((8 ))").unwrap_err()), + err_code_node(ErrorCode::InvalidCondition) ); } #[test] fn test_invalid_spend_list_terminator() { assert_eq!( - cond_test("((({h1} ({h2} (123 (()) 8 ))").unwrap_err().1, - ErrorCode::InvalidCondition + err_code(cond_test("((({h1} ({h2} (123 (()) 8 ))").unwrap_err()), + err_code_node(ErrorCode::InvalidCondition) ); } @@ -2142,17 +2118,23 @@ fn test_strict_args_count( if flags == 0 { // two of the cases won't pass, even when garbage at the end is allowed. if condition == ASSERT_COIN_ANNOUNCEMENT { - assert_eq!(ret.unwrap_err().1, ErrorCode::AssertCoinAnnouncementFailed,); + assert_eq!( + err_code(ret.unwrap_err()), + err_code_node(ErrorCode::AssertCoinAnnouncementFailed) + ); } else if condition == ASSERT_PUZZLE_ANNOUNCEMENT { assert_eq!( - ret.unwrap_err().1, - ErrorCode::AssertPuzzleAnnouncementFailed, + err_code(ret.unwrap_err()), + err_code_node(ErrorCode::AssertPuzzleAnnouncementFailed), ); } else { assert!(ret.is_ok()); } } else { - assert_eq!(ret.unwrap_err().1, ErrorCode::InvalidCondition); + assert_eq!( + err_code(ret.unwrap_err()), + err_code_node(ErrorCode::InvalidCondition) + ); } } @@ -2187,7 +2169,10 @@ fn test_message_strict_args_count( if flags == 0 { ret.unwrap(); } else { - assert_eq!(ret.unwrap_err().1, ErrorCode::InvalidCondition); + assert_eq!( + err_code(ret.unwrap_err()), + err_code_node(ErrorCode::InvalidCondition) + ); } } @@ -2239,18 +2224,19 @@ fn test_extra_arg( // extra args are ignored in consensus mode // and a failure in mempool mode assert_eq!( - cond_test_sig( - &format!( - "((({{h1}} ({{h2}} (123 ((({} ({} ( 1337 ) {} ))))", - condition as u8, arg, extra_cond - ), - &signature, - None, - MEMPOOL_MODE, - ) - .unwrap_err() - .1, - ErrorCode::InvalidCondition + err_code( + cond_test_sig( + &format!( + "((({{h1}} ({{h2}} (123 ((({} ({} ( 1337 ) {} ))))", + condition as u8, arg, extra_cond + ), + &signature, + None, + MEMPOOL_MODE, + ) + .unwrap_err() + ), + err_code_node(ErrorCode::InvalidCondition) ); let (a, conds) = cond_test_sig( @@ -2435,16 +2421,17 @@ fn test_single_condition_no_op(#[case] condition: ConditionOpcode, #[case] value fn test_single_condition_failure( #[case] condition: ConditionOpcode, #[case] arg: &str, - #[case] expected_error: ErrorCode, + #[case] expected_error: fn(NodePtr) -> ErrorCode, ) { - let err = cond_test(&format!( - "((({{h1}} ({{h2}} (123 ((({} ({} )))))", - condition as u8, arg - )) - .unwrap_err() - .1; + let err = err_code( + cond_test(&format!( + "((({{h1}} ({{h2}} (123 ((({} ({} )))))", + condition as u8, arg + )) + .unwrap_err(), + ); - assert_eq!(err, expected_error); + assert_eq!(err, err_code_node(expected_error)); } // this test includes multiple instances of the same condition, to ensure we @@ -2515,13 +2502,14 @@ fn test_multiple_conditions( fn test_missing_arg(#[case] condition: ConditionOpcode) { // extra args are disallowed in mempool mode assert_eq!( - cond_test_flag( - &format!("((({{h1}} ({{h2}} (123 ((({} )))))", condition as u8), - 0 - ) - .unwrap_err() - .1, - ErrorCode::InvalidCondition + err_code( + cond_test_flag( + &format!("((({{h1}} ({{h2}} (123 ((({} )))))", condition as u8), + 0 + ) + .unwrap_err() + ), + err_code_node(ErrorCode::InvalidCondition) ); } @@ -2548,10 +2536,11 @@ fn test_reserve_fee_exceed_max() { // reserve fee // "((({h1} ({h2} (123 (((60 ({msg1} ))) (({h2} ({h2} (123 (((61 ({c11} )))))") assert_eq!( - cond_test("((({h1} ({h2} (0x00ffffffffffffffff (((52 (0x00fffffffffffffff0 ))) (({h2} ({h1} (0x00ffffff (((52 (0x10 )))))") - .unwrap_err() - .1, - ErrorCode::ReserveFeeConditionFailed + err_code( + cond_test("((({h1} ({h2} (0x00ffffffffffffffff (((52 (0x00fffffffffffffff0 ))) (({h2} ({h1} (0x00ffffff (((52 (0x10 )))))") + .unwrap_err() + ), + err_code_node(ErrorCode::ReserveFeeConditionFailed) ); } @@ -2560,10 +2549,10 @@ fn test_reserve_fee_insufficient_spends() { // RESERVE_FEE // We spend a coin with amount 123 but reserve fee 124 assert_eq!( - cond_test("((({h1} ({h2} (123 (((52 (124 ) ))))") - .unwrap_err() - .1, - ErrorCode::ReserveFeeConditionFailed + err_code( + cond_test("((({h1} ({h2} (123 (((52 (124 ) ))))").unwrap_err() + ), + err_code_node(ErrorCode::ReserveFeeConditionFailed) ); } @@ -2573,10 +2562,10 @@ fn test_reserve_fee_insufficient_fee() { // We spend a coin with amount 123 and create a coin worth 24 and reserve fee // of 100 (which adds up to 124, i.e. not enough fee) assert_eq!( - cond_test("((({h1} ({h2} (123 (((52 (100 ) ((51 ({h2} (24 )) )))") - .unwrap_err() - .1, - ErrorCode::ReserveFeeConditionFailed + err_code( + cond_test("((({h1} ({h2} (123 (((52 (100 ) ((51 ({h2} (24 )) )))").unwrap_err() + ), + err_code_node(ErrorCode::ReserveFeeConditionFailed) ); } @@ -2616,10 +2605,10 @@ fn test_cross_coin_announces_consume() { fn test_failing_coin_consume() { // ASSERT_COIN_ANNOUNCEMENT assert_eq!( - cond_test("((({h1} ({h2} (123 (((61 ({c11} )))))") - .unwrap_err() - .1, - ErrorCode::AssertCoinAnnouncementFailed + err_code( + cond_test("((({h1} ({h2} (123 (((61 ({c11} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertCoinAnnouncementFailed) ); } @@ -2628,10 +2617,10 @@ fn test_coin_announce_mismatch() { // CREATE_COIN_ANNOUNCEMENT // ASSERT_COIN_ANNOUNCEMENT assert_eq!( - cond_test("((({h1} ({h2} (123 (((60 ({msg1} ) ((61 ({c12} )))))") - .unwrap_err() - .1, - ErrorCode::AssertCoinAnnouncementFailed + err_code( + cond_test("((({h1} ({h2} (123 (((60 ({msg1} ) ((61 ({c12} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertCoinAnnouncementFailed) ); } @@ -2669,10 +2658,10 @@ fn test_cross_coin_puzzle_announces_consume() { fn test_failing_puzzle_consume() { // ASSERT_PUZZLE_ANNOUNCEMENT assert_eq!( - cond_test("((({h1} ({h2} (123 (((63 ({p21} )))))") - .unwrap_err() - .1, - ErrorCode::AssertPuzzleAnnouncementFailed + err_code( + cond_test("((({h1} ({h2} (123 (((63 ({p21} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertPuzzleAnnouncementFailed) ); } @@ -2681,10 +2670,10 @@ fn test_puzzle_announce_mismatch() { // CREATE_PUZZLE_ANNOUNCEMENT // ASSERT_PUZZLE_ANNOUNCEMENT assert_eq!( - cond_test("((({h1} ({h2} (123 (((62 ({msg1} ) ((63 ({p11} )))))") - .unwrap_err() - .1, - ErrorCode::AssertPuzzleAnnouncementFailed + err_code( + cond_test("((({h1} ({h2} (123 (((62 ({msg1} ) ((63 ({p11} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertPuzzleAnnouncementFailed) ); } @@ -2692,10 +2681,10 @@ fn test_puzzle_announce_mismatch() { fn test_single_assert_my_amount_exceed_max() { // ASSERT_MY_AMOUNT assert_eq!( - cond_test("((({h1} ({h2} (123 (((73 (0x010000000000000000 )))))") - .unwrap_err() - .1, - ErrorCode::AssertMyAmountFailed + err_code( + cond_test("((({h1} ({h2} (123 (((73 (0x010000000000000000 )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyAmountFailed) ); } @@ -2704,10 +2693,10 @@ fn test_single_assert_my_amount_overlong() { // ASSERT_MY_AMOUNT // leading zeroes are disallowed assert_eq!( - cond_test_flag("((({h1} ({h2} (123 (((73 (0x0000007b )))))", 0) - .unwrap_err() - .1, - ErrorCode::AssertMyAmountFailed + err_code( + cond_test_flag("((({h1} ({h2} (123 (((73 (0x0000007b )))))", 0).unwrap_err() + ), + err_code_node(ErrorCode::AssertMyAmountFailed) ); } @@ -2716,10 +2705,10 @@ fn test_single_assert_my_amount_overlong_mempool() { // ASSERT_MY_AMOUNT // leading zeroes are disallowed in mempool mode assert_eq!( - cond_test("((({h1} ({h2} (123 (((73 (0x0000007b )))))") - .unwrap_err() - .1, - ErrorCode::AssertMyAmountFailed + err_code( + cond_test("((({h1} ({h2} (123 (((73 (0x0000007b )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyAmountFailed) ); } @@ -2740,10 +2729,10 @@ fn test_multiple_assert_my_amount() { fn test_multiple_failing_assert_my_amount() { // ASSERT_MY_AMOUNT assert_eq!( - cond_test("((({h1} ({h2} (123 (((73 (123 ) ((73 (122 ) ))))") - .unwrap_err() - .1, - ErrorCode::AssertMyAmountFailed + err_code( + cond_test("((({h1} ({h2} (123 (((73 (123 ) ((73 (122 ) ))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyAmountFailed) ); } @@ -2751,10 +2740,10 @@ fn test_multiple_failing_assert_my_amount() { fn test_single_failing_assert_my_amount() { // ASSERT_MY_AMOUNT assert_eq!( - cond_test("((({h1} ({h2} (123 (((73 (124 ) ))))") - .unwrap_err() - .1, - ErrorCode::AssertMyAmountFailed + err_code( + cond_test("((({h1} ({h2} (123 (((73 (124 ) ))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyAmountFailed) ); } @@ -2763,10 +2752,10 @@ fn test_single_assert_my_coin_id_overlong() { // ASSERT_MY_COIN_ID // leading zeros in the coin amount invalid assert_eq!( - cond_test_flag("((({h1} ({h2} (0x0000007b (((70 ({coin12} )))))", 0) - .unwrap_err() - .1, - ErrorCode::InvalidCoinAmount + err_code( + cond_test_flag("((({h1} ({h2} (0x0000007b (((70 ({coin12} )))))", 0).unwrap_err() + ), + err_code_node(ErrorCode::InvalidCoinAmount) ); } @@ -2788,10 +2777,10 @@ fn test_multiple_assert_my_coin_id() { fn test_single_assert_my_coin_id_mismatch() { // ASSERT_MY_COIN_ID assert_eq!( - cond_test("((({h1} ({h2} (123 (((70 ({coin11} )))))") - .unwrap_err() - .1, - ErrorCode::AssertMyCoinIdFailed + err_code( + cond_test("((({h1} ({h2} (123 (((70 ({coin11} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyCoinIdFailed) ); } @@ -2801,10 +2790,10 @@ fn test_multiple_assert_my_coin_id_mismatch() { // ASSERT_MY_AMOUNT // the coin-ID check matches the *other* coin, not itself assert_eq!( - cond_test("((({h1} ({h2} (123 (((60 (123 ))) (({h1} ({h1} (123 (((70 ({coin12} )))))") - .unwrap_err() - .1, - ErrorCode::AssertMyCoinIdFailed + err_code( + cond_test("((({h1} ({h2} (123 (((60 (123 ))) (({h1} ({h1} (123 (((70 ({coin12} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyCoinIdFailed) ); } @@ -2825,10 +2814,10 @@ fn test_multiple_assert_my_parent_coin_id() { fn test_single_assert_my_parent_coin_id_mismatch() { // ASSERT_MY_PARENT_ID assert_eq!( - cond_test("((({h1} ({h2} (123 (((71 ({h2} )))))") - .unwrap_err() - .1, - ErrorCode::AssertMyParentIdFailed + err_code( + cond_test("((({h1} ({h2} (123 (((71 ({h2} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyParentIdFailed) ); } @@ -2837,10 +2826,10 @@ fn test_single_invalid_assert_my_parent_coin_id() { // ASSERT_MY_PARENT_ID // the parent ID in the condition is 33 bytes long assert_eq!( - cond_test("((({h1} ({h2} (123 (((71 ({long} )))))") - .unwrap_err() - .1, - ErrorCode::AssertMyParentIdFailed + err_code( + cond_test("((({h1} ({h2} (123 (((71 ({long} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyParentIdFailed) ); } @@ -2863,10 +2852,10 @@ fn test_multiple_assert_my_puzzle_hash() { fn test_single_assert_my_puzzle_hash_mismatch() { // ASSERT_MY_PUZZLEHASH assert_eq!( - cond_test("((({h1} ({h2} (123 (((72 ({h1} )))))") - .unwrap_err() - .1, - ErrorCode::AssertMyPuzzleHashFailed + err_code( + cond_test("((({h1} ({h2} (123 (((72 ({h1} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyPuzzleHashFailed) ); } @@ -2875,10 +2864,10 @@ fn test_single_invalid_assert_my_puzzle_hash() { // ASSERT_MY_PUZZLEHASH // the parent ID in the condition is 33 bytes long assert_eq!( - cond_test("((({h1} ({h2} (123 (((72 ({long} )))))") - .unwrap_err() - .1, - ErrorCode::AssertMyPuzzleHashFailed + err_code( + cond_test("((({h1} ({h2} (123 (((72 ({long} )))))").unwrap_err() + ), + err_code_node(ErrorCode::AssertMyPuzzleHashFailed) ); } @@ -2931,10 +2920,10 @@ fn test_minting_coin() { // CREATE_COIN // we spend a coin with value 123 but create a coin with value 124 assert_eq!( - cond_test("((({h1} ({h2} (123 (((51 ({h2} (124 )))))") - .unwrap_err() - .1, - ErrorCode::MintingCoin + err_code( + cond_test("((({h1} ({h2} (123 (((51 ({h2} (124 )))))").unwrap_err() + ), + err_code_node(ErrorCode::MintingCoin) ); } @@ -2942,10 +2931,10 @@ fn test_minting_coin() { fn test_create_coin_amount_exceeds_max() { // CREATE_COIN assert_eq!( - cond_test("((({h1} ({h2} (123 (((51 ({h2} (0x010000000000000000 )))))") - .unwrap_err() - .1, - ErrorCode::CoinAmountExceedsMaximum + err_code( + cond_test("((({h1} ({h2} (123 (((51 ({h2} (0x010000000000000000 )))))").unwrap_err() + ), + err_code_node(ErrorCode::CoinAmountExceedsMaximum) ); } @@ -2953,10 +2942,10 @@ fn test_create_coin_amount_exceeds_max() { fn test_create_coin_negative_amount() { // CREATE_COIN assert_eq!( - cond_test("((({h1} ({h2} (123 (((51 ({h2} (-1 )))))") - .unwrap_err() - .1, - ErrorCode::CoinAmountNegative + err_code( + cond_test("((({h1} ({h2} (123 (((51 ({h2} (-1 )))))").unwrap_err() + ), + err_code_node(ErrorCode::CoinAmountNegative) ); } @@ -2964,10 +2953,10 @@ fn test_create_coin_negative_amount() { fn test_create_coin_invalid_puzzlehash() { // CREATE_COIN assert_eq!( - cond_test("((({h1} ({h2} (123 (((51 ({long} (42 )))))") - .unwrap_err() - .1, - ErrorCode::InvalidPuzzleHash + err_code( + cond_test("((({h1} ({h2} (123 (((51 ({long} (42 )))))").unwrap_err() + ), + err_code_node(ErrorCode::InvalidPuzzleHash) ); } @@ -3084,10 +3073,10 @@ fn test_create_coin_with_invalid_hint_as_terminator_mempool() { // CREATE_COIN // in mempool mode it's not OK to have an invalid terminator assert_eq!( - cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 {h1}))))") - .unwrap_err() - .1, - ErrorCode::InvalidCondition + err_code( + cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 {h1}))))").unwrap_err() + ), + err_code_node(ErrorCode::InvalidCondition) ); } @@ -3213,29 +3202,30 @@ fn test_create_coin_exceed_cost() { // CREATE_COIN // ensure that we terminate parsing conditions once they exceed the max cost assert_eq!( - cond_test_cb( - "((({h1} ({h2} (123 ({} )))", - 0, - Some(Box::new(|a: &mut Allocator| -> NodePtr { - let mut rest: NodePtr = a.nil(); - - for i in 0..6500 { - // this builds one CREATE_COIN condition - let coin = (CREATE_COIN, (Bytes32::from(H2), (i, 0))) - .to_clvm(a) - .unwrap(); - - // add the CREATE_COIN condition to the list (called rest) - rest = a.new_pair(coin, rest).unwrap(); - } - rest - })), - &Signature::default(), - None, - ) - .unwrap_err() - .1, - ErrorCode::CostExceeded + err_code( + cond_test_cb( + "((({h1} ({h2} (123 ({} )))", + 0, + Some(Box::new(|a: &mut Allocator| -> NodePtr { + let mut rest: NodePtr = a.nil(); + + for i in 0..6500 { + // this builds one CREATE_COIN condition + let coin = (CREATE_COIN, (Bytes32::from(H2), (i, 0))) + .to_clvm(a) + .unwrap(); + + // add the CREATE_COIN condition to the list (called rest) + rest = a.new_pair(coin, rest).unwrap(); + } + rest + })), + &Signature::default(), + None, + ) + .unwrap_err() + ), + err_code_opt(ErrorCode::CostExceeded) ); } @@ -3243,10 +3233,11 @@ fn test_create_coin_exceed_cost() { fn test_duplicate_create_coin() { // CREATE_COIN assert_eq!( - cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 ) ((51 ({h2} (42 ) ))))") - .unwrap_err() - .1, - ErrorCode::DuplicateOutput + err_code( + cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 ) ((51 ({h2} (42 ) ))))") + .unwrap_err() + ), + err_code_node(ErrorCode::DuplicateOutput) ); } @@ -3254,10 +3245,10 @@ fn test_duplicate_create_coin() { fn test_duplicate_create_coin_with_hint() { // CREATE_COIN assert_eq!( - cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 (({h1})) ((51 ({h2} (42 ) ))))") - .unwrap_err() - .1, - ErrorCode::DuplicateOutput + err_code( + cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 (({h1})) ((51 ({h2} (42 ) ))))").unwrap_err() + ), + err_code_node(ErrorCode::DuplicateOutput) ); } @@ -3368,16 +3359,17 @@ fn test_agg_sig_invalid_pubkey( #[values(MEMPOOL_MODE, 0)] mempool: u32, ) { assert_eq!( - cond_test_flag( - &format!( - "((({{h1}} ({{h2}} (123 ((({} ({{h2}} ({{msg1}} )))))", - condition as u8 - ), - mempool | DONT_VALIDATE_SIGNATURE - ) - .unwrap_err() - .1, - ErrorCode::InvalidPublicKey + err_code( + cond_test_flag( + &format!( + "((({{h1}} ({{h2}} (123 ((({} ({{h2}} ({{msg1}} )))))", + condition as u8 + ), + mempool | DONT_VALIDATE_SIGNATURE + ) + .unwrap_err() + ), + err_code_node(ErrorCode::InvalidPublicKey) ); } @@ -3403,7 +3395,10 @@ fn test_agg_sig_infinity_pubkey( mempool, ); - assert_eq!(ret.unwrap_err().1, ErrorCode::InvalidPublicKey); + assert_eq!( + err_code(ret.unwrap_err()), + err_code_node(ErrorCode::InvalidPublicKey) + ); } #[cfg(test)] @@ -3420,16 +3415,17 @@ fn test_agg_sig_invalid_msg( #[values(MEMPOOL_MODE, 0)] mempool: u32, ) { assert_eq!( - cond_test_flag( - &format!( - "((({{h1}} ({{h2}} (123 ((({} ({{pubkey}} ({{longmsg}} )))))", - condition as u8 - ), - mempool - ) - .unwrap_err() - .1, - ErrorCode::InvalidMessage + err_code( + cond_test_flag( + &format!( + "((({{h1}} ({{h2}} (123 ((({} ({{pubkey}} ({{longmsg}} )))))", + condition as u8 + ), + mempool + ) + .unwrap_err() + ), + err_code_node(ErrorCode::InvalidMessage) ); } @@ -3445,32 +3441,33 @@ fn test_agg_sig_invalid_msg( fn test_agg_sig_exceed_cost(#[case] condition: ConditionOpcode) { // ensure that we terminate parsing conditions once they exceed the max cost assert_eq!( - cond_test_cb( - "((({h1} ({h2} (123 ({} )))", - 0, - Some(Box::new(move |a: &mut Allocator| -> NodePtr { - let mut rest: NodePtr = a.nil(); - - for _i in 0..9167 { - // this builds one AGG_SIG_* condition - let aggsig = ( - condition, - (Bytes48::from(PUBKEY), (Bytes::from(MSG1.as_slice()), 0)), - ) - .to_clvm(a) - .unwrap(); - - // add the condition to the list (called rest) - rest = a.new_pair(aggsig, rest).unwrap(); - } - rest - })), - &Signature::default(), - None, - ) - .unwrap_err() - .1, - ErrorCode::CostExceeded + err_code( + cond_test_cb( + "((({h1} ({h2} (123 ({} )))", + 0, + Some(Box::new(move |a: &mut Allocator| -> NodePtr { + let mut rest: NodePtr = a.nil(); + + for _i in 0..9167 { + // this builds one AGG_SIG_* condition + let aggsig = ( + condition, + (Bytes48::from(PUBKEY), (Bytes::from(MSG1.as_slice()), 0)), + ) + .to_clvm(a) + .unwrap(); + + // add the condition to the list (called rest) + rest = a.new_pair(aggsig, rest).unwrap(); + } + rest + })), + &Signature::default(), + None, + ) + .unwrap_err() + ), + err_code_opt(ErrorCode::CostExceeded) ); } @@ -3537,16 +3534,17 @@ fn test_agg_sig_extra_arg(#[case] condition: ConditionOpcode) { // but not in mempool mode assert_eq!( - cond_test_flag( - &format!( - "((({{h1}} ({{h2}} (123 ((({} ({{pubkey}} ({{msg1}} ( 1337 ) ))))", - condition as u8 - ), - MEMPOOL_MODE, - ) - .unwrap_err() - .1, - ErrorCode::InvalidCondition + err_code( + cond_test_flag( + &format!( + "((({{h1}} ({{h2}} (123 ((({} ({{pubkey}} ({{msg1}} ( 1337 ) ))))", + condition as u8 + ), + MEMPOOL_MODE, + ) + .unwrap_err() + ), + err_code_node(ErrorCode::InvalidCondition) ); } @@ -3640,10 +3638,10 @@ fn test_duplicate_agg_sig_unsafe() { fn test_agg_sig_unsafe_invalid_pubkey() { // AGG_SIG_UNSAFE assert_eq!( - cond_test("((({h1} ({h2} (123 (((49 ({h2} ({msg1} )))))") - .unwrap_err() - .1, - ErrorCode::InvalidPublicKey + err_code( + cond_test("((({h1} ({h2} (123 (((49 ({h2} ({msg1} )))))").unwrap_err() + ), + err_code_node(ErrorCode::InvalidPublicKey) ); } @@ -3651,10 +3649,10 @@ fn test_agg_sig_unsafe_invalid_pubkey() { fn test_agg_sig_unsafe_long_msg() { // AGG_SIG_UNSAFE assert_eq!( - cond_test("((({h1} ({h2} (123 (((49 ({pubkey} ({longmsg} )))))") - .unwrap_err() - .1, - ErrorCode::InvalidMessage + err_code( + cond_test("((({h1} ({h2} (123 (((49 ({pubkey} ({longmsg} )))))").unwrap_err() + ), + err_code_node(ErrorCode::InvalidMessage) ); } @@ -3742,7 +3740,10 @@ fn test_agg_sig_unsafe_invalid_msg( 0, ); if opcode == AGG_SIG_UNSAFE { - assert_eq!(ret.unwrap_err().1, ErrorCode::InvalidMessage); + assert_eq!( + err_code(ret.unwrap_err()), + err_code_node(ErrorCode::InvalidMessage) + ); } else { assert!(ret.is_ok()); } @@ -3753,32 +3754,33 @@ fn test_agg_sig_unsafe_exceed_cost() { // AGG_SIG_UNSAFE // ensure that we terminate parsing conditions once they exceed the max cost assert_eq!( - cond_test_cb( - "((({h1} ({h2} (123 ({} )))", - 0, - Some(Box::new(|a: &mut Allocator| -> NodePtr { - let mut rest: NodePtr = a.nil(); - - for _i in 0..9167 { - // this builds one AGG_SIG_UNSAFE condition - let aggsig = ( - AGG_SIG_UNSAFE, - (Bytes48::from(PUBKEY), (Bytes::from(MSG1.as_slice()), 0)), - ) - .to_clvm(a) - .unwrap(); - - // add the AGG_SIG_UNSAFE condition to the list (called rest) - rest = a.new_pair(aggsig, rest).unwrap(); - } - rest - })), - &Signature::default(), - None, - ) - .unwrap_err() - .1, - ErrorCode::CostExceeded + err_code( + cond_test_cb( + "((({h1} ({h2} (123 ({} )))", + 0, + Some(Box::new(|a: &mut Allocator| -> NodePtr { + let mut rest: NodePtr = a.nil(); + + for _i in 0..9167 { + // this builds one AGG_SIG_UNSAFE condition + let aggsig = ( + AGG_SIG_UNSAFE, + (Bytes48::from(PUBKEY), (Bytes::from(MSG1.as_slice()), 0)), + ) + .to_clvm(a) + .unwrap(); + + // add the AGG_SIG_UNSAFE condition to the list (called rest) + rest = a.new_pair(aggsig, rest).unwrap(); + } + rest + })), + &Signature::default(), + None, + ) + .unwrap_err() + ), + err_code_opt(ErrorCode::CostExceeded) ); } @@ -3786,10 +3788,10 @@ fn test_agg_sig_unsafe_exceed_cost() { fn test_spend_amount_exceeds_max() { // the coin we're trying to spend has an amount that exceeds maximum assert_eq!( - cond_test("((({h1} ({h2} (0x010000000000000000 ())))") - .unwrap_err() - .1, - ErrorCode::InvalidCoinAmount + err_code( + cond_test("((({h1} ({h2} (0x010000000000000000 ())))").unwrap_err() + ), + err_code_node(ErrorCode::InvalidCoinAmount) ); } @@ -3797,8 +3799,8 @@ fn test_spend_amount_exceeds_max() { fn test_single_spend_negative_amount() { // the coin we're trying to spend has a negative amount (i.e. it's invalid) assert_eq!( - cond_test("((({h1} ({h2} (-123 ())))").unwrap_err().1, - ErrorCode::InvalidCoinAmount + err_code(cond_test("((({h1} ({h2} (-123 ())))").unwrap_err()), + err_code_node(ErrorCode::InvalidCoinAmount) ); } @@ -3806,8 +3808,8 @@ fn test_single_spend_negative_amount() { fn test_single_spend_invalid_puzle_hash() { // the puzzle hash in the spend is 33 bytes assert_eq!( - cond_test("((({h1} ({long} (123 ())))").unwrap_err().1, - ErrorCode::InvalidPuzzleHash + err_code(cond_test("((({h1} ({long} (123 ())))").unwrap_err()), + err_code_node(ErrorCode::InvalidPuzzleHash) ); } @@ -3815,8 +3817,8 @@ fn test_single_spend_invalid_puzle_hash() { fn test_single_spend_invalid_parent_id() { // the parent coin ID is 33 bytes long assert_eq!( - cond_test("((({long} ({h2} (123 ())))").unwrap_err().1, - ErrorCode::InvalidParentId + err_code(cond_test("((({long} ({h2} (123 ())))").unwrap_err()), + err_code_node(ErrorCode::InvalidParentId) ); } @@ -3824,10 +3826,10 @@ fn test_single_spend_invalid_parent_id() { fn test_double_spend() { // we spend the same coin twice assert_eq!( - cond_test("((({h1} ({h2} (123 ()) (({h1} ({h2} (123 ())))") - .unwrap_err() - .1, - ErrorCode::DoubleSpend + err_code( + cond_test("((({h1} ({h2} (123 ()) (({h1} ({h2} (123 ())))").unwrap_err() + ), + err_code_node(ErrorCode::DoubleSpend) ); } @@ -3969,8 +3971,8 @@ fn test_concurrent_spend_fail() { for test in test_cases { assert_eq!( - cond_test(test).unwrap_err().1, - ErrorCode::AssertConcurrentSpendFailed + err_code(cond_test(test).unwrap_err()), + err_code_node(ErrorCode::AssertConcurrentSpendFailed) ); } } @@ -4095,8 +4097,8 @@ fn test_concurrent_puzzle_fail() { for test in test_cases { assert_eq!( - cond_test(test).unwrap_err().1, - ErrorCode::AssertConcurrentPuzzleFailed + err_code(cond_test(test).unwrap_err()), + err_code_node(ErrorCode::AssertConcurrentPuzzleFailed) ); } } @@ -4267,7 +4269,7 @@ fn test_cost_aggsig_conds_after_free(#[case] count: usize) { 100, ASSERT_BEFORE_SECONDS_ABSOLUTE, 100, - Some(ErrorCode::ImpossibleSecondsAbsoluteConstraints) + Some(ErrorCode::ImpossibleSecondsAbsoluteConstraints(NodePtr::NIL)) )] #[case(ASSERT_SECONDS_ABSOLUTE, 99, ASSERT_BEFORE_SECONDS_ABSOLUTE, 100, None)] #[case( @@ -4275,7 +4277,7 @@ fn test_cost_aggsig_conds_after_free(#[case] count: usize) { 100, ASSERT_BEFORE_HEIGHT_ABSOLUTE, 100, - Some(ErrorCode::ImpossibleHeightAbsoluteConstraints) + Some(ErrorCode::ImpossibleHeightAbsoluteConstraints(NodePtr::NIL)) )] #[case(ASSERT_HEIGHT_ABSOLUTE, 99, ASSERT_BEFORE_HEIGHT_ABSOLUTE, 100, None)] #[case( @@ -4283,7 +4285,7 @@ fn test_cost_aggsig_conds_after_free(#[case] count: usize) { 100, ASSERT_BEFORE_SECONDS_RELATIVE, 100, - Some(ErrorCode::ImpossibleSecondsRelativeConstraints) + Some(ErrorCode::ImpossibleSecondsRelativeConstraints(NodePtr::NIL)) )] #[case(ASSERT_SECONDS_RELATIVE, 99, ASSERT_BEFORE_SECONDS_RELATIVE, 100, None)] #[case( @@ -4291,7 +4293,7 @@ fn test_cost_aggsig_conds_after_free(#[case] count: usize) { 100, ASSERT_BEFORE_HEIGHT_RELATIVE, 100, - Some(ErrorCode::ImpossibleHeightRelativeConstraints) + Some(ErrorCode::ImpossibleHeightRelativeConstraints(NodePtr::NIL)) )] #[case(ASSERT_HEIGHT_RELATIVE, 99, ASSERT_BEFORE_HEIGHT_RELATIVE, 100, None)] // order shouldn't matter @@ -4300,7 +4302,7 @@ fn test_cost_aggsig_conds_after_free(#[case] count: usize) { 100, ASSERT_SECONDS_ABSOLUTE, 100, - Some(ErrorCode::ImpossibleSecondsAbsoluteConstraints) + Some(ErrorCode::ImpossibleSecondsAbsoluteConstraints(NodePtr::NIL)) )] #[case(ASSERT_BEFORE_SECONDS_ABSOLUTE, 100, ASSERT_SECONDS_ABSOLUTE, 99, None)] #[case( @@ -4308,7 +4310,7 @@ fn test_cost_aggsig_conds_after_free(#[case] count: usize) { 100, ASSERT_HEIGHT_ABSOLUTE, 100, - Some(ErrorCode::ImpossibleHeightAbsoluteConstraints) + Some(ErrorCode::ImpossibleHeightAbsoluteConstraints(NodePtr::NIL)) )] #[case(ASSERT_BEFORE_HEIGHT_ABSOLUTE, 100, ASSERT_HEIGHT_ABSOLUTE, 99, None)] #[case( @@ -4316,7 +4318,7 @@ fn test_cost_aggsig_conds_after_free(#[case] count: usize) { 100, ASSERT_SECONDS_RELATIVE, 100, - Some(ErrorCode::ImpossibleSecondsRelativeConstraints) + Some(ErrorCode::ImpossibleSecondsRelativeConstraints(NodePtr::NIL)) )] #[case(ASSERT_BEFORE_SECONDS_RELATIVE, 100, ASSERT_SECONDS_RELATIVE, 99, None)] #[case( @@ -4324,7 +4326,7 @@ fn test_cost_aggsig_conds_after_free(#[case] count: usize) { 100, ASSERT_HEIGHT_RELATIVE, 100, - Some(ErrorCode::ImpossibleHeightRelativeConstraints) + Some(ErrorCode::ImpossibleHeightRelativeConstraints(NodePtr::NIL)) )] #[case(ASSERT_BEFORE_HEIGHT_RELATIVE, 100, ASSERT_HEIGHT_RELATIVE, 99, None)] fn test_impossible_constraints_single_spend( @@ -4344,7 +4346,7 @@ fn test_impossible_constraints_single_spend( cond1 as u8, value1, cond2 as u8, value2 ); if let Some(e) = expected_err { - assert_eq!(cond_test(test).unwrap_err().1, e); + assert_eq!(err_code(cond_test(test).unwrap_err()), u32::from(e)); } else { // we don't expect any error let (a, conds) = cond_test(test).unwrap(); @@ -4374,7 +4376,7 @@ fn test_impossible_constraints_single_spend( 100, ASSERT_BEFORE_SECONDS_ABSOLUTE, 100, - Some(ErrorCode::ImpossibleSecondsAbsoluteConstraints) + Some(ErrorCode::ImpossibleSecondsAbsoluteConstraints(NodePtr::NIL)) )] #[case(ASSERT_SECONDS_ABSOLUTE, 99, ASSERT_BEFORE_SECONDS_ABSOLUTE, 100, None)] #[case( @@ -4382,7 +4384,7 @@ fn test_impossible_constraints_single_spend( 100, ASSERT_BEFORE_HEIGHT_ABSOLUTE, 100, - Some(ErrorCode::ImpossibleHeightAbsoluteConstraints) + Some(ErrorCode::ImpossibleHeightAbsoluteConstraints(NodePtr::NIL)) )] #[case(ASSERT_HEIGHT_ABSOLUTE, 99, ASSERT_BEFORE_HEIGHT_ABSOLUTE, 100, None)] #[case( @@ -4401,7 +4403,7 @@ fn test_impossible_constraints_single_spend( 100, ASSERT_SECONDS_ABSOLUTE, 100, - Some(ErrorCode::ImpossibleSecondsAbsoluteConstraints) + Some(ErrorCode::ImpossibleSecondsAbsoluteConstraints(NodePtr::NIL)) )] #[case(ASSERT_BEFORE_SECONDS_ABSOLUTE, 100, ASSERT_SECONDS_ABSOLUTE, 99, None)] #[case( @@ -4409,7 +4411,7 @@ fn test_impossible_constraints_single_spend( 100, ASSERT_HEIGHT_ABSOLUTE, 100, - Some(ErrorCode::ImpossibleHeightAbsoluteConstraints) + Some(ErrorCode::ImpossibleHeightAbsoluteConstraints(NodePtr::NIL)) )] #[case(ASSERT_BEFORE_HEIGHT_ABSOLUTE, 100, ASSERT_HEIGHT_ABSOLUTE, 99, None)] #[case( @@ -4441,7 +4443,7 @@ fn test_impossible_constraints_separate_spends( cond1 as u8, value1, cond2 as u8, value2 ); if let Some(e) = expected_err { - assert_eq!(cond_test(test).unwrap_err().1, e); + assert_eq!(err_code(cond_test(test).unwrap_err()), u32::from(e)); } else { // we don't expect any error let (a, conds) = cond_test(test).unwrap(); @@ -4473,16 +4475,17 @@ fn test_impossible_constraints_separate_spends( #[case(ASSERT_MY_BIRTH_SECONDS, ErrorCode::AssertMyBirthSecondsFailed)] fn test_conflicting_my_birth_assertions( #[case] condition: ConditionOpcode, - #[case] expected: ErrorCode, + #[case] expected: fn(NodePtr) -> ErrorCode, ) { let val = condition as u8; assert_eq!( - cond_test(&format!( - "((({{h1}} ({{h2}} (1234 ((({val} (100 ) (({val} (503 ) (({val} (90 )))))" - )) - .unwrap_err() - .1, - expected + err_code( + cond_test(&format!( + "((({{h1}} ({{h2}} (1234 ((({val} (100 ) (({val} (503 ) (({val} (90 )))))" + )) + .unwrap_err() + ), + err_code_node(expected) ); } @@ -4661,8 +4664,8 @@ fn test_assert_ephemeral_wrong_ph() { // this is an invalid ASSERT_EPHEMERAL assert_eq!( - cond_test(test).unwrap_err().1, - ErrorCode::AssertEphemeralFailed + err_code(cond_test(test).unwrap_err()), + err_code_node(ErrorCode::AssertEphemeralFailed) ); } @@ -4685,8 +4688,8 @@ fn test_assert_ephemeral_wrong_amount() { // this is an invalid ASSERT_EPHEMERAL assert_eq!( - cond_test(test).unwrap_err().1, - ErrorCode::AssertEphemeralFailed + err_code(cond_test(test).unwrap_err()), + err_code_node(ErrorCode::AssertEphemeralFailed) ); } @@ -4709,8 +4712,8 @@ fn test_assert_ephemeral_wrong_parent() { // this is an invalid ASSERT_EPHEMERAL assert_eq!( - cond_test(test).unwrap_err().1, - ErrorCode::AssertEphemeralFailed + err_code(cond_test(test).unwrap_err()), + err_code_node(ErrorCode::AssertEphemeralFailed) ); } @@ -4719,20 +4722,32 @@ fn test_assert_ephemeral_wrong_parent() { // the default expected errors are post soft-fork, when both new rules are // activated #[case(ASSERT_HEIGHT_ABSOLUTE, None)] -#[case(ASSERT_HEIGHT_RELATIVE, Some(ErrorCode::EphemeralRelativeCondition))] +#[case( + ASSERT_HEIGHT_RELATIVE, + Some(ErrorCode::EphemeralRelativeCondition(NodePtr::NIL)) +)] #[case(ASSERT_SECONDS_ABSOLUTE, None)] -#[case(ASSERT_SECONDS_RELATIVE, Some(ErrorCode::EphemeralRelativeCondition))] -#[case(ASSERT_MY_BIRTH_HEIGHT, Some(ErrorCode::EphemeralRelativeCondition))] -#[case(ASSERT_MY_BIRTH_SECONDS, Some(ErrorCode::EphemeralRelativeCondition))] +#[case( + ASSERT_SECONDS_RELATIVE, + Some(ErrorCode::EphemeralRelativeCondition(NodePtr::NIL)) +)] +#[case( + ASSERT_MY_BIRTH_HEIGHT, + Some(ErrorCode::EphemeralRelativeCondition(NodePtr::NIL)) +)] +#[case( + ASSERT_MY_BIRTH_SECONDS, + Some(ErrorCode::EphemeralRelativeCondition(NodePtr::NIL)) +)] #[case(ASSERT_BEFORE_HEIGHT_ABSOLUTE, None)] #[case( ASSERT_BEFORE_HEIGHT_RELATIVE, - Some(ErrorCode::EphemeralRelativeCondition) + Some(ErrorCode::EphemeralRelativeCondition(NodePtr::NIL)) )] #[case(ASSERT_BEFORE_SECONDS_ABSOLUTE, None)] #[case( ASSERT_BEFORE_SECONDS_RELATIVE, - Some(ErrorCode::EphemeralRelativeCondition) + Some(ErrorCode::EphemeralRelativeCondition(NodePtr::NIL)) )] fn test_relative_condition_on_ephemeral( #[case] condition: ConditionOpcode, @@ -4761,7 +4776,7 @@ fn test_relative_condition_on_ephemeral( ); if let Some(err) = expect_error { - assert_eq!(cond_test(&test).unwrap_err().1, err); + assert_eq!(err_code(cond_test(&test).unwrap_err()), u32::from(err)); } else { // we don't expect any error let (a, conds) = cond_test(&test).unwrap(); @@ -4835,18 +4850,19 @@ fn test_softfork_condition(#[case] conditions: &str, #[case] expected_cost: Cost #[cfg(test)] #[rstest] // the cost argument must be positive -#[case("((90 (-1 )", ErrorCode::InvalidSoftforkCost)] +#[case("((90 (-1 )", u32::from(ErrorCode::InvalidSoftforkCost(NodePtr::NIL)))] // the cost argument may not exceed 2^32-1 -#[case("((90 (0x0100000000 )", ErrorCode::InvalidSoftforkCost)] +#[case("((90 (0x0100000000 )", u32::from(ErrorCode::InvalidSoftforkCost(NodePtr::NIL)))] // the test has a cost limit of 11000000000 -#[case("((90 (0x00ffffffff )", ErrorCode::CostExceeded)] -#[case("((90 )", ErrorCode::InvalidCondition)] -fn test_softfork_condition_failures(#[case] conditions: &str, #[case] expected_err: ErrorCode) { +#[case("((90 (0x00ffffffff )", u32::from(ErrorCode::CostExceeded(None)))] +#[case("((90 )", u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)))] +fn test_softfork_condition_failures(#[case] conditions: &str, #[case] expected_err: u32) { // SOFTFORK (90) assert_eq!( - cond_test_flag(&format!("((({{h1}} ({{h2}} (1234 ({conditions}))))"), 0) - .unwrap_err() - .1, + u32::from( + cond_test_flag(&format!("((({{h1}} ({{h2}} (1234 ({conditions}))))"), 0) + .unwrap_err() + ), expected_err ); } @@ -4858,62 +4874,62 @@ fn test_softfork_condition_failures(#[case] conditions: &str, #[case] expected_e CREATE_PUZZLE_ANNOUNCEMENT, 1025, 0, - Some(ErrorCode::TooManyAnnouncements) + Some(ErrorCode::TooManyAnnouncements(NodePtr::NIL)) )] #[case( ASSERT_PUZZLE_ANNOUNCEMENT, 1024, 0, - Some(ErrorCode::AssertPuzzleAnnouncementFailed) + Some(ErrorCode::AssertPuzzleAnnouncementFailed(NodePtr::NIL)) )] #[case( ASSERT_PUZZLE_ANNOUNCEMENT, 1025, 0, - Some(ErrorCode::TooManyAnnouncements) + Some(ErrorCode::TooManyAnnouncements(NodePtr::NIL)) )] #[case(CREATE_COIN_ANNOUNCEMENT, 1000, 0, None)] #[case( CREATE_COIN_ANNOUNCEMENT, 1025, 0, - Some(ErrorCode::TooManyAnnouncements) + Some(ErrorCode::TooManyAnnouncements(NodePtr::NIL)) )] #[case( ASSERT_COIN_ANNOUNCEMENT, 1024, 0, - Some(ErrorCode::AssertCoinAnnouncementFailed) + Some(ErrorCode::AssertCoinAnnouncementFailed(NodePtr::NIL)) )] #[case( ASSERT_COIN_ANNOUNCEMENT, 1025, 0, - Some(ErrorCode::TooManyAnnouncements) + Some(ErrorCode::TooManyAnnouncements(NodePtr::NIL)) )] #[case( ASSERT_CONCURRENT_SPEND, 1024, 0, - Some(ErrorCode::AssertConcurrentSpendFailed) + Some(ErrorCode::AssertConcurrentSpendFailed(NodePtr::NIL)) )] #[case( ASSERT_CONCURRENT_SPEND, 1025, 0, - Some(ErrorCode::TooManyAnnouncements) + Some(ErrorCode::TooManyAnnouncements(NodePtr::NIL)) )] #[case( ASSERT_CONCURRENT_PUZZLE, 1024, 0, - Some(ErrorCode::AssertConcurrentPuzzleFailed) + Some(ErrorCode::AssertConcurrentPuzzleFailed(NodePtr::NIL)) )] #[case( ASSERT_CONCURRENT_PUZZLE, 1025, 0, - Some(ErrorCode::TooManyAnnouncements) + Some(ErrorCode::TooManyAnnouncements(NodePtr::NIL)) )] // new flag tests #[case(CREATE_PUZZLE_ANNOUNCEMENT, 1025, COST_CONDITIONS, None)] @@ -4921,26 +4937,26 @@ fn test_softfork_condition_failures(#[case] conditions: &str, #[case] expected_e ASSERT_PUZZLE_ANNOUNCEMENT, 1025, COST_CONDITIONS, - Some(ErrorCode::AssertPuzzleAnnouncementFailed) + Some(ErrorCode::AssertPuzzleAnnouncementFailed(NodePtr::NIL)) )] #[case(CREATE_COIN_ANNOUNCEMENT, 1025, COST_CONDITIONS, None)] #[case( ASSERT_COIN_ANNOUNCEMENT, 1025, COST_CONDITIONS, - Some(ErrorCode::AssertCoinAnnouncementFailed) + Some(ErrorCode::AssertCoinAnnouncementFailed(NodePtr::NIL)) )] #[case( ASSERT_CONCURRENT_SPEND, 1025, COST_CONDITIONS, - Some(ErrorCode::AssertConcurrentSpendFailed) + Some(ErrorCode::AssertConcurrentSpendFailed(NodePtr::NIL)) )] #[case( ASSERT_CONCURRENT_PUZZLE, 1025, COST_CONDITIONS, - Some(ErrorCode::AssertConcurrentPuzzleFailed) + Some(ErrorCode::AssertConcurrentPuzzleFailed(NodePtr::NIL)) )] fn test_limit_announcements( #[case] cond: ConditionOpcode, @@ -4968,8 +4984,8 @@ fn test_limit_announcements( None, ); - if expect_err.is_some() { - assert_eq!(r.unwrap_err().1, expect_err.unwrap()); + if let Some(expect_err) = expect_err { + assert_eq!(err_code(r.unwrap_err()), u32::from(expect_err)); } else { r.unwrap(); } @@ -5432,9 +5448,9 @@ fn test_message_conditions_single_spend(#[case] test_case: &str, #[case] expect: assert_eq!(a.atom(spend.puzzle_hash).as_ref(), H2); assert_eq!(spend.flags, 0); } else if expect_pass { - panic!("failed: {:?}", ret.unwrap_err().1); + panic!("failed: {:?}", ret.unwrap_err()); } else { - let actual_err = ret.unwrap_err().1; + let actual_err = ret.unwrap_err(); println!("Error: {actual_err:?}"); assert_eq!(ErrorCode::MessageNotSentOrReceived, actual_err); } @@ -5443,7 +5459,7 @@ fn test_message_conditions_single_spend(#[case] test_case: &str, #[case] expect: #[cfg(test)] #[rstest] #[case(512, 0, None)] -#[case(513, 0, Some(ErrorCode::TooManyAnnouncements))] +#[case(513, 0, Some(ErrorCode::TooManyAnnouncements(NodePtr::NIL)))] #[case(513, COST_CONDITIONS, None)] fn test_limit_messages( #[case] count: i32, @@ -5490,8 +5506,8 @@ fn test_limit_messages( None, ); - if expect_err.is_some() { - assert_eq!(r.unwrap_err().1, expect_err.unwrap()); + if let Some(expect_err) = expect_err { + assert_eq!(err_code(r.unwrap_err()), u32::from(expect_err)); } else { r.unwrap(); } @@ -5499,121 +5515,142 @@ fn test_limit_messages( #[cfg(test)] #[rstest] -#[case("(66 (0x38 ({longmsg} )", ErrorCode::InvalidMessage)] -#[case("(66 (0x3c ({long} ({msg1} )", ErrorCode::InvalidParentId)] -#[case("(66 (0x3c ({msg2} ({msg1} )", ErrorCode::InvalidParentId)] -#[case("(66 (0x3a ({long} ({msg1} )", ErrorCode::InvalidPuzzleHash)] -#[case("(66 (0x3a ({msg2} ({msg1} )", ErrorCode::InvalidPuzzleHash)] -#[case("(66 (0x3f ({long} ({msg1} )", ErrorCode::InvalidCoinId)] -#[case("(66 (0x3f ({msg2} ({msg1} )", ErrorCode::InvalidCoinId)] +#[case( + "(66 (0x38 ({longmsg} )", + u32::from(ErrorCode::InvalidMessage(NodePtr::NIL)) +)] +#[case( + "(66 (0x3c ({long} ({msg1} )", + u32::from(ErrorCode::InvalidParentId(NodePtr::NIL)) +)] +#[case( + "(66 (0x3c ({msg2} ({msg1} )", + u32::from(ErrorCode::InvalidParentId(NodePtr::NIL)) +)] +#[case( + "(66 (0x3a ({long} ({msg1} )", + u32::from(ErrorCode::InvalidPuzzleHash(NodePtr::NIL)) +)] +#[case( + "(66 (0x3a ({msg2} ({msg1} )", + u32::from(ErrorCode::InvalidPuzzleHash(NodePtr::NIL)) +)] +#[case( + "(66 (0x3f ({long} ({msg1} )", + u32::from(ErrorCode::InvalidCoinId(NodePtr::NIL)) +)] +#[case( + "(66 (0x3f ({msg2} ({msg1} )", + u32::from(ErrorCode::InvalidCoinId(NodePtr::NIL)) +)] #[case( "(66 (0x08 ({msg1} ) ((67 (0x08 ({msg1} (-1 )", - ErrorCode::CoinAmountNegative + u32::from(ErrorCode::CoinAmountNegative(NodePtr::NIL)) )] #[case( "(66 (0x08 ({msg1} ) ((67 (0x08 ({msg1} )", - ErrorCode::InvalidCondition + u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)) )] #[case( "(66 (0x01 ({msg1} (-1 ) ((67 (0x01 ({msg1} )", - ErrorCode::CoinAmountNegative + u32::from(ErrorCode::CoinAmountNegative(NodePtr::NIL)) )] #[case( "(66 (0x01 ({msg1} ) ((67 (0x01 ({msg1} )", - ErrorCode::InvalidCondition + u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)) )] #[case( "(66 (0x02 ({msg1} ({msg2} ) ((67 (0x02 ({msg1} )", - ErrorCode::InvalidPuzzleHash + u32::from(ErrorCode::InvalidPuzzleHash(NodePtr::NIL)) )] #[case( "(66 (0x02 ({msg1} ) ((67 (0x02 ({msg1} )", - ErrorCode::InvalidCondition + u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)) )] #[case( "(66 (0x10 ({msg1} ) ((67 (0x10 ({msg1} ({msg2} )", - ErrorCode::InvalidPuzzleHash + u32::from(ErrorCode::InvalidPuzzleHash(NodePtr::NIL)) )] #[case( "(66 (0x10 ({msg1} ) ((67 (0x10 ({msg1} )", - ErrorCode::InvalidCondition + u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)) )] #[case( "(66 (0x04 ({msg1} ({msg2} ) ((67 (0x04 ({msg1} )", - ErrorCode::InvalidParentId + u32::from(ErrorCode::InvalidParentId(NodePtr::NIL)) )] #[case( "(66 (0x04 ({msg1} ) ((67 (0x04 ({msg1} )", - ErrorCode::InvalidCondition + u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)) )] #[case( "(66 (0x20 ({msg1} ) ((67 (0x20 ({msg1} ({msg2} )", - ErrorCode::InvalidParentId + u32::from(ErrorCode::InvalidParentId(NodePtr::NIL)) )] #[case( "(66 (0x20 ({msg1} ) ((67 (0x20 ({msg1} )", - ErrorCode::InvalidCondition + u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)) )] #[case( "(66 (0x07 ({msg1} ({msg2} ) ((67 (0x07 ({msg1} )", - ErrorCode::InvalidCoinId + u32::from(ErrorCode::InvalidCoinId(NodePtr::NIL)) )] #[case( "(66 (0x07 ({msg1} ) ((67 (0x07 ({msg1} )", - ErrorCode::InvalidCondition + u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)) )] #[case( "(66 (0x38 ({msg1} ) ((67 (0x38 ({msg1} ({msg2} )", - ErrorCode::InvalidCoinId + u32::from(ErrorCode::InvalidCoinId(NodePtr::NIL)) )] #[case( "(66 (0x38 ({msg1} ) ((67 (0x38 ({msg1} )", - ErrorCode::InvalidCondition + u32::from(ErrorCode::InvalidCondition(NodePtr::NIL)) )] // message mode must be specified in canonical mode #[case( "(66 (0x00 ({msg1} ) ((67 (0x00 ({msg1} )", - ErrorCode::InvalidMessageMode + u32::from(ErrorCode::InvalidMessageMode(None)) )] #[case( "(66 (0x01 ({msg1} (123 ) ((67 (0x00 ({msg1} )", - ErrorCode::InvalidMessageMode + u32::from(ErrorCode::InvalidMessageMode(None)) )] // negative messages modes are not allowed #[case( "(66 (-1 ({msg1} (123 ) ((67 (0x01 ({msg1} )", - ErrorCode::InvalidMessageMode + u32::from(ErrorCode::InvalidMessageMode(None)) )] #[case( "(66 (0x01 ({msg1} (123 ) ((67 (-1 ({msg1} )", - ErrorCode::InvalidMessageMode + u32::from(ErrorCode::InvalidMessageMode(None)) )] // amounts must be specified in canonical mode #[case( "(66 (0x01 ({msg1} (0x0040 ) ((67 (0x01 ({msg1} (123 )", - ErrorCode::InvalidCoinAmount + u32::from(ErrorCode::InvalidCoinAmount(NodePtr::NIL)) )] #[case( "(66 (0x01 ({msg1} (0x00 ) ((67 (0x01 ({msg1} (123 )", - ErrorCode::InvalidCoinAmount + u32::from(ErrorCode::InvalidCoinAmount(NodePtr::NIL)) )] // coin amounts can't be negative #[case( "(66 (0x01 ({msg1} (-1 ) ((67 (0x01 ({msg1} (123 )", - ErrorCode::CoinAmountNegative + u32::from(ErrorCode::CoinAmountNegative(NodePtr::NIL)) )] #[case( "(66 (0x01 ({msg1} (-1 ) ((67 (0x01 ({msg1} (123 )", - ErrorCode::CoinAmountNegative + u32::from(ErrorCode::CoinAmountNegative(NodePtr::NIL)) )] -fn test_message_conditions_failures(#[case] test_case: &str, #[case] expect: ErrorCode) { +fn test_message_conditions_failures(#[case] test_case: &str, #[case] expect: u32) { let flags = MEMPOOL_MODE; let ret = cond_test_flag(&format!("((({{h1}} ({{h2}} (123 (({test_case}))))"), flags); - let Err(ValidationErr(_, code)) = ret else { + let Err(code) = ret else { panic!("expected failure: {expect:?}"); }; - assert_eq!(code, expect); + assert_eq!(u32::from(code), expect); } #[cfg(test)] @@ -5856,9 +5893,9 @@ fn test_message_conditions_two_spends( assert_eq!(a.atom(spend.puzzle_hash).as_ref(), H1); assert_eq!(spend.flags, 0); } else if expect_pass { - panic!("failed: {:?}", ret.unwrap_err().1); + panic!("failed: {:?}", ret.unwrap_err()); } else { - let actual_err = ret.unwrap_err().1; + let actual_err = ret.unwrap_err(); println!("Error: {actual_err:?}"); assert_eq!(ErrorCode::MessageNotSentOrReceived, actual_err); } diff --git a/crates/chia-consensus/src/error.rs b/crates/chia-consensus/src/error.rs index 5b808645d..47fbf4c08 100644 --- a/crates/chia-consensus/src/error.rs +++ b/crates/chia-consensus/src/error.rs @@ -1,4 +1,4 @@ -use crate::validation_error::ValidationErr; +use crate::error_code::ErrorCode; use clvm_traits::{FromClvmError, ToClvmError}; use clvmr::error::EvalErr; use thiserror::Error; @@ -17,8 +17,8 @@ pub enum Error { #[error("Eval {0}")] Eval(#[from] EvalErr), - #[error("Validation {0}")] - Validation(#[from] ValidationErr), + #[error("Validation {0:?}")] + Validation(ErrorCode), #[error("BLS {0}")] Bls(#[from] chia_bls::Error), @@ -59,3 +59,9 @@ impl From for PyErr { } pub type Result = std::result::Result; + +impl From for Error { + fn from(err: ErrorCode) -> Self { + Error::Validation(err) + } +} diff --git a/crates/chia-consensus/src/error_code.rs b/crates/chia-consensus/src/error_code.rs new file mode 100644 index 000000000..94e62f383 --- /dev/null +++ b/crates/chia-consensus/src/error_code.rs @@ -0,0 +1,401 @@ +use clvmr::allocator::{Allocator, Atom, NodePtr, SExp}; +use clvmr::error::EvalErr; + +#[cfg(feature = "py-bindings")] +use pyo3::PyErr; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ErrorCode { + #[default] + Unknown, + InvalidBlockSolution(NodePtr), + InvalidCoinSolution(NodePtr), + DuplicateOutput(NodePtr), + DoubleSpend(NodePtr), + UnknownUnspent(NodePtr), + BadAggregateSignature, + WrongPuzzleHash(NodePtr), + BadFarmerCoinAmount(NodePtr), + InvalidCondition(NodePtr), + InvalidConditionOpcode(NodePtr), + InvalidParentId(NodePtr), + InvalidPuzzleHash(NodePtr), + InvalidPublicKey(NodePtr), + InvalidMessage(NodePtr), + InvalidCoinAmount(NodePtr), + InvalidCoinAnnouncement(NodePtr), + InvalidPuzzleAnnouncement(NodePtr), + AssertMyCoinIdFailed(NodePtr), + AssertPuzzleAnnouncementFailed(NodePtr), + AssertCoinAnnouncementFailed(NodePtr), + AssertHeightRelativeFailed(NodePtr), + AssertHeightAbsoluteFailed(NodePtr), + AssertSecondsAbsoluteFailed(NodePtr), + CoinAmountExceedsMaximum(NodePtr), + SexpError(NodePtr), + InvalidFeeLowFee(NodePtr), + MempoolConflict(NodePtr), + MintingCoin(NodePtr), + ExtendsUnknownBlock(NodePtr), + CoinbaseNotYetSpendable(NodePtr), + /// Renamed from "BlockCostExceedsMax" since it's more generic than that. + CostExceeded(Option), + BadAdditionRoot(NodePtr), + BadRemovalRoot(NodePtr), + InvalidPospaceHash(NodePtr), + InvalidCoinbaseSignature(NodePtr), + InvalidPlotSignature(NodePtr), + TimestampTooFarInPast(NodePtr), + TimestampTooFarInFuture(NodePtr), + InvalidTransactionsFilterHash(NodePtr), + InvalidPospaceChallenge(NodePtr), + InvalidPospace(NodePtr), + InvalidHeight(NodePtr), + InvalidCoinbaseAmount(NodePtr), + InvalidMerkleRoot(NodePtr), + InvalidBlockFeeAmount(NodePtr), + InvalidWeight(NodePtr), + InvalidTotalIters(NodePtr), + BlockIsNotFinished(NodePtr), + InvalidNumIterations(NodePtr), + InvalidPot(NodePtr), + InvalidPotChallenge(NodePtr), + InvalidTransactionsGeneratorHash(NodePtr), + InvalidPoolTarget(NodePtr), + InvalidCoinbaseParent(NodePtr), + InvalidFeesCoinParent(NodePtr), + ReserveFeeConditionFailed(NodePtr), + NotBlockButHasData(NodePtr), + IsTransactionBlockButNoData(NodePtr), + InvalidPrevBlockHash(NodePtr), + InvalidTransactionsInfoHash(NodePtr), + InvalidFoliageBlockHash(NodePtr), + InvalidRewardCoins(NodePtr), + InvalidBlockCost(NodePtr), + NoEndOfSlotInfo(NodePtr), + InvalidPrevChallengeSlotHash(NodePtr), + InvalidSubEpochSummaryHash(NodePtr), + NoSubEpochSummaryHash(NodePtr), + ShouldNotMakeChallengeBlock(NodePtr), + ShouldMakeChallengeBlock(NodePtr), + InvalidChallengeChainData(NodePtr), + InvalidCcEosVdf(NodePtr), + InvalidRcEosVdf(NodePtr), + InvalidChallengeSlotHashRc(NodePtr), + InvalidPriorPointRc(NodePtr), + InvalidDeficit(NodePtr), + InvalidSubEpochSummary(NodePtr), + InvalidPrevSubEpochSummaryHash(NodePtr), + InvalidRewardChainHash(NodePtr), + InvalidSubEpochOverflow(NodePtr), + InvalidNewDifficulty(NodePtr), + InvalidNewSubSlotIters(NodePtr), + InvalidCcSpVdf(NodePtr), + InvalidRcSpVdf(NodePtr), + InvalidCcSignature(NodePtr), + InvalidRcSignature(NodePtr), + CannotMakeCcBlock(NodePtr), + InvalidRcSpPrevIp(NodePtr), + InvalidRcIpPrevIp(NodePtr), + InvalidIsTransactionBlock(NodePtr), + InvalidUrsbHash(NodePtr), + OldPoolTarget(NodePtr), + InvalidPoolSignature(NodePtr), + InvalidFoliageBlockPresence(NodePtr), + InvalidCcIpVdf(NodePtr), + InvalidRcIpVdf(NodePtr), + IpShouldBeNone(NodePtr), + InvalidRewardBlockHash(NodePtr), + InvalidMadeNonOverflowInfusions(NodePtr), + NoOverflowsInFirstSubSlotNewEpoch(NodePtr), + MempoolNotInitialized(NodePtr), + ShouldNotHaveIcc(NodePtr), + ShouldHaveIcc(NodePtr), + InvalidIccVdf(NodePtr), + InvalidIccHashCc(NodePtr), + InvalidIccHashRc(NodePtr), + InvalidIccEosVdf(NodePtr), + InvalidSpIndex(NodePtr), + TooManyBlocks(NodePtr), + InvalidCcChallenge(NodePtr), + InvalidPrefarm(NodePtr), + AssertSecondsRelativeFailed(NodePtr), + BadCoinbaseSignature(NodePtr), + // InitialTransactionFreeze (removed in `chia-blockchain` as well) + NoTransactionsWhileSyncing(NodePtr), + AlreadyIncludingTransaction(NodePtr), + IncompatibleNetworkId(NodePtr), + PreSoftForkMaxGeneratorSize(NodePtr), + InvalidRequiredIters(NodePtr), + TooManyGeneratorRefs, + AssertMyParentIdFailed(NodePtr), + AssertMyPuzzleHashFailed(NodePtr), + AssertMyAmountFailed(NodePtr), + GeneratorRuntimeError(Option), + InvalidCostResult(NodePtr), + InvalidTransactionsGeneratorRefsRoot(NodePtr), + FutureGeneratorRefs(NodePtr), + GeneratorRefHasNoGenerator(NodePtr), + DoubleSpendInFork(NodePtr), + InvalidFeeTooCloseToZero(NodePtr), + CoinAmountNegative(NodePtr), + InternalProtocolError(NodePtr), + InvalidSpendBundle(NodePtr), + FailedGettingGeneratorMultiprocessing(NodePtr), + AssertBeforeSecondsAbsoluteFailed(NodePtr), + AssertBeforeSecondsRelativeFailed(NodePtr), + AssertBeforeHeightAbsoluteFailed(NodePtr), + AssertBeforeHeightRelativeFailed(NodePtr), + AssertConcurrentSpendFailed(NodePtr), + AssertConcurrentPuzzleFailed(NodePtr), + ImpossibleSecondsRelativeConstraints(NodePtr), + ImpossibleSecondsAbsoluteConstraints(NodePtr), + ImpossibleHeightRelativeConstraints(NodePtr), + ImpossibleHeightAbsoluteConstraints(NodePtr), + AssertMyBirthSecondsFailed(NodePtr), + AssertMyBirthHeightFailed(NodePtr), + AssertEphemeralFailed(NodePtr), + EphemeralRelativeCondition(NodePtr), + InvalidSoftforkCondition(NodePtr), + InvalidSoftforkCost(NodePtr), + TooManyAnnouncements(NodePtr), + InvalidMessageMode(Option), + InvalidCoinId(NodePtr), + MessageNotSentOrReceived, + ComplexGeneratorReceived, +} + +impl From for ErrorCode { + fn from(v: EvalErr) -> Self { + match v { + EvalErr::CostExceeded => ErrorCode::CostExceeded(Some(v.node_ptr())), + _ => ErrorCode::GeneratorRuntimeError(Some(v.node_ptr())), + } + } +} + +impl From for ErrorCode { + fn from(_: std::io::Error) -> Self { + ErrorCode::GeneratorRuntimeError(None) + } +} + +#[cfg(feature = "py-bindings")] +impl From for PyErr { + fn from(err: ErrorCode) -> PyErr { + pyo3::exceptions::PyValueError::new_err(("ValidationError", u32::from(err))) + } +} + +// helper functions that fail with ErrorCode +pub fn first(a: &Allocator, n: NodePtr) -> Result { + match a.sexp(n) { + SExp::Pair(left, _) => Ok(left), + SExp::Atom => Err(ErrorCode::InvalidCondition(n)), + } +} + +// from chia-blockchain/chia/util/errors.py +impl From for u32 { + fn from(err: ErrorCode) -> u32 { + match err { + ErrorCode::Unknown => 1, + ErrorCode::InvalidBlockSolution(..) => 2, + ErrorCode::InvalidCoinSolution(..) => 3, + ErrorCode::DuplicateOutput(..) => 4, + ErrorCode::DoubleSpend(..) => 5, + ErrorCode::UnknownUnspent(..) => 6, + ErrorCode::BadAggregateSignature => 7, + ErrorCode::WrongPuzzleHash(..) => 8, + ErrorCode::BadFarmerCoinAmount(..) => 9, + ErrorCode::InvalidCondition(..) + | ErrorCode::InvalidConditionOpcode(..) + | ErrorCode::InvalidParentId(..) + | ErrorCode::InvalidPuzzleHash(..) + | ErrorCode::InvalidPublicKey(..) + | ErrorCode::InvalidMessage(..) + | ErrorCode::InvalidCoinAmount(..) + | ErrorCode::InvalidCoinAnnouncement(..) + | ErrorCode::InvalidPuzzleAnnouncement(..) => 10, + ErrorCode::AssertMyCoinIdFailed(..) => 11, + ErrorCode::AssertPuzzleAnnouncementFailed(..) + | ErrorCode::AssertCoinAnnouncementFailed(..) => { + 12 + } + ErrorCode::AssertHeightRelativeFailed(..) => 13, + ErrorCode::AssertHeightAbsoluteFailed(..) => 14, + ErrorCode::AssertSecondsAbsoluteFailed(..) => 15, + ErrorCode::CoinAmountExceedsMaximum(..) => 16, + ErrorCode::SexpError(..) => 17, + ErrorCode::InvalidFeeLowFee(..) => 18, + ErrorCode::MempoolConflict(..) => 19, + ErrorCode::MintingCoin(..) => 20, + ErrorCode::ExtendsUnknownBlock(..) => 21, + ErrorCode::CoinbaseNotYetSpendable(..) => 22, + ErrorCode::CostExceeded(_) => 23, + ErrorCode::BadAdditionRoot(..) => 24, + ErrorCode::BadRemovalRoot(..) => 25, + ErrorCode::InvalidPospaceHash(..) => 26, + ErrorCode::InvalidCoinbaseSignature(..) => 27, + ErrorCode::InvalidPlotSignature(..) => 28, + ErrorCode::TimestampTooFarInPast(..) => 29, + ErrorCode::TimestampTooFarInFuture(..) => 30, + ErrorCode::InvalidTransactionsFilterHash(..) => 31, + ErrorCode::InvalidPospaceChallenge(..) => 32, + ErrorCode::InvalidPospace(..) => 33, + ErrorCode::InvalidHeight(..) => 34, + ErrorCode::InvalidCoinbaseAmount(..) => 35, + ErrorCode::InvalidMerkleRoot(..) => 36, + ErrorCode::InvalidBlockFeeAmount(..) => 37, + ErrorCode::InvalidWeight(..) => 38, + ErrorCode::InvalidTotalIters(..) => 39, + ErrorCode::BlockIsNotFinished(..) => 40, + ErrorCode::InvalidNumIterations(..) => 41, + ErrorCode::InvalidPot(..) => 42, + ErrorCode::InvalidPotChallenge(..) => 43, + ErrorCode::InvalidTransactionsGeneratorHash(..) => 44, + ErrorCode::InvalidPoolTarget(..) => 45, + ErrorCode::InvalidCoinbaseParent(..) => 46, + ErrorCode::InvalidFeesCoinParent(..) => 47, + ErrorCode::ReserveFeeConditionFailed(..) => 48, + ErrorCode::NotBlockButHasData(..) => 49, + ErrorCode::IsTransactionBlockButNoData(..) => 50, + ErrorCode::InvalidPrevBlockHash(..) => 51, + ErrorCode::InvalidTransactionsInfoHash(..) => 52, + ErrorCode::InvalidFoliageBlockHash(..) => 53, + ErrorCode::InvalidRewardCoins(..) => 54, + ErrorCode::InvalidBlockCost(..) => 55, + ErrorCode::NoEndOfSlotInfo(..) => 56, + ErrorCode::InvalidPrevChallengeSlotHash(..) => 57, + ErrorCode::InvalidSubEpochSummaryHash(..) => 58, + ErrorCode::NoSubEpochSummaryHash(..) => 59, + ErrorCode::ShouldNotMakeChallengeBlock(..) => 60, + ErrorCode::ShouldMakeChallengeBlock(..) => 61, + ErrorCode::InvalidChallengeChainData(..) => 62, + ErrorCode::InvalidCcEosVdf(..) => 65, + ErrorCode::InvalidRcEosVdf(..) => 66, + ErrorCode::InvalidChallengeSlotHashRc(..) => 67, + ErrorCode::InvalidPriorPointRc(..) => 68, + ErrorCode::InvalidDeficit(..) => 69, + ErrorCode::InvalidSubEpochSummary(..) => 70, + ErrorCode::InvalidPrevSubEpochSummaryHash(..) => 71, + ErrorCode::InvalidRewardChainHash(..) => 72, + ErrorCode::InvalidSubEpochOverflow(..) => 73, + ErrorCode::InvalidNewDifficulty(..) => 74, + ErrorCode::InvalidNewSubSlotIters(..) => 75, + ErrorCode::InvalidCcSpVdf(..) => 76, + ErrorCode::InvalidRcSpVdf(..) => 77, + ErrorCode::InvalidCcSignature(..) => 78, + ErrorCode::InvalidRcSignature(..) => 79, + ErrorCode::CannotMakeCcBlock(..) => 80, + ErrorCode::InvalidRcSpPrevIp(..) => 81, + ErrorCode::InvalidRcIpPrevIp(..) => 82, + ErrorCode::InvalidIsTransactionBlock(..) => 83, + ErrorCode::InvalidUrsbHash(..) => 84, + ErrorCode::OldPoolTarget(..) => 85, + ErrorCode::InvalidPoolSignature(..) => 86, + ErrorCode::InvalidFoliageBlockPresence(..) => 87, + ErrorCode::InvalidCcIpVdf(..) => 88, + ErrorCode::InvalidRcIpVdf(..) => 89, + ErrorCode::IpShouldBeNone(..) => 90, + ErrorCode::InvalidRewardBlockHash(..) => 91, + ErrorCode::InvalidMadeNonOverflowInfusions(..) => 92, + ErrorCode::NoOverflowsInFirstSubSlotNewEpoch(..) => 93, + ErrorCode::MempoolNotInitialized(..) => 94, + ErrorCode::ShouldNotHaveIcc(..) => 95, + ErrorCode::ShouldHaveIcc(..) => 96, + ErrorCode::InvalidIccVdf(..) => 97, + ErrorCode::InvalidIccHashCc(..) => 98, + ErrorCode::InvalidIccHashRc(..) => 99, + ErrorCode::InvalidIccEosVdf(..) => 100, + ErrorCode::InvalidSpIndex(..) => 101, + ErrorCode::TooManyBlocks(..) => 102, + ErrorCode::InvalidCcChallenge(..) => 103, + ErrorCode::InvalidPrefarm(..) => 104, + ErrorCode::AssertSecondsRelativeFailed(..) => 105, + ErrorCode::BadCoinbaseSignature(..) => 106, + // ErrorCode::InitialTransactionFreeze => 107 (removed in `chia-blockchain`` as well) + ErrorCode::NoTransactionsWhileSyncing(..) => 108, + ErrorCode::AlreadyIncludingTransaction(..) => 109, + ErrorCode::IncompatibleNetworkId(..) => 110, + ErrorCode::PreSoftForkMaxGeneratorSize(..) => 111, + ErrorCode::InvalidRequiredIters(..) => 112, + ErrorCode::TooManyGeneratorRefs => 113, + ErrorCode::AssertMyParentIdFailed(..) => 114, + ErrorCode::AssertMyPuzzleHashFailed(..) => 115, + ErrorCode::AssertMyAmountFailed(..) => 116, + ErrorCode::GeneratorRuntimeError(_) => 117, + ErrorCode::InvalidCostResult(..) => 118, + ErrorCode::InvalidTransactionsGeneratorRefsRoot(..) => 119, + ErrorCode::FutureGeneratorRefs(..) => 120, + ErrorCode::GeneratorRefHasNoGenerator(..) => 121, + ErrorCode::DoubleSpendInFork(..) => 122, + ErrorCode::InvalidFeeTooCloseToZero(..) => 123, + ErrorCode::CoinAmountNegative(..) => 124, + ErrorCode::InternalProtocolError(..) => 125, + ErrorCode::InvalidSpendBundle(..) => 126, + ErrorCode::FailedGettingGeneratorMultiprocessing(..) => 127, + ErrorCode::AssertBeforeSecondsAbsoluteFailed(..) => 128, + ErrorCode::AssertBeforeSecondsRelativeFailed(..) => 129, + ErrorCode::AssertBeforeHeightAbsoluteFailed(..) => 130, + ErrorCode::AssertBeforeHeightRelativeFailed(..) => 131, + ErrorCode::AssertConcurrentSpendFailed(..) => 132, + ErrorCode::AssertConcurrentPuzzleFailed(..) => 133, + ErrorCode::ImpossibleSecondsRelativeConstraints(..) => 134, + ErrorCode::ImpossibleSecondsAbsoluteConstraints(..) => 135, + ErrorCode::ImpossibleHeightRelativeConstraints(..) => 136, + ErrorCode::ImpossibleHeightAbsoluteConstraints(..) => 137, + ErrorCode::AssertMyBirthSecondsFailed(..) => 138, + ErrorCode::AssertMyBirthHeightFailed(..) => 139, + ErrorCode::AssertEphemeralFailed(..) => 140, + ErrorCode::EphemeralRelativeCondition(..) => 141, + ErrorCode::InvalidSoftforkCondition(..) => 142, + ErrorCode::InvalidSoftforkCost(..) => 143, + ErrorCode::TooManyAnnouncements(..) => 144, + ErrorCode::InvalidMessageMode(_) => 145, + ErrorCode::InvalidCoinId(..) => 146, + ErrorCode::MessageNotSentOrReceived => 147, + ErrorCode::ComplexGeneratorReceived => 148, + } + } +} + +pub fn rest(a: &Allocator, n: NodePtr) -> Result { + match a.sexp(n) { + SExp::Pair(_, right) => Ok(right), + SExp::Atom => Err(ErrorCode::InvalidCondition(n)), + } +} + +pub fn next(a: &Allocator, n: NodePtr) -> Result, ErrorCode> { + match a.sexp(n) { + SExp::Pair(left, right) => Ok(Some((left, right))), + SExp::Atom => { + // this is expected to be a valid list terminator + if a.atom_len(n) == 0 { + Ok(None) + } else { + Err(ErrorCode::InvalidCondition(n)) + } + } + } +} + +pub fn atom( + a: &Allocator, + n: NodePtr, + code: impl Fn(NodePtr) -> ErrorCode + Copy, +) -> Result, ErrorCode> { + match a.sexp(n) { + SExp::Atom => Ok(a.atom(n)), + SExp::Pair(..) => Err(code(n)), + } +} + +pub fn check_nil(a: &Allocator, n: NodePtr) -> Result<(), ErrorCode> { + if atom(a, n, ErrorCode::InvalidCondition)?.as_ref().is_empty() { + Ok(()) + } else { + Err(ErrorCode::InvalidCondition(n)) + } +} diff --git a/crates/chia-consensus/src/fast_forward.rs b/crates/chia-consensus/src/fast_forward.rs index e9baed0ad..9199a8603 100644 --- a/crates/chia-consensus/src/fast_forward.rs +++ b/crates/chia-consensus/src/fast_forward.rs @@ -156,7 +156,7 @@ mod tests { use crate::conditions::{ParseState, SpendBundleConditions, SpendConditions, parse_conditions}; use crate::consensus_constants::TEST_CONSTANTS; use crate::spend_visitor::SpendVisitor; - use crate::validation_error::ValidationErr; + use crate::error_code::ErrorCode; use chia_protocol::Bytes32; use chia_protocol::Coin; use chia_protocol::CoinSpend; @@ -179,7 +179,7 @@ mod tests { solution: &[u8], parent_id: &[u8], amount: u64, - ) -> core::result::Result { + ) -> core::result::Result { let puzzle = node_from_bytes(a, puzzle)?; let solution = node_from_bytes(a, solution)?; diff --git a/crates/chia-consensus/src/get_puzzle_and_solution.rs b/crates/chia-consensus/src/get_puzzle_and_solution.rs index b7163296b..c9a8893d3 100644 --- a/crates/chia-consensus/src/get_puzzle_and_solution.rs +++ b/crates/chia-consensus/src/get_puzzle_and_solution.rs @@ -1,4 +1,4 @@ -use crate::validation_error::{ErrorCode, ValidationErr, atom, check_nil, first, next, rest}; +use crate::error_code::{ErrorCode, atom, check_nil, first, next, rest}; use chia_protocol::Coin; use clvm_utils::{TreeCache, tree_hash_cached}; use clvmr::allocator::{Allocator, Atom, NodePtr}; @@ -8,7 +8,7 @@ use clvmr::op_utils::u64_from_bytes; pub fn parse_coin_spend( a: &Allocator, coin_spend: NodePtr, -) -> Result<(Atom<'_>, u64, NodePtr, NodePtr), ValidationErr> { +) -> Result<(Atom<'_>, u64, NodePtr, NodePtr), ErrorCode> { let parent = atom(a, first(a, coin_spend)?, ErrorCode::InvalidParentId)?; let coin_spend = rest(a, coin_spend)?; let puzzle = first(a, coin_spend)?; @@ -25,7 +25,7 @@ pub fn get_puzzle_and_solution_for_coin( a: &Allocator, generator_result: NodePtr, find_coin: &Coin, -) -> Result<(NodePtr, NodePtr), ValidationErr> { +) -> Result<(NodePtr, NodePtr), ErrorCode> { // the output from the block generator is a list of CoinSpends // with (parent-coin-id puzzle-reveal amount solution) // this function is given the generator output and a parent_coin_id, amount @@ -52,7 +52,7 @@ pub fn get_puzzle_and_solution_for_coin( // we found the coin! return Ok((puzzle, solution)); } - Err(ValidationErr(generator_result, ErrorCode::InvalidCondition)) + Err(ErrorCode::InvalidCondition(generator_result)) } #[cfg(test)] @@ -140,40 +140,37 @@ mod test { ); // wrong parent - assert_eq!( + assert!(matches!( get_puzzle_and_solution_for_coin( &a, generator_output, &Coin::new(make_dummy_id(2), tree_hash(&a, puzzle1).into(), 1337), ) - .unwrap_err() - .1, - ErrorCode::InvalidCondition - ); + .unwrap_err(), + ErrorCode::InvalidCondition(_) + )); // wrong amount - assert_eq!( + assert!(matches!( get_puzzle_and_solution_for_coin( &a, generator_output, &Coin::new(parent, tree_hash(&a, puzzle1).into(), 42), ) - .unwrap_err() - .1, - ErrorCode::InvalidCondition - ); + .unwrap_err(), + ErrorCode::InvalidCondition(_) + )); // wrong puzzle hash - assert_eq!( + assert!(matches!( get_puzzle_and_solution_for_coin( &a, generator_output, &Coin::new(parent, make_dummy_id(4), 1337), ) - .unwrap_err() - .1, - ErrorCode::InvalidCondition - ); + .unwrap_err(), + ErrorCode::InvalidCondition(_) + )); } #[test] @@ -194,17 +191,17 @@ mod test { // this is a spend where the parent is not an atom let spend2 = make_invalid_coin_spend(&mut a, puzzle2, amount_atom, puzzle1, solution1); - assert_eq!( - parse_coin_spend(&a, spend2).unwrap_err().1, - ErrorCode::InvalidParentId - ); + assert!(matches!( + parse_coin_spend(&a, spend2).unwrap_err(), + ErrorCode::InvalidParentId(_) + )); // this is a spend where the amount is not an atom let spend3 = make_invalid_coin_spend(&mut a, parent_atom, puzzle2, puzzle1, solution1); - assert_eq!( - parse_coin_spend(&a, spend3).unwrap_err().1, - ErrorCode::InvalidCoinAmount - ); + assert!(matches!( + parse_coin_spend(&a, spend3).unwrap_err(), + ErrorCode::InvalidCoinAmount(_) + )); } #[rstest] diff --git a/crates/chia-consensus/src/lib.rs b/crates/chia-consensus/src/lib.rs index 2d3230865..b0354db59 100644 --- a/crates/chia-consensus/src/lib.rs +++ b/crates/chia-consensus/src/lib.rs @@ -26,7 +26,7 @@ pub mod solution_generator; pub mod spend_visitor; pub mod spendbundle_conditions; pub mod spendbundle_validation; -pub mod validation_error; +pub mod error_code; // these tests are large and expensive. They take a long time to run in // unoptimized builds. Only run these with --release diff --git a/crates/chia-consensus/src/messages.rs b/crates/chia-consensus/src/messages.rs index 94ee5d4d8..7015c4bee 100644 --- a/crates/chia-consensus/src/messages.rs +++ b/crates/chia-consensus/src/messages.rs @@ -1,6 +1,6 @@ use crate::condition_sanitizers::sanitize_hash; use crate::sanitize_int::{SanitizedUint, sanitize_uint}; -use crate::validation_error::{ErrorCode, ValidationErr, first, rest}; +use crate::error_code::{ErrorCode, first, rest}; use chia_protocol::Bytes32; use clvmr::{Allocator, NodePtr}; use std::sync::Arc; @@ -31,7 +31,7 @@ pub enum SpendId { impl SpendId { // args is an in-out parameter. It's updated to point to then next argument - pub fn parse(a: &Allocator, args: &mut NodePtr, mode: u8) -> Result { + pub fn parse(a: &Allocator, args: &mut NodePtr, mode: u8) -> Result { // we have a special case for when all three mode flags are set. That means // we're committing to parent, puzzle and amount. In this case you just // specify the coin ID @@ -61,10 +61,10 @@ impl SpendId { let amount = match sanitize_uint(a, first(a, *args)?, 8, ErrorCode::InvalidCoinAmount)? { SanitizedUint::PositiveOverflow => { - return Err(ValidationErr(*args, ErrorCode::CoinAmountExceedsMaximum)); + return Err(ErrorCode::CoinAmountExceedsMaximum(*args)); } SanitizedUint::NegativeOverflow => { - return Err(ValidationErr(*args, ErrorCode::CoinAmountNegative)); + return Err(ErrorCode::CoinAmountNegative(*args)); } SanitizedUint::Ok(amount) => amount, }; @@ -82,7 +82,7 @@ impl SpendId { PARENTAMOUNT => Ok(Self::ParentAmount(parent, amount)), PUZZLEAMOUNT => Ok(Self::PuzzleAmount(puzzle, amount)), 0 => Ok(Self::None), - _ => Err(ValidationErr(*args, ErrorCode::InvalidMessageMode)), + _ => Err(ErrorCode::InvalidMessageMode(Some(*args))), } } @@ -92,7 +92,7 @@ impl SpendId { puzzle: NodePtr, amount: u64, coin_id: &Arc, - ) -> Result { + ) -> Result { if mode == COINID { return Ok(Self::OwnedCoinId(coin_id.clone())); } @@ -105,7 +105,7 @@ impl SpendId { PARENTAMOUNT => Ok(Self::ParentAmount(parent, amount)), PUZZLEAMOUNT => Ok(Self::PuzzleAmount(puzzle, amount)), 0 => Ok(Self::None), - _ => Err(ValidationErr(NodePtr::NIL, ErrorCode::InvalidMessageMode)), + _ => Err(ErrorCode::InvalidMessageMode(None)), } } diff --git a/crates/chia-consensus/src/puzzle_fingerprint.rs b/crates/chia-consensus/src/puzzle_fingerprint.rs index 3c984c7af..f502e347a 100644 --- a/crates/chia-consensus/src/puzzle_fingerprint.rs +++ b/crates/chia-consensus/src/puzzle_fingerprint.rs @@ -8,7 +8,7 @@ use super::opcodes::{ REMARK, RESERVE_FEE, parse_opcode, }; use crate::flags::MEMPOOL_MODE; -use crate::validation_error::{ErrorCode, ValidationErr, first}; +use crate::error_code::{ErrorCode, first}; use chia_sha2::Sha256; use clvmr::chia_dialect::ENABLE_KECCAK_OPS_OUTSIDE_GUARD; use clvmr::{Allocator, NodePtr, SExp}; @@ -21,15 +21,15 @@ fn hash_atom_list( a: &Allocator, mut args: NodePtr, mut count: u32, -) -> Result { +) -> Result { while count > 0 { let Some((arg, next)) = a.next(args) else { - return Err(ValidationErr(args, ErrorCode::InvalidCondition)); + return Err(ErrorCode::InvalidCondition(args)); }; args = next; count -= 1; if !matches!(a.sexp(arg), SExp::Atom) { - return Err(ValidationErr(arg, ErrorCode::InvalidCondition)); + return Err(ErrorCode::InvalidCondition(arg)); } let buf = a.atom(arg); @@ -55,7 +55,7 @@ fn hash_atom_list( pub fn compute_puzzle_fingerprint( a: &Allocator, conditions: NodePtr, -) -> core::result::Result<[u8; 32], ValidationErr> { +) -> core::result::Result<[u8; 32], ErrorCode> { // keep in mind that the puzzle has already been validated by the mempool, // so it's trusted. It's OK to enable features that aren't available yet, // because if the puzzle would use them prematurely, the validation would @@ -136,7 +136,7 @@ pub fn compute_puzzle_fingerprint( hash_atom_list(&mut fingerprint, a, c, 1)?; } _ => { - return Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)); + return Err(ErrorCode::InvalidConditionOpcode(c)); } } } @@ -215,10 +215,10 @@ mod tests { let mut ctx1 = Sha256::new(); // we expect 2 elements, but there's only 1 - assert_eq!( - hash_atom_list(&mut ctx1, &a, list, 2).unwrap_err().1, - ErrorCode::InvalidCondition - ); + assert!(matches!( + hash_atom_list(&mut ctx1, &a, list, 2).unwrap_err(), + ErrorCode::InvalidCondition(_) + )); } #[test] @@ -230,10 +230,10 @@ mod tests { let mut ctx1 = Sha256::new(); // we expect all elements to be atoms, but we encountered a pair - assert_eq!( - hash_atom_list(&mut ctx1, &a, list, 1).unwrap_err().1, - ErrorCode::InvalidCondition - ); + assert!(matches!( + hash_atom_list(&mut ctx1, &a, list, 1).unwrap_err(), + ErrorCode::InvalidCondition(_) + )); } #[rstest] diff --git a/crates/chia-consensus/src/run_block_generator.rs b/crates/chia-consensus/src/run_block_generator.rs index cace8c6fe..bd9e257a5 100644 --- a/crates/chia-consensus/src/run_block_generator.rs +++ b/crates/chia-consensus/src/run_block_generator.rs @@ -6,7 +6,7 @@ use crate::conditions::{ }; use crate::consensus_constants::ConsensusConstants; use crate::flags::{DONT_VALIDATE_SIGNATURE, SIMPLE_GENERATOR}; -use crate::validation_error::{ErrorCode, ValidationErr, first}; +use crate::error_code::{ErrorCode, first}; use chia_bls::{BlsCache, Signature}; use chia_protocol::{BytesImpl, Coin, CoinSpend, Program}; use chia_puzzles::{CHIALISP_DESERIALISATION, ROM_BOOTSTRAP_GENERATOR}; @@ -22,12 +22,12 @@ use clvmr::run_program::run_program; use clvmr::serde::{node_from_bytes, node_from_bytes_backrefs}; pub fn subtract_cost( - a: &Allocator, + _a: &Allocator, cost_left: &mut Cost, subtract: Cost, -) -> Result<(), ValidationErr> { +) -> Result<(), ErrorCode> { if subtract > *cost_left { - Err(ValidationErr(a.nil(), ErrorCode::CostExceeded)) + Err(ErrorCode::CostExceeded(None)) } else { *cost_left -= subtract; Ok(()) @@ -40,7 +40,7 @@ pub fn setup_generator_args, I: IntoIterator> a: &mut Allocator, block_refs: I, flags: u32, -) -> Result +) -> Result where ::IntoIter: DoubleEndedIterator, { @@ -48,7 +48,7 @@ where // need to pass in the deserialization program if (flags & SIMPLE_GENERATOR) != 0 { if block_refs.into_iter().next().is_some() { - return Err(ValidationErr(a.nil(), ErrorCode::TooManyGeneratorRefs)); + return Err(ErrorCode::TooManyGeneratorRefs); } return Ok(a.nil()); } @@ -92,7 +92,7 @@ pub fn run_block_generator, I: IntoIterator>( signature: &Signature, bls_cache: Option<&BlsCache>, constants: &ConsensusConstants, -) -> Result +) -> Result where ::IntoIter: DoubleEndedIterator, { @@ -146,8 +146,8 @@ where fn extract_n( a: &Allocator, mut n: NodePtr, - e: ErrorCode, -) -> Result<[NodePtr; N], ValidationErr> { + e: impl Fn(NodePtr) -> ErrorCode + Copy, +) -> Result<[NodePtr; N], ErrorCode> { let mut ret: [NodePtr; N] = [NodePtr::NIL; N]; let mut counter = 0; assert!(N > 0); @@ -160,7 +160,7 @@ fn extract_n( counter += 1; } if counter != N - 1 { - return Err(ValidationErr(n, e)); + return Err(e(n)); } ret[counter] = n; Ok(ret) @@ -170,14 +170,14 @@ fn extract_n( // this is required after the SIMPLE_GENERATOR fork is active #[inline] pub fn check_generator_quote( - a: &Allocator, + _a: &Allocator, program: &[u8], flags: u32, -) -> Result<(), ValidationErr> { +) -> Result<(), ErrorCode> { if flags & SIMPLE_GENERATOR == 0 || program.starts_with(&[0xff, 0x01]) { Ok(()) } else { - Err(ValidationErr(a.nil(), ErrorCode::ComplexGeneratorReceived)) + Err(ErrorCode::ComplexGeneratorReceived) } } @@ -188,13 +188,13 @@ pub fn check_generator_node( a: &Allocator, program: NodePtr, flags: u32, -) -> Result<(), ValidationErr> { +) -> Result<(), ErrorCode> { if flags & SIMPLE_GENERATOR == 0 { return Ok(()); } // this expects an atom with a single byte value of 1 as the first value in the list match <(MatchByte<1>, NodePtr)>::from_clvm(a, program) { - Err(..) => Err(ValidationErr(a.nil(), ErrorCode::ComplexGeneratorReceived)), + Err(..) => Err(ErrorCode::ComplexGeneratorReceived), _ => Ok(()), } } @@ -215,7 +215,7 @@ pub fn run_block_generator2, I: IntoIterator> signature: &Signature, bls_cache: Option<&BlsCache>, constants: &ConsensusConstants, -) -> Result +) -> Result where ::IntoIter: DoubleEndedIterator, { @@ -288,7 +288,7 @@ where )?; } if a.atom_len(iter) != 0 { - return Err(ValidationErr(iter, ErrorCode::GeneratorRuntimeError)); + return Err(ErrorCode::GeneratorRuntimeError(Some(iter))); } validate_conditions(a, &ret, &state, a.nil(), flags)?; @@ -306,7 +306,7 @@ pub fn get_coinspends_for_trusted_block, I: IntoIterator Result, ValidationErr> +) -> Result, ErrorCode> where ::IntoIter: DoubleEndedIterator, { @@ -329,7 +329,7 @@ where let (first, _rest) = a .next(res) - .ok_or(ValidationErr(res, ErrorCode::GeneratorRuntimeError))?; + .ok_or(ErrorCode::GeneratorRuntimeError(Some(res)))?; let mut cache = TreeCache::default(); let mut iter = first; while let Some((spend, rest)) = a.next(iter) { @@ -349,7 +349,7 @@ where }; let puzhash = tree_hash_cached(&a, puzzle, &mut cache); let parent_id = BytesImpl::<32>::from_clvm(&a, parent_id) - .map_err(|_| ValidationErr(first, ErrorCode::InvalidParentId))?; + .map_err(|_| ErrorCode::InvalidParentId(first))?; let coin = Coin::new( parent_id, puzhash.into(), @@ -381,7 +381,7 @@ pub fn get_coinspends_with_conditions_for_trusted_block< generator: &Program, refs: I, flags: u32, -) -> Result>)>)>, ValidationErr> +) -> Result>)>)>, ErrorCode> where ::IntoIter: DoubleEndedIterator, { @@ -401,11 +401,11 @@ where args, constants.max_block_cost_clvm, ) - .map_err(|_| ValidationErr(program, ErrorCode::GeneratorRuntimeError))?; + .map_err(|_| ErrorCode::GeneratorRuntimeError(Some(program)))?; let (first, _rest) = a .next(res) - .ok_or(ValidationErr(res, ErrorCode::GeneratorRuntimeError))?; + .ok_or(ErrorCode::GeneratorRuntimeError(Some(res)))?; let mut cache = TreeCache::default(); let mut iter = first; while let Some((spend, rest)) = a.next(iter) { @@ -424,12 +424,12 @@ where }; let puzhash = tree_hash_cached(&a, puzzle, &mut cache); let parent_id = BytesImpl::<32>::from_clvm(&a, parent_id) - .map_err(|_| ValidationErr(first, ErrorCode::InvalidParentId))?; + .map_err(|_| ErrorCode::InvalidParentId(first))?; let coin = Coin::new( parent_id, puzhash.into(), u64::from_clvm(&a, amount) - .map_err(|_| ValidationErr(first, ErrorCode::InvalidCoinAmount))?, + .map_err(|_| ErrorCode::InvalidCoinAmount(first))?, ); let puzzle_program = Program::from_clvm(&a, puzzle).unwrap_or_default(); let solution_program = Program::from_clvm(&a, solution).unwrap_or_default(); @@ -441,7 +441,7 @@ where solution, constants.max_block_cost_clvm, ) - .map_err(|_| ValidationErr(program, ErrorCode::GeneratorRuntimeError))?; + .map_err(|_| ErrorCode::GeneratorRuntimeError(Some(program)))?; // conditions_list is the full returned output of puzzle ran with solution // ((51 0xcafef00d 100) (51 0x1234 200) ...) diff --git a/crates/chia-consensus/src/sanitize_int.rs b/crates/chia-consensus/src/sanitize_int.rs index 0eb7ab5ee..e1c1ad36b 100644 --- a/crates/chia-consensus/src/sanitize_int.rs +++ b/crates/chia-consensus/src/sanitize_int.rs @@ -1,4 +1,4 @@ -use super::validation_error::{ErrorCode, ValidationErr, atom}; +use super::error_code::{ErrorCode, atom}; use clvmr::allocator::{Allocator, NodePtr}; use clvmr::op_utils::u64_from_bytes; @@ -14,8 +14,8 @@ pub fn sanitize_uint( a: &Allocator, n: NodePtr, max_size: usize, - code: ErrorCode, -) -> Result { + code: impl Fn(NodePtr) -> ErrorCode + Copy, +) -> Result { assert!(max_size <= 8); let buf = atom(a, n, code)?; @@ -34,7 +34,7 @@ pub fn sanitize_uint( // be interpreted as a negative integer. i.e. if the next top bit is set // all other leading zeros are invalid if buf == [0_u8] || (buf.len() > 1 && buf[0] == 0 && (buf[1] & 0x80) == 0) { - return Err(ValidationErr(n, code)); + return Err(code(n)); } // strip the leading zero byte if there is one @@ -75,27 +75,27 @@ fn test_sanitize_uint() { let just_zeros = a.new_substr(atom, 10, 70).unwrap(); // a zero value must be represented by an empty atom assert_eq!( - sanitize_uint(&a, just_zeros, 8, e).unwrap_err().1, - ErrorCode::InvalidCoinAmount + sanitize_uint(&a, just_zeros, 8, e).unwrap_err(), + ErrorCode::InvalidCoinAmount(just_zeros) ); let a1 = a.new_substr(atom, 1, 101).unwrap(); assert_eq!( - sanitize_uint(&a, a1, 8, e).unwrap_err().1, - ErrorCode::InvalidCoinAmount + sanitize_uint(&a, a1, 8, e).unwrap_err(), + ErrorCode::InvalidCoinAmount(a1) ); let a1 = a.new_substr(atom, 1, 101).unwrap(); assert_eq!( - sanitize_uint(&a, a1, 8, e).unwrap_err().1, - ErrorCode::InvalidCoinAmount + sanitize_uint(&a, a1, 8, e).unwrap_err(), + ErrorCode::InvalidCoinAmount(a1) ); // a new all-zeros range let a1 = a.new_substr(atom, 1000, 1024).unwrap(); assert_eq!( - sanitize_uint(&a, a1, 8, e).unwrap_err().1, - ErrorCode::InvalidCoinAmount + sanitize_uint(&a, a1, 8, e).unwrap_err(), + ErrorCode::InvalidCoinAmount(a1) ); let exceed_maximum = a.new_substr(atom, 100, 110).unwrap(); diff --git a/crates/chia-consensus/src/spend_visitor.rs b/crates/chia-consensus/src/spend_visitor.rs index 92781249a..5cde0e4de 100644 --- a/crates/chia-consensus/src/spend_visitor.rs +++ b/crates/chia-consensus/src/spend_visitor.rs @@ -1,5 +1,5 @@ use crate::conditions::{Condition, ParseState, SpendBundleConditions, SpendConditions}; -use crate::validation_error::ValidationErr; +use crate::error_code::ErrorCode; use clvmr::allocator::Allocator; /// These are customization points for the condition parsing and validation. The @@ -14,5 +14,5 @@ pub trait SpendVisitor { a: &Allocator, state: &ParseState, bundle: &mut SpendBundleConditions, - ) -> Result<(), ValidationErr>; + ) -> Result<(), ErrorCode>; } diff --git a/crates/chia-consensus/src/spendbundle_conditions.rs b/crates/chia-consensus/src/spendbundle_conditions.rs index 672ec7a07..83b354e6b 100644 --- a/crates/chia-consensus/src/spendbundle_conditions.rs +++ b/crates/chia-consensus/src/spendbundle_conditions.rs @@ -8,8 +8,7 @@ use crate::puzzle_fingerprint::compute_puzzle_fingerprint; use crate::run_block_generator::subtract_cost; use crate::solution_generator::calculate_generator_length; use crate::spendbundle_validation::get_flags_for_height_and_constants; -use crate::validation_error::ErrorCode; -use crate::validation_error::ValidationErr; +use crate::error_code::ErrorCode; use chia_bls::PublicKey; use chia_protocol::{Bytes, SpendBundle}; @@ -28,7 +27,7 @@ pub fn get_conditions_from_spendbundle( max_cost: u64, prev_tx_height: u32, constants: &ConsensusConstants, -) -> Result { +) -> Result { let flags = get_flags_for_height_and_constants(prev_tx_height, constants); Ok(run_spendbundle( a, @@ -49,7 +48,7 @@ pub fn run_spendbundle( max_cost: u64, flags: u32, constants: &ConsensusConstants, -) -> Result<(SpendBundleConditions, Vec<(PublicKey, Bytes)>), ValidationErr> { +) -> Result<(SpendBundleConditions, Vec<(PublicKey, Bytes)>), ErrorCode> { // below is an adapted version of the code from run_block_generators::run_block_generator2() // it assumes no block references are passed in let mut cost_left = max_cost; @@ -77,7 +76,7 @@ pub fn run_spendbundle( let buf = tree_hash(a, puz); if coin_spend.coin.puzzle_hash != buf.into() { - return Err(ValidationErr(puz, ErrorCode::WrongPuzzleHash)); + return Err(ErrorCode::WrongPuzzleHash(puz)); } let puzzle_hash = a.new_atom(&buf)?; let spend = process_single_spend::( diff --git a/crates/chia-consensus/src/spendbundle_validation.rs b/crates/chia-consensus/src/spendbundle_validation.rs index b691159f8..54864ba3a 100644 --- a/crates/chia-consensus/src/spendbundle_validation.rs +++ b/crates/chia-consensus/src/spendbundle_validation.rs @@ -3,13 +3,13 @@ use crate::consensus_constants::ConsensusConstants; use crate::flags::{COST_CONDITIONS, SIMPLE_GENERATOR}; use crate::owned_conditions::OwnedSpendBundleConditions; use crate::spendbundle_conditions::run_spendbundle; -use crate::validation_error::{ErrorCode, ValidationErr}; +use crate::error_code::ErrorCode; use chia_bls::GTElement; use chia_bls::{aggregate_verify_gt, hash_to_g2}; use chia_protocol::SpendBundle; use chia_sha2::Sha256; use clvmr::chia_dialect::{DISABLE_OP, ENABLE_KECCAK_OPS_OUTSIDE_GUARD}; -use clvmr::{LIMIT_HEAP, NodePtr}; +use clvmr::LIMIT_HEAP; // type definition makes clippy happy pub type ValidationPair = ([u8; 32], GTElement); @@ -22,7 +22,7 @@ pub fn validate_clvm_and_signature( max_cost: u64, constants: &ConsensusConstants, flags: u32, -) -> Result<(OwnedSpendBundleConditions, Vec), ValidationErr> { +) -> Result<(OwnedSpendBundleConditions, Vec), ErrorCode> { let mut a = make_allocator(LIMIT_HEAP); let (sbc, pkm_pairs) = run_spendbundle(&mut a, spend_bundle, max_cost, flags, constants)?; let conditions = OwnedSpendBundleConditions::from(&a, sbc); @@ -49,10 +49,7 @@ pub fn validate_clvm_and_signature( pairs.iter().map(|tuple| &tuple.1), ); if !result { - return Err(ValidationErr( - NodePtr::NIL, - ErrorCode::BadAggregateSignature, - )); + return Err(ErrorCode::BadAggregateSignature); } // Collect results @@ -243,17 +240,16 @@ ff01\ coin_spends: vec![spend], aggregated_signature: Signature::default(), }; - assert_eq!( + assert!(matches!( validate_clvm_and_signature( &spend_bundle, TEST_CONSTANTS.max_block_cost_clvm, &TEST_CONSTANTS, MEMPOOL_MODE, ) - .unwrap_err() - .1, - ErrorCode::WrongPuzzleHash - ); + .unwrap_err(), + ErrorCode::WrongPuzzleHash(_) + )); } #[test] @@ -332,7 +328,7 @@ ff843B9ACA00\ validate_clvm_and_signature(&spend_bundle, max_cost - 1, &TEST_CONSTANTS, MEMPOOL_MODE); assert!(matches!( result, - Err(ValidationErr(_, ErrorCode::CostExceeded)) + Err(ErrorCode::CostExceeded(_)) )); } @@ -417,7 +413,7 @@ ff843B9ACA00\ ); assert!(matches!( result, - Err(ValidationErr(_, ErrorCode::BadAggregateSignature)) + Err(ErrorCode::BadAggregateSignature) )); } } diff --git a/crates/chia-consensus/src/test_generators.rs b/crates/chia-consensus/src/test_generators.rs index 901289629..c5927d471 100644 --- a/crates/chia-consensus/src/test_generators.rs +++ b/crates/chia-consensus/src/test_generators.rs @@ -7,7 +7,7 @@ use crate::allocator::make_allocator; use crate::consensus_constants::TEST_CONSTANTS; use crate::flags::{COST_CONDITIONS, DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE, SIMPLE_GENERATOR}; use crate::run_block_generator::check_generator_node; -use crate::validation_error::ErrorCode; +use crate::error_code::ErrorCode; use chia_bls::Signature; use chia_protocol::Program; use chia_protocol::{Bytes, Bytes48}; @@ -318,16 +318,13 @@ fn run_generator(#[case] name: &str) { None, &TEST_CONSTANTS, ); - assert_eq!( - test_conds.unwrap_err().1, - ErrorCode::ComplexGeneratorReceived - ); + assert_eq!(test_conds.unwrap_err(), ErrorCode::ComplexGeneratorReceived); // now lets specifically check the node generator check let program = node_from_bytes_backrefs(&mut a, generator.as_ref()) .expect("node_from_bytes_backref"); let res = check_generator_node(&a, program, flags | SIMPLE_GENERATOR); - assert_eq!(res.unwrap_err().1, ErrorCode::ComplexGeneratorReceived); + assert_eq!(res.unwrap_err(), ErrorCode::ComplexGeneratorReceived); } else { flags |= SIMPLE_GENERATOR; // ensure SIMPLE_GENERATOR fails if there are any block references @@ -343,7 +340,7 @@ fn run_generator(#[case] name: &str) { None, &TEST_CONSTANTS, ); - assert_eq!(test_conds.unwrap_err().1, ErrorCode::TooManyGeneratorRefs); + assert_eq!(test_conds.unwrap_err(), ErrorCode::TooManyGeneratorRefs); // now lets specifically check the node generator check let program = node_from_bytes_backrefs(&mut a, generator.as_ref()) diff --git a/crates/chia-consensus/src/validation_error.rs b/crates/chia-consensus/src/validation_error.rs deleted file mode 100644 index f1328d5cc..000000000 --- a/crates/chia-consensus/src/validation_error.rs +++ /dev/null @@ -1,401 +0,0 @@ -use clvmr::allocator::{Allocator, Atom, NodePtr, SExp}; -use clvmr::error::EvalErr; -use thiserror::Error; - -#[cfg(feature = "py-bindings")] -use pyo3::PyErr; - -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum ErrorCode { - #[default] - Unknown, - InvalidBlockSolution, - InvalidCoinSolution, - DuplicateOutput, - DoubleSpend, - UnknownUnspent, - BadAggregateSignature, - WrongPuzzleHash, - BadFarmerCoinAmount, - InvalidCondition, - InvalidConditionOpcode, - InvalidParentId, - InvalidPuzzleHash, - InvalidPublicKey, - InvalidMessage, - InvalidCoinAmount, - InvalidCoinAnnouncement, - InvalidPuzzleAnnouncement, - AssertMyCoinIdFailed, - AssertPuzzleAnnouncementFailed, - AssertCoinAnnouncementFailed, - AssertHeightRelativeFailed, - AssertHeightAbsoluteFailed, - AssertSecondsAbsoluteFailed, - CoinAmountExceedsMaximum, - SexpError, - InvalidFeeLowFee, - MempoolConflict, - MintingCoin, - ExtendsUnknownBlock, - CoinbaseNotYetSpendable, - /// Renamed from "BlockCostExceedsMax" since it's more generic than that. - CostExceeded, - BadAdditionRoot, - BadRemovalRoot, - InvalidPospaceHash, - InvalidCoinbaseSignature, - InvalidPlotSignature, - TimestampTooFarInPast, - TimestampTooFarInFuture, - InvalidTransactionsFilterHash, - InvalidPospaceChallenge, - InvalidPospace, - InvalidHeight, - InvalidCoinbaseAmount, - InvalidMerkleRoot, - InvalidBlockFeeAmount, - InvalidWeight, - InvalidTotalIters, - BlockIsNotFinished, - InvalidNumIterations, - InvalidPot, - InvalidPotChallenge, - InvalidTransactionsGeneratorHash, - InvalidPoolTarget, - InvalidCoinbaseParent, - InvalidFeesCoinParent, - ReserveFeeConditionFailed, - NotBlockButHasData, - IsTransactionBlockButNoData, - InvalidPrevBlockHash, - InvalidTransactionsInfoHash, - InvalidFoliageBlockHash, - InvalidRewardCoins, - InvalidBlockCost, - NoEndOfSlotInfo, - InvalidPrevChallengeSlotHash, - InvalidSubEpochSummaryHash, - NoSubEpochSummaryHash, - ShouldNotMakeChallengeBlock, - ShouldMakeChallengeBlock, - InvalidChallengeChainData, - InvalidCcEosVdf, - InvalidRcEosVdf, - InvalidChallengeSlotHashRc, - InvalidPriorPointRc, - InvalidDeficit, - InvalidSubEpochSummary, - InvalidPrevSubEpochSummaryHash, - InvalidRewardChainHash, - InvalidSubEpochOverflow, - InvalidNewDifficulty, - InvalidNewSubSlotIters, - InvalidCcSpVdf, - InvalidRcSpVdf, - InvalidCcSignature, - InvalidRcSignature, - CannotMakeCcBlock, - InvalidRcSpPrevIp, - InvalidRcIpPrevIp, - InvalidIsTransactionBlock, - InvalidUrsbHash, - OldPoolTarget, - InvalidPoolSignature, - InvalidFoliageBlockPresence, - InvalidCcIpVdf, - InvalidRcIpVdf, - IpShouldBeNone, - InvalidRewardBlockHash, - InvalidMadeNonOverflowInfusions, - NoOverflowsInFirstSubSlotNewEpoch, - MempoolNotInitialized, - ShouldNotHaveIcc, - ShouldHaveIcc, - InvalidIccVdf, - InvalidIccHashCc, - InvalidIccHashRc, - InvalidIccEosVdf, - InvalidSpIndex, - TooManyBlocks, - InvalidCcChallenge, - InvalidPrefarm, - AssertSecondsRelativeFailed, - BadCoinbaseSignature, - // InitialTransactionFreeze (removed in `chia-blockchain` as well) - NoTransactionsWhileSyncing, - AlreadyIncludingTransaction, - IncompatibleNetworkId, - PreSoftForkMaxGeneratorSize, - InvalidRequiredIters, - TooManyGeneratorRefs, - AssertMyParentIdFailed, - AssertMyPuzzleHashFailed, - AssertMyAmountFailed, - GeneratorRuntimeError, - InvalidCostResult, - InvalidTransactionsGeneratorRefsRoot, - FutureGeneratorRefs, - GeneratorRefHasNoGenerator, - DoubleSpendInFork, - InvalidFeeTooCloseToZero, - CoinAmountNegative, - InternalProtocolError, - InvalidSpendBundle, - FailedGettingGeneratorMultiprocessing, - AssertBeforeSecondsAbsoluteFailed, - AssertBeforeSecondsRelativeFailed, - AssertBeforeHeightAbsoluteFailed, - AssertBeforeHeightRelativeFailed, - AssertConcurrentSpendFailed, - AssertConcurrentPuzzleFailed, - ImpossibleSecondsRelativeConstraints, - ImpossibleSecondsAbsoluteConstraints, - ImpossibleHeightRelativeConstraints, - ImpossibleHeightAbsoluteConstraints, - AssertMyBirthSecondsFailed, - AssertMyBirthHeightFailed, - AssertEphemeralFailed, - EphemeralRelativeCondition, - InvalidSoftforkCondition, - InvalidSoftforkCost, - TooManyAnnouncements, - InvalidMessageMode, - InvalidCoinId, - MessageNotSentOrReceived, - ComplexGeneratorReceived, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] -#[error("validation error: {1:?}")] -pub struct ValidationErr(pub NodePtr, pub ErrorCode); - -impl From for ValidationErr { - fn from(v: EvalErr) -> Self { - match v { - EvalErr::CostExceeded => ValidationErr(v.node_ptr(), ErrorCode::CostExceeded), - _ => ValidationErr(v.node_ptr(), ErrorCode::GeneratorRuntimeError), - } - } -} - -impl From for ValidationErr { - fn from(_: std::io::Error) -> Self { - ValidationErr(NodePtr::NIL, ErrorCode::GeneratorRuntimeError) - } -} - -#[cfg(feature = "py-bindings")] -impl From for PyErr { - fn from(err: ValidationErr) -> PyErr { - pyo3::exceptions::PyValueError::new_err(("ValidationError", u32::from(err.1))) - } -} - -// helper functions that fail with ValidationErr -pub fn first(a: &Allocator, n: NodePtr) -> Result { - match a.sexp(n) { - SExp::Pair(left, _) => Ok(left), - SExp::Atom => Err(ValidationErr(n, ErrorCode::InvalidCondition)), - } -} - -// from chia-blockchain/chia/util/errors.py -impl From for u32 { - fn from(err: ErrorCode) -> u32 { - match err { - ErrorCode::Unknown => 1, - ErrorCode::InvalidBlockSolution => 2, - ErrorCode::InvalidCoinSolution => 3, - ErrorCode::DuplicateOutput => 4, - ErrorCode::DoubleSpend => 5, - ErrorCode::UnknownUnspent => 6, - ErrorCode::BadAggregateSignature => 7, - ErrorCode::WrongPuzzleHash => 8, - ErrorCode::BadFarmerCoinAmount => 9, - ErrorCode::InvalidCondition - | ErrorCode::InvalidConditionOpcode - | ErrorCode::InvalidParentId - | ErrorCode::InvalidPuzzleHash - | ErrorCode::InvalidPublicKey - | ErrorCode::InvalidMessage - | ErrorCode::InvalidCoinAmount - | ErrorCode::InvalidCoinAnnouncement - | ErrorCode::InvalidPuzzleAnnouncement => 10, - ErrorCode::AssertMyCoinIdFailed => 11, - ErrorCode::AssertPuzzleAnnouncementFailed | ErrorCode::AssertCoinAnnouncementFailed => { - 12 - } - ErrorCode::AssertHeightRelativeFailed => 13, - ErrorCode::AssertHeightAbsoluteFailed => 14, - ErrorCode::AssertSecondsAbsoluteFailed => 15, - ErrorCode::CoinAmountExceedsMaximum => 16, - ErrorCode::SexpError => 17, - ErrorCode::InvalidFeeLowFee => 18, - ErrorCode::MempoolConflict => 19, - ErrorCode::MintingCoin => 20, - ErrorCode::ExtendsUnknownBlock => 21, - ErrorCode::CoinbaseNotYetSpendable => 22, - ErrorCode::CostExceeded => 23, - ErrorCode::BadAdditionRoot => 24, - ErrorCode::BadRemovalRoot => 25, - ErrorCode::InvalidPospaceHash => 26, - ErrorCode::InvalidCoinbaseSignature => 27, - ErrorCode::InvalidPlotSignature => 28, - ErrorCode::TimestampTooFarInPast => 29, - ErrorCode::TimestampTooFarInFuture => 30, - ErrorCode::InvalidTransactionsFilterHash => 31, - ErrorCode::InvalidPospaceChallenge => 32, - ErrorCode::InvalidPospace => 33, - ErrorCode::InvalidHeight => 34, - ErrorCode::InvalidCoinbaseAmount => 35, - ErrorCode::InvalidMerkleRoot => 36, - ErrorCode::InvalidBlockFeeAmount => 37, - ErrorCode::InvalidWeight => 38, - ErrorCode::InvalidTotalIters => 39, - ErrorCode::BlockIsNotFinished => 40, - ErrorCode::InvalidNumIterations => 41, - ErrorCode::InvalidPot => 42, - ErrorCode::InvalidPotChallenge => 43, - ErrorCode::InvalidTransactionsGeneratorHash => 44, - ErrorCode::InvalidPoolTarget => 45, - ErrorCode::InvalidCoinbaseParent => 46, - ErrorCode::InvalidFeesCoinParent => 47, - ErrorCode::ReserveFeeConditionFailed => 48, - ErrorCode::NotBlockButHasData => 49, - ErrorCode::IsTransactionBlockButNoData => 50, - ErrorCode::InvalidPrevBlockHash => 51, - ErrorCode::InvalidTransactionsInfoHash => 52, - ErrorCode::InvalidFoliageBlockHash => 53, - ErrorCode::InvalidRewardCoins => 54, - ErrorCode::InvalidBlockCost => 55, - ErrorCode::NoEndOfSlotInfo => 56, - ErrorCode::InvalidPrevChallengeSlotHash => 57, - ErrorCode::InvalidSubEpochSummaryHash => 58, - ErrorCode::NoSubEpochSummaryHash => 59, - ErrorCode::ShouldNotMakeChallengeBlock => 60, - ErrorCode::ShouldMakeChallengeBlock => 61, - ErrorCode::InvalidChallengeChainData => 62, - ErrorCode::InvalidCcEosVdf => 65, - ErrorCode::InvalidRcEosVdf => 66, - ErrorCode::InvalidChallengeSlotHashRc => 67, - ErrorCode::InvalidPriorPointRc => 68, - ErrorCode::InvalidDeficit => 69, - ErrorCode::InvalidSubEpochSummary => 70, - ErrorCode::InvalidPrevSubEpochSummaryHash => 71, - ErrorCode::InvalidRewardChainHash => 72, - ErrorCode::InvalidSubEpochOverflow => 73, - ErrorCode::InvalidNewDifficulty => 74, - ErrorCode::InvalidNewSubSlotIters => 75, - ErrorCode::InvalidCcSpVdf => 76, - ErrorCode::InvalidRcSpVdf => 77, - ErrorCode::InvalidCcSignature => 78, - ErrorCode::InvalidRcSignature => 79, - ErrorCode::CannotMakeCcBlock => 80, - ErrorCode::InvalidRcSpPrevIp => 81, - ErrorCode::InvalidRcIpPrevIp => 82, - ErrorCode::InvalidIsTransactionBlock => 83, - ErrorCode::InvalidUrsbHash => 84, - ErrorCode::OldPoolTarget => 85, - ErrorCode::InvalidPoolSignature => 86, - ErrorCode::InvalidFoliageBlockPresence => 87, - ErrorCode::InvalidCcIpVdf => 88, - ErrorCode::InvalidRcIpVdf => 89, - ErrorCode::IpShouldBeNone => 90, - ErrorCode::InvalidRewardBlockHash => 91, - ErrorCode::InvalidMadeNonOverflowInfusions => 92, - ErrorCode::NoOverflowsInFirstSubSlotNewEpoch => 93, - ErrorCode::MempoolNotInitialized => 94, - ErrorCode::ShouldNotHaveIcc => 95, - ErrorCode::ShouldHaveIcc => 96, - ErrorCode::InvalidIccVdf => 97, - ErrorCode::InvalidIccHashCc => 98, - ErrorCode::InvalidIccHashRc => 99, - ErrorCode::InvalidIccEosVdf => 100, - ErrorCode::InvalidSpIndex => 101, - ErrorCode::TooManyBlocks => 102, - ErrorCode::InvalidCcChallenge => 103, - ErrorCode::InvalidPrefarm => 104, - ErrorCode::AssertSecondsRelativeFailed => 105, - ErrorCode::BadCoinbaseSignature => 106, - // ErrorCode::InitialTransactionFreeze => 107 (removed in `chia-blockchain`` as well) - ErrorCode::NoTransactionsWhileSyncing => 108, - ErrorCode::AlreadyIncludingTransaction => 109, - ErrorCode::IncompatibleNetworkId => 110, - ErrorCode::PreSoftForkMaxGeneratorSize => 111, - ErrorCode::InvalidRequiredIters => 112, - ErrorCode::TooManyGeneratorRefs => 113, - ErrorCode::AssertMyParentIdFailed => 114, - ErrorCode::AssertMyPuzzleHashFailed => 115, - ErrorCode::AssertMyAmountFailed => 116, - ErrorCode::GeneratorRuntimeError => 117, - ErrorCode::InvalidCostResult => 118, - ErrorCode::InvalidTransactionsGeneratorRefsRoot => 119, - ErrorCode::FutureGeneratorRefs => 120, - ErrorCode::GeneratorRefHasNoGenerator => 121, - ErrorCode::DoubleSpendInFork => 122, - ErrorCode::InvalidFeeTooCloseToZero => 123, - ErrorCode::CoinAmountNegative => 124, - ErrorCode::InternalProtocolError => 125, - ErrorCode::InvalidSpendBundle => 126, - ErrorCode::FailedGettingGeneratorMultiprocessing => 127, - ErrorCode::AssertBeforeSecondsAbsoluteFailed => 128, - ErrorCode::AssertBeforeSecondsRelativeFailed => 129, - ErrorCode::AssertBeforeHeightAbsoluteFailed => 130, - ErrorCode::AssertBeforeHeightRelativeFailed => 131, - ErrorCode::AssertConcurrentSpendFailed => 132, - ErrorCode::AssertConcurrentPuzzleFailed => 133, - ErrorCode::ImpossibleSecondsRelativeConstraints => 134, - ErrorCode::ImpossibleSecondsAbsoluteConstraints => 135, - ErrorCode::ImpossibleHeightRelativeConstraints => 136, - ErrorCode::ImpossibleHeightAbsoluteConstraints => 137, - ErrorCode::AssertMyBirthSecondsFailed => 138, - ErrorCode::AssertMyBirthHeightFailed => 139, - ErrorCode::AssertEphemeralFailed => 140, - ErrorCode::EphemeralRelativeCondition => 141, - ErrorCode::InvalidSoftforkCondition => 142, - ErrorCode::InvalidSoftforkCost => 143, - ErrorCode::TooManyAnnouncements => 144, - ErrorCode::InvalidMessageMode => 145, - ErrorCode::InvalidCoinId => 146, - ErrorCode::MessageNotSentOrReceived => 147, - ErrorCode::ComplexGeneratorReceived => 148, - } - } -} - -pub fn rest(a: &Allocator, n: NodePtr) -> Result { - match a.sexp(n) { - SExp::Pair(_, right) => Ok(right), - SExp::Atom => Err(ValidationErr(n, ErrorCode::InvalidCondition)), - } -} - -pub fn next(a: &Allocator, n: NodePtr) -> Result, ValidationErr> { - match a.sexp(n) { - SExp::Pair(left, right) => Ok(Some((left, right))), - SExp::Atom => { - // this is expected to be a valid list terminator - if a.atom_len(n) == 0 { - Ok(None) - } else { - Err(ValidationErr(n, ErrorCode::InvalidCondition)) - } - } - } -} - -pub fn atom(a: &Allocator, n: NodePtr, code: ErrorCode) -> Result, ValidationErr> { - match a.sexp(n) { - SExp::Atom => Ok(a.atom(n)), - SExp::Pair(..) => Err(ValidationErr(n, code)), - } -} - -pub fn check_nil(a: &Allocator, n: NodePtr) -> Result<(), ValidationErr> { - if atom(a, n, ErrorCode::InvalidCondition)?.as_ref().is_empty() { - Ok(()) - } else { - Err(ValidationErr(n, ErrorCode::InvalidCondition)) - } -} diff --git a/crates/chia-tools/src/bin/run-spend.rs b/crates/chia-tools/src/bin/run-spend.rs index 4f319f21f..6d6381e59 100644 --- a/crates/chia-tools/src/bin/run-spend.rs +++ b/crates/chia-tools/src/bin/run-spend.rs @@ -264,7 +264,7 @@ fn print_puzzle_info(a: &Allocator, puzzle: NodePtr, solution: NodePtr) { fn main() { use chia_consensus::conditions::parse_args; use chia_consensus::opcodes::parse_opcode; - use chia_consensus::validation_error::{first, rest}; + use chia_consensus::error_code::{first, rest}; use chia_protocol::CoinSpend; use clvmr::reduction::Reduction; use clvmr::{ChiaDialect, run_program}; diff --git a/crates/chia-tools/src/visit_spends.rs b/crates/chia-tools/src/visit_spends.rs index 040ff4ef3..5e17272bf 100644 --- a/crates/chia-tools/src/visit_spends.rs +++ b/crates/chia-tools/src/visit_spends.rs @@ -1,4 +1,4 @@ -use chia_consensus::validation_error::{ErrorCode, ValidationErr, first}; +use chia_consensus::error_code::{ErrorCode, first}; use chia_protocol::Bytes32; use chia_protocol::FullBlock; use chia_puzzles::CHIALISP_DESERIALISATION; @@ -96,7 +96,7 @@ pub fn visit_spends< block_refs: &[GenBuf], max_cost: u64, mut callback: F, -) -> Result<(), ValidationErr> { +) -> Result<(), ErrorCode> { let clvm_deserializer = node_from_bytes(a, &CHIALISP_DESERIALISATION)?; let program = node_from_bytes_backrefs(a, program)?; @@ -128,7 +128,7 @@ pub fn visit_spends< // process the spend let destructure_list!(parent_id, puzzle, amount, solution, _spend_level_extra) = ::from_clvm(a, spend) - .map_err(|_| ValidationErr(spend, ErrorCode::InvalidCondition))?; + .map_err(|_| ErrorCode::InvalidCondition(spend))?; callback(a, parent_id, amount, puzzle, solution); } Ok(()) diff --git a/wheel/src/api.rs b/wheel/src/api.rs index 3f3940cfb..f0900ff44 100644 --- a/wheel/src/api.rs +++ b/wheel/src/api.rs @@ -77,7 +77,7 @@ use crate::run_program::{run_chia_program, serialized_length, serialized_length_ use chia_consensus::fast_forward::fast_forward_singleton as native_ff; use chia_consensus::get_puzzle_and_solution::get_puzzle_and_solution_for_coin as parse_puzzle_solution; -use chia_consensus::validation_error::ValidationErr; +use chia_consensus::error_code::ErrorCode; use clvmr::ChiaDialect; use clvmr::allocator::NodePtr; use clvmr::cost::Cost; @@ -91,6 +91,18 @@ use chia_bls::{ BlsCache, DerivableKey, GTElement, PublicKey, SecretKey, Signature, hash_to_g2 as native_hash_to_g2, }; + +fn error_node(err: ErrorCode) -> NodePtr { + match err { + ErrorCode::InvalidCondition(n) + | ErrorCode::InvalidParentId(n) + | ErrorCode::InvalidCoinAmount(n) => n, + ErrorCode::CostExceeded(Some(n)) + | ErrorCode::GeneratorRuntimeError(Some(n)) + | ErrorCode::InvalidMessageMode(Some(n)) => n, + _ => NodePtr::NIL, + } +} #[pyfunction] pub fn compute_merkle_set_root<'p>( py: Python<'p>, @@ -168,9 +180,10 @@ pub fn get_puzzle_and_solution_for_coin<'a>( result, &Coin::new(find_parent, find_ph, find_amount), ) { - Err(ValidationErr(n, _)) => { - Err(EvalErr::InvalidOpArg(n, "coin not found".to_string())) - } + Err(err) => Err(EvalErr::InvalidOpArg( + error_node(err), + "coin not found".to_string(), + )), Ok(pair) => Ok(pair), } }) @@ -223,9 +236,10 @@ pub fn get_puzzle_and_solution_for_coin2<'a>( let Reduction(_cost, result) = run_program(&mut allocator, dialect, generator, args, max_cost)?; match parse_puzzle_solution(&allocator, result, find_coin) { - Err(ValidationErr(n, _)) => { - Err(EvalErr::InvalidOpArg(n, "coin not found".to_string())) - } + Err(err) => Err(EvalErr::InvalidOpArg( + error_node(err), + "coin not found".to_string(), + )), Ok(pair) => Ok(pair), } }) diff --git a/wheel/src/run_generator.rs b/wheel/src/run_generator.rs index 850284149..c504aca7a 100644 --- a/wheel/src/run_generator.rs +++ b/wheel/src/run_generator.rs @@ -5,7 +5,6 @@ use chia_consensus::consensus_constants::ConsensusConstants; use chia_consensus::owned_conditions::OwnedSpendBundleConditions; use chia_consensus::run_block_generator::run_block_generator as native_run_block_generator; use chia_consensus::run_block_generator::run_block_generator2 as native_run_block_generator2; -use chia_consensus::validation_error::ValidationErr; use chia_protocol::{Bytes, Bytes32, Coin}; use clvmr::cost::Cost; @@ -64,9 +63,9 @@ pub fn run_block_generator<'a>( spend_bundle_conds, )), ), - Err(ValidationErr(_, error_code)) => { + Err(error_code) => { // a validation error occurred - (Some(error_code.into()), None) + (Some(u32::from(error_code)), None) } } }) @@ -117,9 +116,9 @@ pub fn run_block_generator2<'a>( spend_bundle_conds, )), ), - Err(ValidationErr(_, error_code)) => { + Err(error_code) => { // a validation error occurred - (Some(error_code.into()), None) + (Some(u32::from(error_code)), None) } } }) @@ -151,7 +150,7 @@ pub fn additions_and_removals<'a>( // a validation error occurred pyo3::exceptions::PyValueError::new_err(format!( "additions_and_removals() failed: {}", - e.1 as u16 + u32::from(e) as u16 )) }) })