Skip to content
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
23 changes: 23 additions & 0 deletions ergotree-interpreter/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use ergotree_ir::mir::value::Value;
use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean;

use ergotree_ir::types::smethod::SMethod;
use ergotree_ir::types::stype::SType;

use self::env::Env;
use ergotree_ir::chain::context::Context;
Expand Down Expand Up @@ -149,6 +150,28 @@ fn validate_self_extension_key_domain(ctx: &Context) -> Result<(), EvalError> {
Ok(())
}

/// Mirror of sigma-state `SType.isValueOfType`'s reachable rejections
/// (`SType.scala:200-205`), invoked by the JVM via `Value.checkType` at the
/// Tuple-eval items (`values.scala:801/804`) and `ConstantPlaceholder.eval`
/// (`values.scala:412`): a tuple type must be a pair (arity 2) and a function
/// type must be unary, otherwise the JVM throws "Unsupported tuple/function
/// type". sigma-rust models flat N-ary tuples (`TupleItems` = `BoundedVec<2,255>`)
/// so an arity≠2 tuple constant/value is representable and would otherwise
/// evaluate where the JVM rejects (a consensus accept/reject divergence). The
/// remaining `isValueOfType` arms only assert a value matches its type, which is
/// an invariant of sigma-rust's typed `Value`s, so they need no run-time check.
pub(crate) fn check_value_type(tpe: &SType) -> Result<(), EvalError> {
match tpe {
SType::STuple(t) if t.items.len() != 2 => {
Err(EvalError::Misc(format!("Unsupported tuple type {tpe:?}")))
}
SType::SFunc(f) if f.t_dom.len() != 1 => Err(EvalError::Misc(format!(
"Unsupported function type {tpe:?}"
))),
_ => Ok(()),
}
}

/// Evaluate the given expression by reducing it to SigmaBoolean value.
pub fn reduce_to_crypto(tree: &ErgoTree, ctx: &Context) -> Result<ReductionResult, EvalError> {
// The JVM rejects a `>= 0x80` self-extension key at context construction,
Expand Down
49 changes: 48 additions & 1 deletion ergotree-interpreter/src/eval/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,18 @@ impl Evaluable for Tuple {
self.items.len()
)));
}
let items_v = self.items.try_mapped_ref(|i| i.eval(env, ctx));
let items_v = self
.items
.try_mapped_ref(|i| -> Result<Value<'ctx>, EvalError> {
// Mirror the JVM Tuple eval, which `checkType`s each item BEFORE
// evaluating it (values.scala:801/804): an item whose type is an
// unsupported tuple (arity != 2) is rejected ("Unsupported tuple
// type", SType.scala:200). Catches an arity-3 tuple constant carried
// as a pair item, which the JVM refuses but sigma-rust would
// otherwise evaluate.
crate::eval::check_value_type(&i.tpe())?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to check type before evaluation to abort earlier if type is not ok?

i.eval(env, ctx)
});
Ok(Value::Tup(items_v?))
}
}
Expand Down Expand Up @@ -69,4 +80,40 @@ mod tests {
"arity-3 Tuple must be rejected at eval, got {res:?}"
);
}

#[test]
fn eval_rejects_arity3_tuple_item_constant() {
use crate::eval::test_util::try_eval_out_wo_ctx;
use ergotree_ir::mir::constant::{Constant, Literal};
use ergotree_ir::types::stuple::{STuple, TupleItems};
use ergotree_ir::types::stype::SType;

// SANTA Tuple.checkType_unsupported (inline): a pair Tuple whose item0 is
// an arity-3 (Bool,Bool,Bool) tuple CONSTANT. The JVM `checkType`s each
// Tuple item (values.scala:801/804) → arity != 2 → "Unsupported tuple
// type" (SType.scala:200-202). sigma-rust models flat N-ary tuples, so the
// value is representable; it must be rejected at eval to match the JVM.
let triple = Constant {
tpe: SType::STuple(STuple::triple(
SType::SBoolean,
SType::SBoolean,
SType::SBoolean,
)),
v: Literal::Tup(
TupleItems::try_from(vec![
Literal::Boolean(true),
Literal::Boolean(true),
Literal::Boolean(true),
])
.unwrap(),
),
};
let tuple: Expr = Tuple::new(vec![Expr::Const(triple), 1i32.into()])
.unwrap()
.into();
assert!(
try_eval_out_wo_ctx::<Value>(&tuple).is_err(),
"arity-3 tuple item constant must be rejected at eval (Unsupported tuple type)"
);
}
}
Loading