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

[6.0] Box.getReg #773

Merged
merged 5 commits into from
Oct 2, 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
17 changes: 17 additions & 0 deletions ergotree-interpreter/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ pub(crate) trait Evaluable {
}

type EvalFn = for<'ctx> fn(
mc: &SMethod,
env: &mut Env<'ctx>,
ctx: &Context<'ctx>,
Value<'ctx>,
Expand Down Expand Up @@ -402,6 +403,22 @@ pub(crate) mod tests {
})
}

// Evaluate with activated version (set block version to version + 1)
pub fn try_eval_out_with_version<'ctx, T: TryExtractFrom<Value<'static>> + 'static>(
expr: &Expr,
ctx: &'ctx Context<'ctx>,
version: u8,
) -> Result<T, EvalError> {
let mut ctx = ctx.clone();
ctx.pre_header.version = version + 1;
let mut env = Env::empty();
expr.eval(&mut env, &ctx).and_then(|v| {
v.to_static()
.try_extract_into::<T>()
.map_err(EvalError::TryExtractFrom)
})
}

pub fn try_eval_out_wo_ctx<T: TryExtractFrom<Value<'static>> + 'static>(
expr: &Expr,
) -> Result<T, EvalError> {
Expand Down
6 changes: 5 additions & 1 deletion ergotree-interpreter/src/eval/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ergotree_ir::chain::ergo_box::ErgoBox;
pub type TxIoVec<T> = BoundedVec<T, 1, { i16::MAX as usize }>;

/// Interpreter's context (blockchain state)
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Context<'ctx> {
/// Current height
pub height: u32,
Expand Down Expand Up @@ -35,6 +35,10 @@ impl<'ctx> Context<'ctx> {
..self
}
}
/// Activated script version corresponds to block version - 1
pub fn activated_script_version(&self) -> u8 {
self.pre_header.version.saturating_sub(1)
}
}

#[cfg(feature = "arbitrary")]
Expand Down
14 changes: 11 additions & 3 deletions ergotree-interpreter/src/eval/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,25 @@ pub enum EvalError {
/// Wrapped error with source span
#[error("eval error: {0:?}")]
Spanned(SpannedEvalError),
/// Script version error
#[error("Method requires at least version {required_version}, but activated version is {activated_version}")]
ScriptVersionError {
/// Opcode/method call requires this version
required_version: u8,
/// Currently activated script version on network
activated_version: u8,
},
}

/// Wrapped error with source span
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct SpannedEvalError {
/// eval error
error: Box<EvalError>,
pub error: Box<EvalError>,
/// source span for the expression where error occurred
source_span: SourceSpan,
pub source_span: SourceSpan,
/// environment at the time when error occurred
env: Env<'static>,
pub env: Env<'static>,
}

/// Wrapped error with source span and source code
Expand Down
27 changes: 25 additions & 2 deletions ergotree-interpreter/src/eval/extract_reg_as.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,16 @@ impl Evaluable for ExtractRegisterAs {
"Error getting the register id {id} with error {e:?}"
))
})?;
Ok(Value::Opt(Box::new(reg_val_opt.map(|c| Value::from(c.v)))))
match reg_val_opt {
Some(constant) if constant.tpe == *self.elem_tpe => {
Ok(Value::Opt(Box::new(Some(constant.v.into()))))
}
Some(constant) => Err(EvalError::UnexpectedValue(format!(
"Expected register {id} to be of type {}, got {}",
self.elem_tpe, constant.tpe
))),
None => Ok(Value::Opt(Box::new(None))),
}
}
}

Expand All @@ -41,7 +50,7 @@ impl Evaluable for ExtractRegisterAs {
mod tests {
use super::*;
use crate::eval::context::Context;
use crate::eval::tests::eval_out;
use crate::eval::tests::{eval_out, try_eval_out};
use ergotree_ir::mir::expr::Expr;
use ergotree_ir::mir::global_vars::GlobalVars;
use ergotree_ir::mir::option_get::OptionGet;
Expand All @@ -63,4 +72,18 @@ mod tests {
let v = eval_out::<i64>(&option_get_expr, &ctx);
assert_eq!(v, ctx.self_box.value.as_i64());
}

#[test]
fn eval_box_get_reg_r0_wrong_type() {
let get_reg_expr: Expr = ExtractRegisterAs::new(
GlobalVars::SelfBox.into(),
0,
SType::SOption(SType::SInt.into()), // R0 (value) is long, but we're expecting int
)
.unwrap()
.into();
let option_get_expr: Expr = OptionGet::try_build(get_reg_expr).unwrap().into();
let ctx = force_any_val::<Context>();
assert!(try_eval_out::<Value>(&option_get_expr, &ctx).is_err());
}
}
37 changes: 1 addition & 36 deletions ergotree-interpreter/src/eval/method_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,6 @@ impl Evaluable for MethodCall {
let ov = self.obj.eval(env, ectx)?;
let argsv: Result<Vec<Value>, EvalError> =
self.args.iter().map(|arg| arg.eval(env, ectx)).collect();
smethod_eval_fn(&self.method)?(env, ectx, ov, argsv?)
}
}

