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_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 println!(
231 "Mapping: {}",
232 if let Some(program) = program { format!("{program}/{name}") } else { name.to_string() }
233 );
234 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 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 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 return Ok(None);
312 }
313 CoreFunction::OptionalUnwrapOr => {
314 return Ok(None);
316 }
317 CoreFunction::VectorPush
318 | CoreFunction::VectorLen
319 | CoreFunction::VectorClear
320 | CoreFunction::VectorPop
321 | CoreFunction::VectorSwapRemove => {
322 return Ok(None);
324 }
325 CoreFunction::FutureAwait => panic!("await must be handled elsewhere"),
326
327 CoreFunction::ProgramChecksum => {
328 return Ok(None);
330 }
331 CoreFunction::ProgramEdition => {
332 return Ok(None);
334 }
335 CoreFunction::ProgramOwner => {
336 return Ok(None);
338 }
339 };
340
341 Ok(Some(value))
342}