• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ergoplatform / sigma-rust / 16405540612

20 Jul 2025 11:50PM UTC coverage: 78.438% (-0.01%) from 78.451%
16405540612

Pull #790

github

web-flow
Merge 7bd76aff4 into 2725f402c
Pull Request #790: Use precomputed tables

62 of 69 new or added lines in 16 files covered. (89.86%)

10 existing lines in 5 files now uncovered.

11961 of 15249 relevant lines covered (78.44%)

2.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.02
/ergotree-ir/src/mir/expr.rs
1
//! IR expression
2

3
use alloc::boxed::Box;
4
use alloc::string::String;
5
use alloc::vec::Vec;
6
use core::convert::Infallible;
7
use core::convert::TryFrom;
8
use core::convert::TryInto;
9

10
use crate::chain::context::Context;
11
use crate::chain::ergo_box::RegisterId;
12
use crate::chain::ergo_box::RegisterIdOutOfBounds;
13
use crate::chain::ergo_box::RegisterValueError;
14
use crate::pretty_printer::PosTrackingWriter;
15
use crate::pretty_printer::Print;
16
use crate::serialization::sigma_byte_reader;
17
use crate::serialization::sigma_byte_reader::SigmaByteRead;
18
use crate::serialization::SigmaParsingError;
19
use crate::serialization::SigmaSerializable;
20
use crate::source_span::Spanned;
21
use crate::traversable::Traversable;
22
use crate::types::stype::LiftIntoSType;
23
use crate::types::stype::SType;
24

25
use super::and::And;
26
use super::apply::Apply;
27
use super::bin_op::BinOp;
28
use super::bit_inversion::BitInversion;
29
use super::block::BlockValue;
30
use super::bool_to_sigma::BoolToSigmaProp;
31
use super::byte_array_to_long::ByteArrayToLong;
32
use super::calc_blake2b256::CalcBlake2b256;
33
use super::calc_sha256::CalcSha256;
34
use super::coll_append::Append;
35
use super::coll_by_index::ByIndex;
36
use super::coll_exists::Exists;
37
use super::coll_filter::Filter;
38
use super::coll_fold::Fold;
39
use super::coll_forall::ForAll;
40
use super::coll_map::Map;
41
use super::coll_size::SizeOf;
42
use super::coll_slice::Slice;
43
use super::collection::Collection;
44
use super::constant::Constant;
45
use super::constant::ConstantPlaceholder;
46
use super::constant::Literal;
47
use super::constant::TryExtractFrom;
48
use super::constant::TryExtractFromError;
49
use super::constant::TryExtractInto;
50
use super::create_avl_tree::CreateAvlTree;
51
use super::create_provedlog::CreateProveDlog;
52
use super::decode_point::DecodePoint;
53
use super::exponentiate::Exponentiate;
54
use super::extract_amount::ExtractAmount;
55
use super::extract_bytes::ExtractBytes;
56
use super::extract_bytes_with_no_ref::ExtractBytesWithNoRef;
57
use super::extract_creation_info::ExtractCreationInfo;
58
use super::extract_id::ExtractId;
59
use super::extract_reg_as::ExtractRegisterAs;
60
use super::extract_script_bytes::ExtractScriptBytes;
61
use super::func_value::FuncValue;
62
use super::global_vars::GlobalVars;
63
use super::if_op::If;
64
use super::logical_not::LogicalNot;
65
use super::long_to_byte_array::LongToByteArray;
66
use super::method_call::MethodCall;
67
use super::multiply_group::MultiplyGroup;
68
use super::negation::Negation;
69
use super::option_get::OptionGet;
70
use super::option_get_or_else::OptionGetOrElse;
71
use super::option_is_defined::OptionIsDefined;
72
use super::or::Or;
73
use super::property_call::PropertyCall;
74
use super::select_field::SelectField;
75
use super::sigma_and::SigmaAnd;
76
use super::sigma_or::SigmaOr;
77
use super::sigma_prop_bytes::SigmaPropBytes;
78
use super::subst_const::SubstConstants;
79
use super::tree_lookup::TreeLookup;
80
use super::tuple::Tuple;
81
use super::upcast::Upcast;
82
use super::val_def::ValDef;
83
use super::val_use::ValUse;
84
use super::xor::Xor;
85

86
extern crate derive_more;
87
use crate::mir::atleast::Atleast;
88
use crate::mir::byte_array_to_bigint::ByteArrayToBigInt;
89
use crate::mir::create_prove_dh_tuple::CreateProveDhTuple;
90
use crate::mir::deserialize_context::DeserializeContext;
91
use crate::mir::deserialize_register::DeserializeRegister;
92
use crate::mir::downcast::Downcast;
93
use crate::mir::get_var::GetVar;
94
use crate::mir::xor_of::XorOf;
95
use bounded_vec::BoundedVecOutOfBounds;
96
use derive_more::From;
97
use derive_more::TryInto;
98
use thiserror::Error;
99

