diff --git a/ethereum/verkletrie/build.gradle b/ethereum/verkletrie/build.gradle new file mode 100644 index 0000000000..a99e79c8c4 --- /dev/null +++ b/ethereum/verkletrie/build.gradle @@ -0,0 +1,56 @@ +/* + * Copyright Besu Contributors. + * + * 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 + */ + +apply plugin: 'java-library' + +jar { + archiveBaseName = 'besu-verkletrie' + manifest { + attributes( + 'Specification-Title': archiveBaseName, + 'Specification-Version': project.version, + 'Implementation-Title': archiveBaseName, + 'Implementation-Version': calculateVersion() + ) + } +} + +dependencies { + implementation project(':crypto') + implementation project(':ethereum:rlp') + implementation project(':ethereum:trie') + + implementation "org.immutables:value-annotations" + implementation 'com.google.guava:guava' + implementation 'io.opentelemetry:opentelemetry-api' + implementation 'org.apache.tuweni:tuweni-bytes' + implementation 'org.bouncycastle:bcprov-jdk15on' + implementation 'org.hyperledger.besu:ipa-multipoint' + + annotationProcessor "org.immutables:value" + + testImplementation project(':services:kvstore') + testImplementation project(':testutil') + + testImplementation 'com.fasterxml.jackson.core:jackson-databind' + testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml' + testImplementation 'junit:junit' + testImplementation 'org.apache.tuweni:tuweni-units' + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.mockito:mockito-core' + + testRuntimeOnly 'org.junit.vintage:junit-vintage-engine' +} diff --git a/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/Point.java b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/Point.java new file mode 100644 index 0000000000..18f9dcc8af --- /dev/null +++ b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/Point.java @@ -0,0 +1,55 @@ +/* + * Copyright Besu Contributors. + * + * 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.ethereum.verkletrie.bandersnatch; + +import org.hyperledger.besu.ethereum.verkletrie.bandersnatch.fp.Element; + +import java.nio.ByteOrder; + +import org.apache.tuweni.bytes.Bytes32; + +public class Point { + + public static final Point EMPTY = new Point(Element.ZERO, Element.ZERO, Element.ZERO); + public static final Point IDENTITY = new Point(Element.ZERO, Element.ONE, Element.ONE); + public final Element x; + public final Element y; + public final Element z; + + public Point(final Element x, final Element y, final Element z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public String toString() { + return "Point{" + "x=" + x + ", y=" + y + ", z=" + z + '}'; + } + + public Bytes32 mapToBaseFieldBytes() { + Element res = x.divide(y); + return res.getBytes(ByteOrder.LITTLE_ENDIAN); + } + + public Bytes32 bytes() { + PointAffine affineRepresentation = PointAffine.fromProj(this); + Element x = affineRepresentation.x; + if (!affineRepresentation.y.lexicographicallyLargest()) { + x = x.neg(); + } + return x.getBytes(ByteOrder.BIG_ENDIAN); + } +} diff --git a/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/PointAffine.java b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/PointAffine.java new file mode 100644 index 0000000000..400fa4fbeb --- /dev/null +++ b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/PointAffine.java @@ -0,0 +1,32 @@ +/* + * Copyright Besu Contributors. + * + * 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.ethereum.verkletrie.bandersnatch; + +import org.hyperledger.besu.ethereum.verkletrie.bandersnatch.fp.Element; + +public class PointAffine { + + final Element x; + final Element y; + + public PointAffine(final Element x, final Element y) { + this.x = x; + this.y = y; + } + + public static PointAffine fromProj(final Point point) { + return new PointAffine(point.x.divide(point.z), point.y.divide(point.z)); + } +} diff --git a/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fp/Element.java b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fp/Element.java new file mode 100644 index 0000000000..80d740a30a --- /dev/null +++ b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fp/Element.java @@ -0,0 +1,526 @@ +/* + * Copyright Besu Contributors. + * + * 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.ethereum.verkletrie.bandersnatch.fp; + +import java.math.BigInteger; +import java.nio.ByteOrder; +import java.util.Objects; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.MutableBytes32; +import org.apache.tuweni.units.bigints.UInt256; + +public class Element { + public static final Element ZERO = new Element(UInt256.ZERO); + public static final Element ONE; + static final Element Q_MODULUS; + private static final Element R_SQUARE; + + static { + { + // z0, z1, z2 and z3 represent the 4 limbs of element + // `0` in Montgomery form. + UInt256 z0 = UInt256.valueOf(8589934590L); + UInt256 z1 = UInt256.valueOf(6378425256633387010L).shiftLeft(64); + UInt256 z2 = UInt256.valueOf(new BigInteger("11064306276430008309", 10)).shiftLeft(128); + UInt256 z3 = UInt256.valueOf(1739710354780652911L).shiftLeft(192); + ONE = new Element(z0.add(z1).add(z2).add(z3)); + } + { + UInt256 z0 = UInt256.valueOf(new BigInteger("18446744069414584321", 10)); + UInt256 z1 = UInt256.valueOf(new BigInteger("6034159408538082302", 10)).shiftLeft(64); + UInt256 z2 = UInt256.valueOf(new BigInteger("3691218898639771653", 10)).shiftLeft(128); + UInt256 z3 = UInt256.valueOf(new BigInteger("8353516859464449352", 10)).shiftLeft(192); + Q_MODULUS = new Element(z0.add(z1).add(z2).add(z3)); + } + { + UInt256 z0 = UInt256.valueOf(new BigInteger("14526898881837571181", 10)); + UInt256 z1 = UInt256.valueOf(new BigInteger("3129137299524312099", 10)).shiftLeft(64); + UInt256 z2 = UInt256.valueOf(new BigInteger("419701826671360399", 10)).shiftLeft(128); + UInt256 z3 = UInt256.valueOf(new BigInteger("524908885293268753", 10)).shiftLeft(192); + R_SQUARE = new Element(z0.add(z1).add(z2).add(z3)); + } + } + + public static Element random() { + UInt256 value = UInt256.fromBytes(Bytes32.random()); + UInt256 divisor = UInt256.fromBytes(Bytes32.rightPad(Q_MODULUS.value.slice(8))); + value = value.mod(divisor); + + if (value.greaterThan(Q_MODULUS.value)) { + value = value.subtract(Q_MODULUS.value); + } + return new Element(value); + } + + final UInt256 value; + + public Element(final UInt256 value) { + this.value = value; + } + + public static Element fromBytes(final Bytes data, final ByteOrder byteOrder) { + return new Element( + UInt256.fromBytes(byteOrder == ByteOrder.BIG_ENDIAN ? data : data.reverse())); + } + + public boolean biggerModulus() { + return value.greaterOrEqualThan(Q_MODULUS.value); + } + + public Element inverse() { + if (isZero()) { + return new Element(UInt256.ZERO); + } + UInt256 u = Q_MODULUS.value; + UInt256 s = R_SQUARE.value; + UInt256 v = value; + UInt256 r = UInt256.ZERO; + while (true) { + while ((v.getLong(24) & 1L) == 0) { + v = v.shiftRight(1); + if ((s.getLong(24) & 1L) == 1) { + s = s.add(Q_MODULUS.value); + } + s = s.shiftRight(1); + } + while ((u.getLong(24) & 1L) == 0) { + u = u.shiftRight(1); + if ((r.getLong(24) & 1L) == 1) { + r = r.add(Q_MODULUS.value); + } + r = r.shiftRight(1); + } + boolean bigger = v.greaterOrEqualThan(u); + if (bigger) { + v = v.subtract(u); + UInt256 oldS = s; + s = s.subtract(r); + if (s.greaterThan(oldS)) { + s = s.add(Q_MODULUS.value); + } + + } else { + u = u.subtract(v); + UInt256 oldR = r; + r = r.subtract(s); + if (r.greaterThan(oldR)) { + r = r.add(Q_MODULUS.value); + } + } + if (u.getLong(24) == 1L && u.shiftRight(8).equals(UInt256.ZERO)) { + return new Element(r); + } + if (v.getLong(24) == 1L && v.shiftRight(8).equals(UInt256.ZERO)) { + return new Element(s); + } + } + } + + public Element neg() { + if (isZero()) { + return this; + } + return new Element(Q_MODULUS.value.subtract(this.value)); + } + + public byte[] limb(final int i) { + return value.slice(32 - (i + 1) * 8, 8).toArrayUnsafe(); + } + + public boolean isZero() { + return value.isZero(); + } + + public Element divide(final Element b) { + Element bInv = b.inverse(); + return this.multiply(bInv); + } + + private UInt256 madd0(final UInt256 a, final UInt256 b, final UInt256 c) { + UInt256 product = a.multiply(b).add(c); + return product; + } + + private UInt256 madd1(final UInt256 a, final UInt256 b, final UInt256 c) { + UInt256 product = a.multiply(b).add(c); + return product; + } + + private UInt256 madd2(final UInt256 a, final UInt256 b, final UInt256 c, final UInt256 d) { + UInt256 product = a.multiply(b).add(c).add(d); + return product; + } + + private UInt256 madd3( + final UInt256 a, final UInt256 b, final UInt256 c, final UInt256 d, final UInt256 e) { + UInt256 product = a.multiply(b); + product = product.add(c).add(d); + product = product.add(e.shiftLeft(64)); + return product; + } + + private UInt256 limb(final UInt256 value, final int index) { + return UInt256.fromBytes(Bytes32.leftPad(value.slice(32 - (index + 1) * 8, 8))); + } + + private UInt256 setLimb(final UInt256 value, final UInt256 limb, final int index) { + MutableBytes32 mutable = value.toBytes().mutableCopy(); + mutable.set(32 - (index + 1) * 8, limb.slice(24, 8)); + return UInt256.fromBytes(mutable); + } + + public Element multiply(final Element y) { + + UInt256 t = UInt256.ZERO; + UInt256 c; + + // round 0 + { + // v := x[0] + UInt256 v = limb(this.value, 0); + // c[1], c[0] = bits.Mul64(v, y[0]) + UInt256 tempC = v.multiply(limb(y.value, 0)); + c = setLimb(UInt256.ZERO, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // m := c[0] * 18446744069414584319 + UInt256 constant = UInt256.valueOf(new BigInteger("18446744069414584319", 10)); + UInt256 c0 = limb(c, 0); + UInt256 m = limb(constant.multiply(c0), 0); + + // c[2] = madd0(m, 18446744069414584321, c[0]) + UInt256 c2 = + madd0(m, UInt256.valueOf(new BigInteger("18446744069414584321", 10)), limb(c, 0)); + c = setLimb(c, limb(c2, 1), 2); + // c[1], c[0] = madd1(v, y[1], c[1]) + tempC = madd1(v, limb(y.value, 1), limb(c, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[0] = madd2(m, 6034159408538082302, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("6034159408538082302", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 0); + // c[1], c[0] = madd1(v, y[2], c[1]) + tempC = madd1(v, limb(y.value, 2), limb(c, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[1] = madd2(m, 3691218898639771653, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("3691218898639771653", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 1); + // c[1], c[0] = madd1(v, y[3], c[1]) + tempC = madd1(v, limb(y.value, 3), limb(c, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // t[3], t[2] = madd3(m, 8353516859464449352, c[0], c[2], c[1]) + tempC = + madd3( + m, + UInt256.valueOf(new BigInteger("8353516859464449352", 10)), + limb(c, 0), + limb(c, 2), + limb(c, 1)); + t = setLimb(t, limb(tempC, 1), 3); + t = setLimb(t, limb(tempC, 0), 2); + } + // round 1 + { + UInt256 v = limb(this.value, 1); + // c[1], c[0] = madd1(v, y[0], t[0]) + UInt256 tempC = madd1(v, limb(y.value, 0), limb(t, 0)); + c = setLimb(UInt256.ZERO, limb(tempC, 0), 0); + c = setLimb(c, limb(tempC, 1), 1); + // m := c[0] * 18446744069414584319 + UInt256 m = + setLimb( + UInt256.ZERO, + limb( + UInt256.valueOf(new BigInteger("18446744069414584319", 10)).multiply(limb(c, 0)), + 0), + 0); + // c[2] = madd0(m, 18446744069414584321, c[0]) + UInt256 c2 = + madd0(m, UInt256.valueOf(new BigInteger("18446744069414584321", 10)), limb(c, 0)); + c = setLimb(c, limb(c2, 1), 2); + // c[1], c[0] = madd2(v, y[1], c[1], t[1]) + tempC = madd2(v, limb(y.value, 1), limb(c, 1), limb(t, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[0] = madd2(m, 6034159408538082302, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("6034159408538082302", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 0); + // c[1], c[0] = madd2(v, y[2], c[1], t[2]) + tempC = madd2(v, limb(y.value, 2), limb(c, 1), limb(t, 2)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[1] = madd2(m, 3691218898639771653, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("3691218898639771653", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 1); + // c[1], c[0] = madd2(v, y[3], c[1], t[3]) + tempC = madd2(v, limb(y.value, 3), limb(c, 1), limb(t, 3)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + + // t[3], t[2] = madd3(m, 8353516859464449352, c[0], c[2], c[1]) + tempC = + madd3( + m, + UInt256.valueOf(new BigInteger("8353516859464449352", 10)), + limb(c, 0), + limb(c, 2), + limb(c, 1)); + t = setLimb(t, limb(tempC, 1), 3); + t = setLimb(t, limb(tempC, 0), 2); + } + // round 2 + { + // v := x[2] + UInt256 v = limb(this.value, 2); + // c[1], c[0] = madd1(v, y[0], t[0]) + UInt256 tempC = madd1(v, limb(y.value, 0), limb(t, 0)); + c = setLimb(UInt256.ZERO, limb(tempC, 0), 0); + c = setLimb(c, limb(tempC, 1), 1); + // m := c[0] * 18446744069414584319 + UInt256 m = + setLimb( + UInt256.ZERO, + limb(c, 0).multiply(UInt256.valueOf(new BigInteger("18446744069414584319", 10))), + 0); + // c[2] = madd0(m, 18446744069414584321, c[0]) + UInt256 c2 = + madd0(m, UInt256.valueOf(new BigInteger("18446744069414584321", 10)), limb(c, 0)); + c = setLimb(c, limb(c2, 1), 2); + // c[1], c[0] = madd2(v, y[1], c[1], t[1]) + tempC = madd2(v, limb(y.value, 1), limb(c, 1), limb(t, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[0] = madd2(m, 6034159408538082302, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("6034159408538082302", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 0); + // c[1], c[0] = madd2(v, y[2], c[1], t[2]) + tempC = madd2(v, limb(y.value, 2), limb(c, 1), limb(t, 2)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[1] = madd2(m, 3691218898639771653, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("3691218898639771653", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 1); + // c[1], c[0] = madd2(v, y[3], c[1], t[3]) + tempC = madd2(v, limb(y.value, 3), limb(c, 1), limb(t, 3)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // t[3], t[2] = madd3(m, 8353516859464449352, c[0], c[2], c[1]) + tempC = + madd3( + m, + UInt256.valueOf(new BigInteger("8353516859464449352", 10)), + limb(c, 0), + limb(c, 2), + limb(c, 1)); + t = setLimb(t, limb(tempC, 1), 3); + t = setLimb(t, limb(tempC, 0), 2); + } + // round 3 + { + // v := x[3] + UInt256 v = limb(this.value, 3); + // c[1], c[0] = madd1(v, y[0], t[0]) + UInt256 tempC = madd1(v, limb(y.value, 0), limb(t, 0)); + c = setLimb(UInt256.ZERO, limb(tempC, 0), 0); + c = setLimb(c, limb(tempC, 1), 1); + // m := c[0] * 18446744069414584319 + UInt256 m = + setLimb( + UInt256.ZERO, + limb(c, 0).multiply(UInt256.valueOf(new BigInteger("18446744069414584319", 10))), + 0); + // c[2] = madd0(m, 18446744069414584321, c[0]) + UInt256 c2 = + madd0(m, UInt256.valueOf(new BigInteger("18446744069414584321", 10)), limb(c, 0)); + c = setLimb(c, limb(c2, 1), 2); + // c[1], c[0] = madd2(v, y[1], c[1], t[1]) + tempC = madd2(v, limb(y.value, 1), limb(c, 1), limb(t, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], z[0] = madd2(m, 6034159408538082302, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("6034159408538082302", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 0); + // c[1], c[0] = madd2(v, y[2], c[1], t[2]) + tempC = madd2(v, limb(y.value, 2), limb(c, 1), limb(t, 2)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], z[1] = madd2(m, 3691218898639771653, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("3691218898639771653", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 1); + // c[1], c[0] = madd2(v, y[3], c[1], t[3]) + tempC = madd2(v, limb(y.value, 3), limb(c, 1), limb(t, 3)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // z[3], z[2] = madd3(m, 8353516859464449352, c[0], c[2], c[1]) + tempC = + madd3( + m, + UInt256.valueOf(new BigInteger("8353516859464449352", 10)), + limb(c, 0), + limb(c, 2), + limb(c, 1)); + t = setLimb(t, limb(tempC, 1), 3); + t = setLimb(t, limb(tempC, 0), 2); + } + + if (t.greaterThan(Q_MODULUS.value)) { + t = t.subtract(Q_MODULUS.value); + } + + return new Element(t); + } + + public boolean lexicographicallyLargest() { + return value.greaterThan((Q_MODULUS.value.subtract(1)).divide(2)); + } + + public Bytes32 getValue(final ByteOrder byteOrder) { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + return this.value; + } else { + return (Bytes32) this.value.reverse(); + } + } + + public Bytes32 getBytes(final ByteOrder byteOrder) { + Element toRegular = fromMontgomery(); + if (byteOrder == ByteOrder.BIG_ENDIAN) { + return toRegular.value; + } else { + return (Bytes32) toRegular.value.reverse(); + } + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Element element = (Element) o; + return Objects.equals(value, element.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "Element{" + "value=" + value + '}'; + } + + private UInt256 add(final UInt256 z) { + UInt256 mutableZ = z; + // m = z[0]n'[0] mod W + // m := z[0] * 18446744069414584319 + UInt256 z0 = limb(mutableZ, 0); + UInt256 m = + setLimb( + UInt256.ZERO, + UInt256.valueOf(new BigInteger("18446744069414584319", 10)).multiply(z0), + 0); + // C := madd0(m, 18446744069414584321, z[0]) + UInt256 tempC = madd0(m, limb(Q_MODULUS.value, 0), limb(mutableZ, 0)); + UInt256 c = setLimb(UInt256.ZERO, limb(tempC, 1), 0); + // C, z[0] = madd2(m, 6034159408538082302, z[1], C) + tempC = madd2(m, limb(Q_MODULUS.value, 1), limb(mutableZ, 1), c); + c = setLimb(c, limb(tempC, 1), 0); + mutableZ = setLimb(mutableZ, limb(tempC, 0), 0); + // C, z[1] = madd2(m, 3691218898639771653, z[2], C) + tempC = madd2(m, limb(Q_MODULUS.value, 2), limb(mutableZ, 2), c); + c = setLimb(c, limb(tempC, 1), 0); + mutableZ = setLimb(mutableZ, limb(tempC, 0), 1); + // C, z[2] = madd2(m, 8353516859464449352, z[3], C) + tempC = madd2(m, limb(Q_MODULUS.value, 3), limb(mutableZ, 3), c); + c = setLimb(c, limb(tempC, 1), 0); + mutableZ = setLimb(mutableZ, limb(tempC, 0), 2); + // z[3] = C + mutableZ = setLimb(mutableZ, limb(c, 0), 3); + return mutableZ; + } + + /** + * fromMontgomery converts the element from Montgomery to regular representation sets and returns + * z = z * 1 + * + * @return z * 1 + */ + public Element fromMontgomery() { + UInt256 calc = add(this.value); + calc = add(calc); + calc = add(calc); + calc = add(calc); + + if (calc.greaterThan(Q_MODULUS.value)) { + return new Element(calc.subtract(Q_MODULUS.value)); + } + return new Element(calc); + } + + public Element toMontgomery() { + return multiply(Element.R_SQUARE); + } +} diff --git a/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fr/Element.java b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fr/Element.java new file mode 100644 index 0000000000..8fd84f7041 --- /dev/null +++ b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fr/Element.java @@ -0,0 +1,508 @@ +/* + * Copyright Besu Contributors. + * + * 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.ethereum.verkletrie.bandersnatch.fr; + +import java.math.BigInteger; +import java.nio.ByteOrder; +import java.util.Objects; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.MutableBytes32; +import org.apache.tuweni.units.bigints.UInt256; + +public class Element { + public static final Element ZERO = new Element(UInt256.ZERO); + public static final Element ONE; + static final Element Q_MODULUS; + private static final Element R_SQUARE; + + static { + { + UInt256 z0 = UInt256.valueOf(new BigInteger("6347764673676886264", 10)); + UInt256 z1 = UInt256.valueOf(new BigInteger("253265890806062196", 10)).shiftLeft(64); + UInt256 z2 = UInt256.valueOf(new BigInteger("11064306276430008312", 10)).shiftLeft(128); + UInt256 z3 = UInt256.valueOf(new BigInteger("1739710354780652911", 10)).shiftLeft(192); + ONE = new Element(z0.add(z1).add(z2).add(z3)); + } + { + UInt256 z0 = UInt256.valueOf(new BigInteger("8429901452645165025", 10)); + UInt256 z1 = UInt256.valueOf(new BigInteger("18415085837358793841", 10)).shiftLeft(64); + UInt256 z2 = UInt256.valueOf(new BigInteger("922804724659942912", 10)).shiftLeft(128); + UInt256 z3 = UInt256.valueOf(new BigInteger("2088379214866112338", 10)).shiftLeft(192); + Q_MODULUS = new Element(z0.add(z1).add(z2).add(z3)); + } + { + UInt256 z0 = UInt256.valueOf(new BigInteger("15831548891076708299", 10)); + UInt256 z1 = UInt256.valueOf(new BigInteger("4682191799977818424", 10)).shiftLeft(64); + UInt256 z2 = UInt256.valueOf(new BigInteger("12294384630081346794", 10)).shiftLeft(128); + UInt256 z3 = UInt256.valueOf(new BigInteger("785759240370973821", 10)).shiftLeft(192); + R_SQUARE = new Element(z0.add(z1).add(z2).add(z3)); + } + } + + public static Element random() { + UInt256 value = UInt256.fromBytes(Bytes32.random()); + UInt256 divisor = UInt256.fromBytes(Bytes32.rightPad(Q_MODULUS.value.slice(8))); + value = value.mod(divisor); + + if (value.greaterThan(Q_MODULUS.value)) { + value = value.subtract(Q_MODULUS.value); + } + return new Element(value); + } + + final UInt256 value; + + public Element(final UInt256 value) { + this.value = value; + } + + public static Element fromBytes(final Bytes data, final ByteOrder byteOrder) { + return new Element( + UInt256.fromBytes(byteOrder == ByteOrder.BIG_ENDIAN ? data : data.reverse())); + } + + public boolean biggerModulus() { + return value.greaterOrEqualThan(Q_MODULUS.value); + } + + public Element inverse() { + if (isZero()) { + return new Element(UInt256.ZERO); + } + UInt256 u = Q_MODULUS.value; + UInt256 s = R_SQUARE.value; + UInt256 v = value; + UInt256 r = UInt256.ZERO; + while (true) { + while ((v.getLong(24) & 1L) == 0) { + v = v.shiftRight(1); + if ((s.getLong(24) & 1L) == 1) { + s = s.add(Q_MODULUS.value); + } + s = s.shiftRight(1); + } + while ((u.getLong(24) & 1L) == 0) { + u = u.shiftRight(1); + if ((r.getLong(24) & 1L) == 1) { + r = r.add(Q_MODULUS.value); + } + r = r.shiftRight(1); + } + boolean bigger = v.greaterOrEqualThan(u); + if (bigger) { + v = v.subtract(u); + UInt256 oldS = s; + s = s.subtract(r); + if (s.greaterThan(oldS)) { + s = s.add(Q_MODULUS.value); + } + + } else { + u = u.subtract(v); + UInt256 oldR = r; + r = r.subtract(s); + if (r.greaterThan(oldR)) { + r = r.add(Q_MODULUS.value); + } + } + if (u.getLong(24) == 1L && u.shiftRight(8).equals(UInt256.ZERO)) { + return new Element(r); + } + if (v.getLong(24) == 1L && v.shiftRight(8).equals(UInt256.ZERO)) { + return new Element(s); + } + } + } + + public Element neg() { + if (isZero()) { + return this; + } + return new Element(Q_MODULUS.value.subtract(this.value)); + } + + public byte[] limb(final int i) { + return value.slice(32 - (i + 1) * 8, 8).toArrayUnsafe(); + } + + public boolean isZero() { + return value.isZero(); + } + + public Element divide(final Element b) { + Element bInv = b.inverse(); + return this.multiply(bInv); + } + + private UInt256 madd0(final UInt256 a, final UInt256 b, final UInt256 c) { + UInt256 product = a.multiply(b).add(c); + return product; + } + + private UInt256 madd1(final UInt256 a, final UInt256 b, final UInt256 c) { + UInt256 product = a.multiply(b).add(c); + return product; + } + + private UInt256 madd2(final UInt256 a, final UInt256 b, final UInt256 c, final UInt256 d) { + UInt256 product = a.multiply(b).add(c).add(d); + return product; + } + + private UInt256 madd3( + final UInt256 a, final UInt256 b, final UInt256 c, final UInt256 d, final UInt256 e) { + UInt256 product = a.multiply(b); + product = product.add(c).add(d); + product = product.add(e.shiftLeft(64)); + return product; + } + + private UInt256 limb(final UInt256 value, final int index) { + return UInt256.fromBytes(Bytes32.leftPad(value.slice(32 - (index + 1) * 8, 8))); + } + + private UInt256 setLimb(final UInt256 value, final UInt256 limb, final int index) { + MutableBytes32 mutable = value.toBytes().mutableCopy(); + mutable.set(32 - (index + 1) * 8, limb.slice(24, 8)); + return UInt256.fromBytes(mutable); + } + + public Element multiply(final Element y) { + + UInt256 t = UInt256.ZERO; + UInt256 c; + + // round 0 + { + // v := x[0] + UInt256 v = limb(this.value, 0); + // c[1], c[0] = bits.Mul64(v, y[0]) + UInt256 tempC = v.multiply(limb(y.value, 0)); + c = setLimb(UInt256.ZERO, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // m := c[0] * 17410672245482742751 + UInt256 constant = UInt256.valueOf(new BigInteger("17410672245482742751", 10)); + UInt256 c0 = limb(c, 0); + UInt256 m = limb(constant.multiply(c0), 0); + + // c[2] = madd0(m, 8429901452645165025, c[0]) + UInt256 c2 = madd0(m, UInt256.valueOf(new BigInteger("8429901452645165025", 10)), limb(c, 0)); + c = setLimb(c, limb(c2, 1), 2); + // c[1], c[0] = madd1(v, y[1], c[1]) + tempC = madd1(v, limb(y.value, 1), limb(c, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[0] = madd2(m, 18415085837358793841, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("18415085837358793841", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 0); + // c[1], c[0] = madd1(v, y[2], c[1]) + tempC = madd1(v, limb(y.value, 2), limb(c, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[1] = madd2(m, 922804724659942912, c[2], c[0]) + tempC = + madd2( + m, UInt256.valueOf(new BigInteger("922804724659942912", 10)), limb(c, 2), limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 1); + // c[1], c[0] = madd1(v, y[3], c[1]) + tempC = madd1(v, limb(y.value, 3), limb(c, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // t[3], t[2] = madd3(m, 2088379214866112338, c[0], c[2], c[1]) + tempC = + madd3( + m, + UInt256.valueOf(new BigInteger("2088379214866112338", 10)), + limb(c, 0), + limb(c, 2), + limb(c, 1)); + t = setLimb(t, limb(tempC, 1), 3); + t = setLimb(t, limb(tempC, 0), 2); + } + // round 1 + { + UInt256 v = limb(this.value, 1); + // c[1], c[0] = madd1(v, y[0], t[0]) + UInt256 tempC = madd1(v, limb(y.value, 0), limb(t, 0)); + c = setLimb(UInt256.ZERO, limb(tempC, 0), 0); + c = setLimb(c, limb(tempC, 1), 1); + // m := c[0] * 17410672245482742751 + UInt256 m = + setLimb( + UInt256.ZERO, + limb( + UInt256.valueOf(new BigInteger("17410672245482742751", 10)).multiply(limb(c, 0)), + 0), + 0); + // c[2] = madd0(m, 8429901452645165025, c[0]) + UInt256 c2 = madd0(m, UInt256.valueOf(new BigInteger("8429901452645165025", 10)), limb(c, 0)); + c = setLimb(c, limb(c2, 1), 2); + // c[1], c[0] = madd2(v, y[1], c[1], t[1]) + tempC = madd2(v, limb(y.value, 1), limb(c, 1), limb(t, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[0] = madd2(m, 18415085837358793841, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("18415085837358793841", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 0); + // c[1], c[0] = madd2(v, y[2], c[1], t[2]) + tempC = madd2(v, limb(y.value, 2), limb(c, 1), limb(t, 2)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[1] = madd2(m, 922804724659942912, c[2], c[0]) + tempC = + madd2( + m, UInt256.valueOf(new BigInteger("922804724659942912", 10)), limb(c, 2), limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 1); + // c[1], c[0] = madd2(v, y[3], c[1], t[3]) + tempC = madd2(v, limb(y.value, 3), limb(c, 1), limb(t, 3)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + + // t[3], t[2] = madd3(m, 2088379214866112338, c[0], c[2], c[1]) + tempC = + madd3( + m, + UInt256.valueOf(new BigInteger("2088379214866112338", 10)), + limb(c, 0), + limb(c, 2), + limb(c, 1)); + t = setLimb(t, limb(tempC, 1), 3); + t = setLimb(t, limb(tempC, 0), 2); + } + // round 2 + { + // v := x[2] + UInt256 v = limb(this.value, 2); + // c[1], c[0] = madd1(v, y[0], t[0]) + UInt256 tempC = madd1(v, limb(y.value, 0), limb(t, 0)); + c = setLimb(UInt256.ZERO, limb(tempC, 0), 0); + c = setLimb(c, limb(tempC, 1), 1); + // m := c[0] * 17410672245482742751 + UInt256 m = + setLimb( + UInt256.ZERO, + limb(c, 0).multiply(UInt256.valueOf(new BigInteger("17410672245482742751", 10))), + 0); + // c[2] = madd0(m, 8429901452645165025, c[0]) + UInt256 c2 = madd0(m, UInt256.valueOf(new BigInteger("8429901452645165025", 10)), limb(c, 0)); + c = setLimb(c, limb(c2, 1), 2); + // c[1], c[0] = madd2(v, y[1], c[1], t[1]) + tempC = madd2(v, limb(y.value, 1), limb(c, 1), limb(t, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[0] = madd2(m, 18415085837358793841, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("18415085837358793841", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 0); + // c[1], c[0] = madd2(v, y[2], c[1], t[2]) + tempC = madd2(v, limb(y.value, 2), limb(c, 1), limb(t, 2)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], t[1] = madd2(m, 922804724659942912, c[2], c[0]) + tempC = + madd2( + m, UInt256.valueOf(new BigInteger("922804724659942912", 10)), limb(c, 2), limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 1); + // c[1], c[0] = madd2(v, y[3], c[1], t[3]) + tempC = madd2(v, limb(y.value, 3), limb(c, 1), limb(t, 3)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // t[3], t[2] = madd3(m, 2088379214866112338, c[0], c[2], c[1]) + tempC = + madd3( + m, + UInt256.valueOf(new BigInteger("2088379214866112338", 10)), + limb(c, 0), + limb(c, 2), + limb(c, 1)); + t = setLimb(t, limb(tempC, 1), 3); + t = setLimb(t, limb(tempC, 0), 2); + } + // round 3 + { + // v := x[3] + UInt256 v = limb(this.value, 3); + // c[1], c[0] = madd1(v, y[0], t[0]) + UInt256 tempC = madd1(v, limb(y.value, 0), limb(t, 0)); + c = setLimb(UInt256.ZERO, limb(tempC, 0), 0); + c = setLimb(c, limb(tempC, 1), 1); + // m := c[0] * 17410672245482742751 + UInt256 m = + setLimb( + UInt256.ZERO, + limb(c, 0).multiply(UInt256.valueOf(new BigInteger("17410672245482742751", 10))), + 0); + // c[2] = madd0(m, 8429901452645165025, c[0]) + UInt256 c2 = madd0(m, UInt256.valueOf(new BigInteger("8429901452645165025", 10)), limb(c, 0)); + c = setLimb(c, limb(c2, 1), 2); + // c[1], c[0] = madd2(v, y[1], c[1], t[1]) + tempC = madd2(v, limb(y.value, 1), limb(c, 1), limb(t, 1)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], z[0] = madd2(m, 18415085837358793841, c[2], c[0]) + tempC = + madd2( + m, + UInt256.valueOf(new BigInteger("18415085837358793841", 10)), + limb(c, 2), + limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 0); + // c[1], c[0] = madd2(v, y[2], c[1], t[2]) + tempC = madd2(v, limb(y.value, 2), limb(c, 1), limb(t, 2)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // c[2], z[1] = madd2(m, 922804724659942912, c[2], c[0]) + tempC = + madd2( + m, UInt256.valueOf(new BigInteger("922804724659942912", 10)), limb(c, 2), limb(c, 0)); + c = setLimb(c, limb(tempC, 1), 2); + t = setLimb(t, limb(tempC, 0), 1); + // c[1], c[0] = madd2(v, y[3], c[1], t[3]) + tempC = madd2(v, limb(y.value, 3), limb(c, 1), limb(t, 3)); + c = setLimb(c, limb(tempC, 1), 1); + c = setLimb(c, limb(tempC, 0), 0); + // z[3], z[2] = madd3(m, 2088379214866112338, c[0], c[2], c[1]) + tempC = + madd3( + m, + UInt256.valueOf(new BigInteger("2088379214866112338", 10)), + limb(c, 0), + limb(c, 2), + limb(c, 1)); + t = setLimb(t, limb(tempC, 1), 3); + t = setLimb(t, limb(tempC, 0), 2); + } + + if (t.greaterThan(Q_MODULUS.value)) { + t = t.subtract(Q_MODULUS.value); + } + + return new Element(t); + } + + public boolean lexicographicallyLargest() { + return value.greaterThan((Q_MODULUS.value.subtract(1)).divide(2)); + } + + public Bytes32 getValue(final ByteOrder byteOrder) { + if (byteOrder == ByteOrder.BIG_ENDIAN) { + return this.value; + } else { + return (Bytes32) this.value.reverse(); + } + } + + public Bytes32 getBytes(final ByteOrder byteOrder) { + Element toRegular = fromMontgomery(); + if (byteOrder == ByteOrder.BIG_ENDIAN) { + return toRegular.value; + } else { + return (Bytes32) toRegular.value.reverse(); + } + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Element element = (Element) o; + return Objects.equals(value, element.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "Element{" + "value=" + value + '}'; + } + + private UInt256 add(final UInt256 z) { + UInt256 mutableZ = z; + // m = z[0]n'[0] mod W + // m := z[0] * 17410672245482742751 + UInt256 z0 = limb(mutableZ, 0); + UInt256 m = + setLimb( + UInt256.ZERO, + UInt256.valueOf(new BigInteger("17410672245482742751", 10)).multiply(z0), + 0); + // C := madd0(m, 8429901452645165025, z[0]) + UInt256 tempC = madd0(m, limb(Q_MODULUS.value, 0), limb(mutableZ, 0)); + UInt256 c = setLimb(UInt256.ZERO, limb(tempC, 1), 0); + // C, z[0] = madd2(m, 18415085837358793841, z[1], C) + tempC = madd2(m, limb(Q_MODULUS.value, 1), limb(mutableZ, 1), c); + c = setLimb(c, limb(tempC, 1), 0); + mutableZ = setLimb(mutableZ, limb(tempC, 0), 0); + // C, z[1] = madd2(m, 922804724659942912, z[2], C) + tempC = madd2(m, limb(Q_MODULUS.value, 2), limb(mutableZ, 2), c); + c = setLimb(c, limb(tempC, 1), 0); + mutableZ = setLimb(mutableZ, limb(tempC, 0), 1); + // C, z[2] = madd2(m, 2088379214866112338, z[3], C) + tempC = madd2(m, limb(Q_MODULUS.value, 3), limb(mutableZ, 3), c); + c = setLimb(c, limb(tempC, 1), 0); + mutableZ = setLimb(mutableZ, limb(tempC, 0), 2); + // z[3] = C + mutableZ = setLimb(mutableZ, limb(c, 0), 3); + return mutableZ; + } + + /** + * fromMontgomery converts the element from Montgomery to regular representation sets and returns + * z = z * 1 + * + * @return z * 1 + */ + public Element fromMontgomery() { + UInt256 calc = add(this.value); + calc = add(calc); + calc = add(calc); + calc = add(calc); + + if (calc.greaterThan(Q_MODULUS.value)) { + return new Element(calc.subtract(Q_MODULUS.value)); + } + return new Element(calc); + } + + public Element toMontgomery() { + return multiply(R_SQUARE); + } +} diff --git a/ethereum/verkletrie/src/test/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fp/ElementTest.java b/ethereum/verkletrie/src/test/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fp/ElementTest.java new file mode 100644 index 0000000000..fe3fcb0082 --- /dev/null +++ b/ethereum/verkletrie/src/test/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fp/ElementTest.java @@ -0,0 +1,220 @@ +/* + * Copyright Besu Contributors. + * + * 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.ethereum.verkletrie.bandersnatch.fp; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigInteger; + +import org.apache.tuweni.units.bigints.UInt256; +import org.junit.Test; + +public class ElementTest { + + @Test + public void testIsZero() { + assertThat(Element.ZERO.isZero()).isTrue(); + assertThat(Element.ONE.isZero()).isFalse(); + assertThat(new Element(UInt256.valueOf(42L)).isZero()).isFalse(); + } + + @Test + public void testInverseZero() { + Element elt = Element.ZERO; + assertThat(elt.inverse()).isEqualTo(elt); + } + + @Test + public void testInverse42() { + assertThat(new Element(UInt256.valueOf(42L)).inverse()) + .isEqualTo( + new Element( + UInt256.fromHexString( + "3fa8731a5789261bc0c32da675b6100c6bd8289edea72861d409c282e75503ba"))); + } + + @Test + public void testInverseOverQ() { + assertThat(new Element(Element.Q_MODULUS.value.add(1)).inverse()) + .isEqualTo( + new Element( + UInt256.fromHexString( + "0748d9d99f59ff1105d314967254398f2b6cedcb87925c23c999e990f3f29c6d"))); + } + + @Test + public void testRandom() { + assertThat(Element.random().value.lessOrEqualThan(Element.Q_MODULUS.value)).isTrue(); + } + + @Test + public void testDivide() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d433339d80809a1d80253bda402fffe5bfcfffffffefffffff3")); + Element y = + new Element( + UInt256.fromHexString( + "73eda753299d7d753339d80809a1d7d553bda402fffe5c1cfffffffeffffffe9")); + assertThat(x.divide(y)) + .isEqualTo( + new Element( + UInt256.fromHexString( + "42291ebda05409e31e85f7061384f4066381dbac31023c5f2609827a639d6ddc"))); + } + + @Test + public void testMultiply() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d433339d80809a1d80253bda402fffe5bfcfffffffefffffff3")); + Element y = + new Element( + UInt256.fromHexString( + "73eda753299d7d753339d80809a1d7d553bda402fffe5c1cfffffffeffffffe9")); + assertThat(x.multiply(y)) + .isEqualTo( + new Element( + UInt256.fromHexString( + "addbab1d634d4bbc1c11001562f72e2a24caa1b482c2f2038b3a8d46bf1416f"))); + } + + @Test + public void testNeg() { + Element x = + new Element( + UInt256.fromHexString( + "05f98ae63ff2eb86b466cc60a939dd4adaeed3599e3ad7a34694ff6dbf518a76")); + Element result = x.neg(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "6df41c6ce9aa91c17ed30ba76067faba78ced0a961c3845bb96b009140ae758b")); + } + + @Test + public void testNegOverQ() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002")); + Element result = x.neg(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + } + + @Test + public void testNegZero() { + Element x = Element.ZERO; + Element result = x.neg(); + assertThat(result.value).isEqualTo(Element.ZERO.value); + } + + @Test + public void testNegOne() { + Element x = new Element(UInt256.ONE); + Element result = x.neg(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000")); + } + + @Test + public void testFromMont() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002")); + Element result = x.fromMontgomery(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "1bbe869330009d577204078a4f77266aab6fca8f09dc705f13f75b69fe75c040")); + } + + @Test + public void testToMont() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002")); + Element result = x.toMontgomery(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe")); + } + + @Test + public void testToMont2() { + Element before = + new Element( + UInt256.valueOf(new BigInteger("999881962499998002", 10)) + .add(UInt256.valueOf(new BigInteger("0", 10)).shiftLeft(64)) + .add(UInt256.valueOf(new BigInteger("1", 10)).shiftLeft(128)) + .add(UInt256.valueOf(new BigInteger("0", 10)).shiftLeft(192))); + Element expected = + new Element( + UInt256.valueOf(new BigInteger("479530773443910689", 10)) + .add(UInt256.valueOf(new BigInteger("6571075494204656015", 10)).shiftLeft(64)) + .add(UInt256.valueOf(new BigInteger("12714171422618877016", 10)).shiftLeft(128)) + .add(UInt256.valueOf(new BigInteger("1309675183017776880", 10)).shiftLeft(192))); + assertThat(before.toMontgomery()).isEqualTo(expected); + } + + // @Test + // public void testToMont3() { + // Element before = + // new Element( + // UInt256.valueOf(new BigInteger("6121572481584493354", 10)) + // .add(UInt256.valueOf(new BigInteger("12601925224297426022", 10)).shiftLeft(64)) + // .add(UInt256.valueOf(new BigInteger("7716030069200636218", 10)).shiftLeft(128)) + // .add(UInt256.valueOf(new BigInteger("1351674413095362254", + // 10)).shiftLeft(192))); + // Element expected = + // new Element( + // UInt256.valueOf(new BigInteger("479530773443910689", 10)) + // .add(UInt256.valueOf(new BigInteger("6571075494204656015", 10)).shiftLeft(64)) + // .add(UInt256.valueOf(new BigInteger("12714171422618877016", 10)).shiftLeft(128)) + // .add(UInt256.valueOf(new BigInteger("1309675183017776880", + // 10)).shiftLeft(192))); + // assertThat(before.toMontgomery()).isEqualTo(expected); + // } + + @Test + public void testSetBytes() { + Element x = + new Element( + UInt256.fromHexString( + "0000000000000000000000000000000100000000000000000de04b58e8220132")); + Element expected = + new Element( + UInt256.valueOf(new BigInteger("999881962499998002", 10)) + .add(UInt256.valueOf(new BigInteger("0", 10)).shiftLeft(64)) + .add(UInt256.valueOf(new BigInteger("1", 10)).shiftLeft(128)) + .add(UInt256.valueOf(new BigInteger("0", 10)).shiftLeft(192))); + assertThat(x).isEqualTo(expected); + Element result = x.toMontgomery(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "122ce6a3d6eb56f0b071cf8bda9efc585b312658d057c98f06a7a2b2a1fe1c21")); + } +} diff --git a/ethereum/verkletrie/src/test/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fr/ElementTest.java b/ethereum/verkletrie/src/test/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fr/ElementTest.java new file mode 100644 index 0000000000..ef7c192332 --- /dev/null +++ b/ethereum/verkletrie/src/test/java/org/hyperledger/besu/ethereum/verkletrie/bandersnatch/fr/ElementTest.java @@ -0,0 +1,199 @@ +/* + * Copyright Besu Contributors. + * + * 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.ethereum.verkletrie.bandersnatch.fr; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigInteger; + +import org.apache.tuweni.units.bigints.UInt256; +import org.junit.Test; + +public class ElementTest { + + @Test + public void testIsZero() { + assertThat(Element.ZERO.isZero()).isTrue(); + assertThat(Element.ONE.isZero()).isFalse(); + assertThat(new Element(UInt256.valueOf(42L)).isZero()).isFalse(); + } + + @Test + public void testInverseZero() { + Element elt = Element.ZERO; + assertThat(elt.inverse()).isEqualTo(elt); + } + + @Test + public void testInverse42() { + assertThat(new Element(UInt256.valueOf(42L)).inverse()) + .isEqualTo( + new Element( + UInt256.fromHexString( + "2546c9a92adbd3384fa236bec5cec2a385f743ac23217035cd355920b19e79b"))); + } + + @Test + public void testInverseOverQ() { + assertThat(new Element(Element.Q_MODULUS.value.add(1)).inverse()) + .isEqualTo( + new Element( + UInt256.fromHexString( + "0ae793ddb14aec7daa9e6daec0055cea40fa7ca27fecb938dbb4f5d658db47cb"))); + } + + @Test + public void testRandom() { + assertThat(Element.random().value.lessOrEqualThan(Element.Q_MODULUS.value)).isTrue(); + } + + @Test + public void testDivide() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d433339d80809a1d80253bda402fffe5bfcfffffffefffffff3")); + Element y = + new Element( + UInt256.fromHexString( + "73eda753299d7d753339d80809a1d7d553bda402fffe5c1cfffffffeffffffe9")); + assertThat(x.divide(y)) + .isEqualTo( + new Element( + UInt256.fromHexString( + "14d7b4f7f22fb5688cab25906b04691b468a5397997285ab6ef862deae100957"))); + } + + @Test + public void testMultiply() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d433339d80809a1d80253bda402fffe5bfcfffffffefffffff3")); + Element y = + new Element( + UInt256.fromHexString( + "73eda753299d7d753339d80809a1d7d553bda402fffe5c1cfffffffeffffffe9")); + assertThat(x.multiply(y)) + .isEqualTo( + new Element( + UInt256.fromHexString( + "209ee057659143a392a0e135c23c3dee1f96f11ebf6151699193e4a29141e0fd"))); + } + + @Test + public void testNeg() { + Element x = + new Element( + UInt256.fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001")); + Element result = x.neg(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "1cfb69d4ca675f520cce760202687600ff8f87007419047174fd06b52876e7e0")); + } + + @Test + public void testNegOverQ() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002")); + Element result = x.neg(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "a90dc281a0c9e209d9949df9f8c69dfbabd1e2fd741aa87274fd06b62876e7df")); + } + + @Test + public void testNegZero() { + Element x = Element.ZERO; + Element result = x.neg(); + assertThat(result.value).isEqualTo(Element.ZERO.value); + } + + @Test + public void testNegOne() { + Element x = new Element(UInt256.ONE); + Element result = x.neg(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "0x1cfb69d4ca675f520cce760202687600ff8f87007419047174fd06b52876e7e0")); + } + + @Test + public void testFromMont() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002")); + Element result = x.fromMontgomery(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "029dcd39374fa1ed499348e004ce8e397648170983b64e150042fce1ccb70b7d")); + } + + @Test + public void testToMont() { + Element x = + new Element( + UInt256.fromHexString( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002")); + Element result = x.toMontgomery(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "ff2df0939978a11eb3730340bb816b35def26d563fd67125f62b5942d4903e8")); + } + + @Test + public void testToMont2() { + Element before = + new Element( + UInt256.valueOf(new BigInteger("999881962499998002", 10)) + .add(UInt256.valueOf(new BigInteger("0", 10)).shiftLeft(64)) + .add(UInt256.valueOf(new BigInteger("1", 10)).shiftLeft(128)) + .add(UInt256.valueOf(new BigInteger("0", 10)).shiftLeft(192))); + Element expected = + new Element( + UInt256.fromHexString( + "12c21cb79c889ece6b14d87776efc93aaee3083940486c6654f431bd0fa6732a")); + assertThat(before.toMontgomery()).isEqualTo(expected); + } + + @Test + public void testSetBytes() { + Element x = + new Element( + UInt256.fromHexString( + "0000000000000000000000000000000100000000000000000de04b58e8220132")); + Element expected = + new Element( + UInt256.valueOf(new BigInteger("999881962499998002", 10)) + .add(UInt256.valueOf(new BigInteger("0", 10)).shiftLeft(64)) + .add(UInt256.valueOf(new BigInteger("1", 10)).shiftLeft(128)) + .add(UInt256.valueOf(new BigInteger("0", 10)).shiftLeft(192))); + assertThat(x).isEqualTo(expected); + Element result = x.toMontgomery(); + assertThat(result.value) + .isEqualTo( + UInt256.fromHexString( + "12c21cb79c889ece6b14d87776efc93aaee3083940486c6654f431bd0fa6732a")); + } +} diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 96ce1603b6..2387771066 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -317,6 +317,22 @@ + + + + + + + + + + + + + + + + @@ -4019,6 +4035,17 @@ + + + + + + + + + + + @@ -4030,6 +4057,17 @@ + + + + + + + + + + + @@ -4041,6 +4079,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -4052,6 +4112,17 @@ + + + + + + + + + + + diff --git a/gradle/versions.gradle b/gradle/versions.gradle index c0b4409d71..9e7620281a 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -155,6 +155,7 @@ dependencyManagement { dependencySet(group: 'org.hyperledger.besu', version: '0.7.1') { entry 'arithmetic' + entry 'ipa-multipoint' entry 'bls12-381' entry 'secp256k1' entry 'secp256r1' diff --git a/settings.gradle b/settings.gradle index 68ad95f90d..17f4e91932 100644 --- a/settings.gradle +++ b/settings.gradle @@ -65,6 +65,7 @@ include 'ethereum:rlp' include 'ethereum:stratum' include 'ethereum:ethstats' include 'ethereum:trie' +include 'ethereum:verkletrie' include 'evm' include 'metrics:core' include 'metrics:rocksdb'