Skip to content

Commit 897d4e8

Browse files
halibobo1205claude
andcommitted
refactor: remove Turkish fallback lookup, rely solely on migration
The dual Turkish fallback (direct + normalized probes) in AccountIdIndexStore is no longer needed because: 1. MigrateTurkishKeyHelper normalizes all legacy keys at startup before any queries are served 2. New writes always use Locale.ROOT, so no new Turkish keys are created 3. If migration is interrupted, the DynamicPropertiesStore flag ensures it reruns on next startup This simplifies get()/has() to single-lookup operations and removes ~100 lines of fallback logic (DOTLESS_I, TURKISH locale, direct/ normalized key methods, lookupWithFallback). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1a0709a commit 897d4e8

2 files changed

Lines changed: 61 additions & 270 deletions

File tree

Lines changed: 16 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package org.tron.core.store;
22

33
import com.google.protobuf.ByteString;
4-
import java.nio.charset.StandardCharsets;
5-
import java.util.Arrays;
64
import java.util.Locale;
75
import java.util.Objects;
8-
import lombok.extern.slf4j.Slf4j;
96
import org.apache.commons.lang3.ArrayUtils;
107
import org.springframework.beans.factory.annotation.Autowired;
118
import org.springframework.beans.factory.annotation.Value;
@@ -14,129 +11,49 @@
1411
import org.tron.core.capsule.BytesCapsule;
1512
import org.tron.core.db.TronStoreWithRevoking;
1613

