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'