Skip to content

Commit

Permalink
Add borsh serialization and deserialization (tari-project#95)
Browse files Browse the repository at this point in the history
This PR adds proof serialization and deserialization via `borsh` using the custom encoding format.
  • Loading branch information
AaronFeickert authored Jul 9, 2024
1 parent bbed559 commit 4c1b6db
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 1 deletion.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ repository = "https://github.com/tari-project/triptych"

[dependencies]
blake3 = { version = "1.5.1", default-features = false }
borsh = { version = "1.5.1", optional = true, default-features = false }
crypto-bigint = { version = "0.5.5", default-features = false }
curve25519-dalek = { version = "4.1.3", default-features = false, features = ["alloc", "digest", "rand_core", "zeroize"] }
itertools = { version = "0.12.1", default-features = false }
Expand All @@ -27,7 +28,8 @@ criterion = { version = "0.5.1", default-features = false, features = ["cargo_be
rand_chacha = { version = "0.3.1", default-features = false }

[features]
default = ["rand", "serde", "std"]
default = ["borsh", "rand", "serde", "std"]
borsh = ["dep:borsh"]
rand = ["rand_core/getrandom"]
serde = ["dep:serde", "curve25519-dalek/serde", "zeroize/serde"]
std = ["blake3/std", "itertools/use_std", "merlin/std", "rand_core/std", "serde?/std", "snafu/std", "subtle/std", "zeroize/std"]
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ This implementation makes several opinionated choices:
The implementation keeps dependencies to a minimum, and is `no_std` friendly.

There are several features that are enabled by default:
- `borsh`: adds proof serialization and deserialization via `borsh`
- `rand`: adds additional prover functionality that supplies a cryptographically-secure random number generator
- `serde`: adds proof serialization and deserialization via `serde`
- `std`: adds corresponding dependency features
Expand All @@ -42,6 +43,7 @@ Using a nightly compiler broadens the backend set, and may provide better perfor
You can examine performance using the benchmarks: either `cargo bench` or `cargo +nightly bench`.

Proofs support a custom serialization format designed to be efficient and canonical.
This is used for `borsh` serialization and deserialization, or can be accessed directly.
This functionality has an associated fuzzer that can be run using a nightly compiler: `cargo +nightly fuzz run proofs`.

## Warning
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
//! The implementation keeps dependencies to a minimum, and is `no_std` friendly.
//!
//! There are several features that are enabled by default:
//! - `borsh`: adds proof serialization and deserialization via `borsh`
//! - `rand`: adds additional prover functionality that supplies a cryptographically-secure random number generator
//! - `serde`: adds proof serialization and deserialization via `serde`
//! - `std`: adds corresponding dependency features
Expand All @@ -46,6 +47,7 @@
//! You can examine performance using the benchmarks: either `cargo bench` or `cargo +nightly bench`.
//!
//! Proofs support a custom serialization format designed to be efficient and canonical.
//! This is used for `borsh` serialization and deserialization, or can be accessed directly.
//! This functionality has an associated fuzzer that can be run using a nightly compiler: `cargo +nightly fuzz run
//! proofs`.
//!
Expand Down
43 changes: 43 additions & 0 deletions src/parallel/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use alloc::{vec, vec::Vec};
use core::{iter::once, slice, slice::ChunksExact};

#[cfg(feature = "borsh")]
use borsh::{io, BorshDeserialize, BorshSerialize};
use curve25519_dalek::{
ristretto::CompressedRistretto,
traits::{Identity, MultiscalarMul, VartimeMultiscalarMul},
Expand Down Expand Up @@ -1001,6 +1003,23 @@ impl TriptychProof {
}
}

#[cfg(feature = "borsh")]
impl BorshSerialize for TriptychProof {
fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
BorshSerialize::serialize(&self.to_bytes(), writer)
}
}

#[cfg(feature = "borsh")]
impl BorshDeserialize for TriptychProof {
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
let bytes: Vec<u8> = BorshDeserialize::deserialize_reader(reader)?;

TriptychProof::from_bytes(&bytes)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Invalid Triptych proof"))
}
}

#[cfg(test)]
mod test {
use alloc::{sync::Arc, vec::Vec};
Expand Down Expand Up @@ -1177,6 +1196,30 @@ mod test {
assert_eq!(deserialized, proof);
}

#[test]
#[cfg(feature = "borsh")]
#[allow(non_snake_case, non_upper_case_globals)]
fn test_borsh() {
// Generate data
const n: u32 = 2;
const m: u32 = 4;
let mut rng = ChaCha12Rng::seed_from_u64(8675309);
let (witnesses, statements, mut transcripts) = generate_data(n, m, 1, &mut rng);

// Generate and verify a proof
let proof =
TriptychProof::prove_with_rng_vartime(&witnesses[0], &statements[0], &mut rng, &mut transcripts[0].clone())
.unwrap();
assert!(proof.verify(&statements[0], &mut transcripts[0]).is_ok());

// Serialize the proof
let serialized = borsh::to_vec(&proof).unwrap();

// Deserialize the proof
let deserialized: TriptychProof = borsh::from_slice(&serialized).unwrap();
assert_eq!(deserialized, proof);
}

#[test]
#[allow(non_snake_case, non_upper_case_globals)]
fn test_prove_verify_batch() {
Expand Down
43 changes: 43 additions & 0 deletions src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use alloc::{vec, vec::Vec};
use core::{iter::once, slice, slice::ChunksExact};

#[cfg(feature = "borsh")]
use borsh::{io, BorshDeserialize, BorshSerialize};
use curve25519_dalek::{
ristretto::CompressedRistretto,
traits::{Identity, MultiscalarMul, VartimeMultiscalarMul},
Expand Down Expand Up @@ -920,6 +922,23 @@ impl TriptychProof {
}
}

#[cfg(feature = "borsh")]
impl BorshSerialize for TriptychProof {
fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
BorshSerialize::serialize(&self.to_bytes(), writer)
}
}

#[cfg(feature = "borsh")]
impl BorshDeserialize for TriptychProof {
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
let bytes: Vec<u8> = BorshDeserialize::deserialize_reader(reader)?;

TriptychProof::from_bytes(&bytes)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Invalid Triptych proof"))
}
}

#[cfg(test)]
mod test {
use alloc::{sync::Arc, vec::Vec};
Expand Down Expand Up @@ -1085,6 +1104,30 @@ mod test {
assert_eq!(deserialized, proof);
}

#[test]
#[cfg(feature = "borsh")]
#[allow(non_snake_case, non_upper_case_globals)]
fn test_borsh() {
// Generate data
const n: u32 = 2;
const m: u32 = 4;
let mut rng = ChaCha12Rng::seed_from_u64(8675309);
let (witnesses, statements, mut transcripts) = generate_data(n, m, 1, &mut rng);

// Generate and verify a proof
let proof =
TriptychProof::prove_with_rng_vartime(&witnesses[0], &statements[0], &mut rng, &mut transcripts[0].clone())
.unwrap();
assert!(proof.verify(&statements[0], &mut transcripts[0]).is_ok());

// Serialize the proof
let serialized = borsh::to_vec(&proof).unwrap();

// Deserialize the proof
let deserialized: TriptychProof = borsh::from_slice(&serialized).unwrap();
assert_eq!(deserialized, proof);
}

#[test]
#[allow(non_snake_case, non_upper_case_globals)]
fn test_prove_verify_batch() {
Expand Down

0 comments on commit 4c1b6db

Please sign in to comment.