Skip to content

Commit 2e216bb

Browse files
authored
Merge pull request #42 from Little-Peony/feature/pqc-signature-v4.8.2-build1
2 parents 42c4b53 + 069aca3 commit 2e216bb

8 files changed

Lines changed: 72 additions & 32 deletions

File tree

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.tron.common.utils;
1717

1818
import com.google.common.collect.Lists;
19+
import java.util.Arrays;
1920
import java.util.List;
2021
import lombok.Getter;
2122
import lombok.Setter;
@@ -37,6 +38,10 @@ public class LocalWitnesses {
3738
@Getter
3839
private List<String> privateKeys = Lists.newArrayList();
3940

41+
/**
42+
* The ECDSA SR account address. Derived from the local ECDSA witness key and
43+
* used by the legacy (secp256k1) block-production / signing path.
44+
*/
4045
@Setter
4146
@Getter
4247
private byte[] witnessAccountAddress;
@@ -115,6 +120,21 @@ public void initPqWitnessAccountAddress(final byte[] explicitAccountAddress) {
115120
}
116121
}
117122

123+
/**
124+
* One account address should authorise its witness permission to only one key
125+
* (either ECDSA or PQ). Throws if the same address is configured for both.
126+
*/
127+
public void checkWitnessAddressConflict() {
128+
if (witnessAccountAddress != null && pqWitnessAccountAddress != null
129+
&& Arrays.equals(witnessAccountAddress, pqWitnessAccountAddress)) {
130+
throw new TronError(
131+
String.format("Witness account address %s is authorised to both an ECDSA and a PQ key; "
132+
+ "one account address should authorise witness permission to only one key.",
133+
ByteArray.toHexString(witnessAccountAddress)),
134+
TronError.ErrCode.WITNESS_INIT);
135+
}
136+
}
137+
118138
/**
119139
* Private key of ECKey.
120140
*/

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,8 @@ private boolean validatePQSignature(DynamicPropertiesStore dynamicPropertiesStor
263263
Verify the PQ scheme is supported and proposal opened
264264
*/
265265
PQScheme scheme = pqAuthSig.getScheme();
266-
if (!PQSchemeRegistry.contains(scheme)) {
267-
throw new ValidateSignatureException("pq_auth_sig scheme " + scheme + " is not registered");
268-
}
269266
if (!dynamicPropertiesStore.isPqSchemeAllowed(scheme)) {
270-
throw new ValidateSignatureException("pq_auth_sig scheme " + scheme + " is not activated");
267+
throw new ValidateSignatureException("pq_auth_sig scheme " + scheme + " is not allowed");
271268
}
272269

273270
byte[] publicKey = pqAuthSig.getPublicKey().toByteArray();

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -747,11 +747,8 @@ public static long validatePQSignatureGetWeight(Transaction transaction, Permiss
747747
long weight = 0L;
748748
for (PQAuthSig witness : transaction.getPqAuthSigList()) {
749749
PQScheme scheme = witness.getScheme();
750-
if (!PQSchemeRegistry.contains(scheme)) {
751-
throw new PermissionException("unsupported pq scheme: " + scheme);
752-
}
753750
if (!dynamicPropertiesStore.isPqSchemeAllowed(scheme)) {
754-
throw new PermissionException(scheme + " is not activated");
751+
throw new PermissionException(scheme + " is not allowed");
755752
}
756753
byte[] pk = witness.getPublicKey().toByteArray();
757754
byte[] sig = witness.getSignature().toByteArray();

‎framework/src/main/java/org/tron/core/config/args/Args.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,8 @@ private static void initLocalWitnesses(Config config, CLIParameter cmd) {
953953
merged.initWitnessAccountAddress(ecdsaWitnesses.getWitnessAccountAddress(),
954954
PARAMETER.isECKeyCryptoEngine());
955955
merged.initPqWitnessAccountAddress(pqWitnesses.getPqWitnessAccountAddress());
956+
// one account address should authorise witness permission to only one key
957+
merged.checkWitnessAddressConflict();
956958
localWitnesses = merged;
957959
} else if (ecdsaWitnesses != null) {
958960
localWitnesses = ecdsaWitnesses;

‎framework/src/main/java/org/tron/core/db/Manager.java‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,18 +1821,6 @@ public BlockCapsule generateBlock(Miner miner, long blockTime, long timeout) {
18211821
private void signBlockCapsuleWithPQ(BlockCapsule blockCapsule, Miner miner) {
18221822
Param.Miner.PQMiner pq = miner.getPq();
18231823
PQScheme scheme = pq.getScheme();
1824-
if (scheme == null || !PQSchemeRegistry.contains(scheme)) {
1825-
throw new IllegalStateException(
1826-
"PQ miner " + Hex.toHexString(pq.getWitnessAddress().toByteArray())
1827-
+ " has scheme " + scheme
1828-
+ " which is not registered in PQSchemeRegistry");
1829-
}
1830-
if (!chainBaseManager.getDynamicPropertiesStore().isPqSchemeAllowed(scheme)) {
1831-
throw new IllegalStateException(
1832-
"PQ miner " + Hex.toHexString(pq.getWitnessAddress().toByteArray())
1833-
+ " has scheme " + scheme
1834-
+ " but it is not allowed by dynamic properties");
1835-
}
18361824
byte[] pqPrivateKey = pq.getPrivateKey();
18371825
byte[] pqPublicKey = pq.getPublicKey();
18381826
if (pqPrivateKey == null || pqPublicKey == null) {

‎framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) {
125125

126126
peer.setHelloMessageReceive(msg);
127127

128+
// Wall-clock from libp2p channel creation to TRON handshake completion (this hello processed).
129+
// Both stamps come from this node's clock, so it is skew-free; not a pure network RTT, but a
130+
// useful latency reference — especially when the HelloMessage is large (e.g. carrying a PQ
131+
// signature), where transfer time grows with message size.
128132
long latencyMs = System.currentTimeMillis() - peer.getChannel().getStartTime();
129133
peer.getChannel().updateAvgLatency(latencyMs);
130134
// Sample only the SR<->FF handshake path:

‎framework/src/main/java/org/tron/core/net/service/relay/RelayService.java‎

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class RelayService {
7474

7575
private final List<InetSocketAddress> fastForwardNodes = parameter.getFastForwardNodes();
7676

77-
private final int keySize = Args.getLocalWitnesses().getPrivateKeys().size();
77+
private final int ecdsaKeySize = Args.getLocalWitnesses().getPrivateKeys().size();
7878

7979
private final int pqKeySize = Args.getLocalWitnesses().getPqKeypairs().size();
8080

@@ -97,10 +97,11 @@ public void init() {
9797
backupManager = ctx.getBean(BackupManager.class);
9898

9999
logger.info(
100-
"Fast forward config, isWitness: {}, keySize: {}, pqKeySize: {}, fastForwardNodes: {}",
101-
parameter.isWitness(), keySize, pqKeySize, fastForwardNodes.size());
100+
"Fast forward config, isWitness: {}, ecdsaKeySize: {}, pqKeySize: {}, fastForwardNodes: {}",
101+
parameter.isWitness(), ecdsaKeySize, pqKeySize, fastForwardNodes.size());
102102

103-
if (!parameter.isWitness() || (keySize == 0 && pqKeySize == 0) || fastForwardNodes.isEmpty()) {
103+
if (!parameter.isWitness() || (ecdsaKeySize == 0 && pqKeySize == 0) ||
104+
fastForwardNodes.isEmpty()) {
104105
return;
105106
}
106107

@@ -144,7 +145,7 @@ public void fillHelloMessage(HelloMessage message, Channel channel) {
144145
// is currently in the active schedule — otherwise the receiver
145146
// rejects on the "not a schedule witness" check in checkHelloMessage.
146147
List<ByteString> active = witnessScheduleStore.getActiveWitnesses();
147-
boolean useEcdsa = keySize > 0 && ecdsaWitnessAddress != null
148+
boolean useEcdsa = ecdsaKeySize > 0 && ecdsaWitnessAddress != null
148149
&& active.contains(ecdsaWitnessAddress);
149150
ByteString announceAddress = useEcdsa ? ecdsaWitnessAddress : pqWitnessAddress;
150151
Protocol.HelloMessage.Builder builder = message.getHelloMessage().toBuilder()
@@ -258,13 +259,8 @@ private boolean verifyEcdsaSignature(byte[] digest, ByteString signature,
258259
private boolean verifyPqAuthSig(byte[] digest, PQAuthSig pqAuthSig,
259260
ByteString witnessAddr, InetAddress remoteAddress) {
260261
PQScheme scheme = pqAuthSig.getScheme();
261-
if (!PQSchemeRegistry.contains(scheme)) {
262-
logger.warn("HelloMessage from {}, pq_auth_sig scheme {} is not registered.", remoteAddress,
263-
scheme);
264-
return false;
265-
}
266262
if (!manager.getDynamicPropertiesStore().isPqSchemeAllowed(scheme)) {
267-
logger.warn("HelloMessage from {}, pq_auth_sig scheme {} is not activated on chain.",
263+
logger.warn("HelloMessage from {}, pq_auth_sig scheme {} is not allowed on chain.",
268264
remoteAddress, scheme);
269265
return false;
270266
}
@@ -320,7 +316,7 @@ private long getPeerCountByAddress(ByteString address) {
320316

321317
private boolean isActiveWitness() {
322318
return parameter.isWitness()
323-
&& (keySize > 0 || pqKeySize > 0)
319+
&& (ecdsaKeySize > 0 || pqKeySize > 0)
324320
&& !fastForwardNodes.isEmpty()
325321
&& isAnyLocalWitnessActive()
326322
&& backupManager.getStatus().equals(BackupStatusEnum.MASTER);

‎framework/src/test/java/org/tron/common/utils/LocalWitnessesTest.java‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,40 @@ public void blankKeyRejected() {
135135
assertEquals(TronError.ErrCode.WITNESS_INIT, err.getErrCode());
136136
assertTrue(err.getMessage().contains("PQ private key"));
137137
}
138+
139+
@Test
140+
public void sameWitnessAddressForEcdsaAndPqRejected() {
141+
LocalWitnesses lw = new LocalWitnesses();
142+
// Distinct array instances with identical content: must be compared by value
143+
// (Arrays.equals), not by reference.
144+
lw.setWitnessAccountAddress(new byte[] {0x41, 1, 2, 3});
145+
lw.setPqWitnessAccountAddress(new byte[] {0x41, 1, 2, 3});
146+
TronError err = assertThrows(TronError.class, lw::checkWitnessAddressConflict);
147+
assertEquals(TronError.ErrCode.WITNESS_INIT, err.getErrCode());
148+
assertTrue(err.getMessage().contains("only one key"));
149+
}
150+
151+
@Test
152+
public void distinctWitnessAddressesAccepted() {
153+
LocalWitnesses lw = new LocalWitnesses();
154+
lw.setWitnessAccountAddress(new byte[] {0x41, 1, 2, 3});
155+
lw.setPqWitnessAccountAddress(new byte[] {0x41, 4, 5, 6});
156+
lw.checkWitnessAddressConflict(); // no throw
157+
}
158+
159+
@Test
160+
public void onlyOneWitnessAddressSetAccepted() {
161+
LocalWitnesses ecdsaOnly = new LocalWitnesses();
162+
ecdsaOnly.setWitnessAccountAddress(new byte[] {0x41, 1, 2, 3});
163+
ecdsaOnly.checkWitnessAddressConflict(); // pq address null -> no throw
164+
165+
LocalWitnesses pqOnly = new LocalWitnesses();
166+
pqOnly.setPqWitnessAccountAddress(new byte[] {0x41, 1, 2, 3});
167+
pqOnly.checkWitnessAddressConflict(); // ecdsa address null -> no throw
168+
}
169+
170+
@Test
171+
public void noWitnessAddressSetAccepted() {
172+
new LocalWitnesses().checkWitnessAddressConflict(); // both null -> no throw
173+
}
138174
}

0 commit comments

Comments
 (0)