Skip to content

Commit

Permalink
Use doc workspace command.
Browse files Browse the repository at this point in the history
  • Loading branch information
mmaker committed Oct 22, 2024
1 parent 39c201d commit ea5a580
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 7 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ jobs:
- name: Build Documentation
uses: actions-rs/cargo@v1
with:
command: rustdoc
args: --all-features -- -Z unstable-options --enable-index-page
command: doc
args: --all-features
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Upload artifact
Expand Down
6 changes: 5 additions & 1 deletion nimue-poseidon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ zeroize = "1.8.1"
ark-bls12-381 = "^0.4.0"

[dev-dependencies]
ark-bls12-381 = "^0.4.0"
ark-bls12-381 = "^0.4.0"

[[example]]
name = "schnorr_algebraic_hash"
required-features = ["ark", "ark-bls112-381"]
File renamed without changes.
24 changes: 24 additions & 0 deletions nimue-poseidon/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::PoseidonHash;
use ark_bls12_381::Fr;
use nimue::IOPattern;
use nimue::UnitTranscript;

/// Check that poseidon can indeed be instantiated and doesn't do terribly stupid things like give 0 challenges.
#[test]
fn test_poseidon_basic() {
type F = Fr;
type H = PoseidonHash<F, 2, 3>;

let io = IOPattern::<H, F>::new("test")
.absorb(1, "in")
.squeeze(10, "out");
let mut merlin = io.to_merlin();
merlin.add_units(&[F::from(0x42)]).unwrap();

let mut challenges = [F::from(0); 10];
merlin.fill_challenge_units(&mut challenges).unwrap();

for challenge in challenges {
assert_ne!(challenge, F::from(0));
}
}
2 changes: 2 additions & 0 deletions nimue/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
rustdocflags = "--html-in-header doc/katex-header.html"
5 changes: 1 addition & 4 deletions nimue/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ curve25519-dalek = { version = "4.0.0", features = ["group"] }
ark-curve25519 = "0.4.0"
# test algebraic hashers
bls12_381 = "0.8.0"
ark-bls12-381 = { version = "0.4.0", features = ["std"] }
anyhow = { version = "1.0.75", features = ["backtrace"] }
ark-pallas = { version = "0.4.0", features = ["std"] }
pallas = "0.22.0"
Expand All @@ -55,10 +56,6 @@ features = ["ark", "group"]
name = "schnorr"
required-features = ["ark"]

[[example]]
name = "schnorr_algebraic_hash"
required-features = ["ark", "ark-bls112-381"]

