1use std::collections::HashSet;
20
21use darkfi_sdk::crypto::{
22 constants::{
23 sinsemilla::{OrchardCommitDomains, OrchardHashDomains, K},
24 util::gen_const_array,
25 ConstBaseFieldElement, OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV,
26 MERKLE_DEPTH_ORCHARD,
27 },
28 smt::SMT_FP_DEPTH,
29};
30use halo2_gadgets::{
31 ecc::{
32 chip::{EccChip, EccConfig},
33 FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point, ScalarFixed,
34 ScalarFixedShort, ScalarVar,
35 },
36 poseidon::{
37 primitives as poseidon, Hash as PoseidonHash, Pow5Chip as PoseidonChip,
38 Pow5Config as PoseidonConfig,
39 },
40 sinsemilla::{
41 chip::{SinsemillaChip, SinsemillaConfig},
42 merkle::{
43 chip::{MerkleChip, MerkleConfig},
44 MerklePath,
45 },
46 },
47 utilities::lookup_range_check::{LookupRangeCheck, LookupRangeCheckConfig},
48};
49use halo2_proofs::{
50 arithmetic::Field,
51 circuit::{floor_planner, AssignedCell, Layouter, Value},
52 pasta::{group::Curve, pallas, Fp},
53 plonk,
54 plonk::{Advice, Circuit, Column, ConstraintSystem, Instance as InstanceColumn},
55};
56use tracing::{error, trace};
57
58pub use super::vm_heap::{HeapVar, Witness};
59use super::{
60 assign_free_advice,
61 gadget::{
62 arithmetic::{ArithChip, ArithConfig, ArithInstruction},
63 cond_select::{ConditionalSelectChip, ConditionalSelectConfig},
64 less_than::{LessThanChip, LessThanConfig},
65 native_range_check::{NativeRangeCheckChip, NativeRangeCheckConfig},
66 small_range_check::{SmallRangeCheckChip, SmallRangeCheckConfig},
67 smt,
68 zero_cond::{ZeroCondChip, ZeroCondConfig},
69 },
70 tracer::ZkTracer,
71};
72use crate::zkas::{
73 types::{HeapType, LitType},
74 Opcode, ZkBinary,
75};
76
77#[derive(Debug, Clone)]
79#[allow(clippy::large_enum_variant)]
80enum VmChip {
81 Ecc(EccConfig<OrchardFixedBases>),
83
84 Merkle(
86 (
87 MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
88 MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
89 ),
90 ),
91
92 SparseTree(smt::PathConfig),
94
95 Sinsemilla(
97 (
98 SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
99 SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
100 ),
101 ),
102
103 Poseidon(PoseidonConfig<pallas::Base, 3, 2>),
105
106 Arithmetic(ArithConfig),
108
109 NativeRange64(NativeRangeCheckConfig<K, 64>),
111
112 NativeRange253(NativeRangeCheckConfig<K, 253>),
114
115 LessThan(LessThanConfig<K, 253>),
117
118 BoolCheck(SmallRangeCheckConfig),
120
121 CondSelect(ConditionalSelectConfig<pallas::Base>),
123
124 ZeroCond(ZeroCondConfig<pallas::Base>),
126}
127
128#[derive(Clone)]
130pub struct VmConfig {
131 chips: Vec<VmChip>,
133 primary: Column<InstanceColumn>,
135 witness: Column<Advice>,
137}
138
139impl VmConfig {
140 fn ecc_chip(&self) -> Option<EccChip<OrchardFixedBases>> {
141 let Some(VmChip::Ecc(ecc_config)) =
142 self.chips.iter().find(|&c| matches!(c, VmChip::Ecc(_)))
143 else {
144 return None
145 };
146
147 Some(EccChip::construct(ecc_config.clone()))
148 }
149
150 fn merkle_chip_1(
151 &self,
152 ) -> Option<MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>> {
153 let Some(VmChip::Merkle((merkle_cfg1, _))) =
154 self.chips.iter().find(|&c| matches!(c, VmChip::Merkle(_)))
155 else {
156 return None
157 };
158
159 Some(MerkleChip::construct(merkle_cfg1.clone()))
160 }
161
162 fn merkle_chip_2(
163 &self,
164 ) -> Option<MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>> {
165 let Some(VmChip::Merkle((_, merkle_cfg2))) =
166 self.chips.iter().find(|&c| matches!(c, VmChip::Merkle(_)))
167 else {
168 return None
169 };
170
171 Some(MerkleChip::construct(merkle_cfg2.clone()))
172 }
173
174 fn smt_chip(&self) -> Option<smt::PathChip> {
175 let Some(VmChip::SparseTree(config)) =
176 self.chips.iter().find(|&c| matches!(c, VmChip::SparseTree(_)))
177 else {
178 return None
179 };
180
181 Some(smt::PathChip::construct(config.clone()))
182 }
183
184 fn poseidon_chip(&self) -> Option<PoseidonChip<pallas::Base, 3, 2>> {
185 let Some(VmChip::Poseidon(poseidon_config)) =
186 self.chips.iter().find(|&c| matches!(c, VmChip::Poseidon(_)))
187 else {
188 return None
189 };
190
191 Some(PoseidonChip::construct(poseidon_config.clone()))
192 }
193
194 fn arithmetic_chip(&self) -> Option<ArithChip<pallas::Base>> {
195 let Some(VmChip::Arithmetic(arith_config)) =
196 self.chips.iter().find(|&c| matches!(c, VmChip::Arithmetic(_)))
197 else {
198 return None
199 };
200
201 Some(ArithChip::construct(arith_config.clone()))
202 }
203
204 fn condselect_chip(&self) -> Option<ConditionalSelectChip<pallas::Base>> {
205 let Some(VmChip::CondSelect(condselect_config)) =
206 self.chips.iter().find(|&c| matches!(c, VmChip::CondSelect(_)))
207 else {
208 return None
209 };
210
211 Some(ConditionalSelectChip::construct(condselect_config.clone()))
212 }
213
214 fn zerocond_chip(&self) -> Option<ZeroCondChip<pallas::Base>> {
215 let Some(VmChip::ZeroCond(zerocond_config)) =
216 self.chips.iter().find(|&c| matches!(c, VmChip::ZeroCond(_)))
217 else {
218 return None
219 };
220
221 Some(ZeroCondChip::construct(zerocond_config.clone()))
222 }
223
224 fn rangecheck64_chip(&self) -> Option<NativeRangeCheckChip<K, 64>> {
225 let Some(VmChip::NativeRange64(range_config)) =
226 self.chips.iter().find(|&c| matches!(c, VmChip::NativeRange64(_)))
227 else {
228 return None
229 };
230
231 Some(NativeRangeCheckChip::construct(range_config.clone()))
232 }
233
234 fn rangecheck253_chip(&self) -> Option<NativeRangeCheckChip<K, 253>> {
235 let Some(VmChip::NativeRange253(range_config)) =
236 self.chips.iter().find(|&c| matches!(c, VmChip::NativeRange253(_)))
237 else {
238 return None
239 };
240
241 Some(NativeRangeCheckChip::construct(range_config.clone()))
242 }
243
244 fn lessthan_chip(&self) -> Option<LessThanChip<K, 253>> {
245 let Some(VmChip::LessThan(lessthan_config)) =
246 self.chips.iter().find(|&c| matches!(c, VmChip::LessThan(_)))
247 else {
248 return None
249 };
250
251 Some(LessThanChip::construct(lessthan_config.clone()))
252 }
253
254 fn boolcheck_chip(&self) -> Option<SmallRangeCheckChip<pallas::Base>> {
255 let Some(VmChip::BoolCheck(boolcheck_config)) =
256 self.chips.iter().find(|&c| matches!(c, VmChip::BoolCheck(_)))
257 else {
258 return None
259 };
260
261 Some(SmallRangeCheckChip::construct(boolcheck_config.clone()))
262 }
263}
264
265#[derive(Default)]
268#[allow(dead_code)]
269pub struct ZkParams {
270 init_ecc: bool,
271 init_poseidon: bool,
272 init_sinsemilla: bool,
273 init_arithmetic: bool,
274 init_nativerange: bool,
275 init_lessthan: bool,
276 init_boolcheck: bool,
277 init_condselect: bool,
278 init_zerocond: bool,
279}
280
281#[derive(Clone, Debug)]
282pub struct ZkCircuit {
283 constants: Vec<String>,
284 pub(super) witnesses: Vec<Witness>,
285 literals: Vec<(LitType, String)>,
286 pub(super) opcodes: Vec<(Opcode, Vec<(HeapType, usize)>)>,
287 pub tracer: ZkTracer,
288}
289
290impl ZkCircuit {
291 pub fn new(witnesses: Vec<Witness>, circuit_code: &ZkBinary) -> Self {
292 let constants = circuit_code.constants.iter().map(|x| x.1.clone()).collect();
293 let literals = circuit_code.literals.clone();
294 Self {
295 constants,
296 witnesses,
297 literals,
298 opcodes: circuit_code.opcodes.clone(),
299 tracer: ZkTracer::new(true),
300 }
301 }
302
303 pub fn enable_trace(&mut self) {
304 self.tracer.init();
305 }
306}
307
308impl Circuit<pallas::Base> for ZkCircuit {
309 type Config = VmConfig;
310 type FloorPlanner = floor_planner::V1;
311 type Params = ZkParams;
312
313 fn without_witnesses(&self) -> Self {
314 Self {
315 constants: self.constants.clone(),
316 witnesses: self.witnesses.clone(),
317 literals: self.literals.clone(),
318 opcodes: self.opcodes.clone(),
319 tracer: ZkTracer::new(false),
320 }
321 }
322
323 fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
324 Self::configure_with_params(meta, ZkParams::default())
325 }
326
327 fn params(&self) -> Self::Params {
328 let mut opcodes = HashSet::new();
330 for (opcode, _) in &self.opcodes {
331 opcodes.insert(opcode);
332 }
333
334 let init_ecc = !self.constants.is_empty() ||
336 opcodes.contains(&Opcode::EcAdd) ||
337 opcodes.contains(&Opcode::EcMul) ||
338 opcodes.contains(&Opcode::EcMulBase) ||
339 opcodes.contains(&Opcode::EcMulShort) ||
340 opcodes.contains(&Opcode::EcMulVarBase) ||
341 opcodes.contains(&Opcode::EcGetX) ||
342 opcodes.contains(&Opcode::EcGetY) ||
343 opcodes.contains(&Opcode::ConstrainEqualPoint) ||
344 self.witnesses.iter().any(|x| {
345 matches!(x, Witness::EcPoint(_)) ||
346 matches!(x, Witness::EcNiPoint(_)) ||
347 matches!(x, Witness::EcFixedPoint(_)) ||
348 matches!(x, Witness::Scalar(_))
349 });
350
351 let init_poseidon = opcodes.contains(&Opcode::PoseidonHash);
353
354 let init_sinsemilla = opcodes.contains(&Opcode::MerkleRoot);
356
357 let init_arithmetic = opcodes.contains(&Opcode::BaseAdd) ||
359 opcodes.contains(&Opcode::BaseSub) ||
360 opcodes.contains(&Opcode::BaseMul);
361
362 let init_nativerange = opcodes.contains(&Opcode::RangeCheck) ||
365 opcodes.contains(&Opcode::LessThanLoose) ||
366 opcodes.contains(&Opcode::LessThanStrict);
367
368 let init_lessthan =
370 opcodes.contains(&Opcode::LessThanLoose) || opcodes.contains(&Opcode::LessThanStrict);
371
372 let init_boolcheck = opcodes.contains(&Opcode::BoolCheck);
374
375 let init_condselect = opcodes.contains(&Opcode::CondSelect);
377
378 let init_zerocond = opcodes.contains(&Opcode::ZeroCondSelect);
380
381 ZkParams {
382 init_ecc,
383 init_poseidon,
384 init_sinsemilla,
385 init_arithmetic,
386 init_nativerange,
387 init_lessthan,
388 init_boolcheck,
389 init_condselect,
390 init_zerocond,
391 }
392 }
393
394 fn configure_with_params(
395 meta: &mut ConstraintSystem<pallas::Base>,
396 _params: Self::Params,
397 ) -> Self::Config {
398 let mut advices = vec![];
400 for _ in 0..10 {
401 advices.push(meta.advice_column());
402 }
403
404 let primary = meta.instance_column();
406 meta.enable_equality(primary);
407
408 for advice in advices.iter() {
410 meta.enable_equality(*advice);
411 }
412
413 let table_idx = meta.lookup_table_column();
415 let lookup = (table_idx, meta.lookup_table_column(), meta.lookup_table_column());
416
417 let lagrange_coeffs = [
423 meta.fixed_column(),
424 meta.fixed_column(),
425 meta.fixed_column(),
426 meta.fixed_column(),
427 meta.fixed_column(),
428 meta.fixed_column(),
429 meta.fixed_column(),
430 meta.fixed_column(),
431 ];
432 let rc_a = lagrange_coeffs[2..5].try_into().unwrap();
433 let rc_b = lagrange_coeffs[5..8].try_into().unwrap();
434
435 meta.enable_constant(lagrange_coeffs[0]);
437
438 let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
440
441 let ecc_config = EccChip::<OrchardFixedBases>::configure(
444 meta,
445 advices[0..10].try_into().unwrap(),
446 lagrange_coeffs,
447 range_check,
448 );
449
450 let poseidon_config = PoseidonChip::configure::<poseidon::P128Pow5T3>(
452 meta,
453 advices[6..9].try_into().unwrap(),
454 advices[5],
455 rc_a,
456 rc_b,
457 );
458
459 let arith_config = ArithChip::configure(meta, advices[7], advices[8], advices[6]);
461
462 let (sinsemilla_cfg1, merkle_cfg1) = {
467 let sinsemilla_cfg1 = SinsemillaChip::configure(
468 meta,
469 advices[..5].try_into().unwrap(),
470 advices[6],
471 lagrange_coeffs[0],
472 lookup,
473 range_check,
474 false, );
476 let merkle_cfg1 = MerkleChip::configure(meta, sinsemilla_cfg1.clone());
477 (sinsemilla_cfg1, merkle_cfg1)
478 };
479
480 let (sinsemilla_cfg2, merkle_cfg2) = {
481 let sinsemilla_cfg2 = SinsemillaChip::configure(
482 meta,
483 advices[5..].try_into().unwrap(),
484 advices[7],
485 lagrange_coeffs[1],
486 lookup,
487 range_check,
488 false, );
490 let merkle_cfg2 = MerkleChip::configure(meta, sinsemilla_cfg2.clone());
491 (sinsemilla_cfg2, merkle_cfg2)
492 };
493
494 let smt_config = smt::PathChip::configure(
495 meta,
496 advices[0..2].try_into().unwrap(),
497 advices[2..6].try_into().unwrap(),
498 poseidon_config.clone(),
499 );
500
501 let native_64_range_check_config =
503 NativeRangeCheckChip::<K, 64>::configure(meta, advices[8], table_idx);
504
505 let native_253_range_check_config =
507 NativeRangeCheckChip::<K, 253>::configure(meta, advices[8], table_idx);
508
509 let z1 = meta.advice_column();
511 let z2 = meta.advice_column();
512
513 let lessthan_config = LessThanChip::<K, 253>::configure(
514 meta, advices[6], advices[7], advices[8], z1, z2, table_idx,
515 );
516
517 let boolcheck_config = SmallRangeCheckChip::configure(meta, advices[9], 2);
520
521 let condselect_config =
523 ConditionalSelectChip::configure(meta, advices[1..5].try_into().unwrap());
524
525 let zerocond_config = ZeroCondChip::configure(meta, advices[1..5].try_into().unwrap());
527
528 let chips = vec![
530 VmChip::Ecc(ecc_config),
531 VmChip::Merkle((merkle_cfg1, merkle_cfg2)),
532 VmChip::SparseTree(smt_config),
533 VmChip::Sinsemilla((sinsemilla_cfg1, sinsemilla_cfg2)),
534 VmChip::Poseidon(poseidon_config),
535 VmChip::Arithmetic(arith_config),
536 VmChip::NativeRange64(native_64_range_check_config),
537 VmChip::NativeRange253(native_253_range_check_config),
538 VmChip::LessThan(lessthan_config),
539 VmChip::BoolCheck(boolcheck_config),
540 VmChip::CondSelect(condselect_config),
541 VmChip::ZeroCond(zerocond_config),
542 ];
543
544 VmConfig { primary, witness: advices[0], chips }
545 }
546
547 fn synthesize(
548 &self,
549 config: Self::Config,
550 mut layouter: impl Layouter<pallas::Base>,
551 ) -> std::result::Result<(), plonk::Error> {
552 trace!(target: "zk::vm", "Entering synthesize()");
553
554 let mut heap: Vec<HeapVar> = vec![];
560
561 let mut litheap: Vec<u64> = vec![];
564
565 let mut public_inputs_offset = 0;
567
568 let mut literals_offset = 0;
570
571 if let Some(VmChip::Sinsemilla((sinsemilla_cfg1, _))) =
573 config.chips.iter().find(|&c| matches!(c, VmChip::Sinsemilla(_)))
574 {
575 trace!(target: "zk::vm", "Initializing Sinsemilla generator lookup table");
576 SinsemillaChip::load(sinsemilla_cfg1.clone(), &mut layouter)?;
577 }
578
579 let no_sinsemilla_chip = !config.chips.iter().any(|c| matches!(c, VmChip::Sinsemilla(_)));
580
581 let rangecheck64_chip = config.rangecheck64_chip();
583 if let Some(VmChip::NativeRange64(rangecheck64_config)) =
584 config.chips.iter().find(|&c| matches!(c, VmChip::NativeRange64(_)))
585 {
586 if no_sinsemilla_chip {
587 trace!(target: "zk::vm", "Initializing k table for 64bit NativeRangeCheck");
588 NativeRangeCheckChip::<K, 64>::load_k_table(
589 &mut layouter,
590 rangecheck64_config.k_values_table,
591 )?;
592 }
593 }
594
595 let no_rangecheck64_chip =
596 !config.chips.iter().any(|c| matches!(c, VmChip::NativeRange64(_)));
597
598 let rangecheck253_chip = config.rangecheck253_chip();
600 let lessthan_chip = config.lessthan_chip();
601
602 if let Some(VmChip::NativeRange253(rangecheck253_config)) =
603 config.chips.iter().find(|&c| matches!(c, VmChip::NativeRange253(_)))
604 {
605 if no_sinsemilla_chip && no_rangecheck64_chip {
606 trace!(target: "zk::vm", "Initializing k table for 253bit NativeRangeCheck");
607 NativeRangeCheckChip::<K, 253>::load_k_table(
608 &mut layouter,
609 rangecheck253_config.k_values_table,
610 )?;
611 }
612 }
613
614 let ecc_chip = config.ecc_chip();
616
617 let arith_chip = config.arithmetic_chip();
619
620 let boolcheck_chip = config.boolcheck_chip();
622
623 let condselect_chip = config.condselect_chip();
625
626 let zerocond_chip = config.zerocond_chip();
628
629 let smt_chip = config.smt_chip().unwrap();
631
632 let one = assign_free_advice(
638 layouter.namespace(|| "Load constant one"),
639 config.witness,
640 Value::known(pallas::Base::ONE),
641 )?;
642 layouter.assign_region(
643 || "constrain constant",
644 |mut region| region.constrain_constant(one.cell(), pallas::Base::ONE),
645 )?;
646
647 for constant in &self.constants {
650 trace!(
651 target: "zk::vm",
652 "Pushing constant `{}` to heap address {}",
653 constant.as_str(),
654 heap.len()
655 );
656 match constant.as_str() {
657 "VALUE_COMMIT_VALUE" => {
658 let vcv = ValueCommitV;
659 let vcv = FixedPointShort::from_inner(ecc_chip.as_ref().unwrap().clone(), vcv);
660 heap.push(HeapVar::EcFixedPointShort(vcv));
661 }
662 "VALUE_COMMIT_RANDOM" => {
663 let vcr = OrchardFixedBasesFull::ValueCommitR;
664 let vcr = FixedPoint::from_inner(ecc_chip.as_ref().unwrap().clone(), vcr);
665 heap.push(HeapVar::EcFixedPoint(vcr));
666 }
667 "VALUE_COMMIT_RANDOM_BASE" => {
668 let vcr = ConstBaseFieldElement::value_commit_r();
669 let vcr =
670 FixedPointBaseField::from_inner(ecc_chip.as_ref().unwrap().clone(), vcr);
671 heap.push(HeapVar::EcFixedPointBase(vcr));
672 }
673 "NULLIFIER_K" => {
674 let nfk = ConstBaseFieldElement::nullifier_k();
675 let nfk =
676 FixedPointBaseField::from_inner(ecc_chip.as_ref().unwrap().clone(), nfk);
677 heap.push(HeapVar::EcFixedPointBase(nfk));
678 }
679
680 _ => {
681 error!(target: "zk::vm", "Invalid constant name: {}", constant.as_str());
682 return Err(plonk::Error::Synthesis)
683 }
684 }
685 }
686 for literal in &self.literals {
692 match literal.0 {
693 LitType::Uint64 => match literal.1.parse::<u64>() {
694 Ok(v) => litheap.push(v),
695 Err(e) => {
696 error!(target: "zk::vm", "Failed converting u64 literal: {e}");
697 return Err(plonk::Error::Synthesis)
698 }
699 },
700 _ => {
701 error!(target: "zk::vm", "Invalid literal: {literal:?}");
702 return Err(plonk::Error::Synthesis)
703 }
704 }
705 }
706 for witness in &self.witnesses {
713 match witness {
714 Witness::EcPoint(w) => {
715 trace!(target: "zk::vm", "Witnessing EcPoint into circuit");
716 let point = Point::new(
717 ecc_chip.as_ref().unwrap().clone(),
718 layouter.namespace(|| "Witness EcPoint"),
719 w.as_ref().map(|cm| cm.to_affine()),
720 )?;
721
722 trace!(target: "zk::vm", "Pushing EcPoint to heap address {}", heap.len());
723 heap.push(HeapVar::EcPoint(point));
724 }
725
726 Witness::EcNiPoint(w) => {
727 trace!(target: "zk::vm", "Witnessing EcNiPoint into circuit");
728 let point = NonIdentityPoint::new(
729 ecc_chip.as_ref().unwrap().clone(),
730 layouter.namespace(|| "Witness EcNiPoint"),
731 w.as_ref().map(|cm| cm.to_affine()),
732 )?;
733
734 trace!(target: "zk::vm", "Pushing EcNiPoint to heap address {}", heap.len());
735 heap.push(HeapVar::EcNiPoint(point));
736 }
737
738 Witness::EcFixedPoint(_) => {
739 error!(target: "zk::vm", "Unable to witness EcFixedPoint, this is unimplemented.");
740 return Err(plonk::Error::Synthesis)
741 }
742
743 Witness::Base(w) => {
744 trace!(target: "zk::vm", "Witnessing Base into circuit");
745 let base = assign_free_advice(
746 layouter.namespace(|| "Witness Base"),
747 config.witness,
748 *w,
749 )?;
750
751 trace!(target: "zk::vm", "Pushing Base to heap address {}", heap.len());
752 heap.push(HeapVar::Base(base));
753 }
754
755 Witness::Scalar(w) => {
756 trace!(target: "zk::vm", "Witnessing Scalar into circuit");
757 let scalar = ScalarFixed::new(
758 ecc_chip.as_ref().unwrap().clone(),
759 layouter.namespace(|| "Witness ScalarFixed"),
760 *w,
761 )?;
762
763 trace!(target: "zk::vm", "Pushing Scalar to heap address {}", heap.len());
764 heap.push(HeapVar::Scalar(scalar));
765 }
766
767 Witness::MerklePath(w) => {
768 trace!(target: "zk::vm", "Witnessing MerklePath into circuit");
769 let path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> =
770 w.map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
771
772 trace!(target: "zk::vm", "Pushing MerklePath to heap address {}", heap.len());
773 heap.push(HeapVar::MerklePath(path));
774 }
775
776 Witness::SparseMerklePath(w) => {
777 let path: Value<[pallas::Base; SMT_FP_DEPTH]> =
778 w.map(|typed_path| gen_const_array(|i| typed_path[i]));
779
780 trace!(target: "zk::vm", "Pushing SparseMerklePath to heap address {}", heap.len());
781 heap.push(HeapVar::SparseMerklePath(path));
782 }
783
784 Witness::Uint32(w) => {
785 trace!(target: "zk::vm", "Pushing Uint32 to heap address {}", heap.len());
786 heap.push(HeapVar::Uint32(*w));
787 }
788
789 Witness::Uint64(w) => {
790 trace!(target: "zk::vm", "Pushing Uint64 to heap address {}", heap.len());
791 heap.push(HeapVar::Uint64(*w));
792 }
793 }
794 }
795 self.tracer.clear();
801 for opcode in &self.opcodes {
804 match opcode.0 {
805 Opcode::EcAdd => {
806 trace!(target: "zk::vm", "Executing `EcAdd{:?}` opcode", opcode.1);
807 let args = &opcode.1;
808
809 let lhs: Point<pallas::Affine, EccChip<OrchardFixedBases>> =
810 heap[args[0].1].clone().try_into()?;
811
812 let rhs: Point<pallas::Affine, EccChip<OrchardFixedBases>> =
813 heap[args[1].1].clone().try_into()?;
814
815 let ret = lhs.add(layouter.namespace(|| "EcAdd()"), &rhs)?;
816
817 trace!(target: "zk::vm", "Pushing result to heap address {}", heap.len());
818 self.tracer.push_ecpoint(&ret);
819 heap.push(HeapVar::EcPoint(ret));
820 }
821 Opcode::EcMul => {
823 trace!(target: "zk::vm", "Executing `EcMul{:?}` opcode", opcode.1);
824 let args = &opcode.1;
825
826 let lhs: FixedPoint<pallas::Affine, EccChip<OrchardFixedBases>> =
827 heap[args[1].1].clone().try_into()?;
828
829 let rhs: ScalarFixed<pallas::Affine, EccChip<OrchardFixedBases>> =
830 heap[args[0].1].clone().try_into()?;
831
832 let (ret, _) = lhs.mul(layouter.namespace(|| "EcMul()"), rhs)?;
833
834 trace!(target: "zk::vm", "Pushing result to heap address {}", heap.len());
835 self.tracer.push_ecpoint(&ret);
836 heap.push(HeapVar::EcPoint(ret));
837 }
838
839 Opcode::EcMulVarBase => {
840 trace!(target: "zk::vm", "Executing `EcMulVarBase{:?}` opcode", opcode.1);
841 let args = &opcode.1;
842
843 let lhs: NonIdentityPoint<pallas::Affine, EccChip<OrchardFixedBases>> =
844 heap[args[1].1].clone().try_into()?;
845
846 let rhs: AssignedCell<Fp, Fp> = heap[args[0].1].clone().try_into()?;
847 let rhs = ScalarVar::from_base(
848 ecc_chip.as_ref().unwrap().clone(),
849 layouter.namespace(|| "EcMulVarBase::from_base()"),
850 &rhs,
851 )?;
852
853 let (ret, _) = lhs.mul(layouter.namespace(|| "EcMulVarBase()"), rhs)?;
854
855 trace!(target: "zk::vm", "Pushing result to heap address {}", heap.len());
856 self.tracer.push_ecpoint(&ret);
857 heap.push(HeapVar::EcPoint(ret));
858 }
859
860 Opcode::EcMulBase => {
861 trace!(target: "zk::vm", "Executing `EcMulBase{:?}` opcode", opcode.1);
862 let args = &opcode.1;
863
864 let lhs: FixedPointBaseField<pallas::Affine, EccChip<OrchardFixedBases>> =
865 heap[args[1].1].clone().try_into()?;
866
867 let rhs: AssignedCell<Fp, Fp> = heap[args[0].1].clone().try_into()?;
868
869 let ret = lhs.mul(layouter.namespace(|| "EcMulBase()"), rhs)?;
870
871 trace!(target: "zk::vm", "Pushing result to heap address {}", heap.len());
872 self.tracer.push_ecpoint(&ret);
873 heap.push(HeapVar::EcPoint(ret));
874 }
875
876 Opcode::EcMulShort => {
877 trace!(target: "zk::vm", "Executing `EcMulShort{:?}` opcode", opcode.1);
878 let args = &opcode.1;
879
880 let lhs: FixedPointShort<pallas::Affine, EccChip<OrchardFixedBases>> =
881 heap[args[1].1].clone().try_into()?;
882
883 let rhs = ScalarFixedShort::new(
884 ecc_chip.as_ref().unwrap().clone(),
885 layouter.namespace(|| "EcMulShort: ScalarFixedShort::new()"),
886 (heap[args[0].1].clone().try_into()?, one.clone()),
887 )?;
888
889 let (ret, _) = lhs.mul(layouter.namespace(|| "EcMulShort()"), rhs)?;
890
891 trace!(target: "zk::vm", "Pushing result to heap address {}", heap.len());
892 self.tracer.push_ecpoint(&ret);
893 heap.push(HeapVar::EcPoint(ret));
894 }
895
896 Opcode::EcGetX => {
897 trace!(target: "zk::vm", "Executing `EcGetX{:?}` opcode", opcode.1);
898 let args = &opcode.1;
899
900 let point: Point<pallas::Affine, EccChip<OrchardFixedBases>> =
901 heap[args[0].1].clone().try_into()?;
902
903 let ret = point.inner().x();
904
905 trace!(target: "zk::vm", "Pushing result to heap address {}", heap.len());
906 self.tracer.push_base(&ret);
907 heap.push(HeapVar::Base(ret));
908 }
909
910 Opcode::EcGetY => {
911 trace!(target: "zk::vm", "Executing `EcGetY{:?}` opcode", opcode.1);
912 let args = &opcode.1;
913
914 let point: Point<pallas::Affine, EccChip<OrchardFixedBases>> =
915 heap[args[0].1].clone().try_into()?;
916
917 let ret = point.inner().y();
918
919 trace!(target: "zk::vm", "Pushing result to heap address {}", heap.len());
920 self.tracer.push_base(&ret);
921 heap.push(HeapVar::Base(ret));
922 }
923
924 Opcode::PoseidonHash => {
925 trace!(target: "zk::vm", "Executing `PoseidonHash{:?}` opcode", opcode.1);
926 let args = &opcode.1;
927
928 let mut poseidon_message: Vec<AssignedCell<Fp, Fp>> =
929 Vec::with_capacity(args.len());
930
931 for idx in args {
932 poseidon_message.push(heap[idx.1].clone().try_into()?);
933 }
934
935 macro_rules! poseidon_hash {
936 ($len:expr, $hasher:ident, $output:ident, $cell:ident) => {
937 let $hasher = PoseidonHash::<
938 _,
939 _,
940 poseidon::P128Pow5T3,
941 poseidon::ConstantLength<$len>,
942 3,
943 2,
944 >::init(
945 config.poseidon_chip().unwrap(),
946 layouter.namespace(|| "PoseidonHash init"),
947 )?;
948
949 let $output = $hasher.hash(
950 layouter.namespace(|| "PoseidonHash hash"),
951 poseidon_message.try_into().unwrap(),
952 )?;
953
954 let $cell: AssignedCell<Fp, Fp> = $output.into();
955
956 trace!(target: "zk::vm", "Pushing hash to heap address {}", heap.len());
957 self.tracer.push_base(&$cell);
958 heap.push(HeapVar::Base($cell));
959 };
960 }
961
962 macro_rules! vla {
963 ($args:ident, $a:ident, $b:ident, $c:ident, $($num:tt)*) => {
964 match $args.len() {
965 $($num => {
966 poseidon_hash!($num, $a, $b, $c);
967 })*
968 _ => {
969 error!(target: "zk::vm", "Unsupported poseidon hash for {} elements", $args.len());
970 return Err(plonk::Error::Synthesis)
971 }
972 }
973 };
974 }
975
976 vla!(args, a, b, c, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24);
977 }
978
979 Opcode::MerkleRoot => {
980 trace!(target: "zk::vm", "Executing `MerkleRoot{:?}` opcode", opcode.1);
982 let args = &opcode.1;
983
984 let leaf_pos = heap[args[0].1].clone().try_into()?;
985 let merkle_path: Value<[Fp; MERKLE_DEPTH_ORCHARD]> =
986 heap[args[1].1].clone().try_into()?;
987 let leaf = heap[args[2].1].clone().try_into()?;
988
989 let merkle_inputs = MerklePath::construct(
990 [config.merkle_chip_1().unwrap(), config.merkle_chip_2().unwrap()],
991 OrchardHashDomains::MerkleCrh,
992 leaf_pos,
993 merkle_path,
994 );
995
996 let root = merkle_inputs
997 .calculate_root(layouter.namespace(|| "MerkleRoot()"), leaf)?;
998
999 trace!(target: "zk::vm", "Pushing merkle root to heap address {}", heap.len());
1000 self.tracer.push_base(&root);
1001 heap.push(HeapVar::Base(root));
1002 }
1003
1004 Opcode::SparseMerkleRoot => {
1005 trace!(target: "zk::vm", "Executing `SparseTreeIsMember{:?}` opcode", opcode.1);
1006 let args = &opcode.1;
1007
1008 let pos = heap[args[0].1].clone().try_into()?;
1009 let path: Value<[Fp; SMT_FP_DEPTH]> = heap[args[1].1].clone().try_into()?;
1010 let leaf = heap[args[2].1].clone().try_into()?;
1011
1012 let root = smt_chip.check_membership(&mut layouter, pos, path, leaf)?;
1013
1014 self.tracer.push_base(&root);
1015 heap.push(HeapVar::Base(root));
1016 }
1017
1018 Opcode::BaseAdd => {
1019 trace!(target: "zk::vm", "Executing `BaseAdd{:?}` opcode", opcode.1);
1020 let args = &opcode.1;
1021
1022 let lhs = &heap[args[0].1].clone().try_into()?;
1023 let rhs = &heap[args[1].1].clone().try_into()?;
1024
1025 let sum = arith_chip.as_ref().unwrap().add(
1026 layouter.namespace(|| "BaseAdd()"),
1027 lhs,
1028 rhs,
1029 )?;
1030
1031 trace!(target: "zk::vm", "Pushing sum to heap address {}", heap.len());
1032 self.tracer.push_base(&sum);
1033 heap.push(HeapVar::Base(sum));
1034 }
1035
1036 Opcode::BaseMul => {
1037 trace!(target: "zk::vm", "Executing `BaseSub{:?}` opcode", opcode.1);
1038 let args = &opcode.1;
1039
1040 let lhs = &heap[args[0].1].clone().try_into()?;
1041 let rhs = &heap[args[1].1].clone().try_into()?;
1042
1043 let product = arith_chip.as_ref().unwrap().mul(
1044 layouter.namespace(|| "BaseMul()"),
1045 lhs,
1046 rhs,
1047 )?;
1048
1049 trace!(target: "zk::vm", "Pushing product to heap address {}", heap.len());
1050 self.tracer.push_base(&product);
1051 heap.push(HeapVar::Base(product));
1052 }
1053
1054 Opcode::BaseSub => {
1055 trace!(target: "zk::vm", "Executing `BaseSub{:?}` opcode", opcode.1);
1056 let args = &opcode.1;
1057
1058 let lhs = &heap[args[0].1].clone().try_into()?;
1059 let rhs = &heap[args[1].1].clone().try_into()?;
1060
1061 let difference = arith_chip.as_ref().unwrap().sub(
1062 layouter.namespace(|| "BaseSub()"),
1063 lhs,
1064 rhs,
1065 )?;
1066
1067 trace!(target: "zk::vm", "Pushing difference to heap address {}", heap.len());
1068 self.tracer.push_base(&difference);
1069 heap.push(HeapVar::Base(difference));
1070 }
1071
1072 Opcode::WitnessBase => {
1073 trace!(target: "zk::vm", "Executing `WitnessBase{:?}` opcode", opcode.1);
1074 let lit = litheap[literals_offset];
1077 literals_offset += 1;
1078
1079 let witness = assign_free_advice(
1080 layouter.namespace(|| "Witness literal"),
1081 config.witness,
1082 Value::known(pallas::Base::from(lit)),
1083 )?;
1084
1085 layouter.assign_region(
1086 || "constrain constant",
1087 |mut region| {
1088 region.constrain_constant(witness.cell(), pallas::Base::from(lit))
1089 },
1090 )?;
1091
1092 trace!(target: "zk::vm", "Pushing assignment to heap address {}", heap.len());
1093 self.tracer.push_base(&witness);
1094 heap.push(HeapVar::Base(witness));
1095 }
1096
1097 Opcode::RangeCheck => {
1098 trace!(target: "zk::vm", "Executing `RangeCheck{:?}` opcode", opcode.1);
1099 let args = &opcode.1;
1100
1101 let lit = litheap[literals_offset];
1102 literals_offset += 1;
1103
1104 let arg = heap[args[1].1].clone();
1105
1106 match lit {
1107 64 => {
1108 rangecheck64_chip.as_ref().unwrap().copy_range_check(
1109 layouter.namespace(|| "copy range check 64"),
1110 arg.try_into()?,
1111 )?;
1112 }
1113 253 => {
1114 rangecheck253_chip.as_ref().unwrap().copy_range_check(
1115 layouter.namespace(|| "copy range check 253"),
1116 arg.try_into()?,
1117 )?;
1118 }
1119 x => {
1120 error!(target: "zk::vm", "Unsupported bit-range {x} for range_check");
1121 return Err(plonk::Error::Synthesis)
1122 }
1123 }
1124 self.tracer.push_void();
1125 }
1126
1127 Opcode::LessThanStrict => {
1128 trace!(target: "zk::vm", "Executing `LessThanStrict{:?}` opcode", opcode.1);
1129 let args = &opcode.1;
1130
1131 let a = heap[args[0].1].clone().try_into()?;
1132 let b = heap[args[1].1].clone().try_into()?;
1133
1134 lessthan_chip.as_ref().unwrap().copy_less_than(
1135 layouter.namespace(|| "copy a<b check"),
1136 a,
1137 b,
1138 0,
1139 true,
1140 )?;
1141 self.tracer.push_void();
1142 }
1143
1144 Opcode::LessThanLoose => {
1145 trace!(target: "zk::vm", "Executing `LessThanLoose{:?}` opcode", opcode.1);
1146 let args = &opcode.1;
1147
1148 let a = heap[args[0].1].clone().try_into()?;
1149 let b = heap[args[1].1].clone().try_into()?;
1150
1151 lessthan_chip.as_ref().unwrap().copy_less_than(
1152 layouter.namespace(|| "copy a<b check"),
1153 a,
1154 b,
1155 0,
1156 false,
1157 )?;
1158 self.tracer.push_void();
1159 }
1160
1161 Opcode::BoolCheck => {
1162 trace!(target: "zk::vm", "Executing `BoolCheck{:?}` opcode", opcode.1);
1163 let args = &opcode.1;
1164
1165 let w = heap[args[0].1].clone().try_into()?;
1166
1167 boolcheck_chip
1168 .as_ref()
1169 .unwrap()
1170 .small_range_check(layouter.namespace(|| "copy boolean check"), w)?;
1171 self.tracer.push_void();
1172 }
1173
1174 Opcode::CondSelect => {
1175 trace!(target: "zk::vm", "Executing `CondSelect{:?}` opcode", opcode.1);
1176 let args = &opcode.1;
1177
1178 let cond: AssignedCell<Fp, Fp> = heap[args[0].1].clone().try_into()?;
1179 let lhs: AssignedCell<Fp, Fp> = heap[args[1].1].clone().try_into()?;
1180 let rhs: AssignedCell<Fp, Fp> = heap[args[2].1].clone().try_into()?;
1181
1182 let out: AssignedCell<Fp, Fp> =
1183 condselect_chip.as_ref().unwrap().conditional_select(
1184 &mut layouter.namespace(|| "cond_select"),
1185 lhs,
1186 rhs,
1187 cond,
1188 )?;
1189
1190 trace!(target: "zk::vm", "Pushing assignment to heap address {}", heap.len());
1191 self.tracer.push_base(&out);
1192 heap.push(HeapVar::Base(out));
1193 }
1194
1195 Opcode::ZeroCondSelect => {
1196 trace!(target: "zk::vm", "Executing `ZeroCondSelect{:?}` opcode", opcode.1);
1197 let args = &opcode.1;
1198
1199 let lhs: AssignedCell<Fp, Fp> = heap[args[0].1].clone().try_into()?;
1200 let rhs: AssignedCell<Fp, Fp> = heap[args[1].1].clone().try_into()?;
1201
1202 let out: AssignedCell<Fp, Fp> = zerocond_chip.as_ref().unwrap().assign(
1203 layouter.namespace(|| "zero_cond"),
1204 lhs,
1205 rhs,
1206 )?;
1207
1208 trace!(target: "zk::vm", "Pushing assignment to heap address {}", heap.len());
1209 self.tracer.push_base(&out);
1210 heap.push(HeapVar::Base(out));
1211 }
1212
1213 Opcode::ConstrainEqualBase => {
1214 trace!(target: "zk::vm", "Executing `ConstrainEqualBase{:?}` opcode", opcode.1);
1215 let args = &opcode.1;
1216
1217 let lhs: AssignedCell<Fp, Fp> = heap[args[0].1].clone().try_into()?;
1218 let rhs: AssignedCell<Fp, Fp> = heap[args[1].1].clone().try_into()?;
1219
1220 layouter.assign_region(
1221 || "constrain witnessed base equality",
1222 |mut region| region.constrain_equal(lhs.cell(), rhs.cell()),
1223 )?;
1224 self.tracer.push_void();
1225 }
1226
1227 Opcode::ConstrainEqualPoint => {
1228 trace!(target: "zk::vm", "Executing `ConstrainEqualPoint{:?}` opcode", opcode.1);
1229 let args = &opcode.1;
1230
1231 let lhs: Point<pallas::Affine, EccChip<OrchardFixedBases>> =
1232 heap[args[0].1].clone().try_into()?;
1233
1234 let rhs: Point<pallas::Affine, EccChip<OrchardFixedBases>> =
1235 heap[args[1].1].clone().try_into()?;
1236
1237 lhs.constrain_equal(
1238 layouter.namespace(|| "constrain ec point equality"),
1239 &rhs,
1240 )?;
1241 self.tracer.push_void();
1242 }
1243
1244 Opcode::ConstrainInstance => {
1245 trace!(target: "zk::vm", "Executing `ConstrainInstance{:?}` opcode", opcode.1);
1246 let args = &opcode.1;
1247
1248 let var: AssignedCell<Fp, Fp> = heap[args[0].1].clone().try_into()?;
1249
1250 layouter.constrain_instance(
1251 var.cell(),
1252 config.primary,
1253 public_inputs_offset,
1254 )?;
1255
1256 public_inputs_offset += 1;
1257 self.tracer.push_void();
1258 }
1259
1260 Opcode::DebugPrint => {
1261 trace!(target: "zk::vm", "Executing `DebugPrint{:?}` opcode", opcode.1);
1262 let args = &opcode.1;
1263
1264 println!("[ZKVM DEBUG] HEAP INDEX: {}", args[0].1);
1265 println!("[ZKVM DEBUG] {:#?}", heap[args[0].1]);
1266 self.tracer.push_void();
1267 }
1268
1269 Opcode::Noop => {
1270 error!(target: "zk::vm", "Unsupported opcode");
1271 return Err(plonk::Error::Synthesis)
1272 }
1273 }
1274 }
1275 self.tracer.assert_correct(self.opcodes.len());
1276
1277 trace!(target: "zk::vm", "Exiting synthesize() successfully");
1278 Ok(())
1279 }
1280}