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

ergoplatform / sigma-rust / 12688397824

09 Jan 2025 10:48AM UTC coverage: 78.524% (+0.07%) from 78.451%
12688397824

Pull #803

github

web-flow
Merge 7cdf7e630 into 444347ba8
Pull Request #803: Adding context.getvarfrominput and context.getvar

42 of 57 new or added lines in 3 files covered. (73.68%)

8 existing lines in 1 file now uncovered.

11148 of 14197 relevant lines covered (78.52%)

3.03 hits per line

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

68.49
/ergotree-interpreter/src/eval/scontext.rs
1
use alloc::boxed::Box;
2
use alloc::string::ToString;
3
use alloc::sync::Arc;
4

5
use ergotree_ir::mir::avl_tree_data::AvlTreeData;
6
use ergotree_ir::mir::avl_tree_data::AvlTreeFlags;
7
use ergotree_ir::mir::value::CollKind;
8
use ergotree_ir::mir::value::Value;
9
use ergotree_ir::reference::Ref;
10
use ergotree_ir::serialization::SigmaSerializable;
11
use ergotree_ir::types::stype::SType;
12

13
use super::EvalError;
14
use super::EvalFn;
15

16
pub(crate) static DATA_INPUTS_EVAL_FN: EvalFn = |_mc, _env, ctx, obj, _args| {
1✔
17
    if obj != Value::Context {
2✔
18
        return Err(EvalError::UnexpectedValue(format!(
×
19
            "Context.dataInputs: expected object of Value::Context, got {:?}",
20
            obj
21
        )));
22
    }
23
    Ok(Value::Coll(CollKind::WrappedColl {
1✔
24
        items: ctx.data_inputs.clone().map_or(Arc::new([]), |d| {
4✔
25
            d.iter().map(|&di| Ref::from(di)).map(Value::CBox).collect()
4✔
26
        }),
27
        elem_tpe: SType::SBox,
1✔
28
    }))
29
};
30

31
pub(crate) static SELF_BOX_INDEX_EVAL_FN: EvalFn = |_mc, _env, ctx, obj, _args| {
1✔
32
    if obj != Value::Context {
2✔
33
        return Err(EvalError::UnexpectedValue(format!(
×
34
            "Context.selfBoxIndex: expected object of Value::Context, got {:?}",
35
            obj
36
        )));
37
    }
38
    let box_index = ctx
2✔
39
        .inputs
40
        .iter()
41
        .position(|it| *it == ctx.self_box)
2✔
42
        .ok_or_else(|| EvalError::NotFound("Context.selfBoxIndex: box not found".to_string()))?;
×
43
    Ok(Value::Int(box_index as i32))
1✔
44
};
45

46
pub(crate) static HEADERS_EVAL_FN: EvalFn = |_mc, _env, ctx, obj, _args| {
1✔
47
    if obj != Value::Context {
2✔
48
        return Err(EvalError::UnexpectedValue(format!(
×
49
            "Context.headers: expected object of Value::Context, got {:?}",
50
            obj
51
        )));
52
    }
53
    Ok(Value::Coll(CollKind::WrappedColl {
1✔
54
        items: Arc::new(ctx.headers.clone().map(Box::new).map(Value::Header)),
2✔
55
        elem_tpe: SType::SHeader,
1✔
56
    }))
57
};
58

59
pub(crate) static PRE_HEADER_EVAL_FN: EvalFn = |_mc, _env, ctx, obj, _args| {
1✔
60
    if obj != Value::Context {
2✔
61
        return Err(EvalError::UnexpectedValue(format!(
×
62
            "Context.preHeader: expected object of Value::Context, got {:?}",
63
            obj
64
        )));
65
    }
66
    Ok(Box::from(ctx.pre_header.clone()).into())
2✔
67
};
68

69
pub(crate) static LAST_BLOCK_UTXO_ROOT_HASH_EVAL_FN: EvalFn = |_mc, _env, ctx, obj, _args| {
1✔
70
    if obj != Value::Context {
2✔
71
        return Err(EvalError::UnexpectedValue(format!(
×
72
            "Context.LastBlockUtxoRootHash: expected object of Value::Context, got {:?}",
73
            obj
74
        )));
75
    }
76
    let digest = ctx.headers[0].state_root;
2✔
77
    let tree_flags = AvlTreeFlags::new(true, true, true);
1✔
78
    Ok(Value::AvlTree(Box::from(AvlTreeData {
1✔
79
        digest,
80
        tree_flags,
81
        key_length: 32,
82
        value_length_opt: None,
1✔
83
    })))
84
};
85

