From ea5a5808969c42b860944083efa81963e0546899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Tue, 22 Oct 2024 14:58:21 +0200 Subject: [PATCH] Use doc workspace command. --- .github/workflows/docs.yml | 4 +- nimue-poseidon/Cargo.toml | 6 +- .../examples/schnorr_algebraic_hash.rs | 0 nimue-poseidon/src/tests.rs | 24 +++ nimue/.cargo/config.toml | 2 + nimue/Cargo.toml | 5 +- src/plugins/ark/mod.rs | 178 ++++++++++++++++++ 7 files changed, 212 insertions(+), 7 deletions(-) rename {nimue => nimue-poseidon}/examples/schnorr_algebraic_hash.rs (100%) create mode 100644 nimue-poseidon/src/tests.rs create mode 100644 nimue/.cargo/config.toml create mode 100644 src/plugins/ark/mod.rs diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a2a8a10..ae034e4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -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 diff --git a/nimue-poseidon/Cargo.toml b/nimue-poseidon/Cargo.toml index d181fc4..e58629b 100644 --- a/nimue-poseidon/Cargo.toml +++ b/nimue-poseidon/Cargo.toml @@ -11,4 +11,8 @@ zeroize = "1.8.1" ark-bls12-381 = "^0.4.0" [dev-dependencies] -ark-bls12-381 = "^0.4.0" \ No newline at end of file +ark-bls12-381 = "^0.4.0" + +[[example]] +name = "schnorr_algebraic_hash" +required-features = ["ark", "ark-bls112-381"] \ No newline at end of file diff --git a/nimue/examples/schnorr_algebraic_hash.rs b/nimue-poseidon/examples/schnorr_algebraic_hash.rs similarity index 100% rename from nimue/examples/schnorr_algebraic_hash.rs rename to nimue-poseidon/examples/schnorr_algebraic_hash.rs diff --git a/nimue-poseidon/src/tests.rs b/nimue-poseidon/src/tests.rs new file mode 100644 index 0000000..6a8a6f7 --- /dev/null +++ b/nimue-poseidon/src/tests.rs @@ -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; + + let io = IOPattern::::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)); + } +} diff --git a/nimue/.cargo/config.toml b/nimue/.cargo/config.toml new file mode 100644 index 0000000..fc808e2 --- /dev/null +++ b/nimue/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustdocflags = "--html-in-header doc/katex-header.html" \ No newline at end of file diff --git a/nimue/Cargo.toml b/nimue/Cargo.toml index 5f98955..d9942d6 100644 --- a/nimue/Cargo.toml +++ b/nimue/Cargo.toml @@ -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" @@ -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"] diff --git a/src/plugins/ark/mod.rs b/src/plugins/ark/mod.rs new file mode 100644 index 0000000..55fefe1 --- /dev/null +++ b/src/plugins/ark/mod.rs @@ -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( +//! 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( +//! merlin: &mut Merlin, +//! x: G::ScalarField, +//! ) -> ProofResult<&[u8]> +//! where +//! Merlin: GroupWriter + 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( +//! merlin: &mut Merlin, +//! 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`][`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( +//! merlin: &mut Merlin, +//! 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, +//! // ... 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: GroupWriter + FieldWriter + 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(a_f1: F1) -> ProofResult { + 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: GroupReader + GroupReader { +// fn fill_next_g1_points(&mut self, input: &mut [P::G1]) -> crate::ProofResult<()> { +// GroupReader::::fill_next_points(self, input) +// } + +// fn fill_next_g2_points(&mut self, input: &mut [P::G2]) -> crate::ProofResult<()> { +// GroupReader::::fill_next_points(self, input) +// } +// } +// pub trait PairingWriter { +// fn add_g1_points(&mut self, input: &[P::G1]) -> crate::ProofResult<()> { +// GroupWriter::::add_points(self, input) +// } + +// fn add_g2_points(&mut self, input: &[P::G2]) -> crate::ProofResult<()> { +// GroupWriter::::add_points(self, input) +// } +// } + +// impl<'a, P: ark_ec::pairing::Pairing, H, U> PairingWriter

for Arthur<'a, H, U> where +// U: Unit, H: DuplexHash, +// Arthur<'a, H, U>: GroupWriter + GroupWriter {}