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

ergoplatform / sigma-rust / 8953175335

04 May 2024 08:51PM UTC coverage: 80.473% (+0.1%) from 80.331%
8953175335

Pull #736

github

web-flow
Merge 0fdf2d258 into 57a105462
Pull Request #736: Transaction Validation

165 of 228 new or added lines in 15 files covered. (72.37%)

8 existing lines in 2 files now uncovered.

10723 of 13325 relevant lines covered (80.47%)

3.29 hits per line

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

21.88
/ergotree-interpreter/src/eval/error.rs
1
use miette::miette;
2
use miette::LabeledSpan;
3
use std::fmt::Debug;
4
use std::fmt::Display;
5

6
use bounded_vec::BoundedVecOutOfBounds;
7
use derive_more::TryInto;
8
use ergotree_ir::ergo_tree::ErgoTreeError;
9
use ergotree_ir::mir::constant::TryExtractFromError;
10
use ergotree_ir::serialization::SigmaParsingError;
11
use ergotree_ir::serialization::SigmaSerializationError;
12
use ergotree_ir::source_span::SourceSpan;
13
use sigma_ser::ScorexParsingError;
14
use sigma_ser::ScorexSerializationError;
15
use thiserror::Error;
16

17
use super::cost_accum::CostError;
18
use super::env::Env;
19

