From 236a0d0d47cff66295b455e509d15083a0fb8d85 Mon Sep 17 00:00:00 2001 From: Giacomo Pasini <21265557+zeegomo@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:42:49 +0100 Subject: [PATCH] compact serialization (#46) --- emmarin/cl/cl/src/cl/merkle.rs | 8 +- emmarin/cl/ledger/src/ledger.rs | 8 +- emmarin/cl/ledger_proof_statements/Cargo.toml | 2 +- .../cl/ledger_proof_statements/src/ledger.rs | 103 +++++++++++++++++- .../ledger_validity_proof/ledger/src/main.rs | 21 +++- 5 files changed, 129 insertions(+), 13 deletions(-) diff --git a/emmarin/cl/cl/src/cl/merkle.rs b/emmarin/cl/cl/src/cl/merkle.rs index 8aecb78..6b0b5db 100644 --- a/emmarin/cl/cl/src/cl/merkle.rs +++ b/emmarin/cl/cl/src/cl/merkle.rs @@ -19,7 +19,7 @@ pub fn leaf(data: &[u8]) -> [u8; 32] { hasher.finalize().into() } -pub fn node(a: [u8; 32], b: [u8; 32]) -> [u8; 32] { +pub fn node(a: impl AsRef<[u8]>, b: impl AsRef<[u8]>) -> [u8; 32] { let mut hasher = Sha256::new(); hasher.update(b"NOMOS_MERKLE_NODE"); hasher.update(a); @@ -55,12 +55,12 @@ pub fn path_root(leaf: [u8; 32], path: &[PathNode]) -> [u8; 32] { let mut computed_hash = leaf; for path_node in path { - match path_node { + match &path_node { PathNode::Left(sibling_hash) => { - computed_hash = node(*sibling_hash, computed_hash); + computed_hash = node(sibling_hash, computed_hash); } PathNode::Right(sibling_hash) => { - computed_hash = node(computed_hash, *sibling_hash); + computed_hash = node(computed_hash, sibling_hash); } } } diff --git a/emmarin/cl/ledger/src/ledger.rs b/emmarin/cl/ledger/src/ledger.rs index 3b06621..7bf35a9 100644 --- a/emmarin/cl/ledger/src/ledger.rs +++ b/emmarin/cl/ledger/src/ledger.rs @@ -1,6 +1,8 @@ use std::collections::BTreeMap; -use ledger_proof_statements::ledger::{LedgerBundleWitness, LedgerProofPrivate, LedgerProofPublic}; +use ledger_proof_statements::ledger::{ + CompactNullifierProofs, LedgerBundleWitness, LedgerProofPrivate, LedgerProofPublic, +}; use crate::bundle::ProvedBundle; use cl::zone_layer::{ledger::LedgerState, notes::ZoneId}; @@ -49,7 +51,7 @@ impl ProvedLedgerTransition { let ledger_bundle = LedgerBundleWitness { bundle, cm_root_proofs, - nf_proofs, + nf_proofs: CompactNullifierProofs::from_paths(nf_proofs), }; witness.bundles.push(ledger_bundle) @@ -93,3 +95,5 @@ impl ProvedLedgerTransition { .is_ok() } } + + diff --git a/emmarin/cl/ledger_proof_statements/Cargo.toml b/emmarin/cl/ledger_proof_statements/Cargo.toml index 50bd1ca..4a2f051 100644 --- a/emmarin/cl/ledger_proof_statements/Cargo.toml +++ b/emmarin/cl/ledger_proof_statements/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" [dependencies] cl = { path = "../cl" } serde = { version = "1.0", features = ["derive"] } -sha2 = "0.10" +sha2 = "0.10" \ No newline at end of file diff --git a/emmarin/cl/ledger_proof_statements/src/ledger.rs b/emmarin/cl/ledger_proof_statements/src/ledger.rs index 4766c89..3bc5e45 100644 --- a/emmarin/cl/ledger_proof_statements/src/ledger.rs +++ b/emmarin/cl/ledger_proof_statements/src/ledger.rs @@ -29,7 +29,7 @@ pub struct LedgerProofPrivate { pub struct LedgerBundleWitness { pub bundle: BundlePublic, pub cm_root_proofs: BTreeMap<[u8; 32], merkle::Path>, - pub nf_proofs: Vec, + pub nf_proofs: CompactNullifierProofs, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -37,3 +37,104 @@ pub struct CrossZoneBundle { pub id: BundleId, pub zones: Vec, } + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct CompactNullifierProofs { + pub siblings: Vec, + pub paths: Vec<[u8; 32]>, +} + +impl CompactNullifierProofs { + pub fn from_paths(input: Vec) -> Self { + let mut siblings = Vec::with_capacity(input.len()); + let mut paths = Vec::with_capacity(input.len()); + + for path in input { + let mut path_bits = [0u8; 32]; + assert_eq!(path.len(), 256); + + for (i, node) in path.iter().enumerate().rev() { + match node { + merkle::PathNode::Left(sibling) => { + siblings.extend(sibling.into_iter()); + } + merkle::PathNode::Right(sibling) => { + siblings.extend(sibling.into_iter()); + set_bit(i as u8, &mut path_bits); + } + } + } + paths.push(path_bits); + } + + Self { siblings, paths } + } + + pub fn len(&self) -> usize { + self.paths.len() + } +} + +impl IntoIterator for CompactNullifierProofs { + type Item = merkle::Path; + type IntoIter = CompactNfIterator; + + fn into_iter(self) -> CompactNfIterator { + CompactNfIterator { + siblings: self.siblings, + paths: self.paths, + } + } +} + +pub struct CompactNfIterator { + pub siblings: Vec, + pub paths: Vec<[u8; 32]>, +} + +impl<'a> Iterator for CompactNfIterator { + type Item = merkle::Path; + + fn next(&mut self) -> Option { + if self.paths.is_empty() { + return None; + } + + let path = self.paths.pop().unwrap(); + + let mut res = Vec::with_capacity(256); + + for i in 0..=255 { + if get_bit(i, path) { + res.push(merkle::PathNode::Right( + self.siblings[self.siblings.len() - 32..] + .try_into() + .unwrap(), + )) + } else { + res.push(merkle::PathNode::Left( + self.siblings[self.siblings.len() - 32..] + .try_into() + .unwrap(), + )) + }; + self.siblings.truncate(self.siblings.len() - 32); + } + + Some(res) + } +} + +fn get_bit(idx: u8, elem: [u8; 32]) -> bool { + let byte = idx / 8; + let bit_in_byte = idx - byte * 8; + + (elem[byte as usize] & (1 << bit_in_byte)) != 0 +} + +fn set_bit(idx: u8, elem: &mut [u8; 32]) { + let byte = idx / 8; + let bit_in_byte = idx - byte * 8; + + elem[byte as usize] |= 1 << bit_in_byte; +} diff --git a/emmarin/cl/ledger_validity_proof/ledger/src/main.rs b/emmarin/cl/ledger_validity_proof/ledger/src/main.rs index 7e2dab8..1459cb9 100644 --- a/emmarin/cl/ledger_validity_proof/ledger/src/main.rs +++ b/emmarin/cl/ledger_validity_proof/ledger/src/main.rs @@ -1,6 +1,6 @@ use cl::cl::merkle; -use ledger_proof_statements::{ - ledger::{CrossZoneBundle, LedgerProofPrivate, LedgerProofPublic, LedgerBundleWitness}, +use ledger_proof_statements::ledger::{ + CrossZoneBundle, LedgerBundleWitness, LedgerProofPrivate, LedgerProofPublic, }; use risc0_zkvm::{guest::env, serde}; @@ -15,12 +15,23 @@ fn main() { let mut cross_bundles = vec![]; let mut outputs = vec![]; - for LedgerBundleWitness { bundle, cm_root_proofs, nf_proofs } in bundles { - env::verify(nomos_cl_bundle_risc0_proof::BUNDLE_ID, &serde::to_vec(&bundle).unwrap()).unwrap(); + for LedgerBundleWitness { + bundle, + cm_root_proofs, + nf_proofs, + } in bundles + { + env::verify( + nomos_cl_bundle_risc0_proof::BUNDLE_ID, + &serde::to_vec(&bundle).unwrap(), + ) + .unwrap(); if let Some(ledger_update) = bundle.zone_ledger_updates.get(&id) { for past_cm_root in &ledger_update.cm_roots { - let past_cm_root_proof = cm_root_proofs.get(past_cm_root).expect("missing cm root proof"); + let past_cm_root_proof = cm_root_proofs + .get(past_cm_root) + .expect("missing cm root proof"); let expected_current_cm_root = merkle::path_root(*past_cm_root, past_cm_root_proof); assert!(old_ledger.valid_cm_root(expected_current_cm_root)) }