darkfi/zk/
debug.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#[cfg(feature = "tinyjson")]
20use {
21    super::halo2::Value,
22    darkfi_sdk::crypto::{pasta_prelude::*, util::FieldElemAsStr, MerkleNode},
23    std::{
24        collections::HashMap,
25        fs::File,
26        io::{Read, Write},
27        path::Path,
28    },
29    tinyjson::JsonValue::{
30        self, Array as JsonArray, Number as JsonNum, Object as JsonObj, String as JsonStr,
31    },
32};
33
34use darkfi_sdk::pasta::pallas;
35use tracing::error;
36
37use super::{Witness, ZkCircuit};
38use crate::{zkas, Error, Result};
39
40#[cfg(feature = "tinyjson")]
41/// Export witness.json which can be used by zkrunner for debugging circuits
42/// Note that this function makes liberal use of unwraps so it could panic.
43pub fn export_witness_json<P: AsRef<Path>>(
44    output_path: P,
45    prover_witnesses: &Vec<Witness>,
46    public_inputs: &Vec<pallas::Base>,
47) {
48    let mut witnesses = Vec::new();
49    for witness in prover_witnesses {
50        let mut value_json = HashMap::new();
51        match witness {
52            Witness::Base(value) => {
53                value.map(|w| {
54                    value_json.insert("Base".to_string(), JsonStr(w.to_string()));
55                    w
56                });
57            }
58            Witness::Scalar(value) => {
59                value.map(|w| {
60                    value_json.insert("Scalar".to_string(), JsonStr(w.to_string()));
61                    w
62                });
63            }
64            Witness::Uint32(value) => {
65                value.map(|w| {
66                    value_json.insert("Uint32".to_string(), JsonNum(w.into()));
67                    w
68                });
69            }
70            Witness::MerklePath(value) => {
71                let mut path = Vec::new();
72                value.map(|w| {
73                    for node in w {
74                        path.push(JsonStr(node.inner().to_string()));
75                    }
76                    w
77                });
78                value_json.insert("MerklePath".to_string(), JsonArray(path));
79            }
80            Witness::SparseMerklePath(value) => {
81                let mut path = Vec::new();
82                value.map(|w| {
83                    for node in w {
84                        path.push(JsonStr(node.to_string()));
85                    }
86                    w
87                });
88                value_json.insert("SparseMerklePath".to_string(), JsonArray(path));
89            }
90            Witness::EcNiPoint(value) => {
91                let (mut x, mut y) = (pallas::Base::ZERO, pallas::Base::ZERO);
92                value.map(|w| {
93                    let coords = w.to_affine().coordinates().unwrap();
94                    (x, y) = (*coords.x(), *coords.y());
95                    w
96                });
97                let coords = vec![JsonStr(x.to_string()), JsonStr(y.to_string())];
98                value_json.insert("EcNiPoint".to_string(), JsonArray(coords));
99            }
100            _ => unimplemented!(),
101        }
102        witnesses.push(JsonObj(value_json));
103    }
104
105    let mut instances = Vec::new();
106    for instance in public_inputs {
107        instances.push(JsonStr(instance.to_string()));
108    }
109
110    let witnesses_json = JsonArray(witnesses);
111    let instances_json = JsonArray(instances);
112    let witness_json = JsonObj(HashMap::from([
113        ("witnesses".to_string(), witnesses_json),
114        ("instances".to_string(), instances_json),
115    ]));
116    // This is a debugging method. We don't care about .expect() crashing.
117    let json = witness_json.format().expect("cannot create debug json");
118    let mut output = File::create(output_path).expect("cannot write file");
119    output.write_all(json.as_bytes()).expect("write failed");
120}
121
122#[cfg(feature = "tinyjson")]
123/// Import witness.json which can be used to debug or benchmark circuits.
124/// Note that if the path or provided json is incorrect then this function will panic.
125pub fn import_witness_json<P: AsRef<Path>>(input_path: P) -> (Vec<Witness>, Vec<pallas::Base>) {
126    let mut input = File::open(input_path).expect("could not open input file");
127    let mut json_str = String::new();
128    input.read_to_string(&mut json_str).expect("unable to read to string");
129    let json: JsonValue = json_str.parse().unwrap();
130    drop(input);
131    drop(json_str);
132
133    let root: &HashMap<_, _> = json.get().expect("root");
134    let json_witness: &Vec<_> = root["witnesses"].get().expect("witnesses");
135
136    let jval_as_fp = |j_val: &JsonValue| {
137        let valstr: &String = j_val.get().expect("value str");
138        pallas::Base::from_str(valstr).unwrap()
139    };
140
141    let jval_as_vecfp = |j_val: &JsonValue| {
142        j_val
143            .get::<Vec<_>>()
144            .expect("value str")
145            .iter()
146            .map(jval_as_fp)
147            .collect::<Vec<pallas::Base>>()
148    };
149
150    let mut witnesses = Vec::new();
151    for j_witness in json_witness {
152        let item: &HashMap<_, _> = j_witness.get().expect("root");
153        assert_eq!(item.len(), 1);
154        let (typename, j_val) = item.iter().next().expect("witness has single item");
155        match typename.as_str() {
156            "Base" => {
157                let fp = jval_as_fp(j_val);
158                witnesses.push(Witness::Base(Value::known(fp)));
159            }
160            "Scalar" => {
161                let valstr: &String = j_val.get().expect("value str");
162                let fq = pallas::Scalar::from_str(valstr).unwrap();
163                witnesses.push(Witness::Scalar(Value::known(fq)));
164            }
165            "Uint32" => {
166                let val: &f64 = j_val.get().expect("value str");
167                witnesses.push(Witness::Uint32(Value::known(*val as u32)));
168            }
169            "MerklePath" => {
170                let vals: Vec<_> = jval_as_vecfp(j_val).into_iter().map(MerkleNode::new).collect();
171                assert_eq!(vals.len(), 32);
172                let vals: [MerkleNode; 32] = vals.try_into().unwrap();
173                witnesses.push(Witness::MerklePath(Value::known(vals)));
174            }
175            "SparseMerklePath" => {
176                let vals = jval_as_vecfp(j_val);
177                assert_eq!(vals.len(), 255);
178                let vals: [pallas::Base; 255] = vals.try_into().unwrap();
179                witnesses.push(Witness::SparseMerklePath(Value::known(vals)));
180            }
181            "EcNiPoint" => {
182                let vals = jval_as_vecfp(j_val);
183                assert_eq!(vals.len(), 2);
184                let (x, y) = (vals[0], vals[1]);
185                let point: pallas::Point = pallas::Affine::from_xy(x, y).unwrap().to_curve();
186                witnesses.push(Witness::EcNiPoint(Value::known(point)));
187            }
188            _ => unimplemented!(),
189        }
190    }
191
192    let instances = jval_as_vecfp(&root["instances"]);
193
194    (witnesses, instances)
195}
196
197/// Call this before `Proof::create()` to perform type checks on the witnesses and check
198/// the amount of provided instances are correct.
199pub fn zkas_type_checks(
200    circuit: &ZkCircuit,
201    binary: &zkas::ZkBinary,
202    instances: &[pallas::Base],
203) -> Result<()> {
204    if circuit.witnesses.len() != binary.witnesses.len() {
205        error!(
206            "Wrong number of witnesses. Should be {}, but instead got {}.",
207            binary.witnesses.len(),
208            circuit.witnesses.len()
209        );
210        return Err(Error::WrongWitnessesCount)
211    }
212
213    for (i, (circuit_witness, binary_witness)) in
214        circuit.witnesses.iter().zip(binary.witnesses.iter()).enumerate()
215    {
216        let is_pass = match circuit_witness {
217            Witness::EcPoint(_) => *binary_witness == zkas::VarType::EcPoint,
218            Witness::EcNiPoint(_) => *binary_witness == zkas::VarType::EcNiPoint,
219            Witness::EcFixedPoint(_) => *binary_witness == zkas::VarType::EcFixedPoint,
220            Witness::Base(_) => *binary_witness == zkas::VarType::Base,
221            Witness::Scalar(_) => *binary_witness == zkas::VarType::Scalar,
222            Witness::MerklePath(_) => *binary_witness == zkas::VarType::MerklePath,
223            Witness::SparseMerklePath(_) => *binary_witness == zkas::VarType::SparseMerklePath,
224            Witness::Uint32(_) => *binary_witness == zkas::VarType::Uint32,
225            Witness::Uint64(_) => *binary_witness == zkas::VarType::Uint64,
226        };
227        if !is_pass {
228            error!(
229                "Wrong witness type at index {}. Expected '{}', but instead got '{}'.",
230                i,
231                binary_witness.name(),
232                circuit_witness.name()
233            );
234            return Err(Error::WrongWitnessType(i))
235        }
236    }
237
238    // Count number of public instances
239    let mut instances_count = 0;
240    for opcode in &circuit.opcodes {
241        if let (zkas::Opcode::ConstrainInstance, _) = opcode {
242            instances_count += 1;
243        }
244    }
245    if instances.len() != instances_count {
246        error!(
247            "Wrong number of public inputs. Should be {}, but instead got {}.",
248            instances_count,
249            instances.len()
250        );
251        return Err(Error::WrongPublicInputsCount)
252    }
253    Ok(())
254}