mirror of
https://github.com/pseXperiments/icicle.git
synced 2026-01-08 20:48:06 -05:00
Pedersen commitment example in c++ (#397)
* initial commit * random elliptic points * initial complete example * public random seed to prevent knowing dlogs * cleaned up code * add README * Update examples/c++/pedersen-commitment/README.md Co-authored-by: Jeremy Felder <jeremy.felder1@gmail.com> * updates to PR comments * codespell compliance * corrected terminology in README --------- Co-authored-by: Jeremy Felder <jeremy.felder1@gmail.com>
This commit is contained in:
25
examples/c++/pedersen-commitment/CMakeLists.txt
Normal file
25
examples/c++/pedersen-commitment/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CUDA_STANDARD 17)
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED TRUE)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
if (${CMAKE_VERSION} VERSION_LESS "3.24.0")
|
||||
set(CMAKE_CUDA_ARCHITECTURES ${CUDA_ARCH})
|
||||
else()
|
||||
set(CMAKE_CUDA_ARCHITECTURES native) # on 3.24+, on earlier it is ignored, and the target is not passed
|
||||
endif ()
|
||||
project(icicle LANGUAGES CUDA CXX)
|
||||
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --expt-relaxed-constexpr")
|
||||
set(CMAKE_CUDA_FLAGS_RELEASE "")
|
||||
set(CMAKE_CUDA_FLAGS_DEBUG "${CMAKE_CUDA_FLAGS_DEBUG} -g -G -O0")
|
||||
# change the path to your Icicle location
|
||||
include_directories("../../../icicle")
|
||||
add_executable(
|
||||
example
|
||||
example.cu
|
||||
)
|
||||
find_library(NVML_LIBRARY nvidia-ml PATHS /usr/local/cuda/targets/x86_64-linux/lib/stubs/ )
|
||||
target_link_libraries(example ${NVML_LIBRARY})
|
||||
set_target_properties(example PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
|
||||
|
||||
33
examples/c++/pedersen-commitment/README.md
Normal file
33
examples/c++/pedersen-commitment/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# ICICLE example: Pedersen Commitment
|
||||
|
||||
## Best-Practices
|
||||
|
||||
We recommend to run our examples in [ZK-containers](../../ZK-containers.md) to save your time and mental energy.
|
||||
|
||||
## Key-Takeaway
|
||||
|
||||
A Pedersen Commitment is a cryptographic primitive to commit to a value or a vector of values while keeping it hidden, yet enabling the committer to reveal the value later. It provides both hiding (the commitment does not reveal any information about the value) and binding properties (once a value is committed, it cannot be changed without detection).
|
||||
|
||||
Pedersen commitment is based on Multi-Scalar Multiplication [MSM](https://github.com/ingonyama-zk/ingopedia/blob/master/src/msm.md).
|
||||
`ICICLE` provides CUDA C++ support for [MSM](https://dev.ingonyama.com/icicle/primitives/msm).
|
||||
An example of MSM is [here](../msm/README.md).
|
||||
|
||||
## Running the example
|
||||
|
||||
- `cd` to your example directory
|
||||
- compile with `./compile.sh`
|
||||
- run with `./run.sh`
|
||||
|
||||
## Concise Explanation
|
||||
|
||||
We recommend this simple [explanation](https://www.rareskills.io/post/pedersen-commitment).
|
||||
|
||||
The original paper: T. P. Pedersen, "Non-Interactive and Information-Theoretic Secure Verifiable Secret Sharing," in Advances in Cryptology — CRYPTO ’91, Lecture Notes in Computer Science, vol 576. Springer, Berlin, Heidelberg.
|
||||
|
||||
## What's in the example
|
||||
|
||||
1. Define the curve and the size of commitment vector
|
||||
2. Use public random seed to transparently generate points on the elliptic curve without known discrete logarithm
|
||||
3. Generate (random) commitment vector and salt (a.k.a blinding factor)
|
||||
4. Configure and execute MSM using on-host data
|
||||
5. Output commitment as elliptic point
|
||||
9
examples/c++/pedersen-commitment/compile.sh
Executable file
9
examples/c++/pedersen-commitment/compile.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit immediately on error
|
||||
set -e
|
||||
|
||||
rm -rf build
|
||||
mkdir -p build
|
||||
cmake -S . -B build
|
||||
cmake --build build
|
||||
159
examples/c++/pedersen-commitment/example.cu
Normal file
159
examples/c++/pedersen-commitment/example.cu
Normal file
@@ -0,0 +1,159 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <nvml.h>
|
||||
|
||||
#define CURVE_ID BN254
|
||||
#include "appUtils/msm/msm.cu"
|
||||
using namespace curve_config;
|
||||
|
||||
typedef point_field_t T;
|
||||
|
||||
// modular power
|
||||
T modPow(T base, T exp) {
|
||||
T r = T::one();
|
||||
T b = base;
|
||||
T e = exp;
|
||||
while (e != T::zero()) {
|
||||
// If exp is odd, multiply the base with result
|
||||
if (T::is_odd(e)) {
|
||||
r = r * b;
|
||||
}
|
||||
// Now exp must be even, divide it by 2
|
||||
e =T::div2(e);
|
||||
b = b * b;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Check if y2 is a quadratic residue using Euler's Criterion
|
||||
bool quadratic_residue(T y2) {
|
||||
return modPow(y2, T::div2(T::zero() - T::one())) == T::one();
|
||||
}
|
||||
|
||||
// modular square root adapted from:
|
||||
// https://github.com/ShahjalalShohag/code-library/blob/main/Number%20Theory/Tonelli%20Shanks%20Algorithm.cpp
|
||||
bool mySQRT(T a, T *result) {
|
||||
if (a == T::zero()) {
|
||||
*result = T::zero();
|
||||
return true;
|
||||
}
|
||||
if (modPow(a, T::div2(T::zero() - T::one())) != T::one() ) {
|
||||
return false; // solution does not exist
|
||||
}
|
||||
// TODO: consider special cases
|
||||
// if (p % 4 == 3) return power(a, (p + 1) / 4, p);
|
||||
T s = T::zero() - T::one(); // p - 1,
|
||||
T n = T::one() + T::one(); //2;
|
||||
T r = T::zero();
|
||||
T m;
|
||||
while (T::is_even(s)) {
|
||||
r = r + T::one();
|
||||
s = T::div2(s); //s /= 2;
|
||||
}
|
||||
// find a non-square mod p
|
||||
while (modPow(n, T::div2((T::zero() - T::one())) ) != T::zero() - T::one()) {
|
||||
n = n + T::one();
|
||||
}
|
||||
T x = modPow(a, T::div2(s + T::one()));
|
||||
T b = modPow(a, s);
|
||||
T g = modPow(n, s);
|
||||
for (;; r = m) {
|
||||
T t = b;
|
||||
for (m = T::zero(); T::lt(m,r) /* m < r*/ && t != T::one(); m = m + T::one()) t = t * t;
|
||||
if (m == T::zero() ) {
|
||||
*result = x;
|
||||
return true;
|
||||
}
|
||||
T gs = modPow(g, modPow(T::one() + T::one(), r - m - T::one()) );
|
||||
g = gs * gs ;
|
||||
x = x * gs ;
|
||||
b = b * g ;
|
||||
}
|
||||
}
|
||||
|
||||
void point_near_x(T x, affine_t *point) {
|
||||
const T wb = T { weierstrass_b };
|
||||
T y2;
|
||||
while (y2 = x*x*x + wb, quadratic_residue(y2) == false)
|
||||
{
|
||||
x = x + T::one();
|
||||
};
|
||||
T y;
|
||||
bool found = mySQRT(y2, &y);
|
||||
assert(y*y == y2);
|
||||
point->x = x;
|
||||
point->y = y;
|
||||
}
|
||||
|
||||
static int seed = 0;
|
||||
static HOST_INLINE T rand_host_seed()
|
||||
{
|
||||
std::mt19937_64 generator(seed++);
|
||||
std::uniform_int_distribution<unsigned> distribution;
|
||||
|
||||
T value;
|
||||
for (unsigned i = 0; i < T::TLC-1 ; i++)
|
||||
// TODO: use the full range of limbs: for (unsigned i = 0; i < T::TLC ; i++)
|
||||
value.limbs_storage.limbs[i] = distribution(generator);
|
||||
// while (lt(Field{get_modulus()}, value))
|
||||
// value = value - Field{get_modulus()};
|
||||
return value;
|
||||
}
|
||||
|
||||
using FpMilliseconds = std::chrono::duration<float, std::chrono::milliseconds::period>;
|
||||
#define START_TIMER(timer) auto timer##_start = std::chrono::high_resolution_clock::now();
|
||||
#define END_TIMER(timer, msg) printf("%s: %.0f ms\n", msg, FpMilliseconds(std::chrono::high_resolution_clock::now() - timer##_start).count());
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const unsigned N = pow(2, 10);
|
||||
std::cout << "Commitment vector size: " << N << "+1 for salt (a.k.a blinding factor)" << std::endl;
|
||||
T* xs = new T[N+1];
|
||||
|
||||
std::cout << "Generating random points transparently using publicly chosen seed" << std::endl;
|
||||
std::cout << "Public seed prevents committer from knowing the discrete logs of points used in the commitment" << std::endl;
|
||||
seed = 1234;
|
||||
std::cout << "Using seed: " << seed << std::endl;
|
||||
std::cout << "Generating random field values" << std::endl;
|
||||
START_TIMER(gen);
|
||||
|
||||
for (unsigned i = 0; i < N; i++) {
|
||||
xs[i] = rand_host_seed();
|
||||
}
|
||||
END_TIMER(gen, "Time to generate field values");
|
||||
std::cout << "xs[0]: " << xs[0] << std::endl;
|
||||
std::cout << "xs[1]: " << xs[1] << std::endl;
|
||||
|
||||
// affine_t points[N];
|
||||
affine_t* points = new affine_t[N+1];
|
||||
std::cout << "Generating point about random field values" << std::endl;
|
||||
START_TIMER(points);
|
||||
for (unsigned i = 0; i < N+1; i++) {
|
||||
point_near_x(xs[i], &points[i]);
|
||||
}
|
||||
END_TIMER(points, "Time to generate points");
|
||||
|
||||
std::cout << "Generating commitment vector" << std::endl;
|
||||
projective_t result;
|
||||
scalar_t* scalars = new scalar_t[N+1];
|
||||
scalar_t::RandHostMany(scalars, N);
|
||||
|
||||
std::cout << "Generating salt" << std::endl;
|
||||
scalars[N] = scalar_t::rand_host();
|
||||
|
||||
std::cout << "Executing MSM" << std::endl;
|
||||
auto config = msm::DefaultMSMConfig<scalar_t>();
|
||||
START_TIMER(msm);
|
||||
msm::MSM<scalar_t, affine_t, projective_t>(scalars, points, N+1, config, &result);
|
||||
END_TIMER(msm, "Time to execute MSM");
|
||||
|
||||
std::cout << "Computed commitment: " << result << std::endl;
|
||||
|
||||
std::cout << "Cleaning up..." << std::endl;
|
||||
delete[] xs;
|
||||
delete[] scalars;
|
||||
delete[] points;
|
||||
return 0;
|
||||
}
|
||||
2
examples/c++/pedersen-commitment/run.sh
Executable file
2
examples/c++/pedersen-commitment/run.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
./build/example
|
||||
Reference in New Issue
Block a user