diff --git a/snark-verifier/src/loader/evm/loader.rs b/snark-verifier/src/loader/evm/loader.rs index 03e8fa8b..f6bf17b2 100644 --- a/snark-verifier/src/loader/evm/loader.rs +++ b/snark-verifier/src/loader/evm/loader.rs @@ -648,7 +648,9 @@ where self.ec_point(Value::Constant((x, y))) } - fn ec_point_assert_eq(&self, _: &str, _: &EcPoint, _: &EcPoint) {} + fn ec_point_assert_eq(&self, _: &str, _: &EcPoint, _: &EcPoint) { + unimplemented!() + } fn multi_scalar_multiplication( pairs: &[(&>::LoadedScalar, &EcPoint)], @@ -672,7 +674,9 @@ impl> ScalarLoader for Rc { self.scalar(Value::Constant(fe_to_u256(*value))) } - fn assert_eq(&self, _: &str, _: &Scalar, _: &Scalar) {} + fn assert_eq(&self, _: &str, _: &Scalar, _: &Scalar) { + unimplemented!() + } fn sum_with_coeff_and_const(&self, values: &[(F, &Scalar)], constant: F) -> Scalar { if values.is_empty() { diff --git a/snark-verifier/src/system/halo2.rs b/snark-verifier/src/system/halo2.rs index 561a1666..98f4488c 100644 --- a/snark-verifier/src/system/halo2.rs +++ b/snark-verifier/src/system/halo2.rs @@ -111,7 +111,7 @@ pub fn compile<'a, C: CurveAffine, P: Params<'a, C>>( .chain((0..num_proof).flat_map(move |t| polynomials.permutation_z_queries::(t))) .chain((0..num_proof).flat_map(move |t| polynomials.lookup_queries::(t))) .collect(); - + // `quotient_query()` is not needed in evaluations because the verifier can compute it itself from the other evaluations. let queries = (0..num_proof) .flat_map(|t| { iter::empty() diff --git a/snark-verifier/src/system/halo2/aggregation.rs b/snark-verifier/src/system/halo2/aggregation.rs deleted file mode 100644 index a3b09c15..00000000 --- a/snark-verifier/src/system/halo2/aggregation.rs +++ /dev/null @@ -1,718 +0,0 @@ -use super::{BITS, LIMBS}; -use crate::{ - loader::{self, native::NativeLoader, Loader}, - pcs::{ - kzg::{ - Bdfg21, Kzg, KzgAccumulator, KzgAs, KzgAsProvingKey, KzgAsVerifyingKey, - KzgSuccinctVerifyingKey, LimbsEncoding, - }, - AccumulationScheme, AccumulationSchemeProver, - }, - system::{ - self, - halo2::{ - compile, read_or_create_srs, transcript::halo2::ChallengeScalar, Config, - Halo2VerifierCircuitConfig, Halo2VerifierCircuitConfigParams, - }, - }, - util::arithmetic::fe_to_limbs, - verifier::{self, PlonkVerifier}, - Protocol, -}; -use ark_std::{end_timer, start_timer}; -use halo2_base::AssignedValue; -pub use halo2_base::{ - utils::{biguint_to_fe, fe_to_biguint}, - Context, ContextParams, -}; -use halo2_curves::bn256::{Bn256, Fr, G1Affine}; -use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner, Value}, - plonk::{ - self, create_proof, keygen_pk, keygen_vk, verify_proof, Circuit, ProvingKey, VerifyingKey, - }, - poly::{ - commitment::{Params, ParamsProver}, - kzg::{ - commitment::{KZGCommitmentScheme, ParamsKZG}, - multiopen::{ProverSHPLONK, VerifierSHPLONK}, - strategy::AccumulatorStrategy, - }, - VerificationStrategy, - }, - transcript::{TranscriptReadBuffer, TranscriptWriterBuffer}, -}; -use itertools::Itertools; -use num_bigint::BigUint; -use num_traits::Num; -use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; -use std::{ - fs::File, - io::{BufReader, BufWriter, Cursor, Read, Write}, - path::Path, - rc::Rc, -}; - -pub const T: usize = 3; -pub const RATE: usize = 2; -pub const R_F: usize = 8; -pub const R_P: usize = 57; - -pub type Halo2Loader<'a, 'b> = loader::halo2::Halo2Loader<'a, 'b, G1Affine>; -pub type PoseidonTranscript = - system::halo2::transcript::halo2::PoseidonTranscript; - -pub type Pcs = Kzg; -pub type Svk = KzgSuccinctVerifyingKey; -pub type As = KzgAs; -pub type AsPk = KzgAsProvingKey; -pub type AsVk = KzgAsVerifyingKey; -pub type Plonk = verifier::Plonk>; - -pub struct Snark { - protocol: Protocol, - instances: Vec>, - proof: Vec, -} - -impl Snark { - pub fn new(protocol: Protocol, instances: Vec>, proof: Vec) -> Self { - Self { protocol, instances, proof } - } - pub fn protocol(&self) -> &Protocol { - &self.protocol - } - pub fn instances(&self) -> &[Vec] { - &self.instances - } - pub fn proof(&self) -> &[u8] { - &self.proof - } -} - -impl From for SnarkWitness { - fn from(snark: Snark) -> Self { - Self { - protocol: snark.protocol, - instances: snark - .instances - .into_iter() - .map(|instances| instances.into_iter().map(Value::known).collect_vec()) - .collect(), - proof: Value::known(snark.proof), - } - } -} - -#[derive(Clone)] -pub struct SnarkWitness { - protocol: Protocol, - instances: Vec>>, - proof: Value>, -} - -impl SnarkWitness { - pub fn without_witnesses(&self) -> Self { - SnarkWitness { - protocol: self.protocol.clone(), - instances: self - .instances - .iter() - .map(|instances| vec![Value::unknown(); instances.len()]) - .collect(), - proof: Value::unknown(), - } - } - - pub fn protocol(&self) -> &Protocol { - &self.protocol - } - - pub fn instances(&self) -> &[Vec>] { - &self.instances - } - - pub fn proof(&self) -> Value<&[u8]> { - self.proof.as_ref().map(Vec::as_slice) - } -} - -pub fn aggregate<'a, 'b>( - svk: &Svk, - loader: &Rc>, - snarks: &[SnarkWitness], - as_vk: &AsVk, - as_proof: Value<&'_ [u8]>, - expose_instances: bool, -) -> Vec> { - let assign_instances = |instances: &[Vec>]| { - instances - .iter() - .map(|instances| { - instances.iter().map(|instance| loader.assign_scalar(*instance)).collect_vec() - }) - .collect_vec() - }; - - let mut instances_to_expose = vec![]; - let mut accumulators = snarks - .iter() - .flat_map(|snark| { - let instances = assign_instances(&snark.instances); - if expose_instances { - instances_to_expose.extend( - instances - .iter() - .flat_map(|instance| instance.iter().map(|scalar| scalar.assigned())), - ); - } - let mut transcript = - PoseidonTranscript::, _, _>::new(loader, snark.proof()); - let proof = - Plonk::read_proof(svk, &snark.protocol, &instances, &mut transcript).unwrap(); - Plonk::succinct_verify(svk, &snark.protocol, &instances, &proof).unwrap() - }) - .collect_vec(); - - let KzgAccumulator { lhs, rhs } = if accumulators.len() > 1 { - let mut transcript = PoseidonTranscript::, _, _>::new(loader, as_proof); - let proof = As::read_proof(as_vk, &accumulators, &mut transcript).unwrap(); - As::verify(as_vk, &accumulators, &proof).unwrap() - } else { - accumulators.pop().unwrap() - }; - - let lhs = lhs.assigned(); - let rhs = rhs.assigned(); - - lhs.x - .truncation - .limbs - .iter() - .chain(lhs.y.truncation.limbs.iter()) - .chain(rhs.x.truncation.limbs.iter()) - .chain(rhs.y.truncation.limbs.iter()) - .chain(instances_to_expose.iter()) - .cloned() - .collect_vec() -} - -pub fn recursive_aggregate<'a, 'b>( - svk: &Svk, - loader: &Rc>, - snarks: &[SnarkWitness], - recursive_snark: &SnarkWitness, - as_vk: &AsVk, - as_proof: Value<&'_ [u8]>, - use_dummy: AssignedValue, -) -> (Vec>, Vec>>) { - let assign_instances = |instances: &[Vec>]| { - instances - .iter() - .map(|instances| { - instances.iter().map(|instance| loader.assign_scalar(*instance)).collect_vec() - }) - .collect_vec() - }; - - let mut assigned_instances = vec![]; - let mut accumulators = snarks - .iter() - .flat_map(|snark| { - let instances = assign_instances(&snark.instances); - assigned_instances.push( - instances - .iter() - .flat_map(|instance| instance.iter().map(|scalar| scalar.assigned())) - .collect_vec(), - ); - let mut transcript = - PoseidonTranscript::, _, _>::new(loader, snark.proof()); - let proof = - Plonk::read_proof(svk, &snark.protocol, &instances, &mut transcript).unwrap(); - Plonk::succinct_verify(svk, &snark.protocol, &instances, &proof).unwrap() - }) - .collect_vec(); - - let use_dummy = loader.scalar_from_assigned(use_dummy); - - let prev_instances = assign_instances(&recursive_snark.instances); - let mut accs = { - let mut transcript = - PoseidonTranscript::, _, _>::new(loader, recursive_snark.proof()); - let proof = - Plonk::read_proof(svk, &recursive_snark.protocol, &prev_instances, &mut transcript) - .unwrap(); - let mut accs = Plonk::succinct_verify_or_dummy( - svk, - &recursive_snark.protocol, - &prev_instances, - &proof, - &use_dummy, - ) - .unwrap(); - for acc in accs.iter_mut() { - (*acc).lhs = - loader.ec_point_select(&accumulators[0].lhs, &acc.lhs, &use_dummy).unwrap(); - (*acc).rhs = - loader.ec_point_select(&accumulators[0].rhs, &acc.rhs, &use_dummy).unwrap(); - } - accs - }; - accumulators.append(&mut accs); - - let KzgAccumulator { lhs, rhs } = { - let mut transcript = PoseidonTranscript::, _, _>::new(loader, as_proof); - let proof = As::read_proof(as_vk, &accumulators, &mut transcript).unwrap(); - As::verify(as_vk, &accumulators, &proof).unwrap() - }; - - let lhs = lhs.assigned(); - let rhs = rhs.assigned(); - - let mut new_instances = prev_instances - .iter() - .flat_map(|instance| instance.iter().map(|scalar| scalar.assigned())) - .collect_vec(); - for (i, acc_limb) in lhs - .x - .truncation - .limbs - .iter() - .chain(lhs.y.truncation.limbs.iter()) - .chain(rhs.x.truncation.limbs.iter()) - .chain(rhs.y.truncation.limbs.iter()) - .enumerate() - { - new_instances[i] = acc_limb.clone(); - } - (new_instances, assigned_instances) -} - -#[derive(Clone)] -pub struct AggregationCircuit { - svk: Svk, - snarks: Vec, - pub instances: Vec, - as_vk: AsVk, - as_proof: Value>, - expose_target_instances: bool, -} - -impl AggregationCircuit { - pub fn new( - params: &ParamsKZG, - snarks: impl IntoIterator, - expose_target_instances: bool, - ) -> Self { - let svk = params.get_g()[0].into(); - let snarks = snarks.into_iter().collect_vec(); - - let mut accumulators = snarks - .iter() - .flat_map(|snark| { - let mut transcript = - PoseidonTranscript::::new(snark.proof.as_slice()); - let proof = - Plonk::read_proof(&svk, &snark.protocol, &snark.instances, &mut transcript) - .unwrap(); - Plonk::succinct_verify(&svk, &snark.protocol, &snark.instances, &proof).unwrap() - }) - .collect_vec(); - - let as_pk = AsPk::new(Some((params.get_g()[0], params.get_g()[1]))); - let (accumulator, as_proof) = if accumulators.len() > 1 { - let mut transcript = PoseidonTranscript::::new(Vec::new()); - let accumulator = As::create_proof( - &as_pk, - &accumulators, - &mut transcript, - ChaCha20Rng::from_seed(Default::default()), - ) - .unwrap(); - (accumulator, Value::known(transcript.finalize())) - } else { - (accumulators.pop().unwrap(), Value::unknown()) - }; - - let KzgAccumulator { lhs, rhs } = accumulator; - let mut instances = - [lhs.x, lhs.y, rhs.x, rhs.y].map(fe_to_limbs::<_, _, LIMBS, BITS>).concat(); - if expose_target_instances { - instances.extend(snarks.iter().flat_map(|snark| snark.instances.iter().flatten())); - } - - Self { - svk, - snarks: snarks.into_iter().map_into().collect(), - instances, - as_vk: as_pk.vk(), - as_proof, - expose_target_instances, - } - } - - pub fn accumulator_indices() -> Vec<(usize, usize)> { - (0..4 * LIMBS).map(|idx| (0, idx)).collect() - } - - pub fn num_instance(&self) -> Vec { - dbg!(self.instances.len()); - vec![self.instances.len()] - } - - pub fn instances(&self) -> Vec> { - vec![self.instances.clone()] - } - - pub fn as_proof(&self) -> Value<&[u8]> { - self.as_proof.as_ref().map(Vec::as_slice) - } - - pub fn synthesize_proof( - &self, - config: Halo2VerifierCircuitConfig, - layouter: &mut impl Layouter, - instance_equalities: Vec<(usize, usize)>, - ) -> Result>, plonk::Error> { - config.base_field_config.load_lookup_table(layouter)?; - - // Need to trick layouter to skip first pass in get shape mode - let using_simple_floor_planner = true; - let mut first_pass = true; - let mut assigned_instances = None; - layouter.assign_region( - || "", - |region| { - if using_simple_floor_planner && first_pass { - first_pass = false; - return Ok(()); - } - let ctx = config.base_field_config.new_context(region); - - let loader = Halo2Loader::new(&config.base_field_config, ctx); - let instances = aggregate( - &self.svk, - &loader, - &self.snarks, - &self.as_vk, - self.as_proof(), - self.expose_target_instances, - ); - - for &(i, j) in &instance_equalities { - loader - .ctx_mut() - .region - .constrain_equal(instances[i].cell(), instances[j].cell())?; - } - // REQUIRED STEP - loader.finalize(); - assigned_instances = Some(instances); - Ok(()) - }, - )?; - Ok(assigned_instances.unwrap()) - } -} - -impl Circuit for AggregationCircuit { - type Config = Halo2VerifierCircuitConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self { - svk: self.svk, - snarks: self.snarks.iter().map(SnarkWitness::without_witnesses).collect(), - instances: Vec::new(), - as_vk: self.as_vk, - as_proof: Value::unknown(), - expose_target_instances: self.expose_target_instances, - } - } - - fn configure(meta: &mut plonk::ConstraintSystem) -> Self::Config { - let path = std::env::var("VERIFY_CONFIG").expect("export VERIFY_CONFIG with config path"); - let params: Halo2VerifierCircuitConfigParams = serde_json::from_reader( - File::open(path.as_str()).expect(format!("{} file should exist", path).as_str()), - ) - .unwrap(); - - Halo2VerifierCircuitConfig::configure(meta, params) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), plonk::Error> { - let config_instance = config.instance.clone(); - let assigned_instances = self.synthesize_proof(config, &mut layouter, vec![])?; - Ok({ - // TODO: use less instances by following Scroll's strategy of keeping only last bit of y coordinate - let mut layouter = layouter.namespace(|| "expose"); - for (i, assigned_instance) in assigned_instances.iter().enumerate() { - layouter.constrain_instance( - assigned_instance.cell().clone(), - config_instance, - i, - )?; - } - }) - } -} - -pub fn gen_srs(k: u32) -> ParamsKZG { - read_or_create_srs::(k, |k| { - ParamsKZG::::setup(k, ChaCha20Rng::from_seed(Default::default())) - }) -} - -pub fn gen_vk>( - params: &ParamsKZG, - circuit: &ConcreteCircuit, - name: &str, -) -> VerifyingKey { - let path = format!("./data/{}_{}.vkey", name, params.k()); - #[cfg(feature = "serialize")] - match File::open(path.as_str()) { - Ok(f) => { - let read_time = start_timer!(|| format!("Reading vkey from {}", path)); - let mut bufreader = BufReader::new(f); - let vk = VerifyingKey::read::<_, ConcreteCircuit>(&mut bufreader, params) - .expect("Reading vkey should not fail"); - end_timer!(read_time); - vk - } - Err(_) => { - let vk_time = start_timer!(|| "vkey"); - let vk = keygen_vk(params, circuit).unwrap(); - end_timer!(vk_time); - let mut f = BufWriter::new(File::create(path.as_str()).unwrap()); - println!("Writing vkey to {}", path); - vk.write(&mut f).unwrap(); - vk - } - } - #[cfg(not(feature = "serialize"))] - { - let vk_time = start_timer!(|| "vkey"); - let vk = keygen_vk(params, circuit).unwrap(); - end_timer!(vk_time); - vk - } -} - -pub fn gen_pk>( - params: &ParamsKZG, - circuit: &ConcreteCircuit, - name: &str, -) -> ProvingKey { - let path = format!("./data/{}_{}.pkey", name, params.k()); - #[cfg(feature = "serialize")] - match File::open(path.as_str()) { - Ok(f) => { - let read_time = start_timer!(|| format!("Reading pkey from {}", path)); - let mut bufreader = BufReader::new(f); - let pk = ProvingKey::read::<_, ConcreteCircuit>(&mut bufreader, params) - .expect("Reading pkey should not fail"); - end_timer!(read_time); - pk - } - Err(_) => { - let vk = gen_vk::(params, circuit, name); - let pk_time = start_timer!(|| "pkey"); - let pk = keygen_pk(params, vk, circuit).unwrap(); - end_timer!(pk_time); - let mut f = BufWriter::new(File::create(path.as_str()).unwrap()); - println!("Writing pkey to {}", path); - pk.write(&mut f).unwrap(); - pk - } - } - #[cfg(not(feature = "serialize"))] - { - let vk = gen_vk::(params, circuit, name); - let pk_time = start_timer!(|| "pkey"); - let pk = keygen_pk(params, vk, circuit).unwrap(); - end_timer!(pk_time); - pk - } -} - -pub fn read_bytes(path: &str) -> Vec { - let mut buf = vec![]; - let mut f = File::open(path).unwrap(); - f.read_to_end(&mut buf).unwrap(); - buf -} - -pub fn write_bytes(path: &str, buf: &Vec) { - let mut f = File::create(path).unwrap(); - f.write(buf).unwrap(); -} - -/// reads the instances for T::N_PROOFS circuits from file -pub fn read_instances(path: &str) -> Option>>> { - let f = File::open(path); - if let Err(_) = f { - return None; - } - let f = f.unwrap(); - let reader = BufReader::new(f); - let instances_str: Vec>> = serde_json::from_reader(reader).unwrap(); - let ret = instances_str - .into_iter() - .map(|circuit_instances| { - circuit_instances - .into_iter() - .map(|instance_column| { - instance_column - .iter() - .map(|str| { - biguint_to_fe::(&BigUint::from_str_radix(str.as_str(), 16).unwrap()) - }) - .collect_vec() - }) - .collect_vec() - }) - .collect_vec(); - Some(ret) -} - -pub fn write_instances(instances: &Vec>>, path: &str) { - let mut hex_strings = vec![]; - for circuit_instances in instances.iter() { - hex_strings.push( - circuit_instances - .iter() - .map(|instance_column| { - instance_column.iter().map(|x| fe_to_biguint(x).to_str_radix(16)).collect_vec() - }) - .collect_vec(), - ); - } - let f = BufWriter::new(File::create(path).unwrap()); - serde_json::to_writer(f, &hex_strings).unwrap(); -} - -pub trait TargetCircuit { - const N_PROOFS: usize; - - type Circuit: Circuit; - - fn name() -> String; -} - -// this is a toggle that should match the fork of halo2_proofs you are using -// the current default in PSE/main is `false`, before 2022_10_22 it was `true`: -// see https://github.com/privacy-scaling-explorations/halo2/pull/96/files -pub const KZG_QUERY_INSTANCE: bool = false; - -pub fn create_snark_shplonk( - params: &ParamsKZG, - circuits: Vec, - instances: Vec>>, // instances[i][j][..] is the i-th circuit's j-th instance column - accumulator_indices: Option>, -) -> Snark { - println!("CREATING SNARK FOR: {}", T::name()); - let config = if let Some(accumulator_indices) = accumulator_indices { - Config::kzg(KZG_QUERY_INSTANCE) - .set_zk(true) - .with_num_proof(T::N_PROOFS) - .with_accumulator_indices(accumulator_indices) - } else { - Config::kzg(KZG_QUERY_INSTANCE).set_zk(true).with_num_proof(T::N_PROOFS) - }; - - let pk = gen_pk(params, &circuits[0], T::name().as_str()); - // num_instance[i] is length of the i-th instance columns in circuit 0 (all circuits should have same shape of instances) - let num_instance = instances[0].iter().map(|instance_column| instance_column.len()).collect(); - let protocol = compile(params, pk.get_vk(), config.with_num_instance(num_instance)); - - // usual shenanigans to turn nested Vec into nested slice - let instances1: Vec> = instances - .iter() - .map(|instances| instances.iter().map(Vec::as_slice).collect_vec()) - .collect_vec(); - let instances2: Vec<&[&[Fr]]> = instances1.iter().map(Vec::as_slice).collect_vec(); - // TODO: need to cache the instances as well! - - let proof = { - let path = format!("./data/proof_{}_{}.dat", T::name(), params.k()); - let instance_path = format!("./data/instances_{}_{}.dat", T::name(), params.k()); - let cached_instances = read_instances::(instance_path.as_str()); - #[cfg(feature = "serialize")] - if cached_instances.is_some() - && Path::new(path.as_str()).exists() - && cached_instances.unwrap() == instances - { - let proof_time = start_timer!(|| "read proof"); - let mut file = File::open(path.as_str()).unwrap(); - let mut buf = vec![]; - file.read_to_end(&mut buf).unwrap(); - end_timer!(proof_time); - buf - } else { - let proof_time = start_timer!(|| "create proof"); - let mut transcript = PoseidonTranscript::, _>::init(Vec::new()); - create_proof::, ProverSHPLONK<_>, ChallengeScalar<_>, _, _, _>( - params, - &pk, - &circuits, - instances2.as_slice(), - &mut ChaCha20Rng::from_seed(Default::default()), - &mut transcript, - ) - .unwrap(); - let proof = transcript.finalize(); - let mut file = File::create(path.as_str()).unwrap(); - file.write_all(&proof).unwrap(); - write_instances(&instances, instance_path.as_str()); - end_timer!(proof_time); - proof - } - #[cfg(not(feature = "serialize"))] - { - let proof_time = start_timer!(|| "create proof"); - let mut transcript = PoseidonTranscript::, _>::init(Vec::new()); - create_proof::, ProverSHPLONK<_>, ChallengeScalar<_>, _, _, _>( - params, - &pk, - &circuits, - instances2.as_slice(), - &mut ChaCha20Rng::from_seed(Default::default()), - &mut transcript, - ) - .unwrap(); - let proof = transcript.finalize(); - end_timer!(proof_time); - proof - } - }; - - let verify_time = start_timer!(|| "verify proof"); - { - let verifier_params = params.verifier_params(); - let strategy = AccumulatorStrategy::new(verifier_params); - let mut transcript = - >, _> as TranscriptReadBuffer< - _, - _, - _, - >>::init(Cursor::new(proof.clone())); - assert!(VerificationStrategy::<_, VerifierSHPLONK<_>>::finalize( - verify_proof::<_, VerifierSHPLONK<_>, _, _, _>( - verifier_params, - pk.get_vk(), - strategy, - instances2.as_slice(), - &mut transcript, - ) - .unwrap() - )) - } - end_timer!(verify_time); - - Snark::new(protocol.clone(), instances.into_iter().flatten().collect_vec(), proof) -} diff --git a/snark-verifier/src/system/halo2/transcript/evm.rs b/snark-verifier/src/system/halo2/transcript/evm.rs index 5ea57189..c71c9e79 100644 --- a/snark-verifier/src/system/halo2/transcript/evm.rs +++ b/snark-verifier/src/system/halo2/transcript/evm.rs @@ -73,6 +73,8 @@ where &self.loader } + /// Does not allow the input to be a one-byte sequence, because the Transcript trait only supports writing scalars and elliptic curve points. + /// If the one-byte sequence [0x01] is a valid input to the transcript, the empty input [] will have the same transcript result as [0x01]. fn squeeze_challenge(&mut self) -> Scalar { let len = if self.buf.len() == 0x20 { assert_eq!(self.loader.ptr(), self.buf.end());