2020import java .lang .reflect .InvocationTargetException ;
2121import java .lang .reflect .Method ;
2222import java .nio .charset .StandardCharsets ;
23+ import java .util .ArrayList ;
24+ import java .util .Collections ;
25+ import java .util .List ;
2326
2427import lombok .SneakyThrows ;
2528import lombok .extern .slf4j .Slf4j ;
2629import org .junit .After ;
30+ import org .junit .Assert ;
2731import org .junit .Test ;
2832import org .mockito .MockedConstruction ;
2933import org .mockito .MockedStatic ;
3236import org .mockito .stubbing .Answer ;
3337
3438import org .tron .common .cron .CronExpression ;
39+ import org .tron .common .logsfilter .EventPluginLoader ;
40+ import org .tron .common .logsfilter .trigger .ContractLogTrigger ;
41+ import org .tron .common .logsfilter .trigger .ContractTrigger ;
3542import org .tron .common .parameter .CommonParameter ;
3643import org .tron .common .runtime .ProgramResult ;
44+ import org .tron .common .runtime .vm .LogInfo ;
3745import org .tron .common .utils .Sha256Hash ;
3846import org .tron .core .ChainBaseManager ;
3947import org .tron .core .capsule .BlockCapsule ;
@@ -438,4 +446,111 @@ public void testReOrgLogsFilter() throws Exception {
438446 privateMethod .invoke (dbManager );
439447 }
440448
449+ @ Test
450+ public void testPostContractTriggerProcessesSync () throws Exception {
451+ Manager dbManager = spy (new Manager ());
452+ Field eventLoadedField = Manager .class .getDeclaredField ("eventPluginLoaded" );
453+ eventLoadedField .setAccessible (true );
454+ eventLoadedField .set (dbManager , true );
455+
456+ ChainBaseManager cbm = mock (ChainBaseManager .class );
457+ DynamicPropertiesStore dps = mock (DynamicPropertiesStore .class );
458+ when (dps .getLatestSolidifiedBlockNum ()).thenReturn (0L );
459+ when (cbm .getDynamicPropertiesStore ()).thenReturn (dps );
460+ Field cbmField = Manager .class .getDeclaredField ("chainBaseManager" );
461+ cbmField .setAccessible (true );
462+ cbmField .set (dbManager , cbm );
463+
464+ EventPluginLoader mockLoader = mock (EventPluginLoader .class );
465+ when (mockLoader .isContractLogTriggerEnable ()).thenReturn (false );
466+ when (mockLoader .isContractEventTriggerEnable ()).thenReturn (false );
467+ when (mockLoader .isSolidityLogTriggerEnable ()).thenReturn (true );
468+ when (mockLoader .isSolidityEventTriggerEnable ()).thenReturn (false );
469+
470+ Field instanceField = EventPluginLoader .class .getDeclaredField ("instance" );
471+ instanceField .setAccessible (true );
472+ EventPluginLoader original = (EventPluginLoader ) instanceField .get (null );
473+ instanceField .set (null , mockLoader );
474+
475+ Args .getSolidityContractLogTriggerMap ().clear ();
476+
477+ try {
478+ ContractLogTrigger trigger = new ContractLogTrigger ();
479+ trigger .setBlockNumber (200L );
480+ trigger .setTransactionId ("tx-id" );
481+ trigger .setContractAddress ("0x01" );
482+ trigger .setLogInfo (new LogInfo (new byte [0 ], new ArrayList <>(), new byte [0 ]));
483+
484+ TransactionTrace traceMock = mock (TransactionTrace .class );
485+ ProgramResult resultMock = mock (ProgramResult .class );
486+ when (traceMock .getRuntimeResult ()).thenReturn (resultMock );
487+ List <ContractTrigger > triggers = new ArrayList <>();
488+ triggers .add (trigger );
489+ when (resultMock .getTriggerList ()).thenReturn (triggers );
490+
491+ Method method = Manager .class .getDeclaredMethod ("postContractTrigger" ,
492+ TransactionTrace .class , boolean .class , String .class );
493+ method .setAccessible (true );
494+ method .invoke (dbManager , traceMock , false , "blockhash" );
495+
496+ Assert .assertNotNull (
497+ "synchronous processTrigger should populate solidity log map" ,
498+ Args .getSolidityContractLogTriggerMap ().get (200L ));
499+ } finally {
500+ instanceField .set (null , original );
501+ eventLoadedField .set (dbManager , false );
502+ Args .getSolidityContractLogTriggerMap ().clear ();
503+ }
504+ }
505+
506+ @ Test
507+ public void testPostContractTriggerSwallowsThrowable () throws Exception {
508+ Manager dbManager = spy (new Manager ());
509+ Field eventLoadedField = Manager .class .getDeclaredField ("eventPluginLoaded" );
510+ eventLoadedField .setAccessible (true );
511+ eventLoadedField .set (dbManager , true );
512+
513+ ChainBaseManager cbm = mock (ChainBaseManager .class );
514+ DynamicPropertiesStore dps = mock (DynamicPropertiesStore .class );
515+ when (dps .getLatestSolidifiedBlockNum ()).thenReturn (0L );
516+ when (cbm .getDynamicPropertiesStore ()).thenReturn (dps );
517+ Field cbmField = Manager .class .getDeclaredField ("chainBaseManager" );
518+ cbmField .setAccessible (true );
519+ cbmField .set (dbManager , cbm );
520+
521+ EventPluginLoader mockLoader = mock (EventPluginLoader .class );
522+ when (mockLoader .isContractLogTriggerEnable ()).thenReturn (false );
523+ when (mockLoader .isContractEventTriggerEnable ()).thenReturn (false );
524+ when (mockLoader .isSolidityLogTriggerEnable ()).thenReturn (true );
525+ when (mockLoader .isSolidityEventTriggerEnable ()).thenReturn (false );
526+
527+ Field instanceField = EventPluginLoader .class .getDeclaredField ("instance" );
528+ instanceField .setAccessible (true );
529+ EventPluginLoader original = (EventPluginLoader ) instanceField .get (null );
530+ instanceField .set (null , mockLoader );
531+
532+ try {
533+ // null logInfo → processTrigger throws NPE on logInfo.getTopics()
534+ ContractLogTrigger trigger = new ContractLogTrigger ();
535+ trigger .setBlockNumber (300L );
536+ trigger .setTransactionId ("tx-id" );
537+ trigger .setContractAddress ("0x01" );
538+
539+ TransactionTrace traceMock = mock (TransactionTrace .class );
540+ ProgramResult resultMock = mock (ProgramResult .class );
541+ when (traceMock .getRuntimeResult ()).thenReturn (resultMock );
542+ when (resultMock .getTriggerList ())
543+ .thenReturn (Collections .singletonList ((ContractTrigger ) trigger ));
544+
545+ Method method = Manager .class .getDeclaredMethod ("postContractTrigger" ,
546+ TransactionTrace .class , boolean .class , String .class );
547+ method .setAccessible (true );
548+ // catch (Throwable) absorbs the NPE — invocation must complete normally
549+ method .invoke (dbManager , traceMock , false , "blockhash" );
550+ } finally {
551+ instanceField .set (null , original );
552+ eventLoadedField .set (dbManager , false );
553+ }
554+ }
555+
441556}
0 commit comments