diff --git a/Cargo.toml b/Cargo.toml index 4f20a57..ac723f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,7 @@ bincode = "1.3.3" # TODO: Remove ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false } bytemuck = "1.13.1" +ethabi = "18.0.0" [dev-dependencies] serial_test = "3" diff --git a/src/lib.rs b/src/lib.rs index 5fefce7..8963765 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub mod hash; pub mod identity; pub mod lazy_merkle_tree; pub mod merkle_tree; +pub mod packed_proof; pub mod poseidon; pub mod poseidon_tree; pub mod protocol; diff --git a/src/packed_proof.rs b/src/packed_proof.rs new file mode 100644 index 0000000..0e0648b --- /dev/null +++ b/src/packed_proof.rs @@ -0,0 +1,112 @@ +use std::{ + fmt::Display, + str::{from_utf8, FromStr}, +}; + +use crate::protocol::Proof; +use ethabi::{decode, encode, ParamType, Token}; +use ethers_core::types::U256; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::util::{bytes_from_hex, bytes_to_hex, deserialize_bytes, serialize_bytes}; + +/// A packed proof is a representation of the ZKP in a single attribute (as opposed to array of arrays) which is easier to transport +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PackedProof(pub [u8; 256]); + +impl From for PackedProof { + fn from(proof: Proof) -> Self { + let tokens = Token::FixedArray(vec![ + Token::Uint(proof.0 .0), + Token::Uint(proof.0 .1), + Token::Uint(proof.1 .0[0]), + Token::Uint(proof.1 .0[1]), + Token::Uint(proof.1 .1[0]), + Token::Uint(proof.1 .1[1]), + Token::Uint(proof.2 .0), + Token::Uint(proof.2 .1), + ]); + + let bytes = encode(&[tokens]); + let mut encoded = [0u8; 256]; + encoded.copy_from_slice(&bytes[..256]); + Self(encoded) + } +} + +impl From for Proof { + fn from(proof: PackedProof) -> Self { + let decoded = decode(&vec![ParamType::Uint(256); 8], &proof.0).unwrap(); + let decoded_uint_array = decoded + .into_iter() + .map(|x| x.into_uint().unwrap()) + .collect::>(); + + let a = (decoded_uint_array[0], decoded_uint_array[1]); + let b = ( + [decoded_uint_array[2], decoded_uint_array[3]], + [decoded_uint_array[4], decoded_uint_array[5]], + ); + let c = (decoded_uint_array[6], decoded_uint_array[7]); + Self(a, b, c) + } +} + +impl Display for PackedProof { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let hex = bytes_to_hex::<256, 514>(&self.0); + write!( + f, + "{}", + from_utf8(&hex).expect("failed to convert to string") + ) + } +} + +impl FromStr for PackedProof { + type Err = hex::FromHexError; + + fn from_str(s: &str) -> Result { + bytes_from_hex::<256>(s).map(Self) + } +} + +impl Serialize for PackedProof { + fn serialize(&self, serializer: S) -> Result { + serialize_bytes::<256, 514, S>(serializer, &self.0) + } +} + +impl<'de> Deserialize<'de> for PackedProof { + fn deserialize>(deserializer: D) -> Result { + let bytes = deserialize_bytes::<256, _>(deserializer)?; + Ok(Self(bytes)) + } +} + +#[cfg(test)] +pub mod test { + use super::*; + + #[test] + fn test_serializing_proof_into_packed_proof() { + let proof = Proof( + (U256::from(1), U256::from(2)), + ( + [U256::from(3), U256::from(4)], + [U256::from(5), U256::from(6)], + ), + (U256::from(7), U256::from(8)), + ); + + let packed_proof = PackedProof::from(proof); + + assert_eq!(packed_proof.to_string(), "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008"); + + dbg!(packed_proof.to_string()); + + let proof2 = Proof::from(packed_proof); + + assert_eq!(proof, proof2); + } +}