Skip to content

Commit

Permalink
dedupe bls params
Browse files Browse the repository at this point in the history
  • Loading branch information
kevincharm committed Aug 5, 2024
1 parent 0cd4a2c commit 3b44e5c
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 49 deletions.
60 changes: 32 additions & 28 deletions src/ff.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
// BLS12-381 field prime
export const P =
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn
// BLS12-381 curve order
export const R = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n
// BLS12-381 x parameter used for the construction of the curve
export const X = 0xd201000000010000n
// https://gist.github.com/HarryR/eb5ad0e5de51633678e015a6b06969a1
export const X = -0xd201000000010000n
// Curve order
export const R = X ** 4n - X ** 2n + 1n
// Field modulus
export const Q = ((X - 1n) ** 2n / 3n) * R + X

export function abs(n: bigint): bigint {
return n < 0n ? -n : n
}

// n mod m
export function mod(n: bigint, m: bigint): bigint {
return ((n % m) + m) % m
}

export function modp(n: bigint): bigint {
return mod(n, P)
return mod(n, Q)
}

export function egcd(a: bigint, b: bigint): [bigint, bigint, bigint] {
Expand All @@ -26,7 +29,7 @@ export function egcd(a: bigint, b: bigint): [bigint, bigint, bigint] {

// x * y (mod p)
export function mulmodp(x: bigint, y: bigint): bigint {
return mod(x * y, P)
return mod(x * y, Q)
}

export function modexp(x: bigint, y: bigint, p: bigint): bigint {
Expand All @@ -53,8 +56,8 @@ function gcd(u: bigint, v: bigint, x1: bigint, x2: bigint, p: bigint): bigint {
}

// Base cases
if (u === 1n) return mod(x1, P)
if (v === 1n) return mod(x2, P)
if (u === 1n) return mod(x1, Q)
if (v === 1n) return mod(x2, Q)
if (u % 2n === 0n) {
if (x1 % 2n === 0n) {
return gcd(u >> 1n, v, x1 >> 1n, x2, p)
Expand All @@ -66,7 +69,7 @@ function gcd(u: bigint, v: bigint, x1: bigint, x2: bigint, p: bigint): bigint {
if (x2 % 2n === 0n) {
return gcd(u, v >> 1n, x1, x2 >> 1n, p)
} else {
return gcd(u, v >> 1n, x1, (x2 + P) >> 1n, p)
return gcd(u, v >> 1n, x1, (x2 + Q) >> 1n, p)
}
}
if (u >= v) {
Expand Down Expand Up @@ -141,7 +144,7 @@ export class Fq implements Field {
value: bigint

constructor(x: bigint) {
this.value = mod(x, P)
this.value = mod(x, Q)
}

static zero(): Fq {
Expand Down Expand Up @@ -184,7 +187,7 @@ export class Fq implements Field {
if (this.value === 0n) {
throw new Error(`Inversion of zero`)
}
return new Fq(gcd(this.value, P, 1n, 0n, P))
return new Fq(gcd(this.value, Q, 1n, 0n, Q))
}

exp(y: bigint): Fq {
Expand All @@ -202,18 +205,18 @@ export class Fq implements Field {
}

legendre(): number {
const x = this.exp((P - 1n) / 2n)
if (x.equals(new Fq(P - 1n))) {
const x = this.exp((Q - 1n) / 2n)
if (x.equals(new Fq(Q - 1n))) {
return -1
}
if (!x.equals(Fq.zero()) && !x.equals(new Fq(1n))) {
throw new Error(`Legendre failed: ${this}^{(${P}-1)//2} = ${x}`)
throw new Error(`Legendre failed: ${this}^{(${Q}-1)//2} = ${x}`)
}
return Number(x)
}

sqrt(): Fq {
const root = this.exp((P + 1n) / 4n)
const root = this.exp((Q + 1n) / 4n)
if (!root.mul(root).equals(this)) {
throw new Error(`No square root exists for ${this}`)
}
Expand Down Expand Up @@ -243,15 +246,15 @@ export class Fq implements Field {
/// Quadratic extension to Fq
export class Fq2 implements Field {
// This is taken from consensus specs
static readonly ORDER = P ** 2n
static readonly ORDER = Q ** 2n
static readonly EIGHTH_ROOTS_OF_UNITY = Array.from({ length: 8 }, (_, k) =>
Fq2.fromTuple([1n, 1n]).exp((Fq2.ORDER * BigInt(k)) / 8n),
)
static readonly EVEN_EIGHT_ROOTS_OF_UNITY = Fq2.EIGHTH_ROOTS_OF_UNITY.filter(
(_, i) => i % 2 === 0,
)
static readonly NON_RESIDUE = Fq2.fromTuple([1n, 1n])
static readonly FROBENIUS_COEFFICIENTS = frobeniusCoeffs(new Fq(-1n), P, 2n)
static readonly FROBENIUS_COEFFICIENTS = frobeniusCoeffs(new Fq(-1n), Q, 2n)

x: Fq
y: Fq
Expand Down Expand Up @@ -401,7 +404,7 @@ type Fq2Tuple = ReturnType<Fq2['toTuple']>

/// Cubic extension to Fq2
export class Fq6 implements Field {
static readonly FROBENIUS_COEFFICIENTS = frobeniusCoeffs(Fq2.NON_RESIDUE, P, 6n, 2n, 3n)
static readonly FROBENIUS_COEFFICIENTS = frobeniusCoeffs(Fq2.NON_RESIDUE, Q, 6n, 2n, 3n)

x: Fq2
y: Fq2
Expand Down Expand Up @@ -543,7 +546,7 @@ type Fq6Tuple = ReturnType<Fq6['toTuple']>

/// 12th degree extension to Fq
export class Fq12 implements Field {
static readonly FROBENIUS_COEFFICIENTS = frobeniusCoeffs(Fq2.NON_RESIDUE, P, 12n, 1n, 6n)
static readonly FROBENIUS_COEFFICIENTS = frobeniusCoeffs(Fq2.NON_RESIDUE, Q, 12n, 1n, 6n)

x: Fq6
y: Fq6
Expand Down Expand Up @@ -664,7 +667,7 @@ export class Fq12 implements Field {
const len = bitlen(power)
for (let i = BigInt(len - 1); i >= 0n; i--) {
z = z.cyclotomicSquare()
if ((X >> i) & 1n) {
if ((power >> i) & 1n) {
z = this.mul(z)
}
}
Expand All @@ -673,16 +676,17 @@ export class Fq12 implements Field {

// Borrowed from https://github.com/paulmillr/noble-curves/blob/main/src/bls12-381.ts
finalExp(): Fq12 {
const x = abs(X)
// this^(q⁶) / this
const t0 = this.frobeniusMap(6n).mul(this.inv())
// t0^(q²) * t0
const t1 = t0.frobeniusMap(2n).mul(t0)
const t2 = t1.cyclotomicExp(X).conjugate()
const t2 = t1.cyclotomicExp(x).conjugate()
const t3 = t1.cyclotomicSquare().conjugate().mul(t2)
const t4 = t3.cyclotomicExp(X).conjugate()
const t5 = t4.cyclotomicExp(X).conjugate()
const t6 = t5.cyclotomicExp(X).conjugate().mul(t2.cyclotomicSquare())
const t7 = t6.cyclotomicExp(X).conjugate()
const t4 = t3.cyclotomicExp(x).conjugate()
const t5 = t4.cyclotomicExp(x).conjugate()
const t6 = t5.cyclotomicExp(x).conjugate().mul(t2.cyclotomicSquare())
const t7 = t6.cyclotomicExp(x).conjugate()
const t2_t5_pow_q2 = t2.mul(t5).frobeniusMap(2n)
const t4_t1_pow_q3 = t4.mul(t1).frobeniusMap(3n)
const t6_t1c_pow_q1 = t6.mul(t1.conjugate()).frobeniusMap(1n)
Expand Down
4 changes: 2 additions & 2 deletions src/pairing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fq12, P, R, X } from './ff'
import { abs, Fq12, X } from './ff'
import { PointG1, PointG2 } from './point'

// Compute asymmetric pairing e(p,q)
Expand Down Expand Up @@ -39,7 +39,7 @@ export function miller(p: PointG1, q: PointG2): Fq12 {
// Binary representation of curve parameter B
// NB: This can be precomputed!
const iterations: boolean[] = []
let curveX = X
let curveX = abs(X)
while (curveX > 0n) {
const isOddBit = Boolean(curveX & 1n)
iterations.push(isOddBit)
Expand Down
4 changes: 2 additions & 2 deletions src/point.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Field, Fq, Fq12, Fq2, Fq6, R, X } from './ff'
import { abs, Field, Fq, Fq12, Fq2, Fq6, R, X } from './ff'
import { toBigInt } from './utils'

export interface Point<F extends Field> {
Expand Down Expand Up @@ -154,7 +154,7 @@ export class PointG1 implements PointInstanceType<Fq> {
clearCofactor(): PointG1 {
// To map an element p in E(Fq) to G1, we can exponentiate p by 1-x
// https://eprint.iacr.org/2019/403.pdf Section 5 "Clearing cofactors"
return this.mul(X).add(this)
return this.mul(abs(X)).add(this)
}

isOnCurve(): boolean {
Expand Down
16 changes: 8 additions & 8 deletions src/sswu.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { modp, mulmodp, mod, P, modexp } from './ff'
import { modp, mulmodp, mod, Q, modexp } from './ff'

// 11-isogeny curve to BLS12-381 G1
export const A =
Expand Down Expand Up @@ -83,7 +83,7 @@ export function mapToPointSSWU(u: bigint): [bigint, bigint] {
tv2 = modp(tv2 + tv1)
let tv3 = modp(tv2 + 1n)
tv3 = mulmodp(B, tv3)
let tv4 = cmov(Z, P - tv2, tv2 !== 0n)
let tv4 = cmov(Z, Q - tv2, tv2 !== 0n)
tv4 = mulmodp(A, tv4)
tv2 = mulmodp(tv3, tv3)
let tv6 = mulmodp(tv4, tv4)
Expand All @@ -100,8 +100,8 @@ export function mapToPointSSWU(u: bigint): [bigint, bigint] {
x = cmov(x, tv3, is_gx1_square)
y = cmov(y, y1, is_gx1_square)
const e1 = sgn0(u) === sgn0(y)
y = cmov(P - y, y, e1)
x = mulmodp(x, inv0(tv4, P))
y = cmov(Q - y, y, e1)
x = mulmodp(x, inv0(tv4, Q))
return isoMapG1(x, y)
}

Expand All @@ -119,12 +119,12 @@ function isoMapG1(u: bigint, v: bigint): [bigint, bigint] {
// y_num = k_(3,15) * x'^15 + k_(3,14) * x'^14 + k_(3,13) * x'^13 + ... + k_(3,0)
const y_num = k3.reduce((acc, i) => modp(i + mulmodp(u, acc)), 0n)
// y_den = x'^15 + k_(4,14) * x'^14 + k_(4,13) * x'^13 + ... + k_(4,0)
const y_den = [1n].concat(k4).reduce((acc, i) => mod(i + mulmodp(u, acc), P), 0n)
const y_den = [1n].concat(k4).reduce((acc, i) => mod(i + mulmodp(u, acc), Q), 0n)

// x = x_num / x_den
const x = mulmodp(x_num, inv0(x_den, P))
const x = mulmodp(x_num, inv0(x_den, Q))
// y = y' * y_num / y_den
const y = mulmodp(v, mulmodp(y_num, inv0(y_den, P)))
const y = mulmodp(v, mulmodp(y_num, inv0(y_den, Q)))

return [x, y]
}
Expand All @@ -146,7 +146,7 @@ function sqrtRatio3mod4(
let tv1 = mulmodp(v, v)
let tv2 = mulmodp(u, v)
tv1 = mulmodp(tv1, tv2)
let y1 = modexp(tv1, C1, P)
let y1 = modexp(tv1, C1, Q)
y1 = mulmodp(y1, tv2)
let y2 = mulmodp(y1, C2)
const tv3 = mulmodp(mulmodp(y1, y1), v)
Expand Down
4 changes: 2 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fq, Fq12, Fq2, Fq6, P } from './ff'
import { Fq, Fq12, Fq2, Fq6, Q } from './ff'

export function randomFq12(): Fq12 {
return new Fq12(randomFq6(), randomFq6())
Expand All @@ -13,7 +13,7 @@ export function randomFq2(): Fq2 {
}

export function randomFq(): Fq {
return new Fq(randomBigIntModP(48, P))
return new Fq(randomBigIntModP(48, Q))
}

export function randomBigIntModP(bytes: number, p: bigint): bigint {
Expand Down
11 changes: 4 additions & 7 deletions src/witness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@
// Ref bls-lambda-roots.ipynb from Novakovic: https://github.com/akinovak/garaga/blob/4af45d5fd88bc92bfb775cd1c5c8836edfcd5c68/hydra/hints/bls-lambda-roots.ipynb
// Ref tower_to_direct_extension.py from feltroidprime: https://gist.github.com/feltroidprime/bd31ab8e0cbc0bf8cd952c8b8ed55bf5
// and "Faster Extension Field multiplications for Emulated Pairing Circuits": https://hackmd.io/@feltroidprime/B1eyHHXNT
import { egcd, Fq12, mod } from './ff'
import { egcd, Fq12, mod, Q, R } from './ff'
import { pair } from './pairing'
import { PointG1, PointG2 } from './point'
import { assert } from './utils'

const x = -0xd201000000010000n
const k = 12n
const r = x ** 4n - x ** 2n + 1n
const q = ((x - 1n) ** 2n / 3n) * r + x
const h = (q ** k - 1n) / r
const k = 12n // Embedding degree, as in BLS12
const h = (Q ** k - 1n) / R

const lambda =
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129030796414117214202539n
const m = lambda / r
const m = lambda / R

const p = 5044125407647214251n
const h3 =
Expand Down

0 comments on commit 3b44e5c

Please sign in to comment.