leo_ast/interpreter_value/
core_function.rs1use std::collections::HashMap;
18
19use rand::Rng as _;
20use rand_chacha::ChaCha20Rng;
21use snarkvm::{
22    prelude::{ToBits, ToBitsRaw},
23    synthesizer::program::{DeserializeVariant, SerializeVariant},
24};
25
26use crate::{
27    ArrayType,
28    CoreFunction,
29    Expression,
30    Type,
31    interpreter_value::{ExpectTc, Value},
32    tc_fail2,
33};
34use leo_errors::{InterpreterHalt, Result};
35use leo_span::{Span, Symbol};
36
37use super::*;
38
39pub trait CoreFunctionHelper {
49    fn pop_value_impl(&mut self) -> Option<Value>;
50
51    fn pop_value(&mut self) -> Result<Value> {
52        match self.pop_value_impl() {
53            Some(v) => Ok(v),
54            None => {
55                Err(InterpreterHalt::new("value expected - this may be a bug in the Leo interpreter".to_string())
56                    .into())
57            }
58        }
59    }
60
61    fn set_block_height(&mut self, _height: u32) {}
62
63    fn set_signer(&mut self, _private_key: String) -> Result<()> {
64        Ok(())
65    }
66
67    fn lookup_mapping(&self, _program: Option<Symbol>, _name: Symbol) -> Option<&HashMap<Value, Value>> {
68        None
69    }
70
71    fn lookup_mapping_mut(&mut self, _program: Option<Symbol>, _name: Symbol) -> Option<&mut HashMap<Value, Value>> {
72        None
73    }
74
75    fn mapping_get(&self, program: Option<Symbol>, name: Symbol, key: &Value) -> Option<Value> {
76        self.lookup_mapping(program, name).and_then(|map| map.get(key).cloned())
77    }
78
79    fn mapping_set(&mut self, program: Option<Symbol>, name: Symbol, key: Value, value: Value) -> Option<()> {
80        self.lookup_mapping_mut(program, name).map(|map| {
81            map.insert(key, value);
82        })
83    }
84
85    fn mapping_remove(&mut self, program: Option<Symbol>, name: Symbol, key: &Value) -> Option<()> {
86        self.lookup_mapping_mut(program, name).map(|map| {
87            map.remove(key);
88        })
89    }
90
91    fn rng(&mut self) -> Option<&mut ChaCha20Rng> {
92        None
93    }
94}
95
96impl CoreFunctionHelper for Vec<Value> {
97    fn pop_value_impl(&mut self) -> Option<Value> {
98        self.pop()
99    }
100}
101
102pub fn evaluate_core_function(
103    helper: &mut dyn CoreFunctionHelper,
104    core_function: CoreFunction,
105    arguments: &[Expression],
106    span: Span,
107) -> Result<Option<Value>> {
108    use snarkvm::{
109        prelude::LiteralType,
110        synthesizer::program::{CommitVariant, ECDSAVerifyVariant, HashVariant},
111    };
112
113    let dohash = |helper: &mut dyn CoreFunctionHelper, variant: HashVariant, typ: Type| -> Result<Value> {
114        let input = helper.pop_value()?.try_into().expect_tc(span)?;
115        let value = snarkvm::synthesizer::program::evaluate_hash(variant, &input, &typ.to_snarkvm()?)?;
116        Ok(value.into())
117    };
118
119    let docommit = |helper: &mut dyn CoreFunctionHelper, variant: CommitVariant, typ: LiteralType| -> Result<Value> {
120        let randomizer: Scalar = helper.pop_value()?.try_into().expect_tc(span)?;
121        let input: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
122        let value = snarkvm::synthesizer::program::evaluate_commit(variant, &input, &randomizer, typ)?;
123        Ok(value.into())
124    };
125
126    let doschnorr = |helper: &mut dyn CoreFunctionHelper| -> Result<Value> {
127        let signature: Signature = helper.pop_value()?.try_into().expect_tc(span)?;
128        let address: Address = helper.pop_value()?.try_into().expect_tc(span)?;
129        let message: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
130        let is_valid = snarkvm::synthesizer::program::evaluate_schnorr_verification(&signature, &address, &message)?;
131        Ok(Boolean::new(is_valid).into())
132    };
133
134    let doecdsa = |helper: &mut dyn CoreFunctionHelper, variant: ECDSAVerifyVariant| -> Result<Value> {
135        let signature: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
136        let public_key: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
137        let message: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
138        let is_valid =
139            snarkvm::synthesizer::program::evaluate_ecdsa_verification(variant, &signature, &public_key, &message)?;
140        Ok(Boolean::new(is_valid).into())
141    };
142
143    let doserialize = |helper: &mut dyn CoreFunctionHelper, variant: SerializeVariant| -> Result<Value> {
144        let input: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
145        let num_bits = match variant {
146            SerializeVariant::ToBits => input.to_bits_le().len(),
147            SerializeVariant::ToBitsRaw => input.to_bits_raw_le().len(),
148        };
149        let Ok(num_bits) = u32::try_from(num_bits) else {
150            crate::halt_no_span2!("cannot serialize value with more than 2^32 bits");
151        };
152        let array_type = ArrayType::bit_array(num_bits).to_snarkvm()?;
153        let value = snarkvm::synthesizer::program::evaluate_serialize(variant, &input, &array_type)?;
154        Ok(value.into())
155    };
156
157    let dodeserialize =
158        |helper: &mut dyn CoreFunctionHelper, variant: DeserializeVariant, type_: Type| -> Result<Value> {
159            let value: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
160            let bits = match value {
161                SvmValue::Plaintext(plaintext) => plaintext.as_bit_array()?,
162                _ => crate::halt_no_span2!("expected array for deserialization"),
163            };
164            let get_struct_fail = |_: &SvmIdentifier| anyhow::bail!("structs are not supported");
165            let value = snarkvm::synthesizer::program::evaluate_deserialize(
166                variant,
167                &bits,
168                &type_.to_snarkvm()?,
169                &get_struct_fail,
170            )?;
171            Ok(value.into())
172        };
173
174    macro_rules! random {
175        ($ty: ident) => {{
176            let Some(rng) = helper.rng() else {
177                return Ok(None);
178            };
179            let value: $ty = rng.r#gen();
180            value.into()
181        }};
182    }
183
184    let value = match core_function {
185        CoreFunction::ChaChaRand(type_) => match type_ {
186            LiteralType::Address => random!(Address),
187            LiteralType::Boolean => random!(bool),
188            LiteralType::Field => random!(Field),
189            LiteralType::Group => random!(Group),
190            LiteralType::I8 => random!(i8),
191            LiteralType::I16 => random!(i16),
192            LiteralType::I32 => random!(i32),
193            LiteralType::I64 => random!(i64),
194            LiteralType::I128 => random!(i128),
195            LiteralType::U8 => random!(u8),
196            LiteralType::U16 => random!(u16),
197            LiteralType::U32 => random!(u32),
198            LiteralType::U64 => random!(u64),
199            LiteralType::U128 => random!(u128),
200            LiteralType::Scalar => random!(Scalar),
201            LiteralType::String | LiteralType::Signature => {
202                crate::halt_no_span2!("cannot generate random value of type `{type_}`")
203            }
204        },
205        CoreFunction::Commit(commit_variant, type_) => docommit(helper, commit_variant, type_)?,
206        CoreFunction::Hash(hash_variant, type_) => dohash(helper, hash_variant, type_)?,
207        CoreFunction::ECDSAVerify(ecdsa_variant) => doecdsa(helper, ecdsa_variant)?,
208        CoreFunction::SignatureVerify => doschnorr(helper)?,
209        CoreFunction::Serialize(variant) => doserialize(helper, variant)?,
210        CoreFunction::Deserialize(variant, type_) => dodeserialize(helper, variant, type_)?,
211        CoreFunction::GroupToXCoordinate => {
212            let g: Group = helper.pop_value()?.try_into().expect_tc(span)?;
213            g.to_x_coordinate().into()
214        }
215        CoreFunction::GroupToYCoordinate => {
216            let g: Group = helper.pop_value()?.try_into().expect_tc(span)?;
217            g.to_y_coordinate().into()
218        }
219        CoreFunction::CheatCodePrintMapping => {
220            let (program, name) = match &arguments[0] {
221                Expression::Path(id) => (None, id.identifier().name),
222                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
223                _ => tc_fail2!(),
224            };
225            if let Some(mapping) = helper.lookup_mapping(program, name) {
226                println!(
229                    "Mapping: {}",
230                    if let Some(program) = program { format!("{program}/{name}") } else { name.to_string() }
231                );
232                for (key, value) in mapping {
234                    println!("  {key} -> {value}");
235                }
236            } else {
237                tc_fail2!();
238            }
239            Value::make_unit()
240        }
241        CoreFunction::CheatCodeSetBlockHeight => {
242            let height: u32 = helper.pop_value()?.try_into().expect_tc(span)?;
243            helper.set_block_height(height);
244            Value::make_unit()
245        }
246        CoreFunction::CheatCodeSetSigner => {
247            let private_key: String = helper.pop_value()?.try_into().expect_tc(span)?;
248            helper.set_signer(private_key)?;
249            Value::make_unit()
250        }
251        CoreFunction::Get => {
252            let key = helper.pop_value().expect_tc(span)?;
254            let (program, name) = match &arguments[0] {
255                Expression::Path(path) => (None, path.identifier().name),
256                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
257                _ => tc_fail2!(),
258            };
259            helper.mapping_get(program, name, &key).expect_tc(span)?.clone()
260        }
261        CoreFunction::Set => {
262            let value = helper.pop_value().expect_tc(span)?;
264            let key = helper.pop_value().expect_tc(span)?;
265            let (program, name) = match &arguments[0] {
266                Expression::Path(path) => (None, path.identifier().name),
267                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
268                _ => tc_fail2!(),
269            };
270            helper.mapping_set(program, name, key, value).expect_tc(span)?;
271            Value::make_unit()
272        }
273        CoreFunction::MappingGetOrUse => {
274            let use_value = helper.pop_value().expect_tc(span)?;
275            let key = helper.pop_value().expect_tc(span)?;
276            let (program, name) = match &arguments[0] {
277                Expression::Path(path) => (None, path.identifier().name),
278                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
279                _ => tc_fail2!(),
280            };
281            helper.mapping_get(program, name, &key).unwrap_or(use_value)
282        }
283        CoreFunction::MappingRemove => {
284            let key = helper.pop_value().expect_tc(span)?;
285            let (program, name) = match &arguments[0] {
286                Expression::Path(path) => (None, path.identifier().name),
287                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
288                _ => tc_fail2!(),
289            };
290            helper.mapping_remove(program, name, &key).expect_tc(span)?;
291            Value::make_unit()
292        }
293        CoreFunction::MappingContains => {
294            let key = helper.pop_value().expect_tc(span)?;
295            let (program, name) = match &arguments[0] {
296                Expression::Path(path) => (None, path.identifier().name),
297                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
298                _ => tc_fail2!(),
299            };
300            helper.mapping_get(program, name, &key).is_some().into()
301        }
302        CoreFunction::OptionalUnwrap => {
303            return Ok(None);
305        }
306        CoreFunction::OptionalUnwrapOr => {
307            return Ok(None);
309        }
310        CoreFunction::VectorPush
311        | CoreFunction::VectorLen
312        | CoreFunction::VectorClear
313        | CoreFunction::VectorPop
314        | CoreFunction::VectorSwapRemove => {
315            return Ok(None);
317        }
318        CoreFunction::FutureAwait => panic!("await must be handled elsewhere"),
319
320        CoreFunction::ProgramChecksum => {
321            return Ok(None);
323        }
324        CoreFunction::ProgramEdition => {
325            return Ok(None);
327        }
328        CoreFunction::ProgramOwner => {
329            return Ok(None);
331        }
332    };
333
334    Ok(Some(value))
335}