From 4e438f03140efcde59b8ebdb4ba86981f04088c8 Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Tue, 15 Oct 2024 17:53:52 +0500 Subject: [PATCH 1/4] [6.0] Add global.serialize without versioning --- ergotree-interpreter/src/eval.rs | 1 + ergotree-interpreter/src/eval/sglobal.rs | 118 ++++++++++++++++++++++- ergotree-ir/src/serialization.rs | 3 +- ergotree-ir/src/serialization/data.rs | 2 + ergotree-ir/src/types/sglobal.rs | 21 +++- 5 files changed, 139 insertions(+), 6 deletions(-) diff --git a/ergotree-interpreter/src/eval.rs b/ergotree-interpreter/src/eval.rs index ddce59719..f7cf0b198 100644 --- a/ergotree-interpreter/src/eval.rs +++ b/ergotree-interpreter/src/eval.rs @@ -345,6 +345,7 @@ fn smethod_eval_fn(method: &SMethod) -> Result { sglobal::FROM_BIGENDIAN_BYTES_METHOD_ID => { self::sglobal::SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN } + sglobal::SERIALIZE_METHOD_ID => self::sglobal::SERIALIZE_EVAL_FN, method_id => { return Err(EvalError::NotFound(format!( "Eval fn: method {:?} with method id {:?} not found in SGlobal", diff --git a/ergotree-interpreter/src/eval/sglobal.rs b/ergotree-interpreter/src/eval/sglobal.rs index d449fc2a2..23f5df886 100644 --- a/ergotree-interpreter/src/eval/sglobal.rs +++ b/ergotree-interpreter/src/eval/sglobal.rs @@ -2,7 +2,13 @@ use alloc::{string::ToString, sync::Arc}; use crate::eval::EvalError; -use ergotree_ir::mir::value::{CollKind, NativeColl, Value}; +use ergotree_ir::{ + mir::{ + constant::Constant, + value::{CollKind, NativeColl, Value}, + }, + serialization::{data::DataSerializer, sigma_byte_writer::SigmaByteWriter}, +}; use super::EvalFn; use crate::eval::Vec; @@ -141,23 +147,66 @@ pub(crate) static SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN: EvalFn = |mc, _env, _ctx } }; +pub(crate) static SERIALIZE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| { + if obj != Value::Global { + return Err(EvalError::UnexpectedValue(format!( + "sglobal.groupGenerator expected obj to be Value::Global, got {:?}", + obj + ))); + } + let arg: Constant = args + .first() + .ok_or_else(|| EvalError::NotFound("serialize: missing first arg".into()))? + .to_static() + .try_into() + .map_err(EvalError::UnexpectedValue)?; + + let mut buf = vec![]; + let mut writer = SigmaByteWriter::new(&mut buf, None); + DataSerializer::sigma_serialize(&arg.v, &mut writer)?; + Ok(Value::from(buf)) +}; + #[allow(clippy::unwrap_used)] #[cfg(test)] #[cfg(feature = "arbitrary")] mod tests { use ergo_chain_types::EcPoint; use ergotree_ir::bigint256::BigInt256; + use ergotree_ir::mir::constant::Constant; use ergotree_ir::mir::expr::Expr; + use ergotree_ir::mir::long_to_byte_array::LongToByteArray; use ergotree_ir::mir::method_call::MethodCall; use ergotree_ir::mir::property_call::PropertyCall; + use ergotree_ir::mir::sigma_prop_bytes::SigmaPropBytes; + use ergotree_ir::mir::unary_op::OneArgOpTryBuild; + use ergotree_ir::sigma_protocol::sigma_boolean::SigmaProp; + use ergotree_ir::types::sgroup_elem::GET_ENCODED_METHOD; + use ergotree_ir::types::stype_param::STypeVar; + use proptest::proptest; use crate::eval::tests::{eval_out, eval_out_wo_ctx}; use ergotree_ir::chain::context::Context; - use ergotree_ir::types::sglobal; + use ergotree_ir::types::sglobal::{self, SERIALIZE_METHOD}; use ergotree_ir::types::stype::SType; - use ergotree_ir::types::stype_param::STypeVar; use sigma_test_util::force_any_val; + fn serialize(val: impl Into) -> Vec { + let constant = val.into(); + let serialize_node = MethodCall::new( + Expr::Global, + SERIALIZE_METHOD.clone().with_concrete_types( + &[(STypeVar::t(), constant.tpe.clone())] + .iter() + .cloned() + .collect(), + ), + vec![constant.into()], + ) + .unwrap(); + eval_out_wo_ctx(&serialize_node.into()) + } + #[test] fn eval_group_generator() { let expr: Expr = PropertyCall::new(Expr::Global, sglobal::GROUP_GENERATOR_METHOD.clone()) @@ -312,4 +361,67 @@ mod tests { assert_eq!(eval_out_wo_ctx::(&expr), BigInt256::from(v_long)); } } + + #[test] + fn serialize_byte() { + assert_eq!(serialize(-128i8), vec![-128i8 as u8]); + assert_eq!(serialize(-1i8), vec![-1i8 as u8]); + assert_eq!(serialize(0i8), vec![0u8]); + assert_eq!(serialize(1i8), vec![1]); + assert_eq!(serialize(127i8), vec![127u8]); + } + + #[test] + fn serialize_short() { + assert_eq!(serialize(i16::MIN), vec![0xff, 0xff, 0x03]); + assert_eq!(serialize(-1i16), vec![0x01]); + assert_eq!(serialize(0i16), vec![0x00]); + assert_eq!(serialize(1i16), vec![0x02]); + assert_eq!(serialize(i16::MAX), vec![0xfe, 0xff, 0x03]); + } + + #[test] + fn serialize_byte_array() { + let arr = vec![0xc0, 0xff, 0xee]; + let serialized = serialize(arr.clone()); + + assert_eq!(serialized[0], arr.len() as u8); + assert_eq!(&serialized[1..], &arr) + } + + // test that serialize(long) != longToByteArray() + #[test] + fn serialize_long_ne_tobytearray() { + let num = -1000i64; + let long_to_byte_array = LongToByteArray::try_build(Constant::from(num).into()).unwrap(); + let serialized = serialize(num); + assert!(serialized != eval_out_wo_ctx::>(&long_to_byte_array.into())) + } + + // test equivalence between Global.serialize and ge.getEncoded + #[test] + fn serialize_group_element() { + let ec_point = EcPoint::from_base16_str(String::from( + "026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b", + )) + .unwrap(); + let get_encoded = MethodCall::new( + Constant::from(ec_point.clone()).into(), + GET_ENCODED_METHOD.clone(), + vec![], + ) + .unwrap(); + assert_eq!( + eval_out_wo_ctx::>(&get_encoded.into()), + serialize(ec_point) + ); + } + + proptest! { + #[test] + fn serialize_sigmaprop_eq_prop_bytes(sigma_prop: SigmaProp) { + let prop_bytes = SigmaPropBytes::try_build(Constant::from(sigma_prop.clone()).into()).unwrap(); + assert_eq!(serialize(sigma_prop), &eval_out_wo_ctx::>(&prop_bytes.into())[2..]) + } + } } diff --git a/ergotree-ir/src/serialization.rs b/ergotree-ir/src/serialization.rs index 96c922ef7..cc9ee48dd 100644 --- a/ergotree-ir/src/serialization.rs +++ b/ergotree-ir/src/serialization.rs @@ -3,7 +3,8 @@ mod bin_op; mod constant; mod constant_placeholder; -pub(crate) mod data; +/// Serializer for literals & constants +pub mod data; mod expr; mod global_vars; mod method_call; diff --git a/ergotree-ir/src/serialization/data.rs b/ergotree-ir/src/serialization/data.rs index cf2a56e43..50943e19b 100644 --- a/ergotree-ir/src/serialization/data.rs +++ b/ergotree-ir/src/serialization/data.rs @@ -31,6 +31,7 @@ use core::convert::TryInto; pub struct DataSerializer {} impl DataSerializer { + /// Serialize Literal without typecode serialized pub fn sigma_serialize(c: &Literal, w: &mut W) -> SigmaSerializeResult { // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala#L26-L26 Ok(match c { @@ -87,6 +88,7 @@ impl DataSerializer { }) } + /// Parse sigma-serialized literal pub fn sigma_parse( tpe: &SType, r: &mut R, diff --git a/ergotree-ir/src/types/sglobal.rs b/ergotree-ir/src/types/sglobal.rs index e4ef726e8..05cfbda2d 100644 --- a/ergotree-ir/src/types/sglobal.rs +++ b/ergotree-ir/src/types/sglobal.rs @@ -22,11 +22,13 @@ pub const GROUP_GENERATOR_METHOD_ID: MethodId = MethodId(1); pub const XOR_METHOD_ID: MethodId = MethodId(2); /// "fromBigEndianBytes" predefined function pub const FROM_BIGENDIAN_BYTES_METHOD_ID: MethodId = MethodId(5); +/// serialize function added in v6.0 +pub const SERIALIZE_METHOD_ID: MethodId = MethodId(3); lazy_static! { /// Global method descriptors pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> = - vec![&GROUP_GENERATOR_METHOD_DESC, &XOR_METHOD_DESC, &FROM_BIGENDIAN_BYTES_METHOD_DESC]; + vec![&GROUP_GENERATOR_METHOD_DESC, &XOR_METHOD_DESC, &SERIALIZE_METHOD_DESC, &FROM_BIGENDIAN_BYTES_METHOD_DESC]; } lazy_static! { @@ -71,11 +73,26 @@ lazy_static! { name: "fromBigEndianBytes", tpe: SFunc { t_dom: vec![SType::SGlobal, SType::SColl(SType::SByte.into())], - t_range:SType::STypeVar(STypeVar::t()).into(), + t_range: SType::STypeVar(STypeVar::t()).into(), tpe_params: vec![], }, explicit_type_args: vec![STypeVar::t()] }; /// GLOBAL.fromBigEndianBytes pub static ref FROM_BIGENDIAN_BYTES_METHOD: SMethod = SMethod::new(STypeCompanion::Global, FROM_BIGENDIAN_BYTES_METHOD_DESC.clone(),); + static ref SERIALIZE_METHOD_DESC: SMethodDesc = SMethodDesc { + method_id: SERIALIZE_METHOD_ID, + name: "serialize", + tpe: SFunc { + t_dom: vec![ + SType::SGlobal, + STypeVar::t().into() + ], + t_range: SType::SColl(SType::SByte.into()).into(), + tpe_params: vec![], + }, + explicit_type_args: vec![] + }; + /// GLOBAL.serialize + pub static ref SERIALIZE_METHOD: SMethod = SMethod::new(STypeCompanion::Global, SERIALIZE_METHOD_DESC.clone(),); } From 2d8133423824c41327157184a435d9da6e0c2892 Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Tue, 3 Dec 2024 13:12:28 +0500 Subject: [PATCH 2/4] Use tree-based versioning for methodcalls, add tree version to deserializer and Context --- bindings/ergo-lib-wasm/Cargo.toml | 2 +- ergo-lib/src/wallet/signing.rs | 1 + ergotree-interpreter/src/eval.rs | 12 ++- ergotree-interpreter/src/eval/downcast.rs | 57 +++++------ ergotree-interpreter/src/eval/error.rs | 7 +- ergotree-interpreter/src/eval/sbox.rs | 25 ++--- ergotree-interpreter/src/eval/sglobal.rs | 11 ++- ergotree-interpreter/src/eval/upcast.rs | 14 +-- ergotree-ir/src/chain/context.rs | 15 ++- ergotree-ir/src/ergo_tree.rs | 95 +++++++++---------- ergotree-ir/src/ergo_tree/tree_header.rs | 14 ++- ergotree-ir/src/mir/expr.rs | 14 ++- ergotree-ir/src/serialization/method_call.rs | 49 +++++++++- .../src/serialization/sigma_byte_reader.rs | 39 ++++++-- ergotree-ir/src/types/savltree.rs | 46 ++++++--- ergotree-ir/src/types/sbox.rs | 6 +- ergotree-ir/src/types/scoll.rs | 22 +++-- ergotree-ir/src/types/sglobal.rs | 13 ++- ergotree-ir/src/types/sgroup_elem.rs | 7 +- ergotree-ir/src/types/smethod.rs | 9 +- ergotree-ir/src/types/soption.rs | 7 +- 21 files changed, 298 insertions(+), 167 deletions(-) diff --git a/bindings/ergo-lib-wasm/Cargo.toml b/bindings/ergo-lib-wasm/Cargo.toml index b1a1b7008..e9210bbbb 100644 --- a/bindings/ergo-lib-wasm/Cargo.toml +++ b/bindings/ergo-lib-wasm/Cargo.toml @@ -41,7 +41,7 @@ console_error_panic_hook = { version = "0.1.6", optional = true } derive_more = { workspace = true } num-traits = { workspace = true } serde_with = { workspace = true } -bounded-vec = { workspace = true, features=["serde"] } +bounded-vec = { workspace = true, features = ["serde"] } wasm-bindgen = { version = "0.2.87", features = [] } wasm-bindgen-futures = { version = "0.4.37" } diff --git a/ergo-lib/src/wallet/signing.rs b/ergo-lib/src/wallet/signing.rs index 27b06552e..bb72232c7 100644 --- a/ergo-lib/src/wallet/signing.rs +++ b/ergo-lib/src/wallet/signing.rs @@ -110,6 +110,7 @@ pub fn make_context<'ctx, T: ErgoTransaction>( pre_header: state_ctx.pre_header.clone(), extension, headers: state_ctx.headers.clone(), + tree_version: Default::default(), }) } // Updates a Context, changing its self box and context extension to transaction.inputs[i] diff --git a/ergotree-interpreter/src/eval.rs b/ergotree-interpreter/src/eval.rs index f7cf0b198..b2db19ff7 100644 --- a/ergotree-interpreter/src/eval.rs +++ b/ergotree-interpreter/src/eval.rs @@ -379,6 +379,9 @@ pub(crate) mod tests { use ergotree_ir::mir::constant::TryExtractInto; use ergotree_ir::mir::val_def::ValDef; use ergotree_ir::mir::val_use::ValUse; + use ergotree_ir::serialization::sigma_byte_reader::from_bytes; + use ergotree_ir::serialization::sigma_byte_reader::SigmaByteRead; + use ergotree_ir::serialization::SigmaSerializable; use ergotree_ir::types::stype::SType; use expect_test::expect; use sigma_test_util::force_any_val; @@ -425,10 +428,15 @@ pub(crate) mod tests { pub fn try_eval_out_with_version<'ctx, T: TryExtractFrom> + 'static>( expr: &Expr, ctx: &'ctx Context<'ctx>, - version: u8, + tree_version: u8, + activated_version: u8, ) -> Result { let mut ctx = ctx.clone(); - ctx.pre_header.version = version + 1; + ctx.pre_header.version = activated_version + 1; + ctx.tree_version.set(tree_version.into()); + // roundtrip expr to test methodcall versioning + from_bytes(&expr.sigma_serialize_bytes()?) + .with_tree_version(ctx.tree_version(), Expr::sigma_parse)?; let mut env = Env::empty(); expr.eval(&mut env, &ctx).and_then(|v| { v.to_static() diff --git a/ergotree-interpreter/src/eval/downcast.rs b/ergotree-interpreter/src/eval/downcast.rs index aeef9962d..866fc6334 100644 --- a/ergotree-interpreter/src/eval/downcast.rs +++ b/ergotree-interpreter/src/eval/downcast.rs @@ -18,11 +18,7 @@ fn downcast_to_bigint<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result 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) - } + Value::BigInt(_) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => Ok(in_v), _ => Err(EvalError::UnexpectedValue(format!( "Downcast: cannot downcast {0:?} to BigInt", in_v @@ -36,9 +32,7 @@ fn downcast_to_long<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, 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 => - { + Value::BigInt(v) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => { v.to_i64().map(Value::from).ok_or_else(|| { EvalError::UnexpectedValue( "Downcast: overflow converting BigInt to Long".to_string(), @@ -63,9 +57,7 @@ fn downcast_to_int<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, "Downcast: Int overflow".to_string(), )), }, - Value::BigInt(v) - if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => - { + Value::BigInt(v) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => { v.to_i32().map(Value::from).ok_or_else(|| { EvalError::UnexpectedValue( "Downcast: overflow converting BigInt to Int".to_string(), @@ -94,9 +86,7 @@ fn downcast_to_short<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result "Downcast: Short overflow".to_string(), )), }, - Value::BigInt(v) - if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => - { + Value::BigInt(v) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => { v.to_i16().map(Value::from).ok_or_else(|| { EvalError::UnexpectedValue( "Downcast: overflow converting BigInt to Short".to_string(), @@ -131,9 +121,7 @@ fn downcast_to_byte<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, "Downcast: Byte overflow".to_string(), )), }, - Value::BigInt(v) - if ctx.activated_script_version() >= ErgoTreeVersion::V6_SOFT_FORK_VERSION => - { + Value::BigInt(v) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => { v.to_i8().map(Value::from).ok_or_else(|| { EvalError::UnexpectedValue( "Downcast: overflow converting BigInt to Byte".to_string(), @@ -214,14 +202,15 @@ mod tests { 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, SType::SBigInt), &ctx, version).is_err())); - (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + (0..ErgoTreeVersion::V3.into()) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SBigInt), &ctx, version, version).is_err())); + (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { assert_eq!( try_eval_out_with_version::( &downcast(v_bigint, SType::SBigInt), &ctx, + version, version ).unwrap(), v_bigint.clone() @@ -254,13 +243,14 @@ mod tests { 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( + (0..ErgoTreeVersion::V3.into()) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(c_bigint.clone(), SType::SLong), &ctx, version, version).is_err())); + (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { let res = try_eval_out_with_version::( &downcast(c_bigint.clone(), SType::SLong), &ctx, + version, version ); if v_bigint < BigInt256::from(i64::MIN) || v_bigint > BigInt256::from(i64::MAX) { @@ -307,13 +297,14 @@ mod tests { ) .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, SType::SInt), &ctx, version).is_err())); - (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + (0..ErgoTreeVersion::V3.into()) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SInt), &ctx, version, version).is_err())); + (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { let res = try_eval_out_with_version::( &downcast(v_bigint, SType::SInt), &ctx, + version, version ); if v_bigint < BigInt256::from(i32::MIN) || v_bigint > BigInt256::from(i32::MAX) { @@ -361,13 +352,14 @@ mod tests { ); 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, SType::SShort), &ctx, version).is_err())); - (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + (0..ErgoTreeVersion::V3.into()) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SShort), &ctx, version, version).is_err())); + (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { let res = try_eval_out_with_version::( &downcast(v_bigint, SType::SShort), &ctx, + version, version ); if v_bigint < BigInt256::from(i16::MIN) || v_bigint > BigInt256::from(i16::MAX) { @@ -426,13 +418,14 @@ mod tests { ); 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, SType::SByte), &ctx, version).is_err())); - (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + (0..ErgoTreeVersion::V3.into()) + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SByte), &ctx, version, version).is_err())); + (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { let res = try_eval_out_with_version::( &downcast(v_bigint, SType::SByte), &ctx, + version, version ); if v_bigint < BigInt256::from(i16::MIN) || v_bigint > BigInt256::from(i16::MAX) { diff --git a/ergotree-interpreter/src/eval/error.rs b/ergotree-interpreter/src/eval/error.rs index 28dfea55d..464736da6 100644 --- a/ergotree-interpreter/src/eval/error.rs +++ b/ergotree-interpreter/src/eval/error.rs @@ -2,6 +2,7 @@ use alloc::boxed::Box; use alloc::string::String; use core::fmt::Debug; use core::fmt::Display; +use ergotree_ir::ergo_tree::ErgoTreeVersion; use ergotree_ir::mir::expr::SubstDeserializeError; use bounded_vec::BoundedVecOutOfBounds; @@ -76,12 +77,12 @@ pub enum EvalError { #[error("eval error: {0:?}")] Spanned(SpannedEvalError), /// Script version error - #[error("Method requires at least version {required_version}, but activated version is {activated_version}")] + #[error("Method requires at least version {required_version:?}, but activated version is {activated_version:?}")] ScriptVersionError { /// Opcode/method call requires this version - required_version: u8, + required_version: ErgoTreeVersion, /// Currently activated script version on network - activated_version: u8, + activated_version: ErgoTreeVersion, }, /// Deserialize substitution error, see [`ergotree_ir::mir::expr::Expr::substitute_deserialize`] #[error("DeserializeRegister/DeserializeContext error: {0}")] diff --git a/ergotree-interpreter/src/eval/sbox.rs b/ergotree-interpreter/src/eval/sbox.rs index 5a142607b..674645fd7 100644 --- a/ergotree-interpreter/src/eval/sbox.rs +++ b/ergotree-interpreter/src/eval/sbox.rs @@ -18,9 +18,9 @@ pub(crate) static VALUE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| { }; pub(crate) static GET_REG_EVAL_FN: EvalFn = |mc, _env, ctx, obj, args| { - if ctx.activated_script_version() < ErgoTreeVersion::V6_SOFT_FORK_VERSION { + if ctx.activated_script_version() < ErgoTreeVersion::V3 { return Err(EvalError::ScriptVersionError { - required_version: ErgoTreeVersion::V6_SOFT_FORK_VERSION, + required_version: ErgoTreeVersion::V3, activated_version: ctx.activated_script_version(), }); } @@ -128,13 +128,13 @@ mod tests { .unwrap() .into(); let ctx = force_any_val::(); - (0..ErgoTreeVersion::V6_SOFT_FORK_VERSION).for_each(|version| { - assert!(try_eval_out_with_version::(&expr, &ctx, version).is_err()) + (0..ErgoTreeVersion::V3.into()).for_each(|version| { + assert!(try_eval_out_with_version::(&expr, &ctx, version, version).is_err()) }); - (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { assert_eq!( - try_eval_out_with_version::>(&expr, &ctx, version) + try_eval_out_with_version::>(&expr, &ctx, version, version) .unwrap() .unwrap(), ctx.self_box.value.as_i64() @@ -156,23 +156,26 @@ mod tests { .unwrap() .into(); let ctx = force_any_val::(); - (0..ErgoTreeVersion::V6_SOFT_FORK_VERSION).for_each(|version| { - let res = try_eval_out_with_version::>(&expr, &ctx, version); + (0..ErgoTreeVersion::V3.into()).for_each(|version| { + let res = try_eval_out_with_version::>(&expr, &ctx, version, version); match res { Err(EvalError::Spanned(err)) if matches!( *err.error, EvalError::ScriptVersionError { - required_version: ErgoTreeVersion::V6_SOFT_FORK_VERSION, + required_version: ErgoTreeVersion::V3, activated_version: _ } ) => {} _ => panic!("Expected script version error"), } }); - (ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each( + (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { - assert!(try_eval_out_with_version::>(&expr, &ctx, version).is_err()) + assert!( + try_eval_out_with_version::>(&expr, &ctx, version, version) + .is_err() + ) }, ); } diff --git a/ergotree-interpreter/src/eval/sglobal.rs b/ergotree-interpreter/src/eval/sglobal.rs index 23f5df886..f4af7d6fb 100644 --- a/ergotree-interpreter/src/eval/sglobal.rs +++ b/ergotree-interpreter/src/eval/sglobal.rs @@ -173,6 +173,7 @@ pub(crate) static SERIALIZE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| { mod tests { use ergo_chain_types::EcPoint; use ergotree_ir::bigint256::BigInt256; + use ergotree_ir::ergo_tree::ErgoTreeVersion; use ergotree_ir::mir::constant::Constant; use ergotree_ir::mir::expr::Expr; use ergotree_ir::mir::long_to_byte_array::LongToByteArray; @@ -185,7 +186,7 @@ mod tests { use ergotree_ir::types::stype_param::STypeVar; use proptest::proptest; - use crate::eval::tests::{eval_out, eval_out_wo_ctx}; + use crate::eval::tests::{eval_out, eval_out_wo_ctx, try_eval_out_with_version}; use ergotree_ir::chain::context::Context; use ergotree_ir::types::sglobal::{self, SERIALIZE_METHOD}; use ergotree_ir::types::stype::SType; @@ -204,7 +205,13 @@ mod tests { vec![constant.into()], ) .unwrap(); - eval_out_wo_ctx(&serialize_node.into()) + let ctx = force_any_val::(); + assert!((0u8..ErgoTreeVersion::V3.into()).all(|version| { + try_eval_out_with_version::>(&serialize_node.clone().into(), &ctx, version, 3) + .is_err() + })); + try_eval_out_with_version(&serialize_node.into(), &ctx, ErgoTreeVersion::V3.into(), 3) + .unwrap() } #[test] diff --git a/ergotree-interpreter/src/eval/upcast.rs b/ergotree-interpreter/src/eval/upcast.rs index b0c384fb1..76281c700 100644 --- a/ergotree-interpreter/src/eval/upcast.rs +++ b/ergotree-interpreter/src/eval/upcast.rs @@ -15,11 +15,7 @@ fn upcast_to_bigint<'a>(in_v: Value<'a>, ctx: &Context) -> Result, Eva 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) - } + Value::BigInt(_) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => Ok(in_v), _ => Err(EvalError::UnexpectedValue(format!( "Upcast: cannot upcast {0:?} to BigInt", in_v @@ -186,11 +182,11 @@ mod tests { fn from_bigint(v in any::()) { let c: Constant = v.into(); 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()); + (0..ErgoTreeVersion::V3.into()).for_each(|version| { + assert!(try_eval_out_with_version::(&Upcast::new(c.clone().into(), SType::SBigInt).unwrap().into(), &ctx, version, 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()); + (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each(|version| { + assert_eq!(try_eval_out_with_version::(&Upcast::new(c.clone().into(), SType::SBigInt).unwrap().into(), &ctx, version, version).unwrap(), v.clone()); }); } } diff --git a/ergotree-ir/src/chain/context.rs b/ergotree-ir/src/chain/context.rs index 9162cbab1..d077635cd 100644 --- a/ergotree-ir/src/chain/context.rs +++ b/ergotree-ir/src/chain/context.rs @@ -1,6 +1,8 @@ //! Context(blockchain) for the interpreter -use crate::chain::context_extension::ContextExtension; +use core::cell::Cell; + use crate::chain::ergo_box::ErgoBox; +use crate::{chain::context_extension::ContextExtension, ergo_tree::ErgoTreeVersion}; use bounded_vec::BoundedVec; use ergo_chain_types::{Header, PreHeader}; @@ -26,6 +28,8 @@ pub struct Context<'ctx> { pub headers: [Header; 10], /// prover-defined key-value pairs, that may be used inside a script pub extension: ContextExtension, + /// ergo tree version + pub tree_version: Cell, } impl<'ctx> Context<'ctx> { @@ -37,8 +41,12 @@ impl<'ctx> Context<'ctx> { } } /// Activated script version corresponds to block version - 1 - pub fn activated_script_version(&self) -> u8 { - self.pre_header.version.saturating_sub(1) + pub fn activated_script_version(&self) -> ErgoTreeVersion { + ErgoTreeVersion::from(self.pre_header.version.saturating_sub(1)) + } + /// Version of ergotree being evaluated under context + pub fn tree_version(&self) -> ErgoTreeVersion { + self.tree_version.get() } } @@ -95,6 +103,7 @@ mod arbitrary { pre_header, extension, headers, + tree_version: Default::default(), } }, ) diff --git a/ergotree-ir/src/ergo_tree.rs b/ergotree-ir/src/ergo_tree.rs index 7316d33ac..1dd032d7e 100644 --- a/ergotree-ir/src/ergo_tree.rs +++ b/ergotree-ir/src/ergo_tree.rs @@ -89,17 +89,6 @@ pub enum SetConstantError { TypeMismatch(String), } -/// ErgoTree root expr parsing (deserialization) error inner -#[derive(Error, PartialEq, Eq, Debug, Clone, From)] -pub enum ErgoTreeRootParsingError { - /// ErgoTree root expr parsing (deserialization) error - #[error("SigmaParsingError: {0:?}")] - SigmaParsingError(SigmaParsingError), - /// Non-consumed bytes after root expr is parsed - #[error("Non-consumed bytes after root expr is parsed")] - NonConsumedBytes, -} - /// ErgoTree serialization and parsing (deserialization) error #[derive(Error, PartialEq, Eq, Debug, Clone, From)] pub enum ErgoTreeError { @@ -109,9 +98,6 @@ pub enum ErgoTreeError { /// ErgoTree constants error #[error("ErgoTree constants error: {0:?}")] ConstantsError(ErgoTreeConstantError), - /// ErgoTree root expr parsing (deserialization) error - #[error("ErgoTree root expr parsing (deserialization) error: {0:?}")] - RootParsingError(ErgoTreeRootParsingError), /// ErgoTree serialization error #[error("ErgoTree serialization error: {0}")] RootSerializationError(SigmaSerializationError), @@ -151,6 +137,11 @@ impl ErgoTree { } } + /// Return ErgoTreeHeader. Errors if deserializing ergotree failed + pub fn header(&self) -> Result { + self.parsed_tree().map(|parsed| parsed.header.clone()) + } + fn sigma_parse_sized( r: &mut R, header: ErgoTreeHeader, @@ -212,7 +203,7 @@ impl ErgoTree { let cursor = Cursor::new(&mut data[..]); let new_cs = ConstantStore::new(constants.clone()); let mut sr = SigmaByteReader::new(cursor, new_cs); - let parsed_expr = Expr::sigma_parse(&mut sr)?; + let parsed_expr = sr.with_tree_version(header.version(), Expr::sigma_parse)?; ErgoTree::Parsed(ParsedErgoTree { header, constants, @@ -381,42 +372,46 @@ impl SigmaSerializable for ErgoTree { fn sigma_parse(r: &mut R) -> Result { let start_pos = r.position()?; let header = ErgoTreeHeader::sigma_parse(r)?; - if header.has_size() { - let tree_size_bytes = r.get_u32()?; - let body_pos = r.position()?; - let mut buf = vec![0u8; tree_size_bytes as usize]; - r.read_exact(buf.as_mut_slice())?; - let mut inner_r = - SigmaByteReader::new(Cursor::new(&mut buf[..]), ConstantStore::empty()); - match ErgoTree::sigma_parse_sized(&mut inner_r, header.clone()) { - Ok(parsed_tree) => Ok(parsed_tree.into()), - Err(error) => { - 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, - }) + r.with_tree_version(header.version(), |r| { + if header.has_size() { + let tree_size_bytes = r.get_u32()?; + let body_pos = r.position()?; + let mut buf = vec![0u8; tree_size_bytes as usize]; + r.read_exact(buf.as_mut_slice())?; + let mut inner_r = + SigmaByteReader::new(Cursor::new(&mut buf[..]), ConstantStore::empty()); + match inner_r.with_tree_version(header.version(), |inner_r| { + ErgoTree::sigma_parse_sized(inner_r, header) + }) { + Ok(parsed_tree) => Ok(parsed_tree.into()), + Err(error) => { + 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, + }) + } } - } - } else { - let constants = if header.is_constant_segregation() { - ErgoTree::sigma_parse_constants(r)? } else { - vec![] - }; - r.set_constant_store(ConstantStore::new(constants.clone())); - let root = Expr::sigma_parse(r)?; - Ok(ErgoTree::Parsed(ParsedErgoTree { - header, - constants, - root, - #[cfg(feature = "std")] - has_deserialize: OnceLock::new(), - })) - } + let constants = if header.is_constant_segregation() { + ErgoTree::sigma_parse_constants(r)? + } else { + vec![] + }; + r.set_constant_store(ConstantStore::new(constants.clone())); + let root = Expr::sigma_parse(r)?; + Ok(ErgoTree::Parsed(ParsedErgoTree { + header, + constants, + root, + #[cfg(feature = "std")] + has_deserialize: OnceLock::new(), + })) + } + }) } } @@ -672,7 +667,7 @@ mod tests { let header = tree.parsed_tree().unwrap().header.clone(); assert!(header.has_size()); assert!(header.is_constant_segregation()); - assert_eq!(header.version(), &ErgoTreeVersion::V1); + assert_eq!(header.version(), ErgoTreeVersion::V1); let new_tree = tree .with_constant(7, 1i64.into()) .unwrap() diff --git a/ergotree-ir/src/ergo_tree/tree_header.rs b/ergotree-ir/src/ergo_tree/tree_header.rs index 12e96b83e..67e1779e7 100644 --- a/ergotree-ir/src/ergo_tree/tree_header.rs +++ b/ergotree-ir/src/ergo_tree/tree_header.rs @@ -1,7 +1,7 @@ //! ErgoTree header use alloc::string::{String, ToString}; -use derive_more::From; +use derive_more::{From, Into}; use thiserror::Error; use crate::serialization::sigma_byte_reader::SigmaByteRead; @@ -103,8 +103,8 @@ impl ErgoTreeHeader { } /// Returns ErgoTree version - pub fn version(&self) -> &ErgoTreeVersion { - &self.version + pub fn version(&self) -> ErgoTreeVersion { + self.version } } @@ -120,7 +120,7 @@ pub enum ErgoTreeHeaderError { } /// ErgoTree version 0..=7, should fit in 3 bits -#[derive(PartialEq, Eq, Debug, Clone)] +#[derive(PartialOrd, Ord, PartialEq, Eq, Debug, Clone, Copy, From, Into, Default)] pub struct ErgoTreeVersion(u8); impl ErgoTreeVersion { @@ -128,9 +128,7 @@ impl ErgoTreeVersion { pub const VERSION_MASK: u8 = 0x07; /// Max version of ErgoTree supported by interpreter - pub const MAX_SCRIPT_VERSION: u8 = 3; - /// Version for v6.0 (Evolution) - pub const V6_SOFT_FORK_VERSION: u8 = 3; + pub const MAX_SCRIPT_VERSION: Self = Self::V3; /// Version 0 pub const V0: Self = ErgoTreeVersion(0); /// Version 1 (size flag is mandatory) @@ -138,7 +136,7 @@ impl ErgoTreeVersion { /// Version 2 (JIT) pub const V2: Self = ErgoTreeVersion(2); /// Version 3 (v6.0/Evolution) - pub const V3: Self = ErgoTreeVersion(Self::V6_SOFT_FORK_VERSION); + pub const V3: Self = ErgoTreeVersion(3); /// Returns a value of the version bits from the given header byte. pub fn parse_version(header_byte: u8) -> Self { diff --git a/ergotree-ir/src/mir/expr.rs b/ergotree-ir/src/mir/expr.rs index 7e7054b0a..71510acd2 100644 --- a/ergotree-ir/src/mir/expr.rs +++ b/ergotree-ir/src/mir/expr.rs @@ -13,6 +13,8 @@ use crate::chain::ergo_box::RegisterIdOutOfBounds; use crate::chain::ergo_box::RegisterValueError; use crate::pretty_printer::PosTrackingWriter; use crate::pretty_printer::Print; +use crate::serialization::sigma_byte_reader; +use crate::serialization::sigma_byte_reader::SigmaByteRead; use crate::serialization::SigmaParsingError; use crate::serialization::SigmaSerializable; use crate::source_span::Spanned; @@ -402,6 +404,7 @@ impl Expr { } /// Rewrite expr, replacing [`DeserializeContext`] and [`DeserializeRegister`] nodes with their respective context extension/register values + // TODO: work on soft-fork for sigma-rust, in that case if deserializing expr fails with validation err, reduce_to_crypto should return true pub fn substitute_deserialize(self, ctx: &Context) -> Result { self.try_rewrite_bu( |expr| { @@ -420,16 +423,21 @@ impl Expr { .ok_or(SubstDeserializeError::ExtensionKeyNotFound(*id))? .clone() .try_extract_into::>()?; - (tpe, Expr::sigma_parse_bytes(&vec)?) + ( + tpe, + sigma_byte_reader::from_bytes(&vec) + .with_tree_version(ctx.tree_version(), Expr::sigma_parse)?, + ) } Expr::DeserializeRegister(DeserializeRegister { reg, tpe, default }) => { let expr = ctx .self_box .get_register(*reg)? .map(|constant| -> Result<_, SubstDeserializeError> { - Ok(Expr::sigma_parse_bytes( + Ok(sigma_byte_reader::from_bytes( &constant.try_extract_into::>()?, - )?) + ) + .with_tree_version(ctx.tree_version(), Expr::sigma_parse)?) }) .transpose()? .or(default.as_deref().cloned()); diff --git a/ergotree-ir/src/serialization/method_call.rs b/ergotree-ir/src/serialization/method_call.rs index 10d8f5519..eded2b47d 100644 --- a/ergotree-ir/src/serialization/method_call.rs +++ b/ergotree-ir/src/serialization/method_call.rs @@ -37,6 +37,12 @@ impl SigmaSerializable for MethodCall { 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)?; + if r.tree_version() < method.method_raw.min_version { + return Err(SigmaParsingError::UnknownMethodId( + method_id, + type_id.value(), + )); + } let explicit_type_args = method .method_raw .explicit_type_args @@ -59,13 +65,18 @@ impl SigmaSerializable for MethodCall { #[allow(clippy::unwrap_used)] mod tests { use alloc::vec; + use core2::io::{Cursor, Seek, SeekFrom}; + use crate::ergo_tree::ErgoTreeVersion; + use crate::mir::constant::Constant; use crate::mir::expr::Expr; use crate::mir::method_call::MethodCall; - use crate::serialization::sigma_serialize_roundtrip; - use crate::types::scoll; + use crate::serialization::constant_store::ConstantStore; + use crate::serialization::sigma_byte_reader::{SigmaByteRead, SigmaByteReader}; + use crate::serialization::{sigma_serialize_roundtrip, SigmaSerializable}; use crate::types::stype::SType; use crate::types::stype_param::STypeVar; + use crate::types::{scoll, sglobal}; #[test] fn ser_roundtrip() { @@ -80,4 +91,38 @@ mod tests { .into(); assert_eq![sigma_serialize_roundtrip(&mc), mc]; } + // test that methodcalls that are added in later versions via soft-fork can't be parsed with older version + #[test] + fn versioned_roundtrip() { + let mc: Expr = MethodCall::new( + Expr::Global, + sglobal::SERIALIZE_METHOD + .clone() + .specialize_for(SType::SGlobal, vec![SType::SInt]) + .unwrap(), + vec![Constant::from(1i32).into()], + ) + .unwrap() + .into(); + let serialized = mc.sigma_serialize_bytes().unwrap(); + let mut cursor = Cursor::new(&serialized); + let mut reader = SigmaByteReader::new(&mut cursor, ConstantStore::empty()); + for version in + u8::from(ErgoTreeVersion::V0)..sglobal::SERIALIZE_METHOD.method_raw.min_version.into() + { + reader.with_tree_version(version.into(), |r| assert!(Expr::sigma_parse(r).is_err())); + reader.seek(SeekFrom::Start(0)).unwrap(); + } + for version in u8::from(sglobal::SERIALIZE_METHOD.method_raw.min_version) + ..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into() + { + assert_eq!( + mc, + reader + .with_tree_version(version.into(), Expr::sigma_parse) + .unwrap() + ); + reader.seek(SeekFrom::Start(0)).unwrap(); + } + } } diff --git a/ergotree-ir/src/serialization/sigma_byte_reader.rs b/ergotree-ir/src/serialization/sigma_byte_reader.rs index 1e224ce68..763650203 100644 --- a/ergotree-ir/src/serialization/sigma_byte_reader.rs +++ b/ergotree-ir/src/serialization/sigma_byte_reader.rs @@ -1,4 +1,6 @@ //! Sigma byte stream writer +use crate::ergo_tree::ErgoTreeVersion; + use super::constant_store::ConstantStore; use super::val_def_type_store::ValDefTypeStore; use core2::io::Cursor; @@ -13,6 +15,7 @@ pub struct SigmaByteReader { substitute_placeholders: bool, val_def_type_store: ValDefTypeStore, was_deserialize: bool, + version: ErgoTreeVersion, } impl SigmaByteReader { @@ -24,6 +27,7 @@ impl SigmaByteReader { substitute_placeholders: false, val_def_type_store: ValDefTypeStore::new(), was_deserialize: false, + version: ErgoTreeVersion::MAX_SCRIPT_VERSION, } } @@ -39,19 +43,14 @@ impl SigmaByteReader { substitute_placeholders: true, val_def_type_store: ValDefTypeStore::new(), was_deserialize: false, + version: ErgoTreeVersion::MAX_SCRIPT_VERSION, } } } /// Create SigmaByteReader from a byte array (with empty constant store) pub fn from_bytes>(bytes: T) -> SigmaByteReader> { - SigmaByteReader { - inner: Cursor::new(bytes), - constant_store: ConstantStore::empty(), - substitute_placeholders: false, - val_def_type_store: ValDefTypeStore::new(), - was_deserialize: false, - } + SigmaByteReader::new(Cursor::new(bytes), ConstantStore::empty()) } /// Sigma byte reader trait with a constant store to resolve segregated constants @@ -85,6 +84,16 @@ pub trait SigmaByteRead: ReadSigmaVlqExt { self.seek(core2::io::SeekFrom::Current(0)) } } + + /// Call `f` with reader's ErgoTree version set to `version` inside f's scope + fn with_tree_version( + &mut self, + version: ErgoTreeVersion, + f: impl FnOnce(&mut Self) -> T, + ) -> T; + + /// Maximum version that deserializer can handle. By default this will be [ErgoTreeVersion::MAX_SCRIPT_VERSION] but for consensus-critical applications it should be set to activated block version + fn tree_version(&self) -> ErgoTreeVersion; } impl Read for SigmaByteReader { @@ -133,4 +142,20 @@ impl SigmaByteRead for SigmaByteReader { fn set_deserialize(&mut self, has_deserialize: bool) { self.was_deserialize = has_deserialize } + + fn with_tree_version( + &mut self, + version: ErgoTreeVersion, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + let tmp = self.version; + self.version = version; + let res = f(self); + self.version = tmp; + res + } + + fn tree_version(&self) -> ErgoTreeVersion { + self.version + } } diff --git a/ergotree-ir/src/types/savltree.rs b/ergotree-ir/src/types/savltree.rs index bd5cb911a..dd3f6030c 100644 --- a/ergotree-ir/src/types/savltree.rs +++ b/ergotree-ir/src/types/savltree.rs @@ -2,6 +2,7 @@ use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; +use crate::ergo_tree::ErgoTreeVersion; use crate::serialization::types::TypeCode; use super::sfunc::SFunc; @@ -80,7 +81,8 @@ lazy_static! { t_range: SType::SColl(Arc::new(SType::SByte)).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.digest pub static ref DIGEST_METHOD: SMethod = @@ -96,7 +98,8 @@ lazy_static! { t_range: SType::SByte.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.enabledOperations pub static ref ENABLED_OPERATIONS_METHOD: SMethod = @@ -112,7 +115,8 @@ lazy_static! { t_range: SType::SInt.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.keyLength pub static ref KEY_LENGTH_METHOD: SMethod = @@ -128,7 +132,8 @@ lazy_static! { t_range: SType::SOption(Arc::new(SType::SInt)).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.valueLengthOpt pub static ref VALUE_LENGTH_OPT_METHOD: SMethod = @@ -144,7 +149,8 @@ lazy_static! { t_range: SType::SBoolean.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.isInsertAllowed pub static ref IS_INSERT_ALLOWED_METHOD: SMethod = @@ -160,7 +166,8 @@ lazy_static! { t_range: SType::SBoolean.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.isUpdateAllowed pub static ref IS_UPDATE_ALLOWED_METHOD: SMethod = @@ -176,7 +183,8 @@ lazy_static! { t_range: SType::SBoolean.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.isRemoveAllowed pub static ref IS_REMOVE_ALLOWED_METHOD: SMethod = @@ -192,7 +200,8 @@ lazy_static! { t_range: SType::SAvlTree.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.updateOperations pub static ref UPDATE_OPERATIONS_METHOD: SMethod = @@ -211,7 +220,8 @@ lazy_static! { t_range: SType::SOption(SType::SColl(SType::SByte.into()).into()).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.get @@ -231,7 +241,8 @@ lazy_static! { t_range: SType::SColl(SType::SOption(SType::SColl(SType::SByte.into()).into()).into()).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.getMany @@ -260,7 +271,8 @@ lazy_static! { t_range: SType::SOption(Arc::new(SType::SAvlTree)).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.insert pub static ref INSERT_METHOD: SMethod = @@ -283,7 +295,8 @@ lazy_static! { t_range: SType::SOption(Arc::new(SType::SAvlTree)).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.remove pub static ref REMOVE_METHOD: SMethod = @@ -303,7 +316,8 @@ lazy_static! { t_range: SType::SBoolean.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.contains pub static ref CONTAINS_METHOD: SMethod = @@ -331,7 +345,8 @@ lazy_static! { t_range: SType::SOption(Arc::new(SType::SAvlTree)).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.update pub static ref UPDATE_METHOD: SMethod = @@ -347,7 +362,8 @@ lazy_static! { t_range: SType::SAvlTree.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// AvlTree.updateDigest pub static ref UPDATE_DIGEST_METHOD: SMethod = diff --git a/ergotree-ir/src/types/sbox.rs b/ergotree-ir/src/types/sbox.rs index c99b0514d..2c3ae8055 100644 --- a/ergotree-ir/src/types/sbox.rs +++ b/ergotree-ir/src/types/sbox.rs @@ -1,3 +1,4 @@ +use crate::ergo_tree::ErgoTreeVersion; use crate::serialization::types::TypeCode; use alloc::boxed::Box; use alloc::sync::Arc; @@ -46,6 +47,7 @@ lazy_static! { tpe_params: vec![], }, explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Box.value pub static ref VALUE_METHOD: SMethod = SMethod::new(STypeCompanion::Box, VALUE_METHOD_DESC.clone(),); @@ -60,7 +62,8 @@ lazy_static! { t_range: SType::SOption(Arc::new(STypeVar::t().into())).into(), tpe_params: vec![], }, - explicit_type_args: vec![STypeVar::t()] + explicit_type_args: vec![STypeVar::t()], + min_version: ErgoTreeVersion::V0 }; /// Box.getReg pub static ref GET_REG_METHOD: SMethod = @@ -81,6 +84,7 @@ lazy_static! { tpe_params: vec![], }, explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Box.tokens pub static ref TOKENS_METHOD: SMethod = diff --git a/ergotree-ir/src/types/scoll.rs b/ergotree-ir/src/types/scoll.rs index d0fad2023..e6f5a6b11 100644 --- a/ergotree-ir/src/types/scoll.rs +++ b/ergotree-ir/src/types/scoll.rs @@ -1,3 +1,4 @@ +use crate::ergo_tree::ErgoTreeVersion; use crate::serialization::types::TypeCode; use crate::types::stuple::STuple; use crate::types::stype_companion::STypeCompanion; @@ -59,7 +60,8 @@ lazy_static! { t_range: SType::SInt.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Coll.indexOf pub static ref INDEX_OF_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, INDEX_OF_METHOD_DESC.clone()); @@ -79,7 +81,8 @@ lazy_static! { ], SType::SColl(SType::STypeVar(STypeVar::ov()).into()), ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Coll.flatMap pub static ref FLATMAP_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, FLATMAP_METHOD_DESC.clone()); @@ -98,7 +101,8 @@ lazy_static! { STypeVar::t().into(), STypeVar::iv().into() )).into()) ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Coll.zip pub static ref ZIP_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, ZIP_METHOD_DESC.clone()); @@ -114,7 +118,8 @@ lazy_static! { ], SType::SColl(SType::SInt.into()) ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Coll.indices pub static ref INDICES_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, INDICES_METHOD_DESC.clone()); @@ -133,7 +138,8 @@ lazy_static! { ], SType::SColl(SType::STypeVar(STypeVar::t()).into()) ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Coll.patch pub static ref PATCH_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, PATCH_METHOD_DESC.clone()); @@ -152,7 +158,8 @@ lazy_static! { ], SType::SColl(SType::STypeVar(STypeVar::t()).into()) ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Coll.updated pub static ref UPDATED_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, UPDATED_METHOD_DESC.clone()); @@ -171,7 +178,8 @@ lazy_static! { ], SType::SColl(SType::STypeVar(STypeVar::t()).into()) ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Coll.updateMany pub static ref UPDATE_MANY_METHOD: SMethod = SMethod::new(STypeCompanion::Coll, UPDATE_MANY_METHOD_DESC.clone()); diff --git a/ergotree-ir/src/types/sglobal.rs b/ergotree-ir/src/types/sglobal.rs index 05cfbda2d..d13d31cfa 100644 --- a/ergotree-ir/src/types/sglobal.rs +++ b/ergotree-ir/src/types/sglobal.rs @@ -1,3 +1,4 @@ +use crate::ergo_tree::ErgoTreeVersion; use crate::serialization::types::TypeCode; use super::sfunc::SFunc; @@ -40,7 +41,8 @@ lazy_static! { t_range: SType::SGroupElement.into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// GLOBAL.GroupGenerator pub static ref GROUP_GENERATOR_METHOD: SMethod = SMethod::new(STypeCompanion::Global, GROUP_GENERATOR_METHOD_DESC.clone(),); @@ -60,7 +62,8 @@ lazy_static! { t_range: SType::SColl(SType::SByte.into()).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// GLOBAL.xor pub static ref XOR_METHOD: SMethod = SMethod::new(STypeCompanion::Global, XOR_METHOD_DESC.clone(),); @@ -76,7 +79,8 @@ lazy_static! { t_range: SType::STypeVar(STypeVar::t()).into(), tpe_params: vec![], }, - explicit_type_args: vec![STypeVar::t()] + explicit_type_args: vec![STypeVar::t()], + min_version: ErgoTreeVersion::V3 }; /// GLOBAL.fromBigEndianBytes pub static ref FROM_BIGENDIAN_BYTES_METHOD: SMethod = SMethod::new(STypeCompanion::Global, FROM_BIGENDIAN_BYTES_METHOD_DESC.clone(),); @@ -91,7 +95,8 @@ lazy_static! { t_range: SType::SColl(SType::SByte.into()).into(), tpe_params: vec![], }, - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V3 }; /// GLOBAL.serialize pub static ref SERIALIZE_METHOD: SMethod = SMethod::new(STypeCompanion::Global, SERIALIZE_METHOD_DESC.clone(),); diff --git a/ergotree-ir/src/types/sgroup_elem.rs b/ergotree-ir/src/types/sgroup_elem.rs index 1623c1630..a9df3150d 100644 --- a/ergotree-ir/src/types/sgroup_elem.rs +++ b/ergotree-ir/src/types/sgroup_elem.rs @@ -1,3 +1,4 @@ +use crate::ergo_tree::ErgoTreeVersion; use crate::serialization::types::TypeCode; use crate::types::stype_companion::STypeCompanion; use alloc::sync::Arc; @@ -38,7 +39,8 @@ lazy_static! { vec![SType::SGroupElement], SType::SColl(Arc::new(SType::SByte)), ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// GroupElement.geEncoded pub static ref GET_ENCODED_METHOD: SMethod = SMethod::new(STypeCompanion::GroupElem, GET_ENCODED_METHOD_DESC.clone(),); @@ -52,7 +54,8 @@ lazy_static! { vec![SType::SGroupElement], SType::SGroupElement, ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// GroupElement.negate pub static ref NEGATE_METHOD: SMethod = SMethod::new(STypeCompanion::GroupElem, NEGATE_METHOD_DESC.clone(),); diff --git a/ergotree-ir/src/types/smethod.rs b/ergotree-ir/src/types/smethod.rs index d0e59545b..a46dd267b 100644 --- a/ergotree-ir/src/types/smethod.rs +++ b/ergotree-ir/src/types/smethod.rs @@ -1,6 +1,7 @@ use alloc::vec; use alloc::vec::Vec; +use crate::ergo_tree::ErgoTreeVersion; use crate::serialization::sigma_byte_reader::SigmaByteRead; use crate::serialization::sigma_byte_writer::SigmaByteWrite; use crate::serialization::types::TypeCode; @@ -17,7 +18,7 @@ use super::type_unify::TypeUnificationError; use crate::serialization::SigmaParsingError::UnknownMethodId; /// Method id unique among the methods of the same object -#[derive(PartialEq, Eq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Copy, Clone)] pub struct MethodId(pub u8); impl MethodId { @@ -71,7 +72,7 @@ impl SMethod { /// Returns method id pub fn method_id(&self) -> MethodId { - self.method_raw.method_id.clone() + self.method_raw.method_id } /// Return new SMethod with type variables substituted @@ -105,6 +106,7 @@ pub struct SMethodDesc { pub(crate) tpe: SFunc, // Typevars that cannot be inferred from arguments. For example Box.getReg[T](4), T can not be inferred thus must be explicitly serialized pub(crate) explicit_type_args: Vec, + pub(crate) min_version: ErgoTreeVersion, } impl SMethodDesc { @@ -123,7 +125,8 @@ impl SMethodDesc { t_range: res_tpe.into(), tpe_params: vec![], }, - explicit_type_args: vec![], // TODO: check if PropertyCalls need explicit type args as well + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0, } } pub(crate) fn as_method(&self, obj_type: STypeCompanion) -> SMethod { diff --git a/ergotree-ir/src/types/soption.rs b/ergotree-ir/src/types/soption.rs index ec50c4f15..84e4d5945 100644 --- a/ergotree-ir/src/types/soption.rs +++ b/ergotree-ir/src/types/soption.rs @@ -1,3 +1,4 @@ +use crate::ergo_tree::ErgoTreeVersion; use crate::serialization::types::TypeCode; use super::sfunc::SFunc; @@ -44,7 +45,8 @@ lazy_static! { ], SType::SOption(SType::STypeVar(STypeVar::ov()).into()), ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Option.map pub static ref MAP_METHOD: SMethod = SMethod::new( @@ -66,7 +68,8 @@ lazy_static! { ], SType::SOption(SType::STypeVar(STypeVar::iv()).into()), ), - explicit_type_args: vec![] + explicit_type_args: vec![], + min_version: ErgoTreeVersion::V0 }; /// Option.map pub static ref FILTER_METHOD: SMethod = SMethod::new( From 8ed292679205ba2e1dc7df163f772cf9bb011d05 Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Thu, 26 Dec 2024 15:59:20 +0500 Subject: [PATCH 3/4] Use tree-based versioning in downcast/upcast --- ergotree-interpreter/src/eval/downcast.rs | 20 ++++++++++---------- ergotree-interpreter/src/eval/upcast.rs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ergotree-interpreter/src/eval/downcast.rs b/ergotree-interpreter/src/eval/downcast.rs index 866fc6334..a2644b731 100644 --- a/ergotree-interpreter/src/eval/downcast.rs +++ b/ergotree-interpreter/src/eval/downcast.rs @@ -18,7 +18,7 @@ fn downcast_to_bigint<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result 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::V3 => Ok(in_v), + Value::BigInt(_) if ctx.tree_version() >= ErgoTreeVersion::V3 => Ok(in_v), _ => Err(EvalError::UnexpectedValue(format!( "Downcast: cannot downcast {0:?} to BigInt", in_v @@ -32,7 +32,7 @@ fn downcast_to_long<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, 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::V3 => { + Value::BigInt(v) if ctx.tree_version() >= ErgoTreeVersion::V3 => { v.to_i64().map(Value::from).ok_or_else(|| { EvalError::UnexpectedValue( "Downcast: overflow converting BigInt to Long".to_string(), @@ -57,7 +57,7 @@ fn downcast_to_int<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, "Downcast: Int overflow".to_string(), )), }, - Value::BigInt(v) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => { + Value::BigInt(v) if ctx.tree_version() >= ErgoTreeVersion::V3 => { v.to_i32().map(Value::from).ok_or_else(|| { EvalError::UnexpectedValue( "Downcast: overflow converting BigInt to Int".to_string(), @@ -86,7 +86,7 @@ fn downcast_to_short<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result "Downcast: Short overflow".to_string(), )), }, - Value::BigInt(v) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => { + Value::BigInt(v) if ctx.tree_version() >= ErgoTreeVersion::V3 => { v.to_i16().map(Value::from).ok_or_else(|| { EvalError::UnexpectedValue( "Downcast: overflow converting BigInt to Short".to_string(), @@ -121,7 +121,7 @@ fn downcast_to_byte<'a>(in_v: Value<'a>, ctx: &Context<'_>) -> Result, "Downcast: Byte overflow".to_string(), )), }, - Value::BigInt(v) if ctx.activated_script_version() >= ErgoTreeVersion::V3 => { + Value::BigInt(v) if ctx.tree_version() >= ErgoTreeVersion::V3 => { v.to_i8().map(Value::from).ok_or_else(|| { EvalError::UnexpectedValue( "Downcast: overflow converting BigInt to Byte".to_string(), @@ -203,7 +203,7 @@ mod tests { ); let ctx = force_any_val::(); (0..ErgoTreeVersion::V3.into()) - .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SBigInt), &ctx, version, version).is_err())); + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SBigInt), &ctx, version, 1).is_err())); (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { assert_eq!( @@ -244,7 +244,7 @@ mod tests { ); let ctx = force_any_val::(); (0..ErgoTreeVersion::V3.into()) - .for_each(|version| assert!(try_eval_out_with_version::(&downcast(c_bigint.clone(), SType::SLong), &ctx, version, version).is_err())); + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(c_bigint.clone(), SType::SLong), &ctx, version, 1).is_err())); (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { let res = try_eval_out_with_version::( @@ -298,7 +298,7 @@ mod tests { .is_err()); let ctx = force_any_val::(); (0..ErgoTreeVersion::V3.into()) - .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SInt), &ctx, version, version).is_err())); + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SInt), &ctx, version, 1).is_err())); (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { let res = try_eval_out_with_version::( @@ -353,7 +353,7 @@ mod tests { assert!(try_eval_out_wo_ctx::(&downcast(c_long_oob, SType::SShort)).is_err()); let ctx = force_any_val::(); (0..ErgoTreeVersion::V3.into()) - .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SShort), &ctx, version, version).is_err())); + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SShort), &ctx, version, 1).is_err())); (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { let res = try_eval_out_with_version::( @@ -419,7 +419,7 @@ mod tests { assert!(try_eval_out_wo_ctx::(&downcast(c_long_oob, SType::SByte)).is_err()); let ctx = force_any_val::(); (0..ErgoTreeVersion::V3.into()) - .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SByte), &ctx, version, version).is_err())); + .for_each(|version| assert!(try_eval_out_with_version::(&downcast(v_bigint, SType::SByte), &ctx, version, 1).is_err())); (ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()).for_each( |version| { let res = try_eval_out_with_version::( diff --git a/ergotree-interpreter/src/eval/upcast.rs b/ergotree-interpreter/src/eval/upcast.rs index 76281c700..7a011bbe5 100644 --- a/ergotree-interpreter/src/eval/upcast.rs +++ b/ergotree-interpreter/src/eval/upcast.rs @@ -15,7 +15,7 @@ fn upcast_to_bigint<'a>(in_v: Value<'a>, ctx: &Context) -> Result, Eva 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::V3 => Ok(in_v), + Value::BigInt(_) if ctx.tree_version() >= ErgoTreeVersion::V3 => Ok(in_v), _ => Err(EvalError::UnexpectedValue(format!( "Upcast: cannot upcast {0:?} to BigInt", in_v From 5365be4f9b721f8d6e312326070b0ce56e741257 Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Fri, 27 Dec 2024 15:14:31 +0500 Subject: [PATCH 4/4] Use tree-based versioning in getReg --- ergotree-interpreter/src/eval/sbox.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ergotree-interpreter/src/eval/sbox.rs b/ergotree-interpreter/src/eval/sbox.rs index 674645fd7..c9e9d8f5d 100644 --- a/ergotree-interpreter/src/eval/sbox.rs +++ b/ergotree-interpreter/src/eval/sbox.rs @@ -18,10 +18,10 @@ pub(crate) static VALUE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| { }; pub(crate) static GET_REG_EVAL_FN: EvalFn = |mc, _env, ctx, obj, args| { - if ctx.activated_script_version() < ErgoTreeVersion::V3 { + if ctx.tree_version() < ErgoTreeVersion::V3 { return Err(EvalError::ScriptVersionError { required_version: ErgoTreeVersion::V3, - activated_version: ctx.activated_script_version(), + activated_version: ctx.tree_version(), }); } #[allow(clippy::unwrap_used)]