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

Issue:
Consider switching `Constant::v` type from `Value`
### Motivation
- Some of the variants of `Value` cannot be serialized (see https://github.com/ergoplatform/sigma-rust/blob/dceb878840a57ac25695818a2137a04e2be21535/ergotree-ir/src/serialization/data.rs#L74-L79
), thus hinting that separate enum for constant values (`Literal`?) might be a better fit. Or even "embedded" enum for such values in `Constant`. After all, `Value` represents "runtime" values during the interpreter run and might not be suited to represent literal values in code.
- `Value::CBox` holds a reference (`IrBoxId`) to the box stored in the interpreter's context (`Context`) which is unavailable during the serialization.  


Discussion / hints:

Fix patch:
diff --git a/ergotree-interpreter/src/eval/expr.rs b/ergotree-interpreter/src/eval/expr.rs
index 37310b245..e1d35ceb3 100644
--- a/ergotree-interpreter/src/eval/expr.rs
+++ b/ergotree-interpreter/src/eval/expr.rs
@@ -10,7 +10,7 @@ impl Evaluable for Expr {
     fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
         ctx.cost_accum.add_cost_of(self)?;
         match self {
-            Expr::Const(c) => Ok(c.v.clone()),
+            Expr::Const(c) => Ok(Value::from(c.v.clone())),
             Expr::SubstConstants(_) => Err(EvalError::NotImplementedYet("Expr::SubstConstants")),
             Expr::ByteArrayToLong(op) => op.eval(env, ctx),
             Expr::ByteArrayToBigInt(op) => op.eval(env, ctx),
diff --git a/ergotree-interpreter/src/eval/extract_reg_as.rs b/ergotree-interpreter/src/eval/extract_reg_as.rs
index e57a44968..fa0cff558 100644
--- a/ergotree-interpreter/src/eval/extract_reg_as.rs
+++ b/ergotree-interpreter/src/eval/extract_reg_as.rs
@@ -16,7 +16,7 @@ impl Evaluable for ExtractRegisterAs {
                 .box_arena
                 .get(&ir_box_id)?
                 .get_register(self.register_id)
-                .map(|c| c.v),
+                .map(|c| Value::from(c.v)),
         )))
     }
 }
diff --git a/ergotree-interpreter/src/eval/sbox.rs b/ergotree-interpreter/src/eval/sbox.rs
index d700997d7..ddfbc1fba 100644
--- a/ergotree-interpreter/src/eval/sbox.rs
+++ b/ergotree-interpreter/src/eval/sbox.rs
@@ -24,7 +24,7 @@ pub(crate) static GET_REG_EVAL_FN: EvalFn = |_env, ctx, obj, args| {
                     .ok_or_else(|| EvalError::NotFound("register index is missing".to_string()))?
                     .try_extract_into::<i8>()?,
             )
-            .map(|c| c.v),
+            .map(|c| Value::from(c.v)),
     )))
 };
 
