Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions discrete_log_example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
circuits/target/
42 changes: 42 additions & 0 deletions discrete_log_example/README.md
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions discrete_log_example/circuits/Nargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
14 changes: 14 additions & 0 deletions discrete_log_example/circuits/Prover.toml
Original file line number Diff line number Diff line change
@@ -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"]
78 changes: 78 additions & 0 deletions discrete_log_example/circuits/src/main.nr
Original file line number Diff line number Diff line change
@@ -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);
}