Skip to content

Commit

Permalink
VDF clean ups (#770)
Browse files Browse the repository at this point in the history
* Clean ups

* Use generic multiplier

* Use bcs

* Serde from workspace

* remove debug code

* docs

* fix test

* test

* Refactor + new serde

* challenge size

* Default challenge size

* clean up

* clippy

* license

* test

* Simplify

* license

* Remove trait

* review comments

* Error on non-prime discriminant

* Error type

* Revert "Error type"

This reverts commit 28422f9.

* test

* fix limit

* test

* clippy

* review

* Document challenge size

* Update test

* Remove debug code
  • Loading branch information
jonas-lj committed May 13, 2024
1 parent a062a77 commit 086e0d7
Show file tree
Hide file tree
Showing 22 changed files with 625 additions and 544 deletions.
7 changes: 5 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

129 changes: 80 additions & 49 deletions fastcrypto-cli/src/vdf.rs

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion fastcrypto-vdf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ repository = "https://github.com/MystenLabs/fastcrypto"

[dependencies]
fastcrypto = { path = "../fastcrypto" }
num-bigint = "0.4.4"
num-bigint = { version = "0.4.4" }
num-traits = "0.2.16"
num-integer = "0.1.45"
num-prime = { version = "0.4.3", features = ["big-int"] }
rand = "0.8.5"
rand_chacha = "0.3.1"
serde.workspace = true
bcs.workspace = true
serde_with = "2.1.0"

[features]
experimental = []
Expand Down
2 changes: 1 addition & 1 deletion fastcrypto-vdf/benches/class_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkGroup, Crit
use fastcrypto::groups::Doubling;
use fastcrypto_vdf::class_group::discriminant::Discriminant;
use fastcrypto_vdf::class_group::QuadraticForm;
use fastcrypto_vdf::ParameterizedGroupElement;
use fastcrypto_vdf::math::parameterized_group::ParameterizedGroupElement;
use num_bigint::BigInt;
use num_traits::Num;
use rand::{thread_rng, RngCore};
Expand Down
26 changes: 5 additions & 21 deletions fastcrypto-vdf/benches/vdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@ extern crate criterion;

use criterion::measurement::Measurement;
use criterion::{BenchmarkGroup, BenchmarkId, Criterion};
use fastcrypto::groups::multiplier::windowed::WindowedScalarMultiplier;
use fastcrypto_vdf::class_group::discriminant::Discriminant;
use fastcrypto_vdf::class_group::QuadraticForm;
use fastcrypto_vdf::math::hash_prime::DefaultPrimalityCheck;
use fastcrypto_vdf::vdf::wesolowski::fiat_shamir::StrongFiatShamir;
use fastcrypto_vdf::vdf::wesolowski::{FastVerifier, StrongVDF, CHALLENGE_SIZE};
use fastcrypto_vdf::math::parameterized_group::Parameter;
use fastcrypto_vdf::vdf::wesolowski::DefaultVDF;
use fastcrypto_vdf::vdf::VDF;
use fastcrypto_vdf::Parameter;
use num_bigint::BigInt;
use num_traits::Num;
use rand::{thread_rng, RngCore};
Expand All @@ -32,30 +29,17 @@ fn verify_single<M: Measurement>(parameters: VerificationInputs, c: &mut Benchma
let discriminant_size = discriminant.bits();

let result_bytes = hex::decode(parameters.result).unwrap();
let result = QuadraticForm::from_bytes(&result_bytes, &discriminant).unwrap();
let result_copy = result.clone();
let result = bcs::from_bytes(&result_bytes).unwrap();

let proof_bytes = hex::decode(parameters.proof).unwrap();
let proof = QuadraticForm::from_bytes(&proof_bytes, &discriminant).unwrap();
let proof_copy = proof.clone();
let proof = bcs::from_bytes(&proof_bytes).unwrap();

let input = QuadraticForm::generator(&discriminant);
let input_copy = input.clone();

let vdf = StrongVDF::new(discriminant.clone(), parameters.iterations);
let vdf = DefaultVDF::new(discriminant.clone(), parameters.iterations);
c.bench_function(discriminant_size.to_string(), move |b| {
b.iter(|| vdf.verify(&input, &result, &proof))
});

let vdf = StrongVDF::new(discriminant.clone(), parameters.iterations);
let fast_verify: FastVerifier<
QuadraticForm,
StrongFiatShamir<QuadraticForm, CHALLENGE_SIZE, DefaultPrimalityCheck>,
WindowedScalarMultiplier<QuadraticForm, BigInt, 256, 5>,
> = FastVerifier::new(vdf, input_copy);
c.bench_function(format!("{} fast", discriminant_size), move |b| {
b.iter(|| fast_verify.verify(&result_copy, &proof_copy))
});
}

fn verify(c: &mut Criterion) {
Expand Down
50 changes: 50 additions & 0 deletions fastcrypto-vdf/src/class_group/bigint_serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use num_bigint::BigInt;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;

pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<BigInt, D::Error>
where
D: Deserializer<'de>,
{
Ok(BigInt::from_signed_bytes_be(&<Vec<u8>>::deserialize(
deserializer,
)?))
}

pub(crate) fn serialize<S>(value: &BigInt, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
value.to_signed_bytes_be().serialize(serializer)
}

#[cfg(test)]
mod tests {
use super::*;
use num_bigint::BigInt;

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
#[serde(transparent)]
struct TestStruct(
#[serde(serialize_with = "serialize", deserialize_with = "deserialize")] BigInt,
);

#[test]
fn test_serde() {
let test_values = vec![
TestStruct(BigInt::from(-1234567890)),
TestStruct(BigInt::from(1234567890)),
TestStruct(BigInt::from(0)),
];
for value in test_values {
let serialized = bcs::to_bytes(&value).unwrap();
let deserialized: TestStruct = bcs::from_bytes(&serialized).unwrap();
assert_eq!(value, deserialized);
}
}
}
102 changes: 81 additions & 21 deletions fastcrypto-vdf/src/class_group/discriminant.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::{math::hash_prime, Parameter, ToBytes};
use crate::math::hash_prime;
use crate::math::hash_prime::is_probable_prime;
use crate::math::parameterized_group::Parameter;
use fastcrypto::error::FastCryptoError::InvalidInput;
use fastcrypto::error::{FastCryptoError, FastCryptoResult};
use num_bigint::{BigInt, Sign, ToBigInt};
use num_bigint::{BigInt, ToBigInt};
use num_integer::Integer;
use num_traits::Signed;
use serde::{Deserialize, Serialize};
use std::ops::Neg;

/// A discriminant for an imaginary class group. The discriminant is a negative integer which is
/// equal to 1 mod 8.
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Discriminant(BigInt);

impl ToBytes for Discriminant {
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes_be().1
}
}
/// A discriminant for an imaginary class group. The discriminant is a negative integer congruent to
/// 1 mod 8.
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
pub struct Discriminant(#[serde(with = "crate::class_group::bigint_serde")] BigInt);

impl TryFrom<BigInt> for Discriminant {
type Error = FastCryptoError;

/// A valid discriminant should be a negative prime congruent to 1 mod 8. The sign and
/// congruency are checked here but the primality is _not_. See also [Discriminant::check_primality].
fn try_from(value: BigInt) -> FastCryptoResult<Self> {
if !value.is_negative() || value.mod_floor(&BigInt::from(8)) != BigInt::from(1) {
return Err(InvalidInput);
Expand All @@ -37,17 +36,24 @@ impl Discriminant {
self.0.bits()
}

/// Try to create a discriminant from a big-endian byte representation of the absolute value.
/// Fails if the discriminant is not equal to 1 mod 8.
pub fn try_from_be_bytes(bytes: &[u8]) -> FastCryptoResult<Self> {
let discriminant = BigInt::from_bytes_be(Sign::Minus, bytes);
Self::try_from(discriminant)
}

/// Borrow a reference to the underlying big integer.
pub fn as_bigint(&self) -> &BigInt {
pub(crate) fn as_bigint(&self) -> &BigInt {
&self.0
}

/// Check the primality of this discriminant and return an error if it is not prime.
pub fn check_primality(&self) -> FastCryptoResult<()> {
match is_probable_prime(
&self
.0
.abs()
.to_biguint()
.expect("Absolute value is non-negative"),
) {
true => Ok(()),
false => Err(InvalidInput),
}
}
}

impl Parameter for Discriminant {
Expand All @@ -59,10 +65,64 @@ impl Parameter for Discriminant {
}
// Set the lower three bits to ensure that the prime is 7 mod 8 which makes the discriminant 1 mod 8.
Self::try_from(
hash_prime::hash_prime_default(seed, size_in_bits / 8, &[0, 1, 2, size_in_bits - 1])
hash_prime::hash_prime(seed, size_in_bits / 8, &[0, 1, 2, size_in_bits - 1])
.to_bigint()
.expect("Never fails")
.neg(),
)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_discriminant() {
let discriminant = Discriminant::try_from(-BigInt::from(223)).unwrap();
assert_eq!(discriminant.bits(), 8);
assert_eq!(discriminant.as_bigint(), &-BigInt::from(223));

// Invalid modulus
let candidate = BigInt::from(-29);
assert!(candidate.is_negative());
assert!(Discriminant::try_from(candidate).is_err());

// Invalid sign
let candidate = BigInt::from(17);
assert_eq!(candidate.mod_floor(&BigInt::from(8)), BigInt::from(1));
assert!(Discriminant::try_from(candidate).is_err());

// Not prime
let candidate = BigInt::from(-231);
let discriminant = Discriminant::try_from(candidate).unwrap();
assert!(discriminant.check_primality().is_err());
}

#[test]
fn test_discriminant_from_seed() {
let seed = hex::decode("d911a54e3bf6f52b4111").unwrap();
let target_size = 1024;
let discriminant = Discriminant::from_seed(&seed, target_size).unwrap();
assert_eq!(discriminant.bits() as usize, target_size);
assert!(discriminant.check_primality().is_ok());

// Test vector from chiavdf computed using https://github.com/Chia-Network/chiavdf/blob/2844974ff81274060778a56dfefd2515bc567b90/tests/test_verifier.py.
assert_eq!(discriminant.as_bigint().to_str_radix(16), "-95a0b0523b6c516e813d745e7e58b3c7223d511f6008a0ff2757c9a0f15cba8841293cc903af3a40654670c9dee17ec14da1457360aafe40a93831d90c3dd59738d8a24e415b6e33780224fa24171de1d4a1ca5fe4c877bf44361e7ba869126ac12367714eb4246a5e310515508ad35e170aee19cae371069d6d92e94c21d63f");
}

#[test]
fn test_discriminant_to_from_bytes() {
let discriminant = Discriminant::try_from(BigInt::from(-223)).unwrap();
let bytes = bcs::to_bytes(&discriminant).unwrap();
let discriminant2 = bcs::from_bytes(&bytes).unwrap();
assert_eq!(discriminant, discriminant2);
assert!(discriminant.check_primality().is_ok());

let discriminant = Discriminant::from_seed(&[0x01, 0x02, 0x03], 512).unwrap();
let bytes = bcs::to_bytes(&discriminant).unwrap();
let discriminant2 = bcs::from_bytes(&bytes).unwrap();
assert_eq!(discriminant, discriminant2);
assert!(discriminant.check_primality().is_ok());
}
}

0 comments on commit 086e0d7

Please sign in to comment.