use ironfish_bellperson::gadgets::boolean::{AllocatedBit, Boolean};
use ironfish_bellperson::gadgets::sha256::sha256_block_no_padding;
use ironfish_bellperson::{ConstraintSystem, SynthesisError};
use ff::PrimeField;
use super::commitment::note_comm;
use super::prfs::*;
use super::*;
pub struct InputNote {
pub nf: Vec<Boolean>,
pub mac: Vec<Boolean>,
}
impl InputNote {
#[allow(clippy::too_many_arguments)]
pub fn compute<Scalar, CS>(
mut cs: CS,
a_sk: Option<SpendingKey>,
rho: Option<UniqueRandomness>,
r: Option<CommitmentRandomness>,
value: &NoteValue,
h_sig: &[Boolean],
nonce: bool,
auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH],
rt: &[Boolean],
) -> Result<InputNote, SynthesisError>
where
Scalar: PrimeField,
CS: ConstraintSystem<Scalar>,
{
let a_sk = witness_u252(
cs.namespace(|| "a_sk"),
a_sk.as_ref().map(|a_sk| &a_sk.0[..]),
)?;
let rho = witness_u256(cs.namespace(|| "rho"), rho.as_ref().map(|rho| &rho.0[..]))?;
let r = witness_u256(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?;
let a_pk = prf_a_pk(cs.namespace(|| "a_pk computation"), &a_sk)?;
let nf = prf_nf(cs.namespace(|| "nf computation"), &a_sk, &rho)?;
let mac = prf_pk(cs.namespace(|| "mac computation"), &a_sk, h_sig, nonce)?;
let cm = note_comm(
cs.namespace(|| "cm computation"),
&a_pk,
&value.bits_le(),
&rho,
&r,
)?;
let mut cur = cm;
for (i, layer) in auth_path.iter().enumerate() {
let cs = &mut cs.namespace(|| format!("layer {}", i));
let cur_is_right = AllocatedBit::alloc(
cs.namespace(|| "cur is right"),
layer.as_ref().map(|&(_, p)| p),
)?;
let lhs = cur;
let rhs = witness_u256(
cs.namespace(|| "sibling"),
layer.as_ref().map(|&(ref sibling, _)| &sibling[..]),
)?;
let preimage = conditionally_swap_u256(
cs.namespace(|| "conditional swap"),
&lhs[..],
&rhs[..],
&cur_is_right,
)?;
cur = sha256_block_no_padding(cs.namespace(|| "hash of this layer"), &preimage)?;
}
let enforce = AllocatedBit::alloc(
cs.namespace(|| "enforce"),
value.get_value().map(|n| n != 0),
)?;
cs.enforce(
|| "enforce validity",
|_| value.lc(),
|lc| lc + CS::one() - enforce.get_variable(),
|lc| lc,
);
assert_eq!(cur.len(), rt.len());
for (i, (cur, rt)) in cur.into_iter().zip(rt.iter()).enumerate() {
cs.enforce(
|| format!("conditionally enforce correct root for bit {}", i),
|_| cur.lc(CS::one(), Scalar::one()) - &rt.lc(CS::one(), Scalar::one()),
|lc| lc + enforce.get_variable(),
|lc| lc,
);
}
Ok(InputNote { mac, nf })
}
}
pub fn conditionally_swap_u256<Scalar, CS>(
mut cs: CS,
lhs: &[Boolean],
rhs: &[Boolean],
condition: &AllocatedBit,
) -> Result<Vec<Boolean>, SynthesisError>
where
Scalar: PrimeField,
CS: ConstraintSystem<Scalar>,
{
assert_eq!(lhs.len(), 256);
assert_eq!(rhs.len(), 256);
let mut new_lhs = vec![];
let mut new_rhs = vec![];
for (i, (lhs, rhs)) in lhs.iter().zip(rhs.iter()).enumerate() {
let cs = &mut cs.namespace(|| format!("bit {}", i));
let x = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "x"),
condition
.get_value()
.and_then(|v| if v { rhs.get_value() } else { lhs.get_value() }),
)?);
cs.enforce(
|| "conditional swap for x",
|lc| lc + &rhs.lc(CS::one(), Scalar::one()) - &lhs.lc(CS::one(), Scalar::one()),
|lc| lc + condition.get_variable(),
|lc| lc + &x.lc(CS::one(), Scalar::one()) - &lhs.lc(CS::one(), Scalar::one()),
);
let y = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "y"),
condition
.get_value()
.and_then(|v| if v { lhs.get_value() } else { rhs.get_value() }),
)?);
cs.enforce(
|| "conditional swap for y",
|lc| lc + &lhs.lc(CS::one(), Scalar::one()) - &rhs.lc(CS::one(), Scalar::one()),
|lc| lc + condition.get_variable(),
|lc| lc + &y.lc(CS::one(), Scalar::one()) - &rhs.lc(CS::one(), Scalar::one()),
);
new_lhs.push(x);
new_rhs.push(y);
}
let mut f = new_lhs;
f.extend(new_rhs);
assert_eq!(f.len(), 512);
Ok(f)
}