Skip to content

Commit

Permalink
Add versioning for getReg, change max script version to 3
Browse files Browse the repository at this point in the history
  • Loading branch information
SethDusek committed Sep 25, 2024
1 parent edf9576 commit 06cab26
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 53 deletions.
16 changes: 16 additions & 0 deletions ergotree-interpreter/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,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
35 changes: 0 additions & 35 deletions ergotree-interpreter/src/eval/method_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,3 @@ impl Evaluable for MethodCall {
smethod_eval_fn(&self.method)?(&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(0i32).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()
);
}
}
50 changes: 44 additions & 6 deletions ergotree-interpreter/src/eval/sbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::convert::TryInto;
use crate::eval::EvalError;

use ergotree_ir::chain::ergo_box::ErgoBox;
use ergotree_ir::ergo_tree::ErgoTreeVersion;
use ergotree_ir::mir::constant::TryExtractInto;
use ergotree_ir::mir::value::Value;
use ergotree_ir::reference::Ref;
Expand All @@ -16,7 +17,13 @@ pub(crate) static VALUE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
))
};

pub(crate) static GET_REG_EVAL_FN: EvalFn = |mc, _env, _ctx, obj, args| {
pub(crate) static GET_REG_EVAL_FN: EvalFn = |mc, _env, ctx, obj, args| {
if ctx.activated_script_version() < ErgoTreeVersion::V6_SOFT_FORK_VERSION {
return Err(EvalError::ScriptVersionError {
required_version: ErgoTreeVersion::V6_SOFT_FORK_VERSION,
activated_version: ctx.activated_script_version(),
});
}
#[allow(clippy::unwrap_used)]
let reg_id: i8 = args
.first()
Expand Down Expand Up @@ -69,22 +76,24 @@ pub(crate) static TOKENS_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
};

#[allow(clippy::unwrap_used)]
#[allow(clippy::panic)]
#[cfg(test)]
#[cfg(feature = "arbitrary")]
mod tests {
use ergotree_ir::ergo_tree::ErgoTreeVersion;
use ergotree_ir::mir::constant::Constant;
use ergotree_ir::mir::expr::Expr;
use ergotree_ir::mir::global_vars::GlobalVars;
use ergotree_ir::mir::method_call::MethodCall;
use ergotree_ir::mir::property_call::PropertyCall;
use ergotree_ir::mir::value::Value;
use ergotree_ir::types::sbox;
use ergotree_ir::types::stype::SType;
use ergotree_ir::types::stype_param::STypeVar;
use sigma_test_util::force_any_val;

use crate::eval::context::Context;
use crate::eval::tests::{eval_out, try_eval_out};
use crate::eval::tests::{eval_out, try_eval_out_with_version};
use crate::eval::EvalError;

#[test]
fn eval_box_value() {
Expand All @@ -107,7 +116,6 @@ mod tests {
);
}

// Attempt to extract SigmaProp from register of type SByte
#[test]
fn eval_reg_out() {
let type_args = std::iter::once((STypeVar::t(), SType::SLong)).collect();
Expand All @@ -120,7 +128,19 @@ mod tests {
.unwrap()
.into();
let ctx = force_any_val::<Context>();
try_eval_out::<Value>(&expr, &ctx).unwrap();
(0..ErgoTreeVersion::V6_SOFT_FORK_VERSION).for_each(|version| {
assert!(try_eval_out_with_version::<i64>(&expr, &ctx, version).is_err())
});
(ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each(
|version| {
assert_eq!(
try_eval_out_with_version::<Option<i64>>(&expr, &ctx, version)
.unwrap()
.unwrap(),
ctx.self_box.value.as_i64()
)
},
);
}

// Attempt to extract SigmaProp from register of type SLong
Expand All @@ -136,6 +156,24 @@ mod tests {
.unwrap()
.into();
let ctx = force_any_val::<Context>();
assert!(try_eval_out::<Value>(&expr, &ctx).is_err());
(0..ErgoTreeVersion::V6_SOFT_FORK_VERSION).for_each(|version| {
let res = try_eval_out_with_version::<Option<i64>>(&expr, &ctx, version);
match res {
Err(EvalError::Spanned(err))
if matches!(
*err.error,
EvalError::ScriptVersionError {
required_version: ErgoTreeVersion::V6_SOFT_FORK_VERSION,
activated_version: _
}
) => {}
_ => panic!("Expected script version error"),
}
});
(ErgoTreeVersion::V6_SOFT_FORK_VERSION..=ErgoTreeVersion::MAX_SCRIPT_VERSION).for_each(
|version| {
assert!(try_eval_out_with_version::<Option<i64>>(&expr, &ctx, version).is_err())
},
);
}
}
20 changes: 12 additions & 8 deletions ergotree-ir/src/ergo_tree/tree_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl ErgoTreeHeader {

/// Parse from byte
pub fn new(header_byte: u8) -> Result<Self, ErgoTreeHeaderError> {
let version = ErgoTreeVersion::parse_version(header_byte)?;
let version = ErgoTreeVersion::parse_version(header_byte);
let has_size = header_byte & Self::HAS_SIZE_FLAG != 0;
let is_constant_segregation = header_byte & Self::CONSTANT_SEGREGATION_FLAG != 0;
Ok(ErgoTreeHeader {
Expand Down Expand Up @@ -125,19 +125,23 @@ pub struct ErgoTreeVersion(u8);
impl ErgoTreeVersion {
/// Header mask to extract version bits.
pub const VERSION_MASK: u8 = 0x07;

/// Max version of ErgoTree supported by interpreter
pub const MAX_SCRIPT_VERSION: u8 = 3;
/// Version for v6.0 (Evolution)
pub const V6_SOFT_FORK_VERSION: u8 = 3;
/// Version 0
pub const V0: Self = ErgoTreeVersion(0);
/// Version 1 (size flag is mandatory)
pub const V1: Self = ErgoTreeVersion(1);
/// Version 2 (JIT)
pub const V2: Self = ErgoTreeVersion(2);
/// Version 3 (v6.0/Evolution)
pub const V3: Self = ErgoTreeVersion(Self::V6_SOFT_FORK_VERSION);

/// Returns a value of the version bits from the given header byte.
pub fn parse_version(header_byte: u8) -> Result<Self, ErgoTreeVersionError> {
let version = header_byte & ErgoTreeVersion::VERSION_MASK;
if version <= 1 {
Ok(ErgoTreeVersion(version))
} else {
Err(ErgoTreeVersionError::InvalidVersion(version))
}
pub fn parse_version(header_byte: u8) -> Self {
ErgoTreeVersion(header_byte & ErgoTreeVersion::VERSION_MASK)
}
}

Expand Down
1 change: 1 addition & 0 deletions ergotree-ir/src/types/sbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ lazy_static! {
SMethod::new( STypeCompanion::Box,TOKENS_METHOD_DESC.clone(),);
}

#[allow(clippy::unwrap_used)]
#[cfg(test)]
mod tests {
use crate::{
Expand Down

0 comments on commit 06cab26

Please sign in to comment.