Skip to content

Commit bd763bc

Browse files
BarbatosBarbatos
authored andcommitted
fix(chainbase,net,plugins): defensive numeric and structural fixes
- TransactionTrace: add zero-check guard before newArea/newSize division in resetAccountUsage() and resetAccountUsageV2() to prevent ArithmeticException when window size is zero - ResilienceService: replace all new Random() with ThreadLocalRandom.current() to avoid unnecessary object allocation; remove Random field from WeightedRandom inner class - DbConvert: merge parallel List<byte[]> (keys/values) into single List<Map.Entry<byte[], byte[]>> to eliminate index-mismatch risk
1 parent 6a30147 commit bd763bc

4 files changed

Lines changed: 98 additions & 20 deletions

File tree

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ private void resetAccountUsage(AccountCapsule accountCap,
300300
- (mergedUsage * mergedSize - usage * size);
301301
// If area merging happened during suicide, use the current window size
302302
long newSize = mergedSize == currentSize ? size : currentSize;
303+
if (newSize == 0) {
304+
accountCap.setEnergyUsage(0);
305+
accountCap.setNewWindowSize(ENERGY, 0L);
306+
return;
307+
}
303308
// Calc new usage by fixed x-axes
304309
long newUsage = max(0, newArea / newSize, dynamicPropertiesStore.disableJavaLangMath());
305310
// Reset account usage and window size
@@ -317,6 +322,11 @@ private void resetAccountUsageV2(AccountCapsule accountCap,
317322
// If area merging happened during suicide, use the current window size
318323
long newSize = mergedSize == currentSize ? size : currentSize;
319324
long newSize2 = mergedSize == currentSize ? size2 : currentSize2;
325+
if (newSize == 0) {
326+
accountCap.setEnergyUsage(0);
327+
accountCap.setNewWindowSizeV2(ENERGY, 0L);
328+
return;
329+
}
320330
// Calc new usage by fixed x-axes
321331
long newUsage = max(0, newArea / newSize, dynamicPropertiesStore.disableJavaLangMath());
322332
// Reset account usage and window size

framework/src/main/java/org/tron/core/net/service/effective/ResilienceService.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import java.util.List;
99
import java.util.Map;
1010
import java.util.Optional;
11-
import java.util.Random;
1211
import java.util.concurrent.ScheduledExecutorService;
12+
import java.util.concurrent.ThreadLocalRandom;
1313
import java.util.concurrent.TimeUnit;
1414
import java.util.stream.Collectors;
1515
import lombok.extern.slf4j.Slf4j;
@@ -114,7 +114,7 @@ private void disconnectRandom() {
114114
.collect(Collectors.toList());
115115
}
116116
if (!peers.isEmpty()) {
117-
int index = new Random().nextInt(peers.size());
117+
int index = ThreadLocalRandom.current().nextInt(peers.size());
118118
disconnectFromPeer(peers.get(index), ReasonCode.RANDOM_ELIMINATION,
119119
DisconnectCause.RANDOM_ELIMINATION);
120120
}
@@ -236,19 +236,17 @@ private enum DisconnectCause {
236236
static class WeightedRandom {
237237

238238
private final Map<Object, Integer> weights;
239-
private final Random random;
240239

241240
public WeightedRandom(Map<Object, Integer> weights) {
242241
this.weights = weights;
243-
this.random = new Random();
244242
}
245243

246244
public Object next() {
247245
int totalWeight = 0;
248246
for (int weight : weights.values()) {
249247
totalWeight += weight;
250248
}
251-
int randomNum = random.nextInt(totalWeight);
249+
int randomNum = ThreadLocalRandom.current().nextInt(totalWeight);
252250
for (Object key : weights.keySet()) {
253251
randomNum -= weights.get(key);
254252
if (randomNum < 0) {

framework/src/test/java/org/tron/core/db/TransactionTraceTest.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.google.protobuf.Any;
1919
import com.google.protobuf.ByteString;
2020
import com.google.protobuf.InvalidProtocolBufferException;
21+
import java.lang.reflect.Method;
2122
import org.junit.Assert;
2223
import org.junit.Before;
2324
import org.junit.Test;
@@ -45,6 +46,7 @@
4546
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
4647
import org.tron.protos.Protocol.Transaction.raw;
4748
import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract;
49+
import org.tron.protos.contract.Common.ResourceCode;
4850
import org.tron.protos.contract.SmartContractOuterClass.SmartContract;
4951
import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract;
5052

@@ -446,4 +448,76 @@ public void testTriggerUseUsageInWindowSizeV2() throws VMIllegalException, Contr
446448
dbManager.getDynamicPropertiesStore().saveUnfreezeDelayDays(0);
447449
dbManager.getDynamicPropertiesStore().saveAllowCancelAllUnfreezeV2(0);
448450
}
451+
452+
@Test
453+
public void testResetAccountUsage_zeroNewSize() throws Exception {
454+
// Ensure V1 path (not V2)
455+
dbManager.getDynamicPropertiesStore().saveAllowCancelAllUnfreezeV2(0);
456+
457+
AccountCapsule account = new AccountCapsule(
458+
ByteString.copyFromUtf8("testZeroSize"),
459+
ByteString.copyFrom(ByteArray.fromInt(99)),
460+
AccountType.Normal,
461+
0L);
462+
// currentSize = 100, currentUsage = 500
463+
account.setNewWindowSize(ResourceCode.ENERGY, 100L);
464+
account.setEnergyUsage(500L);
465+
466+
// Build a minimal TransactionTrace to access the private method
467+
Transaction trxn = Transaction.newBuilder().setRawData(
468+
raw.newBuilder().addContract(
469+
Contract.newBuilder().setType(ContractType.TransferContract))).build();
470+
TransactionCapsule trxCap = new TransactionCapsule(trxn);
471+
TransactionTrace trace = new TransactionTrace(trxCap, StoreFactory.getInstance(),
472+
new RuntimeImpl());
473+
474+
// Use reflection to call private resetAccountUsage
475+
Method method = TransactionTrace.class.getDeclaredMethod("resetAccountUsage",
476+
AccountCapsule.class, long.class, long.class, long.class, long.class, long.class);
477+
method.setAccessible(true);
478+
479+
// mergedSize == currentSize (100) and size == 0 → newSize = 0
480+
method.invoke(trace, account, /*usage=*/0L, /*size=*/0L,
481+
/*mergedUsage=*/0L, /*mergedSize=*/100L, /*size2=*/0L);
482+
483+
// energyUsage should be reset to 0 by the newSize == 0 guard
484+
Assert.assertEquals(0L, account.getEnergyUsage());
485+
// windowSize internal value is 0, but getWindowSize() returns default 28800 when 0
486+
Assert.assertEquals(0L,
487+
account.getInstance().getAccountResource().getEnergyWindowSize());
488+
}
489+
490+
@Test
491+
public void testResetAccountUsageV2_zeroNewSize() throws Exception {
492+
// Ensure V2 path
493+
dbManager.getDynamicPropertiesStore().saveAllowCancelAllUnfreezeV2(1);
494+
495+
AccountCapsule account = new AccountCapsule(
496+
ByteString.copyFromUtf8("testZeroSizeV2"),
497+
ByteString.copyFrom(ByteArray.fromInt(98)),
498+
AccountType.Normal,
499+
0L);
500+
account.setNewWindowSize(ResourceCode.ENERGY, 100L);
501+
account.setEnergyUsage(500L);
502+
503+
Transaction trxn = Transaction.newBuilder().setRawData(
504+
raw.newBuilder().addContract(
505+
Contract.newBuilder().setType(ContractType.TransferContract))).build();
506+
TransactionCapsule trxCap = new TransactionCapsule(trxn);
507+
TransactionTrace trace = new TransactionTrace(trxCap, StoreFactory.getInstance(),
508+
new RuntimeImpl());
509+
510+
Method method = TransactionTrace.class.getDeclaredMethod("resetAccountUsage",
511+
AccountCapsule.class, long.class, long.class, long.class, long.class, long.class);
512+
method.setAccessible(true);
513+
514+
// mergedSize == currentSize (100) and size == 0 → newSize = 0
515+
method.invoke(trace, account, /*usage=*/0L, /*size=*/0L,
516+
/*mergedUsage=*/0L, /*mergedSize=*/100L, /*size2=*/0L);
517+
518+
Assert.assertEquals(0L, account.getEnergyUsage());
519+
520+
// Restore
521+
dbManager.getDynamicPropertiesStore().saveAllowCancelAllUnfreezeV2(0);
522+
}
449523
}

plugins/src/main/java/common/org/tron/plugins/DbConvert.java

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.File;
44
import java.nio.file.Path;
55
import java.nio.file.Paths;
6+
import java.util.AbstractMap;
67
import java.util.ArrayList;
78
import java.util.Arrays;
89
import java.util.List;
@@ -201,18 +202,15 @@ public String name() {
201202
return dbName;
202203
}
203204

204-
private void batchInsert(RocksDB rocks, List<byte[]> keys, List<byte[]> values)
205+
private void batchInsert(RocksDB rocks, List<Map.Entry<byte[], byte[]>> entries)
205206
throws Exception {
206207
try (org.rocksdb.WriteBatch batch = new org.rocksdb.WriteBatch()) {
207-
for (int i = 0; i < keys.size(); i++) {
208-
byte[] k = keys.get(i);
209-
byte[] v = values.get(i);
210-
batch.put(k, v);
208+
for (Map.Entry<byte[], byte[]> entry : entries) {
209+
batch.put(entry.getKey(), entry.getValue());
211210
}
212211
write(rocks, batch);
213212
}
214-
keys.clear();
215-
values.clear();
213+
entries.clear();
216214
}
217215

218216
/**
@@ -254,8 +252,7 @@ private boolean maybeRetry(RocksDBException e) {
254252
* @return if ok
255253
*/
256254
public void convertLevelToRocks() throws Exception {
257-
List<byte[]> keys = new ArrayList<>(BATCH);
258-
List<byte[]> values = new ArrayList<>(BATCH);
255+
List<Map.Entry<byte[], byte[]>> entries = new ArrayList<>(BATCH);
259256
JniDBFactory.pushMemoryPool(1024 * 1024);
260257
try (
261258
DB level = DBUtils.newLevelDb(srcDbPath);
@@ -273,15 +270,14 @@ public void convertLevelToRocks() throws Exception {
273270
srcDbKeyCount++;
274271
srcDbKeySum = byteArrayToIntWithOne(srcDbKeySum, key);
275272
srcDbValueSum = byteArrayToIntWithOne(srcDbValueSum, value);
276-
keys.add(key);
277-
values.add(value);
278-
if (keys.size() >= BATCH) {
279-
batchInsert(rocks, keys, values);
273+
entries.add(new AbstractMap.SimpleEntry<>(key, value));
274+
if (entries.size() >= BATCH) {
275+
batchInsert(rocks, entries);
280276
}
281277
}
282278
// clear
283-
if (!keys.isEmpty()) {
284-
batchInsert(rocks, keys, values);
279+
if (!entries.isEmpty()) {
280+
batchInsert(rocks, entries);
285281
}
286282
} finally {
287283
JniDBFactory.popMemoryPool();

0 commit comments

Comments
 (0)