Skip to content

Commit f92a6a5

Browse files
authored
fix(consensus,framework,actuator): use Locale.ROOT for case-insensitive (#6698)
* fix(consensus,framework,actuator): use Locale.ROOT for case-insensitive String.toLowerCase()/toUpperCase() without an explicit Locale uses Locale.getDefault(), which on Turkish (tr) or Azerbaijani (az) systems folds 'I' to dotless-ı (U+0131) instead of 'i' (U+0069). Changes: - Fix all toLowerCase()/toUpperCase() calls to use Locale.ROOT - Enable the ErrorProne StringCaseLocaleUsage checker at ERROR level to prevent future regressions at compile time - Add one-time data migration (MigrateTurkishKeyHelper) to normalize all Turkish legacy keys (ı → i) at startup. * refactor: consolidate Turkish key migration loops and use ASTHelpers * test(toolkit): fix toLowerCase * fix: use root locale in gradle case folding * fix: gradle verification-metadata.xml
1 parent 279d19b commit f92a6a5

35 files changed

Lines changed: 624 additions & 51 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.ArrayList;
2323
import java.util.Iterator;
2424
import java.util.List;
25+
import java.util.Locale;
2526
import java.util.Objects;
2627
import lombok.extern.slf4j.Slf4j;
2728
import org.tron.common.math.StrictMathWrapper;
@@ -166,7 +167,7 @@ public boolean validate() throws ContractValidateException {
166167
}
167168

168169
if (dynamicStore.getAllowSameTokenName() != 0) {
169-
String name = assetIssueContract.getName().toStringUtf8().toLowerCase();
170+
String name = assetIssueContract.getName().toStringUtf8().toLowerCase(Locale.ROOT);
170171
if (("trx").equals(name)) {
171172
throw new ContractValidateException("assetName can't be trx");
172173
}

build.gradle

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import org.gradle.nativeplatform.platform.internal.Architectures
22
import org.gradle.internal.os.OperatingSystem
3+
4+
plugins {
5+
id 'net.ltgt.errorprone' version '5.0.0' apply false
6+
}
7+
38
allprojects {
49
version = "1.0.0"
510
apply plugin: "java-library"
611
ext {
712
springVersion = "5.3.39"
13+
errorproneVersion = "2.42.0"
814
}
915
}
10-
def arch = System.getProperty("os.arch").toLowerCase()
16+
def arch = System.getProperty("os.arch").toLowerCase(Locale.ROOT)
1117
def javaVersion = JavaVersion.current()
1218
def isArm64 = Architectures.AARCH64.isAlias(arch)
1319
def archSource = isArm64 ? "arm" : "x86"
@@ -108,6 +114,26 @@ subprojects {
108114
testImplementation "org.mockito:mockito-core:4.11.0"
109115
testImplementation "org.mockito:mockito-inline:4.11.0"
110116
}
117+
if (project.name != 'protocol' && project.name != 'errorprone'
118+
&& javaVersion.isJava11Compatible()) {
119+
apply plugin: 'net.ltgt.errorprone'
120+
dependencies {
121+
errorprone "com.google.errorprone:error_prone_core:${errorproneVersion}"
122+
errorprone rootProject.project(':errorprone')
123+
}
124+
tasks.withType(JavaCompile).configureEach {
125+
options.errorprone {
126+
enabled = true
127+
disableWarningsInGeneratedCode = true
128+
disableAllChecks = true
129+
excludedPaths = '.*/generated/.*'
130+
errorproneArgs.addAll([
131+
'-Xep:StringCaseLocaleUsage:ERROR',
132+
'-Xep:StringCaseLocaleUsageMethodRef:ERROR',
133+
])
134+
}
135+
}
136+
}
111137

112138
task sourcesJar(type: Jar, dependsOn: classes) {
113139
classifier = "sources"

chainbase/src/main/java/org/tron/core/db/TronDatabase.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.protobuf.InvalidProtocolBufferException;
44
import java.nio.file.Paths;
55
import java.util.Iterator;
6+
import java.util.Locale;
67
import java.util.Map;
78
import java.util.Map.Entry;
89
import javax.annotation.PostConstruct;
@@ -37,10 +38,10 @@ protected TronDatabase(String dbName) {
3738
this.dbName = dbName;
3839

3940
if ("LEVELDB".equals(CommonParameter.getInstance().getStorage()
40-
.getDbEngine().toUpperCase())) {
41+
.getDbEngine().toUpperCase(Locale.ROOT))) {
4142
dbSource = new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName), dbName);
4243
} else if ("ROCKSDB".equals(CommonParameter.getInstance()
43-
.getStorage().getDbEngine().toUpperCase())) {
44+
.getStorage().getDbEngine().toUpperCase(Locale.ROOT))) {
4445
String parentName = Paths.get(StorageUtils.getOutputDirectoryByDbName(dbName),
4546
CommonParameter.getInstance().getStorage().getDbDirectory()).toString();
4647
dbSource = new RocksDbDataSourceImpl(parentName, dbName);

chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.lang.reflect.InvocationTargetException;
1010
import java.nio.file.Paths;
1111
import java.util.Iterator;
12+
import java.util.Locale;
1213
import java.util.Map;
1314
import java.util.Objects;
1415
import java.util.stream.Collectors;
@@ -38,7 +39,7 @@
3839
@Slf4j(topic = "DB")
3940
public abstract class TronStoreWithRevoking<T extends ProtoCapsule> implements ITronChainBase<T> {
4041

41-
@Getter // only for unit test
42+
@Getter
4243
protected IRevokingDB revokingDB;
4344
private TypeToken<T> token = new TypeToken<T>(getClass()) {
4445
};
@@ -54,10 +55,10 @@ public abstract class TronStoreWithRevoking<T extends ProtoCapsule> implements I
5455

5556
protected TronStoreWithRevoking(String dbName) {
5657
String dbEngine = CommonParameter.getInstance().getStorage().getDbEngine();
57-
if ("LEVELDB".equals(dbEngine.toUpperCase())) {
58+
if ("LEVELDB".equals(dbEngine.toUpperCase(Locale.ROOT))) {
5859
this.db = new LevelDB(
5960
new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName), dbName));
60-
} else if ("ROCKSDB".equals(dbEngine.toUpperCase())) {
61+
} else if ("ROCKSDB".equals(dbEngine.toUpperCase(Locale.ROOT))) {
6162
String parentPath = Paths
6263
.get(StorageUtils.getOutputDirectoryByDbName(dbName), CommonParameter
6364
.getInstance().getStorage().getDbDirectory()).toString();

chainbase/src/main/java/org/tron/core/db2/common/TxCacheDB.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.nio.file.Paths;
2121
import java.nio.file.StandardOpenOption;
2222
import java.util.Iterator;
23+
import java.util.Locale;
2324
import java.util.Map;
2425
import java.util.Map.Entry;
2526
import java.util.Objects;
@@ -102,10 +103,10 @@ public TxCacheDB(String name, RecentTransactionStore recentTransactionStore,
102103
this.recentTransactionStore = recentTransactionStore;
103104
this.dynamicPropertiesStore = dynamicPropertiesStore;
104105
String dbEngine = CommonParameter.getInstance().getStorage().getDbEngine();
105-
if ("LEVELDB".equals(dbEngine.toUpperCase())) {
106+
if ("LEVELDB".equals(dbEngine.toUpperCase(Locale.ROOT))) {
106107
this.persistentStore = new LevelDB(
107108
new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), name));
108-
} else if ("ROCKSDB".equals(dbEngine.toUpperCase())) {
109+
} else if ("ROCKSDB".equals(dbEngine.toUpperCase(Locale.ROOT))) {
109110
String parentPath = Paths
110111
.get(StorageUtils.getOutputDirectoryByDbName(name), CommonParameter
111112
.getInstance().getStorage().getDbDirectory()).toString();

chainbase/src/main/java/org/tron/core/store/AccountIdIndexStore.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.tron.core.store;
22

33
import com.google.protobuf.ByteString;
4+
import java.util.Locale;
45
import java.util.Objects;
56
import org.apache.commons.lang3.ArrayUtils;
67
import org.springframework.beans.factory.annotation.Autowired;
@@ -21,7 +22,8 @@ public AccountIdIndexStore(@Value("accountid-index") String dbName) {
2122

2223
private static byte[] getLowerCaseAccountId(byte[] bsAccountId) {
2324
return ByteString
24-
.copyFromUtf8(ByteString.copyFrom(bsAccountId).toStringUtf8().toLowerCase()).toByteArray();
25+
.copyFromUtf8(ByteString.copyFrom(bsAccountId).toStringUtf8().toLowerCase(Locale.ROOT))
26+
.toByteArray();
2527
}
2628

2729
public void put(AccountCapsule accountCapsule) {
@@ -54,4 +56,4 @@ public boolean has(byte[] key) {
5456
return !ArrayUtils.isEmpty(value);
5557
}
5658

57-
}
59+
}

chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking<BytesCapsule>
246246
private static final byte[] ALLOW_HARDEN_EXCHANGE_CALCULATION =
247247
"ALLOW_HARDEN_EXCHANGE_CALCULATION".getBytes();
248248

249+
private static final byte[] TURKISH_KEY_MIGRATION_DONE =
250+
"TURKISH_KEY_MIGRATION_DONE".getBytes();
251+
249252
@Autowired
250253
private DynamicPropertiesStore(@Value("properties") String dbName) {
251254
super(dbName);
@@ -3029,6 +3032,18 @@ public boolean allowHardenExchangeCalculation() {
30293032
return getAllowHardenExchangeCalculation() == 1L;
30303033
}
30313034

3035+
public void saveTurkishKeyMigrationDone(long num) {
3036+
this.put(TURKISH_KEY_MIGRATION_DONE,
3037+
new BytesCapsule(ByteArray.fromLong(num)));
3038+
}
3039+
3040+
public long getTurkishKeyMigrationDone() {
3041+
return Optional.ofNullable(getUnchecked(TURKISH_KEY_MIGRATION_DONE))
3042+
.map(BytesCapsule::getData)
3043+
.map(ByteArray::toLong)
3044+
.orElse(0L);
3045+
}
3046+
30323047
private static class DynamicResourceProperties {
30333048

30343049
private static final byte[] ONE_DAY_NET_LIMIT = "ONE_DAY_NET_LIMIT".getBytes();

common/src/main/java/org/tron/common/args/Account.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import com.google.protobuf.ByteString;
1919
import java.io.Serializable;
20+
import java.util.Locale;
2021
import lombok.Getter;
2122
import org.apache.commons.lang3.StringUtils;
2223
import org.tron.common.utils.ByteArray;
@@ -120,7 +121,7 @@ public boolean isAccountType(final String accountType) {
120121
return false;
121122
}
122123

123-
switch (accountType.toUpperCase()) {
124+
switch (accountType.toUpperCase(Locale.ROOT)) {
124125
case ACCOUNT_TYPE_NORMAL:
125126
case ACCOUNT_TYPE_ASSETISSUE:
126127
case ACCOUNT_TYPE_CONTRACT:
@@ -138,7 +139,7 @@ public AccountType getAccountTypeByString(final String accountType) {
138139
throw new IllegalArgumentException("Account type error: Not a Normal/AssetIssue/Contract");
139140
}
140141

141-
switch (accountType.toUpperCase()) {
142+
switch (accountType.toUpperCase(Locale.ROOT)) {
142143
case ACCOUNT_TYPE_NORMAL:
143144
return AccountType.Normal;
144145
case ACCOUNT_TYPE_ASSETISSUE:

common/src/main/java/org/tron/common/runtime/vm/DataWord.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.fasterxml.jackson.annotation.JsonValue;
2525
import java.math.BigInteger;
2626
import java.nio.ByteBuffer;
27+
import java.util.Locale;
2728
import org.bouncycastle.util.Arrays;
2829
import org.bouncycastle.util.encoders.Hex;
2930
import org.tron.common.utils.ByteArray;
@@ -121,7 +122,7 @@ public static boolean isZero(byte[] data) {
121122

122123
public static String shortHex(byte[] data) {
123124
byte[] bytes = ByteUtil.stripLeadingZeroes(data);
124-
String hexValue = Hex.toHexString(bytes).toUpperCase();
125+
String hexValue = Hex.toHexString(bytes).toUpperCase(Locale.ROOT);
125126
return "0x" + hexValue.replaceFirst("^0+(?!$)", "");
126127
}
127128

@@ -451,7 +452,7 @@ public String toPrefixString() {
451452
}
452453

453454
public String shortHex() {
454-
String hexValue = Hex.toHexString(getNoLeadZeroesData()).toUpperCase();
455+
String hexValue = Hex.toHexString(getNoLeadZeroesData()).toUpperCase(Locale.ROOT);
455456
return "0x" + hexValue.replaceFirst("^0+(?!$)", "");
456457
}
457458

errorprone/build.gradle

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
if (!JavaVersion.current().isJava11Compatible()) {
2+
// ErrorProne core requires JDK 11+; skip this module on JDK 8
3+
tasks.withType(JavaCompile).configureEach { enabled = false }
4+
tasks.withType(Jar).configureEach { enabled = false }
5+
} else {
6+
dependencies {
7+
compileOnly "com.google.errorprone:error_prone_annotations:${errorproneVersion}"
8+
compileOnly "com.google.errorprone:error_prone_check_api:${errorproneVersion}"
9+
compileOnly "com.google.errorprone:error_prone_core:${errorproneVersion}"
10+
compileOnly "com.google.auto.service:auto-service:1.1.1"
11+
annotationProcessor "com.google.auto.service:auto-service:1.1.1"
12+
}
13+
}

0 commit comments

Comments
 (0)