Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions framework/src/test/java/org/tron/common/BaseMethodTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.tron.common.application.Application;
import org.tron.common.application.ApplicationFactory;
import org.tron.common.application.TronApplicationContext;
import org.tron.common.utils.PeerManagerStateResetter;
import org.tron.core.ChainBaseManager;
import org.tron.core.config.DefaultConfig;
import org.tron.core.config.args.Args;
Expand Down Expand Up @@ -60,6 +61,7 @@ protected String configFile() {

@Before
public final void initContext() throws IOException {
PeerManagerStateResetter.reset();
String[] baseArgs = new String[]{
"--output-directory", temporaryFolder.newFolder().toString()};
String[] allArgs = mergeArgs(baseArgs, extraArgs());
Expand Down
7 changes: 7 additions & 0 deletions framework/src/test/java/org/tron/common/BaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.extern.slf4j.Slf4j;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
Expand All @@ -18,6 +19,7 @@
import org.tron.common.crypto.ECKey;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.utils.Commons;
import org.tron.common.utils.PeerManagerStateResetter;
import org.tron.common.utils.Sha256Hash;
import org.tron.consensus.base.Param;
import org.tron.core.ChainBaseManager;
Expand Down Expand Up @@ -79,6 +81,11 @@ public abstract class BaseTest {
private static Application appT1;


@Before
public void resetPeerManagerState() {
PeerManagerStateResetter.reset();
}

@PostConstruct
private void prepare() {
appT1 = appT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ public void testBaseFee() throws ContractExeException, ReceiptCheckErrException,
factoryAddress, Hex.decode(hexInput), 0, feeLimit, manager, null);
byte[] returnValue = result.getRuntime().getResult().getHReturn();
Assert.assertNull(result.getRuntime().getRuntimeError());
Assert.assertArrayEquals(returnValue,
longTo32Bytes(manager.getDynamicPropertiesStore().getEnergyFee()));
Assert.assertArrayEquals(longTo32Bytes(manager.getDynamicPropertiesStore().getEnergyFee()),
returnValue);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package org.tron.common.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.util.ReflectionUtils;
import org.tron.common.es.ExecutorServiceManager;
import org.tron.core.net.peer.PeerConnection;
import org.tron.core.net.peer.PeerManager;
import org.tron.protos.Protocol.ReasonCode;

/**
* Test source-set utility for restoring PeerManager to a cold-start state.
*
* <p>{@link PeerManager#close()} disconnects visible peers but does not clear its raw static
* peers or counters. Its static executor also remains shut down and is not rebuilt by normal
* initialization. Tests reuse one JVM across Spring contexts, so this reset is needed before
* another test starts.
*
* <p>The executor is drained first because {@code check()} is not synchronized: it iterates a
* snapshot of the peers list and then removes entries and decrements the counters. A reset
* that only cleared the list and counters while an old {@code check()} task was still running
* or queued would let that task decrement the freshly zeroed counters afterwards, leaving
* negative counts. {@code synchronized} on reset alone cannot prevent this, so the old
* executor must be shut down and awaited before any state is touched.
*
* <p>The peers list is only cleared after a best-effort disconnect of its live entries.
* There is no dedicated teardown path, and {@link PeerManager#close()} may fail midway, so
* the raw list can still hold peers whose channels are alive; a bare {@code clear()} would
* orphan those connections. Each peer is disconnected individually and defensively (null
* channel tolerated, per-peer try/catch) so that one broken entry cannot abort the cleanup.
*
* <p>For tests that exercise p2p, reset now terminates any already scheduled
* {@code check()}/{@code logPeerStats()} tasks and installs a fresh executor (its thread is
* created lazily, and no task is scheduled again until the next {@code init()}). This is safe
* for tests: {@code check()} only removes peers whose channel has been half-disconnected for
* over 60 seconds and {@code logPeerStats()} only logs metrics, and no test depends on
* either. Reset also does not interlock with a concurrently running {@code init()} or
* {@code close()}; the sequential test lifecycle keeps that window out of reach.
*
* <p>The current BaseTest/BaseMethodTest wiring is intentionally broad to protect tests that
* reuse Spring contexts or the JVM from unknown preceding pollution. For tests that do not use
* PeerManager, reset is idempotent and low-impact: it typically clears an empty list and zeros
* counters, while the executor swap is cheap (an idle old executor stops immediately and the
* fresh executor only creates its thread on demand). If production PeerManager lifecycle
* becomes restart-safe, this wiring can be narrowed
* or this utility can be removed in the future.
*/
public final class PeerManagerStateResetter {

private static final String EXECUTOR_NAME = "peer-manager";

private PeerManagerStateResetter() {
}

public static synchronized void reset() {
// 1) Drain the old executor first: let running/queued check() tasks die out so they
// cannot interleave with the list/counter reset below.
ScheduledExecutorService executor = getFieldValue("executor");
if (executor != null && !executor.isShutdown()) {
ExecutorServiceManager.shutdownAndAwaitTermination(executor, EXECUTOR_NAME);
}
// 2) Unconditionally install a fresh executor (the old one may be shut down or null);
// its thread is created lazily.
setFieldValue("executor",
ExecutorServiceManager.newSingleThreadScheduledExecutor(EXECUTOR_NAME));

// 3) Release residual live connections before clearing the raw list.
List<PeerConnection> peers = getFieldValue("peers");
if (peers == null) {
setFieldValue("peers", Collections.synchronizedList(new ArrayList<PeerConnection>()));
} else {
for (PeerConnection peer : new ArrayList<>(peers)) {
try {
if (!peer.isDisconnect()) {
peer.disconnect(ReasonCode.PEER_QUITING);
if (peer.getChannel() != null) {
peer.getChannel().close();
}
}
} catch (Exception e) {
// best effort: a single corrupted leftover peer must not fail the reset
}
}
peers.clear();
}

// 4) Zero the counters; old tasks can no longer decrement them at this point.
AtomicInteger active = PeerManager.getActivePeersCount();
AtomicInteger passive = PeerManager.getPassivePeersCount();
active.set(0);
passive.set(0);
}

private static <T> T getFieldValue(String fieldName) {
Field field = ReflectionUtils.findField(PeerManager.class, fieldName);
ReflectionUtils.makeAccessible(field);
return (T) ReflectionUtils.getField(field, null);
}

private static void setFieldValue(String fieldName, Object value) {
Field field = ReflectionUtils.findField(PeerManager.class, fieldName);
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, null, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.tron.common.ClassLevelAppContextFixture;
import org.tron.common.TestConstants;
import org.tron.common.application.TronApplicationContext;
import org.tron.common.utils.PeerManagerStateResetter;
import org.tron.common.utils.ReflectUtils;
import org.tron.common.utils.Sha256Hash;
import org.tron.consensus.pbft.message.PbftMessage;
Expand Down Expand Up @@ -45,6 +46,7 @@ public class MessageHandlerTest {

@BeforeClass
public static void init() throws Exception {
PeerManagerStateResetter.reset();
Args.setParam(new String[] {"--output-directory",
temporaryFolder.newFolder().toString(), "--debug"}, TestConstants.TEST_CONF);
context = APP_FIXTURE.createContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.tron.common.crypto.SignInterface;
import org.tron.common.crypto.SignUtils;
import org.tron.common.utils.FileUtil;
import org.tron.common.utils.PeerManagerStateResetter;
import org.tron.common.utils.PublicMethod;
import org.tron.common.utils.ReflectUtils;
import org.tron.common.utils.Sha256Hash;
Expand Down Expand Up @@ -46,6 +47,7 @@ public class PbftMsgHandlerTest {

@BeforeClass
public static void init() {
PeerManagerStateResetter.reset();
Args.setParam(new String[] {"--output-directory", dbPath, "--debug"},
TestConstants.TEST_CONF);
context = new TronApplicationContext(DefaultConfig.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.springframework.context.ApplicationContext;
import org.tron.common.TestConstants;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.utils.PeerManagerStateResetter;
import org.tron.common.utils.ReflectUtils;
import org.tron.core.config.args.Args;
import org.tron.p2p.connection.Channel;
Expand All @@ -25,6 +26,7 @@ public class PeerManagerTest {

@BeforeClass
public static void initArgs() {
PeerManagerStateResetter.reset();
Args.setParam(new String[]{}, TestConstants.TEST_CONF);
CommonParameter.getInstance().setRateLimiterSyncBlockChain(10);
CommonParameter.getInstance().setRateLimiterFetchInvData(10);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.springframework.context.ApplicationContext;
import org.tron.common.TestConstants;
import org.tron.common.application.TronApplicationContext;
import org.tron.common.utils.PeerManagerStateResetter;
import org.tron.common.utils.ReflectUtils;
import org.tron.common.utils.Sha256Hash;
import org.tron.core.ChainBaseManager;
Expand Down Expand Up @@ -52,6 +53,7 @@ public class HandShakeServiceTest {

@BeforeClass
public static void init() throws Exception {
PeerManagerStateResetter.reset();
Args.setParam(new String[] {"--output-directory",
temporaryFolder.newFolder().toString(), "--debug"}, TestConstants.TEST_CONF);
context = new TronApplicationContext(DefaultConfig.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.tron.common.ClassLevelAppContextFixture;
import org.tron.common.TestConstants;
import org.tron.common.application.TronApplicationContext;
import org.tron.common.utils.PeerManagerStateResetter;
import org.tron.common.utils.PublicMethod;
import org.tron.common.utils.TimeoutInterceptor;
import org.tron.core.config.args.Args;
Expand All @@ -38,6 +39,7 @@ public class WalletApiTest {

@BeforeClass
public static void init() throws IOException {
PeerManagerStateResetter.reset();
Args.setParam(new String[] {"-d", temporaryFolder.newFolder().toString(),
"--p2p-disable", "true"}, TestConstants.TEST_CONF);
Args.getInstance().setRpcPort(PublicMethod.chooseRandomPort());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.Optional;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
Expand Down Expand Up @@ -106,6 +107,7 @@ public class SendCoinShieldTest extends BaseTest {
private static final int VOTE_SCORE = 2;
private static final String DESCRIPTION = "TRX";
private static final String URL = "https://tron.network";
private long previousAllowShieldedTransaction;
@Resource
private Wallet wallet;

Expand All @@ -130,6 +132,8 @@ public static void initZksnarkParams() {
*/
@Before
public void init() {
previousAllowShieldedTransaction = dbManager.getDynamicPropertiesStore()
.getAllowShieldedTransaction();
if (init) {
return;
}
Expand All @@ -155,6 +159,12 @@ public void init() {
init = true;
}

@After
public void restoreAllowShieldedTransaction() {
dbManager.getDynamicPropertiesStore()
.saveAllowShieldedTransaction(previousAllowShieldedTransaction);
}

private void addZeroValueOutputNote(ZenTransactionBuilder builder) throws ZksnarkException {
SpendingKey spendingKey = SpendingKey.random();
FullViewingKey fullViewingKey = spendingKey.fullViewingKey();
Expand Down