|
| 1 | +package org.tron.common.runtime.vm; |
| 2 | + |
| 3 | +import com.fasterxml.jackson.databind.ObjectMapper; |
| 4 | +import java.io.InputStream; |
| 5 | +import java.util.List; |
| 6 | +import lombok.extern.slf4j.Slf4j; |
| 7 | +import org.apache.commons.lang3.tuple.Pair; |
| 8 | +import org.junit.Assert; |
| 9 | +import org.junit.Test; |
| 10 | +import org.tron.common.utils.ByteArray; |
| 11 | +import org.tron.core.vm.PrecompiledContracts; |
| 12 | + |
| 13 | +@Slf4j |
| 14 | +public class P256VerifyTest { |
| 15 | + |
| 16 | + private static final PrecompiledContracts.P256Verify CONTRACT = |
| 17 | + new PrecompiledContracts.P256Verify(); |
| 18 | + |
| 19 | + public static class TestCase { |
| 20 | + public String Input; |
| 21 | + public String Expected; |
| 22 | + public String Name; |
| 23 | + public int Gas; |
| 24 | + public boolean NoBenchmark; |
| 25 | + } |
| 26 | + |
| 27 | + private static byte[] hex(String s) { |
| 28 | + return ByteArray.fromHexString(s); |
| 29 | + } |
| 30 | + |
| 31 | + private static byte[] success() { |
| 32 | + byte[] r = new byte[32]; |
| 33 | + r[31] = 0x01; |
| 34 | + return r; |
| 35 | + } |
| 36 | + |
| 37 | + @Test |
| 38 | + public void gethConformanceVectors() throws Exception { |
| 39 | + ObjectMapper mapper = new ObjectMapper(); |
| 40 | + List<TestCase> cases; |
| 41 | + try (InputStream is = P256VerifyTest.class.getResourceAsStream( |
| 42 | + "/precompiles/p256verify_test_vectors.json")) { |
| 43 | + Assert.assertNotNull("test vectors resource missing", is); |
| 44 | + cases = mapper.readerForListOf(TestCase.class).readValue(is); |
| 45 | + } |
| 46 | + Assert.assertFalse("vector list empty", cases.isEmpty()); |
| 47 | + |
| 48 | + for (TestCase tc : cases) { |
| 49 | + byte[] input = ByteArray.fromHexString(tc.Input); |
| 50 | + byte[] expected = tc.Expected == null || tc.Expected.isEmpty() |
| 51 | + ? new byte[0] |
| 52 | + : ByteArray.fromHexString(tc.Expected); |
| 53 | + |
| 54 | + Pair<Boolean, byte[]> result = CONTRACT.execute(input); |
| 55 | + |
| 56 | + Assert.assertTrue(tc.Name + ": precompile must not revert", result.getLeft()); |
| 57 | + Assert.assertArrayEquals(tc.Name + ": output mismatch", |
| 58 | + expected, result.getRight()); |
| 59 | + Assert.assertEquals(tc.Name + ": gas mismatch", |
| 60 | + tc.Gas, CONTRACT.getEnergyForData(input)); |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + @Test |
| 65 | + public void rejectsNullInput() { |
| 66 | + Pair<Boolean, byte[]> r = CONTRACT.execute(null); |
| 67 | + Assert.assertTrue(r.getLeft()); |
| 68 | + Assert.assertArrayEquals(new byte[0], r.getRight()); |
| 69 | + } |
| 70 | + |
| 71 | + @Test |
| 72 | + public void rejectsEmptyInput() { |
| 73 | + Pair<Boolean, byte[]> r = CONTRACT.execute(new byte[0]); |
| 74 | + Assert.assertTrue(r.getLeft()); |
| 75 | + Assert.assertArrayEquals(new byte[0], r.getRight()); |
| 76 | + } |
| 77 | + |
| 78 | + @Test |
| 79 | + public void rejectsShortInput() { |
| 80 | + Pair<Boolean, byte[]> r = CONTRACT.execute(new byte[159]); |
| 81 | + Assert.assertTrue(r.getLeft()); |
| 82 | + Assert.assertArrayEquals(new byte[0], r.getRight()); |
| 83 | + } |
| 84 | + |
| 85 | + @Test |
| 86 | + public void rejectsLongInput() { |
| 87 | + Pair<Boolean, byte[]> r = CONTRACT.execute(new byte[161]); |
| 88 | + Assert.assertTrue(r.getLeft()); |
| 89 | + Assert.assertArrayEquals(new byte[0], r.getRight()); |
| 90 | + } |
| 91 | + |
| 92 | + @Test |
| 93 | + public void rejectsInfinityPoint() { |
| 94 | + // Valid h, r, s plus qx=qy=0 -> infinity-encoded public key. |
| 95 | + String input = |
| 96 | + "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4d" |
| 97 | + + "a73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac" |
| 98 | + + "36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d60" |
| 99 | + + "0000000000000000000000000000000000000000000000000000000000000000" |
| 100 | + + "0000000000000000000000000000000000000000000000000000000000000000"; |
| 101 | + Pair<Boolean, byte[]> r = CONTRACT.execute(hex(input)); |
| 102 | + Assert.assertTrue(r.getLeft()); |
| 103 | + Assert.assertArrayEquals(new byte[0], r.getRight()); |
| 104 | + } |
| 105 | + |
| 106 | + /** |
| 107 | + * Public key coordinates are valid field elements but the point is NOT on |
| 108 | + * the secp256r1 curve (they happen to be the secp256k1 base point). The |
| 109 | + * precompile must fail the on-curve check before attempting verification. |
| 110 | + * Input lifted from Besu's P256VerifyPrecompiledContractTest. |
| 111 | + */ |
| 112 | + @Test |
| 113 | + public void rejectsOffCurvePoint() { |
| 114 | + String input = |
| 115 | + "44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56" |
| 116 | + + "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" |
| 117 | + + "30dae23890abb63e378e003d7f1d5006ab23cc7b3b65b3d0c7b45c7e1e2e08b9" |
| 118 | + + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" |
| 119 | + + "b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777"; |
| 120 | + Pair<Boolean, byte[]> r = CONTRACT.execute(hex(input)); |
| 121 | + Assert.assertTrue(r.getLeft()); |
| 122 | + Assert.assertArrayEquals(new byte[0], r.getRight()); |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * The recovered point's x-coordinate exceeds n; verification must still |
| 127 | + * succeed because R'.x mod n == r. Input lifted from Besu's |
| 128 | + * testModularComparisonWhenRPrimeExceedsN. |
| 129 | + */ |
| 130 | + @Test |
| 131 | + public void acceptsModularComparisonWhenRPrimeExceedsN() { |
| 132 | + String input = |
| 133 | + "BB5A52F42F9C9261ED4361F59422A1E30036E7C32B270C8807A419FECA605023" |
| 134 | + + "000000000000000000000000000000004319055358E8617B0C46353D039CDAAB" |
| 135 | + + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254E" |
| 136 | + + "0AD99500288D466940031D72A9F5445A4D43784640855BF0A69874D2DE5FE103" |
| 137 | + + "C5011E6EF2C42DCD50D5D3D29F99AE6EBA2C80C9244F4C5422F0979FF0C3BA5E"; |
| 138 | + Pair<Boolean, byte[]> r = CONTRACT.execute(hex(input)); |
| 139 | + Assert.assertTrue(r.getLeft()); |
| 140 | + Assert.assertArrayEquals(success(), r.getRight()); |
| 141 | + } |
| 142 | + |
| 143 | + @Test |
| 144 | + public void gasCostIsConstant6900() { |
| 145 | + Assert.assertEquals(6900L, CONTRACT.getEnergyForData(null)); |
| 146 | + Assert.assertEquals(6900L, CONTRACT.getEnergyForData(new byte[0])); |
| 147 | + Assert.assertEquals(6900L, CONTRACT.getEnergyForData(new byte[160])); |
| 148 | + Assert.assertEquals(6900L, CONTRACT.getEnergyForData(new byte[1024])); |
| 149 | + } |
| 150 | +} |
0 commit comments