ergotree_interpreter/eval/
spreheader.rs

1use std::sync::Arc;
2
3use ergo_chain_types::PreHeader;
4use ergotree_ir::mir::constant::TryExtractInto;
5
6use super::EvalFn;
7
8pub(crate) static VERSION_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
9    let preheader = obj.try_extract_into::<PreHeader>()?;
10    Ok((preheader.version as i8).into())
11};
12
13pub(crate) static PARENT_ID_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
14    let preheader = obj.try_extract_into::<PreHeader>()?;
15    Ok(Into::<Vec<i8>>::into(preheader.parent_id).into())
16};
17
18pub(crate) static TIMESTAMP_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
19    let preheader = obj.try_extract_into::<PreHeader>()?;
20    Ok((preheader.timestamp as i64).into())
21};
22
23pub(crate) static N_BITS_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
24    let preheader = obj.try_extract_into::<PreHeader>()?;
25    Ok((preheader.n_bits as i64).into())
26};
27
28pub(crate) static HEIGHT_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
29    let preheader = obj.try_extract_into::<PreHeader>()?;
30    Ok((preheader.height as i32).into())
31};
32
33pub(crate) static MINER_PK_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
34    let preheader = obj.try_extract_into::<PreHeader>()?;
35    Ok(Arc::new(*preheader.miner_pk).into())
36};
37
38pub(crate) static VOTES_EVAL_FN: EvalFn = |_env, _ctx, obj, _args| {
39    let preheader = obj.try_extract_into::<PreHeader>()?;
40    Ok(Into::<Vec<u8>>::into(preheader.votes).into())
41};
42
43#[cfg(test)]
44#[cfg(feature = "arbitrary")]
45#[allow(clippy::expect_used)]
46mod tests {
47    use std::convert::{TryFrom, TryInto};
48
49    use ergo_chain_types::{BlockId, EcPoint, Votes};
50    use ergotree_ir::{
51        mir::{expr::Expr, property_call::PropertyCall},
52        types::{scontext, smethod::SMethod, spreheader},
53    };
54    use sigma_test_util::force_any_val;
55    use sigma_util::AsVecU8;
56
57    use crate::eval::{
58        context::Context,
59        tests::{eval_out, try_eval_out_wo_ctx},
60    };
61
62    fn create_get_preheader_property_expr(method: SMethod) -> Expr {
63        let get_preheader_expr = create_get_preheader_expr();
64        create_get_preheader_property_expr_impl(get_preheader_expr, method)
65    }
66
67    // An `Expr` for such code in ErgoScript: `CONTEXT.preHeader`
68    fn create_get_preheader_expr() -> Expr {
69        PropertyCall::new(Expr::Context, scontext::PRE_HEADER_PROPERTY.clone())
70            .expect("internal error: invalid preheader property call of Context")
71            .into()
72    }
73
74    fn create_get_preheader_property_expr_impl(headers_expr: Expr, method: SMethod) -> Expr {
75        PropertyCall::new(headers_expr, method)
76            .expect("internal error: invalid property call of PreHeader")
77            .into()
78    }
79
80    fn block_id_from_bytes_signed(bytes: Vec<i8>) -> BlockId {
81        let arr32 = bytes
82            .as_vec_u8()
83            .try_into()
84            .expect("internal error: bytes buffer length is not equal to 32");
85        BlockId(arr32)
86    }
87
88    #[test]
89    fn test_eval_version() {
90        let expr = create_get_preheader_property_expr(spreheader::VERSION_PROPERTY.clone());
91        let ctx = force_any_val::<Context>();
92        let expected = ctx.pre_header.version as i8;
93        assert_eq!(expected, eval_out::<i8>(&expr, &ctx));
94    }
95
96    #[test]
97    fn test_eval_parent_id() {
98        let expr = create_get_preheader_property_expr(spreheader::PARENT_ID_PROPERTY.clone());
99        let ctx = force_any_val::<Context>();
100        let expected = ctx.pre_header.parent_id;
101        let actual = {
102            let bs = eval_out::<Vec<i8>>(&expr, &ctx);
103            block_id_from_bytes_signed(bs)
104        };
105        assert_eq!(expected, actual);
106    }
107
108    #[test]
109    fn test_eval_timestamp() {
110        let expr = create_get_preheader_property_expr(spreheader::TIMESTAMP_PROPERTY.clone());
111        let ctx = force_any_val::<Context>();
112        let expected = ctx.pre_header.timestamp as i64;
113        let actual = eval_out::<i64>(&expr, &ctx);
114        assert_eq!(expected, actual);
115    }
116
117    #[test]
118    fn test_eval_n_bits() {
119        let expr = create_get_preheader_property_expr(spreheader::N_BITS_PROPERTY.clone());
120        let ctx = force_any_val::<Context>();
121        let expected = ctx.pre_header.n_bits as i64;
122        let actual = eval_out::<i64>(&expr, &ctx);
123        assert_eq!(expected, actual);
124    }
125
126    #[test]
127    fn test_eval_height() {
128        let expr = create_get_preheader_property_expr(spreheader::HEIGHT_PROPERTY.clone());
129        let ctx = force_any_val::<Context>();
130        let expected = ctx.pre_header.height as i32;
131        let actual = eval_out::<i32>(&expr, &ctx);
132        assert_eq!(expected, actual);
133    }
134
135    #[test]
136    fn test_eval_miner_pk() {
137        let expr = create_get_preheader_property_expr(spreheader::MINER_PK_PROPERTY.clone());
138        let ctx = force_any_val::<Context>();
139        let expected = ctx.pre_header.miner_pk.clone();
140        let actual = {
141            let pk = eval_out::<EcPoint>(&expr, &ctx);
142            Box::new(pk)
143        };
144        assert_eq!(expected, actual);
145    }
146
147    #[test]
148    fn test_eval_votes() {
149        let expr = create_get_preheader_property_expr(spreheader::VOTES_PROPERTY.clone());
150        let ctx = force_any_val::<Context>();
151        let expected = ctx.pre_header.votes.clone();
152        let actual = {
153            let votes_bytes = eval_out::<Vec<i8>>(&expr, &ctx).as_vec_u8();
154            Votes::try_from(votes_bytes)
155                .expect("internal error: votes bytes buffer length isn't equal to 3")
156        };
157        assert_eq!(expected, actual);
158    }
159
160    #[test]
161    fn test_eval_failed_invalid_obj() {
162        // calling for PreHeader property on Context obj
163        let expr: Expr = PropertyCall {
164            obj: Box::new(Expr::Context),
165            method: spreheader::VERSION_PROPERTY.clone(),
166        }
167        .into();
168        assert!(try_eval_out_wo_ctx::<i8>(&expr).is_err());
169    }
170
171    #[test]
172    fn test_eval_failed_unknown_property() {
173        let unknown_property = {
174            use ergotree_ir::types::{
175                smethod::{MethodId, SMethodDesc},
176                stype::SType,
177                stype_companion::STypeCompanion,
178            };
179            let method_desc =
180                SMethodDesc::property(SType::SPreHeader, "unknown", SType::SByte, MethodId(100));
181            SMethod::new(STypeCompanion::PreHeader, method_desc)
182        };
183        let expr = create_get_preheader_property_expr(unknown_property);
184        assert!(try_eval_out_wo_ctx::<i8>(&expr).is_err());
185    }
186}