diff --git a/tests/dyn_circuit.rs b/tests/dyn_circuit.rs
new file mode 100644
index 000000000..48a8cba48
--- /dev/null
+++ b/tests/dyn_circuit.rs
@@ -0,0 +1,122 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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 .
+ */
+
+use darkfi::zk::assign_free_advice;
+use halo2_proofs::{
+ arithmetic::Field,
+ circuit::{
+ //floor_planner::V1,
+ Layouter,
+ SimpleFloorPlanner,
+ Value,
+ },
+ dev::{CircuitLayout, MockProver},
+ pasta::Fp,
+ plonk::{self, Advice, Circuit, Column, ConstraintSystem, Instance as InstanceColumn},
+};
+use plotters::prelude::*;
+use rand::rngs::OsRng;
+
+#[derive(Clone)]
+struct DynConfig {
+ primary: Column,
+ advices: Vec>,
+}
+
+struct DynCircuit {
+ pub witnesses: Vec>,
+}
+
+impl Circuit for DynCircuit {
+ type Config = DynConfig;
+ //type FloorPlanner = V1;
+ type FloorPlanner = SimpleFloorPlanner;
+ type Params = usize;
+
+ fn without_witnesses(&self) -> Self {
+ let mut witnesses = Vec::with_capacity(self.witnesses.len());
+ for _ in &self.witnesses {
+ witnesses.push(Value::unknown());
+ }
+
+ Self { witnesses }
+ }
+
+ fn params(&self) -> Self::Params {
+ self.witnesses.len()
+ }
+
+ fn configure_with_params(
+ meta: &mut ConstraintSystem,
+ params: Self::Params,
+ ) -> Self::Config {
+ // NOTE: `let advices = vec![meta.advice_column(); params];` does not work as expected.
+ let mut advices = vec![];
+ for _ in 1..params + 1 {
+ advices.push(meta.advice_column());
+ }
+ for advice in advices.iter() {
+ meta.enable_equality(*advice);
+ }
+
+ let primary = meta.instance_column();
+ meta.enable_equality(primary);
+
+ DynConfig { primary, advices }
+ }
+
+ fn configure(_meta: &mut ConstraintSystem) -> Self::Config {
+ unreachable!();
+ }
+
+ fn synthesize(
+ &self,
+ config: Self::Config,
+ mut layouter: impl Layouter,
+ ) -> Result<(), plonk::Error> {
+ for (i, witness) in self.witnesses.iter().enumerate() {
+ let w = assign_free_advice(
+ layouter.namespace(|| "witness element"),
+ config.advices[i],
+ *witness,
+ )?;
+
+ layouter.constrain_instance(w.cell(), config.primary, i)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[test]
+fn dyn_circuit() {
+ const ITERS: usize = 10;
+ const K: u32 = 4;
+
+ for i in 1..ITERS + 1 {
+ let public_inputs = vec![Fp::random(&mut OsRng); i];
+ let witnesses = public_inputs.iter().map(|x| Value::known(*x)).collect();
+ let circuit = DynCircuit { witnesses };
+ let prover = MockProver::run(K, &circuit, vec![public_inputs]).unwrap();
+ prover.assert_satisfied();
+
+ let title = format!("target/dynamic_circuit_{:0>2}.png", i);
+ let root = BitMapBackend::new(&title, (800, 600)).into_drawing_area();
+ CircuitLayout::default().render(K, &circuit, &root).unwrap();
+ }
+}