5353import org .tron .common .crypto .Rsv ;
5454import org .tron .common .crypto .SignUtils ;
5555import org .tron .common .crypto .SignatureInterface ;
56- import org .tron .common .crypto .pqc .FNDSA ;
56+ import org .tron .common .crypto .pqc .FNDSA512 ;
5757import org .tron .common .crypto .pqc .PQSchemeRegistry ;
5858import org .tron .common .crypto .zksnark .BN128 ;
5959import org .tron .common .crypto .zksnark .BN128Fp ;
@@ -120,10 +120,10 @@ public class PrecompiledContracts {
120120 private static final Blake2F blake2F = new Blake2F ();
121121 private static final P256Verify p256Verify = new P256Verify ();
122122
123- private static final VerifyFnDsa verifyFnDsa = new VerifyFnDsa ();
124- private static final ValidateMultiSignPQ validateMultiSignPQ =
125- new ValidateMultiSignPQ ();
126- private static final BatchValidateSignPQ batchValidateSignPQ = new BatchValidateSignPQ ();
123+ private static final VerifyFnDsa512 verifyFnDsa512 = new VerifyFnDsa512 ();
124+ private static final ValidateMultiFnDsa512 validateMultiFnDsa512 =
125+ new ValidateMultiFnDsa512 ();
126+ private static final BatchValidateFnDsa512 batchValidateFnDsa512 = new BatchValidateFnDsa512 ();
127127
128128 // FreezeV2 PrecompileContracts
129129 private static final GetChainParameter getChainParameter = new GetChainParameter ();
@@ -223,19 +223,19 @@ public class PrecompiledContracts {
223223 // EIP-8052 0x16: FN-DSA / Falcon-512 verify (FIPS-206 draft). Input layout:
224224 // [msg 32B | sig_len 2B (big-endian) | sig sig_len B (1..752) | pk 896B].
225225 // Variable-length signature is prefixed with a 2-byte length field.
226- private static final DataWord verifyFnDsaAddr = new DataWord (
226+ private static final DataWord verifyFnDsa512Addr = new DataWord (
227227 "0000000000000000000000000000000000000000000000000000000000000016" );
228228
229229 // 0x17: algorithm-agnostic Permission multi-sign — accepts both ECDSA and
230230 // Falcon-512 signatures against the same Permission.keys[] in one call,
231231 // matching transaction-side §2.3.5 mixed-weight semantics.
232- private static final DataWord validateMultiSignPQAddr = new DataWord (
232+ private static final DataWord validateMultiFnDsa512Addr = new DataWord (
233233 "0000000000000000000000000000000000000000000000000000000000000017" );
234234
235235 // 0x18: batch independent Falcon-512 verify — bitmap of (sig, pk, addr)
236236 // matches; mixed-algorithm contracts call 0x0A and 0x18 separately and OR
237237 // the bitmaps client-side.
238- private static final DataWord batchValidateSignPqAddr = new DataWord (
238+ private static final DataWord batchValidateFnDsa512Addr = new DataWord (
239239 "0000000000000000000000000000000000000000000000000000000000000018" );
240240
241241 public static PrecompiledContract getOptimizedContractForConstant (PrecompiledContract contract ) {
@@ -323,16 +323,18 @@ public static PrecompiledContract getContractForAddress(DataWord address) {
323323 return p256Verify ;
324324 }
325325
326- if (VMConfig .allowFnDsa512 () && address .equals (verifyFnDsaAddr )) {
327- return verifyFnDsa ;
328- }
329-
330- if (VMConfig .allowFnDsa512 () && address .equals (validateMultiSignPQAddr )) {
331- return validateMultiSignPQ ;
332- }
333-
334- if (VMConfig .allowFnDsa512 () && address .equals (batchValidateSignPqAddr )) {
335- return batchValidateSignPQ ;
326+ // FN-DSA-512 is the first PQ signature scheme supported by TRON, so its proposal flag
327+ // gates every PQ-related precompile (single verify, multisig verify, batch verify).
328+ if (VMConfig .allowFnDsa512 ()) {
329+ if (address .equals (verifyFnDsa512Addr )) {
330+ return verifyFnDsa512 ;
331+ }
332+ if (address .equals (validateMultiFnDsa512Addr )) {
333+ return validateMultiFnDsa512 ;
334+ }
335+ if (address .equals (batchValidateFnDsa512Addr )) {
336+ return batchValidateFnDsa512 ;
337+ }
336338 }
337339
338340 if (VMConfig .allowTvmFreezeV2 ()) {
@@ -475,6 +477,35 @@ private static boolean isValidAbiEncoding(byte[] data, int headerWords, int item
475477 return tail > 0 && tail % multiplyExact (itemWords , WORD_SIZE ) == 0 ;
476478 }
477479
480+ /**
481+ * Structural pre-check for ABI head: word-aligned length and room for the
482+ * fixed head. The PQ precompiles cannot reuse {@link #isValidAbiEncoding}
483+ * because their {@code bytes[]} entries (PQ signatures, 1..752 bytes) are
484+ * variable-length, so the trailing divisibility check does not apply.
485+ */
486+ private static boolean isValidAbiHead (byte [] data , int headWords ) {
487+ return data != null
488+ && data .length % WORD_SIZE == 0
489+ && data .length >= multiplyExact (headWords , WORD_SIZE );
490+ }
491+
492+ /**
493+ * Verifies that the array offset stored at {@code words[offsetWordIndex]} is
494+ * word-aligned, falls inside the dynamic data region (≥ head), and points to
495+ * a length word that still fits inside {@code words}. Sister check to
496+ * {@link #isValidAbiEncoding} for ABIs whose items are not uniform width.
497+ */
498+ private static boolean isValidArrayOffset (DataWord [] words , int offsetWordIndex ,
499+ int headWords ) {
500+ long offsetBytes = words [offsetWordIndex ].longValueSafe ();
501+ if (offsetBytes < (long ) headWords * WORD_SIZE
502+ || offsetBytes % WORD_SIZE != 0 ) {
503+ return false ;
504+ }
505+ long lengthWordIdx = offsetBytes / WORD_SIZE ;
506+ return lengthWordIdx < words .length ;
507+ }
508+
478509 public abstract static class PrecompiledContract {
479510
480511 protected static final byte [] DATA_FALSE = new byte [WORD_SIZE ];
@@ -2425,22 +2456,23 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
24252456 * <pre>
24262457 * [msg 32B | sig_len 2B (big-endian, 1..752) | sig sig_len B | pk 896B]
24272458 * </pre>
2428- * Minimum input: 32 + 2 + 1 + 896 = 931 bytes.
2459+ * Total length must equal exactly {@code 32 + 2 + sig_len + 896} (no trailing
2460+ * bytes; matches 0x100 P256Verify / EIP-7951 strictness).
24292461 *
24302462 * <p>Returns a 32-byte word: 1 on valid signature, 0 otherwise.
24312463 * Malformed input (wrong lengths, out-of-range sig_len) returns 0 without error.
24322464 */
2433- public static class VerifyFnDsa extends PrecompiledContract {
2465+ public static class VerifyFnDsa512 extends PrecompiledContract {
24342466
24352467 private static final int MSG_LEN = 32 ;
24362468 private static final int SIG_LEN_FIELD = 2 ;
2437- private static final int PK_LEN = FNDSA .PUBLIC_KEY_LENGTH ;
2438- private static final int MAX_SIG_LEN = FNDSA .SIGNATURE_LENGTH ;
2469+ private static final int PK_LEN = FNDSA512 .PUBLIC_KEY_LENGTH ;
2470+ private static final int MAX_SIG_LEN = FNDSA512 .SIGNATURE_LENGTH ;
24392471 private static final int MIN_INPUT_LEN = MSG_LEN + SIG_LEN_FIELD + 1 + PK_LEN ;
24402472
24412473 @ Override
24422474 public long getEnergyForData (byte [] data ) {
2443- return 2500 ;
2475+ return 4000 ;
24442476 }
24452477
24462478 @ Override
@@ -2455,14 +2487,17 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
24552487 return Pair .of (true , DataWord .ZERO ().getData ());
24562488 }
24572489 int pkOffset = MSG_LEN + SIG_LEN_FIELD + sigLen ;
2458- if (data .length < pkOffset + PK_LEN ) {
2490+ // Strict equality (cf. 0x100 P256Verify): one logical input ↔ one encoding,
2491+ // leaves room for future EIP-8052 trailing fields.
2492+ if (data .length != pkOffset + PK_LEN ) {
24592493 return Pair .of (true , DataWord .ZERO ().getData ());
24602494 }
24612495 byte [] sig = copyOfRange (data , MSG_LEN + SIG_LEN_FIELD , pkOffset );
24622496 byte [] pk = copyOfRange (data , pkOffset , pkOffset + PK_LEN );
2463- boolean ok = FNDSA .verify (pk , msg , sig );
2497+ boolean ok = FNDSA512 .verify (pk , msg , sig );
24642498 return Pair .of (true , ok ? DataWord .ONE ().getData () : DataWord .ZERO ().getData ());
24652499 } catch (Throwable t ) {
2500+ logger .info ("VerifyFnDsa512 error:{}" , t .getMessage ());
24662501 return Pair .of (true , DataWord .ZERO ().getData ());
24672502 }
24682503 }
@@ -2489,15 +2524,17 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
24892524 * </pre>
24902525 *
24912526 * <p>{@code MAX_SIZE = 5} applies to the total signature count
2492- * ({@code ecdsaCnt + pqCnt}). Energy is split: {@code ecdsaCnt × 1500 + pqCnt × 15000 }.
2527+ * ({@code ecdsaCnt + pqCnt}). Energy is split: {@code ecdsaCnt × 1500 + pqCnt × 2000 }.
24932528 */
2494- public static class ValidateMultiSignPQ extends PrecompiledContract {
2529+ public static class ValidateMultiFnDsa512 extends PrecompiledContract {
24952530
24962531 private static final int ECDSA_ENERGY_PER_SIGN = 1500 ;
2497- private static final int PQ_ENERGY_PER_SIGN = 15000 ;
2532+ private static final int PQ_ENERGY_PER_SIGN = 2000 ;
24982533 private static final int MAX_SIZE = 5 ;
2499- private static final int PK_LEN = FNDSA .PUBLIC_KEY_LENGTH ;
2500- private static final int MAX_SIG_LEN = FNDSA .SIGNATURE_LENGTH ;
2534+ private static final int PK_LEN = FNDSA512 .PUBLIC_KEY_LENGTH ;
2535+ private static final int MAX_SIG_LEN = FNDSA512 .SIGNATURE_LENGTH ;
2536+ // address, permissionId, data, ecdsaOffset, pqSigOffset, pqPkOffset.
2537+ private static final int ABI_HEAD_WORDS = 6 ;
25012538
25022539 @ Override
25032540 public long getEnergyForData (byte [] data ) {
@@ -2514,8 +2551,16 @@ public long getEnergyForData(byte[] data) {
25142551
25152552 @ Override
25162553 public Pair <Boolean , byte []> execute (byte [] rawData ) {
2554+ if (!isValidAbiHead (rawData , ABI_HEAD_WORDS )) {
2555+ return Pair .of (false , EMPTY_BYTE_ARRAY );
2556+ }
25172557 try {
25182558 DataWord [] words = DataWord .parseArray (rawData );
2559+ if (!isValidArrayOffset (words , 3 , ABI_HEAD_WORDS )
2560+ || !isValidArrayOffset (words , 4 , ABI_HEAD_WORDS )
2561+ || !isValidArrayOffset (words , 5 , ABI_HEAD_WORDS )) {
2562+ return Pair .of (false , EMPTY_BYTE_ARRAY );
2563+ }
25192564 byte [] address = words [0 ].toTronAddress ();
25202565 int permissionId = words [1 ].intValueSafe ();
25212566 byte [] data = words [2 ].getData ();
@@ -2596,7 +2641,7 @@ public Pair<Boolean, byte[]> execute(byte[] rawData) {
25962641 if (weight == 0 ) {
25972642 return Pair .of (true , DATA_FALSE );
25982643 }
2599- if (!FNDSA .verify (pk , hash , sig )) {
2644+ if (!FNDSA512 .verify (pk , hash , sig )) {
26002645 return Pair .of (true , DATA_FALSE );
26012646 }
26022647 totalWeight += weight ;
@@ -2617,13 +2662,13 @@ public Pair<Boolean, byte[]> execute(byte[] rawData) {
26172662 }
26182663
26192664 /**
2620- * 0x18 BatchValidateSignPQ — independent per-element Falcon-512 verify.
2665+ * 0x18 BatchValidateFnDsa512 — independent per-element Falcon-512 verify.
26212666 * <p>Returns a 256-bit bitmap (matching 0x0A) where bit {@code i} is set iff
2622- * {@code derive(pk_i) == expectedAddr_i} AND {@code FNDSA .verify(pk_i, hash, sig_i)}.
2667+ * {@code derive(pk_i) == expectedAddr_i} AND {@code FNDSA512 .verify(pk_i, hash, sig_i)}.
26232668 *
26242669 * <p>ABI:
26252670 * <pre>
2626- * batchValidateSignPQ (
2671+ * batchValidateFnDsa512 (
26272672 * bytes32 hash, // word[0]
26282673 * bytes[] signatures, // word[1] = offset; each 1..752 B
26292674 * bytes[] publicKeys, // word[2] = offset; each 896 B
@@ -2633,14 +2678,16 @@ public Pair<Boolean, byte[]> execute(byte[] rawData) {
26332678 *
26342679 * <p>Reuses the {@code BatchValidateSign.workers} pool when not in a constant
26352680 * call and enforces {@code getCPUTimeLeftInNanoSecond()} timeout. {@code MAX_SIZE = 16}.
2636- * Energy is {@code cnt × 15000 }.
2681+ * Energy is {@code cnt × 2000 }.
26372682 */
2638- public static class BatchValidateSignPQ extends PrecompiledContract {
2683+ public static class BatchValidateFnDsa512 extends PrecompiledContract {
26392684
2640- private static final int ENERGY_PER_SIGN = 15000 ;
2685+ private static final int ENERGY_PER_SIGN = 2000 ;
26412686 private static final int MAX_SIZE = 16 ;
2642- private static final int PK_LEN = FNDSA .PUBLIC_KEY_LENGTH ;
2643- private static final int MAX_SIG_LEN = FNDSA .SIGNATURE_LENGTH ;
2687+ private static final int PK_LEN = FNDSA512 .PUBLIC_KEY_LENGTH ;
2688+ private static final int MAX_SIG_LEN = FNDSA512 .SIGNATURE_LENGTH ;
2689+ // hash, sigArrayOffset, pkArrayOffset, addrArrayOffset.
2690+ private static final int ABI_HEAD_WORDS = 4 ;
26442691
26452692 @ Override
26462693 public long getEnergyForData (byte [] data ) {
@@ -2670,7 +2717,15 @@ public Pair<Boolean, byte[]> execute(byte[] data) {
26702717
26712718 private Pair <Boolean , byte []> doExecute (byte [] data )
26722719 throws InterruptedException , ExecutionException {
2720+ if (!isValidAbiHead (data , ABI_HEAD_WORDS )) {
2721+ return Pair .of (false , EMPTY_BYTE_ARRAY );
2722+ }
26732723 DataWord [] words = DataWord .parseArray (data );
2724+ if (!isValidArrayOffset (words , 1 , ABI_HEAD_WORDS )
2725+ || !isValidArrayOffset (words , 2 , ABI_HEAD_WORDS )
2726+ || !isValidArrayOffset (words , 3 , ABI_HEAD_WORDS )) {
2727+ return Pair .of (false , EMPTY_BYTE_ARRAY );
2728+ }
26742729 byte [] hash = words [0 ].getData ();
26752730
26762731 int sigArrayWord = words [1 ].intValueSafe () / WORD_SIZE ;
@@ -2718,8 +2773,8 @@ private Pair<Boolean, byte[]> doExecute(byte[] data)
27182773 .await (getCPUTimeLeftInNanoSecond (), TimeUnit .NANOSECONDS );
27192774
27202775 if (!withNoTimeout ) {
2721- logger .info ("BatchValidateSignPQ timeout" );
2722- throw Program .Exception .notEnoughTime ("call BatchValidateSignPQ precompile method" );
2776+ logger .info ("BatchValidateFnDsa512 timeout" );
2777+ throw Program .Exception .notEnoughTime ("call BatchValidateFnDsa512 precompile method" );
27232778 }
27242779
27252780 for (Future <PqVerifyResult > future : futures ) {
@@ -2743,7 +2798,7 @@ private static boolean verifyOne(byte[] sig, byte[] pk, byte[] hash,
27432798 if (!DataWord .equalAddressByteArray (derived , expectedAddr )) {
27442799 return false ;
27452800 }
2746- return FNDSA .verify (pk , hash , sig );
2801+ return FNDSA512 .verify (pk , hash , sig );
27472802 } catch (Throwable t ) {
27482803 return false ;
27492804 }
0 commit comments