leo_ast/interpreter_value/
intrinsic.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 Expression,
29 Intrinsic,
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 IntrinsicHelper {
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 IntrinsicHelper for Vec<Value> {
99 fn pop_value_impl(&mut self) -> Option<Value> {
100 self.pop()
101 }
102}
103
104pub fn evaluate_intrinsic(
105 helper: &mut dyn IntrinsicHelper,
106 intrinsic: Intrinsic,
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 IntrinsicHelper, 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 IntrinsicHelper, 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 IntrinsicHelper| -> 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 IntrinsicHelper, 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 IntrinsicHelper, 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 = |helper: &mut dyn IntrinsicHelper, variant: DeserializeVariant, type_: Type| -> Result<Value> {
160 let value: SvmValue = helper.pop_value()?.try_into().expect_tc(span)?;
161 let bits = match value {
162 SvmValue::Plaintext(plaintext) => plaintext.as_bit_array()?,
163 _ => crate::halt_no_span2!("expected array for deserialization"),
164 };
165 let get_struct_fail = |_: &SvmIdentifier| anyhow::bail!("structs are not supported");
166 let value = snarkvm::synthesizer::program::evaluate_deserialize(
167 variant,
168 &bits,
169 &type_.to_snarkvm()?,
170 &get_struct_fail,
171 )?;
172 Ok(value.into())
173 };
174
175 macro_rules! random {
176 ($ty: ident) => {{
177 let Some(rng) = helper.rng() else {
178 return Ok(None);
179 };
180 let value: $ty = rng.r#gen();
181 value.into()
182 }};
183 }
184
185 let value = match intrinsic {
186 Intrinsic::ChaChaRand(type_) => match type_ {
187 LiteralType::Address => random!(Address),
188 LiteralType::Boolean => random!(bool),
189 LiteralType::Field => random!(Field),
190 LiteralType::Group => random!(Group),
191 LiteralType::I8 => random!(i8),
192 LiteralType::I16 => random!(i16),
193 LiteralType::I32 => random!(i32),
194 LiteralType::I64 => random!(i64),
195 LiteralType::I128 => random!(i128),
196 LiteralType::U8 => random!(u8),
197 LiteralType::U16 => random!(u16),
198 LiteralType::U32 => random!(u32),
199 LiteralType::U64 => random!(u64),
200 LiteralType::U128 => random!(u128),
201 LiteralType::Scalar => random!(Scalar),
202 LiteralType::String | LiteralType::Signature => {
203 crate::halt_no_span2!("cannot generate random value of type `{type_}`")
204 }
205 },
206 Intrinsic::Commit(commit_variant, type_) => docommit(helper, commit_variant, type_)?,
207 Intrinsic::Hash(hash_variant, type_) => dohash(helper, hash_variant, type_)?,
208 Intrinsic::ECDSAVerify(ecdsa_variant) => doecdsa(helper, ecdsa_variant)?,
209 Intrinsic::SignatureVerify => doschnorr(helper)?,
210 Intrinsic::Serialize(variant) => doserialize(helper, variant)?,
211 Intrinsic::Deserialize(variant, type_) => dodeserialize(helper, variant, type_)?,
212 Intrinsic::GroupToXCoordinate => {
213 let g: Group = helper.pop_value()?.try_into().expect_tc(span)?;
214 g.to_x_coordinate().into()
215 }
216 Intrinsic::GroupToYCoordinate => {
217 let g: Group = helper.pop_value()?.try_into().expect_tc(span)?;
218 g.to_y_coordinate().into()
219 }
220 Intrinsic::CheatCodePrintMapping => {
221 let (program, name) = match &arguments[0] {
222 Expression::Path(id) => (None, id.identifier().name),
223 Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
224 _ => tc_fail2!(),
225 };
226 if let Some(mapping) = helper.lookup_mapping(program, name) {
227 println!(
230 "Mapping: {}",
231 if let Some(program) = program { format!("{program}/{name}") } else { name.to_string() }
232 );
233 for (key, value) in mapping {
235 println!(" {key} -> {value}");
236 }
237 } else {
238 tc_fail2!();
239 }
240 Value::make_unit()
241 }
242 Intrinsic::CheatCodeSetBlockHeight => {
243 let height: u32 = helper.pop_value()?.try_into().expect_tc(span)?;
244 helper.set_block_height(height);
245 Value::make_unit()
246 }
247 Intrinsic::CheatCodeSetBlockTimestamp => {
248 let timestamp: i64 = helper.pop_value()?.try_into().expect_tc(span)?;
249 helper.set_block_timestamp(timestamp);
250 Value::make_unit()
251 }
252 Intrinsic::CheatCodeSetSigner => {
253 let private_key: String = helper.pop_value()?.try_into().expect_tc(span)?;
254 helper.set_signer(private_key)?;
255 Value::make_unit()
256 }
257 Intrinsic::Get => {
258 let key = helper.pop_value().expect_tc(span)?;
260 let (program, name) = match &arguments[0] {
261 Expression::Path(path) => (None, path.identifier().name),
262 Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
263 _ => tc_fail2!(),
264 };
265 helper.mapping_get(program, name, &key).expect_tc(span)?.clone()
266 }
267 Intrinsic::Set => {
268 let value = helper.pop_value().expect_tc(span)?;
270 let key = helper.pop_value().expect_tc(span)?;
271 let (program, name) = match &arguments[0] {
272 Expression::Path(path) => (None, path.identifier().name),
273 Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
274 _ => tc_fail2!(),
275 };
276 helper.mapping_set(program, name, key, value).expect_tc(span)?;
277 Value::make_unit()
278 }
279 Intrinsic::MappingGetOrUse => {
280 let use_value = helper.pop_value().expect_tc(span)?;
281 let key = helper.pop_value().expect_tc(span)?;
282 let (program, name) = match &arguments[0] {
283 Expression::Path(path) => (None, path.identifier().name),
284 Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
285 _ => tc_fail2!(),
286 };
287 helper.mapping_get(program, name, &key).unwrap_or(use_value)
288 }
289 Intrinsic::MappingRemove => {
290 let key = helper.pop_value().expect_tc(span)?;
291 let (program, name) = match &arguments[0] {
292 Expression::Path(path) => (None, path.identifier().name),
293 Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
294 _ => tc_fail2!(),
295 };
296 helper.mapping_remove(program, name, &key).expect_tc(span)?;
297 Value::make_unit()
298 }
299 Intrinsic::MappingContains => {
300 let key = helper.pop_value().expect_tc(span)?;
301 let (program, name) = match &arguments[0] {
302 Expression::Path(path) => (None, path.identifier().name),
303 Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
304 _ => tc_fail2!(),
305 };
306 helper.mapping_get(program, name, &key).is_some().into()
307 }
308 Intrinsic::GroupGen => Value::generator(),
309 Intrinsic::OptionalUnwrap => {
310 return Ok(None);
312 }
313 Intrinsic::OptionalUnwrapOr => {
314 return Ok(None);
316 }
317 Intrinsic::VectorPush
318 | Intrinsic::VectorLen
319 | Intrinsic::VectorClear
320 | Intrinsic::VectorPop
321 | Intrinsic::VectorSwapRemove
322 | Intrinsic::SelfAddress
323 | Intrinsic::SelfCaller
324 | Intrinsic::SelfChecksum
325 | Intrinsic::SelfEdition
326 | Intrinsic::SelfId
327 | Intrinsic::SelfProgramOwner
328 | Intrinsic::SelfSigner
329 | Intrinsic::BlockHeight
330 | Intrinsic::BlockTimestamp
331 | Intrinsic::NetworkId => {
332 return Ok(None);
334 }
335 Intrinsic::FutureAwait => panic!("await must be handled elsewhere"),
336 Intrinsic::ProgramChecksum => {
337 return Ok(None);
339 }
340 Intrinsic::ProgramEdition => {
341 return Ok(None);
343 }
344 Intrinsic::ProgramOwner => {
345 return Ok(None);
347 }
348 };
349
350 Ok(Some(value))
351}