86
pub(crate) static MINER_PUBKEY_EVAL_FN: EvalFn = |_mc, _env, ctx, obj, _args| {
1✔
87
    if obj != Value::Context {
2✔
88
        return Err(EvalError::UnexpectedValue(format!(
×
89
            "Context.preHeader: expected object of Value::Context, got {:?}",
90
            obj
91
        )));
92
    }
93
    Ok(ctx
3✔
94
        .pre_header
95
        .miner_pk
96
        .clone()
97
        .sigma_serialize_bytes()?
×
98
        .into())
99
};
100

101
pub(crate) static GET_VAR_EVAL_FN: EvalFn = |mc, _env, ctx, obj, args| {
1✔
102
    if obj != Value::Context {
2✔
NEW
103
        return Err(EvalError::UnexpectedValue(format!(
×
104
            "Context.getVar: expected object of Value::Context, got {:?}",
105
            obj
106
        )));
107
    }
108
    let var_id = match args.first() {
2✔
109
        Some(Value::Byte(id)) => *id as u8,
1✔
110
        _ => {
NEW
111
            return Err(EvalError::UnexpectedValue(
×
NEW
112
                "getVar: invalid variable id".to_string(),
×
113
            ))
114
        }
115
    };
116

117
    let expected_type = match mc.tpe().t_range.as_ref() {
1✔
118
        SType::SOption(inner_type) => inner_type.as_ref(),
2✔
119
        _ => {
NEW
120
            return Err(EvalError::UnexpectedValue(
×
NEW
121
                "Expected SOption type".to_string(),
×
122
            ))
123
        }
124
    };
125

126
    match ctx.extension.values.get(&var_id) {
1✔
NEW
127
        None => Ok(Value::Opt(Box::new(None))),
×
128
        Some(v) if v.tpe == *expected_type => Ok(Value::Opt(Box::new(Some(v.v.clone().into())))),
5✔
129
        Some(_) => Err(EvalError::UnexpectedValue(
1✔
130
            "getVar: type mismatch".to_string(),
1✔
131
        )),
132
    }
133
};
134

135
pub(crate) static GET_VAR_FROM_INPUTS_EVAL_FN: EvalFn = |mc, _env, ctx, obj, args| {
1✔
136
    if obj != Value::Context {
2✔
NEW
137
        return Err(EvalError::UnexpectedValue(format!(
×
138
            "Context.getVarFromInputs: expected object of Value::Context, got {:?}",
139
            obj
140
        )));
141
    }
142

143
    let input_index = match args.first() {
2✔
144
        Some(Value::Short(idx)) if *idx >= 0 => *idx as usize,
1✔
145
        _ => {
NEW
146
            return Err(EvalError::UnexpectedValue(
×
NEW
147
                "getVarFromInput: invalid input index".to_string(),
×
148
            ))
149
        }
150
    };
151
    let var_id = match args.get(1) {
1✔
152
        Some(Value::Byte(id)) => *id as u8,
1✔
153
        _ => {
NEW
154
            return Err(EvalError::UnexpectedValue(
×
NEW
155
                "getVarFromInput: invalid variable id".to_string(),
×
156
            ))
157
        }
158
    };
159

160
    let expected_type = match mc.tpe().t_range.as_ref() {
1✔
161
        SType::SOption(inner_type) => inner_type.as_ref(),
2✔
162
        _ => {
NEW
163
            return Err(EvalError::UnexpectedValue(
×
NEW
164
                "Expected SOption type".to_string(),
×
165
            ))
166
        }
167
    };
168

169
    if ctx.inputs.get(input_index).is_none() {
1✔
NEW
170
        return Err(EvalError::NotFound(format!(
×
171
            "getVarFromInput: input not found at index {}",
172
            input_index
173
        )));
174
    }
175

176
    match ctx.extension.values.get(&var_id) {
2✔
NEW
177
        None => Ok(Value::Opt(Box::new(None))),
×
178
        Some(v) if v.tpe == *expected_type => Ok(Value::Opt(Box::new(Some(v.v.clone().into())))),
5✔
179
        Some(_v) => Ok(Value::Opt(Box::new(None))),
2✔
180
    }
181
};
182

