Skip to content

Commit

Permalink
Merge branch 'main' into feat/curve_bench
Browse files Browse the repository at this point in the history
  • Loading branch information
diegokingston authored Jan 29, 2024
2 parents 9138279 + 66dc5b7 commit 65d4774
Show file tree
Hide file tree
Showing 15 changed files with 620 additions and 64 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]

members = ["math", "crypto", "gpu", "benches", "provers/plonk", "provers/stark", "provers/cairo", "provers/groth16", "provers/groth16/arkworks-adapter", "examples/merkle-tree-cli", "examples/prove-miden", "winterfell_adapter"]
members = ["math", "crypto", "gpu", "benches", "provers/plonk", "provers/stark", "provers/cairo", "provers/groth16", "provers/groth16/arkworks-adapter", "examples/merkle-tree-cli", "examples/prove-miden", "winterfell_adapter", "examples/shamir_secret_sharing"]
exclude = ["ensure-no_std"]
resolver = "2"

Expand Down
66 changes: 9 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ This library provides efficient implementation of cryptographic primitives used

</div>

## Why we built lambdaworks

Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask.

So, we decided to build our library, focusing on performance, with clear documentation and developer-focused. Our core team is a group of passionate people from different backgrounds and different strengths; we think that the whole is greater than just the addition of the parts. We don't want to be a compilation of every research result in the ZK space. We want this to be a library that can be used in production, not just in academic research. We want to offer developers the main building blocks and proof systems so that they can build their applications on top of this library.

## [Documentation](https://lambdaclass.github.io/lambdaworks)

## Main crates
Expand All @@ -36,6 +42,8 @@ Both Math and Crypto support wasm with target `wasm32-unknown-unknown`. To see a

