Repository: ergoplatform/sigma-rust (language: rust)

Issue:
Implement IR nodes (interpreter) for DEX and on-chain swaps contracts
proveDlog
box.creationInfo
Coll.exists
box.id
SigmaProp.propBytes
Option.isDefined
Option.getOrElse


Discussion / hints:

Fix patch:
diff --git a/ergo-lib/CHANGELOG.md b/ergo-lib/CHANGELOG.md
index a4ebed12a..d5135ba95 100644
--- a/ergo-lib/CHANGELOG.md
+++ b/ergo-lib/CHANGELOG.md
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 <!-- next-header -->
 ## [Unreleased] - ReleaseDate
+
+### Added 
+- `proveDlog`, `box.creationInfo`, `Box.id`, `Coll.exists`, `SigmaProp.propBytes`, `Option.isDefined`, `Option.getOrElse` IR nodes (evaluation, serialization) [#209](https://github.com/ergoplatform/sigma-rust/pull/209);
+
 ## [0.7.0] - 2021-03-03
 
 ### Added 
diff --git a/ergo-lib/README.md b/ergo-lib/README.md
index f174262a8..ca87cdfa1 100644
--- a/ergo-lib/README.md
+++ b/ergo-lib/README.md
@@ -63,9 +63,9 @@ Descriptions for the operations can be found in [ErgoTree Specification](https:/
 - treeLookup
 - :heavy_check_mark: blake2b256
 - sha256
-- proveDlog
+- :heavy_check_mark: proveDlog
 - proveDHTuple
-- sigmaProp
+- :heavy_check_mark: sigmaProp
 - executeFromVar
 - executeFromSelfReg
 - getVar
@@ -135,7 +135,7 @@ Descriptions for the operations can be found in [ErgoTree Specification](https:/
 
 #### SigmaProp
 
-- propBytes
+- :heavy_check_mark: propBytes
 - isProven
 
 #### Box
@@ -144,8 +144,8 @@ Descriptions for the operations can be found in [ErgoTree Specification](https:/
 - :heavy_check_mark: propositionBytes
 - bytes
 - bytesWithoutRef
-- id
-- creationInfo
+- :heavy_check_mark: id
+- :heavy_check_mark: creationInfo
 - :heavy_check_mark: getReg
 - :heavy_check_mark: tokens
 - :heavy_check_mark: R0 .. R9
@@ -215,9 +215,9 @@ Descriptions for the operations can be found in [ErgoTree Specification](https:/
 #### Collection
 
 - :heavy_check_mark: size
-- getOrElse
+- :heavy_check_mark: getOrElse
 - :heavy_check_mark: map
-- exists
+- :heavy_check_mark: exists
 - :heavy_check_mark: fold
 - forall
 - slice
@@ -234,9 +234,9 @@ Descriptions for the operations can be found in [ErgoTree Specification](https:/
 
 #### Option
 
-- isDefined
+- :heavy_check_mark: isDefined
 - :heavy_check_mark: get
-- getOrElse
+- :heavy_check_mark: getOrElse
 - map
 - filter
 
diff --git a/ergotree-interpreter/src/eval.rs b/ergotree-interpreter/src/eval.rs
index 75dec5142..f2e3884b4 100644
--- a/ergotree-interpreter/src/eval.rs
+++ b/ergotree-interpreter/src/eval.rs
@@ -27,6 +27,7 @@ pub(crate) mod block;
 pub(crate) mod bool_to_sigma;
 pub(crate) mod calc_blake2b256;
 pub(crate) mod coll_by_index;
+pub(crate) mod coll_exists;
 pub(crate) mod coll_filter;
 pub(crate) mod coll_fold;
 pub(crate) mod coll_map;
@@ -34,8 +35,11 @@ pub(crate) mod coll_size;
 pub(crate) mod collection;
 pub(crate) mod cost_accum;
 pub(crate) mod costs;
+pub(crate) mod create_provedlog;
 pub(crate) mod expr;
 pub(crate) mod extract_amount;
+pub(crate) mod extract_creation_info;
+pub(crate) mod extract_id;
 pub(crate) mod extract_reg_as;
 pub(crate) mod extract_script_bytes;
 pub(crate) mod func_value;
@@ -44,11 +48,14 @@ pub(crate) mod if_op;
 pub(crate) mod logical_not;
 pub(crate) mod method_call;
 pub(crate) mod option_get;
+pub(crate) mod option_get_or_else;
+pub(crate) mod option_is_defined;
 pub(crate) mod or;
 pub(crate) mod property_call;
 pub(crate) mod sbox;
 pub(crate) mod scontext;
 pub(crate) mod select_field;
+pub(crate) mod sigma_prop_bytes;
 pub(crate) mod upcast;
 pub(crate) mod val_use;
 
diff --git a/ergotree-interpreter/src/eval/coll_exists.rs b/ergotree-interpreter/src/eval/coll_exists.rs
new file mode 100644
index 000000000..dfa1fbf4c
--- /dev/null
+++ b/ergotree-interpreter/src/eval/coll_exists.rs
@@ -0,0 +1,125 @@
+use ergotree_ir::mir::coll_exists::Exists;
+use ergotree_ir::mir::constant::TryExtractInto;
+use ergotree_ir::mir::value::Value;
+
+use crate::eval::env::Env;
+use crate::eval::EvalContext;
+use crate::eval::EvalError;
+use crate::eval::Evaluable;
+
+impl Evaluable for Exists {
+    fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
+        let input_v = self.input.eval(env, ctx)?;
+        let condition_v = self.condition.eval(env, ctx)?;
+        let input_v_clone = input_v.clone();
+        let mut condition_call = |arg: Value| match &condition_v {
+            Value::FuncValue(func_value) => {
+                let func_arg = func_value.args().first().ok_or_else(|| {
+                    EvalError::NotFound(
+                        "Exists: evaluated condition has empty arguments list".to_string(),
+                    )
+                })?;
+                let env1 = env.clone().extend(func_arg.idx, arg);
+                func_value.body().eval(&env1, ctx)
+            }
+            _ => Err(EvalError::UnexpectedValue(format!(
+                "expected Exists::condition to be Value::FuncValue got: {0:?}",
+                input_v_clone
+            ))),
+        };
+        let normalized_input_vals: Vec<Value> = match input_v {
+            Value::Coll(coll) => {
+                if *coll.elem_tpe() != self.elem_tpe {
+                    return Err(EvalError::UnexpectedValue(format!(
+                        "expected Exists input element type to be {0:?}, got: {1:?}",
+                        self.elem_tpe,
+                        coll.elem_tpe()
+                    )));
+                };
+                Ok(coll.as_vec())
+            }
+            _ => Err(EvalError::UnexpectedValue(format!(
+                "expected Map input to be Value::Coll, got: {0:?}",
+                input_v
+            ))),
+        }?;
+
+        for item in normalized_input_vals.into_iter() {
+            let res = condition_call(item)?.try_extract_into::<bool>()?;
+            if res {
+                return Ok(true.into());
+            }
+        }
+        Ok(false.into())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::rc::Rc;
+
+    use crate::eval::context::Context;
+    use crate::eval::tests::eval_out;
+
+    use super::*;
+
+    use ergotree_ir::mir::bin_op::BinOp;
+    use ergotree_ir::mir::bin_op::RelationOp;
+    use ergotree_ir::mir::expr::Expr;
+    use ergotree_ir::mir::extract_amount::ExtractAmount;
+    use ergotree_ir::mir::func_value::FuncArg;
+    use ergotree_ir::mir::func_value::FuncValue;
+    use ergotree_ir::mir::property_call::PropertyCall;
+    use ergotree_ir::mir::val_use::ValUse;
+    use ergotree_ir::types::scontext;
+    use ergotree_ir::types::stype::SType;
+    use proptest::prelude::*;
+
+    proptest! {
+
+        #![proptest_config(ProptestConfig::with_cases(16))]
+
+        #[test]
+        fn eval_box_value(ctx in any::<Context>()) {
+            let data_inputs: Expr = PropertyCall {
+                obj: Box::new(Expr::Context),
+                method: scontext::DATA_INPUTS_PROPERTY.clone(),
+            }
+            .into();
+            let val_use: Expr = ValUse {
+                val_id: 1.into(),
+                tpe: SType::SBox,
+            }
+            .into();
+            let body: Expr = BinOp {
+                kind: RelationOp::LE.into(),
+                left: Box::new(Expr::Const(1i64.into())),
+                right: Box::new(Expr::ExtractAmount(
+                        ExtractAmount::new(val_use)
+                    .unwrap(),
+                )),
+            }
+            .into();
+            let expr: Expr = Exists::new(
+                data_inputs,
+                FuncValue::new(
+                    vec![FuncArg {
+                        idx: 1.into(),
+                        tpe: SType::SBox,
+                    }],
+                    body,
+                )
+                .into(),
+            )
+            .unwrap()
+            .into();
+            let ctx = Rc::new(ctx);
+            assert_eq!(
+                eval_out::<bool>(&expr, ctx.clone()),
+                ctx.data_inputs.clone()
+                    .into_iter()
+                    .any(| b| 1 <= b.get_box(&ctx.box_arena).unwrap().value())
+            );
+        }
+    }
+}
diff --git a/ergotree-interpreter/src/eval/context/ir_ergo_box_dummy.rs b/ergotree-interpreter/src/eval/context/ir_ergo_box_dummy.rs
index 26448e058..d672e2576 100644
--- a/ergotree-interpreter/src/eval/context/ir_ergo_box_dummy.rs
+++ b/ergotree-interpreter/src/eval/context/ir_ergo_box_dummy.rs
@@ -29,6 +29,7 @@ pub(crate) struct IrErgoBoxDummy {
     pub(crate) additional_registers: Vec<Constant>,
     pub(crate) creation_height: i32,
     pub(crate) script_bytes: Vec<u8>,
+    pub(crate) creation_info: (i32, Vec<u8>),
 }
 
 impl IrErgoBox for IrErgoBoxDummy {
@@ -51,6 +52,7 @@ impl IrErgoBox for IrErgoBoxDummy {
     fn get_register(&self, id: i8) -> Option<Constant> {
         match id {
             0 => Some(self.value.into()),
+            3 => Some(self.creation_info.clone().into()),
             _ => self.additional_registers.get(id as usize).cloned(),
         }
     }
@@ -86,9 +88,18 @@ pub(crate) mod arbitrary {
                 1i32..1000,
                 vec(any::<Constant>(), 0..5),
                 vec(any::<u8>(), 100..1000),
+                vec(any::<u8>(), DIGEST32_SIZE + 2..=DIGEST32_SIZE + 2),
             )
                 .prop_map(
-                    |(id, value, tokens, creation_height, additional_registers, script_bytes)| {
+                    |(
+                        id,
+                        value,
+                        tokens,
+                        creation_height,
+                        additional_registers,
+                        script_bytes,
+                        tx_id_box_index,
+                    )| {
                         Self {
                             id: IrBoxId(id),
                             value,
@@ -99,6 +110,7 @@ pub(crate) mod arbitrary {
                             additional_registers,
                             creation_height,
                             script_bytes,
+                            creation_info: (creation_height, tx_id_box_index),
                         }
                     },
                 )
diff --git a/ergotree-interpreter/src/eval/create_provedlog.rs b/ergotree-interpreter/src/eval/create_provedlog.rs
new file mode 100644
index 000000000..2fa1f4bbd
--- /dev/null
+++ b/ergotree-interpreter/src/eval/create_provedlog.rs
@@ -0,0 +1,24 @@
+use ergotree_ir::mir::create_provedlog::CreateProveDlog;
+use ergotree_ir::mir::value::Value;
+use ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog;
+
+use crate::eval::env::Env;
+use crate::eval::EvalContext;
+use crate::eval::EvalError;
+use crate::eval::Evaluable;
+
+impl Evaluable for CreateProveDlog {
+    fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
+        let value_v = self.value.eval(env, ctx)?;
+        match value_v {
+            Value::GroupElement(ecpoint) => {
+                let prove_dlog = ProveDlog::new(*ecpoint);
+                Ok(prove_dlog.into())
+            }
+            _ => Err(EvalError::UnexpectedValue(format!(
+                "Expected CreateProveDlog input to be Value::GroupElement, got {0:?}",
+                value_v
+            ))),
+        }
+    }
+}
diff --git a/ergotree-interpreter/src/eval/expr.rs b/ergotree-interpreter/src/eval/expr.rs
index 01a13a537..e0b4ced95 100644
--- a/ergotree-interpreter/src/eval/expr.rs
+++ b/ergotree-interpreter/src/eval/expr.rs
@@ -40,6 +40,13 @@ impl Evaluable for Expr {
             Expr::ByIndex(op) => op.eval(env, ctx),
             Expr::ExtractScriptBytes(op) => op.eval(env, ctx),
             Expr::SizeOf(op) => op.eval(env, ctx),
+            Expr::CreateProveDlog(op) => op.eval(env, ctx),
+            Expr::ExtractCreationInfo(op) => op.eval(env, ctx),
+            Expr::Exists(op) => op.eval(env, ctx),
+            Expr::ExtractId(op) => op.eval(env, ctx),
+            Expr::SigmaPropBytes(op) => op.eval(env, ctx),
+            Expr::OptionIsDefined(op) => op.eval(env, ctx),
+            Expr::OptionGetOrElse(op) => op.eval(env, ctx),
         }
     }
 }
diff --git a/ergotree-interpreter/src/eval/extract_amount.rs b/ergotree-interpreter/src/eval/extract_amount.rs
index a33f88e7c..0026416c4 100644
--- a/ergotree-interpreter/src/eval/extract_amount.rs
+++ b/ergotree-interpreter/src/eval/extract_amount.rs
@@ -18,3 +18,27 @@ impl Evaluable for ExtractAmount {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::eval::context::Context;
+    use crate::eval::tests::eval_out;
+    use ergotree_ir::mir::expr::Expr;
+    use ergotree_ir::mir::global_vars::GlobalVars;
+    use sigma_test_util::force_any_val;
+    use std::rc::Rc;
+
+    #[test]
+    fn eval() {
+        let e: Expr = ExtractAmount {
+            input: Box::new(GlobalVars::SelfBox.into()),
+        }
+        .into();
+        let ctx = Rc::new(force_any_val::<Context>());
+        assert_eq!(
+            eval_out::<i64>(&e, ctx.clone()),
+            ctx.self_box.get_box(&ctx.box_arena).unwrap().value()
+        );
+    }
+}
diff --git a/ergotree-interpreter/src/eval/extract_creation_info.rs b/ergotree-interpreter/src/eval/extract_creation_info.rs
new file mode 100644
index 000000000..1618308cf
--- /dev/null
+++ b/ergotree-interpreter/src/eval/extract_creation_info.rs
@@ -0,0 +1,60 @@
+use ergotree_ir::mir::extract_creation_info::ExtractCreationInfo;
+use ergotree_ir::mir::value::Value;
+
+use crate::eval::env::Env;
+use crate::eval::EvalContext;
+use crate::eval::EvalError;
+use crate::eval::Evaluable;
+
+impl Evaluable for ExtractCreationInfo {
+    fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
+        let input_v = self.input.eval(env, ctx)?;
+        match input_v {
+            Value::CBox(b) => Ok(ctx
+                .ctx
+                .box_arena
+                .get(&b)?
+                .get_register(3)
+                .map(|c| c.v)
+                .unwrap()), // unwrap() is safe because R3 is a mandatory register and cannot be empty
+            _ => Err(EvalError::UnexpectedValue(format!(
+                "Expected ExtractCreationInfo input to be Value::CBox, got {0:?}",
+                input_v
+            ))),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::eval::tests::eval_out;
+    use crate::eval::Context;
+    use std::rc::Rc;
+
+    use ergotree_ir::mir::constant::TryExtractInto;
+    use ergotree_ir::mir::expr::Expr;
+    use ergotree_ir::mir::global_vars::GlobalVars;
+    use sigma_test_util::force_any_val;
+
+    use super::*;
+
+    #[test]
+    fn eval() {
+        let expr: Expr = ExtractCreationInfo::new(GlobalVars::SelfBox.into())
+            .unwrap()
+            .into();
+        let ctx = Rc::new(force_any_val::<Context>());
+        let v = eval_out::<(i32, Vec<u8>)>(&expr, ctx.clone());
+        assert_eq!(
+            v,
+            ctx.self_box
+                .get_box(&ctx.box_arena)
+                .unwrap()
+                .get_register(3)
+                .unwrap()
+                .v
+                .try_extract_into::<(i32, Vec<u8>)>()
+                .unwrap()
+        );
+    }
+}
diff --git a/ergotree-interpreter/src/eval/extract_id.rs b/ergotree-interpreter/src/eval/extract_id.rs
new file mode 100644
index 000000000..f6de7be58
--- /dev/null
+++ b/ergotree-interpreter/src/eval/extract_id.rs
@@ -0,0 +1,44 @@
+use ergotree_ir::mir::extract_id::ExtractId;
+use ergotree_ir::mir::value::Value;
+
+use crate::eval::env::Env;
+use crate::eval::EvalContext;
+use crate::eval::EvalError;
+use crate::eval::Evaluable;
+
+impl Evaluable for ExtractId {
+    fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
+        let input_v = self.input.eval(env, ctx)?;
+        match input_v {
+            Value::CBox(b) => Ok((ctx.ctx.box_arena.get(&b)?.id()).to_vec().into()),
+            _ => Err(EvalError::UnexpectedValue(format!(
+                "Expected ExtractId input to be Value::CBox, got {0:?}",
+                input_v
+            ))),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::eval::context::Context;
+    use crate::eval::tests::eval_out;
+    use ergotree_ir::mir::expr::Expr;
+    use ergotree_ir::mir::global_vars::GlobalVars;
+    use sigma_test_util::force_any_val;
+    use std::rc::Rc;
+
+    #[test]
+    fn eval() {
+        let e: Expr = ExtractId {
+            input: Box::new(GlobalVars::SelfBox.into()),
+        }
+        .into();
+        let ctx = Rc::new(force_any_val::<Context>());
+        assert_eq!(
+            eval_out::<Vec<u8>>(&e, ctx.clone()),
+            ctx.self_box.get_box(&ctx.box_arena).unwrap().id().to_vec()
+        );
+    }
+}
diff --git a/ergotree-interpreter/src/eval/option_get_or_else.rs b/ergotree-interpreter/src/eval/option_get_or_else.rs
new file mode 100644
index 000000000..0c2c310d7
--- /dev/null
+++ b/ergotree-interpreter/src/eval/option_get_or_else.rs
@@ -0,0 +1,71 @@
+use ergotree_ir::mir::option_get_or_else::OptionGetOrElse;
+use ergotree_ir::mir::value::Value;
+
+use crate::eval::env::Env;
+use crate::eval::EvalContext;
+use crate::eval::EvalError;
+use crate::eval::Evaluable;
+
+impl Evaluable for OptionGetOrElse {
+    fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
+        let v = self.input.eval(env, ctx)?;
+        let default_v = self.default.eval(env, ctx)?;
+        match v {
+            Value::Opt(opt_v) => Ok(opt_v.unwrap_or(default_v)),
+            _ => Err(EvalError::UnexpectedExpr(format!(
+                "Don't know how to eval OptM: {0:?}",
+                self
+            ))),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::OptionGetOrElse;
+    use crate::eval::context::Context;
+    use crate::eval::tests::eval_out;
+    use ergotree_ir::mir::constant::Constant;
+    use ergotree_ir::mir::expr::Expr;
+    use ergotree_ir::mir::extract_reg_as::ExtractRegisterAs;
+    use ergotree_ir::mir::global_vars::GlobalVars;
+    use ergotree_ir::types::stype::SType;
+    use sigma_test_util::force_any_val;
+    use std::rc::Rc;
+
+    #[test]
+    fn eval_non_empty() {
+        let get_reg_expr: Expr = ExtractRegisterAs::new(
+            GlobalVars::SelfBox.into(),
+            0,
+            SType::SOption(SType::SLong.into()),
+        )
+        .unwrap()
+        .into();
+        let default_expr: Constant = 1i64.into();
+        let option_get_expr: Expr = OptionGetOrElse::new(get_reg_expr, default_expr.into())
+            .unwrap()
+            .into();
+        let ctx = Rc::new(force_any_val::<Context>());
+        let v = eval_out::<i64>(&option_get_expr, ctx.clone());
+        assert_eq!(v, ctx.self_box.get_box(&ctx.box_arena).unwrap().value());
+    }
+
+    #[test]
+    fn eval_empty() {
+        let get_reg_expr: Expr = ExtractRegisterAs::new(
+            GlobalVars::SelfBox.into(),
+            9,
+            SType::SOption(SType::SLong.into()),
+        )
+        .unwrap()
+        .into();
+        let default_expr: Constant = 1i64.into();
+        let option_get_expr: Expr = OptionGetOrElse::new(get_reg_expr, default_expr.into())
+            .unwrap()
+            .into();
+        let ctx = Rc::new(force_any_val::<Context>());
+        let v = eval_out::<i64>(&option_get_expr, ctx);
+        assert_eq!(v, 1i64);
+    }
+}
diff --git a/ergotree-interpreter/src/eval/option_is_defined.rs b/ergotree-interpreter/src/eval/option_is_defined.rs
new file mode 100644
index 000000000..609e48199
--- /dev/null
+++ b/ergotree-interpreter/src/eval/option_is_defined.rs
@@ -0,0 +1,52 @@
+use ergotree_ir::mir::option_is_defined::OptionIsDefined;
+use ergotree_ir::mir::value::Value;
+
+use crate::eval::env::Env;
+use crate::eval::EvalContext;
+use crate::eval::EvalError;
+use crate::eval::Evaluable;
+
+impl Evaluable for OptionIsDefined {
+    fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
+        let v = self.input.eval(env, ctx)?;
+        match v {
+            Value::Opt(opt_v) => Ok(opt_v.is_some().into()),
+            _ => Err(EvalError::UnexpectedExpr(format!(
+                "Don't know how to eval OptM: {0:?}",
+                self
+            ))),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::OptionIsDefined;
+    use crate::eval::context::Context;
+    use crate::eval::tests::eval_out;
+    use ergotree_ir::mir::expr::Expr;
+    use ergotree_ir::mir::extract_reg_as::ExtractRegisterAs;
+    use ergotree_ir::mir::global_vars::GlobalVars;
+    use ergotree_ir::types::stype::SType;
+    use sigma_test_util::force_any_val;
+    use std::rc::Rc;
+
+    #[test]
+    fn eval() {
+        let get_reg_expr: Expr = ExtractRegisterAs::new(
+            GlobalVars::SelfBox.into(),
+            0,
+            SType::SOption(SType::SLong.into()),
+        )
+        .unwrap()
+        .into();
+        let option_expr: Expr = OptionIsDefined {
+            input: Box::new(get_reg_expr),
+        }
+        .into();
+        let ctx = Rc::new(force_any_val::<Context>());
+        let v = eval_out::<bool>(&option_expr, ctx);
+        // R0 is always defined (box value)
+        assert_eq!(v, true);
+    }
+}
diff --git a/ergotree-interpreter/src/eval/sigma_prop_bytes.rs b/ergotree-interpreter/src/eval/sigma_prop_bytes.rs
new file mode 100644
index 000000000..728c58030
--- /dev/null
+++ b/ergotree-interpreter/src/eval/sigma_prop_bytes.rs
@@ -0,0 +1,47 @@
+use ergotree_ir::mir::sigma_prop_bytes::SigmaPropBytes;
+use ergotree_ir::mir::value::Value;
+
+use crate::eval::env::Env;
+use crate::eval::EvalContext;
+use crate::eval::EvalError;
+use crate::eval::Evaluable;
+
+impl Evaluable for SigmaPropBytes {
+    fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
+        let input_v = self.input.eval(env, ctx)?;
+        match input_v {
+            Value::SigmaProp(sigma_prop) => Ok(sigma_prop.prop_bytes().into()),
+            _ => Err(EvalError::UnexpectedValue(format!(
+                "Expected SigmaPropBytes input to be Value::SigmaProp, got {0:?}",
+                input_v
+            ))),
+        }
+    }
+}
+
+#[cfg(feature = "arbitrary")]
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::eval::tests::eval_out_wo_ctx;
+    use ergotree_ir::mir::constant::Constant;
+    use ergotree_ir::mir::expr::Expr;
+    use ergotree_ir::sigma_protocol::sigma_boolean::SigmaProp;
+    use proptest::prelude::*;
+
+    proptest! {
+
+        #![proptest_config(ProptestConfig::with_cases(8))]
+
+        #[test]
+        fn eval(v in any::<SigmaProp>()) {
+            let expected_bytes = v.prop_bytes();
+            let input: Constant = v.into();
+            let e: Expr = SigmaPropBytes {
+                input: Box::new(input.into()),
+            }
+            .into();
+            prop_assert_eq!(eval_out_wo_ctx::<Vec<u8>>(&e), expected_bytes);
+        }
+    }
+}
diff --git a/ergotree-ir/src/mir.rs b/ergotree-ir/src/mir.rs
index e25f6a4b9..e3d8690fe 100644
--- a/ergotree-ir/src/mir.rs
+++ b/ergotree-ir/src/mir.rs
@@ -9,6 +9,8 @@ pub mod bool_to_sigma;
 pub mod calc_blake2b256;
 /// Get the collection element by index
 pub mod coll_by_index;
+/// Tests whether a predicate holds for at least one element of this collection
+pub mod coll_exists;
 /// Collection.filter
 pub mod coll_filter;
 /// Collection.fold
@@ -20,9 +22,15 @@ pub mod coll_size;
 /// Collection of elements
 pub mod collection;
 pub mod constant;
+/// Create proveDlog from GroupElement(PK)
+pub mod create_provedlog;
 pub mod expr;
 /// Box value
 pub mod extract_amount;
+/// Box.creationInfo (height, tx id + box index)
+pub mod extract_creation_info;
+/// Box id, Blake2b256 hash of this box's content, basically equals to `blake2b256(bytes)`
+pub mod extract_id;
 /// Box register value (Box.RX)
 pub mod extract_reg_as;
 /// Box.scriptBytes
@@ -38,12 +46,18 @@ pub mod logical_not;
 pub mod method_call;
 /// Option.get() op
 pub mod option_get;
+/// Returns the Option's value or error if no value
+pub mod option_get_or_else;
+/// Returns false if the option is None, true otherwise.
+pub mod option_is_defined;
 /// Logical OR op
 pub mod or;
 /// Object property call
 pub mod property_call;
 /// Select a field of the tuple value
 pub mod select_field;
+/// Extract serialized bytes of a SigmaProp value
+pub mod sigma_prop_bytes;
 pub mod upcast;
 /// Variable definition
 pub mod val_def;
diff --git a/ergotree-ir/src/mir/bool_to_sigma.rs b/ergotree-ir/src/mir/bool_to_sigma.rs
index c163d3358..a851ef6d5 100644
--- a/ergotree-ir/src/mir/bool_to_sigma.rs
+++ b/ergotree-ir/src/mir/bool_to_sigma.rs
@@ -7,6 +7,7 @@ use crate::serialization::SigmaSerializable;
 use crate::types::stype::SType;
 
 use super::expr::Expr;
+use super::expr::InvalidArgumentError;
 
 /** Embedding of Boolean values to SigmaProp values. As an example, this operation allows boolean experesions
  * to be used as arguments of `atLeast(..., sigmaProp(boolExpr), ...)` operation.
@@ -21,6 +22,14 @@ pub struct BoolToSigmaProp {
 impl BoolToSigmaProp {
     pub(crate) const OP_CODE: OpCode = OpCode::BOOL_TO_SIGMA_PROP;
 
+    /// Create new object, returns an error if any of the requirements failed
+    pub fn new(input: Expr) -> Result<Self, InvalidArgumentError> {
+        input.check_post_eval_tpe(SType::SBoolean)?;
+        Ok(Self {
+            input: input.into(),
+        })
+    }
+
     /// Type
     pub fn tpe(&self) -> SType {
         SType::SBoolean
diff --git a/ergotree-ir/src/mir/coll_exists.rs b/ergotree-ir/src/mir/coll_exists.rs
new file mode 100644
index 000000000..b3236438e
--- /dev/null
+++ b/ergotree-ir/src/mir/coll_exists.rs
@@ -0,0 +1,125 @@
+use super::expr::Expr;
+use super::expr::InvalidArgumentError;
+use crate::serialization::op_code::OpCode;
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+use crate::types::stype::SType;
+
+/// Tests whether a predicate holds for at least one element of this collection
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct Exists {
+    /// Collection
+    pub input: Box<Expr>,
+    /// Function (lambda) to test each element
+    pub condition: Box<Expr>,
+    /// Collection element type
+    pub elem_tpe: SType,
+}
+
+impl Exists {
+    pub(crate) const OP_CODE: OpCode = OpCode::EXISTS;
+
+    /// Create new object, returns an error if any of the requirements failed
+    pub fn new(input: Expr, condition: Expr) -> Result<Self, InvalidArgumentError> {
+        let input_elem_type: SType = *match input.post_eval_tpe() {
+            SType::SColl(elem_type) => Ok(elem_type),
+            _ => Err(InvalidArgumentError(format!(
+                "Expected Map input to be SColl, got {0:?}",
+                input.tpe()
+            ))),
+        }?;
+        match condition.tpe() {
+            SType::SFunc(sfunc)
+                if sfunc.t_dom == vec![input_elem_type.clone()]
+                    && *sfunc.t_range == SType::SBoolean =>
+            {
+                Ok(Exists {
+                    input: input.into(),
+                    condition: condition.into(),
+                    elem_tpe: input_elem_type,
+                })
+            }
+            _ => Err(InvalidArgumentError(format!(
+                "Invalid condition tpe: {0:?}",
+                condition.tpe()
+            ))),
+        }
+    }
+
+    /// Type
+    pub fn tpe(&self) -> SType {
+        SType::SBoolean
+    }
+
+    pub(crate) fn op_code(&self) -> OpCode {
+        Self::OP_CODE
+    }
+}
+
+impl SigmaSerializable for Exists {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), std::io::Error> {
+        self.input.sigma_serialize(w)?;
+        self.condition.sigma_serialize(w)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        let input = Expr::sigma_parse(r)?;
+        let condition = Expr::sigma_parse(r)?;
+        Ok(Exists::new(input, condition)?)
+    }
+}
+
+#[cfg(feature = "arbitrary")]
+/// Arbitrary impl
+mod arbitrary {
+    use super::*;
+    use crate::mir::expr::arbitrary::ArbExprParams;
+    use crate::types::sfunc::SFunc;
+    use proptest::prelude::*;
+
+    impl Arbitrary for Exists {
+        type Strategy = BoxedStrategy<Self>;
+        type Parameters = ();
+
+        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+            (
+                any_with::<Expr>(ArbExprParams {
+                    tpe: SType::SColl(SType::SBoolean.into()),
+                    depth: 1,
+                }),
+                any_with::<Expr>(ArbExprParams {
+                    tpe: SType::SFunc(SFunc {
+                        t_dom: vec![SType::SBoolean],
+                        t_range: SType::SBoolean.into(),
+                        tpe_params: vec![],
+                    }),
+                    depth: 0,
+                }),
+            )
+                .prop_map(|(input, mapper)| Exists::new(input, mapper).unwrap())
+                .boxed()
+        }
+    }
+}
+
+#[cfg(test)]
+#[cfg(feature = "arbitrary")]
+mod tests {
+    use super::*;
+    use crate::mir::expr::Expr;
+    use crate::serialization::sigma_serialize_roundtrip;
+    use proptest::prelude::*;
+
+    proptest! {
+
+        #![proptest_config(ProptestConfig::with_cases(16))]
+
+        #[test]
+        fn ser_roundtrip(v in any::<Exists>()) {
+            let expr: Expr = v.into();
+            prop_assert_eq![sigma_serialize_roundtrip(&expr), expr];
+        }
+    }
+}
diff --git a/ergotree-ir/src/mir/create_provedlog.rs b/ergotree-ir/src/mir/create_provedlog.rs
new file mode 100644
index 000000000..3d8409b6d
--- /dev/null
+++ b/ergotree-ir/src/mir/create_provedlog.rs
@@ -0,0 +1,71 @@
+use crate::serialization::op_code::OpCode;
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+use crate::types::stype::SType;
+
+use super::expr::Expr;
+use super::expr::InvalidArgumentError;
+
+/// Create ProveDlog from PK
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct CreateProveDlog {
+    /// GroupElement (PK)
+    pub value: Box<Expr>,
+}
+
+impl CreateProveDlog {
+    pub(crate) const OP_CODE: OpCode = OpCode::PROVE_DLOG;
+
+    /// Create new object, returns an error if any of the requirements failed
+    pub fn new(value: Expr) -> Result<Self, InvalidArgumentError> {
+        value.check_post_eval_tpe(SType::SGroupElement)?;
+        Ok(CreateProveDlog {
+            value: value.into(),
+        })
+    }
+
+    /// Type
+    pub fn tpe(&self) -> SType {
+        SType::SSigmaProp
+    }
+
+    pub(crate) fn op_code(&self) -> OpCode {
+        Self::OP_CODE
+    }
+}
+
+impl SigmaSerializable for CreateProveDlog {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), std::io::Error> {
+        self.value.sigma_serialize(w)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        Ok(CreateProveDlog {
+            value: Expr::sigma_parse(r)?.into(),
+        })
+    }
+}
+
+#[cfg(test)]
+#[cfg(feature = "arbitrary")]
+mod tests {
+    use sigma_test_util::force_any_val_with;
+
+    use crate::mir::constant::arbitrary::ArbConstantParams;
+    use crate::mir::constant::Constant;
+    use crate::serialization::sigma_serialize_roundtrip;
+
+    use super::*;
+
+    #[test]
+    fn ser_roundtrip() {
+        let e: Expr = CreateProveDlog::new(
+            force_any_val_with::<Constant>(ArbConstantParams::Exact(SType::SGroupElement)).into(),
+        )
+        .unwrap()
+        .into();
+        assert_eq![sigma_serialize_roundtrip(&e), e];
+    }
+}
diff --git a/ergotree-ir/src/mir/expr.rs b/ergotree-ir/src/mir/expr.rs
index e1fe33f32..0f6d72647 100644
--- a/ergotree-ir/src/mir/expr.rs
+++ b/ergotree-ir/src/mir/expr.rs
@@ -13,6 +13,7 @@ use super::block::BlockValue;
 use super::bool_to_sigma::BoolToSigmaProp;
 use super::calc_blake2b256::CalcBlake2b256;
 use super::coll_by_index::ByIndex;
+use super::coll_exists::Exists;
 use super::coll_filter::Filter;
 use super::coll_fold::Fold;
 use super::coll_map::Map;
@@ -22,7 +23,10 @@ use super::constant::Constant;
 use super::constant::ConstantPlaceholder;
 use super::constant::TryExtractFrom;
 use super::constant::TryExtractFromError;
+use super::create_provedlog::CreateProveDlog;
 use super::extract_amount::ExtractAmount;
+use super::extract_creation_info::ExtractCreationInfo;
+use super::extract_id::ExtractId;
 use super::extract_reg_as::ExtractRegisterAs;
 use super::extract_script_bytes::ExtractScriptBytes;
 use super::func_value::FuncValue;
@@ -31,9 +35,12 @@ use super::if_op::If;
 use super::logical_not::LogicalNot;
 use super::method_call::MethodCall;
 use super::option_get::OptionGet;
+use super::option_get_or_else::OptionGetOrElse;
+use super::option_is_defined::OptionIsDefined;
 use super::or::Or;
 use super::property_call::PropertyCall;
 use super::select_field::SelectField;
+use super::sigma_prop_bytes::SigmaPropBytes;
 use super::upcast::Upcast;
 use super::val_def::ValDef;
 use super::val_use::ValUse;
@@ -83,12 +90,23 @@ pub enum Expr {
     Or(Or),
     /// LogicalNot
     LogicalNot(LogicalNot),
-    /// Option get method
+    /// Option.get method
     OptionGet(OptionGet),
+    /// Option.isDefined method
+    OptionIsDefined(OptionIsDefined),
+    /// Returns the option's value if the option is nonempty, otherwise return the result of evaluating `default`.
+    OptionGetOrElse(OptionGetOrElse),
+    /// Box monetary value
+    ExtractAmount(ExtractAmount),
     /// Extract register's value (box.RX properties)
     ExtractRegisterAs(ExtractRegisterAs),
     /// Extract box's guarding script serialized to bytes
     ExtractScriptBytes(ExtractScriptBytes),
+    /// Tuple of height when block got included into the blockchain and transaction identifier with
+    /// box index in the transaction outputs serialized to the byte array.
+    ExtractCreationInfo(ExtractCreationInfo),
+    /// Box id, Blake2b256 hash of this box's content, basically equals to `blake2b256(bytes)`
+    ExtractId(ExtractId),
     /// Collection, get element by index
     ByIndex(ByIndex),
     /// Collection size
@@ -99,14 +117,18 @@ pub enum Expr {
     Map(Map),
     /// Collection filter op
     Filter(Filter),
+    /// Tests whether a predicate holds for at least one element of this collection
+    Exists(Exists),
     /// Tuple field access
     SelectField(SelectField),
-    /// Box monetary value
-    ExtractAmount(ExtractAmount),
     /// Bool to SigmaProp
     BoolToSigmaProp(BoolToSigmaProp),
     /// Upcast numeric value
     Upcast(Upcast),
+    /// Create proveDlog from GroupElement(PK)
+    CreateProveDlog(CreateProveDlog),
+    /// Extract serialized bytes of a SigmaProp value
+    SigmaPropBytes(SigmaPropBytes),
 }
 
 impl Expr {
@@ -143,6 +165,13 @@ impl Expr {
             Expr::ByIndex(op) => op.op_code(),
             Expr::ExtractScriptBytes(op) => op.op_code(),
             Expr::SizeOf(op) => op.op_code(),
+            Expr::CreateProveDlog(op) => op.op_code(),
+            Expr::ExtractCreationInfo(op) => op.op_code(),
+            Expr::Exists(op) => op.op_code(),
+            Expr::ExtractId(op) => op.op_code(),
+            Expr::SigmaPropBytes(op) => op.op_code(),
+            Expr::OptionIsDefined(op) => op.op_code(),
+            Expr::OptionGetOrElse(op) => op.op_code(),
         }
     }
 
@@ -179,6 +208,13 @@ impl Expr {
             Expr::ByIndex(v) => v.tpe(),
             Expr::ExtractScriptBytes(v) => v.tpe(),
             Expr::SizeOf(v) => v.tpe(),
+            Expr::CreateProveDlog(v) => v.tpe(),
+            Expr::ExtractCreationInfo(v) => v.tpe(),
+            Expr::Exists(v) => v.tpe(),
+            Expr::ExtractId(v) => v.tpe(),
+            Expr::SigmaPropBytes(v) => v.tpe(),
+            Expr::OptionIsDefined(v) => v.tpe(),
+            Expr::OptionGetOrElse(v) => v.tpe(),
         }
     }
 
diff --git a/ergotree-ir/src/mir/extract_amount.rs b/ergotree-ir/src/mir/extract_amount.rs
index bbf5c37c0..d860e81d2 100644
--- a/ergotree-ir/src/mir/extract_amount.rs
+++ b/ergotree-ir/src/mir/extract_amount.rs
@@ -16,6 +16,8 @@ pub struct ExtractAmount {
 }
 
 impl ExtractAmount {
+    pub(crate) const OP_CODE: OpCode = OpCode::EXTRACT_AMOUNT;
+
     /// Create new object, returns an error if any of the requirements failed
     pub fn new(input: Expr) -> Result<Self, InvalidArgumentError> {
         input.check_post_eval_tpe(SType::SBox)?;
@@ -30,7 +32,7 @@ impl ExtractAmount {
     }
 
     pub(crate) fn op_code(&self) -> OpCode {
-        OpCode::EXTRACT_AMOUNT
+        Self::OP_CODE
     }
 }
 
diff --git a/ergotree-ir/src/mir/extract_creation_info.rs b/ergotree-ir/src/mir/extract_creation_info.rs
new file mode 100644
index 000000000..62d130988
--- /dev/null
+++ b/ergotree-ir/src/mir/extract_creation_info.rs
@@ -0,0 +1,69 @@
+use crate::serialization::op_code::OpCode;
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+use crate::types::stuple::STuple;
+use crate::types::stype::SType;
+
+use super::expr::Expr;
+use super::expr::InvalidArgumentError;
+
+/// Tuple of height when block got included into the blockchain and transaction identifier with
+/// box index in the transaction outputs serialized to the byte array.
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ExtractCreationInfo {
+    /// Box (SBox type)
+    pub input: Box<Expr>,
+}
+
+impl ExtractCreationInfo {
+    pub(crate) const OP_CODE: OpCode = OpCode::EXTRACT_CREATION_INFO;
+
+    /// Create new object, returns an error if any of the requirements failed
+    pub fn new(input: Expr) -> Result<Self, InvalidArgumentError> {
+        input.check_post_eval_tpe(SType::SBox)?;
+        Ok(ExtractCreationInfo {
+            input: input.into(),
+        })
+    }
+
+    /// Type
+    pub fn tpe(&self) -> SType {
+        SType::STuple(STuple::pair(SType::SInt, SType::SColl(SType::SByte.into())))
+    }
+
+    pub(crate) fn op_code(&self) -> OpCode {
+        Self::OP_CODE
+    }
+}
+
+impl SigmaSerializable for ExtractCreationInfo {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), std::io::Error> {
+        self.input.sigma_serialize(w)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        Ok(ExtractCreationInfo {
+            input: Expr::sigma_parse(r)?.into(),
+        })
+    }
+}
+
+#[cfg(test)]
+#[cfg(feature = "arbitrary")]
+mod tests {
+    use crate::mir::global_vars::GlobalVars;
+    use crate::serialization::sigma_serialize_roundtrip;
+
+    use super::*;
+
+    #[test]
+    fn ser_roundtrip() {
+        let e: Expr = ExtractCreationInfo {
+            input: Box::new(GlobalVars::SelfBox.into()),
+        }
+        .into();
+        assert_eq![sigma_serialize_roundtrip(&e), e];
+    }
+}
diff --git a/ergotree-ir/src/mir/extract_id.rs b/ergotree-ir/src/mir/extract_id.rs
new file mode 100644
index 000000000..c4cb5fd9a
--- /dev/null
+++ b/ergotree-ir/src/mir/extract_id.rs
@@ -0,0 +1,65 @@
+use crate::serialization::op_code::OpCode;
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+use crate::types::stype::SType;
+
+use super::expr::Expr;
+use super::expr::InvalidArgumentError;
+
+/// Box id, Blake2b256 hash of this box's content, basically equals to `blake2b256(bytes)`
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ExtractId {
+    /// Box (SBox type)
+    pub input: Box<Expr>,
+}
+
+impl ExtractId {
+    pub(crate) const OP_CODE: OpCode = OpCode::EXTRACT_ID;
+
+    /// Create new object, returns an error if any of the requirements failed
+    pub fn new(input: Expr) -> Result<Self, InvalidArgumentError> {
+        input.check_post_eval_tpe(SType::SBox)?;
+        Ok(ExtractId {
+            input: input.into(),
+        })
+    }
+
+    /// Type
+    pub fn tpe(&self) -> SType {
+        SType::SColl(SType::SByte.into())
+    }
+
+    pub(crate) fn op_code(&self) -> OpCode {
+        Self::OP_CODE
+    }
+}
+
+impl SigmaSerializable for ExtractId {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), std::io::Error> {
+        self.input.sigma_serialize(w)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        Ok(ExtractId::new(Expr::sigma_parse(r)?)?)
+    }
+}
+
+#[cfg(test)]
+#[cfg(feature = "arbitrary")]
+mod tests {
+    use crate::mir::global_vars::GlobalVars;
+    use crate::serialization::sigma_serialize_roundtrip;
+
+    use super::*;
+
+    #[test]
+    fn ser_roundtrip() {
+        let e: Expr = ExtractId {
+            input: Box::new(GlobalVars::SelfBox.into()),
+        }
+        .into();
+        assert_eq![sigma_serialize_roundtrip(&e), e];
+    }
+}
diff --git a/ergotree-ir/src/mir/option_get.rs b/ergotree-ir/src/mir/option_get.rs
index 2913018e1..961b05cc8 100644
--- a/ergotree-ir/src/mir/option_get.rs
+++ b/ergotree-ir/src/mir/option_get.rs
@@ -15,6 +15,8 @@ pub struct OptionGet {
 }
 
 impl OptionGet {
+    pub(crate) const OP_CODE: OpCode = OpCode::OPTION_GET;
+
     /// Create new object, returns an error if any of the requirements failed
     pub fn new(input: Expr) -> Result<Self, InvalidArgumentError> {
         match input.post_eval_tpe() {
@@ -29,7 +31,7 @@ impl OptionGet {
     }
 
     pub(crate) fn op_code(&self) -> OpCode {
-        OpCode::OPTION_GET
+        Self::OP_CODE
     }
 
     /// Type
diff --git a/ergotree-ir/src/mir/option_get_or_else.rs b/ergotree-ir/src/mir/option_get_or_else.rs
new file mode 100644
index 000000000..e100ebae3
--- /dev/null
+++ b/ergotree-ir/src/mir/option_get_or_else.rs
@@ -0,0 +1,94 @@
+use super::expr::Expr;
+use super::expr::InvalidArgumentError;
+use crate::serialization::op_code::OpCode;
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+use crate::types::stype::SType;
+
+/// Returns the Option's value or error if no value
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct OptionGetOrElse {
+    /// Object of SOption type
+    pub input: Box<Expr>,
+    /// Default value if option is empty
+    pub default: Box<Expr>,
+}
+
+impl OptionGetOrElse {
+    pub(crate) const OP_CODE: OpCode = OpCode::OPTION_GET_OR_ELSE;
+
+    /// Create new object, returns an error if any of the requirements failed
+    pub fn new(input: Expr, default: Expr) -> Result<Self, InvalidArgumentError> {
+        match input.post_eval_tpe() {
+            SType::SOption(elem_type) => {
+                default.check_post_eval_tpe(*elem_type)?;
+                Ok(OptionGetOrElse {
+                    input: Box::new(input),
+                    default: Box::new(default),
+                })
+            }
+            _ => Err(InvalidArgumentError(format!(
+                "expected OptionGetOrElse::input type to be SOption, got: {0:?}",
+                input.tpe(),
+            ))),
+        }
+    }
+
+    pub(crate) fn op_code(&self) -> OpCode {
+        Self::OP_CODE
+    }
+
+    /// Type
+    pub fn tpe(&self) -> SType {
+        match self.input.tpe() {
+            SType::SOption(o) => *o,
+            _ => panic!(
+                "expected OptionGetOrElse::input type to be SOption, got: {0:?}",
+                self.input.tpe()
+            ),
+        }
+    }
+}
+
+impl SigmaSerializable for OptionGetOrElse {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), std::io::Error> {
+        self.input.sigma_serialize(w)?;
+        self.default.sigma_serialize(w)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        let input = Expr::sigma_parse(r)?;
+        let default = Expr::sigma_parse(r)?;
+        Ok(OptionGetOrElse::new(input, default)?)
+    }
+}
+
+#[cfg(test)]
+#[cfg(feature = "arbitrary")]
+mod tests {
+    use super::*;
+    use crate::mir::constant::Constant;
+    use crate::mir::expr::Expr;
+    use crate::mir::extract_reg_as::ExtractRegisterAs;
+    use crate::mir::global_vars::GlobalVars;
+    use crate::serialization::sigma_serialize_roundtrip;
+    use crate::types::stype::SType;
+
+    #[test]
+    fn ser_roundtrip() {
+        let get_reg_expr: Expr = ExtractRegisterAs::new(
+            GlobalVars::SelfBox.into(),
+            0,
+            SType::SOption(SType::SLong.into()),
+        )
+        .unwrap()
+        .into();
+        let default_expr: Constant = 1i64.into();
+        let e: Expr = OptionGetOrElse::new(get_reg_expr, default_expr.into())
+            .unwrap()
+            .into();
+        assert_eq![sigma_serialize_roundtrip(&e), e];
+    }
+}
diff --git a/ergotree-ir/src/mir/option_is_defined.rs b/ergotree-ir/src/mir/option_is_defined.rs
new file mode 100644
index 000000000..c5d86dc46
--- /dev/null
+++ b/ergotree-ir/src/mir/option_is_defined.rs
@@ -0,0 +1,75 @@
+use super::expr::Expr;
+use super::expr::InvalidArgumentError;
+use crate::serialization::op_code::OpCode;
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+use crate::types::stype::SType;
+
+/// Returns false if the option is None, true otherwise.
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct OptionIsDefined {
+    /// Object of SOption type
+    pub input: Box<Expr>,
+}
+
+impl OptionIsDefined {
+    pub(crate) const OP_CODE: OpCode = OpCode::OPTION_IS_DEFINED;
+
+    /// Create new object, returns an error if any of the requirements failed
+    pub fn new(input: Expr) -> Result<Self, InvalidArgumentError> {
+        match input.post_eval_tpe() {
+            SType::SOption(_) => Ok(OptionIsDefined {
+                input: Box::new(input),
+            }),
+            _ => Err(InvalidArgumentError(format!(
+                "expected OptionIsDefined::input type to be SOption, got: {0:?}",
+                input.tpe(),
+            ))),
+        }
+    }
+
+    pub(crate) fn op_code(&self) -> OpCode {
+        Self::OP_CODE
+    }
+
+    /// Type
+    pub fn tpe(&self) -> SType {
+        SType::SBoolean
+    }
+}
+
+impl SigmaSerializable for OptionIsDefined {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), std::io::Error> {
+        self.input.sigma_serialize(w)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        Ok(OptionIsDefined::new(Expr::sigma_parse(r)?)?)
+    }
+}
+
+#[cfg(test)]
+#[cfg(feature = "arbitrary")]
+mod tests {
+    use super::*;
+    use crate::mir::expr::Expr;
+    use crate::mir::extract_reg_as::ExtractRegisterAs;
+    use crate::mir::global_vars::GlobalVars;
+    use crate::serialization::sigma_serialize_roundtrip;
+    use crate::types::stype::SType;
+
+    #[test]
+    fn ser_roundtrip() {
+        let get_reg_expr: Expr = ExtractRegisterAs::new(
+            GlobalVars::SelfBox.into(),
+            0,
+            SType::SOption(SType::SLong.into()),
+        )
+        .unwrap()
+        .into();
+        let e: Expr = OptionIsDefined::new(get_reg_expr).unwrap().into();
+        assert_eq![sigma_serialize_roundtrip(&e), e];
+    }
+}
diff --git a/ergotree-ir/src/mir/sigma_prop_bytes.rs b/ergotree-ir/src/mir/sigma_prop_bytes.rs
new file mode 100644
index 000000000..5447bf6c0
--- /dev/null
+++ b/ergotree-ir/src/mir/sigma_prop_bytes.rs
@@ -0,0 +1,72 @@
+use crate::serialization::op_code::OpCode;
+use crate::serialization::sigma_byte_reader::SigmaByteRead;
+use crate::serialization::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::SerializationError;
+use crate::serialization::SigmaSerializable;
+use crate::types::stype::SType;
+
+use super::expr::Expr;
+use super::expr::InvalidArgumentError;
+
+/// Extract serialized bytes of a SigmaProp value
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct SigmaPropBytes {
+    /// SigmaProp value
+    pub input: Box<Expr>,
+}
+
+impl SigmaPropBytes {
+    pub(crate) const OP_CODE: OpCode = OpCode::SIGMA_PROP_BYTES;
+
+    /// Create new object, returns an error if any of the requirements failed
+    pub fn new(input: Expr) -> Result<Self, InvalidArgumentError> {
+        input.check_post_eval_tpe(SType::SSigmaProp)?;
+        Ok(SigmaPropBytes {
+            input: input.into(),
+        })
+    }
+
+    /// Type
+    pub fn tpe(&self) -> SType {
+        SType::SColl(SType::SByte.into())
+    }
+
+    pub(crate) fn op_code(&self) -> OpCode {
+        Self::OP_CODE
+    }
+}
+
+impl SigmaSerializable for SigmaPropBytes {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), std::io::Error> {
+        self.input.sigma_serialize(w)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        Ok(SigmaPropBytes::new(Expr::sigma_parse(r)?)?)
+    }
+}
+
+#[cfg(feature = "arbitrary")]
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::mir::constant::Constant;
+    use crate::serialization::sigma_serialize_roundtrip;
+    use crate::sigma_protocol::sigma_boolean::SigmaProp;
+    use proptest::prelude::*;
+
+    proptest! {
+
+        #![proptest_config(ProptestConfig::with_cases(8))]
+
+        #[test]
+        fn ser_roundtrip(v in any::<SigmaProp>()) {
+            let input: Constant = v.into();
+            let e: Expr = SigmaPropBytes {
+                input: Box::new(input.into()),
+            }
+            .into();
+            prop_assert_eq![sigma_serialize_roundtrip(&e), e];
+        }
+    }
+}
diff --git a/ergotree-ir/src/serialization/expr.rs b/ergotree-ir/src/serialization/expr.rs
index 889ab653f..9c846922b 100644
--- a/ergotree-ir/src/serialization/expr.rs
+++ b/ergotree-ir/src/serialization/expr.rs
@@ -9,6 +9,7 @@ use crate::mir::block::BlockValue;
 use crate::mir::bool_to_sigma::BoolToSigmaProp;
 use crate::mir::calc_blake2b256::CalcBlake2b256;
 use crate::mir::coll_by_index::ByIndex;
+use crate::mir::coll_exists::Exists;
 use crate::mir::coll_filter::Filter;
 use crate::mir::coll_fold::Fold;
 use crate::mir::coll_map::Map;
@@ -18,8 +19,11 @@ use crate::mir::collection::coll_sigma_parse;
 use crate::mir::collection::coll_sigma_serialize;
 use crate::mir::constant::Constant;
 use crate::mir::constant::ConstantPlaceholder;
+use crate::mir::create_provedlog::CreateProveDlog;
 use crate::mir::expr::Expr;
 use crate::mir::extract_amount::ExtractAmount;
+use crate::mir::extract_creation_info::ExtractCreationInfo;
+use crate::mir::extract_id::ExtractId;
 use crate::mir::extract_reg_as::ExtractRegisterAs;
 use crate::mir::extract_script_bytes::ExtractScriptBytes;
 use crate::mir::func_value::FuncValue;
@@ -28,9 +32,12 @@ use crate::mir::if_op::If;
 use crate::mir::logical_not::LogicalNot;
 use crate::mir::method_call::MethodCall;
 use crate::mir::option_get::OptionGet;
+use crate::mir::option_get_or_else::OptionGetOrElse;
+use crate::mir::option_is_defined::OptionIsDefined;
 use crate::mir::or::Or;
 use crate::mir::property_call::PropertyCall;
 use crate::mir::select_field::SelectField;
+use crate::mir::sigma_prop_bytes::SigmaPropBytes;
 use crate::mir::upcast::Upcast;
 use crate::mir::val_def::ValDef;
 use crate::mir::val_use::ValUse;
@@ -85,6 +92,13 @@ impl SigmaSerializable for Expr {
                     Expr::ByIndex(op) => op.sigma_serialize(w),
                     Expr::ExtractScriptBytes(op) => op.sigma_serialize(w),
                     Expr::SizeOf(op) => op.sigma_serialize(w),
+                    Expr::CreateProveDlog(op) => op.sigma_serialize(w),
+                    Expr::ExtractCreationInfo(op) => op.sigma_serialize(w),
+                    Expr::Exists(op) => op.sigma_serialize(w),
+                    Expr::ExtractId(op) => op.sigma_serialize(w),
+                    Expr::SigmaPropBytes(op) => op.sigma_serialize(w),
+                    Expr::OptionIsDefined(op) => op.sigma_serialize(w),
+                    Expr::OptionGetOrElse(op) => op.sigma_serialize(w),
                 }
             }
         }
@@ -125,9 +139,13 @@ impl SigmaSerializable for Expr {
                 OpCode::PROPERTY_CALL => Ok(Expr::ProperyCall(PropertyCall::sigma_parse(r)?)),
                 OpCode::METHOD_CALL => Ok(Expr::MethodCall(MethodCall::sigma_parse(r)?)),
                 OpCode::CONTEXT => Ok(Expr::Context),
-                OpCode::OPTION_GET => Ok(OptionGet::sigma_parse(r)?.into()),
+                OptionGet::OP_CODE => Ok(OptionGet::sigma_parse(r)?.into()),
+                OptionIsDefined::OP_CODE => Ok(OptionIsDefined::sigma_parse(r)?.into()),
+                OptionGetOrElse::OP_CODE => Ok(OptionGetOrElse::sigma_parse(r)?.into()),
                 ExtractRegisterAs::OP_CODE => Ok(ExtractRegisterAs::sigma_parse(r)?.into()),
                 ExtractScriptBytes::OP_CODE => Ok(ExtractScriptBytes::sigma_parse(r)?.into()),
+                ExtractCreationInfo::OP_CODE => Ok(ExtractCreationInfo::sigma_parse(r)?.into()),
+                ExtractId::OP_CODE => Ok(ExtractId::sigma_parse(r)?.into()),
                 OpCode::EQ => Ok(bin_op_sigma_parse(RelationOp::Eq.into(), r)?),
                 OpCode::NEQ => Ok(bin_op_sigma_parse(RelationOp::NEq.into(), r)?),
                 OpCode::LOGICAL_NOT => Ok(LogicalNot::sigma_parse(r)?.into()),
@@ -148,7 +166,7 @@ impl SigmaSerializable for Expr {
                 OpCode::APPLY => Ok(Expr::Apply(Apply::sigma_parse(r)?)),
                 OpCode::VAL_DEF => Ok(Expr::ValDef(ValDef::sigma_parse(r)?)),
                 OpCode::VAL_USE => Ok(Expr::ValUse(ValUse::sigma_parse(r)?)),
-                OpCode::EXTRACT_AMOUNT => Ok(Expr::ExtractAmount(ExtractAmount::sigma_parse(r)?)),
+                ExtractAmount::OP_CODE => Ok(Expr::ExtractAmount(ExtractAmount::sigma_parse(r)?)),
                 OpCode::SELECT_FIELD => Ok(Expr::SelectField(SelectField::sigma_parse(r)?)),
                 OpCode::CALC_BLAKE2B256 => Ok(CalcBlake2b256::sigma_parse(r)?.into()),
                 And::OP_CODE => Ok(And::sigma_parse(r)?.into()),
@@ -157,11 +175,14 @@ impl SigmaSerializable for Expr {
                 OpCode::COLL_OF_BOOL_CONST => Ok(bool_const_coll_sigma_parse(r)?.into()),
                 Map::OP_CODE => Ok(Map::sigma_parse(r)?.into()),
                 Filter::OP_CODE => Ok(Filter::sigma_parse(r)?.into()),
+                Exists::OP_CODE => Ok(Exists::sigma_parse(r)?.into()),
                 BoolToSigmaProp::OP_CODE => Ok(BoolToSigmaProp::sigma_parse(r)?.into()),
                 Upcast::OP_CODE => Ok(Upcast::sigma_parse(r)?.into()),
                 If::OP_CODE => Ok(If::sigma_parse(r)?.into()),
                 ByIndex::OP_CODE => Ok(ByIndex::sigma_parse(r)?.into()),
                 SizeOf::OP_CODE => Ok(SizeOf::sigma_parse(r)?.into()),
+                CreateProveDlog::OP_CODE => Ok(CreateProveDlog::sigma_parse(r)?.into()),
+                SigmaPropBytes::OP_CODE => Ok(SigmaPropBytes::sigma_parse(r)?.into()),
                 o => Err(SerializationError::NotImplementedOpCode(format!(
                     "{0}(shift {1})",
                     o.value(),
@@ -193,7 +214,6 @@ mod tests {
         }
     }
 
-    #[test]
     #[test]
     fn full_age_usd_bank_contract() {
         // almost full version of
diff --git a/ergotree-ir/src/sigma_protocol/sigma_boolean.rs b/ergotree-ir/src/sigma_protocol/sigma_boolean.rs
index 81aa90cbc..feddb59bb 100644
--- a/ergotree-ir/src/sigma_protocol/sigma_boolean.rs
+++ b/ergotree-ir/src/sigma_protocol/sigma_boolean.rs
@@ -1,7 +1,10 @@
 //! Sigma boolean types
 
 use super::dlog_group::EcPoint;
+use crate::ergo_tree::ErgoTree;
+use crate::mir::constant::Constant;
 use crate::serialization::op_code::OpCode;
+use crate::serialization::SigmaSerializable;
 use std::convert::TryFrom;
 use std::convert::TryInto;
 
@@ -103,6 +106,15 @@ impl SigmaProp {
     pub fn value(&self) -> &SigmaBoolean {
         &self.0
     }
+
+    /// Serialized bytes of a SigmaProp value
+    pub fn prop_bytes(&self) -> Vec<u8> {
+        // in order to have comparisons like  `box.propositionBytes == pk.propBytes` we need to make sure
+        // the same serialization method is used in both cases
+        let c: Constant = self.clone().into();
+        let ergo_tree = ErgoTree::without_segregation(c.into());
+        ergo_tree.sigma_serialize_bytes()
+    }
 }
 
 impl TryFrom<SigmaProp> for bool {
@@ -113,6 +125,14 @@ impl TryFrom<SigmaProp> for bool {
     }
 }
 
+impl From<ProveDlog> for SigmaProp {
+    fn from(pd: ProveDlog) -> Self {
+        SigmaProp(SigmaBoolean::ProofOfKnowledge(
+            SigmaProofOfKnowledgeTree::ProveDlog(pd),
+        ))
+    }
+}
+
 /// Arbitrary impl for ProveDlog
 #[cfg(feature = "arbitrary")]
 mod arbitrary {


Test patch:
diff --git a/ergotree-interpreter/proptest-regressions/eval/and.txt b/ergotree-interpreter/proptest-regressions/eval/and.txt
new file mode 100644
index 000000000..117deefc6
--- /dev/null
+++ b/ergotree-interpreter/proptest-regressions/eval/and.txt
@@ -0,0 +1,7 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc f0b38d0ed6ef47d0759e2c2f785f333a2370916b274efa3feba53e6ab142419f # shrinks to bools = []
diff --git a/ergotree-interpreter/proptest-regressions/eval/bin_op.txt b/ergotree-interpreter/proptest-regressions/eval/bin_op.txt
new file mode 100644
index 000000000..31f845812
--- /dev/null
+++ b/ergotree-interpreter/proptest-regressions/eval/bin_op.txt
@@ -0,0 +1,8 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc 244d861f4b65cac1f4a1a69ed6f4026fb5664ff0cbb2dc0de2f4be4949614f7a # shrinks to l = false, r = false
+cc 3b89f1d49ec8740e727880deab728e5cd056d3453c97ec4d4286b9c088135944 # shrinks to v = Constant { tpe: SColl(SLong), v: Coll(WrappedColl { elem_tpe: SLong, items: [Long(-26107023029569770)] }) }
diff --git a/ergotree-interpreter/proptest-regressions/eval/bool_to_sigma.txt b/ergotree-interpreter/proptest-regressions/eval/bool_to_sigma.txt
new file mode 100644
index 000000000..49cf1e693
--- /dev/null
+++ b/ergotree-interpreter/proptest-regressions/eval/bool_to_sigma.txt
@@ -0,0 +1,7 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc 82372d4a1cc45af6d727bcb3a137e4e357deee59eeb5b9d595db7d6f20bceee9 # shrinks to b = false
diff --git a/ergotree-interpreter/proptest-regressions/eval/calc_blake2b256.txt b/ergotree-interpreter/proptest-regressions/eval/calc_blake2b256.txt
new file mode 100644
index 000000000..e82d734a1
--- /dev/null
+++ b/ergotree-interpreter/proptest-regressions/eval/calc_blake2b256.txt
@@ -0,0 +1,7 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc f00118026ecb6e8293e2c01d92e78ed8e5df32fc0432f5670870c56a3320d1e9 # shrinks to byte_array = []
diff --git a/ergotree-interpreter/proptest-regressions/eval/collection.txt b/ergotree-interpreter/proptest-regressions/eval/collection.txt
new file mode 100644
index 000000000..b84917796
--- /dev/null
+++ b/ergotree-interpreter/proptest-regressions/eval/collection.txt
@@ -0,0 +1,8 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc c2e2b0c41fedb6b417f86423f8e3cd501e31d579d6c972035405f290e8b2b86a # shrinks to bools = []
+cc baaf903b7b380f701bb84766ee8c3c4d0155aca37dd0ee1bdc0837778c87833e # shrinks to bytes = []
diff --git a/ergotree-interpreter/proptest-regressions/eval/or.txt b/ergotree-interpreter/proptest-regressions/eval/or.txt
new file mode 100644
index 000000000..89eb29054
--- /dev/null
+++ b/ergotree-interpreter/proptest-regressions/eval/or.txt
@@ -0,0 +1,7 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc 7076642c1c345eaf18aabfbd675136788e848415c20c596d2bf33caa1960ab3d # shrinks to bools = []
diff --git a/ergotree-interpreter/proptest-regressions/sigma_protocol/verifier.txt b/ergotree-interpreter/proptest-regressions/sigma_protocol/verifier.txt
new file mode 100644
index 000000000..bd39e49b1
--- /dev/null
+++ b/ergotree-interpreter/proptest-regressions/sigma_protocol/verifier.txt
@@ -0,0 +1,7 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc 175fd85df7535191e7a49ae8a3022e65a15120777adfa667511e8fd2a573ac2a # shrinks to secret = DlogProverInput { w: Scalar(Scalar4x64([2258665393118672676, 9815237481816509392, 12439591203699167698, 12195836321791745360])) }, message = [0]