183
#[cfg(test)]
184
#[cfg(feature = "arbitrary")]
185
#[allow(clippy::unwrap_used, clippy::expect_used)]
186
mod tests {
187
    use crate::eval::tests::eval_out;
188
    use ergo_chain_types::{Header, PreHeader};
189
    use ergotree_ir::chain::context::Context;
190
    use ergotree_ir::chain::ergo_box::ErgoBox;
191
    use ergotree_ir::mir::avl_tree_data::{AvlTreeData, AvlTreeFlags};
192
    use ergotree_ir::mir::constant::Constant;
193
    use ergotree_ir::mir::expr::Expr;
194
    use ergotree_ir::mir::method_call::MethodCall;
195
    use ergotree_ir::mir::property_call::PropertyCall;
196
    use ergotree_ir::mir::value::Value;
197
    use ergotree_ir::serialization::SigmaSerializable;
198
    use ergotree_ir::types::scontext;
199
    use ergotree_ir::types::stype::SType;
200
    use ergotree_ir::types::stype_param::STypeVar;
201
    use proptest::arbitrary::any;
202
    use proptest::prelude::ProptestConfig;
203
    use proptest::proptest;
204
    use sigma_test_util::force_any_val;
205

206
    fn make_ctx_inputs_includes_self_box() -> Context<'static> {
207
        let ctx = force_any_val::<Context>();
208
        let self_box = &*Box::leak(Box::new(force_any_val::<ErgoBox>()));
209
        let inputs = vec![&*Box::leak(Box::new(force_any_val::<ErgoBox>())), self_box]
210
            .try_into()
211
            .unwrap();
212
        Context {
213
            height: 0u32,
214
            self_box,
215
            inputs,
216
            ..ctx
217
        }
218
    }
219

220
    fn prepare_getvar_from_input_context<T>(
221
        input_idx: i16,
222
        var_idx: u8,
223
        var_val: T,
224
    ) -> Context<'static>
225
    where
226
        T: Into<Constant> + Clone,
227
    {
228
        let mut ctx = force_any_val::<Context>();
229
        ctx.extension.values.clear();
230
        ctx.extension.values.insert(var_idx, var_val.into());
231
        let input_box = &*Box::leak(Box::new(force_any_val::<ErgoBox>()));
232
        let other_box = &*Box::leak(Box::new(force_any_val::<ErgoBox>()));
233
        let mut inputs = Vec::new();
234
        if input_idx > 0 {
235
            for _ in 0..input_idx {
236
                inputs.push(other_box);
237
            }
238
        }
239
        inputs.push(input_box);
240
        ctx.inputs = inputs.try_into().unwrap();
241
        ctx
242
    }
243

244
    pub fn create_method_call(stype: SType, input_idx: i16, var_idx: u8) -> Expr {
245
        let type_args = std::iter::once((STypeVar::t(), stype)).collect();
246
        MethodCall::with_type_args(
247
            Expr::Context,
248
            scontext::GET_VAR_FROM_INPUT_METHOD
249
                .clone()
250
                .with_concrete_types(&type_args),
251
            vec![input_idx.into(), (var_idx as i8).into()],
252
            type_args,
253
        )
254
        .unwrap()
255
        .into()
256
    }
257

258
    fn prepare_getvar_context<T>(var_idx: u8, var_val: T) -> Context<'static>
259
    where
260
        T: Into<Constant> + Clone,
261
    {
262
        let mut ctx = force_any_val::<Context>();
263
        ctx.extension.values.clear();
264
        ctx.extension.values.insert(var_idx, var_val.into());
265
        ctx
266
    }
267

268
    pub fn create_getvar_method_call(stype: SType, var_idx: u8) -> Expr {
269
        let type_args = std::iter::once((STypeVar::t(), stype)).collect();
270
        MethodCall::with_type_args(
271
            Expr::Context,
272
            scontext::GET_VAR_METHOD
273
                .clone()
274
                .with_concrete_types(&type_args),
275
            vec![(var_idx as i8).into()],
276
            type_args,
277
        )
278
        .unwrap()
279
        .into()
280
    }
281

282
    #[test]
283
    fn eval_self_box_index() {
284
        let expr: Expr =
285
            PropertyCall::new(Expr::Context, scontext::SELF_BOX_INDEX_PROPERTY.clone())
286
                .unwrap()
287
                .into();
288
        let context = make_ctx_inputs_includes_self_box();
289
        assert_eq!(eval_out::<i32>(&expr, &context), 1);
290
    }
291

292
    #[test]
293
    fn eval_headers() {
294
        let expr: Expr = PropertyCall::new(Expr::Context, scontext::HEADERS_PROPERTY.clone())
295
            .expect("internal error: `headers` method has parameters length != 1")
296
            .into();
297
        let ctx = force_any_val::<Context>();
298
        assert_eq!(eval_out::<[Header; 10]>(&expr, &ctx), ctx.headers);
299
    }
300

301
    #[test]
302
    fn eval_preheader() {
303
        let expr: Expr = PropertyCall::new(Expr::Context, scontext::PRE_HEADER_PROPERTY.clone())
304
            .unwrap()
305
            .into();
306
        let ctx = force_any_val::<Context>();
307
        assert_eq!(eval_out::<PreHeader>(&expr, &ctx), ctx.pre_header);
308
    }
309

310
    #[test]
