diff --git a/framework/src/test/java/org/tron/common/crypto/zksnark/BN128Test.java b/framework/src/test/java/org/tron/common/crypto/zksnark/BN128Test.java
new file mode 100644
index 00000000000..6fb074e8809
--- /dev/null
+++ b/framework/src/test/java/org/tron/common/crypto/zksnark/BN128Test.java
@@ -0,0 +1,154 @@
+package org.tron.common.crypto.zksnark;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.math.BigInteger;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.Test;
+
+/**
+ * Regression baseline for the alt_bn128 (BN254) curve primitives used by the
+ * ecAdd / ecMul / ecPairing precompiles (addresses 0x06 / 0x07 / 0x08).
+ *
+ *
Prior to this class the BN128 stack ({@link BN128Fp}, {@link BN128G1},
+ * {@link BN128G2}, {@link PairingCheck}) had no direct unit coverage — only the
+ * gas-accounting paths were exercised through Solidity contracts in IstanbulTest.
+ * These tests lock in point validation, generator membership, the G1 vs G2
+ * subgroup-check asymmetry, and pairing correctness against known generators.
+ *
+ *
The G1 subgroup behaviour is the core of HackerOne report #3769516: because
+ * the alt_bn128 G1 cofactor is 1, the on-curve check performed by
+ * {@code BN128Fp.create} is itself the subgroup check, so every on-curve G1 point
+ * is a valid group member and no separate isGroupMember() call is required.
+ */
+public class BN128Test {
+
+ // 32-byte big-endian coordinates, parsed by Fp.create via new BigInteger(1, v).
+
+ // G1 generator (1, 2)
+ private static final byte[] G1_X =
+ Hex.decode("0000000000000000000000000000000000000000000000000000000000000001");
+ private static final byte[] G1_Y =
+ Hex.decode("0000000000000000000000000000000000000000000000000000000000000002");
+ // -G1 = (1, p - 2): affine negation, p is the F_p modulus.
+ private static final byte[] G1_NEG_Y =
+ Hex.decode("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45");
+
+ // Subgroup order r of the alt_bn128 G1/G2 groups.
+ private static final BigInteger R = new BigInteger(
+ "21888242871839275222246405745257275088548364400416034343698204186575808495617");
+
+ // G2 generator: x = a + b*i, y = c + d*i (EIP-197 standard generator).
+ // BN128G2.create(a, b, c, d) expects this coordinate order.
+ private static final byte[] G2_A =
+ Hex.decode("1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed");
+ private static final byte[] G2_B =
+ Hex.decode("198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2");
+ private static final byte[] G2_C =
+ Hex.decode("12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa");
+ private static final byte[] G2_D =
+ Hex.decode("090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b");
+
+ private static byte[] word(long v) {
+ byte[] w = new byte[32];
+ for (int i = 0; i < 8; i++) {
+ w[31 - i] = (byte) (v >>> (8 * i));
+ }
+ return w;
+ }
+
+ // ---- BN128Fp / point-on-curve validation ----------------------------------
+
+ @Test
+ public void testG1GeneratorIsOnCurve() {
+ assertNotNull("G1 generator (1,2) must be accepted", BN128G1.create(G1_X, G1_Y));
+ }
+
+ @Test
+ public void testPointAtInfinityAccepted() {
+ // (0, 0) encodes the point at infinity and must be accepted.
+ assertNotNull(BN128G1.create(word(0), word(0)));
+ }
+
+ @Test
+ public void testOffCurvePointRejected() {
+ // (1, 1): y^2 = 1, x^3 + 3 = 4, not on the curve -> rejected.
+ assertNull(BN128G1.create(word(1), word(1)));
+ }
+
+ @Test
+ public void testCoordinateAboveFieldModulusRejected() {
+ // x = p (the field modulus) is not a valid F_p element together with y = 2.
+ byte[] pBytes =
+ Hex.decode("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47");
+ assertNull(BN128G1.create(pBytes, G1_Y));
+ }
+
+ // ---- G1 subgroup behaviour (HackerOne #3769516) ---------------------------
+
+ @Test
+ public void testG1GeneratorHasOrderR() {
+ // alt_bn128 G1 cofactor == 1, so any on-curve point is in the order-r
+ // subgroup. Confirm the generator genuinely has order r: r * G == O.
+ BN128G1 g = BN128G1.create(G1_X, G1_Y);
+ assertNotNull(g);
+ assertEquals("r * G must be the point at infinity", true, g.mul(R).isZero());
+ }
+
+ // ---- BN128G2 / subgroup check ---------------------------------------------
+
+ @Test
+ public void testG2GeneratorIsGroupMember() {
+ assertNotNull("G2 generator must pass the subgroup check",
+ BN128G2.create(G2_A, G2_B, G2_C, G2_D));
+ }
+
+ @Test
+ public void testG2OffCurveRejected() {
+ // All-ones coordinates are not on the twist curve -> rejected.
+ byte[] one = word(1);
+ assertNull(BN128G2.create(one, one, one, one));
+ }
+
+ // ---- PairingCheck correctness ---------------------------------------------
+
+ @Test
+ public void testPairingEmptyIsOne() {
+ // The empty product is the identity; ecPairing of no pairs returns 1.
+ PairingCheck check = PairingCheck.create();
+ check.run();
+ assertEquals(1, check.result());
+ }
+
+ @Test
+ public void testPairingNegationCancels() {
+ // e(G1, G2) * e(-G1, G2) == 1, the canonical pairing sanity check
+ // (mirrors pairing([P1, -P1], [P2, P2]) == true).
+ BN128G1 g1 = BN128G1.create(G1_X, G1_Y);
+ BN128G1 negG1 = BN128G1.create(G1_X, G1_NEG_Y);
+ BN128G2 g2 = BN128G2.create(G2_A, G2_B, G2_C, G2_D);
+ assertNotNull(g1);
+ assertNotNull(negG1);
+ assertNotNull(g2);
+
+ PairingCheck check = PairingCheck.create();
+ check.addPair(g1, g2);
+ check.addPair(negG1, g2);
+ check.run();
+ assertEquals("e(G1,G2)*e(-G1,G2) must equal 1", 1, check.result());
+ }
+
+ @Test
+ public void testPairingSinglePairIsNotOne() {
+ // e(G1, G2) alone is a non-trivial element, so the check returns 0.
+ BN128G1 g1 = BN128G1.create(G1_X, G1_Y);
+ BN128G2 g2 = BN128G2.create(G2_A, G2_B, G2_C, G2_D);
+
+ PairingCheck check = PairingCheck.create();
+ check.addPair(g1, g2);
+ check.run();
+ assertEquals(0, check.result());
+ }
+}
diff --git a/framework/src/test/java/org/tron/common/runtime/vm/Blake2FTest.java b/framework/src/test/java/org/tron/common/runtime/vm/Blake2FTest.java
new file mode 100644
index 00000000000..134edcbb82e
--- /dev/null
+++ b/framework/src/test/java/org/tron/common/runtime/vm/Blake2FTest.java
@@ -0,0 +1,100 @@
+package org.tron.common.runtime.vm;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.Test;
+import org.tron.core.vm.PrecompiledContracts.Blake2F;
+
+/**
+ * Direct execution regression baseline for the BLAKE2 F-compression precompile
+ * (EIP-152, address 0x09 on the compatible-EVM path).
+ *
+ *
The precompile was previously only exercised indirectly through a Solidity
+ * contract. These tests pin the canonical EIP-152 "vector 4" input/output pair
+ * and the input-validation rules: the input must be exactly 213 bytes and the
+ * final flag byte must be 0x00 or 0x01.
+ *
+ *
Input layout (213 bytes): rounds(4) | h(64) | m(128) | t(16) | f(1).
+ */
+public class Blake2FTest {
+
+ private static final Blake2F BLAKE2F = new Blake2F();
+
+ // EIP-152 test vector 4: rounds = 12, f = 1.
+ private static final byte[] VECTOR_4_INPUT = Hex.decode(
+ "0000000c"
+ + "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5"
+ + "d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b"
+ + "6162630000000000000000000000000000000000000000000000000000000000"
+ + "0000000000000000000000000000000000000000000000000000000000000000"
+ + "0000000000000000000000000000000000000000000000000000000000000000"
+ + "0000000000000000000000000000000000000000000000000000000000000000"
+ + "03000000000000000000000000000000"
+ + "01");
+
+ private static final byte[] VECTOR_4_OUTPUT = Hex.decode(
+ "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"
+ + "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923");
+
+ @Test
+ public void matchesEip152Vector4() {
+ Pair result = BLAKE2F.execute(VECTOR_4_INPUT);
+
+ assertTrue(result.getLeft());
+ assertEquals("output must be 64 bytes", 64, result.getRight().length);
+ assertArrayEquals("must match EIP-152 vector 4", VECTOR_4_OUTPUT, result.getRight());
+ }
+
+ @Test
+ public void energyEqualsRounds() {
+ // Gas cost equals the round count (0x0000000c = 12) for well-formed input.
+ assertEquals(12L, BLAKE2F.getEnergyForData(VECTOR_4_INPUT));
+ }
+
+ @Test
+ public void zeroRoundsIsAllowed() {
+ byte[] input = VECTOR_4_INPUT.clone();
+ input[0] = 0;
+ input[1] = 0;
+ input[2] = 0;
+ input[3] = 0;
+
+ Pair result = BLAKE2F.execute(input);
+ assertTrue(result.getLeft());
+ assertEquals(64, result.getRight().length);
+ assertEquals(0L, BLAKE2F.getEnergyForData(input));
+ }
+
+ @Test
+ public void rejectsWrongLength() {
+ // 212 bytes — one short of the required 213.
+ byte[] input = new byte[212];
+ Pair result = BLAKE2F.execute(input);
+ assertFalse("incorrect length must fail", result.getLeft());
+ }
+
+ @Test
+ public void rejectsInvalidFinalFlag() {
+ byte[] input = VECTOR_4_INPUT.clone();
+ input[212] = 0x02; // flag must be 0x00 or 0x01
+
+ Pair result = BLAKE2F.execute(input);
+ assertFalse("invalid finalization flag must fail", result.getLeft());
+ assertEquals("invalid flag must price to zero", 0L, BLAKE2F.getEnergyForData(input));
+ }
+
+ @Test
+ public void nonFinalBlockFlagZeroAccepted() {
+ byte[] input = VECTOR_4_INPUT.clone();
+ input[212] = 0x00; // f = 0 is valid (non-final block)
+
+ Pair result = BLAKE2F.execute(input);
+ assertTrue(result.getLeft());
+ assertEquals(64, result.getRight().length);
+ }
+}
diff --git a/framework/src/test/java/org/tron/common/runtime/vm/ECRecoverTest.java b/framework/src/test/java/org/tron/common/runtime/vm/ECRecoverTest.java
new file mode 100644
index 00000000000..d5159627537
--- /dev/null
+++ b/framework/src/test/java/org/tron/common/runtime/vm/ECRecoverTest.java
@@ -0,0 +1,120 @@
+package org.tron.common.runtime.vm;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import org.apache.commons.lang3.tuple.Pair;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.Test;
+import org.tron.common.crypto.ECKey;
+import org.tron.common.crypto.ECKey.ECDSASignature;
+import org.tron.core.vm.PrecompiledContracts.ECRecover;
+
+/**
+ * Direct execution regression baseline for the ECRECOVER precompile (address 0x01).
+ *
+ * Before this class the precompile body had no asserting test — it was only
+ * invoked once without checking the recovered address, plus a non-asserting
+ * microbenchmark. These tests exercise the success path (recovered address must
+ * match the signer) and the failure paths (bad v, malformed r/s, short input),
+ * all of which must return an empty result rather than throw.
+ *
+ *
ECDSA signing here is deterministic (RFC 6979), so a fixed private key
+ * yields a stable signature and the test needs no externally pinned r/s vector.
+ */
+public class ECRecoverTest {
+
+ private static final ECRecover EC_RECOVER = new ECRecover();
+
+ // Fixed placeholder private key — deterministic so the recovered address is stable.
+ private static final BigInteger PRIV =
+ new BigInteger("c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4", 16);
+ private static final byte[] HASH =
+ Hex.decode("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad");
+
+ private static byte[] fixed32(BigInteger b) {
+ byte[] x = b.toByteArray();
+ byte[] out = new byte[32];
+ if (x.length > 32) {
+ System.arraycopy(x, x.length - 32, out, 0, 32);
+ } else {
+ System.arraycopy(x, 0, out, 32 - x.length, x.length);
+ }
+ return out;
+ }
+
+ /** Build a 128-byte ECRECOVER input: hash(32) | v(32, right-aligned) | r(32) | s(32). */
+ private static byte[] buildInput(byte[] hash, ECDSASignature sig, byte v) {
+ byte[] input = new byte[128];
+ System.arraycopy(hash, 0, input, 0, 32);
+ input[63] = v;
+ System.arraycopy(fixed32(sig.r), 0, input, 64, 32);
+ System.arraycopy(fixed32(sig.s), 0, input, 96, 32);
+ return input;
+ }
+
+ @Test
+ public void recoversSignerAddress() {
+ ECKey key = ECKey.fromPrivate(PRIV);
+ ECDSASignature sig = key.sign(HASH);
+
+ Pair result = EC_RECOVER.execute(buildInput(HASH, sig, sig.v));
+
+ assertTrue(result.getLeft());
+ assertEquals("recovered output must be a 32-byte word", 32, result.getRight().length);
+
+ // The precompile left-pads the 21-byte TRON address into a 32-byte word.
+ byte[] expected = key.getAddress();
+ byte[] tail = Arrays.copyOfRange(result.getRight(), 32 - expected.length, 32);
+ assertArrayEquals("recovered address must match the signer", expected, tail);
+ }
+
+ @Test
+ public void rejectsInvalidRecoveryId() {
+ ECKey key = ECKey.fromPrivate(PRIV);
+ ECDSASignature sig = key.sign(HASH);
+
+ // v = 17 is not a valid recovery id; recovery must fail -> empty result.
+ Pair result = EC_RECOVER.execute(buildInput(HASH, sig, (byte) 17));
+
+ assertTrue(result.getLeft());
+ assertEquals("invalid v must yield empty output", 0, result.getRight().length);
+ }
+
+ @Test
+ public void rejectsNonZeroVPadding() {
+ ECKey key = ECKey.fromPrivate(PRIV);
+ ECDSASignature sig = key.sign(HASH);
+
+ // The 32-byte v word must be zero except its last byte; pollute a high byte.
+ byte[] input = buildInput(HASH, sig, sig.v);
+ input[32] = 0x01;
+
+ Pair result = EC_RECOVER.execute(input);
+ assertEquals("non-zero v padding must yield empty output", 0, result.getRight().length);
+ }
+
+ @Test
+ public void rejectsZeroSignatureComponents() {
+ // r = s = 0 is not a valid signature; must fail gracefully.
+ byte[] input = new byte[128];
+ System.arraycopy(HASH, 0, input, 0, 32);
+ input[63] = 27;
+
+ Pair result = EC_RECOVER.execute(input);
+ assertTrue(result.getLeft());
+ assertEquals("zero r/s must yield empty output", 0, result.getRight().length);
+ }
+
+ @Test
+ public void handlesShortInputWithoutThrowing() {
+ // Truncated input must not throw — the precompile swallows the exception
+ // and returns an empty result.
+ Pair result = EC_RECOVER.execute(new byte[64]);
+ assertTrue(result.getLeft());
+ assertEquals(0, result.getRight().length);
+ }
+}
diff --git a/framework/src/test/java/org/tron/common/runtime/vm/PrecompileVectorTest.java b/framework/src/test/java/org/tron/common/runtime/vm/PrecompileVectorTest.java
new file mode 100644
index 00000000000..46b5a6b7b3c
--- /dev/null
+++ b/framework/src/test/java/org/tron/common/runtime/vm/PrecompileVectorTest.java
@@ -0,0 +1,105 @@
+package org.tron.common.runtime.vm;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.InputStream;
+import java.util.List;
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.tron.common.utils.ByteArray;
+import org.tron.core.vm.PrecompiledContracts.BN128Addition;
+import org.tron.core.vm.PrecompiledContracts.BN128Multiplication;
+import org.tron.core.vm.PrecompiledContracts.BN128Pairing;
+import org.tron.core.vm.PrecompiledContracts.Blake2F;
+import org.tron.core.vm.PrecompiledContracts.PrecompiledContract;
+import org.tron.core.vm.config.VMConfig;
+
+/**
+ * Consolidated, data-driven regression baseline for the cryptographic precompiles.
+ *
+ * Each precompile is exercised against a JSON vector file under
+ * {@code resources/precompiles/}, following the same {@code {Input, Expected, Gas,
+ * Name}} schema already used by {@code p256verify_test_vectors.json}. This keeps the
+ * official / computed vectors in one place so they can be reviewed, extended, and
+ * reused, rather than being scattered as inline literals across test classes.
+ *
+ *
Vector sources:
+ *
+ * - bn128add / bn128mul / bn128pairing — computed against the alt_bn128 curve
+ * using the EIP-197 G2 generator; aligned with go-ethereum's bn256 test data.
+ * Gas values are the Istanbul schedule (EIP-1108).
+ * - blake2f — EIP-152 reference vectors (4–7).
+ *
+ *
+ * An empty {@code Expected} means the precompile is expected to return an empty
+ * byte array (failure / no output) without reverting.
+ */
+public class PrecompileVectorTest {
+
+ public static class TestCase {
+ public String Input;
+ public String Expected;
+ public String Name;
+ public long Gas;
+ }
+
+ @Before
+ public void enableIstanbul() {
+ // BN128 Istanbul gas schedule (EIP-1108) is gated behind this flag.
+ VMConfig.initAllowTvmIstanbul(1);
+ }
+
+ @After
+ public void resetIstanbul() {
+ VMConfig.initAllowTvmIstanbul(0);
+ }
+
+ private static void runVectors(String resource, PrecompiledContract contract)
+ throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ List cases;
+ try (InputStream is = PrecompileVectorTest.class.getResourceAsStream(resource)) {
+ Assert.assertNotNull("vector resource missing: " + resource, is);
+ cases = mapper.readerForListOf(TestCase.class).readValue(is);
+ }
+ Assert.assertFalse("vector list empty: " + resource, cases.isEmpty());
+
+ for (TestCase tc : cases) {
+ byte[] input = tc.Input == null || tc.Input.isEmpty()
+ ? new byte[0]
+ : ByteArray.fromHexString(tc.Input);
+ byte[] expected = tc.Expected == null || tc.Expected.isEmpty()
+ ? new byte[0]
+ : ByteArray.fromHexString(tc.Expected);
+
+ Pair result = contract.execute(input);
+
+ Assert.assertTrue(tc.Name + ": precompile must not revert", result.getLeft());
+ Assert.assertArrayEquals(tc.Name + ": output mismatch", expected, result.getRight());
+ Assert.assertEquals(tc.Name + ": gas mismatch",
+ tc.Gas, contract.getEnergyForData(input));
+ }
+ }
+
+ @Test
+ public void bn128AddVectors() throws Exception {
+ runVectors("/precompiles/bn128add_test_vectors.json", new BN128Addition());
+ }
+
+ @Test
+ public void bn128MulVectors() throws Exception {
+ runVectors("/precompiles/bn128mul_test_vectors.json", new BN128Multiplication());
+ }
+
+ @Test
+ public void bn128PairingVectors() throws Exception {
+ runVectors("/precompiles/bn128pairing_test_vectors.json", new BN128Pairing());
+ }
+
+ @Test
+ public void blake2fVectors() throws Exception {
+ runVectors("/precompiles/blake2f_test_vectors.json", new Blake2F());
+ }
+}
diff --git a/framework/src/test/resources/precompiles/blake2f_test_vectors.json b/framework/src/test/resources/precompiles/blake2f_test_vectors.json
new file mode 100644
index 00000000000..85aa991704e
--- /dev/null
+++ b/framework/src/test/resources/precompiles/blake2f_test_vectors.json
@@ -0,0 +1,26 @@
+[
+ {
+ "Input": "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
+ "Expected": "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
+ "Gas": 12,
+ "Name": "EIP-152 vector 4 (rounds=12, f=1)"
+ },
+ {
+ "Input": "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000",
+ "Expected": "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735",
+ "Gas": 12,
+ "Name": "EIP-152 vector 5 (rounds=12, f=0)"
+ },
+ {
+ "Input": "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
+ "Expected": "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421",
+ "Gas": 1,
+ "Name": "EIP-152 vector 6 (rounds=1, f=1)"
+ },
+ {
+ "Input": "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
+ "Expected": "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b",
+ "Gas": 0,
+ "Name": "EIP-152 vector 7 (rounds=0, f=1)"
+ }
+]
diff --git a/framework/src/test/resources/precompiles/bn128add_test_vectors.json b/framework/src/test/resources/precompiles/bn128add_test_vectors.json
new file mode 100644
index 00000000000..eb9d5365dd1
--- /dev/null
+++ b/framework/src/test/resources/precompiles/bn128add_test_vectors.json
@@ -0,0 +1,20 @@
+[
+ {
+ "Input": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ "Expected": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4",
+ "Gas": 150,
+ "Name": "ecAdd G + G = 2G"
+ },
+ {
+ "Input": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ "Gas": 150,
+ "Name": "ecAdd G + O = G (identity)"
+ },
+ {
+ "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45",
+ "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "Gas": 150,
+ "Name": "ecAdd G + (-G) = O"
+ }
+]
diff --git a/framework/src/test/resources/precompiles/bn128mul_test_vectors.json b/framework/src/test/resources/precompiles/bn128mul_test_vectors.json
new file mode 100644
index 00000000000..ade5ae9e8a7
--- /dev/null
+++ b/framework/src/test/resources/precompiles/bn128mul_test_vectors.json
@@ -0,0 +1,20 @@
+[
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002",
+ "Expected": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4",
+ "Gas": 6000,
+ "Name": "ecMul 2 * G = 2G"
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000",
+ "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "Gas": 6000,
+ "Name": "ecMul 0 * G = O"
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001",
+ "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ "Gas": 6000,
+ "Name": "ecMul 1 * G = G"
+ }
+]
diff --git a/framework/src/test/resources/precompiles/bn128pairing_test_vectors.json b/framework/src/test/resources/precompiles/bn128pairing_test_vectors.json
new file mode 100644
index 00000000000..447936a1174
--- /dev/null
+++ b/framework/src/test/resources/precompiles/bn128pairing_test_vectors.json
@@ -0,0 +1,20 @@
+[
+ {
+ "Input": "",
+ "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
+ "Gas": 45000,
+ "Name": "ecPairing empty input = 1"
+ },
+ {
+ "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
+ "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
+ "Gas": 113000,
+ "Name": "ecPairing e(G1,G2)*e(-G1,G2) = 1"
+ },
+ {
+ "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
+ "Expected": "0000000000000000000000000000000000000000000000000000000000000000",
+ "Gas": 79000,
+ "Name": "ecPairing e(G1,G2) != 1"
+ }
+]