Skip to content

Commit

Permalink
Deserialize merkle proof, added verify_merkle_proof()
Browse files Browse the repository at this point in the history
  • Loading branch information
xqft committed Aug 19, 2024
1 parent 4160da8 commit a598c68
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 14 deletions.
48 changes: 34 additions & 14 deletions operator/mina_account/lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use kimchi::o1_utils::FieldHelpers;
use merkle_verifier::verify_merkle_proof;
use mina_curves::pasta::Fp;
use mina_tree::MerklePath;

mod merkle_verifier;

// TODO(xqft): check sizes
const MAX_PROOF_SIZE: usize = 16 * 1024;
const MAX_PUB_INPUT_SIZE: usize = 6 * 1024;
const HASH_SIZE: usize = 32;
const MERKLE_PATH_LEN_SIZE: usize = 4;

#[no_mangle]
pub extern "C" fn verify_account_inclusion_ffi(
Expand Down Expand Up @@ -35,7 +40,7 @@ pub extern "C" fn verify_account_inclusion_ffi(
}
};

todo!()
verify_merkle_proof(account_hash, merkle_proof, merkle_root)
}

pub fn parse_hash(pub_inputs: &[u8], offset: &mut usize) -> Result<Fp, String> {
Expand All @@ -58,19 +63,34 @@ pub fn parse_pub_inputs(pub_inputs: &[u8]) -> Result<(Fp, Fp), String> {
Ok((merkle_root, account_hash))
}

pub fn parse_proof(proof_bytes: &[u8]) -> Result<(), String> {
todo!()
// std::str::from_utf8(proof_bytes)
// .map_err(|err| err.to_string())
// .and_then(|base64| {
// BASE64_URL_SAFE
// .decode(base64)
// .map_err(|err| err.to_string())
// })
// .and_then(|binprot| {
// MinaBaseProofStableV2::binprot_read(&mut binprot.as_slice())
// .map_err(|err| err.to_string())
// })
pub fn parse_proof(proof_bytes: &[u8]) -> Result<Vec<MerklePath>, String> {
let merkle_path_bytes = proof_bytes
.get(MERKLE_PATH_LEN_SIZE..)
.ok_or("Failed to slice merkle path".to_string())?
.chunks_exact(HASH_SIZE + 1);

if !merkle_path_bytes.remainder().is_empty() {
return Err(format!(
"Merkle path bytes not a multiple of HASH_SIZE + 1 ({})",
HASH_SIZE + 1
));
}

merkle_path_bytes
.map(|bytes| {
let left_or_right = bytes
.first()
.ok_or("left_or_right byte not found".to_string())?;
let hash = Fp::from_bytes(bytes).map_err(|err| {
format!("Failed to convert merkle hash into field element: {err}")
})?;
match left_or_right {
0 => Ok(MerklePath::Left(hash)),
1 => Ok(MerklePath::Right(hash)),
_ => Err("Unexpected left_or_right byte".to_string()),
}
})
.collect()
}

#[cfg(test)]
Expand Down
36 changes: 36 additions & 0 deletions operator/mina_account/lib/src/merkle_verifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use mina_curves::pasta::Fp;
use mina_p2p_messages::v2::hash_with_kimchi;
use mina_tree::MerklePath;
use std::fmt::Write;

/// Based on OpenMina's implementation
/// https://github.com/openmina/openmina/blob/d790af59a8bd815893f7773f659351b79ed87648/ledger/src/account/account.rs#L1444
pub fn verify_merkle_proof(merkle_leaf: Fp, merkle_path: Vec<MerklePath>, merkle_root: Fp) -> bool {
let mut param = String::with_capacity(16);

let calculated_root =
merkle_path
.iter()
.enumerate()
.fold(merkle_leaf, |accum, (depth, path)| {
let hashes = match path {
MerklePath::Left(right) => [accum, *right],
MerklePath::Right(left) => [*left, accum],
};

param.clear();
write!(&mut param, "MinaMklTree{:03}", depth).unwrap();

hash_with_kimchi(param.as_str(), &hashes)
});

calculated_root == merkle_root
}

#[cfg(test)]
mod test {
#[test]
fn test_verify_merkle_proof() {
todo!();
}
}

0 comments on commit a598c68

Please sign in to comment.