## Examples - mini apps
- [Merkle Tree CLI](https://github.com/lambdaclass/lambdaworks/tree/main/examples/merkle-tree-cli)
- [Proving Miden](https://github.com/lambdaclass/lambdaworks/tree/main/examples/prove-miden)
- [Shamir's secret sharing](https://github.com/lambdaclass/lambdaworks/tree/main/examples/shamir_secret_sharing)

## Exercises and Challenges
- [lambdaworks exercises and challenges](https://github.com/lambdaclass/lambdaworks_exercises/tree/main)
Expand All @@ -54,12 +62,6 @@ If you use ```lambdaworks``` libraries in your research projects, please cite th
}
```

## Why we built lambdaworks

Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask.

So, we decided to build our library, focusing on performance, with clear documentation and developer-focused. Our core team is a group of passionate people from different backgrounds and different strengths; we think that the whole is greater than just the addition of the parts. We don't want to be a compilation of every research result in the ZK space. We want this to be a library that can be used in production, not just in academic research. We want to offer developers the main building blocks and proof systems so that they can build their applications on top of this library.

## List of features

Disclaimer: This list contains cryptographic primitives and mathematical structures that we want to support in lambdaworks. It can be expanded later to include new primitives. If you find there is a mistake or there has been an update in another library, please let us know.
Expand Down Expand Up @@ -96,11 +98,11 @@ List of symbols:
| Marlin | :x: | :heavy_check_mark: | :x: | :x: | :x: |
| GKR | :x: | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: |
| **Polynomial Commitment Schemes** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** |
| KZG10 | :heavy_check_mark: | ✔️ | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| FRI | 🏗️ | :x: | :x: | :heavy_check_mark: | :x: |
| IPA | 🏗️ | ✔️ | :heavy_check_mark: | :x: | :x: |
| Brakedown | :x: | :x: | :x: | :x: | :x: |
| Basefold | :x: | :x: | :x: | :x: | :x: |
| KZG10 | :heavy_check_mark: | ✔️ | :heavy_check_mark: | :heavy_check_mark: | :x: |
| **Folding Schemes** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** |
| Nova | :x: | :heavy_check_mark: | :x: | :x: | :x: |
| Supernova | :x: | :x: | :x: | :x: | :x: |
Expand Down Expand Up @@ -155,56 +157,6 @@ To serve the documentation locally, first install both [mdbook](https://rust-lan
make docs
```

## 📊 Benchmarks

Benchmark results are hosted [here](https://lambdaclass.github.io/lambdaworks/bench).

These are the results of execution of the benchmarks for finite field arithmetic using the STARK field prime (p = 3618502788666131213697322783095070105623107215331596699973092056135872020481).

Differences of 3% are common for some measurements, so small differences are not statistically relevant.

ARM - M1

| Operation| N | Arkworks | lambdaworks |
| -------- | --- | --------- | ----------- |
| `mul` | 10k | 112 μs | 115 μs |
| `add` | 1M | 8.5 ms | 7.0 ms |
| `sub` | 1M | 7.53 ms | 7.12 ms |
| `pow` | 10k | 11.2 ms | 12.4 ms |
| `invert` | 10k | 30.0 ms | 27.2 ms |

x86 - AMD Ryzen 7 PRO

| Operation | N | Arkworks (ASM)* | lambdaworks |
| -------- | --- | --------- | ----------- |
| `mul` | 10k | 118.9 us | 95.7 us |
| `add` | 1M | 6.8 ms | 5.4 ms |
| `sub` | 1M | 6.6 ms | 5.2 ms |
| `pow` | 10k | 10.6 ms | 9.4 ms |
| `invert` | 10k | 34.2 ms | 35.74 ms |

*assembly feature was enabled manually for that bench, and is not activated by default when running criterion

To run them locally, you will need `cargo-criterion` and `cargo-flamegraph`. Install it with:

```bash
cargo install cargo-criterion
```

Run the complete benchmark suite with:

```bash
make benchmarks
```

Run a specific benchmark suite with `cargo`, for example to run the one for `field`:

```bash
make benchmark BENCH=field
```

You can check the generated HTML report in `target/criterion/reports/index.html`

## 📚 References

The following links, repos and projects have been important in the development of this library and we want to thank and acknowledge them.
Expand Down
4 changes: 4 additions & 0 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ harness = false
[[bench]]
name = "iai_merkle"
harness = false

[[bench]]
name= "criterion_poseidon"
harness=false
7 changes: 7 additions & 0 deletions crypto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# lambdaworks Crypto

This folder contains different cryptographic primitives needed for proof systems. The main elements are:
- [Merkle trees](https://github.com/lambdaclass/lambdaworks/tree/main/crypto/src/merkle_tree)
- [Hash functions](https://github.com/lambdaclass/lambdaworks/tree/main/crypto/src/hash)
- [Fiat Shamir transformation](https://github.com/lambdaclass/lambdaworks/tree/main/crypto/src/fiat_shamir)
- [Polynomial commitment schemes](https://github.com/lambdaclass/lambdaworks/tree/main/crypto/src/commitments)
18 changes: 18 additions & 0 deletions crypto/benches/criterion_poseidon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use lambdaworks_crypto::hash::poseidon::starknet::PoseidonCairoStark252;
use lambdaworks_crypto::hash::poseidon::Poseidon;
use lambdaworks_math::field::element::FieldElement;
use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField;

fn poseidon_benchmarks(c: &mut Criterion) {
let x = FieldElement::<Stark252PrimeField>::from_hex("0x123456").unwrap();
let y = FieldElement::<Stark252PrimeField>::from_hex("0x789101").unwrap();
let mut group = c.benchmark_group("Poseidon Benchmark");

// Benchmark with black_box is 0.41% faster
group.bench_function("Hashing with black_box", |bench| {
bench.iter(|| black_box(PoseidonCairoStark252::hash(&x, &y)))
});
}
criterion_group!(poseidon, poseidon_benchmarks);
criterion_main!(poseidon);
154 changes: 154 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# lambdaworks examples

This folder contains examples designed to learn how to use the different tools in lambdaworks, such as finite field arithmetics, elliptic curves, provers, and adapters.

## Examples

Below is a list of all lambdaworks examples in the folder:
- [Shamir Secret Sharing](https://github.com/lambdaclass/lambdaworks/tree/main/examples/shamir_secret_sharing): implements example of [Shamir's secret sharing](https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing). Shows use of polynomials and finite fields in lambdaworks.
- [Merkle tree CLI](https://github.com/lambdaclass/lambdaworks/tree/main/examples/merkle-tree-cli): generate inclusion proofs for an element inside a Merkle tree and verify them using a CLI.
- [Proving Miden using lambdaworks STARK Platinum prover](https://github.com/lambdaclass/lambdaworks/tree/main/examples/prove-miden): Executes a Miden vm Fibonacci program, gets the execution trace and generates a proof (and verifies it) using STARK Platinum.

You can also check [lambdaworks exercises](https://github.com/lambdaclass/lambdaworks/tree/main/exercises) to learn more.

## Basic use of Finite Fields

This library works with [finite fields](https://en.wikipedia.org/wiki/Finite_field). A `Field` is an abstract definition. It knows the modulus and defines how the operations are performed.

We usually create a new `Field` by instantiating an optimized backend. For example, this is the definition of the Pallas field:

```rust
// 4 is the number of 64-bit limbs needed to represent the field
type PallasMontgomeryBackendPrimeField<T> = MontgomeryBackendPrimeField<T, 4>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MontgomeryConfigPallas255PrimeField;
impl IsModulus<U256> for MontgomeryConfigPallas255PrimeField {
const MODULUS: U256 = U256::from_hex_unchecked(
"40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
);
}

pub type Pallas255PrimeField =
PallasMontgomeryBackendPrimeField<MontgomeryConfigPallas255PrimeField>;
```

Internally, it resolves all the constants needed and creates all the required operations for the field.

Suppose we want to create a `FieldElement`. This is as easy as instantiating the `FieldElement` over a `Field` and calling a `from_hex` function.

For example:

```rust
let an_element = FieldElement::<Stark252PrimeField>::from_hex_unchecked("030e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662")
```

Notice we can alias the `FieldElement` to something like

```rust
type FE = FieldElement::<Stark252PrimeField>;
```

Once we have a field, we can make all the operations. We usually suggest working with references, but copies work too.

```rust
let field_a = FE::from_hex("3").unwrap();
let field_b = FE::from_hex("7").unwrap();

// We can use pointers to avoid copying the values internally
let operation_result = &field_a * &field_b

// But all the combinations of pointers and values works
let operation_result = field_a * field_b
```

Sometimes, optimized operations are preferred. For example,

```rust
// We can make a square multiplying two numbers
let squared = field_a * field_a;
// Using exponentiation
let squared =
field_a.pow(FE::from_hex("2").unwrap())
// Or using an optimized function
let squared = field_a.square()
```

ome useful instantiation methods are also provided for common constants and whenever const functions can be called. This is when creating functions that do not rely on the `IsField` trait since Rust does not support const functions in traits yet,

```rust
// Defined for all field elements
// Efficient, but nonconst for the compiler
let zero = FE::zero()
let one = FE::one()

// Const alternatives of the functions are provided,
// But the backend needs to be known at compile time.
// This requires adding a where clause to the function

let zero = F::ZERO
let one = F::ONE
let const_intstantiated = FE::from_hex_unchecked("A1B2C3");
```

You will notice traits are followed by an `Is`, so instead of accepting something of the form `IsField`, you can use `IsPrimeField` and access more functions. The most relevant is `.representative()`. This function returns a canonical representation of the element as a number, not a field.

## Basic use of Elliptic curves

lambdaworks supports different elliptic curves. Currently, we support the following elliptic curve types:
- Short Weiestrass: points $(x, y)$ satisfy the equation $y^2 = x^3 + a x + b$. The curve parameters are $a$ and $b$. All elliptic curves can be cast in this form.
- Edwards: points $(x, y)$ satisfy the equation $a x^2 + y^2 = 1 + d x^2 y^2$. The curve parameters are $a$ and $d$.
- Montgomery: points $(x, y)$ satisfy the equation $b y^2 = x^3 + a x^2 + x$. The curve parameters are $b$ and $a$.

To create an elliptic curve in Short Weiestrass form, we have to implement the traits `IsEllipticCurve` and `IsShortWeierstrass`. Below we show how the Pallas curve is defined:
```rust
#[derive(Clone, Debug)]
pub struct PallasCurve;

impl IsEllipticCurve for PallasCurve {
type BaseField = Pallas255PrimeField;
type PointRepresentation = ShortWeierstrassProjectivePoint<Self>;

fn generator() -> Self::PointRepresentation {
Self::PointRepresentation::new([
-FieldElement::<Self::BaseField>::one(),
FieldElement::<Self::BaseField>::from(2),
FieldElement::one(),
])
}
}

impl IsShortWeierstrass for PallasCurve {
fn a() -> FieldElement<Self::BaseField> {
FieldElement::from(0)
}

fn b() -> FieldElement<Self::BaseField> {
FieldElement::from(5)
}
}
```

All curve models have their `defining_equation` method, which allows us to check whether a given $(x,y)$ belongs to the elliptic curve. The `BaseField` is where the coordinates $x,y$ of the curve live. `generator()` provides a point $P$ in the elliptic curve such that, by repeatedly adding $P$ to itself, we can obtain all the points in the elliptic curve group.

The `generator()` returns a vector with three components $(x,y,z)$, instead of the two $(x,y)$. lambdaworks represents points in projective coordinates, where operations like scalar multiplication are much faster. We can generate points by providing $(x,y)$
```rust
let x = FE::from_hex_unchecked(
"bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5",
);
let y = FE::from_hex_unchecked(
"13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8",
);
PallasCurve::create_point_from_affine(x, y).unwrap()
```
If you provide an invalid point, there will be an error. You can obtain the point at infinity (which is the neutral element for the curve operation) by doing
```rust
let point_at_infinity = PallasCurve::neutral_element()
```
Once we have points, we can do operations between points. We have the methods `operate_with_self` and `operate_with_other`. For example,
```rust
let g = PallasCurve::generator();
let g2 = g.operate_with_self(2_u16);
let g3 = g.operate_with_other(&g2);
```
`operate_with_self` takes as argument anything that implements the `IsUnsignedInteger` trait. This operator represents scalar multiplication. `operate_with_other` takes as argument another point in the elliptic curve. When we operate this way, the $z$ coordinate in the result may be different from $1$. We can transform it back to affine form by using `to_affine`.
13 changes: 13 additions & 0 deletions examples/shamir_secret_sharing/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "shamir_secret_sharing"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks.git" }
rand = { version = "0.8", features = [ "std", "std_rng" ] }
34 changes: 34 additions & 0 deletions examples/shamir_secret_sharing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Shamir's Secret Sharing

## Usage example

```rust
// Definition of the secret
// Creation of 6 shares
// 3 shares will be used to recover the secret
let sss = ShamirSecretSharing {
secret: FE::new(1234),
n: 6,
k: 3,
};

// Polynomial calculation
let polynomial = sss.calculate_polynomial();

// Produce shares
let shares = sss.generating_shares(polynomial.clone());

// Specify the x and y coordinates of the shares to use
let shares_to_use_x = vec![shares.x[1], shares.x[3], shares.x[4]];
let shares_to_use_y = vec![shares.y[1], shares.y[3], shares.y[4]];

// Interpolation
let poly_2 = sss.reconstructing(shares_to_use_x, shares_to_use_y);

// Recover the free coefficient of the polynomial
let secret_recovered = sss.recover(&poly_2);

// Verifications
assert_eq!(polynomial, poly_2);
assert_eq!(sss.secret, secret_recovered);
```
1 change: 1 addition & 0 deletions examples/shamir_secret_sharing/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod shamir_secret_sharing;
Loading

0 comments on commit 65d4774

Please sign in to comment.