use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use halo2_proofs::{
pasta::EqAffine,
plonk::{self, create_proof, keygen_pk, keygen_vk, verify_proof, SingleVerifier},
poly::commitment::Params,
transcript::{Blake2bRead, Blake2bWrite, Challenge255},
};
use pasta_curves::{pallas, vesta};
use rand::rngs::OsRng;
use super::circuit::{Circuit, Instance, K};
pub fn delegation_params() -> Params<EqAffine> {
Params::new(K)
}
pub fn delegation_proving_key(
params: &Params<EqAffine>,
) -> (
plonk::ProvingKey<EqAffine>,
plonk::VerifyingKey<EqAffine>,
) {
let empty_circuit = Circuit::default();
let vk = keygen_vk(params, &empty_circuit).expect("delegation keygen_vk should not fail");
let pk = keygen_pk(params, vk.clone(), &empty_circuit)
.expect("delegation keygen_pk should not fail");
(pk, vk)
}
pub fn create_delegation_proof(circuit: Circuit, instance: &Instance) -> Vec<u8> {
let params = delegation_params();
let (pk, _vk) = delegation_proving_key(¶ms);
let public_inputs = instance.to_halo2_instance();
let mut transcript = Blake2bWrite::<_, EqAffine, Challenge255<_>>::init(vec![]);
create_proof(
¶ms,
&pk,
&[circuit],
&[&[&public_inputs]],
OsRng,
&mut transcript,
)
.expect("delegation proof generation should not fail");
transcript.finalize()
}
pub fn verify_delegation_proof(
proof: &[u8],
instance: &Instance,
) -> Result<(), String> {
let params = delegation_params();
let (_pk, vk) = delegation_proving_key(¶ms);
let public_inputs = instance.to_halo2_instance();
let strategy = SingleVerifier::new(¶ms);
let mut transcript = Blake2bRead::<_, EqAffine, Challenge255<_>>::init(proof);
verify_proof(¶ms, &vk, strategy, &[&[&public_inputs]], &mut transcript)
.map_err(|e| format!("delegation verification failed: {:?}", e))
}
pub fn verify_delegation_proof_raw(
proof: &[u8],
public_inputs_bytes: &[u8],
) -> Result<(), String> {
use pasta_curves::group::ff::PrimeField;
if public_inputs_bytes.len() != 14 * 32 {
return Err(format!(
"expected 448 bytes (14 × 32) for public inputs, got {}",
public_inputs_bytes.len()
));
}
let mut public_inputs: Vec<vesta::Scalar> = Vec::with_capacity(14);
for i in 0..14 {
let start = i * 32;
let mut repr = [0u8; 32];
repr.copy_from_slice(&public_inputs_bytes[start..start + 32]);
let fp_opt: Option<pallas::Base> = pallas::Base::from_repr(repr).into();
match fp_opt {
Some(f) => public_inputs.push(f),
None => {
return Err(format!(
"public input {} is not a canonical Pallas Fp encoding",
i
))
}
}
}
let params = delegation_params();
let (_pk, vk) = delegation_proving_key(¶ms);
let strategy = SingleVerifier::new(¶ms);
let mut transcript = Blake2bRead::<_, EqAffine, Challenge255<_>>::init(proof);
verify_proof(
¶ms,
&vk,
strategy,
&[&[&public_inputs]],
&mut transcript,
)
.map_err(|e| format!("delegation verification failed: {:?}", e))
}
#[cfg(test)]
mod prove_tests {
use super::*;
use crate::delegation::builder::{build_delegation_bundle, RealNoteInput};
use crate::delegation::imt::{ImtProvider, SpacedLeafImtProvider};
use orchard::{
keys::{FullViewingKey, Scope, SpendingKey},
note::{commitment::ExtractedNoteCommitment, Note, Rho},
tree::{MerkleHashOrchard, MerklePath},
value::NoteValue,
};
use ff::Field;
use incrementalmerkletree::{Hashable, Level};
use pasta_curves::pallas;
use rand::rngs::OsRng;
use crate::delegation::circuit::K;
#[test]
fn real_proof_roundtrip() {
let mut rng = OsRng;
let sk = SpendingKey::random(&mut rng);
let fvk: FullViewingKey = (&sk).into();
let output_recipient = fvk.address_at(1u32, Scope::External);
let vote_round_id = pallas::Base::random(&mut rng);
let van_comm_rand = pallas::Base::random(&mut rng);
let alpha = pallas::Scalar::random(&mut rng);
let recipient = fvk.address_at(0u32, Scope::External);
let (_, _, dummy) = Note::dummy(&mut rng, None);
let note = Note::new(recipient, NoteValue::from_raw(13_000_000), Rho::from_nf_old(dummy.nullifier(&fvk)), &mut rng);
let cmx = ExtractedNoteCommitment::from(note.commitment());
let leaf = MerkleHashOrchard::from_cmx(&cmx);
let empty = MerkleHashOrchard::empty_leaf();
let mut leaves = [empty; 2];
leaves[0] = leaf;
let l1 = MerkleHashOrchard::combine(Level::from(0), &leaves[0], &leaves[1]);
let mut current = l1;
for level in 1..32u8 {
current = MerkleHashOrchard::combine(Level::from(level), ¤t, &MerkleHashOrchard::empty_root(Level::from(level)));
}
let nc_root = current.inner();
let mut auth_path = [empty; 32];
auth_path[0] = leaves[1];
for level in 1..32u8 {
auth_path[level as usize] = MerkleHashOrchard::empty_root(Level::from(level));
}
let merkle_path = MerklePath::from_parts(0u32, auth_path);
let imt = SpacedLeafImtProvider::new();
let real_nf = note.nullifier(&fvk);
let imt_proof = imt.non_membership_proof(real_nf.0).unwrap();
let input = RealNoteInput { note, fvk: fvk.clone(), merkle_path, imt_proof, scope: Scope::External };
let bundle = build_delegation_bundle(
vec![input], &fvk, alpha, output_recipient, vote_round_id, nc_root, van_comm_rand, &imt, &mut rng, None,
).unwrap();
let proof = create_delegation_proof(bundle.circuit, &bundle.instance);
verify_delegation_proof(&proof, &bundle.instance).expect("real proof roundtrip failed");
}
}