diff --git a/Cargo.toml b/Cargo.toml index a11e9f13..3fa35dc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ rand_chacha = "0.3" itertools = "0.9.0" subtle = "2.4" pasta_curves = { version = "0.5.2", features = ["repr-c", "serde"], package = "fil_pasta_curves" } -neptune = { version = "8.1.0", default-features = false } +neptune = { path = "../neptune", default-features = false } generic-array = "0.14.4" num-bigint = { version = "0.4", features = ["serde", "rand"] } num-traits = "0.2" @@ -32,6 +32,8 @@ flate2 = "1.0" bitvec = "1.0" byteorder = "1.4.3" thiserror = "1.0" +abomonation = "0.7.3" +abomonation_derive = "0.5.0" [target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies] pasta-msm = { version = "0.1.0", package = "lurk-pasta-msm" } diff --git a/examples/minroot_serde.rs b/examples/minroot_serde.rs new file mode 100644 index 00000000..f85f6789 --- /dev/null +++ b/examples/minroot_serde.rs @@ -0,0 +1,218 @@ +#![allow(non_snake_case)] + +//! Demonstrates how to use Nova to produce a recursive proof of the correct execution of +//! iterations of the MinRoot function, thereby realizing a Nova-based verifiable delay function (VDF). +//! We execute a configurable number of iterations of the MinRoot function per step of Nova's recursion. +type G1 = pasta_curves::pallas::Point; +type G2 = pasta_curves::vesta::Point; +use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; +use abomonation::{decode, encode}; +use abomonation_derive::Abomonation; +use ff::PrimeField; +use nova_snark::{ + traits::{ + circuit::{StepCircuit, TrivialTestCircuit}, + Group, + }, + PublicParams, +}; +use std::{io::Write, mem::size_of, time::Instant}; + +/// Unspeakable horrors +unsafe fn any_as_u8_slice(p: &T) -> &[u8] { + ::core::slice::from_raw_parts((p as *const T) as *const u8, ::core::mem::size_of::()) +} + +/// this is **incredibly, INCREDIBLY** dangerous +unsafe fn entomb_F(f: &F, bytes: &mut W) -> std::io::Result<()> { + println!("entomb: {}", size_of::()); + // this is **incredibly, INCREDIBLY** dangerous + bytes.write_all(any_as_u8_slice(&f))?; + Ok(()) +} + +/// this is **incredibly, INCREDIBLY** dangerous +unsafe fn exhume_F<'a, 'b, F: PrimeField>(f: &mut F, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { + let (mine, rest) = bytes.split_at_mut(size_of::()); + let mine = (mine as *const [u8]) as *const F; + std::ptr::write(f, std::ptr::read(mine)); + Some(rest) +} + +#[derive(Clone, PartialEq, Debug)] +struct MinRootIteration { + x_i: F, + y_i: F, + x_i_plus_1: F, + y_i_plus_1: F, +} + +impl abomonation::Abomonation for MinRootIteration { + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + entomb_F(&self.x_i, bytes)?; + entomb_F(&self.y_i, bytes)?; + entomb_F(&self.x_i_plus_1, bytes)?; + entomb_F(&self.y_i_plus_1, bytes)?; + Ok(()) + } + + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let bytes = exhume_F(&mut self.x_i, bytes)?; + let bytes = exhume_F(&mut self.y_i, bytes)?; + let bytes = exhume_F(&mut self.x_i_plus_1, bytes)?; + let bytes = exhume_F(&mut self.y_i_plus_1, bytes)?; + Some(bytes) + } + + fn extent(&self) -> usize { + 0 + } +} + +#[derive(Clone, PartialEq, Debug, Abomonation)] +struct MinRootCircuit { + seq: Vec>, +} + +impl StepCircuit for MinRootCircuit +where + F: PrimeField, +{ + fn arity(&self) -> usize { + 2 + } + + fn synthesize>( + &self, + cs: &mut CS, + z: &[AllocatedNum], + ) -> Result>, SynthesisError> { + let mut z_out: Result>, SynthesisError> = + Err(SynthesisError::AssignmentMissing); + + // use the provided inputs + let x_0 = z[0].clone(); + let y_0 = z[1].clone(); + + // variables to hold running x_i and y_i + let mut x_i = x_0; + let mut y_i = y_0; + for i in 0..self.seq.len() { + // non deterministic advice + let x_i_plus_1 = + AllocatedNum::alloc(cs.namespace(|| format!("x_i_plus_1_iter_{i}")), || { + Ok(self.seq[i].x_i_plus_1) + })?; + + // check the following conditions hold: + // (i) x_i_plus_1 = (x_i + y_i)^{1/5}, which can be more easily checked with x_i_plus_1^5 = x_i + y_i + // (ii) y_i_plus_1 = x_i + // (1) constraints for condition (i) are below + // (2) constraints for condition (ii) is avoided because we just used x_i wherever y_i_plus_1 is used + let x_i_plus_1_sq = x_i_plus_1.square(cs.namespace(|| format!("x_i_plus_1_sq_iter_{i}")))?; + let x_i_plus_1_quad = + x_i_plus_1_sq.square(cs.namespace(|| format!("x_i_plus_1_quad_{i}")))?; + cs.enforce( + || format!("x_i_plus_1_quad * x_i_plus_1 = x_i + y_i_iter_{i}"), + |lc| lc + x_i_plus_1_quad.get_variable(), + |lc| lc + x_i_plus_1.get_variable(), + |lc| lc + x_i.get_variable() + y_i.get_variable(), + ); + + if i == self.seq.len() - 1 { + z_out = Ok(vec![x_i_plus_1.clone(), x_i.clone()]); + } + + // update x_i and y_i for the next iteration + y_i = x_i; + x_i = x_i_plus_1; + } + + z_out + } + + fn output(&self, z: &[F]) -> Vec { + // sanity check + debug_assert_eq!(z[0], self.seq[0].x_i); + debug_assert_eq!(z[1], self.seq[0].y_i); + + // compute output using advice + vec![ + self.seq[self.seq.len() - 1].x_i_plus_1, + self.seq[self.seq.len() - 1].y_i_plus_1, + ] + } +} + +fn main() { + println!("Nova-based VDF with MinRoot delay function"); + println!("========================================================="); + + let num_iters_per_step = 1024; + // number of iterations of MinRoot per Nova's recursive step + let circuit_primary = MinRootCircuit { + seq: vec![ + MinRootIteration { + x_i: ::Scalar::zero(), + y_i: ::Scalar::zero(), + x_i_plus_1: ::Scalar::zero(), + y_i_plus_1: ::Scalar::zero(), + }; + num_iters_per_step + ], + }; + + let circuit_secondary = TrivialTestCircuit::default(); + + println!("Proving {num_iters_per_step} iterations of MinRoot per step"); + + let mut bytes = Vec::new(); + unsafe { + let start = Instant::now(); + println!("Producing public parameters..."); + let pp = PublicParams::< + G1, + G2, + MinRootCircuit<::Scalar>, + TrivialTestCircuit<::Scalar>, + >::setup(circuit_primary.clone(), circuit_secondary.clone()); + println!("PublicParams::setup, took {:?} ", start.elapsed()); + encode(&pp, &mut bytes).unwrap() + }; + println!("Encoded!"); + println!("Read size: {}", bytes.len()); + + if let Some((result, remaining)) = unsafe { + decode::< + PublicParams< + G1, + G2, + MinRootCircuit<::Scalar>, + TrivialTestCircuit<::Scalar>, + >, + >(&mut bytes) + } { + println!("Producing public parameters..."); + let pp = PublicParams::< + G1, + G2, + MinRootCircuit<::Scalar>, + TrivialTestCircuit<::Scalar>, + >::setup(circuit_primary.clone(), circuit_secondary.clone()); + assert!(result.clone() == pp, "not equal!"); + assert!(remaining.len() == 0); + } else { + println!("Something terrible happened"); + } + + // let mut bytes = Vec::new(); + // let zero = pasta_curves::pallas::Scalar::zero(); + // let mut zero_res = pasta_curves::pallas::Scalar::one(); + // println!("equal? {}", zero == zero_res); + // unsafe { + // entomb(zero, &mut bytes).unwrap(); + // exhume(&mut zero_res, &mut bytes); + // }; + // println!("equal? {}", zero == zero_res); + // assert!(zero == zero_res); +} diff --git a/src/circuit.rs b/src/circuit.rs index 0b6d52c0..a8e91e30 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -21,6 +21,7 @@ use crate::{ }, Commitment, }; +use abomonation_derive::Abomonation; use bellperson::{ gadgets::{ boolean::{AllocatedBit, Boolean}, @@ -32,7 +33,7 @@ use bellperson::{ use ff::Field; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Abomonation)] pub struct NovaAugmentedCircuitParams { limb_width: usize, n_limbs: usize, diff --git a/src/lib.rs b/src/lib.rs index 6f424a6e..5e10a03f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ )] #![allow(non_snake_case)] #![allow(clippy::type_complexity)] -#![forbid(unsafe_code)] +// #![forbid(unsafe_code)] // oops... // private modules mod bellperson; @@ -17,6 +17,7 @@ mod circuit; mod constants; mod nifs; mod r1cs; +mod unsafe_serde; // public modules pub mod errors; @@ -31,6 +32,7 @@ use crate::bellperson::{ solver::SatisfyingAssignment, }; use ::bellperson::{Circuit, ConstraintSystem}; +use abomonation::Abomonation; use circuit::{NovaAugmentedCircuit, NovaAugmentedCircuitInputs, NovaAugmentedCircuitParams}; use constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS}; use core::marker::PhantomData; @@ -48,7 +50,7 @@ use traits::{ }; /// A type that holds public parameters of Nova -#[derive(Serialize, Deserialize)] +#[derive(Clone, PartialEq, Serialize, Deserialize)] #[serde(bound = "")] pub struct PublicParams where @@ -73,6 +75,78 @@ where _p_c2: PhantomData, } +impl Abomonation for PublicParams +where + G1: Group::Scalar>, + G2: Group::Scalar>, + C1: StepCircuit, + C2: StepCircuit, +{ + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.F_arity_primary.entomb(bytes)?; + self.F_arity_secondary.entomb(bytes)?; + self.ro_consts_primary.entomb(bytes)?; + self.ro_consts_circuit_primary.entomb(bytes)?; + self.ck_primary.entomb(bytes)?; + self.r1cs_shape_primary.entomb(bytes)?; + self.ro_consts_secondary.entomb(bytes)?; + self.ro_consts_circuit_secondary.entomb(bytes)?; + self.ck_secondary.entomb(bytes)?; + self.r1cs_shape_secondary.entomb(bytes)?; + self.augmented_circuit_params_primary.entomb(bytes)?; + self.augmented_circuit_params_secondary.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.F_arity_primary.exhume(temp)?; + let temp = bytes; + bytes = self.F_arity_secondary.exhume(temp)?; + let temp = bytes; + bytes = self.ro_consts_primary.exhume(temp)?; + let temp = bytes; + bytes = self.ro_consts_circuit_primary.exhume(temp)?; + let temp = bytes; + bytes = self.ck_primary.exhume(temp)?; + let temp = bytes; + bytes = self.r1cs_shape_primary.exhume(temp)?; + let temp = bytes; + bytes = self.ro_consts_secondary.exhume(temp)?; + let temp = bytes; + bytes = self.ro_consts_circuit_secondary.exhume(temp)?; + let temp = bytes; + bytes = self.ck_secondary.exhume(temp)?; + let temp = bytes; + bytes = self.r1cs_shape_secondary.exhume(temp)?; + let temp = bytes; + bytes = self.augmented_circuit_params_primary.exhume(temp)?; + let temp = bytes; + bytes = self.augmented_circuit_params_secondary.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.F_arity_primary.extent(); + size += self.F_arity_secondary.extent(); + size += self.ro_consts_primary.extent(); + size += self.ro_consts_circuit_primary.extent(); + size += self.ck_primary.extent(); + size += self.r1cs_shape_primary.extent(); + size += self.ro_consts_secondary.extent(); + size += self.ro_consts_circuit_secondary.extent(); + size += self.ck_secondary.extent(); + size += self.r1cs_shape_secondary.extent(); + size += self.augmented_circuit_params_primary.extent(); + size += self.augmented_circuit_params_secondary.extent(); + size + } +} + impl PublicParams where G1: Group::Scalar>, @@ -410,7 +484,7 @@ where // check if the output hashes in R1CS instances point to the right running instances let (hash_primary, hash_secondary) = { - let mut hasher = ::RO::new( + let mut hasher = <::RO as ROTrait>::new( pp.ro_consts_secondary.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary, ); @@ -424,7 +498,7 @@ where } self.r_U_secondary.absorb_in_ro(&mut hasher); - let mut hasher2 = ::RO::new( + let mut hasher2 = <::RO as ROTrait>::new( pp.ro_consts_primary.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_secondary, ); @@ -515,6 +589,37 @@ where _p_c2: PhantomData, } +impl Abomonation for ProverKey +where + G1: Group::Scalar>, + G2: Group::Scalar>, + C1: StepCircuit, + C2: StepCircuit, + S1: RelaxedR1CSSNARKTrait, + S2: RelaxedR1CSSNARKTrait, +{ + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.pk_primary.entomb(bytes)?; + self.pk_secondary.entomb(bytes)?; + Ok(()) + } + + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.pk_primary.exhume(temp)?; + let temp = bytes; + bytes = self.pk_secondary.exhume(temp)?; + Some(bytes) + } + + fn extent(&self) -> usize { + let mut size = 0; + size += self.pk_primary.extent(); + size += self.pk_secondary.extent(); + size + } +} + /// A type that holds the verifier key for `CompressedSNARK` #[derive(Clone, Serialize, Deserialize)] #[serde(bound = "")] @@ -539,6 +644,53 @@ where _p_c2: PhantomData, } +impl Abomonation for VerifierKey +where + G1: Group::Scalar>, + G2: Group::Scalar>, + C1: StepCircuit, + C2: StepCircuit, + S1: RelaxedR1CSSNARKTrait, + S2: RelaxedR1CSSNARKTrait, +{ + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.F_arity_primary.entomb(bytes)?; + self.F_arity_secondary.entomb(bytes)?; + self.ro_consts_primary.entomb(bytes)?; + self.ro_consts_secondary.entomb(bytes)?; + unsafe_serde::entomb_T(&self.r1cs_shape_primary_digest, bytes)?; + unsafe_serde::entomb_T(&self.r1cs_shape_secondary_digest, bytes)?; + self.vk_primary.entomb(bytes)?; + self.vk_secondary.entomb(bytes)?; + Ok(()) + } + + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; bytes = self.F_arity_primary.exhume(temp)?; + let temp = bytes; bytes = self.F_arity_secondary.exhume(temp)?; + let temp = bytes; bytes = self.ro_consts_primary.exhume(temp)?; + let temp = bytes; bytes = self.ro_consts_secondary.exhume(temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_T(&mut self.r1cs_shape_primary_digest, temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_T(&mut self.r1cs_shape_secondary_digest, temp)?; + let temp = bytes; bytes = self.vk_primary.exhume(temp)?; + let temp = bytes; bytes = self.vk_secondary.exhume(temp)?; + Some(bytes) + } + + fn extent(&self) -> usize { + let mut size = 0; + size += self.F_arity_primary.extent(); + size += self.F_arity_secondary.extent(); + size += self.ro_consts_primary.extent(); + size += self.ro_consts_secondary.extent(); + size += unsafe_serde::extent_T(&self.r1cs_shape_primary_digest); + size += unsafe_serde::extent_T(&self.r1cs_shape_secondary_digest); + size += self.vk_primary.extent(); + size += self.vk_secondary.extent(); + size + } +} + /// A SNARK that proves the knowledge of a valid `RecursiveSNARK` #[derive(Clone, Serialize, Deserialize)] #[serde(bound = "")] @@ -705,7 +857,7 @@ where // check if the output hashes in R1CS instances point to the right running instances let (hash_primary, hash_secondary) = { - let mut hasher = ::RO::new( + let mut hasher = <::RO as ROTrait>::new( vk.ro_consts_secondary.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_primary, ); @@ -719,7 +871,7 @@ where } self.r_U_secondary.absorb_in_ro(&mut hasher); - let mut hasher2 = ::RO::new( + let mut hasher2 = <::RO as ROTrait>::new( vk.ro_consts_primary.clone(), NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_secondary, ); diff --git a/src/provider/ipa_pc.rs b/src/provider/ipa_pc.rs index e630d8ca..fa5cb943 100644 --- a/src/provider/ipa_pc.rs +++ b/src/provider/ipa_pc.rs @@ -11,6 +11,7 @@ use crate::{ }, Commitment, CommitmentKey, CompressedCommitment, CE, }; +use abomonation::Abomonation; use core::iter; use ff::Field; use rayon::prelude::*; @@ -24,6 +25,28 @@ pub struct ProverKey { ck_s: CommitmentKey, } +impl Abomonation for ProverKey { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.ck_s.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.ck_s.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.ck_s.extent(); + size + } +} + /// Provides an implementation of the verifier key #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] @@ -32,6 +55,32 @@ pub struct VerifierKey { ck_s: CommitmentKey, } +impl Abomonation for VerifierKey { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.ck_v.entomb(bytes)?; + self.ck_s.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.ck_v.exhume(temp)?; + let temp = bytes; + bytes = self.ck_s.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.ck_v.extent(); + size += self.ck_s.extent(); + size + } +} + /// Provides an implementation of a polynomial evaluation argument #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] diff --git a/src/provider/pedersen.rs b/src/provider/pedersen.rs index 33a85261..eec7dd6e 100644 --- a/src/provider/pedersen.rs +++ b/src/provider/pedersen.rs @@ -5,7 +5,9 @@ use crate::{ commitment::{CommitmentEngineTrait, CommitmentTrait}, AbsorbInROTrait, CompressedGroup, Group, ROTrait, TranscriptReprTrait, }, + unsafe_serde, }; +use abomonation::Abomonation; use core::{ fmt::Debug, marker::PhantomData, @@ -22,6 +24,28 @@ pub struct CommitmentKey { _p: PhantomData, } +impl Abomonation for CommitmentKey { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + unsafe_serde::entomb_vec_T(&self.ck, bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = unsafe_serde::exhume_vec_T(&mut self.ck, temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += unsafe_serde::extent_vec_T(&self.ck); + size + } +} + /// A type that holds a commitment #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] @@ -29,6 +53,28 @@ pub struct Commitment { pub(crate) comm: G, } +impl Abomonation for Commitment { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + unsafe_serde::entomb_T(&self.comm, bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = unsafe_serde::exhume_T(&mut self.comm, temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += unsafe_serde::extent_T(&self.comm); + size + } +} + /// A type that holds a compressed commitment #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] diff --git a/src/provider/poseidon.rs b/src/provider/poseidon.rs index 7df46202..4556a799 100644 --- a/src/provider/poseidon.rs +++ b/src/provider/poseidon.rs @@ -1,5 +1,9 @@ //! Poseidon Constants and Poseidon-based RO used in Nova -use crate::traits::{ROCircuitTrait, ROConstantsTrait, ROTrait}; +use crate::{ + traits::{ROCircuitTrait, ROConstantsTrait, ROTrait}, + unsafe_serde, +}; +use abomonation::Abomonation; use bellperson::{ gadgets::{ boolean::{AllocatedBit, Boolean}, @@ -23,9 +27,31 @@ use neptune::{ use serde::{Deserialize, Serialize}; /// All Poseidon Constants that are used in Nova -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Serialize, Deserialize)] pub struct PoseidonConstantsCircuit(PoseidonConstants); +impl Abomonation for PoseidonConstantsCircuit { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.0.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.0.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.0.extent(); + size + } +} + impl ROConstantsTrait for PoseidonConstantsCircuit where Scalar: PrimeField + PrimeFieldBits, @@ -52,6 +78,44 @@ where _p: PhantomData, } +impl Abomonation for PoseidonRO +where + Base: PrimeField + PrimeFieldBits, + Scalar: PrimeField + PrimeFieldBits, +{ + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + unsafe_serde::entomb_vec_T(&self.state, bytes)?; + self.constants.entomb(bytes)?; + self.num_absorbs.entomb(bytes)?; + self.squeezed.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = unsafe_serde::exhume_vec_T(&mut self.state, temp)?; + let temp = bytes; + bytes = self.constants.exhume(temp)?; + let temp = bytes; + bytes = self.num_absorbs.exhume(temp)?; + let temp = bytes; + bytes = self.squeezed.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += unsafe_serde::extent_vec_T(&self.state); + size += self.constants.extent(); + size += self.num_absorbs.extent(); + size += self.squeezed.extent(); + size + } +} + impl ROTrait for PoseidonRO where Base: PrimeField + PrimeFieldBits + Serialize + for<'de> Deserialize<'de>, diff --git a/src/r1cs.rs b/src/r1cs.rs index 7e8d12c9..037d28b9 100644 --- a/src/r1cs.rs +++ b/src/r1cs.rs @@ -10,8 +10,9 @@ use crate::{ traits::{ commitment::CommitmentEngineTrait, AbsorbInROTrait, Group, ROTrait, TranscriptReprTrait, }, - Commitment, CommitmentKey, CE, + unsafe_serde, Commitment, CommitmentKey, CE, }; +use abomonation::Abomonation; use core::{cmp::max, marker::PhantomData}; use ff::Field; use flate2::{write::ZlibEncoder, Compression}; @@ -39,6 +40,45 @@ pub struct R1CSShape { digest: G::Scalar, // digest of the rest of R1CSShape } +impl Abomonation for R1CSShape { + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.num_cons.entomb(bytes)?; + self.num_vars.entomb(bytes)?; + self.num_io.entomb(bytes)?; + unsafe_serde::entomb_vec_usize_usize_T(&self.A, bytes)?; + unsafe_serde::entomb_vec_usize_usize_T(&self.B, bytes)?; + unsafe_serde::entomb_vec_usize_usize_T(&self.C, bytes)?; + Ok(()) + } + + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.num_cons.exhume(temp)?; + let temp = bytes; + bytes = self.num_vars.exhume(temp)?; + let temp = bytes; + bytes = self.num_io.exhume(temp)?; + let temp = bytes; + bytes = unsafe_serde::exhume_vec_usize_usize_T(&mut self.A, temp)?; + let temp = bytes; + bytes = unsafe_serde::exhume_vec_usize_usize_T(&mut self.B, temp)?; + let temp = bytes; + bytes = unsafe_serde::exhume_vec_usize_usize_T(&mut self.C, temp)?; + Some(bytes) + } + + fn extent(&self) -> usize { + let mut size = 0; + size += self.num_cons.extent(); + size += self.num_vars.extent(); + size += self.num_io.extent(); + size += unsafe_serde::extent_vec_usize_usize_T(&self.A); + size += unsafe_serde::extent_vec_usize_usize_T(&self.B); + size += unsafe_serde::extent_vec_usize_usize_T(&self.C); + size + } +} + /// A type that holds a witness for a given R1CS instance #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct R1CSWitness { diff --git a/src/spartan/mod.rs b/src/spartan/mod.rs index b6834d2c..536cafbb 100644 --- a/src/spartan/mod.rs +++ b/src/spartan/mod.rs @@ -14,6 +14,7 @@ use crate::{ }, CommitmentKey, }; +use abomonation::Abomonation; use ff::Field; use itertools::concat; use polynomial::{EqPolynomial, MultilinearPolynomial, SparsePolynomial}; @@ -24,7 +25,7 @@ use sumcheck::SumcheckProof; /// A trait that defines the behavior of a computation commitment engine pub trait CompCommitmentEngineTrait> { /// A type that holds opening hint - type Decommitment: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>; + type Decommitment: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de> + Abomonation; /// A type that holds a commitment type Commitment: Clone @@ -32,7 +33,8 @@ pub trait CompCommitmentEngineTrait + Serialize - + for<'de> Deserialize<'de>; + + for<'de> Deserialize<'de> + + Abomonation; /// A type that holds an evaluation argument type EvaluationArgument: Send + Sync + Serialize + for<'de> Deserialize<'de>; @@ -65,7 +67,7 @@ pub trait CompCommitmentEngineTrait Abomonation for ProverKey +where + G: Group, + EE: EvaluationEngineTrait, + CC: CompCommitmentEngineTrait, +{ + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.pk_ee.entomb(bytes)?; + self.S.entomb(bytes)?; + self.decomm.entomb(bytes)?; + self.comm.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.pk_ee.exhume(temp)?; + let temp = bytes; + bytes = self.S.exhume(temp)?; + let temp = bytes; + bytes = self.decomm.exhume(temp)?; + let temp = bytes; + bytes = self.comm.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.pk_ee.extent(); + size += self.S.extent(); + size += self.decomm.extent(); + size += self.comm.extent(); + size + } +} + /// A type that represents the verifier's key -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] #[serde(bound = "")] pub struct VerifierKey< G: Group, @@ -92,10 +133,49 @@ pub struct VerifierKey< comm: CC::Commitment, } +impl Abomonation for VerifierKey +where + G: Group, + EE: EvaluationEngineTrait, + CC: CompCommitmentEngineTrait, +{ + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.num_cons.entomb(bytes)?; + self.num_vars.entomb(bytes)?; + self.vk_ee.entomb(bytes)?; + self.comm.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.num_cons.exhume(temp)?; + let temp = bytes; + bytes = self.num_vars.exhume(temp)?; + let temp = bytes; + bytes = self.vk_ee.exhume(temp)?; + let temp = bytes; + bytes = self.comm.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.num_cons.extent(); + size += self.num_vars.extent(); + size += self.vk_ee.extent(); + size += self.comm.extent(); + size + } +} + /// A succinct proof of knowledge of a witness to a relaxed R1CS instance /// The proof is produced using Spartan's combination of the sum-check and /// the commitment to a vector viewed as a polynomial commitment -#[derive(Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct RelaxedR1CSSNARK< G: Group, diff --git a/src/spartan/spark/mod.rs b/src/spartan/spark/mod.rs index af7923d2..6ecc57fa 100644 --- a/src/spartan/spark/mod.rs +++ b/src/spartan/spark/mod.rs @@ -8,9 +8,11 @@ use crate::{ CommitmentKey, }; use core::marker::PhantomData; +use abomonation::Abomonation; use serde::{Deserialize, Serialize}; /// A trivial implementation of `ComputationCommitmentEngineTrait` +#[derive(Clone, Debug)] pub struct TrivialCompComputationEngine> { _p: PhantomData, _p2: PhantomData, @@ -23,6 +25,27 @@ pub struct TrivialCommitment { S: R1CSShape, } +impl Abomonation for TrivialCommitment { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.S.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a,'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; bytes = self.S.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.S.extent(); + size + } +} + /// Provides an implementation of a trivial decommitment #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] @@ -30,6 +53,17 @@ pub struct TrivialDecommitment { _p: PhantomData, } +impl Abomonation for TrivialDecommitment { + #[inline] + unsafe fn entomb(&self, _write: &mut W) -> std::io::Result<()> { Ok(()) } + + #[inline] + unsafe fn exhume<'a,'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { Some(bytes) } + + #[inline] + fn extent(&self) -> usize { 0 } +} + /// Provides an implementation of a trivial evaluation argument #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] @@ -37,6 +71,17 @@ pub struct TrivialEvaluationArgument { _p: PhantomData, } +impl Abomonation for TrivialEvaluationArgument { + #[inline] + unsafe fn entomb(&self, _write: &mut W) -> std::io::Result<()> { Ok(()) } + + #[inline] + unsafe fn exhume<'a,'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { Some(bytes) } + + #[inline] + fn extent(&self) -> usize { 0 } +} + impl TranscriptReprTrait for TrivialCommitment { fn to_transcript_bytes(&self) -> Vec { self.S.to_transcript_bytes() @@ -112,6 +157,36 @@ pub struct SparkDecommitment { C: SparsePolynomial, } +impl Abomonation for SparkDecommitment { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.A.entomb(bytes)?; + self.B.entomb(bytes)?; + self.C.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.A.exhume(temp)?; + let temp = bytes; + bytes = self.B.exhume(temp)?; + let temp = bytes; + bytes = self.C.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.A.extent(); + size += self.B.extent(); + size += self.C.extent(); + size + } +} + impl SparkDecommitment { fn new(S: &R1CSShape) -> Self { let ell = (S.num_cons.log_2(), S.num_vars.log_2() + 1); @@ -144,6 +219,36 @@ pub struct SparkCommitment { comm_C: SparsePolynomialCommitment, } +impl Abomonation for SparkCommitment { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.comm_A.entomb(bytes)?; + self.comm_B.entomb(bytes)?; + self.comm_C.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; + bytes = self.comm_A.exhume(temp)?; + let temp = bytes; + bytes = self.comm_B.exhume(temp)?; + let temp = bytes; + bytes = self.comm_C.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.comm_A.extent(); + size += self.comm_B.extent(); + size += self.comm_C.extent(); + size + } +} + impl TranscriptReprTrait for SparkCommitment { fn to_transcript_bytes(&self) -> Vec { let mut bytes = self.comm_A.to_transcript_bytes(); diff --git a/src/spartan/spark/sparse.rs b/src/spartan/spark/sparse.rs index 2f1d5976..a78ac98c 100644 --- a/src/spartan/spark/sparse.rs +++ b/src/spartan/spark/sparse.rs @@ -13,8 +13,9 @@ use crate::{ commitment::CommitmentEngineTrait, evaluation::EvaluationEngineTrait, Group, TranscriptEngineTrait, TranscriptReprTrait, }, - Commitment, CommitmentKey, + Commitment, CommitmentKey, unsafe_serde, }; +use abomonation::Abomonation; use ff::Field; use rayon::prelude::*; use serde::{Deserialize, Serialize}; @@ -37,6 +38,48 @@ pub struct SparsePolynomial { col_audit_ts: Vec, } +impl Abomonation for SparsePolynomial { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.ell.entomb(bytes)?; + unsafe_serde::entomb_vec_T(&self.row, bytes)?; + unsafe_serde::entomb_vec_T(&self.col, bytes)?; + unsafe_serde::entomb_vec_T(&self.val, bytes)?; + unsafe_serde::entomb_vec_T(&self.row_read_ts, bytes)?; + unsafe_serde::entomb_vec_T(&self.row_audit_ts, bytes)?; + unsafe_serde::entomb_vec_T(&self.col_read_ts, bytes)?; + unsafe_serde::entomb_vec_T(&self.col_audit_ts, bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; bytes = self.ell.exhume(temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_vec_T(&mut self.row, temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_vec_T(&mut self.col, temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_vec_T(&mut self.val, temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_vec_T(&mut self.row_read_ts, temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_vec_T(&mut self.row_audit_ts, temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_vec_T(&mut self.col_read_ts, temp)?; + let temp = bytes; bytes = unsafe_serde::exhume_vec_T(&mut self.col_audit_ts, temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.ell.extent(); + size += unsafe_serde::extent_vec_T(&self.row); + size += unsafe_serde::extent_vec_T(&self.col); + size += unsafe_serde::extent_vec_T(&self.val); + size += unsafe_serde::extent_vec_T(&self.row_read_ts); + size += unsafe_serde::extent_vec_T(&self.row_audit_ts); + size += unsafe_serde::extent_vec_T(&self.col_read_ts); + size += unsafe_serde::extent_vec_T(&self.col_audit_ts); + size + } +} + /// A type that holds a commitment to a sparse polynomial #[derive(Clone, Serialize, Deserialize)] #[serde(bound = "")] @@ -56,6 +99,51 @@ pub struct SparsePolynomialCommitment { comm_col_audit_ts: Commitment, } +impl Abomonation for SparsePolynomialCommitment { + #[inline] + unsafe fn entomb(&self, bytes: &mut W) -> std::io::Result<()> { + self.ell.entomb(bytes)?; + self.size.entomb(bytes)?; + self.comm_row.entomb(bytes)?; + self.comm_col.entomb(bytes)?; + self.comm_val.entomb(bytes)?; + self.comm_row_read_ts.entomb(bytes)?; + self.comm_row_audit_ts.entomb(bytes)?; + self.comm_col_read_ts.entomb(bytes)?; + self.comm_col_audit_ts.entomb(bytes)?; + Ok(()) + } + + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, mut bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + let temp = bytes; bytes = self.ell.exhume(temp)?; + let temp = bytes; bytes = self.size.exhume(temp)?; + let temp = bytes; bytes = self.comm_row.exhume(temp)?; + let temp = bytes; bytes = self.comm_col.exhume(temp)?; + let temp = bytes; bytes = self.comm_val.exhume(temp)?; + let temp = bytes; bytes = self.comm_row_read_ts.exhume(temp)?; + let temp = bytes; bytes = self.comm_row_audit_ts.exhume(temp)?; + let temp = bytes; bytes = self.comm_col_read_ts.exhume(temp)?; + let temp = bytes; bytes = self.comm_col_audit_ts.exhume(temp)?; + Some(bytes) + } + + #[inline] + fn extent(&self) -> usize { + let mut size = 0; + size += self.ell.extent(); + size += self.size.extent(); + size += self.comm_row.extent(); + size += self.comm_col.extent(); + size += self.comm_val.extent(); + size += self.comm_row_read_ts.extent(); + size += self.comm_row_audit_ts.extent(); + size += self.comm_col_read_ts.extent(); + size += self.comm_col_audit_ts.extent(); + size + } +} + impl TranscriptReprTrait for SparsePolynomialCommitment { fn to_transcript_bytes(&self) -> Vec { [ diff --git a/src/traits/circuit.rs b/src/traits/circuit.rs index a43a313f..9e019a7a 100644 --- a/src/traits/circuit.rs +++ b/src/traits/circuit.rs @@ -1,4 +1,5 @@ //! This module defines traits that a step function must implement +use abomonation_derive::Abomonation; use bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; use core::marker::PhantomData; use ff::PrimeField; @@ -24,7 +25,7 @@ pub trait StepCircuit: Send + Sync + Clone { } /// A trivial step circuit that simply returns the input -#[derive(Clone, Debug, Default)] +#[derive(Clone, PartialEq, Debug, Default, Abomonation)] pub struct TrivialTestCircuit { _p: PhantomData, } diff --git a/src/traits/commitment.rs b/src/traits/commitment.rs index 9b4725fc..44faa7c4 100644 --- a/src/traits/commitment.rs +++ b/src/traits/commitment.rs @@ -4,6 +4,7 @@ use crate::{ errors::NovaError, traits::{AbsorbInROTrait, Group, TranscriptReprTrait}, }; +use abomonation::Abomonation; use core::{ fmt::Debug, ops::{Add, AddAssign, Mul, MulAssign}, @@ -50,6 +51,7 @@ pub trait CommitmentTrait: + TranscriptReprTrait + Serialize + for<'de> Deserialize<'de> + + Abomonation + AbsorbInROTrait + CommitmentOps + CommitmentOpsOwned @@ -81,7 +83,14 @@ pub trait CommitmentEngineTrait: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de> { /// Holds the type of the commitment key - type CommitmentKey: Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de>; + type CommitmentKey: Clone + + PartialEq + + Debug + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de> + + Abomonation; /// Holds the type of the commitment type Commitment: CommitmentTrait; diff --git a/src/traits/evaluation.rs b/src/traits/evaluation.rs index 3f1fba61..b197d701 100644 --- a/src/traits/evaluation.rs +++ b/src/traits/evaluation.rs @@ -5,6 +5,7 @@ use crate::{ errors::NovaError, traits::{commitment::CommitmentEngineTrait, Group}, }; +use abomonation::Abomonation; use serde::{Deserialize, Serialize}; /// A trait that ties different pieces of the commitment evaluation together @@ -15,10 +16,10 @@ pub trait EvaluationEngineTrait: type CE: CommitmentEngineTrait; /// A type that holds the prover key - type ProverKey: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>; + type ProverKey: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de> + Abomonation; /// A type that holds the verifier key - type VerifierKey: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>; + type VerifierKey: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de> + Abomonation; /// A type that holds the evaluation argument type EvaluationArgument: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>; diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 5138cea8..5617f13f 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -1,5 +1,6 @@ //! This module defines various traits required by the users of the library to implement. use crate::errors::NovaError; +use abomonation::Abomonation; use bellperson::{ gadgets::{boolean::AllocatedBit, num::AllocatedNum}, ConstraintSystem, SynthesisError, @@ -56,7 +57,13 @@ pub trait Group: + for<'de> Deserialize<'de>; /// A type representing preprocessed group element - type PreprocessedGroupElement: Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de>; + type PreprocessedGroupElement: Clone + + PartialEq + + Debug + + Send + + Sync + + Serialize + + for<'de> Deserialize<'de>; /// A type that represents a circuit-friendly sponge that consumes elements /// from the base field and squeezes out elements of the scalar field @@ -131,10 +138,12 @@ pub trait ROTrait { /// A type representing constants/parameters associated with the hash function type Constants: ROConstantsTrait + Clone + + PartialEq + Send + Sync + Serialize - + for<'de> Deserialize<'de>; + + for<'de> Deserialize<'de> + + Abomonation; /// Initializes the hash function fn new(constants: Self::Constants, num_absorbs: usize) -> Self; @@ -151,10 +160,12 @@ pub trait ROCircuitTrait { /// A type representing constants/parameters associated with the hash function type Constants: ROConstantsTrait + Clone + + PartialEq + Send + Sync + Serialize - + for<'de> Deserialize<'de>; + + for<'de> Deserialize<'de> + + Abomonation; /// Initializes the hash function fn new(constants: Self::Constants, num_absorbs: usize) -> Self; diff --git a/src/traits/snark.rs b/src/traits/snark.rs index e2c22d0a..4ef1b30a 100644 --- a/src/traits/snark.rs +++ b/src/traits/snark.rs @@ -6,6 +6,7 @@ use crate::{ CommitmentKey, }; +use abomonation::Abomonation; use serde::{Deserialize, Serialize}; /// A trait that defines the behavior of a zkSNARK @@ -13,10 +14,10 @@ pub trait RelaxedR1CSSNARKTrait: Sized + Send + Sync + Serialize + for<'de> Deserialize<'de> { /// A type that represents the prover's key - type ProverKey: Send + Sync + Serialize + for<'de> Deserialize<'de>; + type ProverKey: Send + Sync + Serialize + for<'de> Deserialize<'de> + Abomonation; /// A type that represents the verifier's key - type VerifierKey: Send + Sync + Serialize + for<'de> Deserialize<'de>; + type VerifierKey: Send + Sync + Serialize + for<'de> Deserialize<'de> + Abomonation; /// Produces the keys for the prover and the verifier fn setup( diff --git a/src/unsafe_serde.rs b/src/unsafe_serde.rs new file mode 100644 index 00000000..8865f12b --- /dev/null +++ b/src/unsafe_serde.rs @@ -0,0 +1,122 @@ +#![allow(non_snake_case)] + +use std::{ + io::Write, + mem::size_of, +}; + +// This method currently enables undefined behavior, by exposing padding bytes. +#[inline] +pub unsafe fn typed_to_bytes(slice: &[T]) -> &[u8] { + std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * size_of::()) +} + +/// this is **incredibly, INCREDIBLY** dangerous +#[inline] +pub unsafe fn entomb_T(_f: &T, _bytes: &mut W) -> std::io::Result<()> { + Ok(()) +} + +/// this is **incredibly, INCREDIBLY** dangerous +#[inline] +pub unsafe fn exhume_T<'a, 'b, T>(_f: &mut T, bytes: &'a mut [u8]) -> Option<&'a mut [u8]> { + Some(bytes) +} + +#[inline] +pub fn extent_T(_this: &T) -> usize { + 0 +} + +/// this is **incredibly, INCREDIBLY** dangerous +#[inline] +pub unsafe fn entomb_usize_usize_T( + _f: &(usize, usize, T), + _bytes: &mut W, +) -> std::io::Result<()> { + Ok(()) +} + +/// this is **incredibly, INCREDIBLY** dangerous +#[inline] +pub unsafe fn exhume_usize_usize_T<'a, 'b, T>( + _f: &mut (usize, usize, T), + bytes: &'a mut [u8], +) -> Option<&'a mut [u8]> { + Some(bytes) +} + +#[inline] +pub fn extent_usize_usize_T(_this: &(usize, usize, T)) -> usize { + 0 +} + +macro_rules! vec_abomonate { + ($entomb_name:ident, $exhume_name:ident, $extent_name:ident, $entomb_inner_name:ident, $exhume_inner_name:ident, $extent_inner_name:ident, $inner_type:ty) => { + #[inline] + pub unsafe fn $entomb_name( + this: &Vec<$inner_type>, + write: &mut W, + ) -> std::io::Result<()> { + write.write_all(typed_to_bytes(&this[..]))?; + for element in this.iter() { + $entomb_inner_name(element, write)?; + } + Ok(()) + } + + #[inline] + pub unsafe fn $exhume_name<'a, 'b, T>( + this: &'a mut Vec<$inner_type>, + bytes: &'b mut [u8], + ) -> Option<&'b mut [u8]> { + // extract memory from bytes to back our vector + let binary_len = this.len() * size_of::<$inner_type>(); + if binary_len > bytes.len() { + None + } else { + let (mine, mut rest) = bytes.split_at_mut(binary_len); + let slice = + std::slice::from_raw_parts_mut(mine.as_mut_ptr() as *mut $inner_type, this.len()); + std::ptr::write( + this, + Vec::from_raw_parts(slice.as_mut_ptr(), this.len(), this.len()), + ); + for element in this.iter_mut() { + let temp = rest; // temp variable explains lifetimes (mysterious!) + rest = $exhume_inner_name(element, temp)?; + } + Some(rest) + } + } + + #[inline] + pub fn $extent_name(this: &Vec<$inner_type>) -> usize { + let mut sum = size_of::<$inner_type>() * this.len(); + for element in this.iter() { + sum += $extent_inner_name(element); + } + sum + } + }; +} + +vec_abomonate!( + entomb_vec_usize_usize_T, + exhume_vec_usize_usize_T, + extent_vec_usize_usize_T, + entomb_usize_usize_T, + exhume_usize_usize_T, + extent_usize_usize_T, + (usize, usize, T) +); + +vec_abomonate!( + entomb_vec_T, + exhume_vec_T, + extent_vec_T, + entomb_T, + exhume_T, + extent_T, + T +); \ No newline at end of file