This repository was archived by the owner on May 14, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathPoint.ts
More file actions
127 lines (113 loc) · 3.41 KB
/
Copy pathPoint.ts
File metadata and controls
127 lines (113 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { IOperable } from "./Operable";
/**
* Defines a point on a specific curve. The curve must have the form
* y^2 = x^3 + ax + b and has the curve defined with two number: a and b.
*/
export class Point<T extends IOperable> {
/**
*
* @param a
* @param b
*/
public static infinity<T extends IOperable>(a: T, b: T) {
return new Point(undefined, undefined, a, b);
}
constructor(readonly x: T, readonly y: T, readonly a: T, readonly b: T) {
if (x === undefined && y === undefined) {
return;
}
// y ** 2 !== x ** 3 + a * x + b
if (y.pow(2n).neq(x.pow(3n).add(a.mul(x)).add(b))) {
throw new Error(`(${x}, ${y}) is not on the curve`);
}
}
public toString() {
if (this.x === undefined) {
return "Point(infinity)";
} else {
return `Point(${this.x},${this.y})_${this.a}_${this.b}`;
}
}
/**
* Returns true when the values (x, y, a, b) of this point match the values
* of the other point.
* @param other
*/
public eq(other: Point<T>): boolean {
if (!other) return false;
return (
((this.x && this.x.eq(other.x)) || (!this.x && !other.x)) &&
((this.y && this.y.eq(other.y)) || (!this.y && !other.y)) &&
this.a.eq(other.a) &&
this.b.eq(other.b)
);
}
/**
* Returns true when the values (x, y, a, b) of this point do not match
* the other point
* @param other
*/
public neq(other: Point<T>): boolean {
return !this.eq(other);
}
/**
* Returns true when the x and y values supplied are on the curve
* @param x
* @param y
*/
public onCurve(x: IOperable, y: IOperable): boolean {
// y ** 2n === x ** 3n + this.a * x + this.b
return y.pow(2n).eq(x.pow(3n).add(this.a.mul(x).add(this.b)));
}
/**
* Perform point addition
* @param other
*/
public add(other: Point<T>): Point<T> {
if (this.a.neq(other.a) || this.b.neq(other.b)) {
throw new Error(`Points ${this} and ${other} are not on same curve`);
}
// handle me as identity point
if (this.x === undefined) return other;
// handle other as identity point
if (other.x === undefined) return this;
// handle additive inverse, same x but different y
if (this.x.eq(other.x) && this.y.neq(other.y)) {
return Point.infinity(this.a, this.b);
}
// handle when this.x !== other.x
if (this.x.neq(other.x)) {
const s = other.y.sub(this.y).div(other.x.sub(this.x));
const x = s.pow(2n).sub(this.x).sub(other.x) as T;
const y = s.mul(this.x.sub(x)).sub(this.y) as T;
return new Point<T>(x, y, this.a, this.b);
}
// handle when tangent line is vertical
if (this.eq(other) && this.y.eq(this.x.smul(0n))) {
return Point.infinity(this.a, this.b);
}
// handle when p1 = p2
if (this.eq(other)) {
const s = this.x.pow(2n).smul(3n).add(this.a).div(this.y.smul(2n));
const x = s.pow(2n).sub(this.x.smul(2n)) as T;
const y = s.mul(this.x.sub(x)).sub(this.y) as T;
return new Point<T>(x, y, this.a, this.b);
}
}
/**
* Scalar multiply a point using binary expansion
* @param scalar
*/
public smul(scalar: bigint): Point<T> {
let current: Point<T> = this;
let result = new Point<T>(undefined, undefined, this.a, this.b);
while (scalar) {
if (scalar & 1n) {
result = result.add(current);
}
current = current.add(current);
scalar = scalar >> 1n;
}
return result;
}
}