Skip to content

Commit 6102162

Browse files
wbclaude
authored andcommitted
fix(event): filter removed=true from solidity maps and sync contract trigger processing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 980c707 commit 6102162

4 files changed

Lines changed: 150 additions & 9 deletions

File tree

framework/src/main/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsule.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,11 @@ public void processTrigger() {
135135
EventPluginLoader.getInstance().postContractEventTrigger((ContractEventTrigger) event);
136136
}
137137

138-
if (EventPluginLoader.getInstance().isSolidityEventTriggerEnable()) {
138+
if (EventPluginLoader.getInstance().isSolidityEventTriggerEnable()
139+
&& !contractTrigger.isRemoved()) {
139140
boolean result = Args.getSolidityContractEventTriggerMap().computeIfAbsent(event
140141
.getBlockNumber(), listBlk -> new LinkedBlockingQueue())
141142
.offer((ContractEventTrigger) event);
142-
143143
if (!result) {
144144
logger.info("too many triggers, solidity event trigger lost: {}",
145145
event.getUniqueId());
@@ -159,11 +159,11 @@ public void processTrigger() {
159159
EventPluginLoader.getInstance().postContractLogTrigger(logTrigger);
160160
}
161161

162-
if (EventPluginLoader.getInstance().isSolidityLogTriggerRedundancy()) {
162+
if (EventPluginLoader.getInstance().isSolidityLogTriggerRedundancy()
163+
&& !contractTrigger.isRemoved()) {
163164
boolean result = Args.getSolidityContractLogTriggerMap().computeIfAbsent(event
164165
.getBlockNumber(), listBlk -> new LinkedBlockingQueue())
165166
.offer(logTrigger);
166-
167167
if (!result) {
168168
logger.info("too many triggers, solidity log trigger lost: {}",
169169
logTrigger.getUniqueId());
@@ -175,11 +175,11 @@ public void processTrigger() {
175175
EventPluginLoader.getInstance().postContractLogTrigger((ContractLogTrigger) event);
176176
}
177177

178-
if (EventPluginLoader.getInstance().isSolidityLogTriggerEnable()) {
178+
if (EventPluginLoader.getInstance().isSolidityLogTriggerEnable()
179+
&& !contractTrigger.isRemoved()) {
179180
boolean result = Args.getSolidityContractLogTriggerMap().computeIfAbsent(event
180181
.getBlockNumber(), listBlk -> new LinkedBlockingQueue())
181182
.offer((ContractLogTrigger) event);
182-
183183
if (!result) {
184184
logger.info("too many triggers, solidity log trigger lost: {}",
185185
event.getUniqueId());

framework/src/main/java/org/tron/core/db/Manager.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,7 @@ public void pushBlock(final BlockCapsule block)
13771377
} catch (Throwable throwable) {
13781378
logger.error(throwable.getMessage(), throwable);
13791379
khaosDb.removeBlk(block.getBlockId());
1380+
clearSolidityContractTriggerCache(block.getNum());
13801381
throw throwable;
13811382
}
13821383
long newSolidNum = getDynamicPropertiesStore().getLatestSolidifiedBlockNum();
@@ -2378,6 +2379,16 @@ private void reOrgContractTrigger() {
23782379
getDynamicPropertiesStore().getLatestBlockHeaderHash());
23792380
}
23802381
}
2382+
clearSolidityContractTriggerCache(getHeadBlockNum());
2383+
}
2384+
2385+
private void clearSolidityContractTriggerCache(long blockNum) {
2386+
if (eventPluginLoaded
2387+
&& (EventPluginLoader.getInstance().isSolidityEventTriggerEnable()
2388+
|| EventPluginLoader.getInstance().isSolidityLogTriggerEnable())) {
2389+
Args.getSolidityContractLogTriggerMap().remove(blockNum);
2390+
Args.getSolidityContractEventTriggerMap().remove(blockNum);
2391+
}
23812392
}
23822393

23832394
private void postContractTrigger(final TransactionTrace trace, boolean remove, String blockHash) {
@@ -2397,9 +2408,14 @@ private void postContractTrigger(final TransactionTrace trace, boolean remove, S
23972408
.getLatestSolidifiedBlockNum());
23982409
contractTriggerCapsule.setBlockHash(blockHash);
23992410

2400-
if (!triggerCapsuleQueue.offer(contractTriggerCapsule)) {
2401-
logger.info("Too many triggers, contract log trigger lost: {}.",
2402-
trigger.getTransactionId());
2411+
// Process synchronously to avoid race condition between async queue and
2412+
// reOrgContractTrigger cache clearing. Performance is not impacted because
2413+
// processTrigger() only enqueues events into the plugin's internal queue
2414+
// without blocking on actual I/O.
2415+
try {
2416+
contractTriggerCapsule.processTrigger();
2417+
} catch (Throwable throwable) {
2418+
logger.warn("Post contract trigger failed. {}", throwable.getMessage());
24032419
}
24042420
}
24052421
}

framework/src/test/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsuleTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@
33
import static com.google.common.collect.Lists.newArrayList;
44
import static org.junit.Assert.assertEquals;
55
import static org.junit.Assert.assertTrue;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.when;
68

79
import com.beust.jcommander.internal.Lists;
10+
import java.lang.reflect.Field;
811
import java.util.ArrayList;
912
import java.util.Arrays;
1013
import lombok.extern.slf4j.Slf4j;
1114
import org.apache.commons.collections4.CollectionUtils;
1215
import org.apache.commons.lang3.ArrayUtils;
1316
import org.junit.Before;
1417
import org.junit.Test;
18+
import org.tron.common.logsfilter.EventPluginLoader;
19+
import org.tron.common.logsfilter.trigger.ContractLogTrigger;
1520
import org.tron.common.logsfilter.trigger.ContractTrigger;
1621
import org.tron.common.runtime.vm.DataWord;
1722
import org.tron.common.runtime.vm.LogInfo;
23+
import org.tron.core.config.args.Args;
1824

1925
@Slf4j
2026
public class ContractTriggerCapsuleTest {
@@ -58,6 +64,45 @@ public void testSetAndGetContractTrigger() {
5864
}
5965
}
6066

67+
@Test
68+
public void testRemovedTriggerNotWrittenToSolidityMap() throws Exception {
69+
Args.getSolidityContractLogTriggerMap().clear();
70+
Args.getSolidityContractEventTriggerMap().clear();
71+
72+
EventPluginLoader mockLoader = mock(EventPluginLoader.class);
73+
when(mockLoader.isSolidityLogTriggerEnable()).thenReturn(true);
74+
when(mockLoader.isSolidityEventTriggerEnable()).thenReturn(false);
75+
when(mockLoader.isContractLogTriggerEnable()).thenReturn(false);
76+
when(mockLoader.isContractEventTriggerEnable()).thenReturn(false);
77+
when(mockLoader.isSolidityLogTriggerRedundancy()).thenReturn(false);
78+
when(mockLoader.isContractLogTriggerRedundancy()).thenReturn(false);
79+
80+
Field instanceField = EventPluginLoader.class.getDeclaredField("instance");
81+
instanceField.setAccessible(true);
82+
EventPluginLoader originalInstance = (EventPluginLoader) instanceField.get(null);
83+
instanceField.set(null, mockLoader);
84+
85+
try {
86+
ContractLogTrigger trigger = new ContractLogTrigger();
87+
trigger.setRemoved(true);
88+
trigger.setBlockNumber(100L);
89+
trigger.setTransactionId("abc");
90+
trigger.setContractAddress("0x01");
91+
LogInfo logInfo = new LogInfo(new byte[0], new ArrayList<>(), new byte[0]);
92+
trigger.setLogInfo(logInfo);
93+
94+
ContractTriggerCapsule capsule = new ContractTriggerCapsule(trigger);
95+
capsule.processTrigger();
96+
97+
assertTrue(Args.getSolidityContractLogTriggerMap().isEmpty());
98+
assertTrue(Args.getSolidityContractEventTriggerMap().isEmpty());
99+
} finally {
100+
instanceField.set(null, originalInstance);
101+
Args.getSolidityContractLogTriggerMap().clear();
102+
Args.getSolidityContractEventTriggerMap().clear();
103+
}
104+
}
105+
61106
@Test
62107
public void testLogInfo() {
63108
logger.info("log info to string: {}, ", logInfo.toString());

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import static org.junit.Assert.assertThrows;
44
import static org.mockito.ArgumentMatchers.any;
55
import static org.mockito.Mockito.doThrow;
6+
import static org.mockito.Mockito.mock;
67
import static org.mockito.Mockito.spy;
8+
import static org.mockito.Mockito.when;
79
import static org.tron.common.utils.Commons.adjustAssetBalanceV2;
810
import static org.tron.common.utils.Commons.adjustTotalShieldedPoolValue;
911
import static org.tron.common.utils.Commons.getExchangeStoreFinal;
@@ -16,12 +18,15 @@
1618
import com.google.common.collect.Sets;
1719
import com.google.protobuf.Any;
1820
import com.google.protobuf.ByteString;
21+
import java.lang.reflect.Field;
22+
import java.lang.reflect.Method;
1923
import java.nio.charset.StandardCharsets;
2024
import java.util.ArrayList;
2125
import java.util.Arrays;
2226
import java.util.List;
2327
import java.util.Map;
2428
import java.util.Set;
29+
import java.util.concurrent.LinkedBlockingQueue;
2530
import java.util.concurrent.atomic.AtomicInteger;
2631
import java.util.stream.Collectors;
2732
import java.util.stream.IntStream;
@@ -32,6 +37,8 @@
3237
import org.tron.common.BaseMethodTest;
3338
import org.tron.common.TestConstants;
3439
import org.tron.common.crypto.ECKey;
40+
import org.tron.common.logsfilter.EventPluginLoader;
41+
import org.tron.common.logsfilter.trigger.ContractLogTrigger;
3542
import org.tron.common.runtime.RuntimeImpl;
3643
import org.tron.common.utils.ByteArray;
3744
import org.tron.common.utils.Commons;
@@ -1292,6 +1299,79 @@ public void blockTrigger() {
12921299
Assert.assertEquals(TronError.ErrCode.EVENT_SUBSCRIBE_ERROR, thrown.getErrCode());
12931300
}
12941301

1302+
@Test
1303+
public void testReOrgContractTriggerClearsMap() throws Exception {
1304+
long forkBlockNum = 1L;
1305+
// set up eventPluginLoaded and mock EventPluginLoader so the guard inside
1306+
// clearSolidityContractTriggerCache actually executes
1307+
ReflectUtils.setFieldValue(dbManager, "eventPluginLoaded", true);
1308+
EventPluginLoader mockLoader = mock(EventPluginLoader.class);
1309+
when(mockLoader.isSolidityLogTriggerEnable()).thenReturn(true);
1310+
when(mockLoader.isSolidityEventTriggerEnable()).thenReturn(false);
1311+
Field instanceField = EventPluginLoader.class.getDeclaredField("instance");
1312+
instanceField.setAccessible(true);
1313+
EventPluginLoader originalLoader = (EventPluginLoader) instanceField.get(null);
1314+
instanceField.set(null, mockLoader);
1315+
1316+
Args.getSolidityContractLogTriggerMap()
1317+
.computeIfAbsent(forkBlockNum, k -> new LinkedBlockingQueue<>())
1318+
.offer(new ContractLogTrigger());
1319+
Args.getSolidityContractEventTriggerMap()
1320+
.computeIfAbsent(forkBlockNum, k -> new LinkedBlockingQueue<>())
1321+
.offer(new org.tron.common.logsfilter.trigger.ContractEventTrigger());
1322+
1323+
try {
1324+
Method method = dbManager.getClass()
1325+
.getDeclaredMethod("clearSolidityContractTriggerCache", long.class);
1326+
method.setAccessible(true);
1327+
method.invoke(dbManager, forkBlockNum);
1328+
1329+
// assert inside try — before finally cleans up — to verify actual cache clear
1330+
Assert.assertNull(Args.getSolidityContractLogTriggerMap().get(forkBlockNum));
1331+
Assert.assertNull(Args.getSolidityContractEventTriggerMap().get(forkBlockNum));
1332+
} finally {
1333+
instanceField.set(null, originalLoader);
1334+
ReflectUtils.setFieldValue(dbManager, "eventPluginLoaded", false);
1335+
Args.getSolidityContractLogTriggerMap().clear();
1336+
Args.getSolidityContractEventTriggerMap().clear();
1337+
}
1338+
}
1339+
1340+
@Test
1341+
public void testClearSolidityContractTriggerCache() throws Exception {
1342+
long blockNum = 999L;
1343+
ReflectUtils.setFieldValue(dbManager, "eventPluginLoaded", true);
1344+
EventPluginLoader mockLoader = mock(EventPluginLoader.class);
1345+
when(mockLoader.isSolidityLogTriggerEnable()).thenReturn(true);
1346+
when(mockLoader.isSolidityEventTriggerEnable()).thenReturn(true);
1347+
Field instanceField = EventPluginLoader.class.getDeclaredField("instance");
1348+
instanceField.setAccessible(true);
1349+
EventPluginLoader originalLoader = (EventPluginLoader) instanceField.get(null);
1350+
instanceField.set(null, mockLoader);
1351+
1352+
Args.getSolidityContractLogTriggerMap()
1353+
.computeIfAbsent(blockNum, k -> new LinkedBlockingQueue<>())
1354+
.offer(new ContractLogTrigger());
1355+
Args.getSolidityContractEventTriggerMap()
1356+
.computeIfAbsent(blockNum, k -> new LinkedBlockingQueue<>());
1357+
Assert.assertFalse(Args.getSolidityContractLogTriggerMap().isEmpty());
1358+
1359+
try {
1360+
Method method = Manager.class.getDeclaredMethod("clearSolidityContractTriggerCache",
1361+
long.class);
1362+
method.setAccessible(true);
1363+
method.invoke(dbManager, blockNum);
1364+
1365+
Assert.assertNull(Args.getSolidityContractLogTriggerMap().get(blockNum));
1366+
Assert.assertNull(Args.getSolidityContractEventTriggerMap().get(blockNum));
1367+
} finally {
1368+
instanceField.set(null, originalLoader);
1369+
ReflectUtils.setFieldValue(dbManager, "eventPluginLoaded", false);
1370+
Args.getSolidityContractLogTriggerMap().clear();
1371+
Args.getSolidityContractEventTriggerMap().clear();
1372+
}
1373+
}
1374+
12951375
public void adjustBalance(AccountStore accountStore, byte[] accountAddress, long amount)
12961376
throws BalanceInsufficientException {
12971377
Commons.adjustBalance(accountStore, accountAddress, amount,

0 commit comments

Comments
 (0)