diff --git a/ergotree-interpreter/src/eval.rs b/ergotree-interpreter/src/eval.rs index b2db19ff7..d1a4d773c 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::DESERIALIZE_METHOD_ID => self::sglobal::DESERIALIZE_EVAL_FN, sglobal::SERIALIZE_METHOD_ID => self::sglobal::SERIALIZE_EVAL_FN, method_id => { return Err(EvalError::NotFound(format!( diff --git a/ergotree-interpreter/src/eval/sglobal.rs b/ergotree-interpreter/src/eval/sglobal.rs index f4af7d6fb..76355853c 100644 --- a/ergotree-interpreter/src/eval/sglobal.rs +++ b/ergotree-interpreter/src/eval/sglobal.rs @@ -4,10 +4,14 @@ use crate::eval::EvalError; use ergotree_ir::{ mir::{ - constant::Constant, + constant::{Constant, TryExtractInto}, value::{CollKind, NativeColl, Value}, }, - serialization::{data::DataSerializer, sigma_byte_writer::SigmaByteWriter}, + serialization::{ + data::DataSerializer, + sigma_byte_reader::{self, SigmaByteRead}, + sigma_byte_writer::SigmaByteWriter, + }, }; use super::EvalFn; @@ -147,6 +151,27 @@ pub(crate) static SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN: EvalFn = |mc, _env, _ctx } }; +pub(crate) static DESERIALIZE_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 output_type = &mc.tpe().t_range; + let bytes = args + .first() + .ok_or_else(|| EvalError::NotFound("deserialize: missing first arg".into()))? + .clone() + .try_extract_into::>()?; + let mut reader = sigma_byte_reader::from_bytes(&bytes); + Ok(Value::from( + reader.with_tree_version(ctx.tree_version(), |reader| { + DataSerializer::sigma_parse(output_type, reader) + })?, + )) +}; + pub(crate) static SERIALIZE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| { if obj != Value::Global { return Err(EvalError::UnexpectedValue(format!( @@ -181,6 +206,7 @@ mod tests { 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::mir::value::Value; use ergotree_ir::sigma_protocol::sigma_boolean::SigmaProp; use ergotree_ir::types::sgroup_elem::GET_ENCODED_METHOD; use ergotree_ir::types::stype_param::STypeVar; @@ -188,7 +214,7 @@ mod tests { 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::sglobal::{self, DESERIALIZE_METHOD, SERIALIZE_METHOD}; use ergotree_ir::types::stype::SType; use sigma_test_util::force_any_val; @@ -198,8 +224,7 @@ mod tests { Expr::Global, SERIALIZE_METHOD.clone().with_concrete_types( &[(STypeVar::t(), constant.tpe.clone())] - .iter() - .cloned() + .into_iter() .collect(), ), vec![constant.into()], @@ -213,6 +238,30 @@ mod tests { try_eval_out_with_version(&serialize_node.into(), &ctx, ErgoTreeVersion::V3.into(), 3) .unwrap() } + fn deserialize(array: &[u8], return_type: SType) -> Constant { + let type_args = [(STypeVar::t(), return_type)].into_iter().collect(); + let deserialize_node = MethodCall::with_type_args( + Expr::Global, + DESERIALIZE_METHOD.clone().with_concrete_types(&type_args), + vec![Constant::from(array.to_owned()).into()], + type_args, + ) + .unwrap(); + let ctx = force_any_val::(); + assert!((0u8..ErgoTreeVersion::V3.into()).all(|version| { + try_eval_out_with_version::>(&deserialize_node.clone().into(), &ctx, version, 3) + .is_err() + })); + try_eval_out_with_version::( + &deserialize_node.into(), + &ctx, + ErgoTreeVersion::V3.into(), + 3, + ) + .unwrap() + .try_into() + .unwrap() + } #[test] fn eval_group_generator() { @@ -424,11 +473,41 @@ mod tests { ); } + #[test] + fn deserialize_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(); + let encoded = eval_out_wo_ctx::>(&get_encoded.into()); + assert_eq!( + deserialize(&encoded, SType::SGroupElement), + Constant::from(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..]) } + #[test] + fn serialize_roundtrip(v in any::()) { + let tpe = v.tpe.clone(); + let res = std::panic::catch_unwind(|| assert_eq!(deserialize(&serialize(v.clone()), tpe.clone()), v)); + if matches!(tpe, SType::SOption(_)) { + assert!(res.is_err()); + } + else { + res.unwrap(); + } + } } } diff --git a/ergotree-ir/src/types/sglobal.rs b/ergotree-ir/src/types/sglobal.rs index d13d31cfa..06f81c263 100644 --- a/ergotree-ir/src/types/sglobal.rs +++ b/ergotree-ir/src/types/sglobal.rs @@ -8,6 +8,7 @@ use super::stype::SType; use crate::types::smethod::SMethod; use crate::types::stype_companion::STypeCompanion; use crate::types::stype_param::STypeVar; +use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; use lazy_static::lazy_static; @@ -21,15 +22,17 @@ pub static TYPE_NAME: &str = "Global"; pub const GROUP_GENERATOR_METHOD_ID: MethodId = MethodId(1); /// "xor" predefined function 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); +/// "fromBigEndianBytes" predefined function +pub const DESERIALIZE_METHOD_ID: MethodId = MethodId(4); +/// "fromBigEndianBytes" predefined function +pub const FROM_BIGENDIAN_BYTES_METHOD_ID: MethodId = MethodId(5); lazy_static! { /// Global method descriptors pub(crate) static ref METHOD_DESC: Vec<&'static SMethodDesc> = - vec![&GROUP_GENERATOR_METHOD_DESC, &XOR_METHOD_DESC, &SERIALIZE_METHOD_DESC, &FROM_BIGENDIAN_BYTES_METHOD_DESC]; + vec![&GROUP_GENERATOR_METHOD_DESC, &XOR_METHOD_DESC, &DESERIALIZE_METHOD_DESC, &SERIALIZE_METHOD_DESC, &FROM_BIGENDIAN_BYTES_METHOD_DESC]; } lazy_static! { @@ -84,6 +87,24 @@ lazy_static! { }; /// GLOBAL.fromBigEndianBytes pub static ref FROM_BIGENDIAN_BYTES_METHOD: SMethod = SMethod::new(STypeCompanion::Global, FROM_BIGENDIAN_BYTES_METHOD_DESC.clone(),); + + static ref DESERIALIZE_METHOD_DESC: SMethodDesc = SMethodDesc { + method_id: DESERIALIZE_METHOD_ID, + name: "deserialize", + tpe: SFunc { + t_dom: vec![ + SType::SGlobal, + SType::SColl(SType::SByte.into()) + ], + t_range: Box::new(STypeVar::t().into()), + tpe_params: vec![], + }, + explicit_type_args: vec![STypeVar::t()], + min_version: ErgoTreeVersion::V3 + }; + /// GLOBAL.deserialize + pub static ref DESERIALIZE_METHOD: SMethod = SMethod::new(STypeCompanion::Global, DESERIALIZE_METHOD_DESC.clone(),); + static ref SERIALIZE_METHOD_DESC: SMethodDesc = SMethodDesc { method_id: SERIALIZE_METHOD_ID, name: "serialize", @@ -98,6 +119,34 @@ lazy_static! { explicit_type_args: vec![], min_version: ErgoTreeVersion::V3 }; - /// GLOBAL.serialize + /// GLOBAL.serialize pub static ref SERIALIZE_METHOD: SMethod = SMethod::new(STypeCompanion::Global, SERIALIZE_METHOD_DESC.clone(),); } + +#[cfg(test)] +#[cfg(feature = "arbitrary")] +#[allow(clippy::unwrap_used)] +mod test { + use proptest::prelude::*; + + use crate::{ + mir::{expr::Expr, method_call::MethodCall}, + serialization::SigmaSerializable, + types::{stype::SType, stype_param::STypeVar}, + }; + + use super::DESERIALIZE_METHOD; + proptest! { + #[test] + fn test_deserialize_method_roundtrip(v in any::()) { + let type_args = core::iter::once((STypeVar::t(), v)).collect(); + let mc = MethodCall::with_type_args( + Expr::Global, + DESERIALIZE_METHOD.clone().with_concrete_types(&type_args), + vec![vec![0i8].into()], + type_args, + ).unwrap(); + assert_eq!(MethodCall::sigma_parse_bytes(&mc.sigma_serialize_bytes().unwrap()).unwrap(), mc); + } + } +}