Skip to content
Merged
15 changes: 11 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ opt-level = 3
codegen-units = 1
lto = "fat"

# Fast release builds for development iteration: cargo build --profile release-fast
[profile.release-fast]
inherits = "release"
codegen-units = 16
lto = "thin"

# Doing light optimizations helps test performance more than it hurts build time.
[profile.test]
opt-level = 2
Expand Down Expand Up @@ -120,6 +126,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha2 = "0.10.9"
test-case = "3.3.1"
tikv-jemallocator = "0.6"
toml = "0.8.8"
tokio = { version = "1.47.1", features = ["full"] }
tokio-util = "0.7.13"
Expand Down Expand Up @@ -155,7 +162,7 @@ ark-poly = "0.5"
ark-serialize = "0.5"
ark-std = { version = "0.5", features = ["std"] }
spongefish = { git = "https://github.com/arkworks-rs/spongefish", features = [
"arkworks-algebra",
], rev = "ecb4f08373ed930175585c856517efdb1851fb47" }
spongefish-pow = { git = "https://github.com/arkworks-rs/spongefish", rev = "ecb4f08373ed930175585c856517efdb1851fb47" }
whir = { git = "https://github.com/WizardOfMenlo/whir/", features = ["tracing"], rev = "cf1599b56ff50e09142ebe6d2e2fbd86875c9986" }
"ark-ff", "sha2",
], rev = "fcc277f8a857fdeeadd7cca92ab08de63b1ff1a1" }
spongefish-pow = { git = "https://github.com/arkworks-rs/spongefish", rev = "fcc277f8a857fdeeadd7cca92ab08de63b1ff1a1" }
whir = { git = "https://github.com/WizardOfMenlo/whir/", rev = "3056565b90931c28f725f6655d954bd8f17eaaf6", features = ["tracing"] }
1 change: 0 additions & 1 deletion provekit/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ noirc_abi.workspace = true

# Cryptography and proof systems
ark-bn254.workspace = true
ark-crypto-primitives.workspace = true
ark-ff.workspace = true
ark-serialize.workspace = true
ark-std.workspace = true
Expand Down
25 changes: 24 additions & 1 deletion provekit/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,32 @@ pub use {
prover::Prover,
r1cs::R1CS,
verifier::Verifier,
whir_r1cs::{IOPattern, WhirConfig, WhirR1CSProof, WhirR1CSScheme},
whir_r1cs::{
WhirConfig, WhirDomainSeparator, WhirProof, WhirProverState, WhirR1CSProof, WhirR1CSScheme,
},
witness::PublicInputs,
};

/// SHA-256 based transcript sponge for Fiat-Shamir.
pub type TranscriptSponge = spongefish::instantiations::SHA256;

/// Register provekit's custom implementations in whir's global registries.
///
/// Must be called once before any prove/verify operations.
/// Idempotent — safe to call multiple times.
pub fn register_ntt() {
use std::sync::{Arc, Once};
static INIT: Once = Once::new();
INIT.call_once(|| {
let ntt: Arc<dyn whir::algebra::ntt::ReedSolomon<FieldElement>> =
Arc::new(whir::algebra::ntt::ArkNtt::<FieldElement>::default());
whir::algebra::ntt::NTT.insert(ntt);

let skyscraper: Arc<dyn whir::hash::HashEngine> =
Arc::new(skyscraper::SkyscraperHashEngine);
whir::hash::ENGINES.register(skyscraper);
});
}

#[cfg(test)]
mod tests {}
2 changes: 1 addition & 1 deletion provekit/common/src/skyscraper/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ mod whir;
pub use self::{
pow::SkyscraperPoW,
sponge::SkyscraperSponge,
whir::{SkyscraperCRH, SkyscraperMerkleConfig},
whir::{SkyscraperHashEngine, SKYSCRAPER},
};
52 changes: 20 additions & 32 deletions provekit/common/src/skyscraper/pow.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,44 @@
use {
skyscraper::pow::{solve, verify},
spongefish_pow::PowStrategy,
spongefish_pow::{PoWSolution, PowStrategy},
zerocopy::transmute,
};

/// Skyscraper proof of work
#[derive(Clone, Copy)]
pub struct SkyscraperPoW {
challenge: [u64; 4],
challenge: [u8; 32],
bits: f64,
}

