diff --git a/Cargo.toml b/Cargo.toml index 0199a66f4..40968104e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ derive_more = { version = "2.0.1", default-features = false, features = [ ] } num-derive = "0.4.2" thiserror = { version = "2.0.1", default-features = false } -bounded-vec = { version = "0.8.0", default-features = false } +bounded-vec = { version = "0.9.0", default-features = false } bitvec = { version = "1.0.1", default-features = false, features = ["alloc"] } blake2 = { version = "0.10.6", default-features = false } sha2 = { version = "0.10", default-features = false } diff --git a/bindings/ergo-lib-c-core/src/error_conversion.rs b/bindings/ergo-lib-c-core/src/error_conversion.rs index 2ffb633be..d5dd0d824 100644 --- a/bindings/ergo-lib-c-core/src/error_conversion.rs +++ b/bindings/ergo-lib-c-core/src/error_conversion.rs @@ -16,6 +16,7 @@ use ergo_lib::ergotree_ir::chain::address::AddressError; use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValueError; use ergo_lib::ergotree_ir::chain::ergo_box::RegisterValueError; use ergo_lib::ergotree_ir::chain::token::TokenAmountError; +use ergo_lib::ergotree_ir::soft_fork::SoftForkError; use ergo_lib::wallet::derivation_path::ChildIndexError; use ergo_lib::wallet::signing::TxSigningError; use ergo_lib::wallet::tx_context::TransactionContextError; @@ -58,6 +59,7 @@ convert_error!(WalletError); convert_error!(DecodeError); convert_error!(TryFromSliceError); convert_error!(TransactionContextError); +convert_error!(SoftForkError); macro_rules! convert_error_via_debug { ($t:ident) => { diff --git a/bindings/ergo-lib-wasm/src/box_selector.rs b/bindings/ergo-lib-wasm/src/box_selector.rs index c1bd1db4d..d6510ac2e 100644 --- a/bindings/ergo-lib-wasm/src/box_selector.rs +++ b/bindings/ergo-lib-wasm/src/box_selector.rs @@ -1,4 +1,5 @@ //! Simple box selection algorithms +use bounded_vec::NonEmptyBoundedVec; use ergo_lib::ergotree_ir::chain; use ergo_lib::wallet; use ergo_lib::wallet::box_selector::BoxSelector; @@ -9,7 +10,6 @@ use crate::ergo_box::BoxValue; use crate::ergo_box::ErgoBoxAssetsDataList; use crate::error_conversion::to_js; use crate::token::Tokens; -use bounded_vec::BoundedVec; extern crate derive_more; use derive_more::{From, Into}; @@ -29,7 +29,7 @@ impl BoxSelection { Ok(BoxSelection(wallet::box_selector::BoxSelection::< ergo_lib::ergotree_ir::chain::ergo_box::ErgoBox, > { - boxes: BoundedVec::from_vec(boxes.clone().into()).map_err(to_js)?, + boxes: NonEmptyBoundedVec::from_vec(boxes.clone().into()).map_err(to_js)?, change_boxes: change.clone().into(), })) } diff --git a/bindings/ergo-lib-wasm/src/error_conversion.rs b/bindings/ergo-lib-wasm/src/error_conversion.rs index ef92aaec4..36678d629 100644 --- a/bindings/ergo-lib-wasm/src/error_conversion.rs +++ b/bindings/ergo-lib-wasm/src/error_conversion.rs @@ -18,6 +18,7 @@ use ergo_lib::ergotree_ir::chain::address::AddressError; use ergo_lib::ergotree_ir::chain::ergo_box::box_value::BoxValueError; use ergo_lib::ergotree_ir::chain::ergo_box::RegisterValueError; use ergo_lib::ergotree_ir::chain::token::TokenAmountError; +use ergo_lib::ergotree_ir::soft_fork::SoftForkError; use ergo_lib::wallet::derivation_path::ChildIndexError; use ergo_lib::wallet::derivation_path::DerivationPathError; use ergo_lib::wallet::ext_pub_key::ExtPubKeyError; @@ -99,6 +100,7 @@ from_error_to_wrap!(TransactionContextError); from_error_to_wrap!(TxValidationError); from_error_to_wrap!(RegisterValueError); from_error_to_wrap!(String); +from_error_to_wrap!(SoftForkError); macro_rules! from_error_to_wrap_via_debug { ($t:ident) => { diff --git a/ergo-lib/src/wallet/multi_sig.rs b/ergo-lib/src/wallet/multi_sig.rs index 06d4950ed..bb625f1b3 100644 --- a/ergo-lib/src/wallet/multi_sig.rs +++ b/ergo-lib/src/wallet/multi_sig.rs @@ -16,7 +16,6 @@ use crate::ergotree_interpreter::sigma_protocol::unproven_tree::NodePosition; use crate::ergotree_interpreter::sigma_protocol::FirstProverMessage; use crate::ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean; use crate::ergotree_ir::sigma_protocol::sigma_boolean::SigmaConjecture; -use crate::ergotree_ir::sigma_protocol::sigma_boolean::SigmaConjectureItems; use crate::ergotree_ir::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree; use crate::wallet::signing::{make_context, TransactionContext, TxSigningError}; use alloc::vec::Vec; @@ -40,7 +39,7 @@ pub fn bag_for_multi_sig( if let SigmaBoolean::TrivialProp(_) = sigma_tree { return Ok(HintsBag::empty()); } - let ut = compute_commitments(parse_sig_compute_challenges(sigma_tree, proof.to_owned())?); + let ut = compute_commitments(parse_sig_compute_challenges(sigma_tree, proof.to_owned())?)?; // Traversing node of sigma tree fn traverse_node( tree: UncheckedTree, @@ -51,8 +50,7 @@ pub fn bag_for_multi_sig( ) -> Result<(), SigParsingError> { match tree { UncheckedTree::UncheckedConjecture(unchecked_conjecture) => { - let items: SigmaConjectureItems = - unchecked_conjecture.children_ust(); + let items = unchecked_conjecture.children_ust(); items .iter() .enumerate() diff --git a/ergo-lib/src/wallet/tx_builder.rs b/ergo-lib/src/wallet/tx_builder.rs index 51c358e88..3bbfcef34 100644 --- a/ergo-lib/src/wallet/tx_builder.rs +++ b/ergo-lib/src/wallet/tx_builder.rs @@ -142,9 +142,6 @@ impl TxBuilder { } fn build_tx(&self) -> Result { - if self.box_selection.boxes.is_empty() { - return Err(TxBuilderError::InvalidArgs("inputs are empty".to_string())); - } if self.output_candidates.is_empty() { return Err(TxBuilderError::InvalidArgs("outputs are empty".to_string())); } diff --git a/ergo-p2p/src/peer_spec.rs b/ergo-p2p/src/peer_spec.rs index 54eeaddb7..780e89f96 100644 --- a/ergo-p2p/src/peer_spec.rs +++ b/ergo-p2p/src/peer_spec.rs @@ -1,7 +1,7 @@ //! PeerSpec types use std::io; -use bounded_vec::BoundedVec; +use bounded_vec::{BoundedVec, NonEmptyBoundedVec}; use ergo_chain_types::PeerAddr; use sigma_ser::vlq_encode::VlqEncodingError; use sigma_ser::{ScorexParsingError, ScorexSerializable, ScorexSerializeResult}; @@ -118,7 +118,7 @@ impl ScorexSerializable for PeerSpec { for _ in 0..n { f.push(PeerFeature::scorex_parse(r)?); } - Some(BoundedVec::from_vec(f)?) + Some(NonEmptyBoundedVec::from_vec(f)?) } }; @@ -152,7 +152,7 @@ pub mod arbitrary { option::of(vec(any::(), 1..4)), ) .prop_map(|(version, declared_addr, features)| { - let feats = features.map(|f| BoundedVec::from_vec(f).unwrap()); + let feats = features.map(|f| NonEmptyBoundedVec::from_vec(f).unwrap()); PeerSpec::new( "/Ergo-Scala-client:2.0.0(iPad; U; CPU OS 3_2_1)/AndroidBuild:0.8/", diff --git a/ergotree-interpreter/src/eval/coll_exists.rs b/ergotree-interpreter/src/eval/coll_exists.rs index 912148fa6..d53bace8d 100644 --- a/ergotree-interpreter/src/eval/coll_exists.rs +++ b/ergotree-interpreter/src/eval/coll_exists.rs @@ -41,16 +41,7 @@ impl Evaluable for Exists { ))), }; let normalized_input_vals: Vec = match input_v { - Value::Coll(coll) => { - if coll.elem_tpe() != &*self.elem_tpe { - return Err(EvalError::UnexpectedValue(format!( - "expected Exists input element type to be {0:?}, got: {1:?}", - self.elem_tpe, - coll.elem_tpe() - ))); - }; - Ok(coll.as_vec()) - } + Value::Coll(coll) => Ok(coll.as_vec()), _ => Err(EvalError::UnexpectedValue(format!( "expected Map input to be Value::Coll, got: {0:?}", input_v diff --git a/ergotree-interpreter/src/eval/coll_filter.rs b/ergotree-interpreter/src/eval/coll_filter.rs index 4d83c8292..879c821cb 100644 --- a/ergotree-interpreter/src/eval/coll_filter.rs +++ b/ergotree-interpreter/src/eval/coll_filter.rs @@ -6,6 +6,7 @@ use ergotree_ir::mir::coll_filter::Filter; use ergotree_ir::mir::constant::TryExtractInto; use ergotree_ir::mir::value::CollKind; use ergotree_ir::mir::value::Value; +use ergotree_ir::types::stype::SType; use crate::eval::env::Env; use crate::eval::Context; @@ -18,6 +19,13 @@ impl Evaluable for Filter { env: &mut Env<'ctx>, ctx: &Context<'ctx>, ) -> Result, EvalError> { + let input_elem_type = match self.input.post_eval_tpe() { + SType::SColl(elem_type) => Ok(elem_type.clone()), + _ => Err(EvalError::UnexpectedExpr(format!( + "Expected ForAll input to be SColl, got {0:?}", + self.input.tpe() + ))), + }?; let input_v = self.input.eval(env, ctx)?; let condition_v = self.condition.eval(env, ctx)?; let input_v_clone = input_v.clone(); @@ -45,10 +53,10 @@ impl Evaluable for Filter { }; let normalized_input_vals: Vec = match input_v { Value::Coll(coll) => { - if coll.elem_tpe() != &*self.elem_tpe { + if coll.elem_tpe() != &*input_elem_type { return Err(EvalError::UnexpectedValue(format!( "expected Filter input element type to be {0:?}, got: {1:?}", - self.elem_tpe, + input_elem_type, coll.elem_tpe() ))); }; @@ -77,7 +85,7 @@ impl Evaluable for Filter { .map(|(item, _)| item) .collect::>(); Ok(Value::Coll(CollKind::from_collection( - (*self.elem_tpe).clone(), + (*input_elem_type).clone(), filtered_items, )?)) } diff --git a/ergotree-interpreter/src/eval/coll_forall.rs b/ergotree-interpreter/src/eval/coll_forall.rs index 49cce49fc..9684f2b43 100644 --- a/ergotree-interpreter/src/eval/coll_forall.rs +++ b/ergotree-interpreter/src/eval/coll_forall.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use ergotree_ir::mir::coll_forall::ForAll; use ergotree_ir::mir::constant::TryExtractInto; use ergotree_ir::mir::value::Value; +use ergotree_ir::types::stype::SType; use crate::eval::env::Env; use crate::eval::Context; @@ -15,6 +16,13 @@ impl Evaluable for ForAll { env: &mut Env<'ctx>, ctx: &Context<'ctx>, ) -> Result, EvalError> { + let input_elem_type = match self.input.post_eval_tpe() { + SType::SColl(elem_type) => Ok(elem_type), + _ => Err(EvalError::UnexpectedExpr(format!( + "Expected ForAll input to be SColl, got {0:?}", + self.input.tpe() + ))), + }?; let input_v = self.input.eval(env, ctx)?; let condition_v = self.condition.eval(env, ctx)?; let input_v_clone = input_v.clone(); @@ -42,10 +50,10 @@ impl Evaluable for ForAll { }; let normalized_input_vals: Vec = match input_v { Value::Coll(coll) => { - if coll.elem_tpe() != &*self.elem_tpe { + if coll.elem_tpe() != &*input_elem_type { return Err(EvalError::UnexpectedValue(format!( "expected ForAll input element type to be {0:?}, got: {1:?}", - self.elem_tpe, + input_elem_type, coll.elem_tpe() ))); }; diff --git a/ergotree-interpreter/src/eval/collection.rs b/ergotree-interpreter/src/eval/collection.rs index b6371a470..754b19bd7 100644 --- a/ergotree-interpreter/src/eval/collection.rs +++ b/ergotree-interpreter/src/eval/collection.rs @@ -24,6 +24,15 @@ impl Evaluable for Collection { Collection::Exprs { elem_tpe, items } => { let items_v: Result, EvalError> = items.iter().map(|i| i.eval(env, ctx)).collect(); + if let Some(tpe) = items + .iter() + .map(|item| item.tpe()) + .find(|tpe| tpe != elem_tpe) + { + return Err(EvalError::UnexpectedExpr(format!( + "Collection: expected value of type {elem_tpe:?}, found {tpe:?}" + ))); + } match elem_tpe { SType::SByte => { let bytes: Result, TryExtractFromError> = items_v? diff --git a/ergotree-interpreter/src/eval/deserialize_register.rs b/ergotree-interpreter/src/eval/deserialize_register.rs index 59fdff6bb..6b1b86be6 100644 --- a/ergotree-interpreter/src/eval/deserialize_register.rs +++ b/ergotree-interpreter/src/eval/deserialize_register.rs @@ -14,6 +14,7 @@ mod tests { use ergotree_ir::mir::global_vars::GlobalVars; use ergotree_ir::mir::value::Value; use ergotree_ir::serialization::SigmaSerializable; + use ergotree_ir::soft_fork::SoftForkError; use ergotree_ir::types::stype::SType; use sigma_test_util::force_any_val; @@ -79,10 +80,9 @@ mod tests { assert!(matches!( try_eval_with_deserialize::(&expr, &ctx), Err(EvalError::SubstDeserializeError( - ergotree_ir::mir::expr::SubstDeserializeError::ExprTpeError { - expected: _, - actual: _ - } + ergotree_ir::mir::expr::SubstDeserializeError::SoftForkError( + SoftForkError::DeserializedScriptError + ) )) )); // default provided @@ -133,10 +133,9 @@ mod tests { assert!(matches!( try_eval_with_deserialize::(&expr, &ctx), Err(EvalError::SubstDeserializeError( - ergotree_ir::mir::expr::SubstDeserializeError::ExprTpeError { - expected: _, - actual: _ - } + ergotree_ir::mir::expr::SubstDeserializeError::SoftForkError( + SoftForkError::DeserializedScriptError + ) )) )); } diff --git a/ergotree-interpreter/src/eval/extract_reg_as.rs b/ergotree-interpreter/src/eval/extract_reg_as.rs index 2f138a5cf..bb6156413 100644 --- a/ergotree-interpreter/src/eval/extract_reg_as.rs +++ b/ergotree-interpreter/src/eval/extract_reg_as.rs @@ -1,5 +1,3 @@ -use core::convert::TryInto; - use alloc::boxed::Box; use ergotree_ir::chain::ergo_box::ErgoBox; use ergotree_ir::mir::constant::TryExtractInto; @@ -22,15 +20,10 @@ impl Evaluable for ExtractRegisterAs { .input .eval(env, ctx)? .try_extract_into::>()?; - let id = self.register_id.try_into().map_err(|e| { - EvalError::RegisterIdOutOfBounds(format!( - "register index {} is out of bounds: {:?} ", - self.register_id, e - )) - })?; - let reg_val_opt = ir_box.get_register(id).map_err(|e| { + let reg_val_opt = ir_box.get_register(self.register_id).map_err(|e| { EvalError::NotFound(format!( - "Error getting the register id {id} with error {e:?}" + "Error getting the register id {} with error {e:?}", + self.register_id )) })?; match reg_val_opt { @@ -38,8 +31,8 @@ impl Evaluable for ExtractRegisterAs { Ok(Value::Opt(Some(Box::new(constant.v.into())))) } Some(constant) => Err(EvalError::UnexpectedValue(format!( - "Expected register {id} to be of type {}, got {}", - self.elem_tpe, constant.tpe + "Expected register {} to be of type {}, got {}", + self.register_id, self.elem_tpe, constant.tpe ))), None => Ok(Value::Opt(None)), } @@ -56,7 +49,6 @@ mod tests { use ergotree_ir::mir::expr::Expr; use ergotree_ir::mir::global_vars::GlobalVars; use ergotree_ir::mir::option_get::OptionGet; - use ergotree_ir::mir::unary_op::OneArgOpTryBuild; use ergotree_ir::types::stype::SType; use sigma_test_util::force_any_val; @@ -69,7 +61,7 @@ mod tests { ) .unwrap() .into(); - let option_get_expr: Expr = OptionGet::try_build(get_reg_expr).unwrap().into(); + let option_get_expr: Expr = OptionGet::new(get_reg_expr).unwrap().into(); let ctx = force_any_val::(); let v = eval_out::(&option_get_expr, &ctx); assert_eq!(v, ctx.self_box.value.as_i64()); @@ -84,7 +76,7 @@ mod tests { ) .unwrap() .into(); - let option_get_expr: Expr = OptionGet::try_build(get_reg_expr).unwrap().into(); + let option_get_expr: Expr = OptionGet::new(get_reg_expr).unwrap().into(); let ctx = force_any_val::(); assert!(try_eval_out::(&option_get_expr, &ctx).is_err()); } diff --git a/ergotree-interpreter/src/eval/option_get.rs b/ergotree-interpreter/src/eval/option_get.rs index c8e839d94..2ad4ef8be 100644 --- a/ergotree-interpreter/src/eval/option_get.rs +++ b/ergotree-interpreter/src/eval/option_get.rs @@ -36,7 +36,6 @@ mod tests { use ergotree_ir::mir::expr::Expr; use ergotree_ir::mir::extract_reg_as::ExtractRegisterAs; use ergotree_ir::mir::global_vars::GlobalVars; - use ergotree_ir::mir::unary_op::OneArgOpTryBuild; use ergotree_ir::types::stype::SType; use sigma_test_util::force_any_val; @@ -49,7 +48,7 @@ mod tests { ) .unwrap() .into(); - let option_get_expr: Expr = OptionGet::try_build(get_reg_expr).unwrap().into(); + let option_get_expr: Expr = OptionGet::new(get_reg_expr).unwrap().into(); let ctx = force_any_val::(); let v = eval_out::(&option_get_expr, &ctx); assert_eq!(v, ctx.self_box.value.as_i64()); diff --git a/ergotree-interpreter/src/eval/scontext.rs b/ergotree-interpreter/src/eval/scontext.rs index 190e2ceed..5029fb7ea 100644 --- a/ergotree-interpreter/src/eval/scontext.rs +++ b/ergotree-interpreter/src/eval/scontext.rs @@ -105,8 +105,16 @@ pub(crate) static GET_VAR_FROM_INPUT_EVAL_FN: EvalFn = |mc, _env, ctx, _obj, arg else { unreachable!() }; - let input_idx = args[0].clone().try_extract_into::()? as usize; - let var_id = args[1].clone().try_extract_into::()? as u8; + let input_idx = args + .first() + .ok_or_else(|| EvalError::NotFound("getVarFromInput: missing input_idx".into()))? + .clone() + .try_extract_into::()? as usize; + let var_id = args + .get(1) + .ok_or_else(|| EvalError::NotFound("getVarFromInput: missing input_idx".into()))? + .clone() + .try_extract_into::()? as u8; Ok( match ctx .extension_provider diff --git a/ergotree-interpreter/src/eval/sigma_and.rs b/ergotree-interpreter/src/eval/sigma_and.rs index 4ef8398c0..423a6c823 100644 --- a/ergotree-interpreter/src/eval/sigma_and.rs +++ b/ergotree-interpreter/src/eval/sigma_and.rs @@ -1,4 +1,6 @@ use alloc::boxed::Box; +use alloc::vec::Vec; +use bounded_vec::NonEmptyVec; use ergotree_ir::mir::constant::TryExtractInto; use ergotree_ir::mir::sigma_and::SigmaAnd; use ergotree_ir::mir::value::Value; @@ -16,8 +18,13 @@ impl Evaluable for SigmaAnd { env: &mut Env<'ctx>, ctx: &Context<'ctx>, ) -> Result, EvalError> { - let items_v_res = self.items.try_mapped_ref(|it| it.eval(env, ctx)); - let items_sigmabool = items_v_res? + let items_v: NonEmptyVec<_> = self + .items + .iter() + .map(|it| it.eval(env, ctx)) + .collect::, _>>()? + .try_into()?; + let items_sigmabool = items_v .try_mapped(|it| it.try_extract_into::())? .mapped(|it| it.value().clone()); Ok(Value::SigmaProp(Box::new(SigmaProp::new( @@ -31,7 +38,6 @@ impl Evaluable for SigmaAnd { #[cfg(test)] #[cfg(feature = "arbitrary")] mod tests { - use core::convert::TryInto; use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean; use ergotree_ir::sigma_protocol::sigma_boolean::SigmaConjecture; @@ -58,7 +64,7 @@ mod tests { let expected_sb: Vec = sigmaprops.into_iter().map(|sp| sp.into()).collect(); prop_assert!(matches!(res.clone().into(), SigmaBoolean::SigmaConjecture(SigmaConjecture::Cand(_)))); if let SigmaBoolean::SigmaConjecture(SigmaConjecture::Cand(Cand {items: actual_sb})) = res.into() { - prop_assert_eq!(actual_sb, expected_sb.try_into().unwrap()); + prop_assert_eq!(actual_sb, expected_sb); } } } diff --git a/ergotree-interpreter/src/eval/sigma_or.rs b/ergotree-interpreter/src/eval/sigma_or.rs index 03aa1349d..c48a230bc 100644 --- a/ergotree-interpreter/src/eval/sigma_or.rs +++ b/ergotree-interpreter/src/eval/sigma_or.rs @@ -1,4 +1,6 @@ use alloc::boxed::Box; +use alloc::vec::Vec; +use bounded_vec::NonEmptyVec; use ergotree_ir::mir::constant::TryExtractInto; use ergotree_ir::mir::sigma_or::SigmaOr; use ergotree_ir::mir::value::Value; @@ -16,8 +18,13 @@ impl Evaluable for SigmaOr { env: &mut Env<'ctx>, ctx: &Context<'ctx>, ) -> Result, EvalError> { - let items_v_res = self.items.try_mapped_ref(|it| it.eval(env, ctx)); - let items_sigmabool = items_v_res? + let items_v: NonEmptyVec<_> = self + .items + .iter() + .map(|it| it.eval(env, ctx)) + .collect::, _>>()? + .try_into()?; + let items_sigmabool = items_v .try_mapped(|it| it.try_extract_into::())? .mapped(|it| it.value().clone()); Ok(Value::SigmaProp(Box::new(SigmaProp::new(Cor::normalized( @@ -31,7 +38,6 @@ impl Evaluable for SigmaOr { #[cfg(test)] #[cfg(feature = "arbitrary")] mod tests { - use core::convert::TryInto; use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean; use ergotree_ir::sigma_protocol::sigma_boolean::SigmaConjecture; @@ -58,7 +64,7 @@ mod tests { let expected_sb: Vec = sigmaprops.into_iter().map(|sp| sp.into()).collect(); prop_assert!(matches!(res.clone().into(), SigmaBoolean::SigmaConjecture(SigmaConjecture::Cor(_)))); if let SigmaBoolean::SigmaConjecture(SigmaConjecture::Cor(Cor {items: actual_sb})) = res.into() { - prop_assert_eq!(actual_sb, expected_sb.try_into().unwrap()); + prop_assert_eq!(actual_sb, expected_sb); } } } diff --git a/ergotree-interpreter/src/sigma_protocol/proof_tree.rs b/ergotree-interpreter/src/sigma_protocol/proof_tree.rs index 887ba375a..b6175ac2b 100644 --- a/ergotree-interpreter/src/sigma_protocol/proof_tree.rs +++ b/ergotree-interpreter/src/sigma_protocol/proof_tree.rs @@ -2,12 +2,12 @@ extern crate derive_more; +use alloc::vec::Vec; use core::fmt::Debug; use derive_more::From; use derive_more::TryInto; use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean; -use ergotree_ir::sigma_protocol::sigma_boolean::SigmaConjectureItems; use crate::sigma_protocol::unproven_tree::CandUnproven; use crate::sigma_protocol::unproven_tree::UnprovenConjecture; @@ -136,7 +136,7 @@ pub(crate) enum ConjectureType { pub(crate) trait ProofTreeConjecture { fn conjecture_type(&self) -> ConjectureType; - fn children(&self) -> SigmaConjectureItems; + fn children(&self) -> Vec; } pub(crate) enum ProofTreeKind<'a> { @@ -150,16 +150,20 @@ pub(crate) fn rewrite_bu(tree: ProofTree, f: &F) -> Result Result, ProverError>, { - let cast_to_ust = |children: SigmaConjectureItems| { - children.try_mapped(|c| { - if let ProofTree::UncheckedTree(ust) = c { - Ok(ust) - } else { - Err(ProverError::Unexpected( - "rewrite: expected UncheckedSigmaTree got UnprovenTree", - )) - } - }) + let cast_to_ust = |children: &[ProofTree]| { + children + .iter() + .cloned() + .map(|c| { + if let ProofTree::UncheckedTree(ust) = c { + Ok(ust) + } else { + Err(ProverError::Unexpected( + "rewrite: expected UncheckedSigmaTree got UnprovenTree", + )) + } + }) + .collect::, _>>() }; let tree_with_updated_children = match &tree { @@ -168,14 +172,24 @@ where UnprovenTree::UnprovenConjecture(conj) => match conj { UnprovenConjecture::CandUnproven(cand) => UnprovenTree::UnprovenConjecture( UnprovenConjecture::CandUnproven(CandUnproven { - children: cand.children.clone().try_mapped(|c| rewrite_bu(c, f))?, + children: cand + .children + .iter() + .cloned() + .map(|c| rewrite_bu(c, f)) + .collect::, _>>()?, ..cand.clone() }), ) .into(), UnprovenConjecture::CorUnproven(cor) => { UnprovenTree::UnprovenConjecture(UnprovenConjecture::CorUnproven(CorUnproven { - children: cor.children.clone().try_mapped(|c| rewrite_bu(c, f))?, + children: cor + .children + .iter() + .cloned() + .map(|c| rewrite_bu(c, f)) + .collect::, _>>()?, ..cor.clone() })) .into() @@ -196,9 +210,12 @@ where challenge, children, } => { - let rewritten_children = - children.clone().try_mapped(|c| rewrite_bu(c.into(), f))?; - let casted_children = cast_to_ust(rewritten_children)?; + let rewritten_children = children + .iter() + .cloned() + .map(|c| rewrite_bu(c.into(), f)) + .collect::, _>>()?; + let casted_children = cast_to_ust(&rewritten_children)?; UncheckedConjecture::CandUnchecked { children: casted_children, challenge: challenge.clone(), @@ -209,9 +226,12 @@ where challenge, children, } => { - let rewritten_children = - children.clone().try_mapped(|c| rewrite_bu(c.into(), f))?; - let casted_children = cast_to_ust(rewritten_children)?; + let rewritten_children = children + .iter() + .cloned() + .map(|c| rewrite_bu(c.into(), f)) + .collect::, _>>()?; + let casted_children = cast_to_ust(&rewritten_children)?; UncheckedConjecture::CorUnchecked { children: casted_children, challenge: challenge.clone(), @@ -224,11 +244,14 @@ where k, polynomial: polynomial_opt, } => { - let rewritten_children = - children.clone().try_mapped(|c| rewrite_bu(c.into(), f))?; - let casted_children = cast_to_ust(rewritten_children)?; + let rewritten_children = children + .iter() + .cloned() + .map(|c| rewrite_bu(c.into(), f)) + .collect::, _>>()?; + let casted_children = cast_to_ust(&rewritten_children)?; UncheckedConjecture::CthresholdUnchecked { - children: casted_children, + children: casted_children.try_into()?, challenge: challenge.clone(), k: *k, polynomial: polynomial_opt.clone(), @@ -247,16 +270,20 @@ pub(crate) fn rewrite_td(tree: ProofTree, f: &F) -> Result Result, ProverError>, { - let cast_to_ust = |children: SigmaConjectureItems| { - children.try_mapped(|c| { - if let ProofTree::UncheckedTree(ust) = c { - Ok(ust) - } else { - Err(ProverError::Unexpected( - "rewrite: expected UncheckedSigmaTree got UnprovenTree", - )) - } - }) + let cast_to_ust = |children: &[ProofTree]| { + children + .iter() + .cloned() + .map(|c| { + if let ProofTree::UncheckedTree(ust) = c { + Ok(ust) + } else { + Err(ProverError::Unexpected( + "rewrite: expected UncheckedSigmaTree got UnprovenTree", + )) + } + }) + .collect::, _>>() }; let rewritten_tree = f(&tree)?.unwrap_or(tree); @@ -266,14 +293,24 @@ where UnprovenTree::UnprovenConjecture(conj) => match conj { UnprovenConjecture::CandUnproven(cand) => UnprovenTree::UnprovenConjecture( UnprovenConjecture::CandUnproven(CandUnproven { - children: cand.children.clone().try_mapped(|c| rewrite_td(c, f))?, + children: cand + .children + .iter() + .cloned() + .map(|c| rewrite_td(c, f)) + .collect::, _>>()?, ..cand.clone() }), ) .into(), UnprovenConjecture::CorUnproven(cor) => { UnprovenTree::UnprovenConjecture(UnprovenConjecture::CorUnproven(CorUnproven { - children: cor.children.clone().try_mapped(|c| rewrite_td(c, f))?, + children: cor + .children + .iter() + .cloned() + .map(|c| rewrite_td(c, f)) + .collect::, _>>()?, ..cor.clone() })) .into() @@ -294,9 +331,12 @@ where challenge, children, } => { - let rewritten_children = - children.clone().try_mapped(|c| rewrite_td(c.into(), f))?; - let casted_children = cast_to_ust(rewritten_children)?; + let rewritten_children = children + .iter() + .cloned() + .map(|c| rewrite_td(c.into(), f)) + .collect::, _>>()?; + let casted_children = cast_to_ust(&rewritten_children)?; UncheckedConjecture::CandUnchecked { children: casted_children, challenge: challenge.clone(), @@ -307,9 +347,12 @@ where challenge, children, } => { - let rewritten_children = - children.clone().try_mapped(|c| rewrite_td(c.into(), f))?; - let casted_children = cast_to_ust(rewritten_children)?; + let rewritten_children = children + .iter() + .cloned() + .map(|c| rewrite_td(c.into(), f)) + .collect::, _>>()?; + let casted_children = cast_to_ust(&rewritten_children)?; UncheckedConjecture::CorUnchecked { children: casted_children, challenge: challenge.clone(), @@ -323,10 +366,11 @@ where polynomial: polynomial_opt, } => { let rewritten_children = - children.clone().try_mapped(|c| rewrite_td(c.into(), f))?; - let casted_children = cast_to_ust(rewritten_children)?; + children.try_mapped_ref(|c| rewrite_td(c.clone().into(), f))?; + let casted_children = cast_to_ust(rewritten_children.as_slice())?; UncheckedConjecture::CthresholdUnchecked { - children: casted_children, + #[allow(clippy::unwrap_used)] // casted_children.len() == children.len(), so conversion can't to BoundedVec can't fail + children: casted_children.try_into().unwrap(), challenge: challenge.clone(), k: *k, polynomial: polynomial_opt.clone(), diff --git a/ergotree-interpreter/src/sigma_protocol/prover.rs b/ergotree-interpreter/src/sigma_protocol/prover.rs index 7a3193131..992c935a8 100644 --- a/ergotree-interpreter/src/sigma_protocol/prover.rs +++ b/ergotree-interpreter/src/sigma_protocol/prover.rs @@ -18,9 +18,9 @@ use crate::sigma_protocol::unproven_tree::UnprovenDhTuple; use crate::sigma_protocol::Challenge; use crate::sigma_protocol::UnprovenLeaf; use alloc::vec::Vec; +use bounded_vec::BoundedVecOutOfBounds; use core::convert::TryInto; use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean; -use ergotree_ir::sigma_protocol::sigma_boolean::SigmaConjectureItems; use gf2_192::gf2_192poly::Gf2_192Poly; use gf2_192::gf2_192poly::Gf2_192PolyError; use gf2_192::Gf2_192Error; @@ -93,6 +93,9 @@ pub enum ProverError { /// Unsupported operation #[error("RNG is not available in no_std environments, can't generate signature without Hint")] Unsupported, + /// BoundedVecOutOfBounds error + #[error("BoundedVec error: {0}")] + BoundedVecOutOfBounds(#[from] BoundedVecOutOfBounds), } impl From for ProverError { @@ -262,7 +265,7 @@ fn mark_real( UnprovenTree::UnprovenConjecture(unp_conj) => match unp_conj { UnprovenConjecture::CandUnproven(cand) => { // If the node is AND, mark it "real" if all of its children are marked real; else mark it "simulated" - let simulated = cast_to_unp(cand.children.clone())? + let simulated = cast_to_unp(cand.children.as_slice())? .iter() .any(|c| c.simulated()); Some( @@ -275,7 +278,7 @@ fn mark_real( } UnprovenConjecture::CorUnproven(cor) => { // If the node is OR, mark it "real" if at least one child is marked real; else mark it "simulated" - let simulated = cast_to_unp(cor.children.clone())? + let simulated = cast_to_unp(cor.children.as_slice())? .iter() .all(|c| c.simulated()); Some( @@ -288,7 +291,7 @@ fn mark_real( } UnprovenConjecture::CthresholdUnproven(ct) => { // If the node is THRESHOLD(k), mark it "real" if at least k of its children are marked real; else mark it "simulated" - let simulated = cast_to_unp(ct.children.clone())? + let simulated = cast_to_unp(ct.children.as_slice())? .iter() .filter(|c| c.is_real()) .count() @@ -308,18 +311,23 @@ fn mark_real( fn set_positions(uc: UnprovenConjecture) -> Result { let upd_children = uc .children() - .try_mapped(|c| match c { + .iter() + .cloned() + .map(|c| match c { ProofTree::UncheckedTree(_) => Err(ProverError::Unexpected( "set_positions: expected UnprovenTree, got UncheckedTree", )), ProofTree::UnprovenTree(unp) => Ok(unp), - })? - .enumerated() - .mapped(|(idx, utree)| utree.with_position(uc.position().child(idx)).into()); + }) + .enumerate() + .map(|(idx, utree)| utree.map(|utree| utree.with_position(uc.position().child(idx)).into())) + .collect::, _>>()?; Ok(match uc { UnprovenConjecture::CandUnproven(cand) => cand.with_children(upd_children).into(), UnprovenConjecture::CorUnproven(cor) => cor.with_children(upd_children).into(), - UnprovenConjecture::CthresholdUnproven(ct) => ct.with_children(upd_children).into(), + UnprovenConjecture::CthresholdUnproven(ct) => { + ct.with_children(upd_children.try_into()?).into() + } }) } @@ -328,37 +336,40 @@ fn set_positions(uc: UnprovenConjecture) -> Result Result { - let casted_children = cast_to_unp(cor.children)?; + let casted_children = cast_to_unp(cor.children.as_slice())?; let first_real_child = casted_children.iter().find(|it| it.is_real()).ok_or({ ProverError::Unexpected( "make_cor_children_simulated: no real child is found amoung Cor children", ) })?; let children = casted_children - .clone() - .mapped(|c| { - if &c == first_real_child || c.simulated() { - c + .iter() + .map(|c| { + if c == first_real_child || c.simulated() { + c.clone() } else { - c.with_simulated(true) + c.clone().with_simulated(true) } }) - .mapped(|c| c.into()); + .map(|c| c.into()) + .collect(); Ok(CorUnproven { children, ..cor }) } -fn cast_to_unp( - children: SigmaConjectureItems, -) -> Result, ProverError> { - children.try_mapped(|c| { - if let ProofTree::UnprovenTree(ut) = c { - Ok(ut) - } else { - Err(ProverError::Unexpected( - "make_cor_children_simulated: expected UnprovenTree got UncheckedTree", - )) - } - }) +fn cast_to_unp(children: &[ProofTree]) -> Result, ProverError> { + children + .iter() + .cloned() + .map(|c| { + if let ProofTree::UnprovenTree(ut) = c { + Ok(ut) + } else { + Err(ProverError::Unexpected( + "make_cor_children_simulated: expected UnprovenTree got UncheckedTree", + )) + } + }) + .collect() } /// Prover Step 3: This step will change some "real" nodes to "simulated" to make sure each node has @@ -376,8 +387,11 @@ fn polish_simulated( // If the node is marked "simulated", mark all of its children "simulated" let a: CandUnproven = if cand.simulated { cand.clone().with_children( - cast_to_unp(cand.children.clone())? - .mapped(|c| c.with_simulated(true).into()), + cast_to_unp(&cand.children)? + .iter() + .cloned() + .map(|c| c.with_simulated(true).into()) + .collect(), ) } else { cand.clone() @@ -388,8 +402,10 @@ fn polish_simulated( // If the node is marked "simulated", mark all of its children "simulated" let o: CorUnproven = if cor.simulated { CorUnproven { - children: cast_to_unp(cor.children.clone())? - .mapped(|c| c.with_simulated(true).into()), + children: cast_to_unp(&cor.children)? + .into_iter() + .map(|c| c.with_simulated(true).into()) + .collect(), ..cor.clone() } } else { @@ -402,8 +418,12 @@ fn polish_simulated( // If the node is marked "simulated", mark all of its children "simulated" let t: CthresholdUnproven = if ct.simulated { ct.clone().with_children( - cast_to_unp(ct.children.clone())? - .mapped(|c| c.with_simulated(true).into()), + cast_to_unp(ct.children.as_slice())? + .iter() + .cloned() + .map(|c| c.with_simulated(true).into()) + .collect::>() + .try_into()?, ) } else { // If the node is THRESHOLD(k) marked "real", mark all but k of its children "simulated" @@ -414,8 +434,8 @@ fn polish_simulated( // We'll mark the first k real ones real let mut count_of_real = 0; let mut children_indices_to_be_marked_simulated = Vec::new(); - let unproven_children = cast_to_unp(ct.children.clone())?; - for (idx, kid) in unproven_children.clone().enumerated() { + let unproven_children = cast_to_unp(ct.children.as_slice())?; + for (idx, kid) in unproven_children.iter().enumerate() { if kid.is_real() { count_of_real += 1; if count_of_real > ct.k { @@ -423,15 +443,22 @@ fn polish_simulated( }; }; } - ct.clone() - .with_children(unproven_children.enumerated().mapped(|(idx, c)| { - if children_indices_to_be_marked_simulated.contains(&idx) { - c.with_simulated(true) - } else { - c - } - .into() - })) + ct.clone().with_children( + unproven_children + .iter() + .cloned() + .enumerate() + .map(|(idx, c)| { + if children_indices_to_be_marked_simulated.contains(&idx) { + c.with_simulated(true) + } else { + c + } + .into() + }) + .collect::>() + .try_into()?, + ) }; Ok(Some(set_positions(t.into())?.into())) } @@ -456,7 +483,9 @@ fn step4_real_conj( //real OR Threshold case UnprovenConjecture::CorUnproven(_) | UnprovenConjecture::CthresholdUnproven(_) => { let new_children = cast_to_unp(uc.children())? - .try_mapped(|c| -> Result<_, ProverError> { + .iter() + .cloned() + .map(|c| -> Result<_, ProverError> { if c.is_real() { Ok(c) } else { @@ -481,14 +510,15 @@ fn step4_real_conj( }; Ok(c.with_challenge(new_challenge)) } - })? - .mapped(|c| c.into()); + }) + .map(|c| c.map(ProofTree::from)) + .collect::, _>>()?; Ok(Some( - uc.with_children(new_children).into(), // CorUnproven { - // children: new_children, - // ..cor.clone() - // } - // .into(), + uc.with_children(new_children)?.into(), // CorUnproven { + // children: new_children, + // ..cor.clone() + // } + // .into(), )) } } @@ -500,8 +530,10 @@ fn step4_simulated_and_conj(cand: CandUnproven) -> Result, Pro if let Some(challenge) = cand.challenge_opt.clone() { let new_children = cand .children - .clone() - .mapped(|it| it.with_challenge(challenge.clone())); + .iter() + .cloned() + .map(|it| it.with_challenge(challenge.clone())) + .collect(); Ok(Some( CandUnproven { children: new_children, @@ -523,7 +555,7 @@ fn step4_simulated_or_conj(cor: CorUnproven) -> Result, Prover // the other children and e_0. assert!(cor.simulated); if let Some(challenge) = cor.challenge_opt.clone() { - let unproven_children = cast_to_unp(cor.children.clone())?; + let unproven_children = cast_to_unp(&cor.children)?; let mut tail: Vec = unproven_children .clone() .into_iter() @@ -538,6 +570,7 @@ fn step4_simulated_or_conj(cor: CorUnproven) -> Result, Prover } let head = unproven_children .first() + .ok_or_else(|| ProverError::Unexpected("Or.unproven_children is empty"))? .clone() .with_challenge(xored_challenge); let mut new_children = vec![head]; @@ -548,9 +581,7 @@ fn step4_simulated_or_conj(cor: CorUnproven) -> Result, Prover children: new_children .into_iter() .map(|c| c.into()) - .collect::>() - .try_into() - .unwrap(), + .collect::>(), ..cor } .into(), @@ -578,24 +609,28 @@ fn step4_simulated_threshold_conj( // to get challenges for child 1, 2, ..., n, respectively. assert!(ct.simulated); if let Some(challenge) = ct.challenge_opt.clone() { - let unproven_children = cast_to_unp(ct.children.clone())?; + let unproven_children = cast_to_unp(ct.children.as_slice())?; let n = ct.children.len(); let q = gf2_192poly_from_byte_array( challenge, super::crypto_utils::secure_random_bytes(super::SOUNDNESS_BYTES * (n - ct.k as usize)), )?; - let new_children = unproven_children - .enumerated() - .mapped(|(idx, c)| { + let new_children: Vec<_> = unproven_children + .into_iter() + .enumerate() + .map(|(idx, c)| { // Note the cast to `u8` is safe since `unproven_children` is of type // `SigmaConjectureItems<_>` which is a `BoundedVec<_, 2, 255>`. let one_based_idx = (idx + 1) as u8; let new_challenge = q.evaluate(one_based_idx).into(); c.with_challenge(new_challenge) }) - .mapped(|c| c.into()); + .map(|c| c.into()) + .collect(); Ok(Some( - ct.with_children(new_children).with_polynomial(q)?.into(), + ct.with_children(new_children.try_into()?) + .with_polynomial(q)? + .into(), )) } else { Err(ProverError::Unexpected( @@ -802,9 +837,10 @@ fn step9_real_and(cand: CandUnproven) -> Result, ProverError> // If the node is AND, let each of its children have the challenge e_0 if let Some(challenge) = cand.challenge_opt.clone() { let updated = cand - .clone() .children - .mapped(|child| child.with_challenge(challenge.clone())); + .iter() + .map(|child| child.with_challenge(challenge.clone())) + .collect(); Ok(Some(cand.with_children(updated).into())) } else { Err(ProverError::Unexpected( @@ -825,10 +861,17 @@ fn step9_real_or(cor: CorUnproven) -> Result, ProverError> { .iter() .flat_map(|c| c.challenge()) .fold(root_challenge.clone(), |acc, c| acc.xor(c)); - let children = cor.children.clone().mapped(|c| match c { - ProofTree::UnprovenTree(ref ut) if ut.is_real() => c.with_challenge(challenge.clone()), - _ => c, - }); + let children = cor + .children + .iter() + .cloned() + .map(|c| match c { + ProofTree::UnprovenTree(ref ut) if ut.is_real() => { + c.with_challenge(challenge.clone()) + } + _ => c, + }) + .collect(); Ok(Some( CorUnproven { children, @@ -1162,7 +1205,9 @@ fn convert_to_unproven(sb: SigmaBoolean) -> Result { simulated: false, children: cand .items - .try_mapped(|it| convert_to_unproven(it).map(Into::into))?, + .into_iter() + .map(|it| convert_to_unproven(it).map(Into::into)) + .collect::, _>>()?, position: NodePosition::crypto_tree_prefix(), } .into(), @@ -1172,7 +1217,9 @@ fn convert_to_unproven(sb: SigmaBoolean) -> Result { simulated: false, children: cor .items - .try_mapped(|it| convert_to_unproven(it).map(Into::into))?, + .into_iter() + .map(|it| convert_to_unproven(it).map(Into::into)) + .collect::, _>>()?, position: NodePosition::crypto_tree_prefix(), } .into(), @@ -1194,7 +1241,7 @@ fn convert_to_unproven(sb: SigmaBoolean) -> Result { } fn convert_to_unchecked(tree: ProofTree) -> Result { - match &tree { + match tree { ProofTree::UncheckedTree(unch_tree) => match unch_tree { UncheckedTree::UncheckedLeaf(_) => Ok(unch_tree.clone()), UncheckedTree::UncheckedConjecture(_) => Err(ProverError::Unexpected( @@ -1211,7 +1258,11 @@ fn convert_to_unchecked(tree: ProofTree) -> Result { .challenge_opt .clone() .ok_or(ProverError::Unexpected("no challenge in CandUnproven"))?, - children: cand.children.clone().try_mapped(convert_to_unchecked)?, + children: cand + .children + .into_iter() + .map(convert_to_unchecked) + .collect::, _>>()?, } .into()), UnprovenConjecture::CorUnproven(cor) => Ok(UncheckedConjecture::CorUnchecked { @@ -1219,7 +1270,11 @@ fn convert_to_unchecked(tree: ProofTree) -> Result { .challenge_opt .clone() .ok_or(ProverError::Unexpected("no challenge in CorUnproven"))?, - children: cor.children.clone().try_mapped(convert_to_unchecked)?, + children: cor + .children + .into_iter() + .map(convert_to_unchecked) + .collect::, _>>()?, } .into()), UnprovenConjecture::CthresholdUnproven(ct) => { diff --git a/ergotree-interpreter/src/sigma_protocol/prover/hint.rs b/ergotree-interpreter/src/sigma_protocol/prover/hint.rs index b5ebeec2e..e1ca48291 100644 --- a/ergotree-interpreter/src/sigma_protocol/prover/hint.rs +++ b/ergotree-interpreter/src/sigma_protocol/prover/hint.rs @@ -389,14 +389,14 @@ mod arbitrary { challenge: _, children, }) => Cand { - items: children.mapped_ref(extract_sigma_boolean), + items: children.iter().map(extract_sigma_boolean).collect(), } .into(), UncheckedTree::UncheckedConjecture(UncheckedConjecture::CorUnchecked { challenge: _, children, }) => Cor { - items: children.mapped_ref(extract_sigma_boolean), + items: children.iter().map(extract_sigma_boolean).collect(), } .into(), UncheckedTree::UncheckedConjecture(UncheckedConjecture::CthresholdUnchecked { diff --git a/ergotree-interpreter/src/sigma_protocol/sig_serializer.rs b/ergotree-interpreter/src/sigma_protocol/sig_serializer.rs index c6e296425..be2dbf362 100644 --- a/ergotree-interpreter/src/sigma_protocol/sig_serializer.rs +++ b/ergotree-interpreter/src/sigma_protocol/sig_serializer.rs @@ -1,7 +1,5 @@ //! Serialization of proof tree signatures -use core::convert::TryInto; - use super::gf2_192::gf2_192poly_from_byte_array; use super::prover::ProofBytes; use super::unchecked_tree::UncheckedConjecture; @@ -18,6 +16,8 @@ use crate::sigma_protocol::UncheckedSchnorr; use alloc::boxed::Box; use alloc::vec::Vec; + +use bounded_vec::BoundedVecOutOfBounds; use ergotree_ir::serialization::sigma_byte_reader; use ergotree_ir::serialization::sigma_byte_reader::SigmaByteRead; use ergotree_ir::serialization::sigma_byte_writer::SigmaByteWrite; @@ -81,7 +81,13 @@ fn sig_write_bytes( children, } => { // don't write last child's challenge -- it's computed by the verifier via XOR - let (last, elements) = children.split_last(); + let (last, elements) = children.split_last().ok_or_else(|| { + #[allow(clippy::io_other_error)] + core2::io::Error::new( + core2::io::ErrorKind::Other, + "CorUnchecked has 0 children", + ) + })?; for child in elements { sig_write_bytes(child, w, true)?; } @@ -175,9 +181,11 @@ fn parse_sig_compute_challenges_reader( SigmaConjecture::Cand(cand) => { // Verifier Step 2: If the node is AND, then all of its children get e_0 as // the challenge - let children = cand.items.try_mapped_ref(|it| { - parse_sig_compute_challenges_reader(it, r, Some(challenge.clone())) - })?; + let children = cand + .items + .iter() + .map(|it| parse_sig_compute_challenges_reader(it, r, Some(challenge.clone()))) + .collect::, _>>()?; Ok(UncheckedConjecture::CandUnchecked { challenge, children, @@ -192,7 +200,10 @@ fn parse_sig_compute_challenges_reader( // Read all the children but the last and compute the XOR of all the challenges including e_0 let mut children: Vec = Vec::with_capacity(cor.items.len()); - let (last, rest) = cor.items.split_last(); + let (last, rest) = cor + .items + .split_last() + .ok_or(SigParsingError::Misc("Cor.items is empty"))?; for it in rest { children.push(parse_sig_compute_challenges_reader(it, r, None)?); } @@ -205,10 +216,9 @@ fn parse_sig_compute_challenges_reader( parse_sig_compute_challenges_reader(last, r, Some(xored_challenge))?; children.push(last_child); - #[allow(clippy::unwrap_used)] // since quantity is preserved unwrap is safe here Ok(UncheckedConjecture::CorUnchecked { challenge, - children: children.try_into().unwrap(), + children, } .into()) } @@ -284,6 +294,12 @@ pub enum SigParsingError { #[error("Error: {0:?} for top level exp: {1:?}")] TopLevelExpWrap(Box, SigmaBoolean), + + #[error("BoundedVec out of bounds: {0}")] + BoundedVecOutOfBounds(#[from] BoundedVecOutOfBounds), + + #[error("Misc error: {0}")] + Misc(&'static str), } #[cfg(test)] diff --git a/ergotree-interpreter/src/sigma_protocol/unchecked_tree.rs b/ergotree-interpreter/src/sigma_protocol/unchecked_tree.rs index 48d7841ff..154e5e89b 100644 --- a/ergotree-interpreter/src/sigma_protocol/unchecked_tree.rs +++ b/ergotree-interpreter/src/sigma_protocol/unchecked_tree.rs @@ -1,6 +1,7 @@ //! Unchecked proof tree types use alloc::vec::Vec; +use bounded_vec::BoundedVecOutOfBounds; use ergo_chain_types::Base16EncodedBytes; use ergotree_ir::sigma_protocol::sigma_boolean::ProveDhTuple; use ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog; @@ -176,14 +177,14 @@ pub enum UncheckedConjecture { /// Challenge challenge: Challenge, /// Children - children: SigmaConjectureItems, + children: Vec, }, /// Unchecked Or Conjecture CorUnchecked { /// Challenge challenge: Challenge, /// Children - children: SigmaConjectureItems, + children: Vec, }, /// Unchecked Cthreshold Conjecture CthresholdUnchecked { @@ -200,8 +201,11 @@ pub enum UncheckedConjecture { impl UncheckedConjecture { /// Set New Children - pub fn with_children(self, new_children: SigmaConjectureItems) -> Self { - match self { + pub fn with_children( + self, + new_children: Vec, + ) -> Result { + Ok(match self { UncheckedConjecture::CandUnchecked { challenge, children: _, @@ -223,14 +227,14 @@ impl UncheckedConjecture { polynomial: polynomial_opt, } => UncheckedConjecture::CthresholdUnchecked { challenge, - children: new_children, + children: new_children.try_into()?, k, polynomial: polynomial_opt, }, - } + }) } /// Get Children - pub fn children_ust(self) -> SigmaConjectureItems { + pub fn children_ust(&self) -> &[UncheckedTree] { match self { UncheckedConjecture::CandUnchecked { challenge: _, @@ -245,7 +249,7 @@ impl UncheckedConjecture { children, k: _, polynomial: _, - } => children, + } => children.as_slice(), } } /// Get Children @@ -310,22 +314,22 @@ impl ProofTreeConjecture for UncheckedConjecture { } /// Get Children - fn children(&self) -> SigmaConjectureItems { + fn children(&self) -> Vec { match self { UncheckedConjecture::CandUnchecked { challenge: _, children, - } => children.mapped_ref(|ust| ust.clone().into()), + } => children.iter().map(|ust| ust.clone().into()).collect(), UncheckedConjecture::CorUnchecked { challenge: _, children, - } => children.mapped_ref(|ust| ust.clone().into()), + } => children.iter().map(|ust| ust.clone().into()).collect(), UncheckedConjecture::CthresholdUnchecked { challenge: _, children, k: _, polynomial: _, - } => children.mapped_ref(|ust| ust.clone().into()), + } => children.mapped_ref(|ust| ust.clone().into()).into(), } } } @@ -359,13 +363,13 @@ mod arbitrary { prop_oneof![ (vec(elem.clone(), 2..=3), any::()) .prop_map(|(elems, challenge)| UncheckedConjecture::CandUnchecked { - children: elems.try_into().unwrap(), + children: elems, challenge, }) .prop_map_into(), (vec(elem.clone(), 2..=3), any::()) .prop_map(|(elems, challenge)| UncheckedConjecture::CorUnchecked { - children: elems.try_into().unwrap(), + children: elems, challenge }) .prop_map_into(), diff --git a/ergotree-interpreter/src/sigma_protocol/unproven_tree.rs b/ergotree-interpreter/src/sigma_protocol/unproven_tree.rs index b6de29614..c63c36057 100644 --- a/ergotree-interpreter/src/sigma_protocol/unproven_tree.rs +++ b/ergotree-interpreter/src/sigma_protocol/unproven_tree.rs @@ -11,6 +11,7 @@ use super::{dlog_protocol::FirstDlogProverMessage, Challenge, FirstProverMessage use crate::sigma_protocol::proof_tree::ProofTreeLeaf; use crate::sigma_protocol::SOUNDNESS_BYTES; use alloc::vec::Vec; +use bounded_vec::BoundedVecOutOfBounds; use ergotree_ir::sigma_protocol::sigma_boolean::cand::Cand; use ergotree_ir::sigma_protocol::sigma_boolean::cor::Cor; use ergotree_ir::sigma_protocol::sigma_boolean::cthreshold::Cthreshold; @@ -205,20 +206,25 @@ pub(crate) enum UnprovenConjecture { } impl UnprovenConjecture { - pub(crate) fn children(&self) -> SigmaConjectureItems { + pub(crate) fn children(&self) -> &[ProofTree] { match self { - UnprovenConjecture::CandUnproven(cand) => cand.children.clone(), - UnprovenConjecture::CorUnproven(cor) => cor.children.clone(), - UnprovenConjecture::CthresholdUnproven(ct) => ct.children.clone(), + UnprovenConjecture::CandUnproven(cand) => &cand.children, + UnprovenConjecture::CorUnproven(cor) => &cor.children, + UnprovenConjecture::CthresholdUnproven(ct) => ct.children.as_slice(), } } - pub(crate) fn with_children(self, children: SigmaConjectureItems) -> Self { - match self { + pub(crate) fn with_children( + self, + children: Vec, + ) -> Result { + Ok(match self { UnprovenConjecture::CandUnproven(cand) => cand.with_children(children).into(), UnprovenConjecture::CorUnproven(cor) => cor.with_children(children).into(), - UnprovenConjecture::CthresholdUnproven(ct) => ct.with_children(children).into(), - } + UnprovenConjecture::CthresholdUnproven(ct) => { + ct.with_children(children.try_into()?).into() + } + }) } pub(crate) fn position(&self) -> &NodePosition { @@ -283,11 +289,11 @@ impl ProofTreeConjecture for UnprovenConjecture { } } - fn children(&self) -> SigmaConjectureItems { + fn children(&self) -> Vec { match self { UnprovenConjecture::CandUnproven(cand) => cand.children.clone(), UnprovenConjecture::CorUnproven(cor) => cor.children.clone(), - UnprovenConjecture::CthresholdUnproven(ct) => ct.children.clone(), + UnprovenConjecture::CthresholdUnproven(ct) => ct.children.clone().into(), } } } @@ -433,7 +439,7 @@ pub(crate) struct CandUnproven { pub(crate) proposition: Cand, pub(crate) challenge_opt: Option, pub(crate) simulated: bool, - pub(crate) children: SigmaConjectureItems, + pub(crate) children: Vec, pub(crate) position: NodePosition, } @@ -460,7 +466,7 @@ impl CandUnproven { Self { simulated, ..self } } - pub(crate) fn with_children(self, children: SigmaConjectureItems) -> Self { + pub(crate) fn with_children(self, children: Vec) -> Self { CandUnproven { children, ..self } } } @@ -470,7 +476,7 @@ pub(crate) struct CorUnproven { pub(crate) proposition: Cor, pub(crate) challenge_opt: Option, pub(crate) simulated: bool, - pub(crate) children: SigmaConjectureItems, + pub(crate) children: Vec, pub(crate) position: NodePosition, } @@ -497,7 +503,7 @@ impl CorUnproven { Self { simulated, ..self } } - pub(crate) fn with_children(self, children: SigmaConjectureItems) -> Self { + pub(crate) fn with_children(self, children: Vec) -> Self { Self { children, ..self } } } diff --git a/ergotree-interpreter/src/sigma_protocol/verifier.rs b/ergotree-interpreter/src/sigma_protocol/verifier.rs index 64202fcc6..d5672bd69 100644 --- a/ergotree-interpreter/src/sigma_protocol/verifier.rs +++ b/ergotree-interpreter/src/sigma_protocol/verifier.rs @@ -15,6 +15,8 @@ use super::{ }; use crate::eval::EvalError; use crate::eval::{reduce_to_crypto, ReductionDiagnosticInfo}; +use alloc::vec::Vec; +use bounded_vec::BoundedVecOutOfBounds; use dlog_protocol::FirstDlogProverMessage; use ergotree_ir::chain::context::Context; use ergotree_ir::ergo_tree::ErgoTree; @@ -38,6 +40,9 @@ pub enum VerifierError { /// Error while tree serialization for Fiat-Shamir hash #[error("Fiat-Shamir tree serialization error: {0}")] FiatShamirTreeSerializationError(FiatShamirTreeSerializationError), + /// BoundedVecOutOfBounds error + #[error("Bounded vec out of bounds: {0}")] + BoundedVecOutOfBounds(BoundedVecOutOfBounds), } /// Result of Box.ergoTree verification procedure (see `verify` method). @@ -113,7 +118,7 @@ pub fn verify_signature( /// Perform Verifier Steps 4-6 fn check_commitments(sp: UncheckedTree, message: &[u8]) -> Result { // Perform Verifier Step 4 - let new_root = compute_commitments(sp); + let new_root = compute_commitments(sp)?; let mut s = fiat_shamir_tree_to_bytes(&new_root.clone().into())?; s.append(&mut message.to_vec()); // Verifier Steps 5-6: Convert the tree to a string `s` for input to the Fiat-Shamir hash function, @@ -127,8 +132,8 @@ fn check_commitments(sp: UncheckedTree, message: &[u8]) -> Result UncheckedTree { - match sp { +pub fn compute_commitments(sp: UncheckedTree) -> Result { + Ok(match sp { UncheckedTree::UncheckedLeaf(leaf) => match leaf { UncheckedLeaf::UncheckedSchnorr(sn) => { let a = dlog_protocol::interactive_prover::compute_commitment( @@ -157,9 +162,15 @@ pub fn compute_commitments(sp: UncheckedTree) -> UncheckedTree { }, UncheckedTree::UncheckedConjecture(conj) => conj .clone() - .with_children(conj.children_ust().mapped(compute_commitments)) + .with_children( + conj.children_ust() + .iter() + .cloned() + .map(compute_commitments) + .collect::, _>>()?, + )? .into(), - } + }) } /// Test Verifier implementation diff --git a/ergotree-ir/src/chain/json/sigma_protocol.rs b/ergotree-ir/src/chain/json/sigma_protocol.rs index 434950db4..fc36e534a 100644 --- a/ergotree-ir/src/chain/json/sigma_protocol.rs +++ b/ergotree-ir/src/chain/json/sigma_protocol.rs @@ -60,22 +60,10 @@ impl From for SigmaBooleanJson { } SigmaBoolean::TrivialProp(tp) => SigmaBooleanJson::TrivialPropFalse { condition: tp }, SigmaBoolean::SigmaConjecture(SigmaConjecture::Cand(cand)) => SigmaBooleanJson::Cand { - args: cand - .items - .as_vec() - .clone() - .into_iter() - .map(Into::into) - .collect(), + args: cand.items.clone().into_iter().map(Into::into).collect(), }, SigmaBoolean::SigmaConjecture(SigmaConjecture::Cor(cor)) => SigmaBooleanJson::Cor { - args: cor - .items - .as_vec() - .clone() - .into_iter() - .map(Into::into) - .collect(), + args: cor.items.clone().into_iter().map(Into::into).collect(), }, SigmaBoolean::SigmaConjecture(SigmaConjecture::Cthreshold(ct)) => { SigmaBooleanJson::Cthreshold { @@ -108,16 +96,14 @@ impl TryFrom for SigmaBoolean { items: args .into_iter() .map(TryInto::try_into) - .collect::, _>>()? - .try_into()?, + .collect::, _>>()?, } .into(), SigmaBooleanJson::Cor { args } => Cor { items: args .into_iter() .map(TryInto::try_into) - .collect::, _>>()? - .try_into()?, + .collect::, _>>()?, } .into(), SigmaBooleanJson::Cthreshold { k, args } => Cthreshold { diff --git a/ergotree-ir/src/ergo_tree.rs b/ergotree-ir/src/ergo_tree.rs index a1d52708d..55f2e6b3e 100644 --- a/ergotree-ir/src/ergo_tree.rs +++ b/ergotree-ir/src/ergo_tree.rs @@ -10,6 +10,8 @@ use crate::serialization::{ SigmaParsingError, SigmaSerializable, }; use crate::sigma_protocol::sigma_boolean::ProveDlog; +use crate::soft_fork::IsSoftForkable; +use crate::soft_fork::SoftForkError; use crate::types::stype::SType; use alloc::string::String; @@ -107,9 +109,9 @@ pub enum ErgoTreeError { /// IO error #[error("IO error: {0:?}")] IoError(String), - /// ErgoTree root error. ErgoTree root TPE should be SigmaProp - #[error("Root Tpe error: expected SigmaProp, got {0}")] - RootTpeError(SType), + /// Soft-fork error condition + #[error("Soft fork error: {0}")] + SoftForkError(SoftForkError), } /// The root of ErgoScript IR. Serialized instances of this class are self sufficient and can be passed around. @@ -120,14 +122,14 @@ pub enum ErgoTree { /// Original tree bytes tree_bytes: Vec, /// Parsing error - error: ErgoTreeError, + error: SoftForkError, }, /// Parsed tree Parsed(ParsedErgoTree), } impl ErgoTree { - fn parsed_tree(&self) -> Result<&ParsedErgoTree, ErgoTreeError> { + fn parsed_tree(&self) -> Result<&ParsedErgoTree, SoftForkError> { match self { ErgoTree::Unparsed { tree_bytes: _, @@ -138,14 +140,14 @@ impl ErgoTree { } /// Return ErgoTreeHeader. Errors if deserializing ergotree failed - pub fn header(&self) -> Result { + pub fn header(&self) -> Result { self.parsed_tree().map(|parsed| parsed.header.clone()) } fn sigma_parse_sized( r: &mut R, header: ErgoTreeHeader, - ) -> Result { + ) -> Result { let constants = if header.is_constant_segregation() { ErgoTree::sigma_parse_constants(r)? } else { @@ -159,7 +161,7 @@ impl ErgoTree { let has_deserialize = r.was_deserialize(); r.set_deserialize(was_deserialize); if root.tpe() != SType::SSigmaProp { - return Err(ErgoTreeError::RootTpeError(root.tpe())); + return Err(SoftForkError::InvalidRootType.into()); } Ok(ParsedErgoTree { header, @@ -273,21 +275,21 @@ impl ErgoTree { /// Returns constants number as stored in serialized ErgoTree or error if the parsing of /// constants is failed - pub fn constants_len(&self) -> Result { + pub fn constants_len(&self) -> Result { self.parsed_tree().map(|tree| tree.constants.len()) } /// Returns constant with given index (as stored in serialized ErgoTree) /// or None if index is out of bounds /// or error if constants parsing were failed - pub fn get_constant(&self, index: usize) -> Result, ErgoTreeError> { + pub fn get_constant(&self, index: usize) -> Result, SoftForkError> { self.parsed_tree() .map(|tree| tree.constants.get(index).cloned()) } /// Returns all constants (as stored in serialized ErgoTree) /// or error if constants parsing were failed - pub fn get_constants(&self) -> Result, ErgoTreeError> { + pub fn get_constants(&self) -> Result, SoftForkError> { self.parsed_tree().map(|tree| tree.constants.clone()) } @@ -390,16 +392,18 @@ impl SigmaSerializable for ErgoTree { ErgoTree::sigma_parse_sized(inner_r, header) }) { Ok(parsed_tree) => Ok(parsed_tree.into()), - Err(error) => { + Err(error) if error.is_soft_fork() => { let num_bytes = (body_pos - start_pos) + tree_size_bytes as u64; r.seek(io::SeekFrom::Start(start_pos))?; let mut bytes = vec![0; num_bytes as usize]; r.read_exact(&mut bytes)?; Ok(ErgoTree::Unparsed { tree_bytes: bytes, - error, + #[allow(clippy::unwrap_used)] // Error is checked to be soft fork condition above + error: error.to_soft_fork().unwrap().clone(), }) } + Err(error) => Err(error), } } else { let constants = if header.is_constant_segregation() { @@ -503,6 +507,7 @@ mod tests { use crate::mir::constant::Literal; use crate::mir::deserialize_context::DeserializeContext; use crate::sigma_protocol::sigma_boolean::SigmaProp; + use crate::soft_fork::SoftForkError; use proptest::prelude::*; proptest! { @@ -537,7 +542,9 @@ mod tests { ]; assert_eq!( ErgoTree::sigma_parse_bytes(&bytes), - Err(SigmaParsingError::InvalidTypeCode(0)) + Err(SigmaParsingError::SoftForkError( + SoftForkError::InvalidPrimitiveType(0) + )) ); } @@ -696,7 +703,7 @@ mod tests { tree, ErgoTree::Unparsed { tree_bytes, - error: ErgoTreeError::RootTpeError(SType::SByte) + error: SoftForkError::InvalidRootType } ); } @@ -725,7 +732,7 @@ mod tests { tree, ErgoTree::Unparsed { tree_bytes: bytes, - error: ErgoTreeError::RootTpeError(SType::SShort) + error: SoftForkError::InvalidRootType } ); } diff --git a/ergotree-ir/src/lib.rs b/ergotree-ir/src/lib.rs index f1f0bca7f..224c9bf9b 100644 --- a/ergotree-ir/src/lib.rs +++ b/ergotree-ir/src/lib.rs @@ -38,6 +38,7 @@ pub mod sigma_protocol; pub mod source_span; #[macro_use] pub mod traversable; +pub mod soft_fork; pub mod type_check; pub mod types; pub mod unsignedbigint256; diff --git a/ergotree-ir/src/mir/apply.rs b/ergotree-ir/src/mir/apply.rs index 42c637169..3c23c046c 100644 --- a/ergotree-ir/src/mir/apply.rs +++ b/ergotree-ir/src/mir/apply.rs @@ -55,7 +55,11 @@ impl Apply { /// Type pub fn tpe(&self) -> SType { - self.tpe.clone() + match self.func.tpe() { + SType::SFunc(sfunc) => *sfunc.t_range.clone(), + SType::SColl(elem_tpe) => (*elem_tpe).clone(), + _ => SType::SAny, + } } } diff --git a/ergotree-ir/src/mir/atleast.rs b/ergotree-ir/src/mir/atleast.rs index e6a561827..b16245f52 100644 --- a/ergotree-ir/src/mir/atleast.rs +++ b/ergotree-ir/src/mir/atleast.rs @@ -64,7 +64,10 @@ impl SigmaSerializable for Atleast { fn sigma_parse(r: &mut R) -> Result { let bound = Expr::sigma_parse(r)?; let input = Expr::sigma_parse(r)?; - Ok(Self::new(bound, input)?) + Ok(Self { + bound: bound.into(), + input: input.into(), + }) } } diff --git a/ergotree-ir/src/mir/bit_inversion.rs b/ergotree-ir/src/mir/bit_inversion.rs index d4ae665f5..0f6de258a 100644 --- a/ergotree-ir/src/mir/bit_inversion.rs +++ b/ergotree-ir/src/mir/bit_inversion.rs @@ -44,9 +44,12 @@ impl OneArgOpTryBuild for BitInversion { post_eval_tpe ))); } - Ok(Self { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/bool_to_sigma.rs b/ergotree-ir/src/mir/bool_to_sigma.rs index 286026cd1..c294490f5 100644 --- a/ergotree-ir/src/mir/bool_to_sigma.rs +++ b/ergotree-ir/src/mir/bool_to_sigma.rs @@ -43,9 +43,13 @@ impl OneArgOp for BoolToSigmaProp { impl OneArgOpTryBuild for BoolToSigmaProp { fn try_build(input: Expr) -> Result { // Input TPE is not checked here as old versions of interpreter (v4.0) accepted SigmaProp as argument to BoolToSigmaProp - Ok(Self { + Ok(Self::build_unchecked(input)) + } + + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/byte_array_to_bigint.rs b/ergotree-ir/src/mir/byte_array_to_bigint.rs index c45aa5359..e34c2bc61 100644 --- a/ergotree-ir/src/mir/byte_array_to_bigint.rs +++ b/ergotree-ir/src/mir/byte_array_to_bigint.rs @@ -41,9 +41,13 @@ impl OneArgOp for ByteArrayToBigInt { impl OneArgOpTryBuild for ByteArrayToBigInt { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SColl(Arc::new(SType::SByte)))?; - Ok(ByteArrayToBigInt { - input: Box::new(input), - }) + Ok(Self::build_unchecked(input)) + } + + fn build_unchecked(input: Expr) -> Self { + Self { + input: input.into(), + } } } diff --git a/ergotree-ir/src/mir/byte_array_to_long.rs b/ergotree-ir/src/mir/byte_array_to_long.rs index 43018d2f9..d97b782a1 100644 --- a/ergotree-ir/src/mir/byte_array_to_long.rs +++ b/ergotree-ir/src/mir/byte_array_to_long.rs @@ -41,9 +41,13 @@ impl OneArgOp for ByteArrayToLong { impl OneArgOpTryBuild for ByteArrayToLong { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SColl(Arc::new(SType::SByte)))?; - Ok(ByteArrayToLong { - input: Box::new(input), - }) + Ok(Self::build_unchecked(input)) + } + + fn build_unchecked(input: Expr) -> Self { + Self { + input: input.into(), + } } } diff --git a/ergotree-ir/src/mir/calc_blake2b256.rs b/ergotree-ir/src/mir/calc_blake2b256.rs index 923dbbdfe..77bb585fd 100644 --- a/ergotree-ir/src/mir/calc_blake2b256.rs +++ b/ergotree-ir/src/mir/calc_blake2b256.rs @@ -40,9 +40,13 @@ impl OneArgOp for CalcBlake2b256 { impl OneArgOpTryBuild for CalcBlake2b256 { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SColl(Arc::new(SType::SByte)))?; - Ok(CalcBlake2b256 { - input: Box::new(input), - }) + Ok(Self::build_unchecked(input)) + } + + fn build_unchecked(input: Expr) -> Self { + Self { + input: input.into(), + } } } diff --git a/ergotree-ir/src/mir/calc_sha256.rs b/ergotree-ir/src/mir/calc_sha256.rs index e415d0b32..949f74994 100644 --- a/ergotree-ir/src/mir/calc_sha256.rs +++ b/ergotree-ir/src/mir/calc_sha256.rs @@ -40,9 +40,13 @@ impl OneArgOp for CalcSha256 { impl OneArgOpTryBuild for CalcSha256 { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SColl(Arc::new(SType::SByte)))?; - Ok(CalcSha256 { - input: Box::new(input), - }) + Ok(CalcSha256::build_unchecked(input)) + } + + fn build_unchecked(input: Expr) -> Self { + Self { + input: input.into(), + } } } diff --git a/ergotree-ir/src/mir/coll_append.rs b/ergotree-ir/src/mir/coll_append.rs index 054e6b0dd..00e3e2ccc 100644 --- a/ergotree-ir/src/mir/coll_append.rs +++ b/ergotree-ir/src/mir/coll_append.rs @@ -74,7 +74,10 @@ impl SigmaSerializable for Append { fn sigma_parse(r: &mut R) -> Result { let input = Expr::sigma_parse(r)?; let col_2 = Expr::sigma_parse(r)?; - Ok(Append::new(input, col_2)?) + Ok(Append { + input: input.into(), + col_2: col_2.into(), + }) } } diff --git a/ergotree-ir/src/mir/coll_by_index.rs b/ergotree-ir/src/mir/coll_by_index.rs index cb3c5bf35..5ecb1362d 100644 --- a/ergotree-ir/src/mir/coll_by_index.rs +++ b/ergotree-ir/src/mir/coll_by_index.rs @@ -66,6 +66,27 @@ impl ByIndex { }) } + // The reference implementation only checks if input is of type SColl[T] when deserializing + fn build_unchecked( + input: Expr, + index: Expr, + default: Option>, + ) -> Result { + let input_elem_type = match input.post_eval_tpe() { + SType::SColl(elem_type) => Ok(elem_type), + _ => Err(InvalidArgumentError(format!( + "Expected ByIndex input to be SColl, got {0:?}", + input.tpe() + ))), + }?; + Ok(Self { + input: input.into(), + index: index.into(), + default, + input_elem_tpe: input_elem_type, + }) + } + /// Type pub fn tpe(&self) -> SType { (*self.input_elem_tpe).clone() @@ -87,7 +108,7 @@ impl SigmaSerializable for ByIndex { let input = Expr::sigma_parse(r)?; let index = Expr::sigma_parse(r)?; let default = Option::>::sigma_parse(r)?; - Ok(Self::new(input, index, default)?) + Ok(Self::build_unchecked(input, index, default)?) } } diff --git a/ergotree-ir/src/mir/coll_exists.rs b/ergotree-ir/src/mir/coll_exists.rs index 97e7bfb83..bb9f6ed94 100644 --- a/ergotree-ir/src/mir/coll_exists.rs +++ b/ergotree-ir/src/mir/coll_exists.rs @@ -1,7 +1,5 @@ use alloc::boxed::Box; -use alloc::sync::Arc; - use super::expr::Expr; use super::expr::InvalidArgumentError; use crate::has_opcode::HasStaticOpCode; @@ -21,8 +19,6 @@ pub struct Exists { pub input: Box, /// Function (lambda) to test each element pub condition: Box, - /// Collection element type - pub elem_tpe: Arc, } impl Exists { @@ -44,7 +40,6 @@ impl Exists { Ok(Exists { input: input.into(), condition: condition.into(), - elem_tpe: input_elem_type, }) } _ => Err(InvalidArgumentError(format!( @@ -73,7 +68,10 @@ impl SigmaSerializable for Exists { fn sigma_parse(r: &mut R) -> Result { let input = Expr::sigma_parse(r)?; let condition = Expr::sigma_parse(r)?; - Ok(Exists::new(input, condition)?) + Ok(Exists { + input: input.into(), + condition: condition.into(), + }) } } diff --git a/ergotree-ir/src/mir/coll_filter.rs b/ergotree-ir/src/mir/coll_filter.rs index 9dd22c079..0dde068fb 100644 --- a/ergotree-ir/src/mir/coll_filter.rs +++ b/ergotree-ir/src/mir/coll_filter.rs @@ -1,7 +1,5 @@ use alloc::boxed::Box; -use alloc::sync::Arc; - use super::expr::Expr; use super::expr::InvalidArgumentError; use crate::has_opcode::HasStaticOpCode; @@ -21,8 +19,6 @@ pub struct Filter { pub input: Box, /// Function (lambda) to test each element pub condition: Box, - /// Collection element type - pub elem_tpe: Arc, } impl Filter { @@ -44,7 +40,6 @@ impl Filter { Ok(Filter { input: input.into(), condition: condition.into(), - elem_tpe: input_elem_type, }) } _ => Err(InvalidArgumentError(format!( @@ -56,7 +51,7 @@ impl Filter { /// Type pub fn tpe(&self) -> SType { - SType::SColl(self.elem_tpe.clone()) + self.input.tpe() } } @@ -73,7 +68,10 @@ impl SigmaSerializable for Filter { fn sigma_parse(r: &mut R) -> Result { let input = Expr::sigma_parse(r)?; let condition = Expr::sigma_parse(r)?; - Ok(Filter::new(input, condition)?) + Ok(Filter { + input: input.into(), + condition: condition.into(), + }) } } diff --git a/ergotree-ir/src/mir/coll_forall.rs b/ergotree-ir/src/mir/coll_forall.rs index f24a56d0a..f83c81f90 100644 --- a/ergotree-ir/src/mir/coll_forall.rs +++ b/ergotree-ir/src/mir/coll_forall.rs @@ -1,7 +1,5 @@ use alloc::boxed::Box; -use alloc::sync::Arc; - use super::expr::Expr; use super::expr::InvalidArgumentError; use crate::has_opcode::HasStaticOpCode; @@ -21,8 +19,6 @@ pub struct ForAll { pub input: Box, /// Function (lambda) to test each element pub condition: Box, - /// Collection element type - pub elem_tpe: Arc, } impl ForAll { @@ -44,7 +40,6 @@ impl ForAll { Ok(ForAll { input: input.into(), condition: condition.into(), - elem_tpe: input_elem_type, }) } _ => Err(InvalidArgumentError(format!( @@ -73,7 +68,10 @@ impl SigmaSerializable for ForAll { fn sigma_parse(r: &mut R) -> Result { let input = Expr::sigma_parse(r)?; let condition = Expr::sigma_parse(r)?; - Ok(ForAll::new(input, condition)?) + Ok(ForAll { + input: input.into(), + condition: condition.into(), + }) } } diff --git a/ergotree-ir/src/mir/coll_map.rs b/ergotree-ir/src/mir/coll_map.rs index a52038e62..3f12826c4 100644 --- a/ergotree-ir/src/mir/coll_map.rs +++ b/ergotree-ir/src/mir/coll_map.rs @@ -50,6 +50,21 @@ impl Map { } } + fn build_unchecked(input: Expr, mapper: Expr) -> Result { + // The reference implementation only checks if mapper is of type SFunc when deserializing + match mapper.tpe() { + SType::SFunc(sfunc) => Ok(Map { + input: input.into(), + mapper: mapper.into(), + mapper_sfunc: sfunc, + }), + _ => Err(InvalidArgumentError(format!( + "Invalid mapper tpe: {0:?}", + mapper.tpe() + ))), + } + } + /// Type pub fn tpe(&self) -> SType { SType::SColl(self.mapper_sfunc.t_range.clone().into()) @@ -74,7 +89,7 @@ impl SigmaSerializable for Map { fn sigma_parse(r: &mut R) -> Result { let input = Expr::sigma_parse(r)?; let mapper = Expr::sigma_parse(r)?; - Ok(Map::new(input, mapper)?) + Ok(Map::build_unchecked(input, mapper)?) } } diff --git a/ergotree-ir/src/mir/coll_size.rs b/ergotree-ir/src/mir/coll_size.rs index 9808d8d67..eedeac430 100644 --- a/ergotree-ir/src/mir/coll_size.rs +++ b/ergotree-ir/src/mir/coll_size.rs @@ -48,6 +48,11 @@ impl OneArgOpTryBuild for SizeOf { ))), } } + fn build_unchecked(input: Expr) -> Self { + Self { + input: input.into(), + } + } } #[cfg(feature = "arbitrary")] diff --git a/ergotree-ir/src/mir/coll_slice.rs b/ergotree-ir/src/mir/coll_slice.rs index bc53d4b34..d3e6aaf9f 100644 --- a/ergotree-ir/src/mir/coll_slice.rs +++ b/ergotree-ir/src/mir/coll_slice.rs @@ -76,7 +76,11 @@ impl SigmaSerializable for Slice { let input = Expr::sigma_parse(r)?; let from = Expr::sigma_parse(r)?; let until = Expr::sigma_parse(r)?; - Ok(Self::new(input, from, until)?) + Ok(Self { + input: input.into(), + from: from.into(), + until: until.into(), + }) } } diff --git a/ergotree-ir/src/mir/create_avl_tree.rs b/ergotree-ir/src/mir/create_avl_tree.rs index 3959b3468..025f51705 100644 --- a/ergotree-ir/src/mir/create_avl_tree.rs +++ b/ergotree-ir/src/mir/create_avl_tree.rs @@ -71,7 +71,12 @@ impl SigmaSerializable for CreateAvlTree { let digest = Expr::sigma_parse(r)?; let key_length = Expr::sigma_parse(r)?; let value_length = Option::>::sigma_parse(r)?; - Ok(Self::new(flags, digest, key_length, value_length)?) + Ok(Self { + flags: flags.into(), + digest: digest.into(), + key_length: key_length.into(), + value_length, + }) } fn sigma_serialize(&self, w: &mut W) -> SigmaSerializeResult { diff --git a/ergotree-ir/src/mir/create_provedlog.rs b/ergotree-ir/src/mir/create_provedlog.rs index de4ea5d8a..b52852df7 100644 --- a/ergotree-ir/src/mir/create_provedlog.rs +++ b/ergotree-ir/src/mir/create_provedlog.rs @@ -43,6 +43,11 @@ impl OneArgOpTryBuild for CreateProveDlog { input: input.into(), }) } + fn build_unchecked(input: Expr) -> Self { + Self { + input: input.into(), + } + } } #[cfg(test)] diff --git a/ergotree-ir/src/mir/decode_point.rs b/ergotree-ir/src/mir/decode_point.rs index 15fd10e58..aa07fdecc 100644 --- a/ergotree-ir/src/mir/decode_point.rs +++ b/ergotree-ir/src/mir/decode_point.rs @@ -42,9 +42,12 @@ impl OneArgOp for DecodePoint { impl OneArgOpTryBuild for DecodePoint { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SColl(Arc::new(SType::SByte)))?; - Ok(Self { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/downcast.rs b/ergotree-ir/src/mir/downcast.rs index c657c6b4e..74d0e8ff9 100644 --- a/ergotree-ir/src/mir/downcast.rs +++ b/ergotree-ir/src/mir/downcast.rs @@ -33,6 +33,10 @@ impl Downcast { target_tpe ))); } + Self::build_unchecked(input, target_tpe) + } + + fn build_unchecked(input: Expr, target_tpe: SType) -> Result { let post_eval_tpe = input.post_eval_tpe(); if post_eval_tpe.is_numeric() { Ok(Self { @@ -64,9 +68,9 @@ impl SigmaSerializable for Downcast { } fn sigma_parse(r: &mut R) -> Result { - let input = Expr::sigma_parse(r)?.into(); + let input = Expr::sigma_parse(r)?; let tpe = SType::sigma_parse(r)?; - Ok(Downcast { input, tpe }) + Ok(Self::build_unchecked(input, tpe)?) } } diff --git a/ergotree-ir/src/mir/expr.rs b/ergotree-ir/src/mir/expr.rs index 867676e5f..b14989d9d 100644 --- a/ergotree-ir/src/mir/expr.rs +++ b/ergotree-ir/src/mir/expr.rs @@ -17,6 +17,7 @@ use crate::serialization::sigma_byte_reader; use crate::serialization::sigma_byte_reader::SigmaByteRead; use crate::serialization::SigmaParsingError; use crate::serialization::SigmaSerializable; +use crate::soft_fork::SoftForkError; use crate::source_span::Spanned; use crate::traversable::Traversable; use crate::types::stype::LiftIntoSType; @@ -450,10 +451,7 @@ impl Expr { _ => unreachable!(), }; if parsed_expr.tpe() != *tpe { - return Err(SubstDeserializeError::ExprTpeError { - expected: tpe.clone(), - actual: parsed_expr.tpe(), - }); + return Err(SoftForkError::DeserializedScriptError.into()); } *expr = parsed_expr; Ok(()) @@ -685,8 +683,8 @@ pub enum SubstDeserializeError { RegisterValueError(#[from] RegisterValueError), #[error("Error while parsing Expr from bytes: {0}")] ExprParsingError(#[from] SigmaParsingError), - #[error("Expected tpe {expected}, found {actual}")] - ExprTpeError { expected: SType, actual: SType }, + #[error("{0}")] + SoftForkError(#[from] SoftForkError), } impl> TryExtractFrom for T { diff --git a/ergotree-ir/src/mir/extract_amount.rs b/ergotree-ir/src/mir/extract_amount.rs index e189f1936..e2ba7994d 100644 --- a/ergotree-ir/src/mir/extract_amount.rs +++ b/ergotree-ir/src/mir/extract_amount.rs @@ -39,9 +39,12 @@ impl OneArgOp for ExtractAmount { impl OneArgOpTryBuild for ExtractAmount { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SBox)?; - Ok(ExtractAmount { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/extract_bytes.rs b/ergotree-ir/src/mir/extract_bytes.rs index b7bdb009c..2412b947b 100644 --- a/ergotree-ir/src/mir/extract_bytes.rs +++ b/ergotree-ir/src/mir/extract_bytes.rs @@ -39,9 +39,12 @@ impl OneArgOp for ExtractBytes { impl OneArgOpTryBuild for ExtractBytes { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SBox)?; - Ok(ExtractBytes { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/extract_bytes_with_no_ref.rs b/ergotree-ir/src/mir/extract_bytes_with_no_ref.rs index 6afa79184..085c367f5 100644 --- a/ergotree-ir/src/mir/extract_bytes_with_no_ref.rs +++ b/ergotree-ir/src/mir/extract_bytes_with_no_ref.rs @@ -39,9 +39,12 @@ impl OneArgOp for ExtractBytesWithNoRef { impl OneArgOpTryBuild for ExtractBytesWithNoRef { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SBox)?; - Ok(ExtractBytesWithNoRef { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/extract_creation_info.rs b/ergotree-ir/src/mir/extract_creation_info.rs index f1ed83157..771f35440 100644 --- a/ergotree-ir/src/mir/extract_creation_info.rs +++ b/ergotree-ir/src/mir/extract_creation_info.rs @@ -41,9 +41,12 @@ impl OneArgOp for ExtractCreationInfo { impl OneArgOpTryBuild for ExtractCreationInfo { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SBox)?; - Ok(ExtractCreationInfo { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/extract_id.rs b/ergotree-ir/src/mir/extract_id.rs index bca53f090..025c75a09 100644 --- a/ergotree-ir/src/mir/extract_id.rs +++ b/ergotree-ir/src/mir/extract_id.rs @@ -39,9 +39,12 @@ impl OneArgOp for ExtractId { impl OneArgOpTryBuild for ExtractId { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SBox)?; - Ok(ExtractId { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/extract_reg_as.rs b/ergotree-ir/src/mir/extract_reg_as.rs index fe4e1b800..ed7e9945d 100644 --- a/ergotree-ir/src/mir/extract_reg_as.rs +++ b/ergotree-ir/src/mir/extract_reg_as.rs @@ -1,7 +1,8 @@ +use crate::alloc::string::ToString; use alloc::boxed::Box; - use alloc::sync::Arc; +use crate::chain::ergo_box::RegisterId; use crate::serialization::op_code::OpCode; use crate::serialization::sigma_byte_reader::SigmaByteRead; use crate::serialization::sigma_byte_writer::SigmaByteWrite; @@ -21,7 +22,7 @@ pub struct ExtractRegisterAs { /// Box pub input: Box, /// Register id to extract value from (0 is R0 .. 9 for R9) - pub register_id: i8, + pub register_id: RegisterId, /// Result type, to be wrapped in SOption pub elem_tpe: Arc, } @@ -35,6 +36,14 @@ impl ExtractRegisterAs { input ))); } + Self::build_unchecked(input, register_id, tpe) + } + + fn build_unchecked( + input: Expr, + register_id: i8, + tpe: SType, + ) -> Result { let elem_tpe = match tpe { SType::SOption(t) => Ok(t), _ => Err(InvalidArgumentError(format!( @@ -45,7 +54,8 @@ impl ExtractRegisterAs { Ok(ExtractRegisterAs { input: input.into(), - register_id, + register_id: RegisterId::try_from(register_id) + .map_err(|e| InvalidArgumentError(e.to_string()))?, elem_tpe, }) } @@ -63,7 +73,7 @@ impl HasStaticOpCode for ExtractRegisterAs { impl SigmaSerializable for ExtractRegisterAs { fn sigma_serialize(&self, w: &mut W) -> SigmaSerializeResult { self.input.sigma_serialize(w)?; - w.put_i8(self.register_id)?; + w.put_u8(self.register_id.into())?; self.elem_tpe.sigma_serialize(w) } @@ -71,7 +81,7 @@ impl SigmaSerializable for ExtractRegisterAs { let input = Expr::sigma_parse(r)?; let register_id = r.get_i8()?; let elem_tpe = SType::sigma_parse(r)?; - Ok(ExtractRegisterAs::new( + Ok(ExtractRegisterAs::build_unchecked( input, register_id, SType::SOption(elem_tpe.into()), diff --git a/ergotree-ir/src/mir/extract_script_bytes.rs b/ergotree-ir/src/mir/extract_script_bytes.rs index cfa8be5ab..632e66fb1 100644 --- a/ergotree-ir/src/mir/extract_script_bytes.rs +++ b/ergotree-ir/src/mir/extract_script_bytes.rs @@ -39,9 +39,12 @@ impl OneArgOp for ExtractScriptBytes { impl OneArgOpTryBuild for ExtractScriptBytes { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SBox)?; - Ok(ExtractScriptBytes { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/logical_not.rs b/ergotree-ir/src/mir/logical_not.rs index da77e303c..1ed44c042 100644 --- a/ergotree-ir/src/mir/logical_not.rs +++ b/ergotree-ir/src/mir/logical_not.rs @@ -38,9 +38,12 @@ impl OneArgOp for LogicalNot { impl OneArgOpTryBuild for LogicalNot { fn try_build(input: Expr) -> Result { input.check_post_eval_tpe(&SType::SBoolean)?; - Ok(Self { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/long_to_byte_array.rs b/ergotree-ir/src/mir/long_to_byte_array.rs index 609a7b491..3f5745768 100644 --- a/ergotree-ir/src/mir/long_to_byte_array.rs +++ b/ergotree-ir/src/mir/long_to_byte_array.rs @@ -43,9 +43,12 @@ impl OneArgOpTryBuild for LongToByteArray { Self: Sized, { input.check_post_eval_tpe(&SType::SLong)?; - Ok(LongToByteArray { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/negation.rs b/ergotree-ir/src/mir/negation.rs index 683288add..a72a2745c 100644 --- a/ergotree-ir/src/mir/negation.rs +++ b/ergotree-ir/src/mir/negation.rs @@ -44,9 +44,12 @@ impl OneArgOpTryBuild for Negation { post_eval_tpe ))); } - Ok(Self { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + Self { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/option_get.rs b/ergotree-ir/src/mir/option_get.rs index 8920f344d..f7093f551 100644 --- a/ergotree-ir/src/mir/option_get.rs +++ b/ergotree-ir/src/mir/option_get.rs @@ -5,9 +5,9 @@ use alloc::sync::Arc; use super::expr::Expr; use super::expr::InvalidArgumentError; use super::unary_op::OneArgOp; -use super::unary_op::OneArgOpTryBuild; use crate::has_opcode::HasStaticOpCode; use crate::serialization::op_code::OpCode; +use crate::serialization::SigmaSerializable; use crate::types::stype::SType; /// Returns the Option's value or error if no value @@ -20,6 +20,19 @@ pub struct OptionGet { } impl OptionGet { + /// Build a new `OptionGet` node + pub fn new(input: Expr) -> Result { + match input.post_eval_tpe() { + SType::SOption(elem_tpe) => Ok(OptionGet { + input: Box::new(input), + elem_tpe, + }), + _ => Err(InvalidArgumentError(format!( + "expected OptionGet::input type to be SOption, got: {0:?}", + input.tpe(), + ))), + } + } /// Type pub fn tpe(&self) -> SType { (*self.elem_tpe).clone() @@ -39,18 +52,19 @@ impl OneArgOp for OptionGet { } } -impl OneArgOpTryBuild for OptionGet { - fn try_build(input: Expr) -> Result { - match input.post_eval_tpe() { - SType::SOption(elem_tpe) => Ok(OptionGet { - input: Box::new(input), - elem_tpe, - }), - _ => Err(InvalidArgumentError(format!( - "expected OptionGet::input type to be SOption, got: {0:?}", - input.tpe(), - ))), - } +impl SigmaSerializable for OptionGet { + fn sigma_serialize( + &self, + w: &mut W, + ) -> crate::serialization::SigmaSerializeResult { + self.input.sigma_serialize(w) + } + + fn sigma_parse( + r: &mut R, + ) -> Result { + let input = Expr::sigma_parse(r)?; + Ok(OptionGet::new(input)?) } } @@ -74,7 +88,7 @@ mod tests { ) .unwrap() .into(); - let e: Expr = OptionGet::try_build(get_reg_expr).unwrap().into(); + let e: Expr = OptionGet::new(get_reg_expr).unwrap().into(); assert_eq![sigma_serialize_roundtrip(&e), e]; } } diff --git a/ergotree-ir/src/mir/option_is_defined.rs b/ergotree-ir/src/mir/option_is_defined.rs index c52fd4ec6..990c2683d 100644 --- a/ergotree-ir/src/mir/option_is_defined.rs +++ b/ergotree-ir/src/mir/option_is_defined.rs @@ -50,6 +50,11 @@ impl OneArgOpTryBuild for OptionIsDefined { ))), } } + fn build_unchecked(input: Expr) -> Self { + Self { + input: input.into(), + } + } } #[cfg(test)] diff --git a/ergotree-ir/src/mir/sigma_and.rs b/ergotree-ir/src/mir/sigma_and.rs index cf66e09cc..9848b54d8 100644 --- a/ergotree-ir/src/mir/sigma_and.rs +++ b/ergotree-ir/src/mir/sigma_and.rs @@ -1,7 +1,5 @@ //! AND conjunction for sigma propositions -use core::convert::TryInto; - use alloc::vec::Vec; use crate::serialization::op_code::OpCode; @@ -10,7 +8,6 @@ use crate::serialization::sigma_byte_writer::SigmaByteWrite; use crate::serialization::SigmaParsingError; use crate::serialization::SigmaSerializable; use crate::serialization::SigmaSerializeResult; -use crate::sigma_protocol::sigma_boolean::SigmaConjectureItems; use crate::traversable::impl_traversable_expr; use crate::types::stype::SType; @@ -22,7 +19,7 @@ use crate::has_opcode::HasStaticOpCode; #[derive(PartialEq, Eq, Debug, Clone)] pub struct SigmaAnd { /// Collection of SSigmaProp - pub items: SigmaConjectureItems, + pub items: Vec, } impl SigmaAnd { @@ -37,9 +34,7 @@ impl SigmaAnd { .iter() .all(|tpe| matches!(tpe, SType::SSigmaProp)) { - Ok(Self { - items: items.try_into()?, - }) + Ok(Self { items }) } else { Err(InvalidArgumentError(format!( "Sigma conjecture: expected all items be of type SSigmaProp, got {:?},\n items: {:?}", @@ -64,7 +59,9 @@ impl SigmaSerializable for SigmaAnd { } fn sigma_parse(r: &mut R) -> Result { - Ok(Self::new(Vec::::sigma_parse(r)?)?) + Ok(Self { + items: Vec::::sigma_parse(r)?, + }) } } @@ -89,9 +86,7 @@ mod arbitrary { items: constants .into_iter() .map(|c| c.into()) - .collect::>() - .try_into() - .unwrap(), + .collect::>(), }) .boxed() } diff --git a/ergotree-ir/src/mir/sigma_or.rs b/ergotree-ir/src/mir/sigma_or.rs index cc49176be..cf8684ef6 100644 --- a/ergotree-ir/src/mir/sigma_or.rs +++ b/ergotree-ir/src/mir/sigma_or.rs @@ -1,7 +1,5 @@ //! OR conjunction for sigma propositions -use core::convert::TryInto; - use alloc::vec::Vec; use crate::serialization::op_code::OpCode; @@ -10,7 +8,6 @@ use crate::serialization::sigma_byte_writer::SigmaByteWrite; use crate::serialization::SigmaParsingError; use crate::serialization::SigmaSerializable; use crate::serialization::SigmaSerializeResult; -use crate::sigma_protocol::sigma_boolean::SigmaConjectureItems; use crate::traversable::impl_traversable_expr; use crate::types::stype::SType; @@ -22,7 +19,7 @@ use crate::has_opcode::HasStaticOpCode; #[derive(PartialEq, Eq, Debug, Clone)] pub struct SigmaOr { /// Collection of SSigmaProp - pub items: SigmaConjectureItems, + pub items: Vec, } impl SigmaOr { @@ -37,9 +34,7 @@ impl SigmaOr { .iter() .all(|tpe| matches!(tpe, SType::SSigmaProp)) { - Ok(Self { - items: items.try_into()?, - }) + Ok(Self { items }) } else { Err(InvalidArgumentError(format!( "Sigma conjecture: expected all items be of type SSigmaProp, got {:?},\n items: {:?}", @@ -64,7 +59,9 @@ impl SigmaSerializable for SigmaOr { } fn sigma_parse(r: &mut R) -> Result { - Ok(Self::new(Vec::::sigma_parse(r)?)?) + Ok(Self { + items: Vec::::sigma_parse(r)?, + }) } } @@ -89,9 +86,7 @@ mod arbitrary { items: constants .into_iter() .map(|c| c.into()) - .collect::>() - .try_into() - .unwrap(), + .collect::>(), }) .boxed() } diff --git a/ergotree-ir/src/mir/sigma_prop_bytes.rs b/ergotree-ir/src/mir/sigma_prop_bytes.rs index 5d7e26d81..65debd2a5 100644 --- a/ergotree-ir/src/mir/sigma_prop_bytes.rs +++ b/ergotree-ir/src/mir/sigma_prop_bytes.rs @@ -38,10 +38,12 @@ impl OneArgOp for SigmaPropBytes { impl OneArgOpTryBuild for SigmaPropBytes { fn try_build(input: Expr) -> Result { - input.check_post_eval_tpe(&SType::SSigmaProp)?; - Ok(SigmaPropBytes { + Ok(Self::build_unchecked(input)) + } + fn build_unchecked(input: Expr) -> Self { + SigmaPropBytes { input: input.into(), - }) + } } } diff --git a/ergotree-ir/src/mir/subst_const.rs b/ergotree-ir/src/mir/subst_const.rs index 46537a689..69f796468 100644 --- a/ergotree-ir/src/mir/subst_const.rs +++ b/ergotree-ir/src/mir/subst_const.rs @@ -70,7 +70,11 @@ impl SigmaSerializable for SubstConstants { let script_bytes = Expr::sigma_parse(r)?; let positions = Expr::sigma_parse(r)?; let new_values = Expr::sigma_parse(r)?; - Ok(SubstConstants::new(script_bytes, positions, new_values)?) + Ok(SubstConstants { + script_bytes: script_bytes.into(), + positions: positions.into(), + new_values: new_values.into(), + }) } } diff --git a/ergotree-ir/src/mir/unary_op.rs b/ergotree-ir/src/mir/unary_op.rs index 11bd51e8f..70aa3e88c 100644 --- a/ergotree-ir/src/mir/unary_op.rs +++ b/ergotree-ir/src/mir/unary_op.rs @@ -21,6 +21,8 @@ pub trait OneArgOp { pub trait OneArgOpTryBuild: Sized { /// Create new IR node, returns an error if any of the requirements failed fn try_build(input: Expr) -> Result; + /// Create new IR node, without checking that the type of the input meets the requirements + fn build_unchecked(input: Expr) -> Self; } impl SigmaSerializable for T { @@ -30,7 +32,7 @@ impl SigmaSerializable for T { fn sigma_parse(r: &mut R) -> Result { let input = Expr::sigma_parse(r)?; - let r = T::try_build(input)?; + let r = T::build_unchecked(input); Ok(r) } } diff --git a/ergotree-ir/src/mir/xor.rs b/ergotree-ir/src/mir/xor.rs index 6560f64aa..3368c2651 100644 --- a/ergotree-ir/src/mir/xor.rs +++ b/ergotree-ir/src/mir/xor.rs @@ -65,7 +65,10 @@ impl SigmaSerializable for Xor { fn sigma_parse(r: &mut R) -> Result { let left = Expr::sigma_parse(r)?; let right = Expr::sigma_parse(r)?; - Ok(Xor::new(left, right)?) + Ok(Xor { + left: left.into(), + right: right.into(), + }) } } diff --git a/ergotree-ir/src/pretty_printer.rs b/ergotree-ir/src/pretty_printer.rs index eee5beb1d..dc74162de 100644 --- a/ergotree-ir/src/pretty_printer.rs +++ b/ergotree-ir/src/pretty_printer.rs @@ -216,14 +216,14 @@ mod tests { val v2 = INPUTS(0) val v3 = INPUTS.filter({ (v3: Box) => - if (v3.getReg(6).isDefined()) v3.creationInfo._1 >= v1 && v3.tokens(0)._1 == "2a472d4a614e645267556b58703273357638792f423f4528482b4d6250655368" && v3.getReg(5).get == v2.getReg(5).get else false + if (v3.getReg(R6).isDefined()) v3.creationInfo._1 >= v1 && v3.tokens(0)._1 == "2a472d4a614e645267556b58703273357638792f423f4528482b4d6250655368" && v3.getReg(R5).get == v2.getReg(R5).get else false } ) val v4 = v3.size val v5 = v3.fold((, 1, (, true, 0)))({ (v5: ((Long, (Boolean, Long)), Box)) => { - val v7 = v5._2.getReg(6).get + val v7 = v5._2.getReg(R6).get val v8 = v5._1 val v9 = v8._2 (, v7, (, v9._1 && v8._1 <= v7, v9._2 + v7)) @@ -257,14 +257,14 @@ mod tests { allOf( allOf( allOf( - proveDlog(v3(getVar(0).get).getReg(4).get), + proveDlog(v3(getVar(0).get).getReg(R4).get), sigmaProp(v2.creationInfo._1 < v1), ), sigmaProp(v4 >= 4), ), sigmaProp(v6._1), ), - sigmaProp(v7 - v3(0).getReg(6).get <= v7 * upcast(5) / 100), + sigmaProp(v7 - v3(0).getReg(R6).get <= v7 * upcast(5) / 100), ), sigmaProp(v9._1 == "472b4b6250655368566d597133743677397a24432646294a404d635166546a57"), ), @@ -276,9 +276,9 @@ mod tests { ), sigmaProp(v11.size == v8.size), ), - sigmaProp(v10.getReg(4).get == v6._2 / upcast(v4)), + sigmaProp(v10.getReg(R4).get == v6._2 / upcast(v4)), ), - sigmaProp(v10.getReg(5).get == v2.getReg(5).get + 1), + sigmaProp(v10.getReg(R5).get == v2.getReg(R5).get + 1), ), sigmaProp(v10.propBytes == v2.propBytes), ), @@ -315,13 +315,13 @@ mod tests { val v8 = v7._1 val v9 = v7._2 val v10 = v2(1) - sigmaProp(v3._1 == "472b4b6250655368566d597133743677397a24432646294a404d635166546a57" && v3 == v5(0) && v1.value == v4.value && v1.getReg(4) == v4.getReg(4) && v1.getReg(5) == v4.getReg(5) && !v4.getReg(6).isDefined() && v6.tokens == SELF.tokens && v6.propBytes == SELF.propBytes && v6.value >= SELF.value && v6.creationInfo._1 > SELF.creationInfo._1 && !v6.getReg(4).isDefined() && INPUTS.filter({ + sigmaProp(v3._1 == "472b4b6250655368566d597133743677397a24432646294a404d635166546a57" && v3 == v5(0) && v1.value == v4.value && v1.getReg(R4) == v4.getReg(R4) && v1.getReg(R5) == v4.getReg(R5) && !v4.getReg(R6).isDefined() && v6.tokens == SELF.tokens && v6.propBytes == SELF.propBytes && v6.value >= SELF.value && v6.creationInfo._1 > SELF.creationInfo._1 && !v6.getReg(R4).isDefined() && INPUTS.filter({ (v11: Box) => { val v13 = v11.tokens if (v13.size > 0) { - val v14 = v11.getReg(7) - v13(0)._1 == "3f4428472d4b6150645367566b5970337336763979244226452948404d625165" && v11.getReg(5).get == SELF.creationInfo._1 && v11.getReg(6).get == blake2b256(v4.propBytes) && if (v14.isDefined() && v11.getReg(8).isDefined()) v14.get == v8 && v11.getReg(8).get == v9 else v10._1 == v8 && v10._2 == v9 + val v14 = v11.getReg(R7) + v13(0)._1 == "3f4428472d4b6150645367566b5970337336763979244226452948404d625165" && v11.getReg(R5).get == SELF.creationInfo._1 && v11.getReg(R6).get == blake2b256(v4.propBytes) && if (v14.isDefined() && v11.getReg(R8).isDefined()) v14.get == v8 && v11.getReg(R8).get == v9 else v10._1 == v8 && v10._2 == v9 } else false } @@ -345,12 +345,12 @@ mod tests { ergo_tree.proposition().unwrap(), expect![[r#" { - val v1 = SELF.getReg(4).get + val v1 = SELF.getReg(R4).get val v2 = OUTPUTS(getVar(0).get) - val v3 = v2.getReg(4) + val v3 = v2.getReg(R4) anyOf( proveDlog(v1), - sigmaProp(v3.isDefined() && v2.propBytes == SELF.propBytes && v2.tokens == SELF.tokens && v2.value >= 10000000 && INPUTS.size > 1 && INPUTS(1).tokens.size > 0 && INPUTS(1).tokens(0)._1 == "6251655468576d5a7134743777217a25432a462d4a404e635266556a586e3272" && v3.get == v1 && v2.value >= SELF.value && !v2.getReg(5).isDefined()), + sigmaProp(v3.isDefined() && v2.propBytes == SELF.propBytes && v2.tokens == SELF.tokens && v2.value >= 10000000 && INPUTS.size > 1 && INPUTS(1).tokens.size > 0 && INPUTS(1).tokens(0)._1 == "6251655468576d5a7134743777217a25432a462d4a404e635266556a586e3272" && v3.get == v1 && v2.value >= SELF.value && !v2.getReg(R5).isDefined()), ) } "#]], @@ -368,12 +368,12 @@ mod tests { val v1 = OUTPUTS(getVar(0).get) val v2 = v1.tokens val v3 = SELF.tokens - val v4 = SELF.getReg(4).get + val v4 = SELF.getReg(R4).get allOf( - sigmaProp(v2(0) == v3(0) && v1.propBytes == SELF.propBytes && v1.getReg(4).isDefined() && v1.value >= 10000000), + sigmaProp(v2(0) == v3(0) && v1.propBytes == SELF.propBytes && v1.getReg(R4).isDefined() && v1.value >= 10000000), anyOf( proveDlog(v4), - sigmaProp(INPUTS(0).tokens(0)._1 == "472b4b6250655368566d597133743677397a24432646294a404d635166546a57" && v2(1)._1 == v3(1)._1 && v2(1)._2 > v3(1)._2 && v1.getReg(4).get == v4 && v1.value >= SELF.value && !v1.getReg(5).isDefined()), + sigmaProp(INPUTS(0).tokens(0)._1 == "472b4b6250655368566d597133743677397a24432646294a404d635166546a57" && v2(1)._1 == v3(1)._1 && v2(1)._2 > v3(1)._2 && v1.getReg(R4).get == v4 && v1.value >= SELF.value && !v1.getReg(R5).isDefined()), ), ) } @@ -410,17 +410,17 @@ mod tests { val v15 = v9(0) val v16 = v15._2 val v17 = v14 != v16 - val v18 = SELF.getReg(5).get - val v19 = v4.getReg(5).get - val v20 = SELF.getReg(4).get - val v21 = v4.getReg(4).get + val v18 = SELF.getReg(R5).get + val v19 = v4.getReg(R5).get + val v20 = SELF.getReg(R4).get + val v21 = v4.getReg(R4).get val v22 = OUTPUTS(1) - val v23 = v22.getReg(4).get + val v23 = v22.getReg(R4).get val v24 = if (v12) 0 else v23 val v25 = if (v12) v23 else 0 val v26 = SELF.value - val v27 = v22.getReg(5).get - val v28 = v2.getReg(4).get / 100 + val v27 = v22.getReg(R5).get + val v28 = v2.getReg(R4).get / 100 val v29 = v26 min v20 * v28 max 0 val v30 = if (v17) v28 min if (v20 == 0) 9223372036854775807 else v29 / v20 * v24 else { val v30 = v26 - v29 @@ -455,11 +455,11 @@ mod tests { val v3 = OUTPUTS(1) val v4 = SELF.id val v5 = OUTPUTS(0) - sigmaProp(v2.size == 3 && v2(2)._1 == "7d672d1def471720ca5782fd6473e47e796d9ac0c138d9911346f118b2f6d9d9" && v2 == v3.tokens && v1.value == v3.value && v1.getReg(4).get == v3.getReg(4).get && v1.getReg(5).get == v3.getReg(5).get && v4 == INPUTS(0).id && SELF.tokens == v5.tokens && SELF.propBytes == v5.propBytes && v5.value >= SELF.value && INPUTS.filter({ + sigmaProp(v2.size == 3 && v2(2)._1 == "7d672d1def471720ca5782fd6473e47e796d9ac0c138d9911346f118b2f6d9d9" && v2 == v3.tokens && v1.value == v3.value && v1.getReg(R4).get == v3.getReg(R4).get && v1.getReg(R5).get == v3.getReg(R5).get && v4 == INPUTS(0).id && SELF.tokens == v5.tokens && SELF.propBytes == v5.propBytes && v5.value >= SELF.value && INPUTS.filter({ (v6: Box) => { val v8 = v6.tokens - v8.size > 0 && v8(0)._1 == "f7995f212216fcf21854f56df7a9a0a9fc9b7ae4c0f1cc40f5b406371286a5e0" && v6.getReg(6).get == v4 && v6.getReg(7).get == blake2b256(v3.propBytes) + v8.size > 0 && v8(0)._1 == "f7995f212216fcf21854f56df7a9a0a9fc9b7ae4c0f1cc40f5b406371286a5e0" && v6.getReg(R6).get == v4 && v6.getReg(R7).get == blake2b256(v3.propBytes) } } @@ -485,12 +485,12 @@ mod tests { expect![[r#" { val v1 = OUTPUTS(INPUTS.indexOf(SELF0)) - val v2 = SELF.getReg(4).get + val v2 = SELF.getReg(R4).get allOf( - sigmaProp(v1.getReg(4).get == v2 && v1.propBytes == SELF.propBytes && v1.tokens == SELF.tokens && v1.value >= SELF.value), + sigmaProp(v1.getReg(R4).get == v2 && v1.propBytes == SELF.propBytes && v1.tokens == SELF.tokens && v1.value >= SELF.value), anyOf( proveDlog(v2), - sigmaProp(INPUTS(0).tokens(0)._1 == "239c170b7e82f94e6b05416f14b8a2a57e0bfff0e3c93f4abbcd160b6a5b271a" && !v1.getReg(7).isDefined()), + sigmaProp(INPUTS(0).tokens(0)._1 == "239c170b7e82f94e6b05416f14b8a2a57e0bfff0e3c93f4abbcd160b6a5b271a" && !v1.getReg(R7).isDefined()), ), ) } @@ -582,17 +582,17 @@ mod tests { val v1 = OUTPUTS(0) val v2 = SELF.tokens(0) val v3 = v2._1 - val v4 = SELF.getReg(4).get - val v5 = SELF.getReg(5).get - val v6 = SELF.getReg(6).get + val v4 = SELF.getReg(R4).get + val v5 = SELF.getReg(R5).get + val v6 = SELF.getReg(R6).get val v7 = OUTPUTS(1) val v8 = v7.tokens - val v9 = v7.getReg(6).get + val v9 = v7.getReg(R6).get val v10 = v5._2 val v11 = v5._1 - val v12 = v7.getReg(5).get - val v13 = v7.getReg(7).get - sigmaProp(v1.propBytes == SELF.propBytes && v1.value >= SELF.value && v1.tokens(0) == (, v3, v2._2 - 1) && v1.getReg(4).get == v4 && v1.getReg(5).get == v5 && v1.getReg(6).get == v6 && v7.getReg(4).get == v4 && v7.getReg(8).get == v6 && v8(1)._1 == v3 && v8(0) == (, SELF.id, 1000000000000000000) && v9._1 * v10 == v9._2 * v11 * v12 && v13._1 * v10 == v13._2 * v11 * v12 + 1) + val v12 = v7.getReg(R5).get + val v13 = v7.getReg(R7).get + sigmaProp(v1.propBytes == SELF.propBytes && v1.value >= SELF.value && v1.tokens(0) == (, v3, v2._2 - 1) && v1.getReg(R4).get == v4 && v1.getReg(R5).get == v5 && v1.getReg(R6).get == v6 && v7.getReg(R4).get == v4 && v7.getReg(R8).get == v6 && v8(1)._1 == v3 && v8(0) == (, SELF.id, 1000000000000000000) && v9._1 * v10 == v9._2 * v11 * v12 && v13._1 * v10 == v13._2 * v11 * v12 + 1) } "#]], ) @@ -612,16 +612,16 @@ mod tests { val v1 = OUTPUTS(0) val v2 = v1.tokens val v3 = SELF.tokens - val v4 = v1.getReg(5).get - val v5 = v1.getReg(6).get + val v4 = v1.getReg(R5).get + val v5 = v1.getReg(R6).get val v6 = v2(3) val v7 = v6._2 val v8 = upcast(v7) - val v9 = v1.getReg(7).get + val v9 = v1.getReg(R7).get val v10 = upcast(v9) val v11 = v2(2) val v12 = v3(0) - sigmaProp(true && v1.value >= SELF.value && v2(0) == (, SELF.id, 1) && v2(1) == (, v3(1)._1, 1) && v1.getReg(4).get == SELF.getReg(4).get && v4 == SELF.getReg(6).get && v5 == SELF.getReg(7).get && (, v6._1, v2(4)._1) == SELF.getReg(8).get && v8 * v8 == v10 * v10 && if (v11._1 == v12._1) v11._2 else 0 >= v12._2 - v9 && v7 * upcast(v4._2) >= v7 * upcast(v4._1) && v7 * upcast(v5._2) < v7 * upcast(v5._1)) + sigmaProp(true && v1.value >= SELF.value && v2(0) == (, SELF.id, 1) && v2(1) == (, v3(1)._1, 1) && v1.getReg(R4).get == SELF.getReg(R4).get && v4 == SELF.getReg(R6).get && v5 == SELF.getReg(R7).get && (, v6._1, v2(4)._1) == SELF.getReg(R8).get && v8 * v8 == v10 * v10 && if (v11._1 == v12._1) v11._2 else 0 >= v12._2 - v9 && v7 * upcast(v4._2) >= v7 * upcast(v4._1) && v7 * upcast(v5._2) < v7 * upcast(v5._1)) } "#]], ) @@ -647,9 +647,9 @@ mod tests { val v7 = v3(3) val v8 = v2(4) val v9 = v3(4) - val v10 = SELF.getReg(4).get - val v11 = SELF.getReg(5).get - val v12 = SELF.getReg(6).get + val v10 = SELF.getReg(R4).get + val v11 = SELF.getReg(R5).get + val v12 = SELF.getReg(R6).get val v13 = 1000000000000000000 - v5._2 val v14 = 1000000000000000000 - v4._2 - v13 val v15 = v6._2 @@ -669,7 +669,7 @@ mod tests { val v29 = upcast(v26) val v30 = upcast(v13) val v31 = upcast(v14) / v30 - sigmaProp(v1.propBytes == SELF.propBytes && v1.value >= SELF.value && v2(0) == v3(0) && v2(1) == v3(1) && v4._1 == v5._1 && v6._1 == v7._1 && v8._1 == v9._1 && v1.getReg(4).get == v10 && v1.getReg(5).get == v11 && v1.getReg(6).get == v12 && if (v14 == 0) if (v22) v24 * v25 * upcast(v10) >= upcast(-v26) * v27 * upcast(v28) + upcast(v21 * upcast(v10)) else v27 * v29 * upcast(v10) >= upcast(-v21) * v24 * upcast(v28) + upcast(v26 * upcast(v10)) && v19 else if (v22 && v26 > 0) upcast(-v14) <= v25 * v30 / v27 min v29 * v30 / v24 && v19 else v25 >= v31 * v27 && v29 >= v31 * v24) + sigmaProp(v1.propBytes == SELF.propBytes && v1.value >= SELF.value && v2(0) == v3(0) && v2(1) == v3(1) && v4._1 == v5._1 && v6._1 == v7._1 && v8._1 == v9._1 && v1.getReg(R4).get == v10 && v1.getReg(R5).get == v11 && v1.getReg(R6).get == v12 && if (v14 == 0) if (v22) v24 * v25 * upcast(v10) >= upcast(-v26) * v27 * upcast(v28) + upcast(v21 * upcast(v10)) else v27 * v29 * upcast(v10) >= upcast(-v21) * v24 * upcast(v28) + upcast(v26 * upcast(v10)) && v19 else if (v22 && v26 > 0) upcast(-v14) <= v25 * v30 / v27 min v29 * v30 / v24 && v19 else v25 >= v31 * v27 && v29 >= v31 * v24) } "#]], ) @@ -688,7 +688,7 @@ mod tests { { val v1 = OUTPUTS(0) val v2 = CONTEXT.preHeader.timestamp - val v3 = SELF.getReg(7).get + val v3 = SELF.getReg(R7).get val v4 = SELF.tokens val v5 = v4.size val v6 = v5 == 1 @@ -698,14 +698,14 @@ mod tests { } val v8 = v7(SELF) - val v9 = SELF.getReg(6).get - val v10 = SELF.getReg(8).get + val v9 = SELF.getReg(R6).get + val v10 = SELF.getReg(R8).get val v11 = Coll[Coll[Byte]]() val v12 = OUTPUTS(1) - val v13 = SELF.getReg(5).get - val v14 = SELF.getReg(4).get + val v13 = SELF.getReg(R5).get + val v14 = SELF.getReg(R4).get val v15 = CONTEXT.dataInputs(0) - val v16 = v15.getReg(8).get + val v16 = v15.getReg(R8).get sigmaProp(v1.value >= SELF.value && v2 < v3 && v1.tokens(0) == v4(0) && { val v17 = v7(v1) v17 >= v8 + v9 || v10 != -1 && v17 >= v10 @@ -718,14 +718,14 @@ mod tests { (v17: Box) => if (v6) v11 else v17.tokens(1)._1 } - (v1) && v12.propBytes == v13 && v7(v12) >= v8 && v1.getReg(4).get == v14 && v1.getReg(5).get.size > 0 && v1.getReg(6).get == v9 && v1.getReg(7).get == if (v3 - v2 <= v16(0)) v3 + v16(1) else v3 && v1.getReg(8).get == v10 && v1.getReg(9) == SELF.getReg(9) || if (OUTPUTS.size == 5) { + (v1) && v12.propBytes == v13 && v7(v12) >= v8 && v1.getReg(R4).get == v14 && v1.getReg(R5).get.size > 0 && v1.getReg(R6).get == v9 && v1.getReg(R7).get == if (v3 - v2 <= v16(0)) v3 + v16(1) else v3 && v1.getReg(R8).get == v10 && v1.getReg(R9) == SELF.getReg(R9) || if (OUTPUTS.size == 5) { val v17 = OUTPUTS(2) - val v18 = v8 / upcast(v15.getReg(4).get) + val v18 = v8 / upcast(v15.getReg(R4).get) val v19 = v4(0) - val v20 = v8 / upcast(v15.getReg(6).get) + val v20 = v8 / upcast(v15.getReg(R6).get) val v21 = OUTPUTS(3) - val v22 = v21.getReg(4).get - v2 >= v3 || v8 >= v10 && v10 != -1 && v7(v17) >= v18 && v17.propBytes == v15.getReg(5).get && v1.tokens(0) == v19 && v1.propBytes == v13 && v7(v12) >= v8 - v18 - v20 - if (v6) v15.getReg(7).get * 2 else 0 && v12.propBytes == v14 && blake2b256(v22.bytes) == v19._1 && v7(v21) >= v20 && v21.propBytes == v22.propBytes + val v22 = v21.getReg(R4).get + v2 >= v3 || v8 >= v10 && v10 != -1 && v7(v17) >= v18 && v17.propBytes == v15.getReg(R5).get && v1.tokens(0) == v19 && v1.propBytes == v13 && v7(v12) >= v8 - v18 - v20 - if (v6) v15.getReg(R7).get * 2 else 0 && v12.propBytes == v14 && blake2b256(v22.bytes) == v19._1 && v7(v21) >= v20 && v21.propBytes == v22.propBytes } else false) } diff --git a/ergotree-ir/src/pretty_printer/print.rs b/ergotree-ir/src/pretty_printer/print.rs index 3aeba4e57..c5cb66b12 100644 --- a/ergotree-ir/src/pretty_printer/print.rs +++ b/ergotree-ir/src/pretty_printer/print.rs @@ -64,7 +64,6 @@ use crate::mir::sigma_prop_bytes::SigmaPropBytes; use crate::mir::subst_const::SubstConstants; use crate::mir::tree_lookup::TreeLookup; use crate::mir::tuple::Tuple; -use crate::mir::unary_op::OneArgOpTryBuild; use crate::mir::upcast::Upcast; use crate::mir::val_def::ValDef; use crate::mir::val_use::ValUse; @@ -452,7 +451,7 @@ impl Print for ExtractRegisterAs { source_span: SourceSpan { offset, length }, expr: ExtractRegisterAs::new( input, - self.register_id, + u8::from(self.register_id) as i8, SType::SOption(self.elem_tpe.clone()), ) .unwrap(), @@ -539,7 +538,7 @@ impl Print for OptionGet { #[allow(clippy::unwrap_used)] // we only added spans Ok(Spanned { source_span: SourceSpan { offset, length }, - expr: OptionGet::try_build(input).unwrap(), + expr: OptionGet::new(input).unwrap(), } .into()) } @@ -571,15 +570,19 @@ impl Print for Tuple { impl Print for SigmaAnd { fn print(&self, w: &mut dyn Printer) -> Result { writeln!(w, "allOf(")?; - let items = self.items.try_mapped_ref(|i| -> Result { - w.inc_ident(); - w.print_indent()?; - let item = i.print(w)?; - write!(w, ", ")?; - writeln!(w)?; - w.dec_ident(); - Ok(item) - })?; + let items = self + .items + .iter() + .map(|i| -> Result { + w.inc_ident(); + w.print_indent()?; + let item = i.print(w)?; + write!(w, ", ")?; + writeln!(w)?; + w.dec_ident(); + Ok(item) + }) + .collect::, _>>()?; w.print_indent()?; write!(w, ")")?; Ok(SigmaAnd { items }.into()) @@ -589,15 +592,19 @@ impl Print for SigmaAnd { impl Print for SigmaOr { fn print(&self, w: &mut dyn Printer) -> Result { writeln!(w, "anyOf(")?; - let items = self.items.try_mapped_ref(|i| -> Result { - w.inc_ident(); - w.print_indent()?; - let item = i.print(w)?; - write!(w, ", ")?; - writeln!(w)?; - w.dec_ident(); - Ok(item) - })?; + let items = self + .items + .iter() + .map(|i| -> Result { + w.inc_ident(); + w.print_indent()?; + let item = i.print(w)?; + write!(w, ", ")?; + writeln!(w)?; + w.dec_ident(); + Ok(item) + }) + .collect::, _>>()?; w.print_indent()?; write!(w, ")")?; Ok(SigmaOr { items }.into()) diff --git a/ergotree-ir/src/serialization/data.rs b/ergotree-ir/src/serialization/data.rs index 563358cc1..0096e4fee 100644 --- a/ergotree-ir/src/serialization/data.rs +++ b/ergotree-ir/src/serialization/data.rs @@ -3,7 +3,6 @@ use ergo_chain_types::Header; use sigma_ser::ScorexSerializable; use alloc::string::String; -use alloc::string::ToString; use alloc::vec; use alloc::vec::Vec; use sigma_util::AsVecU8; @@ -17,12 +16,12 @@ use crate::mir::constant::TryExtractFromError; use crate::mir::constant::TryExtractInto; use crate::mir::value::CollKind; use crate::mir::value::NativeColl; -use crate::serialization::SigmaSerializationError; use crate::serialization::SigmaSerializeResult; use crate::serialization::{ sigma_byte_reader::SigmaByteRead, SigmaParsingError, SigmaSerializable, }; use crate::sigma_protocol::{sigma_boolean::SigmaBoolean, sigma_boolean::SigmaProp}; +use crate::soft_fork::SoftForkError; use crate::types::stuple; use crate::types::stype::SType; use crate::unsignedbigint256::UnsignedBigInt; @@ -59,9 +58,10 @@ impl DataSerializer { v.sigma_serialize(w)? } Literal::UnsignedBigInt(_) => { - return Err(SigmaSerializationError::NotSupported( - "Can't serialize UnsignedBigInt with tree version < 3".into(), - )) + return Err(SoftForkError::NotSerializable( + "Can't serialize UnsignedBigInt with tree version < 3", + ) + .into()) } Literal::AvlTree(a) => a.sigma_serialize(w)?, Literal::CBox(b) => b.sigma_serialize(w)?, @@ -101,14 +101,16 @@ impl DataSerializer { // unsupported, see // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659 Literal::Opt(_) => { - return Err(SigmaSerializationError::NotSupported( - "Option serialization is not supported".to_string(), - )); + return Err(SoftForkError::NotSerializable( + "Option serialization is not supported", + ) + .into()); } Literal::Header(_) => { - return Err(SigmaSerializationError::NotSupported( - "Header serialization is not supported".to_string(), - )); + return Err(SoftForkError::NotSerializable( + "Header serialization is not supported", + ) + .into()); } }) } @@ -187,14 +189,14 @@ impl DataSerializer { SHeader if r.tree_version() >= ErgoTreeVersion::V3 => { Literal::Header(Box::new(Header::scorex_parse(r)?)) } - STypeVar(_) => return Err(SigmaParsingError::NotSupported("TypeVar data")), - SAny => return Err(SigmaParsingError::NotSupported("SAny data")), - SOption(_) => return Err(SigmaParsingError::NotSupported("SOption data")), - SFunc(_) => return Err(SigmaParsingError::NotSupported("SFunc data")), - SContext => return Err(SigmaParsingError::NotSupported("SContext data")), - SHeader => return Err(SigmaParsingError::NotSupported("SHeader data")), - SPreHeader => return Err(SigmaParsingError::NotSupported("SPreHeader data")), - SGlobal => return Err(SigmaParsingError::NotSupported("SGlobal data")), + STypeVar(_) => return Err(SoftForkError::NotSerializable("TypeVar data").into()), + SAny => return Err(SoftForkError::NotSerializable("SAny data").into()), + SOption(_) => return Err(SoftForkError::NotSerializable("SOption data").into()), + SFunc(_) => return Err(SoftForkError::NotSerializable("SFunc data").into()), + SContext => return Err(SoftForkError::NotSerializable("SContext data").into()), + SHeader => return Err(SoftForkError::NotSerializable("SHeader data").into()), + SPreHeader => return Err(SoftForkError::NotSerializable("SPreHeader data").into()), + SGlobal => return Err(SoftForkError::NotSerializable("SGlobal data").into()), }) } } diff --git a/ergotree-ir/src/serialization/expr.rs b/ergotree-ir/src/serialization/expr.rs index 5206721c0..dbcb18664 100644 --- a/ergotree-ir/src/serialization/expr.rs +++ b/ergotree-ir/src/serialization/expr.rs @@ -78,6 +78,7 @@ use crate::serialization::{ }; use crate::mir::xor_of::XorOf; +use crate::soft_fork::SoftForkError; use crate::source_span::Spanned; impl Expr { @@ -192,11 +193,12 @@ impl Expr { XorOf::OP_CODE => Ok(XorOf::sigma_parse(r)?.into()), TreeLookup::OP_CODE => Ok(TreeLookup::sigma_parse(r)?.into()), CreateAvlTree::OP_CODE => Ok(CreateAvlTree::sigma_parse(r)?.into()), - o => Err(SigmaParsingError::NotImplementedOpCode(format!( + o => Err(SoftForkError::InvalidOpCode(format!( "{0}(shift {1})", o.value(), o.shift() - ))), + )) + .into()), } }; res diff --git a/ergotree-ir/src/serialization/method_call.rs b/ergotree-ir/src/serialization/method_call.rs index 653422d38..53ecdd72e 100644 --- a/ergotree-ir/src/serialization/method_call.rs +++ b/ergotree-ir/src/serialization/method_call.rs @@ -4,6 +4,7 @@ use alloc::vec::Vec; use crate::mir::expr::Expr; use crate::mir::method_call::MethodCall; +use crate::soft_fork::SoftForkError; use crate::types::smethod::MethodId; use crate::types::smethod::SMethod; use crate::types::stype::SType; @@ -36,12 +37,9 @@ impl SigmaSerializable for MethodCall { let obj = Expr::sigma_parse(r)?; let args = Vec::::sigma_parse(r)?; let arg_types = args.iter().map(|arg| arg.tpe()).collect(); - let method = SMethod::from_ids(type_id, method_id)?.specialize_for(obj.tpe(), arg_types)?; + let method = SMethod::from_ids(type_id, method_id)?; if r.tree_version() < method.method_raw.min_version { - return Err(SigmaParsingError::UnknownMethodId( - method_id, - type_id.value(), - )); + return Err(SoftForkError::UnknownMethodId(method_id, type_id.value()).into()); } let explicit_type_args = method .method_raw @@ -51,12 +49,14 @@ impl SigmaSerializable for MethodCall { .zip(core::iter::from_fn(|| Some(SType::sigma_parse(r)))) .map(|(tpe, res)| -> Result<(STypeVar, SType), SigmaParsingError> { Ok((tpe, res?)) }) .collect::, _>>()?; - Ok(MethodCall::with_type_args( - obj, - method, + Ok(MethodCall { + method: method + .with_concrete_types(&explicit_type_args) + .specialize_for(obj.tpe(), arg_types)?, + obj: obj.into(), args, explicit_type_args, - )?) + }) } } diff --git a/ergotree-ir/src/serialization/property_call.rs b/ergotree-ir/src/serialization/property_call.rs index 9f1f3429d..cac81a1c9 100644 --- a/ergotree-ir/src/serialization/property_call.rs +++ b/ergotree-ir/src/serialization/property_call.rs @@ -26,7 +26,10 @@ impl SigmaSerializable for PropertyCall { let obj = Expr::sigma_parse(r)?; let method = SMethod::from_ids(type_id, method_id)?.specialize_for(obj.tpe(), Vec::new())?; - Ok(PropertyCall::new(obj, method)?) + Ok(PropertyCall { + obj: obj.into(), + method, + }) } } diff --git a/ergotree-ir/src/serialization/serializable.rs b/ergotree-ir/src/serialization/serializable.rs index 3745aaf01..b1504ee2f 100644 --- a/ergotree-ir/src/serialization/serializable.rs +++ b/ergotree-ir/src/serialization/serializable.rs @@ -3,6 +3,7 @@ use crate::chain::ergo_box::RegisterValueError; use crate::ergo_tree::{ErgoTreeHeaderError, ErgoTreeVersion}; use crate::mir::val_def::ValId; use crate::mir::{constant::TryExtractFromError, expr::InvalidArgumentError}; +use crate::soft_fork::SoftForkError; use crate::types::type_unify::TypeUnificationError; use super::{ @@ -10,7 +11,6 @@ use super::{ sigma_byte_reader::{SigmaByteRead, SigmaByteReader}, sigma_byte_writer::{SigmaByteWrite, SigmaByteWriter}, }; -use crate::types::smethod::MethodId; use alloc::boxed::Box; use alloc::string::{String, ToString}; @@ -41,6 +41,9 @@ pub enum SigmaSerializationError { /// Scorex serialization error #[error("Scorex serialization error: {0}")] ScorexSerializationError(#[from] ScorexSerializationError), + /// Soft-fork error + #[error("{0}")] + SoftForkError(#[from] SoftForkError), } impl From for SigmaSerializationError { @@ -52,18 +55,6 @@ impl From for SigmaSerializationError { /// Ways parsing might fail #[derive(Error, Eq, PartialEq, Debug, Clone)] pub enum SigmaParsingError { - /// Invalid op code - #[error("invalid op code: {0}")] - InvalidOpCode(u8), - /// Lacking support for the op - #[error("not implemented op error: {0}")] - NotImplementedOpCode(String), - /// Failed to parse type - #[error("type parsing error, invalid type code: {0}({0:#04X})")] - InvalidTypeCode(u8), - /// V6 type error - #[error("Can't use v6 types (UnsignedBigInt, Header, Option) in ContextExtension/Registers ")] - V6TypeError, /// Failed to decode VLQ #[error("vlq encode error: {0}")] VlqEncode(#[from] vlq_encode::VlqEncodingError), @@ -91,9 +82,6 @@ pub enum SigmaParsingError { /// Invalid argument on node creation #[error("Invalid argument: {0:?}")] InvalidArgument(#[from] InvalidArgumentError), - /// Unknown method ID for given type code - #[error("No method id {0:?} found in type companion with type id {1:?} ")] - UnknownMethodId(MethodId, u8), /// Feature not supported #[error("parsing not supported: {0}")] NotSupported(&'static str), @@ -112,6 +100,9 @@ pub enum SigmaParsingError { /// Invalid register value #[error("Invalid register value: {0}")] InvalidRegisterValue(#[from] RegisterValueError), + /// Soft-forkable error + #[error("{0}")] + SoftForkError(#[from] SoftForkError), } impl From for SigmaParsingError { diff --git a/ergotree-ir/src/serialization/types.rs b/ergotree-ir/src/serialization/types.rs index 55f0283ff..1abb5abc7 100644 --- a/ergotree-ir/src/serialization/types.rs +++ b/ergotree-ir/src/serialization/types.rs @@ -6,6 +6,7 @@ use crate::serialization::SigmaSerializeResult; use crate::serialization::{ sigma_byte_reader::SigmaByteRead, SigmaParsingError, SigmaSerializable, }; +use crate::soft_fork::SoftForkError; use crate::types::stuple; use crate::types::stype::SType; use crate::types::stype_param::STypeParam; @@ -77,10 +78,10 @@ impl TypeCode { const TUPLE_PAIR_SYMMETRIC_TYPE_CONSTR_ID: u8 = 7; /// Parse type code from byte - fn parse(b: u8) -> Result { + fn parse(b: u8) -> Result { match FromPrimitive::from_u8(b) { Some(t) => Ok(t), - None => Err(SigmaParsingError::InvalidTypeCode(b)), + None => Err(SoftForkError::InvalidTypeCode(b)), } } @@ -106,10 +107,7 @@ impl TypeCode { } } - fn get_embeddable_type( - &self, - tree_version: ErgoTreeVersion, - ) -> Result { + fn get_embeddable_type(&self, tree_version: ErgoTreeVersion) -> Result { use SType::*; // TODO: UnsignedBigInt match self { @@ -122,7 +120,7 @@ impl TypeCode { TypeCode::SGROUP_ELEMENT => Ok(SGroupElement), TypeCode::SSIGMAPROP => Ok(SSigmaProp), TypeCode::SUNSIGNEDBIGINT if tree_version >= ErgoTreeVersion::V3 => Ok(SUnsignedBigInt), - _ => Err(SigmaParsingError::InvalidTypeCode(*self as u8)), + _ => Err(SoftForkError::InvalidPrimitiveType(*self as u8)), } } @@ -167,7 +165,7 @@ impl SigmaSerializable for TypeCode { fn sigma_parse(r: &mut R) -> Result { let b = r.get_u8()?; - Self::parse(b) + Ok(Self::parse(b)?) } } @@ -183,7 +181,10 @@ impl SType { let (container, embeddable) = TypeCode::unpack_tag(c)?; let mut stype = || { embeddable - .map(|e| e.get_embeddable_type(r.tree_version())) + .map(|e| { + e.get_embeddable_type(r.tree_version()) + .map_err(SigmaParsingError::from) + }) .unwrap_or_else(|| SType::sigma_parse(r)) }; Ok(match container { @@ -191,7 +192,7 @@ impl SType { if let Some(embeddable) = embeddable { embeddable.get_embeddable_type(r.tree_version())? } else { - return Err(SigmaParsingError::InvalidTypeCode(c)); + return Err(SoftForkError::InvalidPrimitiveType(c).into()); } } Some(TypeCode::COLL) => SColl(stype()?.into()), @@ -467,9 +468,9 @@ impl SigmaSerializable for SType { SType::STypeVar(tpe_param.ident.clone()).sigma_serialize(w) }) } - SType::SFunc(_) => Err(SigmaSerializationError::NotSupported( - "SFunc serialization is not supported".into(), - )), + SType::SFunc(_) => { + Err(SoftForkError::NotSerializable("SFunc serialization is not supported").into()) + } #[allow(clippy::unreachable)] // Primitive types are covered by if .is_prim() branch _ => unreachable!(), } diff --git a/ergotree-ir/src/sigma_protocol/sigma_boolean.rs b/ergotree-ir/src/sigma_protocol/sigma_boolean.rs index 218f6b0ec..3796e5cbc 100644 --- a/ergotree-ir/src/sigma_protocol/sigma_boolean.rs +++ b/ergotree-ir/src/sigma_protocol/sigma_boolean.rs @@ -387,14 +387,10 @@ mod arbitrary { .prop_recursive(1, 8, 4, |elem| { prop_oneof![ vec(elem.clone(), 2..=4) - .prop_map(|elems| Cand { - items: elems.try_into().unwrap() - }) + .prop_map(|items| Cand { items }) .prop_map_into(), vec(elem.clone(), 2..=4) - .prop_map(|elems| Cor { - items: elems.try_into().unwrap() - }) + .prop_map(|items| Cor { items }) .prop_map_into(), vec(elem, 2..=5) .prop_map(|elems| Cthreshold { diff --git a/ergotree-ir/src/sigma_protocol/sigma_boolean/cand.rs b/ergotree-ir/src/sigma_protocol/sigma_boolean/cand.rs index db561fb73..c10618378 100644 --- a/ergotree-ir/src/sigma_protocol/sigma_boolean/cand.rs +++ b/ergotree-ir/src/sigma_protocol/sigma_boolean/cand.rs @@ -2,13 +2,14 @@ use core::convert::TryInto; use alloc::vec::Vec; +use bounded_vec::NonEmptyVec; use super::SigmaBoolean; -use super::SigmaConjectureItems; use crate::has_opcode::HasStaticOpCode; use crate::serialization::op_code::OpCode; use crate::serialization::sigma_byte_reader::SigmaByteRead; use crate::serialization::sigma_byte_writer::SigmaByteWrite; +use crate::serialization::SigmaSerializationError; use crate::serialization::{SigmaParsingError, SigmaSerializable, SigmaSerializeResult}; use crate::sigma_protocol::sigma_boolean::SigmaConjecture; @@ -16,7 +17,7 @@ use crate::sigma_protocol::sigma_boolean::SigmaConjecture; #[derive(PartialEq, Eq, Debug, Clone)] pub struct Cand { /// Items of the conjunctions - pub items: SigmaConjectureItems, + pub items: Vec, } impl HasStaticOpCode for Cand { @@ -26,7 +27,7 @@ impl HasStaticOpCode for Cand { impl Cand { /// Connects the given sigma propositions into CAND proposition performing /// partial evaluation when some of them are trivial propositioins. - pub fn normalized(items: SigmaConjectureItems) -> SigmaBoolean { + pub fn normalized(items: NonEmptyVec) -> SigmaBoolean { let mut res: Vec = Vec::new(); for it in items { match it { @@ -41,11 +42,7 @@ impl Cand { #[allow(clippy::unwrap_used)] res.first().unwrap().clone() } else { - #[allow(clippy::unwrap_used)] - SigmaBoolean::SigmaConjecture(SigmaConjecture::Cand(Cand { - // should be 2 or more so unwrap is safe here - items: res.try_into().unwrap(), - })) + SigmaBoolean::SigmaConjecture(SigmaConjecture::Cand(Cand { items: res })) } } } @@ -65,7 +62,11 @@ impl core::fmt::Display for Cand { impl SigmaSerializable for Cand { fn sigma_serialize(&self, w: &mut W) -> SigmaSerializeResult { - w.put_u16(self.items.len() as u16)?; + w.put_u16( + self.items.len().try_into().map_err(|_| { + SigmaSerializationError::NotSupported("items.len() > 0xffff".into()) + })?, + )?; self.items.iter().try_for_each(|i| i.sigma_serialize(w)) } @@ -75,9 +76,7 @@ impl SigmaSerializable for Cand { for _ in 0..items_count { items.push(SigmaBoolean::sigma_parse(r)?); } - Ok(Cand { - items: items.try_into()?, - }) + Ok(Cand { items }) } } @@ -94,9 +93,7 @@ mod arbitrary { fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { vec(any::(), 2..=4) - .prop_map(|items| Cand { - items: items.try_into().unwrap(), - }) + .prop_map(|items| Cand { items }) .boxed() } } @@ -147,9 +144,8 @@ mod tests { fn pk_pk() { let pk1 = force_any_val::(); let pk2 = force_any_val::(); - let pks: SigmaConjectureItems = - vec![pk1.into(), pk2.into()].try_into().unwrap(); - let cand = Cand::normalized(pks.clone()); + let pks: Vec = vec![pk1.into(), pk2.into()]; + let cand = Cand::normalized(pks.clone().try_into().unwrap()); assert!(matches!( cand, SigmaBoolean::SigmaConjecture(SigmaConjecture::Cand(Cand {items})) if items == pks diff --git a/ergotree-ir/src/sigma_protocol/sigma_boolean/cor.rs b/ergotree-ir/src/sigma_protocol/sigma_boolean/cor.rs index 9bb0eec91..bf9baac82 100644 --- a/ergotree-ir/src/sigma_protocol/sigma_boolean/cor.rs +++ b/ergotree-ir/src/sigma_protocol/sigma_boolean/cor.rs @@ -2,13 +2,14 @@ use core::convert::TryInto; use alloc::vec::Vec; +use bounded_vec::NonEmptyVec; use super::SigmaBoolean; -use super::SigmaConjectureItems; use crate::has_opcode::HasStaticOpCode; use crate::serialization::op_code::OpCode; use crate::serialization::sigma_byte_reader::SigmaByteRead; use crate::serialization::sigma_byte_writer::SigmaByteWrite; +use crate::serialization::SigmaSerializationError; use crate::serialization::{SigmaParsingError, SigmaSerializable, SigmaSerializeResult}; use crate::sigma_protocol::sigma_boolean::SigmaConjecture; @@ -16,7 +17,7 @@ use crate::sigma_protocol::sigma_boolean::SigmaConjecture; #[derive(PartialEq, Eq, Debug, Clone)] pub struct Cor { /// Items of the conjunctions - pub items: SigmaConjectureItems, + pub items: Vec, } impl HasStaticOpCode for Cor { @@ -26,8 +27,7 @@ impl HasStaticOpCode for Cor { impl Cor { /// Connects the given sigma propositions into COR proposition performing /// partial evaluation when some of them are trivial propositioins. - pub fn normalized(items: SigmaConjectureItems) -> SigmaBoolean { - assert!(!items.is_empty()); + pub fn normalized(items: NonEmptyVec) -> SigmaBoolean { let mut res = Vec::new(); for it in items { match it { @@ -42,11 +42,7 @@ impl Cor { #[allow(clippy::unwrap_used)] res.first().unwrap().clone() } else { - #[allow(clippy::unwrap_used)] - SigmaBoolean::SigmaConjecture(SigmaConjecture::Cor(Cor { - // should be 2 or more so unwrap is safe here - items: res.try_into().unwrap(), - })) + SigmaBoolean::SigmaConjecture(SigmaConjecture::Cor(Cor { items: res })) } } } @@ -66,7 +62,11 @@ impl core::fmt::Display for Cor { impl SigmaSerializable for Cor { fn sigma_serialize(&self, w: &mut W) -> SigmaSerializeResult { - w.put_u16(self.items.len() as u16)?; + w.put_u16( + self.items.len().try_into().map_err(|_| { + SigmaSerializationError::NotSupported("items.len() > 0xffff".into()) + })?, + )?; self.items.iter().try_for_each(|i| i.sigma_serialize(w)) } @@ -76,9 +76,7 @@ impl SigmaSerializable for Cor { for _ in 0..items_count { items.push(SigmaBoolean::sigma_parse(r)?); } - Ok(Cor { - items: items.try_into()?, - }) + Ok(Cor { items }) } } @@ -95,9 +93,7 @@ mod arbitrary { fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { vec(any::(), 2..=4) - .prop_map(|items| Cor { - items: items.try_into().unwrap(), - }) + .prop_map(|items| Cor { items }) .boxed() } } @@ -149,9 +145,8 @@ mod tests { fn pk_pk() { let pk1 = force_any_val::(); let pk2 = force_any_val::(); - let pks: SigmaConjectureItems = - vec![pk1.into(), pk2.into()].try_into().unwrap(); - let cor = Cor::normalized(pks.clone()); + let pks: Vec = vec![pk1.into(), pk2.into()]; + let cor = Cor::normalized(pks.clone().try_into().unwrap()); assert!(matches!( cor, SigmaBoolean::SigmaConjecture(SigmaConjecture::Cor(Cor {items})) if items == pks diff --git a/ergotree-ir/src/sigma_protocol/sigma_boolean/cthreshold.rs b/ergotree-ir/src/sigma_protocol/sigma_boolean/cthreshold.rs index 868cb79cb..e1161cea7 100644 --- a/ergotree-ir/src/sigma_protocol/sigma_boolean/cthreshold.rs +++ b/ergotree-ir/src/sigma_protocol/sigma_boolean/cthreshold.rs @@ -30,6 +30,12 @@ pub struct Cthreshold { } impl Cthreshold { + fn new(k: u8, children: SigmaConjectureItems) -> Result { + if k as usize > children.len() { + return Err("k > children.len()"); + } + Ok(Self { k, children }) + } /// Reduce all possible TrivialProps in the tree pub fn reduce(k: u8, children: SigmaConjectureItems) -> SigmaBoolean { if k == 0 { @@ -73,9 +79,11 @@ impl Cthreshold { // should be 2 or more so unwrap is safe here #[allow(clippy::unwrap_used)] let sigmas: SigmaConjectureItems = res.try_into().unwrap(); + #[allow(clippy::unwrap_used)] + // 1 < sigmas.len() <= 255, so converting to NonEmptyVec never fails match curr_k as usize { - 1 => Cor::normalized(sigmas), - ch if ch == children_left => Cand::normalized(sigmas), + 1 => Cor::normalized(sigmas.to_vec().try_into().unwrap()), + ch if ch == children_left => Cand::normalized(sigmas.to_vec().try_into().unwrap()), _ => SigmaBoolean::SigmaConjecture(SigmaConjecture::Cthreshold(Cthreshold { k: curr_k, children: sigmas, @@ -119,9 +127,6 @@ impl SigmaSerializable for Cthreshold { for _ in 0..items_count { items.push(SigmaBoolean::sigma_parse(r)?); } - Ok(Cthreshold { - k, - children: items.try_into()?, - }) + Cthreshold::new(k, items.try_into()?).map_err(|e| SigmaParsingError::Misc(e.to_string())) } } diff --git a/ergotree-ir/src/soft_fork.rs b/ergotree-ir/src/soft_fork.rs new file mode 100644 index 000000000..7d9d37a62 --- /dev/null +++ b/ergotree-ir/src/soft_fork.rs @@ -0,0 +1,104 @@ +//! Soft-fork error type and related convenience functions +use thiserror::Error; + +use crate::{serialization::types::TypeCode, types::smethod::MethodId}; +use alloc::string::String; + +/// Represents soft-forkable conditions that can be tolerated when parsing. +#[derive(Error, Debug, PartialEq, Eq, Clone)] +pub enum SoftForkError { + /// Primitive type doesn't exist + #[error("Primitive type with code {0} doesn't exist")] + InvalidPrimitiveType(u8), + /// Invalid Type Code + #[error("type parsing error, invalid type code: {0}({0:#04X})")] + InvalidTypeCode(u8), + /// Type can not be serialized + #[error("Type is not serializable: {0}")] + NotSerializable(&'static str), + /// Type has no methods + #[error("Type with code {0:?} has no methods container")] + NoMethods(TypeCode), + /// Unknown method ID for given type code + #[error("No method id {0:?} found in type companion with type id {1:?} ")] + UnknownMethodId(MethodId, u8), + /// ErgoTree root error. ErgoTree root type should be SigmaProp + #[error("Expected ErgoTree root type to be SigmaProp")] + InvalidRootType, + /// Deserialized Script has invalid type + #[error("Deserialized script is of invalid type")] + DeserializedScriptError, + /// OpCode doesn't exist or can't be parsed + #[error("Invalid opcode")] + InvalidOpCode(String), + /// Registers/ContextExtension contained a v6.0 type (UnsignedBigInt, Header, Option), which is not allowed + #[error("Can't use v6 types (UnsignedBigInt, Header, Option) in ContextExtension/Registers ")] + V6TypeError, +} + +/// Convenience trait for checking if an error's source is a [`SoftForkError`] +pub trait IsSoftForkable { + /// Returns true if error's source is a [`SoftForkError`] + fn is_soft_fork(&self) -> bool; + /// Attempt to convert an error to a [`SoftForkError`] + fn to_soft_fork(&self) -> Option<&SoftForkError>; +} + +impl IsSoftForkable for E +where + E: core::error::Error + 'static, +{ + fn is_soft_fork(&self) -> bool { + self.to_soft_fork().is_some() + } + + fn to_soft_fork(&self) -> Option<&SoftForkError> { + let mut cur_err: Option<&dyn core::error::Error> = Some(self); + while let Some(err) = cur_err { + if err.is::() { + return err.downcast_ref(); + } + cur_err = err.source() + } + None + } +} + +/// Executes `f` and returns its output. If `f` raises an error caused by a [`SoftForkError`], then `when_soft_fork` will be executed, otherwise the error will be returned as-is +pub fn try_soft_forkable( + f: impl FnOnce() -> Result, + when_soft_fork: impl FnOnce() -> T, +) -> Result +where + E: core::error::Error + 'static, +{ + match f() { + Ok(t) => Ok(t), + Err(e) if e.is_soft_fork() => Ok(when_soft_fork()), + Err(e) => Err(e), + } +} + +#[cfg(test)] +mod tests { + + use crate::{serialization::SigmaParsingError, soft_fork::IsSoftForkable}; + + use super::SoftForkError; + + #[derive(thiserror::Error, Debug)] + #[error("")] + struct Error3(#[from] SigmaParsingError); + + #[test] + fn check_soft_fork_cause() { + let e0 = SoftForkError::V6TypeError; + assert!(e0.is_soft_fork()); + let e1 = SigmaParsingError::SoftForkError(SoftForkError::V6TypeError); + assert!(e1.is_soft_fork()); + let e2 = SigmaParsingError::Misc("".into()); + assert!(!e2.is_soft_fork()); + assert!(Error3(e1).is_soft_fork()); + assert!(!Error3(e2).is_soft_fork()); + } +} diff --git a/ergotree-ir/src/types/smethod.rs b/ergotree-ir/src/types/smethod.rs index a46dd267b..bab5c6481 100644 --- a/ergotree-ir/src/types/smethod.rs +++ b/ergotree-ir/src/types/smethod.rs @@ -6,6 +6,7 @@ use crate::serialization::sigma_byte_reader::SigmaByteRead; use crate::serialization::sigma_byte_writer::SigmaByteWrite; use crate::serialization::types::TypeCode; use crate::serialization::SigmaParsingError; +use crate::soft_fork::SoftForkError; use core::convert::TryFrom; use hashbrown::HashMap; @@ -15,7 +16,6 @@ use super::stype_companion::STypeCompanion; use super::stype_param::STypeVar; use super::type_unify::unify_many; use super::type_unify::TypeUnificationError; -use crate::serialization::SigmaParsingError::UnknownMethodId; /// Method id unique among the methods of the same object #[derive(PartialEq, Eq, Debug, Copy, Clone)] @@ -56,7 +56,7 @@ impl SMethod { let obj_type = STypeCompanion::try_from(type_id)?; match obj_type.method_by_id(&method_id) { Some(m) => Ok(m), - None => Err(UnknownMethodId(method_id, type_id.value())), + None => Err(SoftForkError::UnknownMethodId(method_id, type_id.value()).into()), } } diff --git a/ergotree-ir/src/types/stype.rs b/ergotree-ir/src/types/stype.rs index 1d00d0f0f..e99cc5557 100644 --- a/ergotree-ir/src/types/stype.rs +++ b/ergotree-ir/src/types/stype.rs @@ -11,11 +11,11 @@ use impl_trait_for_tuples::impl_for_tuples; use crate::bigint256::BigInt256; use crate::chain::ergo_box::ErgoBox; -use crate::serialization::SigmaParsingError; use crate::sigma_protocol::sigma_boolean::SigmaBoolean; use crate::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree; use crate::sigma_protocol::sigma_boolean::SigmaProp; use crate::sigma_protocol::sigma_boolean::{ProveDhTuple, ProveDlog}; +use crate::soft_fork::SoftForkError; use crate::unsignedbigint256::UnsignedBigInt; use ergo_chain_types::EcPoint; @@ -114,10 +114,10 @@ impl SType { ) } - pub(crate) fn check_v6_type(&self) -> Result<(), SigmaParsingError> { + pub(crate) fn check_v6_type(&self) -> Result<(), SoftForkError> { match self { SType::SUnsignedBigInt | SType::SOption(_) | SType::SHeader => { - Err(SigmaParsingError::V6TypeError) + Err(SoftForkError::V6TypeError) } SType::SColl(elem_tpe) => elem_tpe.check_v6_type(), SType::STuple(tuple) => tuple.items.iter().try_for_each(SType::check_v6_type), diff --git a/ergotree-ir/src/types/stype_companion.rs b/ergotree-ir/src/types/stype_companion.rs index 32de3d3a6..ba24302a2 100644 --- a/ergotree-ir/src/types/stype_companion.rs +++ b/ergotree-ir/src/types/stype_companion.rs @@ -2,7 +2,7 @@ use core::convert::TryFrom; use core::fmt::Debug; use crate::serialization::types::TypeCode; -use crate::serialization::SigmaParsingError; +use crate::soft_fork::SoftForkError; use super::savltree; use super::sbox; @@ -143,16 +143,13 @@ impl STypeCompanion { } impl TryFrom for STypeCompanion { - type Error = SigmaParsingError; + type Error = SoftForkError; fn try_from(value: TypeCode) -> Result { for (type_code, type_companion) in STypeCompanion::iter().map(|v| (v.type_code(), v)) { if type_code == value { return Ok(type_companion); } } - Err(SigmaParsingError::NotImplementedYet(format!( - "cannot find STypeCompanion for {0:?} type id", - value, - ))) + Err(SoftForkError::NoMethods(value)) } }