From faf6349af680160c29cd79604293e275c384289f Mon Sep 17 00:00:00 2001 From: sebastianrof Date: Sun, 15 Dec 2024 19:54:15 +0330 Subject: [PATCH 1/2] adding the global.frombytearraybigendian --- ergotree-interpreter/src/eval.rs | 1 + ergotree-interpreter/src/eval/sglobal.rs | 179 ++++++++++++++++++++++- ergotree-ir/src/types/sglobal.rs | 18 +++ ergotree-ir/src/types/stype_param.rs | 11 ++ 4 files changed, 208 insertions(+), 1 deletion(-) diff --git a/ergotree-interpreter/src/eval.rs b/ergotree-interpreter/src/eval.rs index d043ebdee..d2b0789d3 100644 --- a/ergotree-interpreter/src/eval.rs +++ b/ergotree-interpreter/src/eval.rs @@ -340,6 +340,7 @@ fn smethod_eval_fn(method: &SMethod) -> Result { sglobal::TYPE_CODE => match method.method_id() { sglobal::GROUP_GENERATOR_METHOD_ID => self::sglobal::GROUP_GENERATOR_EVAL_FN, sglobal::XOR_METHOD_ID => self::sglobal::XOR_EVAL_FN, + sglobal::FROM_BIGENDIAN_BYTES_METHOD_ID => self::sglobal::SGLOBAL_FROM_BIGENDIAN_BYTES_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 e01508659..a60a08b41 100644 --- a/ergotree-interpreter/src/eval/sglobal.rs +++ b/ergotree-interpreter/src/eval/sglobal.rs @@ -5,7 +5,8 @@ use crate::eval::EvalError; use ergotree_ir::mir::value::{CollKind, NativeColl, Value}; use ergo_chain_types::ec_point::generator; - +use ergotree_ir::bigint256::BigInt256; +use ergotree_ir::types::stype::SType; use super::EvalFn; fn helper_xor(x: &[i8], y: &[i8]) -> Arc<[i8]> { @@ -53,11 +54,103 @@ pub(crate) static XOR_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| { } }; +pub(crate) static SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN: EvalFn = |mc, _env, _ctx, obj, args| { + if obj != Value::Global { + return Err(EvalError::UnexpectedValue(format!( + "sglobal.fromBigEndianBytes expected obj to be Value::Global, got {:?}", + obj + ))); + } + + let bytes_val = args + .first() + .cloned() + .ok_or_else(|| EvalError::NotFound("fromBigEndianBytes: missing bytes arg".to_string()))?; + let type_val = mc.tpe().t_range.clone(); + + let bytes = match bytes_val { + Value::Coll(CollKind::NativeColl(NativeColl::CollByte(bytes))) => bytes, + _ => return Err(EvalError::UnexpectedValue(format!( + "fromBigEndianBytes: expected first argument to be byte array, got {:?}", + bytes_val + ))), + }; + + match *type_val { + SType::SByte => { + if bytes.len() != 1 { + return Err(EvalError::UnexpectedValue( + "To deserialize Byte with fromBigEndianBytes, exactly one byte should be provided".to_string(), + )); + } + Ok(Value::Byte(bytes[0])) + } + SType::SShort => { + if bytes.len() != 2 { + return Err(EvalError::UnexpectedValue( + "To deserialize Short with fromBigEndianBytes, exactly two bytes should be provided".to_string(), + )); + } + let b0 = bytes[0] as i16; + let b1 = bytes[1] as i16; + Ok(Value::Short(((b0 & 0xFF) << 8 | (b1 & 0xFF)) as i16)) + } + SType::SInt => { + if bytes.len() != 4 { + return Err(EvalError::UnexpectedValue( + "To deserialize Int with fromBigEndianBytes, exactly four bytes should be provided".to_string(), + )); + } + let bytes_vec: Vec = bytes.iter().map(|&x| x as u8).collect(); + let int_bytes: [u8; 4] = bytes_vec.try_into().map_err(|_| EvalError::UnexpectedValue( + "Invalid byte array length for Int".to_string(), + ))?; + Ok(Value::Int(i32::from_be_bytes(int_bytes))) + } + SType::SLong => { + if bytes.len() != 8 { + return Err(EvalError::UnexpectedValue( + "To deserialize Long with fromBigEndianBytes, exactly eight bytes should be provided".to_string(), + )); + } + let bytes_vec: Vec = bytes.iter().map(|&x| x as u8).collect(); + let long_bytes: [u8; 8] = bytes_vec.try_into().map_err(|_| EvalError::UnexpectedValue( + "Invalid byte array length for Long".to_string(), + ))?; + Ok(Value::Long(i64::from_be_bytes(long_bytes))) + } + SType::SBigInt => { + if bytes.len() > 32 { + return Err(EvalError::UnexpectedValue( + "BigInt value doesn't fit into 32 bytes in fromBigEndianBytes".to_string(), + )); + } + let bytes_vec: Vec = bytes.iter().map(|&x| x as u8).collect(); + let big_int = num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes_vec); + Ok(Value::BigInt(BigInt256::try_from(big_int).map_err(|e| + EvalError::UnexpectedValue(format!("Failed to convert to BigInt256: {:?}", e)) + )?)) + } + SType::SUnit => { + if !bytes.is_empty() { + return Err(EvalError::UnexpectedValue( + "To deserialize Unit with fromBigEndianBytes, empty byte array should be provided".to_string(), + )); + } + Ok(Value::Unit) + } + _ => Err(EvalError::UnexpectedValue(format!( + "Unsupported type provided in fromBigEndianBytes: {:?}", type_val + ))), + } +}; + #[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::expr::Expr; use ergotree_ir::mir::method_call::MethodCall; use ergotree_ir::mir::property_call::PropertyCall; @@ -65,6 +158,8 @@ mod tests { use crate::eval::tests::eval_out; use ergotree_ir::chain::context::Context; use ergotree_ir::types::sglobal; + use ergotree_ir::types::stype::SType; + use ergotree_ir::types::stype_param::STypeVar; use sigma_test_util::force_any_val; #[test] @@ -98,4 +193,86 @@ mod tests { expected_xor.as_slice() ); } + + #[test] + fn eval_from_bigendian_bytes() { + let type_args = std::iter::once((STypeVar::t(), SType::SInt)).collect(); + + let bytes = vec![0_i8, 0, 0, 1]; + let expr: Expr = MethodCall::new( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + ) + .unwrap() + .into(); + let ctx = force_any_val::(); + assert_eq!(eval_out::(&expr, &ctx), 1); + } + + #[test] + fn eval_from_bigendian_bytes_short() { + let type_args = std::iter::once((STypeVar::t(), SType::SShort)).collect(); + + let bytes = vec![0_i8, 1]; + let expr: Expr = MethodCall::new( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + ) + .unwrap() + .into(); + let ctx = force_any_val::(); + assert_eq!(eval_out::(&expr, &ctx), 1); + } + + #[test] + fn eval_from_bigendian_bytes_long() { + let type_args = std::iter::once((STypeVar::t(), SType::SLong)).collect(); + + let bytes = vec![0_i8, 0, 0, 0, 0, 0, 0, 1]; + let expr: Expr = MethodCall::new( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + ) + .unwrap() + .into(); + let ctx = force_any_val::(); + assert_eq!(eval_out::(&expr, &ctx), 1); + } + + #[test] + fn eval_from_bigendian_bytes_wrong_answer() { + let type_args = std::iter::once((STypeVar::t(), SType::SInt)).collect(); + + let bytes = vec![0_i8, 0, 0, 1]; + let expr: Expr = MethodCall::new( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + ) + .unwrap() + .into(); + let ctx = force_any_val::(); + assert_ne!(eval_out::(&expr, &ctx), 2); + } + + #[test] + fn eval_from_bigendian_bytes_bigint() { + let type_args = std::iter::once((STypeVar::t(), SType::SBigInt)).collect(); + + let bytes = vec![0_i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; + let expr: Expr = MethodCall::new( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + ) + .unwrap() + .into(); + let ctx = force_any_val::(); + let expected_bigint = num_bigint::BigInt::from(1); + assert_eq!(eval_out::(&expr, &ctx), BigInt256::try_from(expected_bigint).unwrap()); + } + } diff --git a/ergotree-ir/src/types/sglobal.rs b/ergotree-ir/src/types/sglobal.rs index 9e90c2e75..4f9b015ec 100644 --- a/ergotree-ir/src/types/sglobal.rs +++ b/ergotree-ir/src/types/sglobal.rs @@ -7,6 +7,7 @@ use super::stype::SType; use crate::types::smethod::SMethod; use crate::types::stype_companion::STypeCompanion; use lazy_static::lazy_static; +use crate::types::stype_param::{STypeParam, STypeVar}; /// SGlobal type code pub const TYPE_CODE: TypeCode = TypeCode::SGLOBAL; @@ -17,6 +18,8 @@ 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); lazy_static! { /// Global method descriptors @@ -59,3 +62,18 @@ lazy_static! { pub static ref XOR_METHOD: SMethod = SMethod::new(STypeCompanion::Global, XOR_METHOD_DESC.clone(),); } + +lazy_static!{ + static ref FROM_BIGENDIAN_BYTES_METHOD_DESC: SMethodDesc = SMethodDesc { + method_id: FROM_BIGENDIAN_BYTES_METHOD_ID, + name: "fromBigEndianBytes", + tpe: SFunc { + t_dom: vec![SType::SGlobal, SType::SColl(SType::SByte.into())], + t_range:SType::STypeVar(STypeVar::t()).into(), + tpe_params: vec![STypeParam::param_t()], + }, + explicit_type_args: vec![] + }; + /// GLOBAL.fromBigEndianBytes + pub static ref FROM_BIGENDIAN_BYTES_METHOD: SMethod = SMethod::new(STypeCompanion::Global, FROM_BIGENDIAN_BYTES_METHOD_DESC.clone(),); +} diff --git a/ergotree-ir/src/types/stype_param.rs b/ergotree-ir/src/types/stype_param.rs index 39e7c2f59..40c3d1a87 100644 --- a/ergotree-ir/src/types/stype_param.rs +++ b/ergotree-ir/src/types/stype_param.rs @@ -96,3 +96,14 @@ pub struct STypeParam { upper_bound: Option, lower_bound: Option, } + +impl STypeParam { + /// paramT + pub fn param_t() -> Self { + Self { + ident: STypeVar::t(), + upper_bound: None, + lower_bound: None, + } + } +} From c1022e7846b19db9d59f01058fec16f5e381e5a7 Mon Sep 17 00:00:00 2001 From: sebastianrof Date: Wed, 18 Dec 2024 03:53:20 -0500 Subject: [PATCH 2/2] Fixing the issues --- ergotree-interpreter/src/eval.rs | 4 +- ergotree-interpreter/src/eval/sglobal.rs | 239 +++++++++++++---------- ergotree-ir/src/types/sglobal.rs | 10 +- ergotree-ir/src/types/stype_param.rs | 11 -- 4 files changed, 146 insertions(+), 118 deletions(-) diff --git a/ergotree-interpreter/src/eval.rs b/ergotree-interpreter/src/eval.rs index ba646f462..ddce59719 100644 --- a/ergotree-interpreter/src/eval.rs +++ b/ergotree-interpreter/src/eval.rs @@ -342,7 +342,9 @@ fn smethod_eval_fn(method: &SMethod) -> Result { sglobal::TYPE_CODE => match method.method_id() { sglobal::GROUP_GENERATOR_METHOD_ID => self::sglobal::GROUP_GENERATOR_EVAL_FN, sglobal::XOR_METHOD_ID => self::sglobal::XOR_EVAL_FN, - sglobal::FROM_BIGENDIAN_BYTES_METHOD_ID => self::sglobal::SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN, + sglobal::FROM_BIGENDIAN_BYTES_METHOD_ID => { + self::sglobal::SGLOBAL_FROM_BIGENDIAN_BYTES_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 b7677ebf0..d449fc2a2 100644 --- a/ergotree-interpreter/src/eval/sglobal.rs +++ b/ergotree-interpreter/src/eval/sglobal.rs @@ -4,10 +4,11 @@ use crate::eval::EvalError; use ergotree_ir::mir::value::{CollKind, NativeColl, Value}; +use super::EvalFn; +use crate::eval::Vec; use ergo_chain_types::ec_point::generator; use ergotree_ir::bigint256::BigInt256; use ergotree_ir::types::stype::SType; -use super::EvalFn; fn helper_xor(x: &[i8], y: &[i8]) -> Arc<[i8]> { x.iter().zip(y.iter()).map(|(x1, x2)| *x1 ^ *x2).collect() @@ -70,10 +71,12 @@ pub(crate) static SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN: EvalFn = |mc, _env, _ctx let bytes = match bytes_val { Value::Coll(CollKind::NativeColl(NativeColl::CollByte(bytes))) => bytes, - _ => return Err(EvalError::UnexpectedValue(format!( - "fromBigEndianBytes: expected first argument to be byte array, got {:?}", - bytes_val - ))), + _ => { + return Err(EvalError::UnexpectedValue(format!( + "fromBigEndianBytes: expected first argument to be byte array, got {:?}", + bytes_val + ))) + } }; match *type_val { @@ -91,9 +94,10 @@ pub(crate) static SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN: EvalFn = |mc, _env, _ctx "To deserialize Short with fromBigEndianBytes, exactly two bytes should be provided".to_string(), )); } - let b0 = bytes[0] as i16; - let b1 = bytes[1] as i16; - Ok(Value::Short(((b0 & 0xFF) << 8 | (b1 & 0xFF)) as i16)) + let value = bytes + .iter() + .fold(0i16, |acc, &x| (acc << 8) | (x as u8 as i16)); + Ok(Value::Short(value)) } SType::SInt => { if bytes.len() != 4 { @@ -101,11 +105,10 @@ pub(crate) static SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN: EvalFn = |mc, _env, _ctx "To deserialize Int with fromBigEndianBytes, exactly four bytes should be provided".to_string(), )); } - let bytes_vec: Vec = bytes.iter().map(|&x| x as u8).collect(); - let int_bytes: [u8; 4] = bytes_vec.try_into().map_err(|_| EvalError::UnexpectedValue( - "Invalid byte array length for Int".to_string(), - ))?; - Ok(Value::Int(i32::from_be_bytes(int_bytes))) + let value = bytes + .iter() + .fold(0i32, |acc, &x| (acc << 8) | (x as u8 as i32)); + Ok(Value::Int(value)) } SType::SLong => { if bytes.len() != 8 { @@ -113,11 +116,10 @@ pub(crate) static SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN: EvalFn = |mc, _env, _ctx "To deserialize Long with fromBigEndianBytes, exactly eight bytes should be provided".to_string(), )); } - let bytes_vec: Vec = bytes.iter().map(|&x| x as u8).collect(); - let long_bytes: [u8; 8] = bytes_vec.try_into().map_err(|_| EvalError::UnexpectedValue( - "Invalid byte array length for Long".to_string(), - ))?; - Ok(Value::Long(i64::from_be_bytes(long_bytes))) + let value = bytes + .iter() + .fold(0i64, |acc, &x| (acc << 8) | (x as u8 as i64)); + Ok(Value::Long(value)) } SType::SBigInt => { if bytes.len() > 32 { @@ -126,21 +128,15 @@ pub(crate) static SGLOBAL_FROM_BIGENDIAN_BYTES_EVAL_FN: EvalFn = |mc, _env, _ctx )); } let bytes_vec: Vec = bytes.iter().map(|&x| x as u8).collect(); - let big_int = num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes_vec); - Ok(Value::BigInt(BigInt256::try_from(big_int).map_err(|e| - EvalError::UnexpectedValue(format!("Failed to convert to BigInt256: {:?}", e)) - )?)) - } - SType::SUnit => { - if !bytes.is_empty() { - return Err(EvalError::UnexpectedValue( - "To deserialize Unit with fromBigEndianBytes, empty byte array should be provided".to_string(), - )); - } - Ok(Value::Unit) + Ok(Value::BigInt( + BigInt256::from_be_slice(&bytes_vec).ok_or_else(|| { + EvalError::UnexpectedValue("Failed to convert to BigInt256".to_string()) + })?, + )) } _ => Err(EvalError::UnexpectedValue(format!( - "Unsupported type provided in fromBigEndianBytes: {:?}", type_val + "Unsupported type provided in fromBigEndianBytes: {:?}", + type_val ))), } }; @@ -155,7 +151,7 @@ mod tests { use ergotree_ir::mir::method_call::MethodCall; use ergotree_ir::mir::property_call::PropertyCall; - use crate::eval::tests::eval_out; + 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::stype::SType; @@ -194,85 +190,126 @@ mod tests { ); } - #[test] - fn eval_from_bigendian_bytes() { - let type_args = std::iter::once((STypeVar::t(), SType::SInt)).collect(); + use proptest::prelude::*; - let bytes = vec![0_i8, 0, 0, 1]; - let expr: Expr = MethodCall::new( - Expr::Global, - sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), - vec![bytes.into()], - ) - .unwrap() - .into(); - let ctx = force_any_val::(); - assert_eq!(eval_out::(&expr, &ctx), 1); - } + proptest! { + #![proptest_config(ProptestConfig::with_cases(64))] - #[test] - fn eval_from_bigendian_bytes_short() { - let type_args = std::iter::once((STypeVar::t(), SType::SShort)).collect(); + #[test] + fn test_bigendian_bytes_roundtrip( + v_byte in any::(), + v_short in any::(), + v_int in any::(), + v_long in any::() + ) { + { + let bytes = vec![v_byte]; - let bytes = vec![0_i8, 1]; - let expr: Expr = MethodCall::new( - Expr::Global, - sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), - vec![bytes.into()], - ) - .unwrap() - .into(); - let ctx = force_any_val::(); - assert_eq!(eval_out::(&expr, &ctx), 1); - } + let type_args = std::iter::once((STypeVar::t(), SType::SByte)).collect(); + let expr: Expr = MethodCall::with_type_args( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + type_args, + ) + .unwrap() + .into(); + assert_eq!(eval_out_wo_ctx::(&expr), v_byte); + } - #[test] - fn eval_from_bigendian_bytes_long() { - let type_args = std::iter::once((STypeVar::t(), SType::SLong)).collect(); + { + let bytes = vec![(v_short >> 8) as i8, v_short as i8]; - let bytes = vec![0_i8, 0, 0, 0, 0, 0, 0, 1]; - let expr: Expr = MethodCall::new( - Expr::Global, - sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), - vec![bytes.into()], - ) - .unwrap() - .into(); - let ctx = force_any_val::(); - assert_eq!(eval_out::(&expr, &ctx), 1); - } + let type_args = std::iter::once((STypeVar::t(), SType::SShort)).collect(); + let expr: Expr = MethodCall::with_type_args( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + type_args, + ) + .unwrap() + .into(); + assert_eq!(eval_out_wo_ctx::(&expr), v_short); + } - #[test] - fn eval_from_bigendian_bytes_wrong_answer() { - let type_args = std::iter::once((STypeVar::t(), SType::SInt)).collect(); + { + let bytes = vec![ + (v_int >> 24) as i8, + (v_int >> 16) as i8, + (v_int >> 8) as i8, + v_int as i8 + ]; - let bytes = vec![0_i8, 0, 0, 1]; - let expr: Expr = MethodCall::new( - Expr::Global, - sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), - vec![bytes.into()], - ) - .unwrap() - .into(); - let ctx = force_any_val::(); - assert_ne!(eval_out::(&expr, &ctx), 2); - } + let type_args = std::iter::once((STypeVar::t(), SType::SInt)).collect(); + let expr: Expr = MethodCall::with_type_args( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + type_args, + ) + .unwrap() + .into(); + assert_eq!(eval_out_wo_ctx::(&expr), v_int); + } - #[test] - fn eval_from_bigendian_bytes_bigint() { - let type_args = std::iter::once((STypeVar::t(), SType::SBigInt)).collect(); + { + let bytes = vec![ + (v_long >> 56) as i8, + (v_long >> 48) as i8, + (v_long >> 40) as i8, + (v_long >> 32) as i8, + (v_long >> 24) as i8, + (v_long >> 16) as i8, + (v_long >> 8) as i8, + v_long as i8 + ]; - let bytes = vec![0_i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; - let expr: Expr = MethodCall::new( - Expr::Global, - sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), - vec![bytes.into()], - ) + let type_args = std::iter::once((STypeVar::t(), SType::SLong)).collect(); + let expr: Expr = MethodCall::with_type_args( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.clone().into()], + type_args, + ) + .unwrap() + .into(); + assert_eq!(eval_out_wo_ctx::(&expr), v_long); + + let original_long = ((bytes[0] as i64) << 56) | + (((bytes[1] as i64) & 0xFF) << 48) | + (((bytes[2] as i64) & 0xFF) << 40) | + (((bytes[3] as i64) & 0xFF) << 32) | + (((bytes[4] as i64) & 0xFF) << 24) | + (((bytes[5] as i64) & 0xFF) << 16) | + (((bytes[6] as i64) & 0xFF) << 8) | + ((bytes[7] as i64) & 0xFF); + assert_eq!(original_long, v_long); + } + } + + #[test] + fn test_bigint_roundtrip(v_long in any::()) { + let bytes = vec![ + (v_long >> 56) as i8, + (v_long >> 48) as i8, + (v_long >> 40) as i8, + (v_long >> 32) as i8, + (v_long >> 24) as i8, + (v_long >> 16) as i8, + (v_long >> 8) as i8, + v_long as i8 + ]; + + let type_args = std::iter::once((STypeVar::t(), SType::SBigInt)).collect(); + let expr: Expr = MethodCall::with_type_args( + Expr::Global, + sglobal::FROM_BIGENDIAN_BYTES_METHOD.clone().with_concrete_types(&type_args), + vec![bytes.into()], + type_args, + ) .unwrap() .into(); - let ctx = force_any_val::(); - let expected_bigint = num_bigint::BigInt::from(1); - assert_eq!(eval_out::(&expr, &ctx), BigInt256::try_from(expected_bigint).unwrap()); + assert_eq!(eval_out_wo_ctx::(&expr), BigInt256::from(v_long)); + } } - } diff --git a/ergotree-ir/src/types/sglobal.rs b/ergotree-ir/src/types/sglobal.rs index 9dd59cc9e..e4ef726e8 100644 --- a/ergotree-ir/src/types/sglobal.rs +++ b/ergotree-ir/src/types/sglobal.rs @@ -6,10 +6,10 @@ use super::smethod::SMethodDesc; use super::stype::SType; use crate::types::smethod::SMethod; use crate::types::stype_companion::STypeCompanion; +use crate::types::stype_param::STypeVar; use alloc::vec; use alloc::vec::Vec; use lazy_static::lazy_static; -use crate::types::stype_param::{STypeParam, STypeVar}; /// SGlobal type code pub const TYPE_CODE: TypeCode = TypeCode::SGLOBAL; @@ -26,7 +26,7 @@ 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,]; + vec![&GROUP_GENERATOR_METHOD_DESC, &XOR_METHOD_DESC, &FROM_BIGENDIAN_BYTES_METHOD_DESC]; } lazy_static! { @@ -65,16 +65,16 @@ lazy_static! { } -lazy_static!{ +lazy_static! { static ref FROM_BIGENDIAN_BYTES_METHOD_DESC: SMethodDesc = SMethodDesc { method_id: FROM_BIGENDIAN_BYTES_METHOD_ID, name: "fromBigEndianBytes", tpe: SFunc { t_dom: vec![SType::SGlobal, SType::SColl(SType::SByte.into())], t_range:SType::STypeVar(STypeVar::t()).into(), - tpe_params: vec![STypeParam::param_t()], + tpe_params: vec![], }, - explicit_type_args: 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(),); diff --git a/ergotree-ir/src/types/stype_param.rs b/ergotree-ir/src/types/stype_param.rs index cd24ea4e1..ae760caf1 100644 --- a/ergotree-ir/src/types/stype_param.rs +++ b/ergotree-ir/src/types/stype_param.rs @@ -100,14 +100,3 @@ pub struct STypeParam { upper_bound: Option, lower_bound: Option, } - -impl STypeParam { - /// paramT - pub fn param_t() -> Self { - Self { - ident: STypeVar::t(), - upper_bound: None, - lower_bound: None, - } - } -}