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

Issue:
Add constant segregation support for ErgoTree
Needed for proposition serialization in prover step 7 in #11 


Discussion / hints:

Fix patch:
diff --git a/sigma-ser/src/lib.rs b/sigma-ser/src/lib.rs
index c420b7437..d743ffc62 100644
--- a/sigma-ser/src/lib.rs
+++ b/sigma-ser/src/lib.rs
@@ -13,12 +13,7 @@
 
 /// io::Read wrapper with `peek` operation
 pub mod peekable_reader;
-/// Sigma serializer
-pub mod serializer;
 /// VLQ encoder
 pub mod vlq_encode;
 /// ZigZag encoder
 pub mod zig_zag_encode;
-
-// #[cfg(test)]
-pub mod test_helpers;
diff --git a/sigma-tree/src/ast.rs b/sigma-tree/src/ast.rs
index 49bdc70b3..e39109e1f 100644
--- a/sigma-tree/src/ast.rs
+++ b/sigma-tree/src/ast.rs
@@ -8,15 +8,17 @@ pub mod ops;
 
 pub use constant::*;
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 /// newtype for box register id
 pub struct RegisterId(u8);
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 /// Expression in ErgoTree
 pub enum Expr {
     /// Constant value
     Const(Constant),
+    /// Placeholder for a constant
+    ConstPlaceholder(ConstantPlaceholder),
     /// Collection of values (same type)
     Coll {
         /// Collection type
@@ -58,15 +60,9 @@ impl Expr {
     /// Code (used in serialization)
     pub fn op_code(&self) -> OpCode {
         match self {
-            Const { .. } => todo!(),
-            Coll { .. } => todo!(),
-            Tup { .. } => todo!(),
-            BoxM(boxm) => boxm.op_code(),
-            CollM(_) => todo!(),
-            CtxM(_) => todo!(),
-            MethodCall { .. } => todo!(),
-            PredefFunc(_) => todo!(),
-            BinOp(_, _, _) => todo!(),
+            Const(_) => todo!(),
+            ConstPlaceholder(cp) => cp.op_code(),
+            _ => todo!(),
         }
     }
 
@@ -85,7 +81,7 @@ impl fmt::Display for Expr {
     }
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 /// Methods for Collection type instance
 pub enum CollMethods {
     /// Fold method
@@ -99,7 +95,7 @@ pub enum CollMethods {
     },
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 /// Methods for Box type instance
 pub enum BoxMethods {
     /// Box.RX methods
@@ -118,7 +114,7 @@ impl BoxMethods {
     }
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 /// Methods for Context type instance
 pub enum ContextMethods {
     /// Tx inputs
@@ -127,7 +123,7 @@ pub enum ContextMethods {
     Outputs,
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 /// Predefined (global) functions
 pub enum PredefFunc {
     /// SHA256
diff --git a/sigma-tree/src/ast/constant.rs b/sigma-tree/src/ast/constant.rs
index 44d9efb89..e3cf6b9df 100644
--- a/sigma-tree/src/ast/constant.rs
+++ b/sigma-tree/src/ast/constant.rs
@@ -1,13 +1,12 @@
 use crate::chain::{Base16DecodedBytes, Base16EncodedBytes};
 use crate::{
     chain::ErgoBox,
+    serialization::{op_code::OpCode, SerializationError, SigmaSerializable},
     sigma_protocol::{dlog_group::EcPoint, SigmaProp},
     types::{LiftIntoSType, SType},
 };
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
 use std::convert::TryFrom;
 
 #[derive(PartialEq, Eq, Debug, Clone)]
@@ -261,6 +260,25 @@ impl Into<Constant> for Vec<i8> {
     }
 }
 
+/// Placeholder for a constant in ErgoTree.
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ConstantPlaceholder {
+    /// Zero based index in ErgoTree.constants array.
+    pub id: u32,
+    /// Type of the constant value
+    pub tpe: SType,
+}
+
+impl ConstantPlaceholder {
+    /// OpCode value
+    pub const OP_CODE: OpCode = OpCode::CONSTANT_PLACEHOLDER;
+
+    /// OpCode for the serialization
+    pub fn op_code(&self) -> OpCode {
+        OpCode::CONSTANT_PLACEHOLDER
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/sigma-tree/src/ast/ops.rs b/sigma-tree/src/ast/ops.rs
index 4ac7c2a1c..5a5ae237a 100644
--- a/sigma-tree/src/ast/ops.rs
+++ b/sigma-tree/src/ast/ops.rs
@@ -1,12 +1,12 @@
 //! Operators in ErgoTree
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 /// Operations for numerical types
 pub enum NumOp {
     /// Addition
     Add,
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 /// Binary operations
 pub enum BinOp {
     /// Binary operations for numerical types
diff --git a/sigma-tree/src/big_integer.rs b/sigma-tree/src/big_integer.rs
index c577b496b..d520b1187 100644
--- a/sigma-tree/src/big_integer.rs
+++ b/sigma-tree/src/big_integer.rs
@@ -39,6 +39,7 @@ mod tests {
         let bytes = b.0.to_signed_bytes_be();
         let s2 =
             Scalar::from_bytes(bytes.as_slice().try_into().expect("expected 32 bytes")).unwrap();
+        // TODO: failed on CI with non-32 bytes length
         let b2: BigInteger = s2.into();
         assert_eq!(b, b2);
     }
diff --git a/sigma-tree/src/chain/address.rs b/sigma-tree/src/chain/address.rs
index 8cfc38450..35434b652 100644
--- a/sigma-tree/src/chain/address.rs
+++ b/sigma-tree/src/chain/address.rs
@@ -1,12 +1,12 @@
 use super::digest32;
 use crate::{
     ast::Expr,
+    serialization::{SerializationError, SigmaSerializable},
     sigma_protocol::{
         dlog_group::EcPoint, ProveDlog, SigmaBoolean, SigmaProofOfKnowledgeTree, SigmaProp,
     },
     ErgoTree,
 };
-use sigma_ser::serializer::{SerializationError, SigmaSerializable};
 use std::{
     convert::{TryFrom, TryInto},
     rc::Rc,
diff --git a/sigma-tree/src/chain/box_id.rs b/sigma-tree/src/chain/box_id.rs
index 17f01f242..a8fa72370 100644
--- a/sigma-tree/src/chain/box_id.rs
+++ b/sigma-tree/src/chain/box_id.rs
@@ -1,13 +1,14 @@
 //! Box id type
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
-use sigma_ser::vlq_encode;
 use std::io;
 
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
 
 use super::digest32::Digest32;
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
+    SigmaSerializable,
+};
 #[cfg(test)]
 use proptest_derive::Arbitrary;
 
@@ -41,11 +42,11 @@ impl Into<String> for BoxId {
 }
 
 impl SigmaSerializable for BoxId {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.0.sigma_serialize(w)?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         Ok(Self(Digest32::sigma_parse(r)?))
     }
 }
@@ -53,8 +54,8 @@ impl SigmaSerializable for BoxId {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::prelude::*;
-    use sigma_ser::test_helpers::*;
 
     proptest! {
 
diff --git a/sigma-tree/src/chain/context_extension.rs b/sigma-tree/src/chain/context_extension.rs
index d26518790..66531f855 100644
--- a/sigma-tree/src/chain/context_extension.rs
+++ b/sigma-tree/src/chain/context_extension.rs
@@ -1,8 +1,9 @@
 //! ContextExtension type
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
+};
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
 use sigma_ser::vlq_encode;
 use std::collections::HashMap;
 use std::io;
@@ -30,7 +31,7 @@ impl SigmaSerializable for ContextExtension {
         assert!(self.values.is_empty(), "implemented only for empty");
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let _ = r.get_u8()?;
         Ok(ContextExtension::empty())
     }
diff --git a/sigma-tree/src/chain/contract.rs b/sigma-tree/src/chain/contract.rs
index b868fd51c..241517259 100644
--- a/sigma-tree/src/chain/contract.rs
+++ b/sigma-tree/src/chain/contract.rs
@@ -1,8 +1,7 @@
 //! Ergo contract
 
 use super::Address;
-use crate::ErgoTree;
-use sigma_ser::serializer::SerializationError;
+use crate::{serialization::SerializationError, ErgoTree};
 
 /// High-level wrapper for ErgoTree
 pub struct Contract {
diff --git a/sigma-tree/src/chain/data_input.rs b/sigma-tree/src/chain/data_input.rs
index bca6c35ab..a4491fdd2 100644
--- a/sigma-tree/src/chain/data_input.rs
+++ b/sigma-tree/src/chain/data_input.rs
@@ -1,11 +1,12 @@
 //! DataInput type
 
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
-use sigma_ser::vlq_encode;
 use std::io;
 
 use super::box_id::BoxId;
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
+    SigmaSerializable,
+};
 #[cfg(test)]
 use proptest::prelude::*;
 #[cfg(test)]
@@ -24,11 +25,11 @@ pub struct DataInput {
 }
 
 impl SigmaSerializable for DataInput {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.box_id.sigma_serialize(w)?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let box_id = BoxId::sigma_parse(r)?;
         Ok(DataInput { box_id })
     }
@@ -37,7 +38,7 @@ impl SigmaSerializable for DataInput {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use sigma_ser::test_helpers::*;
+    use crate::serialization::sigma_serialize_roundtrip;
 
     proptest! {
 
diff --git a/sigma-tree/src/chain/digest32.rs b/sigma-tree/src/chain/digest32.rs
index 3ee354612..87a836048 100644
--- a/sigma-tree/src/chain/digest32.rs
+++ b/sigma-tree/src/chain/digest32.rs
@@ -1,14 +1,14 @@
-use crate::chain::{Base16DecodedBytes, Base16EncodedBytes};
+use crate::{
+    chain::{Base16DecodedBytes, Base16EncodedBytes},
+    serialization::{sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable},
+};
 use blake2::digest::{Update, VariableOutput};
 use blake2::VarBlake2b;
 #[cfg(test)]
 use proptest_derive::Arbitrary;
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode,
-};
+use sigma_ser::vlq_encode;
 use std::convert::TryFrom;
 use std::convert::TryInto;
 use std::io;
@@ -76,7 +76,7 @@ impl SigmaSerializable for Digest32 {
         w.write_all(self.0.as_ref())?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let mut bytes = [0; Digest32::SIZE];
         r.read_exact(&mut bytes)?;
         Ok(Self(bytes.into()))
diff --git a/sigma-tree/src/chain/ergo_box.rs b/sigma-tree/src/chain/ergo_box.rs
index 8d3544231..0e9547296 100644
--- a/sigma-tree/src/chain/ergo_box.rs
+++ b/sigma-tree/src/chain/ergo_box.rs
@@ -12,16 +12,18 @@ use super::{
 };
 use crate::{
     ergo_tree::ErgoTree,
-    serialization::ergo_box::{parse_box_with_indexed_digests, serialize_box_with_indexed_digests},
+    serialization::{
+        ergo_box::{parse_box_with_indexed_digests, serialize_box_with_indexed_digests},
+        sigma_byte_reader::SigmaByteRead,
+        sigma_byte_writer::SigmaByteWrite,
+        SerializationError, SigmaSerializable,
+    },
 };
 use box_value::BoxValue;
 use indexmap::IndexSet;
 use register::NonMandatoryRegisters;
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
-use sigma_ser::vlq_encode;
 #[cfg(feature = "with-serde")]
 use std::convert::TryFrom;
 use std::io;
@@ -182,7 +184,7 @@ impl TryFrom<json::ergo_box::ErgoBoxFromJson> for ErgoBox {
 }
 
 impl SigmaSerializable for ErgoBox {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         let ergo_tree_bytes = self.ergo_tree.sigma_serialise_bytes();
         serialize_box_with_indexed_digests(
             &self.value,
@@ -197,7 +199,7 @@ impl SigmaSerializable for ErgoBox {
         w.put_u16(self.index)?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let box_candidate = ErgoBoxCandidate::parse_body_with_indexed_digests(None, r)?;
         let tx_id = TxId::sigma_parse(r)?;
         let index = r.get_u16()?;
@@ -237,7 +239,7 @@ impl ErgoBoxCandidate {
 
     /// Box serialization with token ids optionally saved in transaction
     /// (in this case only token index is saved)
-    pub fn serialize_body_with_indexed_digests<W: vlq_encode::WriteSigmaVlqExt>(
+    pub fn serialize_body_with_indexed_digests<W: SigmaByteWrite>(
         &self,
         token_ids_in_tx: Option<&IndexSet<TokenId>>,
         w: &mut W,
@@ -254,7 +256,7 @@ impl ErgoBoxCandidate {
     }
 
     /// Box deserialization with token ids optionally parsed in transaction
-    pub fn parse_body_with_indexed_digests<R: vlq_encode::ReadSigmaVlqExt>(
+    pub fn parse_body_with_indexed_digests<R: SigmaByteRead>(
         digests_in_tx: Option<&IndexSet<TokenId>>,
         r: &mut R,
     ) -> Result<ErgoBoxCandidate, SerializationError> {
@@ -263,10 +265,10 @@ impl ErgoBoxCandidate {
 }
 
 impl SigmaSerializable for ErgoBoxCandidate {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.serialize_body_with_indexed_digests(None, w)
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         ErgoBoxCandidate::parse_body_with_indexed_digests(None, r)
     }
 }
@@ -286,8 +288,8 @@ impl From<ErgoBox> for ErgoBoxCandidate {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*};
-    use sigma_ser::test_helpers::*;
 
     impl Arbitrary for ErgoBoxCandidate {
         type Parameters = ();
diff --git a/sigma-tree/src/chain/ergo_box/box_value.rs b/sigma-tree/src/chain/ergo_box/box_value.rs
index 5f9910a20..b29b16b86 100644
--- a/sigma-tree/src/chain/ergo_box/box_value.rs
+++ b/sigma-tree/src/chain/ergo_box/box_value.rs
@@ -1,8 +1,10 @@
 //! Box value newtype
 
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
+};
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
-use sigma_ser::serializer::{SerializationError, SigmaSerializable};
 use sigma_ser::vlq_encode;
 use std::{convert::TryFrom, io};
 
@@ -62,7 +64,7 @@ impl SigmaSerializable for BoxValue {
     fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
         w.put_u64(self.0)
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let v = r.get_u64()?;
         Ok(BoxValue(v))
     }
diff --git a/sigma-tree/src/chain/ergo_box/register.rs b/sigma-tree/src/chain/ergo_box/register.rs
index 313b3db05..c47834d1b 100644
--- a/sigma-tree/src/chain/ergo_box/register.rs
+++ b/sigma-tree/src/chain/ergo_box/register.rs
@@ -1,9 +1,8 @@
 //! Box registers
 
-use crate::ast::Constant;
+use crate::{ast::Constant, serialization::SerializationError};
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
-use sigma_ser::serializer::SerializationError;
 use std::{collections::HashMap, convert::TryFrom};
 use thiserror::Error;
 
diff --git a/sigma-tree/src/chain/input.rs b/sigma-tree/src/chain/input.rs
index 5ddce24e5..2bf9c1b87 100644
--- a/sigma-tree/src/chain/input.rs
+++ b/sigma-tree/src/chain/input.rs
@@ -1,10 +1,11 @@
 //! Transaction input
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
-use sigma_ser::vlq_encode;
 use std::io;
 
 use super::{box_id::BoxId, prover_result::ProverResult};
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
+    SigmaSerializable,
+};
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
 
@@ -35,12 +36,12 @@ impl Input {
 }
 
 impl SigmaSerializable for Input {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.box_id.sigma_serialize(w)?;
         self.spending_proof.sigma_serialize(w)?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let box_id = BoxId::sigma_parse(r)?;
         let spending_proof = ProverResult::sigma_parse(r)?;
         Ok(Input {
@@ -53,8 +54,8 @@ impl SigmaSerializable for Input {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::prelude::*;
-    use sigma_ser::test_helpers::sigma_serialize_roundtrip;
 
     proptest! {
 
diff --git a/sigma-tree/src/chain/json.rs b/sigma-tree/src/chain/json.rs
index 55f87e1be..145abe34c 100644
--- a/sigma-tree/src/chain/json.rs
+++ b/sigma-tree/src/chain/json.rs
@@ -13,9 +13,8 @@ where
 pub mod ergo_tree {
 
     use super::*;
-    use crate::ErgoTree;
+    use crate::{serialization::SigmaSerializable, ErgoTree};
     use serde::{Deserialize, Deserializer, Serializer};
-    use sigma_ser::serializer::SigmaSerializable;
 
     pub fn serialize<S>(ergo_tree: &ErgoTree, serializer: S) -> Result<S::Ok, S::Error>
     where
diff --git a/sigma-tree/src/chain/prover_result.rs b/sigma-tree/src/chain/prover_result.rs
index 2b5ce2068..bb9d4787e 100644
--- a/sigma-tree/src/chain/prover_result.rs
+++ b/sigma-tree/src/chain/prover_result.rs
@@ -1,10 +1,11 @@
 //! ProverResult
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
-use sigma_ser::vlq_encode;
 use std::io;
 
 use super::context_extension::ContextExtension;
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
+    SigmaSerializable,
+};
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
 
@@ -21,13 +22,13 @@ pub struct ProverResult {
 }
 
 impl SigmaSerializable for ProverResult {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         w.put_u16(self.proof.len() as u16)?;
         w.write_all(&self.proof)?;
         self.extension.sigma_serialize(w)?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let proof_len = r.get_u16()?;
         let mut proof = vec![0; proof_len as usize];
         r.read_exact(&mut proof)?;
@@ -39,8 +40,8 @@ impl SigmaSerializable for ProverResult {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::{collection::vec, prelude::*};
-    use sigma_ser::test_helpers::*;
 
     impl Arbitrary for ProverResult {
         type Parameters = ();
diff --git a/sigma-tree/src/chain/token.rs b/sigma-tree/src/chain/token.rs
index 65b142cd7..62bf2b820 100644
--- a/sigma-tree/src/chain/token.rs
+++ b/sigma-tree/src/chain/token.rs
@@ -1,8 +1,9 @@
 //! Token related types
 
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
-use sigma_ser::vlq_encode;
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
+    SigmaSerializable,
+};
 use std::io;
 
 use super::digest32::Digest32;
@@ -23,11 +24,11 @@ impl TokenId {
 }
 
 impl SigmaSerializable for TokenId {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.0.sigma_serialize(w)?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         Ok(Self(Digest32::sigma_parse(r)?))
     }
 }
@@ -48,8 +49,8 @@ pub struct TokenAmount {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::prelude::*;
-    use sigma_ser::test_helpers::*;
 
     proptest! {
 
diff --git a/sigma-tree/src/chain/transaction.rs b/sigma-tree/src/chain/transaction.rs
index 01ccebb53..7e13e228f 100644
--- a/sigma-tree/src/chain/transaction.rs
+++ b/sigma-tree/src/chain/transaction.rs
@@ -10,14 +10,16 @@ use super::{
     token::TokenId,
     ErgoBox,
 };
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SerializationError,
+    SigmaSerializable,
+};
 use indexmap::IndexSet;
 #[cfg(test)]
 use proptest_derive::Arbitrary;
 #[cfg(feature = "with-serde")]
 use serde::{Deserialize, Serialize};
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
-use sigma_ser::vlq_encode;
+
 use std::convert::TryFrom;
 use std::io;
 use std::iter::FromIterator;
@@ -38,11 +40,11 @@ impl TxId {
 }
 
 impl SigmaSerializable for TxId {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.0.sigma_serialize(w)?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         Ok(Self(Digest32::sigma_parse(r)?))
     }
 }
@@ -133,7 +135,7 @@ impl Transaction {
 }
 
 impl SigmaSerializable for Transaction {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         // reference implementation - https://github.com/ScorexFoundation/sigmastate-interpreter/blob/9b20cb110effd1987ff76699d637174a4b2fb441/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala#L112-L112
         w.put_usize_as_u16(self.inputs.len())?;
         self.inputs.iter().try_for_each(|i| i.sigma_serialize(w))?;
@@ -164,7 +166,7 @@ impl SigmaSerializable for Transaction {
         Ok(())
     }
 
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         // reference implementation - https://github.com/ScorexFoundation/sigmastate-interpreter/blob/9b20cb110effd1987ff76699d637174a4b2fb441/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala#L146-L146
 
         // parse transaction inputs
@@ -251,8 +253,8 @@ impl TryFrom<json::transaction::TransactionJson> for Transaction {
 mod tests {
 
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*};
-    use sigma_ser::test_helpers::*;
 
     impl Arbitrary for Transaction {
         type Parameters = ();
diff --git a/sigma-tree/src/ergo_tree.rs b/sigma-tree/src/ergo_tree.rs
index 3d37e7993..7cd7edb29 100644
--- a/sigma-tree/src/ergo_tree.rs
+++ b/sigma-tree/src/ergo_tree.rs
@@ -1,15 +1,20 @@
 //! ErgoTree
+use crate::serialization::{
+    sigma_byte_reader::{SigmaByteRead, SigmaByteReader},
+    sigma_byte_writer::{SigmaByteWrite, SigmaByteWriter},
+    SerializationError, SigmaSerializable,
+};
 use crate::{
     ast::{Constant, Expr},
     types::SType,
 };
 use io::{Cursor, Read};
-use sigma_ser::serializer::SerializationError;
-use sigma_ser::serializer::SigmaSerializable;
+
+use crate::serialization::constant_store::ConstantStore;
 use sigma_ser::{peekable_reader::PeekableReader, vlq_encode};
 use std::io;
 use std::rc::Rc;
-use vlq_encode::{ReadSigmaVlqExt, WriteSigmaVlqExt};
+use vlq_encode::ReadSigmaVlqExt;
 
 #[derive(PartialEq, Eq, Debug, Clone)]
 struct ParsedTree {
@@ -68,10 +73,28 @@ impl ErgoTree {
 
     /// get Expr out of ErgoTree
     pub fn proposition(&self) -> Result<Rc<Expr>, ErgoTreeParsingError> {
-        self.tree
+        let root = self
+            .tree
             .clone()
             .map_err(ErgoTreeParsingError::TreeParsingError)
-            .and_then(|t| t.root.map_err(ErgoTreeParsingError::RootParsingError))
+            .and_then(|t| t.root.map_err(ErgoTreeParsingError::RootParsingError))?;
+        if self.header.is_constant_segregation() {
+            let mut data = Vec::new();
+            let mut cs = ConstantStore::empty();
+            let mut w = SigmaByteWriter::new(&mut data, Some(&mut cs));
+            root.sigma_serialize(&mut w).unwrap();
+            let cursor = Cursor::new(&mut data[..]);
+            let pr = PeekableReader::new(cursor);
+            let mut sr = SigmaByteReader::new_with_substitute_placeholders(
+                pr,
+                ConstantStore::new(self.tree.clone().unwrap().constants),
+            );
+            let parsed_expr = Expr::sigma_parse(&mut sr).unwrap();
+            // todo!("substitute placeholders: {:?}", self.tree);
+            Ok(Rc::new(parsed_expr))
+        } else {
+            Ok(root)
+        }
     }
 
     /// Build ErgoTree using expr as is, without constants segregated
@@ -86,8 +109,23 @@ impl ErgoTree {
     }
 
     /// Build ErgoTree with constants segregated from expr
-    pub fn with_segregation(_: Rc<Expr>) -> ErgoTree {
-        todo!()
+    pub fn with_segregation(expr: Rc<Expr>) -> ErgoTree {
+        let mut data = Vec::new();
+        let mut cs = ConstantStore::empty();
+        let mut w = SigmaByteWriter::new(&mut data, Some(&mut cs));
+        expr.sigma_serialize(&mut w).unwrap();
+        let cursor = Cursor::new(&mut data[..]);
+        let pr = PeekableReader::new(cursor);
+        let constants = cs.get_all();
+        let mut sr = SigmaByteReader::new(pr, cs);
+        let parsed_expr = Expr::sigma_parse(&mut sr).unwrap();
+        ErgoTree {
+            header: ErgoTreeHeader(ErgoTreeHeader::CONSTANT_SEGREGATION_FLAG),
+            tree: Ok(ParsedTree {
+                constants,
+                root: Ok(Rc::new(parsed_expr)),
+            }),
+        }
     }
 }
 
@@ -102,27 +140,24 @@ impl From<Rc<Expr>> for ErgoTree {
     }
 }
 impl SigmaSerializable for ErgoTreeHeader {
-    fn sigma_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         w.put_u8(self.0)?;
         Ok(())
     }
-    fn sigma_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let header = r.get_u8()?;
         Ok(ErgoTreeHeader(header))
     }
 }
 
 impl SigmaSerializable for ErgoTree {
-    fn sigma_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.header.sigma_serialize(w)?;
         match &self.tree {
             Ok(ParsedTree { constants, root }) => {
                 if self.header.is_constant_segregation() {
                     w.put_usize_as_u32(constants.len())?;
-                    assert!(
-                        constants.is_empty(),
-                        "separate constants serialization is not yet supported"
-                    );
+                    constants.iter().try_for_each(|c| c.sigma_serialize(w))?;
                 }
                 match root {
                     Ok(expr) => expr.sigma_serialize(w)?,
@@ -134,7 +169,7 @@ impl SigmaSerializable for ErgoTree {
         Ok(())
     }
 
-    fn sigma_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let header = ErgoTreeHeader::sigma_parse(r)?;
         if header.is_constant_segregation() {
             let constants_len = r.get_u32()?;
@@ -157,27 +192,40 @@ impl SigmaSerializable for ErgoTree {
 
     fn sigma_parse_bytes(mut bytes: Vec<u8>) -> Result<Self, SerializationError> {
         let cursor = Cursor::new(&mut bytes[..]);
-        let mut r = PeekableReader::new(cursor);
+        let mut r = SigmaByteReader::new(PeekableReader::new(cursor), ConstantStore::empty());
         let header = ErgoTreeHeader::sigma_parse(&mut r)?;
-        if header.is_constant_segregation() {
+        let constants = if header.is_constant_segregation() {
             let constants_len = r.get_u32()?;
-            if constants_len != 0 {
-                return Ok(ErgoTree {
-                    header,
-                    tree: Err(ErgoTreeConstantsParsingError {
-                        bytes: bytes[1..].to_vec(),
-                        error: SerializationError::NotImplementedYet(
-                            "separate constants serialization is not yet supported".to_string(),
-                        ),
-                    }),
-                });
+            let mut constants = Vec::with_capacity(constants_len as usize);
+            for _ in 0..constants_len {
+                match Constant::sigma_parse(&mut r) {
+                    Ok(c) => constants.push(c),
+                    Err(_) => {
+                        return Ok(ErgoTree {
+                            header,
+                            tree: Err(ErgoTreeConstantsParsingError {
+                                bytes: bytes[1..].to_vec(),
+                                error: SerializationError::NotImplementedYet(
+                                    "not all constant types serialization is supported".to_string(),
+                                ),
+                            }),
+                        })
+                    }
+                }
             }
-        }
-        let constants = Vec::new();
+            constants
+        } else {
+            vec![]
+        };
         let mut rest_of_the_bytes = Vec::new();
         let _ = r.read_to_end(&mut rest_of_the_bytes);
         let rest_of_the_bytes_copy = rest_of_the_bytes.clone();
-        match Expr::sigma_parse_bytes(rest_of_the_bytes) {
+        let mut new_r = SigmaByteReader::new(
+            PeekableReader::new(Cursor::new(&mut rest_of_the_bytes[..])),
+            ConstantStore::new(constants.clone()),
+        );
+
+        match Expr::sigma_parse(&mut new_r) {
             Ok(parsed) => Ok(ErgoTree {
                 header,
                 tree: Ok(ParsedTree {
@@ -202,9 +250,9 @@ impl SigmaSerializable for ErgoTree {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use crate::{ast::ConstantVal, chain, sigma_protocol::SigmaProp, types::SType};
     use proptest::prelude::*;
-    use sigma_ser::test_helpers::*;
 
     impl Arbitrary for ErgoTree {
         type Parameters = ();
@@ -232,13 +280,19 @@ mod tests {
 
     #[test]
     fn deserialization_non_parseable_tree_ok() {
-        // constants length is set
-        assert!(ErgoTree::sigma_parse_bytes(vec![0, 1]).is_ok());
+        // constants length is set, invalid constant
+        assert!(ErgoTree::sigma_parse_bytes(vec![
+            ErgoTreeHeader::CONSTANT_SEGREGATION_FLAG,
+            1,
+            99,
+            99
+        ])
+        .is_ok());
     }
 
     #[test]
     fn deserialization_non_parseable_root_ok() {
-        // constants length is zero, but Expr is invalid
+        // no constant segregation, Expr is invalid
         assert!(ErgoTree::sigma_parse_bytes(vec![0, 0, 1]).is_ok());
     }
 
@@ -253,4 +307,19 @@ mod tests {
         let bytes = &contract.get_ergo_tree().sigma_serialise_bytes();
         assert_eq!(&bytes[..2], vec![0u8, 8u8].as_slice());
     }
+
+    #[test]
+    fn test_constant_segregation() {
+        let expr = Expr::Const(Constant {
+            tpe: SType::SBoolean,
+            v: ConstantVal::Boolean(true),
+        });
+        let ergo_tree = ErgoTree::with_segregation(Rc::new(expr.clone()));
+        let bytes = ergo_tree.sigma_serialise_bytes();
+        let parsed_expr = ErgoTree::sigma_parse_bytes(bytes)
+            .unwrap()
+            .proposition()
+            .unwrap();
+        assert_eq!(*parsed_expr, expr)
+    }
 }
diff --git a/sigma-tree/src/eval/costs.rs b/sigma-tree/src/eval/costs.rs
index b116c97af..d8ff7ea61 100644
--- a/sigma-tree/src/eval/costs.rs
+++ b/sigma-tree/src/eval/costs.rs
@@ -9,17 +9,7 @@ impl Costs {
 }
 
 impl Costs {
-    pub fn cost_of(&self, expr: &Expr) -> Cost {
-        match expr {
-            Expr::Const(_) => todo!(),
-            Expr::Coll { .. } => todo!(),
-            Expr::Tup { .. } => todo!(),
-            Expr::PredefFunc(_) => todo!(),
-            Expr::CollM(_) => todo!(),
-            Expr::BoxM(_) => todo!(),
-            Expr::CtxM(_) => todo!(),
-            Expr::MethodCall { .. } => todo!(),
-            Expr::BinOp(_, _, _) => todo!(),
-        }
+    pub fn cost_of(&self, _: &Expr) -> Cost {
+        todo!()
     }
 }
diff --git a/sigma-tree/src/serialization.rs b/sigma-tree/src/serialization.rs
index 4a95daff9..aa8c42920 100644
--- a/sigma-tree/src/serialization.rs
+++ b/sigma-tree/src/serialization.rs
@@ -1,11 +1,18 @@
 //! Serializers
 
 mod constant;
+mod constant_placeholder;
 mod data;
 mod expr;
 mod fold;
+mod serializable;
 mod sigmaboolean;
 
+pub mod constant_store;
 pub mod ergo_box;
 pub mod op_code;
+pub mod sigma_byte_reader;
+pub mod sigma_byte_writer;
 pub mod types;
+
+pub use serializable::*;
diff --git a/sigma-tree/src/serialization/constant.rs b/sigma-tree/src/serialization/constant.rs
index 8488ea836..47279f02d 100644
--- a/sigma-tree/src/serialization/constant.rs
+++ b/sigma-tree/src/serialization/constant.rs
@@ -1,18 +1,18 @@
-use super::data::DataSerializer;
-use crate::{ast::Constant, types::SType};
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode::{ReadSigmaVlqExt, WriteSigmaVlqExt},
+use super::{data::DataSerializer, sigma_byte_writer::SigmaByteWrite};
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
+use crate::{ast::Constant, types::SType};
+
 use std::io;
 
 impl SigmaSerializable for Constant {
-    fn sigma_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.tpe.sigma_serialize(w)?;
         DataSerializer::sigma_serialize(&self.v, w)
     }
 
-    fn sigma_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala#L84-L84
         let tpe = SType::sigma_parse(r)?;
         let v = DataSerializer::sigma_parse(&tpe, r)?;
@@ -23,8 +23,8 @@ impl SigmaSerializable for Constant {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::prelude::*;
-    use sigma_ser::test_helpers::*;
 
     proptest! {
 
diff --git a/sigma-tree/src/serialization/constant_placeholder.rs b/sigma-tree/src/serialization/constant_placeholder.rs
new file mode 100644
index 000000000..94cfc3bd7
--- /dev/null
+++ b/sigma-tree/src/serialization/constant_placeholder.rs
@@ -0,0 +1,58 @@
+use super::sigma_byte_writer::SigmaByteWrite;
+use crate::ast::ConstantPlaceholder;
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
+};
+
+use std::io;
+
+impl SigmaSerializable for ConstantPlaceholder {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
+        w.put_u32(self.id)
+    }
+
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
+        let id = r.get_u32()?;
+        if let Some(c) = r.constant_store().get(id) {
+            Ok(ConstantPlaceholder {
+                id,
+                tpe: c.tpe.clone(),
+            })
+        } else {
+            Err(SerializationError::ConstantForPlaceholderNotFound(id))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{
+        ast::Constant,
+        serialization::{
+            constant_store::ConstantStore, sigma_byte_reader::SigmaByteReader,
+            sigma_byte_writer::SigmaByteWriter,
+        },
+    };
+    use io::Cursor;
+    use proptest::prelude::*;
+    use sigma_ser::peekable_reader::PeekableReader;
+
+    proptest! {
+
+        #[test]
+        fn ser_roundtrip(c in any::<Constant>()) {
+            let mut data = Vec::new();
+            let mut cs = ConstantStore::empty();
+            let ph = cs.put(c);
+            let mut w = SigmaByteWriter::new(&mut data, Some(&mut cs));
+            ph.sigma_serialize(&mut w).expect("serialization failed");
+
+            let cursor = Cursor::new(&mut data[..]);
+            let pr = PeekableReader::new(cursor);
+            let mut sr = SigmaByteReader::new(pr, cs);
+            let ph_parsed = ConstantPlaceholder::sigma_parse(&mut sr).expect("parse failed");
+            prop_assert_eq![ph, ph_parsed];
+        }
+    }
+}
diff --git a/sigma-tree/src/serialization/constant_store.rs b/sigma-tree/src/serialization/constant_store.rs
new file mode 100644
index 000000000..6441f97fa
--- /dev/null
+++ b/sigma-tree/src/serialization/constant_store.rs
@@ -0,0 +1,75 @@
+//! Constant store for Sigma byte reader
+
+use crate::ast::{Constant, ConstantPlaceholder};
+
+/// Storage for constants used in ErgoTree constant segregation
+pub struct ConstantStore {
+    constants: Vec<Constant>,
+}
+
+impl ConstantStore {
+    pub fn empty() -> Self {
+        ConstantStore { constants: vec![] }
+    }
+
+    pub fn new(constants: Vec<Constant>) -> Self {
+        ConstantStore { constants }
+    }
+
+    pub fn get(&self, index: u32) -> Option<&Constant> {
+        self.constants.get(index as usize)
+    }
+
+    pub fn put(&mut self, c: Constant) -> ConstantPlaceholder {
+        self.constants.push(c.clone());
+        assert!(self.constants.len() <= u32::MAX as usize);
+        ConstantPlaceholder {
+            id: (self.constants.len() - 1) as u32,
+            tpe: c.tpe,
+        }
+    }
+
+    pub fn get_all(&self) -> Vec<Constant> {
+        self.constants.clone()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{ast::ConstantVal, types::SType};
+
+    #[test]
+    fn test_empty() {
+        let s = ConstantStore::empty();
+        assert!(s.get_all().is_empty());
+        assert!(s.get(0).is_none());
+    }
+
+    #[test]
+    fn test_non_empty() {
+        let c = Constant {
+            tpe: SType::SBoolean,
+            v: ConstantVal::Boolean(true),
+        };
+        let s = ConstantStore::new(vec![c.clone()]);
+        assert!(s.get(0).is_some());
+        assert_eq!(s.get(0).unwrap().clone(), c);
+        assert!(!s.get_all().is_empty());
+        assert_eq!(s.get_all().get(0).unwrap().clone(), c);
+    }
+
+    #[test]
+    fn test_put() {
+        let c = Constant {
+            tpe: SType::SBoolean,
+            v: ConstantVal::Boolean(true),
+        };
+        let mut s = ConstantStore::empty();
+        s.put(c.clone());
+        assert!(s.get(0).is_some());
+        assert_eq!(s.get(0).unwrap().clone(), c);
+        assert!(!s.get_all().is_empty());
+        assert_eq!(s.get_all().get(0).unwrap().clone(), c);
+    }
+}
diff --git a/sigma-tree/src/serialization/data.rs b/sigma-tree/src/serialization/data.rs
index 695eb2498..50539860a 100644
--- a/sigma-tree/src/serialization/data.rs
+++ b/sigma-tree/src/serialization/data.rs
@@ -1,3 +1,6 @@
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
+};
 use crate::{
     ast::ConstantVal,
     ast::ConstantVal::*,
@@ -7,19 +10,14 @@ use crate::{
     types::SType::*,
 };
 use sigma_protocol::{dlog_group::EcPoint, SigmaBoolean, SigmaProp};
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode::{ReadSigmaVlqExt, WriteSigmaVlqExt},
-};
+
+use super::sigma_byte_writer::SigmaByteWrite;
 use std::io;
 
 pub struct DataSerializer {}
 
 impl DataSerializer {
-    pub fn sigma_serialize<W: WriteSigmaVlqExt>(
-        c: &ConstantVal,
-        w: &mut W,
-    ) -> Result<(), io::Error> {
+    pub fn sigma_serialize<W: SigmaByteWrite>(c: &ConstantVal, w: &mut W) -> Result<(), io::Error> {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala#L26-L26
         match c {
             Boolean(v) => w.put_u8(if *v { 1 } else { 0 }),
@@ -48,7 +46,7 @@ impl DataSerializer {
         }
     }
 
-    pub fn sigma_parse<R: ReadSigmaVlqExt>(
+    pub fn sigma_parse<R: SigmaByteRead>(
         tpe: &SType,
         r: &mut R,
     ) -> Result<ConstantVal, SerializationError> {
diff --git a/sigma-tree/src/serialization/ergo_box.rs b/sigma-tree/src/serialization/ergo_box.rs
index d82088815..58411e10a 100644
--- a/sigma-tree/src/serialization/ergo_box.rs
+++ b/sigma-tree/src/serialization/ergo_box.rs
@@ -1,3 +1,6 @@
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
+};
 use crate::{
     ast::Constant,
     chain::{
@@ -7,16 +10,14 @@ use crate::{
     ErgoTree,
 };
 use indexmap::IndexSet;
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode,
-};
+
+use super::sigma_byte_writer::SigmaByteWrite;
 use std::convert::TryFrom;
 use std::io;
 
 /// Box serialization with token ids optionally saved in transaction
 /// (in this case only token index is saved)
-pub fn serialize_box_with_indexed_digests<W: vlq_encode::WriteSigmaVlqExt>(
+pub fn serialize_box_with_indexed_digests<W: SigmaByteWrite>(
     box_value: &BoxValue,
     ergo_tree_bytes: Vec<u8>,
     tokens: &[TokenAmount],
@@ -61,7 +62,7 @@ pub fn serialize_box_with_indexed_digests<W: vlq_encode::WriteSigmaVlqExt>(
 }
 
 /// Box deserialization with token ids optionally parsed in transaction
-pub fn parse_box_with_indexed_digests<R: vlq_encode::ReadSigmaVlqExt>(
+pub fn parse_box_with_indexed_digests<R: SigmaByteRead>(
     digests_in_tx: Option<&IndexSet<TokenId>>,
     r: &mut R,
 ) -> Result<ErgoBoxCandidate, SerializationError> {
diff --git a/sigma-tree/src/serialization/expr.rs b/sigma-tree/src/serialization/expr.rs
index 24eab448c..25db8d542 100644
--- a/sigma-tree/src/serialization/expr.rs
+++ b/sigma-tree/src/serialization/expr.rs
@@ -1,15 +1,22 @@
-use super::{fold::FoldSerializer, op_code::OpCode};
-use crate::ast::{CollMethods, Constant, Expr};
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode,
+use super::{fold::FoldSerializer, op_code::OpCode, sigma_byte_writer::SigmaByteWrite};
+use crate::ast::{CollMethods, Constant, ConstantPlaceholder, Expr};
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
+
 use std::io;
 
 impl SigmaSerializable for Expr {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         match self {
-            Expr::Const(c) => c.sigma_serialize(w),
+            Expr::Const(c) => match w.constant_store() {
+                Some(cs) => {
+                    let ph = cs.put(c.clone());
+                    ph.op_code().sigma_serialize(w)?;
+                    ph.sigma_serialize(w)
+                }
+                None => c.sigma_serialize(w),
+            },
             expr => {
                 let op_code = self.op_code();
                 op_code.sigma_serialize(w)?;
@@ -17,13 +24,14 @@ impl SigmaSerializable for Expr {
                     Expr::CollM(cm) => match cm {
                         CollMethods::Fold { .. } => FoldSerializer::sigma_serialize(expr, w),
                     },
-                    _ => panic!(format!("don't know how to serialize {}", expr)),
+                    Expr::ConstPlaceholder(cp) => cp.sigma_serialize(w),
+                    _ => panic!(format!("don't know how to serialize {:?}", expr)),
                 }
             }
         }
     }
 
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let first_byte = match r.peek_u8() {
             Ok(b) => Ok(b),
             Err(_) => {
@@ -39,6 +47,17 @@ impl SigmaSerializable for Expr {
             let op_code = OpCode::sigma_parse(r)?;
             match op_code {
                 FoldSerializer::OP_CODE => FoldSerializer::sigma_parse(r),
+                ConstantPlaceholder::OP_CODE => {
+                    let cp = ConstantPlaceholder::sigma_parse(r)?;
+                    if r.substitute_placeholders() {
+                        // ConstantPlaceholder itself can be created only if a corresponding
+                        // constant is in the constant_store, thus unwrap() is safe here
+                        let c = r.constant_store().get(cp.id).unwrap();
+                        Ok(Expr::Const(c.clone()))
+                    } else {
+                        Ok(Expr::ConstPlaceholder(cp))
+                    }
+                }
                 o => Err(SerializationError::NotImplementedOpCode(o.value())),
             }
         }
diff --git a/sigma-tree/src/serialization/fold.rs b/sigma-tree/src/serialization/fold.rs
index fe492c0e6..6697c5481 100644
--- a/sigma-tree/src/serialization/fold.rs
+++ b/sigma-tree/src/serialization/fold.rs
@@ -1,9 +1,9 @@
-use super::op_code::OpCode;
+use super::{op_code::OpCode, sigma_byte_writer::SigmaByteWrite};
 use crate::ast::{CollMethods, Expr};
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode::{ReadSigmaVlqExt, WriteSigmaVlqExt},
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
+
 use std::io;
 
 pub struct FoldSerializer {}
@@ -11,7 +11,7 @@ pub struct FoldSerializer {}
 impl FoldSerializer {
     pub const OP_CODE: OpCode = OpCode::FOLD;
 
-    pub fn sigma_serialize<W: WriteSigmaVlqExt>(expr: &Expr, w: &mut W) -> Result<(), io::Error> {
+    pub fn sigma_serialize<W: SigmaByteWrite>(expr: &Expr, w: &mut W) -> Result<(), io::Error> {
         match expr {
             Expr::CollM(CollMethods::Fold {
                 input,
@@ -27,7 +27,7 @@ impl FoldSerializer {
         }
     }
 
-    pub fn sigma_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Expr, SerializationError> {
+    pub fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Expr, SerializationError> {
         let input = Expr::sigma_parse(r)?;
         let zero = Expr::sigma_parse(r)?;
         let fold_op = Expr::sigma_parse(r)?;
diff --git a/sigma-tree/src/serialization/op_code.rs b/sigma-tree/src/serialization/op_code.rs
index 2838b7005..12c2a4004 100644
--- a/sigma-tree/src/serialization/op_code.rs
+++ b/sigma-tree/src/serialization/op_code.rs
@@ -1,7 +1,8 @@
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode,
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
+use sigma_ser::vlq_encode;
+
 use std::io;
 use vlq_encode::WriteSigmaVlqExt;
 
@@ -12,6 +13,7 @@ impl OpCode {
     pub const LAST_DATA_TYPE: OpCode = OpCode(111);
     pub const LAST_CONSTANT_CODE: OpCode = OpCode(Self::LAST_DATA_TYPE.value() + 1);
 
+    pub const CONSTANT_PLACEHOLDER: OpCode = Self::new_op_code(3);
     pub const FOLD: OpCode = Self::new_op_code(64);
     pub const PROVE_DLOG: OpCode = Self::new_op_code(93);
 
@@ -33,7 +35,7 @@ impl SigmaSerializable for OpCode {
         w.put_u8(self.0)?;
         Ok(())
     }
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let code = r.get_u8()?;
         Ok(OpCode::parse(code))
     }
diff --git a/sigma-ser/src/serializer.rs b/sigma-tree/src/serialization/serializable.rs
similarity index 64%
rename from sigma-ser/src/serializer.rs
rename to sigma-tree/src/serialization/serializable.rs
index 3d08a8324..3c224c671 100644
--- a/sigma-ser/src/serializer.rs
+++ b/sigma-tree/src/serialization/serializable.rs
@@ -1,6 +1,10 @@
-use super::vlq_encode;
-use crate::peekable_reader::PeekableReader;
+use super::{
+    constant_store::ConstantStore,
+    sigma_byte_reader::{SigmaByteRead, SigmaByteReader},
+    sigma_byte_writer::{SigmaByteWrite, SigmaByteWriter},
+};
 use io::Cursor;
+use sigma_ser::{peekable_reader::PeekableReader, vlq_encode};
 use std::io;
 use thiserror::Error;
 
@@ -28,6 +32,9 @@ pub enum SerializationError {
     /// Feature not yet implemented
     #[error("feature not yet implemented: {0}")]
     NotImplementedYet(String),
+    /// Constant with given index not found in constant store
+    #[error("Constant with index {0} not found in constant store")]
+    ConstantForPlaceholderNotFound(u32),
 }
 
 impl From<vlq_encode::VlqEncodingError> for SerializationError {
@@ -51,17 +58,18 @@ pub trait SigmaSerializable: Sized {
     /// serialization MUST be infallible up to errors in the underlying writer.
     /// In other words, any type implementing `SigmaSerializable`
     /// must make illegal states unrepresentable.
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error>;
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error>;
 
     /// Try to read `self` from the given `reader`.
     /// `sigma-` prefix to alert the reader that the serialization in use
     /// is consensus-critical
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError>;
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError>;
 
     /// Serialize any SigmaSerializable value into bytes
     fn sigma_serialise_bytes(&self) -> Vec<u8> {
         let mut data = Vec::new();
-        self.sigma_serialize(&mut data)
+        let mut w = SigmaByteWriter::new(&mut data, None);
+        self.sigma_serialize(&mut w)
             // since serialization may fail only for underlying IO errors it's ok to force unwrap
             .expect("serialization failed");
         data
@@ -70,7 +78,20 @@ pub trait SigmaSerializable: Sized {
     /// Parse `self` from the bytes
     fn sigma_parse_bytes(mut bytes: Vec<u8>) -> Result<Self, SerializationError> {
         let cursor = Cursor::new(&mut bytes[..]);
-        let mut reader = PeekableReader::new(cursor);
-        Self::sigma_parse(&mut reader)
+        let pr = PeekableReader::new(cursor);
+        let mut sr = SigmaByteReader::new(pr, ConstantStore::empty());
+        Self::sigma_parse(&mut sr)
     }
 }
+
+/// serialization roundtrip
+#[cfg(test)]
+pub fn sigma_serialize_roundtrip<T: SigmaSerializable>(v: &T) -> T {
+    let mut data = Vec::new();
+    let mut w = SigmaByteWriter::new(&mut data, None);
+    v.sigma_serialize(&mut w).expect("serialization failed");
+    let cursor = Cursor::new(&mut data[..]);
+    let pr = PeekableReader::new(cursor);
+    let mut sr = SigmaByteReader::new(pr, ConstantStore::empty());
+    T::sigma_parse(&mut sr).expect("parse failed")
+}
diff --git a/sigma-tree/src/serialization/sigma_byte_reader.rs b/sigma-tree/src/serialization/sigma_byte_reader.rs
new file mode 100644
index 000000000..fd784a669
--- /dev/null
+++ b/sigma-tree/src/serialization/sigma_byte_reader.rs
@@ -0,0 +1,59 @@
+use super::constant_store::ConstantStore;
+use sigma_ser::{peekable_reader::Peekable, vlq_encode::ReadSigmaVlqExt};
+use std::io::Read;
+
+pub struct SigmaByteReader<R> {
+    inner: R,
+    constant_store: ConstantStore,
+    substitute_placeholders: bool,
+}
+
+impl<R: Peekable> SigmaByteReader<R> {
+    /// Create new reader from PeekableReader
+    pub fn new(pr: R, constant_store: ConstantStore) -> SigmaByteReader<R> {
+        SigmaByteReader {
+            inner: pr,
+            constant_store,
+            substitute_placeholders: false,
+        }
+    }
+
+    pub fn new_with_substitute_placeholders(
+        pr: R,
+        constant_store: ConstantStore,
+    ) -> SigmaByteReader<R> {
+        SigmaByteReader {
+            inner: pr,
+            constant_store,
+            substitute_placeholders: true,
+        }
+    }
+}
+
+pub trait SigmaByteRead: ReadSigmaVlqExt {
+    fn constant_store(&mut self) -> &mut ConstantStore;
+
+    fn substitute_placeholders(&self) -> bool;
+}
+
+impl<R: Peekable> Read for SigmaByteReader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.inner.read(buf)
+    }
+}
+
+impl<R: Peekable> Peekable for SigmaByteReader<R> {
+    fn peek_u8(&mut self) -> Result<u8, &std::io::Error> {
+        self.inner.peek_u8()
+    }
+}
+
+impl<R: ReadSigmaVlqExt> SigmaByteRead for SigmaByteReader<R> {
+    fn constant_store(&mut self) -> &mut ConstantStore {
+        &mut self.constant_store
+    }
+
+    fn substitute_placeholders(&self) -> bool {
+        self.substitute_placeholders
+    }
+}
diff --git a/sigma-tree/src/serialization/sigma_byte_writer.rs b/sigma-tree/src/serialization/sigma_byte_writer.rs
new file mode 100644
index 000000000..0c54bb4dc
--- /dev/null
+++ b/sigma-tree/src/serialization/sigma_byte_writer.rs
@@ -0,0 +1,43 @@
+use super::constant_store::ConstantStore;
+use sigma_ser::vlq_encode::WriteSigmaVlqExt;
+use std::io::Write;
+
+pub struct SigmaByteWriter<'a, W> {
+    inner: &'a mut W,
+    constant_store: Option<&'a mut ConstantStore>,
+}
+
+impl<'a, W: Write> SigmaByteWriter<'a, W> {
+    pub fn new(
+        w: &'a mut W,
+        constant_store: Option<&'a mut ConstantStore>,
+    ) -> SigmaByteWriter<'a, W> {
+        SigmaByteWriter {
+            inner: w,
+            constant_store,
+        }
+    }
+}
+
+pub trait SigmaByteWrite: WriteSigmaVlqExt {
+    fn constant_store(&mut self) -> Option<&mut ConstantStore>;
+}
+
+impl<'a, W: Write> Write for SigmaByteWriter<'a, W> {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.inner.write(buf)
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.inner.flush()
+    }
+}
+
+impl<'a, W: Write> SigmaByteWrite for SigmaByteWriter<'a, W> {
+    fn constant_store(&mut self) -> Option<&mut ConstantStore> {
+        match self.constant_store.as_mut() {
+            Some(store) => Some(store),
+            None => None,
+        }
+    }
+}
diff --git a/sigma-tree/src/serialization/sigmaboolean.rs b/sigma-tree/src/serialization/sigmaboolean.rs
index 872d03346..e89507f25 100644
--- a/sigma-tree/src/serialization/sigmaboolean.rs
+++ b/sigma-tree/src/serialization/sigmaboolean.rs
@@ -1,15 +1,15 @@
-use super::op_code::OpCode;
+use super::{op_code::OpCode, sigma_byte_writer::SigmaByteWrite};
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
+};
 use crate::sigma_protocol::{
     dlog_group::EcPoint, ProveDlog, SigmaBoolean, SigmaProofOfKnowledgeTree,
 };
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode,
-};
+
 use std::io;
 
 impl SigmaSerializable for SigmaBoolean {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.op_code().sigma_serialize(w)?;
         match self {
             SigmaBoolean::ProofOfKnowledge(proof) => match proof {
@@ -21,7 +21,7 @@ impl SigmaSerializable for SigmaBoolean {
         }
     }
 
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let op_code = OpCode::sigma_parse(r)?;
         match op_code {
             OpCode::PROVE_DLOG => Ok(SigmaBoolean::ProofOfKnowledge(
@@ -33,11 +33,11 @@ impl SigmaSerializable for SigmaBoolean {
 }
 
 impl SigmaSerializable for ProveDlog {
-    fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         self.h.sigma_serialize(w)
     }
 
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let p = EcPoint::sigma_parse(r)?;
         Ok(ProveDlog::new(p))
     }
diff --git a/sigma-tree/src/serialization/types.rs b/sigma-tree/src/serialization/types.rs
index 0d02002d5..a3c658d4e 100644
--- a/sigma-tree/src/serialization/types.rs
+++ b/sigma-tree/src/serialization/types.rs
@@ -1,10 +1,11 @@
-use crate::types::SType;
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode,
+use super::sigma_byte_writer::SigmaByteWrite;
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
 };
+use crate::types::SType;
+use sigma_ser::vlq_encode;
 use std::{io, ops::Add};
-use vlq_encode::{ReadSigmaVlqExt, WriteSigmaVlqExt};
+use vlq_encode::WriteSigmaVlqExt;
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct TypeCode(u8);
@@ -52,7 +53,7 @@ impl SigmaSerializable for TypeCode {
         w.put_u8(self.value())
     }
 
-    fn sigma_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let b = r.get_u8()?;
         match b {
             0 => Err(SerializationError::InvalidTypePrefix),
@@ -90,7 +91,7 @@ fn is_stype_embeddable(tpe: &SType) -> bool {
 }
 
 impl SigmaSerializable for SType {
-    fn sigma_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
+    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> Result<(), io::Error> {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala#L25-L25
         match self {
             SType::SAny => self.type_code().sigma_serialize(w),
@@ -117,7 +118,7 @@ impl SigmaSerializable for SType {
         }
     }
 
-    fn sigma_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         // for reference see http://github.com/ScorexFoundation/sigmastate-interpreter/blob/25251c1313b0131835f92099f02cef8a5d932b5e/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala#L118-L118
         let type_code = TypeCode::sigma_parse(r)?;
         let constr_id = type_code.value() / TypeCode::PRIM_RANGE;
@@ -143,8 +144,8 @@ impl SigmaSerializable for SType {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::prelude::*;
-    use sigma_ser::test_helpers::*;
 
     proptest! {
 
diff --git a/sigma-tree/src/sigma_protocol/dlog_group.rs b/sigma-tree/src/sigma_protocol/dlog_group.rs
index 510e4ebc1..aecf85e4c 100644
--- a/sigma-tree/src/sigma_protocol/dlog_group.rs
+++ b/sigma-tree/src/sigma_protocol/dlog_group.rs
@@ -16,13 +16,14 @@
 //! On the other hand, any group element can be mapped to some string.
 
 use super::DlogProverInput;
+use crate::serialization::{
+    sigma_byte_reader::SigmaByteRead, SerializationError, SigmaSerializable,
+};
 use k256::arithmetic::{ProjectivePoint, Scalar};
 use k256::{arithmetic::AffinePoint, PublicKey};
 use num_bigint::{BigInt, Sign};
-use sigma_ser::{
-    serializer::{SerializationError, SigmaSerializable},
-    vlq_encode,
-};
+use sigma_ser::vlq_encode;
+
 use std::{convert::TryInto, io};
 
 #[derive(PartialEq, Debug, Clone)]
@@ -107,7 +108,7 @@ impl SigmaSerializable for EcPoint {
         Ok(())
     }
 
-    fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
+    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SerializationError> {
         let mut buf = [0; EcPoint::GROUP_SIZE];
         r.read_exact(&mut buf[..])?;
         if buf[0] != 0 {
@@ -132,8 +133,8 @@ impl SigmaSerializable for EcPoint {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::serialization::sigma_serialize_roundtrip;
     use proptest::prelude::*;
-    use sigma_ser::test_helpers::*;
 
     impl Arbitrary for EcPoint {
         type Parameters = ();
diff --git a/sigma-tree/src/types.rs b/sigma-tree/src/types.rs
index 3eafa0704..5b72ed877 100644
--- a/sigma-tree/src/types.rs
+++ b/sigma-tree/src/types.rs
@@ -2,9 +2,9 @@
 
 use super::serialization::types::TypeCode;
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub struct MethodId(u8);
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub struct TypeId(u8);
 
 #[derive(PartialEq, Eq, Debug, Clone)]
@@ -117,14 +117,14 @@ pub struct SFunc {
     tpe_params: Vec<STypeParam>,
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub struct STypeCompanion {
     pub type_id: TypeId,
     pub type_name: String,
     pub methods: Vec<SMethod>,
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub struct SMethod {
     pub obj_type: Box<STypeCompanion>,
     pub name: String,


Test patch:
diff --git a/sigma-ser/src/test_helpers.rs b/sigma-ser/src/test_helpers.rs
deleted file mode 100644
index 5db0b7cfd..000000000
--- a/sigma-ser/src/test_helpers.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-//! Helper function for testing purposes
-use crate::peekable_reader::PeekableReader;
-use std::io::Cursor;
-
-/// serialization roundtrip
-pub fn sigma_serialize_roundtrip<T: crate::serializer::SigmaSerializable>(v: &T) -> T {
-    let mut data = Vec::new();
-    v.sigma_serialize(&mut data).expect("serialization failed");
-    let cursor = Cursor::new(&mut data[..]);
-    let mut reader = PeekableReader::new(cursor);
-    T::sigma_parse(&mut reader).expect("parse failed")
-}