Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global frombytearray #795

Merged
merged 4 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ergotree-interpreter/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ fn smethod_eval_fn(method: &SMethod) -> Result<EvalFn, EvalError> {
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",
Expand Down
220 changes: 217 additions & 3 deletions ergotree-interpreter/src/eval/sglobal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use crate::eval::EvalError;

use ergotree_ir::mir::value::{CollKind, NativeColl, Value};

use ergo_chain_types::ec_point::generator;

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;

fn helper_xor(x: &[i8], y: &[i8]) -> Arc<[i8]> {
x.iter().zip(y.iter()).map(|(x1, x2)| *x1 ^ *x2).collect()
Expand Down Expand Up @@ -53,18 +55,107 @@ 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 value = bytes
.iter()
.fold(0i16, |acc, &x| (acc << 8) | (x as u8 as i16));
Ok(Value::Short(value))
}
SType::SInt => {
if bytes.len() != 4 {
return Err(EvalError::UnexpectedValue(
"To deserialize Int with fromBigEndianBytes, exactly four bytes should be provided".to_string(),
));
}
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 {
return Err(EvalError::UnexpectedValue(
"To deserialize Long with fromBigEndianBytes, exactly eight bytes should be provided".to_string(),
));
}
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 {
return Err(EvalError::UnexpectedValue(
"BigInt value doesn't fit into 32 bytes in fromBigEndianBytes".to_string(),
));
}
let bytes_vec: Vec<u8> = bytes.iter().map(|&x| x as u8).collect();
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
))),
}
};

#[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;

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;
use ergotree_ir::types::stype_param::STypeVar;
use sigma_test_util::force_any_val;

#[test]
Expand Down Expand Up @@ -98,4 +189,127 @@ mod tests {
expected_xor.as_slice()
);
}

use proptest::prelude::*;

proptest! {
#![proptest_config(ProptestConfig::with_cases(64))]

#[test]
fn test_bigendian_bytes_roundtrip(
v_byte in any::<i8>(),
v_short in any::<i16>(),
v_int in any::<i32>(),
v_long in any::<i64>()
) {
{
let bytes = vec![v_byte];

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::<i8>(&expr), v_byte);
}

{
let bytes = vec![(v_short >> 8) as i8, v_short as i8];

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::<i16>(&expr), v_short);
}

{
let bytes = vec![
(v_int >> 24) as i8,
(v_int >> 16) as i8,
(v_int >> 8) as i8,
v_int as i8
];

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::<i32>(&expr), v_int);
}

{
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::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::<i64>(&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::<i64>()) {
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();
assert_eq!(eval_out_wo_ctx::<BigInt256>(&expr), BigInt256::from(v_long));
}
}
}
20 changes: 19 additions & 1 deletion ergotree-ir/src/types/sglobal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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;
Expand All @@ -19,11 +20,13 @@ 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
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! {
Expand Down Expand Up @@ -61,3 +64,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![],
},
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(),);
}
Loading