344 lines
12 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 anyhow::Result;
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};
use plonky2::iop::witness::{PartialWitness, WitnessWrite, Witness};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher, GenericHashOut};
use std::marker::PhantomData;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use plonky2::hash::hashing::PlonkyPermutation;
2024-11-05 12:57:49 +01:00
use crate::circuits::params::HF;
2024-10-16 11:21:33 +02:00
2024-11-05 12:57:49 +01:00
use crate::circuits::merkle_circuit::{MerkleTreeCircuit, MerkleTreeTargets, MerkleProofTarget};
use crate::circuits::utils::{assign_hash_out_targets, bits_le_padded_to_usize, calculate_cell_index_bits};
2024-10-16 11:21:33 +02:00
// ------ Dataset Tree --------
///dataset tree containing all slot trees
#[derive(Clone)]
2024-10-17 21:38:14 +02:00
pub struct DatasetTreeCircuit<
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-05 12:57:49 +01:00
> DatasetTreeCircuit<F, D> {
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-05 12:57:49 +01:00
// params used for the circuits
// should be defined prior to building the circuit
#[derive(Clone)]
pub struct CircuitParams{
pub max_depth: usize,
pub max_log2_n_slots: usize,
pub block_tree_depth: usize,
pub n_field_elems_per_cell: usize,
pub n_samples: usize,
}
2024-10-16 11:21:33 +02:00
2024-11-05 12:57:49 +01:00
#[derive(Clone)]
pub struct SampleTargets {
2024-10-18 12:01:01 +02:00
2024-11-05 12:57:49 +01:00
pub entropy: HashOutTarget,
pub dataset_root: HashOutTarget,
pub slot_index: Target,
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-05 12:57:49 +01:00
pub slot_proof: MerkleProofTarget, // proof that slot_root in dataset tree
2024-10-16 11:21:33 +02:00
2024-11-05 12:57:49 +01:00
pub cell_data: Vec<Vec<Target>>,
pub merkle_paths: Vec<MerkleProofTarget>,
}
2024-10-16 11:21:33 +02:00
2024-11-05 12:57:49 +01:00
#[derive(Clone)]
pub struct SampleCircuitInput<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
>{
pub entropy: Vec<F>,
pub dataset_root: HashOut<F>,
pub slot_index: F,
pub slot_root: HashOut<F>,
pub n_cells_per_slot: F,
pub n_slots_per_dataset: F,
pub slot_proof: Vec<HashOut<F>>, // proof that slot_root in dataset tree
pub cell_data: Vec<Vec<F>>,
pub merkle_paths: Vec<Vec<HashOut<F>>>,
2024-10-16 11:21:33 +02:00
}
2024-10-21 13:31:54 +02:00
#[derive(Clone)]
2024-11-05 12:57:49 +01:00
pub struct MerklePath<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
> {
path: Vec<HashOut<F>>
}
2024-10-17 09:12:27 +02:00
2024-11-05 12:57:49 +01:00
#[derive(Clone)]
pub struct CellTarget {
pub data: Vec<Target>
2024-10-17 09:12:27 +02:00
}
//------- circuit impl --------
impl<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
2024-11-03 11:45:20 +01:00
> DatasetTreeCircuit<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();
let slot_index = builder.add_virtual_target();
// 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-05 12:57:49 +01:00
// dataset last bits (binary decomposition of last_index = nleaves - 1)
let dataset_last_index = builder.sub(n_slots_per_dataset, one);
let d_last_bits = builder.split_le(dataset_last_index,max_log2_n_slots);
let d_mask_bits = builder.split_le(dataset_last_index,max_log2_n_slots+1);
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-10-22 13:56:39 +02:00
let d_expected_root = builder.add_virtual_hash();
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-03 11:39:59 +01:00
let entropy_target = builder.add_virtual_hash();
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();
let slot_last_index = builder.sub(n_cells_per_slot, one);
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-03 11:50:46 +01:00
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-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-10-22 13:56:39 +02:00
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-10-22 13:56:39 +02:00
// counter constant
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-10-22 13:56:39 +02:00
// paths
2024-11-05 12:57:49 +01:00
let mut b_path_bits = self.calculate_cell_index_bits(builder, &entropy_target, &d_targets.leaf, &ctr);
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);
data_targets.push(data_i);
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-05 12:57:49 +01:00
pub fn calculate_cell_index_bits(&self, builder: &mut CircuitBuilder::<F, D>, entropy: &HashOutTarget, slot_root: &HashOutTarget, ctr: &HashOutTarget) -> 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);
2024-11-03 11:45:20 +01:00
let hash_out = builder.hash_n_to_hash_no_pad::<HF>(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-03 11:39:59 +01:00
cell_index_bits
2024-10-22 13:56:39 +02:00
}
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-05 12:57:49 +01:00
assign_hash_out_targets(pw, &targets.entropy.elements, &witnesses.entropy);
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 {
let cell_index_bits = calculate_cell_index_bits(&witnesses.entropy,witnesses.slot_root,i+1,max_depth);
2024-10-18 10:09:41 +02:00
let cell_index = bits_le_padded_to_usize(&cell_index_bits);
2024-10-22 13:56:39 +02:00
// assign cell data
2024-11-05 12:57:49 +01:00
let leaf = witnesses.cell_data[i].clone();
for j in 0..n_field_elems_per_cell{
2024-10-22 13:56:39 +02:00
pw.set_target(targets.cell_data[i][j], leaf[j]);
}
2024-11-05 12:57:49 +01:00
2024-10-22 13:56:39 +02:00
// assign proof for that cell
2024-11-05 12:57:49 +01:00
let cell_proof = witnesses.merkle_paths[i].clone();
for k in 0..max_depth {
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
}
}