use halo2_proofs::{
circuit::{AssignedCell, Layouter},
plonk::{Advice, Column, Error, Instance as InstanceColumn},
};
use pasta_curves::arithmetic::CurveAffine;
use pasta_curves::pallas;
use orchard::constants::{OrchardBaseFieldBases, OrchardFixedBases, OrchardShortScalarBases};
use halo2_gadgets::ecc::{
chip::EccChip,
FixedPointBaseField, FixedPointShort, NonIdentityPoint, ScalarFixedShort, ScalarVar,
};
pub(crate) struct EaPkInstanceLoc {
pub instance: Column<InstanceColumn>,
pub x_row: usize,
pub y_row: usize,
}
pub fn spend_auth_g_affine() -> pallas::Affine {
use group::Curve;
let g = orchard::constants::fixed_bases::spend_auth_g::generator();
pallas::Point::from(g).to_affine()
}
pub fn base_to_scalar(b: pallas::Base) -> Option<pallas::Scalar> {
use ff::PrimeField;
pallas::Scalar::from_repr(b.to_repr()).into()
}
pub fn elgamal_encrypt(
share_value: pallas::Base,
randomness: pallas::Base,
ea_pk: pallas::Point,
) -> (pallas::Base, pallas::Base, pallas::Base, pallas::Base) {
use group::Curve;
let g = pallas::Point::from(spend_auth_g_affine());
let r_scalar = base_to_scalar(randomness)
.expect("randomness must be < scalar field modulus");
let v_scalar = base_to_scalar(share_value)
.expect("share value must be < scalar field modulus");
let c1 = g * r_scalar;
let c2 = g * v_scalar + ea_pk * r_scalar;
let c1_coords = c1.to_affine().coordinates().unwrap();
let c2_coords = c2.to_affine().coordinates().unwrap();
(*c1_coords.x(), *c2_coords.x(), *c1_coords.y(), *c2_coords.y())
}
pub(crate) fn prove_elgamal_encryptions(
ecc_chip: EccChip<OrchardFixedBases>,
mut layouter: impl Layouter<pallas::Base>,
namespace: &str,
ea_pk: halo2_proofs::circuit::Value<pallas::Affine>,
ea_pk_loc: EaPkInstanceLoc,
advice_col: Column<Advice>,
share_cells: [AssignedCell<pallas::Base, pallas::Base>; 16],
r_cells: [AssignedCell<pallas::Base, pallas::Base>; 16],
enc_c1_cells: [AssignedCell<pallas::Base, pallas::Base>; 16],
enc_c2_cells: [AssignedCell<pallas::Base, pallas::Base>; 16],
enc_c1_y_cells: [AssignedCell<pallas::Base, pallas::Base>; 16],
enc_c2_y_cells: [AssignedCell<pallas::Base, pallas::Base>; 16],
) -> Result<(), Error> {
let ea_pk_point = NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| alloc::format!("{namespace} ea_pk witness")),
ea_pk,
)?;
layouter.constrain_instance(
ea_pk_point.inner().x().cell(),
ea_pk_loc.instance,
ea_pk_loc.x_row,
)?;
layouter.constrain_instance(
ea_pk_point.inner().y().cell(),
ea_pk_loc.instance,
ea_pk_loc.y_row,
)?;
let spend_auth_g_base = FixedPointBaseField::from_inner(
ecc_chip.clone(),
OrchardBaseFieldBases::SpendAuthGBase,
);
let spend_auth_g_short = FixedPointShort::from_inner(
ecc_chip.clone(),
OrchardShortScalarBases::SpendAuthGShort,
);
for i in 0..16 {
let c1_point = spend_auth_g_base
.clone()
.mul(
layouter.namespace(|| alloc::format!("{namespace} [r_{i}] * G")),
r_cells[i].clone(),
)?;
let c1_x = c1_point.extract_p().inner().clone();
layouter.assign_region(
|| alloc::format!("{namespace} C1[{i}] x == enc_c1_x[{i}]"),
|mut region| region.constrain_equal(c1_x.cell(), enc_c1_cells[i].cell()),
)?;
let c1_y = c1_point.inner().y();
layouter.assign_region(
|| alloc::format!("{namespace} C1[{i}] y == enc_c1_y[{i}]"),
|mut region| region.constrain_equal(c1_y.cell(), enc_c1_y_cells[i].cell()),
)?;
let sign_one = layouter.assign_region(
|| alloc::format!("{namespace} sign_one[{i}]"),
|mut region| {
region.assign_advice_from_constant(
|| "sign = +1",
advice_col,
0,
pallas::Base::one(),
)
},
)?;
let v_scalar = ScalarFixedShort::new(
ecc_chip.clone(),
layouter.namespace(|| alloc::format!("{namespace} v_{i} short scalar")),
(share_cells[i].clone(), sign_one),
)?;
let (v_g_point, _) = spend_auth_g_short
.clone()
.mul(
layouter.namespace(|| alloc::format!("{namespace} [v_{i}] * G")),
v_scalar,
)?;
let r_i_scalar = ScalarVar::from_base(
ecc_chip.clone(),
layouter.namespace(|| alloc::format!("{namespace} r[{i}] to ScalarVar")),
&r_cells[i],
)?;
let (r_ea_pk_point, _) = ea_pk_point.mul(
layouter.namespace(|| alloc::format!("{namespace} [r_{i}] * ea_pk")),
r_i_scalar,
)?;
let c2_point = v_g_point.add(
layouter.namespace(|| alloc::format!("{namespace} C2[{i}] = vG + rP")),
&r_ea_pk_point,
)?;
let c2_x = c2_point.extract_p().inner().clone();
layouter.assign_region(
|| alloc::format!("{namespace} C2[{i}] x == enc_c2_x[{i}]"),
|mut region| region.constrain_equal(c2_x.cell(), enc_c2_cells[i].cell()),
)?;
let c2_y = c2_point.inner().y();
layouter.assign_region(
|| alloc::format!("{namespace} C2[{i}] y == enc_c2_y[{i}]"),
|mut region| region.constrain_equal(c2_y.cell(), enc_c2_y_cells[i].cell()),
)?;
}
Ok(())
}