use std::marker::PhantomData;
use bellperson::{
gadgets::{boolean::Boolean, multipack, num::AllocatedNum, sha256::sha256 as sha256_circuit},
Circuit, ConstraintSystem, SynthesisError,
};
use blstrs::Scalar as Fr;
use ff::PrimeField;
use filecoin_hashers::Hasher;
use storage_proofs_core::{
compound_proof::CircuitComponent,
error::Result,
gadgets::{constraint, encode, por::PoRCircuit, uint64::UInt64, variables::Root},
merkle::BinaryMerkleTree,
util::reverse_bit_numbering,
};
pub struct DrgPoRepCircuit<'a, H: Hasher> {
pub replica_nodes: Vec<Option<Fr>>,
#[allow(clippy::type_complexity)]
pub replica_nodes_paths: Vec<Vec<(Vec<Option<Fr>>, Option<usize>)>>,
pub replica_root: Root<Fr>,
pub replica_parents: Vec<Vec<Option<Fr>>>,
#[allow(clippy::type_complexity)]
pub replica_parents_paths: Vec<Vec<Vec<(Vec<Option<Fr>>, Option<usize>)>>>,
pub data_nodes: Vec<Option<Fr>>,
#[allow(clippy::type_complexity)]
pub data_nodes_paths: Vec<Vec<(Vec<Option<Fr>>, Option<usize>)>>,
pub data_root: Root<Fr>,
pub replica_id: Option<Fr>,
pub private: bool,
pub _h: PhantomData<&'a H>,
}
impl<'a, H: 'static + Hasher> DrgPoRepCircuit<'a, H> {
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
pub fn synthesize<CS>(
mut cs: CS,
replica_nodes: Vec<Option<Fr>>,
replica_nodes_paths: Vec<Vec<(Vec<Option<Fr>>, Option<usize>)>>,
replica_root: Root<Fr>,
replica_parents: Vec<Vec<Option<Fr>>>,
replica_parents_paths: Vec<Vec<Vec<(Vec<Option<Fr>>, Option<usize>)>>>,
data_nodes: Vec<Option<Fr>>,
data_nodes_paths: Vec<Vec<(Vec<Option<Fr>>, Option<usize>)>>,
data_root: Root<Fr>,
replica_id: Option<Fr>,
private: bool,
) -> Result<(), SynthesisError>
where
CS: ConstraintSystem<Fr>,
{
DrgPoRepCircuit::<H> {
replica_nodes,
replica_nodes_paths,
replica_root,
replica_parents,
replica_parents_paths,
data_nodes,
data_nodes_paths,
data_root,
replica_id,
private,
_h: Default::default(),
}
.synthesize(&mut cs)
}
}
#[derive(Default, Clone)]
pub struct ComponentPrivateInputs {
pub comm_r: Option<Root<Fr>>,
pub comm_d: Option<Root<Fr>>,
}
impl<'a, H: Hasher> CircuitComponent for DrgPoRepCircuit<'a, H> {
type ComponentPrivateInputs = ComponentPrivateInputs;
}
impl<'a, H: 'static + Hasher> Circuit<Fr> for DrgPoRepCircuit<'a, H> {
fn synthesize<CS: ConstraintSystem<Fr>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
let replica_id = self.replica_id;
let replica_root = self.replica_root;
let data_root = self.data_root;
let nodes = self.data_nodes.len();
assert_eq!(self.replica_nodes.len(), nodes);
assert_eq!(self.replica_nodes_paths.len(), nodes);
assert_eq!(self.replica_parents.len(), nodes);
assert_eq!(self.replica_parents_paths.len(), nodes);
assert_eq!(self.data_nodes_paths.len(), nodes);
let replica_node_num = AllocatedNum::alloc(cs.namespace(|| "replica_id_num"), || {
replica_id.ok_or(SynthesisError::AssignmentMissing)
})?;
replica_node_num.inputize(cs.namespace(|| "replica_id"))?;
let replica_id_bits =
reverse_bit_numbering(replica_node_num.to_bits_le(cs.namespace(|| "replica_id_bits"))?);
let replica_root_var = Root::Var(replica_root.allocated(cs.namespace(|| "replica_root"))?);
let data_root_var = Root::Var(data_root.allocated(cs.namespace(|| "data_root"))?);
for i in 0..self.data_nodes.len() {
let mut cs = cs.namespace(|| format!("challenge_{}", i));
let replica_node_path = &self.replica_nodes_paths[i];
let replica_parents_paths = &self.replica_parents_paths[i];
let data_node_path = &self.data_nodes_paths[i];
let replica_node = &self.replica_nodes[i];
let replica_parents = &self.replica_parents[i];
let data_node = &self.data_nodes[i];
assert_eq!(replica_parents.len(), replica_parents_paths.len());
assert_eq!(data_node_path.len(), replica_node_path.len());
assert_eq!(replica_node.is_some(), data_node.is_some());
{
let mut cs = cs.namespace(|| "inclusion_checks");
PoRCircuit::<BinaryMerkleTree<H>>::synthesize(
cs.namespace(|| "replica_inclusion"),
Root::Val(*replica_node),
replica_node_path.clone().into(),
replica_root_var.clone(),
self.private,
)?;
for j in 0..replica_parents.len() {
PoRCircuit::<BinaryMerkleTree<H>>::synthesize(
cs.namespace(|| format!("parents_inclusion_{}", j)),
Root::Val(replica_parents[j]),
replica_parents_paths[j].clone().into(),
replica_root_var.clone(),
self.private,
)?;
}
PoRCircuit::<BinaryMerkleTree<H>>::synthesize(
cs.namespace(|| "data_inclusion"),
Root::Val(*data_node),
data_node_path.clone().into(),
data_root_var.clone(),
self.private,
)?;
}
{
let mut cs = cs.namespace(|| "encoding_checks");
let parents_bits: Vec<Vec<Boolean>> = replica_parents
.iter()
.enumerate()
.map(|(i, val)| {
let num = AllocatedNum::alloc(
cs.namespace(|| format!("parents_{}_num", i)),
|| val.map(Into::into).ok_or(SynthesisError::AssignmentMissing),
)?;
Ok(reverse_bit_numbering(num.to_bits_le(
cs.namespace(|| format!("parents_{}_bits", i)),
)?))
})
.collect::<Result<Vec<Vec<Boolean>>, SynthesisError>>()?;
let key = kdf(
cs.namespace(|| "kdf"),
&replica_id_bits,
parents_bits,
None,
None,
)?;
let replica_node_num =
AllocatedNum::alloc(cs.namespace(|| "replica_node"), || {
(*replica_node).ok_or(SynthesisError::AssignmentMissing)
})?;
let decoded = encode::decode(cs.namespace(|| "decode"), &key, &replica_node_num)?;
let expected = AllocatedNum::alloc(cs.namespace(|| "data node"), || {
data_node.ok_or(SynthesisError::AssignmentMissing)
})?;
constraint::equal(&mut cs, || "equality", &expected, &decoded);
}
}
Ok(())
}
}
fn kdf<Scalar, CS>(
mut cs: CS,
id: &[Boolean],
parents: Vec<Vec<Boolean>>,
window_index: Option<UInt64>,
node: Option<UInt64>,
) -> Result<AllocatedNum<Scalar>, SynthesisError>
where
Scalar: PrimeField,
CS: ConstraintSystem<Scalar>,
{
let mut ciphertexts = id.to_vec();
if let Some(window_index) = window_index {
ciphertexts.extend_from_slice(&window_index.to_bits_be());
}
if let Some(node) = node {
ciphertexts.extend_from_slice(&node.to_bits_be());
}
for parent in parents.into_iter() {
ciphertexts.extend_from_slice(&parent);
}
let alloc_bits = sha256_circuit(cs.namespace(|| "hash"), &ciphertexts[..])?;
let fr = if alloc_bits[0].get_value().is_some() {
let be_bits = alloc_bits
.iter()
.map(|v| v.get_value().ok_or(SynthesisError::AssignmentMissing))
.collect::<Result<Vec<bool>, SynthesisError>>()?;
let le_bits = be_bits
.chunks(8)
.flat_map(|chunk| chunk.iter().rev())
.copied()
.take(Scalar::CAPACITY as usize)
.collect::<Vec<bool>>();
Ok(multipack::compute_multipacking::<Scalar>(&le_bits)[0])
} else {
Err(SynthesisError::AssignmentMissing)
};
AllocatedNum::<Scalar>::alloc(cs.namespace(|| "result_num"), || fr)
}