Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ring VRF test vectors #28

Merged
merged 11 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions data/bandersnatch_ed_sha512_ell2_ring_vectors.json

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions data/bandersnatch_sw_sha512_tai_pedersen_vectors.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"comment": "Bandersnatch_SHA-512_TAI - vector-1",
"comment": "Bandersnatch_SW_SHA-512_TAI - vector-1",
"sk": "3d6406500d4009fdf2604546093665911e753f2213570a29521fd88bc30ede18",
"pk": "fd72a90d1eeba6733824e76bb31991b8108d6562756b85f244333e3c7205225200",
"alpha": "",
Expand All @@ -16,7 +16,7 @@
"proof_sb": "6b2dbc4b088b21d732cc5193439b5186cddecb4ee12f8233cbe3057c06849216"
},
{
"comment": "Bandersnatch_SHA-512_TAI - vector-2",
"comment": "Bandersnatch_SW_SHA-512_TAI - vector-2",
"sk": "8b9063872331dda4c3c282f7d813fb3c13e7339b7dc9635fdc764e32cc57cb15",
"pk": "e30eae606d21dff460cdaecfc9bfcd2e319628ccc0242f3ca21f2d5c940ba41680",
"alpha": "0a",
Expand All @@ -32,7 +32,7 @@
"proof_sb": "f05d631c7ae9fbc33ea4464af81188c1342f7c6aa127531e1f1bd203d608f613"
},
{
"comment": "Bandersnatch_SHA-512_TAI - vector-3",
"comment": "Bandersnatch_SW_SHA-512_TAI - vector-3",
"sk": "6db187202f69e627e432296ae1d0f166ae6ac3c1222585b6ceae80ea07670b14",
"pk": "2a00e5a32e2f097858a1a4a73cf5c2fb4e6d375a4ea4cc3ae3e91660eade850c80",
"alpha": "",
Expand All @@ -48,7 +48,7 @@
"proof_sb": "07a177cf9a9d08282194c8cc0d856be3de2681c642ecd0cb95a014a82f7f5c11"
},
{
"comment": "Bandersnatch_SHA-512_TAI - vector-4",
"comment": "Bandersnatch_SW_SHA-512_TAI - vector-4",
"sk": "b56cc204f1b6c2323709012cb16c72f3021035ce935fbe69b600a88d842c7407",
"pk": "4f29d79a27b9545d7223431eb6a63776949454b16e2ac0b7a959304ce3e52b6a00",
"alpha": "73616d706c65",
Expand All @@ -64,7 +64,7 @@
"proof_sb": "5b4d6ef3445d8dc8bcc38f44b3649d39fcb5c0b169c5085245f6903ef8ba940c"
},
{
"comment": "Bandersnatch_SHA-512_TAI - vector-5",
"comment": "Bandersnatch_SW_SHA-512_TAI - vector-5",
"sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302",
"pk": "e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480",
"alpha": "42616e646572736e6174636820766563746f72",
Expand All @@ -80,7 +80,7 @@
"proof_sb": "bd365d1ff09f59a9ad885bf3f71e81151747757788f6c07387f4100842784e13"
},
{
"comment": "Bandersnatch_SHA-512_TAI - vector-6",
"comment": "Bandersnatch_SW_SHA-512_TAI - vector-6",
"sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302",
"pk": "e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480",
"alpha": "42616e646572736e6174636820766563746f72",
Expand All @@ -96,7 +96,7 @@
"proof_sb": "9e989c2ae5177ef9505705cc144116f0b633693ca4fcdb85ec9b073fa06afb08"
},
{
"comment": "Bandersnatch_SHA-512_TAI - vector-7",
"comment": "Bandersnatch_SW_SHA-512_TAI - vector-7",
"sk": "35b877a25c394512292b82bdf8468e98eaf03c79c7fc9d53546dadc5fb75b500",
"pk": "7cba529a807bf602c84625fff28f4d1d836cf40a83f42d95c412f7b62ed9192900",
"alpha": "42616e646572736e6174636820766563746f72",
Expand Down
128 changes: 128 additions & 0 deletions data/bandersnatch_sw_sha512_tai_ring_vectors.json

Large diffs are not rendered by default.

Binary file added data/zcash-bls12-381-srs-2-11-uncompressed.bin
Binary file not shown.
175 changes: 170 additions & 5 deletions src/ring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,9 @@ where
#[cfg(test)]
pub(crate) mod testing {
use super::*;
use crate::testing::*;
use crate::{pedersen, testing as common};

pub const TEST_RING_SIZE: usize = 8;

pub fn prove_verify<S: RingSuite>()
where
Expand All @@ -318,17 +320,17 @@ pub(crate) mod testing {
AffinePoint<S>: utils::te_sw_map::SWMapping<CurveConfig<S>>,
{
let rng = &mut ark_std::test_rng();
let ring_ctx = RingContext::<S>::from_rand(512, rng);
let ring_ctx = RingContext::<S>::from_rand(TEST_RING_SIZE, rng);

let secret = Secret::<S>::from_seed(TEST_SEED);
let secret = Secret::<S>::from_seed(common::TEST_SEED);
let public = secret.public();
let input = Input::from(random_val(Some(rng)));
let input = Input::from(common::random_val(Some(rng)));
let output = secret.output(input);

let ring_size = ring_ctx.max_ring_size();

let prover_idx = 3;
let mut pks = random_vec::<AffinePoint<S>>(ring_size, Some(rng));
let mut pks = common::random_vec::<AffinePoint<S>>(ring_size, Some(rng));
pks[prover_idx] = public.0;

let prover_key = ring_ctx.prover_key(&pks);
Expand Down Expand Up @@ -370,4 +372,167 @@ pub(crate) mod testing {
};
($suite:ident, false) => {};
}

pub trait RingSuiteExt: RingSuite
where
BaseField<Self>: ark_ff::PrimeField,
CurveConfig<Self>: SWCurveConfig + Clone,
AffinePoint<Self>: SWMapping<CurveConfig<Self>>,
{
fn ring_context() -> &'static RingContext<Self>;
}

pub struct TestVector<S: RingSuite>
where
BaseField<S>: ark_ff::PrimeField,
CurveConfig<S>: SWCurveConfig + Clone,
AffinePoint<S>: SWMapping<CurveConfig<S>>,
{
pub pedersen: pedersen::testing::TestVector<S>,
pub ring_pks: Box<[AffinePoint<S>; TEST_RING_SIZE]>,
pub ring_proof: RingProof<S>,
}

impl<S: RingSuite> core::fmt::Debug for TestVector<S>
where
BaseField<S>: ark_ff::PrimeField,
CurveConfig<S>: SWCurveConfig + Clone,
AffinePoint<S>: SWMapping<CurveConfig<S>>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TestVector")
.field("pedersen", &self.pedersen)
.field("ring_proof", &"...")
.finish()
}
}

impl<S: RingSuiteExt + std::fmt::Debug + 'static> common::TestVectorTrait for TestVector<S>
where
BaseField<S>: ark_ff::PrimeField,
CurveConfig<S>: SWCurveConfig + Clone,
AffinePoint<S>: SWMapping<CurveConfig<S>>,
{
fn new(comment: &str, seed: &[u8], alpha: &[u8], salt: Option<&[u8]>, ad: &[u8]) -> Self {
use super::Prover;
let pedersen = pedersen::testing::TestVector::new(comment, seed, alpha, salt, ad);

let secret = Secret::<S>::from_scalar(pedersen.base.sk);
let public = secret.public();

let input = Input::<S>::from(pedersen.base.h);
let output = Output::from(pedersen.base.gamma);

let ring_ctx = <S as RingSuiteExt>::ring_context();

use ark_std::rand::SeedableRng;
let rng = &mut rand_chacha::ChaCha20Rng::from_seed([0x11; 32]);
let prover_idx = 3;
let mut ring_pks = common::random_vec::<AffinePoint<S>>(TEST_RING_SIZE, Some(rng));
ring_pks[prover_idx] = public.0;

let prover_key = ring_ctx.prover_key(&ring_pks);
let prover = ring_ctx.prover(prover_key, prover_idx);
let proof = secret.prove(input, output, ad, &prover);

{
// Just in case...
let mut p = (Vec::new(), Vec::new());
pedersen.proof.serialize_compressed(&mut p.0).unwrap();
proof.pedersen_proof.serialize_compressed(&mut p.1).unwrap();
assert_eq!(p.0, p.1);
}

// TODO: also dump the verifier pks commitmet
Self {
pedersen,
ring_pks: crate::testing::vec_to_array(ring_pks).unwrap(),
ring_proof: proof.ring_proof,
}
}

fn from_map(map: &common::TestVectorMap) -> Self {
let pedersen = pedersen::testing::TestVector::from_map(map);

let ring_pks_raw = map.item_bytes("ring_pks");
let ring_pks =
<[AffinePoint<S>; TEST_RING_SIZE]>::deserialize_compressed(&ring_pks_raw[..])
.map(Box::new)
.unwrap();

let ring_proof_raw = map.item_bytes("ring_proof");
let ring_proof = RingProof::<S>::deserialize_compressed(&ring_proof_raw[..]).unwrap();

Self {
pedersen,
ring_pks,
ring_proof,
}
}

fn to_map(&self) -> common::TestVectorMap {
let mut map = self.pedersen.to_map();

let mut ring_pks_raw = Vec::new();
self.ring_pks
.serialize_compressed(&mut ring_pks_raw)
.unwrap();
let ring_pks_hex = hex::encode(ring_pks_raw);
map.0.insert("ring_pks".to_string(), ring_pks_hex);

let mut ring_proof_raw = Vec::new();
self.ring_proof
.serialize_compressed(&mut ring_proof_raw)
.unwrap();
let ring_proof_hex = hex::encode(ring_proof_raw);
map.0.insert("ring_proof".to_string(), ring_proof_hex);

map
}

fn run(&self) {
self.pedersen.run();

let input = Input::<S>::from(self.pedersen.base.h);
let output = Output::from(self.pedersen.base.gamma);
let secret = Secret::from_scalar(self.pedersen.base.sk);
let public = secret.public();
assert_eq!(public.0, self.pedersen.base.pk);

let ring_ctx = <S as RingSuiteExt>::ring_context();

let ring_pks = &*self.ring_pks;
let prover_idx = ring_pks.iter().position(|&pk| pk == public.0).unwrap();

let prover_key = ring_ctx.prover_key(ring_pks);
let prover = ring_ctx.prover(prover_key, prover_idx);

let verifier_key = ring_ctx.verifier_key(ring_pks);
let verifier = ring_ctx.verifier(verifier_key);

let proof = secret.prove(input, output, &self.pedersen.base.ad, &prover);

{
// Check if Pedersen proof matches
let mut p = (Vec::new(), Vec::new());
self.pedersen.proof.serialize_compressed(&mut p.0).unwrap();
proof.pedersen_proof.serialize_compressed(&mut p.1).unwrap();
assert_eq!(p.0, p.1);
}

// TODO
#[cfg(feature = "test-vectors")]
{
// Check if Ring proof matches
let mut p = (Vec::new(), Vec::new());
self.ring_proof.serialize_compressed(&mut p.0).unwrap();
proof.ring_proof.serialize_compressed(&mut p.1).unwrap();
assert_eq!(p.0, p.1);
}

assert!(
Public::verify(input, output, &self.pedersen.base.ad, &proof, &verifier).is_ok()
);
}
}
}
84 changes: 83 additions & 1 deletion src/suites/bandersnatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,47 @@ mod test_vectors_pedersen_ed {
}
}

