Skip to content

Commit 70b9645

Browse files
committed
feat(precompiles): implement montgomery arithmetic
1. implement montgomery arithmetic 2. add tests from go-ethereum
1 parent a66316c commit 70b9645

File tree

15 files changed

+610
-93
lines changed

15 files changed

+610
-93
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ src/test/java/org/tron/consensus2
3636
src/main/java/META-INF/
3737
src/main/resources/META-INF/
3838
/bin/
39+
bin
3940

4041
# Eclipse IDE specific files and folders
4142
/.project

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ subprojects {
5252
compile group: 'org.apache.commons', name: 'commons-math', version: '2.2'
5353
compile "org.apache.commons:commons-collections4:4.1"
5454
compile group: 'joda-time', name: 'joda-time', version: '2.3'
55+
implementation 'javax.annotation:javax.annotation-api:1.3.2'
5556

5657
}
5758

crypto/src/main/java/org/tron/common/crypto/zksnark/BN128.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -220,21 +220,6 @@ public boolean isZero() {
220220
return z.isZero();
221221
}
222222

223-
protected boolean isValid() {
224-
225-
// check whether coordinates belongs to the Field
226-
if (!x.isValid() || !y.isValid() || !z.isValid()) {
227-
return false;
228-
}
229-
230-
// check whether point is on the curve
231-
if (!isOnCurve()) {
232-
return false;
233-
}
234-
235-
return true;
236-
}
237-
238223
@Override
239224
public String toString() {
240225
return String.format("(%s; %s; %s)", x.toString(), y.toString(), z.toString());

crypto/src/main/java/org/tron/common/crypto/zksnark/BN128Fp.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public static BN128<Fp> create(byte[] xx, byte[] yy) {
4747
Fp x = Fp.create(xx);
4848
Fp y = Fp.create(yy);
4949

50+
if (x == null || y == null) {
51+
// It means that one or both coordinates are not elements of Fp
52+
return null;
53+
}
54+
5055
// check for point at infinity
5156
if (x.isZero() && y.isZero()) {
5257
return ZERO;
@@ -55,7 +60,7 @@ public static BN128<Fp> create(byte[] xx, byte[] yy) {
5560
BN128<Fp> p = new BN128Fp(x, y, Fp._1);
5661

5762
// check whether point is a valid one
58-
if (p.isValid()) {
63+
if (p.isOnCurve()) {
5964
return p;
6065
} else {
6166
return null;

crypto/src/main/java/org/tron/common/crypto/zksnark/BN128Fp2.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ public static BN128<Fp2> create(byte[] aa, byte[] bb, byte[] cc, byte[] dd) {
5252
Fp2 x = Fp2.create(aa, bb);
5353
Fp2 y = Fp2.create(cc, dd);
5454

55+
if (x == null || y == null) {
56+
// It means that one or both coordinates are not elements of Fp
57+
return null;
58+
}
59+
5560
// check for point at infinity
5661
if (x.isZero() && y.isZero()) {
5762
return ZERO;
@@ -60,7 +65,7 @@ public static BN128<Fp2> create(byte[] aa, byte[] bb, byte[] cc, byte[] dd) {
6065
BN128<Fp2> p = new BN128Fp2(x, y, Fp2._1);
6166

6267
// check whether point is a valid one
63-
if (p.isValid()) {
68+
if (p.isOnCurve()) {
6469
return p;
6570
} else {
6671
return null;

crypto/src/main/java/org/tron/common/crypto/zksnark/Field.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,4 @@ interface Field<T> {
4040
T negate();
4141

4242
boolean isZero();
43-
44-
boolean isValid();
4543
}

crypto/src/main/java/org/tron/common/crypto/zksnark/Fp.java

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,68 @@
1717
*/
1818
package org.tron.common.crypto.zksnark;
1919

20-
import static org.tron.common.crypto.zksnark.Params.P;
21-
2220
import java.math.BigInteger;
2321

2422
/**
2523
* Arithmetic in F_p, p = 21888242871839275222246405745257275088696311157297823662689037894645226208583
26-
*
24+
* This class stores elements of F_p in the Montgomery form: a*r mod p.
25+
*
2726
* @author Mikhail Kalinin
2827
* @since 01.09.2017
2928
*/
3029
public class Fp implements Field<Fp> {
30+
/**
31+
* "p" field parameter of F_p, F_p2, F_p6 and F_p12
32+
*/
33+
static final BigInteger P = new BigInteger(
34+
"21888242871839275222246405745257275088696311157297823662689037894645226208583");
3135

32-
static final Fp ZERO = new Fp(BigInteger.ZERO);
33-
static final Fp _1 = new Fp(BigInteger.ONE);
34-
static final Fp NON_RESIDUE = new Fp(new BigInteger(
36+
/**
37+
* This value is equal to 2^256. It should be greater than {@link #P} and coprime to it.
38+
* Field elements are represented in Montgomery form as a*{@link #REDUCER} mod {@link #P}.
39+
* This specific value of {@link #REDUCER} is selected to facilitate efficient division
40+
* by {@link #REDUCER} through simple shifting.
41+
*/
42+
static final BigInteger REDUCER = new BigInteger(
43+
"115792089237316195423570985008687907853269984665640564039457584007913129639936");
44+
45+
/**
46+
* The number of bits in the {@link #REDUCER} value.
47+
*/
48+
static final int REDUCER_BITS = 256;
49+
50+
/**
51+
* A precomputed value of {@link #REDUCER}^2 mod {@link #P}.
52+
*/
53+
static final BigInteger REDUCER_SQUARED = new BigInteger(
54+
"3096616502983703923843567936837374451735540968419076528771170197431451843209");
55+
56+
/**
57+
* A precomputed value of {@link #REDUCER}^3 mod {@link #P}.
58+
*/
59+
static final BigInteger REDUCER_CUBED = new BigInteger(
60+
"14921786541159648185948152738563080959093619838510245177710943249661917737183");
61+
62+
/**
63+
* A precomputed value of -{@link #P}^{-1} mod {@link #REDUCER}.
64+
*/
65+
static final BigInteger FACTOR = new BigInteger(
66+
"111032442853175714102588374283752698368366046808579839647964533820976443843465");
67+
68+
/**
69+
* The MASK value is set to 2^256 - 1 and is utilized to replace the operation % 2^256
70+
* with a bitwise AND using this value. This choice ensures that only the lower 256 bits
71+
* of a result are retained, effectively simulating the modulus operation.
72+
*/
73+
static final BigInteger MASK = new BigInteger(
74+
"115792089237316195423570985008687907853269984665640564039457584007913129639935");
75+
76+
static final Fp ZERO = Fp.create(BigInteger.ZERO);
77+
static final Fp _1 = Fp.create(BigInteger.ONE);
78+
static final Fp NON_RESIDUE = Fp.create(new BigInteger(
3579
"21888242871839275222246405745257275088696311157297823662689037894645226208582"));
3680

37-
static final Fp _2_INV = new Fp(BigInteger.valueOf(2).modInverse(P));
81+
static final Fp _2_INV = Fp.create(BigInteger.valueOf(2).modInverse(P));
3882

3983
BigInteger v;
4084

@@ -43,41 +87,52 @@ public class Fp implements Field<Fp> {
4387
}
4488

4589
static Fp create(byte[] v) {
46-
return new Fp(new BigInteger(1, v));
90+
BigInteger value = new BigInteger(1, v);
91+
if (value.compareTo(P) >= 0) {
92+
// Only the values less than P are valid
93+
return null;
94+
}
95+
return new Fp(toMontgomery(value));
4796
}
4897

4998
static Fp create(BigInteger v) {
50-
return new Fp(v);
99+
if (v.compareTo(P) >= 0) {
100+
// Only the values less than P are valid
101+
return null;
102+
}
103+
return new Fp(toMontgomery(v));
51104
}
52105

53106
@Override
54107
public Fp add(Fp o) {
55-
return new Fp(this.v.add(o.v).mod(P));
108+
BigInteger r = v.add(o.v);
109+
return new Fp(r.compareTo(P) < 0 ? r : r.subtract(P));
56110
}
57111

58112
@Override
59113
public Fp mul(Fp o) {
60-
return new Fp(this.v.multiply(o.v).mod(P));
114+
return new Fp(redc(v.multiply(o.v)));
61115
}
62116

63117
@Override
64118
public Fp sub(Fp o) {
65-
return new Fp(this.v.subtract(o.v).mod(P));
119+
BigInteger r = v.subtract(o.v);
120+
return new Fp(r.compareTo(BigInteger.ZERO) < 0 ? r.add(P) : r);
66121
}
67122

68123
@Override
69124
public Fp squared() {
70-
return new Fp(v.multiply(v).mod(P));
125+
return new Fp(redc(v.multiply(v)));
71126
}
72127

73128
@Override
74129
public Fp dbl() {
75-
return new Fp(v.add(v).mod(P));
130+
return add(this);
76131
}
77132

78133
@Override
79134
public Fp inverse() {
80-
return new Fp(v.modInverse(P));
135+
return new Fp(redc(v.modInverse(P).multiply(REDUCER_CUBED)));
81136
}
82137

83138
@Override
@@ -90,20 +145,12 @@ public boolean isZero() {
90145
return v.compareTo(BigInteger.ZERO) == 0;
91146
}
92147

93-
/**
94-
* Checks if provided value is a valid Fp member
95-
*/
96-
@Override
97-
public boolean isValid() {
98-
return v.compareTo(P) < 0;
99-
}
100-
101148
Fp2 mul(Fp2 o) {
102149
return new Fp2(o.a.mul(this), o.b.mul(this));
103150
}
104151

105152
public byte[] bytes() {
106-
return v.toByteArray();
153+
return fromMontgomery(v).toByteArray();
107154
}
108155

109156
@Override
@@ -129,4 +176,36 @@ public int hashCode() {
129176
public String toString() {
130177
return v.toString();
131178
}
179+
180+
/**
181+
* Converts a value in normal representation to Montgomery form.
182+
*
183+
* @param n value in normal form
184+
* @return value in Montgomery form
185+
*/
186+
private static BigInteger toMontgomery(BigInteger n) {
187+
return redc(n.multiply(REDUCER_SQUARED));
188+
}
189+
190+
/**
191+
* Converts a value in Montgomery form to a normal representation.
192+
*
193+
* @param n value in Montgomery form
194+
* @return value in normal form
195+
*/
196+
private static BigInteger fromMontgomery(BigInteger n) {
197+
return redc(n);
198+
}
199+
200+
/**
201+
* Montgomery reduction; given a value x, computes x*{@link #REDUCER}^{-1} mod {@link #P}
202+
*
203+
* @param x value to reduce
204+
* @return x*{@link #REDUCER}^{-1} mod {@link #P}
205+
*/
206+
private static BigInteger redc(BigInteger x) {
207+
BigInteger temp = x.multiply(FACTOR).and(MASK);
208+
BigInteger reduced = temp.multiply(P).add(x).shiftRight(REDUCER_BITS);
209+
return reduced.compareTo(P) < 0 ? reduced : reduced.subtract(P);
210+
}
132211
}

crypto/src/main/java/org/tron/common/crypto/zksnark/Fp12.java

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,53 +36,53 @@ class Fp12 implements Field<Fp12> {
3636
static final Fp12 _1 = new Fp12(Fp6._1, Fp6.ZERO);
3737
static final Fp2[] FROBENIUS_COEFFS_B = new Fp2[]{
3838

39-
new Fp2(BigInteger.ONE,
39+
Fp2.create(BigInteger.ONE,
4040
BigInteger.ZERO),
4141

42-
new Fp2(new BigInteger(
42+
Fp2.create(new BigInteger(
4343
"8376118865763821496583973867626364092589906065868298776909617916018768340080"),
4444
new BigInteger(
4545
"16469823323077808223889137241176536799009286646108169935659301613961712198316")),
4646

47-
new Fp2(new BigInteger(
47+
Fp2.create(new BigInteger(
4848
"21888242871839275220042445260109153167277707414472061641714758635765020556617"),
4949
BigInteger.ZERO),
5050

51-
new Fp2(new BigInteger(
51+
Fp2.create(new BigInteger(
5252
"11697423496358154304825782922584725312912383441159505038794027105778954184319"),
5353
new BigInteger(
5454
"303847389135065887422783454877609941456349188919719272345083954437860409601")),
5555

56-
new Fp2(new BigInteger(
56+
Fp2.create(new BigInteger(
5757
"21888242871839275220042445260109153167277707414472061641714758635765020556616"),
5858
BigInteger.ZERO),
5959

60-
new Fp2(new BigInteger(
60+
Fp2.create(new BigInteger(
6161
"3321304630594332808241809054958361220322477375291206261884409189760185844239"),
6262
new BigInteger(
6363
"5722266937896532885780051958958348231143373700109372999374820235121374419868")),
6464

65-
new Fp2(new BigInteger(
65+
Fp2.create(new BigInteger(
6666
"21888242871839275222246405745257275088696311157297823662689037894645226208582"),
6767
BigInteger.ZERO),
6868

69-
new Fp2(new BigInteger(
69+
Fp2.create(new BigInteger(
7070
"13512124006075453725662431877630910996106405091429524885779419978626457868503"),
7171
new BigInteger(
7272
"5418419548761466998357268504080738289687024511189653727029736280683514010267")),
7373

74-
new Fp2(new BigInteger("2203960485148121921418603742825762020974279258880205651966"),
74+
Fp2.create(new BigInteger("2203960485148121921418603742825762020974279258880205651966"),
7575
BigInteger.ZERO),
7676

77-
new Fp2(new BigInteger(
77+
Fp2.create(new BigInteger(
7878
"10190819375481120917420622822672549775783927716138318623895010788866272024264"),
7979
new BigInteger(
8080
"21584395482704209334823622290379665147239961968378104390343953940207365798982")),
8181

82-
new Fp2(new BigInteger("2203960485148121921418603742825762020974279258880205651967"),
82+
Fp2.create(new BigInteger("2203960485148121921418603742825762020974279258880205651967"),
8383
BigInteger.ZERO),
8484

85-
new Fp2(new BigInteger(
85+
Fp2.create(new BigInteger(
8686
"18566938241244942414004596690298913868373833782006617400804628704885040364344"),
8787
new BigInteger(
8888
"16165975933942742336466353786298926857552937457188450663314217659523851788715"))
@@ -233,11 +233,6 @@ public boolean isZero() {
233233
return this.equals(ZERO);
234234
}
235235

236-
@Override
237-
public boolean isValid() {
238-
return a.isValid() && b.isValid();
239-
}
240-
241236
Fp12 frobeniusMap(int power) {
242237

243238
Fp6 ra = a.frobeniusMap(power);

0 commit comments

Comments
 (0)