mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2025-04-02 01:35:16 +00:00
252 lines
8.9 KiB
Rust
252 lines
8.9 KiB
Rust
// Copyright 2018 Chia Network Inc and POA Networks Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
#![deny(unsafe_code)]
|
|
use num_traits::{One, Zero};
|
|
use std::ops::{Mul, MulAssign, Rem, ShlAssign};
|
|
|
|
pub mod gmp;
|
|
|
|
pub mod gmp_classgroup;
|
|
pub use self::gmp_classgroup::{
|
|
do_compute,
|
|
ffi::{export_obj, import_obj},
|
|
};
|
|
pub trait BigNum:
|
|
Zero
|
|
+ One
|
|
+ Clone
|
|
+ PartialOrd
|
|
+ std::fmt::Debug
|
|
+ Rem
|
|
+ ShlAssign<usize>
|
|
+ for<'a> MulAssign<&'a Self>
|
|
+ std::ops::Sub<u64, Output = Self>
|
|
+ std::ops::Add<u64, Output = Self>
|
|
+ std::convert::From<u64>
|
|
+ for<'a> std::convert::From<&'a [u8]>
|
|
+ std::ops::Shl<usize, Output = Self>
|
|
+ std::ops::Shr<usize, Output = Self>
|
|
+ std::ops::Neg<Output = Self>
|
|
+ std::str::FromStr
|
|
+ for<'a> std::ops::Div<&'a Self, Output = Self>
|
|
+ Eq
|
|
+ std::hash::Hash
|
|
{
|
|
fn probab_prime(&self, iterations: u32) -> bool;
|
|
fn setbit(&mut self, offset: usize);
|
|
fn mod_powm(&mut self, base: &Self, exponent: &Self, modulus: &Self);
|
|
}
|
|
|
|
pub trait BigNumExt: BigNum {
|
|
fn frem_u32(&self, modulus: u32) -> u32;
|
|
fn crem_u16(&mut self, modulus: u16) -> u16;
|
|
}
|
|
|
|
pub trait ClassGroup:
|
|
Sized + Clone + for<'a> MulAssign<&'a Self> + for<'a> Mul<&'a Self> + PartialEq + std::fmt::Debug
|
|
{
|
|
type BigNum: BigNum;
|
|
|
|
/// Produces a `Self` from `a`, `b`, and a discriminant.
|
|
fn from_ab_discriminant(a: Self::BigNum, b: Self::BigNum, discriminant: Self::BigNum) -> Self;
|
|
|
|
/// Unmarshals a `Self` from a byte array and discriminant.
|
|
///
|
|
/// The byte array will be in the format of two big-endian byte sequences
|
|
/// concatenated together.
|
|
fn from_bytes(bytearray: &[u8], discriminant: Self::BigNum) -> Self;
|
|
|
|
/// Computes the identity element of `Self` for a given discriminant.
|
|
///
|
|
/// If the discriminant is not valid, the result is unspecified.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// This may panic (but is not required to) if the discriminant is not
|
|
/// valid. If this function does not panic, the results of future
|
|
/// operations are unspecified: they will not invoke undefined behavior,
|
|
/// but may panic, loop forever, or just compute garbage.
|
|
///
|
|
/// In debug builds, this will always panic if the discriminant is invalid.
|
|
fn identity_for_discriminant(discriminant: Self::BigNum) -> Self {
|
|
Self::from_ab_discriminant(Self::BigNum::one(), Self::BigNum::one(), discriminant)
|
|
}
|
|
|
|
/// Serializes `self` to a byte array. Returns `Err(s)` if there
|
|
/// is not enough space in the buffer.
|
|
///
|
|
/// The data must be serialized in twos-complement, big-endian format.
|
|
fn serialize(&self, buf: &mut [u8]) -> std::result::Result<(), usize>;
|
|
|
|
/// Deserializes a bignum from raw bytes. The bytes **must** be interpreted
|
|
/// as a big-endian unsigned integer.
|
|
fn unsigned_deserialize_bignum(_: &[u8]) -> Self::BigNum;
|
|
|
|
/// Reduce `self` in-place.
|
|
fn reduce(&mut self);
|
|
|
|
/// Squares `self`, modifying it in-place.
|
|
///
|
|
/// A default implementation is provided, but implementations are suggested
|
|
/// to override it for performance reasons.
|
|
fn square(&mut self) {
|
|
let s = self.clone();
|
|
self.mul_assign(&s)
|
|
}
|
|
|
|
/// Normalize `self`.
|
|
fn normalize(&mut self);
|
|
|
|
/// The length of `num` in **bits**
|
|
fn size_in_bits(num: &Self::BigNum) -> usize;
|
|
|
|
/// Gets the discriminant of `self`.
|
|
fn discriminant(&self) -> &Self::BigNum;
|
|
|
|
/// Computes the identity element of a `ClassGroup`.
|
|
fn identity(&self) -> Self {
|
|
Self::identity_for_discriminant(self.discriminant().clone())
|
|
}
|
|
|
|
/// Generates a *generator* for the class group of `Self`, given a
|
|
/// discriminant.
|
|
///
|
|
/// If the discriminant is not valid, the result is unspecified.
|
|
///
|
|
/// # Relation to `Self::identity_for_discriminant`
|
|
///
|
|
/// This is *not* the same as `Self::identity_for_discriminant`: the
|
|
/// identity element is *never* a generator for *any* group. This follows
|
|
/// from their definitions: the identity element, when multiplied by another
|
|
/// element, always gives that other element, whereas *every* element in the
|
|
/// group is some power of a generator.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// This may panic (but is not required to) if the discriminant is not
|
|
/// valid. If this function does not panic, the results of future
|
|
/// operations are unspecified: they will not invoke undefined behavior,
|
|
/// but may panic, loop forever, or just compute garbage.
|
|
///
|
|
/// If the global allocator panics on running out of memory, then this
|
|
/// function may panic in the same situation, but it may also just abort the
|
|
/// program instead.
|
|
///
|
|
/// In debug builds, this will always panic if the discriminant is invalid.
|
|
fn generator_for_discriminant(discriminant: Self::BigNum) -> Self {
|
|
Self::from_ab_discriminant(2.into(), One::one(), discriminant)
|
|
}
|
|
|
|
/// Replaces `*self` with its inverse.
|
|
fn inverse(&mut self);
|
|
|
|
/// Squares `self` repeatedly in-place.
|
|
///
|
|
/// Implementors of this trait are encouraged to override this
|
|
/// with a more efficient implementation, if one exists.
|
|
fn repeated_square(&mut self, iterations: u64) {
|
|
for _ in 0..iterations {
|
|
self.square()
|
|
}
|
|
}
|
|
|
|
/// Exponentiation
|
|
fn pow(&mut self, exponent: Self::BigNum);
|
|
|
|
/// Deserialization
|
|
fn deserialize(buf: &[u8], discriminant: Self::BigNum) -> Self;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
|
|
use std::{
|
|
fs::File,
|
|
io::{BufRead, BufReader},
|
|
path::PathBuf,
|
|
};
|
|
|
|
use super::{gmp_classgroup::GmpClassGroup, ClassGroup};
|
|
use super::gmp::mpz::Mpz;
|
|
|
|
fn split_into_three_pieces(line: &str, c: char) -> [&str; 3] {
|
|
let mut iter = line.split(c);
|
|
let fst = iter.next().expect("bad test file");
|
|
let snd = iter.next().expect("bad test file");
|
|
let thd = iter.next().expect("bad test file");
|
|
assert!(iter.next().is_none(), "bad test file");
|
|
[fst, snd, thd]
|
|
}
|
|
|
|
#[test]
|
|
fn multiplication_is_correct() {
|
|
let manifest_path =
|
|
std::env::var("CARGO_MANIFEST_DIR").expect("cargo should have set this");
|
|
let mut path = PathBuf::from(&manifest_path);
|
|
path.push("tests/multiply.txt");
|
|
let mut f = BufReader::new(File::open(path).expect("test file missing or unreadable"));
|
|
let mut buffer = String::new();
|
|
loop {
|
|
let bytes_read = f
|
|
.read_line(&mut buffer)
|
|
.expect("could not read from test file");
|
|
assert!(bytes_read == buffer.len());
|
|
if bytes_read == 0 {
|
|
break;
|
|
}
|
|
if buffer.ends_with('\n') {
|
|
buffer.pop();
|
|
}
|
|
if buffer.ends_with('\r') {
|
|
buffer.pop();
|
|
}
|
|
let mut current_discriminant: Option<Mpz> = None;
|
|
let q: Vec<_> = split_into_three_pieces(&buffer, '|')
|
|
.iter()
|
|
.map(|i| {
|
|
let k = split_into_three_pieces(i, ',');
|
|
|
|
let a = Mpz::from_str_radix(k[0], 10).expect("bad test file");
|
|
let b = Mpz::from_str_radix(k[1], 10).expect("bad test file");
|
|
let c = Mpz::from_str_radix(k[2], 10).expect("bad test file");
|
|
let mut discriminant: Mpz = &b * &b;
|
|
let mut minuand: Mpz = (4u64).into();
|
|
minuand *= &a * &c;
|
|
discriminant -= &minuand;
|
|
assert!(discriminant < Mpz::zero());
|
|
// takes waaaay too long
|
|
// assert!(discriminant.probab_prime(20) !=
|
|
// gmp::mpz::ProbabPrimeResult::NotPrime);
|
|
if let Some(ref q) = current_discriminant {
|
|
assert_eq!(q, &discriminant, "mismatching discriminant in test files");
|
|
} else {
|
|
current_discriminant = Some(discriminant.clone());
|
|
}
|
|
GmpClassGroup::from_ab_discriminant(a, b, discriminant)
|
|
})
|
|
.collect();
|
|
assert_eq!(q.len(), 3);
|
|
if q[0] == q[1] {
|
|
let mut i = q[0].clone();
|
|
i.square();
|
|
assert_eq!(i, q[2]);
|
|
}
|
|
assert_eq!(&q[1] * &q[0], q[2], "multiplication not valid");
|
|
assert_eq!(&q[0] * &q[1], q[2], "multiplication not valid");
|
|
buffer.clear();
|
|
}
|
|
}
|
|
|
|
}
|