Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 40 additions & 0 deletions .github/workflows/wasm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: WASM

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
wasm:
name: Build, lint, test
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install stable toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'

- name: Install wasm-pack
uses: taiki-e/install-action@v2
with:
tool: wasm-pack

- name: Build with wasm-pack
working-directory: twenty-first
run: wasm-pack build --release --target nodejs

- name: Run wasm-pack tests
working-directory: twenty-first
run: wasm-pack test --node
45 changes: 15 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,25 @@ A collection of cryptography primitives written in Rust.
This library contains primarily the following cryptographic primitives:

- The Tip5 hash function
- [The Tip5 Hash Function for Recursive STARKs](https://eprint.iacr.org/2023/107)
- [The Tip5 Hash Function for Recursive STARKs](https://eprint.iacr.org/2023/107)
- Lattice-crypto
- arithmetic for the quotient ring $\mathbb{F}_ p[X] / \langle X^{64} + 1 \rangle$
- arithmetic for modules over this quotient ring
- a IND-CCA2-secure key encapsulation mechanism
- [Lattice-Based Cryptography in Miden VM](https://eprint.iacr.org/2022/1041)
- arithmetic for the quotient ring $\mathbb{F}_ p[X] / \langle X^{64} + 1 \rangle$
- arithmetic for modules over this quotient ring
- a IND-CCA2-secure key encapsulation mechanism
- [Lattice-Based Cryptography in Miden VM](https://eprint.iacr.org/2022/1041)
- `BFieldElement`, `XFieldElement`
- The prime-field type $\mathbb{F}_p$ where $p = 2^{64} - 2^{32} + 1$
- The extension field $\mathbb{F}_p[x]/(x^3 - x + 1)$
- A codec trait for encoding and decoding structs as `Vec`s of `BFieldElement`
- [An efficient prime for number-theoretic transforms](https://cp4space.hatsya.com/2021/09/01/an-efficient-prime-for-number-theoretic-transforms/)
- The prime-field type $\mathbb{F}_p$ where $p = 2^{64} - 2^{32} + 1$
- The extension field $\mathbb{F}_p[x]/(x^3 - x + 1)$
- A codec trait for encoding and decoding structs as `Vec`s of `BFieldElement`
- [An efficient prime for number-theoretic transforms](https://cp4space.hatsya.com/2021/09/01/an-efficient-prime-for-number-theoretic-transforms/)
- NTT
- Number Theoretic Transform (discrete Fast Fourier Transform)
- [Anatomy of a STARK, Part 6: Speeding Things Up](https://neptune.cash/learn/stark-anatomy/faster/)
- Univariate and multivariate polynomials
- Number Theoretic Transform (discrete Fast Fourier Transform)
- [Anatomy of a STARK, Part 6: Speeding Things Up](https://neptune.cash/learn/stark-anatomy/faster/)
- Univariate polynomials
- Merkle Trees
- Merkle Mountain Ranges

## Release protocol
## Wasm support

While twenty-first's version is `0.x.y`, releasing a new version:

1. Is the release backwards-compatible?
Then the new version is `0.x.y+1`. Otherwise the new version is `0.x+1.0`.
2. Checkout the last commit on Mjolnir, and run `make bench-publish`. Save the benchmark's result
and verify that there is no performance degradation.
3. Create a commit that increases `version = "0.x.y"` in twenty-first/Cargo.toml.
The commit message should give a one-line summary of each release change. Include the benchmark
result at the bottom.
4. Have a `v0.x.y` [git tag][tag] on this commit created. (`git tag v0.x.y [sha]`, `git push upstream --tags`)
5. Have this commit `cargo publish`ed on [crates.io][crates] and in GitHub [tags][tags].

[tag]: https://git-scm.com/book/en/v2/Git-Basics-Tagging
[tags]: https://github.com/Neptune-Crypto/twenty-first/tags
[crates]: https://crates.io/crates/twenty-first/versions

If you do not have the privilege to create git tags or run `cargo publish`, submit a PR and the merger will take care of these.
The `twenty-first` library can be built for WebAssembly. See the [dedicated readme](twenty-first/README-wasm32.md) for
further information.
9 changes: 8 additions & 1 deletion twenty-first/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
[target.wasm32-unknown-unknown]
rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]
# debuginfo and symbols are stripped to decrease size of generated debug binary.
# Otherwise, the binary would be so huge that the wasm test harness barfs on it.
rustflags = [
"--cfg", "getrandom_backend=\"wasm_js\"",
"-C", "debuginfo=0",
"-C", "strip=symbols",
]
runner = "wasm-bindgen-test-runner"
18 changes: 11 additions & 7 deletions twenty-first/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ readme.workspace = true
keywords = ["polynomial", "merkle-tree", "post-quantum", "algebra", "tip5"]
categories = ["cryptography", "mathematics"]

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
arbitrary = { version = "1", features = ["derive"] }
bfieldcodec_derive = "0.7"
Expand All @@ -34,27 +37,28 @@ sha3 = "^0.10.8"
thiserror = "2.0"
zeroize = { version = "1.8.1", features = ["derive"] }

# deps for wasm32 target arch
[target.'cfg(target_arch = "wasm32")'.dependencies]

# convince getrandom to build for wasm target.
# note that there is also a flag in .cargo/config.toml
getrandom = { version = "0.3", features = ["wasm_js"] }
getrandom = { version = "0.3", features = ["wasm_js"] } # note that there is also a flag in .cargo/config.toml
wasm-bindgen = "=0.2.104"

[dev-dependencies]
bincode = "1.3.3"
blake3 = "1.5.5"
macro_rules_attr = "0.1.3"
test-strategy = "=0.4.3"
trybuild = "1.0"
proptest = { version = "1.7.0", default-features = false, features = ["std"] }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { package = "codspeed-criterion-compat", version = "4.0", features = ["html_reports"] }
proptest = { version = "1.7.0", default-features = false, features = ["std"] }
proptest-arbitrary-interop = "0.1"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
criterion = { version = "0.7.0", default-features = false }
wasm-bindgen-test = "0.3.42"

# Workaround for Rust 1.87.0 (see also: <https://github.com/rust-lang/rust/issues/141048>)
[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-O4", "--enable-bulk-memory"]

[lints]
workspace = true
Expand Down
71 changes: 71 additions & 0 deletions twenty-first/README-wasm32.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Building and Testing `twenty-first` for wasm32

This document provides instructions on how to build and test the `twenty-first` crate for the `wasm32-unknown-unknown`
target, which allows the library to run in WebAssembly environments.

## What is wasm32?

WebAssembly (Wasm) is a binary instruction format for a stack-based virtual machine. The `wasm32-unknown-unknown` target
allows Rust code to be compiled into Wasm, enabling high-performance applications to run directly in web browsers and
other Wasm-compatible environments. This is ideal for bringing computationally intensive tasks, like the cryptographic
operations in `twenty-first`, to the web without sacrificing performance.

For more detailed information, see the official [WebAssembly website](https://webassembly.org/).

## Required Tools and Setup

To build and test for `wasm32`, you need to set up your Rust environment with the correct target and tooling.

### Add the `wasm32` Target

First, add the `wasm32-unknown-unknown` target to your Rust toolchain using `rustup`:

```shell
rustup target add wasm32-unknown-unknown
```

### Install `wasm-pack`

`wasm-pack` is the primary tool for building, testing, and publishing Rust-generated WebAssembly. It coordinates the
build process and handles the interaction with other tools like `wasm-bindgen`.

Install `wasm-pack` using `cargo`:

```shell
cargo install wasm-pack
```

### Install Node.js (for Testing)

Running the `wasm32` test suite requires a JavaScript runtime. `wasm-pack` uses Node.js for this purpose.

You must have Node.js v20 (LTS) or later installed. The `getrandom` crate, a dependency for our tests, requires the Web
Crypto API, which is stable and fully supported in all modern LTS releases of Node.js.

You can download Node.js from the [official Node.js website](https://nodejs.org/) or install it using a version manager
like `nvm`.

## Build and Test Commands

With the environment configured, you can now build and test the crate. Make sure your current working directory is
`twenty-first/twenty-first` before executing any of the commands below.

### Build the Crate

To compile the `twenty-first` crate for WebAssembly, run the following command:

```shell
wasm-pack build --target nodejs
```

This command compiles the crate and generates the necessary JavaScript bindings, placing the output in a `pkg/`
directory.

### Run Tests

To run the test suite for the `wasm32` target, use the `test` command from `wasm-pack`. This command will compile the
tests and execute them using your installed Node.js runtime.

```shell
wasm-pack test --node
```
68 changes: 65 additions & 3 deletions twenty-first/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub mod prelude;
pub mod tip5;
pub mod util_types;

#[cfg(test)]
#[cfg_attr(coverage_nightly, feature(coverage_attribute))]
mod proptest_arbitrary_interop;

// This is needed for `#[derive(BFieldCodec)]` macro to work consistently across crates.
// Specifically:
// From inside the `twenty-first` crate, we need to refer to `twenty-first` by `crate`.
Expand All @@ -40,6 +44,64 @@ pub(crate) mod tests {

use super::*;

/// A crate-specific replacement of the `#[test]` attribute for tests that
/// should also be executed on `wasm` targets (which is almost all tests).
///
/// If you specifically want to exclude a test from `wasm` targets, use the
/// usual `#[test]` attribute instead.
///
/// # Usage
///
/// ```
/// #[macro_rules_attr::apply(test)]
/// fn foo() {
/// assert_eq!(4, 2 + 2);
/// }
/// ```
macro_rules! test {
($item:item) => {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
$item
};
}
pub(crate) use test;

/// A crate-specific replacement of the `#[test_strategy::proptest]`
/// attribute for tests that should also be executed on `wasm` targets
/// (which is almost all tests).
///
/// If you specifically want to exclude a test from `wasm` targets, use the
/// usual `#[test_strategy::proptest]` attribute instead.
///
/// # Usage
///
/// ```
/// # use proptest::prop_assert_eq;
/// #[macro_rules_attr::apply(proptest)]
/// fn foo(#[strategy(0..=42)] x: i32) {
/// prop_assert_eq!(2 * x, x + x);
/// }
/// ```
///
/// If you want to configure the test, use the usual syntax defined by
/// [`test_strategy`]:
/// ```
/// # use proptest::prop_assert_eq;
/// #[macro_rules_attr::apply(proptest(cases = 10, max_local_rejects = 5))]
/// fn foo(#[strategy(0..=42)] x: i32) {
/// prop_assert_eq!(2 * x, x + x);
/// }
/// ```
macro_rules! proptest {
($item:item $(($($config:tt)*))?) => {
#[test_strategy::proptest $(($($config)*))?]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
$item
};
}
pub(crate) use proptest;

/// The compiler automatically adds any applicable auto trait (all of which are
/// marker traits) to self-defined types. This implies that these trait bounds
/// might vanish if the necessary pre-conditions are no longer met. That'd be a
Expand All @@ -55,7 +117,7 @@ pub(crate) mod tests {
/// Inspired by “Rust for Rustaceans” by Jon Gjengset.
pub fn implements_usual_auto_traits<T: Sized + Send + Sync + Unpin>() {}

#[test]
#[macro_rules_attr::apply(test)]
fn types_in_prelude_implement_the_usual_auto_traits() {
implements_usual_auto_traits::<BFieldElement>();
implements_usual_auto_traits::<Polynomial<BFieldElement>>();
Expand All @@ -68,7 +130,7 @@ pub(crate) mod tests {
implements_usual_auto_traits::<MmrMembershipProof>();
}

#[test]
#[macro_rules_attr::apply(test)]
fn public_types_implement_the_usual_auto_traits() {
implements_usual_auto_traits::<math::lattice::CyclotomicRingElement>();
implements_usual_auto_traits::<math::lattice::ModuleElement<42>>();
Expand All @@ -85,7 +147,7 @@ pub(crate) mod tests {
>();
}

#[test]
#[macro_rules_attr::apply(test)]
fn errors_implement_the_usual_auto_traits() {
implements_usual_auto_traits::<error::BFieldCodecError>();
implements_usual_auto_traits::<error::PolynomialBFieldCodecError>();
Expand Down
Loading