aztec_pxe/kernel/
oracle.rs1use aztec_core::abi::{ContractArtifact, FunctionSelector};
7use aztec_core::error::Error;
8use aztec_core::hash::{
9 compute_artifact_hash, compute_private_functions_root_from_artifact,
10 compute_public_bytecode_commitment, compute_salted_initialization_hash,
11};
12use aztec_core::types::{AztecAddress, Fr};
13use aztec_node_client::AztecNode;
14
15use crate::stores::{ContractStore, KeyStore};
16
17pub struct PrivateKernelOracle<'a, N: AztecNode> {
26 node: &'a N,
27 contract_store: &'a ContractStore,
28 key_store: &'a KeyStore,
29 block_hash: Fr,
31}
32
33impl<'a, N: AztecNode> PrivateKernelOracle<'a, N> {
34 pub fn new(
35 node: &'a N,
36 contract_store: &'a ContractStore,
37 key_store: &'a KeyStore,
38 block_hash: Fr,
39 ) -> Self {
40 Self {
41 node,
42 contract_store,
43 key_store,
44 block_hash,
45 }
46 }
47
48 pub async fn get_contract_address_preimage(
53 &self,
54 address: &AztecAddress,
55 ) -> Result<serde_json::Value, Error> {
56 let instance = if let Some(instance) = self.contract_store.get_instance(address).await? {
57 instance
58 } else {
59 match self.node.get_contract(address).await? {
60 Some(instance) => instance,
61 None => {
62 return Err(Error::InvalidData(format!(
63 "contract instance not found for address {address}"
64 )))
65 }
66 }
67 };
68
69 let salted_initialization_hash = compute_salted_initialization_hash(
70 instance.inner.salt,
71 instance.inner.initialization_hash,
72 instance.inner.deployer,
73 );
74
75 Ok(serde_json::json!({
76 "address": instance.address,
77 "saltedInitializationHash": salted_initialization_hash,
78 "version": instance.inner.version,
79 "salt": instance.inner.salt,
80 "deployer": instance.inner.deployer,
81 "currentContractClassId": instance.inner.current_contract_class_id,
82 "originalContractClassId": instance.inner.original_contract_class_id,
83 "initializationHash": instance.inner.initialization_hash,
84 "publicKeys": instance.inner.public_keys,
85 }))
86 }
87
88 fn contract_class_preimage(artifact: &ContractArtifact) -> Result<serde_json::Value, Error> {
89 let artifact_hash = compute_artifact_hash(artifact);
90 let private_functions_root = compute_private_functions_root_from_artifact(artifact)?;
91 let public_bytecode_commitment =
92 compute_public_bytecode_commitment(&extract_packed_public_bytecode(artifact));
93
94 Ok(serde_json::json!({
95 "artifactHash": artifact_hash,
96 "privateFunctionsRoot": private_functions_root,
97 "publicBytecodeCommitment": public_bytecode_commitment,
98 }))
99 }
100
101 pub async fn get_contract_class_id_preimage(
105 &self,
106 class_id: &Fr,
107 ) -> Result<serde_json::Value, Error> {
108 if let Some(artifact) = self.contract_store.get_artifact(class_id).await? {
109 return Self::contract_class_preimage(&artifact);
110 }
111
112 match self.node.get_contract_class(class_id).await? {
113 Some(class_data) => Ok(class_data),
114 None => Err(Error::InvalidData(format!(
115 "contract class not found for id {class_id}"
116 ))),
117 }
118 }
119
120 pub async fn get_function_membership_witness(
124 &self,
125 class_id: &Fr,
126 function_selector: &Fr,
127 ) -> Result<serde_json::Value, Error> {
128 Err(Error::InvalidData(format!(
129 "private function membership witness for class {class_id} selector {function_selector} is not implemented yet"
130 )))
131 }
132
133 pub async fn get_vk_membership_witness(
137 &self,
138 vk_hash: &Fr,
139 ) -> Result<serde_json::Value, Error> {
140 Err(Error::InvalidData(format!(
141 "protocol VK membership witness for vk hash {vk_hash} is not implemented yet"
142 )))
143 }
144
145 pub async fn get_note_hash_membership_witness(
147 &self,
148 note_hash: &Fr,
149 ) -> Result<Option<serde_json::Value>, Error> {
150 self.node
152 .get_note_hash_membership_witness(0, note_hash)
153 .await
154 }
155
156 pub async fn get_nullifier_membership_witness(
158 &self,
159 nullifier: &Fr,
160 ) -> Result<Option<serde_json::Value>, Error> {
161 self.node
162 .get_nullifier_membership_witness(0, nullifier)
163 .await
164 }
165
166 pub async fn get_note_hash_tree_root(&self) -> Result<Fr, Error> {
168 let header = self.node.get_block_header(0).await?;
169 if let Some(root) = header
171 .pointer("/state/partial/noteHashTree/root")
172 .and_then(|v| v.as_str())
173 {
174 Fr::from_hex(root)
175 } else {
176 Err(Error::InvalidData(
177 "note hash tree root not found in block header".into(),
178 ))
179 }
180 }
181
182 pub async fn get_master_secret_key(&self, pk_hash: &Fr) -> Result<Option<Fr>, Error> {
184 self.key_store.get_secret_key(pk_hash).await
185 }
186
187 pub async fn get_block_hash_membership_witness(
189 &self,
190 block_hash: &Fr,
191 ) -> Result<Option<serde_json::Value>, Error> {
192 self.node
193 .get_block_hash_membership_witness(0, block_hash)
194 .await
195 }
196
197 pub async fn get_updated_class_id_hints(
199 &self,
200 address: &AztecAddress,
201 ) -> Result<serde_json::Value, Error> {
202 Err(Error::InvalidData(format!(
203 "updated class-id hints for contract {address} are not implemented yet"
204 )))
205 }
206
207 pub async fn get_debug_function_name(
209 &self,
210 contract_address: &AztecAddress,
211 function_selector: &FunctionSelector,
212 ) -> Result<Option<String>, Error> {
213 if let Some(instance) = self.contract_store.get_instance(contract_address).await? {
214 if let Some(artifact) = self
215 .contract_store
216 .get_artifact(&instance.inner.current_contract_class_id)
217 .await?
218 {
219 for func in &artifact.functions {
220 if let Some(ref sel) = func.selector {
221 if sel == function_selector {
222 return Ok(Some(func.name.clone()));
223 }
224 }
225 }
226 }
227 }
228 Ok(None)
229 }
230
231 pub fn block_hash(&self) -> &Fr {
233 &self.block_hash
234 }
235}
236
237fn extract_packed_public_bytecode(artifact: &ContractArtifact) -> Vec<u8> {
238 artifact
240 .functions
241 .iter()
242 .find(|f| {
243 f.function_type == aztec_core::abi::FunctionType::Public && f.name == "public_dispatch"
244 })
245 .and_then(|f| f.bytecode.as_deref())
246 .map(|bc| decode_bytecode(bc))
247 .unwrap_or_default()
248}
249
250fn decode_bytecode(encoded: &str) -> Vec<u8> {
251 let Some(hex) = encoded.strip_prefix("0x") else {
252 return Vec::new();
253 };
254 let mut bytes = Vec::with_capacity(hex.len() / 2);
255 let mut chars = hex.as_bytes().chunks_exact(2);
256 for pair in &mut chars {
257 if let Ok(pair_str) = std::str::from_utf8(pair) {
258 if let Ok(byte) = u8::from_str_radix(pair_str, 16) {
259 bytes.push(byte);
260 } else {
261 return Vec::new();
262 }
263 } else {
264 return Vec::new();
265 }
266 }
267 if !chars.remainder().is_empty() {
268 return Vec::new();
269 }
270 bytes
271}