diff --git a/examples/c++/pedersen-commitment/CMakeLists.txt b/examples/c++/pedersen-commitment/CMakeLists.txt new file mode 100644 index 00000000..424b3e9b --- /dev/null +++ b/examples/c++/pedersen-commitment/CMakeLists.txt @@ -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) + diff --git a/examples/c++/pedersen-commitment/README.md b/examples/c++/pedersen-commitment/README.md new file mode 100644 index 00000000..c9b8693f --- /dev/null +++ b/examples/c++/pedersen-commitment/README.md @@ -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 diff --git a/examples/c++/pedersen-commitment/compile.sh b/examples/c++/pedersen-commitment/compile.sh new file mode 100755 index 00000000..36c1ddac --- /dev/null +++ b/examples/c++/pedersen-commitment/compile.sh @@ -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 diff --git a/examples/c++/pedersen-commitment/example.cu b/examples/c++/pedersen-commitment/example.cu new file mode 100644 index 00000000..567a9e05 --- /dev/null +++ b/examples/c++/pedersen-commitment/example.cu @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include + +#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 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; +#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(); + START_TIMER(msm); + msm::MSM(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; +} diff --git a/examples/c++/pedersen-commitment/run.sh b/examples/c++/pedersen-commitment/run.sh new file mode 100755 index 00000000..6e3fc976 --- /dev/null +++ b/examples/c++/pedersen-commitment/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +./build/example