Skip to content

Commit a598c68

Browse files
committed
Deserialize merkle proof, added verify_merkle_proof()
1 parent 4160da8 commit a598c68

2 files changed

Lines changed: 70 additions & 14 deletions

File tree

operator/mina_account/lib/src/lib.rs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
use kimchi::o1_utils::FieldHelpers;
2+
use merkle_verifier::verify_merkle_proof;
23
use mina_curves::pasta::Fp;
4+
use mina_tree::MerklePath;
5+
6+
mod merkle_verifier;
37

48
// TODO(xqft): check sizes
59
const MAX_PROOF_SIZE: usize = 16 * 1024;
610
const MAX_PUB_INPUT_SIZE: usize = 6 * 1024;
711
const HASH_SIZE: usize = 32;
12+
const MERKLE_PATH_LEN_SIZE: usize = 4;
813

914
#[no_mangle]
1015
pub extern "C" fn verify_account_inclusion_ffi(
@@ -35,7 +40,7 @@ pub extern "C" fn verify_account_inclusion_ffi(
3540
}
3641
};
3742

38-
todo!()
43+
verify_merkle_proof(account_hash, merkle_proof, merkle_root)
3944
}
4045

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

61-
pub fn parse_proof(proof_bytes: &[u8]) -> Result<(), String> {
62-
todo!()
63-
// std::str::from_utf8(proof_bytes)
64-
// .map_err(|err| err.to_string())
65-
// .and_then(|base64| {
66-
// BASE64_URL_SAFE
67-
// .decode(base64)
68-
// .map_err(|err| err.to_string())
69-
// })
70-
// .and_then(|binprot| {
71-
// MinaBaseProofStableV2::binprot_read(&mut binprot.as_slice())
72-
// .map_err(|err| err.to_string())
73-
// })
66+
pub fn parse_proof(proof_bytes: &[u8]) -> Result<Vec<MerklePath>, String> {
67+
let merkle_path_bytes = proof_bytes
68+
.get(MERKLE_PATH_LEN_SIZE..)
69+
.ok_or("Failed to slice merkle path".to_string())?
70+
.chunks_exact(HASH_SIZE + 1);
71+
72+
if !merkle_path_bytes.remainder().is_empty() {
73+
return Err(format!(
74+
"Merkle path bytes not a multiple of HASH_SIZE + 1 ({})",
75+
HASH_SIZE + 1
76+
));
77+
}
78+
79+
merkle_path_bytes
80+
.map(|bytes| {
81+
let left_or_right = bytes
82+
.first()
83+
.ok_or("left_or_right byte not found".to_string())?;
84+
let hash = Fp::from_bytes(bytes).map_err(|err| {
85+
format!("Failed to convert merkle hash into field element: {err}")
86+
})?;
87+
match left_or_right {
88+
0 => Ok(MerklePath::Left(hash)),
89+
1 => Ok(MerklePath::Right(hash)),
90+
_ => Err("Unexpected left_or_right byte".to_string()),
91+
}
92+
})
93+
.collect()
7494
}
7595

7696
#[cfg(test)]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use mina_curves::pasta::Fp;
2+
use mina_p2p_messages::v2::hash_with_kimchi;
3+
use mina_tree::MerklePath;
4+
use std::fmt::Write;
5+
6+
/// Based on OpenMina's implementation
7+
/// https://github.com/openmina/openmina/blob/d790af59a8bd815893f7773f659351b79ed87648/ledger/src/account/account.rs#L1444
8+
pub fn verify_merkle_proof(merkle_leaf: Fp, merkle_path: Vec<MerklePath>, merkle_root: Fp) -> bool {
9+
let mut param = String::with_capacity(16);
10+
11+
let calculated_root =
12+
merkle_path
13+
.iter()
14+
.enumerate()
15+
.fold(merkle_leaf, |accum, (depth, path)| {
16+
let hashes = match path {
17+
MerklePath::Left(right) => [accum, *right],
18+
MerklePath::Right(left) => [*left, accum],
19+
};
20+
21+
param.clear();
22+
write!(&mut param, "MinaMklTree{:03}", depth).unwrap();
23+
24+
hash_with_kimchi(param.as_str(), &hashes)
25+
});
26+
27+
calculated_root == merkle_root
28+
}
29+
30+
#[cfg(test)]
31+
mod test {
32+
#[test]
33+
fn test_verify_merkle_proof() {
34+
todo!();
35+
}
36+
}

0 commit comments

Comments
 (0)