zkVM
====

The DarkFi zkVM is a single zkSNARK circuit based on
[Halo2](https://github.com/zcash/halo2) which requires no trusted
setup and is able to execute and prove compiled _zkas_ bincode.

The zkVM is designed in such a way that it's able to choose code paths
based on the bincode and as such, create a zkSNARK proof specific to
the zkas circuit. In this document, we'll explain this machinery from
a high level. A preliminary to understanding the zkVM is to understand
the [zkas bincode](bincode.md) and its layout.

## High-level operation

The entire VM can be thought of as a machine with heap
access to values (variables) that are constructed within
the ZK circuit.  Upon initialization, the VM instantiates
two heaps, of which one holds literals (currently `u64` is
supported), and the other holds arbitrary types defined in
[`HeapVar`](https://darkrenaissance.github.io/darkfi/development/darkfi/zk/vm_heap/enum.HeapVar.html)

Once the heaps are instantiated, the circuit initializes all the
available halo2 gadgets so they're ready for use, and also to create
and have access to any lookup tables.

Next, if there are any constants defined in the `constant` section
of zkas, they are created and pushed to the heap:

```rust,no_run,no_playground
{{#include ../../../src/zk/vm.rs:constant_init}}
```

If all is successful, the VM proceeds with any available literals in
the `circuit` section and pushes them onto the literals heap:

```rust,no_run,no_playground
{{#include ../../../src/zk/vm.rs:literals_init}}
```

At this point, the VM is done with initializing the constants used,
and proceeds with the private witnesses of the ZK proof that are
located in the `witness` section of the zkas bincode. We simply
loop through the witnesses in order, and depending on what they are,
we witness them with specialized halo2 functions:

```rust,no_run,no_playground
{{#include ../../../src/zk/vm.rs:witness_init}}
```

Once this is done, everything is set up and the VM proceeds with
executing the input opcodes that are located in the `circuit` section
of the zkas bincode in a sequential fashion. Opcodes are able to
take a defined number of inputs and are able to optionally produce
a single output. The inputs are referenced from the heap, by index.
The output that can be produced by an opcode is also pushed onto the
heap when created. An example of this operation can be seen within
the following snippet from the zkVM:

```rust,no_run,no_playground
{{#include ../../../src/zk/vm.rs:opcode_begin}}
```

As the opcodes are being executed, the halo2 API lets us return any
possible proof verification error so the verifier is able to know
if the input proof is valid or not. Any possible public inputs to a
circuit are also fed into the `constrain_instance` opcode, so that's
how even public inputs can be enforced in the same uniform fashion
like the rest.
