Home

Awesome

noir-trie-proofs

Description

This repository contains Noir primitives necessary for

A Rust crate is also provided for the purpose of data preprocessing and reproducibility of some of the examples provided (cf. Rust component).

The following subsections elaborate on potential use-cases, which are centred around RLP list decoding and storage and state trie proof verification. Though the methods here do not cover other interesting trie proof verification use-cases, e.g. the case of variable key length, the building blocks are provided. For more information, consult lib/src/rlp.nr and lib/src/lib.nr.

RLP decoding

Constants

Types

To streamline RLP decoding, two types are provided:

Associated functions

	let decoded_bytes: RLP_List<NUM_FIELDS> = decode1(input);

Trie proof verification

Constants

Types

Methods

Examples

Besides the unit tests, the following examples are provided as integration tests:

Preprocessing

As remarked above, trie proofs must be appropriately padded and flattened for use in circuits. Concretely, if trie proofs are expressed as a list of lists of 8-bit words, the preprocessing required may be expressed by the following mapping (constants as above modulo capitalisation):

pad :: [[Word8]] -> [Word8]
pad xs = concat
         $ map (\x -> x ++ byte_padding x)
         $ xs ++ depth_padding
  where
    byte_padding node = replicate (max_trie_node_length - length node) 0
    depth_padding = replicate (max_depth - length xs) []

Moreover, the values that trie proofs terminate in are assumed to be in a byte array left-padded with zeros to the maximum size of the slot they are stored in, i.e. if max_len denotes the maximum possible length of a value, then the mapping to be applied is given as follows:

left_pad :: [Word8] -> Int -> [Word8]
left_pad xs max_len = (replicate (max_len - length xs) 0) ++ xs

Rust component

For convenience, we provide Rust code in the form of a library-and-binary crate. The library contains helper functions for fetching state and storage proofs via JSON RPC as well as applying the steps outlined in Preprocessing, while the binary calls these helper functions and dumps the results to standard output in Toml form. The binary may be used to reproduce the depth_8_state_proof and depth_8_storage_proof examples, as is shown below.

Library

Constants

The following constants mirror their Noir counterparts:

Types

Methods

Functions

Binary

Description

The program ntp-fetch is provided for testing purposes. It calls the fetch_state_proof or fetch_storage_proof function when called with the state-proof or storage-proof subcommand.

The following are the global arguments:

The following are the subcommands together with their arguments:

Examples

The binary may be invoked from the repository root by running cargo run with the appropriate arguments. For the following examples, we assume that the environment variable RPC_URL contains the address of JSON-RPC supporting Ethereum /archive/ node as provided e.g. by Infura. Failing this, any node may be used, but the block number argument will have to be changed (or omitted), in which case the examples will not reproduce the provided test cases.

cargo run state-proof -m 8 --address 0xb47e3cd837dDF8e4c57f05d70ab865de6e193bbb -b 14194126 > tests/depth_8_state_proof/Prover.toml
cargo run storage-proof -m 8 --address 0xb47e3cd837dDF8e4c57f05d70ab865de6e193bbb --key 0xbbc70db1b6c7afd11e79c0fb0051300458f1a3acb8ee9789d9b6b26c61ad9bc7 -b 14194126 > tests/depth_8_storage_proof/Prover.toml

Consuming this library

With Nargo v0.10.4, you can depend on this library by adding the following to your Nargo.toml:

[dependencies]
noir_trie_proofs = { git = "aragonzkresearch/noir-trie-proofs", tag = "main", directory = "lib" }

Testing

To run the unit tests, you can run nargo test in the project root.

To run the integration tests, you can execute against various projects in tests/:

Benchmarks

depth_8_storage_proof

As of Noir v0.21.0 paired with the default proving backend, circuit size of the depth_8_storage_proof test program is:

+-----------------------+------------------------+--------------+----------------------+
| Package               | Language               | ACIR Opcodes | Backend Circuit Size |
+-----------------------+------------------------+--------------+----------------------+
| depth_8_storage_proof | PLONKCSat { width: 3 } | 51563        | 1687738              |
+-----------------------+------------------------+--------------+----------------------+

CLI

On M2 Macbook Air, using Nargo v0.21.0 paired with the default proving backend:

Compiling takes approximately 2 seconds:

% time nargo compile --package depth_8_storage_proof
nargo compile --package depth_8_storage_proof  1.84s user 0.07s system 99% cpu 1.925 total

Executing for witness takes approximately 1 second:

% time nargo execute --package depth_8_storage_proof
[depth_8_storage_proof] Circuit witness successfully solved
nargo execute --package depth_8_storage_proof  1.40s user 0.05s system 140% cpu 1.028 total

Executing + proving (as nargo prove always re-executes for witness) takes approximately 1.5 mins:

% time nargo prove --package depth_8_storage_proof
nargo prove --package depth_8_storage_proof  408.52s user 18.15s system 548% cpu 1:17.81 total

NOTE: Running nargo prove the first time / before nargo compile would automatically include program compilation. Subsequent runs without program modifications would make use of the cached artifacts and provide more representative benchmarking results.