311
    fn eval_miner_pubkey() {
312
        let expr: Expr = PropertyCall::new(Expr::Context, scontext::MINER_PUBKEY_PROPERTY.clone())
313
            .unwrap()
314
            .into();
315
        let ctx = force_any_val::<Context>();
316
        assert_eq!(
317
            eval_out::<Vec<u8>>(&expr, &ctx),
318
            ctx.pre_header.miner_pk.sigma_serialize_bytes().unwrap()
319
        );
320
    }
321

322
    #[test]
323
    fn eval_last_block_utxo_root_hash() {
324
        let expr: Expr = PropertyCall::new(
325
            Expr::Context,
326
            scontext::LAST_BLOCK_UTXO_ROOT_HASH_PROPERTY.clone(),
327
        )
328
        .unwrap()
329
        .into();
330
        let ctx = force_any_val::<Context>();
331
        let digest = ctx.headers[0].state_root;
332
        let tree_flags = AvlTreeFlags::new(true, true, true);
333
        let avl_tree_data = AvlTreeData {
334
            digest,
335
            tree_flags,
336
            key_length: 32,
337
            value_length_opt: None,
338
        };
339
        assert_eq!(eval_out::<AvlTreeData>(&expr, &ctx), avl_tree_data);
340
    }
341

342
    #[test]
343
    fn eval_getvar_from_input_type_mismatch() {
344
        let ctx = prepare_getvar_from_input_context(0i16, 1u8, 123i32);
345
        let result = eval_out::<Option<Value>>(&create_method_call(SType::SByte, 0, 1), &ctx);
346
        assert_eq!(result, None);
347
    }
348

349
    #[test]
350
    #[should_panic(expected = "getVar: type mismatch")]
351
    fn eval_getvar_type_mismatch() {
352
        let ctx = prepare_getvar_context(1u8, 123i32);
353
        eval_out::<Option<Value>>(&create_getvar_method_call(SType::SByte, 1), &ctx);
354
    }
355

356
    proptest! {
357
    #![proptest_config(ProptestConfig::with_cases(64))]
358

359
         #[test]
360
    fn eval_get_var_from_input_int(
361
       input_idx in 0..10i16,
362
       var_idx in any::<u8>(),
363
       var_val in any::<i32>()
364
    ) {
365
       let result = eval_out::<Option<Value>>(
366
           &create_method_call(SType::SInt, input_idx, var_idx),
367
           &prepare_getvar_from_input_context(input_idx, var_idx, var_val)
368
       );
369
      assert_eq!(result, Some(Value::Int(var_val)));
370
    }
371

372
    #[test]
373
    fn eval_get_var_from_input_long(
374
       input_idx in 0..10i16,
375
       var_idx in any::<u8>(),
376
       var_val in any::<i64>()
377
    ) {
378
       let result = eval_out::<Option<Value>>(
379
           &create_method_call(SType::SLong, input_idx, var_idx),
380
           &prepare_getvar_from_input_context(input_idx, var_idx, var_val)
381
       );
382
       assert_eq!(result, Some(Value::Long(var_val)));
383
    }
384

385
    #[test]
386
    fn eval_get_var_from_input_byte(
387
       input_idx in 0..10i16,
388
       var_idx in any::<u8>(),
389
       var_val in any::<i8>()
390
    ) {
391
       let result = eval_out::<Option<Value>>(
392
           &create_method_call(SType::SByte, input_idx, var_idx),
393
           &prepare_getvar_from_input_context(input_idx, var_idx, var_val)
394
       );
395
       assert_eq!(result, Some(Value::Byte(var_val)));
396
    }
397

398
        #[test]
399
    fn eval_get_var_int(
400
        var_idx in any::<u8>(),
401
        var_val in any::<i32>()
402
    ) {
403
        let result = eval_out::<Option<Value>>(
404
            &create_getvar_method_call(SType::SInt, var_idx),
405
            &prepare_getvar_context(var_idx, var_val)
406
        );
407
       assert_eq!(result, Some(Value::Int(var_val)));
408
    }
409

410
    #[test]
411
    fn eval_get_var_long(
412
        var_idx in any::<u8>(),
413
        var_val in any::<i64>()
414
    ) {
415
        let result = eval_out::<Option<Value>>(
416
            &create_getvar_method_call(SType::SLong, var_idx),
417
            &prepare_getvar_context(var_idx, var_val)
418
        );
419
        assert_eq!(result, Some(Value::Long(var_val)));
420
    }
421

422
    #[test]
423
    fn eval_get_var_byte(
424
        var_idx in any::<u8>(),
425
        var_val in any::<i8>()
426
    ) {
427
        let result = eval_out::<Option<Value>>(
428
            &create_getvar_method_call(SType::SByte, var_idx),
429
            &prepare_getvar_context(var_idx, var_val)
430
        );
431
        assert_eq!(result, Some(Value::Byte(var_val)));
432
    }
433

434
        }
435
}
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