#[cfg(all(test, feature = "ring"))]
mod test_vectors_ring_ed {
use super::edwards::*;
use crate::testing;

type V = crate::ring::testing::TestVector<BandersnatchSha512Ell2>;

const TEST_VECTORS_FILE: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/data/bandersnatch_ed_sha512_ell2_ring_vectors.json"
);

impl crate::ring::testing::RingSuiteExt for BandersnatchSha512Ell2 {
fn ring_context() -> &'static RingContext {
use ark_serialize::CanonicalDeserialize;
use std::sync::OnceLock;
static RING_CTX: OnceLock<RingContext> = OnceLock::new();
RING_CTX.get_or_init(|| {
use std::{fs::File, io::Read};
let mut file = File::open(crate::testing::PCS_SRS_FILE).unwrap();
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
let pcs_params =
PcsParams::deserialize_uncompressed_unchecked(&mut &buf[..]).unwrap();
RingContext::from_srs(crate::ring::testing::TEST_RING_SIZE, pcs_params).unwrap()
})
}
}

#[test]
#[ignore = "test vectors generator"]
fn generate() {
testing::test_vectors_generate::<V>(TEST_VECTORS_FILE, "Bandersnatch_SHA-512_ELL2");
}

#[test]
fn process() {
testing::test_vectors_process::<V>(TEST_VECTORS_FILE);
}
}