100
#[derive(PartialEq, Eq, Debug, Clone, From, TryInto)]
101
/// Expression in ErgoTree
102
pub enum Expr {
103
    /// Append - Concatenation of two collections
104
    Append(Spanned<Append>),
105
    /// Constant value
106
    Const(Constant),
107
    /// Placeholder for a constant
108
    ConstPlaceholder(ConstantPlaceholder),
109
    /// Substitute constants in serialized ergo tree
110
    SubstConstants(Spanned<SubstConstants>),
111
    /// Convert byte array to SLong
112
    ByteArrayToLong(Spanned<ByteArrayToLong>),
113
    /// Convert byte array to SLong
114
    ByteArrayToBigInt(Spanned<ByteArrayToBigInt>),
115
    /// Convert SLong to a byte array
116
    LongToByteArray(LongToByteArray),
117
    /// Collection declaration (array of expressions of the same type)
118
    Collection(Collection),
119
    /// Tuple declaration
120
    Tuple(Tuple),
121
    /// Predefined functions (global)
122
    /// Blake2b256 hash calculation
123
    CalcBlake2b256(CalcBlake2b256),
124
    /// Sha256 hash calculation
125
    CalcSha256(CalcSha256),
126
    /// Context variables (external)
127
    Context,
128
    /// Special global value which is used to define methods
129
    Global,
130
    /// Predefined global variables
131
    GlobalVars(GlobalVars),
132
    /// Function definition
133
    FuncValue(FuncValue),
134
    /// Function application
135
    Apply(Apply),
136
    /// Method call
137
    MethodCall(Spanned<MethodCall>),
138
    /// Property call
139
    PropertyCall(Spanned<PropertyCall>),
140
    /// Block (statements, followed by an expression)
141
    BlockValue(Spanned<BlockValue>),
142
    /// let-bound expression
143
    ValDef(Spanned<ValDef>),
144
    /// Reference to ValDef
145
    ValUse(ValUse),
146
    /// If, non-lazy - evaluate both branches
147
    If(If),
148
    /// Binary operation
149
    BinOp(Spanned<BinOp>),
150
    /// Logical AND
151
    And(Spanned<And>),
152
    /// Logical OR
153
    Or(Spanned<Or>),
154
    /// Byte-wise XOR
155
    Xor(Xor),
156
    /// THRESHOLD composition for sigma expressions
157
    Atleast(Atleast),
158
    /// LogicalNot
159
    LogicalNot(Spanned<LogicalNot>),
160
    /// Negation on numeric type
161
    Negation(Spanned<Negation>),
162
    /// Bit inversion on numeric type
163
    BitInversion(BitInversion),
164
    /// Option.get method
165
    OptionGet(Spanned<OptionGet>),
166
    /// Option.isDefined method
167
    OptionIsDefined(Spanned<OptionIsDefined>),
168
    /// Returns the option's value if the option is nonempty, otherwise return the result of evaluating `default`.
169
    OptionGetOrElse(Spanned<OptionGetOrElse>),
170
    /// Box monetary value
171
    ExtractAmount(ExtractAmount),
172
    /// Extract register's value (box.RX properties)
173
    ExtractRegisterAs(Spanned<ExtractRegisterAs>),
174
    /// Extract serialized box bytes
175
    ExtractBytes(ExtractBytes),
176
    /// Extract serialized box bytes excluding transaction_id & index
177
    ExtractBytesWithNoRef(ExtractBytesWithNoRef),
178
    /// Extract box's guarding script serialized to bytes
179
    ExtractScriptBytes(ExtractScriptBytes),
180
    /// Tuple of height when block got included into the blockchain and transaction identifier with
181
    /// box index in the transaction outputs serialized to the byte array.
182
    ExtractCreationInfo(ExtractCreationInfo),
183
    /// Box id, Blake2b256 hash of this box's content, basically equals to `blake2b256(bytes)`
184
    ExtractId(ExtractId),
185
    /// Collection, get element by index
186
    ByIndex(Spanned<ByIndex>),
187
    /// Collection size
188
    SizeOf(SizeOf),
189
    /// Collection slice
190
    Slice(Spanned<Slice>),
191
    /// Collection fold op
192
    Fold(Spanned<Fold>),
193
    /// Collection map op
194
    Map(Spanned<Map>),
195
    /// Collection filter op
196
    Filter(Spanned<Filter>),
197
    /// Tests whether a predicate holds for at least one element of this collection
198
    Exists(Spanned<Exists>),
199
    /// Tests whether a predicate holds for all elements of this collection.
200
    ForAll(Spanned<ForAll>),
201
    /// Tuple field access
202
    SelectField(Spanned<SelectField>),
203
    /// Bool to SigmaProp
204
    BoolToSigmaProp(BoolToSigmaProp),
205
    /// Upcast numeric value
206
    Upcast(Upcast),
207
    /// Downcast numeric value
208
    Downcast(Downcast),
209
    /// Create proveDlog from GroupElement(PK)
210
    CreateProveDlog(CreateProveDlog),
211
    /// Create proveDlog from GroupElement(PK)
212
    CreateProveDhTuple(CreateProveDhTuple),
213
    /// Extract serialized bytes of a SigmaProp value
214
    SigmaPropBytes(SigmaPropBytes),
215
    /// Decode byte array to EC point
216
    DecodePoint(DecodePoint),
217
    /// AND conjunction for sigma propositions
218
    SigmaAnd(SigmaAnd),
219
    /// OR conjunction for sigma propositions
220
    SigmaOr(SigmaOr),
221
    /// Extracts Context variable by id and type
222
    GetVar(Spanned<GetVar>),
223
    /// Extract register of SELF box as `Coll[Byte]`, deserialize it into Value and inline into
224
    /// the executing script.
225
    DeserializeRegister(DeserializeRegister),
226
    /// Extracts context variable as `Coll[Byte]`, deserializes it to script and then executes
227
    /// this script in the current context. The original `Coll[Byte]` of the script is
228
    /// available as `getVar[Coll[Byte]](id)` On evaluation returns the result of the
229
    /// script execution in the current context
230
    DeserializeContext(DeserializeContext),
231
    /// MultiplyGroup op for GroupElement
232
    MultiplyGroup(MultiplyGroup),
233
    /// Exponentiate op for GroupElement
234
    Exponentiate(Exponentiate),
235
    /// XOR for collection of booleans
236
    XorOf(XorOf),
237
    /// Perform a lookup by key in an AVL tree
238
    TreeLookup(Spanned<TreeLookup>),
239
    /// Create an AVL tree
240
    CreateAvlTree(CreateAvlTree),
241
}
242

