mirror of
https://github.com/vacp2p/zerokit.git
synced 2026-01-08 21:28:11 -05:00
feat(rln-wasm-utils): extracting identity generation and hash functions into a separate module (#332)
- separated all identity generation functions as separate functions, rather than RLN methods - added BE support - only for these functions so far - covered the functions with tests, as well as conversion to big endian - prepared for publication, but is actually awaiting the initial publication of the RLN module @vinhtc27, please check that everything is correct from the wasm point of view. This module does not require parallel computing, so if there are any unnecessary dependencies, builds, etc., please let me know. --------- Co-authored-by: vinhtc27 <vinhtc27@gmail.com>
This commit is contained in:
committed by
GitHub
parent
578e0507b3
commit
6965cf2852
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
@@ -9,6 +9,7 @@ on:
|
||||
- "!rln/src/**"
|
||||
- "!rln/resources/**"
|
||||
- "!utils/src/**"
|
||||
- "!rln-wasm-utils/**"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
@@ -17,6 +18,7 @@ on:
|
||||
- "!rln/src/**"
|
||||
- "!rln/resources/**"
|
||||
- "!utils/src/**"
|
||||
- "!rln-wasm-utils/**"
|
||||
|
||||
name: Tests
|
||||
|
||||
@@ -124,12 +126,32 @@ jobs:
|
||||
run: cargo make test_parallel --release
|
||||
working-directory: ${{ matrix.crate }}
|
||||
|
||||
rln-wasm-utils-test:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, macos-latest]
|
||||
crate: [rln-wasm-utils]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
timeout-minutes: 60
|
||||
|
||||
name: Test - ${{ matrix.crate }} - ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install stable toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Install dependencies
|
||||
run: make installdeps
|
||||
- name: Test rln-wasm-utils
|
||||
run: cargo make test --release
|
||||
working-directory: ${{ matrix.crate }}
|
||||
|
||||
lint:
|
||||
strategy:
|
||||
matrix:
|
||||
# we run lint tests only on ubuntu
|
||||
platform: [ubuntu-latest]
|
||||
crate: [rln, rln-wasm, utils]
|
||||
crate: [rln, rln-wasm, rln-wasm-utils, utils]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
timeout-minutes: 60
|
||||
|
||||
|
||||
62
.github/workflows/nightly-release.yml
vendored
62
.github/workflows/nightly-release.yml
vendored
@@ -85,8 +85,8 @@ jobs:
|
||||
path: ${{ matrix.target }}-${{ matrix.feature }}-rln.tar.gz
|
||||
retention-days: 2
|
||||
|
||||
browser-rln-wasm:
|
||||
name: Browser build
|
||||
rln-wasm:
|
||||
name: Build rln-wasm
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
components: rust-src
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: wasm-${{ matrix.feature }}
|
||||
key: rln-wasm-${{ matrix.feature }}
|
||||
- name: Install dependencies
|
||||
run: make installdeps
|
||||
- name: Install wasm-pack
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y binaryen
|
||||
- name: Build wasm package
|
||||
- name: Build rln-wasm package
|
||||
run: |
|
||||
if [[ ${{ matrix.feature }} == *parallel* ]]; then
|
||||
env RUSTFLAGS="-C target-feature=+atomics,+bulk-memory,+mutable-globals" \
|
||||
@@ -136,18 +136,64 @@ jobs:
|
||||
|
||||
mkdir release
|
||||
cp -r pkg/* release/
|
||||
tar -czvf browser-rln-wasm-${{ matrix.feature }}.tar.gz release/
|
||||
tar -czvf rln-wasm-${{ matrix.feature }}.tar.gz release/
|
||||
working-directory: rln-wasm
|
||||
- name: Upload archive artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Browser-${{ matrix.feature }}-rln-wasm-archive
|
||||
path: rln-wasm/browser-${{ matrix.feature }}-rln-wasm.tar.gz
|
||||
name: rln-wasm-${{ matrix.feature }}-archive
|
||||
path: rln-wasm/rln-wasm-${{ matrix.feature }}.tar.gz
|
||||
retention-days: 2
|
||||
|
||||
rln-wasm-utils:
|
||||
name: Build rln-wasm-utils
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Install stable toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: wasm32-unknown-unknown
|
||||
- name: Install nightly toolchain
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
targets: wasm32-unknown-unknown
|
||||
components: rust-src
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: rln-wasm-utils
|
||||
- name: Install dependencies
|
||||
run: make installdeps
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
- name: Install binaryen
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y binaryen
|
||||
- name: Build rln-wasm-utils package
|
||||
run: |
|
||||
wasm-pack build --release --target web --scope waku
|
||||
|
||||
sed -i.bak 's/rln-wasm-utils/zerokit-rln-wasm-utils/g' pkg/package.json && rm pkg/package.json.bak
|
||||
|
||||
wasm-opt pkg/rln_wasm_utils_bg.wasm -Oz --strip-debug --strip-dwarf \
|
||||
--remove-unused-module-elements --vacuum -o pkg/rln_wasm_utils_bg.wasm
|
||||
|
||||
mkdir release
|
||||
cp -r pkg/* release/
|
||||
tar -czvf rln-wasm-utils.tar.gz release/
|
||||
working-directory: rln-wasm-utils
|
||||
- name: Upload archive artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rln-wasm-utils-archive
|
||||
path: rln-wasm-utils/rln-wasm-utils.tar.gz
|
||||
retention-days: 2
|
||||
|
||||
prepare-prerelease:
|
||||
name: Prepare pre-release
|
||||
needs: [linux, macos, browser-rln-wasm]
|
||||
needs: [linux, macos, rln-wasm, rln-wasm-utils]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
members = ["rln", "rln-cli", "utils"]
|
||||
exclude = ["rln-wasm"]
|
||||
exclude = ["rln-wasm", "rln-wasm-utils"]
|
||||
resolver = "2"
|
||||
|
||||
# Compilation profile for any non-workspace member.
|
||||
|
||||
@@ -12,7 +12,8 @@ A collection of Zero Knowledge modules written in Rust and designed to be used i
|
||||
Zerokit provides zero-knowledge cryptographic primitives with a focus on performance, security, and usability.
|
||||
The current focus is on Rate-Limiting Nullifier [RLN](https://github.com/Rate-Limiting-Nullifier) implementation.
|
||||
|
||||
Current implementation is based on the following [specification](https://github.com/vacp2p/rfc-index/blob/main/vac/raw/rln-v2.md)
|
||||
Current implementation is based on the following
|
||||
[specification](https://github.com/vacp2p/rfc-index/blob/main/vac/raw/rln-v2.md)
|
||||
and focused on RLNv2 which allows to set a rate limit for the number of messages that can be sent by a user.
|
||||
|
||||
## Features
|
||||
@@ -24,7 +25,8 @@ and focused on RLNv2 which allows to set a rate limit for the number of messages
|
||||
|
||||
## Architecture
|
||||
|
||||
Zerokit currently focuses on RLN (Rate-Limiting Nullifier) implementation using [Circom](https://iden3.io/circom) circuits through ark-circom, providing an alternative to existing native Rust implementations.
|
||||
Zerokit currently focuses on RLN (Rate-Limiting Nullifier) implementation using [Circom](https://iden3.io/circom)
|
||||
circuits through ark-circom, providing an alternative to existing native Rust implementations.
|
||||
|
||||
## Build and Test
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use clap::{Parser, Subcommand};
|
||||
use color_eyre::{eyre::eyre, Report, Result};
|
||||
use rln::{
|
||||
circuit::Fr,
|
||||
hashers::{hash_to_field, poseidon_hash},
|
||||
hashers::{hash_to_field_le, poseidon_hash},
|
||||
protocol::{keygen, prepare_prove_input, prepare_verify_input},
|
||||
public::RLN,
|
||||
utils::{fr_to_bytes_le, generate_input_buffer, IdSecret},
|
||||
@@ -244,8 +244,8 @@ fn main() -> Result<()> {
|
||||
println!("Initializing RLN instance...");
|
||||
print!("\x1B[2J\x1B[1;1H");
|
||||
let mut rln_system = RLNSystem::new()?;
|
||||
let rln_epoch = hash_to_field(b"epoch");
|
||||
let rln_identifier = hash_to_field(b"rln-identifier");
|
||||
let rln_epoch = hash_to_field_le(b"epoch");
|
||||
let rln_identifier = hash_to_field_le(b"rln-identifier");
|
||||
let external_nullifier = poseidon_hash(&[rln_epoch, rln_identifier]);
|
||||
println!("RLN Relay Example:");
|
||||
println!("Message Limit: {MESSAGE_LIMIT}");
|
||||
|
||||
@@ -9,7 +9,7 @@ use clap::{Parser, Subcommand};
|
||||
use color_eyre::{eyre::eyre, Result};
|
||||
use rln::{
|
||||
circuit::{Fr, TEST_TREE_HEIGHT},
|
||||
hashers::{hash_to_field, poseidon_hash, PoseidonHash},
|
||||
hashers::{hash_to_field_le, poseidon_hash, PoseidonHash},
|
||||
protocol::{keygen, prepare_verify_input, rln_witness_from_values, serialize_witness},
|
||||
public::RLN,
|
||||
utils::{fr_to_bytes_le, IdSecret},
|
||||
@@ -128,7 +128,7 @@ impl RLNSystem {
|
||||
};
|
||||
|
||||
let merkle_proof = self.tree.proof(user_index)?;
|
||||
let x = hash_to_field(signal.as_bytes());
|
||||
let x = hash_to_field_le(signal.as_bytes());
|
||||
|
||||
let rln_witness = rln_witness_from_values(
|
||||
identity.identity_secret_hash.clone(),
|
||||
@@ -244,8 +244,8 @@ fn main() -> Result<()> {
|
||||
println!("Initializing RLN instance...");
|
||||
print!("\x1B[2J\x1B[1;1H");
|
||||
let mut rln_system = RLNSystem::new()?;
|
||||
let rln_epoch = hash_to_field(b"epoch");
|
||||
let rln_identifier = hash_to_field(b"rln-identifier");
|
||||
let rln_epoch = hash_to_field_le(b"epoch");
|
||||
let rln_identifier = hash_to_field_le(b"rln-identifier");
|
||||
let external_nullifier = poseidon_hash(&[rln_epoch, rln_identifier]);
|
||||
println!("RLN Stateless Relay Example:");
|
||||
println!("Message Limit: {MESSAGE_LIMIT}");
|
||||
|
||||
21
rln-wasm-utils/.gitignore
vendored
Normal file
21
rln-wasm-utils/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Common files to ignore in Rust projects
|
||||
.DS_Store
|
||||
.idea
|
||||
*.log
|
||||
tmp/
|
||||
|
||||
# Generated by Cargo will have compiled files and executables
|
||||
/target
|
||||
Cargo.lock
|
||||
|
||||
# Generated by rln-wasm
|
||||
pkg/
|
||||
|
||||
# Generated by Nix
|
||||
result
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
35
rln-wasm-utils/Cargo.toml
Normal file
35
rln-wasm-utils/Cargo.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "rln-wasm-utils"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
# TODO: remove this once we have a proper release
|
||||
rln = { path = "../rln", default-features = false, features = ["stateless"] }
|
||||
js-sys = "0.3.77"
|
||||
wasm-bindgen = "0.2.100"
|
||||
rand = "0.8.5"
|
||||
|
||||
# The `console_error_panic_xhook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||
# code size when deploying.
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
getrandom = { version = "0.2.16", features = ["js"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.37"
|
||||
web-sys = { version = "0.3.77", features = ["console"] }
|
||||
ark-std = { version = "0.5.0", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
36
rln-wasm-utils/Makefile.toml
Normal file
36
rln-wasm-utils/Makefile.toml
Normal file
@@ -0,0 +1,36 @@
|
||||
[tasks.build]
|
||||
clear = true
|
||||
dependencies = ["pack_build", "pack_rename", "pack_resize"]
|
||||
|
||||
[tasks.pack_build]
|
||||
command = "wasm-pack"
|
||||
args = ["build", "--release", "--target", "web", "--scope", "waku"]
|
||||
|
||||
[tasks.pack_rename]
|
||||
script = "sed -i.bak 's/rln-wasm-utils/zerokit-rln-wasm-utils/g' pkg/package.json && rm pkg/package.json.bak"
|
||||
|
||||
[tasks.pack_resize]
|
||||
command = "wasm-opt"
|
||||
args = [
|
||||
"pkg/rln_wasm_utils_bg.wasm",
|
||||
"-Oz",
|
||||
"--strip-debug",
|
||||
"--strip-dwarf",
|
||||
"--remove-unused-module-elements",
|
||||
"--vacuum",
|
||||
"-o",
|
||||
"pkg/rln_wasm_utils_bg.wasm",
|
||||
]
|
||||
|
||||
[tasks.test]
|
||||
command = "wasm-pack"
|
||||
args = [
|
||||
"test",
|
||||
"--release",
|
||||
"--node",
|
||||
"--target",
|
||||
"wasm32-unknown-unknown",
|
||||
"--",
|
||||
"--nocapture",
|
||||
]
|
||||
dependencies = ["build"]
|
||||
206
rln-wasm-utils/README.md
Normal file
206
rln-wasm-utils/README.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# RLN WASM Utils
|
||||
|
||||
[](https://badge.fury.io/js/@waku%2Fzerokit-rln-wasm-utils)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
The Zerokit RLN WASM Utils Module provides WebAssembly bindings for Rate-Limiting Nullifier [RLN](https://rfc.vac.dev/spec/32/) cryptographic primitives.
|
||||
This module offers comprehensive functionality for identity generation and hashing needed for RLN applications.
|
||||
|
||||
## Features
|
||||
|
||||
### Identity Generation
|
||||
|
||||
- **Random Identity Generation**: Generate cryptographically secure random identities
|
||||
- **Seeded Identity Generation**: Generate deterministic identities from seeds
|
||||
- **Extended Identity Generation**: Generate extended identities with additional parameters
|
||||
- **Seeded Extended Identity Generation**: Generate deterministic extended identities from seeds
|
||||
- **Endianness Support**: Both little-endian and big-endian serialization support
|
||||
|
||||
### Hashing
|
||||
|
||||
- **Standard Hashing**: Hash arbitrary data to field elements
|
||||
- **Poseidon Hashing**: Advanced cryptographic hashing using Poseidon hash function
|
||||
- **Endianness Support**: Both little-endian and big-endian serialization support
|
||||
|
||||
## API Reference
|
||||
|
||||
### Identity Generation Functions
|
||||
|
||||
#### `generateMembershipKey(isLittleEndian: boolean): Uint8Array`
|
||||
|
||||
Generates a random membership key pair (identity secret and commitment).
|
||||
|
||||
**Inputs:**
|
||||
|
||||
- `isLittleEndian`: Boolean indicating endianness for serialization
|
||||
|
||||
**Outputs:** Serialized identity pair as `Uint8Array` in corresponding endianness
|
||||
|
||||
#### `generateExtendedMembershipKey(isLittleEndian: boolean): Uint8Array`
|
||||
|
||||
Generates an extended membership key with additional parameters.
|
||||
|
||||
**Inputs:**
|
||||
|
||||
- `isLittleEndian`: Boolean indicating endianness for serialization
|
||||
|
||||
**Outputs:** Serialized extended identity tuple as `Uint8Array` in corresponding endianness
|
||||
|
||||
#### `generateSeededMembershipKey(seed: Uint8Array, isLittleEndian: boolean): Uint8Array`
|
||||
|
||||
Generates a deterministic membership key from a seed.
|
||||
|
||||
**Inputs:**
|
||||
|
||||
- `seed`: Seed data as `Uint8Array`
|
||||
- `isLittleEndian`: Boolean indicating endianness for serialization
|
||||
|
||||
**Outputs:** Serialized identity pair as `Uint8Array` in corresponding endianness
|
||||
|
||||
#### `generateSeededExtendedMembershipKey(seed: Uint8Array, isLittleEndian: boolean): Uint8Array`
|
||||
|
||||
Generates a deterministic extended membership key from a seed.
|
||||
|
||||
**Inputs:**
|
||||
|
||||
- `seed`: Seed data as `Uint8Array`
|
||||
- `isLittleEndian`: Boolean indicating endianness for serialization
|
||||
|
||||
**Outputs:** Serialized extended identity tuple as `Uint8Array` in corresponding endianness
|
||||
|
||||
### Hashing Functions
|
||||
|
||||
#### `hash(input: Uint8Array, isLittleEndian: boolean): Uint8Array`
|
||||
|
||||
Hashes input data to a field element.
|
||||
|
||||
**Inputs:**
|
||||
|
||||
- `input`: Input data as `Uint8Array`
|
||||
- `isLittleEndian`: Boolean indicating endianness for serialization
|
||||
|
||||
**Outputs:** Serialized hash result as `Uint8Array` in corresponding endianness
|
||||
|
||||
#### `poseidonHash(input: Uint8Array, isLittleEndian: boolean): Uint8Array`
|
||||
|
||||
Computes Poseidon hash of input field elements.
|
||||
|
||||
**Inputs:**
|
||||
|
||||
- `input`: Serialized field elements as `Uint8Array` (format: length + field elements)
|
||||
- `isLittleEndian`: Boolean indicating endianness for serialization
|
||||
|
||||
**Outputs:** Serialized hash result as `Uint8Array` in corresponding endianness
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### JavaScript/TypeScript
|
||||
|
||||
```javascript
|
||||
import init, {
|
||||
generateMembershipKey,
|
||||
generateSeededMembershipKey,
|
||||
hash,
|
||||
poseidonHash
|
||||
} from '@waku/zerokit-rln-wasm-utils';
|
||||
|
||||
// Initialize the WASM module
|
||||
await init();
|
||||
|
||||
// Generate a random membership key
|
||||
const membershipKey = generateMembershipKey(true); // little-endian
|
||||
console.log('Membership key:', membershipKey);
|
||||
|
||||
// Generate a deterministic membership key from seed
|
||||
const seed = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
const seededKey = generateSeededMembershipKey(seed, true);
|
||||
console.log('Seeded key:', seededKey);
|
||||
|
||||
// Hash some data
|
||||
const input = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
const hashResult = hash(input, true);
|
||||
console.log('Hash result:', hashResult);
|
||||
|
||||
// Poseidon hash with field elements
|
||||
const fieldElements = new Uint8Array([
|
||||
// Length (8 bytes) + field elements (32 bytes each)
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // length = 1
|
||||
// field element data...
|
||||
]);
|
||||
const poseidonResult = poseidonHash(fieldElements, true);
|
||||
console.log('Poseidon hash:', poseidonResult);
|
||||
```
|
||||
|
||||
## Install Dependencies
|
||||
|
||||
> [!NOTE]
|
||||
> This project requires the following tools:
|
||||
>
|
||||
> - `wasm-pack` - for compiling Rust to WebAssembly
|
||||
> - `cargo-make` - for running build commands
|
||||
> - `nvm` - to install and manage Node.js
|
||||
>
|
||||
> Ensure all dependencies are installed before proceeding.
|
||||
|
||||
### Manually
|
||||
|
||||
#### Install `wasm-pack`
|
||||
|
||||
```bash
|
||||
cargo install wasm-pack --version=0.13.1
|
||||
```
|
||||
|
||||
#### Install `cargo-make`
|
||||
|
||||
```bash
|
||||
cargo install cargo-make
|
||||
```
|
||||
|
||||
#### Install `Node.js`
|
||||
|
||||
If you don't have `nvm` (Node Version Manager), install it by following
|
||||
the [installation instructions](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script).
|
||||
|
||||
After installing `nvm`, install and use Node.js `v22.14.0`:
|
||||
|
||||
```bash
|
||||
nvm install 22.14.0
|
||||
nvm use 22.14.0
|
||||
nvm alias default 22.14.0
|
||||
```
|
||||
|
||||
If you already have Node.js installed,
|
||||
check your version with `node -v` command — the version must be strictly greater than 22.
|
||||
|
||||
### Or install everything
|
||||
|
||||
You can run the following command from the root of the repository to install all required dependencies for `zerokit`
|
||||
|
||||
```bash
|
||||
make installdeps
|
||||
```
|
||||
|
||||
## Building the library
|
||||
|
||||
First, navigate to the rln-wasm-utils directory:
|
||||
|
||||
```bash
|
||||
cd rln-wasm-utils
|
||||
```
|
||||
|
||||
Compile rln-wasm-utils for `wasm32-unknown-unknown`:
|
||||
|
||||
```bash
|
||||
cargo make build
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
||||
```bash
|
||||
cargo make test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under both MIT and Apache 2.0 licenses. See the LICENSE files for details.
|
||||
112
rln-wasm-utils/src/lib.rs
Normal file
112
rln-wasm-utils/src/lib.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
use js_sys::Uint8Array;
|
||||
use rln::public::{
|
||||
extended_key_gen, hash, key_gen, poseidon_hash, seeded_extended_key_gen, seeded_key_gen,
|
||||
};
|
||||
use std::vec::Vec;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = generateMembershipKey)]
|
||||
pub fn wasm_key_gen(is_little_endian: bool) -> Result<Uint8Array, String> {
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
if let Err(err) = key_gen(&mut output_data, is_little_endian) {
|
||||
std::mem::forget(output_data);
|
||||
Err(format!(
|
||||
"Msg: could not generate membership keys, Error: {:#?}",
|
||||
err
|
||||
))
|
||||
} else {
|
||||
let result = Uint8Array::from(&output_data[..]);
|
||||
std::mem::forget(output_data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = generateExtendedMembershipKey)]
|
||||
pub fn wasm_extended_key_gen(is_little_endian: bool) -> Result<Uint8Array, String> {
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
if let Err(err) = extended_key_gen(&mut output_data, is_little_endian) {
|
||||
std::mem::forget(output_data);
|
||||
Err(format!(
|
||||
"Msg: could not generate membership keys, Error: {:#?}",
|
||||
err
|
||||
))
|
||||
} else {
|
||||
let result = Uint8Array::from(&output_data[..]);
|
||||
std::mem::forget(output_data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = generateSeededMembershipKey)]
|
||||
pub fn wasm_seeded_key_gen(seed: Uint8Array, is_little_endian: bool) -> Result<Uint8Array, String> {
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
let input_data = &seed.to_vec()[..];
|
||||
if let Err(err) = seeded_key_gen(input_data, &mut output_data, is_little_endian) {
|
||||
std::mem::forget(output_data);
|
||||
Err(format!(
|
||||
"Msg: could not generate membership key, Error: {:#?}",
|
||||
err
|
||||
))
|
||||
} else {
|
||||
let result = Uint8Array::from(&output_data[..]);
|
||||
std::mem::forget(output_data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = generateSeededExtendedMembershipKey)]
|
||||
pub fn wasm_seeded_extended_key_gen(
|
||||
seed: Uint8Array,
|
||||
is_little_endian: bool,
|
||||
) -> Result<Uint8Array, String> {
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
let input_data = &seed.to_vec()[..];
|
||||
if let Err(err) = seeded_extended_key_gen(input_data, &mut output_data, is_little_endian) {
|
||||
std::mem::forget(output_data);
|
||||
Err(format!(
|
||||
"Msg: could not generate membership key, Error: {:#?}",
|
||||
err
|
||||
))
|
||||
} else {
|
||||
let result = Uint8Array::from(&output_data[..]);
|
||||
std::mem::forget(output_data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = hash)]
|
||||
pub fn wasm_hash(input: Uint8Array, is_little_endian: bool) -> Result<Uint8Array, String> {
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
let input_data = &input.to_vec()[..];
|
||||
if let Err(err) = hash(input_data, &mut output_data, is_little_endian) {
|
||||
std::mem::forget(output_data);
|
||||
Err(format!("Msg: could not generate hash, Error: {:#?}", err))
|
||||
} else {
|
||||
let result = Uint8Array::from(&output_data[..]);
|
||||
std::mem::forget(output_data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = poseidonHash)]
|
||||
pub fn wasm_poseidon_hash(input: Uint8Array, is_little_endian: bool) -> Result<Uint8Array, String> {
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
let input_data = &input.to_vec()[..];
|
||||
if let Err(err) = poseidon_hash(input_data, &mut output_data, is_little_endian) {
|
||||
std::mem::forget(output_data);
|
||||
Err(format!(
|
||||
"Msg: could not generate poseidon hash, Error: {:#?}",
|
||||
err
|
||||
))
|
||||
} else {
|
||||
let result = Uint8Array::from(&output_data[..]);
|
||||
std::mem::forget(output_data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
114
rln-wasm-utils/tests/wasm_utils_test.rs
Normal file
114
rln-wasm-utils/tests/wasm_utils_test.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use ark_std::{UniformRand, rand::thread_rng};
|
||||
use rand::Rng;
|
||||
use rln::circuit::Fr;
|
||||
use rln::hashers::{ROUND_PARAMS, hash_to_field_le, poseidon_hash};
|
||||
use rln::protocol::{
|
||||
deserialize_identity_pair_be, deserialize_identity_pair_le, deserialize_identity_tuple_be,
|
||||
deserialize_identity_tuple_le,
|
||||
};
|
||||
use rln::utils::{bytes_le_to_fr, vec_fr_to_bytes_le};
|
||||
use rln_wasm_utils::{
|
||||
wasm_extended_key_gen, wasm_hash, wasm_key_gen, wasm_poseidon_hash,
|
||||
wasm_seeded_extended_key_gen, wasm_seeded_key_gen,
|
||||
};
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_key_gen() {
|
||||
let result_le = wasm_key_gen(true);
|
||||
assert!(result_le.is_ok());
|
||||
deserialize_identity_pair_le(result_le.unwrap().to_vec());
|
||||
|
||||
let result_be = wasm_key_gen(false);
|
||||
assert!(result_be.is_ok());
|
||||
deserialize_identity_pair_be(result_be.unwrap().to_vec());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_extended_key_gen() {
|
||||
let result_le = wasm_extended_key_gen(true);
|
||||
assert!(result_le.is_ok());
|
||||
deserialize_identity_tuple_le(result_le.unwrap().to_vec());
|
||||
|
||||
let result_be = wasm_extended_key_gen(false);
|
||||
assert!(result_be.is_ok());
|
||||
deserialize_identity_tuple_be(result_be.unwrap().to_vec());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_seeded_key_gen() {
|
||||
// Create a test seed
|
||||
let seed_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let seed = js_sys::Uint8Array::from(&seed_data[..]);
|
||||
|
||||
let result_le = wasm_seeded_key_gen(seed.clone(), true);
|
||||
assert!(result_le.is_ok());
|
||||
let fr_le = deserialize_identity_pair_le(result_le.unwrap().to_vec());
|
||||
|
||||
let result_be = wasm_seeded_key_gen(seed, false);
|
||||
assert!(result_be.is_ok());
|
||||
let fr_be = deserialize_identity_pair_be(result_be.unwrap().to_vec());
|
||||
|
||||
assert_eq!(fr_le, fr_be);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_seeded_extended_key_gen() {
|
||||
// Create a test seed
|
||||
let seed_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let seed = js_sys::Uint8Array::from(&seed_data[..]);
|
||||
|
||||
let result_le = wasm_seeded_extended_key_gen(seed.clone(), true);
|
||||
assert!(result_le.is_ok());
|
||||
let fr_le = deserialize_identity_tuple_le(result_le.unwrap().to_vec());
|
||||
|
||||
let result_be = wasm_seeded_extended_key_gen(seed, false);
|
||||
assert!(result_be.is_ok());
|
||||
let fr_be = deserialize_identity_tuple_be(result_be.unwrap().to_vec());
|
||||
|
||||
assert_eq!(fr_le, fr_be);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_hash() {
|
||||
// Create test input data
|
||||
let signal: [u8; 32] = [0; 32];
|
||||
let input = js_sys::Uint8Array::from(&signal[..]);
|
||||
|
||||
let result_le = wasm_hash(input.clone(), true);
|
||||
assert!(result_le.is_ok());
|
||||
|
||||
let serialized_hash = result_le.unwrap().to_vec();
|
||||
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
let hash2 = hash_to_field_le(&signal);
|
||||
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_poseidon_hash() {
|
||||
let mut rng = thread_rng();
|
||||
let number_of_inputs = rng.gen_range(1..ROUND_PARAMS.len());
|
||||
let mut inputs = Vec::with_capacity(number_of_inputs);
|
||||
for _ in 0..number_of_inputs {
|
||||
inputs.push(Fr::rand(&mut rng));
|
||||
}
|
||||
let inputs_ser = vec_fr_to_bytes_le(&inputs);
|
||||
let input = js_sys::Uint8Array::from(&inputs_ser[..]);
|
||||
|
||||
let expected_hash = poseidon_hash(inputs.as_ref());
|
||||
|
||||
let result_le = wasm_poseidon_hash(input.clone(), true);
|
||||
assert!(result_le.is_ok());
|
||||
|
||||
let serialized_hash = result_le.unwrap().to_vec();
|
||||
let (received_hash, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
assert_eq!(received_hash, expected_hash);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ crate-type = ["cdylib", "rlib"]
|
||||
rln = { path = "../rln", version = "0.8.0", default-features = false, features = [
|
||||
"stateless",
|
||||
] }
|
||||
rln-wasm-utils = { path = "../rln-wasm-utils", version = "0.1.0", default-features = false }
|
||||
zerokit_utils = { path = "../utils", version = "0.6.0", default-features = false }
|
||||
num-bigint = { version = "0.4.6", default-features = false }
|
||||
js-sys = "0.3.77"
|
||||
|
||||
@@ -108,11 +108,3 @@ dependencies = ["build_parallel"]
|
||||
|
||||
[tasks.bench]
|
||||
disabled = true
|
||||
|
||||
[tasks.login]
|
||||
command = "wasm-pack"
|
||||
args = ["login"]
|
||||
|
||||
[tasks.publish]
|
||||
command = "wasm-pack"
|
||||
args = ["publish", "--access", "public", "--target", "web"]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use js_sys::{BigInt as JsBigInt, Object, Uint8Array};
|
||||
use num_bigint::BigInt;
|
||||
use rln::public::{hash, poseidon_hash, RLN};
|
||||
use rln::public::RLN;
|
||||
use std::vec::Vec;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -78,40 +78,6 @@ macro_rules! call_bool_method_with_error_msg {
|
||||
}
|
||||
}
|
||||
|
||||
// Macro to execute a function with arbitrary amount of arguments,
|
||||
// First argument is the function to execute
|
||||
// Rest are all other arguments to the method
|
||||
macro_rules! fn_call_with_output_and_error_msg {
|
||||
// this variant is needed for the case when
|
||||
// there are zero other arguments
|
||||
($func:ident, $error_msg:expr) => {
|
||||
{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
if let Err(err) = $func(&mut output_data) {
|
||||
std::mem::forget(output_data);
|
||||
Err(format!("Msg: {:#?}, Error: {:#?}", $error_msg, err))
|
||||
} else {
|
||||
let result = Uint8Array::from(&output_data[..]);
|
||||
std::mem::forget(output_data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
};
|
||||
($func:ident, $error_msg:expr, $( $arg:expr ),* ) => {
|
||||
{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
if let Err(err) = $func($($arg.process()),*, &mut output_data) {
|
||||
std::mem::forget(output_data);
|
||||
Err(format!("Msg: {:#?}, Error: {:#?}", $error_msg, err))
|
||||
} else {
|
||||
let result = Uint8Array::from(&output_data[..]);
|
||||
std::mem::forget(output_data);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
trait ProcessArg {
|
||||
type ReturnType;
|
||||
fn process(self) -> Self::ReturnType;
|
||||
@@ -213,43 +179,6 @@ pub fn wasm_generate_rln_proof_with_witness(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = generateMembershipKey)]
|
||||
pub fn wasm_key_gen(ctx: *const RLNWrapper) -> Result<Uint8Array, String> {
|
||||
call_with_output_and_error_msg!(ctx, key_gen, "could not generate membership keys")
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = generateExtendedMembershipKey)]
|
||||
pub fn wasm_extended_key_gen(ctx: *const RLNWrapper) -> Result<Uint8Array, String> {
|
||||
call_with_output_and_error_msg!(ctx, extended_key_gen, "could not generate membership keys")
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = generateSeededMembershipKey)]
|
||||
pub fn wasm_seeded_key_gen(ctx: *const RLNWrapper, seed: Uint8Array) -> Result<Uint8Array, String> {
|
||||
call_with_output_and_error_msg!(
|
||||
ctx,
|
||||
seeded_key_gen,
|
||||
"could not generate membership key",
|
||||
&seed.to_vec()[..]
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = generateSeededExtendedMembershipKey)]
|
||||
pub fn wasm_seeded_extended_key_gen(
|
||||
ctx: *const RLNWrapper,
|
||||
seed: Uint8Array,
|
||||
) -> Result<Uint8Array, String> {
|
||||
call_with_output_and_error_msg!(
|
||||
ctx,
|
||||
seeded_extended_key_gen,
|
||||
"could not generate membership key",
|
||||
&seed.to_vec()[..]
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = recovedIDSecret)]
|
||||
pub fn wasm_recover_id_secret(
|
||||
@@ -281,17 +210,3 @@ pub fn wasm_verify_with_roots(
|
||||
&roots.to_vec()[..]
|
||||
)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = hash)]
|
||||
pub fn wasm_hash(input: Uint8Array) -> Result<Uint8Array, String> {
|
||||
fn_call_with_output_and_error_msg!(hash, "could not generate hash", &input.to_vec()[..])
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = poseidonHash)]
|
||||
pub fn wasm_poseidon_hash(input: Uint8Array) -> Result<Uint8Array, String> {
|
||||
fn_call_with_output_and_error_msg!(
|
||||
poseidon_hash,
|
||||
"could not generate poseidon hash",
|
||||
&input.to_vec()[..]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
mod tests {
|
||||
use js_sys::{BigInt as JsBigInt, Date, Object, Uint8Array};
|
||||
use rln::circuit::{Fr, TEST_TREE_HEIGHT};
|
||||
use rln::hashers::{hash_to_field, poseidon_hash, PoseidonHash};
|
||||
use rln::hashers::{hash_to_field_le, poseidon_hash, PoseidonHash};
|
||||
use rln::protocol::{prepare_verify_input, rln_witness_from_values, serialize_witness};
|
||||
use rln::utils::{bytes_le_to_fr, fr_to_bytes_le, IdSecret};
|
||||
use rln_wasm::{
|
||||
wasm_generate_rln_proof_with_witness, wasm_key_gen, wasm_new, wasm_rln_witness_to_json,
|
||||
wasm_generate_rln_proof_with_witness, wasm_new, wasm_rln_witness_to_json,
|
||||
wasm_verify_with_roots,
|
||||
};
|
||||
use rln_wasm_utils::wasm_key_gen;
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||
use wasm_bindgen_test::{console_log, wasm_bindgen_test, wasm_bindgen_test_configure};
|
||||
use zerokit_utils::{
|
||||
@@ -121,18 +122,18 @@ mod tests {
|
||||
// Benchmark wasm_key_gen
|
||||
let start_wasm_key_gen = Date::now();
|
||||
for _ in 0..iterations {
|
||||
let _ = wasm_key_gen(rln_instance).expect("Failed to generate keys");
|
||||
let _ = wasm_key_gen(true).expect("Failed to generate keys");
|
||||
}
|
||||
let wasm_key_gen_result = Date::now() - start_wasm_key_gen;
|
||||
|
||||
// Generate identity pair for other benchmarks
|
||||
let mem_keys = wasm_key_gen(rln_instance).expect("Failed to generate keys");
|
||||
let mem_keys = wasm_key_gen(true).expect("Failed to generate keys");
|
||||
let id_key = mem_keys.subarray(0, 32);
|
||||
let (identity_secret_hash, _) = IdSecret::from_bytes_le(&id_key.to_vec());
|
||||
let (id_commitment, _) = bytes_le_to_fr(&mem_keys.subarray(32, 64).to_vec());
|
||||
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
let external_nullifier = poseidon_hash(&[epoch, rln_identifier]);
|
||||
|
||||
let identity_index = tree.leaves_set();
|
||||
@@ -145,7 +146,7 @@ mod tests {
|
||||
|
||||
let message_id = Fr::from(0);
|
||||
let signal: [u8; 32] = [0; 32];
|
||||
let x = hash_to_field(&signal);
|
||||
let x = hash_to_field_le(&signal);
|
||||
|
||||
let merkle_proof: OptimalMerkleProof<PoseidonHash> = tree
|
||||
.proof(identity_index)
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
mod tests {
|
||||
use js_sys::{BigInt as JsBigInt, Date, Object, Uint8Array};
|
||||
use rln::circuit::{Fr, TEST_TREE_HEIGHT};
|
||||
use rln::hashers::{hash_to_field, poseidon_hash, PoseidonHash};
|
||||
use rln::hashers::{hash_to_field_le, poseidon_hash, PoseidonHash};
|
||||
use rln::protocol::{prepare_verify_input, rln_witness_from_values, serialize_witness};
|
||||
use rln::utils::{bytes_le_to_fr, fr_to_bytes_le, IdSecret};
|
||||
use rln_wasm::{
|
||||
wasm_generate_rln_proof_with_witness, wasm_key_gen, wasm_new, wasm_rln_witness_to_json,
|
||||
wasm_generate_rln_proof_with_witness, wasm_new, wasm_rln_witness_to_json,
|
||||
wasm_verify_with_roots,
|
||||
};
|
||||
use rln_wasm_utils::wasm_key_gen;
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||
use wasm_bindgen_test::{console_log, wasm_bindgen_test};
|
||||
use zerokit_utils::{
|
||||
@@ -102,18 +103,18 @@ mod tests {
|
||||
// Benchmark wasm_key_gen
|
||||
let start_wasm_key_gen = Date::now();
|
||||
for _ in 0..iterations {
|
||||
let _ = wasm_key_gen(rln_instance).expect("Failed to generate keys");
|
||||
let _ = wasm_key_gen(true).expect("Failed to generate keys");
|
||||
}
|
||||
let wasm_key_gen_result = Date::now() - start_wasm_key_gen;
|
||||
|
||||
// Generate identity pair for other benchmarks
|
||||
let mem_keys = wasm_key_gen(rln_instance).expect("Failed to generate keys");
|
||||
let mem_keys = wasm_key_gen(true).expect("Failed to generate keys");
|
||||
let id_key = mem_keys.subarray(0, 32);
|
||||
let (identity_secret_hash, _) = IdSecret::from_bytes_le(&id_key.to_vec());
|
||||
let (id_commitment, _) = bytes_le_to_fr(&mem_keys.subarray(32, 64).to_vec());
|
||||
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
let external_nullifier = poseidon_hash(&[epoch, rln_identifier]);
|
||||
|
||||
let identity_index = tree.leaves_set();
|
||||
@@ -126,7 +127,7 @@ mod tests {
|
||||
|
||||
let message_id = Fr::from(0);
|
||||
let signal: [u8; 32] = [0; 32];
|
||||
let x = hash_to_field(&signal);
|
||||
let x = hash_to_field_le(&signal);
|
||||
|
||||
let merkle_proof: OptimalMerkleProof<PoseidonHash> = tree
|
||||
.proof(identity_index)
|
||||
|
||||
@@ -19,6 +19,8 @@ pub enum ConversionError {
|
||||
ToUsize(#[from] TryFromIntError),
|
||||
#[error("{0}")]
|
||||
FromSlice(#[from] TryFromSliceError),
|
||||
#[error("Input data too short: expected at least {expected} bytes, got {actual} bytes")]
|
||||
InsufficientData { expected: usize, actual: usize },
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
|
||||
172
rln/src/ffi.rs
172
rln/src/ffi.rs
@@ -2,7 +2,12 @@
|
||||
|
||||
use std::slice;
|
||||
|
||||
use crate::public::{hash as public_hash, poseidon_hash as public_poseidon_hash, RLN};
|
||||
use crate::public::{
|
||||
extended_key_gen as public_extended_key_gen, hash as public_hash, key_gen as public_key_gen,
|
||||
poseidon_hash as public_poseidon_hash,
|
||||
seeded_extended_key_gen as public_seeded_extended_key_gen,
|
||||
seeded_key_gen as public_seeded_key_gen, RLN,
|
||||
};
|
||||
|
||||
// Macro to call methods with arbitrary amount of arguments,
|
||||
// First argument to the macro is context,
|
||||
@@ -80,23 +85,48 @@ macro_rules! call_with_output_arg {
|
||||
// Second argument is the output buffer argument
|
||||
// The remaining arguments are all other inputs to the method
|
||||
macro_rules! no_ctx_call_with_output_arg {
|
||||
($method:ident, $output_arg:expr, $( $arg:expr ),* ) => {
|
||||
{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
match $method($($arg.process()),*, &mut output_data) {
|
||||
Ok(()) => {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
std::mem::forget(output_data);
|
||||
eprintln!("execution error: {err}");
|
||||
false
|
||||
}
|
||||
($method:ident, $output_arg:expr, $input_arg:expr, $endianness_arg:expr) => {{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
match $method(
|
||||
$input_arg.process(),
|
||||
&mut output_data,
|
||||
$endianness_arg.process(),
|
||||
) {
|
||||
Ok(()) => {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
std::mem::forget(output_data);
|
||||
eprintln!("execution error: {err}");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Macro to call methods with arbitrary amount of arguments,
|
||||
// which are not implemented in a ctx RLN object
|
||||
// First argument is the method to call
|
||||
// Second argument is the output buffer argument
|
||||
// The remaining arguments are all other inputs to the method
|
||||
macro_rules! no_ctx_call_with_output_arg_and_endianness {
|
||||
($method:ident, $output_arg:expr, $endianness_arg:expr) => {{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
match $method(&mut output_data, $endianness_arg.process()) {
|
||||
Ok(()) => {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
std::mem::forget(output_data);
|
||||
eprintln!("execution error: {err}");
|
||||
false
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Macro to call methods with arbitrary amount of arguments,
|
||||
@@ -158,6 +188,13 @@ impl ProcessArg for *mut RLN {
|
||||
}
|
||||
}
|
||||
|
||||
impl ProcessArg for bool {
|
||||
type ReturnType = bool;
|
||||
fn process(self) -> Self::ReturnType {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
///// Buffer struct is taken from
|
||||
///// <https://github.com/celo-org/celo-threshold-bls-rs/blob/master/crates/threshold-bls-ffi/src/ffi.rs>
|
||||
/////
|
||||
@@ -460,38 +497,6 @@ pub extern "C" fn verify_with_roots(
|
||||
////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
////////////////////////////////////////////////////////
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn key_gen(ctx: *const RLN, output_buffer: *mut Buffer) -> bool {
|
||||
call_with_output_arg!(ctx, key_gen, output_buffer)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn seeded_key_gen(
|
||||
ctx: *const RLN,
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
) -> bool {
|
||||
call_with_output_arg!(ctx, seeded_key_gen, output_buffer, input_buffer)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn extended_key_gen(ctx: *const RLN, output_buffer: *mut Buffer) -> bool {
|
||||
call_with_output_arg!(ctx, extended_key_gen, output_buffer)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn seeded_extended_key_gen(
|
||||
ctx: *const RLN,
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
) -> bool {
|
||||
call_with_output_arg!(ctx, seeded_extended_key_gen, output_buffer, input_buffer)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn recover_id_secret(
|
||||
@@ -534,14 +539,77 @@ pub extern "C" fn flush(ctx: *mut RLN) -> bool {
|
||||
call!(ctx, flush)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Utils APIs
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn hash(input_buffer: *const Buffer, output_buffer: *mut Buffer) -> bool {
|
||||
no_ctx_call_with_output_arg!(public_hash, output_buffer, input_buffer)
|
||||
pub extern "C" fn hash(
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
is_little_endian: bool,
|
||||
) -> bool {
|
||||
no_ctx_call_with_output_arg!(public_hash, output_buffer, input_buffer, is_little_endian)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn poseidon_hash(input_buffer: *const Buffer, output_buffer: *mut Buffer) -> bool {
|
||||
no_ctx_call_with_output_arg!(public_poseidon_hash, output_buffer, input_buffer)
|
||||
pub extern "C" fn poseidon_hash(
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
is_little_endian: bool,
|
||||
) -> bool {
|
||||
no_ctx_call_with_output_arg!(
|
||||
public_poseidon_hash,
|
||||
output_buffer,
|
||||
input_buffer,
|
||||
is_little_endian
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn key_gen(output_buffer: *mut Buffer, is_little_endian: bool) -> bool {
|
||||
no_ctx_call_with_output_arg_and_endianness!(public_key_gen, output_buffer, is_little_endian)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn seeded_key_gen(
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
is_little_endian: bool,
|
||||
) -> bool {
|
||||
no_ctx_call_with_output_arg!(
|
||||
public_seeded_key_gen,
|
||||
output_buffer,
|
||||
input_buffer,
|
||||
is_little_endian
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn extended_key_gen(output_buffer: *mut Buffer, is_little_endian: bool) -> bool {
|
||||
no_ctx_call_with_output_arg_and_endianness!(
|
||||
public_extended_key_gen,
|
||||
output_buffer,
|
||||
is_little_endian
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn seeded_extended_key_gen(
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
is_little_endian: bool,
|
||||
) -> bool {
|
||||
no_ctx_call_with_output_arg!(
|
||||
public_seeded_extended_key_gen,
|
||||
output_buffer,
|
||||
input_buffer,
|
||||
is_little_endian
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/// This crate instantiates the Poseidon hash algorithm.
|
||||
use crate::{circuit::Fr, utils::bytes_le_to_fr};
|
||||
use crate::{
|
||||
circuit::Fr,
|
||||
utils::{bytes_be_to_fr, bytes_le_to_fr},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use tiny_keccak::{Hasher, Keccak};
|
||||
use utils::poseidon::Poseidon;
|
||||
@@ -45,7 +48,7 @@ impl utils::merkle_tree::Hasher for PoseidonHash {
|
||||
}
|
||||
|
||||
/// Hashes arbitrary signal to the underlying prime field.
|
||||
pub fn hash_to_field(signal: &[u8]) -> Fr {
|
||||
pub fn hash_to_field_le(signal: &[u8]) -> Fr {
|
||||
// We hash the input signal using Keccak256
|
||||
let mut hash = [0; 32];
|
||||
let mut hasher = Keccak::v256();
|
||||
@@ -56,3 +59,19 @@ pub fn hash_to_field(signal: &[u8]) -> Fr {
|
||||
let (el, _) = bytes_le_to_fr(hash.as_ref());
|
||||
el
|
||||
}
|
||||
|
||||
/// Hashes arbitrary signal to the underlying prime field.
|
||||
pub fn hash_to_field_be(signal: &[u8]) -> Fr {
|
||||
// We hash the input signal using Keccak256
|
||||
let mut hash = [0; 32];
|
||||
let mut hasher = Keccak::v256();
|
||||
hasher.update(signal);
|
||||
hasher.finalize(&mut hash);
|
||||
|
||||
// Reverse the bytes to get big endian representation
|
||||
hash.reverse();
|
||||
|
||||
// We export the hash as a field element
|
||||
let (el, _) = bytes_be_to_fr(hash.as_ref());
|
||||
el
|
||||
}
|
||||
|
||||
@@ -9,11 +9,12 @@ use {
|
||||
|
||||
use crate::circuit::{calculate_rln_witness, qap::CircomReduction, Curve};
|
||||
use crate::error::{ComputeIdSecretError, ProofError, ProtocolError};
|
||||
use crate::hashers::{hash_to_field, poseidon_hash};
|
||||
use crate::hashers::{hash_to_field_le, poseidon_hash};
|
||||
use crate::public::RLN_IDENTIFIER;
|
||||
use crate::utils::{
|
||||
bytes_le_to_fr, bytes_le_to_vec_fr, bytes_le_to_vec_u8, fr_byte_size, fr_to_bytes_le,
|
||||
normalize_usize, to_bigint, vec_fr_to_bytes_le, vec_u8_to_bytes_le, FrOrSecret, IdSecret,
|
||||
bytes_be_to_fr, bytes_le_to_fr, bytes_le_to_vec_fr, bytes_le_to_vec_u8, fr_byte_size,
|
||||
fr_to_bytes_le, normalize_usize_le, to_bigint, vec_fr_to_bytes_le, vec_u8_to_bytes_le,
|
||||
FrOrSecret, IdSecret,
|
||||
};
|
||||
use ark_bn254::{Fr, FrConfig};
|
||||
use ark_ff::{AdditiveGroup, Fp, MontBackend};
|
||||
@@ -71,14 +72,21 @@ pub fn deserialize_field_element(serialized: Vec<u8>) -> Fr {
|
||||
element
|
||||
}
|
||||
|
||||
pub fn deserialize_identity_pair(serialized: Vec<u8>) -> (Fr, Fr) {
|
||||
pub fn deserialize_identity_pair_le(serialized: Vec<u8>) -> (Fr, Fr) {
|
||||
let (identity_secret_hash, read) = bytes_le_to_fr(&serialized);
|
||||
let (id_commitment, _) = bytes_le_to_fr(&serialized[read..]);
|
||||
|
||||
(identity_secret_hash, id_commitment)
|
||||
}
|
||||
|
||||
pub fn deserialize_identity_tuple(serialized: Vec<u8>) -> (Fr, Fr, Fr, Fr) {
|
||||
pub fn deserialize_identity_pair_be(serialized: Vec<u8>) -> (Fr, Fr) {
|
||||
let (identity_secret_hash, read) = bytes_be_to_fr(&serialized);
|
||||
let (id_commitment, _) = bytes_be_to_fr(&serialized[read..]);
|
||||
|
||||
(identity_secret_hash, id_commitment)
|
||||
}
|
||||
|
||||
pub fn deserialize_identity_tuple_le(serialized: Vec<u8>) -> (Fr, Fr, Fr, Fr) {
|
||||
let mut all_read = 0;
|
||||
|
||||
let (identity_trapdoor, read) = bytes_le_to_fr(&serialized[all_read..]);
|
||||
@@ -100,6 +108,28 @@ pub fn deserialize_identity_tuple(serialized: Vec<u8>) -> (Fr, Fr, Fr, Fr) {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deserialize_identity_tuple_be(serialized: Vec<u8>) -> (Fr, Fr, Fr, Fr) {
|
||||
let mut all_read = 0;
|
||||
|
||||
let (identity_trapdoor, read) = bytes_be_to_fr(&serialized[all_read..]);
|
||||
all_read += read;
|
||||
|
||||
let (identity_nullifier, read) = bytes_be_to_fr(&serialized[all_read..]);
|
||||
all_read += read;
|
||||
|
||||
let (identity_secret_hash, read) = bytes_be_to_fr(&serialized[all_read..]);
|
||||
all_read += read;
|
||||
|
||||
let (identity_commitment, _) = bytes_be_to_fr(&serialized[all_read..]);
|
||||
|
||||
(
|
||||
identity_trapdoor,
|
||||
identity_nullifier,
|
||||
identity_secret_hash,
|
||||
identity_commitment,
|
||||
)
|
||||
}
|
||||
|
||||
/// Serializes witness
|
||||
///
|
||||
/// # Errors
|
||||
@@ -223,7 +253,7 @@ pub fn proof_inputs_to_rln_witness(
|
||||
let path_elements = merkle_proof.get_path_elements();
|
||||
let identity_path_index = merkle_proof.get_path_index();
|
||||
|
||||
let x = hash_to_field(&signal);
|
||||
let x = hash_to_field_le(&signal);
|
||||
|
||||
Ok((
|
||||
RLNWitnessInput {
|
||||
@@ -270,15 +300,15 @@ pub fn random_rln_witness(tree_height: usize) -> RLNWitnessInput {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let identity_secret = IdSecret::rand(&mut rng);
|
||||
let x = hash_to_field(&rng.gen::<[u8; 32]>());
|
||||
let epoch = hash_to_field(&rng.gen::<[u8; 32]>());
|
||||
let rln_identifier = hash_to_field(RLN_IDENTIFIER); //hash_to_field(&rng.gen::<[u8; 32]>());
|
||||
let x = hash_to_field_le(&rng.gen::<[u8; 32]>());
|
||||
let epoch = hash_to_field_le(&rng.gen::<[u8; 32]>());
|
||||
let rln_identifier = hash_to_field_le(RLN_IDENTIFIER); //hash_to_field(&rng.gen::<[u8; 32]>());
|
||||
|
||||
let mut path_elements: Vec<Fr> = Vec::new();
|
||||
let mut identity_path_index: Vec<u8> = Vec::new();
|
||||
|
||||
for _ in 0..tree_height {
|
||||
path_elements.push(hash_to_field(&rng.gen::<[u8; 32]>()));
|
||||
path_elements.push(hash_to_field_le(&rng.gen::<[u8; 32]>()));
|
||||
identity_path_index.push(rng.gen_range(0..2) as u8);
|
||||
}
|
||||
|
||||
@@ -395,11 +425,11 @@ pub fn prepare_prove_input(
|
||||
let mut serialized = Vec::with_capacity(fr_byte_size() * 4 + 16 + signal.len()); // length of 4 fr elements + 16 bytes (id_index + len) + signal length
|
||||
|
||||
serialized.extend_from_slice(&identity_secret.to_bytes_le());
|
||||
serialized.extend_from_slice(&normalize_usize(id_index));
|
||||
serialized.extend_from_slice(&normalize_usize_le(id_index));
|
||||
serialized.extend_from_slice(&fr_to_bytes_le(&user_message_limit));
|
||||
serialized.extend_from_slice(&fr_to_bytes_le(&message_id));
|
||||
serialized.extend_from_slice(&fr_to_bytes_le(&external_nullifier));
|
||||
serialized.extend_from_slice(&normalize_usize(signal.len()));
|
||||
serialized.extend_from_slice(&normalize_usize_le(signal.len()));
|
||||
serialized.extend_from_slice(signal);
|
||||
|
||||
serialized
|
||||
@@ -414,7 +444,7 @@ pub fn prepare_verify_input(proof_data: Vec<u8>, signal: &[u8]) -> Vec<u8> {
|
||||
let mut serialized = Vec::with_capacity(proof_data.len() + 8 + signal.len());
|
||||
|
||||
serialized.extend(proof_data);
|
||||
serialized.extend_from_slice(&normalize_usize(signal.len()));
|
||||
serialized.extend_from_slice(&normalize_usize_le(signal.len()));
|
||||
serialized.extend_from_slice(signal);
|
||||
|
||||
serialized
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use crate::circuit::{zkey_from_raw, Curve, Fr};
|
||||
use crate::hashers::{hash_to_field, poseidon_hash as utils_poseidon_hash};
|
||||
use crate::hashers::{hash_to_field_le, poseidon_hash as utils_poseidon_hash};
|
||||
use crate::protocol::{
|
||||
compute_id_secret, deserialize_proof_values, deserialize_witness, extended_keygen,
|
||||
extended_seeded_keygen, keygen, proof_values_from_witness, rln_witness_to_bigint_json,
|
||||
rln_witness_to_json, seeded_keygen, serialize_proof_values, verify_proof,
|
||||
};
|
||||
use crate::utils::{bytes_le_to_fr, bytes_le_to_vec_fr, fr_byte_size, fr_to_bytes_le};
|
||||
use crate::utils::{
|
||||
bytes_be_to_vec_fr, bytes_le_to_fr, bytes_le_to_vec_fr, fr_byte_size, fr_to_bytes_be,
|
||||
fr_to_bytes_le,
|
||||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use {
|
||||
crate::{
|
||||
@@ -729,10 +732,12 @@ impl RLN {
|
||||
////////////////////////////////////////////////////////
|
||||
// zkSNARK APIs
|
||||
////////////////////////////////////////////////////////
|
||||
/// Computes a zkSNARK RLN proof using a [`RLNWitnessInput`].
|
||||
/// Computes a zkSNARK RLN proof using a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `input_data`: a reader for the serialization of a [`RLNWitnessInput`] object, containing the public and private inputs to the ZK circuits (serialization done using [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness))
|
||||
/// - `input_data`: a reader for the serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput)
|
||||
/// object, containing the public and private inputs to the ZK circuits (serialization done using
|
||||
/// [`serialize_witness`])
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the zkSNARK proof
|
||||
@@ -987,7 +992,7 @@ impl RLN {
|
||||
let signal: Vec<u8> = serialized[all_read..all_read + signal_len].to_vec();
|
||||
|
||||
let verified = verify_proof(&self.verification_key, &proof, &proof_values)?;
|
||||
let x = hash_to_field(&signal);
|
||||
let x = hash_to_field_le(&signal);
|
||||
|
||||
// Consistency checks to counter proof tampering
|
||||
Ok(verified && (self.tree.root() == proof_values.root) && (x == proof_values.x))
|
||||
@@ -1071,7 +1076,7 @@ impl RLN {
|
||||
let verified = verify_proof(&self.verification_key, &proof, &proof_values)?;
|
||||
|
||||
// First consistency checks to counter proof tampering
|
||||
let x = hash_to_field(&signal);
|
||||
let x = hash_to_field_le(&signal);
|
||||
let partial_result = verified && (x == proof_values.x);
|
||||
|
||||
// We skip root validation if proof is already invalid
|
||||
@@ -1113,151 +1118,6 @@ impl RLN {
|
||||
////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns an identity secret and identity commitment pair.
|
||||
///
|
||||
/// The identity commitment is the Poseidon hash of the identity secret.
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the identity secret and identity commitment (serialization done with `rln::utils::fr_to_bytes_le`)
|
||||
///
|
||||
/// Example
|
||||
/// ```
|
||||
/// use rln::protocol::*;
|
||||
///
|
||||
/// // We generate an identity pair
|
||||
/// let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// rln.key_gen(&mut buffer).unwrap();
|
||||
///
|
||||
/// // We serialize_compressed the keygen output
|
||||
/// let (identity_secret_hash, id_commitment) = deserialize_identity_pair(buffer.into_inner());
|
||||
/// ```
|
||||
pub fn key_gen<W: Write>(&self, mut output_data: W) -> Result<(), RLNError> {
|
||||
let (identity_secret_hash, id_commitment) = keygen();
|
||||
output_data.write_all(&identity_secret_hash.to_bytes_le())?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns an identity trapdoor, nullifier, secret and commitment tuple.
|
||||
///
|
||||
/// The identity secret is the Poseidon hash of the identity trapdoor and identity nullifier.
|
||||
///
|
||||
/// The identity commitment is the Poseidon hash of the identity secret.
|
||||
///
|
||||
/// Generated credentials are compatible with [Semaphore](https://semaphore.appliedzkp.org/docs/guides/identities)'s credentials.
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the identity trapdoor, identity nullifier, identity secret and identity commitment (serialization done with `rln::utils::fr_to_bytes_le`)
|
||||
///
|
||||
/// Example
|
||||
/// ```
|
||||
/// use rln::protocol::*;
|
||||
///
|
||||
/// // We generate an identity tuple
|
||||
/// let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// rln.extended_key_gen(&mut buffer).unwrap();
|
||||
///
|
||||
/// // We serialize_compressed the keygen output
|
||||
/// let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) = deserialize_identity_tuple(buffer.into_inner());
|
||||
/// ```
|
||||
pub fn extended_key_gen<W: Write>(&self, mut output_data: W) -> Result<(), RLNError> {
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
extended_keygen();
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_trapdoor))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_nullifier))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns an identity secret and identity commitment pair generated using a seed.
|
||||
///
|
||||
/// The identity commitment is the Poseidon hash of the identity secret.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `input_data`: a reader for the byte vector containing the seed
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the identity secret and identity commitment (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le))
|
||||
///
|
||||
/// Example
|
||||
/// ```
|
||||
/// use rln::protocol::*;
|
||||
///
|
||||
/// let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
///
|
||||
/// let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
/// let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// rln.seeded_key_gen(&mut input_buffer, &mut output_buffer)
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // We serialize_compressed the keygen output
|
||||
/// let (identity_secret_hash, id_commitment) = deserialize_identity_pair(output_buffer.into_inner());
|
||||
/// ```
|
||||
pub fn seeded_key_gen<R: Read, W: Write>(
|
||||
&self,
|
||||
mut input_data: R,
|
||||
mut output_data: W,
|
||||
) -> Result<(), RLNError> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let (identity_secret_hash, id_commitment) = seeded_keygen(&serialized);
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns an identity trapdoor, nullifier, secret and commitment tuple generated using a seed.
|
||||
///
|
||||
/// The identity secret is the Poseidon hash of the identity trapdoor and identity nullifier.
|
||||
///
|
||||
/// The identity commitment is the Poseidon hash of the identity secret.
|
||||
///
|
||||
/// Generated credentials are compatible with [Semaphore](https://semaphore.appliedzkp.org/docs/guides/identities)'s credentials.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `input_data`: a reader for the byte vector containing the seed
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the identity trapdoor, identity nullifier, identity secret and identity commitment (serialization done with `rln::utils::fr_to_bytes_le`)
|
||||
///
|
||||
/// Example
|
||||
/// ```
|
||||
/// use rln::protocol::*;
|
||||
///
|
||||
/// let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
///
|
||||
/// let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
/// let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// rln.seeded_key_gen(&mut input_buffer, &mut output_buffer)
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // We serialize_compressed the keygen output
|
||||
/// let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) = deserialize_identity_tuple(buffer.into_inner());
|
||||
/// ```
|
||||
pub fn seeded_extended_key_gen<R: Read, W: Write>(
|
||||
&self,
|
||||
mut input_data: R,
|
||||
mut output_data: W,
|
||||
) -> Result<(), RLNError> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
extended_seeded_keygen(&serialized);
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_trapdoor))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_nullifier))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Recovers the identity secret from two set of proof values computed for same secret in same epoch with same rln identifier.
|
||||
///
|
||||
/// Input values are:
|
||||
@@ -1331,12 +1191,12 @@ impl RLN {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the serialization of a [`RLNWitnessInput`] populated from the identity secret, the Merkle tree index, the user message limit, the message id, the external nullifier (which include epoch and rln identifier) and signal.
|
||||
/// Returns the serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) populated from the identity secret, the Merkle tree index, the user message limit, the message id, the external nullifier (which include epoch and rln identifier) and signal.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `input_data`: a reader for the serialization of `[ identity_secret<32> | id_index<8> | user_message_limit<32> | message_id<32> | external_nullifier<32> | signal_len<8> | signal<var> ]`
|
||||
///
|
||||
/// The function returns the corresponding [`RLNWitnessInput`] object serialized using [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness).
|
||||
/// The function returns the corresponding [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object serialized using [`serialize_witness`].
|
||||
#[cfg(not(feature = "stateless"))]
|
||||
pub fn get_serialized_rln_witness<R: Read>(
|
||||
&mut self,
|
||||
@@ -1350,12 +1210,13 @@ impl RLN {
|
||||
serialize_witness(&rln_witness).map_err(RLNError::Protocol)
|
||||
}
|
||||
|
||||
/// Converts a byte serialization of a [`RLNWitnessInput`] object to the corresponding JSON serialization.
|
||||
/// Converts a byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object to the corresponding JSON serialization.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`] object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)).
|
||||
/// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput)
|
||||
/// object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)).
|
||||
///
|
||||
/// The function returns the corresponding JSON encoding of the input [`RLNWitnessInput`] object.
|
||||
/// The function returns the corresponding JSON encoding of the input.
|
||||
pub fn get_rln_witness_json(
|
||||
&mut self,
|
||||
serialized_witness: &[u8],
|
||||
@@ -1364,13 +1225,15 @@ impl RLN {
|
||||
rln_witness_to_json(&rln_witness)
|
||||
}
|
||||
|
||||
/// Converts a byte serialization of a [`RLNWitnessInput`] object to the corresponding JSON serialization.
|
||||
/// Converts a byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object
|
||||
/// to the corresponding JSON serialization.
|
||||
/// Before serialization the data will be translated into big int for further calculation in the witness calculator.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`] object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)).
|
||||
/// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput)
|
||||
/// object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)).
|
||||
///
|
||||
/// The function returns the corresponding JSON encoding of the input [`RLNWitnessInput`] object.
|
||||
/// The function returns the corresponding JSON encoding of the input [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object.
|
||||
pub fn get_rln_witness_bigint_json(
|
||||
&mut self,
|
||||
serialized_witness: &[u8],
|
||||
@@ -1428,12 +1291,18 @@ impl Default for RLN {
|
||||
pub fn hash<R: Read, W: Write>(
|
||||
mut input_data: R,
|
||||
mut output_data: W,
|
||||
is_little_endian: bool,
|
||||
) -> Result<(), std::io::Error> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let hash = hash_to_field(&serialized);
|
||||
output_data.write_all(&fr_to_bytes_le(&hash))?;
|
||||
if is_little_endian {
|
||||
let hash = hash_to_field_le(&serialized);
|
||||
output_data.write_all(&fr_to_bytes_le(&hash))?;
|
||||
} else {
|
||||
let hash = hash_to_field_le(&serialized);
|
||||
output_data.write_all(&fr_to_bytes_be(&hash))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1464,13 +1333,208 @@ pub fn hash<R: Read, W: Write>(
|
||||
pub fn poseidon_hash<R: Read, W: Write>(
|
||||
mut input_data: R,
|
||||
mut output_data: W,
|
||||
is_little_endian: bool,
|
||||
) -> Result<(), RLNError> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let (inputs, _) = bytes_le_to_vec_fr(&serialized)?;
|
||||
let hash = utils_poseidon_hash(inputs.as_ref());
|
||||
output_data.write_all(&fr_to_bytes_le(&hash))?;
|
||||
if is_little_endian {
|
||||
let (inputs, _) = bytes_le_to_vec_fr(&serialized)?;
|
||||
let hash = utils_poseidon_hash(inputs.as_ref());
|
||||
output_data.write_all(&fr_to_bytes_le(&hash))?;
|
||||
} else {
|
||||
let (inputs, _) = bytes_be_to_vec_fr(&serialized)?;
|
||||
let hash = utils_poseidon_hash(inputs.as_ref());
|
||||
output_data.write_all(&fr_to_bytes_be(&hash))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate an identity which is composed of an identity secret and identity commitment.
|
||||
/// The identity secret is a random field element.
|
||||
/// The identity commitment is the Poseidon hash of the identity secret.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// - `output_data`: a writer receiving the serialization of
|
||||
/// the identity secret and identity commitment in correct endianness.
|
||||
/// - `is_little_endian`: a boolean indicating whether the identity secret and identity commitment
|
||||
/// should be serialized in little endian or big endian.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rln::protocol::*;
|
||||
///
|
||||
/// // We generate an identity pair
|
||||
/// let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// let is_little_endian = true;
|
||||
/// key_gen(&mut buffer, is_little_endian).unwrap();
|
||||
///
|
||||
/// // We serialize_compressed the keygen output
|
||||
/// let (identity_secret_hash, id_commitment) = deserialize_identity_pair_le(buffer.into_inner());
|
||||
/// ```
|
||||
pub fn key_gen<W: Write>(mut output_data: W, is_little_endian: bool) -> Result<(), RLNError> {
|
||||
let (identity_secret_hash, id_commitment) = keygen();
|
||||
if is_little_endian {
|
||||
output_data.write_all(&identity_secret_hash.to_bytes_le())?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment))?;
|
||||
} else {
|
||||
output_data.write_all(&identity_secret_hash.to_bytes_be())?;
|
||||
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate an identity which is composed of an identity trapdoor, nullifier, secret and commitment.
|
||||
/// The identity secret is the Poseidon hash of the identity trapdoor and identity nullifier.
|
||||
/// The identity commitment is the Poseidon hash of the identity secret.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// - `output_data`: a writer receiving the serialization of
|
||||
/// the identity trapdoor, nullifier, secret and commitment in correct endianness.
|
||||
/// - `is_little_endian`: a boolean indicating whether the identity trapdoor, nullifier, secret and commitment
|
||||
/// should be serialized in little endian or big endian.
|
||||
///
|
||||
/// Generated credentials are compatible with
|
||||
/// [Semaphore](https://semaphore.appliedzkp.org/docs/guides/identities)'s credentials.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rln::protocol::*;
|
||||
///
|
||||
/// // We generate an identity tuple
|
||||
/// let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// let is_little_endian = true;
|
||||
/// extended_key_gen(&mut buffer, is_little_endian).unwrap();
|
||||
///
|
||||
/// // We serialize_compressed the keygen output
|
||||
/// let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment)
|
||||
/// = deserialize_identity_tuple_le(buffer.into_inner());
|
||||
/// ```
|
||||
pub fn extended_key_gen<W: Write>(
|
||||
mut output_data: W,
|
||||
is_little_endian: bool,
|
||||
) -> Result<(), RLNError> {
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
extended_keygen();
|
||||
if is_little_endian {
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_trapdoor))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_nullifier))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment))?;
|
||||
} else {
|
||||
output_data.write_all(&fr_to_bytes_be(&identity_trapdoor))?;
|
||||
output_data.write_all(&fr_to_bytes_be(&identity_nullifier))?;
|
||||
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate an identity which is composed of an identity secret and identity commitment using a seed.
|
||||
/// The identity secret is a random field element,
|
||||
/// where RNG is instantiated using 20 rounds of ChaCha seeded with the hash of the input.
|
||||
/// The identity commitment is the Poseidon hash of the identity secret.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// - `input_data`: a reader for the byte vector containing the seed
|
||||
/// - `output_data`: a writer receiving the serialization of
|
||||
/// the identity secret and identity commitment in correct endianness.
|
||||
/// - `is_little_endian`: a boolean indicating whether the identity secret and identity commitment
|
||||
/// should be serialized in little endian or big endian.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rln::protocol::*;
|
||||
///
|
||||
/// let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
///
|
||||
/// let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
/// let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// let is_little_endian = true;
|
||||
/// seeded_key_gen(&mut input_buffer, &mut output_buffer, is_little_endian).unwrap();
|
||||
///
|
||||
///
|
||||
/// // We serialize_compressed the keygen output
|
||||
/// let (identity_secret_hash, id_commitment) = deserialize_identity_pair_le(output_buffer.into_inner());
|
||||
/// ```
|
||||
pub fn seeded_key_gen<R: Read, W: Write>(
|
||||
mut input_data: R,
|
||||
mut output_data: W,
|
||||
is_little_endian: bool,
|
||||
) -> Result<(), RLNError> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let (identity_secret_hash, id_commitment) = seeded_keygen(&serialized);
|
||||
if is_little_endian {
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment))?;
|
||||
} else {
|
||||
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate an identity which is composed of an identity trapdoor, nullifier, secret and commitment using a seed.
|
||||
/// The identity trapdoor and nullifier are random field elements,
|
||||
/// where RNG is instantiated using 20 rounds of ChaCha seeded with the hash of the input.
|
||||
/// The identity secret is the Poseidon hash of the identity trapdoor and identity nullifier.
|
||||
/// The identity commitment is the Poseidon hash of the identity secret.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// - `input_data`: a reader for the byte vector containing the seed
|
||||
/// - `output_data`: a writer receiving the serialization of
|
||||
/// the identity trapdoor, nullifier, secret and commitment in correct endianness.
|
||||
/// - `is_little_endian`: a boolean indicating whether the identity trapdoor, nullifier, secret and commitment
|
||||
/// should be serialized in little endian or big endian.
|
||||
///
|
||||
/// Generated credentials are compatible with
|
||||
/// [Semaphore](https://semaphore.appliedzkp.org/docs/guides/identities)'s credentials.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rln::protocol::*;
|
||||
///
|
||||
/// let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
///
|
||||
/// let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
/// let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// let is_little_endian = true;
|
||||
/// seeded_extended_key_gen(&mut input_buffer, &mut output_buffer, is_little_endian).unwrap();
|
||||
///
|
||||
/// // We serialize_compressed the keygen output
|
||||
/// let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) = deserialize_identity_tuple(buffer.into_inner());
|
||||
/// ```
|
||||
pub fn seeded_extended_key_gen<R: Read, W: Write>(
|
||||
mut input_data: R,
|
||||
mut output_data: W,
|
||||
is_little_endian: bool,
|
||||
) -> Result<(), RLNError> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
extended_seeded_keygen(&serialized);
|
||||
if is_little_endian {
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_trapdoor))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_nullifier))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment))?;
|
||||
} else {
|
||||
output_data.write_all(&fr_to_bytes_be(&identity_trapdoor))?;
|
||||
output_data.write_all(&fr_to_bytes_be(&identity_nullifier))?;
|
||||
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
|
||||
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ fn test_groth16_proof() {
|
||||
#[cfg(not(feature = "stateless"))]
|
||||
mod tree_test {
|
||||
use crate::circuit::{Fr, TEST_TREE_HEIGHT};
|
||||
use crate::hashers::{hash_to_field, poseidon_hash as utils_poseidon_hash};
|
||||
use crate::hashers::{hash_to_field_le, poseidon_hash as utils_poseidon_hash};
|
||||
use crate::protocol::*;
|
||||
use crate::public::RLN;
|
||||
use crate::utils::*;
|
||||
@@ -622,9 +622,9 @@ mod tree_test {
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
// We generate a external nullifier
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
// We choose a message_id satisfy 0 <= message_id < MESSAGE_LIMIT
|
||||
@@ -694,9 +694,9 @@ mod tree_test {
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
// We generate a external nullifier
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
// We choose a message_id satisfy 0 <= message_id < MESSAGE_LIMIT
|
||||
@@ -777,9 +777,9 @@ mod tree_test {
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
// We generate a external nullifier
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
// We choose a message_id satisfy 0 <= message_id < MESSAGE_LIMIT
|
||||
@@ -869,9 +869,9 @@ mod tree_test {
|
||||
let signal2: [u8; 32] = rng.gen();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
// We generate a external nullifier
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
// We choose a message_id satisfy 0 <= message_id < MESSAGE_LIMIT
|
||||
@@ -994,7 +994,7 @@ mod tree_test {
|
||||
#[cfg(feature = "stateless")]
|
||||
mod stateless_test {
|
||||
use crate::circuit::{Fr, TEST_TREE_HEIGHT};
|
||||
use crate::hashers::{hash_to_field, poseidon_hash as utils_poseidon_hash, PoseidonHash};
|
||||
use crate::hashers::{hash_to_field_le, poseidon_hash as utils_poseidon_hash, PoseidonHash};
|
||||
use crate::protocol::*;
|
||||
use crate::public::RLN;
|
||||
use crate::utils::*;
|
||||
@@ -1033,15 +1033,15 @@ mod stateless_test {
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
|
||||
// We prepare input for generate_rln_proof API
|
||||
// input_data is [ identity_secret<32> | id_index<8> | user_message_limit<32> | message_id<32> | external_nullifier<32> | signal_len<8> | signal<var> ]
|
||||
|
||||
let x = hash_to_field(&signal);
|
||||
let x = hash_to_field_le(&signal);
|
||||
let merkle_proof = tree.proof(identity_index).expect("proof should exist");
|
||||
|
||||
let rln_witness = rln_witness_from_values(
|
||||
@@ -1124,18 +1124,18 @@ mod stateless_test {
|
||||
tree.update_next(rate_commitment).unwrap();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
|
||||
// We generate a random signal
|
||||
let mut rng = thread_rng();
|
||||
let signal1: [u8; 32] = rng.gen();
|
||||
let x1 = hash_to_field(&signal1);
|
||||
let x1 = hash_to_field_le(&signal1);
|
||||
|
||||
let signal2: [u8; 32] = rng.gen();
|
||||
let x2 = hash_to_field(&signal2);
|
||||
let x2 = hash_to_field_le(&signal2);
|
||||
|
||||
let identity_index = tree.leaves_set();
|
||||
let merkle_proof = tree.proof(identity_index).expect("proof should exist");
|
||||
@@ -1203,7 +1203,7 @@ mod stateless_test {
|
||||
tree.update_next(rate_commitment_new).unwrap();
|
||||
|
||||
let signal3: [u8; 32] = rng.gen();
|
||||
let x3 = hash_to_field(&signal3);
|
||||
let x3 = hash_to_field_le(&signal3);
|
||||
|
||||
let identity_index_new = tree.leaves_set();
|
||||
let merkle_proof_new = tree.proof(identity_index_new).expect("proof should exist");
|
||||
|
||||
200
rln/src/utils.rs
200
rln/src/utils.rs
@@ -53,6 +53,15 @@ pub fn bytes_le_to_fr(input: &[u8]) -> (Fr, usize) {
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bytes_be_to_fr(input: &[u8]) -> (Fr, usize) {
|
||||
let el_size = fr_byte_size();
|
||||
(
|
||||
Fr::from(BigUint::from_bytes_be(&input[0..el_size])),
|
||||
el_size,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fr_to_bytes_le(input: &Fr) -> Vec<u8> {
|
||||
let input_biguint: BigUint = (*input).into();
|
||||
@@ -62,6 +71,19 @@ pub fn fr_to_bytes_le(input: &Fr) -> Vec<u8> {
|
||||
res
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fr_to_bytes_be(input: &Fr) -> Vec<u8> {
|
||||
let input_biguint: BigUint = (*input).into();
|
||||
let mut res = input_biguint.to_bytes_be();
|
||||
// For BE, insert 0 at the start of the Vec (see also fr_to_bytes_le comments)
|
||||
let to_insert_count = fr_byte_size().saturating_sub(res.len());
|
||||
if to_insert_count > 0 {
|
||||
// Insert multi 0 at index 0
|
||||
res.splice(0..0, std::iter::repeat_n(0, to_insert_count));
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn vec_fr_to_bytes_le(input: &[Fr]) -> Vec<u8> {
|
||||
// Calculate capacity for Vec:
|
||||
@@ -70,7 +92,7 @@ pub fn vec_fr_to_bytes_le(input: &[Fr]) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(8 + input.len() * fr_byte_size());
|
||||
|
||||
// We store the vector length
|
||||
bytes.extend_from_slice(&normalize_usize(input.len()));
|
||||
bytes.extend_from_slice(&normalize_usize_le(input.len()));
|
||||
|
||||
// We store each element
|
||||
for el in input {
|
||||
@@ -80,6 +102,24 @@ pub fn vec_fr_to_bytes_le(input: &[Fr]) -> Vec<u8> {
|
||||
bytes
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn vec_fr_to_bytes_be(input: &[Fr]) -> Vec<u8> {
|
||||
// Calculate capacity for Vec:
|
||||
// - 8 bytes for normalized vector length (usize)
|
||||
// - each Fr element requires fr_byte_size() bytes (typically 32 bytes)
|
||||
let mut bytes = Vec::with_capacity(8 + input.len() * fr_byte_size());
|
||||
|
||||
// We store the vector length
|
||||
bytes.extend_from_slice(&normalize_usize_be(input.len()));
|
||||
|
||||
// We store each element
|
||||
for el in input {
|
||||
bytes.extend_from_slice(&fr_to_bytes_be(el));
|
||||
}
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn vec_u8_to_bytes_le(input: &[u8]) -> Vec<u8> {
|
||||
// Calculate capacity for Vec:
|
||||
@@ -88,7 +128,23 @@ pub fn vec_u8_to_bytes_le(input: &[u8]) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(8 + input.len());
|
||||
|
||||
// We store the vector length
|
||||
bytes.extend_from_slice(&normalize_usize(input.len()));
|
||||
bytes.extend_from_slice(&normalize_usize_le(input.len()));
|
||||
|
||||
// We store the input
|
||||
bytes.extend_from_slice(input);
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn vec_u8_to_bytes_be(input: &[u8]) -> Vec<u8> {
|
||||
// Calculate capacity for Vec:
|
||||
// - 8 bytes for normalized vector length (usize)
|
||||
// - variable length input data
|
||||
let mut bytes = Vec::with_capacity(8 + input.len());
|
||||
|
||||
// We store the vector length
|
||||
bytes.extend_from_slice(&normalize_usize_be(input.len()));
|
||||
|
||||
// We store the input
|
||||
bytes.extend_from_slice(input);
|
||||
@@ -99,58 +155,177 @@ pub fn vec_u8_to_bytes_le(input: &[u8]) -> Vec<u8> {
|
||||
#[inline(always)]
|
||||
pub fn bytes_le_to_vec_u8(input: &[u8]) -> Result<(Vec<u8>, usize), ConversionError> {
|
||||
let mut read: usize = 0;
|
||||
|
||||
if input.len() < 8 {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let len = usize::try_from(u64::from_le_bytes(input[0..8].try_into()?))?;
|
||||
read += 8;
|
||||
|
||||
if input.len() < 8 + len {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8 + len,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let res = input[8..8 + len].to_vec();
|
||||
read += res.len();
|
||||
Ok((res, read))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bytes_be_to_vec_u8(input: &[u8]) -> Result<(Vec<u8>, usize), ConversionError> {
|
||||
let mut read: usize = 0;
|
||||
if input.len() < 8 {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let len = usize::try_from(u64::from_be_bytes(input[0..8].try_into()?))?;
|
||||
read += 8;
|
||||
if input.len() < 8 + len {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8 + len,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let res = input[8..8 + len].to_vec();
|
||||
read += res.len();
|
||||
Ok((res, read))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bytes_le_to_vec_fr(input: &[u8]) -> Result<(Vec<Fr>, usize), ConversionError> {
|
||||
let mut read: usize = 0;
|
||||
if input.len() < 8 {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let len = usize::try_from(u64::from_le_bytes(input[0..8].try_into()?))?;
|
||||
read += 8;
|
||||
let mut res: Vec<Fr> = Vec::with_capacity(len);
|
||||
|
||||
let el_size = fr_byte_size();
|
||||
if input.len() < 8 + len * el_size {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8 + len * el_size,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let mut res: Vec<Fr> = Vec::with_capacity(len);
|
||||
for i in 0..len {
|
||||
let (curr_el, _) = bytes_le_to_fr(&input[8 + el_size * i..8 + el_size * (i + 1)]);
|
||||
res.push(curr_el);
|
||||
read += el_size;
|
||||
}
|
||||
Ok((res, read))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bytes_be_to_vec_fr(input: &[u8]) -> Result<(Vec<Fr>, usize), ConversionError> {
|
||||
let mut read: usize = 0;
|
||||
if input.len() < 8 {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let len = usize::try_from(u64::from_be_bytes(input[0..8].try_into()?))?;
|
||||
read += 8;
|
||||
let el_size = fr_byte_size();
|
||||
if input.len() < 8 + len * el_size {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8 + len * el_size,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let mut res: Vec<Fr> = Vec::with_capacity(len);
|
||||
for i in 0..len {
|
||||
let (curr_el, _) = bytes_be_to_fr(&input[8 + el_size * i..8 + el_size * (i + 1)]);
|
||||
res.push(curr_el);
|
||||
read += el_size;
|
||||
}
|
||||
Ok((res, read))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bytes_le_to_vec_usize(input: &[u8]) -> Result<Vec<usize>, ConversionError> {
|
||||
if input.len() < 8 {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let nof_elem = usize::try_from(u64::from_le_bytes(input[0..8].try_into()?))?;
|
||||
if nof_elem == 0 {
|
||||
Ok(vec![])
|
||||
} else {
|
||||
if input.len() < 8 + nof_elem * 8 {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8 + nof_elem * 8,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let elements: Vec<usize> = input[8..]
|
||||
.chunks(8)
|
||||
.take(nof_elem)
|
||||
.map(|ch| usize::from_le_bytes(ch[0..8].try_into().unwrap()))
|
||||
.collect();
|
||||
Ok(elements)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bytes_be_to_vec_usize(input: &[u8]) -> Result<Vec<usize>, ConversionError> {
|
||||
if input.len() < 8 {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let nof_elem = usize::try_from(u64::from_be_bytes(input[0..8].try_into()?))?;
|
||||
if nof_elem == 0 {
|
||||
Ok(vec![])
|
||||
} else {
|
||||
if input.len() < 8 + nof_elem * 8 {
|
||||
return Err(ConversionError::InsufficientData {
|
||||
expected: 8 + nof_elem * 8,
|
||||
actual: input.len(),
|
||||
});
|
||||
}
|
||||
let elements: Vec<usize> = input[8..]
|
||||
.chunks(8)
|
||||
.take(nof_elem)
|
||||
.map(|ch| usize::from_be_bytes(ch[0..8].try_into().unwrap()))
|
||||
.collect();
|
||||
Ok(elements)
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalizes a `usize` into an 8-byte array, ensuring consistency across architectures.
|
||||
/// On 32-bit systems, the result is zero-padded to 8 bytes.
|
||||
/// On 64-bit systems, it directly represents the `usize` value.
|
||||
#[inline(always)]
|
||||
pub fn normalize_usize(input: usize) -> [u8; 8] {
|
||||
pub fn normalize_usize_le(input: usize) -> [u8; 8] {
|
||||
let mut bytes = [0u8; 8];
|
||||
let input_bytes = input.to_le_bytes();
|
||||
bytes[..input_bytes.len()].copy_from_slice(&input_bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Normalizes a `usize` into an 8-byte array, ensuring consistency across architectures.
|
||||
/// On 32-bit systems, the result is zero-padded to 8 bytes.
|
||||
/// On 64-bit systems, it directly represents the `usize` value.
|
||||
#[inline(always)]
|
||||
pub fn normalize_usize_be(input: usize) -> [u8; 8] {
|
||||
let mut bytes = [0u8; 8];
|
||||
let input_bytes = input.to_be_bytes();
|
||||
bytes[..input_bytes.len()].copy_from_slice(&input_bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
#[inline(always)] // using for test
|
||||
pub fn generate_input_buffer() -> Cursor<String> {
|
||||
Cursor::new(json!({}).to_string())
|
||||
@@ -186,6 +361,17 @@ impl IdSecret {
|
||||
Zeroizing::new(res)
|
||||
}
|
||||
|
||||
pub(crate) fn to_bytes_be(&self) -> Zeroizing<Vec<u8>> {
|
||||
let input_biguint: BigUint = self.0.into();
|
||||
let mut res = input_biguint.to_bytes_be();
|
||||
let to_insert_count = fr_byte_size().saturating_sub(res.len());
|
||||
if to_insert_count > 0 {
|
||||
// Insert multi 0 at index 0
|
||||
res.splice(0..0, std::iter::repeat_n(0, to_insert_count));
|
||||
}
|
||||
Zeroizing::new(res)
|
||||
}
|
||||
|
||||
/// Warning: this can leak the secret value
|
||||
/// Warning: Leaked value is of type 'U256' which implement Copy (every copy will not be zeroized)
|
||||
pub(crate) fn to_u256(&self) -> U256 {
|
||||
|
||||
349
rln/tests/ffi.rs
349
rln/tests/ffi.rs
@@ -4,9 +4,9 @@ mod test {
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use rand::Rng;
|
||||
use rln::circuit::{Fr, TEST_TREE_HEIGHT};
|
||||
use rln::ffi::{hash as ffi_hash, poseidon_hash as ffi_poseidon_hash, *};
|
||||
use rln::hashers::{hash_to_field, poseidon_hash as utils_poseidon_hash, ROUND_PARAMS};
|
||||
use rln::protocol::*;
|
||||
use rln::ffi::*;
|
||||
use rln::hashers::{hash_to_field_le, poseidon_hash as utils_poseidon_hash};
|
||||
use rln::protocol::{deserialize_identity_tuple_le, *};
|
||||
use rln::public::RLN;
|
||||
use rln::utils::*;
|
||||
use serde_json::json;
|
||||
@@ -50,9 +50,9 @@ mod test {
|
||||
root
|
||||
}
|
||||
|
||||
fn identity_pair_gen(rln_pointer: &mut RLN) -> (IdSecret, Fr) {
|
||||
fn identity_pair_gen() -> (IdSecret, Fr) {
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = key_gen(rln_pointer, output_buffer.as_mut_ptr());
|
||||
let success = key_gen(output_buffer.as_mut_ptr(), true);
|
||||
assert!(success, "key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
@@ -267,7 +267,7 @@ mod test {
|
||||
let rln_pointer = create_rln_instance();
|
||||
|
||||
// generate identity
|
||||
let mut identity_secret_hash_ = hash_to_field(b"test-merkle-proof");
|
||||
let mut identity_secret_hash_ = hash_to_field_le(b"test-merkle-proof");
|
||||
let identity_secret_hash = IdSecret::from(&mut identity_secret_hash_);
|
||||
let mut to_hash = [*identity_secret_hash.clone()];
|
||||
let id_commitment = utils_poseidon_hash(&to_hash);
|
||||
@@ -472,7 +472,7 @@ mod test {
|
||||
set_leaves_init(rln_pointer, &leaves);
|
||||
|
||||
// We generate a new identity pair
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen(rln_pointer);
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen();
|
||||
let identity_index: usize = NO_OF_LEAVES;
|
||||
|
||||
// We generate a random signal
|
||||
@@ -480,9 +480,9 @@ mod test {
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
// We generate a external nullifier
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
// We choose a message_id satisfy 0 <= message_id < MESSAGE_LIMIT
|
||||
@@ -539,7 +539,7 @@ mod test {
|
||||
set_leaves_init(rln_pointer, &leaves);
|
||||
|
||||
// We generate a new identity pair
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen(rln_pointer);
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen();
|
||||
let rate_commitment = utils_poseidon_hash(&[id_commitment, user_message_limit]);
|
||||
let identity_index: usize = NO_OF_LEAVES;
|
||||
|
||||
@@ -548,9 +548,9 @@ mod test {
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
// We generate a external nullifier
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
// We choose a message_id satisfy 0 <= message_id < MESSAGE_LIMIT
|
||||
@@ -636,7 +636,7 @@ mod test {
|
||||
let rln_pointer = create_rln_instance();
|
||||
|
||||
// We generate a new identity pair
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen(rln_pointer);
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen();
|
||||
|
||||
let user_message_limit = Fr::from(100);
|
||||
let rate_commitment = utils_poseidon_hash(&[id_commitment, user_message_limit]);
|
||||
@@ -659,9 +659,9 @@ mod test {
|
||||
let signal2: [u8; 32] = rng.gen();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
// We generate a random rln_identifier
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
// We generate a external nullifier
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
// We choose a message_id satisfy 0 <= message_id < MESSAGE_LIMIT
|
||||
@@ -719,7 +719,7 @@ mod test {
|
||||
// We now test that computing identity_secret_hash is unsuccessful if shares computed from two different identity secret hashes but within same epoch are passed
|
||||
|
||||
// We generate a new identity pair
|
||||
let (identity_secret_hash_new, id_commitment_new) = identity_pair_gen(rln_pointer);
|
||||
let (identity_secret_hash_new, id_commitment_new) = identity_pair_gen();
|
||||
let rate_commitment_new = utils_poseidon_hash(&[id_commitment_new, user_message_limit]);
|
||||
|
||||
// We set as leaf id_commitment, its index would be equal to 1 since at 0 there is id_commitment
|
||||
@@ -774,139 +774,6 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_seeded_keygen_ffi() {
|
||||
// We create a RLN instance
|
||||
let rln_pointer = create_rln_instance();
|
||||
|
||||
// We generate a new identity pair from an input seed
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let input_buffer = &Buffer::from(seed_bytes);
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = seeded_key_gen(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "seeded key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (identity_secret_hash, read) = bytes_le_to_fr(&result_data);
|
||||
let (id_commitment, _) = bytes_le_to_fr(&result_data[read..]);
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_secret_hash_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
);
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0xbf16d2b5c0d6f9d9d561e05bfca16a81b4b873bb063508fae360d8c74cef51f",
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
identity_secret_hash,
|
||||
expected_identity_secret_hash_seed_bytes.unwrap()
|
||||
);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_seeded_extended_keygen_ffi() {
|
||||
// We create a RLN instance
|
||||
let rln_pointer = create_rln_instance();
|
||||
|
||||
// We generate a new identity tuple from an input seed
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let input_buffer = &Buffer::from(seed_bytes);
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success =
|
||||
seeded_extended_key_gen(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "seeded key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
deserialize_identity_tuple(result_data);
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_trapdoor_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
);
|
||||
let expected_identity_nullifier_seed_bytes = str_to_fr(
|
||||
"0x1f18714c7bc83b5bca9e89d404cf6f2f585bc4c0f7ed8b53742b7e2b298f50b4",
|
||||
16,
|
||||
);
|
||||
let expected_identity_secret_hash_seed_bytes = str_to_fr(
|
||||
"0x2aca62aaa7abaf3686fff2caf00f55ab9462dc12db5b5d4bcf3994e671f8e521",
|
||||
16,
|
||||
);
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0x68b66aa0a8320d2e56842581553285393188714c48f9b17acd198b4f1734c5c",
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
identity_trapdoor,
|
||||
expected_identity_trapdoor_seed_bytes.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
identity_nullifier,
|
||||
expected_identity_nullifier_seed_bytes.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
identity_secret_hash,
|
||||
expected_identity_secret_hash_seed_bytes.unwrap()
|
||||
);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_hash_to_field_ffi() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
// We prepare id_commitment and we set the leaf at provided index
|
||||
let input_buffer = &Buffer::from(signal.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = ffi_hash(input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "hash call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
|
||||
// We read the returned proof and we append proof values for verify
|
||||
let serialized_hash = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
let hash2 = hash_to_field(&signal);
|
||||
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Test Poseidon hash FFI
|
||||
fn test_poseidon_hash_ffi() {
|
||||
// generate random number between 1..ROUND_PARAMS.len()
|
||||
let mut rng = thread_rng();
|
||||
let number_of_inputs = rng.gen_range(1..ROUND_PARAMS.len());
|
||||
let mut inputs = Vec::with_capacity(number_of_inputs);
|
||||
for _ in 0..number_of_inputs {
|
||||
inputs.push(Fr::rand(&mut rng));
|
||||
}
|
||||
let inputs_ser = vec_fr_to_bytes_le(&inputs);
|
||||
let input_buffer = &Buffer::from(inputs_ser.as_ref());
|
||||
|
||||
let expected_hash = utils_poseidon_hash(inputs.as_ref());
|
||||
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = ffi_poseidon_hash(input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "poseidon hash call failed");
|
||||
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (received_hash, _) = bytes_le_to_fr(&result_data);
|
||||
|
||||
assert_eq!(received_hash, expected_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_leaf_ffi() {
|
||||
// We create a RLN instance
|
||||
@@ -919,13 +786,12 @@ mod test {
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let input_buffer = &Buffer::from(seed_bytes);
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success =
|
||||
seeded_extended_key_gen(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
let success = seeded_extended_key_gen(input_buffer, output_buffer.as_mut_ptr(), true);
|
||||
assert!(success, "seeded key gen call failed");
|
||||
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (_, _, _, id_commitment) = deserialize_identity_tuple(result_data);
|
||||
let (_, _, _, id_commitment) = deserialize_identity_tuple_le(result_data);
|
||||
|
||||
// We insert the id_commitment into the tree at a random index
|
||||
let mut rng = thread_rng();
|
||||
@@ -989,10 +855,8 @@ mod stateless_test {
|
||||
use rand::Rng;
|
||||
use rln::circuit::*;
|
||||
use rln::ffi::generate_rln_proof_with_witness;
|
||||
use rln::ffi::{hash as ffi_hash, poseidon_hash as ffi_poseidon_hash, *};
|
||||
use rln::hashers::{
|
||||
hash_to_field, poseidon_hash as utils_poseidon_hash, PoseidonHash, ROUND_PARAMS,
|
||||
};
|
||||
use rln::ffi::*;
|
||||
use rln::hashers::{hash_to_field_le, poseidon_hash as utils_poseidon_hash, PoseidonHash};
|
||||
use rln::protocol::*;
|
||||
use rln::public::RLN;
|
||||
use rln::utils::*;
|
||||
@@ -1009,9 +873,9 @@ mod stateless_test {
|
||||
unsafe { &mut *rln_pointer.assume_init() }
|
||||
}
|
||||
|
||||
fn identity_pair_gen(rln_pointer: &mut RLN) -> (IdSecret, Fr) {
|
||||
fn identity_pair_gen() -> (IdSecret, Fr) {
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = key_gen(rln_pointer, output_buffer.as_mut_ptr());
|
||||
let success = key_gen(output_buffer.as_mut_ptr(), true);
|
||||
assert!(success, "key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
@@ -1043,25 +907,25 @@ mod stateless_test {
|
||||
let rln_pointer = create_rln_instance();
|
||||
|
||||
// We generate a new identity pair
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen(rln_pointer);
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen();
|
||||
|
||||
let user_message_limit = Fr::from(100);
|
||||
let rate_commitment = utils_poseidon_hash(&[id_commitment, user_message_limit]);
|
||||
tree.update_next(rate_commitment).unwrap();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
|
||||
// We generate two proofs using same epoch but different signals.
|
||||
// We generate a random signal
|
||||
let mut rng = thread_rng();
|
||||
let signal1: [u8; 32] = rng.gen();
|
||||
let x1 = hash_to_field(&signal1);
|
||||
let x1 = hash_to_field_le(&signal1);
|
||||
|
||||
let signal2: [u8; 32] = rng.gen();
|
||||
let x2 = hash_to_field(&signal2);
|
||||
let x2 = hash_to_field_le(&signal2);
|
||||
|
||||
let identity_index = tree.leaves_set();
|
||||
let merkle_proof = tree.proof(identity_index).expect("proof should exist");
|
||||
@@ -1124,13 +988,13 @@ mod stateless_test {
|
||||
// We now test that computing identity_secret_hash is unsuccessful if shares computed from two different identity secret hashes but within same epoch are passed
|
||||
|
||||
// We generate a new identity pair
|
||||
let (identity_secret_hash_new, id_commitment_new) = identity_pair_gen(rln_pointer);
|
||||
let (identity_secret_hash_new, id_commitment_new) = identity_pair_gen();
|
||||
let rate_commitment_new = utils_poseidon_hash(&[id_commitment_new, user_message_limit]);
|
||||
tree.update_next(rate_commitment_new).unwrap();
|
||||
|
||||
// We generate a random signals
|
||||
let signal3: [u8; 32] = rng.gen();
|
||||
let x3 = hash_to_field(&signal3);
|
||||
let x3 = hash_to_field_le(&signal3);
|
||||
|
||||
let identity_index_new = tree.leaves_set();
|
||||
let merkle_proof_new = tree.proof(identity_index_new).expect("proof should exist");
|
||||
@@ -1189,7 +1053,7 @@ mod stateless_test {
|
||||
let rln_pointer = create_rln_instance();
|
||||
|
||||
// We generate a new identity pair
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen(rln_pointer);
|
||||
let (identity_secret_hash, id_commitment) = identity_pair_gen();
|
||||
|
||||
let identity_index = tree.leaves_set();
|
||||
let user_message_limit = Fr::from(100);
|
||||
@@ -1197,15 +1061,15 @@ mod stateless_test {
|
||||
tree.update_next(rate_commitment).unwrap();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
let external_nullifier = utils_poseidon_hash(&[epoch, rln_identifier]);
|
||||
|
||||
// We generate two proofs using same epoch but different signals.
|
||||
// We generate a random signal
|
||||
let mut rng = thread_rng();
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
let x = hash_to_field(&signal);
|
||||
let x = hash_to_field_le(&signal);
|
||||
|
||||
let merkle_proof = tree.proof(identity_index).expect("proof should exist");
|
||||
|
||||
@@ -1323,18 +1187,29 @@ mod stateless_test {
|
||||
Duration::from_nanos((verify_time / sample_size).try_into().unwrap())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod general_tests {
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use rand::Rng;
|
||||
use rln::circuit::*;
|
||||
use rln::ffi::{hash as ffi_hash, poseidon_hash as ffi_poseidon_hash, *};
|
||||
use rln::hashers::{
|
||||
hash_to_field_be, hash_to_field_le, poseidon_hash as utils_poseidon_hash, ROUND_PARAMS,
|
||||
};
|
||||
use rln::protocol::*;
|
||||
use rln::utils::*;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_seeded_keygen_stateless_ffi() {
|
||||
// We create a RLN instance
|
||||
let rln_pointer = create_rln_instance();
|
||||
|
||||
// We generate a new identity pair from an input seed
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let input_buffer = &Buffer::from(seed_bytes);
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = seeded_key_gen(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
let success = seeded_key_gen(input_buffer, output_buffer.as_mut_ptr(), true);
|
||||
assert!(success, "seeded key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
@@ -1358,23 +1233,47 @@ mod stateless_test {
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seeded_keygen_big_endian_ffi() {
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let input_buffer = &Buffer::from(seed_bytes);
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = seeded_key_gen(input_buffer, output_buffer.as_mut_ptr(), false);
|
||||
assert!(success, "seeded key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (identity_secret_hash, read) = bytes_be_to_fr(&result_data);
|
||||
let (id_commitment, _) = bytes_be_to_fr(&result_data[read..]);
|
||||
|
||||
let expected_identity_secret_hash_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
);
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0xbf16d2b5c0d6f9d9d561e05bfca16a81b4b873bb063508fae360d8c74cef51f",
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
identity_secret_hash,
|
||||
expected_identity_secret_hash_seed_bytes.unwrap()
|
||||
);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_seeded_extended_keygen_stateless_ffi() {
|
||||
// We create a RLN instance
|
||||
let rln_pointer = create_rln_instance();
|
||||
|
||||
// We generate a new identity tuple from an input seed
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let input_buffer = &Buffer::from(seed_bytes);
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success =
|
||||
seeded_extended_key_gen(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
let success = seeded_extended_key_gen(input_buffer, output_buffer.as_mut_ptr(), true);
|
||||
assert!(success, "seeded key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
deserialize_identity_tuple(result_data);
|
||||
deserialize_identity_tuple_le(result_data);
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_trapdoor_seed_bytes = str_to_fr(
|
||||
@@ -1409,6 +1308,50 @@ mod stateless_test {
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seeded_extended_keygen_big_endian_ffi() {
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let input_buffer = &Buffer::from(seed_bytes);
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = seeded_extended_key_gen(input_buffer, output_buffer.as_mut_ptr(), false);
|
||||
assert!(success, "seeded key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
deserialize_identity_tuple_be(result_data);
|
||||
|
||||
let expected_identity_trapdoor_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
);
|
||||
let expected_identity_nullifier_seed_bytes = str_to_fr(
|
||||
"0x1f18714c7bc83b5bca9e89d404cf6f2f585bc4c0f7ed8b53742b7e2b298f50b4",
|
||||
16,
|
||||
);
|
||||
let expected_identity_secret_hash_seed_bytes = str_to_fr(
|
||||
"0x2aca62aaa7abaf3686fff2caf00f55ab9462dc12db5b5d4bcf3994e671f8e521",
|
||||
16,
|
||||
);
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0x68b66aa0a8320d2e56842581553285393188714c48f9b17acd198b4f1734c5c",
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
identity_trapdoor,
|
||||
expected_identity_trapdoor_seed_bytes.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
identity_nullifier,
|
||||
expected_identity_nullifier_seed_bytes.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
identity_secret_hash,
|
||||
expected_identity_secret_hash_seed_bytes.unwrap()
|
||||
);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_hash_to_field_stateless_ffi() {
|
||||
@@ -1418,7 +1361,7 @@ mod stateless_test {
|
||||
// We prepare id_commitment and we set the leaf at provided index
|
||||
let input_buffer = &Buffer::from(signal.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = ffi_hash(input_buffer, output_buffer.as_mut_ptr());
|
||||
let success = ffi_hash(input_buffer, output_buffer.as_mut_ptr(), true);
|
||||
assert!(success, "hash call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
|
||||
@@ -1426,7 +1369,25 @@ mod stateless_test {
|
||||
let serialized_hash = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
let hash2 = hash_to_field(&signal);
|
||||
let hash2 = hash_to_field_le(&signal);
|
||||
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_field_big_endian_ffi() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
let input_buffer = &Buffer::from(signal.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = ffi_hash(input_buffer, output_buffer.as_mut_ptr(), false);
|
||||
assert!(success, "hash call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let serialized_hash = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (hash1, _) = bytes_be_to_fr(&serialized_hash);
|
||||
|
||||
let hash2 = hash_to_field_be(&signal);
|
||||
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
@@ -1447,7 +1408,7 @@ mod stateless_test {
|
||||
let expected_hash = utils_poseidon_hash(inputs.as_ref());
|
||||
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = ffi_poseidon_hash(input_buffer, output_buffer.as_mut_ptr());
|
||||
let success = ffi_poseidon_hash(input_buffer, output_buffer.as_mut_ptr(), true);
|
||||
assert!(success, "poseidon hash call failed");
|
||||
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
@@ -1456,4 +1417,28 @@ mod stateless_test {
|
||||
|
||||
assert_eq!(received_hash, expected_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_poseidon_hash_big_endian_ffi() {
|
||||
let mut rng = thread_rng();
|
||||
let number_of_inputs = rng.gen_range(1..ROUND_PARAMS.len());
|
||||
let mut inputs = Vec::with_capacity(number_of_inputs);
|
||||
for _ in 0..number_of_inputs {
|
||||
inputs.push(Fr::rand(&mut rng));
|
||||
}
|
||||
let inputs_ser = vec_fr_to_bytes_be(&inputs);
|
||||
let input_buffer = &Buffer::from(inputs_ser.as_ref());
|
||||
|
||||
let expected_hash = utils_poseidon_hash(inputs.as_ref());
|
||||
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = ffi_poseidon_hash(input_buffer, output_buffer.as_mut_ptr(), false);
|
||||
assert!(success, "poseidon hash call failed");
|
||||
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (received_hash, _) = bytes_be_to_fr(&result_data);
|
||||
|
||||
assert_eq!(received_hash, expected_hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ mod test {
|
||||
use ark_ff::BigInt;
|
||||
use rln::circuit::{graph_from_folder, zkey_from_folder};
|
||||
use rln::circuit::{Fr, TEST_TREE_HEIGHT};
|
||||
use rln::hashers::{hash_to_field, poseidon_hash};
|
||||
use rln::hashers::{hash_to_field_le, poseidon_hash};
|
||||
use rln::poseidon_tree::PoseidonTree;
|
||||
use rln::protocol::{
|
||||
deserialize_proof_values, deserialize_witness, generate_proof, keygen,
|
||||
@@ -24,7 +24,7 @@ mod test {
|
||||
let leaf_index = 3;
|
||||
|
||||
// generate identity
|
||||
let identity_secret_hash = hash_to_field(b"test-merkle-proof");
|
||||
let identity_secret_hash = hash_to_field_le(b"test-merkle-proof");
|
||||
let id_commitment = poseidon_hash(&[identity_secret_hash]);
|
||||
let rate_commitment = poseidon_hash(&[id_commitment, 100.into()]);
|
||||
|
||||
@@ -112,11 +112,11 @@ mod test {
|
||||
let merkle_proof = tree.proof(leaf_index).expect("proof should exist");
|
||||
|
||||
let signal = b"hey hey";
|
||||
let x = hash_to_field(signal);
|
||||
let x = hash_to_field_le(signal);
|
||||
|
||||
// We set the remaining values to random ones
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
let rln_identifier = hash_to_field(b"test-rln-identifier");
|
||||
let epoch = hash_to_field_le(b"test-epoch");
|
||||
let rln_identifier = hash_to_field_le(b"test-rln-identifier");
|
||||
let external_nullifier = poseidon_hash(&[epoch, rln_identifier]);
|
||||
|
||||
rln_witness_from_values(
|
||||
|
||||
@@ -6,6 +6,7 @@ mod test {
|
||||
rln::{
|
||||
circuit::TEST_TREE_HEIGHT,
|
||||
protocol::compute_tree_root,
|
||||
public::RLN,
|
||||
utils::{
|
||||
bytes_le_to_vec_fr, bytes_le_to_vec_u8, bytes_le_to_vec_usize, fr_to_bytes_le,
|
||||
generate_input_buffer, IdSecret,
|
||||
@@ -17,10 +18,20 @@ mod test {
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use rand::Rng;
|
||||
use rln::circuit::Fr;
|
||||
use rln::hashers::{hash_to_field, poseidon_hash as utils_poseidon_hash, ROUND_PARAMS};
|
||||
use rln::protocol::deserialize_identity_tuple;
|
||||
use rln::public::{hash as public_hash, poseidon_hash as public_poseidon_hash, RLN};
|
||||
use rln::utils::{bytes_le_to_fr, str_to_fr, vec_fr_to_bytes_le};
|
||||
use rln::hashers::{
|
||||
hash_to_field_be, hash_to_field_le, poseidon_hash as utils_poseidon_hash, ROUND_PARAMS,
|
||||
};
|
||||
use rln::protocol::{
|
||||
deserialize_identity_pair_be, deserialize_identity_pair_le, deserialize_identity_tuple_be,
|
||||
deserialize_identity_tuple_le,
|
||||
};
|
||||
use rln::public::{
|
||||
hash as public_hash, poseidon_hash as public_poseidon_hash, seeded_extended_key_gen,
|
||||
seeded_key_gen,
|
||||
};
|
||||
use rln::utils::{
|
||||
bytes_be_to_fr, bytes_le_to_fr, str_to_fr, vec_fr_to_bytes_be, vec_fr_to_bytes_le,
|
||||
};
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
@@ -33,7 +44,7 @@ mod test {
|
||||
let mut rln = RLN::new(TEST_TREE_HEIGHT, generate_input_buffer()).unwrap();
|
||||
|
||||
// generate identity
|
||||
let mut identity_secret_hash_ = hash_to_field(b"test-merkle-proof");
|
||||
let mut identity_secret_hash_ = hash_to_field_le(b"test-merkle-proof");
|
||||
let identity_secret_hash = IdSecret::from(&mut identity_secret_hash_);
|
||||
|
||||
let mut to_hash = [*identity_secret_hash.clone()];
|
||||
@@ -149,19 +160,46 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_seeded_keygen() {
|
||||
let rln = RLN::default();
|
||||
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
rln.seeded_key_gen(&mut input_buffer, &mut output_buffer)
|
||||
.unwrap();
|
||||
seeded_key_gen(&mut input_buffer, &mut output_buffer, true).unwrap();
|
||||
let serialized_output = output_buffer.into_inner();
|
||||
|
||||
let (identity_secret_hash, read) = bytes_le_to_fr(&serialized_output);
|
||||
let (id_commitment, _) = bytes_le_to_fr(&serialized_output[read..]);
|
||||
let (identity_secret_hash, id_commitment) = deserialize_identity_pair_le(serialized_output);
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_secret_hash_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
)
|
||||
.unwrap();
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0xbf16d2b5c0d6f9d9d561e05bfca16a81b4b873bb063508fae360d8c74cef51f",
|
||||
16,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
identity_secret_hash,
|
||||
expected_identity_secret_hash_seed_bytes
|
||||
);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seeded_keygen_big_endian() {
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
seeded_key_gen(&mut input_buffer, &mut output_buffer, false).unwrap();
|
||||
let serialized_output = output_buffer.into_inner();
|
||||
|
||||
let (identity_secret_hash, id_commitment) = deserialize_identity_pair_be(serialized_output);
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_secret_hash_seed_bytes = str_to_fr(
|
||||
@@ -184,19 +222,60 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_seeded_extended_keygen() {
|
||||
let rln = RLN::default();
|
||||
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
rln.seeded_extended_key_gen(&mut input_buffer, &mut output_buffer)
|
||||
.unwrap();
|
||||
seeded_extended_key_gen(&mut input_buffer, &mut output_buffer, true).unwrap();
|
||||
let serialized_output = output_buffer.into_inner();
|
||||
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
deserialize_identity_tuple(serialized_output);
|
||||
deserialize_identity_tuple_le(serialized_output);
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_trapdoor_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
)
|
||||
.unwrap();
|
||||
let expected_identity_nullifier_seed_bytes = str_to_fr(
|
||||
"0x1f18714c7bc83b5bca9e89d404cf6f2f585bc4c0f7ed8b53742b7e2b298f50b4",
|
||||
16,
|
||||
)
|
||||
.unwrap();
|
||||
let expected_identity_secret_hash_seed_bytes = str_to_fr(
|
||||
"0x2aca62aaa7abaf3686fff2caf00f55ab9462dc12db5b5d4bcf3994e671f8e521",
|
||||
16,
|
||||
)
|
||||
.unwrap();
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0x68b66aa0a8320d2e56842581553285393188714c48f9b17acd198b4f1734c5c",
|
||||
16,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(identity_trapdoor, expected_identity_trapdoor_seed_bytes);
|
||||
assert_eq!(identity_nullifier, expected_identity_nullifier_seed_bytes);
|
||||
assert_eq!(
|
||||
identity_secret_hash,
|
||||
expected_identity_secret_hash_seed_bytes
|
||||
);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seeded_extended_keygen_big_endian() {
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
seeded_extended_key_gen(&mut input_buffer, &mut output_buffer, false).unwrap();
|
||||
let serialized_output = output_buffer.into_inner();
|
||||
|
||||
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
|
||||
deserialize_identity_tuple_be(serialized_output);
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_trapdoor_seed_bytes = str_to_fr(
|
||||
@@ -237,11 +316,28 @@ mod test {
|
||||
let mut input_buffer = Cursor::new(&signal);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
public_hash(&mut input_buffer, &mut output_buffer).unwrap();
|
||||
public_hash(&mut input_buffer, &mut output_buffer, true).unwrap();
|
||||
let serialized_hash = output_buffer.into_inner();
|
||||
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
let hash2 = hash_to_field(&signal);
|
||||
let hash2 = hash_to_field_le(&signal);
|
||||
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_field_big_endian() {
|
||||
let mut rng = thread_rng();
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
let mut input_buffer = Cursor::new(&signal);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
public_hash(&mut input_buffer, &mut output_buffer, false).unwrap();
|
||||
let serialized_hash = output_buffer.into_inner();
|
||||
let (hash1, _) = bytes_be_to_fr(&serialized_hash);
|
||||
|
||||
let hash2 = hash_to_field_be(&signal);
|
||||
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
@@ -259,10 +355,30 @@ mod test {
|
||||
let mut input_buffer = Cursor::new(vec_fr_to_bytes_le(&inputs));
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
public_poseidon_hash(&mut input_buffer, &mut output_buffer).unwrap();
|
||||
public_poseidon_hash(&mut input_buffer, &mut output_buffer, true).unwrap();
|
||||
let serialized_hash = output_buffer.into_inner();
|
||||
let (hash, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
assert_eq!(hash, expected_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_poseidon_hash_big_endian() {
|
||||
let mut rng = thread_rng();
|
||||
let number_of_inputs = rng.gen_range(1..ROUND_PARAMS.len());
|
||||
let mut inputs = Vec::with_capacity(number_of_inputs);
|
||||
for _ in 0..number_of_inputs {
|
||||
inputs.push(Fr::rand(&mut rng));
|
||||
}
|
||||
let expected_hash = utils_poseidon_hash(&inputs);
|
||||
|
||||
let mut input_buffer = Cursor::new(vec_fr_to_bytes_be(&inputs));
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
public_poseidon_hash(&mut input_buffer, &mut output_buffer, false).unwrap();
|
||||
let serialized_hash = output_buffer.into_inner();
|
||||
let (hash, _) = bytes_be_to_fr(&serialized_hash);
|
||||
|
||||
assert_eq!(hash, expected_hash);
|
||||
}
|
||||
}
|
||||
|
||||
411
rln/tests/utils.rs
Normal file
411
rln/tests/utils.rs
Normal file
@@ -0,0 +1,411 @@
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rln::utils::{
|
||||
bytes_be_to_fr, bytes_be_to_vec_fr, bytes_be_to_vec_u8, bytes_be_to_vec_usize,
|
||||
bytes_le_to_fr, bytes_le_to_vec_fr, bytes_le_to_vec_u8, bytes_le_to_vec_usize,
|
||||
fr_to_bytes_be, fr_to_bytes_le, normalize_usize_be, normalize_usize_le, str_to_fr,
|
||||
vec_fr_to_bytes_be, vec_fr_to_bytes_le, vec_u8_to_bytes_be, vec_u8_to_bytes_le,
|
||||
};
|
||||
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use rln::circuit::Fr;
|
||||
|
||||
#[test]
|
||||
fn test_normalize_usize_le() {
|
||||
// Test basic cases
|
||||
assert_eq!(normalize_usize_le(0), [0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(normalize_usize_le(1), [1, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(normalize_usize_le(255), [255, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(normalize_usize_le(256), [0, 1, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(normalize_usize_le(65535), [255, 255, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(normalize_usize_le(65536), [0, 0, 1, 0, 0, 0, 0, 0]);
|
||||
|
||||
// Test 32-bit boundary
|
||||
assert_eq!(
|
||||
normalize_usize_le(4294967295),
|
||||
[255, 255, 255, 255, 0, 0, 0, 0]
|
||||
);
|
||||
assert_eq!(normalize_usize_le(4294967296), [0, 0, 0, 0, 1, 0, 0, 0]);
|
||||
|
||||
// Test maximum value
|
||||
assert_eq!(
|
||||
normalize_usize_le(usize::MAX),
|
||||
[255, 255, 255, 255, 255, 255, 255, 255]
|
||||
);
|
||||
|
||||
// Test that result is always 8 bytes
|
||||
assert_eq!(normalize_usize_le(0).len(), 8);
|
||||
assert_eq!(normalize_usize_le(usize::MAX).len(), 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_usize_be() {
|
||||
// Test basic cases
|
||||
assert_eq!(normalize_usize_be(0), [0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(normalize_usize_be(1), [0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
assert_eq!(normalize_usize_be(255), [0, 0, 0, 0, 0, 0, 0, 255]);
|
||||
assert_eq!(normalize_usize_be(256), [0, 0, 0, 0, 0, 0, 1, 0]);
|
||||
assert_eq!(normalize_usize_be(65535), [0, 0, 0, 0, 0, 0, 255, 255]);
|
||||
assert_eq!(normalize_usize_be(65536), [0, 0, 0, 0, 0, 1, 0, 0]);
|
||||
|
||||
// Test 32-bit boundary
|
||||
assert_eq!(
|
||||
normalize_usize_be(4294967295),
|
||||
[0, 0, 0, 0, 255, 255, 255, 255]
|
||||
);
|
||||
assert_eq!(normalize_usize_be(4294967296), [0, 0, 0, 1, 0, 0, 0, 0]);
|
||||
|
||||
// Test maximum value
|
||||
assert_eq!(
|
||||
normalize_usize_be(usize::MAX),
|
||||
[255, 255, 255, 255, 255, 255, 255, 255]
|
||||
);
|
||||
|
||||
// Test that result is always 8 bytes
|
||||
assert_eq!(normalize_usize_be(0).len(), 8);
|
||||
assert_eq!(normalize_usize_be(usize::MAX).len(), 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_usize_endianness() {
|
||||
// Test that little-endian and big-endian produce different results for non-zero values
|
||||
let test_values = vec![1, 255, 256, 65535, 65536, 4294967295, 4294967296];
|
||||
|
||||
for &value in &test_values {
|
||||
let le_result = normalize_usize_le(value);
|
||||
let be_result = normalize_usize_be(value);
|
||||
|
||||
// For non-zero values, LE and BE should be different
|
||||
assert_ne!(
|
||||
le_result, be_result,
|
||||
"LE and BE should differ for value {value}"
|
||||
);
|
||||
|
||||
// Both should be 8 bytes
|
||||
assert_eq!(le_result.len(), 8);
|
||||
assert_eq!(be_result.len(), 8);
|
||||
}
|
||||
|
||||
// Zero should be the same in both endianness
|
||||
assert_eq!(normalize_usize_le(0), normalize_usize_be(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_usize_roundtrip() {
|
||||
// Test that we can reconstruct the original value from the normalized bytes
|
||||
let test_values = vec![
|
||||
0,
|
||||
1,
|
||||
255,
|
||||
256,
|
||||
65535,
|
||||
65536,
|
||||
4294967295,
|
||||
4294967296,
|
||||
usize::MAX,
|
||||
];
|
||||
|
||||
for &value in &test_values {
|
||||
let le_bytes = normalize_usize_le(value);
|
||||
let be_bytes = normalize_usize_be(value);
|
||||
|
||||
// Reconstruct from little-endian bytes
|
||||
let reconstructed_le = usize::from_le_bytes(le_bytes);
|
||||
assert_eq!(
|
||||
reconstructed_le, value,
|
||||
"LE roundtrip failed for value {value}"
|
||||
);
|
||||
|
||||
// Reconstruct from big-endian bytes
|
||||
let reconstructed_be = usize::from_be_bytes(be_bytes);
|
||||
assert_eq!(
|
||||
reconstructed_be, value,
|
||||
"BE roundtrip failed for value {value}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_usize_edge_cases() {
|
||||
// Test edge cases and boundary values
|
||||
let edge_cases = vec![
|
||||
0,
|
||||
1,
|
||||
255,
|
||||
256,
|
||||
65535,
|
||||
65536,
|
||||
16777215, // 2^24 - 1
|
||||
16777216, // 2^24
|
||||
4294967295, // 2^32 - 1
|
||||
4294967296, // 2^32
|
||||
1099511627775, // 2^40 - 1
|
||||
1099511627776, // 2^40
|
||||
281474976710655, // 2^48 - 1
|
||||
281474976710656, // 2^48
|
||||
72057594037927935, // 2^56 - 1
|
||||
72057594037927936, // 2^56
|
||||
usize::MAX,
|
||||
];
|
||||
|
||||
for &value in &edge_cases {
|
||||
let le_result = normalize_usize_le(value);
|
||||
let be_result = normalize_usize_be(value);
|
||||
|
||||
// Both should be 8 bytes
|
||||
assert_eq!(le_result.len(), 8);
|
||||
assert_eq!(be_result.len(), 8);
|
||||
|
||||
// Roundtrip should work
|
||||
assert_eq!(usize::from_le_bytes(le_result), value);
|
||||
assert_eq!(usize::from_be_bytes(be_result), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_usize_architecture_independence() {
|
||||
// Test that the functions work consistently regardless of the underlying architecture
|
||||
// This test ensures that the functions provide consistent 8-byte output
|
||||
// even on 32-bit systems where usize might be 4 bytes
|
||||
|
||||
let test_values = vec![0, 1, 255, 256, 65535, 65536, 4294967295, 4294967296];
|
||||
|
||||
for &value in &test_values {
|
||||
let le_result = normalize_usize_le(value);
|
||||
let be_result = normalize_usize_be(value);
|
||||
|
||||
// Always 8 bytes regardless of architecture
|
||||
assert_eq!(le_result.len(), 8);
|
||||
assert_eq!(be_result.len(), 8);
|
||||
|
||||
// The result should be consistent with the original value
|
||||
assert_eq!(usize::from_le_bytes(le_result), value);
|
||||
assert_eq!(usize::from_be_bytes(be_result), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fr_serialization_roundtrip() {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// Test multiple random Fr values
|
||||
for _ in 0..10 {
|
||||
let fr = Fr::rand(&mut rng);
|
||||
|
||||
// Test little-endian roundtrip
|
||||
let le_bytes = fr_to_bytes_le(&fr);
|
||||
let (reconstructed_le, _) = bytes_le_to_fr(&le_bytes);
|
||||
assert_eq!(fr, reconstructed_le);
|
||||
|
||||
// Test big-endian roundtrip
|
||||
let be_bytes = fr_to_bytes_be(&fr);
|
||||
let (reconstructed_be, _) = bytes_be_to_fr(&be_bytes);
|
||||
assert_eq!(fr, reconstructed_be);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_fr_serialization_roundtrip() {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// Test with different vector sizes
|
||||
for size in [0, 1, 5, 10] {
|
||||
let fr_vec: Vec<Fr> = (0..size).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
// Test little-endian roundtrip
|
||||
let le_bytes = vec_fr_to_bytes_le(&fr_vec);
|
||||
let (reconstructed_le, _) = bytes_le_to_vec_fr(&le_bytes).unwrap();
|
||||
assert_eq!(fr_vec, reconstructed_le);
|
||||
|
||||
// Test big-endian roundtrip
|
||||
let be_bytes = vec_fr_to_bytes_be(&fr_vec);
|
||||
let (reconstructed_be, _) = bytes_be_to_vec_fr(&be_bytes).unwrap();
|
||||
assert_eq!(fr_vec, reconstructed_be);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_u8_serialization_roundtrip() {
|
||||
// Test with different vector sizes and content
|
||||
let test_cases = vec![
|
||||
vec![],
|
||||
vec![0],
|
||||
vec![255],
|
||||
vec![1, 2, 3, 4, 5],
|
||||
vec![0, 255, 128, 64, 32, 16, 8, 4, 2, 1],
|
||||
(0..100).collect::<Vec<u8>>(),
|
||||
];
|
||||
|
||||
for test_case in test_cases {
|
||||
// Test little-endian roundtrip
|
||||
let le_bytes = vec_u8_to_bytes_le(&test_case);
|
||||
let (reconstructed_le, _) = bytes_le_to_vec_u8(&le_bytes).unwrap();
|
||||
assert_eq!(test_case, reconstructed_le);
|
||||
|
||||
// Test big-endian roundtrip
|
||||
let be_bytes = vec_u8_to_bytes_be(&test_case);
|
||||
let (reconstructed_be, _) = bytes_be_to_vec_u8(&be_bytes).unwrap();
|
||||
assert_eq!(test_case, reconstructed_be);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vec_usize_serialization_roundtrip() {
|
||||
// Test with different vector sizes and content
|
||||
let test_cases = vec![
|
||||
vec![],
|
||||
vec![0],
|
||||
vec![usize::MAX],
|
||||
vec![1, 2, 3, 4, 5],
|
||||
vec![0, 255, 65535, 4294967295, usize::MAX],
|
||||
(0..10).collect::<Vec<usize>>(),
|
||||
];
|
||||
|
||||
for test_case in test_cases {
|
||||
// Test little-endian roundtrip
|
||||
let le_bytes = {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&normalize_usize_le(test_case.len()));
|
||||
for &value in &test_case {
|
||||
bytes.extend_from_slice(&normalize_usize_le(value));
|
||||
}
|
||||
bytes
|
||||
};
|
||||
let reconstructed_le = bytes_le_to_vec_usize(&le_bytes).unwrap();
|
||||
assert_eq!(test_case, reconstructed_le);
|
||||
|
||||
// Test big-endian roundtrip
|
||||
let be_bytes = {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&normalize_usize_be(test_case.len()));
|
||||
for &value in &test_case {
|
||||
bytes.extend_from_slice(&normalize_usize_be(value));
|
||||
}
|
||||
bytes
|
||||
};
|
||||
let reconstructed_be = bytes_be_to_vec_usize(&be_bytes).unwrap();
|
||||
assert_eq!(test_case, reconstructed_be);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_to_fr() {
|
||||
// Test valid hex strings
|
||||
let test_cases = vec![
|
||||
("0x0", 16, Fr::from(0u64)),
|
||||
("0x1", 16, Fr::from(1u64)),
|
||||
("0xff", 16, Fr::from(255u64)),
|
||||
("0x100", 16, Fr::from(256u64)),
|
||||
];
|
||||
|
||||
for (input, radix, expected) in test_cases {
|
||||
let result = str_to_fr(input, radix).unwrap();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
// Test invalid inputs
|
||||
assert!(str_to_fr("invalid", 16).is_err());
|
||||
assert!(str_to_fr("0x", 16).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_endianness_differences() {
|
||||
let mut rng = thread_rng();
|
||||
let fr = Fr::rand(&mut rng);
|
||||
|
||||
// Test that LE and BE produce different byte representations
|
||||
let le_bytes = fr_to_bytes_le(&fr);
|
||||
let be_bytes = fr_to_bytes_be(&fr);
|
||||
|
||||
// They should be different (unless the value is symmetric)
|
||||
if le_bytes != be_bytes {
|
||||
// Verify they can both be reconstructed correctly
|
||||
let (reconstructed_le, _) = bytes_le_to_fr(&le_bytes);
|
||||
let (reconstructed_be, _) = bytes_be_to_fr(&be_bytes);
|
||||
assert_eq!(fr, reconstructed_le);
|
||||
assert_eq!(fr, reconstructed_be);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_handling() {
|
||||
// Test with valid length but insufficient data
|
||||
let valid_length_invalid_data = vec![0u8; 8]; // Length 0, but no data
|
||||
assert!(bytes_le_to_vec_u8(&valid_length_invalid_data).is_ok());
|
||||
assert!(bytes_be_to_vec_u8(&valid_length_invalid_data).is_ok());
|
||||
assert!(bytes_le_to_vec_fr(&valid_length_invalid_data).is_ok());
|
||||
assert!(bytes_be_to_vec_fr(&valid_length_invalid_data).is_ok());
|
||||
assert!(bytes_le_to_vec_usize(&valid_length_invalid_data).is_ok());
|
||||
assert!(bytes_be_to_vec_usize(&valid_length_invalid_data).is_ok());
|
||||
|
||||
// Test with reasonable length but insufficient data for vector deserialization
|
||||
let reasonable_length = {
|
||||
let mut bytes = vec![0u8; 8];
|
||||
bytes[0] = 1; // Length 1
|
||||
bytes
|
||||
};
|
||||
// This should fail because we don't have enough data for the vector elements
|
||||
assert!(bytes_le_to_vec_u8(&reasonable_length).is_err());
|
||||
assert!(bytes_be_to_vec_u8(&reasonable_length).is_err());
|
||||
assert!(bytes_le_to_vec_fr(&reasonable_length).is_err());
|
||||
assert!(bytes_be_to_vec_fr(&reasonable_length).is_err());
|
||||
assert!(bytes_le_to_vec_usize(&reasonable_length).is_err());
|
||||
assert!(bytes_be_to_vec_usize(&reasonable_length).is_err());
|
||||
|
||||
// Test with valid data for u8 vector
|
||||
let valid_u8_data_le = {
|
||||
let mut bytes = vec![0u8; 9];
|
||||
bytes[..8].copy_from_slice(&(1u64.to_le_bytes())); // Length 1, little-endian
|
||||
bytes[8] = 42; // One byte of data
|
||||
bytes
|
||||
};
|
||||
let valid_u8_data_be = {
|
||||
let mut bytes = vec![0u8; 9];
|
||||
bytes[..8].copy_from_slice(&(1u64.to_be_bytes())); // Length 1, big-endian
|
||||
bytes[8] = 42; // One byte of data
|
||||
bytes
|
||||
};
|
||||
assert!(bytes_le_to_vec_u8(&valid_u8_data_le).is_ok());
|
||||
assert!(bytes_be_to_vec_u8(&valid_u8_data_be).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_vectors() {
|
||||
// Test empty vector serialization/deserialization
|
||||
let empty_fr: Vec<Fr> = vec![];
|
||||
let empty_u8: Vec<u8> = vec![];
|
||||
let empty_usize: Vec<usize> = vec![];
|
||||
|
||||
// Test Fr vectors
|
||||
let le_fr_bytes = vec_fr_to_bytes_le(&empty_fr);
|
||||
let be_fr_bytes = vec_fr_to_bytes_be(&empty_fr);
|
||||
let (reconstructed_le_fr, _) = bytes_le_to_vec_fr(&le_fr_bytes).unwrap();
|
||||
let (reconstructed_be_fr, _) = bytes_be_to_vec_fr(&be_fr_bytes).unwrap();
|
||||
assert_eq!(empty_fr, reconstructed_le_fr);
|
||||
assert_eq!(empty_fr, reconstructed_be_fr);
|
||||
|
||||
// Test u8 vectors
|
||||
let le_u8_bytes = vec_u8_to_bytes_le(&empty_u8);
|
||||
let be_u8_bytes = vec_u8_to_bytes_be(&empty_u8);
|
||||
let (reconstructed_le_u8, _) = bytes_le_to_vec_u8(&le_u8_bytes).unwrap();
|
||||
let (reconstructed_be_u8, _) = bytes_be_to_vec_u8(&be_u8_bytes).unwrap();
|
||||
assert_eq!(empty_u8, reconstructed_le_u8);
|
||||
assert_eq!(empty_u8, reconstructed_be_u8);
|
||||
|
||||
// Test usize vectors
|
||||
let le_usize_bytes = {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&normalize_usize_le(0));
|
||||
bytes
|
||||
};
|
||||
let be_usize_bytes = {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&normalize_usize_be(0));
|
||||
bytes
|
||||
};
|
||||
let reconstructed_le_usize = bytes_le_to_vec_usize(&le_usize_bytes).unwrap();
|
||||
let reconstructed_be_usize = bytes_be_to_vec_usize(&be_usize_bytes).unwrap();
|
||||
assert_eq!(empty_usize, reconstructed_le_usize);
|
||||
assert_eq!(empty_usize, reconstructed_be_usize);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user