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

Issue:
ErgoTree for P2PK contract serialized into wrong bytes
Following peace of code 
```
    let encoder = sigma_tree::chain::AddressEncoder::new(sigma_tree::chain::NetworkPrefix::Mainnet);
    let address = encoder.parse_address_from_str("9hzP24a2q8KLPVCUk7gdMDXYc7vinmGuxmLp5KU7k9UwptgYBYV").unwrap();

    let contract = sigma_tree::chain::Contract::pay_to_address(address).unwrap();
   let bytes = &contract.get_ergo_tree().sigma_serialise_bytes();
```
returns bytes
```
[
    0,  0,   8, 205,   3,  51,  44, 148,  61,
  231, 78, 149,   5,  42,  24, 133, 168, 248,
   27, 78, 229, 222, 113,  89, 161, 129,  66,
  225, 90,  46, 207,  24, 205,  23,   3, 104,
  219
]

```

It seems that first zero byte is redundant


Discussion / hints:

Fix patch:
diff --git a/sigma-tree/src/chain/json.rs b/sigma-tree/src/chain/json.rs
index 9d5093b2b..55f87e1be 100644
--- a/sigma-tree/src/chain/json.rs
+++ b/sigma-tree/src/chain/json.rs
@@ -140,6 +140,6 @@ mod tests {
             {"boxId":"dd4e69ae683d7c2d1de2b3174182e6c443fd68abbcc24002ddc99adb599e0193","value":1000000,"ergoTree":"0008cd03f1102eb87a4166bf9fbd6247d087e92e1412b0e819dbb5fbc4e716091ec4e4ec","assets":[],"creationHeight":268539,"additionalRegisters":{},"transactionId":"8204d2bbaabf946f89a27b366d1356eb10241dc1619a70b4e4a4a38b520926ce","index":0}
         "#;
         let b: ergo_box::ErgoBoxFromJson = serde_json::from_str(json).unwrap();
-        assert!(b.ergo_tree.proposition().is_err())
+        assert!(b.ergo_tree.proposition().is_ok())
     }
 }