243
impl Expr {
244
    /// Type of the expression
245
    pub fn tpe(&self) -> SType {
7✔
246
        match self {
10✔
247
            Expr::Append(ap) => ap.expr().tpe(),
×
248
            Expr::Const(v) => v.tpe.clone(),
7✔
249
            Expr::Collection(v) => v.tpe(),
3✔
250
            Expr::SubstConstants(v) => v.expr().tpe(),
×
251
            Expr::ByteArrayToLong(v) => v.expr().tpe(),
1✔
252
            Expr::ByteArrayToBigInt(v) => v.expr().tpe(),
×
253
            Expr::LongToByteArray(v) => v.tpe(),
×
254
            Expr::ConstPlaceholder(v) => v.tpe.clone(),
8✔
255
            Expr::CalcBlake2b256(v) => v.tpe(),
×
256
            Expr::CalcSha256(v) => v.tpe(),
×
257
            Expr::Global => SType::SGlobal,
2✔
258
            Expr::Context => SType::SContext,
3✔
259
            Expr::GlobalVars(v) => v.tpe(),
5✔
260
            Expr::FuncValue(v) => v.tpe(),
3✔
261
            Expr::Apply(v) => v.tpe(),
2✔
262
            Expr::MethodCall(v) => v.expr().tpe(),
2✔
263
            Expr::PropertyCall(v) => v.expr().tpe(),
8✔
264
            Expr::BlockValue(v) => v.expr().tpe(),
5✔
265
            Expr::ValDef(v) => v.expr().tpe(),
×
266
            Expr::ValUse(v) => v.tpe.clone(),
5✔
267
            Expr::BinOp(v) => v.expr().tpe(),
7✔
268
            Expr::OptionGet(v) => v.expr().tpe(),
5✔
269
            Expr::ExtractRegisterAs(v) => v.expr().tpe(),
6✔
270
            Expr::Fold(v) => v.expr().tpe(),
2✔
271
            Expr::SelectField(v) => v.expr().tpe(),
6✔
272
            Expr::ExtractAmount(v) => v.tpe(),
3✔
273
            Expr::And(v) => v.expr().tpe(),
1✔
274
            Expr::Or(v) => v.expr().tpe(),
1✔
275
            Expr::Xor(v) => v.tpe(),
×
276
            Expr::Atleast(v) => v.tpe(),
2✔
277
            Expr::LogicalNot(v) => v.expr().tpe(),
1✔
278
            Expr::Map(v) => v.expr().tpe(),
2✔
279
            Expr::Filter(v) => v.expr().tpe(),
1✔
280
            Expr::BoolToSigmaProp(v) => v.tpe(),
6✔
281
            Expr::Upcast(v) => v.tpe(),
5✔
282
            Expr::Downcast(v) => v.tpe(),
×
283
            Expr::If(v) => v.tpe(),
3✔
284
            Expr::ByIndex(v) => v.expr().tpe(),
6✔
285
            Expr::ExtractScriptBytes(v) => v.tpe(),
3✔
286
            Expr::SizeOf(v) => v.tpe(),
2✔
287
            Expr::Slice(v) => v.expr().tpe(),
1✔
288
            Expr::CreateProveDlog(v) => v.tpe(),
3✔
289
            Expr::CreateProveDhTuple(v) => v.tpe(),
×
290
            Expr::ExtractCreationInfo(v) => v.tpe(),
2✔
291
            Expr::Exists(v) => v.expr().tpe(),
1✔
292
            Expr::ExtractId(v) => v.tpe(),
2✔
293
            Expr::SigmaPropBytes(v) => v.tpe(),
×
294
            Expr::OptionIsDefined(v) => v.expr().tpe(),
2✔
295
            Expr::OptionGetOrElse(v) => v.expr().tpe(),
1✔
296
            Expr::Negation(v) => v.expr().tpe(),
1✔
297
            Expr::BitInversion(v) => v.tpe(),
×
298
            Expr::ForAll(v) => v.expr().tpe(),
×
299
            Expr::Tuple(v) => v.tpe(),
2✔
300
            Expr::DecodePoint(v) => v.tpe(),
1✔
301
            Expr::SigmaAnd(v) => v.tpe(),
5✔
302
            Expr::SigmaOr(v) => v.tpe(),
5✔
303
            Expr::DeserializeRegister(v) => v.tpe(),
×
304
            Expr::DeserializeContext(v) => v.tpe(),
3✔
305
            Expr::GetVar(v) => v.expr().tpe(),
2✔
306
            Expr::MultiplyGroup(v) => v.tpe(),
×
307
            Expr::Exponentiate(v) => v.tpe(),
×
308
            Expr::XorOf(v) => v.tpe(),
×
309
            Expr::ExtractBytes(v) => v.tpe(),
2✔
310
            Expr::ExtractBytesWithNoRef(v) => v.tpe(),
1✔
311
            Expr::TreeLookup(v) => v.expr().tpe(),
×
312
            Expr::CreateAvlTree(v) => v.tpe(),
×
313
        }
314
    }
315

316
    /// Type expected after the evaluation
317
    pub fn post_eval_tpe(&self) -> SType {
6✔
318
        match self.tpe() {
8✔
319
            SType::SFunc(sfunc) => *sfunc.t_range,
×
320
            tpe => tpe,
8✔
321
        }
322
    }
323

324
    /// Check if given expected_tpe type is the same as the expression's post-evaluation type
325
    pub fn check_post_eval_tpe(
5✔
326
        &self,
327
        expected_tpe: &SType,
328
    ) -> Result<(), InvalidExprEvalTypeError> {
329
        let expr_tpe = self.post_eval_tpe();
5✔
330
        if &expr_tpe == expected_tpe {
15✔
331
            Ok(())
5✔
332
        } else {
333
            #[cfg(feature = "std")]
334
            let backtrace = std::backtrace::Backtrace::capture();
×
335
            #[cfg(not(feature = "std"))]
336
            let backtrace = "Backtraces not supported without std";
337
            Err(InvalidExprEvalTypeError(format!(
×
338
                "expected: {0:?}, got: {1:?}\nBacktrace:\n{backtrace}",
339
                expected_tpe, expr_tpe
340
            )))
341
        }
342
    }
343

344
    /// Rewrite tree bottom-up, matching nodes that satisfy `rule` and applying `subst` to them
345
    pub fn rewrite_bu(&self, rule: impl Fn(&Expr) -> bool, subst: impl Fn(&mut Expr)) -> Self {
×
346
        let mut new_root = self.clone();
×
347
        Self::rewrite_bu_inner(&mut new_root, &rule, &|node| {
×
348
            Ok::<_, Infallible>(subst(node))
×
349
        })
350
        .unwrap();
351
        new_root
×
352
    }
353

354
    /// Attempt to rewrite tree, returning early if `subst` returns `Err(E)`
355
    pub fn try_rewrite_bu<E>(
6✔
356
        mut self,
357
        rule: impl Fn(&Expr) -> bool,
358
        subst: impl Fn(&mut Expr) -> Result<(), E>,
359
    ) -> Result<Self, E> {
360
        Self::rewrite_bu_inner(&mut self, &rule, &subst).map(|_| self)
23✔
361
    }
362

363
    fn rewrite_bu_inner<E>(
6✔
364
        root: &mut Expr,
365
        rule: &impl Fn(&Expr) -> bool,
366
        subst: &impl Fn(&mut Expr) -> Result<(), E>,
367
    ) -> Result<(), E> {
368
        root.children_mut()
12✔
369
            .try_for_each(|expr| Self::rewrite_bu_inner(expr, rule, subst))?;
18✔
370
        if rule(root) {
6✔
371
            subst(root)?;
8✔
372
        }
373
        Ok(())
7✔
374
    }
375

376
    /// Return an iterator over the [`Expr`]
377
    /// The iterator performs a pre-order traversal
378
    pub fn iter(&self) -> impl Iterator<Item = &Expr> {
3✔
379
        struct ExprIterator<'a> {
380
            stack: Vec<&'a Expr>,
381
        }
382
        impl<'a> Iterator for ExprIterator<'a> {
383
            type Item = &'a Expr;
384

385
            fn next(&mut self) -> Option<Self::Item> {
3✔
386
                let cur = self.stack.pop()?;
5✔
387
                for node in cur.children() {
6✔
388
                    self.stack.push(node);
6✔
389
                }
390
                Some(cur)
5✔
391
            }
392
        }
393
        ExprIterator { stack: vec![&self] }
5✔
394
    }
