From 2063a5d1bf4b026f5634dd313c1908494bec8f07 Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Fri, 4 Oct 2024 14:08:02 +0500 Subject: [PATCH 1/2] [6.0] Add downcast to BigInt256 --- ergotree-interpreter/src/eval/downcast.rs | 289 +++++++++++++--------- ergotree-ir/src/bigint256.rs | 35 ++- 2 files changed, 211 insertions(+), 113 deletions(-) diff --git a/ergotree-interpreter/src/eval/downcast.rs b/ergotree-interpreter/src/eval/downcast.rs index 8dba979f3..cf469c3af 100644 --- a/ergotree-interpreter/src/eval/downcast.rs +++ b/ergotree-interpreter/src/eval/downcast.rs @@ -1,7 +1,9 @@ use ergotree_ir::bigint256::BigInt256; +use ergotree_ir::ergo_tree::ErgoTreeVersion; use ergotree_ir::mir::downcast::Downcast; use ergotree_ir::mir::value::Value; use ergotree_ir::types::stype::SType; +use num_traits::ToPrimitive; use crate::eval::env::Env; use crate::eval::Context; @@ -9,12 +11,17 @@ use crate::eval::EvalError; use crate::eval::Evaluable; use std::convert::TryFrom; -fn downcast_to_bigint(in_v: Value) -> Result { +fn downcast_to_bigint<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, EvalError> { match in_v { Value::Byte(v) => Ok(BigInt256::from(v).into()), Value::Short(v) => Ok(BigInt256::from(v).into()), Value::Int(v) => Ok(BigInt256::from(v).into()), Value::Long(v) => Ok(BigInt256::from(v).into()), + Value::BigInt(_) + if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => + { + Ok(in_v) + } _ => Err(EvalError::UnexpectedValue(format!( "Downcast: cannot downcast {0:?} to BigInt", in_v @@ -22,12 +29,21 @@ fn downcast_to_bigint(in_v: Value) -> Result { } } -fn downcast_to_long(in_v: Value) -> Result { +fn downcast_to_long<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, EvalError> { match in_v { Value::Byte(v) => Ok((v as i64).into()), Value::Short(v) => Ok((v as i64).into()), Value::Int(v) => Ok((v as i64).into()), Value::Long(_) => Ok(in_v), + Value::BigInt(v) + if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => + { + v.to_i64().map(Value::from).ok_or_else(|| { + EvalError::UnexpectedValue( + "Downcast: overflow converting BigInt to Long".to_string(), + ) + }) + } _ => Err(EvalError::UnexpectedValue(format!( "Downcast: cannot downcast {0:?} to Long", in_v @@ -35,7 +51,7 @@ fn downcast_to_long(in_v: Value) -> Result { } } -fn downcast_to_int(in_v: Value) -> Result { +fn downcast_to_int<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, EvalError> { match in_v { Value::Byte(x) => Ok((x as i32).into()), Value::Short(s) => Ok((s as i32).into()), @@ -46,6 +62,15 @@ fn downcast_to_int(in_v: Value) -> Result { "Downcast: Int overflow".to_string(), )), }, + Value::BigInt(v) + if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => + { + v.to_i32().map(Value::from).ok_or_else(|| { + EvalError::UnexpectedValue( + "Downcast: overflow converting BigInt to Int".to_string(), + ) + }) + } _ => Err(EvalError::UnexpectedValue(format!( "Downcast: cannot downcast {0:?} to Int", in_v @@ -53,7 +78,7 @@ fn downcast_to_int(in_v: Value) -> Result { } } -fn downcast_to_short(in_v: Value) -> Result { +fn downcast_to_short<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, EvalError> { match in_v { Value::Short(_) => Ok(in_v), Value::Int(i) => match i16::try_from(i).ok() { @@ -68,6 +93,15 @@ fn downcast_to_short(in_v: Value) -> Result { "Downcast: Short overflow".to_string(), )), }, + Value::BigInt(v) + if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => + { + v.to_i16().map(Value::from).ok_or_else(|| { + EvalError::UnexpectedValue( + "Downcast: overflow converting BigInt to Short".to_string(), + ) + }) + } _ => Err(EvalError::UnexpectedValue(format!( "Downcast: cannot downcast {0:?} to Short", in_v @@ -75,7 +109,7 @@ fn downcast_to_short(in_v: Value) -> Result { } } -fn downcast_to_byte(in_v: Value) -> Result { +fn downcast_to_byte<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, EvalError> { match in_v { Value::Byte(_) => Ok(in_v), Value::Short(s) => match i8::try_from(s).ok() { @@ -96,6 +130,15 @@ fn downcast_to_byte(in_v: Value) -> Result { "Downcast: Byte overflow".to_string(), )), }, + Value::BigInt(v) + if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => + { + v.to_i8().map(Value::from).ok_or_else(|| { + EvalError::UnexpectedValue( + "Downcast: overflow converting BigInt to Byte".to_string(), + ) + }) + } _ => Err(EvalError::UnexpectedValue(format!( "Downcast: cannot downcast {0:?} to Byte", in_v @@ -111,11 +154,11 @@ impl Evaluable for Downcast { ) -> Result, EvalError> { let input_v = self.input.eval(env, ctx)?; match self.tpe { - SType::SBigInt => downcast_to_bigint(input_v), - SType::SLong => downcast_to_long(input_v), - SType::SInt => downcast_to_int(input_v), - SType::SShort => downcast_to_short(input_v), - SType::SByte => downcast_to_byte(input_v), + SType::SBigInt => downcast_to_bigint(input_v, ctx), + SType::SLong => downcast_to_long(input_v, ctx), + SType::SInt => downcast_to_int(input_v, ctx), + SType::SShort => downcast_to_short(input_v, ctx), + SType::SByte => downcast_to_byte(input_v, ctx), _ => Err(EvalError::UnexpectedValue(format!( "Downcast: expected numeric value, got {0:?}", input_v @@ -128,71 +171,107 @@ impl Evaluable for Downcast { #[allow(clippy::unwrap_used)] #[cfg(test)] mod tests { - use ergotree_ir::mir::constant::Constant; + use ergotree_ir::mir::{constant::Constant, expr::Expr}; + use sigma_test_util::force_any_val; - use crate::eval::tests::{eval_out_wo_ctx, try_eval_out_wo_ctx}; + use crate::eval::tests::{eval_out_wo_ctx, try_eval_out_with_version, try_eval_out_wo_ctx}; use super::*; use proptest::prelude::*; + fn downcast(c: impl Into, return_type: SType) -> Expr { + Downcast::new(c.into().into(), return_type).unwrap().into() + } + proptest! { #![proptest_config(ProptestConfig::with_cases(64))] #[test] - fn to_bigint(v_byte in any::(), v_short in any::(), v_int in any::(), v_long in any::()) { + fn to_bigint(v_byte in any::(), v_short in any::(), v_int in any::(), v_long in any::(), v_bigint in any::()) { assert_eq!( eval_out_wo_ctx::( - &Downcast::new(v_byte.into(), SType::SBigInt).unwrap().into() + &downcast(v_byte, SType::SBigInt) ), (v_byte as i64).into() ); assert_eq!( eval_out_wo_ctx::( - &Downcast::new(v_short.into(), SType::SBigInt) - .unwrap() - .into() + &downcast(v_short, SType::SBigInt) ), (v_short as i64).into() ); assert_eq!( eval_out_wo_ctx::( - &Downcast::new(v_int.into(), SType::SBigInt).unwrap().into() + &downcast(v_int, SType::SBigInt) ), (v_int as i64).into() ); assert_eq!( eval_out_wo_ctx::( - &Downcast::new(v_long.into(), SType::SBigInt).unwrap().into() + &downcast(v_long, SType::SBigInt) ), v_long.into() ); + let ctx = force_any_val::(); + (0..ErgoTreeVersion::V6_SOFT_FORK_VERSION) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint.clone(), SType::SBigInt), &ctx, version).is_err())); + (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + |version| { + assert_eq!( + try_eval_out_with_version::( + &downcast(v_bigint.clone(), SType::SBigInt), + &ctx, + version + ).unwrap(), + v_bigint.clone() + ) + }, + ); } #[test] - fn to_long(v_byte in any::(), v_short in any::(), v_int in any::(), v_long in any::()) { + fn to_long(v_byte in any::(), v_short in any::(), v_int in any::(), v_long in any::(), v_bigint in any::()) { let c_byte: Constant = v_byte.into(); let c_short: Constant = v_short.into(); let c_int: Constant = v_int.into(); let c_long: Constant = v_long.into(); + let c_bigint: Constant = v_bigint.clone().into(); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_byte.into(), SType::SLong).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_byte, SType::SLong)), v_byte as i64 ); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_short.into(), SType::SLong).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_short, SType::SLong)), v_short as i64 ); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_int.into(), SType::SLong).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_int, SType::SLong)), v_int as i64 ); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_long.into(), SType::SLong).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_long, SType::SLong)), v_long ); + let ctx = force_any_val::(); + (0..ErgoTreeVersion::V6_SOFT_FORK_VERSION) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(c_bigint.clone(), SType::SLong), &ctx, version).is_err())); + (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + |version| { + let res = try_eval_out_with_version::( + &downcast(c_bigint.clone(), SType::SLong), + &ctx, + version + ); + if v_bigint < BigInt256::from(i64::MIN) || v_bigint > BigInt256::from(i64::MAX) { + assert!(res.is_err()); + } else { + assert_eq!(res.unwrap(), v_bigint.to_i64().unwrap()); + } + } + ); } #[test] - fn to_int(v_byte in any::(), v_short in any::(), v_int in any::()) { + fn to_int(v_byte in any::(), v_short in any::(), v_int in any::(), v_bigint in any::()) { let v_long = v_int as i64; let v_long_oob = if v_long.is_positive() { v_long + i32::MAX as i64 + 1 @@ -207,31 +286,45 @@ mod tests { let c_long_oob: Constant = v_long_oob.into(); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_byte.into(), SType::SInt).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_byte, SType::SInt)), v_byte as i32 ); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_short.into(), SType::SInt).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_short, SType::SInt)), v_short as i32 ); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_int.into(), SType::SInt).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_int, SType::SInt)), v_int ); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_long.into(), SType::SInt).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_long, SType::SInt)), v_long as i32 ); assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_long_oob.into(), SType::SInt) - .unwrap() - .into() + &downcast(c_long_oob, SType::SInt) ) - .is_err()) + .is_err()); + let ctx = force_any_val::(); + (0..ErgoTreeVersion::V6_SOFT_FORK_VERSION) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint.clone(), SType::SInt), &ctx, version).is_err())); + (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + |version| { + let res = try_eval_out_with_version::( + &downcast(v_bigint.clone(), SType::SInt), + &ctx, + version + ); + if v_bigint < BigInt256::from(i32::MIN) || v_bigint > BigInt256::from(i32::MAX) { + assert!(res.is_err()); + } else { + assert_eq!(res.unwrap(), v_bigint.to_i32().unwrap()); + } + }); } #[test] - fn to_short(v_short in any::()) { + fn to_short(v_short in any::(), v_bigint in any::()) { let v_int = v_short as i32; let v_int_oob = if v_int.is_positive() { v_int + i16::MAX as i32 + 1 @@ -252,33 +345,39 @@ mod tests { let c_long_oob: Constant = v_long_oob.into(); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_short.into(), SType::SShort).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_short, SType::SShort)), v_short ); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_int.into(), SType::SShort).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_int, SType::SShort)), v_int as i16 ); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_int_oob.into(), SType::SShort) - .unwrap() - .into() - ) - .is_err()); + assert!(try_eval_out_wo_ctx::(&downcast(c_int_oob, SType::SShort)).is_err()); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_long.into(), SType::SShort).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_long, SType::SShort)), v_long as i16 ); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_long_oob.into(), SType::SShort) - .unwrap() - .into() - ) - .is_err()); + assert!(try_eval_out_wo_ctx::(&downcast(c_long_oob, SType::SShort)).is_err()); + let ctx = force_any_val::(); + (0..ErgoTreeVersion::V6_SOFT_FORK_VERSION) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint.clone(), SType::SShort), &ctx, version).is_err())); + (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + |version| { + let res = try_eval_out_with_version::( + &downcast(v_bigint.clone(), SType::SShort), + &ctx, + version + ); + if v_bigint < BigInt256::from(i16::MIN) || v_bigint > BigInt256::from(i16::MAX) { + assert!(res.is_err()); + } else { + assert_eq!(res.unwrap(), v_bigint.to_i16().unwrap()); + } + }); } #[test] - fn to_byte(v_byte in any::()) { + fn to_byte(v_byte in any::(), v_bigint in any::()) { let v_short = v_byte as i16; let v_short_oob = if v_short.is_positive() { v_short + i8::MAX as i16 + 1 @@ -307,84 +406,50 @@ mod tests { let c_long_oob: Constant = v_long_oob.into(); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_byte.into(), SType::SByte).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_byte, SType::SByte)), v_byte ); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_short.into(), SType::SByte).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_short, SType::SByte)), v_short as i8 ); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_short_oob.into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); + assert!(try_eval_out_wo_ctx::(&downcast(c_short_oob, SType::SByte)).is_err()); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_int.into(), SType::SByte).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_int, SType::SByte)), v_int as i8 ); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_int_oob.into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); + assert!(try_eval_out_wo_ctx::(&downcast(c_int_oob, SType::SByte)).is_err()); assert_eq!( - eval_out_wo_ctx::(&Downcast::new(c_long.into(), SType::SByte).unwrap().into()), + eval_out_wo_ctx::(&downcast(c_long, SType::SByte)), v_long as i8 ); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_long_oob.into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); + assert!(try_eval_out_wo_ctx::(&downcast(c_long_oob, SType::SByte)).is_err()); + let ctx = force_any_val::(); + (0..ErgoTreeVersion::V6_SOFT_FORK_VERSION) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint.clone(), SType::SByte), &ctx, version).is_err())); + (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + |version| { + let res = try_eval_out_with_version::( + &downcast(v_bigint.clone(), SType::SByte), + &ctx, + version + ); + if v_bigint < BigInt256::from(i16::MIN) || v_bigint > BigInt256::from(i16::MAX) { + assert!(res.is_err()); + } else { + assert_eq!(res.unwrap(), v_bigint.to_i8().unwrap()); + } + }); } #[test] fn test_overflow(v_short_oob in (i8::MAX as i16 + 1..i16::MAX).prop_union(i16::MIN..i8::MIN as i16), v_int_oob in (i16::MAX as i32 + 1..i32::MAX).prop_union(i32::MIN..i16::MIN as i32), v_long_oob in (i32::MAX as i64 + 1..i64::MAX).prop_union(i64::MIN..i32::MIN as i64)) { - let c_short_oob: Constant = v_short_oob.into(); - let c_int_oob: Constant = v_int_oob.into(); - let c_long_oob: Constant = v_long_oob.into(); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_short_oob.into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_int_oob.clone().into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_long_oob.clone().into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); - - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_int_oob.into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_long_oob.clone().into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); - assert!(try_eval_out_wo_ctx::( - &Downcast::new(c_long_oob.into(), SType::SByte) - .unwrap() - .into() - ) - .is_err()); + let v_bigint_oob = BigInt256::from(v_long_oob); + assert!(try_eval_out_wo_ctx::(&downcast(v_short_oob, SType::SByte)).is_err()); + assert!(try_eval_out_wo_ctx::(&downcast(v_int_oob, SType::SByte)).is_err()); + assert!(try_eval_out_wo_ctx::(&downcast(v_long_oob, SType::SByte)).is_err()); + assert!(try_eval_out_wo_ctx::(&downcast(v_bigint_oob, SType::SByte)).is_err()); } } } diff --git a/ergotree-ir/src/bigint256.rs b/ergotree-ir/src/bigint256.rs index e7ffc38dd..986e52396 100644 --- a/ergotree-ir/src/bigint256.rs +++ b/ergotree-ir/src/bigint256.rs @@ -11,7 +11,8 @@ use num_bigint::ToBigInt; use num_derive::{One, Zero}; use num_integer::Integer; use num_traits::{ - Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, Num, Zero, + Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, Num, + ToPrimitive, Zero, }; /// 256-bit signed integer type @@ -270,12 +271,44 @@ impl<'a> BitXor<&'a BigInt256> for &'a BigInt256 { } } +impl ToPrimitive for BigInt256 { + fn to_i64(&self) -> Option { + self.0.to_i64() + } + + fn to_u64(&self) -> Option { + self.0.to_u64() + } +} + impl fmt::Display for BigInt256 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", &self.to_str_radix(10)) } } +#[cfg(feature = "arbitrary")] +mod arbitrary { + use proptest::{ + arbitrary::{any, Arbitrary}, + strategy::{BoxedStrategy, Strategy}, + }; + + use super::BigInt256; + + impl Arbitrary for BigInt256 { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + #[allow(clippy::unwrap_used)] + any::<[u8; 32]>() + .prop_map(|bytes| Self::try_from(&bytes[..]).unwrap()) + .boxed() + } + } +} + #[allow(clippy::unwrap_used)] #[cfg(test)] mod tests { From 78b25992068e756a55d5d0ce6b63209ec9b238aa Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Fri, 4 Oct 2024 14:23:18 +0500 Subject: [PATCH 2/2] Fix versioning for upcasting from bigint to bigint Previously this worked unconditionally, even on v4.0 and v5.0 --- ergotree-interpreter/src/eval/upcast.rs | 28 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ergotree-interpreter/src/eval/upcast.rs b/ergotree-interpreter/src/eval/upcast.rs index e3cf40a41..a04c442b2 100644 --- a/ergotree-interpreter/src/eval/upcast.rs +++ b/ergotree-interpreter/src/eval/upcast.rs @@ -1,4 +1,5 @@ use ergotree_ir::bigint256::BigInt256; +use ergotree_ir::ergo_tree::ErgoTreeVersion; use ergotree_ir::mir::upcast::Upcast; use ergotree_ir::mir::value::Value; use ergotree_ir::types::stype::SType; @@ -8,13 +9,17 @@ use crate::eval::Context; use crate::eval::EvalError; use crate::eval::Evaluable; -fn upcast_to_bigint(in_v: Value) -> Result { +fn upcast_to_bigint<'a>(in_v: Value<'a>, ctx: &Context) -> Result, EvalError> { match in_v { Value::Byte(v) => Ok(BigInt256::from(v).into()), Value::Short(v) => Ok(BigInt256::from(v).into()), Value::Int(v) => Ok(BigInt256::from(v).into()), Value::Long(v) => Ok(BigInt256::from(v).into()), - Value::BigInt(_) => Ok(in_v), + Value::BigInt(_) + if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => + { + Ok(in_v) + } _ => Err(EvalError::UnexpectedValue(format!( "Upcast: cannot upcast {0:?} to BigInt", in_v @@ -76,7 +81,7 @@ impl Evaluable for Upcast { ) -> Result, EvalError> { let input_v = self.input.eval(env, ctx)?; match self.tpe { - SType::SBigInt => upcast_to_bigint(input_v), + SType::SBigInt => upcast_to_bigint(input_v, ctx), SType::SLong => upcast_to_long(input_v), SType::SInt => upcast_to_int(input_v), SType::SShort => upcast_to_short(input_v), @@ -94,8 +99,9 @@ impl Evaluable for Upcast { #[cfg(test)] mod tests { use ergotree_ir::mir::constant::Constant; + use sigma_test_util::force_any_val; - use crate::eval::tests::eval_out_wo_ctx; + use crate::eval::tests::{eval_out_wo_ctx, try_eval_out_with_version}; use super::*; use proptest::prelude::*; @@ -177,13 +183,15 @@ mod tests { } #[test] - fn from_bigint(v in any::()) { - let v: BigInt256 = v.into(); + fn from_bigint(v in any::()) { let c: Constant = v.clone().into(); - assert_eq!( - eval_out_wo_ctx::(&Upcast::new(c.into(), SType::SBigInt).unwrap().into()), - v - ); + let ctx = force_any_val::(); + (0..ErgoTreeVersion::V6_SOFT_FORK_VERSION).for_each(|version| { + assert!(try_eval_out_with_version::(&Upcast::new(c.clone().into(), SType::SBigInt).unwrap().into(), &ctx, version).is_err()); + }); + (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each(|version| { + assert_eq!(try_eval_out_with_version::(&Upcast::new(c.clone().into(), SType::SBigInt).unwrap().into(), &ctx, version).unwrap(), v.clone()); + }); } } }