Skip to content

Commit

Permalink
Merge pull request #773 from ergoplatform/getreg6.0
Browse files Browse the repository at this point in the history
[6.0] Box.getReg
  • Loading branch information
SethDusek authored Oct 2, 2024
2 parents 2613159 + f6048c1 commit 274287e
Show file tree
Hide file tree
Showing 31 changed files with 418 additions and 227 deletions.
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

0 comments on commit 274287e

Please sign in to comment.