Proving and verifying keys, and placeholder proof logic

This commit is contained in:
Jack Grigg 2021-04-15 16:35:48 +12:00
parent f62bbbbb95
commit 4c4400cb63
1 changed files with 228 additions and 0 deletions

View File

@ -1,4 +1,232 @@
use group::Curve;
use halo2::{
plonk,
poly::{EvaluationDomain, LagrangeCoeff, Polynomial, Rotation},
transcript::{Blake2bRead, Blake2bWrite},
};
use pasta_curves::{pallas, vesta};
use crate::{
note::ExtractedNoteCommitment,
primitives::redpallas::{SpendAuth, VerificationKey},
tree::Anchor,
value::ValueCommitment,
Nullifier,
};
pub(crate) mod gadget;
/// Size of the Orchard circuit.
const K: u32 = 11;
/// The Orchard Action circuit.
#[derive(Debug)]
pub struct Circuit {}
impl plonk::Circuit<pallas::Base> for Circuit {
type Config = ();
fn configure(meta: &mut plonk::ConstraintSystem<pallas::Base>) -> Self::Config {
// Placeholder so the proving key is correctly built.
meta.instance_column();
// Placeholder gate so there is something for the prover to operate on.
let advice = meta.advice_column();
meta.create_gate("TODO", |meta| meta.query_advice(advice, Rotation::cur()));
}
fn synthesize(
&self,
_cs: &mut impl plonk::Assignment<pallas::Base>,
_config: Self::Config,
) -> Result<(), plonk::Error> {
Ok(())
}
}
/// The verifying key for the Orchard Action circuit.
#[derive(Debug)]
pub struct VerifyingKey {
params: halo2::poly::commitment::Params<vesta::Affine>,
vk: plonk::VerifyingKey<vesta::Affine>,
}
impl VerifyingKey {
/// Builds the verifying key.
pub fn build() -> Self {
let params = halo2::poly::commitment::Params::new(K);
let circuit = Circuit {}; // TODO
let vk = plonk::keygen_vk(&params, &circuit).unwrap();
VerifyingKey { params, vk }
}
}
/// The proving key for the Orchard Action circuit.
#[derive(Debug)]
pub struct ProvingKey {
params: halo2::poly::commitment::Params<vesta::Affine>,
pk: plonk::ProvingKey<vesta::Affine>,
}
impl ProvingKey {
/// Builds the proving key.
pub fn build() -> Self {
let params = halo2::poly::commitment::Params::new(K);
let circuit = Circuit {}; // TODO
let vk = plonk::keygen_vk(&params, &circuit).unwrap();
let pk = plonk::keygen_pk(&params, vk, &circuit).unwrap();
ProvingKey { params, pk }
}
}
/// Public inputs to the Orchard Action circuit.
#[derive(Debug)]
pub struct Instance {
pub(crate) anchor: Anchor,
pub(crate) cv_net: ValueCommitment,
pub(crate) nf_old: Nullifier,
pub(crate) rk: VerificationKey<SpendAuth>,
pub(crate) cmx: ExtractedNoteCommitment,
pub(crate) enable_spend: bool,
pub(crate) enable_output: bool,
}
impl Instance {
fn to_halo2_instance(
&self,
domain: &EvaluationDomain<vesta::Scalar>,
) -> [Polynomial<vesta::Scalar, LagrangeCoeff>; 1] {
// TODO
[domain.empty_lagrange()]
}
fn to_halo2_instance_commitments(&self, vk: &VerifyingKey) -> [vesta::Affine; 1] {
[vk.params
.commit_lagrange(
&self.to_halo2_instance(vk.vk.get_domain())[0],
Default::default(),
)
.to_affine()]
}
}
/// A proof of the validity of an Orchard [`Bundle`].
///
/// [`Bundle`]: crate::bundle::Bundle
#[derive(Debug)]
pub struct Proof(Vec<u8>);
impl Proof {
/// Creates a proof for the given circuits and instances.
pub fn create(
pk: &ProvingKey,
circuits: &[Circuit],
instances: &[Instance],
) -> Result<Self, plonk::Error> {
let instances: Vec<_> = instances
.iter()
.map(|i| i.to_halo2_instance(pk.pk.get_vk().get_domain()))
.collect();
let instances: Vec<_> = instances.iter().map(|i| &i[..]).collect();
let mut transcript = Blake2bWrite::<_, vesta::Affine>::init(vec![]);
plonk::create_proof(&pk.params, &pk.pk, circuits, &instances, &mut transcript)?;
Ok(Proof(transcript.finalize()))
}
/// Verifies this proof with the given instances.
pub fn verify(&self, vk: &VerifyingKey, instances: &[Instance]) -> Result<(), plonk::Error> {
let instances: Vec<_> = instances
.iter()
.map(|i| i.to_halo2_instance_commitments(vk))
.collect();
let instances: Vec<_> = instances.iter().map(|i| &i[..]).collect();
let msm = vk.params.empty_msm();
let mut transcript = Blake2bRead::init(&self.0[..]);
let guard = plonk::verify_proof(&vk.params, &vk.vk, msm, &instances, &mut transcript)?;
let msm = guard.clone().use_challenges();
if msm.eval() {
Ok(())
} else {
Err(plonk::Error::ConstraintSystemFailure)
}
}
}
#[cfg(test)]
mod tests {
use ff::Field;
use halo2::dev::MockProver;
use pasta_curves::pallas;
use rand::rngs::OsRng;
use std::iter;
use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K};
use crate::{
keys::SpendValidatingKey,
note::Note,
tree::Anchor,
value::{ValueCommitTrapdoor, ValueCommitment},
};
#[test]
fn round_trip() {
let mut rng = OsRng;
let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(())
.map(|()| {
let (fvk, spent_note) = Note::dummy(&mut rng, None);
let nf_old = spent_note.nullifier(&fvk);
let ak: SpendValidatingKey = fvk.into();
let alpha = pallas::Scalar::random(&mut rng);
let rk = ak.randomize(&alpha);
let (_, output_note) = Note::dummy(&mut rng, Some(nf_old.clone()));
let cmx = output_note.commitment().into();
let value = spent_note.value() - output_note.value();
let cv_net = ValueCommitment::derive(value.unwrap(), ValueCommitTrapdoor::zero());
(
Circuit {},
Instance {
anchor: Anchor,
cv_net,
nf_old,
rk,
cmx,
enable_spend: true,
enable_output: true,
},
)
})
.unzip();
let vk = VerifyingKey::build();
for (circuit, instance) in circuits.iter().zip(instances.iter()) {
assert_eq!(
MockProver::run(
K,
circuit,
instance
.to_halo2_instance(vk.vk.get_domain())
.iter()
.map(|p| p.iter().cloned().collect())
.collect()
)
.unwrap()
.verify(),
Ok(())
);
}
let pk = ProvingKey::build();
let proof = Proof::create(&pk, &circuits, &instances).unwrap();
assert!(proof.verify(&vk, &instances).is_ok());
}
}