impl PowStrategy for SkyscraperPoW {
fn new(challenge: [u8; 32], bits: f64) -> Self {
assert!((0.0..60.0).contains(&bits), "bits must be smaller than 60");
Self {
challenge: transmute!(challenge),
bits,
}
Self { challenge, bits }
}

fn check(&mut self, nonce: u64) -> bool {
verify(self.challenge, self.bits, nonce)
verify(transmute!(self.challenge), self.bits, nonce)
}

fn solution(&self, nonce: u64) -> PoWSolution {
PoWSolution {
challenge: self.challenge,
nonce,
}
}

fn solve(&mut self) -> Option<u64> {
Some(solve(self.challenge, self.bits))
fn solve(&mut self) -> Option<PoWSolution> {
let nonce = solve(transmute!(self.challenge), self.bits);
Some(self.solution(nonce))
}
}

#[test]
fn test_pow_skyscraper() {
use {
spongefish::{
ByteDomainSeparator, BytesToUnitDeserialize, BytesToUnitSerialize, DefaultHash,
DomainSeparator,
},
spongefish_pow::{PoWChallenge, PoWDomainSeparator},
};

const BITS: f64 = 10.0;

let iopattern = DomainSeparator::<DefaultHash>::new("the proof of work lottery 🎰")
.add_bytes(1, "something")
.challenge_pow("rolling dices");

let mut prover = iopattern.to_prover_state();
prover.add_bytes(b"\0").expect("Invalid IOPattern");
prover.challenge_pow::<SkyscraperPoW>(BITS).unwrap();

let mut verifier = iopattern.to_verifier_state(prover.narg_string());
let byte = verifier.next_bytes::<1>().unwrap();
assert_eq!(&byte, b"\0");
verifier.challenge_pow::<SkyscraperPoW>(BITS).unwrap();
let challenge = [42u8; 32];
let bits = 10.0;
let mut pow = SkyscraperPoW::new(challenge, bits);
let solution = pow.solve().expect("should find nonce");
assert_eq!(solution.challenge, challenge);
assert!(pow.check(solution.nonce));
}
71 changes: 26 additions & 45 deletions provekit/common/src/skyscraper/sponge.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,41 @@
use {
crate::FieldElement,
ark_bn254::Fr,
ark_ff::{BigInt, PrimeField},
spongefish::duplex_sponge::{DuplexSponge, Permutation},
zeroize::Zeroize,
spongefish::{DuplexSponge, Permutation},
};

fn to_fr(x: FieldElement) -> Fr {
Fr::new(BigInt(x.into_bigint().0))
}
fn from_fr(x: Fr) -> FieldElement {
FieldElement::new(x.into_bigint())
}

fn bigint_from_bytes_le<const N: usize>(bytes: &[u8]) -> BigInt<N> {
let limbs = bytes
.chunks_exact(8)
.map(|s| u64::from_le_bytes(s.try_into().unwrap()))
.collect::<Vec<_>>();
BigInt::new(limbs.try_into().unwrap())
}

type State = [FieldElement; 2];

#[derive(Clone, Default, Zeroize)]
pub struct Skyscraper {
state: State,
}

impl AsRef<[FieldElement]> for Skyscraper {
fn as_ref(&self) -> &[FieldElement] {
&self.state
fn bytes_to_fr(bytes: &[u8]) -> Fr {
let mut limbs = [0u64; 4];
for (i, chunk) in bytes.chunks_exact(8).enumerate() {
limbs[i] = u64::from_le_bytes(chunk.try_into().unwrap());
}
Fr::new(BigInt(limbs))
}
impl AsMut<[FieldElement]> for Skyscraper {
fn as_mut(&mut self) -> &mut [FieldElement] {
&mut self.state

fn fr_to_bytes(f: Fr) -> [u8; 32] {
let limbs = f.into_bigint().0;
let mut out = [0u8; 32];
for (i, &limb) in limbs.iter().enumerate() {
out[i * 8..(i + 1) * 8].copy_from_slice(&limb.to_le_bytes());
}
out
}

impl Permutation for Skyscraper {
type U = FieldElement;
const N: usize = 2;
const R: usize = 1;
#[derive(Clone, Default)]
pub struct Skyscraper;

fn new(iv: [u8; 32]) -> Self {
let felt = FieldElement::new(bigint_from_bytes_le(&iv));
Self {
state: [0.into(), felt],
}
}
impl Permutation<64> for Skyscraper {
type U = u8;

fn permute(&mut self) {
let (l2, r2) = skyscraper::reference::permute(to_fr(self.state[0]), to_fr(self.state[1]));
self.state = [from_fr(l2), from_fr(r2)];
fn permute(&self, state: &[u8; 64]) -> [u8; 64] {
let left = bytes_to_fr(&state[..32]);
let right = bytes_to_fr(&state[32..]);
let (l2, r2) = skyscraper::reference::permute(left, right);
let mut out = [0u8; 64];
out[..32].copy_from_slice(&fr_to_bytes(l2));
out[32..].copy_from_slice(&fr_to_bytes(r2));
out
}
}

pub type SkyscraperSponge = DuplexSponge<Skyscraper>;
pub type SkyscraperSponge = DuplexSponge<Skyscraper, 64, 32>;
Loading
Loading