ergotree_ir/
ergo_tree.rs

1//! ErgoTree
2use crate::mir::constant::Constant;
3use crate::mir::constant::TryExtractFromError;
4use crate::mir::expr::Expr;
5use crate::serialization::SigmaSerializationError;
6use crate::serialization::SigmaSerializeResult;
7use crate::serialization::{
8    sigma_byte_reader::{SigmaByteRead, SigmaByteReader},
9    sigma_byte_writer::{SigmaByteWrite, SigmaByteWriter},
10    SigmaParsingError, SigmaSerializable,
11};
12use crate::sigma_protocol::sigma_boolean::ProveDlog;
13use crate::types::stype::SType;
14use io::Cursor;
15use sigma_ser::vlq_encode::ReadSigmaVlqExt;
16use sigma_ser::vlq_encode::WriteSigmaVlqExt;
17
18use crate::serialization::constant_store::ConstantStore;
19use derive_more::From;
20use std::convert::TryFrom;
21use std::io;
22use std::io::Read;
23use thiserror::Error;
24
25mod tree_header;
26pub use tree_header::*;
27
28/// Parsed ErgoTree
29#[derive(PartialEq, Eq, Debug, Clone)]
30pub struct ParsedErgoTree {
31    header: ErgoTreeHeader,
32    constants: Vec<Constant>,
33    root: Expr,
34}
35
36impl ParsedErgoTree {
37    /// Returns new ParsedTree with a new constant value for a given index in constants list
38    /// (as stored in serialized ErgoTree), or an error
39    fn with_constant(self, index: usize, constant: Constant) -> Result<Self, SetConstantError> {
40        let mut new_constants = self.constants.clone();
41        if let Some(old_constant) = self.constants.get(index) {
42            if constant.tpe == old_constant.tpe {
43                let _ = std::mem::replace(&mut new_constants[index], constant);
44                Ok(Self {
45                    constants: new_constants,
46                    ..self
47                })
48            } else {
49                Err(SetConstantError::TypeMismatch(format!(
50                    "with_constant: expected constant type to be {:?}, got {:?}",
51                    old_constant.tpe, constant.tpe
52                )))
53            }
54        } else {
55            Err(SetConstantError::OutOfBounds(format!(
56                "with_constant: index({0}) out of bounds (lengh = {1})",
57                index,
58                self.constants.len()
59            )))
60        }
61    }
62
63    fn template_bytes(&self) -> Result<Vec<u8>, ErgoTreeError> {
64        Ok(self.root.sigma_serialize_bytes()?)
65    }
66}
67
68/// Errors on fail to set a new constant value
69#[derive(Error, PartialEq, Eq, Debug, Clone)]
70pub enum SetConstantError {
71    /// Index is out of bounds
72    #[error("Index is out of bounds: {0}")]
73    OutOfBounds(String),
74    /// Existing constant type differs from the provided new constant type
75    #[error("Existing constant type differs from the provided new constant type: {0}")]
76    TypeMismatch(String),
77}
78
79/// ErgoTree root expr parsing (deserialization) error inner
80#[derive(Error, PartialEq, Eq, Debug, Clone, From)]
81pub enum ErgoTreeRootParsingError {
82    /// ErgoTree root expr parsing (deserialization) error
83    #[error("SigmaParsingError: {0:?}")]
84    SigmaParsingError(SigmaParsingError),
85    /// Non-consumed bytes after root expr is parsed
86    #[error("Non-consumed bytes after root expr is parsed")]
87    NonConsumedBytes,
88}
89
90/// ErgoTree serialization and parsing (deserialization) error
91#[derive(Error, PartialEq, Eq, Debug, Clone, From)]
92pub enum ErgoTreeError {
93    /// ErgoTree header error
94    #[error("ErgoTree header error: {0:?}")]
95    HeaderError(ErgoTreeHeaderError),
96    /// ErgoTree constants error
97    #[error("ErgoTree constants error: {0:?}")]
98    ConstantsError(ErgoTreeConstantError),
99    /// ErgoTree root expr parsing (deserialization) error
100    #[error("ErgoTree root expr parsing (deserialization) error: {0:?}")]
101    RootParsingError(ErgoTreeRootParsingError),
102    /// ErgoTree serialization error
103    #[error("ErgoTree serialization error: {0}")]
104    RootSerializationError(SigmaSerializationError),
105    /// Sigma parsing error
106    #[error("Sigma parsing error: {0:?}")]
107    SigmaParsingError(SigmaParsingError),
108    /// IO error
109    #[error("IO error: {0:?}")]
110    IoError(String),
111}
112
113/// The root of ErgoScript IR. Serialized instances of this class are self sufficient and can be passed around.
114#[derive(PartialEq, Eq, Debug, Clone, From)]
115pub enum ErgoTree {
116    /// Unparsed tree, with original bytes and error
117    Unparsed {
118        /// Original tree bytes
119        tree_bytes: Vec<u8>,
120        /// Parsing error
121        error: ErgoTreeError,
122    },
123    /// Parsed tree
124    Parsed(ParsedErgoTree),
125}
126
127impl ErgoTree {
128    fn parsed_tree(&self) -> Result<&ParsedErgoTree, ErgoTreeError> {
129        match self {
130            ErgoTree::Unparsed {
131                tree_bytes: _,
132                error,
133            } => Err(error.clone()),
134            ErgoTree::Parsed(parsed) => Ok(parsed),
135        }
136    }
137
138    fn sigma_parse_sized<R: SigmaByteRead>(
139        r: &mut R,
140        header: ErgoTreeHeader,
141    ) -> Result<ParsedErgoTree, ErgoTreeError> {
142        let constants = if header.is_constant_segregation() {
143            ErgoTree::sigma_parse_constants(r)?
144        } else {
145            vec![]
146        };
147        r.set_constant_store(ConstantStore::new(constants.clone()));
148        let root = Expr::sigma_parse(r)?;
149        Ok(ParsedErgoTree {
150            header,
151            constants,
152            root,
153        })
154    }
155
156    fn sigma_parse_constants<R: SigmaByteRead>(
157        r: &mut R,
158    ) -> Result<Vec<Constant>, SigmaParsingError> {
159        let constants_len = r.get_u32()?;
160        if constants_len as usize > ErgoTree::MAX_CONSTANTS_COUNT {
161            return Err(SigmaParsingError::ValueOutOfBounds(
162                "too many constants".to_string(),
163            ));
164        }
165        //dbg!(&constants_len);
166        let mut constants = Vec::with_capacity(constants_len as usize);
167        for _ in 0..constants_len {
168            let c = Constant::sigma_parse(r)?;
169            //dbg!(&c);
170            constants.push(c);
171        }
172        Ok(constants)
173    }
174
175    /// Creates a tree using provided header and root expression
176    pub fn new(header: ErgoTreeHeader, expr: &Expr) -> Result<Self, ErgoTreeError> {
177        Ok(if header.is_constant_segregation() {
178            let mut data = Vec::new();
179            let cs = ConstantStore::empty();
180            let ww = &mut data;
181            let mut w = SigmaByteWriter::new(ww, Some(cs));
182            expr.sigma_serialize(&mut w)?;
183            #[allow(clippy::unwrap_used)]
184            // We set constant store earlier
185            let constants = w.constant_store_mut_ref().unwrap().get_all();
186            let cursor = Cursor::new(&mut data[..]);
187            let new_cs = ConstantStore::new(constants.clone());
188            let mut sr = SigmaByteReader::new(cursor, new_cs);
189            let parsed_expr = Expr::sigma_parse(&mut sr)?;
190            ErgoTree::Parsed(ParsedErgoTree {
191                header,
192                constants,
193                root: parsed_expr,
194            })
195        } else {
196            ErgoTree::Parsed(ParsedErgoTree {
197                header,
198                constants: Vec::new(),
199                root: expr.clone(),
200            })
201        })
202    }
203
204    /// Reasonable limit for the number of constants allowed in the ErgoTree
205    pub const MAX_CONSTANTS_COUNT: usize = 4096;
206
207    /// get Expr out of ErgoTree
208    pub fn proposition(&self) -> Result<Expr, ErgoTreeError> {
209        let tree = self.parsed_tree()?.clone();
210        // This tree has ConstantPlaceholder nodes instead of Constant nodes.
211        // We need to substitute placeholders with constant values.
212        // So far the easiest way to do it is during deserialization (after the serialization)
213        let root = tree.root;
214        if tree.header.is_constant_segregation() {
215            let mut data = Vec::new();
216            let constants = {
217                let cs = ConstantStore::new(tree.constants.clone());
218                let mut w = SigmaByteWriter::new(&mut data, Some(cs));
219                root.sigma_serialize(&mut w)?;
220                #[allow(clippy::unwrap_used)] // constant store is specified in SigmaByteWriter::new
221                w.constant_store.unwrap()
222            };
223            let cursor = Cursor::new(&mut data[..]);
224            let mut sr = SigmaByteReader::new_with_substitute_placeholders(cursor, constants);
225            let parsed_expr = Expr::sigma_parse(&mut sr)?;
226            Ok(parsed_expr)
227        } else {
228            Ok(root)
229        }
230    }
231
232    /// Prints with newlines
233    pub fn debug_tree(&self) -> String {
234        let tree = format!("{:#?}", self);
235        tree
236    }
237
238    /// Returns pretty printed tree
239    pub fn pretty_print(&self) -> Result<(Expr, String), String> {
240        let tree = self.parsed_tree().map_err(|e| e.to_string())?;
241        tree.root.pretty_print().map_err(|e| e.to_string())
242    }
243
244    /// Returns Base16-encoded serialized bytes
245    pub fn to_base16_bytes(&self) -> Result<String, SigmaSerializationError> {
246        let bytes = self.sigma_serialize_bytes()?;
247        Ok(base16::encode_lower(&bytes))
248    }
249
250    /// Returns constants number as stored in serialized ErgoTree or error if the parsing of
251    /// constants is failed
252    pub fn constants_len(&self) -> Result<usize, ErgoTreeError> {
253        self.parsed_tree().map(|tree| tree.constants.len())
254    }
255
256    /// Returns constant with given index (as stored in serialized ErgoTree)
257    /// or None if index is out of bounds
258    /// or error if constants parsing were failed
259    pub fn get_constant(&self, index: usize) -> Result<Option<Constant>, ErgoTreeError> {
260        self.parsed_tree()
261            .map(|tree| tree.constants.get(index).cloned())
262    }
263
264    /// Returns all constants (as stored in serialized ErgoTree)
265    /// or error if constants parsing were failed
266    pub fn get_constants(&self) -> Result<Vec<Constant>, ErgoTreeError> {
267        self.parsed_tree().map(|tree| tree.constants.clone())
268    }
269
270    /// Returns new ErgoTree with a new constant value for a given index in constants list (as
271    /// stored in serialized ErgoTree), or an error. Note that the type of the new constant must
272    /// coincide with that of the constant being replaced, or an error is returned too.
273    pub fn with_constant(self, index: usize, constant: Constant) -> Result<Self, ErgoTreeError> {
274        let parsed_tree = self.parsed_tree()?.clone();
275        Ok(Self::Parsed(
276            parsed_tree
277                .with_constant(index, constant)
278                .map_err(ErgoTreeConstantError::from)?,
279        ))
280    }
281
282    /// Serialized proposition expression of SigmaProp type with
283    /// ConstantPlaceholder nodes instead of Constant nodes
284    pub fn template_bytes(&self) -> Result<Vec<u8>, ErgoTreeError> {
285        self.clone().parsed_tree()?.template_bytes()
286    }
287}
288
289/// Constants related errors
290#[derive(Error, PartialEq, Eq, Debug, Clone, From)]
291pub enum ErgoTreeConstantError {
292    /// Fail to parse a constant when deserializing an ErgoTree
293    #[error("Fail to parse a constant when deserializing an ErgoTree: {0}")]
294    ParsingError(SigmaParsingError),
295    /// Fail to set a new constant value
296    #[error("Fail to set a new constant value: {0}")]
297    SetConstantError(SetConstantError),
298}
299
300impl TryFrom<Expr> for ErgoTree {
301    type Error = ErgoTreeError;
302
303    fn try_from(expr: Expr) -> Result<Self, Self::Error> {
304        match &expr {
305            Expr::Const(c) => match c {
306                Constant { tpe, .. } if *tpe == SType::SSigmaProp => {
307                    ErgoTree::new(ErgoTreeHeader::v0(false), &expr)
308                }
309                _ => ErgoTree::new(ErgoTreeHeader::v0(true), &expr),
310            },
311            _ => ErgoTree::new(ErgoTreeHeader::v0(true), &expr),
312        }
313    }
314}
315
316impl SigmaSerializable for ErgoTree {
317    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
318        match self {
319            ErgoTree::Unparsed {
320                tree_bytes,
321                error: _,
322            } => w.write_all(&tree_bytes[..])?,
323            ErgoTree::Parsed(parsed_tree) => {
324                let bytes = {
325                    let mut data = Vec::new();
326                    let mut inner_w = SigmaByteWriter::new(&mut data, None);
327                    if parsed_tree.header.is_constant_segregation() {
328                        inner_w.put_usize_as_u32_unwrapped(parsed_tree.constants.len())?;
329                        parsed_tree
330                            .constants
331                            .iter()
332                            .try_for_each(|c| c.sigma_serialize(&mut inner_w))?;
333                    };
334                    parsed_tree.root.sigma_serialize(&mut inner_w)?;
335                    data
336                };
337
338                parsed_tree.header.sigma_serialize(w)?;
339                if parsed_tree.header.has_size() {
340                    w.put_usize_as_u32_unwrapped(bytes.len())?;
341                }
342                w.write_all(&bytes)?;
343            }
344        };
345        Ok(())
346    }
347
348    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SigmaParsingError> {
349        let header = ErgoTreeHeader::sigma_parse(r)?;
350        if header.has_size() {
351            let tree_size_bytes = r.get_u32()?;
352            let mut buf = vec![0u8; tree_size_bytes as usize];
353            r.read_exact(buf.as_mut_slice())?;
354            let buf_copy = buf.clone();
355            let mut inner_r =
356                SigmaByteReader::new(Cursor::new(&mut buf[..]), ConstantStore::empty());
357            match ErgoTree::sigma_parse_sized(&mut inner_r, header.clone()) {
358                Ok(parsed_tree) => Ok(parsed_tree.into()),
359                Err(error) => {
360                    let mut bytes = vec![header.serialized()];
361                    #[allow(clippy::unwrap_used)]
362                    bytes.put_u32(tree_size_bytes).unwrap();
363                    bytes.extend_from_slice(&buf_copy);
364                    Ok(ErgoTree::Unparsed {
365                        tree_bytes: bytes,
366                        error,
367                    })
368                }
369            }
370        } else {
371            let constants = if header.is_constant_segregation() {
372                ErgoTree::sigma_parse_constants(r)?
373            } else {
374                vec![]
375            };
376            r.set_constant_store(ConstantStore::new(constants.clone()));
377            let root = Expr::sigma_parse(r)?;
378            Ok(ErgoTree::Parsed(ParsedErgoTree {
379                header,
380                constants,
381                root,
382            }))
383        }
384    }
385
386    fn sigma_parse_bytes(bytes: &[u8]) -> Result<Self, SigmaParsingError> {
387        let wrap_in_ergotree = |r: Result<ParsedErgoTree, ErgoTreeError>| -> Self {
388            match r {
389                Ok(parsed_tree) => ErgoTree::Parsed(parsed_tree),
390                Err(error) => ErgoTree::Unparsed {
391                    tree_bytes: bytes.to_vec(),
392                    error,
393                },
394            }
395        };
396        let mut r = SigmaByteReader::new(Cursor::new(bytes), ConstantStore::empty());
397        let tree: Result<ErgoTree, SigmaParsingError> = match ErgoTreeHeader::sigma_parse(&mut r) {
398            Ok(header) => {
399                if header.has_size() {
400                    let tree_size_bytes = r.get_u32()?;
401                    let mut buf = vec![0u8; tree_size_bytes as usize];
402                    r.read_exact(buf.as_mut_slice())?;
403                    let mut inner_r =
404                        SigmaByteReader::new(Cursor::new(&mut buf[..]), ConstantStore::empty());
405                    Ok(wrap_in_ergotree(ErgoTree::sigma_parse_sized(
406                        &mut inner_r,
407                        header,
408                    )))
409                } else {
410                    Ok(wrap_in_ergotree(ErgoTree::sigma_parse_sized(
411                        &mut r, header,
412                    )))
413                }
414            }
415            Err(e) => Ok(ErgoTree::Unparsed {
416                tree_bytes: bytes.to_vec(),
417                error: e.into(),
418            }),
419        };
420        let mut buffer = Vec::new();
421        if let Ok(0) = r.read_to_end(&mut buffer) {
422            tree
423        } else {
424            Ok(ErgoTree::Unparsed {
425                tree_bytes: bytes.to_vec(),
426                error: ErgoTreeRootParsingError::NonConsumedBytes.into(),
427            })
428        }
429    }
430}
431
432impl TryFrom<ErgoTree> for ProveDlog {
433    type Error = TryExtractFromError;
434
435    fn try_from(tree: ErgoTree) -> Result<Self, Self::Error> {
436        let expr = tree
437            .proposition()
438            .map_err(|_| TryExtractFromError("cannot read root expr".to_string()))?;
439        match expr {
440            Expr::Const(Constant {
441                tpe: SType::SSigmaProp,
442                v,
443            }) => ProveDlog::try_from(v),
444            _ => Err(TryExtractFromError(
445                "expected ProveDlog in the root".to_string(),
446            )),
447        }
448    }
449}
450
451impl From<std::io::Error> for ErgoTreeError {
452    fn from(e: std::io::Error) -> Self {
453        ErgoTreeError::IoError(e.to_string())
454    }
455}
456
457#[cfg(feature = "arbitrary")]
458#[allow(clippy::unwrap_used)]
459pub(crate) mod arbitrary {
460
461    use crate::mir::expr::arbitrary::ArbExprParams;
462
463    use super::*;
464    use proptest::prelude::*;
465
466    impl Arbitrary for ErgoTree {
467        type Parameters = ();
468        type Strategy = BoxedStrategy<Self>;
469
470        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
471            // make sure that P2PK tree is included
472            prop_oneof![
473                any::<ProveDlog>().prop_map(|p| ErgoTree::new(
474                    ErgoTreeHeader::v0(false),
475                    &Expr::Const(p.into())
476                )
477                .unwrap()),
478                any::<ProveDlog>().prop_map(|p| ErgoTree::new(
479                    ErgoTreeHeader::v1(false),
480                    &Expr::Const(p.into())
481                )
482                .unwrap()),
483                // SigmaProp with constant segregation using both v0 and v1 versions
484                any_with::<Expr>(ArbExprParams {
485                    tpe: SType::SSigmaProp,
486                    depth: 1
487                })
488                .prop_map(|e| ErgoTree::new(ErgoTreeHeader::v1(true), &e).unwrap()),
489                any_with::<Expr>(ArbExprParams {
490                    tpe: SType::SSigmaProp,
491                    depth: 1
492                })
493                .prop_map(|e| ErgoTree::new(ErgoTreeHeader::v0(true), &e).unwrap()),
494            ]
495            .boxed()
496        }
497    }
498}
499
500#[cfg(test)]
501#[cfg(feature = "arbitrary")]
502#[allow(clippy::unwrap_used)]
503#[allow(clippy::panic)]
504#[allow(clippy::expect_used)]
505mod tests {
506    use super::*;
507    use crate::chain::address::AddressEncoder;
508    use crate::chain::address::NetworkPrefix;
509    use crate::mir::constant::Literal;
510    use proptest::prelude::*;
511
512    proptest! {
513        #[test]
514        fn ser_roundtrip(v in any::<ErgoTree>()) {
515          //dbg!(&v);
516            let mut data = Vec::new();
517            let mut w = SigmaByteWriter::new(&mut data, None);
518            v.sigma_serialize(&mut w).expect("serialization failed");
519            // sigma_parse
520            let cursor = Cursor::new(&mut data[..]);
521            let mut sr = SigmaByteReader::new(cursor, ConstantStore::empty());
522            let res = ErgoTree::sigma_parse(&mut sr).expect("parse failed");
523            // prop_assert_eq!(&res.template_bytes().unwrap(), &v.template_bytes().unwrap());
524            prop_assert_eq![&res, &v];
525            // sigma_parse_bytes
526            let res = ErgoTree::sigma_parse_bytes(&data).expect("parse failed");
527            prop_assert_eq!(&res.template_bytes().unwrap(), &v.template_bytes().unwrap());
528            prop_assert_eq![res, v];
529        }
530    }
531
532    #[test]
533    fn deserialization_non_parseable_tree_v0() {
534        // constants length is set, invalid constant
535        let bytes = [
536            ErgoTreeHeader::v0(true).serialized(),
537            1, // constants quantity
538            0, // invalid constant type
539            99,
540            99,
541        ];
542        let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
543        assert!(tree.parsed_tree().is_err(), "parsing constants should fail");
544        assert_eq!(
545            tree.sigma_serialize_bytes().unwrap(),
546            bytes,
547            "serialization should return original bytes"
548        );
549        assert!(
550            tree.template_bytes().is_err(),
551            "template bytes should not be parsed"
552        );
553    }
554
555    #[test]
556    fn deserialization_non_parseable_tree_v1() {
557        // v1(size is set), constants length is set, invalid constant
558        let bytes = [
559            ErgoTreeHeader::v1(true).serialized(),
560            4, // tree size
561            1, // constants quantity
562            0, // invalid constant type
563            99,
564            99,
565        ];
566        let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
567        assert!(tree.parsed_tree().is_err(), "parsing constants should fail");
568        assert_eq!(
569            tree.sigma_serialize_bytes().unwrap(),
570            bytes,
571            "serialization should return original bytes"
572        );
573        assert!(
574            tree.template_bytes().is_err(),
575            "template bytes should not be parsed"
576        );
577    }
578
579    #[test]
580    fn deserialization_non_parseable_root_v0() {
581        // no constant segregation, Expr is invalid
582        let bytes = [ErgoTreeHeader::v0(false).serialized(), 0, 1];
583        let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
584        assert!(tree.parsed_tree().is_err(), "parsing root should fail");
585        assert_eq!(
586            tree.sigma_serialize_bytes().unwrap(),
587            bytes,
588            "serialization should return original bytes"
589        );
590        assert!(
591            tree.template_bytes().is_err(),
592            "template bytes should not be parsed"
593        );
594    }
595
596    #[test]
597    fn deserialization_non_parseable_root_v1() {
598        // no constant segregation, Expr is invalid
599        let bytes = [
600            ErgoTreeHeader::v1(false).serialized(),
601            2, // tree size
602            0,
603            1,
604        ];
605        let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
606        assert!(tree.parsed_tree().is_err(), "parsing root should fail");
607        assert_eq!(
608            tree.sigma_serialize_bytes().unwrap(),
609            bytes,
610            "serialization should return original bytes"
611        );
612        assert!(
613            tree.template_bytes().is_err(),
614            "template bytes should not be parsed"
615        );
616        // parsing via sigma_parse should fail as well
617        let mut reader = SigmaByteReader::new(Cursor::new(&bytes), ConstantStore::empty());
618        let tree = ErgoTree::sigma_parse(&mut reader).unwrap();
619        assert!(tree.parsed_tree().is_err(), "parsing root should fail");
620        assert_eq!(
621            tree.sigma_serialize_bytes().unwrap(),
622            bytes,
623            "serialization should return original bytes"
624        );
625        assert!(
626            tree.template_bytes().is_err(),
627            "template bytes should not be parsed"
628        );
629    }
630
631    #[test]
632    fn test_constant_segregation_header_flag_support() {
633        let encoder = AddressEncoder::new(NetworkPrefix::Mainnet);
634        let address = encoder
635            .parse_address_from_str("9hzP24a2q8KLPVCUk7gdMDXYc7vinmGuxmLp5KU7k9UwptgYBYV")
636            .unwrap();
637        let bytes = address.script().unwrap().sigma_serialize_bytes().unwrap();
638        assert_eq!(&bytes[..2], vec![0u8, 8u8].as_slice());
639    }
640
641    #[test]
642    fn test_constant_segregation() {
643        let expr = Expr::Const(Constant {
644            tpe: SType::SBoolean,
645            v: Literal::Boolean(true),
646        });
647        let ergo_tree = ErgoTree::new(ErgoTreeHeader::v0(false), &expr).unwrap();
648        let bytes = ergo_tree.sigma_serialize_bytes().unwrap();
649        let parsed_expr = ErgoTree::sigma_parse_bytes(&bytes)
650            .unwrap()
651            .proposition()
652            .unwrap();
653        assert_eq!(parsed_expr, expr)
654    }
655
656    #[test]
657    fn test_constant_len() {
658        let expr = Expr::Const(Constant {
659            tpe: SType::SBoolean,
660            v: Literal::Boolean(false),
661        });
662        let ergo_tree = ErgoTree::new(ErgoTreeHeader::v0(true), &expr).unwrap();
663        assert_eq!(ergo_tree.constants_len().unwrap(), 1);
664    }
665
666    #[test]
667    fn test_get_constant() {
668        let expr = Expr::Const(Constant {
669            tpe: SType::SBoolean,
670            v: Literal::Boolean(false),
671        });
672        let ergo_tree = ErgoTree::new(ErgoTreeHeader::v0(true), &expr).unwrap();
673        assert_eq!(ergo_tree.constants_len().unwrap(), 1);
674        assert_eq!(ergo_tree.get_constant(0).unwrap().unwrap(), false.into());
675    }
676
677    #[test]
678    fn test_set_constant() {
679        let expr = Expr::Const(Constant {
680            tpe: SType::SBoolean,
681            v: Literal::Boolean(false),
682        });
683        let ergo_tree = ErgoTree::new(ErgoTreeHeader::v0(true), &expr).unwrap();
684        let new_ergo_tree = ergo_tree.with_constant(0, true.into()).unwrap();
685        assert_eq!(new_ergo_tree.get_constant(0).unwrap().unwrap(), true.into());
686    }
687
688    #[test]
689    fn dex_t2tpool_parse() {
690        let base16_str = "19a3030f0400040204020404040404060406058080a0f6f4acdbe01b058080a0f6f4acdbe01b050004d00f0400040005000500d81ad601b2a5730000d602e4c6a70405d603db63087201d604db6308a7d605b27203730100d606b27204730200d607b27203730300d608b27204730400d609b27203730500d60ab27204730600d60b9973078c720602d60c999973088c720502720bd60d8c720802d60e998c720702720dd60f91720e7309d6108c720a02d6117e721006d6127e720e06d613998c7209027210d6147e720d06d615730ad6167e721306d6177e720c06d6187e720b06d6199c72127218d61a9c72167218d1edededededed93c27201c2a793e4c672010405720292c17201c1a793b27203730b00b27204730c00938c7205018c720601ed938c7207018c720801938c7209018c720a019593720c730d95720f929c9c721172127e7202069c7ef07213069a9c72147e7215067e9c720e720206929c9c721472167e7202069c7ef0720e069a9c72117e7215067e9c721372020695ed720f917213730e907217a19d721972149d721a7211ed9272199c7217721492721a9c72177211";
691        let tree_bytes = base16::decode(base16_str.as_bytes()).unwrap();
692        let tree = ErgoTree::sigma_parse_bytes(&tree_bytes).unwrap();
693        //dbg!(&tree);
694        let header = tree.parsed_tree().unwrap().header.clone();
695        assert!(header.has_size());
696        assert!(header.is_constant_segregation());
697        assert_eq!(header.version(), &ErgoTreeVersion::V1);
698        let new_tree = tree
699            .with_constant(7, 1i64.into())
700            .unwrap()
701            .with_constant(8, 2i64.into())
702            .unwrap();
703        assert_eq!(new_tree.get_constant(7).unwrap().unwrap(), 1i64.into());
704        assert_eq!(new_tree.get_constant(8).unwrap().unwrap(), 2i64.into());
705        assert!(new_tree.sigma_serialize_bytes().unwrap().len() > 1);
706    }
707
708    #[test]
709    fn parse_invalid_677() {
710        // also see https://github.com/ergoplatform/sigma-rust/issues/587
711        let base16_str = "cd07021a8e6f59fd4a";
712        let tree_bytes = base16::decode(base16_str.as_bytes()).unwrap();
713        let tree = ErgoTree::sigma_parse_bytes(&tree_bytes).unwrap();
714        //dbg!(&tree);
715        assert_eq!(tree.sigma_serialize_bytes().unwrap(), tree_bytes);
716        assert_eq!(
717            tree,
718            ErgoTree::Unparsed {
719                tree_bytes,
720                error: ErgoTreeRootParsingError::NonConsumedBytes.into()
721            }
722        );
723    }
724
725    #[test]
726    fn parse_invalid_tree_extra_bytes() {
727        let valid_ergo_tree_hex =
728            "0008cd02a706374307f3038cb2f16e7ae9d3e29ca03ea5333681ca06a9bd87baab1164bc";
729        // extra bytes at the end will be left unparsed
730        let invalid_ergo_tree_with_extra_bytes = format!("{}aaaa", valid_ergo_tree_hex);
731        let bytes = base16::decode(invalid_ergo_tree_with_extra_bytes.as_bytes()).unwrap();
732        let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
733        //dbg!(&tree);
734        assert_eq!(tree.sigma_serialize_bytes().unwrap(), bytes);
735        assert_eq!(
736            tree,
737            ErgoTree::Unparsed {
738                tree_bytes: bytes,
739                error: ErgoTreeRootParsingError::NonConsumedBytes.into()
740            }
741        );
742    }
743
744    #[test]
745    fn parse_p2pk_672() {
746        // see https://github.com/ergoplatform/sigma-rust/issues/672
747        let valid_p2pk = "0e2103e02fa2bbd85e9298aa37fe2634602a0fba746234fe2a67f04d14deda55fac491";
748        let bytes = base16::decode(valid_p2pk).unwrap();
749        let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
750        //dbg!(&tree);
751        assert_eq!(tree.sigma_serialize_bytes().unwrap(), bytes);
752        assert_eq!(
753            tree,
754            ErgoTree::Unparsed {
755                tree_bytes: bytes,
756                error: ErgoTreeRootParsingError::NonConsumedBytes.into()
757            }
758        );
759    }
760
761    #[test]
762    fn parse_invalid_tree_707() {
763        // see https://github.com/ergoplatform/sigma-rust/issues/707
764        let ergo_tree_hex =
765            "100208cd03553448c194fdd843c87d080f5e8ed983f5bb2807b13b45a9683bba8c7bfb5ae808cd0354c06b1af711e51986d787ff1df2883fcaf8d34865fea720f549e382063a08ebd1eb0273007301";
766        let bytes = base16::decode(ergo_tree_hex.as_bytes()).unwrap();
767        let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
768        //dbg!(&tree);
769        assert!(tree.parsed_tree().is_err(), "the tree is BoolToSigmaProp(SigmaOr(pk1, pk2)) is invalid (BoolToSigmaProp expects bool");
770    }
771
772    // Test Ergotree.proposition() for contract with some constants segregated already and some not. See: https://github.com/ergoplatform/sigma-rust/issues/757
773    #[test]
774    fn test_contract_template() {
775        let ergo_tree_hex =
776            "10010e20007a24c677a4dc0fdbeaa1c6db1052fc1839b7675851358aaf96823b2245408bd80ed60183200202de02ae02cf025b026402ba02d602f50257020b02ad020a0261020c024e02480249025702cf0247028202300284020002bc02900240024c021d0214021002dad602d9010263e4c672020464d603d901033c0c630eb58c720301d9010563aedb63087205d901074d0e938c7207018c720302d604b2da7203018602db6501fe7300040000d605e3010ed606d9010632b4e4720604020442d607d9010763b2db63087207040000d608da720701a7d609b2da7203018602a58c720801040000d60ad9010a63b2db6308720a040200d60bd9010b0ed801d60ddc640bda72020172040283020e7201720be472059683020193b1dad9010e3c0c630eb58c720e01d901106393cbc272108c720e02018602a4da720601b2720d0402000402dad9010e0e9683030193cbc27209720e93da72070172097208938cda720a017209018cda720a01a70101da720601b2720d040000d60ce4e30002d60ddc0c1aa402a70400d60ed9010e05958f720e0580020402958f720e058080020404958f720e05808080020406958f720e0580808080020408958f720e05808080808002040a958f720e0580808080808002040c958f720e058080808080808002040e958f720e0580808080808080800204100412d197830801dad9010f029593720f0200da720b0183200202030292020802bc024e02ef029a020302e802d7028b0286026302a3020102bb025f02ad02dc02a7028b02e1029d027f02e5023502b302c6024c02be02fe0242010001720cdad9010f029593720f0202da720b01832002028b02c7028f021c026a02ae02c9021e0262028e021502cf0266028c021602cc021e029b02d802e402b902b702e1026d0263021802b502f5022302a502e902bd010001720cdad9010f029593720f0201da720b01832002028802300261022c02520235025f026f0228020d0212029702f1029f026702b0027802c902da02a702d702b0024b0245029c029102cc02640249025702c20280010001720cdad9010f029593720f0203da720b01832002024f02d802b002d602d9028202420272026f025702b302df02a6028602120267029202b802e50205026e021d025102b602e9020d0268028002cf022d02cd02c5010001720cdad9010f029593720f0204da720b018320020289022e026f024702a1020d025c029002b8027a02d402860233025502ce02ad020002c302e202980232021702ee021502530232025302cd029a0260022502c2010001720cdad9010f029593720f0205da720b01832002023a02110295025c0247021902e5028802bc02e602a70261021d022702bd021f02df02db02570238025c02ae02e2026602d80204020c0289024f021c022e021d010001720cdad9010f029593720f0206da720b0183200202090282020f02cb0288027102fb0245020c023e020602b702cb025e022702b002450250028702a302660262021a029d02de0275028202a002190211021e023e010001720cdad9010f029593720f0207dad901113c0e639592720db1a50100d809d613b2a5720d00d614c17213d615c1a7d616c27213d617c4a7d618c2a7d6198cc7a701d61ac47213d61b8cc772130196830401927214997215058092f40193cb7216da720601b2dc640bda7202018c7211020283010e8c721101e5720583000204000093b472179a9ada720e017215b17218da720e017e721905b17217b4721a9a9ada720e017214b17216da720e017e721b05b1721a978302019299721b72190480c33d947218721601860272017204010001720c";
777        let bytes = base16::decode(ergo_tree_hex.as_bytes()).unwrap();
778        let tree = ErgoTree::sigma_parse_bytes(&bytes).unwrap();
779        tree.proposition().unwrap();
780    }
781}