mirror of
https://github.com/vacp2p/semaphore-rs.git
synced 2025-02-24 01:28:28 +00:00
commit
6964f9f93d
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
/target
|
||||
*.profraw
|
||||
.*
|
||||
snarkfiles_tmp
|
||||
Cargo.lock
|
||||
snarkfiles_tmp
|
||||
|
3236
Cargo.lock
generated
3236
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,6 @@ ark-relations = { version = "0.3.0", default-features = false }
|
||||
ark-std = { version = "0.3.0", default-features = false, features = ["parallel"] }
|
||||
color-eyre = "0.5"
|
||||
criterion = { version = "0.3", optional = true, features = [ "async_tokio" ] }
|
||||
ethers-core = "0.6.3"
|
||||
ff = { package="ff_ce", version="0.11"}
|
||||
hex = "0.4.0"
|
||||
hex-literal = "0.3"
|
||||
@ -45,9 +44,13 @@ proptest = { version = "1.0", optional = true }
|
||||
rayon = "1.5.1"
|
||||
serde = "1.0"
|
||||
sha2 = "0.10.1"
|
||||
thiserror = "1.0.0"
|
||||
tiny-keccak = { version = "2.0.2", optional = true }
|
||||
zkp-u256 = { version = "0.2", optional = true }
|
||||
|
||||
# Use the same `ethers-core` version as ark-circom
|
||||
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.0"
|
||||
serde_json = "1.0.79"
|
||||
@ -55,8 +58,8 @@ tempfile = "3.0"
|
||||
tiny-keccak = "2.0.2"
|
||||
tracing-test = "0.2"
|
||||
|
||||
[patch.crates-io]
|
||||
wasmer = { git = 'https://github.com/philsippl/wasmer', rev = "e776616"}
|
||||
# [patch.crates-io]
|
||||
# wasmer = { git = 'https://github.com/philsippl/wasmer', rev = "e776616"}
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
15
README.md
15
README.md
@ -5,7 +5,8 @@ Rust support library for using [semaphore](https://github.com/appliedzkp/semapho
|
||||
## Usage
|
||||
|
||||
Add this line to your `cargo.toml`:
|
||||
```
|
||||
|
||||
```toml
|
||||
semaphore = { git = "https://github.com/worldcoin/semaphore-rs" }
|
||||
```
|
||||
|
||||
@ -13,7 +14,7 @@ semaphore = { git = "https://github.com/worldcoin/semaphore-rs" }
|
||||
|
||||
1. Check out submodule (if not done before already): `git submodule update --init --recursive`
|
||||
1. Install semaphore dependencies `cd semaphore && npm install`
|
||||
1. Compile circuits `ts-node ./scripts/compile-circuits.ts`
|
||||
1. Compile circuits `npm exec ts-node ./scripts/compile-circuits.ts`
|
||||
1. You'll find the `zkey` and `wasm` file in `semaphore/build/snark`
|
||||
|
||||
## Example
|
||||
@ -21,6 +22,10 @@ semaphore = { git = "https://github.com/worldcoin/semaphore-rs" }
|
||||
Example as in `src/lib.rs`, run with `cargo test`.
|
||||
|
||||
```rust
|
||||
use semaphore::{identity::Identity, hash::Hash, poseidon_tree::PoseidonTree,
|
||||
protocol::* };
|
||||
use num_bigint::BigInt;
|
||||
|
||||
// generate identity
|
||||
let id = Identity::new(b"secret");
|
||||
|
||||
@ -35,8 +40,8 @@ let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||
let root = tree.root();
|
||||
|
||||
// change signal and external_nullifier here
|
||||
let signal = "xxx".as_bytes();
|
||||
let external_nullifier = "appId".as_bytes();
|
||||
let signal = b"xxx";
|
||||
let external_nullifier = b"appId";
|
||||
|
||||
let external_nullifier_hash = hash_external_nullifier(external_nullifier);
|
||||
let nullifier_hash = generate_nullifier_hash(&id, &external_nullifier_hash);
|
||||
@ -50,4 +55,4 @@ let proof = generate_proof(&config, &id, &merkle_proof, &external_nullifier_hash
|
||||
let success = verify_proof(&config, &root.into(), &nullifier_hash, signal, &external_nullifier_hash, &proof).unwrap();
|
||||
|
||||
assert!(success);
|
||||
```
|
||||
```
|
||||
|
22
cspell.json
Normal file
22
cspell.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": "0.2",
|
||||
"ignorePaths": [],
|
||||
"dictionaryDefinitions": [],
|
||||
"dictionaries": [],
|
||||
"words": [
|
||||
"biguint",
|
||||
"circom",
|
||||
"groth",
|
||||
"hasher",
|
||||
"keccak",
|
||||
"merkle",
|
||||
"mimc",
|
||||
"mmaped",
|
||||
"modpow",
|
||||
"Repr",
|
||||
"snarkfiles",
|
||||
"zkey"
|
||||
],
|
||||
"ignoreWords": [],
|
||||
"import": []
|
||||
}
|
@ -65,7 +65,7 @@ impl From<Vec<u8>> for Hash {
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion to BigInt
|
||||
/// Conversion to `BigInt`
|
||||
impl From<Hash> for BigInt {
|
||||
fn from(hash: Hash) -> Self {
|
||||
Self::from_bytes_be(Sign::Plus, hash.as_bytes_be())
|
||||
|
@ -23,6 +23,7 @@ fn sha(msg: &[u8]) -> [u8; 32] {
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
#[must_use]
|
||||
pub fn new(seed: &[u8]) -> Self {
|
||||
let seed_hash = &sha(seed);
|
||||
|
||||
@ -42,20 +43,22 @@ impl Identity {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn secret_hash(&self) -> BigInt {
|
||||
let res = POSEIDON
|
||||
.hash(vec![
|
||||
bigint_to_fr(&self.nullifier),
|
||||
bigint_to_fr(&self.trapdoor),
|
||||
])
|
||||
.unwrap();
|
||||
.expect("input length is constant and valid for the hash");
|
||||
fr_to_bigint(res)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn commitment(&self) -> BigInt {
|
||||
let res = POSEIDON
|
||||
.hash(vec![bigint_to_fr(&self.secret_hash())])
|
||||
.unwrap();
|
||||
.expect("input length is constant and valid for the hash");
|
||||
fr_to_bigint(res)
|
||||
}
|
||||
}
|
||||
|
57
src/lib.rs
57
src/lib.rs
@ -1,3 +1,8 @@
|
||||
#![doc = include_str!("../Readme.md")]
|
||||
#![warn(clippy::all, clippy::pedantic, clippy::cargo, clippy::nursery)]
|
||||
// TODO: ark-circom and ethers-core pull in a lot of deps, some duplicate.
|
||||
#![allow(clippy::multiple_crate_versions)]
|
||||
|
||||
pub mod hash;
|
||||
pub mod identity;
|
||||
pub mod merkle_tree;
|
||||
@ -18,20 +23,27 @@ pub type EthereumGroth16Proof = ark_circom::ethereum::Proof;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use hash::*;
|
||||
use identity::*;
|
||||
use poseidon_tree::*;
|
||||
use protocol::*;
|
||||
use crate::{
|
||||
hash::Hash,
|
||||
identity::Identity,
|
||||
poseidon_tree::PoseidonTree,
|
||||
protocol::{
|
||||
generate_nullifier_hash, generate_proof, hash_external_nullifier, verify_proof,
|
||||
SnarkFileConfig,
|
||||
},
|
||||
};
|
||||
use hex_literal::hex;
|
||||
|
||||
#[test]
|
||||
fn test_end_to_end() {
|
||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
));
|
||||
|
||||
// generate identity
|
||||
let id = Identity::new(b"secret");
|
||||
let id = Identity::new(b"hello");
|
||||
|
||||
// generate merkle tree
|
||||
const LEAF: Hash = Hash::from_bytes_be([0u8; 32]);
|
||||
|
||||
let mut tree = PoseidonTree::new(21, LEAF);
|
||||
let (_, leaf) = id.commitment().to_bytes_be();
|
||||
tree.set(0, leaf.into());
|
||||
@ -40,8 +52,8 @@ mod test {
|
||||
let root = tree.root();
|
||||
|
||||
// change signal and external_nullifier here
|
||||
let signal = "xxx".as_bytes();
|
||||
let external_nullifier = "appId".as_bytes();
|
||||
let signal = b"xxx";
|
||||
let external_nullifier = b"appId";
|
||||
|
||||
let external_nullifier_hash = hash_external_nullifier(external_nullifier);
|
||||
let nullifier_hash = generate_nullifier_hash(&id, &external_nullifier_hash);
|
||||
@ -51,8 +63,14 @@ mod test {
|
||||
wasm: "./semaphore/build/snark/semaphore.wasm".to_string(),
|
||||
};
|
||||
|
||||
let proof =
|
||||
generate_proof(&config, &id, &merkle_proof, &external_nullifier_hash, signal).unwrap();
|
||||
let proof = generate_proof(
|
||||
&config,
|
||||
&id,
|
||||
&merkle_proof,
|
||||
&external_nullifier_hash,
|
||||
signal,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let success = verify_proof(
|
||||
&config,
|
||||
@ -88,29 +106,30 @@ pub mod bench {
|
||||
}
|
||||
|
||||
fn bench_proof(criterion: &mut Criterion) {
|
||||
// Create tree
|
||||
let id = Identity::new(b"hello");
|
||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
));
|
||||
|
||||
// Create tree
|
||||
let id = Identity::new(b"hello");
|
||||
let mut tree = PoseidonTree::new(21, LEAF);
|
||||
let (_, leaf) = id.commitment().to_bytes_be();
|
||||
tree.set(0, leaf.into());
|
||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||
|
||||
// change signal and external_nullifier here
|
||||
let signal = "xxx".as_bytes();
|
||||
let external_nullifier = "appId".as_bytes();
|
||||
let signal = b"xxx";
|
||||
let external_nullifier = b"appId";
|
||||
|
||||
let config = SnarkFileConfig {
|
||||
zkey: "./snarkfiles/semaphore.zkey".to_string(),
|
||||
wasm: "./snarkfiles/semaphore.wasm".to_string(),
|
||||
zkey: "./semaphore/build/snark/semaphore_final.zkey".to_string(),
|
||||
wasm: "./semaphore/build/snark/semaphore.wasm".to_string(),
|
||||
};
|
||||
|
||||
criterion.bench_function("proof", move |b| {
|
||||
b.iter(|| {
|
||||
generate_proof(&config, &id, &merkle_proof, external_nullifier, signal).unwrap();
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,12 @@
|
||||
//!
|
||||
//! * Disk based storage backend (using mmaped files should be easy)
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
iter::{once, repeat, successors},
|
||||
};
|
||||
use num_bigint::BigInt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Hash types, values and algorithms for a Merkle tree
|
||||
pub trait Hasher {
|
||||
@ -95,6 +95,7 @@ impl<H: Hasher> MerkleTree<H> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn num_leaves(&self) -> usize {
|
||||
self.depth
|
||||
.checked_sub(1)
|
||||
@ -102,6 +103,7 @@ impl<H: Hasher> MerkleTree<H> {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn root(&self) -> H::Hash {
|
||||
self.nodes[0].clone()
|
||||
}
|
||||
@ -134,6 +136,7 @@ impl<H: Hasher> MerkleTree<H> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn proof(&self, leaf: usize) -> Option<Proof<H>> {
|
||||
if leaf >= self.num_leaves() {
|
||||
return None;
|
||||
@ -152,12 +155,12 @@ impl<H: Hasher> MerkleTree<H> {
|
||||
Some(Proof(path))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[must_use]
|
||||
pub fn verify(&self, hash: H::Hash, proof: &Proof<H>) -> bool {
|
||||
proof.root(hash) == self.root()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[must_use]
|
||||
pub fn leaves(&self) -> &[H::Hash] {
|
||||
&self.nodes[(self.num_leaves() - 1)..]
|
||||
}
|
||||
@ -165,7 +168,7 @@ impl<H: Hasher> MerkleTree<H> {
|
||||
|
||||
impl<H: Hasher> Proof<H> {
|
||||
/// Compute the leaf index for this proof
|
||||
#[allow(dead_code)]
|
||||
#[must_use]
|
||||
pub fn leaf_index(&self) -> usize {
|
||||
self.0.iter().rev().fold(0, |index, branch| match branch {
|
||||
Branch::Left(_) => index << 1,
|
||||
@ -174,7 +177,7 @@ impl<H: Hasher> Proof<H> {
|
||||
}
|
||||
|
||||
/// Compute path index (TODO: do we want to keep this here?)
|
||||
#[allow(dead_code)]
|
||||
#[must_use]
|
||||
pub fn path_index(&self) -> Vec<BigInt> {
|
||||
self.0
|
||||
.iter()
|
||||
@ -186,7 +189,7 @@ impl<H: Hasher> Proof<H> {
|
||||
}
|
||||
|
||||
/// Compute the Merkle root given a leaf hash
|
||||
#[allow(dead_code)]
|
||||
#[must_use]
|
||||
pub fn root(&self, hash: H::Hash) -> H::Hash {
|
||||
self.0.iter().fold(hash, |hash, branch| match branch {
|
||||
Branch::Left(sibling) => H::hash_node(&hash, sibling),
|
||||
|
@ -9,8 +9,8 @@
|
||||
//! * Instantiate a `PrimeField` to use Montgomery form.
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use zkp_u256::U256;
|
||||
use tiny_keccak::{Hasher as _, Keccak};
|
||||
use zkp_u256::U256;
|
||||
|
||||
const NUM_ROUNDS: usize = 220;
|
||||
|
||||
@ -21,7 +21,7 @@ static MODULUS: Lazy<U256> = Lazy::new(|| {
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
fn keccak256(bytes: &[u8]) -> [u8;32] {
|
||||
fn keccak256(bytes: &[u8]) -> [u8; 32] {
|
||||
let mut output = [0; 32];
|
||||
let mut hasher = Keccak::v256();
|
||||
hasher.update(bytes);
|
||||
@ -58,6 +58,7 @@ fn mix(left: &mut U256, right: &mut U256) {
|
||||
std::mem::swap(left, right);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn hash(values: &[U256]) -> U256 {
|
||||
let mut left = U256::ZERO;
|
||||
let mut right = U256::ZERO;
|
||||
|
@ -4,16 +4,17 @@ use ark_ec::bn::Bn;
|
||||
use ark_ff::Fp256;
|
||||
use ark_groth16::{create_proof_with_reduction_and_matrices, prepare_verifying_key, Proof};
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
use ark_std::rand::thread_rng;
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use color_eyre::Result;
|
||||
use ethers_core::utils::keccak256;
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use once_cell::sync::Lazy;
|
||||
use poseidon_rs::Poseidon;
|
||||
use std::{collections::HashMap, fs::File, ops::Shr};
|
||||
use std::{collections::HashMap, fs::File, ops::Shr, time::Instant};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
identity::*,
|
||||
identity::Identity,
|
||||
merkle_tree::{self, Branch},
|
||||
poseidon_tree::PoseidonHash,
|
||||
util::{bigint_to_fr, fr_to_bigint},
|
||||
@ -33,8 +34,7 @@ fn merkle_proof_to_vec(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<BigInt>
|
||||
.0
|
||||
.iter()
|
||||
.map(|x| match x {
|
||||
Branch::Left(value) => value.into(),
|
||||
Branch::Right(value) => value.into(),
|
||||
Branch::Left(value) | Branch::Right(value) => value.into(),
|
||||
})
|
||||
.collect::<Vec<BigInt>>()
|
||||
}
|
||||
@ -45,6 +45,7 @@ fn hash_signal(signal: &[u8]) -> BigInt {
|
||||
}
|
||||
|
||||
/// Internal helper to hash the external nullifier
|
||||
#[must_use]
|
||||
pub fn hash_external_nullifier(nullifier: &[u8]) -> [u8; 32] {
|
||||
let mut hash = keccak256(nullifier);
|
||||
hash[0] = 0;
|
||||
@ -55,29 +56,41 @@ pub fn hash_external_nullifier(nullifier: &[u8]) -> [u8; 32] {
|
||||
}
|
||||
|
||||
/// Generates the nullifier hash
|
||||
#[must_use]
|
||||
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: &[u8]) -> BigInt {
|
||||
let res = POSEIDON
|
||||
.hash(vec![
|
||||
bigint_to_fr(&BigInt::from_bytes_be(
|
||||
Sign::Plus,
|
||||
external_nullifier,
|
||||
)),
|
||||
bigint_to_fr(&BigInt::from_bytes_be(Sign::Plus, external_nullifier)),
|
||||
bigint_to_fr(&identity.nullifier),
|
||||
])
|
||||
.unwrap();
|
||||
.expect("hash with fixed input size can't fail");
|
||||
fr_to_bigint(res)
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ProofError {
|
||||
#[error("Error reading circuit key: {0}")]
|
||||
CircuitKeyError(#[from] std::io::Error),
|
||||
#[error("Error producing witness: {0}")]
|
||||
WitnessError(color_eyre::Report),
|
||||
#[error("Error producing proof: {0}")]
|
||||
SynthesisError(#[from] SynthesisError),
|
||||
}
|
||||
|
||||
/// Generates a semaphore proof
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a [`ProofError`] if proving fails.
|
||||
pub fn generate_proof(
|
||||
config: &SnarkFileConfig,
|
||||
identity: &Identity,
|
||||
merkle_proof: &merkle_tree::Proof<PoseidonHash>,
|
||||
external_nullifier: &[u8],
|
||||
signal: &[u8],
|
||||
) -> Result<Proof<Bn<Parameters>>, SynthesisError> {
|
||||
let mut file = File::open(&config.zkey).unwrap();
|
||||
let (params, matrices) = read_zkey(&mut file).unwrap();
|
||||
) -> Result<Proof<Bn<Parameters>>, ProofError> {
|
||||
let mut file = File::open(&config.zkey)?;
|
||||
let (params, matrices) = read_zkey(&mut file)?;
|
||||
let num_inputs = matrices.num_instance_variables;
|
||||
let num_constraints = matrices.num_constraints;
|
||||
|
||||
@ -103,19 +116,17 @@ pub fn generate_proof(
|
||||
inputs
|
||||
};
|
||||
|
||||
use std::time::Instant;
|
||||
let now = Instant::now();
|
||||
|
||||
let mut wtns = WitnessCalculator::new(&config.wasm).unwrap();
|
||||
let mut witness = WitnessCalculator::new(&config.wasm).map_err(ProofError::WitnessError)?;
|
||||
|
||||
let full_assignment = wtns
|
||||
let full_assignment = witness
|
||||
.calculate_witness_element::<Bn254, _>(inputs, false)
|
||||
.unwrap();
|
||||
.map_err(ProofError::WitnessError)?;
|
||||
|
||||
println!("witness generation took: {:.2?}", now.elapsed());
|
||||
|
||||
let mut rng = thread_rng();
|
||||
use ark_std::UniformRand;
|
||||
let rng = &mut rng;
|
||||
|
||||
let r = ark_bn254::Fr::rand(rng);
|
||||
@ -131,14 +142,19 @@ pub fn generate_proof(
|
||||
num_inputs,
|
||||
num_constraints,
|
||||
full_assignment.as_slice(),
|
||||
);
|
||||
)?;
|
||||
|
||||
println!("proof generation took: {:.2?}", now.elapsed());
|
||||
|
||||
proof
|
||||
Ok(proof)
|
||||
}
|
||||
|
||||
/// Verifies a given semaphore proof
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a [`ProofError`] if verifying fails. Verification failure does not
|
||||
/// necessarily mean the proof is incorrect.
|
||||
pub fn verify_proof(
|
||||
config: &SnarkFileConfig,
|
||||
root: &BigInt,
|
||||
@ -146,21 +162,26 @@ pub fn verify_proof(
|
||||
signal: &[u8],
|
||||
external_nullifier: &[u8],
|
||||
proof: &Proof<Bn<Parameters>>,
|
||||
) -> Result<bool, SynthesisError> {
|
||||
let mut file = File::open(&config.zkey).unwrap();
|
||||
let (params, _) = read_zkey(&mut file).unwrap();
|
||||
) -> Result<bool, ProofError> {
|
||||
let mut file = File::open(&config.zkey)?;
|
||||
let (params, _) = read_zkey(&mut file)?;
|
||||
|
||||
let pvk = prepare_verifying_key(¶ms.vk);
|
||||
|
||||
let public_inputs = vec![
|
||||
Fp256::from(root.to_biguint().unwrap()),
|
||||
Fp256::from(nullifier_hash.to_biguint().unwrap()),
|
||||
Fp256::from(hash_signal(signal).to_biguint().unwrap()),
|
||||
Fp256::from(root.to_biguint().expect("can not be negative")),
|
||||
Fp256::from(nullifier_hash.to_biguint().expect("can not be negative")),
|
||||
Fp256::from(
|
||||
hash_signal(signal)
|
||||
.to_biguint()
|
||||
.expect("can not be negative"),
|
||||
),
|
||||
Fp256::from(
|
||||
BigInt::from_bytes_be(Sign::Plus, external_nullifier)
|
||||
.to_biguint()
|
||||
.unwrap(),
|
||||
.expect("can not be negative"),
|
||||
),
|
||||
];
|
||||
ark_groth16::verify_proof(&pvk, proof, &public_inputs)
|
||||
let result = ark_groth16::verify_proof(&pvk, proof, &public_inputs)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -2,12 +2,16 @@ use ff::{PrimeField, PrimeFieldRepr};
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use poseidon_rs::{Fr, FrRepr};
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::missing_panics_doc)] // TODO: Remove panics
|
||||
pub fn fr_to_bigint(fr: Fr) -> BigInt {
|
||||
let mut bytes = [0_u8; 32];
|
||||
fr.into_repr().write_be(&mut bytes[..]).unwrap();
|
||||
BigInt::from_bytes_be(Sign::Plus, &bytes)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::missing_panics_doc)] // TODO: Remove panics
|
||||
pub fn bigint_to_fr(bi: &BigInt) -> Fr {
|
||||
// dirty: have to force the point into the field manually, otherwise you get an
|
||||
// error if bi not in field
|
||||
|
Loading…
x
Reference in New Issue
Block a user