Skip to content

Commit

Permalink
Add pre-commit for Linting
Browse files Browse the repository at this point in the history
  • Loading branch information
bhoberman committed Jun 29, 2024
1 parent 88a5b31 commit 18cd994
Show file tree
Hide file tree
Showing 31 changed files with 83 additions and 51 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: pre-commit

on:
pull_request:
push:
branches: [main]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.12'

- uses: pre-commit/action@v3.0.1
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ Cargo.lock
nexus-proof

# macos
.DS_Store
.DS_Store
19 changes: 19 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: detect-private-key
- repo: https://github.com/yoheimuta/protolint
rev: v0.50.2 # Select a release here like v0.44.0
hooks:
- id: protolint
- repo: local
hooks:
- id: cargo fmt
name: cargo fmt
entry: cargo fmt --all -- --check --color always
language: system
pass_filenames: false
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ There are three main ways to contribute:

**Anybody can participate in any stage of contribution**. We urge you to participate in all discussions around bugs, feature requests, existing code, and PRs.

## Reporting Issues
## Reporting Issues

#### Asking for help

If you have reviewed this document and existing documentation and still have questions or are still having problems, but don't quite know enough to create a bug report, then
you can get help by **starting a discussion**.
If you have reviewed this document and existing documentation and still have questions or are still having problems, but don't quite know enough to create a bug report, then
you can get help by **starting a discussion**.

You can do so either on the [dev Telegram][dev-tg] or on discussiom board of the [Github][gh] page for this repository. Look for the "Discussions" tab at the top.

Expand Down Expand Up @@ -57,7 +57,7 @@ If you have examples of other tools that have the feature you are requesting, pl

Pull requests are the way concrete changes are made to the code, documentation, and dependencies of the Nexus zkVM.

Before making a large change, it is usually a good idea to first open an issue describing the change to solicit feedback and guidance.
Before making a large change, it is usually a good idea to first open an issue describing the change to solicit feedback and guidance.
This will increase the likelihood of the PR getting merged. Striking up a discussion the [dev Telegram][dev-tg] to let the community know
what you'll be working on can also be helpful for getting early feedback before diving in.

Expand All @@ -66,7 +66,7 @@ contributors are not duplicating work.

#### Discussion

You will probably get feedback or requests for changes to your pull request.
You will probably get feedback or requests for changes to your pull request.
This is a regular and important part of the submission process, so don't be discouraged! Some reviewers may sign off on the pull
request right away, others may have more detailed comments or feedback. This is a necessary part of the process in order
to evaluate whether the changes are correct and necessary.
Expand Down
2 changes: 1 addition & 1 deletion config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ config = { version = "0.13.4", default-features = false }

url = { version = "2.5.0", features = ["serde"] }

clap = { workspace = true, optional = true }
clap = { workspace = true, optional = true }

[features]
clap_derive = ["clap"]
2 changes: 1 addition & 1 deletion docs/NEXTRA_README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Nextra Docs Template
# Nextra Docs Template