17-
@Slf4j(topic = "DB")
14+
//todo : need Compatibility test
1815
@Component
1916
public class AccountIdIndexStore extends TronStoreWithRevoking<BytesCapsule> {
2017

21-
/**
22-
* Turkish dotless-ı (U+0131). On Turkish/Azerbaijani locales,
23-
* {@code 'I'.toLowerCase()} produces this instead of ASCII {@code 'i'}.
24-
* This is the ONLY ASCII letter that differs between ROOT and Turkish
25-
* {@code toLowerCase()} — verified by testTurkishLowerCaseDiffForAllAsciiLetters.
26-
*/
27-
private static final char DOTLESS_I = '\u0131'; // ı Turkish dotless-i
28-
private static final Locale TURKISH = Locale.forLanguageTag("tr");
29-
3018
@Autowired
3119
public AccountIdIndexStore(@Value("accountid-index") String dbName) {
3220
super(dbName);
3321
}
3422

35-
public static byte[] getLowerCaseAccountId(byte[] accountId) {
23+
public static byte[] getLowerCaseAccountId(byte[] bsAccountId) {
3624
return ByteString
37-
.copyFromUtf8(ByteString.copyFrom(accountId).toStringUtf8().toLowerCase(Locale.ROOT))
25+
.copyFromUtf8(ByteString.copyFrom(bsAccountId).toStringUtf8().toLowerCase(Locale.ROOT))
3826
.toByteArray();
3927
}
4028

41-
/**
42-
* Turkish direct key: {@code toLowerCase(TURKISH)} on the original input.
43-
* Reproduces the exact key a Turkish node stored for the same-case input.
44-
* Handles lookups where query case matches the original accountId case.
45-
*
46-
* <p>Example: input "AiBI" → "aibı" (lowercase 'i' stays, uppercase 'I' → 'ı').
47-
*/
48-
@SuppressWarnings("StringCaseLocaleUsage")
49-
private static byte[] getTurkishDirectKey(byte[] accountId) {
50-
String str = ByteString.copyFrom(accountId).toStringUtf8();
51-
return ByteString.copyFromUtf8(str.toLowerCase(TURKISH)).toByteArray();
52-
}
53-
54-
/**
55-
* Turkish normalized key: ROOT key with all {@code 'i'} replaced by {@code 'ı'}.
56-
* Handles cross-case lookups (e.g., lowercase query for an accountId that
57-
* was originally uppercase on a Turkish node).
58-
*
59-
* <p>Example: rootKey "aibi" → "aıbı".
60-
*
61-
* @param rootKey the already-computed ROOT-lowered key
62-
* @return the normalized key, or {@code rootKey} itself if no 'i' is present
63-
*/
64-
private static byte[] getTurkishNormalizedKey(byte[] rootKey) {
65-
String str = new String(rootKey, StandardCharsets.UTF_8);
66-
if (str.indexOf('i') < 0) {
67-
return rootKey;
68-
}
69-
return str.replace('i', DOTLESS_I).getBytes(StandardCharsets.UTF_8);
70-
}
71-
7229
public void put(AccountCapsule accountCapsule) {
7330
byte[] lowerCaseAccountId = getLowerCaseAccountId(accountCapsule.getAccountId().toByteArray());
7431
super.put(lowerCaseAccountId, new BytesCapsule(accountCapsule.getAddress().toByteArray()));
7532
}
7633

77-
public byte[] get(ByteString accountId) {
78-
BytesCapsule bytesCapsule = get(accountId.toByteArray());
34+
public byte[] get(ByteString name) {
35+
BytesCapsule bytesCapsule = get(name.toByteArray());
7936
if (Objects.nonNull(bytesCapsule)) {
8037
return bytesCapsule.getData();
8138
}
8239
return null;
8340
}
8441

85-
/**
86-
* Look up by the standard (Locale.ROOT) accountId first; on miss, fall back to
87-
* Turkish legacy keys. The fallback covers nodes that previously ran under
88-
* tr/az locale and wrote keys containing dotless-ı (U+0131).
89-
*
90-
* <p>Two fallback probes are used:
91-
* <ol>
92-
* <li><b>Direct</b>: {@code toLowerCase(TURKISH)} — matches when query
93-
* case equals original accountId case (handles mixed 'i'/'I').</li>
94-
* <li><b>Normalized</b>: ROOT accountId with all 'i' → 'ı' — matches when
95-
* query case differs from original (e.g., all-lowercase query for
96-
* an all-uppercase stored accountId).</li>
97-
* </ol>
98-
*
99-
* <p>Each probe is skipped when it produces the same accountId as the ROOT accountId
100-
* (i.e., input contains no 'I' or 'i'). AccountIdIndexStore is a small
101-
* dataset, so the overhead of up to two extra lookups is negligible.
102-
*/
10342
@Override
104-
public BytesCapsule get(byte[] accountId) {
105-
byte[] value = lookupWithFallback(accountId);
106-
return ArrayUtils.isEmpty(value) ? null : new BytesCapsule(value);
43+
public BytesCapsule get(byte[] key) {
44+
byte[] lowerCaseKey = getLowerCaseAccountId(key);
45+
byte[] value = revokingDB.getUnchecked(lowerCaseKey);
46+
if (ArrayUtils.isEmpty(value)) {
47+
return null;
48+
}
49+
return new BytesCapsule(value);
10750
}
10851

109-
/** See {@link #get(byte[])} for fallback strategy. */
11052
@Override
111-
public boolean has(byte[] accountId) {
112-
return !ArrayUtils.isEmpty(lookupWithFallback(accountId));
113-
}
114-
115-
private byte[] lookupWithFallback(byte[] accountId) {
116-
byte[] rootLocaleKey = getLowerCaseAccountId(accountId);
117-
byte[] value = revokingDB.getUnchecked(rootLocaleKey);
118-
// Fallback 1: direct Turkish accountId (same-case match).
119-
// Needed for accountIds containing BOTH 'i' and 'I' (e.g., "AiBI").
120-
// A Turkish node stored toLowerCase(TURKISH) = "aibı" — only the
121-
// direct probe reproduces this mixed 'i'/'ı' key correctly.
122-
// The normalized probe (Fallback 2) would produce "aıbı" instead.
123-
if (ArrayUtils.isEmpty(value)) {
124-
byte[] directKey = getTurkishDirectKey(accountId);
125-
if (!Arrays.equals(rootLocaleKey, directKey)) {
126-
value = revokingDB.getUnchecked(directKey);
127-
}
128-
}
129-
// Fallback 2: normalized Turkish accountId (cross-case match).
130-
// Handles queries where case differs from the original accountId,
131-
// e.g., lowercase "aibi" looking up an entry stored as "AIBI"
132-
// on a Turkish node (stored key = "aıbı").
133-
if (ArrayUtils.isEmpty(value)) {
134-
byte[] normalizedKey = getTurkishNormalizedKey(rootLocaleKey);
135-
if (!Arrays.equals(rootLocaleKey, normalizedKey)) {
136-
value = revokingDB.getUnchecked(normalizedKey);
137-
}
138-
}
139-
return value;
53+
public boolean has(byte[] key) {
54+
byte[] lowerCaseKey = getLowerCaseAccountId(key);
55+
byte[] value = revokingDB.getUnchecked(lowerCaseKey);
56+
return !ArrayUtils.isEmpty(value);
14057
}
14158

14259
}

0 commit comments

Comments
 (0)