diff --git a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java index cf83ceeb8e..23bfc6ba64 100755 --- a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java @@ -657,20 +657,20 @@ public boolean validatePubSignature(AccountStore accountStore, DynamicPropertiesStore dynamicPropertiesStore) throws ValidateSignatureException { if (!isVerified) { - int legacyCount = this.transaction.getSignatureCount(); + int signatureCount = this.transaction.getSignatureCount(); int pqCount = this.transaction.getPqAuthSigCount(); - if (pqCount > 0 && !dynamicPropertiesStore.isAnyPqSchemeAllowed()) { + if (dynamicPropertiesStore.isAnyPqSchemeAllowed()) { + signatureCount += pqCount; + } else if (pqCount > 0) { throw new ValidateSignatureException( "pq_auth_sig not allowed: no post-quantum scheme is activated"); } - if (legacyCount == 0 && pqCount == 0) { - throw new ValidateSignatureException("miss sig"); - } - if (this.transaction.getRawData().getContractCount() <= 0) { - throw new ValidateSignatureException("miss contract"); + + if (signatureCount == 0 || this.transaction.getRawData().getContractCount() <= 0) { + throw new ValidateSignatureException("miss sig or contract"); } - if (legacyCount + pqCount > dynamicPropertiesStore.getTotalSignNum()) { + if (signatureCount > dynamicPropertiesStore.getTotalSignNum()) { throw new ValidateSignatureException("too many signatures"); } @@ -800,7 +800,7 @@ public boolean validateSignature(AccountStore accountStore, validatePubSignature(accountStore, dynamicPropertiesStore); } else { //transfer from shielded address if (this.transaction.getSignatureCount() > 0 - || this.transaction.getPqAuthSigCount() > 0) { + || (this.transaction.getPqAuthSigCount() > 0)) { throw new ValidateSignatureException("there should be no signatures signed by " + "transparent address when transfer from shielded address"); } diff --git a/chainbase/src/main/java/org/tron/core/db/BandwidthProcessor.java b/chainbase/src/main/java/org/tron/core/db/BandwidthProcessor.java index 1d2987d4d4..6b7e9795bf 100644 --- a/chainbase/src/main/java/org/tron/core/db/BandwidthProcessor.java +++ b/chainbase/src/main/java/org/tron/core/db/BandwidthProcessor.java @@ -23,6 +23,7 @@ import org.tron.core.exception.ContractValidateException; import org.tron.core.exception.TooBigTransactionException; import org.tron.core.exception.TooBigTransactionResultException; +import org.tron.protos.Protocol.PQAuthSig; import org.tron.protos.Protocol.Transaction.Contract; import org.tron.protos.contract.AssetIssueContractOuterClass.TransferAssetContract; import org.tron.protos.contract.BalanceContract.TransferContract; @@ -141,11 +142,12 @@ public void consume(TransactionCapsule trx, TransactionTrace trace) long maxCreateAccountTxSize = dynamicPropertiesStore.getMaxCreateAccountTxSize(); int signatureCount = trx.getInstance().getSignatureCount(); long sigOverhead = signatureCount * PER_SIGN_LENGTH; + + // PQAuthSig bytes are subtracted as signature overhead regardless of open or not if (trx.getInstance().getPqAuthSigCount() > 0) { long pqAuthSigBytes = 0L; - for (org.tron.protos.Protocol.PQAuthSig aw - : trx.getInstance().getPqAuthSigList()) { - pqAuthSigBytes += aw.getSerializedSize(); + for (PQAuthSig pqAuthSig : trx.getInstance().getPqAuthSigList()) { + pqAuthSigBytes += pqAuthSig.getSerializedSize(); } sigOverhead += pqAuthSigBytes; } diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index ea88128c54..aa7103fb95 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -16,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.tron.common.crypto.pqc.PQSchemeRegistry; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; import org.tron.common.utils.Sha256Hash; @@ -3101,9 +3102,20 @@ public boolean allowFnDsa512() { return getAllowFnDsa512() == 1L; } - /** Returns true iff at least one post-quantum signature scheme is currently activated. */ + /** + * Returns true iff at least one post-quantum signature scheme is currently + * activated. Driven by {@link PQSchemeRegistry#registeredSchemes()} so that + * adding a new scheme to the registry (and its corresponding case in + * {@link #isPqSchemeAllowed}) automatically propagates here — no manual edit + * needed. + */ public boolean isAnyPqSchemeAllowed() { - return allowFnDsa512(); + for (PQScheme scheme : PQSchemeRegistry.registeredSchemes()) { + if (isPqSchemeAllowed(scheme)) { + return true; + } + } + return false; } /** diff --git a/consensus/src/main/java/org/tron/consensus/base/Param.java b/consensus/src/main/java/org/tron/consensus/base/Param.java index 71f23ce23a..fd9b130a3c 100644 --- a/consensus/src/main/java/org/tron/consensus/base/Param.java +++ b/consensus/src/main/java/org/tron/consensus/base/Param.java @@ -80,18 +80,15 @@ public class Miner { private byte[] pqPublicKey; - @Getter - @Setter - private PQScheme pqScheme; - /** - * Explicit signing-family marker so the block producer doesn't have to infer - * key type from {@code privateKey == null}. Defaults to {@link MinerType#ECDSA}; - * PQ-only miners must call {@link #setType(MinerType)} with {@link MinerType#PQ}. + * Post-quantum signature scheme for this miner. When unset (null), the + * miner signs blocks with the legacy ECDSA path using {@link #privateKey}; + * when set (e.g. {@code FN_DSA_512}), the PQ path is used with + * {@link #pqPrivateKey} / {@link #pqPublicKey}. */ @Getter @Setter - private MinerType type = MinerType.ECDSA; + private PQScheme pqScheme; public Miner(byte[] privateKey, ByteString privateKeyAddress, ByteString witnessAddress) { this.privateKey = privateKey; diff --git a/crypto/src/main/java/org/tron/common/crypto/pqc/PQSchemeRegistry.java b/crypto/src/main/java/org/tron/common/crypto/pqc/PQSchemeRegistry.java index c57d7e702f..757eb9d831 100644 --- a/crypto/src/main/java/org/tron/common/crypto/pqc/PQSchemeRegistry.java +++ b/crypto/src/main/java/org/tron/common/crypto/pqc/PQSchemeRegistry.java @@ -3,6 +3,7 @@ import java.util.Collections; import java.util.EnumMap; import java.util.Map; +import java.util.Set; import org.tron.common.crypto.Hash; import org.tron.protos.Protocol.PQScheme; @@ -124,6 +125,17 @@ public static boolean contains(PQScheme scheme) { return resolved != null && SCHEMES.containsKey(resolved); } + /** + * Returns the set of post-quantum schemes that are registered (i.e. have an + * active {@link SignatureOps} entry). Lets governance / config layers + * enumerate "all PQ schemes" without hard-coding the list — adding a new + * scheme to the registry then auto-propagates to any caller iterating over + * this set. + */ + public static Set registeredSchemes() { + return SCHEMES.keySet(); + } + public static int getPrivateKeyLength(PQScheme scheme) { return require(scheme).privateKeyLength; } diff --git a/framework/src/main/java/org/tron/core/consensus/ConsensusService.java b/framework/src/main/java/org/tron/core/consensus/ConsensusService.java index 600262c624..a41e16b40f 100644 --- a/framework/src/main/java/org/tron/core/consensus/ConsensusService.java +++ b/framework/src/main/java/org/tron/core/consensus/ConsensusService.java @@ -106,7 +106,6 @@ public void start() { miner.setPQPrivateKey(sk); miner.setPQPublicKey(pk); miner.setPqScheme(scheme); - miner.setType(Param.MinerType.PQ); miners.add(miner); logger.info("Add {} witness (from configured keypair): {}, size: {}", scheme, Hex.toHexString(pqAddress), miners.size()); @@ -145,7 +144,6 @@ private Miner buildPQOnlyMinerFromKeypair(Param param, PqKeypair pqKeypair) { miner.setPQPrivateKey(sk); miner.setPQPublicKey(pk); miner.setPqScheme(scheme); - miner.setType(Param.MinerType.PQ); logger.info("Add {} witness (from configured keypair): {}", scheme, Hex.toHexString(witnessAddress)); return miner; diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index e51215163b..59747b9945 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -86,6 +86,7 @@ import org.tron.common.utils.StringUtil; import org.tron.common.zksnark.MerkleContainer; import org.tron.consensus.Consensus; +import org.tron.consensus.base.Param; import org.tron.consensus.base.Param.Miner; import org.tron.core.ChainBaseManager; import org.tron.core.Constant; @@ -1746,7 +1747,12 @@ public BlockCapsule generateBlock(Miner miner, long blockTime, long timeout) { session.reset(); blockCapsule.setMerkleRoot(); - signBlockCapsule(blockCapsule, miner); + if (getDynamicPropertiesStore().isAnyPqSchemeAllowed() && + miner.getPqScheme() != null) { + signBlockCapsuleWithPQ(blockCapsule, miner); + } else { + blockCapsule.sign(miner.getPrivateKey()); + } BlockCapsule capsule = new BlockCapsule(blockCapsule.getInstance()); capsule.generatedByMyself = true; @@ -1762,57 +1768,23 @@ public BlockCapsule generateBlock(Miner miner, long blockTime, long timeout) { return capsule; } - private void signBlockCapsule(BlockCapsule blockCapsule, Miner miner) { - switch (miner.getType()) { - case PQ: - PQScheme scheme = resolveWitnessScheme(miner); - if (scheme == null) { - // PQ-only miner whose configured scheme is not currently usable - // (proposal not activated, scheme allow flag flipped, witness - // permission missing, etc.). Surface a clear cause; DposTask's - // Throwable handler will log and the witness will miss this slot, - // but the producer thread keeps running. - throw new IllegalStateException( - "PQ-only miner " + Hex.toHexString(miner.getWitnessAddress().toByteArray()) - + " has scheme " + miner.getPqScheme() - + " configured but it is not currently usable " - + "(scheme not allowed by dynamic properties, " - + "or witness permission is missing/empty)"); - } - signWitnessAuth(blockCapsule, miner, scheme); - break; - case ECDSA: - blockCapsule.sign(miner.getPrivateKey()); - break; - default: - throw new IllegalStateException("unknown miner type: " + miner.getType()); - } - } - - private PQScheme resolveWitnessScheme(Miner miner) { - if (!chainBaseManager.getDynamicPropertiesStore().isAnyPqSchemeAllowed()) { - return null; - } + private void signBlockCapsuleWithPQ(BlockCapsule blockCapsule, Miner miner) { PQScheme scheme = miner.getPqScheme(); if (scheme == null || !PQSchemeRegistry.contains(scheme)) { - return null; + throw new IllegalStateException( + "PQ-only miner " + Hex.toHexString(miner.getWitnessAddress().toByteArray()) + + " has scheme " + miner.getPqScheme() + + " configured but it is not currently usable " + + "or witness permission is missing/empty)"); } if (!chainBaseManager.getDynamicPropertiesStore().isPqSchemeAllowed(scheme)) { - return null; - } - byte[] witnessAddress = miner.getWitnessAddress().toByteArray(); - AccountCapsule accountCapsule = chainBaseManager.getAccountStore().get(witnessAddress); - if (accountCapsule == null || !accountCapsule.getInstance().hasWitnessPermission()) { - return null; - } - Permission witnessPermission = accountCapsule.getInstance().getWitnessPermission(); - if (witnessPermission.getKeysCount() == 0) { - return null; + throw new IllegalStateException( + "PQ-only miner " + Hex.toHexString(miner.getWitnessAddress().toByteArray()) + + " has scheme " + miner.getPqScheme() + + " but it is not allowed by dynamic properties"); } - return scheme; - } - private void signWitnessAuth(BlockCapsule blockCapsule, Miner miner, PQScheme scheme) { + byte[] pqPrivateKey = miner.getPQPrivateKey(); byte[] pqPublicKey = miner.getPQPublicKey(); if (pqPrivateKey == null || pqPublicKey == null) {