diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 2d6660f9a6a..7cc1c5b870b 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -167,16 +167,19 @@ public static void setParam(final String[] args, final String confFileName) { ? cmd.shellConfFileName : confFileName; Config config = Configuration.getByFileName(configFilePath); - // 2. Config overrides defaults + // 2. Config overrides defaults (event config bean is read here but not yet applied) applyConfigParams(config); - // 3. CLI overrides Config (highest priority) + // 3. CLI overrides Config (highest priority, including --es → eventSubscribe) applyCLIParams(cmd, jc); - // 4. Apply platform constraints (e.g. ARM64 forces RocksDB) + // 4. Apply event config after CLI + applyEventConfig(eventConfig); + + // 5. Apply platform constraints (e.g. ARM64 forces RocksDB) applyPlatformConstraints(); - // 5. Init witness (depends on CLI witness flag) + // 6. Init witness (depends on CLI witness flag) initLocalWitnesses(config, cmd); } @@ -217,7 +220,7 @@ private static void applyStorageConfig(StorageConfig sc) { PARAMETER.storage.setIndexSwitch( org.apache.commons.lang3.StringUtils.isNotEmpty(indexSwitch) ? indexSwitch : "on"); PARAMETER.storage.setTransactionHistorySwitch(sc.getTransHistory().getSwitch()); - // contractParse is set in applyEventConfig — it belongs to event.subscribe domain + // contractParse is set in applyConfigParams alongside event config, not here PARAMETER.storage.setCheckpointVersion(sc.getCheckpoint().getVersion()); PARAMETER.storage.setCheckpointSync(sc.getCheckpoint().isSync()); @@ -343,21 +346,21 @@ private static void applyRateLimiterConfig(RateLimiterConfig rl) { PARAMETER.rateLimiterInitialization = initialization; } + /** + * Package-private entry point only for tests + */ + static void applyEventConfig() { + applyEventConfig(eventConfig); + } + /** * Bridge EventConfig bean values to CommonParameter fields. * Converts EventConfig (raw bean) into EventPluginConfig and FilterQuery (business objects). */ private static void applyEventConfig(EventConfig ec) { - PARAMETER.eventSubscribe = ec.isEnable(); - // contractParse belongs to event.subscribe but Storage object holds it - PARAMETER.storage.setContractParseSwitch(ec.isContractParse()); - - // PARAMETER.eventPluginConfig and PARAMETER.eventFilter are only consumed by - // Manager.startEventSubscribing(), which itself is gated by isEventSubscribe() - // (= ec.isEnable()) at Manager.java:564. When subscribe is disabled, building - // these objects has no observable effect — skip both early so PARAMETER stays - // consistent with the runtime intent. - if (!ec.isEnable()) { + // cmd parameter has higher priority + PARAMETER.eventSubscribe = PARAMETER.eventSubscribe || ec.isEnable(); + if (!PARAMETER.eventSubscribe) { return; } @@ -770,9 +773,12 @@ public static void applyConfigParams( // node.shutdown — handled in applyNodeConfig - // Event config: bind from config.conf "event.subscribe" section + // Event config: read bean here; applyEventConfig() is called once in setParam() + // after applyCLIParams() so that --es is already reflected in eventSubscribe. eventConfig = EventConfig.fromConfig(config); - applyEventConfig(eventConfig); + // contractParse is event-domain but must be set from config before CLI can + // override it with --contract-parse-enable (which runs in applyCLIParams). + PARAMETER.storage.setContractParseSwitch(eventConfig.isContractParse()); logConfig(); } diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index 8d7818d1608..7d7457cf2fc 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -170,7 +170,8 @@ public class PeerConnection { public void setChannel(Channel channel) { this.channel = channel; - if (relayNodes.stream().anyMatch(n -> n.getAddress().equals(channel.getInetAddress()))) { + if (relayNodes != null + && relayNodes.stream().anyMatch(n -> n.getAddress().equals(channel.getInetAddress()))) { this.isRelayPeer = true; } this.nodeStatistics = TronStatsManager.getNodeStatistics(channel.getInetAddress()); diff --git a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java index 4b6b7ad0a7a..3ae5677fbda 100644 --- a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java +++ b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java @@ -344,6 +344,21 @@ public void testCliEsOverridesConfig() { Args.clearParam(); } + /** + * Regression: when --es is the sole source of event.subscribe.enable=true + * (config has it disabled), eventPluginConfig must be built. + * Previously applyEventConfig() ran before applyCLIParams() and returned + * early (both flags false), leaving eventPluginConfig=null; Manager then + * called EventPluginLoader.start(null) and threw "Failed to load eventPlugin." + */ + @Test + public void testCliEsBuildsEventPluginConfig() { + Args.setParam(new String[] {"--es"}, TestConstants.TEST_CONF); + Assert.assertTrue(Args.getInstance().isEventSubscribe()); + Assert.assertNotNull(Args.getInstance().getEventPluginConfig()); + Args.clearParam(); + } + /** * Verify that config file storage values are applied when no CLI override is present. * @@ -454,6 +469,7 @@ public void testEventConfigDisabledSkipsEpcAndFilter() { Config config = ConfigFactory.parseMap(override) .withFallback(ConfigFactory.defaultReference()); Args.applyConfigParams(config); + Args.applyEventConfig(); Assert.assertNull(Args.getInstance().getEventPluginConfig()); Assert.assertNull(Args.getInstance().getEventFilter()); Args.clearParam(); @@ -467,6 +483,7 @@ public void testEventConfigEnabledBuildsEpcAndFilter() { Config config = ConfigFactory.parseMap(override) .withFallback(ConfigFactory.defaultReference()); Args.applyConfigParams(config); + Args.applyEventConfig(); Assert.assertNotNull(Args.getInstance().getEventPluginConfig()); Assert.assertNotNull(Args.getInstance().getEventFilter()); Args.clearParam(); @@ -481,6 +498,7 @@ public void testEventConfigEnabledWithInvalidFromBlockLeavesFilterNull() { Config config = ConfigFactory.parseMap(override) .withFallback(ConfigFactory.defaultReference()); Args.applyConfigParams(config); + Args.applyEventConfig(); // epc still built; filter rejected Assert.assertNotNull(Args.getInstance().getEventPluginConfig()); Assert.assertNull(Args.getInstance().getEventFilter()); diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/ChainInventoryMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/ChainInventoryMsgHandlerTest.java index dab76cfcb46..56853c3dbb7 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/ChainInventoryMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/ChainInventoryMsgHandlerTest.java @@ -3,11 +3,15 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import org.junit.AfterClass; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; +import org.tron.common.TestConstants; import org.tron.common.utils.Pair; import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.config.Parameter.NetConstants; +import org.tron.core.config.args.Args; import org.tron.core.exception.P2pException; import org.tron.core.net.message.keepalive.PingMessage; import org.tron.core.net.message.sync.ChainInventoryMessage; @@ -15,6 +19,16 @@ public class ChainInventoryMsgHandlerTest { + @BeforeClass + public static void init() { + Args.setParam(new String[]{}, TestConstants.TEST_CONF); + } + + @AfterClass + public static void destroy() { + Args.clearParam(); + } + private ChainInventoryMsgHandler handler = new ChainInventoryMsgHandler(); private PeerConnection peer = new PeerConnection(); private ChainInventoryMessage msg = new ChainInventoryMessage(new ArrayList<>(), 0L);