364 lines
13 KiB
Rust
Raw Normal View History

2024-10-16 11:21:33 +02:00
// Sample cells
// consistent with:
// https://github.com/codex-storage/codex-storage-proofs-circuits/blob/master/circuit/codex/sample_cells.circom
// circuit consists of:
// - reconstruct the dataset merkle root using the slot root as leaf
// - samples multiple cells by calling the sample_cells
use plonky2::field::extension::Extendable;
2024-10-21 13:31:54 +02:00
use plonky2::hash::hash_types::{HashOut, HashOutTarget, NUM_HASH_OUT_ELTS, RichField};
2024-10-16 11:21:33 +02:00
use plonky2::iop::target::{BoolTarget, Target};
2024-11-07 09:32:29 +01:00
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
2024-10-16 11:21:33 +02:00
use plonky2::plonk::circuit_builder::CircuitBuilder;
2024-11-07 09:32:29 +01:00
use plonky2::plonk::config::GenericConfig;
2024-10-16 11:21:33 +02:00
use std::marker::PhantomData;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use plonky2::hash::hashing::PlonkyPermutation;
2024-11-07 09:32:29 +01:00
use crate::circuits::params::{CircuitParams, HF};
2024-10-16 11:21:33 +02:00
2024-11-07 09:32:29 +01:00
use crate::circuits::merkle_circuit::{MerkleProofTarget, MerkleTreeCircuit, MerkleTreeTargets};
2024-11-12 12:03:56 +01:00
use crate::circuits::sponge::{hash_n_no_padding, hash_n_with_padding};
2024-11-12 13:06:28 +01:00
use crate::circuits::utils::{assign_hash_out_targets, ceiling_log2};
2024-10-16 11:21:33 +02:00
2024-11-07 09:32:29 +01:00
/// circuit for sampling a slot in a dataset merkle tree
2024-10-16 11:21:33 +02:00
#[derive(Clone)]
2024-11-07 09:32:29 +01:00
pub struct SampleCircuit<
2024-10-17 21:38:14 +02:00
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
> {
2024-11-05 12:57:49 +01:00
params: CircuitParams,
phantom_data: PhantomData<F>,
2024-10-16 11:21:33 +02:00
}
2024-10-17 21:38:14 +02:00
impl<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
2024-11-07 09:32:29 +01:00
> SampleCircuit<F, D> {
2024-11-05 12:57:49 +01:00
pub fn new(params: CircuitParams) -> Self{
2024-10-16 11:21:33 +02:00
Self{
2024-11-05 12:57:49 +01:00
params,
phantom_data: Default::default(),
2024-10-16 11:21:33 +02:00
}
}
}
2024-11-07 09:32:29 +01:00
/// struct of input to the circuit as targets
/// used to build the circuit and can be assigned after building
2024-11-05 12:57:49 +01:00
#[derive(Clone)]
pub struct SampleTargets {
2024-10-18 12:01:01 +02:00
2024-11-26 12:10:17 +01:00
pub entropy: HashOutTarget, // public input
pub dataset_root: HashOutTarget, // public input
pub slot_index: Target, // public input
2024-10-18 12:01:01 +02:00
2024-11-05 12:57:49 +01:00
pub slot_root: HashOutTarget,
pub n_cells_per_slot: Target,
pub n_slots_per_dataset: Target,
2024-10-16 11:21:33 +02:00
2024-11-07 09:32:29 +01:00
pub slot_proof: MerkleProofTarget,
2024-10-16 11:21:33 +02:00
2024-11-07 09:32:29 +01:00
pub cell_data: Vec<CellTarget>,
2024-11-05 12:57:49 +01:00
pub merkle_paths: Vec<MerkleProofTarget>,
}
2024-10-16 11:21:33 +02:00
2024-11-07 09:32:29 +01:00
/// circuit input as field elements
#[derive(Clone, Debug, PartialEq)]
2024-11-05 12:57:49 +01:00
pub struct SampleCircuitInput<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
>{
2024-11-26 12:10:17 +01:00
pub entropy: HashOut<F>, // public input
pub dataset_root: HashOut<F>, // public input
pub slot_index: F, // public input
2024-11-05 12:57:49 +01:00
pub slot_root: HashOut<F>,
pub n_cells_per_slot: F,
pub n_slots_per_dataset: F,
2024-11-07 09:32:29 +01:00
pub slot_proof: Vec<HashOut<F>>,
2024-11-05 12:57:49 +01:00
2024-11-07 09:32:29 +01:00
pub cell_data: Vec<Cell<F,D>>,
pub merkle_paths: Vec<MerklePath<F,D>>,
2024-10-16 11:21:33 +02:00
}
2024-11-07 09:32:29 +01:00
/// merkle path from leaf to root as vec of HashOut (4 Goldilocks field elems)
#[derive(Clone, Debug, PartialEq)]
2024-11-05 12:57:49 +01:00
pub struct MerklePath<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
> {
2024-11-07 09:32:29 +01:00
pub path: Vec<HashOut<F>>
2024-11-05 12:57:49 +01:00
}
2024-10-17 09:12:27 +02:00
2024-11-07 09:32:29 +01:00
/// a vec of cell targets
#[derive(Clone, Debug, PartialEq)]
2024-11-05 12:57:49 +01:00
pub struct CellTarget {
pub data: Vec<Target>
2024-10-17 09:12:27 +02:00
}
2024-11-07 09:32:29 +01:00
/// cell data as field elements
#[derive(Clone, Debug, PartialEq)]
pub struct Cell<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
> {
pub data: Vec<F>,
}
2024-10-17 09:12:27 +02:00
//------- circuit impl --------
impl<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
2024-11-07 09:32:29 +01:00
> SampleCircuit<F, D> {
2024-10-17 09:12:27 +02:00
2024-10-22 13:56:39 +02:00
// in-circuit sampling
// TODO: make it more modular
2024-10-17 09:12:27 +02:00
pub fn sample_slot_circuit(
2024-11-05 12:57:49 +01:00
&self,
2024-10-17 09:12:27 +02:00
builder: &mut CircuitBuilder::<F, D>,
2024-11-05 12:57:49 +01:00
)-> SampleTargets {
// circuit params
let CircuitParams {
max_depth,
max_log2_n_slots,
block_tree_depth,
n_field_elems_per_cell,
n_samples,
} = self.params;
2024-10-17 09:12:27 +02:00
2024-10-22 13:56:39 +02:00
// constants
2024-11-03 11:39:59 +01:00
let zero = builder.zero();
2024-10-22 13:56:39 +02:00
let one = builder.one();
let two = builder.two();
// ***** prove slot root is in dataset tree *********
// Create virtual target for slot root and index
let slot_root = builder.add_virtual_hash();
2024-11-26 12:10:17 +01:00
let slot_index = builder.add_virtual_public_input();// public input
2024-10-22 13:56:39 +02:00
// dataset path bits (binary decomposition of leaf_index)
2024-11-05 12:57:49 +01:00
let d_path_bits = builder.split_le(slot_index,max_log2_n_slots);
2024-10-22 13:56:39 +02:00
2024-11-05 12:57:49 +01:00
// create virtual target for n_slots_per_dataset
let n_slots_per_dataset = builder.add_virtual_target();
2024-10-22 13:56:39 +02:00
2024-11-12 13:06:28 +01:00
// dataset last bits and mask bits
let (d_last_bits, d_mask_bits) =
ceiling_log2(builder, n_slots_per_dataset, max_log2_n_slots);
2024-11-03 11:50:46 +01:00
2024-10-22 13:56:39 +02:00
// dataset Merkle path (sibling hashes from leaf to root)
let d_merkle_path = MerkleProofTarget {
2024-11-05 12:57:49 +01:00
path: (0..max_log2_n_slots).map(|_| builder.add_virtual_hash()).collect(),
2024-10-22 13:56:39 +02:00
};
// create MerkleTreeTargets struct
2024-11-03 11:45:20 +01:00
let mut d_targets = MerkleTreeTargets{
2024-10-22 13:56:39 +02:00
leaf: slot_root,
path_bits: d_path_bits,
last_bits: d_last_bits,
2024-11-03 11:50:46 +01:00
mask_bits: d_mask_bits,
2024-10-22 13:56:39 +02:00
merkle_path: d_merkle_path,
};
// dataset reconstructed root
let d_reconstructed_root =
2024-11-05 12:57:49 +01:00
MerkleTreeCircuit::<F,D>::reconstruct_merkle_root_circuit_with_mask(builder, &mut d_targets, max_log2_n_slots);
2024-10-21 13:31:54 +02:00
// expected Merkle root
2024-11-26 12:10:17 +01:00
let d_expected_root = builder.add_virtual_hash_public_input(); // public input
2024-10-21 13:31:54 +02:00
// check equality with expected root
for i in 0..NUM_HASH_OUT_ELTS {
2024-10-22 13:56:39 +02:00
builder.connect(d_expected_root.elements[i], d_reconstructed_root.elements[i]);
2024-10-21 13:31:54 +02:00
}
2024-10-22 13:56:39 +02:00
//*********** do the sampling ************
let mut data_targets =vec![];
let mut slot_sample_proofs = vec![];
2024-11-26 12:10:17 +01:00
let entropy_target = builder.add_virtual_hash_public_input(); // public input
2024-11-03 11:41:21 +01:00
2024-11-05 12:57:49 +01:00
// virtual target for n_cells_per_slot
let n_cells_per_slot = builder.add_virtual_target();
2024-11-07 09:32:29 +01:00
// calculate last index = n_cells_per_slot-1
2024-11-05 12:57:49 +01:00
let slot_last_index = builder.sub(n_cells_per_slot, one);
2024-11-07 09:32:29 +01:00
// create the mask bits
// TODO: re-use this for block and slot trees
2024-11-07 09:32:29 +01:00
let mask_bits = builder.split_le(slot_last_index,max_depth);
// last and mask bits for block tree
2024-11-05 12:57:49 +01:00
let mut b_last_bits = builder.split_le(slot_last_index,max_depth);
let mut b_mask_bits = builder.split_le(slot_last_index,max_depth);
2024-11-03 11:41:21 +01:00
2024-11-07 09:32:29 +01:00
// last and mask bits for the slot tree
2024-11-05 12:57:49 +01:00
let mut s_last_bits = b_last_bits.split_off(block_tree_depth);
let mut s_mask_bits = b_mask_bits.split_off(block_tree_depth);
2024-11-03 11:41:21 +01:00
2024-11-07 09:32:29 +01:00
// pad mask bits with 0
2024-11-05 12:57:49 +01:00
b_mask_bits.push(BoolTarget::new_unsafe(zero.clone()));
s_mask_bits.push(BoolTarget::new_unsafe(zero.clone()));
2024-11-03 11:50:46 +01:00
2024-11-05 12:57:49 +01:00
for i in 0..n_samples{
2024-10-22 13:56:39 +02:00
// cell data targets
2024-11-05 12:57:49 +01:00
let mut data_i = (0..n_field_elems_per_cell).map(|_| builder.add_virtual_target()).collect::<Vec<_>>();
2024-11-07 09:32:29 +01:00
// hash the cell data
2024-11-03 11:48:35 +01:00
let mut hash_inputs:Vec<Target>= Vec::new();
hash_inputs.extend_from_slice(&data_i);
// let data_i_hash = builder.hash_n_to_hash_no_pad::<HF>(hash_inputs);
2024-11-12 12:03:56 +01:00
let data_i_hash = hash_n_no_padding::<F,D,HF>(builder, hash_inputs);
2024-11-07 09:32:29 +01:00
// make the counter into hash digest
2024-11-03 11:43:07 +01:00
let ctr_target = builder.constant(F::from_canonical_u64((i+1) as u64));
2024-11-03 11:39:59 +01:00
let mut ctr = builder.add_virtual_hash();
for i in 0..ctr.elements.len() {
if(i==0){
ctr.elements[i] = ctr_target;
}else{
ctr.elements[i] = zero.clone();
}
}
2024-11-07 09:32:29 +01:00
// paths for block and slot
let mut b_path_bits = self.calculate_cell_index_bits(builder, &entropy_target, &d_targets.leaf, &ctr, mask_bits.clone());
2024-11-05 12:57:49 +01:00
let mut s_path_bits = b_path_bits.split_off(block_tree_depth);
2024-10-22 13:56:39 +02:00
let mut b_merkle_path = MerkleProofTarget {
2024-11-05 12:57:49 +01:00
path: (0..block_tree_depth).map(|_| builder.add_virtual_hash()).collect(),
2024-10-22 13:56:39 +02:00
};
let mut s_merkle_path = MerkleProofTarget {
2024-11-05 12:57:49 +01:00
path: (0..(max_depth - block_tree_depth)).map(|_| builder.add_virtual_hash()).collect(),
2024-10-22 13:56:39 +02:00
};
let mut block_targets = MerkleTreeTargets {
leaf: data_i_hash,
path_bits:b_path_bits,
2024-11-03 11:41:21 +01:00
last_bits: b_last_bits.clone(),
2024-11-03 11:50:46 +01:00
mask_bits: b_mask_bits.clone(),
2024-10-22 13:56:39 +02:00
merkle_path: b_merkle_path,
};
// reconstruct block root
2024-11-05 12:57:49 +01:00
let b_root = MerkleTreeCircuit::<F,D>::reconstruct_merkle_root_circuit_with_mask(builder, &mut block_targets, block_tree_depth);
2024-10-22 13:56:39 +02:00
let mut slot_targets = MerkleTreeTargets {
leaf: b_root,
path_bits:s_path_bits,
2024-11-03 11:41:21 +01:00
last_bits:s_last_bits.clone(),
2024-11-03 11:50:46 +01:00
mask_bits:s_mask_bits.clone(),
2024-10-22 13:56:39 +02:00
merkle_path:s_merkle_path,
};
// reconstruct slot root with block root as leaf
2024-11-05 12:57:49 +01:00
let slot_reconstructed_root = MerkleTreeCircuit::<F,D>::reconstruct_merkle_root_circuit_with_mask(builder, &mut slot_targets, max_depth-block_tree_depth);
2024-10-22 13:56:39 +02:00
// check equality with expected root
for i in 0..NUM_HASH_OUT_ELTS {
builder.connect( d_targets.leaf.elements[i], slot_reconstructed_root.elements[i]);
}
// combine block and slot path to get the full path so we can assign it later.
let mut slot_sample_proof_target = MerkleProofTarget{
path: block_targets.merkle_path.path,
};
slot_sample_proof_target.path.extend_from_slice(&slot_targets.merkle_path.path);
2024-11-07 09:32:29 +01:00
let cell_i = CellTarget{
data: data_i
};
data_targets.push(cell_i);
2024-10-22 13:56:39 +02:00
slot_sample_proofs.push(slot_sample_proof_target);
2024-10-17 09:12:27 +02:00
}
2024-11-05 12:57:49 +01:00
SampleTargets {
2024-10-22 13:56:39 +02:00
entropy: entropy_target,
2024-11-05 12:57:49 +01:00
dataset_root: d_expected_root,
2024-10-22 13:56:39 +02:00
slot_index,
slot_root: d_targets.leaf,
2024-11-05 12:57:49 +01:00
n_cells_per_slot,
n_slots_per_dataset,
slot_proof: d_targets.merkle_path,
cell_data: data_targets,
merkle_paths: slot_sample_proofs,
2024-10-17 09:12:27 +02:00
}
}
2024-11-07 09:32:29 +01:00
/// calculate the cell index = H( entropy | slotRoot | counter ) `mod` nCells
pub fn calculate_cell_index_bits(&self, builder: &mut CircuitBuilder::<F, D>, entropy: &HashOutTarget, slot_root: &HashOutTarget, ctr: &HashOutTarget, mask_bits: Vec<BoolTarget>) -> Vec<BoolTarget> {
2024-11-03 11:39:59 +01:00
let mut hash_inputs:Vec<Target>= Vec::new();
hash_inputs.extend_from_slice(&entropy.elements);
hash_inputs.extend_from_slice(&slot_root.elements);
hash_inputs.extend_from_slice(&ctr.elements);
// let hash_out = builder.hash_n_to_hash_no_pad::<HF>(hash_inputs);
let hash_out = hash_n_with_padding::<F,D,HF>(builder, hash_inputs);
2024-11-05 12:57:49 +01:00
let cell_index_bits = builder.low_bits(hash_out.elements[0], self.params.max_depth, 64);
2024-10-22 13:56:39 +02:00
2024-11-07 09:32:29 +01:00
let mut masked_cell_index_bits = vec![];
// extract the lowest 32 bits using the bit mask
for i in 0..self.params.max_depth{
masked_cell_index_bits.push(BoolTarget::new_unsafe(builder.mul(mask_bits[i].target, cell_index_bits[i].target)));
}
masked_cell_index_bits
2024-10-22 13:56:39 +02:00
}
2024-11-07 09:32:29 +01:00
/// helper method to assign the targets in the circuit to actual field elems
2024-10-17 09:12:27 +02:00
pub fn sample_slot_assign_witness(
2024-11-05 12:57:49 +01:00
&self,
2024-10-17 09:12:27 +02:00
pw: &mut PartialWitness<F>,
2024-11-05 12:57:49 +01:00
targets: &mut SampleTargets,
witnesses: SampleCircuitInput<F, D>,
2024-10-17 09:12:27 +02:00
){
2024-11-05 12:57:49 +01:00
// circuit params
let CircuitParams {
max_depth,
max_log2_n_slots,
block_tree_depth,
n_field_elems_per_cell,
n_samples,
} = self.params;
// assign n_cells_per_slot
pw.set_target(targets.n_cells_per_slot, witnesses.n_cells_per_slot);
// assign n_slots_per_dataset
pw.set_target(targets.n_slots_per_dataset, witnesses.n_slots_per_dataset);
2024-10-22 13:56:39 +02:00
// assign dataset proof
2024-11-05 12:57:49 +01:00
for (i, sibling_hash) in witnesses.slot_proof.iter().enumerate() {
pw.set_hash_target(targets.slot_proof.path[i], *sibling_hash);
2024-10-22 13:56:39 +02:00
}
// assign slot index
2024-11-05 12:57:49 +01:00
pw.set_target(targets.slot_index, witnesses.slot_index);
2024-10-21 13:31:54 +02:00
// assign the expected Merkle root of dataset to the target
2024-11-05 12:57:49 +01:00
pw.set_hash_target(targets.dataset_root, witnesses.dataset_root);
2024-10-21 13:31:54 +02:00
2024-11-05 12:57:49 +01:00
// assign the sampled slot
pw.set_hash_target(targets.slot_root, witnesses.slot_root);
2024-10-22 13:56:39 +02:00
// assign entropy
2024-11-26 11:04:22 +01:00
assign_hash_out_targets(pw, &targets.entropy.elements, &witnesses.entropy.elements);
2024-10-17 09:12:27 +02:00
2024-10-21 13:31:54 +02:00
// do the sample N times
2024-11-05 12:57:49 +01:00
for i in 0..n_samples {
2024-10-22 13:56:39 +02:00
// assign cell data
2024-11-07 09:32:29 +01:00
let leaf = witnesses.cell_data[i].data.clone();
2024-11-05 12:57:49 +01:00
for j in 0..n_field_elems_per_cell{
2024-11-07 09:32:29 +01:00
pw.set_target(targets.cell_data[i].data[j], leaf[j]);
2024-10-22 13:56:39 +02:00
}
// assign proof for that cell
2024-11-07 09:32:29 +01:00
let cell_proof = witnesses.merkle_paths[i].path.clone();
2024-11-05 12:57:49 +01:00
for k in 0..max_depth {
2024-11-28 10:20:01 +01:00
pw.set_hash_target(targets.merkle_paths[i].path[k], cell_proof[k]);
2024-10-22 13:56:39 +02:00
}
2024-10-18 10:09:41 +02:00
}
2024-10-17 09:12:27 +02:00
}
}