1use aztec_core::error::Error;
4use aztec_core::kernel_types::{
5 CallContext, ContractClassLog, CountedContractClassLog, NoteAndSlot, NoteHash, Nullifier,
6 ScopedNoteHash, ScopedNullifier, ScopedReadRequest,
7};
8use aztec_core::tx::HashedValues;
9use aztec_core::types::{AztecAddress, ContractInstance, Fr};
10use aztec_node_client::AztecNode;
11
12use super::acvm_executor::{AcvmExecutionOutput, OracleCallback};
13use super::execution_result::{
14 PrivateCallExecutionResult, PrivateExecutionResult, PrivateLogData, PublicCallRequestData,
15};
16use crate::stores::note_store::{NoteFilter, NoteStatus, StoredNote};
17use crate::stores::{
18 AddressStore, CapsuleStore, ContractStore, KeyStore, NoteStore, SenderTaggingStore,
19};
20
21pub struct PrivateExecutionOracle<'a, N: AztecNode> {
26 node: &'a N,
27 contract_store: &'a ContractStore,
28 key_store: &'a KeyStore,
29 note_store: &'a NoteStore,
30 capsule_store: &'a CapsuleStore,
31 address_store: &'a AddressStore,
32 sender_tagging_store: &'a SenderTaggingStore,
33 block_header: serde_json::Value,
35 contract_address: AztecAddress,
37 protocol_nullifier: Fr,
39 execution_cache: std::collections::HashMap<Fr, Vec<Fr>>,
41 auth_witnesses: Vec<(Fr, Vec<Fr>)>,
43 sender_for_tags: Option<AztecAddress>,
45 scopes: Vec<AztecAddress>,
47 call_is_static: bool,
49
50 pub(crate) side_effect_counter: u32,
53 pub(crate) new_notes: Vec<NoteAndSlot>,
55 pub(crate) note_hashes: Vec<ScopedNoteHash>,
57 pub(crate) nullifiers: Vec<ScopedNullifier>,
59 pub(crate) note_hash_nullifier_counter_map: std::collections::HashMap<u32, u32>,
61 consumed_db_nullifiers: std::collections::HashSet<Fr>,
64 pub(crate) private_logs: Vec<PrivateLogData>,
66 pub(crate) contract_class_logs: Vec<CountedContractClassLog>,
68 offchain_effects: Vec<Vec<Fr>>,
70 public_call_requests: Vec<PublicCallRequestData>,
72 public_teardown_call_request: Option<PublicCallRequestData>,
74 pub(crate) note_hash_read_requests: Vec<ScopedReadRequest>,
76 pub(crate) nullifier_read_requests: Vec<ScopedReadRequest>,
78 pub(crate) min_revertible_side_effect_counter: u32,
80 public_function_calldata: Vec<HashedValues>,
82 pub(crate) nested_results: Vec<PrivateCallExecutionResult>,
84 pub(crate) context_witness_prefix: Vec<Fr>,
87 capsules: Vec<aztec_core::tx::Capsule>,
90}
91
92fn decode_base64_sibling_path(encoded: &str) -> Result<Vec<Fr>, Error> {
93 use base64::Engine;
94
95 let bytes = base64::engine::general_purpose::STANDARD
96 .decode(encoded)
97 .map_err(|e| Error::InvalidData(format!("invalid siblingPath base64: {e}")))?;
98
99 let payload = if bytes.len() >= 4 {
100 let declared_len =
101 u32::from_be_bytes(bytes[..4].try_into().expect("length prefix is 4 bytes")) as usize;
102 let payload = &bytes[4..];
103 if payload.len() == declared_len.saturating_mul(32) {
104 payload
105 } else if bytes.len() % 32 == 0 {
106 bytes.as_slice()
107 } else {
108 return Err(Error::InvalidData(format!(
109 "siblingPath payload length mismatch: declared {declared_len} elements, got {} bytes",
110 payload.len()
111 )));
112 }
113 } else {
114 bytes.as_slice()
115 };
116
117 Ok(payload
118 .chunks(32)
119 .map(|chunk| {
120 let mut padded = [0u8; 32];
121 let start = 32usize.saturating_sub(chunk.len());
122 padded[start..].copy_from_slice(chunk);
123 Fr::from(padded)
124 })
125 .collect())
126}
127
128fn parse_field_string(value: &str) -> Result<Fr, Error> {
129 if value.starts_with("0x") {
130 Fr::from_hex(value)
131 } else {
132 value
133 .parse::<u128>()
134 .map(Fr::from)
135 .map_err(|_| Error::InvalidData(format!("unsupported field string value: {value}")))
136 }
137}
138
139#[derive(Debug, Clone)]
141pub struct CachedNote {
142 pub contract_address: AztecAddress,
143 pub storage_slot: Fr,
144 pub note_hash: Fr,
145 pub note_data: Vec<Fr>,
146}
147
148impl<'a, N: AztecNode + 'static> PrivateExecutionOracle<'a, N> {
149 fn extract_side_effects_from_witness(
154 witness: &acir::native_types::WitnessMap<acir::FieldElement>,
155 params_size: usize,
156 contract_address: AztecAddress,
157 ) -> (
158 Vec<aztec_core::kernel_types::ScopedNoteHash>,
159 Vec<aztec_core::kernel_types::ScopedNullifier>,
160 Vec<PrivateLogData>,
161 ) {
162 use aztec_core::kernel_types::{NoteHash, Nullifier, ScopedNoteHash, ScopedNullifier};
163
164 const PCPI_LENGTH: usize = 870;
165 const NOTE_HASHES_OFFSET: usize = 454;
166 const NOTE_HASH_LEN: usize = 2;
167 const MAX_NOTE_HASHES: usize = 16;
168 const NOTE_HASHES_ARRAY_LEN: usize = MAX_NOTE_HASHES * NOTE_HASH_LEN + 1;
169 const NULLIFIERS_OFFSET: usize = 487;
170 const NULLIFIER_LEN: usize = 3;
171 const MAX_NULLIFIERS: usize = 16;
172 const NULLIFIERS_ARRAY_LEN: usize = MAX_NULLIFIERS * NULLIFIER_LEN + 1;
173 const PRIVATE_LOGS_OFFSET: usize = 561;
174 const PRIVATE_LOG_DATA_LEN: usize = 19;
175 const PRIVATE_LOG_FIELDS: usize = 16;
176 const MAX_LOGS: usize = 16;
177 const PRIVATE_LOGS_ARRAY_LEN: usize = MAX_LOGS * PRIVATE_LOG_DATA_LEN + 1;
178
179 let pcpi_start = params_size;
180 let mut pcpi = Vec::with_capacity(PCPI_LENGTH);
181 for i in 0..PCPI_LENGTH {
182 let idx = acir::native_types::Witness((pcpi_start + i) as u32);
183 let val = witness
184 .get(&idx)
185 .map(|fe| super::field_conversion::fe_to_fr(fe))
186 .unwrap_or_else(Fr::zero);
187 pcpi.push(val);
188 }
189
190 let nh_slice = &pcpi[NOTE_HASHES_OFFSET..][..NOTE_HASHES_ARRAY_LEN];
192 let nh_count = nh_slice[NOTE_HASHES_ARRAY_LEN - 1]
193 .to_usize()
194 .min(MAX_NOTE_HASHES);
195 let mut note_hashes = Vec::with_capacity(nh_count);
196 for i in 0..nh_count {
197 let base = i * NOTE_HASH_LEN;
198 let value = nh_slice[base];
199 let counter = nh_slice[base + 1].to_usize() as u32;
200 if value != Fr::zero() {
201 note_hashes.push(ScopedNoteHash {
202 note_hash: NoteHash { value, counter },
203 contract_address,
204 });
205 }
206 }
207
208 let null_slice = &pcpi[NULLIFIERS_OFFSET..][..NULLIFIERS_ARRAY_LEN];
210 let null_count = null_slice[NULLIFIERS_ARRAY_LEN - 1]
211 .to_usize()
212 .min(MAX_NULLIFIERS);
213 let mut nullifiers = Vec::with_capacity(null_count);
214 for i in 0..null_count {
215 let base = i * NULLIFIER_LEN;
216 let value = null_slice[base];
217 let note_hash = null_slice[base + 1];
218 let counter = null_slice[base + 2].to_usize() as u32;
219 if value != Fr::zero() {
220 nullifiers.push(ScopedNullifier {
221 nullifier: Nullifier {
222 value,
223 note_hash,
224 counter,
225 },
226 contract_address,
227 });
228 }
229 }
230
231 let logs_slice = &pcpi[PRIVATE_LOGS_OFFSET..][..PRIVATE_LOGS_ARRAY_LEN];
233 let log_count = logs_slice[PRIVATE_LOGS_ARRAY_LEN - 1]
234 .to_usize()
235 .min(MAX_LOGS);
236 let mut logs = Vec::with_capacity(log_count);
237 for i in 0..log_count {
238 let base = i * PRIVATE_LOG_DATA_LEN;
239 let fields: Vec<Fr> = logs_slice[base..base + PRIVATE_LOG_FIELDS].to_vec();
240 let emitted_length = logs_slice[base + PRIVATE_LOG_FIELDS].to_usize() as u32;
241 let note_hash_counter = logs_slice[base + PRIVATE_LOG_FIELDS + 1].to_usize() as u32;
242 let counter = logs_slice[base + PRIVATE_LOG_DATA_LEN - 1].to_usize() as u32;
243 if emitted_length > 0 {
244 logs.push(PrivateLogData {
245 fields,
246 emitted_length,
247 note_hash_counter,
248 counter,
249 contract_address,
250 });
251 }
252 }
253
254 (note_hashes, nullifiers, logs)
255 }
256}
257
258impl<'a, N: AztecNode + 'static> PrivateExecutionOracle<'a, N> {
259 fn merge_nested_private_logs(
260 nested_logs: Vec<PrivateLogData>,
261 circuit_logs: Vec<PrivateLogData>,
262 ) -> Vec<PrivateLogData> {
263 if circuit_logs.is_empty() {
264 return nested_logs;
265 }
266
267 let mut merged = nested_logs;
268 for circuit_log in circuit_logs {
269 if let Some(existing) = merged
270 .iter_mut()
271 .find(|log| log.counter == circuit_log.counter)
272 {
273 *existing = circuit_log;
274 } else {
275 merged.push(circuit_log);
276 }
277 }
278 merged
279 }
280
281 fn try_handle_protocol_nested_private_call(
282 &mut self,
283 target_address: AztecAddress,
284 selector: aztec_core::abi::FunctionSelector,
285 encoded_args: &[Fr],
286 circuit_side_effect_counter: u32,
287 is_static: bool,
288 ) -> Result<Option<Vec<Vec<Fr>>>, Error> {
289 if is_static {
290 return Ok(None);
291 }
292
293 let publish_class_selector =
295 aztec_core::abi::FunctionSelector::from_signature("publish(Field,Field,Field)");
296 if target_address
297 == aztec_core::constants::protocol_contract_address::contract_class_registry()
298 && selector == publish_class_selector
299 {
300 return self.handle_nested_publish_class(
301 target_address,
302 selector,
303 encoded_args,
304 circuit_side_effect_counter,
305 );
306 }
307
308 let publish_instance_selector = aztec_core::abi::FunctionSelector::from_signature(
310 "publish_for_public_execution(Field,(Field),Field,(((Field,Field,bool)),((Field,Field,bool)),((Field,Field,bool)),((Field,Field,bool))),bool)",
311 );
312 if target_address
313 == aztec_core::constants::protocol_contract_address::contract_instance_registry()
314 && selector == publish_instance_selector
315 {
316 return self.handle_nested_publish_instance(
317 target_address,
318 selector,
319 encoded_args,
320 circuit_side_effect_counter,
321 );
322 }
323
324 Ok(None)
325 }
326
327 fn handle_nested_publish_class(
332 &mut self,
333 target_address: AztecAddress,
334 selector: aztec_core::abi::FunctionSelector,
335 encoded_args: &[Fr],
336 circuit_side_effect_counter: u32,
337 ) -> Result<Option<Vec<Vec<Fr>>>, Error> {
338 if encoded_args.len() < 3 {
339 return Err(Error::InvalidData(format!(
340 "nested publish args too short: {}",
341 encoded_args.len()
342 )));
343 }
344
345 let artifact_hash = encoded_args[0];
346 let private_functions_root = encoded_args[1];
347 let public_bytecode_commitment = encoded_args[2];
348 let class_id = aztec_core::hash::compute_contract_class_id(
349 artifact_hash,
350 private_functions_root,
351 public_bytecode_commitment,
352 );
353
354 let bytecode_fields = self
356 .capsules
357 .iter()
358 .find(|capsule| {
359 capsule.contract_address
360 == aztec_core::constants::protocol_contract_address::contract_class_registry()
361 && capsule.storage_slot
362 == aztec_core::constants::contract_class_registry_bytecode_capsule_slot()
363 })
364 .map(|capsule| capsule.data.clone())
365 .unwrap_or_default();
366
367 let mut emitted_fields = Vec::with_capacity(
370 aztec_core::constants::MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS + 5,
371 );
372 emitted_fields.push(aztec_core::constants::contract_class_published_magic_value());
373 emitted_fields.push(class_id);
374 emitted_fields.push(Fr::from(1u64)); emitted_fields.push(artifact_hash);
376 emitted_fields.push(private_functions_root);
377 emitted_fields.extend(bytecode_fields);
378
379 let nullifier_counter = circuit_side_effect_counter;
380 let class_log_counter = nullifier_counter.saturating_add(1);
381 let end_side_effect_counter = class_log_counter.saturating_add(1);
382
383 self.nullifiers.push(ScopedNullifier {
385 nullifier: Nullifier {
386 value: class_id,
387 note_hash: Fr::zero(),
388 counter: nullifier_counter,
389 },
390 contract_address: target_address,
391 });
392
393 self.contract_class_logs.push(CountedContractClassLog {
395 log: ContractClassLog {
396 contract_address: target_address,
397 emitted_length: emitted_fields.len() as u32,
398 fields: emitted_fields,
399 },
400 counter: class_log_counter,
401 });
402
403 self.side_effect_counter = self.side_effect_counter.max(end_side_effect_counter);
404
405 let returns_hash = aztec_core::hash::compute_var_args_hash(&[]);
406 self.execution_cache.entry(returns_hash).or_default();
407 self.nested_results.push(PrivateCallExecutionResult {
408 contract_address: target_address,
409 call_context: CallContext {
410 msg_sender: self.contract_address,
411 contract_address: target_address,
412 function_selector: selector.to_field(),
413 is_static_call: false,
414 },
415 start_side_effect_counter: nullifier_counter,
416 end_side_effect_counter,
417 min_revertible_side_effect_counter: nullifier_counter,
418 ..Default::default()
419 });
420
421 Ok(Some(vec![vec![
422 Fr::from(u64::from(end_side_effect_counter)),
423 returns_hash,
424 ]]))
425 }
426
427 fn handle_nested_publish_instance(
429 &mut self,
430 target_address: AztecAddress,
431 selector: aztec_core::abi::FunctionSelector,
432 encoded_args: &[Fr],
433 circuit_side_effect_counter: u32,
434 ) -> Result<Option<Vec<Vec<Fr>>>, Error> {
435 if encoded_args.len() < 16 {
436 return Err(Error::InvalidData(format!(
437 "nested publish_for_public_execution args too short: {}",
438 encoded_args.len()
439 )));
440 }
441
442 let salt = encoded_args[0];
443 let class_id = encoded_args[1];
444 let initialization_hash = encoded_args[2];
445 let public_keys = aztec_core::types::PublicKeys {
446 master_nullifier_public_key: aztec_core::types::Point {
447 x: encoded_args[3],
448 y: encoded_args[4],
449 is_infinite: encoded_args[5] != Fr::zero(),
450 },
451 master_incoming_viewing_public_key: aztec_core::types::Point {
452 x: encoded_args[6],
453 y: encoded_args[7],
454 is_infinite: encoded_args[8] != Fr::zero(),
455 },
456 master_outgoing_viewing_public_key: aztec_core::types::Point {
457 x: encoded_args[9],
458 y: encoded_args[10],
459 is_infinite: encoded_args[11] != Fr::zero(),
460 },
461 master_tagging_public_key: aztec_core::types::Point {
462 x: encoded_args[12],
463 y: encoded_args[13],
464 is_infinite: encoded_args[14] != Fr::zero(),
465 },
466 };
467 let universal_deploy = encoded_args[15] != Fr::zero();
468 let origin = self.sender_for_tags.unwrap_or(self.contract_address);
469 let deployer = if universal_deploy {
470 AztecAddress::zero()
471 } else {
472 origin
473 };
474
475 let inner = ContractInstance {
476 version: 1,
477 salt,
478 deployer,
479 current_contract_class_id: class_id,
480 original_contract_class_id: class_id,
481 initialization_hash,
482 public_keys: public_keys.clone(),
483 };
484 let instance_address = aztec_core::hash::compute_contract_address_from_instance(&inner)?;
485
486 let event_payload = vec![
487 aztec_core::constants::contract_instance_published_magic_value(),
488 instance_address.0,
489 Fr::from(1u64),
490 salt,
491 class_id,
492 initialization_hash,
493 public_keys.master_nullifier_public_key.x,
494 public_keys.master_nullifier_public_key.y,
495 public_keys.master_incoming_viewing_public_key.x,
496 public_keys.master_incoming_viewing_public_key.y,
497 public_keys.master_outgoing_viewing_public_key.x,
498 public_keys.master_outgoing_viewing_public_key.y,
499 public_keys.master_tagging_public_key.x,
500 public_keys.master_tagging_public_key.y,
501 deployer.0,
502 ];
503 let mut emitted_private_log_fields = event_payload;
504 emitted_private_log_fields.push(Fr::zero());
505
506 let nullifier_counter = circuit_side_effect_counter;
507 let private_log_counter = nullifier_counter.saturating_add(1);
508 let end_side_effect_counter = private_log_counter.saturating_add(1);
509
510 self.nullifiers.push(ScopedNullifier {
511 nullifier: Nullifier {
512 value: instance_address.0,
513 note_hash: Fr::zero(),
514 counter: nullifier_counter,
515 },
516 contract_address: target_address,
517 });
518 self.private_logs.push(PrivateLogData {
519 fields: emitted_private_log_fields,
520 emitted_length: 15,
521 note_hash_counter: 0,
522 counter: private_log_counter,
523 contract_address: target_address,
524 });
525 self.side_effect_counter = self.side_effect_counter.max(end_side_effect_counter);
526
527 let returns_hash = aztec_core::hash::compute_var_args_hash(&[]);
528 self.execution_cache.entry(returns_hash).or_default();
529 self.nested_results.push(PrivateCallExecutionResult {
530 contract_address: target_address,
531 call_context: CallContext {
532 msg_sender: self.contract_address,
533 contract_address: target_address,
534 function_selector: selector.to_field(),
535 is_static_call: false,
536 },
537 start_side_effect_counter: nullifier_counter,
538 end_side_effect_counter,
539 min_revertible_side_effect_counter: nullifier_counter,
540 ..Default::default()
541 });
542
543 Ok(Some(vec![vec![
544 Fr::from(u64::from(end_side_effect_counter)),
545 returns_hash,
546 ]]))
547 }
548
549 fn note_status_from_field(value: Fr) -> Result<NoteStatus, Error> {
551 match value.to_usize() as u64 {
552 1 => Ok(NoteStatus::Active),
553 2 => Ok(NoteStatus::ActiveOrNullified),
554 other => Err(Error::InvalidData(format!("unknown note status: {other}"))),
555 }
556 }
557
558 fn pack_hinted_note(note: &StoredNote) -> Result<Vec<Fr>, Error> {
559 let mut packed = note.note_data.clone();
560 packed.push(note.contract_address.0);
561 packed.push(note.owner.0);
562 packed.push(note.randomness);
563 packed.push(note.storage_slot);
564 let stage = if note.is_pending {
565 if note.note_nonce == Fr::zero() {
566 1u64
567 } else {
568 2u64
569 }
570 } else {
571 if note.note_nonce == Fr::zero() {
572 return Err(Error::InvalidData(
573 "cannot pack settled note with zero note_nonce".into(),
574 ));
575 }
576 3u64
577 };
578 packed.push(Fr::from(stage));
579 packed.push(note.note_nonce);
580 Ok(packed)
581 }
582
583 fn pack_bounded_vec_of_arrays(
584 arrays: &[Vec<Fr>],
585 max_len: usize,
586 nested_len: usize,
587 ) -> Result<Vec<Vec<Fr>>, Error> {
588 if arrays.len() > max_len {
589 return Err(Error::InvalidData(format!(
590 "bounded vec overflow: {} > {max_len}",
591 arrays.len()
592 )));
593 }
594
595 let mut flattened = Vec::with_capacity(max_len.saturating_mul(nested_len));
596 for array in arrays {
597 if array.len() != nested_len {
598 return Err(Error::InvalidData(format!(
599 "packed hinted note length mismatch: {} != {nested_len}",
600 array.len()
601 )));
602 }
603 flattened.extend_from_slice(array);
604 }
605
606 flattened.resize(max_len.saturating_mul(nested_len), Fr::zero());
607 Ok(vec![flattened, vec![Fr::from(arrays.len() as u64)]])
608 }
609
610 pub fn new(
611 node: &'a N,
612 contract_store: &'a ContractStore,
613 key_store: &'a KeyStore,
614 note_store: &'a NoteStore,
615 capsule_store: &'a CapsuleStore,
616 address_store: &'a AddressStore,
617 sender_tagging_store: &'a SenderTaggingStore,
618 block_header: serde_json::Value,
619 contract_address: AztecAddress,
620 protocol_nullifier: Fr,
621 sender_for_tags: Option<AztecAddress>,
622 scopes: Vec<AztecAddress>,
623 call_is_static: bool,
624 ) -> Self {
625 Self {
626 node,
627 contract_store,
628 key_store,
629 note_store,
630 capsule_store,
631 address_store,
632 sender_tagging_store,
633 block_header,
634 contract_address,
635 protocol_nullifier,
636 execution_cache: std::collections::HashMap::new(),
637 auth_witnesses: Vec::new(),
638 sender_for_tags,
639 scopes,
640 call_is_static,
641 side_effect_counter: 0,
642 new_notes: Vec::new(),
643 note_hashes: Vec::new(),
644 nullifiers: Vec::new(),
645 note_hash_nullifier_counter_map: std::collections::HashMap::new(),
646 consumed_db_nullifiers: std::collections::HashSet::new(),
647 private_logs: Vec::new(),
648 contract_class_logs: Vec::new(),
649 offchain_effects: Vec::new(),
650 public_call_requests: Vec::new(),
651 public_teardown_call_request: None,
652 note_hash_read_requests: Vec::new(),
653 nullifier_read_requests: Vec::new(),
654 min_revertible_side_effect_counter: 0,
655 public_function_calldata: Vec::new(),
656 nested_results: Vec::new(),
657 context_witness_prefix: Vec::new(),
658 capsules: Vec::new(),
659 }
660 }
661
662 fn ensure_mutable_context(&self) -> Result<(), Error> {
663 if self.call_is_static {
664 return Err(Error::InvalidData(
665 "Static call cannot update the state".into(),
666 ));
667 }
668 Ok(())
669 }
670
671 pub fn set_auth_witnesses(&mut self, witnesses: Vec<(Fr, Vec<Fr>)>) {
673 self.auth_witnesses = witnesses;
674 }
675
676 pub fn set_capsules(&mut self, capsules: Vec<aztec_core::tx::Capsule>) {
679 self.capsules = capsules;
680 }
681
682 pub fn seed_execution_cache(&mut self, hashed_values: &[aztec_core::tx::HashedValues]) {
689 for hv in hashed_values {
690 self.execution_cache.insert(hv.hash, hv.values.clone());
691 }
692 }
693
694 pub fn take_public_call_requests(
696 &mut self,
697 ) -> Vec<crate::execution::execution_result::PublicCallRequestData> {
698 std::mem::take(&mut self.public_call_requests)
699 }
700
701 pub fn take_public_function_calldata(&mut self) -> Vec<aztec_core::tx::HashedValues> {
703 std::mem::take(&mut self.public_function_calldata)
704 }
705
706 pub fn take_teardown_call_request(
708 &mut self,
709 ) -> Option<crate::execution::execution_result::PublicCallRequestData> {
710 self.public_teardown_call_request.take()
711 }
712
713 pub async fn handle_foreign_call(
718 &mut self,
719 name: &str,
720 args: Vec<Vec<Fr>>,
721 ) -> Result<Vec<Vec<Fr>>, Error> {
722 let stripped = name
724 .strip_prefix("private")
725 .or_else(|| name.strip_prefix("utility"))
726 .unwrap_or(name);
727
728 let handler = if !stripped.is_empty() {
730 let mut chars = stripped.chars();
731 let first = chars.next().unwrap().to_lowercase().to_string();
732 format!("{first}{}", chars.as_str())
733 } else {
734 name.to_owned()
735 };
736
737 match handler.as_str() {
738 "getSecretKey" | "getKeyValidationRequest" => self.get_secret_key(&args).await,
740 "getPublicKeysAndPartialAddress" | "tryGetPublicKeysAndPartialAddress" => {
741 self.get_public_keys_and_partial_address(&args).await
742 }
743
744 "getNotes" => self.get_notes(&args).await,
746 "checkNoteHashExists" => self.check_note_hash_exists(&args).await,
747 "notifyCreatedNote" => self.notify_created_note(&args),
748 "notifyNullifiedNote" => self.notify_nullified_note(&args),
749 "notifyCreatedNullifier" => self.notify_created_nullifier(&args),
750 "isNullifierPending" => self.is_nullifier_pending(&args),
751
752 "getPublicStorageAt" | "storageRead" => self.get_public_storage_at(&args).await,
754 "getContractInstance" => self.get_contract_instance(&args).await,
755
756 "getCapsule" | "loadCapsule" => self.get_capsule(&args).await,
758 "storeCapsule" => self.store_capsule(&args).await,
759
760 "getBlockHeader" => self.get_block_header(&args).await,
762
763 "emitNote" => self.notify_created_note(&args),
765 "emitNullifier" => self.notify_created_nullifier(&args),
766 "emitPrivateLog" | "emitEncryptedLog" => self.emit_private_log(&args),
767 "notifyCreatedContractClassLog" => self.emit_contract_class_log(&args),
768
769 "storeInExecutionCache" => self.store_in_execution_cache(&args),
771 "loadFromExecutionCache" => self.load_from_execution_cache(&args),
772
773 "getAuthWitness" => self.get_auth_witness(&args),
775
776 "notifyEnqueuedPublicFunctionCall" | "enqueuePublicFunctionCall" => {
778 self.enqueue_public_function_call(&args, false)
779 }
780 "notifySetPublicTeardownFunctionCall" => self.enqueue_public_function_call(&args, true),
781
782 "notifySetMinRevertibleSideEffectCounter" => {
784 if let Some(counter) = args.first().and_then(|v| v.first()) {
785 self.min_revertible_side_effect_counter = counter.to_usize() as u32;
786 }
787 Ok(vec![])
788 }
789 "isSideEffectCounterRevertible" => {
790 let counter = args
791 .first()
792 .and_then(|v| v.first())
793 .map(|f| f.to_usize() as u32)
794 .unwrap_or(0);
795 let revertible = counter >= self.min_revertible_side_effect_counter;
796 Ok(vec![vec![Fr::from(revertible)]])
797 }
798
799 "getSenderForTags" => self.get_sender_for_tags(),
801 "setSenderForTags" => self.set_sender_for_tags(&args),
802 "getNextAppTagAsSender" => self.get_next_app_tag_as_sender(&args).await,
803
804 "getRandomField" => Ok(vec![vec![Fr::random()]]),
806 "assertCompatibleOracleVersion" => Ok(vec![]),
807 "log" => {
808 let _level = args
810 .first()
811 .and_then(|v| v.first())
812 .map(|f| f.to_usize())
813 .unwrap_or(0);
814 tracing::debug!("noir log oracle call");
815 Ok(vec![])
816 }
817 "getUtilityContext" => Ok(vec![]),
818 "aes128Decrypt" => Err(Error::InvalidData("aes128Decrypt not implemented".into())),
819 "getSharedSecret" => Err(Error::InvalidData("getSharedSecret not implemented".into())),
820 "emitOffchainEffect" => {
821 let data = args.first().cloned().unwrap_or_default();
822 self.offchain_effects.push(data);
823 Ok(vec![])
824 }
825
826 "getNoteHashMembershipWitness" => self.get_note_hash_membership_witness(&args).await,
828 "getNullifierMembershipWitness" => self.get_nullifier_membership_witness(&args).await,
829 "getPublicDataWitness" => self.get_public_data_witness(&args).await,
830 "getBlockHashMembershipWitness" => self.get_block_hash_membership_witness(&args).await,
831 "getL1ToL2MembershipWitness" | "utilityGetL1ToL2MembershipWitness" => {
832 self.get_l1_to_l2_membership_witness(&args).await
833 }
834
835 "fetchTaggedLogs" | "bulkRetrieveLogs" => Ok(vec![]),
837 "validateAndStoreEnqueuedNotesAndEvents" => Ok(vec![]),
838
839 "callPrivateFunction" => self.call_private_function(&args).await,
841
842 "checkNullifierExists" => self.check_nullifier_exists(&args).await,
844
845 _ => {
846 tracing::error!(
847 oracle = name,
848 handler = handler.as_str(),
849 "unsupported oracle call"
850 );
851 Err(Error::InvalidData(format!(
852 "unsupported oracle call: '{name}' (handler: '{handler}'). \
853 All production oracle calls must be implemented."
854 )))
855 }
856 }
857 }
858
859 async fn get_secret_key(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
865 use aztec_core::hash::poseidon2_hash;
866
867 let pk_m_hash = *args.first().and_then(|v| v.first()).ok_or_else(|| {
868 Error::InvalidData("getKeyValidationRequest: missing pk_m_hash".into())
869 })?;
870
871 if !self.scopes.is_empty() {
873 let mut key_in_scope = false;
874 for scope in &self.scopes {
875 if let Some(complete) = self.address_store.get(scope).await? {
876 let pk = &complete.public_keys;
877 for point in [
878 &pk.master_nullifier_public_key,
879 &pk.master_incoming_viewing_public_key,
880 &pk.master_outgoing_viewing_public_key,
881 &pk.master_tagging_public_key,
882 ] {
883 let hash = poseidon2_hash(&[point.x, point.y, Fr::from(point.is_infinite)]);
884 if hash == pk_m_hash {
885 key_in_scope = true;
886 break;
887 }
888 }
889 if key_in_scope {
890 break;
891 }
892 }
893 }
894 if !key_in_scope {
895 return Err(Error::InvalidData("Key validation request denied".into()));
896 }
897 }
898
899 match self
900 .key_store
901 .get_key_validation_request(&pk_m_hash, &self.contract_address)
902 .await?
903 {
904 Some((pk_m, sk_app)) => Ok(vec![
905 vec![pk_m.x],
906 vec![pk_m.y],
907 vec![Fr::from(pk_m.is_infinite)],
908 vec![sk_app],
909 ]),
910 None => Ok(vec![
911 vec![Fr::zero()],
912 vec![Fr::zero()],
913 vec![Fr::zero()],
914 vec![Fr::zero()],
915 ]),
916 }
917 }
918
919 async fn get_public_keys_and_partial_address(
921 &self,
922 args: &[Vec<Fr>],
923 ) -> Result<Vec<Vec<Fr>>, Error> {
924 let address = AztecAddress(
925 *args
926 .first()
927 .and_then(|v| v.first())
928 .ok_or_else(|| Error::InvalidData("missing address arg".into()))?,
929 );
930
931 let Some(complete) = self.address_store.get(&address).await? else {
932 tracing::debug!(
933 queried_address = %address,
934 "getPublicKeysAndPartialAddress: address not found in store"
935 );
936 return Ok(vec![vec![Fr::zero()], vec![Fr::zero(); 13]]);
937 };
938
939 let pk = &complete.public_keys;
940 let mut fields = Vec::with_capacity(13);
941 for point in [
942 &pk.master_nullifier_public_key,
943 &pk.master_incoming_viewing_public_key,
944 &pk.master_outgoing_viewing_public_key,
945 &pk.master_tagging_public_key,
946 ] {
947 fields.push(point.x);
948 fields.push(point.y);
949 fields.push(Fr::from(point.is_infinite));
950 }
951 fields.push(complete.partial_address);
952 Ok(vec![vec![Fr::from(true)], fields])
953 }
954
955 async fn get_notes(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
956 let owner = match (
957 args.first()
958 .and_then(|v| v.first())
959 .copied()
960 .unwrap_or(Fr::zero()),
961 args.get(1)
962 .and_then(|v| v.first())
963 .copied()
964 .unwrap_or(Fr::zero()),
965 ) {
966 (flag, value) if flag != Fr::zero() => Some(AztecAddress(value)),
967 _ => None,
968 };
969 let storage_slot = args
970 .get(2)
971 .and_then(|v| v.first())
972 .copied()
973 .ok_or_else(|| Error::InvalidData("getNotes: missing storage_slot".into()))?;
974 let limit = args
975 .get(13)
976 .and_then(|v| v.first())
977 .copied()
978 .unwrap_or(Fr::zero())
979 .to_usize();
980 let offset = args
981 .get(14)
982 .and_then(|v| v.first())
983 .copied()
984 .unwrap_or(Fr::zero())
985 .to_usize();
986 let status = Self::note_status_from_field(
987 args.get(15)
988 .and_then(|v| v.first())
989 .copied()
990 .unwrap_or(Fr::zero()),
991 )?;
992 let max_notes = args
993 .get(16)
994 .and_then(|v| v.first())
995 .copied()
996 .unwrap_or(Fr::zero())
997 .to_usize();
998 let packed_hinted_note_length = args
999 .get(17)
1000 .and_then(|v| v.first())
1001 .copied()
1002 .unwrap_or(Fr::zero())
1003 .to_usize();
1004
1005 let mut notes = self
1006 .note_store
1007 .get_notes(&NoteFilter {
1008 contract_address: Some(self.contract_address),
1009 storage_slot: Some(storage_slot),
1010 owner,
1011 status,
1012 ..Default::default()
1013 })
1014 .await?;
1015
1016 let mut nullified_hash_counts: std::collections::HashMap<Fr, usize> =
1019 std::collections::HashMap::new();
1020 for nh_counter in self.note_hash_nullifier_counter_map.keys() {
1021 if let Some(nh) = self
1022 .note_hashes
1023 .iter()
1024 .find(|h| h.note_hash.counter == *nh_counter)
1025 {
1026 *nullified_hash_counts.entry(nh.note_hash.value).or_insert(0) += 1;
1027 }
1028 }
1029
1030 notes.retain(|n| {
1034 if n.siloed_nullifier.is_zero() {
1035 return true;
1036 }
1037 !self.consumed_db_nullifiers.contains(&n.siloed_nullifier)
1038 });
1039
1040 let mut consumed_hash_counts: std::collections::HashMap<Fr, usize> =
1044 std::collections::HashMap::new();
1045 for pending in &self.new_notes {
1046 if pending.contract_address != self.contract_address {
1047 continue;
1048 }
1049 if pending.storage_slot != storage_slot {
1050 continue;
1051 }
1052 if let Some(owner_addr) = owner {
1053 if pending.owner != owner_addr {
1054 continue;
1055 }
1056 }
1057 if let Some(&max_nullified) = nullified_hash_counts.get(&pending.note_hash) {
1060 let already_consumed = consumed_hash_counts.entry(pending.note_hash).or_insert(0);
1061 if *already_consumed < max_nullified {
1062 *already_consumed += 1;
1063 continue;
1064 }
1065 }
1066 notes.push(StoredNote {
1067 contract_address: pending.contract_address,
1068 owner: pending.owner,
1069 storage_slot: pending.storage_slot,
1070 randomness: pending.randomness,
1071 note_nonce: Fr::zero(), note_hash: pending.note_hash,
1073 siloed_nullifier: Fr::zero(),
1074 note_data: pending.note_items.clone(),
1075 nullified: false,
1076 is_pending: true,
1077 nullification_block_number: None,
1078 leaf_index: None,
1079 block_number: None,
1080 tx_index_in_block: None,
1081 note_index_in_tx: None,
1082 scopes: vec![pending.owner],
1083 });
1084 }
1085
1086 let selects = super::pick_notes::parse_select_clauses(args);
1088 notes = super::pick_notes::select_notes(notes, &selects);
1089
1090 if offset >= notes.len() {
1091 notes.clear();
1092 } else if offset > 0 {
1093 notes = notes.split_off(offset);
1094 }
1095
1096 if limit > 0 && notes.len() > limit {
1097 notes.truncate(limit);
1098 }
1099 if notes.len() > max_notes {
1100 notes.truncate(max_notes);
1101 }
1102
1103 let packed = notes
1104 .iter()
1105 .map(Self::pack_hinted_note)
1106 .collect::<Result<Vec<_>, _>>()?;
1107
1108 Self::pack_bounded_vec_of_arrays(&packed, max_notes, packed_hinted_note_length)
1109 }
1110
1111 async fn check_note_hash_exists(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1112 let note_hash = args
1113 .first()
1114 .and_then(|v| v.first())
1115 .ok_or_else(|| Error::InvalidData("missing note_hash".into()))?;
1116 let mut exists = self
1118 .note_store
1119 .has_note(&self.contract_address, note_hash)
1120 .await?;
1121 if !exists {
1124 exists = self
1125 .note_hashes
1126 .iter()
1127 .any(|nh| nh.note_hash.value == *note_hash);
1128 }
1129 Ok(vec![vec![Fr::from(exists)]])
1130 }
1131
1132 fn notify_created_note(&mut self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1133 self.ensure_mutable_context()?;
1134 let owner = args
1135 .first()
1136 .and_then(|v| v.first())
1137 .copied()
1138 .unwrap_or(Fr::zero());
1139 let storage_slot = args
1140 .get(1)
1141 .and_then(|v| v.first())
1142 .copied()
1143 .unwrap_or(Fr::zero());
1144 let randomness = args
1145 .get(2)
1146 .and_then(|v| v.first())
1147 .copied()
1148 .unwrap_or(Fr::zero());
1149 let note_type_id = args
1150 .get(3)
1151 .and_then(|v| v.first())
1152 .copied()
1153 .unwrap_or(Fr::zero());
1154 let note_items = args.get(4).cloned().unwrap_or_default();
1155 let note_hash = args
1156 .get(5)
1157 .and_then(|v| v.first())
1158 .copied()
1159 .unwrap_or(Fr::zero());
1160 let counter = args
1161 .get(6)
1162 .and_then(|v| v.first())
1163 .map(|f| f.to_usize() as u32)
1164 .unwrap_or_else(|| {
1165 self.side_effect_counter += 1;
1166 self.side_effect_counter
1167 });
1168
1169 self.new_notes.push(NoteAndSlot {
1170 contract_address: self.contract_address,
1171 owner: AztecAddress(owner),
1172 storage_slot,
1173 randomness,
1174 note_type_id,
1175 note_items: note_items.clone(),
1176 note_hash,
1177 counter,
1178 });
1179
1180 self.note_hashes.push(ScopedNoteHash {
1181 note_hash: NoteHash {
1182 value: note_hash,
1183 counter,
1184 },
1185 contract_address: self.contract_address,
1186 });
1187
1188 Ok(vec![])
1189 }
1190
1191 fn notify_nullified_note(&mut self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1192 self.ensure_mutable_context()?;
1193 let inner_nullifier = args
1194 .first()
1195 .and_then(|v| v.first())
1196 .copied()
1197 .unwrap_or(Fr::zero());
1198 let note_hash = args
1199 .get(1)
1200 .and_then(|v| v.first())
1201 .copied()
1202 .unwrap_or(Fr::zero());
1203 let counter = args
1204 .get(2)
1205 .and_then(|v| v.first())
1206 .map(|f| f.to_usize() as u32)
1207 .unwrap_or_else(|| {
1208 self.side_effect_counter += 1;
1209 self.side_effect_counter
1210 });
1211
1212 let siloed = aztec_core::hash::silo_nullifier(&self.contract_address, &inner_nullifier);
1214 self.consumed_db_nullifiers.insert(siloed);
1215
1216 self.nullifiers.push(ScopedNullifier {
1217 nullifier: Nullifier {
1218 value: inner_nullifier,
1219 note_hash,
1220 counter,
1221 },
1222 contract_address: self.contract_address,
1223 });
1224
1225 if note_hash != Fr::zero() {
1227 if let Some(nh) = self
1228 .note_hashes
1229 .iter()
1230 .find(|nh| nh.note_hash.value == note_hash)
1231 {
1232 self.note_hash_nullifier_counter_map
1233 .insert(nh.note_hash.counter, counter);
1234 }
1235 }
1236
1237 Ok(vec![])
1238 }
1239
1240 fn notify_created_nullifier(&mut self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1241 self.ensure_mutable_context()?;
1242 let inner_nullifier = args
1243 .first()
1244 .and_then(|v| v.first())
1245 .copied()
1246 .unwrap_or(Fr::zero());
1247 self.side_effect_counter += 1;
1248 let counter = self.side_effect_counter;
1249
1250 self.nullifiers.push(ScopedNullifier {
1251 nullifier: Nullifier {
1252 value: inner_nullifier,
1253 note_hash: Fr::zero(),
1254 counter,
1255 },
1256 contract_address: self.contract_address,
1257 });
1258
1259 Ok(vec![])
1260 }
1261
1262 fn is_nullifier_pending(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1263 let nullifier = args
1264 .first()
1265 .and_then(|v| v.first())
1266 .ok_or_else(|| Error::InvalidData("missing nullifier".into()))?;
1267 let pending = self
1268 .nullifiers
1269 .iter()
1270 .any(|n| n.nullifier.value == *nullifier);
1271 Ok(vec![vec![Fr::from(pending)]])
1272 }
1273
1274 async fn get_public_storage_at(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1275 fn slot_with_offset(start_slot: Fr, offset: usize) -> Fr {
1276 let mut bytes = start_slot.to_be_bytes();
1277 let mut carry = offset as u128;
1278 for byte in bytes.iter_mut().rev() {
1279 if carry == 0 {
1280 break;
1281 }
1282 let sum = u128::from(*byte) + (carry & 0xff);
1283 *byte = (sum & 0xff) as u8;
1284 carry = (carry >> 8) + (sum >> 8);
1285 }
1286 Fr::from(bytes)
1287 }
1288
1289 let (block_hash, contract, start_slot, number_of_elements) = if args.len() >= 4 {
1290 let block_hash = args
1291 .first()
1292 .and_then(|v| v.first())
1293 .copied()
1294 .unwrap_or_else(Fr::zero);
1295 let contract = args
1296 .get(1)
1297 .and_then(|v| v.first())
1298 .ok_or_else(|| Error::InvalidData("missing contract address".into()))?;
1299 let start_slot = args
1300 .get(2)
1301 .and_then(|v| v.first())
1302 .ok_or_else(|| Error::InvalidData("missing storage slot".into()))?;
1303 let count = args
1304 .get(3)
1305 .and_then(|v| v.first())
1306 .copied()
1307 .unwrap_or_else(Fr::zero)
1308 .to_usize();
1309 (Some(block_hash), contract, start_slot, count.max(1))
1310 } else {
1311 let contract = args
1312 .first()
1313 .and_then(|v| v.first())
1314 .ok_or_else(|| Error::InvalidData("missing contract address".into()))?;
1315 let slot = args
1316 .get(1)
1317 .and_then(|v| v.first())
1318 .ok_or_else(|| Error::InvalidData("missing storage slot".into()))?;
1319 (None, contract, slot, 1)
1320 };
1321
1322 let contract_addr = AztecAddress(*contract);
1323 let mut values = Vec::with_capacity(number_of_elements);
1324 for offset in 0..number_of_elements {
1325 let slot = slot_with_offset(*start_slot, offset);
1326 let value = match block_hash.as_ref() {
1327 Some(block_hash) => {
1328 self.node
1329 .get_public_storage_at_by_hash(block_hash, &contract_addr, &slot)
1330 .await?
1331 }
1332 None => {
1333 self.node
1334 .get_public_storage_at(0, &contract_addr, &slot)
1335 .await?
1336 }
1337 };
1338 values.push(value);
1339 }
1340
1341 Ok(vec![values])
1342 }
1343
1344 async fn get_contract_instance(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1345 let address = args
1346 .first()
1347 .and_then(|v| v.first())
1348 .ok_or_else(|| Error::InvalidData("missing address".into()))?;
1349 let addr = AztecAddress(*address);
1350
1351 let inst = self.contract_store.get_instance(&addr).await?;
1353 let inst = match inst {
1354 Some(i) => Some(i),
1355 None => self.node.get_contract(&addr).await?,
1356 };
1357
1358 match inst {
1359 Some(inst) => Ok(contract_instance_to_fields(&inst.inner)),
1360 None => Ok(vec![vec![Fr::zero()]; 16]),
1361 }
1362 }
1363
1364 async fn get_capsule(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1365 let contract_id = args
1366 .first()
1367 .and_then(|v| v.first())
1368 .ok_or_else(|| Error::InvalidData("missing capsule contract id".into()))?;
1369 match self.capsule_store.pop(contract_id).await? {
1370 Some(capsule) => Ok(capsule),
1371 None => Err(Error::InvalidData("no capsule available".into())),
1372 }
1373 }
1374
1375 async fn store_capsule(&self, _args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1376 Ok(vec![])
1378 }
1379
1380 async fn get_block_header(&self, _args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1381 Ok(vec![])
1382 }
1383
1384 fn emit_private_log(&mut self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1385 self.ensure_mutable_context()?;
1386 let fields = args.first().cloned().unwrap_or_default();
1387 let emitted_length = fields.len() as u32;
1388 self.side_effect_counter += 1;
1389 let counter = self.side_effect_counter;
1390
1391 self.private_logs.push(PrivateLogData {
1392 fields,
1393 emitted_length,
1394 note_hash_counter: 0,
1395 counter,
1396 contract_address: self.contract_address,
1397 });
1398 Ok(vec![])
1399 }
1400
1401 fn emit_contract_class_log(&mut self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1402 self.ensure_mutable_context()?;
1403 let contract_addr = args
1404 .first()
1405 .and_then(|v| v.first())
1406 .copied()
1407 .unwrap_or(Fr::zero());
1408 let fields = args.get(1).cloned().unwrap_or_default();
1409 let emitted_length = args
1410 .get(2)
1411 .and_then(|v| v.first())
1412 .map(|f| f.to_usize() as u32)
1413 .unwrap_or(fields.len() as u32);
1414 let counter = args
1415 .get(3)
1416 .and_then(|v| v.first())
1417 .map(|f| f.to_usize() as u32)
1418 .unwrap_or_else(|| {
1419 self.side_effect_counter += 1;
1420 self.side_effect_counter
1421 });
1422
1423 self.contract_class_logs.push(CountedContractClassLog {
1424 log: ContractClassLog {
1425 contract_address: AztecAddress(contract_addr),
1426 fields,
1427 emitted_length,
1428 },
1429 counter,
1430 });
1431 Ok(vec![])
1432 }
1433
1434 fn store_in_execution_cache(&mut self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1435 let values = args.first().cloned().unwrap_or_default();
1436 let hash = args
1437 .get(1)
1438 .and_then(|v| v.first())
1439 .copied()
1440 .unwrap_or(Fr::zero());
1441 self.execution_cache.insert(hash, values);
1442 Ok(vec![])
1443 }
1444
1445 pub fn get_execution_cache_entry(&self, hash: &Fr) -> Option<Vec<Fr>> {
1447 self.execution_cache.get(hash).cloned()
1448 }
1449
1450 fn load_from_execution_cache(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1451 let hash = args
1452 .first()
1453 .and_then(|v| v.first())
1454 .ok_or_else(|| Error::InvalidData("missing hash".into()))?;
1455 match self.execution_cache.get(hash) {
1456 Some(values) => Ok(vec![values.clone()]),
1457 None => Err(Error::InvalidData(
1458 "value not found in execution cache".into(),
1459 )),
1460 }
1461 }
1462
1463 fn get_auth_witness(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1464 let message_hash = args
1465 .first()
1466 .and_then(|v| v.first())
1467 .ok_or_else(|| Error::InvalidData("missing message hash".into()))?;
1468 for (hash, witness) in &self.auth_witnesses {
1469 if hash == message_hash {
1470 return Ok(vec![witness.clone()]);
1471 }
1472 }
1473 Err(Error::InvalidData(format!(
1474 "Unknown auth witness for message hash {message_hash}"
1475 )))
1476 }
1477
1478 fn enqueue_public_function_call(
1479 &mut self,
1480 args: &[Vec<Fr>],
1481 is_teardown: bool,
1482 ) -> Result<Vec<Vec<Fr>>, Error> {
1483 let contract_addr = args
1484 .first()
1485 .and_then(|v| v.first())
1486 .copied()
1487 .unwrap_or(Fr::zero());
1488 let calldata_hash = args
1489 .get(1)
1490 .and_then(|v| v.first())
1491 .copied()
1492 .unwrap_or(Fr::zero());
1493 self.side_effect_counter += 1;
1494 let counter = args
1495 .get(2)
1496 .and_then(|v| v.first())
1497 .map(|f| f.to_usize() as u32)
1498 .unwrap_or(self.side_effect_counter);
1499 let is_static = args
1500 .get(3)
1501 .and_then(|v| v.first())
1502 .map(|f| *f != Fr::zero())
1503 .unwrap_or(false);
1504
1505 let request = PublicCallRequestData {
1506 contract_address: AztecAddress(contract_addr),
1507 msg_sender: self.contract_address,
1508 is_static_call: is_static,
1509 calldata_hash,
1510 counter,
1511 };
1512
1513 if let Some(calldata) = self.execution_cache.get(&calldata_hash).cloned() {
1517 self.public_function_calldata
1518 .push(HashedValues::from_calldata(calldata));
1519 }
1520
1521 if is_teardown {
1522 self.public_teardown_call_request = Some(request);
1523 } else {
1524 self.public_call_requests.push(request);
1525 }
1526 Ok(vec![])
1527 }
1528
1529 async fn get_l1_to_l2_membership_witness(
1530 &self,
1531 args: &[Vec<Fr>],
1532 ) -> Result<Vec<Vec<Fr>>, Error> {
1533 let msg_hash = args
1535 .get(1)
1536 .and_then(|v| v.first())
1537 .ok_or_else(|| Error::InvalidData("missing L1→L2 message hash".into()))?;
1538
1539 let block_number = self
1543 .block_header
1544 .pointer("/globalVariables/blockNumber")
1545 .and_then(|v| v.as_u64())
1546 .or_else(|| {
1547 self.block_header
1548 .get("blockNumber")
1549 .and_then(|v| v.as_u64())
1550 })
1551 .unwrap_or(0);
1552 let witness_json = self
1553 .node
1554 .get_l1_to_l2_message_membership_witness(block_number, msg_hash)
1555 .await?
1556 .ok_or_else(|| {
1557 Error::InvalidData(format!(
1558 "L1→L2 message membership witness not found for {msg_hash} at block {block_number}"
1559 ))
1560 })?;
1561
1562 let leaf_index = witness_json
1565 .get(0)
1566 .and_then(|v| v.as_str())
1567 .and_then(|s| {
1568 Fr::from_hex(s)
1570 .ok()
1571 .or_else(|| s.parse::<u64>().ok().map(Fr::from))
1572 })
1573 .or_else(|| witness_json.get(0).and_then(|v| v.as_u64()).map(Fr::from))
1574 .unwrap_or(Fr::zero());
1575
1576 let mut sibling_path = Vec::with_capacity(aztec_core::constants::L1_TO_L2_MSG_TREE_HEIGHT);
1580 if let Some(path_arr) = witness_json.get(1).and_then(|v| v.as_array()) {
1581 for node in path_arr {
1583 let fr = if let Some(s) = node.as_str() {
1584 Fr::from_hex(s).unwrap_or(Fr::zero())
1585 } else {
1586 Fr::zero()
1587 };
1588 sibling_path.push(fr);
1589 }
1590 } else if let Some(b64_str) = witness_json.get(1).and_then(|v| v.as_str()) {
1591 use base64::Engine;
1593 if let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(b64_str) {
1594 let data = if bytes.len() >= 4 {
1596 &bytes[4..]
1597 } else {
1598 &bytes
1599 };
1600 for chunk in data.chunks(32) {
1601 if chunk.len() == 32 {
1602 let mut arr = [0u8; 32];
1603 arr.copy_from_slice(chunk);
1604 sibling_path.push(Fr::from(arr));
1605 }
1606 }
1607 }
1608 }
1609 sibling_path.resize(aztec_core::constants::L1_TO_L2_MSG_TREE_HEIGHT, Fr::zero());
1611
1612 Ok(vec![vec![leaf_index], sibling_path])
1614 }
1615
1616 async fn get_note_hash_membership_witness(
1617 &self,
1618 args: &[Vec<Fr>],
1619 ) -> Result<Vec<Vec<Fr>>, Error> {
1620 let note_hash = args
1621 .get(1)
1622 .and_then(|v| v.first())
1623 .ok_or_else(|| Error::InvalidData("missing note hash".into()))?;
1624 let _witness = self
1625 .node
1626 .get_note_hash_membership_witness(0, note_hash)
1627 .await?;
1628 Ok(vec![])
1630 }
1631
1632 async fn get_nullifier_membership_witness(
1633 &self,
1634 args: &[Vec<Fr>],
1635 ) -> Result<Vec<Vec<Fr>>, Error> {
1636 let nullifier = args
1637 .get(1)
1638 .and_then(|v| v.first())
1639 .ok_or_else(|| Error::InvalidData("missing nullifier".into()))?;
1640 let _witness = self
1641 .node
1642 .get_nullifier_membership_witness(0, nullifier)
1643 .await?;
1644 Ok(vec![])
1645 }
1646
1647 async fn get_public_data_witness(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1648 fn fr_at(value: &serde_json::Value, path: &str) -> Result<Fr, Error> {
1649 let raw = value.pointer(path).ok_or_else(|| {
1650 Error::InvalidData(format!("public data witness missing field at {path}"))
1651 })?;
1652 if let Some(s) = raw.as_str() {
1653 return parse_field_string(s).map_err(|_| {
1654 Error::InvalidData(format!(
1655 "public data witness field at {path} has unsupported string value: {s}"
1656 ))
1657 });
1658 }
1659 if let Some(n) = raw.as_u64() {
1660 return Ok(Fr::from(n));
1661 }
1662 Err(Error::InvalidData(format!(
1663 "public data witness field at {path} has unsupported shape: {raw:?}"
1664 )))
1665 }
1666
1667 let block_hash = args
1668 .first()
1669 .and_then(|v| v.first())
1670 .copied()
1671 .unwrap_or_else(Fr::zero);
1672 let leaf_slot = args
1673 .get(1)
1674 .and_then(|v| v.first())
1675 .ok_or_else(|| Error::InvalidData("missing leaf slot".into()))?;
1676 let witness = self
1677 .node
1678 .get_public_data_witness_by_hash(&block_hash, leaf_slot)
1679 .await?;
1680 let Some(witness) = witness else {
1681 return Ok(vec![
1682 vec![Fr::zero()],
1683 vec![Fr::zero()],
1684 vec![Fr::zero()],
1685 vec![Fr::zero()],
1686 vec![Fr::zero()],
1687 vec![Fr::zero(); aztec_core::constants::PUBLIC_DATA_TREE_HEIGHT],
1688 ]);
1689 };
1690
1691 let sibling_path = match witness.pointer("/siblingPath") {
1692 Some(serde_json::Value::Array(entries)) => entries
1693 .iter()
1694 .map(|entry| {
1695 if let Some(s) = entry.as_str() {
1696 parse_field_string(s).map_err(|_| {
1697 Error::InvalidData(format!(
1698 "public data witness siblingPath entry has unsupported string value: {s}"
1699 ))
1700 })
1701 } else if let Some(n) = entry.as_u64() {
1702 Ok(Fr::from(n))
1703 } else {
1704 Err(Error::InvalidData(format!(
1705 "public data witness siblingPath entry has unsupported shape: {entry:?}"
1706 )))
1707 }
1708 })
1709 .collect::<Result<Vec<_>, _>>()?,
1710 Some(serde_json::Value::String(encoded)) => {
1711 decode_base64_sibling_path(encoded)?
1712 }
1713 _ => {
1714 return Err(Error::InvalidData(
1715 "public data witness missing siblingPath".into(),
1716 ))
1717 }
1718 };
1719
1720 let mut sibling_path = sibling_path;
1721 sibling_path.resize(aztec_core::constants::PUBLIC_DATA_TREE_HEIGHT, Fr::zero());
1722 sibling_path.truncate(aztec_core::constants::PUBLIC_DATA_TREE_HEIGHT);
1723
1724 Ok(vec![
1725 vec![fr_at(&witness, "/index")?],
1726 vec![fr_at(&witness, "/leafPreimage/leaf/slot")?],
1727 vec![fr_at(&witness, "/leafPreimage/leaf/value")?],
1728 vec![fr_at(&witness, "/leafPreimage/nextKey")?],
1729 vec![fr_at(&witness, "/leafPreimage/nextIndex")?],
1730 sibling_path,
1731 ])
1732 }
1733
1734 async fn get_block_hash_membership_witness(
1735 &self,
1736 args: &[Vec<Fr>],
1737 ) -> Result<Vec<Vec<Fr>>, Error> {
1738 let block_hash = args
1739 .get(1)
1740 .and_then(|v| v.first())
1741 .ok_or_else(|| Error::InvalidData("missing block hash".into()))?;
1742 let _witness = self
1743 .node
1744 .get_block_hash_membership_witness(0, block_hash)
1745 .await?;
1746 Ok(vec![])
1747 }
1748
1749 fn get_sender_for_tags(&self) -> Result<Vec<Vec<Fr>>, Error> {
1750 let (is_some, sender) = match self.sender_for_tags {
1751 Some(sender) => (Fr::one(), sender.0),
1752 None => (Fr::zero(), Fr::zero()),
1753 };
1754 Ok(vec![vec![is_some], vec![sender]])
1755 }
1756
1757 fn set_sender_for_tags(&mut self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1758 let sender = args
1759 .first()
1760 .and_then(|v| v.first())
1761 .copied()
1762 .ok_or_else(|| Error::InvalidData("missing sender_for_tags".into()))?;
1763 self.sender_for_tags = Some(AztecAddress(sender));
1764 Ok(vec![])
1765 }
1766
1767 async fn get_next_app_tag_as_sender(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1768 use aztec_core::hash::poseidon2_hash;
1769
1770 let sender = AztecAddress(
1771 *args
1772 .first()
1773 .and_then(|v| v.first())
1774 .ok_or_else(|| Error::InvalidData("missing sender".into()))?,
1775 );
1776 let recipient = AztecAddress(
1777 *args
1778 .get(1)
1779 .and_then(|v| v.first())
1780 .ok_or_else(|| Error::InvalidData("missing recipient".into()))?,
1781 );
1782
1783 let Some(sender_complete) = self.address_store.get(&sender).await? else {
1785 return Err(Error::InvalidData(format!(
1786 "sender {sender} not in address store"
1787 )));
1788 };
1789 let pk_hash = sender_complete.public_keys.hash();
1790 let ivsk = self
1791 .key_store
1792 .get_master_incoming_viewing_secret_key(&pk_hash)
1793 .await?
1794 .ok_or_else(|| Error::InvalidData(format!("ivsk not found for sender {sender}")))?;
1795 let secret = super::utility_oracle::compute_directional_tagging_secret(
1796 &sender_complete,
1797 ivsk,
1798 &recipient,
1799 &self.contract_address,
1800 &recipient,
1801 )?;
1802
1803 let index = self.sender_tagging_store.get_next_index(&secret).await?;
1805
1806 let tag = poseidon2_hash(&[secret, Fr::from(index)]);
1807 Ok(vec![vec![tag]])
1808 }
1809
1810 async fn check_nullifier_exists(&self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1811 let inner_nullifier = args
1812 .first()
1813 .and_then(|v| v.first())
1814 .ok_or_else(|| Error::InvalidData("missing nullifier".into()))?;
1815 let nullifier = aztec_core::hash::silo_nullifier(&self.contract_address, inner_nullifier);
1818 if self
1820 .nullifiers
1821 .iter()
1822 .any(|n| n.nullifier.value == nullifier)
1823 {
1824 return Ok(vec![vec![Fr::from(true)]]);
1825 }
1826 let witness = self
1828 .node
1829 .get_nullifier_membership_witness(0, &nullifier)
1830 .await?;
1831 let exists = witness.is_some();
1832 Ok(vec![vec![Fr::from(exists)]])
1833 }
1834
1835 async fn call_private_function(&mut self, args: &[Vec<Fr>]) -> Result<Vec<Vec<Fr>>, Error> {
1844 let target_address = AztecAddress(
1845 *args
1846 .first()
1847 .and_then(|v| v.first())
1848 .ok_or_else(|| Error::InvalidData("missing target address".into()))?,
1849 );
1850 let selector_field = *args
1851 .get(1)
1852 .and_then(|v| v.first())
1853 .ok_or_else(|| Error::InvalidData("missing function selector".into()))?;
1854 let args_hash = *args
1855 .get(2)
1856 .and_then(|v| v.first())
1857 .ok_or_else(|| Error::InvalidData("missing args hash".into()))?;
1858 let circuit_side_effect_counter = args
1859 .get(3)
1860 .and_then(|v| v.first())
1861 .map(|f| f.to_usize() as u32)
1862 .unwrap_or(self.side_effect_counter);
1863 let is_static = args
1864 .get(4)
1865 .and_then(|v| v.first())
1866 .map(|f| *f != Fr::zero())
1867 .unwrap_or(false);
1868
1869 let selector = aztec_core::abi::FunctionSelector::from_field(selector_field);
1872 let cached_args = self
1873 .execution_cache
1874 .get(&args_hash)
1875 .cloned()
1876 .unwrap_or_default();
1877
1878 if let Some(result) = self.try_handle_protocol_nested_private_call(
1879 target_address,
1880 selector,
1881 &cached_args,
1882 circuit_side_effect_counter,
1883 is_static,
1884 )? {
1885 return Ok(result);
1886 }
1887
1888 let instance = self
1890 .contract_store
1891 .get_instance(&target_address)
1892 .await?
1893 .ok_or_else(|| {
1894 Error::InvalidData(format!("nested call: contract not found: {target_address}"))
1895 })?;
1896 let artifact = self
1897 .contract_store
1898 .get_artifact(&instance.inner.current_contract_class_id)
1899 .await?
1900 .ok_or_else(|| {
1901 Error::InvalidData(format!(
1902 "nested call: artifact not found for contract {target_address}"
1903 ))
1904 })?;
1905
1906 let function = artifact
1908 .find_function_by_selector(&selector)
1909 .ok_or_else(|| {
1910 Error::InvalidData(format!(
1911 "nested call: function with selector {selector} not found in {target_address}"
1912 ))
1913 })?;
1914 let function_name = function.name.clone();
1915
1916 if function.is_static && !is_static {
1917 return Err(Error::InvalidData("can only be called statically".into()));
1918 }
1919
1920 let context_inputs_size = artifact.private_context_inputs_size(&function_name);
1925
1926 let mut full_witness = if !self.context_witness_prefix.is_empty()
1930 && self.context_witness_prefix.len() + 4 <= context_inputs_size
1931 {
1932 let mut w = Vec::with_capacity(context_inputs_size);
1934 w.push(self.contract_address.0); w.push(target_address.0); w.push(selector_field); w.push(Fr::from(is_static)); w.extend_from_slice(&self.context_witness_prefix);
1941 w.push(Fr::from(circuit_side_effect_counter as u64));
1945 w.resize(context_inputs_size, Fr::zero());
1947 w
1948 } else {
1949 let mut w = vec![Fr::zero(); context_inputs_size];
1950 if w.len() >= 4 {
1951 w[0] = self.contract_address.0;
1952 w[1] = target_address.0;
1953 w[2] = selector_field;
1954 w[3] = Fr::from(is_static);
1955 }
1956 w
1957 };
1958
1959 full_witness.extend_from_slice(&cached_args);
1961
1962 let mut nested_oracle = PrivateExecutionOracle::new(
1964 self.node,
1965 self.contract_store,
1966 self.key_store,
1967 self.note_store,
1968 self.capsule_store,
1969 self.address_store,
1970 self.sender_tagging_store,
1971 self.block_header.clone(),
1972 target_address,
1973 self.protocol_nullifier,
1974 self.sender_for_tags,
1975 self.scopes.clone(),
1976 is_static,
1977 );
1978
1979 nested_oracle.execution_cache = self.execution_cache.clone();
1981 nested_oracle.auth_witnesses = self.auth_witnesses.clone();
1983 nested_oracle.side_effect_counter = circuit_side_effect_counter;
1986 nested_oracle.min_revertible_side_effect_counter = self.min_revertible_side_effect_counter;
1989 nested_oracle.context_witness_prefix = self.context_witness_prefix.clone();
1991 nested_oracle.capsules = self.capsules.clone();
1993 nested_oracle.new_notes = self.new_notes.clone();
1996 nested_oracle.note_hashes = self.note_hashes.clone();
1997 nested_oracle.nullifiers = self.nullifiers.clone();
1998 nested_oracle.note_hash_nullifier_counter_map =
1999 self.note_hash_nullifier_counter_map.clone();
2000 nested_oracle.consumed_db_nullifiers = self.consumed_db_nullifiers.clone();
2001 let inherited_new_notes = self.new_notes.len();
2002 let inherited_note_hashes = self.note_hashes.len();
2003 let inherited_nullifiers = self.nullifiers.len();
2004 let inherited_counter_map_keys: std::collections::HashSet<u32> = self
2005 .note_hash_nullifier_counter_map
2006 .keys()
2007 .copied()
2008 .collect();
2009
2010 let acvm_output = super::acvm_executor::AcvmExecutor::execute_private(
2012 &artifact,
2013 &function_name,
2014 &full_witness,
2015 &mut nested_oracle,
2016 )
2017 .await?;
2018
2019 let end_counter = {
2024 let nh_max = nested_oracle
2025 .note_hashes
2026 .iter()
2027 .skip(inherited_note_hashes)
2028 .map(|nh| nh.note_hash.counter)
2029 .max()
2030 .unwrap_or(0);
2031 let null_max = nested_oracle
2032 .nullifiers
2033 .iter()
2034 .skip(inherited_nullifiers)
2035 .map(|n| n.nullifier.counter)
2036 .max()
2037 .unwrap_or(0);
2038 let log_max = nested_oracle
2039 .private_logs
2040 .iter()
2041 .map(|l| l.counter)
2042 .max()
2043 .unwrap_or(0);
2044 let oracle_counter = nested_oracle.side_effect_counter;
2045 nh_max.max(null_max).max(log_max).max(oracle_counter)
2046 };
2047
2048 let nested_ctx_size_for_pcpi = artifact.private_context_inputs_size(&function_name);
2052 let pcpi_start = nested_ctx_size_for_pcpi + cached_args.len();
2053 const PCPI_RETURNS_HASH_OFFSET: usize = 5;
2055
2056 let returns_hash = {
2057 let idx = acir::native_types::Witness((pcpi_start + PCPI_RETURNS_HASH_OFFSET) as u32);
2058 acvm_output
2059 .witness
2060 .get(&idx)
2061 .map(|fe| super::field_conversion::fe_to_fr(fe))
2062 .unwrap_or_else(|| {
2063 aztec_core::hash::compute_var_args_hash(&acvm_output.return_values)
2065 })
2066 };
2067
2068 if !self.execution_cache.contains_key(&returns_hash) {
2074 if let Some(cached) = nested_oracle.execution_cache.get(&returns_hash) {
2075 self.execution_cache.insert(returns_hash, cached.clone());
2076 } else {
2077 self.execution_cache
2079 .insert(returns_hash, acvm_output.return_values.clone());
2080 }
2081 }
2082
2083 let nested_ctx_size = artifact.private_context_inputs_size(&function_name);
2086 let nested_params_size = nested_ctx_size + cached_args.len();
2087 let (circuit_note_hashes, _circuit_nullifiers, circuit_logs) =
2088 Self::extract_side_effects_from_witness(
2089 &acvm_output.witness,
2090 nested_params_size,
2091 target_address,
2092 );
2093
2094 {
2105 let mut minimal = PrivateCallExecutionResult::default();
2106 minimal.contract_address = target_address;
2107 minimal.return_values = if !acvm_output.first_acir_call_return_values.is_empty() {
2108 acvm_output.first_acir_call_return_values.clone()
2109 } else {
2110 acvm_output.return_values.clone()
2111 };
2112 self.nested_results.push(minimal);
2113 }
2114
2115 for (k, v) in nested_oracle.execution_cache {
2117 self.execution_cache.entry(k).or_insert(v);
2118 }
2119
2120 let new_note_hashes: Vec<_> = nested_oracle
2123 .note_hashes
2124 .into_iter()
2125 .skip(inherited_note_hashes)
2126 .collect();
2127 let oracle_has_note_hashes = !new_note_hashes.is_empty();
2128 self.note_hashes.extend(new_note_hashes);
2129 if !oracle_has_note_hashes && !circuit_note_hashes.is_empty() {
2130 self.note_hashes.extend(circuit_note_hashes);
2131 }
2132 self.nullifiers.extend(
2133 nested_oracle
2134 .nullifiers
2135 .into_iter()
2136 .skip(inherited_nullifiers),
2137 );
2138 self.private_logs.extend(Self::merge_nested_private_logs(
2141 nested_oracle.private_logs,
2142 circuit_logs,
2143 ));
2144 self.contract_class_logs
2145 .extend(nested_oracle.contract_class_logs);
2146 self.new_notes.extend(
2147 nested_oracle
2148 .new_notes
2149 .into_iter()
2150 .skip(inherited_new_notes),
2151 );
2152 self.note_hash_read_requests
2153 .extend(nested_oracle.note_hash_read_requests);
2154 self.nullifier_read_requests
2155 .extend(nested_oracle.nullifier_read_requests);
2156 self.public_call_requests
2157 .extend(nested_oracle.public_call_requests);
2158 self.public_function_calldata
2159 .extend(nested_oracle.public_function_calldata);
2160 self.offchain_effects.extend(nested_oracle.offchain_effects);
2161 for (k, v) in nested_oracle.note_hash_nullifier_counter_map {
2162 if !inherited_counter_map_keys.contains(&k) {
2163 self.note_hash_nullifier_counter_map.insert(k, v);
2164 }
2165 }
2166 if nested_oracle.public_teardown_call_request.is_some() {
2167 self.public_teardown_call_request = nested_oracle.public_teardown_call_request;
2168 }
2169 self.consumed_db_nullifiers
2171 .extend(&nested_oracle.consumed_db_nullifiers);
2172
2173 self.side_effect_counter = end_counter;
2175
2176 Ok(vec![vec![Fr::from(end_counter as u64), returns_hash]])
2178 }
2179
2180 pub fn block_header(&self) -> &serde_json::Value {
2182 &self.block_header
2183 }
2184
2185 pub fn build_execution_result(
2189 &self,
2190 acvm_output: AcvmExecutionOutput,
2191 contract_address: AztecAddress,
2192 expiration_timestamp: u64,
2193 ) -> PrivateExecutionResult {
2194 let entrypoint = PrivateCallExecutionResult {
2195 acir: acvm_output.acir_bytecode,
2196 vk: Vec::new(), partial_witness: acvm_output.witness,
2198 contract_address,
2199 call_context: CallContext {
2200 msg_sender: AztecAddress::zero(), contract_address,
2202 function_selector: Fr::zero(),
2203 is_static_call: self.call_is_static,
2204 },
2205 return_values: acvm_output.return_values,
2206 new_notes: self.new_notes.clone(),
2207 note_hash_nullifier_counter_map: self.note_hash_nullifier_counter_map.clone(),
2208 offchain_effects: self.offchain_effects.clone(),
2209 pre_tags: Vec::new(),
2210 nested_execution_results: self.nested_results.clone(),
2211 contract_class_logs: self.contract_class_logs.clone(),
2212 note_hashes: self.note_hashes.clone(),
2213 nullifiers: self.nullifiers.clone(),
2214 note_hash_read_requests: self.note_hash_read_requests.clone(),
2215 nullifier_read_requests: self.nullifier_read_requests.clone(),
2216 private_logs: self.private_logs.clone(),
2217 public_call_requests: self.public_call_requests.clone(),
2218 public_teardown_call_request: self.public_teardown_call_request.clone(),
2219 start_side_effect_counter: 0,
2220 end_side_effect_counter: self.side_effect_counter,
2221 min_revertible_side_effect_counter: self.min_revertible_side_effect_counter,
2222 };
2223
2224 let first_nullifier = self.protocol_nullifier;
2227
2228 PrivateExecutionResult {
2229 entrypoint,
2230 first_nullifier,
2231 expiration_timestamp,
2232 public_function_calldata: self.public_function_calldata.clone(),
2233 }
2234 }
2235}
2236
2237pub(crate) fn contract_instance_to_fields(inst: &ContractInstance) -> Vec<Vec<Fr>> {
2244 let pk = &inst.public_keys;
2245 vec![
2246 vec![inst.salt],
2247 vec![Fr::from(inst.deployer)],
2248 vec![inst.current_contract_class_id],
2249 vec![inst.initialization_hash],
2250 vec![pk.master_nullifier_public_key.x],
2251 vec![pk.master_nullifier_public_key.y],
2252 vec![Fr::from(pk.master_nullifier_public_key.is_infinite)],
2253 vec![pk.master_incoming_viewing_public_key.x],
2254 vec![pk.master_incoming_viewing_public_key.y],
2255 vec![Fr::from(pk.master_incoming_viewing_public_key.is_infinite)],
2256 vec![pk.master_outgoing_viewing_public_key.x],
2257 vec![pk.master_outgoing_viewing_public_key.y],
2258 vec![Fr::from(pk.master_outgoing_viewing_public_key.is_infinite)],
2259 vec![pk.master_tagging_public_key.x],
2260 vec![pk.master_tagging_public_key.y],
2261 vec![Fr::from(pk.master_tagging_public_key.is_infinite)],
2262 ]
2263}
2264
2265#[async_trait::async_trait]
2266impl<'a, N: AztecNode + Send + Sync + 'static> OracleCallback for PrivateExecutionOracle<'a, N> {
2267 async fn handle_foreign_call(
2268 &mut self,
2269 function: &str,
2270 inputs: Vec<Vec<Fr>>,
2271 ) -> Result<Vec<Vec<Fr>>, Error> {
2272 self.handle_foreign_call(function, inputs).await
2273 }
2274}