o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
136 lines (119 loc) • 4.64 kB
text/typescript
import { createCurveAffine, createCurveProjective, Pallas, Vesta } from './elliptic-curve.js';
import { Fp, Fq } from './finite-field.js';
import assert from 'node:assert/strict';
import { test, Random } from '../../lib/testing/property.js';
import { CurveParams } from './elliptic-curve-examples.js';
for (let [G, Field, Scalar] of [
[ ] as const,
[ ] as const,
curveWithFields(CurveParams.Secp256k1),
curveWithFields(CurveParams.Secp256r1),
]) {
// endomorphism constants
if (G.hasEndomorphism) {
assert.equal(Field.power(G.endoBase, 3n), 1n, 'cube root in base field');
assert.equal(Scalar.power(G.endoScalar, 3n), 1n, 'cube root in scalar field');
}
let randomScalar = Random(Scalar.random);
let randomField = Random(Field.random);
// create random points by scaling 1 with a random scalar
let randomPoint = Random(() => G.scale(G.one, Scalar.random()));
// let one / zero be sampled 20% of times each
randomPoint = Random.oneOf(G.zero, G.one, randomPoint, randomPoint, randomPoint);
test(
randomPoint,
randomPoint,
randomPoint,
randomScalar,
randomScalar,
randomField,
(X, Y, Z, x, y, f) => {
// check on curve
assert(G.isOnCurve(X) && G.isOnCurve(Y) && G.isOnCurve(Z), 'on curve');
if (G.a === 0n) {
// can't be on curve because b=5 is a non-square
assert(!Field.isSquare(G.b));
assert(!G.isOnCurve({ x: 0n, y, z: 1n }), 'x=0 => y^2 = b is not on curve');
// can't be on curve because the implied equation is f^6 = f^6 + b
assert(
!G.isOnCurve({ x: Field.power(f, 2n), y: Field.power(f, 3n), z: 1n }),
'x^3 = y^2 is not on curve'
);
}
// equal
assert(G.equal(X, X), 'equal');
assert(
!G.equal(X, G.add(X, X)) || G.equal(X, G.zero),
'not equal to double of itself (or zero)'
);
assert(
!G.equal(X, G.negate(X)) || G.equal(X, G.zero),
'not equal to negation of itself (or zero)'
);
assert(!G.equal(X, Y) || X === Y, 'not equal (random points)');
// case where `equal` checks non-trivial z relationship
let X_ = {
x: Field.mul(X.x, Field.square(f)),
y: Field.mul(X.y, Field.power(f, 3n)),
z: Field.mul(X.z, f),
};
assert(G.equal(X, X_), 'equal non-trivial');
// algebraic laws - addition
assert(G.equal(G.add(X, Y), G.add(Y, X)), 'commutative');
assert(G.equal(G.add(X, G.add(Y, Z)), G.add(G.add(X, Y), Z)), 'associative');
assert(G.equal(G.add(X, G.zero), X), 'identity');
assert(G.equal(G.add(X, G.negate(X)), G.zero), 'inverse');
// addition does doubling
assert(G.equal(G.add(X, X), G.double(X)), 'double');
// scaling by small factors
assert(G.equal(G.scale(X, 0n), G.zero), 'scale by 0');
assert(G.equal(G.scale(X, 1n), X), 'scale by 1');
assert(G.equal(G.scale(X, 2n), G.add(X, X)), 'scale by 2');
assert(G.equal(G.scale(X, 3n), G.add(X, G.add(X, X))), 'scale by 3');
assert(G.equal(G.scale(X, 4n), G.double(G.double(X))), 'scale by 4');
// algebraic laws - scaling
assert(
G.equal(G.scale(X, Scalar.add(x, y)), G.add(G.scale(X, x), G.scale(X, y))),
'distributive'
);
assert(
G.equal(G.scale(X, Scalar.negate(x)), G.negate(G.scale(X, x))),
'distributive (negation)'
);
assert(
G.equal(G.scale(X, Scalar.mul(x, y)), G.scale(G.scale(X, x), y)),
'scale / multiply is associative'
);
// endomorphism
if (G.hasEndomorphism) {
assert(G.equal(G.endomorphism(X), G.scale(X, G.endoScalar)), 'efficient endomorphism');
}
// subgroup
assert(G.isInSubgroup(X), 'subgroup check');
// affine
let affineX = G.toAffine(X);
assert(G.equal(G.fromAffine(affineX), X), 'affine - projective roundtrip');
let { x: xa, y: ya } = affineX;
assert(
G.equal(X, G.zero) ||
Field.square(ya) === Field.add(Field.add(Field.power(xa, 3n), Field.mul(G.a, xa)), G.b),
'affine on curve (or zero)'
);
}
);
}
// helper
function curveWithFields(params: CurveParams) {
// this computes endo constants if they aren't there from the beginning
let Affine = createCurveAffine(params);
if (Affine.hasEndomorphism) {
params = {
...params,
endoBase: Affine.Endo.base,
endoScalar: Affine.Endo.scalar,
};
}
let Projective = createCurveProjective(params);
// return Curve, Field and Scalar
return [Projective, Affine.Field, Affine.Scalar] as const;
}