395

396
    /// Returns true if the [`Expr`] has deserialize nodes, see: [`DeserializeContext`] and [`DeserializeRegister`]
397
    pub(crate) fn has_deserialize(&self) -> bool {
3✔
398
        self.iter().any(|c| {
8✔
399
            matches!(
3✔
400
                c,
5✔
401
                Expr::DeserializeContext(_) | Expr::DeserializeRegister(_)
402
            )
403
        })
404
    }
405

406
    /// Rewrite expr, replacing [`DeserializeContext`] and [`DeserializeRegister`] nodes with their respective context extension/register values
407
    // TODO: work on soft-fork for sigma-rust, in that case if deserializing expr fails with validation err, reduce_to_crypto should return true
408
    pub fn substitute_deserialize(self, ctx: &Context) -> Result<Self, SubstDeserializeError> {
1✔
409
        self.try_rewrite_bu(
1✔
410
            |expr| {
1✔
411
                matches!(
1✔
412
                    expr,
1✔
413
                    Expr::DeserializeContext(_) | Expr::DeserializeRegister(_)
414
                )
415
            },
416
            |expr| {
1✔
417
                let (tpe, parsed_expr): (&mut SType, Expr) = match expr {
2✔
418
                    Expr::DeserializeContext(DeserializeContext { tpe, id }) => {
1✔
419
                        let vec = ctx
4✔
420
                            .extension
421
                            .values
422
                            .get(&*id)
423
                            .ok_or(SubstDeserializeError::ExtensionKeyNotFound(*id))?
2✔
424
                            .clone()
425
                            .try_extract_into::<Vec<u8>>()?;
1✔
426
                        (
427
                            tpe,
428
                            sigma_byte_reader::from_bytes(&vec)
3✔
429
                                .with_tree_version(ctx.tree_version(), Expr::sigma_parse)?,
2✔
430
                        )
431
                    }
432
                    Expr::DeserializeRegister(DeserializeRegister { reg, tpe, default }) => {
1✔
433
                        let expr = ctx
7✔
434
                            .self_box
435
                            .get_register(*reg)?
1✔
436
                            .map(|constant| -> Result<_, SubstDeserializeError> {
2✔
437
                                Ok(sigma_byte_reader::from_bytes(
2✔
438
                                    &constant.try_extract_into::<Vec<u8>>()?,
1✔
439
                                )
440
                                .with_tree_version(ctx.tree_version(), Expr::sigma_parse)?)
1✔
441
                            })
442
                            .transpose()?
1✔
443
                            .or(default.as_deref().cloned());
3✔
444
                        match expr {
1✔
445
                            Some(expr) => (tpe, expr),
1✔
446
                            None => return Ok(()), // When script in register is not found, and default is not defined, leave DeserializeRegisterNode unchanged, which will error on evaluation
×
447
                        }
448
                    }
449
                    #[allow(clippy::unreachable)] // Rule is already checked in filter
450
                    _ => unreachable!(),
×
451
                };
452
                if parsed_expr.tpe() != *tpe {
2✔
453
                    return Err(SubstDeserializeError::ExprTpeError {
1✔
454
                        expected: tpe.clone(),
1✔
455
                        actual: parsed_expr.tpe(),
1✔
456
                    });
457
                }
458
                *expr = parsed_expr;
1✔
459
                Ok(())
1✔
460
            },
461
        )
462
    }
463

464
    /// Substitute [`ConstantPlaceholder`] nodes in `self` with [`Constant`]
465
    /// Errors if a constant can not be found in `constants`
