Cleanup lookup_test module and reduce module visibility

This commit is contained in:
Robin Salen 2023-09-18 09:50:31 -04:00
parent 15064b3aa7
commit d1c00767bd
No known key found for this signature in database
GPG Key ID: FB87BACFB3CB2007
2 changed files with 479 additions and 523 deletions

View File

@ -12,7 +12,9 @@ pub mod gadgets;
pub mod gates;
pub mod hash;
pub mod iop;
pub mod lookup_test;
pub mod plonk;
pub mod recursion;
pub mod util;
#[cfg(test)]
mod lookup_test;

View File

@ -1,523 +1,477 @@
#[cfg(test)]
mod tests {
static LOGGER_INITIALIZED: Once = Once::new();
use alloc::sync::Arc;
use std::sync::Once;
use itertools::Itertools;
use log::{Level, LevelFilter};
use crate::gadgets::lookup::{OTHER_TABLE, SMALLER_TABLE, TIP5_TABLE};
use crate::gates::lookup_table::LookupTable;
use crate::gates::noop::NoopGate;
use crate::plonk::prover::prove;
use crate::util::timing::TimingTree;
#[test]
fn test_no_lookup() -> anyhow::Result<()> {
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
use crate::iop::witness::PartialWitness;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
builder.add_gate(NoopGate, vec![]);
let pw = PartialWitness::new();
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove first", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
timing.print();
data.verify(proof)?;
Ok(())
}
#[should_panic]
#[test]
fn test_lookup_table_not_used() {
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let tip5_table = TIP5_TABLE.to_vec();
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
builder.add_lookup_table_from_pairs(table);
builder.build::<C>();
}
#[should_panic]
#[test]
fn test_lookup_without_table() {
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let dummy = builder.add_virtual_target();
builder.add_lookup_from_index(dummy, 0);
builder.build::<C>();
}
// Tests two lookups in one lookup table.
#[test]
fn test_one_lookup() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let tip5_table = TIP5_TABLE.to_vec();
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let look_val_a = 1;
let look_val_b = 2;
let out_a = table[look_val_a].1;
let out_b = table[look_val_b].1;
let table_index = builder.add_lookup_table_from_pairs(table);
let output_a = builder.add_lookup_from_index(initial_a, table_index);
let output_b = builder.add_lookup_from_index(initial_b, table_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove one lookup", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
timing.print();
data.verify(proof.clone())?;
assert!(
proof.public_inputs[2] == F::from_canonical_u16(out_a),
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[0]
);
assert!(
proof.public_inputs[3] == F::from_canonical_u16(out_b),
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[1]
);
Ok(())
}
// Tests one lookup in two different lookup tables.
#[test]
pub fn test_two_luts() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let look_val_a = 1;
let look_val_b = 2;
let tip5_table = TIP5_TABLE.to_vec();
let first_out = tip5_table[look_val_a];
let second_out = tip5_table[look_val_b];
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
let other_table = OTHER_TABLE.to_vec();
let table_index = builder.add_lookup_table_from_pairs(table);
let output_a = builder.add_lookup_from_index(initial_a, table_index);
let output_b = builder.add_lookup_from_index(initial_b, table_index);
let sum = builder.add(output_a, output_b);
let s = first_out + second_out;
let final_out = other_table[s as usize];
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
let table2_index = builder.add_lookup_table_from_pairs(table2);
let output_final = builder.add_lookup_from_index(sum, table2_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(sum);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
builder.register_public_input(output_final);
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove two_luts", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
data.verify(proof.clone())?;
timing.print();
assert!(
proof.public_inputs[3] == F::from_canonical_u16(first_out),
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[0]
);
assert!(
proof.public_inputs[4] == F::from_canonical_u16(second_out),
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[1]
);
assert!(
proof.public_inputs[2] == F::from_canonical_u16(s),
"Sum between the first two LUT outputs is incorrect."
);
assert!(
proof.public_inputs[5] == F::from_canonical_u16(final_out),
"Output of the second LUT at index {} is incorrect.",
s
);
Ok(())
}
#[test]
pub fn test_different_inputs() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let init_a = 1;
let init_b = 2;
let tab: Vec<u16> = SMALLER_TABLE.to_vec();
let table: LookupTable = Arc::new((2..10).zip_eq(tab).collect());
let other_table = OTHER_TABLE.to_vec();
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
let small_index = builder.add_lookup_table_from_pairs(table.clone());
let output_a = builder.add_lookup_from_index(initial_a, small_index);
let output_b = builder.add_lookup_from_index(initial_b, small_index);
let sum = builder.add(output_a, output_b);
let other_index = builder.add_lookup_table_from_pairs(table2.clone());
let output_final = builder.add_lookup_from_index(sum, other_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(sum);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
builder.register_public_input(output_final);
let mut pw = PartialWitness::new();
let look_val_a = table[init_a].0;
let look_val_b = table[init_b].0;
pw.set_target(initial_a, F::from_canonical_u16(look_val_a));
pw.set_target(initial_b, F::from_canonical_u16(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove different lookups", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
data.verify(proof.clone())?;
timing.print();
let out_a = table[init_a].1;
let out_b = table[init_b].1;
let s = out_a + out_b;
let out_final = table2[s as usize].1;
assert!(
proof.public_inputs[3] == F::from_canonical_u16(out_a),
"First lookup, at index {} in the smaller LUT gives an incorrect output.",
proof.public_inputs[0]
);
assert!(
proof.public_inputs[4] == F::from_canonical_u16(out_b),
"Second lookup, at index {} in the smaller LUT gives an incorrect output.",
proof.public_inputs[1]
);
assert!(
proof.public_inputs[2] == F::from_canonical_u16(s),
"Sum between the first two LUT outputs is incorrect."
);
assert!(
proof.public_inputs[5] == F::from_canonical_u16(out_final),
"Output of the second LUT at index {} is incorrect.",
s
);
Ok(())
}
// This test looks up over 514 values for one LookupTableGate, which means that several LookupGates are created.
#[test]
pub fn test_many_lookups() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let look_val_a = 1;
let look_val_b = 2;
let tip5_table = TIP5_TABLE.to_vec();
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
let out_a = table[look_val_a].1;
let out_b = table[look_val_b].1;
let tip5_index = builder.add_lookup_table_from_pairs(table);
let output_a = builder.add_lookup_from_index(initial_a, tip5_index);
let output_b = builder.add_lookup_from_index(initial_b, tip5_index);
let sum = builder.add(output_a, output_b);
for _ in 0..514 {
builder.add_lookup_from_index(initial_a, tip5_index);
}
let other_table = OTHER_TABLE.to_vec();
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
let s = out_a + out_b;
let out_final = table2[s as usize].1;
let other_index = builder.add_lookup_table_from_pairs(table2);
let output_final = builder.add_lookup_from_index(sum, other_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(sum);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
builder.register_public_input(output_final);
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove different lookups", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
data.verify(proof.clone())?;
timing.print();
assert!(
proof.public_inputs[3] == F::from_canonical_u16(out_a),
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[0]
);
assert!(
proof.public_inputs[4] == F::from_canonical_u16(out_b),
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[1]
);
assert!(
proof.public_inputs[2] == F::from_canonical_u16(s),
"Sum between the first two LUT outputs is incorrect."
);
assert!(
proof.public_inputs[5] == F::from_canonical_u16(out_final),
"Output of the second LUT at index {} is incorrect.",
s
);
Ok(())
}
// Tests whether, when adding the same LUT to the circuit, the circuit only adds one copy, with the same index.
#[test]
pub fn test_same_luts() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let look_val_a = 1;
let look_val_b = 2;
let tip5_table = TIP5_TABLE.to_vec();
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
let table_index = builder.add_lookup_table_from_pairs(table.clone());
let output_a = builder.add_lookup_from_index(initial_a, table_index);
let output_b = builder.add_lookup_from_index(initial_b, table_index);
let sum = builder.add(output_a, output_b);
let table2_index = builder.add_lookup_table_from_pairs(table);
let output_final = builder.add_lookup_from_index(sum, table2_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(sum);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
builder.register_public_input(output_final);
let luts_length = builder.get_luts_length();
assert!(
luts_length == 1,
"There are {} LUTs when there should be only one",
luts_length
);
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove two_luts", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
data.verify(proof)?;
timing.print();
Ok(())
}
#[test]
fn test_circuit_build_mock() {
// This code is taken from examples/fibonacci.rs
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
// The arithmetic circuit.
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let mut prev_target = initial_a;
let mut cur_target = initial_b;
for _ in 0..99 {
let temp = builder.add(prev_target, cur_target);
prev_target = cur_target;
cur_target = temp;
}
// Public inputs are the two initial values (provided below) and the result (which is generated).
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(cur_target);
// Provide initial values.
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::ZERO);
pw.set_target(initial_b, F::ONE);
let data = builder.mock_build::<C>();
let partition_witness = data.generate_witness(pw);
let result = partition_witness.try_get_target(cur_target).unwrap();
assert_eq!(result, F::from_canonical_u64(3736710860384812976));
}
fn init_logger() -> anyhow::Result<()> {
let mut builder = env_logger::Builder::from_default_env();
builder.format_timestamp(None);
builder.filter_level(LevelFilter::Debug);
builder.try_init()?;
Ok(())
}
static LOGGER_INITIALIZED: Once = Once::new();
use alloc::sync::Arc;
use std::sync::Once;
use itertools::Itertools;
use log::{Level, LevelFilter};
use crate::gadgets::lookup::{OTHER_TABLE, SMALLER_TABLE, TIP5_TABLE};
use crate::gates::lookup_table::LookupTable;
use crate::gates::noop::NoopGate;
use crate::plonk::prover::prove;
use crate::util::timing::TimingTree;
#[test]
fn test_no_lookup() -> anyhow::Result<()> {
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
use crate::iop::witness::PartialWitness;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
builder.add_gate(NoopGate, vec![]);
let pw = PartialWitness::new();
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove first", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
timing.print();
data.verify(proof)?;
Ok(())
}
#[should_panic]
#[test]
fn test_lookup_table_not_used() {
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let tip5_table = TIP5_TABLE.to_vec();
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
builder.add_lookup_table_from_pairs(table);
builder.build::<C>();
}
#[should_panic]
#[test]
fn test_lookup_without_table() {
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let dummy = builder.add_virtual_target();
builder.add_lookup_from_index(dummy, 0);
builder.build::<C>();
}
// Tests two lookups in one lookup table.
#[test]
fn test_one_lookup() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let tip5_table = TIP5_TABLE.to_vec();
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let look_val_a = 1;
let look_val_b = 2;
let out_a = table[look_val_a].1;
let out_b = table[look_val_b].1;
let table_index = builder.add_lookup_table_from_pairs(table);
let output_a = builder.add_lookup_from_index(initial_a, table_index);
let output_b = builder.add_lookup_from_index(initial_b, table_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove one lookup", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
timing.print();
data.verify(proof.clone())?;
assert!(
proof.public_inputs[2] == F::from_canonical_u16(out_a),
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[0]
);
assert!(
proof.public_inputs[3] == F::from_canonical_u16(out_b),
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[1]
);
Ok(())
}
// Tests one lookup in two different lookup tables.
#[test]
pub fn test_two_luts() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let look_val_a = 1;
let look_val_b = 2;
let tip5_table = TIP5_TABLE.to_vec();
let first_out = tip5_table[look_val_a];
let second_out = tip5_table[look_val_b];
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
let other_table = OTHER_TABLE.to_vec();
let table_index = builder.add_lookup_table_from_pairs(table);
let output_a = builder.add_lookup_from_index(initial_a, table_index);
let output_b = builder.add_lookup_from_index(initial_b, table_index);
let sum = builder.add(output_a, output_b);
let s = first_out + second_out;
let final_out = other_table[s as usize];
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
let table2_index = builder.add_lookup_table_from_pairs(table2);
let output_final = builder.add_lookup_from_index(sum, table2_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(sum);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
builder.register_public_input(output_final);
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove two_luts", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
data.verify(proof.clone())?;
timing.print();
assert!(
proof.public_inputs[3] == F::from_canonical_u16(first_out),
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[0]
);
assert!(
proof.public_inputs[4] == F::from_canonical_u16(second_out),
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[1]
);
assert!(
proof.public_inputs[2] == F::from_canonical_u16(s),
"Sum between the first two LUT outputs is incorrect."
);
assert!(
proof.public_inputs[5] == F::from_canonical_u16(final_out),
"Output of the second LUT at index {} is incorrect.",
s
);
Ok(())
}
#[test]
pub fn test_different_inputs() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let init_a = 1;
let init_b = 2;
let tab: Vec<u16> = SMALLER_TABLE.to_vec();
let table: LookupTable = Arc::new((2..10).zip_eq(tab).collect());
let other_table = OTHER_TABLE.to_vec();
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
let small_index = builder.add_lookup_table_from_pairs(table.clone());
let output_a = builder.add_lookup_from_index(initial_a, small_index);
let output_b = builder.add_lookup_from_index(initial_b, small_index);
let sum = builder.add(output_a, output_b);
let other_index = builder.add_lookup_table_from_pairs(table2.clone());
let output_final = builder.add_lookup_from_index(sum, other_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(sum);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
builder.register_public_input(output_final);
let mut pw = PartialWitness::new();
let look_val_a = table[init_a].0;
let look_val_b = table[init_b].0;
pw.set_target(initial_a, F::from_canonical_u16(look_val_a));
pw.set_target(initial_b, F::from_canonical_u16(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove different lookups", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
data.verify(proof.clone())?;
timing.print();
let out_a = table[init_a].1;
let out_b = table[init_b].1;
let s = out_a + out_b;
let out_final = table2[s as usize].1;
assert!(
proof.public_inputs[3] == F::from_canonical_u16(out_a),
"First lookup, at index {} in the smaller LUT gives an incorrect output.",
proof.public_inputs[0]
);
assert!(
proof.public_inputs[4] == F::from_canonical_u16(out_b),
"Second lookup, at index {} in the smaller LUT gives an incorrect output.",
proof.public_inputs[1]
);
assert!(
proof.public_inputs[2] == F::from_canonical_u16(s),
"Sum between the first two LUT outputs is incorrect."
);
assert!(
proof.public_inputs[5] == F::from_canonical_u16(out_final),
"Output of the second LUT at index {} is incorrect.",
s
);
Ok(())
}
// This test looks up over 514 values for one LookupTableGate, which means that several LookupGates are created.
#[test]
pub fn test_many_lookups() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let look_val_a = 1;
let look_val_b = 2;
let tip5_table = TIP5_TABLE.to_vec();
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
let out_a = table[look_val_a].1;
let out_b = table[look_val_b].1;
let tip5_index = builder.add_lookup_table_from_pairs(table);
let output_a = builder.add_lookup_from_index(initial_a, tip5_index);
let output_b = builder.add_lookup_from_index(initial_b, tip5_index);
let sum = builder.add(output_a, output_b);
for _ in 0..514 {
builder.add_lookup_from_index(initial_a, tip5_index);
}
let other_table = OTHER_TABLE.to_vec();
let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect());
let s = out_a + out_b;
let out_final = table2[s as usize].1;
let other_index = builder.add_lookup_table_from_pairs(table2);
let output_final = builder.add_lookup_from_index(sum, other_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(sum);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
builder.register_public_input(output_final);
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove different lookups", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
data.verify(proof.clone())?;
timing.print();
assert!(
proof.public_inputs[3] == F::from_canonical_u16(out_a),
"First lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[0]
);
assert!(
proof.public_inputs[4] == F::from_canonical_u16(out_b),
"Second lookup, at index {} in the Tip5 table gives an incorrect output.",
proof.public_inputs[1]
);
assert!(
proof.public_inputs[2] == F::from_canonical_u16(s),
"Sum between the first two LUT outputs is incorrect."
);
assert!(
proof.public_inputs[5] == F::from_canonical_u16(out_final),
"Output of the second LUT at index {} is incorrect.",
s
);
Ok(())
}
// Tests whether, when adding the same LUT to the circuit, the circuit only adds one copy, with the same index.
#[test]
pub fn test_same_luts() -> anyhow::Result<()> {
use crate::field::types::Field;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
LOGGER_INITIALIZED.call_once(|| init_logger().unwrap());
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let initial_a = builder.add_virtual_target();
let initial_b = builder.add_virtual_target();
let look_val_a = 1;
let look_val_b = 2;
let tip5_table = TIP5_TABLE.to_vec();
let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect());
let table_index = builder.add_lookup_table_from_pairs(table.clone());
let output_a = builder.add_lookup_from_index(initial_a, table_index);
let output_b = builder.add_lookup_from_index(initial_b, table_index);
let sum = builder.add(output_a, output_b);
let table2_index = builder.add_lookup_table_from_pairs(table);
let output_final = builder.add_lookup_from_index(sum, table2_index);
builder.register_public_input(initial_a);
builder.register_public_input(initial_b);
builder.register_public_input(sum);
builder.register_public_input(output_a);
builder.register_public_input(output_b);
builder.register_public_input(output_final);
let luts_length = builder.get_luts_length();
assert!(
luts_length == 1,
"There are {} LUTs when there should be only one",
luts_length
);
let mut pw = PartialWitness::new();
pw.set_target(initial_a, F::from_canonical_usize(look_val_a));
pw.set_target(initial_b, F::from_canonical_usize(look_val_b));
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove two_luts", Level::Debug);
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
data.verify(proof)?;
timing.print();
Ok(())
}
fn init_logger() -> anyhow::Result<()> {
let mut builder = env_logger::Builder::from_default_env();
builder.format_timestamp(None);
builder.filter_level(LevelFilter::Debug);
builder.try_init()?;
Ok(())
}