From 1ca2fe1f43bcf2b35c3f8f14abc33e7742a73d7b Mon Sep 17 00:00:00 2001 From: Lukasz Kozlowski Date: Tue, 5 Nov 2024 02:51:33 +0100 Subject: [PATCH] Refactor proof --- src/proof.rs | 190 ++++++++++++++++++--------------------------------- 1 file changed, 66 insertions(+), 124 deletions(-) diff --git a/src/proof.rs b/src/proof.rs index cf8b1ecd..02d4e7ce 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -26,6 +26,29 @@ impl From for Direction { } } +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum ProofVerifyError { + #[error("{0}")] + Other(String), + #[error("{0}")] + Parse(String), + #[error("{0}")] + RPC(#[from] iamgroot::jsonrpc::Error), + // iamgroot::jsonrpc::Error(#[from] reqwest::Error), +} + +impl From for iamgroot::jsonrpc::Error { + fn from(error: ProofVerifyError) -> Self { + match error { + ProofVerifyError::Other(e) => jsonrpc::Error::new(-32700, format!("{e}").to_string()), + ProofVerifyError::Parse(e) => jsonrpc::Error::new(-32701, format!("{e}").to_string()), + ProofVerifyError::RPC(e) => e, + } + } +} + impl GetProofResult { pub fn verify( &self, @@ -37,8 +60,13 @@ impl GetProofResult { let contract_data = self.contract_data.as_ref().ok_or( jsonrpc::Error::new(-32700, "No contract data found".to_string()), )?; - self.verify_storage_proofs(contract_data, key, value)?; - self.verify_contract_proof(contract_data, global_root, contract_address) + + let storage_proofs = &contract_data.storage_proofs.as_ref().ok_or( + jsonrpc::Error::new(-32700, "No storage proof found".to_string()), + )?; + self.verify_storage_proofs(contract_data, key, value, storage_proofs)?; + self.verify_contract_proof(contract_data, global_root, contract_address)?; + Ok(()) } fn verify_storage_proofs( @@ -46,29 +74,20 @@ impl GetProofResult { contract_data: &ContractData, key: StorageKey, value: Felt, - ) -> Result<(), jsonrpc::Error> { + storage_proofs: &Vec>, + ) -> Result<(), ProofVerifyError> { let root = &contract_data.root; - let storage_proofs = &contract_data.storage_proofs.as_ref().ok_or( - jsonrpc::Error::new(-32700, "No storage proof found".to_string()), - )?[0]; - - match Self::parse_proof(key.as_ref(), value, storage_proofs)? { + let sp = &storage_proofs[0]; + match Self::parse_proof(key.as_ref(), value, &sp)? { Some(computed_root) if computed_root.as_ref() == root.as_ref() => { Ok(()) } Some(computed_root) => { - Err(jsonrpc::Error::new( - -32700, - format!( - "Proof invalid:\nprovided-root -> {}\ncomputed-root -> {}\n", - root.as_ref(), computed_root.as_ref() - ), - )) + Err(ProofVerifyError::Other( + format!("Proof invalid:\nprovided-root -> {root:?}\ncomputed-root -> {computed_root:?}\n")) + ) }, - None => Err(jsonrpc::Error::new( - -32700, - format!("Proof invalid for root -> {}\n", root.as_ref()), - )), + None => Err(ProofVerifyError::Other(format!("Proof invalid for root -> {root:?}\n"))), } } @@ -77,7 +96,7 @@ impl GetProofResult { contract_data: &ContractData, global_root: Felt, contract_address: Address, - ) -> Result<(), jsonrpc::Error> { + ) -> Result<(), ProofVerifyError> { let state_hash = Self::calculate_contract_state_hash(contract_data)?; match Self::parse_proof( @@ -87,10 +106,7 @@ impl GetProofResult { )? { Some(storage_commitment) => { let class_commitment = self.class_commitment.as_ref().ok_or( - jsonrpc::Error::new( - -32700, - "No class commitment".to_string(), - ), + ProofVerifyError::Other("No class commitment".to_string()) )?; let parsed_global_root = Self::calculate_global_root( class_commitment, @@ -103,30 +119,21 @@ impl GetProofResult { ) })?; let state_commitment = self.state_commitment.as_ref().ok_or( - jsonrpc::Error::new( - -32700, - "No state commitment".to_string(), - ), + ProofVerifyError::Other("No state state".to_string()) )?; if state_commitment.as_ref() == parsed_global_root.as_ref() && global_root.as_ref() == parsed_global_root.as_ref() { Ok(()) } else { - Err(jsonrpc::Error::new( - -32700, - format!("Proof invalid:\nstate commitment -> {}\nparsed global root -> {}\n global root -> {}", - state_commitment.as_ref(), parsed_global_root.as_ref(), global_root.as_ref()) + Err(ProofVerifyError::Other( + format!( + "Proof invalid:\nstate commitment -> {state_commitment:?}\nparsed global root -> {parsed_global_root:?}\n global root -> {global_root:?}" + ) )) } } - None => Err(jsonrpc::Error::new( - -32700, - format!( - "Could not parse global root for root: {}", - global_root.as_ref() - ), - )), + None => Err(ProofVerifyError::Parse(format!("Could not parse global root for root: {}", global_root.as_ref()))) } } @@ -136,32 +143,12 @@ impl GetProofResult { // The contract state hash is defined as H(H(H(hash, root), nonce), CONTRACT_STATE_HASH_VERSION) const CONTRACT_STATE_HASH_VERSION: FieldElement = FieldElement::ZERO; let hash = pedersen_hash( - &FieldElement::from_hex(contract_data.class_hash.as_ref()) - .map_err(|_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - })?, - &FieldElement::from_hex(contract_data.root.as_ref()).map_err( - |_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - }, - )?, + &Self::create_field_element_from_hex(&contract_data.class_hash.as_ref())?, + &Self::create_field_element_from_hex(&contract_data.root.as_ref())?, ); let hash = pedersen_hash( &hash, - &FieldElement::from_hex(contract_data.nonce.as_ref()).map_err( - |_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - }, - )?, + &Self::create_field_element_from_hex(&contract_data.nonce.as_ref())?, ); let hash = pedersen_hash(&hash, &CONTRACT_STATE_HASH_VERSION); Felt::try_new(&format!("0x{:x}", hash)).map_err(|_| { @@ -180,22 +167,8 @@ impl GetProofResult { FieldElement::from_bytes_be_slice(b"STARKNET_STATE_V0"); let hash = poseidon_hash_many(&[ global_state_ver, - FieldElement::from_hex(storage_commitment.as_ref()).map_err( - |_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - }, - )?, - FieldElement::from_hex(class_commitment.as_ref()).map_err( - |_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - }, - )?, + Self::create_field_element_from_hex(&storage_commitment.as_ref())?, + Self::create_field_element_from_hex(&class_commitment.as_ref())?, ]); Felt::try_new(&format!("0x{:x}", hash)).map_err(|_| { jsonrpc::Error::new( @@ -209,23 +182,13 @@ impl GetProofResult { key: impl Into, value: Felt, proof: &[Node], - ) -> Result, jsonrpc::Error> { - let key = FieldElement::from_hex(&key.into()).map_err(|_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - })?; + ) -> Result, ProofVerifyError> { + let key = Self::create_field_element_from_hex(&key.into())?; let key = felt_to_bits(&key.to_bytes_be()); if key.len() != 251 { return Ok(None); } - let value = FieldElement::from_hex(value.as_ref()).map_err(|_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - })?; + let value = Self::create_field_element_from_hex(&value.as_ref())?; // initialized to the value so if the last node // in the proof is a binary node we can still verify let (mut hold, mut path_len) = (value, 0); @@ -236,22 +199,8 @@ impl GetProofResult { edge: EdgeNodeEdge { child, path }, }) => { // calculate edge hash given by provider - let child_felt = FieldElement::from_hex(child.as_ref()) - .map_err(|_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - })?; - let path_value = FieldElement::from_hex( - path.value.as_ref(), - ) - .map_err(|_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - })?; + let child_felt = Self::create_field_element_from_hex(&child.as_ref())?; + let path_value = Self::create_field_element_from_hex(&path.value.as_ref())?; let provided_hash = pedersen_hash(&child_felt, &path_value) + FieldElement::from(path.len as u64); if i == 0 { @@ -280,21 +229,8 @@ impl GetProofResult { binary: BinaryNodeBinary { left, right }, }) => { path_len += 1; - let left = FieldElement::from_hex(left.as_ref()).map_err( - |_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - }, - )?; - let right = FieldElement::from_hex(right.as_ref()) - .map_err(|_| { - jsonrpc::Error::new( - -32701, - "Failed to create Field Element".to_string(), - ) - })?; + let left = Self::create_field_element_from_hex(&left.as_ref())?; + let right = Self::create_field_element_from_hex(&right.as_ref())?; // identify path direction for this node let expected_hash = match Direction::from(key[251 - path_len as usize]) { @@ -313,6 +249,10 @@ impl GetProofResult { Ok(Some(Felt::try_new(&format!("0x{:x}", hold))?)) } + + fn create_field_element_from_hex(hex: &str) -> Result { + FieldElement::from_hex(hex).map_err(|_| ProofVerifyError::Parse("Failed to create Field Element".to_string())) + } } #[cfg(test)] mod tests { @@ -520,9 +460,10 @@ mod tests { state_commitment: Some(Felt::try_new("0x0").unwrap()) }; let contract_data = storage_proof.contract_data.as_ref().unwrap(); + let proofs = contract_data.storage_proofs.as_ref().unwrap(); assert!(storage_proof - .verify_storage_proofs(contract_data, key, value) + .verify_storage_proofs(contract_data, key, value, proofs) .is_ok()); } @@ -556,9 +497,10 @@ mod tests { state_commitment: Some(Felt::try_new("0x0").unwrap()), }; let contract_data = storage_proof.contract_data.as_ref().unwrap(); + let proofs = contract_data.storage_proofs.as_ref().unwrap(); assert!(storage_proof - .verify_storage_proofs(contract_data, key, value) + .verify_storage_proofs(contract_data, key, value, proofs) .is_err()); }