Skip to content

Commit

Permalink
Implement NIVC Coprocessors.
Browse files Browse the repository at this point in the history
  • Loading branch information
porcuquine authored and winston-h-zhang committed Sep 18, 2023
1 parent 997553e commit 96ade66
Show file tree
Hide file tree
Showing 21 changed files with 1,429 additions and 218 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dashmap = "5.5.0"
ff = { workspace = true }
generic-array = "0.14.7"
hex = { version = "0.4.3", features = ["serde"] }
indexmap = { version = "1.9.3", features = ["rayon"] }
indexmap = { version = "1.9.3", features = ["rayon", "serde"] }
itertools = "0.9"
lurk-macros = { path = "lurk-macros" }
lurk-metrics = { path = "lurk-metrics" }
Expand Down Expand Up @@ -100,6 +100,7 @@ pprof = { version = "0.11" }
structopt = { version = "0.3", default-features = false }
tap = "1.0.1"
tempfile = { workspace = true }
tracing-test = "0.1"

[build-dependencies]
vergen = { version = "8", features = ["build", "git", "gitcl"] }
Expand Down
32 changes: 28 additions & 4 deletions benches/end2end.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,13 @@ fn prove_benchmark(c: &mut Criterion) {
let pp =
public_parameters::public_params(&instance, Utf8Path::new(PUBLIC_PARAMS_PATH)).unwrap();
let frames = prover
.get_evaluation_frames(ptr, empty_sym_env(&store), &mut store, limit, &lang_pallas)
.get_evaluation_frames(
ptr,
empty_sym_env(&store),
&mut store,
limit,
lang_pallas_rc.clone(),
)
.unwrap();

b.iter(|| {
Expand Down Expand Up @@ -333,7 +339,13 @@ fn prove_compressed_benchmark(c: &mut Criterion) {
let pp =
public_parameters::public_params(&instance, Utf8Path::new(PUBLIC_PARAMS_PATH)).unwrap();
let frames = prover
.get_evaluation_frames(ptr, empty_sym_env(&store), &mut store, limit, &lang_pallas)
.get_evaluation_frames(
ptr,
empty_sym_env(&store),
&mut store,
limit,
lang_pallas_rc.clone(),
)
.unwrap();

b.iter(|| {
Expand Down Expand Up @@ -375,7 +387,13 @@ fn verify_benchmark(c: &mut Criterion) {
let pp = public_parameters::public_params(&instance, Utf8Path::new(PUBLIC_PARAMS_PATH))
.unwrap();
let frames = prover
.get_evaluation_frames(ptr, empty_sym_env(&store), &mut store, limit, &lang_pallas)
.get_evaluation_frames(
ptr,
empty_sym_env(&store),
&mut store,
limit,
lang_pallas_rc.clone(),
)
.unwrap();
let (proof, z0, zi, num_steps) = prover
.prove(&pp, &frames, &mut store, lang_pallas_rc.clone())
Expand Down Expand Up @@ -423,7 +441,13 @@ fn verify_compressed_benchmark(c: &mut Criterion) {
let pp = public_parameters::public_params(&instance, Utf8Path::new(PUBLIC_PARAMS_PATH))
.unwrap();
let frames = prover
.get_evaluation_frames(ptr, empty_sym_env(&store), &mut store, limit, &lang_pallas)
.get_evaluation_frames(
ptr,
empty_sym_env(&store),
&mut store,
limit,
lang_pallas_rc.clone(),
)
.unwrap();
let (proof, z0, zi, num_steps) = prover
.prove(&pp, &frames, &mut store, lang_pallas_rc.clone())
Expand Down
2 changes: 1 addition & 1 deletion benches/fibonacci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ fn fibo_prove<M: measurement::Measurement>(
let prover = NovaProver::new(prove_params.reduction_count, lang_pallas.clone());

let frames = &prover
.get_evaluation_frames(ptr, env, &mut store, limit, &lang_pallas)
.get_evaluation_frames(ptr, env, &mut store, limit, lang_rc.clone())
.unwrap();

b.iter_batched(
Expand Down
4 changes: 2 additions & 2 deletions benches/sha256_ivc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ fn sha256_ivc_prove<M: measurement::Measurement>(
let prover = NovaProver::new(prove_params.reduction_count, lang.clone());

let frames = &prover
.get_evaluation_frames(ptr, env, store, limit, &lang)
.get_evaluation_frames(ptr, env, store, limit, lang_rc.clone())
.unwrap();

b.iter_batched(
Expand Down Expand Up @@ -325,7 +325,7 @@ fn sha256_ivc_prove_compressed<M: measurement::Measurement>(
let prover = NovaProver::new(prove_params.reduction_count, lang.clone());

let frames = &prover
.get_evaluation_frames(ptr, env, store, limit, &lang)
.get_evaluation_frames(ptr, env, store, limit, lang_rc.clone())
.unwrap();

b.iter_batched(
Expand Down
7 changes: 5 additions & 2 deletions benches/synthesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use lurk::{
},
field::LurkField,
proof::nova::NovaProver,
proof::supernova::FoldingConfig,
proof::Prover,
ptr::Ptr,
state::State,
Expand Down Expand Up @@ -60,11 +61,13 @@ fn synthesize<M: measurement::Measurement>(
let prover = NovaProver::new(*reduction_count, lang_pallas.clone());

let frames = prover
.get_evaluation_frames(ptr, env, &mut store, limit, &lang_pallas)
.get_evaluation_frames(ptr, env, &mut store, limit, lang_rc.clone())
.unwrap();
let folding_config =
Arc::new(FoldingConfig::new_ivc(lang_rc.clone(), *reduction_count));

let multiframe =
MultiFrame::from_frames(*reduction_count, &frames, &store, lang_rc.clone())[0]
MultiFrame::from_frames(*reduction_count, &frames, &store, folding_config)[0]
.clone();

b.iter_batched(
Expand Down
222 changes: 222 additions & 0 deletions examples/sha256_nivc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
use std::marker::PhantomData;
use std::sync::Arc;
use std::time::Instant;

use lurk::circuit::gadgets::data::GlobalAllocations;
use lurk::circuit::gadgets::pointer::{AllocatedContPtr, AllocatedPtr};
use lurk::coprocessor::{CoCircuit, Coprocessor};
use lurk::eval::{empty_sym_env, lang::Lang};
use lurk::field::LurkField;
use lurk::proof::{supernova::SuperNovaProver, Prover};
use lurk::ptr::Ptr;

use lurk::state::user_sym;
use lurk::store::Store;
use lurk::tag::{ExprTag, Tag};
use lurk::Num;
use lurk_macros::Coproc;

use bellpepper::gadgets::multipack::pack_bits;
use bellpepper::gadgets::sha256::sha256;
use bellpepper_core::boolean::Boolean;
use bellpepper_core::{ConstraintSystem, SynthesisError};

use pasta_curves::pallas::Scalar as Fr;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
use tracing_texray::TeXRayLayer;

const REDUCTION_COUNT: usize = 10;

fn sha256_encode<F: LurkField>(store: &mut Store<F>) -> Ptr<F> {
let program = r#"
(letrec ((encode-1 (lambda (term)
(let ((type (car term))
(value (cdr term)))
(if (eq 'sha256 type)
(eval (cons 'sha256 (cons value nil)))
(if (eq 'lurk type)
(commit value)
(if (eq 'id type)
value))))))
(encode (lambda (input)
(if input
(cons
(encode-1 (car input))
(encode (cdr input)))))))
(encode '((sha256 . 123) (lurk . 5) (id . 15))))
"#
.to_string();

store.read(&program).unwrap()
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Sha256Coprocessor<F: LurkField> {
pub(crate) _p: PhantomData<F>,
}

impl<F: LurkField> CoCircuit<F> for Sha256Coprocessor<F> {
fn arity(&self) -> usize {
1
}

fn synthesize<CS: ConstraintSystem<F>>(
&self,
cs: &mut CS,
_g: &GlobalAllocations<F>,
_store: &Store<F>,
input_exprs: &[AllocatedPtr<F>],
input_env: &AllocatedPtr<F>,
input_cont: &AllocatedContPtr<F>,
) -> Result<(AllocatedPtr<F>, AllocatedPtr<F>, AllocatedContPtr<F>), SynthesisError> {
let zero = Boolean::constant(false);

let mut bits = vec![];

// println!("{:?}", input_exprs);

for input_ptr in input_exprs {
let tag_bits = input_ptr
.tag()
.to_bits_le_strict(&mut cs.namespace(|| "preimage_tag_bits"))?;
let hash_bits = input_ptr
.hash()
.to_bits_le_strict(&mut cs.namespace(|| "preimage_hash_bits"))?;

bits.extend(tag_bits);
bits.push(zero.clone()); // need 256 bits (or some multiple of 8).
bits.extend(hash_bits);
bits.push(zero.clone()); // need 256 bits (or some multiple of 8).
}

bits.reverse();

let mut digest_bits = sha256(cs.namespace(|| "digest_bits"), &bits)?;

digest_bits.reverse();

// Fine to lose the last <1 bit of precision.
let digest_scalar = pack_bits(cs.namespace(|| "digest_scalar"), &digest_bits)?;
let output_expr = AllocatedPtr::alloc_tag(
&mut cs.namespace(|| "output_expr"),
ExprTag::Num.to_field(),
digest_scalar,
)?;
Ok((output_expr, input_env.clone(), input_cont.clone()))
}
}

impl<F: LurkField> Coprocessor<F> for Sha256Coprocessor<F> {
fn eval_arity(&self) -> usize {
1
}

fn simple_evaluate(&self, s: &mut Store<F>, args: &[Ptr<F>]) -> Ptr<F> {
let mut hasher = Sha256::new();

let mut input = vec![0u8; 64];

for (i, input_ptr) in args.iter().enumerate() {
let input_zptr = s.hash_expr(input_ptr).unwrap();
let tag_zptr: F = input_zptr.tag().to_field();
let hash_zptr = input_zptr.value();
input[(64 * i)..(64 * i + 32)].copy_from_slice(&tag_zptr.to_bytes());
input[(64 * i + 32)..(64 * (i + 1))].copy_from_slice(&hash_zptr.to_bytes());
}

input.reverse();

hasher.update(input);
let mut bytes = hasher.finalize();
bytes.reverse();
let l = bytes.len();
// Discard the two most significant bits.
bytes[l - 1] &= 0b00111111;

let scalar = F::from_bytes(&bytes).unwrap();
let result = Num::from_scalar(scalar);

s.intern_num(result)
}

fn has_circuit(&self) -> bool {
true
}
}

impl<F: LurkField> Sha256Coprocessor<F> {
pub(crate) fn new() -> Self {
Self {
_p: Default::default(),
}
}
}

#[derive(Clone, Debug, Coproc, Serialize, Deserialize)]
enum Sha256Coproc<F: LurkField> {
SC(Sha256Coprocessor<F>),
}

/// Run the example in this file with
/// `cargo run --release --example sha256_nivc`
/// where `n` is the needed arity
fn main() {
let subscriber = Registry::default()
.with(fmt::layer().pretty())
.with(EnvFilter::from_default_env())
.with(TeXRayLayer::new());
tracing::subscriber::set_global_default(subscriber).unwrap();

let store = &mut Store::<Fr>::new();
let cproc_sym = user_sym("sha256");

let call = sha256_encode(store);

let lang = Lang::<Fr, Sha256Coproc<Fr>>::new_with_bindings(
store,
vec![(cproc_sym, Sha256Coprocessor::new().into())],
);
let lang_rc = Arc::new(lang.clone());

let supernova_prover = SuperNovaProver::<Fr, Sha256Coproc<Fr>>::new(REDUCTION_COUNT, lang);

// println!("Setting up public parameters (rc = {REDUCTION_COUNT})...");

let pp_start = Instant::now();

// // see the documentation on `with_public_params`
// with_public_params(REDUCTION_COUNT, lang_rc.clone(), |pp| {
let pp_end = pp_start.elapsed();
// println!("Public parameters took {:?}", pp_end);

println!("Beginning proof+public-parameters step...");
let proof_start = Instant::now();
let (_proof, _z0, _zi, _num_steps) = supernova_prover
.evaluate_and_prove(None, call, empty_sym_env(store), store, 10000, lang_rc)
.unwrap();
let proof_end = proof_start.elapsed();

println!("Proofs took {:?}", proof_end);

// TODO: plumb verification.
// println!("Verifying proof...");

// let verify_start = Instant::now();
// let res = proof.verify(claim, &z0, &zi).unwrap();
// let verify_end = verify_start.elapsed();

// println!("Verify took {:?}", verify_end);

// if res {
println!(
// "Congratulations! You proved and verified a SHA256 hash calculation in {:?} time!",
// pp_end + proof_end + verify_end
"Congratulations! You proved a dynamic SHA256 encoding in {:?} time!",
pp_end + proof_end
);
// }
// })
// .unwrap();
}
Loading

0 comments on commit 96ade66

Please sign in to comment.