This is a template for creating documentation with [Nextra](https://nextra.site).

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ const App = ({ Component, pageProps }) => {
return <Component {...pageProps} />
}

export default App
export default App
3 changes: 1 addition & 2 deletions docs/pages/specs/nexus-costs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ image: "/nexus-head.png"

Most instructions of the Nexus VM instruction set can be implemented quite efficiently requiring less than 2000 constraints in total. The main exception are memory operations, which require expensive Merkle proof computations. More precisely, in the case of a memory update, one needs to compute 3 Merkle proofs, one to read the current instruction from memory, one to read the current contents of the memory location, and one to update that memory location. Since each of these Merkle proofs requires about 4300 constraints, as indicated in the [memory checking costs section](nexus-memory-checking#cost-profile), the total cost of a memory update is about 13000 constraints.

Since the current version of the Nexus VM uses a universal model of computation, the total cost of each proof accumulation step ends up being around 15000 constraints, which is quite inefficient. However, we expect to significantly improve these costs by using better memory checking techniques and a non-uniform model of computation. The use of precompiles also has the potential to significantly improve the efficiency of the Nexus Proof System.

Since the current version of the Nexus VM uses a universal model of computation, the total cost of each proof accumulation step ends up being around 15000 constraints, which is quite inefficient. However, we expect to significantly improve these costs by using better memory checking techniques and a non-uniform model of computation. The use of precompiles also has the potential to significantly improve the efficiency of the Nexus Proof System.
7 changes: 3 additions & 4 deletions docs/pages/specs/nexus-memory-checking.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "A description of the Nexus zkVM memory checking."
image: "/nexus-head.png"
---

## Nexus zkVM Memory Checking
## Nexus zkVM Memory Checking

Since the external memory used by the Nexus Virtual Machine is assumed to be untrusted, the Nexus zkVM must ensure read-write consistency throughout the execution of a program. Informally, this requires that reading the contents of any cell of the memory should always return the value last written to that cell.

Expand All @@ -17,7 +17,7 @@ In the current version of Nexus VM, the memory size is set to $\mathbf{2^{22}}$

![merkle-hash-tree](/images/merkle-hash-tree.svg)

Initially, each memory cell is assumed to be the $0$ string. Hence, in order to compute the Merkle root for the initial memory, we first compute the default hashes for each level of the tree, starting with the leaves and ending with the Merkle root. For the leaves, the default value is simply the hash of the $0$ string. For any subsequent level, their default values then become the hash of the concatenation of two of the default values for the level below it.
Initially, each memory cell is assumed to be the $0$ string. Hence, in order to compute the Merkle root for the initial memory, we first compute the default hashes for each level of the tree, starting with the leaves and ending with the Merkle root. For the leaves, the default value is simply the hash of the $0$ string. For any subsequent level, their default values then become the hash of the concatenation of two of the default values for the level below it.

### Merkle Tree Updates and Proofs

Expand All @@ -35,7 +35,7 @@ In order to prove that $m$ is the correct value at address $64$, the untrusted m

Next, consider the case of a *write operation* at address $64$, where a new value $\mathtt{0xFF}$ should replace the old value $\mathtt{0x0F}$. In order to update the Merkle tree to match the new desired memory configuration, all the node values along the path from the leaf associated with $\textbf{MS}$ to the Merkle root $h_{0}$ need to be recomputed. We highlight these values in blue in the figure below. To achieve this goal, the zkVM should proceed as follows:
1. First perform a *read operation* to obtain $\textbf{M}[64]$ along with a Merkle opening proof for it (highlighted by red boxes in the figure below);
2. Next, update the value at $\textbf{M}[64]$ to $\mathtt{0xFF}$ in the memory segment $\textbf{MS}=\textbf{M}[64 \ldots 95]$ as well as all the nodes in the path $\{h_{17}^{0,\ldots,1,0},\ldots, h_{1}^0, h_{0}\}$ starting from the leaf associated with $\textbf{MS}$;
2. Next, update the value at $\textbf{M}[64]$ to $\mathtt{0xFF}$ in the memory segment $\textbf{MS}=\textbf{M}[64 \ldots 95]$ as well as all the nodes in the path $\{h_{17}^{0,\ldots,1,0},\ldots, h_{1}^0, h_{0}\}$ starting from the leaf associated with $\textbf{MS}$;
3. Finally, keep the updated value $h_0$ as the new Merkle root.

![merkle-hash-tree-path-update](/images/merkle-hash-tree-path-update.svg)
Expand All @@ -49,4 +49,3 @@ For a memory $\textbf{M}$ of size $2^{22}$, read and write operations will respe
[[GKR21](https://www.usenix.org/system/files/sec21-grassi.pdf)] Lorenzo Grassi, Dmitry Khovratovich, Christian Rechberger, Arnab Roy, and Markus Schofnegger. “Poseidon: A new hash function for Zero-Knowledge proof systems”. In: 30th USENIX Security Symposium (USENIX Security 21). 2021, pp. 519–535

[[M87](https://link.springer.com/chapter/10.1007/3-540-48184-2_32)] Ralph C Merkle. “A digital signature based on a conventional encryption function”. In CRYPTO 1987.

17 changes: 8 additions & 9 deletions docs/pages/specs/nexus-prover.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ image: "/nexus-head.png"

The second main component of the Nexus zkVM is the Nexus Proof System, which provides proofs of correctness for any computation being run on the Nexus VM. In order to realize this goal and be able to support arbitrarily large computations, the Nexus Proof System relies on modern recursive zero-knowledge proof systems such as folding and accumulation schemes [[KST22; KS22; BC23](#references)] to achieve a high level of parallelization.

Let $P$ be a program that has been compiled to the Nexus VM, possibly with access to co-processors. The proof generation process for $P$ has four main steps:
Let $P$ be a program that has been compiled to the Nexus VM, possibly with access to co-processors. The proof generation process for $P$ has four main steps:
1. zkVM initialization;
2. program execution;
3. proof accumulation; and
Expand All @@ -19,11 +19,11 @@ The following subsections provide an overview of each of these components.

### Initialization

Let $P$ be a program which we want to execute in a verifiable manner. As described above, before loading $P$ into the memory starting at address $\mathtt{0x0000}$, the Nexus VM initially sets the contents of its global-purpose registers to 0 and the value of each memory location is assumed to be undefined.
Let $P$ be a program which we want to execute in a verifiable manner. As described above, before loading $P$ into the memory starting at address $\mathtt{0x0000}$, the Nexus VM initially sets the contents of its global-purpose registers to 0 and the value of each memory location is assumed to be undefined.

In order to enforce the consistency of the memory throughout the execution of the program $P$, the Nexus zkVM currently uses Merkle Trees [[M87](#references)] for memory checking together with the Poseidon hash function [[GKR21](#references)]. In this method, we compute a Merkle root associated with the current contents of the memory and we update its value whenever the contents of the memory change.

Once the Merkle Root for the initial state is computed, the Nexus zkVM starts loading the program $P$ one instruction at a time, updating the Merkle Tree Root accordingly after each memory update.
Once the Merkle Root for the initial state is computed, the Nexus zkVM starts loading the program $P$ one instruction at a time, updating the Merkle Tree Root accordingly after each memory update.

Finally, once $P$ is fully loaded into the memory, we use the Merkle Root for the current state of the memory as the public input to the execution step of the proof system.

Expand Down Expand Up @@ -67,7 +67,7 @@ The reduction in circuit complexity over the secondary curve has, however, impli

### Proof Compression

Despite being highly parallelizable, the accumulated proof generated in the IVC phase is quite large, which is undesirable in practice. Besides their sizes, such proofs are also not easily verifiable by other systems. For these reasons, the final step of the proof generation is to compress the accumulated proof with a sequence of succinct (zero-knowledge) Non-Interactive Arguments of Knowledge or (zk)-SNARKs for short.
Despite being highly parallelizable, the accumulated proof generated in the IVC phase is quite large, which is undesirable in practice. Besides their sizes, such proofs are also not easily verifiable by other systems. For these reasons, the final step of the proof generation is to compress the accumulated proof with a sequence of succinct (zero-knowledge) Non-Interactive Arguments of Knowledge or (zk)-SNARKs for short.

In the current design of the Nexus zkVM, the final compression step is split into two phases. In the first phase, accumulated proofs generated in the IVC phase are turned into proofs of knowledge of valid IVC proofs, whose sizes are logarithmic in the size of the underlying Nova IVC proofs. Then, in the second phase, these proofs are converted into succinct proofs of constant size which can be cheaply verified on systems such as Ethereum.

Expand All @@ -88,7 +88,7 @@ Two of the main configuration parameters for the Nexus zkVM are:
- The number $k$ of Nexus VM instructions per folding step.

As for the public parameters, these consist essentially of the following:
- Parameters for the Pedersen commitment used by Nova in the proof accumulation phase for each curve in the cycle;
- Parameters for the Pedersen commitment used by Nova in the proof accumulation phase for each curve in the cycle;
- Parameters for the Zeromorph commitment used by Spartan in the compression phase; and
- Parameters for the remaining SNARKs in the compression phase.

Expand All @@ -107,19 +107,19 @@ const MODULUS_BITS: u64 = 254;

#### Instructions Per Folding Step

The Nexus zkVM's approach based on Nova provides a proof system with a recursion overhead which is independent of the total number of computation steps and of the step function itself. However, in our current implementation, the recursion overhead remains quite high (in the order of $120{,}000$ and $340{,}000$ constraints in the IVC and PCD settings) compared to the cost of a single step of the Nexus VM (around $15{,}000$ constraints).
The Nexus zkVM's approach based on Nova provides a proof system with a recursion overhead which is independent of the total number of computation steps and of the step function itself. However, in our current implementation, the recursion overhead remains quite high (in the order of $120{,}000$ and $340{,}000$ constraints in the IVC and PCD settings) compared to the cost of a single step of the Nexus VM (around $15{,}000$ constraints).

Hence, in order to offset the current recursion overhead, the Nexus zkVM includes a configuration parameter $k$ which allows the prover to combine several steps of the Nexus VM into a single folding step. Even though a higher value of $k$ will increase the overall complexity of the prover, the value $k$ has no impact on the recursion overhead. As a result, one can improve the overall performance of the Nexus zkVM by increasing the value $k$. For instance, by choosing $k=8$ in the IVC setting, the cost of each folding step will approximately double while the total number of folding steps will be reduced by a factor $8$.

The current default value for $k$ is 16, which seems to avoid large RAM requirements for the prover while providing a reasonable offset for the recursion overhead. This value can, however, be set by the user of the Nexus zkVM.
The current default value for $k$ is 16, which seems to avoid large RAM requirements for the prover while providing a reasonable offset for the recursion overhead. This value can, however, be set by the user of the Nexus zkVM.

### Public Parameters

As mentioned above, the public parameters for the Nexus zkVM consist of those needed by the Pedersen commitment scheme during the accumulation phase and by the Zeromorph commitment scheme during the compression phase. Other public parameters will also be needed for the remaining SNARKs of the compression, but these are currently under development and hence omitted for now.

Regarding the public parameters for the Pedersen commitment scheme used in Nova, these consist of a vector of random group elements of sufficient size in the respective curve in the cycle. In the PCD setting with $k=1$, Nova circuits for the primary curve are represented by three sparse matrices $(A,B,C)$ each containing about $2^{19}$ rows and columns and about 8 non-zero entries per row. As a result, the size of the public parameters for the Pedersen commitment scheme contains about $2^{19}$ `bn254` elliptic curve elements. Since larger values of $k$ would increase the witness size, the size of the vector would need to increase accordingly. For the secondary curve, the size of the vector depends on the size of the witness for the secondary circuit, containing about $3{,}000$ `grumpkin` elliptic curve elements.

As for the public parameters for the Zeromorph commitment scheme used in Spartan, these consist of a sufficiently large vector of group elements of the form $\tau^i g$, where $g$ is a generator for the group and $\tau$ is the “toxic waste” secret used to generate the vector. These public parameters are exactly the same as those used for the KZG polynomial commitment scheme [[KZG10](#references)], requiring a trusted setup phase as well. As in the case of systems such as Plonk-KZG [[GWC19](#references)], the trusted setup is universal and only depends on a global bound on the size of committed vectors. Regarding the actual size of this vector, the current implementation requires committing to a single vector of size $2^{26}$ when $k=1$ since the pre-processing phase of Spartan needs to commit to a specialized sparse matrix representation of $(A, B, C)$ used in Nova (see [[Set20, Sections 6-7](#references)] for details). As for Pedersen commitments, larger values of $k$ would also result in larger parameters for Zeromorph.
As for the public parameters for the Zeromorph commitment scheme used in Spartan, these consist of a sufficiently large vector of group elements of the form $\tau^i g$, where $g$ is a generator for the group and $\tau$ is the “toxic waste” secret used to generate the vector. These public parameters are exactly the same as those used for the KZG polynomial commitment scheme [[KZG10](#references)], requiring a trusted setup phase as well. As in the case of systems such as Plonk-KZG [[GWC19](#references)], the trusted setup is universal and only depends on a global bound on the size of committed vectors. Regarding the actual size of this vector, the current implementation requires committing to a single vector of size $2^{26}$ when $k=1$ since the pre-processing phase of Spartan needs to commit to a specialized sparse matrix representation of $(A, B, C)$ used in Nova (see [[Set20, Sections 6-7](#references)] for details). As for Pedersen commitments, larger values of $k$ would also result in larger parameters for Zeromorph.

Finally, since the parameters for the Pedersen and Zeromorph commitment schemes consist of a vector of group elements and need to be compatible in order to make Spartan a proof system for committed relaxed R1CS, the Nexus zkVM currently generates the parameters for the Pedersen commitment scheme by truncating the parameters used for Zeromorph, instead of randomly sampling a vector of fresh group elements. Note that this is cryptographically indistinguishable from a random vector, unless one knows the toxic waste secret.

Expand Down Expand Up @@ -157,4 +157,3 @@ Finally, since the parameters for the Pedersen and Zeromorph commitment schemes
[TinyRAM]: (https://www.scipr-lab.org/doc/TinyRAM-spec-2.000.pdf)
[RISCZero]: (https://www.risczero.com)
[zkMISP]: (https://whitepaper.zkm.io/whitepaper1.2.pdf)

Loading

0 comments on commit 18cd994

Please sign in to comment.