|
| 1 | +/* |
| 2 | + * Copyright (c) [2016] [ <ether.camp> ] |
| 3 | + * This file is part of the ethereumJ library. |
| 4 | + * |
| 5 | + * The ethereumJ library is free software: you can redistribute it and/or modify |
| 6 | + * it under the terms of the GNU Lesser General Public License as published by |
| 7 | + * the Free Software Foundation, either version 3 of the License, or |
| 8 | + * (at your option) any later version. |
| 9 | + * |
| 10 | + * The ethereumJ library is distributed in the hope that it will be useful, |
| 11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | + * GNU Lesser General Public License for more details. |
| 14 | + * |
| 15 | + * You should have received a copy of the GNU Lesser General Public License |
| 16 | + * along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>. |
| 17 | + */ |
| 18 | +package org.tron.common.crypto.zksnark; |
| 19 | + |
| 20 | +import java.math.BigInteger; |
| 21 | + |
| 22 | +/** |
| 23 | + * Implementation of Barreto–Naehrig curve defined over abstract finite field. This curve is one of the keys to zkSNARKs. <br/> |
| 24 | + * This specific curve was introduced in |
| 25 | + * <a href="https://github.com/scipr-lab/libff#elliptic-curve-choices">libff</a> |
| 26 | + * and used by a proving system in |
| 27 | + * <a href="https://github.com/zcash/zcash/wiki/specification#zcash-protocol">ZCash protocol</a> <br/> |
| 28 | + * <br/> |
| 29 | + * |
| 30 | + * Curve equation: <br/> |
| 31 | + * Y^2 = X^3 + b, where "b" is a constant number belonging to corresponding specific field <br/> |
| 32 | + * Point at infinity is encoded as <code>(0, 0, 0)</code> <br/> |
| 33 | + * <br/> |
| 34 | + * |
| 35 | + * This curve has embedding degree 12 with respect to "r" (see {@link Params#R}), which means that "r" is a multiple of "p ^ 12 - 1", |
| 36 | + * this condition is important for pairing operation implemented in {@link PairingCheck}<br/> |
| 37 | + * <br/> |
| 38 | + * |
| 39 | + * Code of curve arithmetic has been ported from |
| 40 | + * <a href="https://github.com/scipr-lab/libff/blob/master/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp">libff</a> <br/> |
| 41 | + * <br/> |
| 42 | + * |
| 43 | + * Current implementation uses Jacobian coordinate system as |
| 44 | + * <a href="https://github.com/scipr-lab/libff/blob/master/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp">libff</a> does, |
| 45 | + * use {@link #toEthNotation()} to convert Jacobian coords to Ethereum encoding <br/> |
| 46 | + * |
| 47 | + * @author Mikhail Kalinin |
| 48 | + * @since 05.09.2017 |
| 49 | + */ |
| 50 | +public abstract class BN128<T extends Field<T>> { |
| 51 | + |
| 52 | + protected T x; |
| 53 | + protected T y; |
| 54 | + protected T z; |
| 55 | + |
| 56 | + protected BN128(T x, T y, T z) { |
| 57 | + this.x = x; |
| 58 | + this.y = y; |
| 59 | + this.z = z; |
| 60 | + } |
| 61 | + |
| 62 | + /** |
| 63 | + * Point at infinity in Ethereum notation: should return (0; 0; 0), |
| 64 | + * {@link #isZero()} method called for that point, also, returns {@code true} |
| 65 | + */ |
| 66 | + abstract protected BN128<T> zero(); |
| 67 | + abstract protected BN128<T> instance(T x, T y, T z); |
| 68 | + abstract protected T b(); |
| 69 | + abstract protected T one(); |
| 70 | + |
| 71 | + /** |
| 72 | + * Transforms given Jacobian to affine coordinates and then creates a point |
| 73 | + */ |
| 74 | + public BN128<T> toAffine() { |
| 75 | + |
| 76 | + if (isZero()) { |
| 77 | + BN128<T> zero = zero(); |
| 78 | + return instance(zero.x, one(), zero.z); // (0; 1; 0) |
| 79 | + } |
| 80 | + |
| 81 | + T zInv = z.inverse(); |
| 82 | + T zInv2 = zInv.squared(); |
| 83 | + T zInv3 = zInv2.mul(zInv); |
| 84 | + |
| 85 | + T ax = x.mul(zInv2); |
| 86 | + T ay = y.mul(zInv3); |
| 87 | + |
| 88 | + return instance(ax, ay, one()); |
| 89 | + } |
| 90 | + |
| 91 | + /** |
| 92 | + * Runs affine transformation and encodes point at infinity as (0; 0; 0) |
| 93 | + */ |
| 94 | + public BN128<T> toEthNotation() { |
| 95 | + BN128<T> affine = toAffine(); |
| 96 | + |
| 97 | + // affine zero is (0; 1; 0), convert to Ethereum zero: (0; 0; 0) |
| 98 | + if (affine.isZero()) { |
| 99 | + return zero(); |
| 100 | + } else { |
| 101 | + return affine; |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + protected boolean isOnCurve() { |
| 106 | + |
| 107 | + if (isZero()) return true; |
| 108 | + |
| 109 | + T z6 = z.squared().mul(z).squared(); |
| 110 | + |
| 111 | + T left = y.squared(); // y^2 |
| 112 | + T right = x.squared().mul(x).add(b().mul(z6)); // x^3 + b * z^6 |
| 113 | + return left.equals(right); |
| 114 | + } |
| 115 | + |
| 116 | + public BN128<T> add(BN128<T> o) { |
| 117 | + |
| 118 | + if (this.isZero()) return o; // 0 + P = P |
| 119 | + if (o.isZero()) return this; // P + 0 = P |
| 120 | + |
| 121 | + T x1 = this.x, y1 = this.y, z1 = this.z; |
| 122 | + T x2 = o.x, y2 = o.y, z2 = o.z; |
| 123 | + |
| 124 | + // ported code is started from here |
| 125 | + // next calculations are done in Jacobian coordinates |
| 126 | + |
| 127 | + T z1z1 = z1.squared(); |
| 128 | + T z2z2 = z2.squared(); |
| 129 | + |
| 130 | + T u1 = x1.mul(z2z2); |
| 131 | + T u2 = x2.mul(z1z1); |
| 132 | + |
| 133 | + T z1Cubed = z1.mul(z1z1); |
| 134 | + T z2Cubed = z2.mul(z2z2); |
| 135 | + |
| 136 | + T s1 = y1.mul(z2Cubed); // s1 = y1 * Z2^3 |
| 137 | + T s2 = y2.mul(z1Cubed); // s2 = y2 * Z1^3 |
| 138 | + |
| 139 | + if (u1.equals(u2) && s1.equals(s2)) { |
| 140 | + return dbl(); // P + P = 2P |
| 141 | + } |
| 142 | + |
| 143 | + T h = u2.sub(u1); // h = u2 - u1 |
| 144 | + T i = h.dbl().squared(); // i = (2 * h)^2 |
| 145 | + T j = h.mul(i); // j = h * i |
| 146 | + T r = s2.sub(s1).dbl(); // r = 2 * (s2 - s1) |
| 147 | + T v = u1.mul(i); // v = u1 * i |
| 148 | + T zz = z1.add(z2).squared() |
| 149 | + .sub(z1.squared()).sub(z2.squared()); |
| 150 | + |
| 151 | + T x3 = r.squared().sub(j).sub(v.dbl()); // x3 = r^2 - j - 2 * v |
| 152 | + T y3 = v.sub(x3).mul(r).sub(s1.mul(j).dbl()); // y3 = r * (v - x3) - 2 * (s1 * j) |
| 153 | + T z3 = zz.mul(h); // z3 = ((z1+z2)^2 - z1^2 - z2^2) * h = zz * h |
| 154 | + |
| 155 | + return instance(x3, y3, z3); |
| 156 | + } |
| 157 | + |
| 158 | + public BN128<T> mul(BigInteger s) { |
| 159 | + |
| 160 | + if (s.compareTo(BigInteger.ZERO) == 0) // P * 0 = 0 |
| 161 | + return zero(); |
| 162 | + |
| 163 | + if (isZero()) return this; // 0 * s = 0 |
| 164 | + |
| 165 | + BN128<T> res = zero(); |
| 166 | + |
| 167 | + for (int i = s.bitLength() - 1; i >= 0; i--) { |
| 168 | + |
| 169 | + res = res.dbl(); |
| 170 | + |
| 171 | + if (s.testBit(i)) { |
| 172 | + res = res.add(this); |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + return res; |
| 177 | + } |
| 178 | + |
| 179 | + private BN128<T> dbl() { |
| 180 | + |
| 181 | + if (isZero()) return this; |
| 182 | + |
| 183 | + // ported code is started from here |
| 184 | + // next calculations are done in Jacobian coordinates with z = 1 |
| 185 | + |
| 186 | + T a = x.squared(); // a = x^2 |
| 187 | + T b = y.squared(); // b = y^2 |
| 188 | + T c = b.squared(); // c = b^2 |
| 189 | + T d = x.add(b).squared().sub(a).sub(c); |
| 190 | + d = d.add(d); // d = 2 * ((x + b)^2 - a - c) |
| 191 | + T e = a.add(a).add(a); // e = 3 * a |
| 192 | + T f = e.squared(); // f = e^2 |
| 193 | + |
| 194 | + T x3 = f.sub(d.add(d)); // rx = f - 2 * d |
| 195 | + T y3 = e.mul(d.sub(x3)).sub(c.dbl().dbl().dbl()); // ry = e * (d - rx) - 8 * c |
| 196 | + T z3 = y.mul(z).dbl(); // z3 = 2 * y * z |
| 197 | + |
| 198 | + return instance(x3, y3, z3); |
| 199 | + } |
| 200 | + |
| 201 | + public T x() { |
| 202 | + return x; |
| 203 | + } |
| 204 | + |
| 205 | + public T y() { |
| 206 | + return y; |
| 207 | + } |
| 208 | + |
| 209 | + public boolean isZero() { |
| 210 | + return z.isZero(); |
| 211 | + } |
| 212 | + |
| 213 | + protected boolean isValid() { |
| 214 | + |
| 215 | + // check whether coordinates belongs to the Field |
| 216 | + if (!x.isValid() || !y.isValid() || !z.isValid()) { |
| 217 | + return false; |
| 218 | + } |
| 219 | + |
| 220 | + // check whether point is on the curve |
| 221 | + if (!isOnCurve()) { |
| 222 | + return false; |
| 223 | + } |
| 224 | + |
| 225 | + return true; |
| 226 | + } |
| 227 | + |
| 228 | + @Override |
| 229 | + public String toString() { |
| 230 | + return String.format("(%s; %s; %s)", x.toString(), y.toString(), z.toString()); |
| 231 | + } |
| 232 | + |
| 233 | + @Override |
| 234 | + @SuppressWarnings("all") |
| 235 | + public boolean equals(Object o) { |
| 236 | + if (this == o) return true; |
| 237 | + if (!(o instanceof BN128)) return false; |
| 238 | + |
| 239 | + BN128<?> bn128 = (BN128<?>) o; |
| 240 | + |
| 241 | + if (x != null ? !x.equals(bn128.x) : bn128.x != null) return false; |
| 242 | + if (y != null ? !y.equals(bn128.y) : bn128.y != null) return false; |
| 243 | + return !(z != null ? !z.equals(bn128.z) : bn128.z != null); |
| 244 | + } |
| 245 | +} |
0 commit comments