#[allow(clippy::unwrap_used)]
#[cfg(test)]
#[cfg(feature = "arbitrary")]
mod tests {
use ergotree_ir::mir::constant::Constant;
use ergotree_ir::mir::expr::Expr;
use ergotree_ir::mir::global_vars::GlobalVars;
use ergotree_ir::mir::option_get::OptionGet;
use ergotree_ir::mir::unary_op::OneArgOpTryBuild;
use ergotree_ir::types::sbox;
use sigma_test_util::force_any_val;

use crate::eval::context::Context;
use crate::eval::tests::eval_out;

use super::*;

#[test]
fn eval_box_get_reg() {
let mc: Expr = MethodCall::new(
GlobalVars::SelfBox.into(),
sbox::GET_REG_METHOD.clone(),
vec![Constant::from(0i8).into()],
)
.unwrap()
.into();
let option_get_expr: Expr = OptionGet::try_build(mc).unwrap().into();
let ctx = force_any_val::<Context>();
assert_eq!(
eval_out::<i64>(&option_get_expr, &ctx),
ctx.self_box.value.as_i64()
);
smethod_eval_fn(&self.method)?(&self.method, env, ectx, ov, argsv?)
}
}
2 changes: 1 addition & 1 deletion ergotree-interpreter/src/eval/property_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl Evaluable for PropertyCall {
ectx: &Context<'ctx>,
) -> Result<Value<'ctx>, EvalError> {
let ov = self.obj.eval(env, ectx)?;
smethod_eval_fn(&self.method)?(env, ectx, ov, vec![])
smethod_eval_fn(&self.method)?(&self.method, env, ectx, ov, vec![])
}
}

Expand Down
30 changes: 15 additions & 15 deletions ergotree-interpreter/src/eval/savltree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,24 @@ use super::EvalError;
use super::EvalFn;
use ergotree_ir::types::stype::SType;

pub(crate) static DIGEST_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static DIGEST_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Coll(CollKind::NativeColl(NativeColl::CollByte(
avl_tree_data.digest.0.iter().map(|&b| b as i8).collect(),
))))
};

pub(crate) static ENABLED_OPERATIONS_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static ENABLED_OPERATIONS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Byte(avl_tree_data.tree_flags.serialize() as i8))
};

pub(crate) static KEY_LENGTH_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static KEY_LENGTH_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Int(avl_tree_data.key_length as i32))
};

pub(crate) static VALUE_LENGTH_OPT_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static VALUE_LENGTH_OPT_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Opt(Box::new(
avl_tree_data
Expand All @@ -46,22 +46,22 @@ pub(crate) static VALUE_LENGTH_OPT_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
)))
};

pub(crate) static IS_INSERT_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static IS_INSERT_ALLOWED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Boolean(avl_tree_data.tree_flags.insert_allowed()))
};

pub(crate) static IS_UPDATE_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static IS_UPDATE_ALLOWED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Boolean(avl_tree_data.tree_flags.update_allowed()))
};

pub(crate) static IS_REMOVE_ALLOWED_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
pub(crate) static IS_REMOVE_ALLOWED_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
Ok(Value::Boolean(avl_tree_data.tree_flags.remove_allowed()))
};

pub(crate) static UPDATE_OPERATIONS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static UPDATE_OPERATIONS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
let new_operations = {
let v = args.first().cloned().ok_or_else(|| {
Expand All @@ -73,7 +73,7 @@ pub(crate) static UPDATE_OPERATIONS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
Ok(Value::AvlTree(Box::new(avl_tree_data)))
};

pub(crate) static UPDATE_DIGEST_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static UPDATE_DIGEST_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
let new_digest = {
let v = args.first().cloned().ok_or_else(|| {
Expand All @@ -86,7 +86,7 @@ pub(crate) static UPDATE_DIGEST_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
Ok(Value::AvlTree(Box::new(avl_tree_data)))
};

pub(crate) static GET_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static GET_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
let key = {
let v = args
Expand Down Expand Up @@ -135,7 +135,7 @@ pub(crate) static GET_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
};

pub(crate) static GET_MANY_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;

let keys = {
Expand Down Expand Up @@ -197,7 +197,7 @@ pub(crate) static GET_MANY_EVAL_FN: EvalFn =
};

pub(crate) static INSERT_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;

if !avl_tree_data.tree_flags.insert_allowed() {
Expand Down Expand Up @@ -260,7 +260,7 @@ pub(crate) static INSERT_EVAL_FN: EvalFn =
};

pub(crate) static REMOVE_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;

if !avl_tree_data.tree_flags.remove_allowed() {
Expand Down Expand Up @@ -319,7 +319,7 @@ pub(crate) static REMOVE_EVAL_FN: EvalFn =
}
};

pub(crate) static CONTAINS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
pub(crate) static CONTAINS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, args| {
let avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;
let key = {
let v = args
Expand Down Expand Up @@ -367,7 +367,7 @@ pub(crate) static CONTAINS_EVAL_FN: EvalFn = |_env, _ctx, obj, args| {
};

pub(crate) static UPDATE_EVAL_FN: EvalFn =
|_env, _ctx, obj, args| {
|_mc, _env, _ctx, obj, args| {
let mut avl_tree_data = obj.try_extract_into::<AvlTreeData>()?;

if !avl_tree_data.tree_flags.update_allowed() {
Expand Down
Loading
Loading