Skip to content

Commit

Permalink
Refactor proof
Browse files Browse the repository at this point in the history
  • Loading branch information
LKozlowski committed Nov 5, 2024
1 parent 0ec5ff0 commit 1ca2fe1
Showing 1 changed file with 66 additions and 124 deletions.
190 changes: 66 additions & 124 deletions src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,29 @@ impl From<bool> 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<ProofVerifyError> 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,
Expand All @@ -37,38 +60,34 @@ 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(
&self,
contract_data: &ContractData,
key: StorageKey,
value: Felt,
) -> Result<(), jsonrpc::Error> {
storage_proofs: &Vec<Vec<Node>>,
) -> 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"))),
}
}

Expand All @@ -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(
Expand All @@ -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,
Expand All @@ -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())))
}
}

Expand All @@ -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(|_| {
Expand All @@ -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(
Expand All @@ -209,23 +182,13 @@ impl GetProofResult {
key: impl Into<String>,
value: Felt,
proof: &[Node],
) -> Result<Option<Felt>, jsonrpc::Error> {
let key = FieldElement::from_hex(&key.into()).map_err(|_| {
jsonrpc::Error::new(
-32701,
"Failed to create Field Element".to_string(),
)
})?;
) -> Result<Option<Felt>, 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);
Expand All @@ -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 {
Expand Down Expand Up @@ -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]) {
Expand All @@ -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, ProofVerifyError> {
FieldElement::from_hex(hex).map_err(|_| ProofVerifyError::Parse("Failed to create Field Element".to_string()))
}
}
#[cfg(test)]
mod tests {
Expand Down Expand Up @@ -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());
}

Expand Down Expand Up @@ -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());
}

Expand Down

0 comments on commit 1ca2fe1

Please sign in to comment.