AbstractFieldPoint.java
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.crypto.altbn128;
import java.math.BigInteger;
import java.util.Objects;
import com.google.common.base.MoreObjects;
/**
* Adapted from the pc_ecc (Apache 2 License) implementation:
* https://github.com/ethereum/py_ecc/blob/master/py_ecc/bn128/bn128_field_elements.py
*
* @param <U> the type parameter
*/
@SuppressWarnings("rawtypes")
public abstract class AbstractFieldPoint<U extends AbstractFieldPoint> implements FieldPoint<U> {
private static final BigInteger TWO = BigInteger.valueOf(2);
/** The X. */
@SuppressWarnings("rawtypes")
protected final FieldElement x;
/** The Y. */
@SuppressWarnings("rawtypes")
protected final FieldElement y;
/**
* Instantiates a new Abstract field point.
*
* @param x the x
* @param y the y
*/
@SuppressWarnings("rawtypes")
AbstractFieldPoint(final FieldElement x, final FieldElement y) {
this.x = x;
this.y = y;
}
/**
* Infinity u.
*
* @return the u
*/
protected abstract U infinity();
/**
* New instance of generic type U.
*
* @param x the x
* @param y the y
* @return the U
*/
@SuppressWarnings("rawtypes")
protected abstract U newInstance(final FieldElement x, final FieldElement y);
@Override
public boolean isInfinity() {
return x.isZero() && y.isZero();
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public U add(final U other) {
if (isInfinity() || other.isInfinity()) {
return isInfinity() ? other : (U) this;
} else if (equals(other)) {
return doub();
} else if (x.equals(other.x)) {
return infinity();
} else {
final FieldElement x1 = x;
final FieldElement y1 = y;
final FieldElement x2 = other.x;
final FieldElement y2 = other.y;
final FieldElement m = y2.subtract(y1).divide(x2.subtract(x1));
final FieldElement mSquared = m.power(2);
final FieldElement newX = mSquared.subtract(x1).subtract(x2);
final FieldElement newY = m.negate().multiply(newX).add(m.multiply(x1)).subtract(y1);
return newInstance(newX, newY);
}
}
@SuppressWarnings("unchecked")
@Override
public U multiply(final U other) {
return null;
}
@SuppressWarnings("unchecked")
@Override
public U multiply(final BigInteger n) {
if (n.compareTo(BigInteger.ZERO) == 0) {
return infinity();
} else if (n.compareTo(BigInteger.ONE) == 0) {
return newInstance(x, y);
} else if (n.mod(TWO).compareTo(BigInteger.ZERO) == 0) {
return (U) doub().multiply(n.divide(TWO));
} else {
return (U) doub().multiply(n.divide(TWO)).add(this);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public U doub() {
final FieldElement xSquared = x.power(2);
final FieldElement m = xSquared.multiply(3).divide(y.multiply(2));
final FieldElement mSquared = m.power(2);
final FieldElement newX = mSquared.subtract(x.multiply(2));
final FieldElement newY = m.negate().multiply(newX).add(m.multiply(x)).subtract(y);
return newInstance(newX, newY);
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public U negate() {
if (isInfinity()) {
return (U) this;
}
return newInstance(x, y.negate());
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass()).add("x", x).add("y", y).toString();
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@SuppressWarnings("rawtypes")
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AbstractFieldPoint)) {
return false;
}
final AbstractFieldPoint other = (AbstractFieldPoint) obj;
return Objects.equals(x, other.x) && Objects.equals(y, other.y);
}
}