[[example]]
name = "bulletproof"
required-features = ["ark"]
178 changes: 178 additions & 0 deletions src/plugins/ark/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//! This module contains utilities for working with [arkworks](https://arkworks.rs) types
//! and aid in the Fiat-Shamir heuristic for protocols dealing with
//! field elements and group elements.
//!
//! # Examples
//!
//! Here's a protocol that does Fiat-Shamir without caring about the hash function used
//! or the serialization format.
//!
//! ```rust
//! use ark_ec::CurveGroup;
//! use ark_std::UniformRand;
//! use nimue::{IOPattern, Merlin, DuplexHash, ProofResult};
//! use nimue::plugins::ark::*;
//!
//! fn prove<G: CurveGroup>(
//! merlin: &mut Merlin,
//! x: G::ScalarField,
//! ) -> ProofResult<&[u8]>
//! {
//! let k = G::ScalarField::rand(merlin.rng());
//! merlin.add_points(&[G::generator() * k])?;
//! let [c]: [G::ScalarField; 1] = merlin.challenge_scalars()?;
//! merlin.add_scalars(&[k + c * x])?;
//! Ok(merlin.transcript())
//! }
//! ```
//! The type constraint on [`Merlin`][`crate::Merlin`] hints the compiler that we are going to be absorbing elements from the group `G` and squeezing challenges in the scalar field `G::ScalarField`. Similarly, we could have been squeezing out bytes.
//!
//! ```rust
//! # use ark_ec::CurveGroup;
//! # use ark_std::UniformRand;
//! # use ark_ff::PrimeField;
//! # use nimue::{IOPattern, Merlin, DuplexHash, ProofResult};
//! # use nimue::plugins::ark::*;
//!
//! fn prove<G: CurveGroup>(
//! merlin: &mut Merlin,
//! x: G::ScalarField,
//! ) -> ProofResult<&[u8]>
//! where
//! Merlin: GroupWriter<G> + ByteChallenges,
//! {
//! let k = G::ScalarField::rand(merlin.rng());
//! merlin.add_points(&[G::generator() * k])?;
//! let c_bytes = merlin.challenge_bytes::<16>()?;
//! let c = G::ScalarField::from_le_bytes_mod_order(&c_bytes);
//! merlin.add_scalars(&[k + c * x])?;
//! Ok(merlin.transcript())
//! }
//! ```
//!
//! [`Merlin`][`crate::Merlin`] is actually more general than this, and can be used with any hash function, over any field.
//! Let's for instance use [`sha2`](https://crates.io/crates/sha2) on the above transcript instead of Keccak.
//!
//! ```rust
//! # use ark_ec::CurveGroup;
//! # use ark_std::UniformRand;
//! # use ark_ff::PrimeField;
//! # use nimue::{IOPattern, Merlin, DuplexHash, ProofResult};
//! # use nimue::plugins::ark::*;
//!
//! fn prove<G: CurveGroup, H: DuplexHash>(
//! merlin: &mut Merlin<H>,
//! x: G::ScalarField,
//! ) -> ProofResult<&[u8]>
//! # {
//! # let k = G::ScalarField::rand(merlin.rng());
//! # merlin.add_points(&[G::generator() * k])?;
//! # let c_bytes = merlin.challenge_bytes::<16>()?;
//! # let c = G::ScalarField::from_le_bytes_mod_order(&c_bytes);
//! # merlin.add_scalars(&[k + c * x])?;
//! # Ok(merlin.transcript())
//! # }
//! ```
//! No change to the function body is needed.
//! Now the proving function can be called with [`nimue::DigestBridge<sha2::Sha256>`][`crate::DigestBridge`].
//! As easy as that.
//! More _modern_ hash functions may want to operate over some some field different than $\mathbb{F}_8$,
//! for instance over the base field of the sponge.
//! Also in this case it's sufficient to slightly change the proving function to specify the field over which the
//! hash function operates, to something like:
//!
//! ```rust
//! # use ark_ec::CurveGroup;
//! # use ark_std::UniformRand;
//! # use ark_ff::{PrimeField, BigInteger};
//! # use nimue::{IOPattern, Merlin, DuplexHash, ProofResult};
//! # use nimue::plugins::ark::*;
//!
//! fn prove<G, H, U>(
//! merlin: &mut Merlin<H, U>,
//! x: G::ScalarField,
//! ) -> ProofResult<&[u8]>
//! where
//! G: CurveGroup,
//! G::BaseField: PrimeField,
//! // Declares the type the hash function works on
//! U: Unit,
//! // Constrains the hash function to work over U, ...
//! H: DuplexHash<U>,
//! // ... and the prover to be able to absorb and squeeze elements from the group and the base field.
//! // (normally would be the ScalarField but this is to make it work nicely with algebraic hashes)
//! Merlin<H, U>: GroupWriter<G> + FieldWriter<G::BaseField> + ByteChallenges,
//! {
//! let k = G::ScalarField::rand(merlin.rng());
//! merlin.add_points(&[G::generator() * k])?;
//! let c_bytes = merlin.challenge_bytes::<16>()?;
//! let c = G::ScalarField::from_le_bytes_mod_order(&c_bytes);
//! // XXX. very YOLO code, don't do this at home.
//! // The resulting proof is malleable and could also not be correct if
//! // G::BaseField::MODULUS < G::ScalarField::MODULUS
//! let r = G::BaseField::from_le_bytes_mod_order(&(k + c * x).into_bigint().to_bytes_le());
//! merlin.add_scalars(&[r])?;
//! Ok(merlin.transcript())
//! }
//! ```
//! Now the above code should work with algebraic hashes such as [`PoseidonHash`][`nimue-poseidon::PoseidonHash`] just as fine as [`Keccak`][`crate::hash::Keccak`].
//!
/// Add public elements (field or group elements) to the protocol transcript.
mod common;
/// IO Pattern utilities.
mod iopattern;
/// (WIP) Support for the Poseidon Hash function.
pub mod poseidon;
/// Veririfer's utilities for decoding a transcript.
mod reader;
/// Prover's utilities for encoding into a transcript.
mod writer;

#[cfg(test)]
/// Tests for arkworks.
mod tests;

#[cfg(feature = "anemoi")]
pub mod anemoi;

pub use crate::traits::*;
pub use crate::{hash::Unit, Arthur, DuplexHash, IOPattern, Merlin, ProofError, ProofResult, Safe};

super::traits::field_traits!(ark_ff::Field);
super::traits::group_traits!(ark_ec::CurveGroup, Scalar: ark_ff::PrimeField);

/// Move a value from prime field F1 to prime field F2.
///
/// Return an error if the element considered mod |F1| is different, when seen as an integer, mod |F2|.
/// This in particular happens when element > |F2|.
pub fn swap_field<F1: ark_ff::PrimeField, F2: ark_ff::PrimeField>(a_f1: F1) -> ProofResult<F2> {
use ark_ff::BigInteger;
let a_f2 = F2::from_le_bytes_mod_order(&a_f1.into_bigint().to_bytes_le());
let a_f1_control = F1::from_le_bytes_mod_order(&a_f2.into_bigint().to_bytes_le());
(a_f1 == a_f1_control)
.then(|| a_f2)
.ok_or(ProofError::SerializationError)
}

// pub trait PairingReader<P: ark_ec::pairing::Pairing>: GroupReader<P::G1> + GroupReader<P::G2> {
// fn fill_next_g1_points(&mut self, input: &mut [P::G1]) -> crate::ProofResult<()> {
// GroupReader::<P::G1>::fill_next_points(self, input)
// }

// fn fill_next_g2_points(&mut self, input: &mut [P::G2]) -> crate::ProofResult<()> {
// GroupReader::<P::G2>::fill_next_points(self, input)
// }
// }
// pub trait PairingWriter<P: ark_ec::pairing::Pairing> {
// fn add_g1_points(&mut self, input: &[P::G1]) -> crate::ProofResult<()> {
// GroupWriter::<P::G1>::add_points(self, input)
// }

// fn add_g2_points(&mut self, input: &[P::G2]) -> crate::ProofResult<()> {
// GroupWriter::<P::G2>::add_points(self, input)
// }
// }

// impl<'a, P: ark_ec::pairing::Pairing, H, U> PairingWriter<P> for Arthur<'a, H, U> where
// U: Unit, H: DuplexHash<U>,
// Arthur<'a, H, U>: GroupWriter<P::G1> + GroupWriter<P::G2> {}

0 comments on commit ea5a580

Please sign in to comment.