466
    pub fn substitute_constants(self, constants: &[Constant]) -> Result<Self, SigmaParsingError> {
5✔
467
        self.try_rewrite_bu::<SigmaParsingError>(
5✔
468
            |expr| matches!(expr, Expr::ConstPlaceholder(_)),
14✔
469
            |expr| {
6✔
470
                if let Expr::ConstPlaceholder(ConstantPlaceholder { id, tpe: _ }) = expr {
10✔
471
                    *expr = constants
22✔
472
                        .get(*id as usize)
4✔
473
                        .cloned()
474
                        .map(Expr::from)
475
                        .ok_or(SigmaParsingError::ConstantForPlaceholderNotFound(*id))?;
4✔
476
                }
477
                Ok(())
4✔
478
            },
479
        )
480
    }
481

482
    /// Prints the tree with newlines
483
    pub fn debug_tree(&self) -> String {
1✔
484
        let tree = format!("{:#?}", self);
1✔
485
        tree
486
    }
487

488
    /// Pretty prints the tree
489
    pub fn to_string_pretty(&self) -> String {
×
490
        let mut printer = PosTrackingWriter::new();
×
491
        #[allow(clippy::unwrap_used)] // it only fail due to formatting errors
492
        let _spanned_expr = self.print(&mut printer).unwrap();
×
493
        printer.as_string()
×
494
    }
495
}
496

497
impl Traversable for Expr {
498
    type Item = Expr;
499

500
    fn children<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Expr> + 'a> {
5✔
501
        match self {
6✔
502
            Expr::Const(_) => Box::new(core::iter::empty()),
3✔
503
            Expr::SubstConstants(op) => op.children(),
×
504
            Expr::ByteArrayToLong(op) => op.children(),
×
505
            Expr::ByteArrayToBigInt(op) => op.children(),
×
506
            Expr::LongToByteArray(op) => op.children(),
×
507
            Expr::CalcBlake2b256(op) => op.children(),
×
508
            Expr::CalcSha256(op) => op.children(),
×
509
            Expr::Fold(op) => op.children(),
×
510
            Expr::ExtractRegisterAs(op) => op.children(),
×
511
            Expr::GlobalVars(op) => op.children(),
×
512
            Expr::MethodCall(op) => op.children(),
×
513
            Expr::PropertyCall(op) => op.children(),
×
514
            Expr::BinOp(op) => op.children(),
1✔
515
            Expr::Global => Box::new(core::iter::empty()),
×
516
            Expr::Context => Box::new(core::iter::empty()),
×
517
            Expr::OptionGet(v) => v.children(),
×
518
            Expr::Apply(op) => op.children(),
×
519
            Expr::FuncValue(op) => op.children(),
×
520
            Expr::ValUse(op) => op.children(),
1✔
521
            Expr::BlockValue(op) => op.children(),
1✔
522
            Expr::SelectField(op) => op.children(),
×
523
            Expr::ExtractAmount(op) => op.children(),
×
524
            Expr::ConstPlaceholder(_) => Box::new(core::iter::empty()),
5✔
525
            Expr::Collection(op) => op.children(),
1✔
526
            Expr::ValDef(op) => op.children(),
1✔
527
            Expr::And(op) => op.children(),
×
528
            Expr::Or(op) => op.children(),
×
529
            Expr::Xor(op) => op.children(),
×
530
            Expr::Atleast(op) => op.children(),
3✔
531
            Expr::LogicalNot(op) => op.children(),
×
532
            Expr::Map(op) => op.children(),
×
533
            Expr::Filter(op) => op.children(),
×
534
            Expr::BoolToSigmaProp(op) => op.children(),
×
535
            Expr::Upcast(op) => op.children(),
×
536
            Expr::Downcast(op) => op.children(),
×
537
            Expr::If(op) => op.children(),
×
538
            Expr::Append(op) => op.children(),
×
539
            Expr::ByIndex(op) => op.children(),
×
540
            Expr::ExtractScriptBytes(op) => op.children(),
×
541
            Expr::SizeOf(op) => op.children(),
×
542
            Expr::Slice(op) => op.children(),
×
543
            Expr::CreateProveDlog(op) => op.children(),
×
544
            Expr::CreateProveDhTuple(op) => op.children(),
×
545
            Expr::ExtractCreationInfo(op) => op.children(),
×
546
            Expr::Exists(op) => op.children(),
×
547
            Expr::ExtractId(op) => op.children(),
×
548
            Expr::SigmaPropBytes(op) => op.children(),
×
549
            Expr::OptionIsDefined(op) => op.children(),
×
550
            Expr::OptionGetOrElse(op) => op.children(),
×
551
            Expr::Negation(op) => op.children(),
×
552
            Expr::BitInversion(op) => op.children(),
×
553
            Expr::ForAll(op) => op.children(),
×
554
            Expr::Tuple(op) => op.children(),
×
555
            Expr::DecodePoint(op) => op.children(),
×
556
            Expr::SigmaAnd(op) => op.children(),
4✔
557
            Expr::SigmaOr(op) => op.children(),
3✔
558
            Expr::DeserializeRegister(op) => op.children(),
×
559
            Expr::DeserializeContext(op) => op.children(),
1✔
560
            Expr::GetVar(op) => op.children(),
×
561
            Expr::MultiplyGroup(op) => op.children(),
×
562
            Expr::Exponentiate(op) => op.children(),
×
563
            Expr::XorOf(op) => op.children(),
×
564
            Expr::ExtractBytes(op) => op.children(),
×
565
            Expr::ExtractBytesWithNoRef(op) => op.children(),
×
566
            Expr::TreeLookup(op) => op.children(),
×
567
            Expr::CreateAvlTree(op) => op.children(),
×
568
        }
569
    }
570
    fn children_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut Expr> + 'a> {
