Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

README update & Solidity NatSpec comments #44

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,66 @@ forge test --match-path test/* -vv

More details about Foundry tooling is [here](https://book.getfoundry.sh/).

# Repository structure

```
├── lib
| └── forge-std # Forge standard library for testing utilities.
├── src
| ├── blocks # Cryptographic building blocks shared between all our features.
| └── Utilities.sol # Mostly Mathematical building blocks such as Field operations or Polynomial-related methods.
└── test # Unit test for our contracts.
```

# Features

This section aims to describe the main features currently being developed and outline their specificities. It has to be
noted that each of these branches have dedicated e2e testing, documented in their respective README.

## Pasta

[Feature branch: `pasta`](https://github.com/lurk-lab/solidity-verifier/tree/pasta)

Orignal feature branch, implementing the [Nova](https://github.com/microsoft/Nova) Verifier over
[Pallas/Vesta (Pasta) curve cycles](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/). The reference Nova implementation over Pasta can be found over [the lurk-lab/Nova
repository]( https://github.com/lurk-lab/Nova/tree/solidity-verifier-pp-spartan).

Development is nearly finalized but there are some compatibility checks to be run between the latest version of [Arecibo](https://github.com/lurk-lab/arecibo)
storojs72 marked this conversation as resolved.
Show resolved Hide resolved
and our solidity verifier.

## Grumpkin

[Feature branch: `grumpkin`](https://github.com/lurk-lab/solidity-verifier/tree/grumpkin)

Feature branch aiming to implement our Nova Verifier over BN254/Grumpkin curve cycle instead of Pasta, to keep up with the
development on the Rust implementation side. As for Pasta, the reference implementation can be found over [the lurk-lab/Nova
repository]( https://github.com/lurk-lab/Nova/tree/solidity-verifier-pp-spartan).

Development is nearly finalized but there are some compatibility checks to be run between the latest version of [Arecibo](https://github.com/lurk-lab/arecibo)
storojs72 marked this conversation as resolved.
Show resolved Hide resolved
and our solidity verifier.

## Zeromorph

[Feature branch: `zeromorph`](https://github.com/lurk-lab/solidity-verifier/tree/zeromorph)

The goal is to take into account the [Zeromorph](https://eprint.iacr.org/2023/917.pdf) feature done in Arecibo. Zeromorph
impacts how we generate prover randomness at proving time, and allows us to have a new (and faster) Polynomial Commitment
Scheme (PCS). The reference implementation for the Zeromorph feature can be found in [the Arecibo repository](https://github.com/lurk-lab/arecibo/tree/solidity-verifier-zeromorph ).

The branch needs to integrate the [lastest updates pushed over Arecibo](https://github.com/lurk-lab/arecibo/pull/145) and will
storojs72 marked this conversation as resolved.
Show resolved Hide resolved
most likely need some development in Assembly to properly work.

## Gas Optimization

[Feature branch: `gas-optimizing`](https://github.com/lurk-lab/solidity-verifier/tree/gas-optimizing)

This last branch contains development in [Assembly](https://docs.soliditylang.org/en/latest/assembly.html), leveraging
[Yul](https://docs.soliditylang.org/en/latest/yul.html). This development will allow optimization on gas consumption, readying
our contracts for production. Based on the [Grumpkin feature branch](https://github.com/lurk-lab/solidity-verifier/tree/grumpkin),
it should aim to implement a Grumpkin contract in Yul.

The verification steps 1 and 2 have been implemented but the rest of the steps need to be developed.
storojs72 marked this conversation as resolved.
Show resolved Hide resolved

# Solidity contracts generation

Poseidon contracts in this repository have been generated with a help of correspondent Python scripts.
Expand Down
127 changes: 120 additions & 7 deletions src/Utilities.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@ pragma solidity ^0.8.16;

import "src/blocks/EqPolynomial.sol";

/**
* @title Field Library
* @notice Provides arithmetic operations in finite fields, including inversion, exponentiation, and square root.
*/
library Field {
uint256 constant ZERO = 0;
uint256 constant ONE = 1;

/// @dev Compute f^-1 for f \in Fr scalar field
/// @notice credit: Aztec, Spilsbury Holdings Ltd
/**
* @notice Credit: Aztec, Spilsbury Holdings Ltd
* @dev Compute f^-1 for f \in Fr scalar field
* @param _x The field element to invert.
* @param _mod The modulus of the field.
* @return output The inverse of the field element.
*/
function invert(uint256 _x, uint256 _mod) public view returns (uint256 output) {
assembly {
let mPtr := mload(0x40)
Expand All @@ -23,6 +32,14 @@ library Field {
}
}

/**
* @notice Performs modular exponentiation.
* @dev This function uses precompiled contract for big modular exponentiation.
* @param _base The base for exponentiation.
* @param _exp The exponent.
* @param _mod The modulus.
* @return result The result of base^exp mod mod.
*/
function fieldpow(uint256 _base, uint256 _exp, uint256 _mod) public view returns (uint256 result) {
assembly {
// Free memory pointer
Expand All @@ -43,10 +60,14 @@ library Field {
}
}

// @dev Perform a modular exponentiation.
// @return base^exponent (mod modulus)
// This method is ideal for small exponents (~64 bits or less), as it is cheaper than using the pow precompile
// @notice credit: credit: Aztec, Spilsbury Holdings Ltd
/**
* @notice Credit: credit: Aztec, Spilsbury Holdings Ltd
* @dev This method is ideal for small exponents (~64 bits or less).
* @param base The base for exponentiation.
* @param exponent The small exponent.
* @param modulus The modulus.
* @return The result of base^exponent mod modulus.
*/
function powSmall(uint256 base, uint256 exponent, uint256 modulus) internal pure returns (uint256) {
uint256 result = 1;
uint256 input = base;
Expand All @@ -65,6 +86,14 @@ library Field {
// Implementation of the Tonelli-Shanks square root algorithm
// NOTE: It is assumed that _mod is a prime for this algorithm to work, and when _mod is congruent to 3 mod 4
// a direct calculation is used instead.
/**
* @notice Computes the square root of a field element.
* @dev Implements the Tonelli-Shanks square root algorithm. It is assumed that _mod is a prime for this algorithm
* to work, and when _mod is congruent to 3 mod 4 a direct calculation is used instead.
* @param _x The field element.
* @param _mod The modulus, assumed to be prime.
* @return The square root of x in the field.
*/

function sqrt(uint256 _x, uint256 _mod) public view returns (uint256) {
assembly {
Expand Down Expand Up @@ -141,6 +170,12 @@ library Field {
revert();
}

/**
* @notice Reverses the byte order of a 256-bit integer.
* @dev This function reverses the byte order of a 256-bit integer.
* @param input The integer to reverse.
* @return v The reversed integer.
*/
function reverse256(uint256 input) public pure returns (uint256 v) {
v = input;

Expand All @@ -164,7 +199,11 @@ library Field {
v = (v >> 128) | (v << 128);
}

// Returns the 4 limbs in little-endian order of a 256-bit field element.
/**
* @notice Extracts the 4 limbs of a 256-bit field element in little-endian order.
* @param x The field element.
* @return The 4 limbs of the field element.
*/
function extractLimbs(uint256 x) public pure returns (uint256, uint256, uint256, uint256) {
uint256 limb1 = (0x000000000000000000000000000000000000000000000000ffffffffffffffff & x);
uint256 limb2 = (0x00000000000000000000000000000000ffffffffffffffff0000000000000000 & x) >> 64;
Expand All @@ -174,6 +213,11 @@ library Field {
return (limb1, limb2, limb3, limb4);
}

/**
* @notice Converts a uint8 array to a bytes32.
* @param input The uint8 array.
* @return The converted bytes32.
*/
function uint8ArrayToBytes32(uint8[32] memory input) public pure returns (bytes32) {
bytes32 output;

Expand All @@ -185,7 +229,17 @@ library Field {
}
}

/**
* @title Common Utilities Library
* @dev Library providing common utility functions.
*/
library CommonUtilities {
/**
* @notice Calculates the base-2 logarithm of a given number.
* @dev This function uses bitwise operations to efficiently compute the log2 of the input.
* @param x The number to calculate the logarithm for.
* @return y The base-2 logarithm of the input number.
*/
function log2(uint256 x) public pure returns (uint256 y) {
assembly {
let arg := x
Expand Down Expand Up @@ -217,6 +271,14 @@ library CommonUtilities {
}
}

/**
* @notice Generates an array of powers of a given base up to a specified length.
* @dev Computes s^0, s^1, ..., s^(len-1) modulo a given modulus.
* @param s The base to raise to successive powers.
* @param len The number of powers to compute.
* @param modulus The modulus to use for the calculation.
* @return An array containing the computed powers of s.
*/
function powers(uint256 s, uint256 len, uint256 modulus) public pure returns (uint256[] memory) {
require(len >= 1);
uint256[] memory result = new uint256[](len);
Expand All @@ -230,6 +292,9 @@ library CommonUtilities {
}
}

/**
* @title Polynomial Library
*/
library PolyLib {
struct UniPoly {
uint256[] coeffs;
Expand All @@ -243,21 +308,44 @@ library PolyLib {
PolyLib.CompressedUniPoly[] compressed_polys;
}

/**
* @notice Determines the degree of a univariate polynomial.
* @param poly The polynomial to determine the degree of.
* @return The degree of the polynomial.
*/
function degree(UniPoly memory poly) public pure returns (uint256) {
return poly.coeffs.length - 1;
}

/**
* @notice Evaluates the polynomial at zero.
* @param poly The polynomial to evaluate.
* @return The value of the polynomial at zero.
*/
function evalAtZero(UniPoly memory poly) public pure returns (uint256) {
return poly.coeffs[0];
}

/**
* @notice Evaluates the polynomial at one.
* @param poly The polynomial to evaluate.
* @param mod The modulus for the computation.
* @return result The value of the polynomial at one.
*/
function evalAtOne(UniPoly memory poly, uint256 mod) public pure returns (uint256 result) {
for (uint256 i = 0; i < poly.coeffs.length; i++) {
// result += poly.coeffs[i];
result = addmod(result, poly.coeffs[i], mod);
}
}

/**
* @notice Evaluates the polynomial at a given point.
* @param poly The polynomial to evaluate.
* @param r The point at which to evaluate the polynomial.
* @param mod The modulus for the computation.
* @return The value of the polynomial at point r.
*/
function evaluate(UniPoly memory poly, uint256 r, uint256 mod) public pure returns (uint256) {
uint256 power = r;
uint256 result = poly.coeffs[0];
Expand All @@ -271,10 +359,23 @@ library PolyLib {
return result;
}

/**
* @notice Negates a given field element.
* @param x The field element to negate.
* @param mod The modulus for the computation.
* @return The negation of x in the field.
*/
function negate(uint256 x, uint256 mod) internal pure returns (uint256) {
return mod - (x % mod);
}

/**
* @notice Decompresses a compressed univariate polynomial.
* @param poly The compressed polynomial to decompress.
* @param hint A hint used in the decompression algorithm.
* @param mod The modulus for the computation.
* @return A decompressed univariate polynomial.
*/
function decompress(CompressedUniPoly calldata poly, uint256 hint, uint256 mod)
public
pure
Expand Down Expand Up @@ -305,6 +406,11 @@ library PolyLib {
return UniPoly(coeffs);
}

/**
* @notice Converts a uint256 to an array of uint8.
* @param input The uint256 input.
* @return An array of uint8 representing the input.
*/
function toUInt8Array(uint256 input) private pure returns (uint8[] memory) {
uint8[] memory result = new uint8[](32);

Expand All @@ -316,6 +422,13 @@ library PolyLib {
return result;
}

/**
* @notice Converts a univariate polynomial to an array of bytes for use in a transcript.
* @dev The method converts each coefficient of the polynomial into a byte array in little-endian order
* and concatenates these arrays to form the final byte array representation of the polynomial.
* @param poly The univariate polynomial to convert.
* @return An array of bytes representing the polynomial's coefficients in a format suitable for a transcript.
*/
function toTranscriptBytes(UniPoly memory poly) public pure returns (uint8[] memory) {
uint8[] memory result = new uint8[](32 * (poly.coeffs.length - 1));

Expand Down
23 changes: 22 additions & 1 deletion src/blocks/EqPolynomial.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@ pragma solidity ^0.8.16;
import "src/blocks/pasta/Vesta.sol";
import "src/blocks/pasta/Pallas.sol";

library EqPolinomialLib {
/**
* @title EqPolynomial Library
* @dev Provides functions for evaluating equality polynomials in cryptographic protocols..
*/
library EqPolynomialLib {
/**
* @notice Evaluates the equation polynomial.
* @dev This function computes the evaluation of an equation polynomial based on the given parameters. It iteratively computes the polynomial evaluation using assembly for optimized gas efficiency.
* @param r The array of 'r' values, representing one part of the inputs to the polynomial.
* @param rx The array of 'rx' values, representing another part of the inputs to the polynomial.
* @param modulus The modulus to be used for the polynomial computation, ensuring calculations are done in a finite field.
* @param negateBase A function that negates a base element in the finite field.
* @return The result of the polynomial evaluation.
*/
function evaluate(
uint256[] memory r,
uint256[] memory rx,
Expand Down Expand Up @@ -50,6 +63,14 @@ library EqPolinomialLib {
return result;
}

/**
* @notice Computes evaluations of a polynomial for all combinations of inputs.
* @dev Iteratively calculates the evaluations for all possible combinations of 'r' values. This function is used to generate a full set of evaluations for a given polynomial.
* @param r The array of 'r' values, representing the inputs to the polynomial.
* @param modulus The modulus to be used for polynomial computation.
* @param negateBase A function that negates a base element in the finite field.
* @return An array containing the evaluations for all input combinations.
*/
function evals(uint256[] memory r, uint256 modulus, function (uint256) returns (uint256) negateBase)
internal
returns (uint256[] memory)
Expand Down
15 changes: 14 additions & 1 deletion src/blocks/IdentityPolynomial.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,21 @@ pragma solidity ^0.8.16;
import "src/blocks/pasta/Vesta.sol";
import "src/blocks/pasta/Pallas.sol";

// Port of Nova' IdentityPolynomial (https://github.com/lurk-lab/Nova/blob/solidity-verifier-pp-spartan/src/spartan/ppsnark.rs#L35)
/**
* @title IdentityPolynomial Library
* @notice Implements the evaluation of the Identity Polynomial.
* @dev This library is a Solidity port of Nova's IdentityPolynomial (https://github.com/lurk-lab/Nova/blob/solidity-verifier-pp-spartan/src/spartan/ppsnark.rs#L35),
* which is used in Spartan-based zero-knowledge proofs. The function calculates the identity polynomial based on a
* given array of 'r' values.
*/
library IdentityPolynomialLib {
/**
* @notice Evaluate the Identity Polynomial with given 'r' values.
* @dev Calculates the identity polynomial by iterating over each element of the 'r' array. Each element is scaled by a power of 2 based on its position and then reduced modulo the given modulus.
* @param r Array of 'r' values, inputs to the polynomial.
* @param modulus The modulus for performing operations in a finite field.
* @return The result of the polynomial evaluation.
*/
function evaluate(uint256[] memory r, uint256 modulus) public pure returns (uint256) {
uint256 result;
uint256 tmp;
Expand Down
Loading