diff --git a/sigma-tree/src/ergo_tree.rs b/sigma-tree/src/ergo_tree.rs
index f67e48b23..eec73291b 100644
--- a/sigma-tree/src/ergo_tree.rs
+++ b/sigma-tree/src/ergo_tree.rs
@@ -30,6 +30,14 @@ pub struct ErgoTree {
 #[derive(PartialEq, Eq, Debug, Clone)]
 struct ErgoTreeHeader(u8);
 
+impl ErgoTreeHeader {
+    const CONSTANT_SEGREGATION_FLAG: u8 = 0x10;
+
+    pub fn is_constant_segregation(&self) -> bool {
+        self.0 & ErgoTreeHeader::CONSTANT_SEGREGATION_FLAG != 0
+    }
+}
+
 /// Whole ErgoTree parsing (deserialization) error
 #[derive(PartialEq, Eq, Debug, Clone)]
 pub struct ErgoTreeConstantsParsingError {
@@ -99,13 +107,15 @@ impl SigmaSerializable for ErgoTree {
         self.header.sigma_serialize(w)?;
         match &self.tree {
             Ok(ParsedTree { constants, root }) => {
-                w.put_usize_as_u32(constants.len())?;
-                assert!(
-                    constants.is_empty(),
-                    "separate constants serialization is not yet supported"
-                );
+                if self.header.is_constant_segregation() {
+                    w.put_usize_as_u32(constants.len())?;
+                    assert!(
+                        constants.is_empty(),
+                        "separate constants serialization is not yet supported"
+                    );
+                }
                 match root {
-                    Ok(tree) => tree.sigma_serialize(w)?,
+                    Ok(expr) => expr.sigma_serialize(w)?,
                     Err(ErgoTreeRootParsingError { bytes, .. }) => w.write_all(&bytes[..])?,
                 }
             }
@@ -116,63 +126,65 @@ impl SigmaSerializable for ErgoTree {
 
     fn sigma_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
         let header = ErgoTreeHeader::sigma_parse(r)?;
-        let constants_len = r.get_u32()?;
-        if constants_len != 0 {
-            Err(SerializationError::NotImplementedYet(
-                "separate constants serialization is not yet supported".to_string(),
-            ))
-        } else {
-            let constants = Vec::new();
-            let root = Expr::sigma_parse(r)?;
-            Ok(ErgoTree {
-                header,
-                tree: Ok(ParsedTree {
-                    constants,
-                    root: Ok(Rc::new(root)),
-                }),
-            })
+        if header.is_constant_segregation() {
+            let constants_len = r.get_u32()?;
+            if constants_len != 0 {
+                return Err(SerializationError::NotImplementedYet(
+                    "separate constants serialization is not yet supported".to_string(),
+                ));
+            }
         }
+        let constants = Vec::new();
+        let root = Expr::sigma_parse(r)?;
+        Ok(ErgoTree {
+            header,
+            tree: Ok(ParsedTree {
+                constants,
+                root: Ok(Rc::new(root)),
+            }),
+        })
     }
 
     fn sigma_parse_bytes(mut bytes: Vec<u8>) -> Result<Self, SerializationError> {
         let cursor = Cursor::new(&mut bytes[..]);
         let mut r = PeekableReader::new(cursor);
         let header = ErgoTreeHeader::sigma_parse(&mut r)?;
-        let constants_len = r.get_u32()?;
-        if constants_len != 0 {
-            Ok(ErgoTree {
-                header,
-                tree: Err(ErgoTreeConstantsParsingError {
-                    bytes: bytes[1..].to_vec(),
-                    error: SerializationError::NotImplementedYet(
-                        "separate constants serialization is not yet supported".to_string(),
-                    ),
-                }),
-            })
-        } else {
-            let constants = Vec::new();
-            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) {
-                Ok(parsed) => Ok(ErgoTree {
+        if header.is_constant_segregation() {
+            let constants_len = r.get_u32()?;
+            if constants_len != 0 {
+                return Ok(ErgoTree {
                     header,
-                    tree: Ok(ParsedTree {
-                        constants,
-                        root: Ok(Rc::new(parsed)),
+                    tree: Err(ErgoTreeConstantsParsingError {
+                        bytes: bytes[1..].to_vec(),
+                        error: SerializationError::NotImplementedYet(
+                            "separate constants serialization is not yet supported".to_string(),
+                        ),
                     }),
+                });
+            }
+        }
+        let constants = Vec::new();
+        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) {
+            Ok(parsed) => Ok(ErgoTree {
+                header,
+                tree: Ok(ParsedTree {
+                    constants,
+                    root: Ok(Rc::new(parsed)),
                 }),
-                Err(err) => Ok(ErgoTree {
-                    header,
-                    tree: Ok(ParsedTree {
-                        constants,
-                        root: Err(ErgoTreeRootParsingError {
-                            bytes: rest_of_the_bytes_copy,
-                            error: err,
-                        }),
+            }),
+            Err(err) => Ok(ErgoTree {
+                header,
+                tree: Ok(ParsedTree {
+                    constants,
+                    root: Err(ErgoTreeRootParsingError {
+                        bytes: rest_of_the_bytes_copy,
+                        error: err,
                     }),
                 }),
-            }
+            }),
         }
     }
 }
@@ -180,7 +192,7 @@ impl SigmaSerializable for ErgoTree {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{ast::ConstantVal, sigma_protocol::SigmaProp};
+    use crate::{ast::ConstantVal, chain, sigma_protocol::SigmaProp};
     use proptest::prelude::*;
     use sigma_ser::test_helpers::*;
 
@@ -219,4 +231,16 @@ mod tests {
         // constants length is zero, but Expr is invalid
         assert!(ErgoTree::sigma_parse_bytes(vec![0, 0, 1]).is_ok());
     }
+
+    #[test]
+    fn test_constant_segregation_header_flag_support() {
+        let encoder = chain::AddressEncoder::new(chain::NetworkPrefix::Mainnet);
+        let address = encoder
+            .parse_address_from_str("9hzP24a2q8KLPVCUk7gdMDXYc7vinmGuxmLp5KU7k9UwptgYBYV")
+            .unwrap();
+
+        let contract = chain::Contract::pay_to_address(address).unwrap();
+        let bytes = &contract.get_ergo_tree().sigma_serialise_bytes();
+        assert_eq!(&bytes[..2], vec![0u8, 8u8].as_slice());
+    }
 }


Test patch: