1use std::sync::Arc;
4
5use async_trait::async_trait;
6use aztec_core::abi::FunctionSelector;
7use aztec_core::abi::{abi_type_signature, ContractArtifact, EventSelector, FunctionType};
8use aztec_core::constants::{
9 contract_class_published_magic_value, contract_instance_published_magic_value,
10 current_vk_tree_root, protocol_contract_address, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
11};
12use aztec_core::error::Error;
13use aztec_core::hash::{
14 compute_contract_address_from_instance, compute_contract_class_id,
15 compute_contract_class_id_from_artifact, compute_protocol_contracts_hash,
16 compute_protocol_nullifier,
17};
18use aztec_core::tx::{compute_tx_request_hash, Capsule, FunctionCall, TxContext};
19use aztec_core::types::{
20 AztecAddress, CompleteAddress, ContractInstance, ContractInstanceWithAddress, Fr, Point,
21 PublicKeys,
22};
23use aztec_crypto::complete_address_from_secret_key_and_partial_address;
24use aztec_crypto::schnorr::{schnorr_verify, SchnorrSignature};
25use aztec_node_client::AztecNode;
26use aztec_pxe_client::{
27 BlockHeader, ExecuteUtilityOpts, PackedPrivateEvent, PrivateEventFilter, ProfileTxOpts, Pxe,
28 RegisterContractRequest, SimulateTxOpts, TxExecutionRequest, TxProfileResult, TxProvingResult,
29 TxSimulationResult, UtilityExecutionResult,
30};
31
32use crate::kernel::prover::{BbPrivateKernelProver, BbProverConfig};
33use crate::stores::anchor_block_store::AnchorBlockHeader;
34use crate::stores::kv::KvStore;
35use crate::stores::{
36 AddressStore, AnchorBlockStore, CapsuleStore, ContractStore, KeyStore, NoteStore,
37 PrivateEventStore, RecipientTaggingStore, SenderStore, SenderTaggingStore,
38};
39use crate::sync::block_state_synchronizer::{BlockStateSynchronizer, BlockSyncConfig};
40use crate::sync::event_filter::PrivateEventFilterValidator;
41use crate::sync::ContractSyncService;
42
43#[derive(Debug, serde::Deserialize)]
44#[serde(rename_all = "camelCase")]
45struct DecodedTxExecutionRequest {
46 origin: AztecAddress,
47 first_call_args_hash: Fr,
48 args_of_calls: Vec<aztec_core::tx::HashedValues>,
49 #[serde(default)]
50 fee_payer: Option<AztecAddress>,
51}
52
53#[derive(Debug, Clone, Copy)]
54struct ParsedEntrypointCall {
55 args_hash: Fr,
56 selector: aztec_core::abi::FunctionSelector,
57 to: AztecAddress,
58 is_public: bool,
59 hide_msg_sender: bool,
60 is_static: bool,
61}
62
63#[derive(Debug)]
64struct DecodedEntrypointCall {
65 to: AztecAddress,
66 selector: aztec_core::abi::FunctionSelector,
67 encoded_args: Vec<Fr>,
68 hide_msg_sender: bool,
69 is_static: bool,
70}
71
72#[derive(Debug, Clone)]
73struct CallExecutionBundle {
74 execution_result: crate::execution::execution_result::PrivateExecutionResult,
75 contract_class_log_fields: Vec<aztec_core::tx::ContractClassLogFields>,
76 public_function_calldata: Vec<aztec_core::tx::HashedValues>,
77 first_acir_call_return_values: Vec<Fr>,
79 simulated_return_values: Vec<Fr>,
81}
82
83fn parse_encoded_calls(fields: &[Fr]) -> Result<Vec<ParsedEntrypointCall>, Error> {
84 const CALL_FIELDS: usize = 6;
85 const APP_MAX_CALLS: usize = 5;
86 let required = CALL_FIELDS * APP_MAX_CALLS + 1;
87 if fields.len() < required {
88 return Err(Error::InvalidData(format!(
89 "entrypoint args too short: {} < {}",
90 fields.len(),
91 required
92 )));
93 }
94
95 let mut calls = Vec::with_capacity(APP_MAX_CALLS);
96 for idx in 0..APP_MAX_CALLS {
97 let offset = idx * CALL_FIELDS;
98 calls.push(ParsedEntrypointCall {
99 args_hash: fields[offset],
100 selector: aztec_core::abi::FunctionSelector::from_field(fields[offset + 1]),
101 to: AztecAddress(fields[offset + 2]),
102 is_public: fields[offset + 3] != Fr::zero(),
103 hide_msg_sender: fields[offset + 4] != Fr::zero(),
104 is_static: fields[offset + 5] != Fr::zero(),
105 });
106 }
107 Ok(calls)
108}
109
110pub struct EmbeddedPxe<N: AztecNode> {
119 node: N,
120 contract_store: ContractStore,
121 key_store: KeyStore,
122 address_store: AddressStore,
123 note_store: Arc<NoteStore>,
124 #[allow(dead_code)] capsule_store: CapsuleStore,
126 sender_store: SenderStore,
128 #[allow(dead_code)] sender_tagging_store: SenderTaggingStore,
131 #[allow(dead_code)] recipient_tagging_store: RecipientTaggingStore,
134 private_event_store: Arc<PrivateEventStore>,
136 #[allow(dead_code)] kernel_prover: BbPrivateKernelProver,
139 anchor_block_store: Arc<AnchorBlockStore>,
141 block_synchronizer: BlockStateSynchronizer,
143 contract_sync_service: ContractSyncService<N>,
145 vk_tree_root: Fr,
147 protocol_contracts_hash: Fr,
149}
150
151#[derive(Debug, Clone)]
153pub struct EmbeddedPxeConfig {
154 pub prover_config: BbProverConfig,
156 pub block_sync_config: BlockSyncConfig,
158}
159
160impl Default for EmbeddedPxeConfig {
161 fn default() -> Self {
162 Self {
163 prover_config: BbProverConfig::default(),
164 block_sync_config: BlockSyncConfig::default(),
165 }
166 }
167}
168
169impl<N: AztecNode + Clone + 'static> EmbeddedPxe<N> {
170 async fn execute_sync_state_for_contract(
171 &self,
172 contract_address: AztecAddress,
173 scopes: Vec<AztecAddress>,
174 ) -> Result<(), Error> {
175 let Some(instance) = self.contract_store.get_instance(&contract_address).await? else {
176 return Ok(());
177 };
178 let Some(artifact) = self
179 .contract_store
180 .get_artifact(&instance.inner.current_contract_class_id)
181 .await?
182 else {
183 return Ok(());
184 };
185
186 let Ok(function) = artifact.find_function("sync_state") else {
187 return Ok(());
188 };
189 if function.function_type != FunctionType::Utility {
190 return Ok(());
191 }
192
193 let selector = function.selector.ok_or_else(|| {
194 Error::InvalidData(format!(
195 "sync_state missing selector in artifact {}",
196 artifact.name
197 ))
198 })?;
199
200 let call = FunctionCall {
201 to: contract_address,
202 selector,
203 args: vec![],
204 function_type: FunctionType::Utility,
205 is_static: function.is_static,
206 hide_msg_sender: false,
207 };
208
209 self.execute_utility(
210 &call,
211 ExecuteUtilityOpts {
212 scopes,
213 ..Default::default()
214 },
215 )
216 .await?;
217 Ok(())
218 }
219
220 async fn persist_pending_notes(
221 &self,
222 exec_result: &crate::execution::PrivateExecutionResult,
223 scopes: &[AztecAddress],
224 ) -> Result<(), Error> {
225 let nullifiers_by_counter: std::collections::HashMap<u32, Fr> = exec_result
226 .all_nullifiers()
227 .into_iter()
228 .map(|n| (n.nullifier.counter, n.nullifier.value))
229 .collect();
230 let note_to_nullifier_counter = exec_result.all_note_hash_nullifier_counter_maps();
231
232 for call in exec_result.iter_all_calls() {
233 for note in &call.new_notes {
234 let (siloed_nullifier, nullified) = if let Some(nullifier_counter) =
237 note_to_nullifier_counter.get(¬e.counter).copied()
238 {
239 if let Some(sn) = nullifiers_by_counter.get(&nullifier_counter).copied() {
240 (sn, true)
241 } else {
242 (Fr::zero(), false)
243 }
244 } else {
245 (Fr::zero(), false)
246 };
247
248 let stored = crate::stores::note_store::StoredNote {
249 contract_address: note.contract_address,
250 owner: note.owner,
251 storage_slot: note.storage_slot,
252 randomness: note.randomness,
253 note_nonce: Fr::zero(),
254 note_hash: note.note_hash,
255 siloed_nullifier,
256 note_data: note.note_items.clone(),
257 nullified,
258 is_pending: true,
259 nullification_block_number: None,
260 leaf_index: None,
261 block_number: None,
262 tx_index_in_block: None,
263 note_index_in_tx: None,
264 scopes: vec![],
265 };
266
267 let mut stored_for_owner = false;
268 for scope in scopes {
269 if *scope == note.owner {
270 self.note_store.add_notes(&[stored.clone()], scope).await?;
271 stored_for_owner = true;
272 }
273 }
274
275 if !stored_for_owner {
276 let fallback_scope = if !note.owner.0.is_zero() {
277 note.owner
278 } else {
279 AztecAddress::zero()
280 };
281 self.note_store
282 .add_notes(&[stored], &fallback_scope)
283 .await?;
284 }
285 }
286 }
287
288 Ok(())
289 }
290
291 fn tx_request_hash(tx_request: &TxExecutionRequest) -> Result<Fr, Error> {
292 #[derive(serde::Deserialize)]
293 #[serde(rename_all = "camelCase")]
294 struct WireTxRequest {
295 origin: AztecAddress,
296 function_selector: FunctionSelector,
297 first_call_args_hash: Fr,
298 tx_context: TxContext,
299 salt: Fr,
300 }
301
302 let request: WireTxRequest = serde_json::from_value(tx_request.data.clone())
303 .map_err(|err| Error::InvalidData(format!("invalid tx request payload: {err}")))?;
304
305 Ok(compute_tx_request_hash(
306 request.origin,
307 request.first_call_args_hash,
308 &request.tx_context,
309 request.function_selector,
310 true, request.salt,
312 ))
313 }
314
315 pub async fn create(node: N, kv: Arc<dyn KvStore>) -> Result<Self, Error> {
317 Self::create_with_config(node, kv, EmbeddedPxeConfig::default()).await
318 }
319
320 pub async fn create_with_prover_config(
322 node: N,
323 kv: Arc<dyn KvStore>,
324 prover_config: BbProverConfig,
325 ) -> Result<Self, Error> {
326 Self::create_with_config(
327 node,
328 kv,
329 EmbeddedPxeConfig {
330 prover_config,
331 ..Default::default()
332 },
333 )
334 .await
335 }
336
337 pub async fn create_with_config(
339 node: N,
340 kv: Arc<dyn KvStore>,
341 config: EmbeddedPxeConfig,
342 ) -> Result<Self, Error> {
343 let contract_store = ContractStore::new(Arc::clone(&kv));
344 let key_store = KeyStore::new(Arc::clone(&kv));
345 let address_store = AddressStore::new(Arc::clone(&kv));
346 let note_store = Arc::new(NoteStore::new(Arc::clone(&kv)));
347 let capsule_store = CapsuleStore::new(Arc::clone(&kv));
348 let sender_store = SenderStore::new(Arc::clone(&kv));
349 let sender_tagging_store = SenderTaggingStore::new(Arc::clone(&kv));
350 let recipient_tagging_store = RecipientTaggingStore::new(Arc::clone(&kv));
351 let private_event_store = Arc::new(PrivateEventStore::new(Arc::clone(&kv)));
352 let anchor_block_store = Arc::new(AnchorBlockStore::new(Arc::clone(&kv)));
353 let kernel_prover = BbPrivateKernelProver::new(config.prover_config);
354
355 let block_synchronizer = BlockStateSynchronizer::new(
357 Arc::clone(&anchor_block_store),
358 Arc::clone(¬e_store),
359 Arc::clone(&private_event_store),
360 config.block_sync_config,
361 );
362
363 let contract_sync_service =
365 ContractSyncService::new(Arc::new(node.clone()), Arc::clone(¬e_store));
366
367 let node_info = node.get_node_info().await?;
371 let vk_tree_root = node_info
372 .l2_circuits_vk_tree_root
373 .as_deref()
374 .and_then(|s| Fr::from_hex(s).ok())
375 .unwrap_or_else(current_vk_tree_root);
376 let protocol_contracts_hash = node_info
377 .l2_protocol_contracts_hash
378 .as_deref()
379 .and_then(|s| Fr::from_hex(s).ok())
380 .unwrap_or_else(compute_protocol_contracts_hash);
381
382 let pxe = Self {
383 node,
384 contract_store,
385 key_store,
386 address_store,
387 note_store,
388 capsule_store,
389 sender_store,
390 sender_tagging_store,
391 recipient_tagging_store,
392 private_event_store,
393 kernel_prover,
394 anchor_block_store,
395 block_synchronizer,
396 contract_sync_service,
397 vk_tree_root,
398 protocol_contracts_hash,
399 };
400
401 pxe.block_synchronizer.sync(&pxe.node).await?;
403
404 Ok(pxe)
405 }
406
407 pub async fn create_ephemeral(node: N) -> Result<Self, Error> {
409 let kv = Arc::new(crate::stores::InMemoryKvStore::new());
410 Self::create(node, kv).await
411 }
412
413 fn abi_value_to_fields(v: &aztec_core::abi::AbiValue) -> Vec<Fr> {
415 match v {
416 aztec_core::abi::AbiValue::Field(f) => vec![*f],
417 aztec_core::abi::AbiValue::Integer(i) => vec![Fr::from(*i as u64)],
418 aztec_core::abi::AbiValue::Boolean(b) => vec![Fr::from(*b)],
419 aztec_core::abi::AbiValue::String(s) => s.chars().map(|c| Fr::from(c as u64)).collect(),
420 aztec_core::abi::AbiValue::Array(arr) => {
421 arr.iter().flat_map(Self::abi_value_to_fields).collect()
422 }
423 aztec_core::abi::AbiValue::Struct(fields) => fields
424 .values()
425 .flat_map(Self::abi_value_to_fields)
426 .collect(),
427 aztec_core::abi::AbiValue::Tuple(elems) => {
428 elems.iter().flat_map(Self::abi_value_to_fields).collect()
429 }
430 }
431 }
432
433 async fn sync_block_state(&self) -> Result<(), Error> {
437 self.block_synchronizer.sync(&self.node).await?;
438
439 if self.block_synchronizer.take_anchor_changed().await {
441 self.contract_sync_service.wipe().await;
442 }
443
444 Ok(())
445 }
446
447 async fn get_anchor_block_header(&self) -> Result<AnchorBlockHeader, Error> {
449 self.sync_block_state().await?;
450 self.block_synchronizer
451 .get_anchor_block_header()
452 .await?
453 .ok_or_else(|| Error::InvalidData("anchor block header not set after sync".into()))
454 }
455
456 async fn get_anchor_block_number(&self) -> Result<u64, Error> {
458 self.sync_block_state().await?;
459 self.block_synchronizer.get_anchor_block_number().await
460 }
461
462 pub fn node(&self) -> &N {
464 &self.node
465 }
466
467 pub fn contract_store(&self) -> &ContractStore {
469 &self.contract_store
470 }
471
472 pub fn key_store(&self) -> &KeyStore {
474 &self.key_store
475 }
476
477 pub fn address_store(&self) -> &AddressStore {
479 &self.address_store
480 }
481
482 pub fn note_store(&self) -> &NoteStore {
484 &self.note_store
485 }
486
487 pub fn anchor_block_store(&self) -> &AnchorBlockStore {
489 &self.anchor_block_store
490 }
491
492 pub fn private_event_store(&self) -> &PrivateEventStore {
494 &self.private_event_store
495 }
496
497 #[allow(dead_code)]
509 async fn extract_call_info(
510 &self,
511 tx_request: &TxExecutionRequest,
512 ) -> Result<(AztecAddress, String, Vec<Fr>, AztecAddress), Error> {
513 let request: DecodedTxExecutionRequest = serde_json::from_value(tx_request.data.clone())?;
514 let origin = request.origin;
515
516 if let Some(call) = Self::decode_entrypoint_call(&request)? {
517 let function_name = self
518 .resolve_function_name_by_selector(&call.to, call.selector)
519 .await;
520 return Ok((call.to, function_name, call.encoded_args, origin));
521 }
522
523 let contracts = self.contract_store.get_contract_addresses().await?;
525 if let Some(addr) = contracts.first() {
526 return Ok((*addr, "unknown".to_owned(), vec![], origin));
527 }
528
529 Err(Error::InvalidData(
530 "could not determine target contract from TxExecutionRequest".into(),
531 ))
532 }
533
534 async fn resolve_function_name_by_selector(
536 &self,
537 addr: &AztecAddress,
538 sel: aztec_core::abi::FunctionSelector,
539 ) -> String {
540 if let Some(name) = Self::resolve_protocol_function_name(addr, sel) {
541 return name.to_owned();
542 }
543
544 let inst = match self.contract_store.get_instance(addr).await {
545 Ok(Some(i)) => i,
546 _ => return "unknown".to_owned(),
547 };
548 let artifact = match self
549 .contract_store
550 .get_artifact(&inst.inner.current_contract_class_id)
551 .await
552 {
553 Ok(Some(a)) => a,
554 _ => return "unknown".to_owned(),
555 };
556 artifact
557 .find_function_by_selector(&sel)
558 .map(|f| f.name.clone())
559 .unwrap_or_else(|| "unknown".to_owned())
560 }
561
562 fn resolve_protocol_function_name(
563 addr: &AztecAddress,
564 sel: aztec_core::abi::FunctionSelector,
565 ) -> Option<&'static str> {
566 if *addr == protocol_contract_address::contract_class_registry()
567 && sel
568 == aztec_core::abi::FunctionSelector::from_signature("publish(Field,Field,Field)")
569 {
570 return Some("publish");
571 }
572
573 if *addr == protocol_contract_address::contract_instance_registry()
574 && sel
575 == aztec_core::abi::FunctionSelector::from_signature(
576 "publish_for_public_execution(Field,(Field),Field,(((Field,Field,bool)),((Field,Field,bool)),((Field,Field,bool)),((Field,Field,bool))),bool)",
577 )
578 {
579 return Some("publish_for_public_execution");
580 }
581
582 if *addr == protocol_contract_address::auth_registry() {
584 if sel
585 == aztec_core::abi::FunctionSelector::from_signature("set_authorized(Field,bool)")
586 {
587 return Some("set_authorized(Field,bool)");
588 }
589 if sel == aztec_core::abi::FunctionSelector::from_signature("consume((Field),Field)") {
590 return Some("consume((Field),Field)");
591 }
592 }
593
594 None
595 }
596
597 fn protocol_private_execution(
598 &self,
599 tx_request: &TxExecutionRequest,
600 contract_address: AztecAddress,
601 function_name: &str,
602 encoded_args: &[Fr],
603 origin: AztecAddress,
604 first_nullifier: Fr,
605 ) -> Result<Option<CallExecutionBundle>, Error> {
606 match (contract_address, function_name) {
607 (addr, "publish") if addr == protocol_contract_address::contract_class_registry() => {
608 let artifact_hash = *encoded_args
609 .first()
610 .ok_or_else(|| Error::InvalidData("publish missing artifact_hash".into()))?;
611 let private_functions_root = *encoded_args.get(1).ok_or_else(|| {
612 Error::InvalidData("publish missing private_functions_root".into())
613 })?;
614 let public_bytecode_commitment = *encoded_args.get(2).ok_or_else(|| {
615 Error::InvalidData("publish missing public_bytecode_commitment".into())
616 })?;
617 let class_id = compute_contract_class_id(
618 artifact_hash,
619 private_functions_root,
620 public_bytecode_commitment,
621 );
622
623 let capsules = tx_request
624 .data
625 .get("capsules")
626 .cloned()
627 .map(serde_json::from_value::<Vec<Capsule>>)
628 .transpose()?
629 .unwrap_or_default();
630 let bytecode_fields = capsules
631 .into_iter()
632 .find(|capsule| {
633 capsule.contract_address == protocol_contract_address::contract_class_registry()
634 && capsule.storage_slot
635 == aztec_core::constants::contract_class_registry_bytecode_capsule_slot()
636 })
637 .map(|capsule| capsule.data)
638 .unwrap_or_default();
639
640 let mut emitted_fields =
641 Vec::with_capacity(MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS + 5);
642 emitted_fields.push(contract_class_published_magic_value());
643 emitted_fields.push(class_id);
644 emitted_fields.push(Fr::from(1u64));
645 emitted_fields.push(artifact_hash);
646 emitted_fields.push(private_functions_root);
647 emitted_fields.extend(bytecode_fields);
648 let entrypoint = crate::execution::execution_result::PrivateCallExecutionResult {
649 contract_address,
650 call_context: aztec_core::kernel_types::CallContext {
651 msg_sender: origin,
652 contract_address,
653 function_selector: FunctionSelector::from_signature(
654 "publish(Field,Field,Field)",
655 )
656 .to_field(),
657 is_static_call: false,
658 },
659 nullifiers: vec![aztec_core::kernel_types::ScopedNullifier {
660 nullifier: aztec_core::kernel_types::Nullifier {
661 value: class_id,
662 note_hash: Fr::zero(),
663 counter: 2,
664 },
665 contract_address,
666 }],
667 contract_class_logs: vec![aztec_core::kernel_types::CountedContractClassLog {
668 log: aztec_core::kernel_types::ContractClassLog {
669 contract_address,
670 emitted_length: emitted_fields.len() as u32,
671 fields: emitted_fields.clone(),
672 },
673 counter: 3,
674 }],
675 start_side_effect_counter: 2,
676 end_side_effect_counter: 4,
677 min_revertible_side_effect_counter: 2,
678 ..Default::default()
679 };
680
681 return Ok(Some(CallExecutionBundle {
682 first_acir_call_return_values: Vec::new(),
683 simulated_return_values: Vec::new(),
684 execution_result: crate::execution::execution_result::PrivateExecutionResult {
685 entrypoint,
686 first_nullifier,
687 expiration_timestamp: 0,
688 public_function_calldata: vec![],
689 },
690 contract_class_log_fields: vec![
691 aztec_core::tx::ContractClassLogFields::from_emitted_fields(emitted_fields),
692 ],
693 public_function_calldata: vec![],
694 }));
695 }
696 (addr, "publish_for_public_execution")
697 if addr == protocol_contract_address::contract_instance_registry() =>
698 {
699 if encoded_args.len() < 16 {
700 return Err(Error::InvalidData(format!(
701 "publish_for_public_execution args too short: {}",
702 encoded_args.len()
703 )));
704 }
705
706 let salt = encoded_args[0];
707 let class_id = encoded_args[1];
708 let initialization_hash = encoded_args[2];
709 let public_keys = PublicKeys {
710 master_nullifier_public_key: Point {
711 x: encoded_args[3],
712 y: encoded_args[4],
713 is_infinite: encoded_args[5] != Fr::zero(),
714 },
715 master_incoming_viewing_public_key: Point {
716 x: encoded_args[6],
717 y: encoded_args[7],
718 is_infinite: encoded_args[8] != Fr::zero(),
719 },
720 master_outgoing_viewing_public_key: Point {
721 x: encoded_args[9],
722 y: encoded_args[10],
723 is_infinite: encoded_args[11] != Fr::zero(),
724 },
725 master_tagging_public_key: Point {
726 x: encoded_args[12],
727 y: encoded_args[13],
728 is_infinite: encoded_args[14] != Fr::zero(),
729 },
730 };
731 let universal_deploy = encoded_args[15] != Fr::zero();
732 let deployer = if universal_deploy {
733 AztecAddress::zero()
734 } else {
735 origin
736 };
737 let instance = ContractInstanceWithAddress {
738 address: compute_contract_address_from_instance(&ContractInstance {
739 version: 1,
740 salt,
741 deployer,
742 current_contract_class_id: class_id,
743 original_contract_class_id: class_id,
744 initialization_hash,
745 public_keys: public_keys.clone(),
746 })?,
747 inner: ContractInstance {
748 version: 1,
749 salt,
750 deployer,
751 current_contract_class_id: class_id,
752 original_contract_class_id: class_id,
753 initialization_hash,
754 public_keys: public_keys.clone(),
755 },
756 };
757
758 let event_payload = vec![
759 contract_instance_published_magic_value(),
760 instance.address.0,
761 Fr::from(1u64),
762 salt,
763 class_id,
764 initialization_hash,
765 public_keys.master_nullifier_public_key.x,
766 public_keys.master_nullifier_public_key.y,
767 public_keys.master_incoming_viewing_public_key.x,
768 public_keys.master_incoming_viewing_public_key.y,
769 public_keys.master_outgoing_viewing_public_key.x,
770 public_keys.master_outgoing_viewing_public_key.y,
771 public_keys.master_tagging_public_key.x,
772 public_keys.master_tagging_public_key.y,
773 deployer.0,
774 ];
775 let mut emitted_private_log_fields = event_payload.clone();
776 emitted_private_log_fields.push(Fr::zero());
777 let entrypoint = crate::execution::execution_result::PrivateCallExecutionResult {
780 contract_address,
781 call_context: aztec_core::kernel_types::CallContext {
782 msg_sender: origin,
783 contract_address,
784 function_selector: FunctionSelector::from_signature(
785 "publish_for_public_execution(Field,(Field),Field,(((Field,Field,bool)),((Field,Field,bool)),((Field,Field,bool)),((Field,Field,bool))),bool)",
786 )
787 .to_field(),
788 is_static_call: false,
789 },
790 nullifiers: vec![aztec_core::kernel_types::ScopedNullifier {
791 nullifier: aztec_core::kernel_types::Nullifier {
792 value: instance.address.0,
793 note_hash: Fr::zero(),
794 counter: 2,
795 },
796 contract_address,
797 }],
798 private_logs: vec![crate::execution::execution_result::PrivateLogData {
799 fields: emitted_private_log_fields,
800 emitted_length: 15,
801 note_hash_counter: 0,
802 counter: 3,
803 contract_address,
804 }],
805 start_side_effect_counter: 2,
806 end_side_effect_counter: 4,
807 min_revertible_side_effect_counter: 2,
808 ..Default::default()
809 };
810
811 return Ok(Some(CallExecutionBundle {
812 first_acir_call_return_values: Vec::new(),
813 simulated_return_values: Vec::new(),
814 execution_result: crate::execution::execution_result::PrivateExecutionResult {
815 entrypoint,
816 first_nullifier,
817 expiration_timestamp: 0,
818 public_function_calldata: vec![],
819 },
820 contract_class_log_fields: vec![],
821 public_function_calldata: vec![],
822 }));
823 }
824 _ => {}
825 }
826
827 Ok(None)
828 }
829
830 fn protocol_public_execution(
836 contract_address: AztecAddress,
837 function_name: &str,
838 encoded_args: &[Fr],
839 origin: AztecAddress,
840 first_nullifier: Fr,
841 hide_msg_sender: bool,
842 is_static_call: bool,
843 ) -> Result<Option<CallExecutionBundle>, Error> {
844 use crate::execution::execution_result::{
845 PrivateCallExecutionResult, PrivateExecutionResult, PublicCallRequestData,
846 };
847
848 if contract_address != protocol_contract_address::auth_registry() {
850 return Ok(None);
851 }
852
853 let selector_fr: Fr = FunctionSelector::from_signature(function_name).into();
855 let mut calldata = vec![selector_fr];
856 calldata.extend_from_slice(encoded_args);
857 let hashed = aztec_core::tx::HashedValues::from_calldata(calldata);
858 let calldata_hash = hashed.hash();
859 let msg_sender = if hide_msg_sender {
860 AztecAddress::zero()
861 } else {
862 origin
863 };
864
865 let entrypoint = PrivateCallExecutionResult {
866 contract_address: origin,
867 public_call_requests: vec![PublicCallRequestData {
868 contract_address,
869 msg_sender,
870 is_static_call,
871 calldata_hash,
872 counter: 2,
873 }],
874 start_side_effect_counter: 2,
875 min_revertible_side_effect_counter: 2,
876 end_side_effect_counter: 3,
877 ..Default::default()
878 };
879
880 let exec_result = PrivateExecutionResult {
881 entrypoint,
882 first_nullifier,
883 expiration_timestamp: 0,
884 public_function_calldata: vec![hashed.clone()],
885 };
886
887 Ok(Some(CallExecutionBundle {
888 first_acir_call_return_values: Vec::new(),
889 simulated_return_values: Vec::new(),
890 execution_result: exec_result,
891 contract_class_log_fields: vec![],
892 public_function_calldata: vec![hashed],
893 }))
894 }
895
896 #[allow(dead_code)]
897 fn decode_entrypoint_call(
898 request: &DecodedTxExecutionRequest,
899 ) -> Result<Option<DecodedEntrypointCall>, Error> {
900 Ok(Self::decode_entrypoint_calls(request)?.into_iter().next())
901 }
902
903 fn decode_entrypoint_calls(
904 request: &DecodedTxExecutionRequest,
905 ) -> Result<Vec<DecodedEntrypointCall>, Error> {
906 let entrypoint_args = request
907 .args_of_calls
908 .iter()
909 .find(|hv| hv.hash == request.first_call_args_hash)
910 .ok_or_else(|| {
911 Error::InvalidData("firstCallArgsHash not found in argsOfCalls".into())
912 })?;
913
914 let encoded_calls = parse_encoded_calls(&entrypoint_args.values)?;
915 let mut decoded = Vec::new();
916 for call in encoded_calls {
917 if call.to == AztecAddress::zero()
918 || call.selector == aztec_core::abi::FunctionSelector::empty()
919 {
920 continue;
921 }
922
923 let hashed_args = request
924 .args_of_calls
925 .iter()
926 .find(|hv| hv.hash == call.args_hash)
927 .ok_or_else(|| {
928 Error::InvalidData(format!(
929 "call args hash {} not found in argsOfCalls",
930 call.args_hash
931 ))
932 })?;
933
934 let encoded_args = if call.is_public {
935 hashed_args.values.iter().copied().skip(1).collect()
936 } else {
937 hashed_args.values.clone()
938 };
939
940 decoded.push(DecodedEntrypointCall {
941 to: call.to,
942 selector: call.selector,
943 encoded_args,
944 hide_msg_sender: call.hide_msg_sender,
945 is_static: call.is_static,
946 });
947 }
948
949 Ok(decoded)
950 }
951
952 #[allow(dead_code)] fn extract_args(data: &serde_json::Value) -> Vec<Fr> {
955 data.get("args")
956 .and_then(|v| v.as_array())
957 .map(|arr| {
958 arr.iter()
959 .filter_map(|v| v.as_str().and_then(|s| Fr::from_hex(s).ok()))
960 .collect()
961 })
962 .unwrap_or_default()
963 }
964
965 fn build_private_witness(
971 &self,
972 artifact: &ContractArtifact,
973 function_name: &str,
974 user_args: &[Fr],
975 contract_address: AztecAddress,
976 msg_sender: AztecAddress,
977 tx_request: &TxExecutionRequest,
978 anchor: &AnchorBlockHeader,
979 function_selector: aztec_core::abi::FunctionSelector,
980 is_static_call: bool,
981 ) -> Vec<Fr> {
982 let context_size = artifact.private_context_inputs_size(function_name);
983 if context_size == 0 {
984 return user_args.to_vec();
986 }
987
988 let mut witness = Vec::with_capacity(context_size + user_args.len());
989 let tx_constants = Self::build_tx_constant_data(
990 anchor,
991 tx_request,
992 self.vk_tree_root,
993 self.protocol_contracts_hash,
994 );
995 let call_context = aztec_core::kernel_types::CallContext {
996 msg_sender,
997 contract_address,
998 function_selector: function_selector.to_field(),
999 is_static_call,
1000 };
1001
1002 witness.extend(call_context.to_fields());
1003 witness.extend(tx_constants.anchor_block_header.to_fields());
1004 witness.extend(tx_constants.tx_context.to_fields());
1005 witness.push(Fr::from(2u64));
1007
1008 witness.truncate(context_size);
1010 while witness.len() < context_size {
1011 witness.push(Fr::zero());
1012 }
1013
1014 witness.extend_from_slice(user_args);
1016
1017 witness
1018 }
1019
1020 fn offset_private_call_result(
1021 call: &crate::execution::execution_result::PrivateCallExecutionResult,
1022 offset: u32,
1023 ) -> crate::execution::execution_result::PrivateCallExecutionResult {
1024 let mut adjusted = call.clone();
1025 adjusted.start_side_effect_counter =
1026 adjusted.start_side_effect_counter.saturating_add(offset);
1027 adjusted.end_side_effect_counter = adjusted.end_side_effect_counter.saturating_add(offset);
1028 adjusted.min_revertible_side_effect_counter = adjusted
1029 .min_revertible_side_effect_counter
1030 .saturating_add(offset);
1031
1032 for note in &mut adjusted.new_notes {
1033 note.counter = note.counter.saturating_add(offset);
1034 }
1035 adjusted.note_hash_nullifier_counter_map = adjusted
1036 .note_hash_nullifier_counter_map
1037 .iter()
1038 .map(|(note_counter, nullifier_counter)| {
1039 (
1040 note_counter.saturating_add(offset),
1041 nullifier_counter.saturating_add(offset),
1042 )
1043 })
1044 .collect();
1045 for log in &mut adjusted.contract_class_logs {
1046 log.counter = log.counter.saturating_add(offset);
1047 }
1048 for note_hash in &mut adjusted.note_hashes {
1049 note_hash.note_hash.counter = note_hash.note_hash.counter.saturating_add(offset);
1050 }
1051 for nullifier in &mut adjusted.nullifiers {
1052 nullifier.nullifier.counter = nullifier.nullifier.counter.saturating_add(offset);
1053 }
1054 for req in &mut adjusted.note_hash_read_requests {
1055 req.read_request.counter = req.read_request.counter.saturating_add(offset);
1056 }
1057 for req in &mut adjusted.nullifier_read_requests {
1058 req.read_request.counter = req.read_request.counter.saturating_add(offset);
1059 }
1060 for log in &mut adjusted.private_logs {
1061 log.counter = log.counter.saturating_add(offset);
1062 }
1063 for req in &mut adjusted.public_call_requests {
1064 req.counter = req.counter.saturating_add(offset);
1065 }
1066 if let Some(req) = &mut adjusted.public_teardown_call_request {
1067 req.counter = req.counter.saturating_add(offset);
1068 }
1069 adjusted.nested_execution_results = adjusted
1070 .nested_execution_results
1071 .iter()
1072 .map(|nested| Self::offset_private_call_result(nested, offset))
1073 .collect();
1074 adjusted
1075 }
1076
1077 fn aggregate_call_bundles(
1078 origin: AztecAddress,
1079 bundles: Vec<CallExecutionBundle>,
1080 ) -> CallExecutionBundle {
1081 let mut offset = 0u32;
1082 let mut nested_execution_results = Vec::with_capacity(bundles.len());
1083 let mut first_nullifier = Fr::zero();
1084 let mut public_function_calldata = Vec::new();
1085 let mut contract_class_log_fields = Vec::new();
1086 let mut min_revertible_side_effect_counter = 0u32;
1087 let mut first_acir_returns = Vec::new();
1088 let mut simulated_return_values = Vec::new();
1089 let mut expiration_timestamp = 0u64;
1090
1091 for (idx, bundle) in bundles.into_iter().enumerate() {
1092 if idx == 0 {
1093 first_nullifier = bundle.execution_result.first_nullifier;
1094 first_acir_returns = bundle.first_acir_call_return_values;
1095 simulated_return_values = bundle.simulated_return_values;
1096 expiration_timestamp = bundle.execution_result.expiration_timestamp;
1097 min_revertible_side_effect_counter = bundle
1098 .execution_result
1099 .entrypoint
1100 .min_revertible_side_effect_counter;
1101 } else if bundle.execution_result.expiration_timestamp != 0 {
1102 expiration_timestamp = if expiration_timestamp == 0 {
1103 bundle.execution_result.expiration_timestamp
1104 } else {
1105 expiration_timestamp.min(bundle.execution_result.expiration_timestamp)
1106 };
1107 }
1108 let adjusted_entrypoint =
1109 Self::offset_private_call_result(&bundle.execution_result.entrypoint, offset);
1110 offset = adjusted_entrypoint.end_side_effect_counter;
1111 nested_execution_results.push(adjusted_entrypoint);
1112 public_function_calldata.extend(bundle.public_function_calldata);
1113 contract_class_log_fields.extend(bundle.contract_class_log_fields);
1114 }
1115
1116 let root = crate::execution::execution_result::PrivateCallExecutionResult {
1117 contract_address: origin,
1118 call_context: aztec_core::kernel_types::CallContext {
1119 msg_sender: origin,
1120 contract_address: origin,
1121 function_selector: Fr::zero(),
1122 is_static_call: false,
1123 },
1124 nested_execution_results,
1125 start_side_effect_counter: 0,
1126 end_side_effect_counter: offset,
1127 min_revertible_side_effect_counter,
1128 ..Default::default()
1129 };
1130
1131 CallExecutionBundle {
1132 first_acir_call_return_values: first_acir_returns,
1133 simulated_return_values,
1134 execution_result: crate::execution::execution_result::PrivateExecutionResult {
1135 entrypoint: root,
1136 first_nullifier,
1137 expiration_timestamp,
1138 public_function_calldata: public_function_calldata.clone(),
1139 },
1140 contract_class_log_fields,
1141 public_function_calldata,
1142 }
1143 }
1144
1145 async fn execute_entrypoint_call_bundle(
1146 &self,
1147 tx_request: &TxExecutionRequest,
1148 call: &DecodedEntrypointCall,
1149 origin: AztecAddress,
1150 protocol_nullifier: Fr,
1151 anchor: &AnchorBlockHeader,
1152 scopes: &[AztecAddress],
1153 ) -> Result<CallExecutionBundle, Error> {
1154 let contract_address = call.to;
1155 let function_name = self
1156 .resolve_function_name_by_selector(&contract_address, call.selector)
1157 .await;
1158
1159 if let Some(bundle) = self.protocol_private_execution(
1160 tx_request,
1161 contract_address,
1162 &function_name,
1163 &call.encoded_args,
1164 origin,
1165 protocol_nullifier,
1166 )? {
1167 return Ok(bundle);
1168 }
1169
1170 if let Some(bundle) = Self::protocol_public_execution(
1173 contract_address,
1174 &function_name,
1175 &call.encoded_args,
1176 origin,
1177 protocol_nullifier,
1178 call.hide_msg_sender,
1179 call.is_static,
1180 )? {
1181 return Ok(bundle);
1182 }
1183
1184 let contract_instance = self.contract_store.get_instance(&contract_address).await?;
1185 let class_id = contract_instance
1186 .as_ref()
1187 .map(|i| i.inner.current_contract_class_id)
1188 .ok_or_else(|| Error::InvalidData(format!("contract not found: {contract_address}")))?;
1189 let artifact = self
1190 .contract_store
1191 .get_artifact(&class_id)
1192 .await?
1193 .ok_or_else(|| {
1194 Error::InvalidData(format!("artifact not found for class {class_id}"))
1195 })?;
1196 let function = artifact.find_function(&function_name)?;
1197
1198 if function.function_type == FunctionType::Public {
1199 let (execution_result, contract_class_log_fields, public_function_calldata) =
1200 Self::build_public_call_execution(
1201 &artifact,
1202 &function_name,
1203 &call.encoded_args,
1204 contract_address,
1205 origin,
1206 protocol_nullifier,
1207 call.hide_msg_sender,
1208 call.is_static,
1209 )?;
1210 return Ok(CallExecutionBundle {
1211 first_acir_call_return_values: Vec::new(),
1212 simulated_return_values: Vec::new(),
1213 execution_result,
1214 contract_class_log_fields,
1215 public_function_calldata,
1216 });
1217 }
1218
1219 let full_witness = self.build_private_witness(
1220 &artifact,
1221 &function_name,
1222 &call.encoded_args,
1223 contract_address,
1224 origin,
1225 tx_request,
1226 anchor,
1227 function.selector.expect("private function selector"),
1228 function.is_static,
1229 );
1230
1231 let mut oracle = crate::execution::PrivateExecutionOracle::new(
1232 &self.node,
1233 &self.contract_store,
1234 &self.key_store,
1235 &self.note_store,
1236 &self.capsule_store,
1237 &self.address_store,
1238 &self.sender_tagging_store,
1239 anchor.data.clone(),
1240 contract_address,
1241 protocol_nullifier,
1242 Some(origin),
1243 scopes.to_vec(),
1244 call.is_static,
1245 );
1246
1247 if let Some(auth_witnesses) = tx_request.data.get("authWitnesses").and_then(|v| {
1250 serde_json::from_value::<Vec<aztec_core::tx::AuthWitness>>(v.clone()).ok()
1251 }) {
1252 let pairs: Vec<(Fr, Vec<Fr>)> = auth_witnesses
1253 .iter()
1254 .map(|aw| (aw.request_hash, aw.fields.clone()))
1255 .collect();
1256 oracle.set_auth_witnesses(pairs);
1257 }
1258
1259 let context_inputs_size = artifact.private_context_inputs_size(&function_name);
1262 if context_inputs_size > 5 {
1263 oracle.context_witness_prefix =
1266 full_witness[4..context_inputs_size.saturating_sub(1)].to_vec();
1267 }
1268
1269 let acvm_output = crate::execution::AcvmExecutor::execute_private(
1270 &artifact,
1271 &function_name,
1272 &full_witness,
1273 &mut oracle,
1274 )
1275 .await?;
1276
1277 let acir_call_returns = {
1281 let ctx_size = artifact.private_context_inputs_size(&function_name);
1282 let user_args_size = call.encoded_args.len();
1283 let pcpi_start = ctx_size + user_args_size;
1284 const PCPI_RETURNS_HASH_OFFSET: usize = 5;
1285 let returns_hash_idx =
1286 acir::native_types::Witness((pcpi_start + PCPI_RETURNS_HASH_OFFSET) as u32);
1287 let returns_hash = acvm_output
1288 .witness
1289 .get(&returns_hash_idx)
1290 .map(super::execution::field_conversion::fe_to_fr);
1291 if let Some(rh) = returns_hash {
1292 oracle.get_execution_cache_entry(&rh).unwrap_or_default()
1293 } else {
1294 acvm_output.first_acir_call_return_values.clone()
1295 }
1296 };
1297 let mut execution_result = oracle.build_execution_result(acvm_output, contract_address, 0);
1298 execution_result.entrypoint.call_context = aztec_core::kernel_types::CallContext {
1299 msg_sender: origin,
1300 contract_address,
1301 function_selector: function
1302 .selector
1303 .expect("private function selector")
1304 .to_field(),
1305 is_static_call: function.is_static,
1306 };
1307
1308 {
1311 let ctx_size = artifact.private_context_inputs_size(&function_name);
1312 let user_args_size = call.encoded_args.len();
1313 let params_size = ctx_size + user_args_size;
1314 let expiration_timestamp = Self::extract_expiration_timestamp_from_witness(
1315 &execution_result.entrypoint.partial_witness,
1316 params_size,
1317 ctx_size,
1318 );
1319 execution_result.expiration_timestamp = expiration_timestamp;
1320
1321 let (circuit_note_hashes, _circuit_nullifiers, circuit_logs) =
1322 Self::extract_side_effects_from_witness(
1323 &execution_result.entrypoint.partial_witness,
1324 params_size,
1325 contract_address,
1326 );
1327 if execution_result.entrypoint.note_hashes.is_empty() && !circuit_note_hashes.is_empty()
1331 {
1332 execution_result
1333 .entrypoint
1334 .note_hashes
1335 .extend(circuit_note_hashes);
1336 }
1337 if !circuit_logs.is_empty() {
1339 execution_result
1340 .entrypoint
1341 .private_logs
1342 .extend(circuit_logs);
1343 }
1344 }
1345
1346 self.persist_pending_notes(&execution_result, scopes)
1347 .await?;
1348
1349 let contract_class_log_fields = execution_result
1350 .all_contract_class_logs_sorted()
1351 .iter()
1352 .map(|ccl| {
1353 aztec_core::tx::ContractClassLogFields::from_emitted_fields(ccl.log.fields.clone())
1354 })
1355 .collect::<Vec<_>>();
1356 let public_function_calldata = execution_result.public_function_calldata.clone();
1357
1358 let simulated_return_values = if !acir_call_returns.is_empty() {
1359 acir_call_returns.clone()
1360 } else {
1361 execution_result.entrypoint.return_values.clone()
1362 };
1363
1364 Ok(CallExecutionBundle {
1365 execution_result,
1366 contract_class_log_fields,
1367 public_function_calldata,
1368 first_acir_call_return_values: acir_call_returns,
1369 simulated_return_values,
1370 })
1371 }
1372
1373 fn extract_side_effects_from_witness(
1380 witness: &acir::native_types::WitnessMap<acir::FieldElement>,
1381 params_size: usize,
1382 contract_address: AztecAddress,
1383 ) -> (
1384 Vec<aztec_core::kernel_types::ScopedNoteHash>,
1385 Vec<aztec_core::kernel_types::ScopedNullifier>,
1386 Vec<crate::execution::PrivateLogData>,
1387 ) {
1388 use aztec_core::kernel_types::{NoteHash, Nullifier, ScopedNoteHash, ScopedNullifier};
1389
1390 const PCPI_LENGTH: usize = 870;
1391 const NOTE_HASHES_OFFSET: usize = 454;
1392 const NOTE_HASH_LEN: usize = 2;
1393 const MAX_NOTE_HASHES: usize = 16;
1394 const NOTE_HASHES_ARRAY_LEN: usize = MAX_NOTE_HASHES * NOTE_HASH_LEN + 1;
1395 const NULLIFIERS_OFFSET: usize = 487;
1396 const NULLIFIER_LEN: usize = 3;
1397 const MAX_NULLIFIERS: usize = 16;
1398 const NULLIFIERS_ARRAY_LEN: usize = MAX_NULLIFIERS * NULLIFIER_LEN + 1;
1399 const PRIVATE_LOGS_OFFSET: usize = 561;
1400 const PRIVATE_LOG_DATA_LEN: usize = 19;
1401 const PRIVATE_LOG_FIELDS: usize = 16;
1402 const MAX_LOGS: usize = 16;
1403 const PRIVATE_LOGS_ARRAY_LEN: usize = MAX_LOGS * PRIVATE_LOG_DATA_LEN + 1;
1404
1405 let pcpi_start = params_size;
1406 let mut pcpi = Vec::with_capacity(PCPI_LENGTH);
1407 for i in 0..PCPI_LENGTH {
1408 let idx = acir::native_types::Witness((pcpi_start + i) as u32);
1409 let val = witness
1410 .get(&idx)
1411 .map(|fe| crate::execution::field_conversion::fe_to_fr(fe))
1412 .unwrap_or_else(Fr::zero);
1413 pcpi.push(val);
1414 }
1415
1416 let nh_slice = &pcpi[NOTE_HASHES_OFFSET..][..NOTE_HASHES_ARRAY_LEN];
1418 let nh_count = nh_slice[NOTE_HASHES_ARRAY_LEN - 1]
1419 .to_usize()
1420 .min(MAX_NOTE_HASHES);
1421 let mut note_hashes = Vec::with_capacity(nh_count);
1422 for i in 0..nh_count {
1423 let base = i * NOTE_HASH_LEN;
1424 let value = nh_slice[base];
1425 let counter = nh_slice[base + 1].to_usize() as u32;
1426 if value != Fr::zero() {
1427 note_hashes.push(ScopedNoteHash {
1428 note_hash: NoteHash { value, counter },
1429 contract_address,
1430 });
1431 }
1432 }
1433
1434 let null_slice = &pcpi[NULLIFIERS_OFFSET..][..NULLIFIERS_ARRAY_LEN];
1436 let null_count = null_slice[NULLIFIERS_ARRAY_LEN - 1]
1437 .to_usize()
1438 .min(MAX_NULLIFIERS);
1439 let mut nullifiers = Vec::with_capacity(null_count);
1440 for i in 0..null_count {
1441 let base = i * NULLIFIER_LEN;
1442 let value = null_slice[base];
1443 let note_hash = null_slice[base + 1];
1444 let counter = null_slice[base + 2].to_usize() as u32;
1445 if value != Fr::zero() {
1446 nullifiers.push(ScopedNullifier {
1447 nullifier: Nullifier {
1448 value,
1449 note_hash,
1450 counter,
1451 },
1452 contract_address,
1453 });
1454 }
1455 }
1456
1457 let logs_slice = &pcpi[PRIVATE_LOGS_OFFSET..][..PRIVATE_LOGS_ARRAY_LEN];
1459 let log_count = logs_slice[PRIVATE_LOGS_ARRAY_LEN - 1]
1460 .to_usize()
1461 .min(MAX_LOGS);
1462 let mut logs = Vec::with_capacity(log_count);
1463 for i in 0..log_count {
1464 let base = i * PRIVATE_LOG_DATA_LEN;
1465 let fields: Vec<Fr> = logs_slice[base..base + PRIVATE_LOG_FIELDS].to_vec();
1466 let emitted_length = logs_slice[base + PRIVATE_LOG_FIELDS].to_usize() as u32;
1467 let note_hash_counter = logs_slice[base + PRIVATE_LOG_FIELDS + 1].to_usize() as u32;
1468 let counter = logs_slice[base + PRIVATE_LOG_DATA_LEN - 1].to_usize() as u32;
1469 if emitted_length > 0 {
1470 logs.push(crate::execution::PrivateLogData {
1471 fields,
1472 emitted_length,
1473 note_hash_counter,
1474 counter,
1475 contract_address,
1476 });
1477 }
1478 }
1479
1480 (note_hashes, nullifiers, logs)
1481 }
1482
1483 fn extract_expiration_timestamp_from_witness(
1484 witness: &acir::native_types::WitnessMap<acir::FieldElement>,
1485 params_size: usize,
1486 context_inputs_size: usize,
1487 ) -> u64 {
1488 let prefix_len = context_inputs_size.saturating_sub(5);
1489 let expiration_offset = prefix_len + 8;
1490 let idx = acir::native_types::Witness((params_size + expiration_offset) as u32);
1491 witness
1492 .get(&idx)
1493 .map(crate::execution::field_conversion::fe_to_fr)
1494 .map(|fr| fr.to_usize() as u64)
1495 .unwrap_or(0)
1496 }
1497
1498 fn extract_origin(&self, tx_request: &TxExecutionRequest) -> AztecAddress {
1499 tx_request
1500 .data
1501 .get("origin")
1502 .and_then(|v| v.as_str())
1503 .and_then(|s| Fr::from_hex(s).ok())
1504 .map(AztecAddress)
1505 .unwrap_or(AztecAddress(Fr::zero()))
1506 }
1507
1508 async fn verify_auth_witness_signatures(
1516 &self,
1517 tx_request: &TxExecutionRequest,
1518 origin: AztecAddress,
1519 ) -> Result<(), Error> {
1520 let auth_witnesses: Vec<aztec_core::tx::AuthWitness> = tx_request
1521 .data
1522 .get("authWitnesses")
1523 .and_then(|v| serde_json::from_value(v.clone()).ok())
1524 .unwrap_or_default();
1525
1526 if auth_witnesses.is_empty() {
1527 return Ok(());
1528 }
1529
1530 let signing_key_notes = self
1534 .note_store
1535 .get_notes_by_slot(&origin, &Fr::from(1u64))
1536 .await
1537 .unwrap_or_default();
1538
1539 let signing_pk = signing_key_notes.iter().find_map(|note| {
1540 if note.note_data.len() >= 2 && !note.nullified {
1541 Some(Point {
1542 x: note.note_data[0],
1543 y: note.note_data[1],
1544 is_infinite: false,
1545 })
1546 } else {
1547 None
1548 }
1549 });
1550
1551 let Some(pk) = signing_pk else {
1552 return Ok(());
1555 };
1556
1557 for aw in &auth_witnesses {
1558 if aw.fields.len() != 64 {
1560 continue;
1561 }
1562
1563 let sig_bytes: Vec<u8> = aw.fields.iter().map(|f| f.to_usize() as u8).collect();
1564 let mut sig_arr = [0u8; 64];
1565 sig_arr.copy_from_slice(&sig_bytes);
1566 let sig = SchnorrSignature::from_bytes(&sig_arr);
1567
1568 if !schnorr_verify(&pk, &aw.request_hash, &sig) {
1569 return Err(Error::InvalidData(
1570 "Cannot satisfy constraint: auth witness signature verification failed".into(),
1571 ));
1572 }
1573 }
1574
1575 Ok(())
1576 }
1577
1578 fn build_tx_constant_data(
1580 anchor: &AnchorBlockHeader,
1581 tx_request: &TxExecutionRequest,
1582 vk_tree_root: Fr,
1583 protocol_contracts_hash: Fr,
1584 ) -> aztec_core::kernel_types::TxConstantData {
1585 let h = &anchor.data;
1586
1587 let fr_at = |val: &serde_json::Value, path: &str| -> Fr {
1589 let v = val.pointer(path);
1590 match v {
1591 Some(serde_json::Value::String(s)) => Fr::from_hex(s).unwrap_or(Fr::zero()),
1592 Some(serde_json::Value::Number(n)) => Fr::from(n.as_u64().unwrap_or(0)),
1593 _ => Fr::zero(),
1594 }
1595 };
1596 let parse_u64_str = |s: &str| -> u64 {
1598 if let Some(hex) = s.strip_prefix("0x") {
1599 u64::from_str_radix(hex, 16).unwrap_or(0)
1600 } else {
1601 s.parse::<u64>().unwrap_or(0)
1602 }
1603 };
1604 let parse_u128_str = |s: &str| -> u128 {
1605 if let Some(hex) = s.strip_prefix("0x") {
1606 u128::from_str_radix(hex, 16).unwrap_or(0)
1607 } else {
1608 s.parse::<u128>().unwrap_or(0)
1609 }
1610 };
1611 let u32_at = |val: &serde_json::Value, path: &str| -> u32 {
1612 let v = val.pointer(path);
1613 match v {
1614 Some(serde_json::Value::Number(n)) => n.as_u64().unwrap_or(0) as u32,
1615 Some(serde_json::Value::String(s)) => parse_u64_str(s) as u32,
1616 _ => 0,
1617 }
1618 };
1619 let u64_at = |val: &serde_json::Value, path: &str| -> u64 {
1620 let v = val.pointer(path);
1621 match v {
1622 Some(serde_json::Value::Number(n)) => n.as_u64().unwrap_or(0),
1623 Some(serde_json::Value::String(s)) => parse_u64_str(s),
1624 _ => 0,
1625 }
1626 };
1627 let u128_at = |val: &serde_json::Value, path: &str| -> u128 {
1628 let v = val.pointer(path);
1629 match v {
1630 Some(serde_json::Value::Number(n)) => n.as_u64().unwrap_or(0) as u128,
1631 Some(serde_json::Value::String(s)) => parse_u128_str(s),
1632 _ => 0,
1633 }
1634 };
1635 let eth_at = |val: &serde_json::Value, path: &str| -> aztec_core::types::EthAddress {
1636 match val.pointer(path).and_then(|v| v.as_str()) {
1638 Some(s) => {
1639 let fr = Fr::from_hex(s).unwrap_or(Fr::zero());
1641 let bytes = fr.to_be_bytes();
1642 let mut addr = [0u8; 20];
1643 addr.copy_from_slice(&bytes[12..32]);
1644 aztec_core::types::EthAddress(addr)
1645 }
1646 None => aztec_core::types::EthAddress::default(),
1647 }
1648 };
1649
1650 let snap = |val: &serde_json::Value,
1651 prefix: &str|
1652 -> aztec_core::kernel_types::AppendOnlyTreeSnapshot {
1653 aztec_core::kernel_types::AppendOnlyTreeSnapshot {
1654 root: fr_at(val, &format!("{prefix}/root")),
1655 next_available_leaf_index: u32_at(val, &format!("{prefix}/nextAvailableLeafIndex")),
1656 }
1657 };
1658
1659 let block_header = aztec_core::kernel_types::BlockHeader {
1660 last_archive: snap(h, "/lastArchive"),
1661 state: aztec_core::kernel_types::StateReference {
1662 l1_to_l2_message_tree: snap(h, "/state/l1ToL2MessageTree"),
1663 partial: aztec_core::kernel_types::PartialStateReference {
1664 note_hash_tree: snap(h, "/state/partial/noteHashTree"),
1665 nullifier_tree: snap(h, "/state/partial/nullifierTree"),
1666 public_data_tree: snap(h, "/state/partial/publicDataTree"),
1667 },
1668 },
1669 sponge_blob_hash: fr_at(h, "/spongeBlobHash"),
1670 global_variables: aztec_core::kernel_types::GlobalVariables {
1671 chain_id: fr_at(h, "/globalVariables/chainId"),
1672 version: fr_at(h, "/globalVariables/version"),
1673 block_number: u64_at(h, "/globalVariables/blockNumber"),
1674 slot_number: u64_at(h, "/globalVariables/slotNumber"),
1675 timestamp: u64_at(h, "/globalVariables/timestamp"),
1676 coinbase: eth_at(h, "/globalVariables/coinbase"),
1677 fee_recipient: AztecAddress(fr_at(h, "/globalVariables/feeRecipient")),
1678 gas_fees: aztec_core::fee::GasFees {
1679 fee_per_da_gas: u128_at(h, "/globalVariables/gasFees/feePerDaGas"),
1680 fee_per_l2_gas: u128_at(h, "/globalVariables/gasFees/feePerL2Gas"),
1681 },
1682 },
1683 total_fees: fr_at(h, "/totalFees"),
1684 total_mana_used: fr_at(h, "/totalManaUsed"),
1685 };
1686
1687 let req = &tx_request.data;
1689 let tx_context = aztec_core::kernel_types::TxContext {
1690 chain_id: fr_at(req, "/txContext/chainId"),
1691 version: fr_at(req, "/txContext/version"),
1692 gas_settings: aztec_core::fee::GasSettings {
1693 gas_limits: Some(aztec_core::fee::Gas {
1694 da_gas: u64_at(req, "/txContext/gasSettings/gasLimits/daGas"),
1695 l2_gas: u64_at(req, "/txContext/gasSettings/gasLimits/l2Gas"),
1696 }),
1697 teardown_gas_limits: Some(aztec_core::fee::Gas {
1698 da_gas: u64_at(req, "/txContext/gasSettings/teardownGasLimits/daGas"),
1699 l2_gas: u64_at(req, "/txContext/gasSettings/teardownGasLimits/l2Gas"),
1700 }),
1701 max_fee_per_gas: Some(aztec_core::fee::GasFees {
1702 fee_per_da_gas: u128_at(req, "/txContext/gasSettings/maxFeePerGas/feePerDaGas"),
1703 fee_per_l2_gas: u128_at(req, "/txContext/gasSettings/maxFeePerGas/feePerL2Gas"),
1704 }),
1705 max_priority_fee_per_gas: Some(aztec_core::fee::GasFees {
1706 fee_per_da_gas: u128_at(
1707 req,
1708 "/txContext/gasSettings/maxPriorityFeePerGas/feePerDaGas",
1709 ),
1710 fee_per_l2_gas: u128_at(
1711 req,
1712 "/txContext/gasSettings/maxPriorityFeePerGas/feePerL2Gas",
1713 ),
1714 }),
1715 },
1716 };
1717
1718 aztec_core::kernel_types::TxConstantData {
1719 anchor_block_header: block_header,
1720 tx_context,
1721 vk_tree_root,
1722 protocol_contracts_hash,
1723 }
1724 }
1725
1726 fn compute_expiration(anchor: &AnchorBlockHeader) -> u64 {
1728 let timestamp = anchor
1729 .data
1730 .pointer("/globalVariables/timestamp")
1731 .and_then(|v| {
1732 v.as_u64().or_else(|| {
1733 v.as_str().and_then(|s| {
1734 if let Some(hex) = s.strip_prefix("0x") {
1735 u64::from_str_radix(hex, 16).ok()
1736 } else {
1737 s.parse::<u64>().ok()
1738 }
1739 })
1740 })
1741 })
1742 .unwrap_or(0);
1743 timestamp + aztec_core::constants::MAX_TX_LIFETIME
1744 }
1745
1746 fn ensure_min_fees(
1751 tx_constants: &mut aztec_core::kernel_types::TxConstantData,
1752 anchor: &AnchorBlockHeader,
1753 ) {
1754 let parse_u128 = |val: &serde_json::Value| -> u128 {
1755 if let Some(s) = val.as_str() {
1756 let s = s.strip_prefix("0x").unwrap_or(s);
1757 u128::from_str_radix(
1758 s,
1759 if val.as_str().unwrap_or("").starts_with("0x") {
1760 16
1761 } else {
1762 10
1763 },
1764 )
1765 .unwrap_or(0)
1766 } else {
1767 val.as_u64().unwrap_or(0) as u128
1768 }
1769 };
1770
1771 let block_da_fee = anchor
1772 .data
1773 .pointer("/globalVariables/gasFees/feePerDaGas")
1774 .map(|v| parse_u128(v))
1775 .unwrap_or(0);
1776 let block_l2_fee = anchor
1777 .data
1778 .pointer("/globalVariables/gasFees/feePerL2Gas")
1779 .map(|v| parse_u128(v))
1780 .unwrap_or(0);
1781
1782 let min_da = block_da_fee + block_da_fee / 2;
1784 let min_l2 = block_l2_fee + block_l2_fee / 2;
1785
1786 if let Some(ref mut fees) = tx_constants.tx_context.gas_settings.max_fee_per_gas {
1787 if fees.fee_per_da_gas < min_da {
1788 fees.fee_per_da_gas = min_da;
1789 }
1790 if fees.fee_per_l2_gas < min_l2 {
1791 fees.fee_per_l2_gas = min_l2;
1792 }
1793 } else {
1794 tx_constants.tx_context.gas_settings.max_fee_per_gas = Some(aztec_core::fee::GasFees {
1795 fee_per_da_gas: min_da,
1796 fee_per_l2_gas: min_l2,
1797 });
1798 }
1799 }
1800
1801 async fn execute_entrypoint_via_acvm(
1810 &self,
1811 tx_request: &TxExecutionRequest,
1812 origin: AztecAddress,
1813 protocol_nullifier: Fr,
1814 anchor: &AnchorBlockHeader,
1815 scopes: &[AztecAddress],
1816 ) -> Result<CallExecutionBundle, Error> {
1817 let request: DecodedTxExecutionRequest = serde_json::from_value(tx_request.data.clone())?;
1818
1819 let contract_instance = self.contract_store.get_instance(&origin).await?;
1821 let class_id = contract_instance
1822 .as_ref()
1823 .map(|i| i.inner.current_contract_class_id)
1824 .ok_or_else(|| Error::InvalidData(format!("account contract not found: {origin}")))?;
1825 let artifact = self
1826 .contract_store
1827 .get_artifact(&class_id)
1828 .await?
1829 .ok_or_else(|| {
1830 Error::InvalidData(format!(
1831 "account contract artifact not found for class {class_id}"
1832 ))
1833 })?;
1834
1835 let entrypoint_args = request
1837 .args_of_calls
1838 .iter()
1839 .find(|hv| hv.hash == request.first_call_args_hash)
1840 .ok_or_else(|| {
1841 Error::InvalidData("firstCallArgsHash not found in argsOfCalls".into())
1842 })?;
1843
1844 let function_name = {
1845 let selector_field = tx_request
1846 .data
1847 .get("functionSelector")
1848 .and_then(|v| v.as_str())
1849 .and_then(|s| aztec_core::abi::FunctionSelector::from_hex(s).ok());
1850 if let Some(sel) = selector_field {
1851 self.resolve_function_name_by_selector(&origin, sel).await
1852 } else {
1853 "entrypoint".to_owned()
1855 }
1856 };
1857
1858 let function = artifact.find_function(&function_name)?;
1859
1860 let full_witness = self.build_private_witness(
1862 &artifact,
1863 &function_name,
1864 &entrypoint_args.values,
1865 origin, origin, tx_request,
1868 anchor,
1869 function
1870 .selector
1871 .unwrap_or_else(aztec_core::abi::FunctionSelector::empty),
1872 function.is_static,
1873 );
1874
1875 let mut oracle = crate::execution::PrivateExecutionOracle::new(
1877 &self.node,
1878 &self.contract_store,
1879 &self.key_store,
1880 &self.note_store,
1881 &self.capsule_store,
1882 &self.address_store,
1883 &self.sender_tagging_store,
1884 anchor.data.clone(),
1885 origin,
1886 protocol_nullifier,
1887 Some(origin),
1888 scopes.to_vec(),
1889 false,
1890 );
1891
1892 oracle.seed_execution_cache(&request.args_of_calls);
1895
1896 if let Some(auth_witnesses) = tx_request.data.get("authWitnesses").and_then(|v| {
1898 serde_json::from_value::<Vec<aztec_core::tx::AuthWitness>>(v.clone()).ok()
1899 }) {
1900 let pairs: Vec<(Fr, Vec<Fr>)> = auth_witnesses
1901 .iter()
1902 .map(|aw| (aw.request_hash, aw.fields.clone()))
1903 .collect();
1904 oracle.set_auth_witnesses(pairs);
1905 }
1906
1907 if let Some(capsules) = tx_request
1910 .data
1911 .get("capsules")
1912 .cloned()
1913 .map(serde_json::from_value::<Vec<aztec_core::tx::Capsule>>)
1914 .transpose()?
1915 {
1916 oracle.set_capsules(capsules);
1917 }
1918
1919 let context_inputs_size = artifact.private_context_inputs_size(&function_name);
1921 if context_inputs_size > 5 {
1922 oracle.context_witness_prefix =
1923 full_witness[4..context_inputs_size.saturating_sub(1)].to_vec();
1924 }
1925
1926 let acvm_output = crate::execution::AcvmExecutor::execute_private(
1928 &artifact,
1929 &function_name,
1930 &full_witness,
1931 &mut oracle,
1932 )
1933 .await?;
1934
1935 let public_call_requests = oracle.take_public_call_requests();
1937 let public_function_calldata = oracle.take_public_function_calldata();
1938 let teardown_request = oracle.take_teardown_call_request();
1939
1940 let entrypoint_result = crate::execution::execution_result::PrivateCallExecutionResult {
1942 contract_address: origin,
1943 call_context: aztec_core::kernel_types::CallContext {
1944 msg_sender: origin,
1945 contract_address: origin,
1946 function_selector: function
1947 .selector
1948 .map(|s| s.to_field())
1949 .unwrap_or(Fr::zero()),
1950 is_static_call: false,
1951 },
1952 acir: acvm_output.acir_bytecode.clone(),
1953 vk: Vec::new(),
1954 partial_witness: acvm_output.witness.clone(),
1955 return_values: acvm_output.return_values.clone(),
1956 new_notes: oracle.new_notes.clone(),
1957 note_hash_nullifier_counter_map: oracle.note_hash_nullifier_counter_map.clone(),
1958 offchain_effects: Vec::new(),
1959 pre_tags: Vec::new(),
1960 note_hashes: oracle.note_hashes.clone(),
1961 nullifiers: oracle.nullifiers.clone(),
1962 private_logs: oracle.private_logs.clone(),
1963 contract_class_logs: oracle.contract_class_logs.clone(),
1964 public_call_requests,
1965 public_teardown_call_request: teardown_request,
1966 start_side_effect_counter: 2,
1967 end_side_effect_counter: oracle.side_effect_counter,
1968 min_revertible_side_effect_counter: oracle.min_revertible_side_effect_counter,
1969 note_hash_read_requests: oracle.note_hash_read_requests.clone(),
1970 nullifier_read_requests: oracle.nullifier_read_requests.clone(),
1971 nested_execution_results: oracle.nested_results.clone(),
1972 };
1973
1974 let contract_class_log_fields = entrypoint_result
1975 .contract_class_logs
1976 .iter()
1977 .map(|log| {
1978 aztec_core::tx::ContractClassLogFields::from_emitted_fields(log.log.fields.clone())
1979 })
1980 .collect();
1981 let expiration_timestamp = Self::extract_expiration_timestamp_from_witness(
1982 &acvm_output.witness,
1983 full_witness.len(),
1984 context_inputs_size,
1985 );
1986
1987 Ok(CallExecutionBundle {
1988 simulated_return_values: if !acvm_output.first_acir_call_return_values.is_empty() {
1989 acvm_output.first_acir_call_return_values.clone()
1990 } else {
1991 acvm_output.return_values.clone()
1992 },
1993 first_acir_call_return_values: acvm_output.first_acir_call_return_values,
1994 execution_result: crate::execution::execution_result::PrivateExecutionResult {
1995 entrypoint: entrypoint_result,
1996 first_nullifier: protocol_nullifier,
1997 expiration_timestamp,
1998 public_function_calldata: public_function_calldata.clone(),
1999 },
2000 contract_class_log_fields,
2001 public_function_calldata,
2002 })
2003 }
2004
2005 fn build_public_call_execution(
2009 artifact: &aztec_core::abi::ContractArtifact,
2010 function_name: &str,
2011 encoded_args: &[Fr],
2012 contract_address: AztecAddress,
2013 origin: AztecAddress,
2014 first_nullifier: Fr,
2015 hide_msg_sender: bool,
2016 is_static_call: bool,
2017 ) -> Result<
2018 (
2019 crate::execution::execution_result::PrivateExecutionResult,
2020 Vec<aztec_core::tx::ContractClassLogFields>,
2021 Vec<aztec_core::tx::HashedValues>,
2022 ),
2023 Error,
2024 > {
2025 use crate::execution::execution_result::{
2026 PrivateCallExecutionResult, PrivateExecutionResult, PublicCallRequestData,
2027 };
2028
2029 let args = encoded_args.to_vec();
2030
2031 let func = artifact.find_function(function_name)?;
2033 let selector_fr: Fr = func
2034 .selector
2035 .ok_or_else(|| Error::InvalidData("public function has no selector".into()))?
2036 .into();
2037 let mut calldata = vec![selector_fr];
2038 calldata.extend_from_slice(&args);
2039 let hashed = aztec_core::tx::HashedValues::from_calldata(calldata);
2040 let calldata_hash = hashed.hash();
2041 let msg_sender = if hide_msg_sender {
2042 AztecAddress::zero()
2043 } else {
2044 origin
2045 };
2046
2047 let entrypoint = PrivateCallExecutionResult {
2048 contract_address: origin,
2049 public_call_requests: vec![PublicCallRequestData {
2050 contract_address,
2051 msg_sender,
2052 is_static_call,
2053 calldata_hash,
2054 counter: 2,
2055 }],
2056 start_side_effect_counter: 2,
2057 min_revertible_side_effect_counter: 2,
2058 end_side_effect_counter: 3,
2059 ..Default::default()
2060 };
2061
2062 let exec_result = PrivateExecutionResult {
2063 entrypoint,
2064 first_nullifier,
2065 expiration_timestamp: 0,
2066 public_function_calldata: vec![hashed.clone()],
2067 };
2068
2069 Ok((exec_result, vec![], vec![hashed]))
2070 }
2071
2072 async fn is_registered_account(&self, address: &AztecAddress) -> Result<bool, Error> {
2073 let Some(complete) = self.address_store.get(address).await? else {
2074 return Ok(false);
2075 };
2076 let accounts = self.key_store.get_accounts().await?;
2077 Ok(accounts.contains(&complete.public_keys.hash()))
2078 }
2079}
2080
2081fn public_function_signatures(artifact: &ContractArtifact) -> Vec<String> {
2082 artifact
2083 .functions
2084 .iter()
2085 .filter(|function| function.function_type == FunctionType::Public)
2086 .map(|function| {
2087 let params = function
2088 .parameters
2089 .iter()
2090 .map(|param| abi_type_signature(¶m.typ))
2091 .collect::<Vec<_>>()
2092 .join(",");
2093 format!("{}({params})", function.name)
2094 })
2095 .collect()
2096}
2097
2098#[async_trait]
2099impl<N: AztecNode + Clone + 'static> Pxe for EmbeddedPxe<N> {
2100 async fn get_synced_block_header(&self) -> Result<BlockHeader, Error> {
2101 let anchor = self.get_anchor_block_header().await?;
2102 Ok(BlockHeader { data: anchor.data })
2103 }
2104
2105 async fn get_contract_instance(
2106 &self,
2107 address: &AztecAddress,
2108 ) -> Result<Option<ContractInstanceWithAddress>, Error> {
2109 if let Some(inst) = self.contract_store.get_instance(address).await? {
2111 return Ok(Some(inst));
2112 }
2113 self.node.get_contract(address).await
2115 }
2116
2117 async fn get_contract_artifact(&self, id: &Fr) -> Result<Option<ContractArtifact>, Error> {
2118 self.contract_store.get_artifact(id).await
2119 }
2120
2121 async fn get_contracts(&self) -> Result<Vec<AztecAddress>, Error> {
2122 self.contract_store.get_contract_addresses().await
2123 }
2124
2125 async fn register_account(
2126 &self,
2127 secret_key: &Fr,
2128 partial_address: &Fr,
2129 ) -> Result<CompleteAddress, Error> {
2130 tracing::debug!("registering account");
2131
2132 let _derived = self.key_store.add_account(secret_key).await?;
2134
2135 let complete =
2137 complete_address_from_secret_key_and_partial_address(secret_key, partial_address)?;
2138
2139 self.address_store.add(&complete).await?;
2141
2142 tracing::debug!(address = %complete.address, "account registered");
2143 Ok(complete)
2144 }
2145
2146 async fn get_registered_accounts(&self) -> Result<Vec<CompleteAddress>, Error> {
2147 let accounts = self.key_store.get_accounts().await?;
2148 let complete_addresses = self.address_store.get_all().await?;
2149 Ok(complete_addresses
2150 .into_iter()
2151 .filter(|complete| accounts.contains(&complete.public_keys.hash()))
2152 .collect())
2153 }
2154
2155 async fn register_sender(&self, sender: &AztecAddress) -> Result<AztecAddress, Error> {
2156 if self.is_registered_account(sender).await? {
2157 return Ok(*sender);
2158 }
2159 self.sender_store.add(sender).await?;
2160 Ok(*sender)
2161 }
2162
2163 async fn get_senders(&self) -> Result<Vec<AztecAddress>, Error> {
2164 self.sender_store.get_all().await
2165 }
2166
2167 async fn remove_sender(&self, sender: &AztecAddress) -> Result<(), Error> {
2168 self.sender_store.remove(sender).await
2169 }
2170
2171 async fn register_contract_class(&self, artifact: &ContractArtifact) -> Result<(), Error> {
2172 tracing::debug!(name = %artifact.name, "registering contract class");
2173 self.contract_store.add_class(artifact).await?;
2174 Ok(())
2175 }
2176
2177 async fn register_contract(&self, request: RegisterContractRequest) -> Result<(), Error> {
2178 tracing::debug!(address = %request.instance.address, "registering contract");
2179
2180 if let Some(ref artifact) = request.artifact {
2181 let computed_class_id = compute_contract_class_id_from_artifact(artifact)?;
2182 if computed_class_id != request.instance.inner.current_contract_class_id {
2183 return Err(Error::InvalidData(format!(
2184 "artifact class id {} does not match instance class id {}",
2185 computed_class_id, request.instance.inner.current_contract_class_id
2186 )));
2187 }
2188
2189 let computed_address = compute_contract_address_from_instance(&request.instance.inner)?;
2190 if computed_address != request.instance.address {
2191 return Err(Error::InvalidData(format!(
2192 "artifact instance address {} does not match computed contract address {}",
2193 request.instance.address, computed_address
2194 )));
2195 }
2196
2197 let public_function_signatures = public_function_signatures(artifact);
2198 if !public_function_signatures.is_empty() {
2199 self.node
2200 .register_contract_function_signatures(&public_function_signatures)
2201 .await?;
2202 }
2203 } else if self
2204 .contract_store
2205 .get_artifact(&request.instance.inner.current_contract_class_id)
2206 .await?
2207 .is_none()
2208 {
2209 return Err(Error::InvalidData(format!(
2210 "artifact not found for contract class {}",
2211 request.instance.inner.current_contract_class_id
2212 )));
2213 }
2214
2215 self.contract_store.add_instance(&request.instance).await?;
2217
2218 if let Some(ref artifact) = request.artifact {
2220 self.contract_store
2221 .add_artifact(&request.instance.inner.current_contract_class_id, artifact)
2222 .await?;
2223 }
2224
2225 Ok(())
2226 }
2227
2228 async fn update_contract(
2229 &self,
2230 address: &AztecAddress,
2231 artifact: &ContractArtifact,
2232 ) -> Result<(), Error> {
2233 self.contract_store.update_artifact(address, artifact).await
2234 }
2235
2236 async fn simulate_tx(
2237 &self,
2238 tx_request: &TxExecutionRequest,
2239 opts: SimulateTxOpts,
2240 ) -> Result<TxSimulationResult, Error> {
2241 self.sync_block_state().await?;
2242
2243 let anchor = self.get_anchor_block_header().await?;
2244 let protocol_nullifier = compute_protocol_nullifier(&Self::tx_request_hash(tx_request)?);
2245 let request: DecodedTxExecutionRequest = serde_json::from_value(tx_request.data.clone())?;
2246 let origin = request.origin;
2247 let decoded_calls = Self::decode_entrypoint_calls(&request)?;
2248 let mut bundles = Vec::with_capacity(decoded_calls.len());
2249 for call in &decoded_calls {
2250 bundles.push(
2251 self.execute_entrypoint_call_bundle(
2252 tx_request,
2253 call,
2254 origin,
2255 protocol_nullifier,
2256 &anchor,
2257 &opts.scopes,
2258 )
2259 .await?,
2260 );
2261 }
2262 let bundled_private_return_values: Vec<Vec<String>> = bundles
2263 .iter()
2264 .map(|bundle| {
2265 bundle
2266 .simulated_return_values
2267 .iter()
2268 .map(|f| f.to_string())
2269 .collect()
2270 })
2271 .collect();
2272 let aggregated = Self::aggregate_call_bundles(origin, bundles);
2273
2274 self.verify_auth_witness_signatures(tx_request, origin)
2282 .await?;
2283
2284 let kernel_output = crate::kernel::SimulatedKernel::process(
2286 &aggregated.execution_result,
2287 aztec_core::kernel_types::TxConstantData::default(), &origin.0,
2289 0, )?;
2291
2292 let pi = &kernel_output.public_inputs;
2293
2294 let private_return_values: Vec<String> = {
2316 let from_tree: Vec<String> = aggregated
2317 .execution_result
2318 .entrypoint
2319 .nested_execution_results
2320 .first()
2321 .and_then(|ep| {
2322 if !ep.nested_execution_results.is_empty() {
2325 ep.nested_execution_results.first()
2326 } else {
2327 Some(ep)
2329 }
2330 })
2331 .map(|r| r.return_values.iter().map(|f| f.to_string()).collect())
2332 .unwrap_or_default();
2333
2334 if from_tree.is_empty() && !aggregated.first_acir_call_return_values.is_empty() {
2335 aggregated
2337 .first_acir_call_return_values
2338 .iter()
2339 .map(|f| f.to_string())
2340 .collect()
2341 } else {
2342 from_tree
2343 }
2344 };
2345
2346 let return_values = if bundled_private_return_values.len() > 1 {
2347 serde_json::Value::Array(
2348 bundled_private_return_values
2349 .into_iter()
2350 .map(serde_json::to_value)
2351 .collect::<Result<Vec<_>, _>>()
2352 .map_err(Error::from)?,
2353 )
2354 } else {
2355 serde_json::to_value(private_return_values).map_err(Error::from)?
2356 };
2357
2358 Ok(TxSimulationResult {
2359 data: serde_json::json!({
2360 "returnValues": return_values,
2361 "gasUsed": {
2362 "daGas": pi.gas_used.da_gas,
2363 "l2Gas": pi.gas_used.l2_gas,
2364 },
2365 "isForPublic": pi.is_for_public(),
2366 }),
2367 })
2368 }
2369
2370 async fn prove_tx(
2371 &self,
2372 tx_request: &TxExecutionRequest,
2373 scopes: Vec<AztecAddress>,
2374 ) -> Result<TxProvingResult, Error> {
2375 self.sync_block_state().await?;
2376
2377 let anchor = self.get_anchor_block_header().await?;
2378 let tx_req_hash = Self::tx_request_hash(tx_request)?;
2379 let protocol_nullifier = compute_protocol_nullifier(&tx_req_hash);
2380 let origin = self.extract_origin(tx_request);
2381 let mut tx_constants = Self::build_tx_constant_data(
2382 &anchor,
2383 tx_request,
2384 self.vk_tree_root,
2385 self.protocol_contracts_hash,
2386 );
2387 Self::ensure_min_fees(&mut tx_constants, &anchor);
2388 let request: DecodedTxExecutionRequest = serde_json::from_value(tx_request.data.clone())?;
2389 let fee_payer = request.fee_payer.unwrap_or(origin);
2390 let decoded_calls = Self::decode_entrypoint_calls(&request)?;
2391
2392 for call in &decoded_calls {
2393 for _scope in &scopes {
2394 let _ = self
2395 .contract_sync_service
2396 .ensure_contract_synced(
2397 &call.to,
2398 &scopes,
2399 &anchor.block_hash,
2400 |contract, scopes| async move {
2401 self.execute_sync_state_for_contract(contract, scopes).await
2402 },
2403 )
2404 .await;
2405 }
2406 }
2407
2408 let has_public_calls = {
2416 let entrypoint_args = request
2417 .args_of_calls
2418 .iter()
2419 .find(|hv| hv.hash == request.first_call_args_hash);
2420 if let Some(ea) = entrypoint_args {
2421 parse_encoded_calls(&ea.values)
2422 .map(|calls| calls.iter().any(|c| c.is_public))
2423 .unwrap_or(false)
2424 } else {
2425 false
2426 }
2427 };
2428
2429 let aggregated = if has_public_calls {
2430 match self
2431 .execute_entrypoint_via_acvm(
2432 tx_request,
2433 origin,
2434 protocol_nullifier,
2435 &anchor,
2436 &scopes,
2437 )
2438 .await
2439 {
2440 Ok(bundle) => bundle,
2441 Err(Error::InvalidData(msg))
2442 if msg.contains("account contract not found")
2443 || msg.contains("account contract artifact not found") =>
2444 {
2445 let mut bundles = Vec::with_capacity(decoded_calls.len());
2446 for call in &decoded_calls {
2447 bundles.push(
2448 self.execute_entrypoint_call_bundle(
2449 tx_request,
2450 call,
2451 origin,
2452 protocol_nullifier,
2453 &anchor,
2454 &scopes,
2455 )
2456 .await?,
2457 );
2458 }
2459 Self::aggregate_call_bundles(origin, bundles)
2460 }
2461 Err(err) => return Err(err),
2462 }
2463 } else {
2464 let mut bundles = Vec::with_capacity(decoded_calls.len());
2465 for call in &decoded_calls {
2466 bundles.push(
2467 self.execute_entrypoint_call_bundle(
2468 tx_request,
2469 call,
2470 origin,
2471 protocol_nullifier,
2472 &anchor,
2473 &scopes,
2474 )
2475 .await?,
2476 );
2477 }
2478 Self::aggregate_call_bundles(origin, bundles)
2479 };
2480 let expiration_timestamp = if aggregated.execution_result.expiration_timestamp != 0 {
2481 aggregated.execution_result.expiration_timestamp
2482 } else {
2483 Self::compute_expiration(&anchor)
2484 };
2485
2486 self.verify_auth_witness_signatures(tx_request, origin)
2488 .await?;
2489
2490 let sorted_calldata = {
2494 let requests = aggregated.execution_result.all_public_call_requests();
2495 let calldata = &aggregated.public_function_calldata;
2496 if requests.len() == calldata.len() && !calldata.is_empty() {
2497 let mut paired: Vec<_> =
2498 requests.into_iter().zip(calldata.iter().cloned()).collect();
2499 paired.sort_by_key(|(req, _)| req.counter);
2500 paired.into_iter().map(|(_, cd)| cd).collect()
2501 } else {
2502 aggregated.public_function_calldata.clone()
2503 }
2504 };
2505
2506 let kernel_output = crate::kernel::SimulatedKernel::process(
2508 &aggregated.execution_result,
2509 tx_constants,
2510 &fee_payer.0,
2511 expiration_timestamp,
2512 )?;
2513 let tx_hash = aztec_core::tx::TxHash(kernel_output.public_inputs.hash().to_be_bytes());
2515 let public_inputs_buffer = kernel_output.public_inputs.to_buffer();
2516 let public_inputs =
2517 aztec_core::tx::PrivateKernelTailCircuitPublicInputs::from_bytes(public_inputs_buffer);
2518 let mut chonk_bytes =
2520 Vec::with_capacity(4 + aztec_core::constants::CHONK_PROOF_LENGTH * 32);
2521 chonk_bytes
2522 .extend_from_slice(&(aztec_core::constants::CHONK_PROOF_LENGTH as u32).to_be_bytes());
2523 chonk_bytes.resize(4 + aztec_core::constants::CHONK_PROOF_LENGTH * 32, 0);
2524 let chonk_proof = aztec_core::tx::ChonkProof::from_bytes(chonk_bytes);
2525
2526 Ok(TxProvingResult {
2527 tx_hash: Some(tx_hash),
2528 private_execution_result: serde_json::json!({}),
2529 public_inputs,
2530 chonk_proof,
2531 contract_class_log_fields: aggregated.contract_class_log_fields,
2532 public_function_calldata: sorted_calldata,
2533 stats: None,
2534 })
2535 }
2536
2537 async fn profile_tx(
2538 &self,
2539 tx_request: &TxExecutionRequest,
2540 opts: ProfileTxOpts,
2541 ) -> Result<TxProfileResult, Error> {
2542 self.sync_block_state().await?;
2543
2544 let anchor = self.get_anchor_block_header().await?;
2545 let protocol_nullifier = compute_protocol_nullifier(&Self::tx_request_hash(tx_request)?);
2546 let request: DecodedTxExecutionRequest = serde_json::from_value(tx_request.data.clone())?;
2547 let origin = request.origin;
2548 let decoded_calls = Self::decode_entrypoint_calls(&request)?;
2549 let fee_payer = request.fee_payer.unwrap_or(origin);
2550 let mut bundles = Vec::with_capacity(decoded_calls.len());
2551 for call in &decoded_calls {
2552 bundles.push(
2553 self.execute_entrypoint_call_bundle(
2554 tx_request,
2555 call,
2556 origin,
2557 protocol_nullifier,
2558 &anchor,
2559 &opts.scopes,
2560 )
2561 .await?,
2562 );
2563 }
2564 let aggregated = Self::aggregate_call_bundles(origin, bundles);
2565
2566 let mut tx_constants = Self::build_tx_constant_data(
2567 &anchor,
2568 tx_request,
2569 self.vk_tree_root,
2570 self.protocol_contracts_hash,
2571 );
2572 let expiration_timestamp = if aggregated.execution_result.expiration_timestamp != 0 {
2573 aggregated.execution_result.expiration_timestamp
2574 } else {
2575 Self::compute_expiration(&anchor)
2576 };
2577 Self::ensure_min_fees(&mut tx_constants, &anchor);
2578
2579 let _kernel_output = crate::kernel::SimulatedKernel::process(
2580 &aggregated.execution_result,
2581 tx_constants,
2582 &fee_payer.0,
2583 expiration_timestamp,
2584 )?;
2585
2586 let data = serde_json::json!({
2587 "expirationTimestamp": expiration_timestamp,
2588 "data": {
2589 "expirationTimestamp": expiration_timestamp,
2590 },
2591 });
2592
2593 Ok(TxProfileResult { data })
2594 }
2595
2596 async fn execute_utility(
2597 &self,
2598 call: &FunctionCall,
2599 opts: ExecuteUtilityOpts,
2600 ) -> Result<UtilityExecutionResult, Error> {
2601 self.sync_block_state().await?;
2602 self.contract_sync_service.wipe().await;
2606 let anchor = self.get_anchor_block_header().await?;
2607
2608 let contract_instance = self.contract_store.get_instance(&call.to).await?;
2610 let class_id = contract_instance
2611 .as_ref()
2612 .map(|i| i.inner.current_contract_class_id)
2613 .ok_or_else(|| Error::InvalidData(format!("contract not found: {}", call.to)))?;
2614
2615 let artifact = self
2616 .contract_store
2617 .get_artifact(&class_id)
2618 .await?
2619 .ok_or_else(|| {
2620 Error::InvalidData(format!("artifact not found for class {class_id}"))
2621 })?;
2622
2623 let function = artifact
2625 .find_function_by_selector(&call.selector)
2626 .or_else(|| {
2627 artifact
2629 .functions
2630 .iter()
2631 .find(|f| f.function_type == FunctionType::Utility)
2632 })
2633 .ok_or_else(|| {
2634 Error::InvalidData(format!(
2635 "utility function with selector {} not found in {}",
2636 call.selector, artifact.name
2637 ))
2638 })?;
2639
2640 let function_name = function.name.clone();
2641
2642 if function_name != "sync_state" {
2643 self.contract_sync_service
2644 .ensure_contract_synced(
2645 &call.to,
2646 &opts.scopes,
2647 &anchor.block_hash,
2648 |contract, scopes| async move {
2649 self.execute_sync_state_for_contract(contract, scopes).await
2650 },
2651 )
2652 .await?;
2653 }
2654
2655 let args: Vec<Fr> = call
2657 .args
2658 .iter()
2659 .flat_map(Self::abi_value_to_fields)
2660 .collect();
2661
2662 let mut oracle = crate::execution::UtilityExecutionOracle::new(
2664 &self.node,
2665 &self.contract_store,
2666 &self.key_store,
2667 &self.note_store,
2668 &self.address_store,
2669 &self.capsule_store,
2670 &self.sender_store,
2671 &self.sender_tagging_store,
2672 &self.recipient_tagging_store,
2673 &self.private_event_store,
2674 &self.anchor_block_store,
2675 anchor.data.clone(),
2676 call.to,
2677 opts.scopes.clone(),
2678 );
2679
2680 let auth_witness_pairs: Vec<(Fr, Vec<Fr>)> = opts
2682 .authwits
2683 .iter()
2684 .map(|aw| (aw.request_hash, aw.fields.clone()))
2685 .collect();
2686 oracle.set_auth_witnesses(auth_witness_pairs);
2687
2688 let result = crate::execution::AcvmExecutor::execute_utility(
2690 &artifact,
2691 &function_name,
2692 &args,
2693 &mut oracle,
2694 )
2695 .await?;
2696
2697 Ok(UtilityExecutionResult {
2698 result: result.return_values,
2699 stats: None,
2700 })
2701 }
2702
2703 async fn get_private_events(
2704 &self,
2705 event_selector: &EventSelector,
2706 filter: PrivateEventFilter,
2707 ) -> Result<Vec<PackedPrivateEvent>, Error> {
2708 self.sync_block_state().await?;
2719
2720 let anchor_block_number = self.get_anchor_block_number().await?;
2721
2722 let anchor_hash = self
2725 .get_anchor_block_header()
2726 .await
2727 .map(|h| h.block_hash)
2728 .unwrap_or_default();
2729 self.contract_sync_service
2730 .ensure_contract_synced(
2731 &filter.contract_address,
2732 &filter.scopes,
2733 &anchor_hash,
2734 |contract, scopes| async move {
2735 self.execute_sync_state_for_contract(contract, scopes).await
2736 },
2737 )
2738 .await?;
2739
2740 let validator = PrivateEventFilterValidator::new(anchor_block_number);
2742 let query_filter = validator.validate(&filter)?;
2743
2744 tracing::debug!(
2745 contract = %filter.contract_address,
2746 from_block = ?query_filter.from_block,
2747 to_block = ?query_filter.to_block,
2748 "getting private events"
2749 );
2750
2751 let stored_events = self
2753 .private_event_store
2754 .get_private_events(event_selector, &query_filter)
2755 .await?;
2756
2757 let packed_events: Vec<PackedPrivateEvent> = stored_events
2759 .into_iter()
2760 .map(|e| {
2761 let l2_block_hash =
2762 aztec_pxe_client::BlockHash::from_hex(&e.l2_block_hash).unwrap_or_default();
2763
2764 PackedPrivateEvent {
2765 packed_event: e.msg_content,
2766 tx_hash: e.tx_hash,
2767 l2_block_number: e.l2_block_number,
2768 l2_block_hash,
2769 event_selector: e.event_selector,
2770 }
2771 })
2772 .collect();
2773
2774 Ok(packed_events)
2775 }
2776
2777 async fn stop(&self) -> Result<(), Error> {
2778 tracing::debug!("EmbeddedPxe stopped");
2779 Ok(())
2780 }
2781}
2782
2783#[cfg(test)]
2784mod tests {
2785 use super::*;
2786 use aztec_core::types::{ContractInstance, PublicKeys};
2787 use std::sync::Mutex;
2788
2789 fn sample_public_artifact() -> ContractArtifact {
2790 ContractArtifact::from_json(
2791 r#"{
2792 "name": "Counter",
2793 "functions": [
2794 {
2795 "name": "constructor",
2796 "function_type": "private",
2797 "is_initializer": true,
2798 "is_static": false,
2799 "parameters": [],
2800 "return_types": [],
2801 "selector": "0xe5fb6c81",
2802 "bytecode": "0x01"
2803 },
2804 {
2805 "name": "increment",
2806 "function_type": "public",
2807 "is_initializer": false,
2808 "is_static": false,
2809 "parameters": [
2810 { "name": "value", "type": { "kind": "field" } }
2811 ],
2812 "return_types": [],
2813 "selector": "0x12345678",
2814 "bytecode": "0x01"
2815 }
2816 ]
2817 }"#,
2818 )
2819 .unwrap()
2820 }
2821
2822 #[derive(Clone)]
2824 struct MockNode {
2825 registered_signatures: Arc<Mutex<Vec<String>>>,
2826 }
2827
2828 impl Default for MockNode {
2829 fn default() -> Self {
2830 Self {
2831 registered_signatures: Arc::new(Mutex::new(vec![])),
2832 }
2833 }
2834 }
2835
2836 #[async_trait]
2837 impl AztecNode for MockNode {
2838 async fn get_node_info(&self) -> Result<aztec_node_client::NodeInfo, Error> {
2839 Ok(aztec_node_client::NodeInfo {
2840 node_version: "mock".into(),
2841 l1_chain_id: 1,
2842 rollup_version: 1,
2843 enr: None,
2844 l1_contract_addresses: serde_json::Value::Null,
2845 protocol_contract_addresses: serde_json::Value::Null,
2846 real_proofs: false,
2847 l2_circuits_vk_tree_root: None,
2848 l2_protocol_contracts_hash: None,
2849 })
2850 }
2851 async fn get_block_number(&self) -> Result<u64, Error> {
2852 Ok(1)
2853 }
2854 async fn get_proven_block_number(&self) -> Result<u64, Error> {
2855 Ok(1)
2856 }
2857 async fn get_tx_receipt(
2858 &self,
2859 _tx_hash: &aztec_core::tx::TxHash,
2860 ) -> Result<aztec_core::tx::TxReceipt, Error> {
2861 Err(Error::InvalidData("mock".into()))
2862 }
2863 async fn get_tx_effect(
2864 &self,
2865 _tx_hash: &aztec_core::tx::TxHash,
2866 ) -> Result<Option<serde_json::Value>, Error> {
2867 Ok(None)
2868 }
2869 async fn get_tx_by_hash(
2870 &self,
2871 _tx_hash: &aztec_core::tx::TxHash,
2872 ) -> Result<Option<serde_json::Value>, Error> {
2873 Ok(None)
2874 }
2875 async fn get_public_logs(
2876 &self,
2877 _filter: aztec_node_client::PublicLogFilter,
2878 ) -> Result<aztec_node_client::PublicLogsResponse, Error> {
2879 Ok(aztec_node_client::PublicLogsResponse {
2880 logs: vec![],
2881 max_logs_hit: false,
2882 })
2883 }
2884 async fn send_tx(&self, _tx: &serde_json::Value) -> Result<(), Error> {
2885 Err(Error::InvalidData("mock".into()))
2886 }
2887 async fn get_contract(
2888 &self,
2889 _address: &AztecAddress,
2890 ) -> Result<Option<ContractInstanceWithAddress>, Error> {
2891 Ok(None)
2892 }
2893 async fn get_contract_class(&self, _id: &Fr) -> Result<Option<serde_json::Value>, Error> {
2894 Ok(None)
2895 }
2896 async fn get_block_header(&self, _block_number: u64) -> Result<serde_json::Value, Error> {
2897 Ok(serde_json::json!({"globalVariables": {"blockNumber": 1}, "blockHash": "0x01"}))
2898 }
2899 async fn get_block(&self, _block_number: u64) -> Result<Option<serde_json::Value>, Error> {
2900 Ok(None)
2901 }
2902 async fn get_note_hash_membership_witness(
2903 &self,
2904 _block_number: u64,
2905 _note_hash: &Fr,
2906 ) -> Result<Option<serde_json::Value>, Error> {
2907 Ok(None)
2908 }
2909 async fn get_nullifier_membership_witness(
2910 &self,
2911 _block_number: u64,
2912 _nullifier: &Fr,
2913 ) -> Result<Option<serde_json::Value>, Error> {
2914 Ok(None)
2915 }
2916 async fn get_low_nullifier_membership_witness(
2917 &self,
2918 _block_number: u64,
2919 _nullifier: &Fr,
2920 ) -> Result<Option<serde_json::Value>, Error> {
2921 Ok(None)
2922 }
2923 async fn get_public_storage_at(
2924 &self,
2925 _block_number: u64,
2926 _contract: &AztecAddress,
2927 _slot: &Fr,
2928 ) -> Result<Fr, Error> {
2929 Ok(Fr::zero())
2930 }
2931 async fn get_public_data_witness(
2932 &self,
2933 _block_number: u64,
2934 _leaf_slot: &Fr,
2935 ) -> Result<Option<serde_json::Value>, Error> {
2936 Ok(None)
2937 }
2938 async fn get_l1_to_l2_message_membership_witness(
2939 &self,
2940 _block_number: u64,
2941 _entry_key: &Fr,
2942 ) -> Result<Option<serde_json::Value>, Error> {
2943 Ok(None)
2944 }
2945 async fn simulate_public_calls(
2946 &self,
2947 _tx: &serde_json::Value,
2948 _skip_fee_enforcement: bool,
2949 ) -> Result<serde_json::Value, Error> {
2950 Ok(serde_json::Value::Null)
2951 }
2952 async fn is_valid_tx(
2953 &self,
2954 _tx: &serde_json::Value,
2955 ) -> Result<aztec_node_client::TxValidationResult, Error> {
2956 Ok(aztec_node_client::TxValidationResult::Valid)
2957 }
2958 async fn get_private_logs_by_tags(&self, _tags: &[Fr]) -> Result<serde_json::Value, Error> {
2959 Ok(serde_json::json!([]))
2960 }
2961 async fn get_public_logs_by_tags_from_contract(
2962 &self,
2963 _contract: &AztecAddress,
2964 _tags: &[Fr],
2965 ) -> Result<serde_json::Value, Error> {
2966 Ok(serde_json::json!([]))
2967 }
2968 async fn register_contract_function_signatures(
2969 &self,
2970 signatures: &[String],
2971 ) -> Result<(), Error> {
2972 self.registered_signatures
2973 .lock()
2974 .unwrap()
2975 .extend(signatures.iter().cloned());
2976 Ok(())
2977 }
2978 async fn get_block_hash_membership_witness(
2979 &self,
2980 _block_number: u64,
2981 _block_hash: &Fr,
2982 ) -> Result<Option<serde_json::Value>, Error> {
2983 Ok(None)
2984 }
2985 async fn find_leaves_indexes(
2986 &self,
2987 _block_number: u64,
2988 _tree_id: &str,
2989 _leaves: &[Fr],
2990 ) -> Result<Vec<Option<u64>>, Error> {
2991 Ok(vec![])
2992 }
2993 }
2994
2995 #[tokio::test]
2996 async fn create_and_register_account() {
2997 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
2998 .await
2999 .unwrap();
3000 let sk = Fr::from(8923u64);
3001 let partial = Fr::from(243523u64);
3002 let complete = pxe.register_account(&sk, &partial).await.unwrap();
3003 assert_eq!(complete.partial_address, partial);
3004
3005 let accounts = pxe.get_registered_accounts().await.unwrap();
3006 assert_eq!(accounts.len(), 1);
3007 assert_eq!(accounts[0].address, complete.address);
3008 }
3009
3010 #[tokio::test]
3011 async fn register_and_retrieve_contract() {
3012 use aztec_pxe_client::RegisterContractRequest;
3013
3014 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
3015 .await
3016 .unwrap();
3017 let artifact = sample_public_artifact();
3018 let class_id = compute_contract_class_id_from_artifact(&artifact).unwrap();
3019 let inner = ContractInstance {
3020 version: 1,
3021 salt: Fr::from(1u64),
3022 deployer: AztecAddress::zero(),
3023 current_contract_class_id: class_id,
3024 original_contract_class_id: class_id,
3025 initialization_hash: Fr::zero(),
3026 public_keys: PublicKeys::default(),
3027 };
3028 let address = compute_contract_address_from_instance(&inner).unwrap();
3029 let instance = ContractInstanceWithAddress { address, inner };
3030
3031 pxe.register_contract_class(&artifact).await.unwrap();
3032
3033 pxe.register_contract(RegisterContractRequest {
3034 instance: instance.clone(),
3035 artifact: None,
3036 })
3037 .await
3038 .unwrap();
3039
3040 let retrieved = pxe.get_contract_instance(&instance.address).await.unwrap();
3041 assert!(retrieved.is_some());
3042
3043 let contracts = pxe.get_contracts().await.unwrap();
3044 assert_eq!(contracts.len(), 1);
3045 }
3046
3047 #[tokio::test]
3048 async fn sender_management() {
3049 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
3050 .await
3051 .unwrap();
3052 let sender = AztecAddress::from(99u64);
3053
3054 pxe.register_sender(&sender).await.unwrap();
3055 let senders = pxe.get_senders().await.unwrap();
3056 assert_eq!(senders.len(), 1);
3057
3058 pxe.remove_sender(&sender).await.unwrap();
3059 let senders = pxe.get_senders().await.unwrap();
3060 assert!(senders.is_empty());
3061 }
3062
3063 #[tokio::test]
3064 async fn block_header_sync() {
3065 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
3066 .await
3067 .unwrap();
3068 let header = pxe.get_synced_block_header().await.unwrap();
3069 assert!(header.data.is_object());
3070 }
3071
3072 #[tokio::test]
3073 async fn register_sender_ignores_registered_accounts() {
3074 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
3075 .await
3076 .unwrap();
3077 let sk = Fr::from(77u64);
3078 let partial = Fr::from(123u64);
3079 let complete = pxe.register_account(&sk, &partial).await.unwrap();
3080
3081 pxe.register_sender(&complete.address).await.unwrap();
3082
3083 assert!(pxe.get_senders().await.unwrap().is_empty());
3084 }
3085
3086 #[tokio::test]
3087 async fn register_contract_validates_artifact_and_registers_public_signatures() {
3088 use aztec_pxe_client::RegisterContractRequest;
3089
3090 let node = MockNode::default();
3091 let artifact = sample_public_artifact();
3092 let class_id = compute_contract_class_id_from_artifact(&artifact).unwrap();
3093 let inner = ContractInstance {
3094 version: 1,
3095 salt: Fr::from(5u64),
3096 deployer: AztecAddress::zero(),
3097 current_contract_class_id: class_id,
3098 original_contract_class_id: class_id,
3099 initialization_hash: Fr::zero(),
3100 public_keys: PublicKeys::default(),
3101 };
3102 let address = compute_contract_address_from_instance(&inner).unwrap();
3103 let instance = ContractInstanceWithAddress { address, inner };
3104
3105 let pxe = EmbeddedPxe::create_ephemeral(node).await.unwrap();
3106 pxe.register_contract(RegisterContractRequest {
3107 instance,
3108 artifact: Some(artifact),
3109 })
3110 .await
3111 .unwrap();
3112
3113 let registered = pxe.node().registered_signatures.lock().unwrap().clone();
3114 assert_eq!(registered, vec!["increment(Field)".to_owned()]);
3115 }
3116
3117 #[tokio::test]
3118 async fn register_contract_rejects_missing_artifact_for_unknown_class() {
3119 use aztec_pxe_client::RegisterContractRequest;
3120
3121 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
3122 .await
3123 .unwrap();
3124 let instance = ContractInstanceWithAddress {
3125 address: AztecAddress::from(42u64),
3126 inner: ContractInstance {
3127 version: 1,
3128 salt: Fr::from(1u64),
3129 deployer: AztecAddress::zero(),
3130 current_contract_class_id: Fr::from(999u64),
3131 original_contract_class_id: Fr::from(999u64),
3132 initialization_hash: Fr::zero(),
3133 public_keys: PublicKeys::default(),
3134 },
3135 };
3136
3137 let err = pxe
3138 .register_contract(RegisterContractRequest {
3139 instance,
3140 artifact: None,
3141 })
3142 .await
3143 .unwrap_err();
3144
3145 assert!(err.to_string().contains("artifact not found"));
3146 }
3147
3148 #[tokio::test]
3149 async fn get_private_events_returns_empty_when_no_events() {
3150 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
3151 .await
3152 .unwrap();
3153 let events = pxe
3154 .get_private_events(
3155 &EventSelector(Fr::from(1u64)),
3156 PrivateEventFilter {
3157 contract_address: AztecAddress::from(1u64),
3158 scopes: vec![AztecAddress::from(99u64)],
3159 ..Default::default()
3160 },
3161 )
3162 .await
3163 .unwrap();
3164 assert!(events.is_empty());
3165 }
3166
3167 #[tokio::test]
3168 async fn get_private_events_rejects_empty_scopes() {
3169 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
3170 .await
3171 .unwrap();
3172 let result = pxe
3173 .get_private_events(
3174 &EventSelector(Fr::from(1u64)),
3175 PrivateEventFilter {
3176 contract_address: AztecAddress::from(1u64),
3177 scopes: vec![],
3178 ..Default::default()
3179 },
3180 )
3181 .await;
3182 assert!(result.is_err());
3183 }
3184
3185 #[tokio::test]
3186 async fn anchor_block_store_is_populated_after_create() {
3187 let pxe = EmbeddedPxe::create_ephemeral(MockNode::default())
3188 .await
3189 .unwrap();
3190 let anchor = pxe.anchor_block_store().get_block_header().await.unwrap();
3191 assert!(anchor.is_some());
3192 }
3193}