diff --git a/discrete_log_example/.gitignore b/discrete_log_example/.gitignore new file mode 100644 index 0000000..d4e3d73 --- /dev/null +++ b/discrete_log_example/.gitignore @@ -0,0 +1 @@ +circuits/target/ \ No newline at end of file diff --git a/discrete_log_example/README.md b/discrete_log_example/README.md new file mode 100644 index 0000000..5274a0f --- /dev/null +++ b/discrete_log_example/README.md @@ -0,0 +1,42 @@ +# Noir Elliptic Curve Discrete Log Proof Circuit + +This repository contains a Noir circuit for proving knowledge of the discrete logarithm on the secp256k1 elliptic curve. The circuit demonstrates, in zero-knowledge, that the prover knows a secret scalar \( k \) such that \( Q = k \cdot G \), where \( G \) is a public point and \( Q \) is the resulting public point. + +## Features + +- **Proves knowledge of discrete log:** + Given a base point \( G = (x, y) \), a secret scalar \( k \) (kept private), and a result point \( Q = (x', y') \), the circuit proves that \( Q = k \cdot G \) without revealing \( k \). + +- **Curve Membership Checks:** + Both \( G \) and \( Q \) are asserted to be on the secp256k1 curve. + +- **Flexible Test Suite:** + Includes tests for: + - Correct proofs (valid \( k \), \( G \), \( Q \)) + - Invalid results + - Non-curve points + - Edge cases (e.g., zero scalar, point at infinity) + +## Structure + +- `src/main.nr` + Main Noir circuit, containing the core logic and tests. +- `src/` + May contain additional helper or test files. +- `README.md` + This file. + +## Usage + +### Requirements + +- [Noir](https://noir-lang.org/) (v0.18+ recommended) +- [nargo](https://noir-lang.org/docs/getting_started/quick_start#installation) (Noir package manager) +- Rust (for installing Noir) + +### Running Tests + +To run all tests: + +```bash +nargo test \ No newline at end of file diff --git a/discrete_log_example/circuits/Nargo.toml b/discrete_log_example/circuits/Nargo.toml new file mode 100644 index 0000000..89cb089 --- /dev/null +++ b/discrete_log_example/circuits/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "discrete_log_example" +type = "bin" +authors = [""] +compiler_version = ">=1.0.0" + +[dependencies] +noir_bigcurve = { git = "https://github.com/noir-lang/noir_bigcurve", tag = "v0.9.0" } +bignum = { tag = "v0.7.3", git = "https://github.com/noir-lang/noir-bignum" } \ No newline at end of file diff --git a/discrete_log_example/circuits/Prover.toml b/discrete_log_example/circuits/Prover.toml new file mode 100644 index 0000000..978e596 --- /dev/null +++ b/discrete_log_example/circuits/Prover.toml @@ -0,0 +1,14 @@ +[g_x] +limbs = ["809934545436666192144300149831112600", "532189659306663695932703699144214274", "31166"] + +[g_y] +limbs = ["123079417438355278731207347528586424", "1134337383257087261818981465599813885", "18490"] + +[k] +limbs = ["2", "0", "0"] + +[q_x] +limbs = ["620769414058672675662360539292540645", "662428720985405666719129047941306460", "50692"] + +[q_y] +limbs = ["1278327165322711450801813382677652778", "545163776316525295403132726738087671", "6881"] diff --git a/discrete_log_example/circuits/src/main.nr b/discrete_log_example/circuits/src/main.nr new file mode 100644 index 0000000..e161961 --- /dev/null +++ b/discrete_log_example/circuits/src/main.nr @@ -0,0 +1,78 @@ +use noir_bigcurve::curves::secp256k1::Secp256k1; +use noir_bigcurve::curves::secp256k1::Secp256k1Scalar; +use noir_bigcurve::scalar_field::ScalarFieldTrait; +use noir_bigcurve::BigCurveTrait; +use bignum::{Secp256k1_Fq, Secp256k1_Fr, BigNum}; + +// No 'pub' on these +fn prove_discrete_log( + g_x: Secp256k1_Fq, + g_y: Secp256k1_Fq, + k: Secp256k1_Fr, + q_x: Secp256k1_Fq, + q_y: Secp256k1_Fq, +) { + let G = Secp256k1 { x: g_x, y: g_y, is_infinity: false }; + assert(!G.is_infinity); + G.validate_on_curve(); + + let Q = Secp256k1 { x: q_x, y: q_y, is_infinity: false }; + Q.validate_on_curve(); + + let k_scalar: Secp256k1Scalar = Secp256k1Scalar::from_bignum(k); + let computed_Q = G.mul(k_scalar); + + assert(computed_Q.x == q_x); + assert(computed_Q.y == q_y); +} + +// Only 'pub' in main for public inputs +pub fn main( + g_x: pub Secp256k1_Fq, + g_y: pub Secp256k1_Fq, + k: Secp256k1_Fr, + q_x: pub Secp256k1_Fq, + q_y: pub Secp256k1_Fq, +) { + prove_discrete_log(g_x, g_y, k, q_x, q_y); +} +// Example test: Prove knowledge of k such that Q = kG +#[test] +fn test_prove_discrete_log() { + let g_x = Secp256k1_Fq::from_limbs([809934545436666192144300149831112600, 532189659306663695932703699144214274, 31166]); + let g_y = Secp256k1_Fq::from_limbs([123079417438355278731207347528586424, 1134337383257087261818981465599813885, 18490]); + + // Secret scalar k (witness) + let k = Secp256k1_Fr::from_limbs([2,0,0]); + + // Public Q = k * G + let q_x = Secp256k1_Fq::from_limbs([620769414058672675662360539292540645, 662428720985405666719129047941306460, 50692]); + let q_y = Secp256k1_Fq::from_limbs([1278327165322711450801813382677652778, 545163776316525295403132726738087671, 6881]); + + prove_discrete_log(g_x, g_y, k, q_x, q_y); +} + +#[test(should_fail)] +fn test_prove_discrete_log_wrong_Q() { + let g_x = Secp256k1_Fq::from_limbs([809934545436666192144300149831112600, 532189659306663695932703699144214274, 31166]); + let g_y = Secp256k1_Fq::from_limbs([123079417438355278731207347528586424, 1134337383257087261818981465599813885, 18490]); + + let k = Secp256k1_Fr::from_limbs([2,0,0]); + + // Wrong Q + let q_x = Secp256k1_Fq::from_limbs([258484535409603203050605737993582329, 716568843669635254349561846039718325, 63792]); + let q_y = Secp256k1_Fq::from_limbs([3439865459799723184042666981123698, 638964607539318458049780617139410533, 14479]); + + prove_discrete_log(g_x, g_y, k, q_x, q_y); +} +#[test(should_fail)] +fn test_prove_discrete_log_wrong_point() { + // Deliberately choose a point NOT on secp256k1 + let bad_x = Secp256k1_Fq::from_limbs([258484535409603203050605737993582330, 716568843669635254349561846039718325, 63792]); + let bad_y = Secp256k1_Fq::from_limbs([3439865459799723184042666981123699, 638964607539318458049780617139410533, 14479]); + let k = Secp256k1_Fr::from_limbs([2,0,0]); + let q_x = Secp256k1_Fq::from_limbs([516969070819206406101211480282132933, 103909691554354635795316631799092074, 62049]); + let q_y = Secp256k1_Fq::from_limbs([6879730919599446368085333962247398, 1277929215078636916099561234278821066, 28958]); + + prove_discrete_log(bad_x, bad_y, k, q_x, q_y); +} \ No newline at end of file