20
/// Interpreter errors
21
#[derive(Error, PartialEq, Eq, Debug, Clone, TryInto)]
22
pub enum EvalError {
23
    /// AVL tree errors
24
    #[error("AvlTree: {0}")]
25
    AvlTree(String),
26
    /// Only boolean or SigmaBoolean is a valid result expr type
27
    #[error("Only boolean or SigmaBoolean is a valid result expr type")]
28
    InvalidResultType,
29
    /// Unexpected Expr encountered during the evaluation
30
    #[error("Unexpected Expr: {0}")]
31
    UnexpectedExpr(String),
32
    /// Error on cost calculation
33
    #[error("Error on cost calculation: {0:?}")]
34
    CostError(#[from] CostError),
35
    /// Unexpected value type
36
    #[error("Unexpected value type: {0:?}")]
37
    TryExtractFrom(#[from] TryExtractFromError),
38
    /// Not found (missing value, argument, etc.)
39
    #[error("Not found: {0}")]
40
    NotFound(String),
41
    /// Register id out of bounds
42
    #[error("{0}")]
43
    RegisterIdOutOfBounds(String),
44
    /// Unexpected value
45
    #[error("Unexpected value: {0}")]
46
    UnexpectedValue(String),
47
    /// Arithmetic exception error
48
    #[error("Arithmetic exception: {0}")]
49
    ArithmeticException(String),
50
    /// Misc error
51
    #[error("error: {0}")]
52
    Misc(String),
53
    /// Sigma serialization error
54
    #[error("Serialization error: {0}")]
55
    SigmaSerializationError(#[from] SigmaSerializationError),
56
    /// Sigma serialization parsing error
57
    #[error("Serialization parsing error: {0}")]
58
    SigmaParsingError(#[from] SigmaParsingError),
59
    /// ErgoTree error
60
    #[error("ErgoTree error: {0}")]
61
    ErgoTreeError(#[from] ErgoTreeError),
62
    /// Invalid item quantity for BoundedVec
63
    #[error("Invalid item quantity for BoundedVec: {0}")]
64
    BoundedVecError(#[from] BoundedVecOutOfBounds),
65
    /// Scorex serialization error
66
    #[error("Serialization error: {0}")]
67
    ScorexSerializationError(#[from] ScorexSerializationError),
68
    /// Scorex serialization parsing error
69
    #[error("Serialization parsing error: {0}")]
70
    ScorexParsingError(#[from] ScorexParsingError),
71
    /// Wrapped error with source span and source code
72
    #[error("eval error: {0}")]
73
    SpannedWithSource(SpannedWithSourceEvalError),
74
    /// Wrapped error with source span
75
    #[error("eval error: {0:?}")]
76
    Spanned(SpannedEvalError),
77
}
78

79
/// Wrapped error with source span
80
#[derive(PartialEq, Eq, Debug, Clone)]
81
pub struct SpannedEvalError {
82
    /// eval error
83
    error: Box<EvalError>,
84
    /// source span for the expression where error occurred
85
    source_span: SourceSpan,
86
    /// environment at the time when error occurred
87
    env: Env,
88
}
89

90
/// Wrapped error with source span and source code
91
#[derive(PartialEq, Eq, Clone)]
92
pub struct SpannedWithSourceEvalError {
93
    /// eval error
94
    error: Box<EvalError>,
95
    /// source span for the expression where error occurred
96
    source_span: SourceSpan,
97
    /// environment at the time when error occurred
98
    env: Env,
99
    /// source code
100
    source: String,
101
}
102

103
impl Display for SpannedWithSourceEvalError {
104
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
NEW
105
        let _ = miette::set_hook(Box::new(|_| {
×
106
            Box::new(
107
                miette::MietteHandlerOpts::new()
×
108
                    .terminal_links(false)
109
                    .unicode(false)
110
                    .color(false)
111
                    .context_lines(5)
112
                    .tab_width(2)
113
                    .build(),
114
            )
115
        }));
UNCOV
116
        let err_msg = self.error.to_string();
×
117
        let report = miette!(
×
118
            labels = vec![LabeledSpan::at(self.source_span, err_msg,)],
×
119
            // help = "Help msg",
120
            "Evaluation error"
121
        )
122
        .with_source_code(self.source.clone());
×
123
        write!(f, "{:?}", report)
×
124
    }
125
}
126

127
impl Debug for SpannedWithSourceEvalError {
128
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
NEW
129
        let _ = miette::set_hook(Box::new(|_| {
×
130
            Box::new(
131
                miette::MietteHandlerOpts::new()
×
132
                    .terminal_links(false)
133
                    .unicode(false)
134
                    .color(false)
135
                    .context_lines(5)
136
                    .tab_width(2)
137
                    .build(),
138
            )
139
        }));
UNCOV
140
        let err_msg = self.error.to_string();
×
141
        let report = miette!(
×
142
            labels = vec![LabeledSpan::at(self.source_span, err_msg,)],
×
143
            // help = "Help msg",
144
            "Evaluation error"
145
        )
146
        .with_source_code(self.source.clone());
×
147
        write!(f, "{:?}", report)?;
×
148
        write!(f, "Env:\n{}", self.env)
×
149
    }
150
}
151

152
impl EvalError {
153
    /// Wrap eval error with source span
154
    pub fn wrap(self, source_span: SourceSpan, env: Env) -> Self {
1✔
155
        EvalError::Spanned(SpannedEvalError {
1✔
156
            error: Box::new(self),
157
            source_span,
158
            env,
1✔
159
        })
160
    }
161

162
    /// Wrap eval error with source code
163
    pub fn wrap_spanned_with_src(self, source: String) -> Self {
×
164
        #[allow(clippy::panic)]
165
        match self {
×
166
            EvalError::Spanned(e) => EvalError::SpannedWithSource(SpannedWithSourceEvalError {
×
167
                error: e.error,
×
168
                source_span: e.source_span,
×
169
                env: e.env,
×
170
                source,
×
171
            }),
172
            e => panic!("Expected Spanned, got {:?}", e),
×
173
        }
174
    }
175
}
176

177
pub trait ExtResultEvalError<T> {
178
    fn enrich_err(self, span: SourceSpan, env: &Env) -> Result<T, EvalError>;
179
}
180

181
impl<T> ExtResultEvalError<T> for Result<T, EvalError> {
182
    fn enrich_err(self, span: SourceSpan, env: &Env) -> Result<T, EvalError> {
4✔
183
        self.map_err(|e| match e {
7✔
184
            // skip already wrapped errors
185
            w @ EvalError::Spanned { .. } => w,
1✔
186
            e => e.wrap(span, env.clone()),
1✔
187
        })
188
    }
189
}
190

191
#[allow(clippy::unwrap_used, unused_imports, dead_code)]
192
#[cfg(test)]
193
mod tests {
194
    use std::rc::Rc;
195

196
    use ergotree_ir::mir::coll_by_index::ByIndex;
197
    use ergotree_ir::mir::global_vars::GlobalVars;
198
    use ergotree_ir::source_span::SourceSpan;
199
    use expect_test::expect;
200

201
    use ergotree_ir::mir::bin_op::ArithOp;
202
    use ergotree_ir::mir::bin_op::BinOp;
203
    use ergotree_ir::mir::block::BlockValue;
204
    use ergotree_ir::mir::expr::Expr;
205
    use ergotree_ir::mir::val_def::ValDef;
206
    use ergotree_ir::mir::val_use::ValUse;
207
    use ergotree_ir::pretty_printer::PosTrackingWriter;
208
    use ergotree_ir::pretty_printer::Print;
209
    use ergotree_ir::types::stype::SType;
210
    use sigma_test_util::force_any_val;
211

212
    use crate::eval::context::Context;
213
    use crate::eval::error::SpannedEvalError;
214
    use crate::eval::error::SpannedWithSourceEvalError;
215
    use crate::eval::tests::try_eval_out;
216

217
    fn check(expr: Expr, expected_tree: expect_test::Expect) {
218
        let mut w = PosTrackingWriter::new();
219
        let spanned_expr = expr.print(&mut w).unwrap();
220
        dbg!(&spanned_expr);
221
        let ctx = Rc::new(force_any_val::<Context>());
222
        let err_raw: SpannedEvalError = try_eval_out::<i32>(&spanned_expr, ctx)
223
            .err()
224
            .unwrap()
225
            .try_into()
226
            .unwrap();
227
        let err = SpannedWithSourceEvalError {
228
            error: err_raw.error,
229
            source_span: err_raw.source_span,
230
            env: err_raw.env,
231
            source: w.get_buf().to_string(),
232
        };
233
        expected_tree.assert_eq(&err.to_string());
234
    }
235

236
    fn check_error_span(expr: Expr, expected_span: SourceSpan) {
237
        let mut w = PosTrackingWriter::new();
238
        let spanned_expr = expr.print(&mut w).unwrap();
239
        dbg!(&spanned_expr);
240
        let ctx = Rc::new(force_any_val::<Context>());
241
        let err_raw: SpannedEvalError = try_eval_out::<i32>(&spanned_expr, ctx)
242
            .err()
243
            .unwrap()
244
            .try_into()
245
            .unwrap();
246
        assert_eq!(err_raw.source_span, expected_span);
247
    }
248

249
    #[test]
250
    fn pretty_binop_div_zero() {
251
        let lhs_val_id = 1.into();
252
        let rhs_val_id = 2.into();
253
        let res_val_id = 3.into();
254
        let expr = Expr::BlockValue(
255
            BlockValue {
256
                items: vec![
257
                    ValDef {
258
                        id: lhs_val_id,
259
                        rhs: Box::new(Expr::Const(42i32.into())),
260
                    }
261
                    .into(),
262
                    ValDef {
263
                        id: rhs_val_id,
264
                        rhs: Box::new(Expr::Const(0i32.into())),
265
                    }
266
                    .into(),
267
                    ValDef {
268
                        id: res_val_id,
269
                        rhs: Box::new(
270
                            BinOp {
271
                                kind: ArithOp::Divide.into(),
272
                                left: Box::new(
273
                                    ValUse {
274
                                        val_id: lhs_val_id,
275
                                        tpe: SType::SInt,
276
                                    }
277
                                    .into(),
278
                                ),
279
                                right: Box::new(
280
                                    ValUse {
281
                                        val_id: rhs_val_id,
282
                                        tpe: SType::SInt,
283
                                    }
284
                                    .into(),
285
                                ),
286
                            }
287
                            .into(),
288
                        ),
289
                    }
290
                    .into(),
291
                ],
292
                result: Box::new(
293
                    ValUse {
294
                        val_id: res_val_id,
295
                        tpe: SType::SInt,
296
                    }
297
                    .into(),
298
                ),
299
            }
300
            .into(),
301
        );
302
        // check(
303
        //     expr,
304
        //     expect![[r#"
305
        //           x Evaluation error
306
        //            ,-[1:1]
307
        //          1 | {
308
        //          2 |   val v1 = 42
309
        //          3 |   val v2 = 0
310
        //          4 |   val v3 = v1 / v2
311
        //            :            ^^^|^^^
312
        //            :               `-- Arithmetic exception: (42) / (0) resulted in exception
313
        //          5 |   v3
314
        //          6 | }
315
        //            `----
316
        //     "#]],
317
        // );
318
        check_error_span(expr, (40, 7).into());
319
    }
320

321
    #[test]
322
    fn pretty_out_of_bounds() {
323
        let v1_id = 1.into();
324
        let expr = Expr::BlockValue(
325
            BlockValue {
326
                items: vec![ValDef {
327
                    id: v1_id,
328
                    rhs: Box::new(Expr::Const(99999999i32.into())),
329
                }
330
                .into()],
331
                result: Box::new(Expr::ByIndex(
332
                    ByIndex::new(
333
                        Expr::GlobalVars(GlobalVars::Outputs),
334
                        ValUse {
335
                            val_id: v1_id,
336
                            tpe: SType::SInt,
337
                        }
338
                        .into(),
339
                        None,
340
                    )
341
                    .unwrap()
342
                    .into(),
343
                )),
344
            }
345
            .into(),
346
        );
347
        // check(
348
        //     expr,
349
        //     expect![[r#"
350
        //           x Evaluation error
351
        //            ,-[1:1]
352
        //          1 | {
353
        //          2 |   val v1 = 99999999
354
        //          3 |   OUTPUTS(v1)
355
        //            :          ^^|^
356
        //            :            `-- error: ByIndex: index Int(99999999) out of bounds for collection size 1
357
        //          4 | }
358
        //            `----
359
        //     "#]],
360
        // );
361
        check_error_span(expr, (31, 4).into());
362
    }
363
}
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