5✔
571
        match self {
10✔
572
            Expr::Const(_) => Box::new(core::iter::empty()),
2✔
573
            Expr::SubstConstants(op) => op.children_mut(),
×
574
            Expr::ByteArrayToLong(op) => op.children_mut(),
×
575
            Expr::ByteArrayToBigInt(op) => op.children_mut(),
×
576
            Expr::LongToByteArray(op) => op.children_mut(),
×
577
            Expr::CalcBlake2b256(op) => op.children_mut(),
2✔
578
            Expr::CalcSha256(op) => op.children_mut(),
×
579
            Expr::Fold(op) => op.children_mut(),
2✔
580
            Expr::ExtractRegisterAs(op) => op.children_mut(),
2✔
581
            Expr::GlobalVars(op) => op.children_mut(),
2✔
582
            Expr::MethodCall(op) => op.children_mut(),
2✔
583
            Expr::PropertyCall(op) => op.children_mut(),
2✔
584
            Expr::BinOp(op) => op.children_mut(),
2✔
585
            Expr::Global => Box::new(core::iter::empty()),
×
586
            Expr::Context => Box::new(core::iter::empty()),
2✔
587
            Expr::OptionGet(v) => v.children_mut(),
2✔
588
            Expr::Apply(op) => op.children_mut(),
2✔
589
            Expr::FuncValue(op) => op.children_mut(),
2✔
590
            Expr::ValUse(op) => op.children_mut(),
2✔
591
            Expr::BlockValue(op) => op.children_mut(),
2✔
592
            Expr::SelectField(op) => op.children_mut(),
2✔
593
            Expr::ExtractAmount(op) => op.children_mut(),
2✔
594
            Expr::ConstPlaceholder(_) => Box::new(core::iter::empty()),
5✔
595
            Expr::Collection(op) => op.children_mut(),
2✔
596
            Expr::ValDef(op) => op.children_mut(),
2✔
597
            Expr::And(op) => op.children_mut(),
1✔
598
            Expr::Or(op) => op.children_mut(),
1✔
599
            Expr::Xor(op) => op.children_mut(),
×
600
            Expr::Atleast(op) => op.children_mut(),
3✔
601
            Expr::LogicalNot(op) => op.children_mut(),
2✔
602
            Expr::Map(op) => op.children_mut(),
1✔
603
            Expr::Filter(op) => op.children_mut(),
2✔
604
            Expr::BoolToSigmaProp(op) => op.children_mut(),
2✔
605
            Expr::Upcast(op) => op.children_mut(),
2✔
606
            Expr::Downcast(op) => op.children_mut(),
×
607
            Expr::If(op) => op.children_mut(),
2✔
608
            Expr::Append(op) => op.children_mut(),
×
609
            Expr::ByIndex(op) => op.children_mut(),
2✔
610
            Expr::ExtractScriptBytes(op) => op.children_mut(),
2✔
611
            Expr::SizeOf(op) => op.children_mut(),
2✔
612
            Expr::Slice(op) => op.children_mut(),
1✔
613
            Expr::CreateProveDlog(op) => op.children_mut(),
2✔
614
            Expr::CreateProveDhTuple(op) => op.children_mut(),
×
615
            Expr::ExtractCreationInfo(op) => op.children_mut(),
1✔
616
            Expr::Exists(op) => op.children_mut(),
2✔
617
            Expr::ExtractId(op) => op.children_mut(),
2✔
618
            Expr::SigmaPropBytes(op) => op.children_mut(),
×
619
            Expr::OptionIsDefined(op) => op.children_mut(),
2✔
620
            Expr::OptionGetOrElse(op) => op.children_mut(),
1✔
621
            Expr::Negation(op) => op.children_mut(),
2✔
622
            Expr::BitInversion(op) => op.children_mut(),
×
623
            Expr::ForAll(op) => op.children_mut(),
×
624
            Expr::Tuple(op) => op.children_mut(),
3✔
625
            Expr::DecodePoint(op) => op.children_mut(),
×
626
            Expr::SigmaAnd(op) => op.children_mut(),
4✔
627
            Expr::SigmaOr(op) => op.children_mut(),
4✔
628
            Expr::DeserializeRegister(op) => op.children_mut(),
1✔
629
            Expr::DeserializeContext(op) => op.children_mut(),
1✔
630
            Expr::GetVar(op) => op.children_mut(),
1✔
631
            Expr::MultiplyGroup(op) => op.children_mut(),
×
632
            Expr::Exponentiate(op) => op.children_mut(),
×
633
            Expr::XorOf(op) => op.children_mut(),
×
634
            Expr::ExtractBytes(op) => op.children_mut(),
2✔
635
            Expr::ExtractBytesWithNoRef(op) => op.children_mut(),
1✔
636
            Expr::TreeLookup(op) => op.children_mut(),
×
637
            Expr::CreateAvlTree(op) => op.children_mut(),
×
638
        }
639
    }
640
}
641

642
impl<T: Into<Literal> + LiftIntoSType> From<T> for Expr {
643
    fn from(t: T) -> Self {
20✔
644
        Expr::Const(Constant {
40✔
645
            tpe: T::stype(),
20✔
646
            v: t.into(),
20✔
647
        })
648
    }
649
}
650

651
/// Unexpected argument on node construction (i.e non-Option input in OptionGet)
652
#[derive(Error, PartialEq, Eq, Debug, Clone)]
653
#[error("InvalidArgumentError: {0}")]
654
pub struct InvalidArgumentError(pub String);
655

656
/// Invalid (unexpected) expr type
657
#[derive(PartialEq, Eq, Debug, Clone, Error)]
658
#[error("InvalidExprEvalTypeError: {0}")]
659
pub struct InvalidExprEvalTypeError(pub String);
660

661
impl From<InvalidExprEvalTypeError> for InvalidArgumentError {
662
    fn from(e: InvalidExprEvalTypeError) -> Self {
×
663
        InvalidArgumentError(format!("InvalidExprEvalTypeError: {0}", e))
×
664
    }
665
}
666

667
impl From<BoundedVecOutOfBounds> for InvalidArgumentError {
668
    fn from(e: BoundedVecOutOfBounds) -> Self {
×
669
        InvalidArgumentError(format!("BoundedVecOutOfBounds: {0}", e))
×
670
    }
671
}
672