diff --git a/ergotree-interpreter/src/sigma_protocol/prover.rs b/ergotree-interpreter/src/sigma_protocol/prover.rs
index ddfd7e01c..3648094a3 100644
--- a/ergotree-interpreter/src/sigma_protocol/prover.rs
+++ b/ergotree-interpreter/src/sigma_protocol/prover.rs
@@ -915,10 +915,10 @@ mod tests {
     use crate::sigma_protocol::private_input::DhTupleProverInput;
     use crate::sigma_protocol::private_input::DlogProverInput;
     use ergotree_ir::mir::constant::Constant;
+    use ergotree_ir::mir::constant::Literal;
     use ergotree_ir::mir::expr::Expr;
     use ergotree_ir::mir::sigma_and::SigmaAnd;
     use ergotree_ir::mir::sigma_or::SigmaOr;
-    use ergotree_ir::mir::value::Value;
     use ergotree_ir::types::stype::SType;
     use sigma_test_util::force_any_val;
     use std::convert::TryFrom;
@@ -928,7 +928,7 @@ mod tests {
     fn test_prove_true_prop() {
         let bool_true_tree = ErgoTree::try_from(Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: Value::Boolean(true),
+            v: Literal::Boolean(true),
         }))
         .unwrap();
         let message = vec![0u8; 100];
@@ -949,7 +949,7 @@ mod tests {
     fn test_prove_false_prop() {
         let bool_false_tree = ErgoTree::try_from(Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: Value::Boolean(false),
+            v: Literal::Boolean(false),
         }))
         .unwrap();
         let message = vec![0u8; 100];
diff --git a/ergotree-ir/src/ergo_tree.rs b/ergotree-ir/src/ergo_tree.rs
index ca0b36702..9e99ddacb 100644
--- a/ergotree-ir/src/ergo_tree.rs
+++ b/ergotree-ir/src/ergo_tree.rs
@@ -596,7 +596,7 @@ mod tests {
     use super::*;
     use crate::address::AddressEncoder;
     use crate::address::NetworkPrefix;
-    use crate::mir::value::Value;
+    use crate::mir::constant::Literal;
     use proptest::prelude::*;
 
     proptest! {
@@ -728,7 +728,7 @@ mod tests {
     fn test_constant_segregation() {
         let expr = Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: Value::Boolean(true),
+            v: Literal::Boolean(true),
         });
         let ergo_tree = ErgoTree::new(ErgoTreeHeader::default(), &expr).unwrap();
         let bytes = ergo_tree.sigma_serialize_bytes().unwrap();
@@ -743,7 +743,7 @@ mod tests {
     fn test_constant_len() {
         let expr = Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: Value::Boolean(false),
+            v: Literal::Boolean(false),
         });
         let ergo_tree = ErgoTree::new(ErgoTreeHeader::v0(true), &expr).unwrap();
         assert_eq!(ergo_tree.constants_len().unwrap(), 1);
@@ -753,7 +753,7 @@ mod tests {
     fn test_get_constant() {
         let expr = Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: Value::Boolean(false),
+            v: Literal::Boolean(false),
         });
         let ergo_tree = ErgoTree::new(ErgoTreeHeader::v0(true), &expr).unwrap();
         assert_eq!(ergo_tree.constants_len().unwrap(), 1);
@@ -764,7 +764,7 @@ mod tests {
     fn test_set_constant() {
         let expr = Expr::Const(Constant {
             tpe: SType::SBoolean,
-            v: Value::Boolean(false),
+            v: Literal::Boolean(false),
         });
         let ergo_tree = ErgoTree::new(ErgoTreeHeader::v0(true), &expr).unwrap();
         let new_ergo_tree = ergo_tree.with_constant(0, true.into()).unwrap();
diff --git a/ergotree-ir/src/mir/bin_op.rs b/ergotree-ir/src/mir/bin_op.rs
index 990e2796a..e048663c3 100644
--- a/ergotree-ir/src/mir/bin_op.rs
+++ b/ergotree-ir/src/mir/bin_op.rs
@@ -267,8 +267,8 @@ mod tests {
 
     use super::*;
     use crate::mir::constant::Constant;
+    use crate::mir::constant::Literal::Boolean;
     use crate::mir::expr::Expr;
-    use crate::mir::value::Value::Boolean;
     use crate::serialization::sigma_serialize_roundtrip;
     use crate::serialization::SigmaSerializable;
     use proptest::prelude::*;
diff --git a/ergotree-ir/src/mir/constant.rs b/ergotree-ir/src/mir/constant.rs
index 62828d0f2..db6575190 100644
--- a/ergotree-ir/src/mir/constant.rs
+++ b/ergotree-ir/src/mir/constant.rs
@@ -2,23 +2,24 @@
 
 use crate::bigint256::BigInt256;
 use crate::ir_ergo_box::IrBoxId;
+use crate::mir::value::CollKind;
 use crate::sigma_protocol::sigma_boolean::SigmaBoolean;
 use crate::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree;
 use crate::sigma_protocol::sigma_boolean::{ProveDhTuple, ProveDlog};
 use crate::sigma_protocol::{dlog_group::EcPoint, sigma_boolean::SigmaProp};
+use crate::types::stuple::TupleItems;
 use crate::types::stype::LiftIntoSType;
 use crate::types::stype::SType;
 use impl_trait_for_tuples::impl_for_tuples;
+use std::convert::TryFrom;
 use std::convert::TryInto;
 
 mod constant_placeholder;
 
 pub use constant_placeholder::*;
 
-use super::value::CollKind;
 use super::value::NativeColl;
 use super::value::StoreWrapped;
-use super::value::Value;
 
 use thiserror::Error;
 
@@ -28,9 +29,119 @@ pub struct Constant {
     /// Constant type
     pub tpe: SType,
     /// Constant value
-    pub v: Value,
+    pub v: Literal,
 }
 
+#[derive(PartialEq, Eq, Debug, Clone)]
+/// Possible values for `Constant`
+pub enum Literal {
+    /// Boolean
+    Boolean(bool),
+    /// i8
+    Byte(i8),
+    /// Short
+    Short(i16),
+    /// Int
+    Int(i32),
+    /// Long
+    Long(i64),
+    /// Big integer
+    BigInt(BigInt256),
+    /// Sigma property
+    SigmaProp(Box<SigmaProp>),
+    /// GroupElement
+    GroupElement(Box<EcPoint>),
+    /// Ergo box ID
+    CBox(IrBoxId),
+    /// Collection
+    Coll(CollKind<Literal>),
+    /// Option type
+    Opt(Box<Option<Literal>>),
+    /// Tuple (arbitrary type values)
+    Tup(TupleItems<Literal>),
+}
+impl From<bool> for Literal {
+    fn from(v: bool) -> Literal {
+        Literal::Boolean(v)
+    }
+}
+
+impl From<i8> for Literal {
+    fn from(v: i8) -> Literal {
+        Literal::Byte(v)
+    }
+}
+
+impl From<i16> for Literal {
+    fn from(v: i16) -> Literal {
+        Literal::Short(v)
+    }
+}
+
+impl From<i32> for Literal {
+    fn from(v: i32) -> Literal {
+        Literal::Int(v)
+    }
+}
+
+impl From<i64> for Literal {
+    fn from(v: i64) -> Literal {
+        Literal::Long(v)
+    }
+}
+
+impl From<BigInt256> for Literal {
+    fn from(v: BigInt256) -> Literal {
+        Literal::BigInt(v)
+    }
+}
+
+impl From<SigmaProp> for Literal {
+    fn from(v: SigmaProp) -> Literal {
+        Literal::SigmaProp(Box::new(v))
+    }
+}
+
+impl From<EcPoint> for Literal {
+    fn from(v: EcPoint) -> Literal {
+        Literal::GroupElement(Box::new(v))
+    }
+}
+
+impl From<IrBoxId> for Literal {
+    fn from(b: IrBoxId) -> Self {
+        Literal::CBox(b)
+    }
+}
+
+impl From<Vec<u8>> for Literal {
+    fn from(v: Vec<u8>) -> Self {
+        Literal::Coll(CollKind::NativeColl(NativeColl::CollByte(
+            v.into_iter().map(|b| b as i8).collect(),
+        )))
+    }
+}
+
+impl From<Vec<i8>> for Literal {
+    fn from(v: Vec<i8>) -> Literal {
+        Literal::Coll(CollKind::NativeColl(NativeColl::CollByte(v)))
+    }
+}
+
+impl<T: LiftIntoSType + StoreWrapped + Into<Literal>> From<Vec<T>> for Literal {
+    fn from(v: Vec<T>) -> Self {
+        Literal::Coll(CollKind::WrappedColl {
+            elem_tpe: T::stype(),
+            items: v.into_iter().map(|i| i.into()).collect(),
+        })
+    }
+}
+
+impl<T: LiftIntoSType + Into<Literal>> From<Option<T>> for Literal {
+    fn from(opt: Option<T>) -> Self {
+        Literal::Opt(Box::new(opt.map(|e| e.into())))
+    }
+}
 impl From<bool> for Constant {
     fn from(v: bool) -> Constant {
         Constant {
@@ -80,7 +191,7 @@ impl From<SigmaProp> for Constant {
     fn from(v: SigmaProp) -> Constant {
         Constant {
             tpe: SType::SSigmaProp,
-            v: Box::new(v).into(),
+            v: v.into(),
         }
     }
 }
@@ -107,9 +218,7 @@ impl From<Vec<u8>> for Constant {
     fn from(v: Vec<u8>) -> Self {
         Constant {
             tpe: SType::SColl(Box::new(SType::SByte)),
-            v: Value::Coll(CollKind::NativeColl(NativeColl::CollByte(
-                v.into_iter().map(|b| b as i8).collect(),
-            ))),
+            v: v.into(),
         }
     }
 }
@@ -123,20 +232,23 @@ impl From<Vec<i8>> for Constant {
     }
 }
 
-impl<T: LiftIntoSType + StoreWrapped + Into<Value>> From<Vec<T>> for Constant {
+impl<T: LiftIntoSType + StoreWrapped + Into<Constant>> From<Vec<T>> for Constant {
     fn from(v: Vec<T>) -> Self {
         Constant {
             tpe: Vec::<T>::stype(),
-            v: v.into(),
+            v: Literal::Coll(CollKind::WrappedColl {
+                elem_tpe: T::stype(),
+                items: v.into_iter().map(|i| i.into().v).collect(),
+            }),
         }
     }
 }
 
-impl<T: LiftIntoSType + Into<Value>> From<Option<T>> for Constant {
+impl<T: LiftIntoSType + Into<Constant>> From<Option<T>> for Constant {
     fn from(opt: Option<T>) -> Self {
         Constant {
             tpe: SType::SOption(Box::new(T::stype())),
-            v: opt.into(),
+            v: Literal::Opt(Box::new(opt.map(|e| e.into().v))),
         }
     }
 }
@@ -167,7 +279,7 @@ impl From<BigInt256> for Constant {
     fn from(b: BigInt256) -> Self {
         Constant {
             tpe: SType::SBigInt,
-            v: b.into(),
+            v: Literal::BigInt(b),
         }
     }
 }
@@ -178,11 +290,11 @@ impl From<BigInt256> for Constant {
 impl Into<Constant> for Tuple {
     fn into(self) -> Constant {
         let constants: Vec<Constant> = [for_tuples!(  #( Tuple.into() ),* )].to_vec();
-        let (types, values): (Vec<SType>, Vec<Value>) =
+        let (types, values): (Vec<SType>, Vec<Literal>) =
             constants.into_iter().map(|c| (c.tpe, c.v)).unzip();
         Constant {
             tpe: SType::STuple(types.try_into().unwrap()),
-            v: Value::Tup(values.try_into().unwrap()),
+            v: Literal::Tup(values.try_into().unwrap()),
         }
     }
 }
@@ -211,12 +323,221 @@ pub trait TryExtractFrom<T>: Sized {
     fn try_extract_from(v: T) -> Result<Self, TryExtractFromError>;
 }
 
-impl<T: TryExtractFrom<Value>> TryExtractFrom<Constant> for T {
+impl<T: TryExtractFrom<Literal>> TryExtractFrom<Constant> for T {
     fn try_extract_from(cv: Constant) -> Result<Self, TryExtractFromError> {
         T::try_extract_from(cv.v)
     }
 }
 
+impl TryExtractFrom<Literal> for bool {
+    fn try_extract_from(cv: Literal) -> Result<bool, TryExtractFromError> {
+        match cv {
+            Literal::Boolean(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!(
+                "expected bool, found {:?}",
+                cv
+            ))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for i8 {
+    fn try_extract_from(cv: Literal) -> Result<i8, TryExtractFromError> {
+        match cv {
+            Literal::Byte(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!("expected i8, found {:?}", cv))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for i16 {
+    fn try_extract_from(cv: Literal) -> Result<i16, TryExtractFromError> {
+        match cv {
+            Literal::Short(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!("expected i16, found {:?}", cv))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for i32 {
+    fn try_extract_from(cv: Literal) -> Result<i32, TryExtractFromError> {
+        match cv {
+            Literal::Int(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!("expected i32, found {:?}", cv))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for i64 {
+    fn try_extract_from(cv: Literal) -> Result<i64, TryExtractFromError> {
+        match cv {
+            Literal::Long(v) => Ok(v),
+            _ => Err(TryExtractFromError(format!("expected i64, found {:?}", cv))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for EcPoint {
+    fn try_extract_from(cv: Literal) -> Result<EcPoint, TryExtractFromError> {
+        match cv {
+            Literal::GroupElement(v) => Ok(*v),
+            _ => Err(TryExtractFromError(format!(
+                "expected EcPoint, found {:?}",
+                cv
+            ))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for SigmaProp {
+    fn try_extract_from(cv: Literal) -> Result<SigmaProp, TryExtractFromError> {
+        match cv {
+            Literal::SigmaProp(v) => Ok(*v),
+            _ => Err(TryExtractFromError(format!(
+                "expected SigmaProp, found {:?}",
+                cv
+            ))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for IrBoxId {
+    fn try_extract_from(c: Literal) -> Result<Self, TryExtractFromError> {
+        match c {
+            Literal::CBox(b) => Ok(b),
+            _ => Err(TryExtractFromError(format!(
+                "expected IrErgoBox, found {:?}",
+                c
+            ))),
+        }
+    }
+}
+
+impl<T: TryExtractFrom<Literal> + StoreWrapped> TryExtractFrom<Literal> for Vec<T> {
+    fn try_extract_from(c: Literal) -> Result<Self, TryExtractFromError> {
+        match c {
+            Literal::Coll(coll) => match coll {
+                CollKind::WrappedColl {
+                    elem_tpe: _,
+                    items: v,
+                } => v.into_iter().map(T::try_extract_from).collect(),
+                _ => Err(TryExtractFromError(format!(
+                    "expected {:?}, found {:?}",
+                    std::any::type_name::<Self>(),
+                    coll
+                ))),
+            },
+            _ => Err(TryExtractFromError(format!(
+                "expected {:?}, found {:?}",
+                std::any::type_name::<Self>(),
+                c
+            ))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for Vec<i8> {
+    fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
+        match v {
+            Literal::Coll(v) => match v {
+                CollKind::NativeColl(NativeColl::CollByte(bs)) => Ok(bs),
+                _ => Err(TryExtractFromError(format!(
+                    "expected {:?}, found {:?}",
+                    std::any::type_name::<Self>(),
+                    v
+                ))),
+            },
+            _ => Err(TryExtractFromError(format!(
+                "expected {:?}, found {:?}",
+                std::any::type_name::<Self>(),
+                v
+            ))),
+        }
+    }
+}
+
+impl TryExtractFrom<Literal> for Vec<u8> {
+    fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
+        use crate::util::FromVecI8;
+        Vec::<i8>::try_extract_from(v).map(Vec::<u8>::from_vec_i8)
+    }
+}
+
+impl TryExtractFrom<Literal> for Literal {
+    fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
+        Ok(v)
+    }
+}
+
+impl TryExtractFrom<Literal> for BigInt256 {
+    fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
+        match v {
+            Literal::BigInt(bi) => Ok(bi),
+            _ => Err(TryExtractFromError(format!(
+                "expected {:?}, found {:?}",
+                std::any::type_name::<Self>(),
+                v
+            ))),
+        }
+    }
+}
+
+impl<T: TryExtractFrom<Literal>> TryExtractFrom<Literal> for Option<T> {
+    fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
+        match v {
+            Literal::Opt(opt) => opt.map(T::try_extract_from).transpose(),
+            _ => Err(TryExtractFromError(format!(
+                "expected Option, found {:?}",
+                v
+            ))),
+        }
+    }
+}
+
+#[impl_for_tuples(2, 4)]
+impl TryExtractFrom<Literal> for Tuple {
+    fn try_extract_from(v: Literal) -> Result<Self, TryExtractFromError> {
+        match v {
+            Literal::Tup(items) => {
+                let mut iter = items.iter();
+                Ok(for_tuples!( ( #(
+                                Tuple::try_extract_from(
+                                    iter
+                                        .next()
+                                        .cloned()
+                                        .ok_or_else(|| TryExtractFromError("not enough items in STuple".to_string()))?
+                                )?
+                                ),* ) ))
+            }
+            _ => Err(TryExtractFromError(format!(
+                "expected Context, found {:?}",
+                v
+            ))),
+        }
+    }
+}
+
+impl TryFrom<Literal> for ProveDlog {
+    type Error = TryExtractFromError;
+    fn try_from(cv: Literal) -> Result<Self, Self::Error> {
+        match cv {
+            Literal::SigmaProp(sp) => match sp.value() {
+                SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
+                    prove_dlog,
+                )) => Ok(prove_dlog.clone()),
+                _ => Err(TryExtractFromError(format!(
+                    "expected ProveDlog, found {:?}",
+                    sp
+                ))),
+            },
+            _ => Err(TryExtractFromError(format!(
+                "expected SigmaProp, found {:?}",
+                cv
+            ))),
+        }
+    }
+}
+
 #[cfg(feature = "arbitrary")]
 #[allow(clippy::unwrap_used)]
 #[allow(clippy::todo)]
@@ -225,6 +546,7 @@ pub(crate) mod arbitrary {
     use std::convert::TryFrom;
 
     use super::*;
+    use crate::mir::value::CollKind;
     use crate::types::stuple::STuple;
     use proptest::collection::vec;
     use proptest::prelude::*;
@@ -252,7 +574,7 @@ pub(crate) mod arbitrary {
     fn coll_from_constant(c: Constant, length: usize) -> Constant {
         Constant {
             tpe: SType::SColl(Box::new(c.tpe.clone())),
-            v: Value::Coll(if c.tpe == SType::SByte {
+            v: Literal::Coll(if c.tpe == SType::SByte {
                 let mut values: Vec<i8> = Vec::with_capacity(length);
                 let byte: i8 = c.v.try_extract_into().unwrap();
                 for _ in 0..length {
@@ -260,7 +582,7 @@ pub(crate) mod arbitrary {
                 }
                 CollKind::NativeColl(NativeColl::CollByte(values))
             } else {
-                let mut values: Vec<Value> = Vec::with_capacity(length);
+                let mut values: Vec<Literal> = Vec::with_capacity(length);
                 for _ in 0..length {
                     values.push(c.v.clone());
                 }
@@ -356,11 +678,11 @@ pub(crate) mod arbitrary {
                                         )
                                         .unwrap()
                                     ),
-                                    v: Value::Tup(
+                                    v: Literal::Tup(
                                         constants
                                             .into_iter()
                                             .map(|c| c.v)
-                                            .collect::<Vec<Value>>()
+                                            .collect::<Vec<Literal>>()
                                             .try_into()
                                             .unwrap()
                                     )
@@ -386,7 +708,7 @@ pub mod tests {
 
     fn test_constant_roundtrip<T>(v: T)
     where
-        T: TryExtractInto<T> + TryExtractFrom<Value> + Into<Constant> + fmt::Debug + Eq + Clone,
+        T: TryExtractInto<T> + TryExtractFrom<Literal> + Into<Constant> + fmt::Debug + Eq + Clone,
     {
         let constant: Constant = v.clone().into();
         let v_extracted: T = constant.try_extract_into::<T>().unwrap();
diff --git a/ergotree-ir/src/mir/expr.rs b/ergotree-ir/src/mir/expr.rs
index a4d62e11a..be5f240ce 100644
--- a/ergotree-ir/src/mir/expr.rs
+++ b/ergotree-ir/src/mir/expr.rs
@@ -27,6 +27,7 @@ use super::coll_slice::Slice;
 use super::collection::Collection;
 use super::constant::Constant;
 use super::constant::ConstantPlaceholder;
+use super::constant::Literal;
 use super::constant::TryExtractFrom;
 use super::constant::TryExtractFromError;
 use super::create_provedlog::CreateProveDlog;
@@ -59,7 +60,6 @@ use super::tuple::Tuple;
 use super::upcast::Upcast;
 use super::val_def::ValDef;
 use super::val_use::ValUse;
-use super::value::Value;
 use super::xor::Xor;
 
 extern crate derive_more;
@@ -307,7 +307,7 @@ impl Expr {
     }
 }
 
-impl<T: Into<Value> + LiftIntoSType> From<T> for Expr {
+impl<T: Into<Literal> + LiftIntoSType> From<T> for Expr {
     fn from(t: T) -> Self {
         Expr::Const(Constant {
             tpe: T::stype(),
diff --git a/ergotree-ir/src/mir/value.rs b/ergotree-ir/src/mir/value.rs
index 701ff4c9c..209f1e884 100644
--- a/ergotree-ir/src/mir/value.rs
+++ b/ergotree-ir/src/mir/value.rs
@@ -1,6 +1,5 @@
 //! Ergo data type
 
-use std::convert::TryFrom;
 use std::convert::TryInto;
 
 use impl_trait_for_tuples::impl_for_tuples;
@@ -8,15 +7,13 @@ use impl_trait_for_tuples::impl_for_tuples;
 use crate::bigint256::BigInt256;
 use crate::ir_ergo_box::IrBoxId;
 use crate::sigma_protocol::dlog_group::EcPoint;
-use crate::sigma_protocol::sigma_boolean::ProveDlog;
-use crate::sigma_protocol::sigma_boolean::SigmaBoolean;
-use crate::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree;
 use crate::sigma_protocol::sigma_boolean::SigmaProp;
 use crate::types::stuple::TupleItems;
 use crate::types::stype::LiftIntoSType;
 use crate::types::stype::SType;
 use crate::util::AsVecI8;
 
+use super::constant::Literal;
 use super::constant::TryExtractFrom;
 use super::constant::TryExtractFromError;
 use super::constant::TryExtractInto;
@@ -44,7 +41,7 @@ impl NativeColl {
 
 /// Collection elements
 #[derive(PartialEq, Eq, Debug, Clone)]
-pub enum CollKind {
+pub enum CollKind<T> {
     /// Collection elements stored as a vector of Rust values
     NativeColl(NativeColl),
     /// Collection elements stored as a vector of Value's
@@ -52,13 +49,20 @@ pub enum CollKind {
         /// Collection element type
         elem_tpe: SType,
         /// Collection elements
-        items: Vec<Value>,
+        items: Vec<T>,
     },
 }
 
-impl CollKind {
+impl<T> CollKind<T>
+where
+    T: PartialEq + Eq + Clone,
+    T: From<i8>,
+    i8: TryExtractFrom<T>,
+    Vec<i8>: TryExtractFrom<T>,
+    Vec<T>: TryExtractFrom<T>,
+{
     /// Build a collection from items, storing them as Rust types values when neccessary
-    pub fn from_vec(elem_tpe: SType, items: Vec<Value>) -> Result<CollKind, TryExtractFromError> {
+    pub fn from_vec(elem_tpe: SType, items: Vec<T>) -> Result<CollKind<T>, TryExtractFromError> {
         match elem_tpe {
             SType::SByte => items
                 .into_iter()
@@ -72,8 +76,8 @@ impl CollKind {
     /// Build a collection from items where each is a collection as well, storing them as Rust types values when neccessary
     pub fn from_vec_vec(
         elem_tpe: SType,
-        items: Vec<Value>,
-    ) -> Result<CollKind, TryExtractFromError> {
+        items: Vec<T>,
+    ) -> Result<CollKind<T>, TryExtractFromError> {
         match elem_tpe {
             SType::SByte => items
                 .into_iter()
@@ -82,7 +86,7 @@ impl CollKind {
                 .map(|bytes| CollKind::NativeColl(NativeColl::CollByte(bytes.concat()))),
             _ => items
                 .into_iter()
-                .map(|v| v.try_extract_into::<Vec<Value>>())
+                .map(|v| v.try_extract_into::<Vec<T>>())
                 .collect::<Result<Vec<_>, _>>()
                 .map(|v| CollKind::WrappedColl {
                     elem_tpe,
@@ -100,7 +104,7 @@ impl CollKind {
     }
 
     /// Return items, as vector of Values
-    pub fn as_vec(&self) -> Vec<Value> {
+    pub fn as_vec(&self) -> Vec<T> {
         match self {
             CollKind::NativeColl(NativeColl::CollByte(coll_byte)) => coll_byte
                 .clone()
@@ -148,7 +152,7 @@ pub enum Value {
     /// AVL tree
     AvlTree,
     /// Collection of values of the same type
-    Coll(CollKind),
+    Coll(CollKind<Value>),
     /// Tuple (arbitrary type values)
     Tup(TupleItems<Value>),
     /// Transaction(and blockchain) context info
@@ -198,6 +202,34 @@ impl<T: Into<Value>> From<Option<T>> for Value {
     }
 }
 
+impl From<Literal> for Value {
+    fn from(lit: Literal) -> Self {
+        match lit {
+            Literal::Boolean(b) => Value::Boolean(b),
+            Literal::Byte(b) => Value::Byte(b),
+            Literal::Short(s) => Value::Short(s),
+            Literal::Int(i) => Value::Int(i),
+            Literal::Long(l) => Value::Long(l),
+            Literal::BigInt(b) => Value::BigInt(b),
+            Literal::SigmaProp(s) => Value::SigmaProp(s),
+            Literal::GroupElement(e) => Value::GroupElement(e),
+            Literal::CBox(i) => Value::CBox(i),
+            Literal::Coll(coll) => {
+                let converted_coll = match coll {
+                    CollKind::NativeColl(n) => CollKind::NativeColl(n),
+                    CollKind::WrappedColl { elem_tpe, items } => CollKind::WrappedColl {
+                        elem_tpe,
+                        items: items.into_iter().map(Value::from).collect(),
+                    },
+                };
+                Value::Coll(converted_coll)
+            }
+            Literal::Opt(lit) => Value::Opt(Box::new(lit.into_iter().next().map(Value::from))),
+            Literal::Tup(t) => Value::Tup(t.mapped(Value::from)),
+        }
+    }
+}
+
 /// Marker trait to select types which stored as Vec of wrapped Value's
 pub trait StoreWrapped {}
 
@@ -394,27 +426,6 @@ impl<T: TryExtractFrom<Value> + StoreWrapped> TryExtractFrom<Vec<Value>> for Vec
     }
 }
 
-impl TryFrom<Value> for ProveDlog {
-    type Error = TryExtractFromError;
-    fn try_from(cv: Value) -> Result<Self, Self::Error> {
-        match cv {
-            Value::SigmaProp(sp) => match sp.value() {
-                SigmaBoolean::ProofOfKnowledge(SigmaProofOfKnowledgeTree::ProveDlog(
-                    prove_dlog,
-                )) => Ok(prove_dlog.clone()),
-                _ => Err(TryExtractFromError(format!(
-                    "expected ProveDlog, found {:?}",
-                    sp
-                ))),
-            },
-            _ => Err(TryExtractFromError(format!(
-                "expected SigmaProp, found {:?}",
-                cv
-            ))),
-        }
-    }
-}
-
 // impl TryExtractFrom<Value> for Rc<Context> {
 //     fn try_extract_from(v: Value) -> Result<Self, TryExtractFromError> {
 //         match v {
diff --git a/ergotree-ir/src/serialization/constant_store.rs b/ergotree-ir/src/serialization/constant_store.rs
index df9500ae8..9d1a5ef98 100644
--- a/ergotree-ir/src/serialization/constant_store.rs
+++ b/ergotree-ir/src/serialization/constant_store.rs
@@ -43,7 +43,7 @@ impl ConstantStore {
 #[cfg(feature = "arbitrary")]
 mod tests {
     use super::*;
-    use crate::mir::value::Value;
+    use crate::mir::constant::Literal;
     use crate::types::stype::SType;
 
     #[test]
@@ -57,7 +57,7 @@ mod tests {
     fn test_non_empty() {
         let c = Constant {
             tpe: SType::SBoolean,
-            v: Value::Boolean(true),
+            v: Literal::Boolean(true),
         };
         let s = ConstantStore::new(vec![c.clone()]);
         assert!(s.get(0).is_some());
@@ -70,7 +70,7 @@ mod tests {
     fn test_put() {
         let c = Constant {
             tpe: SType::SBoolean,
-            v: Value::Boolean(true),
+            v: Literal::Boolean(true),
         };
         let mut s = ConstantStore::empty();
         s.put(c.clone());
diff --git a/ergotree-ir/src/serialization/data.rs b/ergotree-ir/src/serialization/data.rs
index ff1ea18a2..19dbecebe 100644
--- a/ergotree-ir/src/serialization/data.rs
+++ b/ergotree-ir/src/serialization/data.rs
@@ -1,8 +1,8 @@
+use crate::mir::constant::Literal;
 use crate::mir::constant::TryExtractFromError;
 use crate::mir::constant::TryExtractInto;
 use crate::mir::value::CollKind;
 use crate::mir::value::NativeColl;
-use crate::mir::value::Value;
 use crate::serialization::SigmaSerializationError;
 use crate::serialization::SigmaSerializeResult;
 use crate::serialization::{
@@ -21,24 +21,23 @@ use std::convert::TryInto;
 pub struct DataSerializer {}
 
 impl DataSerializer {
-    pub fn sigma_serialize<W: SigmaByteWrite>(c: &Value, w: &mut W) -> SigmaSerializeResult {
+    pub fn sigma_serialize<W: SigmaByteWrite>(c: &Literal, w: &mut W) -> SigmaSerializeResult {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala#L26-L26
         Ok(match c {
-            Value::Boolean(v) => w.put_u8(if *v { 1 } else { 0 })?,
-            Value::Byte(v) => w.put_i8(*v)?,
-            Value::Short(v) => w.put_i16(*v)?,
-            Value::Int(v) => w.put_i32(*v)?,
-            Value::Long(v) => w.put_i64(*v)?,
-            Value::BigInt(v) => {
+            Literal::Boolean(v) => w.put_u8(if *v { 1 } else { 0 })?,
+            Literal::Byte(v) => w.put_i8(*v)?,
+            Literal::Short(v) => w.put_i16(*v)?,
+            Literal::Int(v) => w.put_i32(*v)?,
+            Literal::Long(v) => w.put_i64(*v)?,
+            Literal::BigInt(v) => {
                 let bytes = v.to_signed_bytes_be();
                 w.put_u16(bytes.len() as u16)?;
                 w.write_all(&bytes)?
             }
-            Value::GroupElement(ecp) => ecp.sigma_serialize(w)?,
-            Value::SigmaProp(s) => s.value().sigma_serialize(w)?,
-            Value::CBox(_) => return Err(SigmaSerializationError::NotImplementedYet("Box")),
-            Value::AvlTree => return Err(SigmaSerializationError::NotImplementedYet("AvlTree")),
-            Value::Coll(ct) => match ct {
+            Literal::GroupElement(ecp) => ecp.sigma_serialize(w)?,
+            Literal::SigmaProp(s) => s.value().sigma_serialize(w)?,
+            Literal::CBox(_) => return Err(SigmaSerializationError::NotImplementedYet("Box")),
+            Literal::Coll(ct) => match ct {
                 CollKind::NativeColl(NativeColl::CollByte(b)) => {
                     w.put_usize_as_u16_unwrapped(b.len())?;
                     w.write_all(b.clone().as_vec_u8().as_slice())?
@@ -64,32 +63,28 @@ impl DataSerializer {
                         .try_for_each(|e| DataSerializer::sigma_serialize(e, w))?
                 }
             },
-            Value::Tup(items) => items
+            Literal::Tup(items) => items
                 .iter()
                 .try_for_each(|i| DataSerializer::sigma_serialize(i, w))?,
             // unsupported, see
             // https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659
-            Value::Opt(_) => {
+            Literal::Opt(_) => {
                 return Err(SigmaSerializationError::NotSupported("Option"));
             }
-            Value::Context => return Err(SigmaSerializationError::NotSupported("Context")),
-            Value::Global => return Err(SigmaSerializationError::NotSupported("Global")),
-            Value::Lambda(_) => return Err(SigmaSerializationError::NotSupported("Lambda")),
         })
     }
-
     pub fn sigma_parse<R: SigmaByteRead>(
         tpe: &SType,
         r: &mut R,
-    ) -> Result<Value, SigmaParsingError> {
+    ) -> Result<Literal, SigmaParsingError> {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala#L84-L84
         use SType::*;
         Ok(match tpe {
-            SBoolean => Value::Boolean(r.get_u8()? != 0),
-            SByte => Value::Byte(r.get_i8()?),
-            SShort => Value::Short(r.get_i16()?),
-            SInt => Value::Int(r.get_i32()?),
-            SLong => Value::Long(r.get_i64()?),
+            SBoolean => Literal::Boolean(r.get_u8()? != 0),
+            SByte => Literal::Byte(r.get_i8()?),
+            SShort => Literal::Short(r.get_i16()?),
+            SInt => Literal::Int(r.get_i32()?),
+            SLong => Literal::Long(r.get_i64()?),
             SBigInt => {
                 let size = r.get_u16()?;
                 if size > 32 {
@@ -101,24 +96,26 @@ impl DataSerializer {
                 let mut buf = vec![0u8; size as usize];
                 r.read_exact(&mut buf)?;
                 match buf.as_slice().try_into() {
-                    Ok(x) => Value::BigInt(x),
+                    Ok(x) => Literal::BigInt(x),
                     Err(e) => return Err(SigmaParsingError::ValueOutOfBounds(e)),
                 }
             }
-            SGroupElement => Value::GroupElement(Box::new(EcPoint::sigma_parse(r)?)),
-            SSigmaProp => Value::sigma_prop(SigmaProp::new(SigmaBoolean::sigma_parse(r)?)),
+            SGroupElement => Literal::GroupElement(Box::new(EcPoint::sigma_parse(r)?)),
+            SSigmaProp => {
+                Literal::SigmaProp(Box::new(SigmaProp::new(SigmaBoolean::sigma_parse(r)?)))
+            }
             SColl(elem_type) if **elem_type == SByte => {
                 let len = r.get_u16()? as usize;
                 let mut buf = vec![0u8; len];
                 r.read_exact(&mut buf)?;
-                Value::Coll(CollKind::NativeColl(NativeColl::CollByte(
+                Literal::Coll(CollKind::NativeColl(NativeColl::CollByte(
                     buf.into_iter().map(|v| v as i8).collect(),
                 )))
             }
             SColl(elem_type) if **elem_type == SBoolean => {
                 let len = r.get_u16()? as usize;
                 let bools = r.get_bits(len)?;
-                Value::Coll(CollKind::WrappedColl {
+                Literal::Coll(CollKind::WrappedColl {
                     elem_tpe: *elem_type.clone(),
                     items: bools.into_iter().map(|b| b.into()).collect(),
                 })
@@ -129,7 +126,7 @@ impl DataSerializer {
                 for _ in 0..len {
                     elems.push(DataSerializer::sigma_parse(elem_type, r)?);
                 }
-                Value::Coll(CollKind::WrappedColl {
+                Literal::Coll(CollKind::WrappedColl {
                     elem_tpe: *elem_type.clone(),
                     items: elems,
                 })
@@ -142,7 +139,7 @@ impl DataSerializer {
                 // we get the tuple item value for each tuple item type,
                 // since items types quantity has checked bounds, we can be sure that items count
                 // is correct
-                Value::Tup(items.try_into()?)
+                Literal::Tup(items.try_into()?)
             }
             SBox => {
                 return Err(SigmaParsingError::NotImplementedYet(


Test patch:
diff --git a/ergotree-interpreter/tests/signing_spec_tests.rs b/ergotree-interpreter/tests/signing_spec_tests.rs
index ef7f35e0b..fe451a118 100644
--- a/ergotree-interpreter/tests/signing_spec_tests.rs
+++ b/ergotree-interpreter/tests/signing_spec_tests.rs
@@ -35,7 +35,7 @@ fn sig_test_vector_provedlog() {
         "03cb0d49e4eae7e57059a3da8ac52626d26fc11330af8fb093fa597d8b93deb7b1"
     );
 
-    let expr: Expr = sk.public_image().into();
+    let expr: Expr = Expr::Const(sk.public_image().into());
     let verifier = TestVerifier;
     let ver_res = verifier.verify(
         &expr.try_into().unwrap(),
@@ -57,7 +57,7 @@ fn sig_test_vector_prove_dht() {
     // let pdht = random_pdht_input.public_image().clone();
     dbg!(base16::encode_lower(&pdht.sigma_serialize_bytes().unwrap()));
     let signature = base16::decode(b"eba93a69b28cfdea261e9ea8914fca9a0b3868d50ce68c94f32e875730f8ca361bd3783c5d3e25802e54f49bd4fb9fafe51f4e8aafbf9815").unwrap();
-    let expr: Expr = pdht.into();
+    let expr: Expr = Expr::Const(pdht.into());
 
     // let random_pdht_input = DhTupleProverInput::random();
     // let tree: ErgoTree = expr.clone().into();
@@ -218,9 +218,12 @@ fn sig_test_vector_conj_or_prove_dht() {
     // let pdht = random_pdht_input.public_image().clone();
     // dbg!(base16::encode_lower(&pdht.sigma_serialize_bytes()));
     let signature = base16::decode(b"a80daebdcd57874296f49fd9910ddaefbf517ca076b6e16b97678e96a20239978836e7ec5b795cf3a55616d394f07c004f85e0d3e71880d4734b57ea874c7eba724e8887280f1affadaad962ee916b39207af2d2ab2a69a2e6f4d652f7389cc4f582bbe6d7937c59aa64cf2965a8b36a").unwrap();
-    let expr: Expr = SigmaOr::new(vec![Expr::Const(sk1.public_image().into()), pdht.into()])
-        .unwrap()
-        .into();
+    let expr: Expr = SigmaOr::new(vec![
+        Expr::Const(sk1.public_image().into()),
+        Expr::Const(pdht.into()),
+    ])
+    .unwrap()
+    .into();
 
     // let tree: ErgoTree = expr.clone().into();
     // let prover = TestProver {