Nexus Virtual Machine
The Nexus Virtual Machine (Nexus VM or NVM) is a reduced instruction set computer (RISC) with a byte-addressable random-access memory and a private input tape. This virtual machine abstraction is comparable to others used in the zero-knowledge research space, such as the TinyRAM architecture specification. The advantage of using such random-access machines is that they abstract away details of the underlying hardware or operating system and provide a convenient tool for generating claims and proofs about the correctness of a computation.
Nexus Virtual Machine Architecture
The Nexus Virtual Machine has a von Neumann architecture, storing instructions and data in the same read-write memory space. The machine has 32 registers and a read-write memory with addresses in the range {0…232−1}. The state of the machine is defined as a four-tuple, (pc;M;R;I), where
- pc denotes the program counter register;
- M denotes the memory;
- R denotes the state of registers;
- I is the private input.
Both M and R are finite maps. The keys of M are addresses in the range {0,…,232−1}. The values of M are 8-bit bytes. The keys of R are register selectors from the set {x0…x31}. The values of R are 32-bit words. A word (resp. a halfword) is represented as four (resp. two) consecutive bytes in little endian. By design, updates to register x0 are ignored and the value of R[x0] is always zero.
The current implementation of the Nexus VM does not yet support providing public inputs at runtime. Also, we remark that during compilation the Nexus VM may be configured to use memory sizes smaller than 232 for efficiency reasons.
At initialization, all the general-purpose registers are set to 0. The program counter pc is set to 0x0000. The private input tape contains the byte-encoded private input for the program. Since pc is initially 0x0000, the first instruction to be executed will be the one stored at the position 0x0000 of the memory. Since the program code resides in the same area as the data, the initial memory can contain not only the program code but also some initial public input data for the program.
The program counter pc is always advanced by 4 bytes after the execution of each instruction, unless the instruction itself sets the value of pc. Moreover, the Nexus VM enforces 4-byte memory alignment for the program counter by checking that pc is a multiple of 4 when reading an instruction.
Nexus Virtual Machine Instruction Set
The Nexus VM instruction set contains 41 instructions in total, as summarized in table below. Each instruction is specified via an mnemonic and can take some arguments, typically register selectors and an immediate value. The exact format of each instruction is defined as follows:
- mnemonic is a string name of the instruction;
- rd is a register selector specifying the destination register;
- rs1 is a register selector specifying the first operand;
- rs2 is a register selector specifying the second operand; and
- i is an immediate value, whose size varies according to instructions.
Table: Summary of the Nexus Virtual Machine Instruction Set, where operations are mod 232.
Instruction mnemonic | Arguments | Description of functionality |
---|
lui | rd i | sets R[rd] to i |
auipc | rd i | sets R[rd] to pc+i |
add | rd rs1 rs2 | sets R[rd] to R[rs1]+R[rs2] |
addi | rd rs1 i | sets R[rd] to R[rs1]+i |
sub | rd rs1 rs2 | sets R[rd] to R[rs1]−R[rs2] |
slt | rd rs1 rs2 | sets R[rd] to (R[rs1]<R[rs2]) (signed comparison) |
slti | rd rs1 i | sets R[rd] to (R[rs1]<i) (signed comparison) |
sltu | rd rs1 rs2 | sets R[rd] to (R[rs1]<R[rs2]) (unsigned comparison) |
sltui | rd rs1 i | sets R[rd] to (R[rs1]<i) (unsigned comparison) |
sll | rd rs1 rs2 | sets R[rd] to R[rs1]≪R[rs2]&0x1F |
slli | rd rs1 i | sets R[rd] to R[rs1]≪i&0x1F |
srl | rd rs1 rs2 | sets R[rd] to R[rs1]≫R[rs2]&0x1F (with zero extension) |
srli | rd rs1 i | sets R[rd] to R[rs1]≫i&0x1F (with zero extension) |
sra | rd rs1 rs2 | sets R[rd] to R[rs1]≫R[rs2]&0x1F (with sign extension) |
srai | rd rs1 i | sets R[rd] to R[rs1]≫i&0x1F (with sign extension) |
xor | rd rs1 rs2 | sets R[rd] to the bitwise XOR of R[rs1] and R[rs2] |
xori | rd rs1 i | sets R[rd] to the bitwise XOR of R[rs1] and i |
and | rd rs1 rs2 | sets R[rd] to the bitwise AND of R[rs1] and R[rs2] |
andi | rd rs1 i | sets R[rd] to the bitwise AND of R[rs1] and i |
or | rd rs1 rs2 | sets R[rd] to the bitwise OR of R[rs1] and R[rs2] |
ori | rd rs1 i | sets R[rd] to the bitwise OR of R[rs1] and i |
beq | rs1 rs2 i | branches to pc+i if (R[rs1]=R[rs2]) |
bne | rs1 rs2 i | branches to pc+i if (R[rs1]=R[rs2]) |
blt | rs1 rs2 i | branches to pc+i if (R[rs1]<R[rs2]) (signed comparison) |
bge | rs1 rs2 i | branches to pc+i if (R[rs1]≥R[rs2]) (signed comparison) |
bltu | rs1 rs2 i | branches to pc+i if (R[rs1]<R[rs2]) (unsigned comparison) |
bgeu | rs1 rs2 i | branches to pc+i if (R[rs1]≥R[rs2]) (unsigned comparison) |
lb | rd rs1 i | loads the sign extension of the byte at M[R[rs1]+i] into R[rd] |
lh | rd rs1 i | loads the sign extension of the half-word at M[R[rs1]+i] into R[rd] |
lw | rd rs1 i | loads the word at M[R[rs1]+i] into R[rd] |
lbu | rd rs1 i | loads the zero extension of the byte at M[R[rs1]+i] into R[rd] |
lhu | rd rs1 i | loads the zero extension of the half-word at M[R[rs1]+i] into R[rd] |
sb | rs1 rs2 i | stores the least significant byte of R[rs2] at M[R[rs1]+i] |
sh | rs1 rs2 i | stores the less significant half-word of R[rs2] at M[R[rs1]+i] |
sw | rs1 rs2 i | stores R[rs2] at M[R[rs1]+i] |
jal | rd i | jumps to pc+i and stores pc+4 into R[rd] |
jalr | rd rs1 i | jumps to R[rs1]+i and stores pc+4 into R[rd] |
fence | | No operation |
ecall | rd | system call |
ebreak | rd | system call |
unimp | | jumps to pc; in effect looping forever at the current program counter |
The Nexus VM also enforces 2-byte and 4-byte memory alignments for the instructions operating on half-words and words.
Each instruction is encoded as a 32-bit-long string, ending with 7-bit-long opcode string, preceded by a 5-bit-long register selector in many cases, and other data depending on the operation.
Table: Binary Encoding of Nexus Virtual Machine Instructions, where ∗m denotes any binary string of m bits, and ⟨d⟩, ⟨s1⟩, ⟨s2⟩ denote respectively the binary representation of the 5-bit-long register selectors rd, rs1, rs2.
The notation ⟨ix⟩ each denote immediate values, interpreted the same way as x-immediate values of 32-bit RISC-V architecture. Some immediate values (type B and S) occupy discontiguous positions, so their fragments are written as ⟨ix0⟩ and ⟨ix1⟩. ⟨iSH⟩ denotes a 5-bit long immediate value.
⟨iU⟩ and ⟨iJ⟩ contain 20 bits.
⟨iI⟩ contains 12 bits.
⟨iS0⟩ and ⟨iB0⟩ contain 5 bits.
⟨iS1⟩ and ⟨iB1⟩ contain 7 bits.
Instruction mnemonic | Arguments | Binary Encodings (left: most significant bit) |
---|
lui | rd i | ⟨iU⟩⟨d⟩0b_011_0111 |
auipc | rd i | ⟨iU⟩⟨d⟩0b_001_0111 |
jal | rd i | ⟨iJ⟩⟨d⟩0b_110_1111 |
jalr | rd rs1 i | ⟨iI⟩⟨s1⟩0b_000⟨d⟩0b_110_0111 |
beq | rs1 rs2 i | ⟨iB1⟩⟨s2⟩⟨s1⟩0b_000⟨iB0⟩ 0b_110_0011 |
bne | rs1 rs2 i | ⟨iB1⟩⟨s2⟩⟨s1⟩0b_001⟨iB0⟩ 0b_110_0011 |
blt | rs1 rs2 i | ⟨iB1⟩⟨s2⟩⟨s1⟩0b_100⟨iB0⟩ 0b_110_0011 |
bge | rs1 rs2 i | ⟨iB1⟩⟨s2⟩⟨s1⟩0b_101⟨iB0⟩ 0b_110_0011 |
bltu | rs1 rs2 i | ⟨iB1⟩⟨s2⟩⟨s1⟩0b_110⟨iB0⟩ 0b_110_0011 |
bgeu | rs1 rs2 i | ⟨iB1⟩⟨s2⟩⟨s1⟩0b_111⟨iB0⟩ 0b_110_0011 |
lb | rd rs1 i | ⟨iI⟩⟨s1⟩0b_000⟨d⟩0b_000_0011 |
lh | rd rs1 i | ⟨iI⟩⟨s1⟩0b_001⟨d⟩0b_000_0011 |
lw | rd rs1 i | ⟨iI⟩⟨s1⟩0b_010⟨d⟩0b_000_0011 |
lbu | rd rs1 i | ⟨iI⟩⟨s1⟩0b_011⟨d⟩0b_000_0011 |
lhu | rd rs1 i | ⟨iI⟩⟨s1⟩0b_100⟨d⟩0b_000_0011 |
sb | rs1 rs2 i | ⟨iS1⟩⟨s2⟩⟨s1⟩0b_000⟨iS0⟩0b_010_0011 |
sh | rs1 rs2 i | ⟨iS1⟩⟨s2⟩⟨s1⟩0b_001⟨iS0⟩0b_010_0011 |
sw | rs1 rs2 i | ⟨iS1⟩⟨s2⟩⟨s1⟩0b_010⟨iS0⟩0b_010_0011 |
addi | rd rs1 i | ⟨iI⟩⟨s1⟩0b_000⟨d⟩0b_001_0011 |
slli | rd rs1 i | ⟨iI⟩⟨s1⟩0b_001⟨d⟩0b_001_0011 |
slti | rd rs1 i | ⟨iI⟩⟨s1⟩0b_010⟨d⟩0b_001_0011 |
sltui | rd rs1 i | ⟨iI⟩⟨s1⟩0b_011⟨d⟩0b_001_0011 |
xori | rd rs1 i | ⟨iI⟩⟨s1⟩0b_100⟨d⟩0b_001_0011 |
srli | rd rs1 i | 0b_000_0000⟨iSH⟩⟨s1⟩0b_101⟨d⟩0b_001_0011 |
srai | rd rs1 i | 0b_010_0000⟨iSH⟩⟨s1⟩0b_101⟨d⟩0b_001_0011 |
ori | rd rs1 i | ⟨iI⟩⟨s1⟩0b_110⟨d⟩0b_001_0011 |
andi | rd rs1 i | ⟨iI⟩⟨s1⟩0b_111⟨d⟩0b_001_0011 |
add | rd rs1 rs2 | 0b_000_0000⟨s2⟩⟨s1⟩0b_000⟨d⟩0b_011_0011 |
sub | rd rs1 rs2 | 0b_010_0000⟨s2⟩⟨s1⟩0b_000⟨d⟩0b_011_0011 |
sll | rd rs1 rs2 | 0b_000_0000⟨s2⟩⟨s1⟩0b_001⟨d⟩0b_011_0011 |
slt | rd rs1 rs2 | 0b_000_0000⟨s2⟩⟨s1⟩0b_010⟨d⟩0b_011_0011 |
sltu | rd rs1 rs2 | 0b_000_0000⟨s2⟩⟨s1⟩0b_011⟨d⟩0b_011_0011 |
xor | rd rs1 rs2 | 0b_000_0000⟨s2⟩⟨s1⟩0b_100⟨d⟩0b_011_0011 |
srl | rd rs1 rs2 | 0b_000_0000⟨s2⟩⟨s1⟩0b_101⟨d⟩0b_011_0011 |
sra | rd rs1 rs2 | 0b_010_0000⟨s2⟩⟨s1⟩0b_101⟨d⟩0b_011_0011 |
or | rd rs1 rs2 | 0b_000_0000⟨s2⟩⟨s1⟩0b_110⟨d⟩0b_011_0011 |
and | rd rs1 rs2 | 0b_000_0000⟨s2⟩⟨s1⟩0b_111⟨d⟩0b_011_0011 |
fence | | ∗250b_000_1111 |
ecall | rd | 0x00000⟨d⟩0b_111_0011 |
ebreak | rd | 0x00100⟨d⟩0b_111_0011 |
unimp | | 0xc0001∗50b_111_0011 |
The current architecture does not specify an output tape. Nevertheless, one can easily return arbitrary strings as output by encoding that string in some specific region of the memory.
Nexus Virtual Machine Initialization
Initially, the memory is assumed to only contain zero values and all the general-purpose registers are set to 0. The program counter pc is also set to 0x0000. The program itself is provided to the Nexus VM via a file in an Executable and Linkable Format (ELF) encoded according to the RV32I Instruction Set in the Volume I, Unprivileged Specification version 20191213 in the The RISC-V Instruction Set Manual.
Each instruction in the program is loaded one at a time into the memory starting at address 0x0000.
Nexus Virtual Machine Extensions
While the universality of the current instruction set allows for executing any program on the Nexus VM, writing a program for the VM may yield inefficient programs due to the limited instruction set of the Nexus VM. As a result, proving the correctness of such computations within the Nexus zkVM may become infeasible for more complex programs. The cost of such an abstraction may be multiplied by more than a 1000 factor for functions such as the SHA-256 circuit.
To deal with such scenarios, the Nexus Virtual Machine is being designed with extensibility in mind in order to enable the addition of custom instructions for more complex functions, such as hashing and signature verification.
Currently, the Nexus Virtual Machine uses universal circuits to simulate the whole CPU and this unfortunately increases the complexity of the Nexus Proof System with each additional instruction.
Nevertheless, we will soon be switching to a non-uniform computation model based on recent advances in folding and accumulation techniques (e.g., [KS22], [BC23]), via the concept of zkVM precompiles. In the new model, the cost of proving custom precompile extensions of the NVM instruction set only occurs when that particular precompile is executed by the program.
The main advantage of using precompiles is that it maintains a developer-friendly CPU abstraction while efficiently allowing for the addition of more complex functions.
We intend to eventually support user-defined precompiles that could be provided as extensions of the Nexus zkVM. We expect to first implement these special functions as part of the Nexus VM instruction set.
References
[BBHR19] Eli Ben-Sasson, Iddo Bentov, Yinon Horesh, and Michael Riabzev. “Scalable zero knowledge with no trusted setup”. In CRYPTO 2019.
[BC23] Benedikt Bünz and Binyi Chen. “Protostar: Generic efficient accumulation/folding for special sound protocols”. In: Cryptology ePrint Archive (2023)
[BCKL22] Eli Ben-Sasson, Dan Carmon, Swastik Kopparty, and David Levit. “Scalable and transparent proofs over all large fields, via elliptic curves”. In: Electronic Colloquium on Computational Complexity, Report. Vol. 110. 2022, p. 2022
[CBBZ23] Binyi Chen, Benedikt Bünz, Dan Boneh, and Zhenfei Zhang. “Hyperplonk: Plonk with linear-time prover and high-degree custom gates”. In EUROCRYPT 2023.
[GGPR13] Rosario Gennaro, Craig Gentry, Bryan Parno, and Mariana Raykova. “Quadratic span programs and succinct NIZKs without PCPs”. In EUROCRYPT 2013.
[GWC19] Ariel Gabizon, Zachary J Williamson, and Oana Ciobotaru. “Plonk: Permutations over lagrange-bases for oecumenical noninteractive arguments of knowledge”. In: Cryptology ePrint Archive (2019)
[KS22] Abhiram Kothapalli and Srinath Setty. “SuperNova: Proving universal machine executions without universal circuits”. In: Cryptology ePrint Archive (2022)
[Sta21] StarkWare. ethSTARK Documentation. Cryptology ePrint Archive, Paper 2021/582.
[STW23] Srinath Setty, Justin Thaler, and Riad Wahby. “Customizable constraint systems for succinct arguments”. In: Cryptology ePrint Archive (2023)