#[cfg(test)]
mod test_vectors_ietf_sw {
use super::weierstrass::*;
Expand Down Expand Up @@ -332,7 +373,48 @@ mod test_vectors_pedersen_sw {
#[test]
#[ignore = "test vectors generator"]
fn generate() {
testing::test_vectors_generate::<V>(TEST_VECTORS_FILE, "Bandersnatch_SHA-512_TAI");
testing::test_vectors_generate::<V>(TEST_VECTORS_FILE, "Bandersnatch_SW_SHA-512_TAI");
}

#[test]
fn process() {
testing::test_vectors_process::<V>(TEST_VECTORS_FILE);
}
}

#[cfg(all(test, feature = "ring"))]
mod test_vectors_ring_sw {
use super::weierstrass::*;
use crate::testing;

type V = crate::ring::testing::TestVector<BandersnatchSha512Tai>;

const TEST_VECTORS_FILE: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/data/bandersnatch_sw_sha512_tai_ring_vectors.json"
);

impl crate::ring::testing::RingSuiteExt for BandersnatchSha512Tai {
fn ring_context() -> &'static RingContext {
use ark_serialize::CanonicalDeserialize;
use std::sync::OnceLock;
static RING_CTX: OnceLock<RingContext> = OnceLock::new();
RING_CTX.get_or_init(|| {
use std::{fs::File, io::Read};
let mut file = File::open(crate::testing::PCS_SRS_FILE).unwrap();
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
let pcs_params =
PcsParams::deserialize_uncompressed_unchecked(&mut &buf[..]).unwrap();
RingContext::from_srs(crate::ring::testing::TEST_RING_SIZE, pcs_params).unwrap()
})
}
}

