Skip to content

Commit 1ead96e

Browse files
committed
fix(event): reject incompatible event-plugin versions below 3.0.0
Pre-3.0.0(The previous event-plugin public release is 2.2.0) event-plugin builds still link against com.alibaba.fastjson, which was removed from java-tron in #6701. When such a plugin is loaded, the NoClassDefFoundError surfaces inside the plugin's own worker thread and is swallowed by its catch(Throwable) handler. The node keeps running while silently dropping every trigger, leaving operators with no signal that the event subscription is broken. Enforce a minimum Plugin-Version at startup in EventPluginLoader.startPlugin using pf4j's VersionManager (semver). When the descriptor version is below 3.0.0, log a clear upgrade hint and return false; the existing call chain in Manager.startEventSubscribing wraps that into TronError(EVENT_SUBSCRIBE_INIT) and aborts node startup instead of silently degrading.
1 parent 0a26d23 commit 1ead96e

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.beust.jcommander.internal.Sets;
44
import com.fasterxml.jackson.core.JsonProcessingException;
55
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.google.common.base.Strings;
67
import java.io.File;
78
import java.util.HashSet;
89
import java.util.List;
@@ -14,8 +15,10 @@
1415
import org.bouncycastle.util.encoders.Hex;
1516
import org.pf4j.CompoundPluginDescriptorFinder;
1617
import org.pf4j.DefaultPluginManager;
18+
import org.pf4j.DefaultVersionManager;
1719
import org.pf4j.ManifestPluginDescriptorFinder;
1820
import org.pf4j.PluginManager;
21+
import org.pf4j.VersionManager;
1922
import org.springframework.util.StringUtils;
2023
import org.tron.common.logsfilter.nativequeue.NativeMessageQueue;
2124
import org.tron.common.logsfilter.trigger.BlockLogTrigger;
@@ -29,6 +32,16 @@
2932
@Slf4j
3033
public class EventPluginLoader {
3134

35+
/**
36+
* Minimum event-plugin Plugin-Version compatible with this node. Bumped to 3.0.0 to
37+
* reject pre-fastjson-removal builds whose worker threads would fail with
38+
* NoClassDefFoundError on com.alibaba.fastjson at runtime. The previous event-plugin
39+
* release is 2.2.0, so 3.0.0 is the first version that ships the Jackson replacement.
40+
*/
41+
static final String MIN_PLUGIN_VERSION = "3.0.0";
42+
43+
private static final VersionManager VERSION_MANAGER = new DefaultVersionManager();
44+
3245
private static EventPluginLoader instance;
3346

3447
private long MAX_PENDING_SIZE = 50000;
@@ -457,6 +470,10 @@ protected CompoundPluginDescriptorFinder createPluginDescriptorFinder() {
457470
return false;
458471
}
459472

473+
if (!isPluginVersionSupported(pluginManager, pluginId)) {
474+
return false;
475+
}
476+
460477
pluginManager.startPlugins();
461478

462479
eventListeners = pluginManager.getExtensions(IPluginEventListener.class);
@@ -471,6 +488,21 @@ protected CompoundPluginDescriptorFinder createPluginDescriptorFinder() {
471488
return true;
472489
}
473490

491+
static boolean isPluginVersionSupported(PluginManager pm, String pluginId) {
492+
String pluginVersion = pm.getPlugin(pluginId).getDescriptor().getVersion();
493+
if (Strings.isNullOrEmpty(pluginVersion)) {
494+
return false;
495+
}
496+
boolean isSupported = VERSION_MANAGER.compareVersions(pluginVersion, MIN_PLUGIN_VERSION) >= 0;
497+
498+
if (!isSupported) {
499+
logger.error(
500+
"event-plugin '{}' version {} is older than required {}, please upgrade event-plugin",
501+
pluginId, pluginVersion, MIN_PLUGIN_VERSION);
502+
}
503+
return isSupported;
504+
}
505+
474506
public void stopPlugin() {
475507
if (Objects.nonNull(pluginManager)) {
476508
pluginManager.stopPlugins();

framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,25 @@
33
import static org.junit.Assert.assertEquals;
44
import static org.junit.Assert.assertFalse;
55
import static org.junit.Assert.assertTrue;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.when;
68

9+
import java.io.OutputStream;
10+
import java.nio.charset.StandardCharsets;
11+
import java.nio.file.Files;
12+
import java.nio.file.Path;
713
import java.util.ArrayList;
814
import java.util.List;
15+
import java.util.zip.ZipEntry;
16+
import java.util.zip.ZipOutputStream;
917
import org.junit.Assert;
1018
import org.junit.Test;
19+
import org.pf4j.PluginDescriptor;
20+
import org.pf4j.PluginManager;
21+
import org.pf4j.PluginWrapper;
1122
import org.tron.common.logsfilter.trigger.BlockLogTrigger;
1223
import org.tron.common.logsfilter.trigger.TransactionLogTrigger;
24+
import org.tron.common.utils.ReflectUtils;
1325

1426
public class EventLoaderTest {
1527

@@ -48,6 +60,65 @@ public void launchNativeQueue() {
4860
EventPluginLoader.getInstance().stopPlugin();
4961
}
5062

63+
@Test
64+
public void testIsPluginVersionSupported() {
65+
assertEquals("3.0.0", EventPluginLoader.MIN_PLUGIN_VERSION);
66+
// last releases before fastjson removal — must be rejected
67+
assertFalse(checkVersion("1.0.0"));
68+
assertFalse(checkVersion("2.2.0"));
69+
assertFalse(checkVersion("2.9.9"));
70+
// 3.0.0 onward — must be accepted
71+
assertTrue(checkVersion("3.0.0"));
72+
assertTrue(checkVersion("3.1.5"));
73+
assertTrue(checkVersion("10.0.0"));
74+
// empty/null version — reject
75+
assertFalse(checkVersion(""));
76+
assertFalse(checkVersion(null));
77+
}
78+
79+
private static boolean checkVersion(String version) {
80+
PluginManager pm = mock(PluginManager.class);
81+
PluginWrapper wrapper = mock(PluginWrapper.class);
82+
PluginDescriptor desc = mock(PluginDescriptor.class);
83+
when(pm.getPlugin("test")).thenReturn(wrapper);
84+
when(wrapper.getDescriptor()).thenReturn(desc);
85+
when(desc.getVersion()).thenReturn(version);
86+
return EventPluginLoader.isPluginVersionSupported(pm, "test");
87+
}
88+
89+
@Test
90+
public void rejectsObsoletePluginAtStartup() throws Exception {
91+
Path zip = createMinimalPluginZip("1.0.0");
92+
EventPluginConfig config = new EventPluginConfig();
93+
config.setPluginPath(zip.toString());
94+
config.setUseNativeQueue(false);
95+
config.setServerAddress("");
96+
config.setDbConfig("");
97+
config.setTriggerConfigList(new ArrayList<>());
98+
99+
EventPluginLoader loader = EventPluginLoader.getInstance();
100+
ReflectUtils.setFieldValue(loader, "pluginManager", null);
101+
102+
assertFalse(loader.start(config));
103+
}
104+
105+
private static Path createMinimalPluginZip(String version) throws Exception {
106+
Path dir = Files.createTempDirectory("plugintest-");
107+
Path zip = dir.resolve("plugin-test-" + version + ".zip");
108+
String manifest = "Manifest-Version: 1.0\r\n"
109+
+ "Plugin-Id: testplugin\r\n"
110+
+ "Plugin-Class: org.tron.common.logsfilter.NoopTestPlugin\r\n"
111+
+ "Plugin-Version: " + version + "\r\n"
112+
+ "Plugin-Provider: test\r\n\r\n";
113+
try (OutputStream os = Files.newOutputStream(zip);
114+
ZipOutputStream zos = new ZipOutputStream(os)) {
115+
zos.putNextEntry(new ZipEntry("classes/META-INF/MANIFEST.MF"));
116+
zos.write(manifest.getBytes(StandardCharsets.UTF_8));
117+
zos.closeEntry();
118+
}
119+
return zip;
120+
}
121+
51122
@Test
52123
public void testBlockLogTrigger() {
53124
BlockLogTrigger blt = new BlockLogTrigger();

0 commit comments

Comments
 (0)