/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2024 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #[cfg(feature = "tinyjson")] use { super::halo2::Value, darkfi_sdk::crypto::{pasta_prelude::*, util::FieldElemAsStr, MerkleNode}, std::{ collections::HashMap, fs::File, io::{Read, Write}, path::Path, }, tinyjson::JsonValue::{ self, Array as JsonArray, Number as JsonNum, Object as JsonObj, String as JsonStr, }, }; use darkfi_sdk::pasta::pallas; use log::error; use super::{Witness, ZkCircuit}; use crate::{zkas, Error, Result}; #[cfg(feature = "tinyjson")] /// Export witness.json which can be used by zkrunner for debugging circuits /// Note that this function makes liberal use of unwraps so it could panic. pub fn export_witness_json>( output_path: P, prover_witnesses: &Vec, public_inputs: &Vec, ) { let mut witnesses = Vec::new(); for witness in prover_witnesses { let mut value_json = HashMap::new(); match witness { Witness::Base(value) => { value.map(|w| { value_json.insert("Base".to_string(), JsonStr(w.to_string())); w }); } Witness::Scalar(value) => { value.map(|w| { value_json.insert("Scalar".to_string(), JsonStr(w.to_string())); w }); } Witness::Uint32(value) => { value.map(|w| { value_json.insert("Uint32".to_string(), JsonNum(w.into())); w }); } Witness::MerklePath(value) => { let mut path = Vec::new(); value.map(|w| { for node in w { path.push(JsonStr(node.inner().to_string())); } w }); value_json.insert("MerklePath".to_string(), JsonArray(path)); } Witness::SparseMerklePath(value) => { let mut path = Vec::new(); value.map(|w| { for node in w { path.push(JsonStr(node.to_string())); } w }); value_json.insert("SparseMerklePath".to_string(), JsonArray(path)); } Witness::EcNiPoint(value) => { let (mut x, mut y) = (pallas::Base::ZERO, pallas::Base::ZERO); value.map(|w| { let coords = w.to_affine().coordinates().unwrap(); (x, y) = (*coords.x(), *coords.y()); w }); let coords = vec![JsonStr(x.to_string()), JsonStr(y.to_string())]; value_json.insert("EcNiPoint".to_string(), JsonArray(coords)); } _ => unimplemented!(), } witnesses.push(JsonObj(value_json)); } let mut instances = Vec::new(); for instance in public_inputs { instances.push(JsonStr(instance.to_string())); } let witnesses_json = JsonArray(witnesses); let instances_json = JsonArray(instances); let witness_json = JsonObj(HashMap::from([ ("witnesses".to_string(), witnesses_json), ("instances".to_string(), instances_json), ])); // This is a debugging method. We don't care about .expect() crashing. let json = witness_json.format().expect("cannot create debug json"); let mut output = File::create(output_path).expect("cannot write file"); output.write_all(json.as_bytes()).expect("write failed"); } #[cfg(feature = "tinyjson")] /// Import witness.json which can be used to debug or benchmark circuits. /// Note that if the path or provided json is incorrect then this function will panic. pub fn import_witness_json>(input_path: P) -> (Vec, Vec) { let mut input = File::open(input_path).expect("could not open input file"); let mut json_str = String::new(); input.read_to_string(&mut json_str).expect("unable to read to string"); let json: JsonValue = json_str.parse().unwrap(); drop(input); drop(json_str); let root: &HashMap<_, _> = json.get().expect("root"); let json_witness: &Vec<_> = root["witnesses"].get().expect("witnesses"); let jval_as_fp = |j_val: &JsonValue| { let valstr: &String = j_val.get().expect("value str"); pallas::Base::from_str(valstr).unwrap() }; let jval_as_vecfp = |j_val: &JsonValue| { j_val .get::>() .expect("value str") .iter() .map(jval_as_fp) .collect::>() }; let mut witnesses = Vec::new(); for j_witness in json_witness { let item: &HashMap<_, _> = j_witness.get().expect("root"); assert_eq!(item.len(), 1); let (typename, j_val) = item.iter().next().expect("witness has single item"); match typename.as_str() { "Base" => { let fp = jval_as_fp(j_val); witnesses.push(Witness::Base(Value::known(fp))); } "Scalar" => { let valstr: &String = j_val.get().expect("value str"); let fq = pallas::Scalar::from_str(valstr).unwrap(); witnesses.push(Witness::Scalar(Value::known(fq))); } "Uint32" => { let val: &f64 = j_val.get().expect("value str"); witnesses.push(Witness::Uint32(Value::known(*val as u32))); } "MerklePath" => { let vals: Vec<_> = jval_as_vecfp(j_val).into_iter().map(MerkleNode::new).collect(); assert_eq!(vals.len(), 32); let vals: [MerkleNode; 32] = vals.try_into().unwrap(); witnesses.push(Witness::MerklePath(Value::known(vals))); } "SparseMerklePath" => { let vals = jval_as_vecfp(j_val); assert_eq!(vals.len(), 255); let vals: [pallas::Base; 255] = vals.try_into().unwrap(); witnesses.push(Witness::SparseMerklePath(Value::known(vals))); } "EcNiPoint" => { let vals = jval_as_vecfp(j_val); assert_eq!(vals.len(), 2); let (x, y) = (vals[0], vals[1]); let point: pallas::Point = pallas::Affine::from_xy(x, y).unwrap().to_curve(); witnesses.push(Witness::EcNiPoint(Value::known(point))); } _ => unimplemented!(), } } let instances = jval_as_vecfp(&root["instances"]); (witnesses, instances) } /// Call this before `Proof::create()` to perform type checks on the witnesses and check /// the amount of provided instances are correct. pub fn zkas_type_checks( circuit: &ZkCircuit, binary: &zkas::ZkBinary, instances: &[pallas::Base], ) -> Result<()> { if circuit.witnesses.len() != binary.witnesses.len() { error!( "Wrong number of witnesses. Should be {}, but instead got {}.", binary.witnesses.len(), circuit.witnesses.len() ); return Err(Error::WrongWitnessesCount) } for (i, (circuit_witness, binary_witness)) in circuit.witnesses.iter().zip(binary.witnesses.iter()).enumerate() { let is_pass = match circuit_witness { Witness::EcPoint(_) => *binary_witness == zkas::VarType::EcPoint, Witness::EcNiPoint(_) => *binary_witness == zkas::VarType::EcNiPoint, Witness::EcFixedPoint(_) => *binary_witness == zkas::VarType::EcFixedPoint, Witness::Base(_) => *binary_witness == zkas::VarType::Base, Witness::Scalar(_) => *binary_witness == zkas::VarType::Scalar, Witness::MerklePath(_) => *binary_witness == zkas::VarType::MerklePath, Witness::SparseMerklePath(_) => *binary_witness == zkas::VarType::SparseMerklePath, Witness::Uint32(_) => *binary_witness == zkas::VarType::Uint32, Witness::Uint64(_) => *binary_witness == zkas::VarType::Uint64, }; if !is_pass { error!( "Wrong witness type at index {}. Expected '{}', but instead got '{}'.", i, binary_witness.name(), circuit_witness.name() ); return Err(Error::WrongWitnessType(i)) } } // Count number of public instances let mut instances_count = 0; for opcode in &circuit.opcodes { if let (zkas::Opcode::ConstrainInstance, _) = opcode { instances_count += 1; } } if instances.len() != instances_count { error!( "Wrong number of public inputs. Should be {}, but instead got {}.", instances_count, instances.len() ); return Err(Error::WrongPublicInputsCount) } Ok(()) }