leo_ast/interpreter_value/
core_function.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use 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
39/// A context in which we can evaluate core functions.
40///
41/// This is intended to be implemented by `Cursor`, which will be used during
42/// execution of the interpreter, and by `Vec<Value>`, which will be used
43/// during compile time evaluation for constant folding.
44///
45/// The default implementations for `rng`, `set_block_height`, and mapping lookup
46/// do nothing, as those features will not be available during compile time
47/// evaluation.
48pub 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_block_timestamp(&mut self, _timestamp: i64) {}
64
65    fn set_signer(&mut self, _private_key: String) -> Result<()> {
66        Ok(())
67    }
68
69    fn lookup_mapping(&self, _program: Option<Symbol>, _name: Symbol) -> Option<&HashMap<Value, Value>> {
70        None
71    }
72
73    fn lookup_mapping_mut(&mut self, _program: Option<Symbol>, _name: Symbol) -> Option<&mut HashMap<Value, Value>> {
74        None
75    }
76
77    fn mapping_get(&self, program: Option<Symbol>, name: Symbol, key: &Value) -> Option<Value> {
78        self.lookup_mapping(program, name).and_then(|map| map.get(key).cloned())
79    }
80
81    fn mapping_set(&mut self, program: Option<Symbol>, name: Symbol, key: Value, value: Value) -> Option<()> {
82        self.lookup_mapping_mut(program, name).map(|map| {
83            map.insert(key, value);
84        })
85    }
86
87    fn mapping_remove(&mut self, program: Option<Symbol>, name: Symbol, key: &Value) -> Option<()> {
88        self.lookup_mapping_mut(program, name).map(|map| {
89            map.remove(key);
90        })
91    }
92
93    fn rng(&mut self) -> Option<&mut ChaCha20Rng> {
94        None
95    }
96}
97
98impl CoreFunctionHelper for Vec<Value> {
99    fn pop_value_impl(&mut self) -> Option<Value> {
100        self.pop()
101    }
102}
103
104pub fn evaluate_core_function(
105    helper: &mut dyn CoreFunctionHelper,
106    core_function: CoreFunction,
107    arguments: &[Expression],
108    span: Span,
109) -> Result<Option<Value>> {
110    use snarkvm::{
111        prelude::LiteralType,
112        synthesizer::program::{CommitVariant, ECDSAVerifyVariant, HashVariant},
113    };
114
115    let dohash = |helper: &mut dyn CoreFunctionHelper, variant: HashVariant, typ: Type| -> Result<Value> {
116        let input = helper.pop_value()?.try_into().expect_tc(span)?;
117        let value = snarkvm::synthesizer::program::evaluate_hash(variant, &input, &typ.to_snarkvm()?)?;
118        Ok(value.into())
119    };
120
121    let docommit = |helper: &mut dyn CoreFunctionHelper, variant: CommitVariant, typ: LiteralType| -> Result<Value> {
122        let randomizer: Scalar = helper.pop_value()?.try_into().expect_tc(span)?;
123        let input: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
124        let value = snarkvm::synthesizer::program::evaluate_commit(variant, &input, &randomizer, typ)?;
125        Ok(value.into())
126    };
127
128    let doschnorr = |helper: &mut dyn CoreFunctionHelper| -> Result<Value> {
129        let signature: Signature = helper.pop_value()?.try_into().expect_tc(span)?;
130        let address: Address = helper.pop_value()?.try_into().expect_tc(span)?;
131        let message: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
132        let is_valid = snarkvm::synthesizer::program::evaluate_schnorr_verification(&signature, &address, &message)?;
133        Ok(Boolean::new(is_valid).into())
134    };
135
136    let doecdsa = |helper: &mut dyn CoreFunctionHelper, variant: ECDSAVerifyVariant| -> Result<Value> {
137        let signature: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
138        let public_key: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
139        let message: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
140        let is_valid =
141            snarkvm::synthesizer::program::evaluate_ecdsa_verification(variant, &signature, &public_key, &message)?;
142        Ok(Boolean::new(is_valid).into())
143    };
144
145    let doserialize = |helper: &mut dyn CoreFunctionHelper, variant: SerializeVariant| -> Result<Value> {
146        let input: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
147        let num_bits = match variant {
148            SerializeVariant::ToBits => input.to_bits_le().len(),
149            SerializeVariant::ToBitsRaw => input.to_bits_raw_le().len(),
150        };
151        let Ok(num_bits) = u32::try_from(num_bits) else {
152            crate::halt_no_span2!("cannot serialize value with more than 2^32 bits");
153        };
154        let array_type = ArrayType::bit_array(num_bits).to_snarkvm()?;
155        let value = snarkvm::synthesizer::program::evaluate_serialize(variant, &input, &array_type)?;
156        Ok(value.into())
157    };
158
159    let dodeserialize =
160        |helper: &mut dyn CoreFunctionHelper, variant: DeserializeVariant, type_: Type| -> Result<Value> {
161            let value: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
162            let bits = match value {
163                SvmValue::Plaintext(plaintext) => plaintext.as_bit_array()?,
164                _ => crate::halt_no_span2!("expected array for deserialization"),
165            };
166            let get_struct_fail = |_: &SvmIdentifier| anyhow::bail!("structs are not supported");
167            let value = snarkvm::synthesizer::program::evaluate_deserialize(
168                variant,
169                &bits,
170                &type_.to_snarkvm()?,
171                &get_struct_fail,
172            )?;
173            Ok(value.into())
174        };
175
176    macro_rules! random {
177        ($ty: ident) => {{
178            let Some(rng) = helper.rng() else {
179                return Ok(None);
180            };
181            let value: $ty = rng.r#gen();
182            value.into()
183        }};
184    }
185
186    let value = match core_function {
187        CoreFunction::ChaChaRand(type_) => match type_ {
188            LiteralType::Address => random!(Address),
189            LiteralType::Boolean => random!(bool),
190            LiteralType::Field => random!(Field),
191            LiteralType::Group => random!(Group),
192            LiteralType::I8 => random!(i8),
193            LiteralType::I16 => random!(i16),
194            LiteralType::I32 => random!(i32),
195            LiteralType::I64 => random!(i64),
196            LiteralType::I128 => random!(i128),
197            LiteralType::U8 => random!(u8),
198            LiteralType::U16 => random!(u16),
199            LiteralType::U32 => random!(u32),
200            LiteralType::U64 => random!(u64),
201            LiteralType::U128 => random!(u128),
202            LiteralType::Scalar => random!(Scalar),
203            LiteralType::String | LiteralType::Signature => {
204                crate::halt_no_span2!("cannot generate random value of type `{type_}`")
205            }
206        },
207        CoreFunction::Commit(commit_variant, type_) => docommit(helper, commit_variant, type_)?,
208        CoreFunction::Hash(hash_variant, type_) => dohash(helper, hash_variant, type_)?,
209        CoreFunction::ECDSAVerify(ecdsa_variant) => doecdsa(helper, ecdsa_variant)?,
210        CoreFunction::SignatureVerify => doschnorr(helper)?,
211        CoreFunction::Serialize(variant) => doserialize(helper, variant)?,
212        CoreFunction::Deserialize(variant, type_) => dodeserialize(helper, variant, type_)?,
213        CoreFunction::GroupToXCoordinate => {
214            let g: Group = helper.pop_value()?.try_into().expect_tc(span)?;
215            g.to_x_coordinate().into()
216        }
217        CoreFunction::GroupToYCoordinate => {
218            let g: Group = helper.pop_value()?.try_into().expect_tc(span)?;
219            g.to_y_coordinate().into()
220        }
221        CoreFunction::CheatCodePrintMapping => {
222            let (program, name) = match &arguments[0] {
223                Expression::Path(id) => (None, id.identifier().name),
224                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
225                _ => tc_fail2!(),
226            };
227            if let Some(mapping) = helper.lookup_mapping(program, name) {
228                // TODO: What is the appropriate way to print this to the console.
229                // Print the name of the mapping.
230                println!(
231                    "Mapping: {}",
232                    if let Some(program) = program { format!("{program}/{name}") } else { name.to_string() }
233                );
234                // Print the contents of the mapping.
235                for (key, value) in mapping {
236                    println!("  {key} -> {value}");
237                }
238            } else {
239                tc_fail2!();
240            }
241            Value::make_unit()
242        }
243        CoreFunction::CheatCodeSetBlockHeight => {
244            let height: u32 = helper.pop_value()?.try_into().expect_tc(span)?;
245            helper.set_block_height(height);
246            Value::make_unit()
247        }
248        CoreFunction::CheatCodeSetBlockTimestamp => {
249            let timestamp: i64 = helper.pop_value()?.try_into().expect_tc(span)?;
250            helper.set_block_timestamp(timestamp);
251            Value::make_unit()
252        }
253        CoreFunction::CheatCodeSetSigner => {
254            let private_key: String = helper.pop_value()?.try_into().expect_tc(span)?;
255            helper.set_signer(private_key)?;
256            Value::make_unit()
257        }
258        CoreFunction::Get => {
259            // TODO handle vector get
260            let key = helper.pop_value().expect_tc(span)?;
261            let (program, name) = match &arguments[0] {
262                Expression::Path(path) => (None, path.identifier().name),
263                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
264                _ => tc_fail2!(),
265            };
266            helper.mapping_get(program, name, &key).expect_tc(span)?.clone()
267        }
268        CoreFunction::Set => {
269            // TODO handle vector set
270            let value = helper.pop_value().expect_tc(span)?;
271            let key = helper.pop_value().expect_tc(span)?;
272            let (program, name) = match &arguments[0] {
273                Expression::Path(path) => (None, path.identifier().name),
274                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
275                _ => tc_fail2!(),
276            };
277            helper.mapping_set(program, name, key, value).expect_tc(span)?;
278            Value::make_unit()
279        }
280        CoreFunction::MappingGetOrUse => {
281            let use_value = helper.pop_value().expect_tc(span)?;
282            let key = helper.pop_value().expect_tc(span)?;
283            let (program, name) = match &arguments[0] {
284                Expression::Path(path) => (None, path.identifier().name),
285                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
286                _ => tc_fail2!(),
287            };
288            helper.mapping_get(program, name, &key).unwrap_or(use_value)
289        }
290        CoreFunction::MappingRemove => {
291            let key = helper.pop_value().expect_tc(span)?;
292            let (program, name) = match &arguments[0] {
293                Expression::Path(path) => (None, path.identifier().name),
294                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
295                _ => tc_fail2!(),
296            };
297            helper.mapping_remove(program, name, &key).expect_tc(span)?;
298            Value::make_unit()
299        }
300        CoreFunction::MappingContains => {
301            let key = helper.pop_value().expect_tc(span)?;
302            let (program, name) = match &arguments[0] {
303                Expression::Path(path) => (None, path.identifier().name),
304                Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
305                _ => tc_fail2!(),
306            };
307            helper.mapping_get(program, name, &key).is_some().into()
308        }
309        CoreFunction::OptionalUnwrap => {
310            // TODO
311            return Ok(None);
312        }
313        CoreFunction::OptionalUnwrapOr => {
314            // TODO
315            return Ok(None);
316        }
317        CoreFunction::VectorPush
318        | CoreFunction::VectorLen
319        | CoreFunction::VectorClear
320        | CoreFunction::VectorPop
321        | CoreFunction::VectorSwapRemove => {
322            // TODO
323            return Ok(None);
324        }
325        CoreFunction::FutureAwait => panic!("await must be handled elsewhere"),
326
327        CoreFunction::ProgramChecksum => {
328            // TODO: This is a placeholder. The actual implementation should look up the program in the global context and get its checksum.
329            return Ok(None);
330        }
331        CoreFunction::ProgramEdition => {
332            // TODO: This is a placeholder. The actual implementation should look up the program in the global context and get its edition.
333            return Ok(None);
334        }
335        CoreFunction::ProgramOwner => {
336            // TODO: This is a placeholder. The actual implementation should look up the program in the global context and get its owner.
337            return Ok(None);
338        }
339    };
340
341    Ok(Some(value))
342}