#[test]
#[ignore = "test vectors generator"]
fn generate() {
testing::test_vectors_generate::<V>(TEST_VECTORS_FILE, "Bandersnatch_SW_SHA-512_TAI");
}

#[test]
Expand Down
16 changes: 16 additions & 0 deletions src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ use ark_std::{rand::RngCore, UniformRand};

pub const TEST_SEED: &[u8] = b"seed";

// Zcash SRS file derived from (https://zfnd.org/conclusion-of-the-powers-of-tau-ceremony).
pub const PCS_SRS_FILE: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/data/zcash-bls12-381-srs-2-11-uncompressed.bin"
);

/// Generate a vector of random values.
pub fn random_vec<T: UniformRand>(n: usize, rng: Option<&mut dyn RngCore>) -> Vec<T> {
let mut local_rng = ark_std::test_rng();
Expand All @@ -21,6 +27,16 @@ pub fn random_val<T: UniformRand>(rng: Option<&mut dyn RngCore>) -> T {
T::rand(rng)
}

pub fn vec_to_array<T: core::fmt::Debug, const N: usize>(v: Vec<T>) -> Option<Box<[T; N]>> {
if v.len() != N {
return None;
}
// Safe because we checked the length
let boxed_slice = v.into_boxed_slice();
let boxed_array = boxed_slice.try_into().unwrap();
Some(boxed_array)
}

#[macro_export]
macro_rules! suite_tests {
($suite:ident, $build_ring:ident) => {
Expand Down
Loading