673
#[derive(Error, Debug, Clone, PartialEq, Eq)]
674
#[allow(missing_docs)]
675
pub enum SubstDeserializeError {
676
    #[error("TryExtractFromError: {0}")]
677
    TryExtractFromError(#[from] TryExtractFromError),
678
    #[error("Could not find context extension variable {0}")]
679
    ExtensionKeyNotFound(u8),
680
    #[error("executeFromReg: Register out of bounds {0}")]
681
    InvalidRegister(#[from] RegisterIdOutOfBounds),
682
    #[error("Register {0} does not exist")]
683
    RegisterNotFound(RegisterId),
684
    #[error("RegisterValueError: {0}")]
685
    RegisterValueError(#[from] RegisterValueError),
686
    #[error("Error while parsing Expr from bytes: {0}")]
687
    ExprParsingError(#[from] SigmaParsingError),
688
    #[error("Expected tpe {expected}, found {actual}")]
689
    ExprTpeError { expected: SType, actual: SType },
690
}
691

692
impl<T: TryFrom<Expr>> TryExtractFrom<Expr> for T {
693
    fn try_extract_from(v: Expr) -> Result<Self, TryExtractFromError> {
3✔
694
        let res: Result<Self, TryExtractFromError> = v.clone().try_into().map_err(|_| {
7✔
695
            TryExtractFromError(format!(
1✔
696
                "Cannot extract {0:?} from {1:?}",
×
697
                core::any::type_name::<T>(),
2✔
698
                v
×
699
            ))
700
        });
701
        res
×
702
    }
703
}
704

705
#[cfg(feature = "arbitrary")]
706
#[allow(clippy::unwrap_used)]
707
#[allow(clippy::panic)]
708
#[allow(clippy::todo)]
709
/// Arbitrary impl
710
pub(crate) mod arbitrary {
711
    use super::*;
712
    use crate::mir::func_value::FuncArg;
713
    use crate::sigma_protocol::sigma_boolean::ProveDlog;
714
    use crate::types::sfunc::SFunc;
715
    use alloc::sync::Arc;
716
    use alloc::vec;
717
    use proptest::collection::*;
718
    use proptest::prelude::*;
719

720
    /// Parameters for arbitrary Expr generation
721
    #[derive(PartialEq, Eq, Debug, Clone)]
722
    pub struct ArbExprParams {
723
        /// Expr type
724
        pub tpe: SType,
725
        /// Expr tree depth (levels)
726
        pub depth: usize,
727
    }
728

729
    impl Default for ArbExprParams {
730
        fn default() -> Self {
2✔
731
            ArbExprParams {
732
                tpe: SType::SBoolean,
733
                depth: 1,
734
            }
735
        }
736
    }
737

738
    fn numeric_nested_expr(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
1✔
739
        prop_oneof![any_with::<BinOp>(ArbExprParams {
1✔
740
            tpe: elem_tpe.clone(),
1✔
741
            depth
742
        })
743
        .prop_map_into(),]
744
        .boxed()
745
    }
746

747
    fn bool_nested_expr(depth: usize) -> BoxedStrategy<Expr> {
2✔
748
        prop_oneof![
8✔
749
            any_with::<BinOp>(ArbExprParams {
2✔
750
                tpe: SType::SBoolean,
2✔
751
                depth
752
            })
753
            .prop_map_into(),
754
            any_with::<And>(depth).prop_map_into(),
4✔
755
            any_with::<Or>(depth).prop_map_into(),
4✔
756
            any_with::<LogicalNot>(depth).prop_map_into(),
4✔
757
        ]
758
        .boxed()
759
    }
760

761
    fn coll_nested_numeric(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
1✔
762
        let ty = elem_tpe.clone();
1✔
763
        vec(numeric_nested_expr(depth, elem_tpe), 0..10)
3✔
764
            .prop_map(move |items| Collection::new(ty.clone(), items).unwrap())
3✔
765
            .prop_map_into()
766
            .boxed()
767
    }
768

769
    fn sigma_prop_nested_expr(_depth: usize) -> BoxedStrategy<Expr> {
8✔
770
        prop_oneof![
24✔
771
            any::<ProveDlog>().prop_map(|pk| Expr::Const(pk.into())),
19✔
772
            any::<SigmaAnd>().prop_map_into(),
18✔
773
            any::<SigmaOr>().prop_map_into(),
15✔
774
        ]
775
        .boxed()
776
    }
777

778
    fn coll_nested_expr(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
1✔
779
        match elem_tpe {
1✔
780
            SType::SBoolean => vec(bool_nested_expr(depth), 0..10)
1✔
781
                .prop_map(|items| Collection::new(SType::SBoolean, items).unwrap())
2✔
782
                .prop_map_into()
783
                .boxed(),
784
            SType::SByte => coll_nested_numeric(depth, elem_tpe),
1✔
785
            SType::SShort => coll_nested_numeric(depth, elem_tpe),
×
786
            SType::SInt => coll_nested_numeric(depth, elem_tpe),
×
787
            SType::SLong => coll_nested_numeric(depth, elem_tpe),
×
788
            SType::SBigInt => coll_nested_numeric(depth, elem_tpe),
×
789

790
            SType::STypeVar(_) => prop_oneof![
2✔
791
                vec(bool_nested_expr(depth), 0..10).prop_map(|items| Collection::new(
3✔
792
                    SType::SBoolean,
1✔
793
                    items
794
                )
795
                .unwrap()),
1✔
796
                vec(numeric_nested_expr(depth, &SType::SInt), 0..10)
2✔
797
                    .prop_map(|items| Collection::new(SType::SInt, items).unwrap())
2✔
798
            ]
799
            .prop_map_into()
800
            .boxed(),
801
            SType::SSigmaProp => vec(sigma_prop_nested_expr(depth), 0..10)
1✔
802
                .prop_map(|items| Collection::new(SType::SSigmaProp, items).unwrap())
2✔
803
                .prop_map_into()
804
                .boxed(),
805

806
            _ => panic!("Nested expression not implemented for {:?}", &elem_tpe),
×
807
        }
808
    }
809

810
    fn any_nested_expr(depth: usize) -> BoxedStrategy<Expr> {
1✔
811
        prop_oneof![
1✔
812
            bool_nested_expr(depth),
1✔
813
            numeric_nested_expr(depth, &SType::SByte),
1✔
814
            numeric_nested_expr(depth, &SType::SShort),
1✔
815
            numeric_nested_expr(depth, &SType::SInt),
1✔
816
            numeric_nested_expr(depth, &SType::SLong),
1✔
817
            numeric_nested_expr(depth, &SType::SBigInt),
1✔
818
        ]
819
        .boxed()
820
    }
821

822
    fn nested_expr(tpe: SType, depth: usize) -> BoxedStrategy<Expr> {
8✔
823
        match tpe {
17✔
824
            SType::SAny => any_nested_expr(depth),
2✔
825
            SType::SBoolean => bool_nested_expr(depth),
4✔
826
            SType::SByte => numeric_nested_expr(depth, &tpe),
×
827
            SType::SShort => numeric_nested_expr(depth, &tpe),
×
828
            SType::SInt => numeric_nested_expr(depth, &tpe),
2✔
829
            SType::SLong => numeric_nested_expr(depth, &tpe),
2✔
830
            SType::SBigInt => numeric_nested_expr(depth, &tpe),
×
831
            SType::SColl(elem_type) => coll_nested_expr(depth, elem_type.as_ref()),
1✔
832
            SType::SSigmaProp => sigma_prop_nested_expr(depth),
14✔
833
            _ => todo!("nested expr is not implemented for type: {:?}", tpe),
834
        }
835
        .boxed()
836
    }
837

838
    fn int_non_nested_expr() -> BoxedStrategy<Expr> {
2✔
839
        prop_oneof![Just(GlobalVars::Height.into()),].boxed()
2✔
840
    }
841

842
    fn constant(tpe: &SType) -> BoxedStrategy<Expr> {
1✔
843
        any_with::<Constant>(tpe.clone().into())
1✔
844
            .prop_map_into()
845
            .boxed()
846
    }
847

848
    fn bool_non_nested_expr() -> BoxedStrategy<Expr> {
2✔
849
        prop_oneof![any_with::<Constant>(SType::SBoolean.into()).prop_map_into()].boxed()
2✔
850
    }
851

852
    fn any_non_nested_expr() -> BoxedStrategy<Expr> {
2✔
853
        prop_oneof![int_non_nested_expr(), bool_non_nested_expr()].boxed()
2✔
854
    }
855

856
    fn coll_non_nested_expr(elem_tpe: &SType) -> BoxedStrategy<Expr> {
2✔
857
        match elem_tpe {
2✔
858
            SType::SBoolean => any_with::<Constant>(SType::SColl(Arc::new(SType::SBoolean)).into())
2✔
859
                .prop_map(Expr::Const)
860
                .boxed(),
861
            SType::SByte => any_with::<Constant>(SType::SColl(Arc::new(SType::SByte)).into())
1✔
862
                .prop_map(Expr::Const)
863
                .boxed(),
864
            SType::SShort => any_with::<Constant>(SType::SColl(Arc::new(SType::SShort)).into())
1✔
865
                .prop_map(Expr::Const)
866
                .boxed(),
867
            SType::SInt => any_with::<Constant>(SType::SColl(Arc::new(SType::SInt)).into())
1✔
868
                .prop_map(Expr::Const)
869
                .boxed(),
UNCOV
870
            SType::SLong => any_with::<Constant>(SType::SColl(Arc::new(SType::SLong)).into())
×
871
                .prop_map(Expr::Const)
872
                .boxed(),
873
            _ => todo!("Collection of {0:?} is not yet implemented", elem_tpe),
874
        }
875
    }
876

877
    fn non_nested_expr(tpe: &SType) -> BoxedStrategy<Expr> {
2✔
878
        match tpe {
2✔
879
            SType::SAny => any_non_nested_expr(),
2✔
880
            SType::SInt => int_non_nested_expr(),
1✔
881
            SType::SBoolean => bool_non_nested_expr(),
2✔
882
            SType::SColl(elem_type) => coll_non_nested_expr(elem_type),
2✔
883
            t => constant(t),
1✔
884
        }
885
    }
886

887
    fn sfunc_expr(sfunc: SFunc) -> BoxedStrategy<Expr> {
1✔
888
        match (sfunc.t_dom.first().unwrap(), *sfunc.t_range) {
2✔
889
            (SType::SBoolean, SType::SBoolean) => any_with::<Expr>(ArbExprParams {
2✔
890
                tpe: SType::SBoolean,
1✔
891
                depth: 2,
892
            })
893
            .prop_map(|expr| {
1✔
894
                Expr::FuncValue(FuncValue::new(
2✔
895
                    vec![FuncArg {
3✔
896
                        idx: 1.into(),
1✔
897
                        tpe: SType::SBoolean,
1✔
898
                    }],
899
                    expr,
1✔
900
                ))
901
            })
902
            .boxed(),
903
            _ => todo!(),
904
        }
905
    }
906

907
    impl Arbitrary for Expr {
908
        type Parameters = ArbExprParams;
909
        type Strategy = BoxedStrategy<Self>;
910

911
        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
8✔
912
            if args.depth == 0 {
14✔
913
                match args.tpe {
2✔
914
                    SType::SFunc(sfunc) => sfunc_expr(sfunc),
2✔
915
                    _ => prop_oneof![
2✔
916
                        any_with::<Constant>(args.tpe.clone().into())
4✔
917
                            .prop_map(Expr::Const)
918
                            .boxed(),
919
                        non_nested_expr(&args.tpe)
2✔
920
                    ]
921
                    .boxed(),
922
                }
923
            } else {
924
                nested_expr(args.tpe, args.depth - 1)
15✔
925
            }
926
        }
927
    }
928
}
929

930
#[cfg(test)]
931
mod tests {}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc