Skip to content

Commit befadeb

Browse files
authored
Merge pull request #28 from Little-Peony/pqc-falcon512
2 parents 6b7c139 + edad7b3 commit befadeb

14 files changed

Lines changed: 174 additions & 205 deletions

File tree

actuator/src/main/java/org/tron/core/actuator/VMActuator.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.tron.core.db.TransactionContext;
3838
import org.tron.core.exception.ContractExeException;
3939
import org.tron.core.exception.ContractValidateException;
40+
import org.tron.core.store.DynamicPropertiesStore;
4041
import org.tron.core.utils.TransactionUtil;
4142
import org.tron.core.vm.EnergyCost;
4243
import org.tron.core.vm.LogInfoTriggerParser;
@@ -177,7 +178,8 @@ public void execute(Object object) throws ContractExeException {
177178
ProgramResult result = context.getProgramResult();
178179
try {
179180
if (program != null) {
180-
if (null != blockCap && blockCap.generatedByMyself && blockCap.hasWitnessSignature()
181+
if (null != blockCap && blockCap.generatedByMyself && blockCap.hasWitnessSignature(context.getStoreFactory().getChainBaseManager()
182+
.getDynamicPropertiesStore())
181183
&& null != TransactionUtil.getContractRet(trx)
182184
&& contractResult.OUT_OF_TIME == TransactionUtil.getContractRet(trx)) {
183185
result = program.getResult();
@@ -400,7 +402,7 @@ private void create()
400402

401403
long thisTxCPULimitInUs = calculateCpuLimitInUs(isConstantCall,
402404
rootRepository.getDynamicPropertiesStore().getMaxCpuTimeOfOneTx(),
403-
getCpuLimitInUsRatio(), CommonParameter.getInstance().getConstantCallTimeoutMs());
405+
getCpuLimitInUsRatio(rootRepository.getDynamicPropertiesStore()), CommonParameter.getInstance().getConstantCallTimeoutMs());
404406
long vmStartInUs = System.nanoTime() / VMConstant.ONE_THOUSAND;
405407
long vmShouldEndInUs = vmStartInUs + thisTxCPULimitInUs;
406408
ProgramInvoke programInvoke = ProgramInvokeFactory
@@ -514,7 +516,7 @@ private void call()
514516

515517
long thisTxCPULimitInUs = calculateCpuLimitInUs(isConstantCall,
516518
rootRepository.getDynamicPropertiesStore().getMaxCpuTimeOfOneTx(),
517-
getCpuLimitInUsRatio(), CommonParameter.getInstance().getConstantCallTimeoutMs());
519+
getCpuLimitInUsRatio(rootRepository.getDynamicPropertiesStore()), CommonParameter.getInstance().getConstantCallTimeoutMs());
518520
long vmStartInUs = System.nanoTime() / VMConstant.ONE_THOUSAND;
519521
long vmShouldEndInUs = vmStartInUs + thisTxCPULimitInUs;
520522
ProgramInvoke programInvoke = ProgramInvokeFactory
@@ -666,14 +668,14 @@ public void checkTokenValueAndId(long tokenValue, long tokenId) throws ContractV
666668
}
667669

668670

669-
private double getCpuLimitInUsRatio() {
671+
private double getCpuLimitInUsRatio(DynamicPropertiesStore dynamicPropertiesStore) {
670672

671673
double cpuLimitRatio;
672674

673675
if (ExecutorType.ET_NORMAL_TYPE == executorType) {
674676
// self witness generates block
675677
if (blockCap != null && blockCap.generatedByMyself
676-
&& !blockCap.hasWitnessSignature()) {
678+
&& !blockCap.hasWitnessSignature(dynamicPropertiesStore)) {
677679
cpuLimitRatio = 1.0;
678680
} else {
679681
// self witness or other witness or fullnode verifies block

actuator/src/main/java/org/tron/core/utils/TransactionUtil.java

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -222,27 +222,28 @@ public TransactionSignWeight getTransactionSignWeight(Transaction trx) {
222222
}
223223
}
224224
tswBuilder.setPermission(permission);
225-
if (trx.getSignatureCount() > 0 || trx.getPqAuthSigCount() > 0) {
226-
List<ByteString> approveList = new ArrayList<>();
227-
long currentWeight = 0L;
228-
if (trx.getSignatureCount() > 0) {
229-
currentWeight = TransactionCapsule.checkWeight(permission, trx.getSignatureList(),
230-
Sha256Hash.hash(CommonParameter.getInstance()
231-
.isECKeyCryptoEngine(), trx.getRawData().toByteArray()), approveList);
232-
}
233-
if (trx.getPqAuthSigCount() > 0) {
234-
java.util.Set<ByteString> signedAddresses = new java.util.HashSet<>(approveList);
235-
try {
236-
currentWeight = StrictMathWrapper.addExact(currentWeight,
237-
TransactionCapsule.validatePQSignature(trx, permission, signedAddresses,
238-
chainBaseManager.getDynamicPropertiesStore(), approveList));
239-
} catch (ArithmeticException e) {
240-
throw new PermissionException("weight overflow");
241-
}
225+
long currentWeight = 0L;
226+
List<ByteString> approveList = new ArrayList<>();
227+
if (trx.getSignatureCount() > 0 ) {
228+
currentWeight = TransactionCapsule.checkWeight(permission, trx.getSignatureList(),
229+
Sha256Hash.hash(CommonParameter.getInstance()
230+
.isECKeyCryptoEngine(), trx.getRawData().toByteArray()), approveList);
231+
}
232+
if (chainBaseManager.getDynamicPropertiesStore().isAnyPqSchemeAllowed()
233+
&& trx.getPqAuthSigCount() > 0) {
234+
try {
235+
long pqWeight = TransactionCapsule.validatePQSignatureGetWeight(trx, permission,
236+
chainBaseManager.getDynamicPropertiesStore(), approveList);
237+
// sum all signature weight
238+
currentWeight = StrictMathWrapper.addExact(currentWeight,pqWeight);
239+
} catch (ArithmeticException e) {
240+
throw new PermissionException("weight overflow");
242241
}
243-
tswBuilder.addAllApprovedList(approveList);
244-
tswBuilder.setCurrentWeight(currentWeight);
245242
}
243+
244+
tswBuilder.addAllApprovedList(approveList);
245+
tswBuilder.setCurrentWeight(currentWeight);
246+
246247
if (tswBuilder.getCurrentWeight() >= permission.getThreshold()) {
247248
resultBuilder.setCode(Result.response_code.ENOUGH_PERMISSION);
248249
} else {

chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.tron.common.crypto.SignInterface;
2727
import org.tron.common.crypto.SignUtils;
2828
import org.tron.common.crypto.pqc.PQSchemeRegistry;
29+
import org.tron.common.crypto.pqc.PqKeypair;
2930
import org.tron.core.config.Parameter.ChainConstant;
3031
import org.tron.core.exception.TronError;
3132
import org.tron.protos.Protocol.PQScheme;
@@ -37,27 +38,20 @@ public class LocalWitnesses {
3738
private List<String> privateKeys = Lists.newArrayList();
3839

3940
/**
40-
* Pre-derived PQ private keys in hex format, one per witness. The expected
41-
* byte length depends on {@link #pqScheme}: 1280 bytes (2560 hex chars) for
42-
* FN-DSA-512. Index-aligned with {@link #pqPublicKeys}.
41+
* Pre-derived PQ keypairs (private + public, hex), one per witness. The
42+
* expected byte lengths depend on {@link #pqScheme}: for FN-DSA-512 each
43+
* private key is 1280 bytes (2560 hex chars) and each public key is 896
44+
* bytes (1792 hex chars).
4345
*
4446
* <p>Configured directly (rather than derived from a seed on the node) so
4547
* the runtime path is not exposed to potential cross-platform floating-point
4648
* non-determinism in BC's Falcon keygen — operators generate the keypair
4749
* off-line and ship both halves to the node.
4850
*/
4951
@Getter
50-
private List<String> pqPrivateKeys = Lists.newArrayList();
52+
private List<PqKeypair> pqKeypairs = Lists.newArrayList();
5153

52-
/**
53-
* PQ public keys in hex format, one per witness. The expected byte length
54-
* depends on {@link #pqScheme}: 896 bytes (1792 hex chars) for FN-DSA-512.
55-
* Index-aligned with {@link #pqPrivateKeys}.
56-
*/
57-
@Getter
58-
private List<String> pqPublicKeys = Lists.newArrayList();
59-
60-
/** PQ signature scheme used by the configured {@link #pqPrivateKeys}. */
54+
/** PQ signature scheme used by the configured {@link #pqKeypairs}. */
6155
@Getter
6256
private PQScheme pqScheme = PQScheme.FN_DSA_512;
6357

@@ -134,32 +128,22 @@ public void addPrivateKeys(String privateKey) {
134128

135129
/**
136130
* Pre-derived PQ keypairs (priv + pub) used as signing keys under
137-
* {@link #pqScheme}. The two lists must be the same length and index-aligned;
138-
* each entry must be a hex string whose byte length matches the scheme's
139-
* required private/public key size. Callers must therefore set the scheme
140-
* via {@link #setPqScheme(PQScheme)} before calling this method when
141-
* targeting a non-default scheme.
131+
* {@link #pqScheme}. Each entry's private/public hex byte length must match
132+
* the scheme's required size. Callers must therefore set the scheme via
133+
* {@link #setPqScheme(PQScheme)} before calling this method when targeting a
134+
* non-default scheme.
142135
*/
143-
public void setPqKeypairs(final List<String> pqPrivateKeys,
144-
final List<String> pqPublicKeys) {
145-
int privCount = CollectionUtils.isEmpty(pqPrivateKeys) ? 0 : pqPrivateKeys.size();
146-
int pubCount = CollectionUtils.isEmpty(pqPublicKeys) ? 0 : pqPublicKeys.size();
147-
if (privCount == 0 && pubCount == 0) {
136+
public void setPqKeypairs(final List<PqKeypair> pqKeypairs) {
137+
if (CollectionUtils.isEmpty(pqKeypairs)) {
148138
return;
149139
}
150-
if (privCount != pubCount) {
151-
throw new TronError(String.format(
152-
"PQ keypair list size mismatch: priv=%d, pub=%d", privCount, pubCount),
153-
TronError.ErrCode.WITNESS_INIT);
154-
}
155140
int expectedPrivLen = PQSchemeRegistry.getPrivateKeyLength(pqScheme);
156141
int expectedPubLen = PQSchemeRegistry.getPublicKeyLength(pqScheme);
157-
for (int i = 0; i < privCount; i++) {
158-
validatePqKey(pqPrivateKeys.get(i), expectedPrivLen, "PQ private key");
159-
validatePqKey(pqPublicKeys.get(i), expectedPubLen, "PQ public key");
142+
for (PqKeypair kp : pqKeypairs) {
143+
validatePqKey(kp.getPrivateKey(), expectedPrivLen, "PQ private key");
144+
validatePqKey(kp.getPublicKey(), expectedPubLen, "PQ public key");
160145
}
161-
this.pqPrivateKeys = pqPrivateKeys;
162-
this.pqPublicKeys = pqPublicKeys;
146+
this.pqKeypairs = pqKeypairs;
163147
}
164148

165149
private static void validatePqKey(String key, int expectedLen, String label) {

chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java

Lines changed: 39 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@
4747
import org.tron.core.store.DynamicPropertiesStore;
4848
import org.tron.protos.Protocol.Block;
4949
import org.tron.protos.Protocol.BlockHeader;
50-
import org.tron.protos.Protocol.Key;
5150
import org.tron.protos.Protocol.PQScheme;
5251
import org.tron.protos.Protocol.PQAuthSig;
53-
import org.tron.protos.Protocol.Permission;
5452
import org.tron.protos.Protocol.Transaction;
5553

5654
@Slf4j(topic = "capsule")
@@ -203,41 +201,36 @@ private Sha256Hash getRawHash() {
203201
public boolean validateSignature(DynamicPropertiesStore dynamicPropertiesStore,
204202
AccountStore accountStore) throws ValidateSignatureException {
205203
BlockHeader header = block.getBlockHeader();
206-
boolean hasLegacy = !header.getWitnessSignature().isEmpty();
207-
boolean hasPq = header.hasPqAuthSig();
204+
byte[] witnessAccountAddress = header.getRawData().getWitnessAddress()
205+
.toByteArray();
208206

209-
if (hasLegacy && hasPq) {
210-
throw new ValidateSignatureException(
211-
"witness_signature and pq_auth_sig are mutually exclusive");
212-
}
213-
if (!hasLegacy && !hasPq) {
214-
throw new ValidateSignatureException("missing witness signature");
207+
byte[] witnessPermissionAddress;
208+
if (dynamicPropertiesStore.getAllowMultiSign() != 1) {
209+
witnessPermissionAddress = witnessAccountAddress;
210+
} else {
211+
witnessPermissionAddress = accountStore.get(witnessAccountAddress)
212+
.getWitnessPermissionAddress();
215213
}
216214

217-
byte[] witnessAccountAddress = header.getRawData().getWitnessAddress().toByteArray();
218-
if (hasPq) {
219-
return validatePQSignature(dynamicPropertiesStore, accountStore,
220-
witnessAccountAddress, header.getPqAuthSig());
215+
if (dynamicPropertiesStore.isAnyPqSchemeAllowed()) {
216+
boolean hasLegacy = !header.getWitnessSignature().isEmpty();
217+
boolean hasPq = header.hasPqAuthSig();
218+
if (hasLegacy && hasLegacy) {
219+
throw new ValidateSignatureException(
220+
"witness_signature and pq_auth_sig are mutually exclusive");
221+
}
222+
if (!hasLegacy && !hasPq) {
223+
throw new ValidateSignatureException("missing witness signature");
224+
}
225+
return validatePQSignature(dynamicPropertiesStore, accountStore, witnessPermissionAddress,
226+
header.getPqAuthSig());
221227
}
222-
return validateLegacySignature(dynamicPropertiesStore, accountStore, witnessAccountAddress);
223-
}
224228

225-
private boolean validateLegacySignature(DynamicPropertiesStore dynamicPropertiesStore,
226-
AccountStore accountStore, byte[] witnessAccountAddress)
227-
throws ValidateSignatureException {
228229
try {
229230
byte[] sigAddress = SignUtils.signatureToAddress(getRawHash().getBytes(),
230-
TransactionCapsule.getBase64FromByteString(
231-
block.getBlockHeader().getWitnessSignature()),
231+
TransactionCapsule.getBase64FromByteString(header.getWitnessSignature()),
232232
CommonParameter.getInstance().isECKeyCryptoEngine());
233-
if (dynamicPropertiesStore.getAllowMultiSign() != 1) {
234-
return Arrays.equals(sigAddress, witnessAccountAddress);
235-
}
236-
AccountCapsule witnessAccount = accountStore.get(witnessAccountAddress);
237-
if (witnessAccount == null) {
238-
throw new ValidateSignatureException("witness account does not exist");
239-
}
240-
byte[] witnessPermissionAddress = witnessAccount.getWitnessPermissionAddress();
233+
241234
return Arrays.equals(sigAddress, witnessPermissionAddress);
242235
} catch (SignatureException e) {
243236
throw new ValidateSignatureException(e.getMessage());
@@ -250,8 +243,11 @@ private boolean validateLegacySignature(DynamicPropertiesStore dynamicProperties
250243
* the witness account's Witness Permission keys[].
251244
*/
252245
private boolean validatePQSignature(DynamicPropertiesStore dynamicPropertiesStore,
253-
AccountStore accountStore, byte[] witnessAccountAddress, PQAuthSig pqAuthSig)
246+
AccountStore accountStore, byte[] witnessPermissionAddress, PQAuthSig pqAuthSig)
254247
throws ValidateSignatureException {
248+
/*
249+
Verify the PQ scheme is supported and proposal opened
250+
*/
255251
PQScheme scheme = pqAuthSig.getScheme();
256252
if (!PQSchemeRegistry.contains(scheme)) {
257253
throw new ValidateSignatureException(
@@ -262,38 +258,22 @@ private boolean validatePQSignature(DynamicPropertiesStore dynamicPropertiesStor
262258
"pq_auth_sig scheme " + scheme + " is not activated");
263259
}
264260

265-
AccountCapsule accountCapsule = accountStore.get(witnessAccountAddress);
266-
Permission witnessPermission = null;
267-
if (accountCapsule != null && accountCapsule.getInstance().hasWitnessPermission()) {
268-
witnessPermission = accountCapsule.getInstance().getWitnessPermission();
269-
}
270-
if (witnessPermission == null || witnessPermission.getKeysCount() == 0) {
271-
throw new ValidateSignatureException(
272-
"pq_auth_sig present but witness permission is not configured");
273-
}
274-
275261
byte[] publicKey = pqAuthSig.getPublicKey().toByteArray();
276262
if (publicKey.length != PQSchemeRegistry.getPublicKeyLength(scheme)) {
277263
throw new ValidateSignatureException(
278264
"pq_auth_sig public key length mismatch for scheme " + scheme);
279265
}
280-
byte[] signature = pqAuthSig.getSignature().toByteArray();
281-
if (!PQSchemeRegistry.isValidSignatureLength(scheme, signature.length)) {
282-
throw new ValidateSignatureException(
283-
"pq_auth_sig signature length mismatch for scheme " + scheme);
284-
}
285266

286267
byte[] derivedAddr = PQSchemeRegistry.computeAddress(scheme, publicKey);
287-
Key matched = null;
288-
for (Key k : witnessPermission.getKeysList()) {
289-
if (Arrays.equals(k.getAddress().toByteArray(), derivedAddr)) {
290-
matched = k;
291-
break;
292-
}
268+
if (!Arrays.equals(derivedAddr, witnessPermissionAddress)) {
269+
throw new ValidateSignatureException(
270+
"pq_auth_sig public key does not match witness permission address");
293271
}
294-
if (matched == null) {
272+
273+
byte[] signature = pqAuthSig.getSignature().toByteArray();
274+
if (!PQSchemeRegistry.isValidSignatureLength(scheme, signature.length)) {
295275
throw new ValidateSignatureException(
296-
"pq_auth_sig public key does not match any witness permission key");
276+
"pq_auth_sig signature length mismatch for scheme " + scheme);
297277
}
298278

299279
byte[] digest = getRawHash().getBytes();
@@ -419,10 +399,13 @@ public long getTimeStamp() {
419399
return this.block.getBlockHeader().getRawData().getTimestamp();
420400
}
421401

422-
public boolean hasWitnessSignature() {
402+
public boolean hasWitnessSignature(DynamicPropertiesStore dynamicPropertiesStore) {
423403
BlockHeader header = getInstance().getBlockHeader();
424-
return !header.getWitnessSignature().isEmpty()
425-
|| !header.getPqAuthSig().getSignature().isEmpty();
404+
boolean hasLegacySignature = !header.getWitnessSignature().isEmpty();
405+
if (!dynamicPropertiesStore.isAnyPqSchemeAllowed()) {
406+
return hasLegacySignature;
407+
}
408+
return hasLegacySignature || !header.getPqAuthSig().getSignature().isEmpty();
426409
}
427410

428411
@Override

chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import java.security.SignatureException;
3333
import java.util.ArrayList;
3434
import java.util.HashMap;
35+
import java.util.HashSet;
3536
import java.util.List;
37+
import java.util.Set;
3638
import java.util.concurrent.ExecutorService;
3739
import java.util.concurrent.Future;
3840
import java.util.concurrent.TimeUnit;
@@ -495,16 +497,13 @@ public static boolean validateSignature(Transaction transaction,
495497
// Hybrid weight: ECDSA signatures and PQ witnesses share one threshold
496498
// check. The two domains derive distinct addresses (Keccak vs SHA-256
497499
// tagged with 0x41), so a key entry contributes to at most one path.
498-
java.util.Set<ByteString> signedAddresses = new java.util.HashSet<>();
499500
List<ByteString> approveList = new ArrayList<>();
500501
long weight = checkWeight(permission, transaction.getSignatureList(), hash, approveList);
501-
signedAddresses.addAll(approveList);
502502

503-
if (transaction.getPqAuthSigCount() > 0) {
503+
if (dynamicPropertiesStore.isAnyPqSchemeAllowed() && transaction.getPqAuthSigCount() > 0) {
504504
try {
505505
weight = StrictMathWrapper.addExact(weight,
506-
validatePQSignature(transaction, permission, signedAddresses,
507-
dynamicPropertiesStore, approveList));
506+
validatePQSignatureGetWeight(transaction, permission, dynamicPropertiesStore, approveList));
508507
} catch (ArithmeticException e) {
509508
throw new PermissionException("weight overflow");
510509
}
@@ -723,13 +722,15 @@ void logSlowSigVerify(long startNs) {
723722
* part of {@code raw_data}.</li>
724723
* </ol>
725724
*/
726-
public static long validatePQSignature(Transaction transaction, Permission permission,
727-
java.util.Set<ByteString> signedAddresses,
725+
public static long validatePQSignatureGetWeight(Transaction transaction, Permission permission,
728726
DynamicPropertiesStore dynamicPropertiesStore,
729727
List<ByteString> approveList)
730728
throws PermissionException {
729+
731730
byte[] digest = computeRawHash(transaction).getBytes();
732731

732+
Set<ByteString> signedAddresses = new HashSet<>(approveList);
733+
733734
long weight = 0L;
734735
for (PQAuthSig witness : transaction.getPqAuthSigList()) {
735736
PQScheme scheme = witness.getScheme();

0 commit comments

Comments
 (0)