From 0640dd6a35b1d23b656b344e00e15ea531d4103a Mon Sep 17 00:00:00 2001 From: zeusoo001 Date: Wed, 5 Feb 2025 17:32:02 +0800 Subject: [PATCH 1/2] feat(event): optimize the event service --- .../common/logsfilter/EventPluginConfig.java | 8 + .../logsfilter/trigger/BlockLogTrigger.java | 2 +- .../logsfilter/trigger/SolidityTrigger.java | 2 +- .../src/main/java/org/tron/core/Constant.java | 2 + .../common/application/ApplicationImpl.java | 6 + .../common/logsfilter/EventPluginLoader.java | 17 + .../java/org/tron/core/config/args/Args.java | 9 + .../main/java/org/tron/core/db/Manager.java | 37 +- .../core/services/event/BlockEventCache.java | 96 +++++ .../core/services/event/BlockEventGet.java | 387 ++++++++++++++++++ .../core/services/event/BlockEventLoad.java | 86 ++++ .../core/services/event/EventService.java | 61 +++ .../services/event/HistoryEventService.java | 76 ++++ .../services/event/RealtimeEventService.java | 120 ++++++ .../services/event/SolidEventService.java | 110 +++++ .../core/services/event/bo/BlockEvent.java | 28 ++ .../tron/core/services/event/bo/Event.java | 14 + .../event/bo/SmartContractTrigger.java | 14 + .../event/exception/EventException.java | 7 + .../tron/core/event/BlockEventCacheTest.java | 90 ++++ .../tron/core/event/BlockEventGetTest.java | 185 +++++++++ .../tron/core/event/BlockEventLoadTest.java | 124 ++++++ .../org/tron/core/event/EventServiceTest.java | 34 ++ .../core/event/HistoryEventServiceTest.java | 85 ++++ .../core/event/RealtimeEventServiceTest.java | 118 ++++++ .../core/event/SolidEventServiceTest.java | 116 ++++++ 26 files changed, 1818 insertions(+), 16 deletions(-) create mode 100644 framework/src/main/java/org/tron/core/services/event/BlockEventCache.java create mode 100644 framework/src/main/java/org/tron/core/services/event/BlockEventGet.java create mode 100644 framework/src/main/java/org/tron/core/services/event/BlockEventLoad.java create mode 100644 framework/src/main/java/org/tron/core/services/event/EventService.java create mode 100644 framework/src/main/java/org/tron/core/services/event/HistoryEventService.java create mode 100644 framework/src/main/java/org/tron/core/services/event/RealtimeEventService.java create mode 100644 framework/src/main/java/org/tron/core/services/event/SolidEventService.java create mode 100644 framework/src/main/java/org/tron/core/services/event/bo/BlockEvent.java create mode 100644 framework/src/main/java/org/tron/core/services/event/bo/Event.java create mode 100644 framework/src/main/java/org/tron/core/services/event/bo/SmartContractTrigger.java create mode 100644 framework/src/main/java/org/tron/core/services/event/exception/EventException.java create mode 100644 framework/src/test/java/org/tron/core/event/BlockEventCacheTest.java create mode 100644 framework/src/test/java/org/tron/core/event/BlockEventGetTest.java create mode 100644 framework/src/test/java/org/tron/core/event/BlockEventLoadTest.java create mode 100644 framework/src/test/java/org/tron/core/event/EventServiceTest.java create mode 100644 framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java create mode 100644 framework/src/test/java/org/tron/core/event/RealtimeEventServiceTest.java create mode 100644 framework/src/test/java/org/tron/core/event/SolidEventServiceTest.java diff --git a/common/src/main/java/org/tron/common/logsfilter/EventPluginConfig.java b/common/src/main/java/org/tron/common/logsfilter/EventPluginConfig.java index 050499b6123..e2aa0950d72 100644 --- a/common/src/main/java/org/tron/common/logsfilter/EventPluginConfig.java +++ b/common/src/main/java/org/tron/common/logsfilter/EventPluginConfig.java @@ -15,6 +15,14 @@ public class EventPluginConfig { public static final String SOLIDITY_EVENT_NAME = "solidityevent"; public static final String SOLIDITY_LOG_NAME = "soliditylog"; + @Getter + @Setter + private int version; + + @Getter + @Setter + private long startSyncBlockNum; + @Getter @Setter private String pluginPath; diff --git a/common/src/main/java/org/tron/common/logsfilter/trigger/BlockLogTrigger.java b/common/src/main/java/org/tron/common/logsfilter/trigger/BlockLogTrigger.java index 5e64c5a1050..b878597045d 100644 --- a/common/src/main/java/org/tron/common/logsfilter/trigger/BlockLogTrigger.java +++ b/common/src/main/java/org/tron/common/logsfilter/trigger/BlockLogTrigger.java @@ -34,7 +34,7 @@ public BlockLogTrigger() { @Override public String toString() { return new StringBuilder().append("triggerName: ").append(getTriggerName()) - .append("timestamp: ") + .append(", timestamp: ") .append(timeStamp) .append(", blockNumber: ") .append(blockNumber) diff --git a/common/src/main/java/org/tron/common/logsfilter/trigger/SolidityTrigger.java b/common/src/main/java/org/tron/common/logsfilter/trigger/SolidityTrigger.java index d9745f4724a..230544a91ff 100644 --- a/common/src/main/java/org/tron/common/logsfilter/trigger/SolidityTrigger.java +++ b/common/src/main/java/org/tron/common/logsfilter/trigger/SolidityTrigger.java @@ -16,7 +16,7 @@ public SolidityTrigger() { @Override public String toString() { return new StringBuilder().append("triggerName: ").append(getTriggerName()) - .append("timestamp: ") + .append(", timestamp: ") .append(timeStamp) .append(", latestSolidifiedBlockNumber: ") .append(latestSolidifiedBlockNumber).toString(); diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 3bdbf2113af..00f9723abed 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -280,6 +280,8 @@ public class Constant { public static final String NATIVE_QUEUE_SEND_LENGTH = "event.subscribe.native.sendqueuelength"; + public static final String EVENT_SUBSCRIBE_VERSION = "event.subscribe.version"; + public static final String EVENT_SUBSCRIBE_START_SYNC_BLOCK_NUM = "event.subscribe.startSyncBlockNum"; public static final String EVENT_SUBSCRIBE_PATH = "event.subscribe.path"; public static final String EVENT_SUBSCRIBE_SERVER = "event.subscribe.server"; public static final String EVENT_SUBSCRIBE_DB_CONFIG = "event.subscribe.dbconfig"; diff --git a/framework/src/main/java/org/tron/common/application/ApplicationImpl.java b/framework/src/main/java/org/tron/common/application/ApplicationImpl.java index 3cb75cb1e24..443f93e446d 100644 --- a/framework/src/main/java/org/tron/common/application/ApplicationImpl.java +++ b/framework/src/main/java/org/tron/common/application/ApplicationImpl.java @@ -10,6 +10,7 @@ import org.tron.core.db.Manager; import org.tron.core.metrics.MetricsUtil; import org.tron.core.net.TronNetService; +import org.tron.core.services.event.EventService; @Slf4j(topic = "app") @Component @@ -17,6 +18,9 @@ public class ApplicationImpl implements Application { private ServiceContainer services; + @Autowired + private EventService eventService; + @Autowired private TronNetService tronNetService; @@ -56,6 +60,7 @@ public void initServices(CommonParameter parameter) { public void startup() { this.initServices(Args.getInstance()); this.startServices(); + eventService.init(); if ((!Args.getInstance().isSolidityNode()) && (!Args.getInstance().isP2pDisable())) { tronNetService.start(); } @@ -66,6 +71,7 @@ public void startup() { @Override public void shutdown() { this.shutdownServices(); + eventService.close(); consensusService.stop(); if (!Args.getInstance().isSolidityNode() && (!Args.getInstance().p2pDisable)) { tronNetService.close(); diff --git a/framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java b/framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java index c8019dac93a..7896eeffae4 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java +++ b/framework/src/main/java/org/tron/common/logsfilter/EventPluginLoader.java @@ -24,6 +24,7 @@ import org.tron.common.logsfilter.trigger.SolidityTrigger; import org.tron.common.logsfilter.trigger.TransactionLogTrigger; import org.tron.common.logsfilter.trigger.Trigger; +import org.tron.common.utils.JsonUtil; @Slf4j public class EventPluginLoader { @@ -42,6 +43,10 @@ public class EventPluginLoader { private List triggerConfigList; + private int version = 0; + + private long startSyncBlockNum = 0; + private boolean blockLogTriggerEnable = false; private boolean blockLogTriggerSolidified = false; @@ -219,6 +224,10 @@ public boolean start(EventPluginConfig config) { return false; } + this.version = config.getVersion(); + + this.startSyncBlockNum = config.getStartSyncBlockNum(); + this.triggerConfigList = config.getTriggerConfigList(); useNativeQueue = config.isUseNativeQueue(); @@ -358,6 +367,14 @@ public void postSolidityTrigger(SolidityTrigger trigger) { } } + public synchronized int getVersion() { + return version; + } + + public synchronized long getStartSyncBlockNum() { + return startSyncBlockNum; + } + public synchronized boolean isBlockLogTriggerEnable() { return blockLogTriggerEnable; } 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 7397b69cde3..2dbfc04e8d5 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 @@ -1323,6 +1323,15 @@ private static EventPluginConfig getEventPluginConfig( final com.typesafe.config.Config config) { EventPluginConfig eventPluginConfig = new EventPluginConfig(); + if (config.hasPath(Constant.EVENT_SUBSCRIBE_VERSION)) { + eventPluginConfig.setVersion(config.getInt(Constant.EVENT_SUBSCRIBE_VERSION)); + } + + if (config.hasPath(Constant.EVENT_SUBSCRIBE_START_SYNC_BLOCK_NUM)) { + eventPluginConfig.setStartSyncBlockNum(config + .getLong(Constant.EVENT_SUBSCRIBE_START_SYNC_BLOCK_NUM)); + } + boolean useNativeQueue = false; int bindPort = 0; int sendQueueLength = 0; diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 908e248bdee..f6076c1f292 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -237,6 +237,7 @@ public class Manager { Collections.synchronizedList(Lists.newArrayList()); // the capacity is equal to Integer.MAX_VALUE default private BlockingQueue rePushTransactions; + @Getter private BlockingQueue triggerCapsuleQueue; // log filter private boolean isRunFilterProcessThread = true; @@ -1100,7 +1101,9 @@ private void switchFork(BlockCapsule newHead) while (!getDynamicPropertiesStore() .getLatestBlockHeaderHash() .equals(binaryTree.getValue().peekLast().getParentHash())) { - reOrgContractTrigger(); + if (EventPluginLoader.getInstance().getVersion() == 0) { + reOrgContractTrigger(); + } reOrgLogsFilter(); eraseBlock(); } @@ -1362,11 +1365,26 @@ public void pushBlock(final BlockCapsule block) } void blockTrigger(final BlockCapsule block, long oldSolid, long newSolid) { + // post block and logs for jsonrpc try { + if (CommonParameter.getInstance().isJsonRpcHttpFullNodeEnable()) { + postBlockFilter(block, false); + postLogsFilter(block, false, false); + } + + if (CommonParameter.getInstance().isJsonRpcHttpSolidityNodeEnable()) { + postSolidityFilter(oldSolid, newSolid); + } + + if (EventPluginLoader.getInstance().getVersion() != 0) { + lastUsedSolidityNum = newSolid; + return; + } + // if event subscribe is enabled, post block trigger to queue postBlockTrigger(block); // if event subscribe is enabled, post solidity trigger to queue - postSolidityTrigger(oldSolid, newSolid); + postSolidityTrigger(newSolid); } catch (Exception e) { logger.error("Block trigger failed. head: {}, oldSolid: {}, newSolid: {}", block.getNum(), oldSolid, newSolid, e); @@ -1506,7 +1524,8 @@ public TransactionInfo processTransaction(final TransactionCapsule trxCap, Block // if event subscribe is enabled, post contract triggers to queue // only trigger when process block - if (Objects.nonNull(blockCap) && !blockCap.isMerkleRootEmpty()) { + if (Objects.nonNull(blockCap) && !blockCap.isMerkleRootEmpty() + && EventPluginLoader.getInstance().getVersion() == 0) { String blockHash = blockCap.getBlockId().toString(); postContractTrigger(trace, false, blockHash); } @@ -2083,7 +2102,7 @@ private void postSolidityFilter(final long oldSolidNum, final long latestSolidif } } - private void postSolidityTrigger(final long oldSolidNum, final long latestSolidifiedBlockNumber) { + private void postSolidityTrigger(final long latestSolidifiedBlockNumber) { if (eventPluginLoaded && EventPluginLoader.getInstance().isSolidityLogTriggerEnable()) { for (Long i : Args.getSolidityContractLogTriggerMap().keySet()) { postSolidityLogContractTrigger(i, latestSolidifiedBlockNumber); @@ -2109,10 +2128,6 @@ private void postSolidityTrigger(final long oldSolidNum, final long latestSolidi } } } - - if (CommonParameter.getInstance().isJsonRpcHttpSolidityNodeEnable()) { - postSolidityFilter(oldSolidNum, latestSolidifiedBlockNumber); - } lastUsedSolidityNum = latestSolidifiedBlockNumber; } @@ -2224,12 +2239,6 @@ private void postLogsFilter(final BlockCapsule blockCapsule, boolean solidified, } void postBlockTrigger(final BlockCapsule blockCapsule) { - // post block and logs for jsonrpc - if (CommonParameter.getInstance().isJsonRpcHttpFullNodeEnable()) { - postBlockFilter(blockCapsule, false); - postLogsFilter(blockCapsule, false, false); - } - // process block trigger long solidityBlkNum = getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); if (eventPluginLoaded && EventPluginLoader.getInstance().isBlockLogTriggerEnable()) { diff --git a/framework/src/main/java/org/tron/core/services/event/BlockEventCache.java b/framework/src/main/java/org/tron/core/services/event/BlockEventCache.java new file mode 100644 index 00000000000..40abae07946 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/BlockEventCache.java @@ -0,0 +1,96 @@ +package org.tron.core.services.event; + +import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.services.event.exception.EventException; + +@Slf4j(topic = "event") +public class BlockEventCache { + @Getter + private static volatile long solidNum; + + @Getter + private static volatile BlockEvent head; + + @Getter + private static volatile BlockCapsule.BlockId solidId; + + private static Map blockEventMap = new ConcurrentHashMap<>(); + + private static Map> numMap = new ConcurrentHashMap<>(); + + public static BlockEvent getBlockEvent(BlockCapsule.BlockId blockId) { + return blockEventMap.get(blockId); + } + + public static void init(BlockCapsule.BlockId blockId) { + blockEventMap.clear(); + numMap.clear(); + solidNum = blockId.getNum(); + head = new BlockEvent(blockId); + solidId = blockId; + List list = new ArrayList<>(); + list.add(head); + numMap.put(blockId.getNum(), list); + blockEventMap.put(blockId, head); + } + + public static void add(BlockEvent blockEvent) throws EventException { + logger.info("Add block event, {}", blockEvent.getBlockId().getString(), + blockEvent.getParentId().getString()); + if (blockEventMap.get(blockEvent.getParentId()) == null) { + throw new EventException("unlink BlockEvent, " + + blockEvent.getBlockId().getString() + ", " + + blockEvent.getParentId().getString()); + } + + long num = blockEvent.getBlockId().getNum(); + List list = numMap.get(num); + if (list == null) { + list = new ArrayList<>(); + numMap.put(num, list); + } + list.add(blockEvent); + + blockEventMap.put(blockEvent.getBlockId(), blockEvent); + + if (num > head.getBlockId().getNum()) { + head = blockEvent; + } + + if (blockEvent.getSolidId().getNum() > solidId.getNum()) { + solidId = blockEvent.getSolidId(); + } + } + + public static void remove(BlockCapsule.BlockId solidId) { + logger.info("Remove solidId {}, solidNum {}, {}, {}", + solidId.getString(), solidNum, numMap.size(), blockEventMap.size()); + numMap.forEach((k, v) -> { + if (k < solidId.getNum()) { + v.forEach(value -> blockEventMap.remove(value.getBlockId())); + numMap.remove(k); + } + }); + solidNum = solidId.getNum(); + } + + public static List getSolidBlockEvents(BlockCapsule.BlockId solidId) { + List blockEvents = new ArrayList<>(); + BlockCapsule.BlockId tmp = solidId; + while (tmp.getNum() > solidNum) { + BlockEvent blockEvent = blockEventMap.get(tmp); + blockEvents.add(blockEvent); + tmp = blockEvent.getParentId(); + } + + return Lists.reverse(blockEvents); + } +} diff --git a/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java b/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java new file mode 100644 index 00000000000..8ffc333eda4 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java @@ -0,0 +1,387 @@ +package org.tron.core.services.event; + +import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.util.encoders.Hex; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.api.GrpcAPI; +import org.tron.common.crypto.Hash; +import org.tron.common.logsfilter.ContractEventParserAbi; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.logsfilter.capsule.BlockLogTriggerCapsule; +import org.tron.common.logsfilter.capsule.RawData; +import org.tron.common.logsfilter.capsule.SolidityTriggerCapsule; +import org.tron.common.logsfilter.capsule.TransactionLogTriggerCapsule; +import org.tron.common.logsfilter.trigger.ContractEventTrigger; +import org.tron.common.logsfilter.trigger.ContractLogTrigger; +import org.tron.common.logsfilter.trigger.ContractTrigger; +import org.tron.common.runtime.vm.DataWord; +import org.tron.common.runtime.vm.LogInfo; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.StringUtil; +import org.tron.core.capsule.AbiCapsule; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.ContractCapsule; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.capsule.TransactionRetCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.db.Manager; +import org.tron.core.db.TransactionTrace; +import org.tron.core.exception.BadItemException; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.services.event.bo.SmartContractTrigger; +import org.tron.core.store.StoreFactory; +import org.tron.protos.Protocol; +import org.tron.protos.contract.SmartContractOuterClass; + +@Slf4j(topic = "event") +@Component +public class BlockEventGet { + + private EventPluginLoader instance = EventPluginLoader.getInstance(); + + @Autowired + private Manager manager; + + public BlockEvent getBlockEvent(long blockNum) throws Exception { + BlockCapsule block = manager.getChainBaseManager().getBlockByNum(blockNum); + long solidNum = manager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); + BlockEvent blockEvent = new BlockEvent(); + blockEvent.setBlockId(block.getBlockId()); + blockEvent.setParentId(block.getParentBlockId()); + blockEvent.setSolidId(manager.getChainBaseManager().getBlockIdByNum(solidNum)); + blockEvent.setBlockTime(block.getTimeStamp()); + if (instance.isBlockLogTriggerEnable()) { + blockEvent.setBlockLogTriggerCapsule(getBlockLogTrigger(block, solidNum)); + } + + if (instance.isTransactionLogTriggerEnable()) { + blockEvent.setTransactionLogTriggerCapsules(getTransactionLogTrigger(block, solidNum)); + } + + if (instance.isContractLogTriggerEnable() + || instance.isContractEventTriggerEnable() + || instance.isSolidityLogTriggerEnable() + || instance.isSolidityEventTriggerEnable()) { + blockEvent.setSmartContractTrigger(getContractTrigger(block, solidNum)); + } + + if (instance.isSolidityTriggerEnable()) { + SolidityTriggerCapsule capsule = new SolidityTriggerCapsule(block.getNum()); + capsule.setTimeStamp(block.getTimeStamp()); + blockEvent.setSolidityTriggerCapsule(capsule); + } + + return blockEvent; + } + + public SmartContractTrigger getContractTrigger(BlockCapsule block, long solidNum) { + TransactionRetCapsule result; + try { + result = manager.getChainBaseManager().getTransactionRetStore() + .getTransactionInfoByBlockNum(ByteArray.fromLong(block.getNum())); + } catch (BadItemException e) { + throw new RuntimeException(e); + } + + SmartContractTrigger contractTrigger = new SmartContractTrigger(); + for (int i = 0; i < block.getTransactions().size(); i++) { + Protocol.Transaction tx = block.getInstance().getTransactions(i); + Protocol.TransactionInfo txInfo = result.getInstance().getTransactioninfo(i); + + List triggers = parseLogs(tx, txInfo); + for (ContractTrigger trigger : triggers) { + if (!EventPluginLoader.matchFilter(trigger)) { + continue; + } + ContractTrigger eventOrLog = processTrigger(trigger); + eventOrLog.setBlockHash(Hex.toHexString(block.getBlockId().getBytes())); + eventOrLog.setLatestSolidifiedBlockNumber(solidNum); + if (eventOrLog instanceof ContractEventTrigger) { + ContractEventTrigger event = (ContractEventTrigger) eventOrLog; + if (instance.isContractEventTriggerEnable() || instance.isSolidityEventTriggerEnable()) { + contractTrigger.getContractEventTriggers().add(event); + } + if ((instance.isContractLogTriggerEnable() + && instance.isContractLogTriggerRedundancy()) + || (instance.isSolidityLogTriggerEnable() + && instance.isSolidityLogTriggerRedundancy())) { + ContractLogTrigger logTrigger = new ContractLogTrigger(event); + logTrigger.setTopicList(trigger.getLogInfo().getHexTopics()); + logTrigger.setData(trigger.getLogInfo().getHexData()); + contractTrigger.getRedundancies().add(logTrigger); + } + } else if (eventOrLog instanceof ContractLogTrigger) { + ContractLogTrigger log = (ContractLogTrigger) eventOrLog; + if (instance.isContractLogTriggerEnable() || instance.isSolidityLogTriggerEnable()) { + contractTrigger.getContractLogTriggers().add(log); + } + } + } + } + + return contractTrigger; + } + + private List parseLogs(Protocol.Transaction tx, + Protocol.TransactionInfo txInfo) { + String originAddress = StringUtil + .encode58Check(TransactionCapsule.getOwner(tx.getRawData().getContract(0))); + + List logs = txInfo.getLogList(); + List list = new LinkedList<>(); + if (logs.isEmpty()) { + return list; + } + + Map addrMap = new HashMap<>(); + Map abiMap = new HashMap<>(); + + for (Protocol.TransactionInfo.Log log : logs) { + + byte[] contractAddress = TransactionTrace + .convertToTronAddress(log.getAddress().toByteArray()); + String strContractAddr = + ArrayUtils.isEmpty(contractAddress) ? "" : StringUtil.encode58Check(contractAddress); + if (addrMap.get(strContractAddr) != null) { + continue; + } + ContractCapsule contract = manager.getContractStore().get(contractAddress); + if (contract == null) { + // never + addrMap.put(strContractAddr, originAddress); + abiMap.put(strContractAddr, SmartContractOuterClass.SmartContract.ABI.getDefaultInstance()); + continue; + } + AbiCapsule abiCapsule = StoreFactory.getInstance().getChainBaseManager() + .getAbiStore().get(contractAddress); + SmartContractOuterClass.SmartContract.ABI abi; + if (abiCapsule == null || abiCapsule.getInstance() == null) { + abi = SmartContractOuterClass.SmartContract.ABI.getDefaultInstance(); + } else { + abi = abiCapsule.getInstance(); + } + String creatorAddr = StringUtil.encode58Check(TransactionTrace + .convertToTronAddress(contract.getInstance().getOriginAddress().toByteArray())); + addrMap.put(strContractAddr, creatorAddr); + abiMap.put(strContractAddr, abi); + } + + int index = 1; + for (Protocol.TransactionInfo.Log log : logs) { + + byte[] contractAddress = TransactionTrace + .convertToTronAddress(log.getAddress().toByteArray()); + String strContractAddress = + ArrayUtils.isEmpty(contractAddress) ? "" : StringUtil.encode58Check(contractAddress); + SmartContractOuterClass.SmartContract.ABI abi = abiMap.get(strContractAddress); + ContractTrigger event = new ContractTrigger(); + String creatorAddr = addrMap.get(strContractAddress); + String txId = Hex.toHexString(txInfo.getId().toByteArray()); + event.setUniqueId(txId + "_" + index); + event.setTransactionId(txId); + event.setContractAddress(strContractAddress); + event.setOriginAddress(originAddress); + event.setCallerAddress(""); + event.setCreatorAddress(StringUtils.isEmpty(creatorAddr) ? "" : creatorAddr); + event.setBlockNumber(txInfo.getBlockNumber()); + event.setTimeStamp(txInfo.getBlockTimeStamp()); + event.setLogInfo(buildLogInfo(log)); + event.setAbi(abi); + + list.add(event); + index++; + } + + return list; + } + + private LogInfo buildLogInfo(Protocol.TransactionInfo.Log log) { + List topics = Lists.newArrayList(); + log.getTopicsList().forEach(topic -> + topics.add(new DataWord(topic.toByteArray())) + ); + byte[] address = log.getAddress().toByteArray(); + byte[] data = log.getData().toByteArray(); + return new LogInfo(address, topics, data); + } + + private ContractTrigger processTrigger(ContractTrigger contractTrigger) { + ContractTrigger event; + boolean isEvent = false; + LogInfo logInfo = contractTrigger.getLogInfo(); + SmartContractOuterClass.SmartContract.ABI abi = contractTrigger.getAbi(); + List topics = logInfo.getTopics(); + + String eventSignature = ""; + String eventSignatureFull = "fallback()"; + String entryName = ""; + SmartContractOuterClass.SmartContract.ABI.Entry eventEntry = null; + + if (abi != null && abi.getEntrysCount() > 0 && topics != null && !topics.isEmpty() + && !ArrayUtils.isEmpty(topics.get(0).getData()) + && Args.getInstance().getStorage().isContractParseSwitch()) { + String logHash = topics.get(0).toString(); + + for (SmartContractOuterClass.SmartContract.ABI.Entry entry : abi.getEntrysList()) { + if (entry.getType() != SmartContractOuterClass.SmartContract.ABI.Entry.EntryType.Event + || entry.getAnonymous()) { + continue; + } + + String signature = entry.getName() + "("; + String signatureFull = entry.getName() + "("; + StringBuilder signBuilder = new StringBuilder(); + StringBuilder signFullBuilder = new StringBuilder(); + for (SmartContractOuterClass.SmartContract.ABI.Entry.Param param : entry.getInputsList()) { + if (signBuilder.length() > 0) { + signBuilder.append(","); + signFullBuilder.append(","); + } + String type = param.getType(); + String name = param.getName(); + signBuilder.append(type); + signFullBuilder.append(type); + if (org.pf4j.util.StringUtils.isNotNullOrEmpty(name)) { + signFullBuilder.append(" ").append(name); + } + } + signature += signBuilder + ")"; + signatureFull += signFullBuilder + ")"; + String sha3 = Hex.toHexString(Hash.sha3(signature.getBytes())); + if (sha3.equals(logHash)) { + eventSignature = signature; + eventSignatureFull = signatureFull; + entryName = entry.getName(); + eventEntry = entry; + isEvent = true; + break; + } + } + } + + if (isEvent) { + event = new ContractEventTrigger(); + ((ContractEventTrigger) event).setEventSignature(eventSignature); + ((ContractEventTrigger) event).setEventSignatureFull(eventSignatureFull); + ((ContractEventTrigger) event).setEventName(entryName); + + List topicList = logInfo.getClonedTopics(); + byte[] data = logInfo.getClonedData(); + + ((ContractEventTrigger) event) + .setTopicMap(ContractEventParserAbi.parseTopics(topicList, eventEntry)); + ((ContractEventTrigger) event) + .setDataMap(ContractEventParserAbi.parseEventData(data, topicList, eventEntry)); + } else { + event = new ContractLogTrigger(); + ((ContractLogTrigger) event).setTopicList(logInfo.getHexTopics()); + ((ContractLogTrigger) event).setData(logInfo.getHexData()); + } + + RawData rawData = new RawData(logInfo.getAddress(), logInfo.getTopics(), logInfo.getData()); + + event.setRawData(rawData); + + event.setLatestSolidifiedBlockNumber(contractTrigger.getLatestSolidifiedBlockNumber()); + event.setRemoved(contractTrigger.isRemoved()); + event.setUniqueId(contractTrigger.getUniqueId()); + event.setTransactionId(contractTrigger.getTransactionId()); + event.setContractAddress(contractTrigger.getContractAddress()); + event.setOriginAddress(contractTrigger.getOriginAddress()); + event.setCallerAddress(""); + event.setCreatorAddress(contractTrigger.getCreatorAddress()); + event.setBlockNumber(contractTrigger.getBlockNumber()); + event.setTimeStamp(contractTrigger.getTimeStamp()); + event.setBlockHash(contractTrigger.getBlockHash()); + + return event; + } + + public BlockLogTriggerCapsule getBlockLogTrigger(BlockCapsule block, long solidNum) { + BlockLogTriggerCapsule blockLogTriggerCapsule = new BlockLogTriggerCapsule(block); + blockLogTriggerCapsule.setLatestSolidifiedBlockNumber(solidNum); + return blockLogTriggerCapsule; + } + + public List getTransactionLogTrigger(BlockCapsule block, + long solidNum) { + List transactionLogTriggerCapsules = new ArrayList<>(); + if (!EventPluginLoader.getInstance().isTransactionLogTriggerEthCompatible()) { + for (TransactionCapsule t : block.getTransactions()) { + TransactionLogTriggerCapsule trx = new TransactionLogTriggerCapsule(t, block); + trx.setLatestSolidifiedBlockNumber(solidNum); + transactionLogTriggerCapsules.add(trx); + } + return transactionLogTriggerCapsules; + } + List transactionCapsuleList = block.getTransactions(); + GrpcAPI.TransactionInfoList transactionInfoList = GrpcAPI + .TransactionInfoList.newBuilder().build(); + GrpcAPI.TransactionInfoList.Builder transactionInfoListBuilder = GrpcAPI + .TransactionInfoList.newBuilder(); + try { + TransactionRetCapsule result = manager.getChainBaseManager().getTransactionRetStore() + .getTransactionInfoByBlockNum(ByteArray.fromLong(block.getNum())); + if (!Objects.isNull(result) && !Objects.isNull(result.getInstance())) { + result.getInstance().getTransactioninfoList() + .forEach(transactionInfoListBuilder::addTransactionInfo); + transactionInfoList = transactionInfoListBuilder.build(); + } + } catch (BadItemException e) { + logger.error("Get TransactionInfo failed, blockNum {}, {}.", block.getNum(), e.getMessage()); + } + if (transactionCapsuleList.size() != transactionInfoList.getTransactionInfoCount()) { + logger.error("Get TransactionInfo size not eq, blockNum {}, {}, {}", + block.getNum(), transactionCapsuleList.size(), + transactionInfoList.getTransactionInfoCount()); + for (TransactionCapsule t : block.getTransactions()) { + TransactionLogTriggerCapsule trx = new TransactionLogTriggerCapsule(t, block); + trx.setLatestSolidifiedBlockNumber(solidNum); + transactionLogTriggerCapsules.add(trx); + } + return transactionLogTriggerCapsules; + } + long cumulativeEnergyUsed = 0; + long cumulativeLogCount = 0; + long energyUnitPrice = getEnergyPrice(block.getTimeStamp()); + for (int i = 0; i < transactionCapsuleList.size(); i++) { + Protocol.TransactionInfo transactionInfo = transactionInfoList.getTransactionInfo(i); + TransactionCapsule transactionCapsule = transactionCapsuleList.get(i); + transactionCapsule.setBlockNum(block.getNum()); + TransactionLogTriggerCapsule trx = new TransactionLogTriggerCapsule(transactionCapsule, block, + i, cumulativeEnergyUsed, cumulativeLogCount, transactionInfo, energyUnitPrice); + trx.setLatestSolidifiedBlockNumber(solidNum); + cumulativeEnergyUsed += trx.getTransactionLogTrigger().getEnergyUsageTotal(); + cumulativeLogCount += transactionInfo.getLogCount(); + transactionLogTriggerCapsules.add(trx); + } + return transactionLogTriggerCapsules; + } + + public long getEnergyPrice(long blockTime) { + String energyPriceHistory = manager.getDynamicPropertiesStore().getEnergyPriceHistory(); + + String[] energyPrices = energyPriceHistory.split(","); + String[] lastPrice = energyPrices[energyPrices.length - 1].split(":"); + long energyPrice = Long.parseLong(lastPrice[1]); + + for (int i = 1; i < energyPrices.length; i++) { + long effectiveTime = Long.parseLong(energyPrices[i].split(":")[0]); + if (blockTime < effectiveTime) { + energyPrice = Long.parseLong(energyPrices[i - 1].split(":")[1]); + break; + } + } + return energyPrice; + } +} \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/event/BlockEventLoad.java b/framework/src/main/java/org/tron/core/services/event/BlockEventLoad.java new file mode 100644 index 00000000000..1af9d768c19 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/BlockEventLoad.java @@ -0,0 +1,86 @@ +package org.tron.core.services.event; + +import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.tron.common.es.ExecutorServiceManager; +import org.tron.core.db.Manager; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.services.event.bo.Event; + +@Service +@Slf4j(topic = "event") +public class BlockEventLoad { + + @Autowired + private Manager manager; + + @Autowired + private RealtimeEventService realtimeEventService; + + @Autowired + private BlockEventGet blockEventGet; + + private final ScheduledExecutorService executor = ExecutorServiceManager + .newSingleThreadScheduledExecutor("event-load"); + + public void init() { + executor.scheduleWithFixedDelay(() -> { + try { + load(); + } catch (Exception exception) { + close(); + logger.error("Spread thread error", exception); + } + }, 100, 100, TimeUnit.MILLISECONDS); + logger.info("Event load service start."); + } + + public void close() { + executor.shutdown(); + logger.info("Event load service close."); + } + + public void load() throws Exception { + long cacheHeadNum = BlockEventCache.getHead().getBlockId().getNum(); + long tmpNum = manager.getDynamicPropertiesStore().getLatestBlockHeaderNumber(); + if (cacheHeadNum >= tmpNum) { + return; + } + synchronized (manager) { + tmpNum = manager.getDynamicPropertiesStore().getLatestBlockHeaderNumber(); + if (cacheHeadNum >= tmpNum) { + return; + } + List l1 = new ArrayList<>(); + List l2 = new ArrayList<>(); + BlockEvent tmp = BlockEventCache.getHead(); + + BlockEvent blockEvent = blockEventGet.getBlockEvent(tmpNum); + l1.add(blockEvent); + while (!blockEvent.getParentId().equals(tmp.getBlockId())) { + tmpNum--; + if (tmpNum == tmp.getBlockId().getNum()) { + l2.add(tmp); + tmp = BlockEventCache.getBlockEvent(tmp.getParentId()); + } + blockEvent = blockEventGet.getBlockEvent(tmpNum); + l1.add(blockEvent); + } + + l2.forEach(e -> realtimeEventService.add(new Event(e, true))); + + List l = Lists.reverse(l1); + for (BlockEvent e: l) { + BlockEventCache.add(e); + realtimeEventService.add(new Event(e, false)); + } + } + } + +} diff --git a/framework/src/main/java/org/tron/core/services/event/EventService.java b/framework/src/main/java/org/tron/core/services/event/EventService.java new file mode 100644 index 00000000000..b2e01ab3a9d --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/EventService.java @@ -0,0 +1,61 @@ +package org.tron.core.services.event; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.core.db.Manager; + +@Slf4j(topic = "event") +@Component +public class EventService { + @Autowired + private RealtimeEventService realtimeEventService; + + @Autowired + private BlockEventLoad blockEventLoad; + + @Autowired + private HistoryEventService historyEventService; + + @Autowired + private Manager manager; + + public void init() { + logger.info("Start to load eventPlugin. {}, {}, {} " + + "block: {}, {} trx: {}, {}, {} event: {}, {} log: {}, {}, {}, {} solid: {}", + manager.isEventPluginLoaded(), + + EventPluginLoader.getInstance().getVersion(), + EventPluginLoader.getInstance().getStartSyncBlockNum(), + + EventPluginLoader.getInstance().isBlockLogTriggerEnable(), + EventPluginLoader.getInstance().isBlockLogTriggerSolidified(), + + EventPluginLoader.getInstance().isTransactionLogTriggerEnable(), + EventPluginLoader.getInstance().isTransactionLogTriggerSolidified(), + EventPluginLoader.getInstance().isTransactionLogTriggerEthCompatible(), + + EventPluginLoader.getInstance().isContractEventTriggerEnable(), + EventPluginLoader.getInstance().isSolidityEventTriggerEnable(), + + EventPluginLoader.getInstance().isContractLogTriggerEnable(), + EventPluginLoader.getInstance().isContractLogTriggerRedundancy(), + EventPluginLoader.getInstance().isSolidityLogTriggerEnable(), + EventPluginLoader.getInstance().isSolidityLogTriggerRedundancy(), + + EventPluginLoader.getInstance().isSolidityTriggerEnable()); + + if (!manager.isEventPluginLoaded() || EventPluginLoader.getInstance().getVersion() != 1) { + return; + } + + historyEventService.init(); + } + + public void close() { + realtimeEventService.close(); + blockEventLoad.close(); + historyEventService.close(); + } +} diff --git a/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java b/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java new file mode 100644 index 00000000000..0f26490d16b --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java @@ -0,0 +1,76 @@ +package org.tron.core.services.event; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.db.Manager; +import org.tron.core.services.event.bo.BlockEvent; + +@Slf4j(topic = "event") +@Component +public class HistoryEventService { + + private EventPluginLoader instance = EventPluginLoader.getInstance(); + + @Autowired + private BlockEventGet blockEventGet; + + @Autowired + private SolidEventService solidEventService; + + @Autowired + private RealtimeEventService realtimeEventService; + + @Autowired + private BlockEventLoad blockEventLoad; + + @Autowired + private Manager manager; + + private volatile boolean isRunning; + + public void init() { + if (instance.getStartSyncBlockNum() <= 0) { + initEventService(manager.getChainBaseManager().getHeadBlockId()); + return; + } + + isRunning = true; + + new Thread(() -> syncEvent()).start(); + + logger.info("History event service start."); + } + + public void close() { + isRunning = false; + } + + private void syncEvent() { + try { + long tmp = instance.getStartSyncBlockNum(); + long endNum = manager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); + while (tmp <= endNum && isRunning) { + BlockEvent blockEvent = blockEventGet.getBlockEvent(tmp); + realtimeEventService.flush(blockEvent, false); + solidEventService.flush(blockEvent); + tmp++; + endNum = manager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); + Thread.sleep(30); + } + initEventService(manager.getChainBaseManager().getBlockIdByNum(endNum)); + } catch (Exception e) { + logger.error("Sync event failed.", e); + } + } + + private void initEventService(BlockCapsule.BlockId blockId) { + logger.info("Init event service, {}", blockId.getString()); + BlockEventCache.init(blockId); + realtimeEventService.init(); + blockEventLoad.init(); + solidEventService.init(); + } +} diff --git a/framework/src/main/java/org/tron/core/services/event/RealtimeEventService.java b/framework/src/main/java/org/tron/core/services/event/RealtimeEventService.java new file mode 100644 index 00000000000..8375b2fd114 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/RealtimeEventService.java @@ -0,0 +1,120 @@ +package org.tron.core.services.event; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.common.es.ExecutorServiceManager; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.logsfilter.trigger.Trigger; +import org.tron.core.db.Manager; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.services.event.bo.Event; + +@Slf4j(topic = "event") +@Component +public class RealtimeEventService { + + private EventPluginLoader instance = EventPluginLoader.getInstance(); + + @Autowired + private Manager manager; + + @Autowired + private SolidEventService solidEventService; + + private static BlockingQueue queue = new LinkedBlockingQueue<>(); + + private int maxEventSize = 10000; + + private final ScheduledExecutorService executor = ExecutorServiceManager + .newSingleThreadScheduledExecutor("realtime-event"); + + public void init() { + executor.scheduleWithFixedDelay(() -> { + try { + work(); + } catch (Exception e) { + logger.info("Real-time event service fail. {}", e); + } + }, 1, 1, TimeUnit.SECONDS); + logger.info("Realtime event service start."); + } + + public void close() { + executor.shutdown(); + logger.info("Realtime event service close."); + } + + public void add(Event event) { + if (queue.size() >= maxEventSize) { + logger.warn("Add event failed, blockId {}.", event.getBlockEvent().getBlockId().getString()); + return; + } + queue.offer(event); + } + + public void work() { + while (queue.size() > 0) { + Event event = queue.poll(); + flush(event.getBlockEvent(), event.isRemove()); + } + } + + public void flush(BlockEvent blockEvent, boolean isRemove) { + logger.info("Flush realtime event {}", blockEvent.getBlockId().getString()); + + if (instance.isBlockLogTriggerEnable() + && !instance.isBlockLogTriggerSolidified() + && !isRemove) { + if (blockEvent.getBlockLogTriggerCapsule() == null) { + logger.warn("BlockLogTriggerCapsule is null. {}", blockEvent.getBlockId().getString()); + } else { + manager.getTriggerCapsuleQueue().offer(blockEvent.getBlockLogTriggerCapsule()); + } + } + + if (instance.isTransactionLogTriggerEnable() + && !instance.isTransactionLogTriggerSolidified() + && !isRemove) { + if (blockEvent.getTransactionLogTriggerCapsules() == null) { + logger.info("TransactionLogTriggerCapsules is null. {}", + blockEvent.getBlockId().getString()); + } else { + blockEvent.getTransactionLogTriggerCapsules().forEach(v -> + manager.getTriggerCapsuleQueue().offer(v)); + } + } + + if (instance.isContractEventTriggerEnable()) { + if (blockEvent.getSmartContractTrigger() == null) { + logger.info("SmartContractTrigger is null. {}", blockEvent.getBlockId().getString()); + } else { + blockEvent.getSmartContractTrigger().getContractEventTriggers().forEach(v -> { + v.setTriggerName(Trigger.CONTRACTEVENT_TRIGGER_NAME); + v.setRemoved(isRemove); + EventPluginLoader.getInstance().postContractEventTrigger(v); + }); + } + } + + if (instance.isContractLogTriggerEnable() && blockEvent.getSmartContractTrigger() != null) { + blockEvent.getSmartContractTrigger().getContractLogTriggers().forEach(v -> { + v.setTriggerName(Trigger.CONTRACTLOG_TRIGGER_NAME); + v.setRemoved(isRemove); + EventPluginLoader.getInstance().postContractLogTrigger(v); + }); + if (instance.isContractLogTriggerRedundancy()) { + blockEvent.getSmartContractTrigger().getRedundancies().forEach(v -> { + v.setTriggerName(Trigger.CONTRACTLOG_TRIGGER_NAME); + v.setRemoved(isRemove); + EventPluginLoader.getInstance().postContractLogTrigger(v); + }); + } + } + } + +} diff --git a/framework/src/main/java/org/tron/core/services/event/SolidEventService.java b/framework/src/main/java/org/tron/core/services/event/SolidEventService.java new file mode 100644 index 00000000000..5bf83ea0da9 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/SolidEventService.java @@ -0,0 +1,110 @@ +package org.tron.core.services.event; + +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.common.es.ExecutorServiceManager; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.logsfilter.trigger.Trigger; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.db.Manager; +import org.tron.core.services.event.bo.BlockEvent; + +@Slf4j(topic = "event") +@Component +public class SolidEventService { + + private EventPluginLoader instance = EventPluginLoader.getInstance(); + + @Autowired + private Manager manager; + + private final ScheduledExecutorService executor = ExecutorServiceManager + .newSingleThreadScheduledExecutor("solid-event"); + + public void init() { + executor.scheduleWithFixedDelay(() -> { + try { + work(); + } catch (Exception exception) { + logger.error("Spread thread error", exception); + } + }, 1, 1, TimeUnit.SECONDS); + logger.info("Solid event service start."); + } + + public void close() { + executor.shutdown(); + logger.info("Solid event service close."); + } + + public void work() { + BlockCapsule.BlockId solidId = BlockEventCache.getSolidId(); + if (solidId.getNum() <= BlockEventCache.getSolidNum()) { + return; + } + + List blockEvents = BlockEventCache.getSolidBlockEvents(solidId); + + blockEvents.forEach(v -> flush(v)); + + BlockEventCache.remove(solidId); + } + + public void flush(BlockEvent blockEvent) { + logger.info("Flush solid event {}", blockEvent.getBlockId().getString()); + + if (instance.isBlockLogTriggerEnable() && instance.isBlockLogTriggerSolidified()) { + if (blockEvent.getBlockLogTriggerCapsule() == null) { + logger.warn("BlockLogTrigger is null. {}", blockEvent.getBlockId()); + } else { + manager.getTriggerCapsuleQueue().offer(blockEvent.getBlockLogTriggerCapsule()); + } + } + + if (instance.isTransactionLogTriggerEnable() && instance.isTransactionLogTriggerSolidified()) { + if (blockEvent.getTransactionLogTriggerCapsules() == null) { + logger.info("TransactionLogTrigger is null. {}", blockEvent.getBlockId()); + } else { + blockEvent.getTransactionLogTriggerCapsules().forEach(v -> + manager.getTriggerCapsuleQueue().offer(v)); + } + } + + if (instance.isSolidityEventTriggerEnable()) { + if (blockEvent.getSmartContractTrigger() == null) { + logger.info("SmartContractTrigger is null. {}", blockEvent.getBlockId()); + } else { + blockEvent.getSmartContractTrigger().getContractEventTriggers().forEach(v -> { + v.setTriggerName(Trigger.SOLIDITYEVENT_TRIGGER_NAME); + EventPluginLoader.getInstance().postSolidityEventTrigger(v); + }); + } + } + + if (instance.isSolidityLogTriggerEnable() && blockEvent.getSmartContractTrigger() != null) { + blockEvent.getSmartContractTrigger().getContractLogTriggers().forEach(v -> { + v.setTriggerName(Trigger.SOLIDITYLOG_TRIGGER_NAME); + EventPluginLoader.getInstance().postSolidityLogTrigger(v); + }); + if (instance.isSolidityLogTriggerRedundancy()) { + blockEvent.getSmartContractTrigger().getRedundancies().forEach(v -> { + v.setTriggerName(Trigger.SOLIDITYLOG_TRIGGER_NAME); + EventPluginLoader.getInstance().postSolidityLogTrigger(v); + }); + } + } + + if (instance.isSolidityTriggerEnable()) { + if (blockEvent.getSolidityTriggerCapsule() == null) { + logger.info("SolidityTrigger is null. {}", blockEvent.getBlockId()); + } else { + manager.getTriggerCapsuleQueue().offer(blockEvent.getSolidityTriggerCapsule()); + } + } + } + +} diff --git a/framework/src/main/java/org/tron/core/services/event/bo/BlockEvent.java b/framework/src/main/java/org/tron/core/services/event/bo/BlockEvent.java new file mode 100644 index 00000000000..9e314bc7d5a --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/bo/BlockEvent.java @@ -0,0 +1,28 @@ +package org.tron.core.services.event.bo; + +import java.util.List; +import lombok.Data; +import org.tron.common.logsfilter.capsule.BlockLogTriggerCapsule; +import org.tron.common.logsfilter.capsule.SolidityTriggerCapsule; +import org.tron.common.logsfilter.capsule.TransactionLogTriggerCapsule; +import org.tron.core.capsule.BlockCapsule; + +@Data +public class BlockEvent { + private BlockCapsule.BlockId blockId; + private BlockCapsule.BlockId parentId; + private BlockCapsule.BlockId solidId; + private long blockTime; + + private BlockLogTriggerCapsule blockLogTriggerCapsule; + private List transactionLogTriggerCapsules; + private SolidityTriggerCapsule solidityTriggerCapsule; + private SmartContractTrigger smartContractTrigger; + + public BlockEvent() {} + + public BlockEvent(BlockCapsule.BlockId blockId) { + this.blockId = blockId; + } +} + diff --git a/framework/src/main/java/org/tron/core/services/event/bo/Event.java b/framework/src/main/java/org/tron/core/services/event/bo/Event.java new file mode 100644 index 00000000000..142e1be3c22 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/bo/Event.java @@ -0,0 +1,14 @@ +package org.tron.core.services.event.bo; + +import lombok.Data; + +@Data +public class Event { + private boolean isRemove; + private BlockEvent blockEvent; + + public Event(BlockEvent blockEvent, boolean isRemove) { + this.blockEvent = blockEvent; + this.isRemove = isRemove; + } +} diff --git a/framework/src/main/java/org/tron/core/services/event/bo/SmartContractTrigger.java b/framework/src/main/java/org/tron/core/services/event/bo/SmartContractTrigger.java new file mode 100644 index 00000000000..7413c309169 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/bo/SmartContractTrigger.java @@ -0,0 +1,14 @@ +package org.tron.core.services.event.bo; + +import java.util.ArrayList; +import java.util.List; +import lombok.Data; +import org.tron.common.logsfilter.trigger.ContractEventTrigger; +import org.tron.common.logsfilter.trigger.ContractLogTrigger; + +@Data +public class SmartContractTrigger { + private List contractLogTriggers = new ArrayList<>(); + private List contractEventTriggers = new ArrayList<>(); + private List redundancies = new ArrayList<>(); +} diff --git a/framework/src/main/java/org/tron/core/services/event/exception/EventException.java b/framework/src/main/java/org/tron/core/services/event/exception/EventException.java new file mode 100644 index 00000000000..09df1d832f4 --- /dev/null +++ b/framework/src/main/java/org/tron/core/services/event/exception/EventException.java @@ -0,0 +1,7 @@ +package org.tron.core.services.event.exception; + +public class EventException extends Exception { + public EventException(String errMsg) { + super(errMsg); + } +} diff --git a/framework/src/test/java/org/tron/core/event/BlockEventCacheTest.java b/framework/src/test/java/org/tron/core/event/BlockEventCacheTest.java new file mode 100644 index 00000000000..a23c4b5c9c8 --- /dev/null +++ b/framework/src/test/java/org/tron/core/event/BlockEventCacheTest.java @@ -0,0 +1,90 @@ +package org.tron.core.event; + +import java.util.List; +import java.util.Random; +import org.junit.Assert; +import org.junit.Test; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.services.event.BlockEventCache; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.services.event.exception.EventException; + +public class BlockEventCacheTest { + + @Test + public void test() throws Exception { + BlockEvent be1 = new BlockEvent(); + BlockCapsule.BlockId b1 = new BlockCapsule.BlockId(getBlockId(), 1); + be1.setBlockId(b1); + be1.setParentId(b1); + try { + BlockEventCache.add(be1); + Assert.fail(); + } catch (Exception e) { + Assert.assertTrue(e instanceof EventException); + } + + BlockEventCache.init(new BlockCapsule.BlockId(getBlockId(), 100)); + + try { + BlockEventCache.add(be1); + Assert.fail(); + } catch (Exception e) { + Assert.assertTrue(e instanceof EventException); + } + + BlockEventCache.init(b1); + + BlockEvent be2 = new BlockEvent(); + BlockCapsule.BlockId b2 = new BlockCapsule.BlockId(getBlockId(), 2); + be2.setBlockId(b2); + be2.setParentId(b1); + be2.setSolidId(b1); + BlockEventCache.add(be2); + Assert.assertEquals(be2, BlockEventCache.getHead()); + Assert.assertEquals(be2, BlockEventCache.getBlockEvent(b2)); + + BlockEvent be22 = new BlockEvent(); + BlockCapsule.BlockId b22 = new BlockCapsule.BlockId(getBlockId(), 2); + be22.setBlockId(b22); + be22.setParentId(b1); + be22.setSolidId(b22); + BlockEventCache.add(be22); + Assert.assertEquals(be2, BlockEventCache.getHead()); + Assert.assertEquals(be22, BlockEventCache.getBlockEvent(b22)); + Assert.assertEquals(b22, BlockEventCache.getSolidId()); + + BlockEvent be3 = new BlockEvent(); + BlockCapsule.BlockId b3 = new BlockCapsule.BlockId(getBlockId(), 3); + be3.setBlockId(b3); + be3.setParentId(b22); + be3.setSolidId(b22); + BlockEventCache.add(be3); + Assert.assertEquals(be3, BlockEventCache.getHead()); + + List list = BlockEventCache.getSolidBlockEvents(b2); + Assert.assertEquals(1, list.size()); + list = BlockEventCache.getSolidBlockEvents(b22); + Assert.assertEquals(1, list.size()); + + list = BlockEventCache.getSolidBlockEvents(b3); + Assert.assertEquals(2, list.size()); + + BlockEventCache.remove(b22); + Assert.assertEquals(2, BlockEventCache.getSolidNum()); + + list = BlockEventCache.getSolidBlockEvents(b2); + Assert.assertEquals(0, list.size()); + list = BlockEventCache.getSolidBlockEvents(b22); + Assert.assertEquals(0, list.size()); + + list = BlockEventCache.getSolidBlockEvents(b3); + Assert.assertEquals(1, list.size()); + } + + public static byte[] getBlockId() { + byte[] id = new byte[32]; + new Random().nextBytes(id); + return id; + } +} diff --git a/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java b/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java new file mode 100644 index 00000000000..db959d9a0fd --- /dev/null +++ b/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java @@ -0,0 +1,185 @@ +package org.tron.core.event; + +import com.google.protobuf.ByteString; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicInteger; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.tron.common.application.TronApplicationContext; +import org.tron.common.logsfilter.EventPluginConfig; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.logsfilter.TriggerConfig; +import org.tron.common.runtime.TvmTestUtils; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.PublicMethod; +import org.tron.core.ChainBaseManager; +import org.tron.core.Constant; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.WitnessCapsule; +import org.tron.core.config.DefaultConfig; +import org.tron.core.config.args.Args; +import org.tron.core.consensus.ConsensusService; +import org.tron.core.db.BlockGenerate; +import org.tron.core.db.Manager; +import org.tron.core.net.TronNetDelegate; +import org.tron.core.services.event.BlockEventGet; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.store.DynamicPropertiesStore; +import org.tron.protos.Protocol; + +@Slf4j +public class BlockEventGetTest extends BlockGenerate { + + @ClassRule + public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + static ChainBaseManager chainManager; + + private final String key = PublicMethod.getRandomPrivateKey(); + private final byte[] privateKey = ByteArray.fromHexString(key); + private final byte[] address = PublicMethod.getAddressByteByPrivateKey(key); + + private final AtomicInteger port = new AtomicInteger(0); + protected String dbPath; + protected Manager dbManager; + long currentHeader = -1; + private TronNetDelegate tronNetDelegate; + private TronApplicationContext context; + + + static LocalDateTime localDateTime = LocalDateTime.now(); + private long time = ZonedDateTime.of(localDateTime, + ZoneId.systemDefault()).toInstant().toEpochMilli(); + + protected void initDbPath() throws IOException { + dbPath = temporaryFolder.newFolder().toString(); + } + + @Before + public void before() throws IOException { + initDbPath(); + logger.info("Full node running."); + Args.setParam(new String[] {"-d", dbPath, "-w"}, Constant.TEST_CONF); + Args.getInstance().setNodeListenPort(10000 + port.incrementAndGet()); + + context = new TronApplicationContext(DefaultConfig.class); + + dbManager = context.getBean(Manager.class); + setManager(dbManager); + + context.getBean(ConsensusService.class).start(); + chainManager = dbManager.getChainBaseManager(); + tronNetDelegate = context.getBean(TronNetDelegate.class); + tronNetDelegate.setExit(false); + currentHeader = dbManager.getDynamicPropertiesStore() + .getLatestBlockHeaderNumberFromDB(); + + ByteString addressBS = ByteString.copyFrom(address); + WitnessCapsule witnessCapsule = new WitnessCapsule(addressBS); + chainManager.getWitnessStore().put(address, witnessCapsule); + chainManager.addWitness(addressBS); + + AccountCapsule accountCapsule = new AccountCapsule(Protocol.Account.newBuilder() + .setAddress(addressBS).setBalance((long) 1e10).build()); + chainManager.getAccountStore().put(address, accountCapsule); + + DynamicPropertiesStore dps = dbManager.getDynamicPropertiesStore(); + dps.saveAllowTvmTransferTrc10(1); + dps.saveAllowTvmConstantinople(1); + dps.saveAllowTvmShangHai(1); + } + + @After + public void after() throws IOException { + } + + @Test + public void test() throws Exception { + BlockEventGet blockEventGet = context.getBean(BlockEventGet.class); + Manager manager = context.getBean(Manager.class); + + WitnessCapsule witnessCapsule = new WitnessCapsule(ByteString.copyFrom(address)); + ChainBaseManager.getChainBaseManager() + .getWitnessScheduleStore().saveActiveWitnesses(new ArrayList<>()); + ChainBaseManager.getChainBaseManager().addWitness(ByteString.copyFrom(address)); + + String code = "608060405234801561000f575f80fd5b50d3801561001b575f80fd5b50d28015610027575f" + + "80fd5b503373ffffffffffffffffffffffffffffffffffffffff165f73ffffffffffffffffffffffff" + + "ffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3" + + "ef6402540be40060405161008a91906100e2565b60405180910390a36100fb565b5f81905091905056" + + "5b5f819050919050565b5f819050919050565b5f6100cc6100c76100c284610097565b6100a9565b61" + + "00a0565b9050919050565b6100dc816100b2565b82525050565b5f6020820190506100f55f83018461" + + "00d3565b92915050565b603e806101075f395ff3fe60806040525f80fdfea26474726f6e582212200c" + + "57c973388f044038eff0e6474425b38037e75e66d6b3047647290605449c7764736f6c63430008140033"; + Protocol.Transaction trx = TvmTestUtils.generateDeploySmartContractAndGetTransaction( + "TestTRC20", address, "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\"" + + ":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"}" + + ",{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\"," + + "\"type\":\"event\"}]", code, 0, (long) 1e9, 100, null, 1); + trx = trx.toBuilder().addRet( + Protocol.Transaction.Result.newBuilder() + .setContractRetValue(Protocol.Transaction.Result.contractResult.SUCCESS_VALUE) + .build()).build(); + + Protocol.Block block = getSignedBlock(witnessCapsule.getAddress(), time, privateKey); + BlockCapsule blockCapsule = new BlockCapsule(block.toBuilder().addTransactions(trx).build()); + blockCapsule.generatedByMyself = true; + blockCapsule.getTransactions().forEach(txCap -> { + txCap.setVerified(true); + chainManager.setBlockReference(txCap); + txCap.setExpiration(3000); + }); + manager.pushBlock(blockCapsule); + + EventPluginConfig config = new EventPluginConfig(); + config.setSendQueueLength(1000); + config.setBindPort(5555); + config.setUseNativeQueue(true); + config.setTriggerConfigList(new ArrayList<>()); + + TriggerConfig blockTriggerConfig = new TriggerConfig(); + blockTriggerConfig.setTriggerName("block"); + blockTriggerConfig.setEnabled(true); + config.getTriggerConfigList().add(blockTriggerConfig); + + TriggerConfig txTriggerConfig = new TriggerConfig(); + txTriggerConfig.setTriggerName("transaction"); + txTriggerConfig.setEnabled(true); + txTriggerConfig.setEthCompatible(true); + config.getTriggerConfigList().add(txTriggerConfig); + + TriggerConfig solidityTriggerConfig = new TriggerConfig(); + solidityTriggerConfig.setTriggerName("solidity"); + solidityTriggerConfig.setEnabled(true); + config.getTriggerConfigList().add(solidityTriggerConfig); + + TriggerConfig contracteventTriggerConfig = new TriggerConfig(); + contracteventTriggerConfig.setTriggerName("contractevent"); + contracteventTriggerConfig.setEnabled(true); + config.getTriggerConfigList().add(contracteventTriggerConfig); + + TriggerConfig contractlogTriggerConfig = new TriggerConfig(); + contractlogTriggerConfig.setTriggerName("contractlog"); + contractlogTriggerConfig.setEnabled(true); + contractlogTriggerConfig.setRedundancy(true); + config.getTriggerConfigList().add(contractlogTriggerConfig); + + EventPluginLoader.getInstance().start(config); + try { + BlockEvent blockEvent = blockEventGet.getBlockEvent(1); + Assert.assertNotNull(blockEvent); + } catch (Exception e) { + Assert.fail(); + } + } +} \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/event/BlockEventLoadTest.java b/framework/src/test/java/org/tron/core/event/BlockEventLoadTest.java new file mode 100644 index 00000000000..991133fee78 --- /dev/null +++ b/framework/src/test/java/org/tron/core/event/BlockEventLoadTest.java @@ -0,0 +1,124 @@ +package org.tron.core.event; + +import static org.mockito.Mockito.mock; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.concurrent.BlockingQueue; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.tron.common.utils.ReflectUtils; +import org.tron.core.ChainBaseManager; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.db.Manager; +import org.tron.core.services.event.BlockEventCache; +import org.tron.core.services.event.BlockEventGet; +import org.tron.core.services.event.BlockEventLoad; +import org.tron.core.services.event.RealtimeEventService; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.services.event.bo.Event; +import org.tron.core.store.DynamicPropertiesStore; + +public class BlockEventLoadTest { + BlockEventLoad blockEventLoad = new BlockEventLoad(); + + @Test + public void test() throws Exception { + Method method = blockEventLoad.getClass().getDeclaredMethod("load"); + method.setAccessible(true); + + RealtimeEventService realtimeEventService = new RealtimeEventService(); + Field field = realtimeEventService.getClass().getDeclaredField("queue"); + field.setAccessible(true); + BlockingQueue queue = (BlockingQueue)field.get(BlockingQueue.class); + + BlockEventGet blockEventGet = mock(BlockEventGet.class); + Manager manager = mock(Manager.class); + ReflectUtils.setFieldValue(blockEventLoad, "realtimeEventService", realtimeEventService); + ReflectUtils.setFieldValue(blockEventLoad, "blockEventGet", blockEventGet); + ReflectUtils.setFieldValue(blockEventLoad, "manager", manager); + + DynamicPropertiesStore dynamicPropertiesStore = mock(DynamicPropertiesStore.class); + ChainBaseManager chainBaseManager = mock(ChainBaseManager.class); + Mockito.when(manager.getDynamicPropertiesStore()).thenReturn(dynamicPropertiesStore); + Mockito.when(manager.getChainBaseManager()).thenReturn(chainBaseManager); + + BlockCapsule.BlockId b0 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 0); + BlockEventCache.init(b0); + + /********** 1 ***********/ + Mockito.when(dynamicPropertiesStore.getLatestBlockHeaderNumber()).thenReturn(0L); + method.invoke(blockEventLoad); + Assert.assertEquals(0, queue.size()); + + /********** 2 ***********/ + BlockEvent be1 = new BlockEvent(); + BlockCapsule.BlockId b1 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 1); + be1.setBlockId(b1); + be1.setParentId(b0); + be1.setSolidId(b0); + + Mockito.when(dynamicPropertiesStore.getLatestBlockHeaderNumber()).thenReturn(1L); + Mockito.when(blockEventGet.getBlockEvent(1L)).thenReturn(be1); + method.invoke(blockEventLoad); + Assert.assertEquals(1, queue.size()); + Assert.assertEquals(b1, BlockEventCache.getHead().getBlockId()); + + /********** 3 ***********/ + BlockEventCache.init(b0); + queue.clear(); + + BlockEvent be2 = new BlockEvent(); + BlockCapsule.BlockId b2 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 2L); + be2.setBlockId(b2); + be2.setParentId(b1); + be2.setSolidId(b0); + + Mockito.when(dynamicPropertiesStore.getLatestBlockHeaderNumber()).thenReturn(2L); + Mockito.when(blockEventGet.getBlockEvent(2L)).thenReturn(be2); + method.invoke(blockEventLoad); + Assert.assertEquals(2, queue.size()); + Assert.assertEquals(b2, BlockEventCache.getHead().getBlockId()); + + /********** 4 ***********/ + BlockEventCache.init(b0); + queue.clear(); + + Mockito.when(dynamicPropertiesStore.getLatestBlockHeaderNumber()).thenReturn(1L); + method.invoke(blockEventLoad); + Assert.assertEquals(1, queue.size()); + queue.clear(); + + BlockEvent be21 = new BlockEvent(); + BlockCapsule.BlockId b21 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 1L); + be21.setBlockId(b21); + be21.setParentId(b0); + be21.setSolidId(b0); + + BlockEvent be22 = new BlockEvent(); + BlockCapsule.BlockId b22 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 2L); + be22.setBlockId(b22); + be22.setParentId(b21); + be22.setSolidId(b21); + + Mockito.when(dynamicPropertiesStore.getLatestBlockHeaderNumber()).thenReturn(2L); + Mockito.when(blockEventGet.getBlockEvent(1L)).thenReturn(be21); + Mockito.when(blockEventGet.getBlockEvent(2L)).thenReturn(be22); + method.invoke(blockEventLoad); + Assert.assertEquals(3, queue.size()); + Assert.assertEquals(b22, BlockEventCache.getHead().getBlockId()); + + Event event = queue.poll(); + Assert.assertEquals(b1, event.getBlockEvent().getBlockId()); + Assert.assertEquals(true, event.isRemove()); + + event = queue.poll(); + Assert.assertEquals(b21, event.getBlockEvent().getBlockId()); + Assert.assertEquals(false, event.isRemove()); + + event = queue.poll(); + Assert.assertEquals(b22, event.getBlockEvent().getBlockId()); + Assert.assertEquals(false, event.isRemove()); + } +} diff --git a/framework/src/test/java/org/tron/core/event/EventServiceTest.java b/framework/src/test/java/org/tron/core/event/EventServiceTest.java new file mode 100644 index 00000000000..b20e33404fd --- /dev/null +++ b/framework/src/test/java/org/tron/core/event/EventServiceTest.java @@ -0,0 +1,34 @@ +package org.tron.core.event; + +import static org.mockito.Mockito.mock; + +import org.junit.Test; +import org.mockito.Mockito; +import org.tron.common.utils.ReflectUtils; +import org.tron.core.db.Manager; +import org.tron.core.services.event.BlockEventLoad; +import org.tron.core.services.event.EventService; +import org.tron.core.services.event.HistoryEventService; +import org.tron.core.services.event.RealtimeEventService; + +public class EventServiceTest { + + @Test + public void test() { + EventService eventService = new EventService(); + HistoryEventService historyEventService = new HistoryEventService(); + RealtimeEventService realtimeEventService = new RealtimeEventService(); + BlockEventLoad blockEventLoad = new BlockEventLoad(); + + ReflectUtils.setFieldValue(eventService, "historyEventService", historyEventService); + ReflectUtils.setFieldValue(eventService, "realtimeEventService", realtimeEventService); + ReflectUtils.setFieldValue(eventService, "blockEventLoad", blockEventLoad); + + Manager manager = mock(Manager.class); + ReflectUtils.setFieldValue(eventService, "manager", manager); + Mockito.when(manager.isEventPluginLoaded()).thenReturn(true); + + eventService.init(); + eventService.close(); + } +} diff --git a/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java b/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java new file mode 100644 index 00000000000..f51d2dbe490 --- /dev/null +++ b/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java @@ -0,0 +1,85 @@ +package org.tron.core.event; + +import static org.mockito.Mockito.mock; + +import java.lang.reflect.Method; +import org.junit.Test; +import org.mockito.Mockito; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.utils.ReflectUtils; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.ChainBaseManager; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.db.Manager; +import org.tron.core.services.event.BlockEventGet; +import org.tron.core.services.event.BlockEventLoad; +import org.tron.core.services.event.HistoryEventService; +import org.tron.core.services.event.RealtimeEventService; +import org.tron.core.services.event.SolidEventService; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.store.DynamicPropertiesStore; + +public class HistoryEventServiceTest { + + HistoryEventService historyEventService = new HistoryEventService(); + + @Test + public void test() throws Exception { + EventPluginLoader instance = mock(EventPluginLoader.class); + ReflectUtils.setFieldValue(historyEventService, "instance", instance); + + DynamicPropertiesStore dynamicPropertiesStore = mock(DynamicPropertiesStore.class); + ChainBaseManager chainBaseManager = mock(ChainBaseManager.class); + Manager manager = mock(Manager.class); + ReflectUtils.setFieldValue(historyEventService, "manager", manager); + Mockito.when(manager.getChainBaseManager()).thenReturn(chainBaseManager); + Mockito.when(manager.getDynamicPropertiesStore()).thenReturn(dynamicPropertiesStore); + Mockito.when(chainBaseManager.getHeadBlockId()).thenReturn(new BlockCapsule.BlockId()); + + SolidEventService solidEventService = new SolidEventService(); + RealtimeEventService realtimeEventService = new RealtimeEventService(); + BlockEventLoad blockEventLoad = new BlockEventLoad(); + + ReflectUtils.setFieldValue(historyEventService, "solidEventService", solidEventService); + ReflectUtils.setFieldValue(historyEventService, "realtimeEventService", realtimeEventService); + ReflectUtils.setFieldValue(historyEventService, "blockEventLoad", blockEventLoad); + historyEventService.init(); + solidEventService.close(); + realtimeEventService.close(); + blockEventLoad.close(); + + solidEventService = mock(SolidEventService.class); + ReflectUtils.setFieldValue(historyEventService, "solidEventService", solidEventService); + realtimeEventService = mock(RealtimeEventService.class); + ReflectUtils.setFieldValue(historyEventService, "realtimeEventService", realtimeEventService); + blockEventLoad = mock(BlockEventLoad.class); + ReflectUtils.setFieldValue(historyEventService, "blockEventLoad", blockEventLoad); + + Mockito.when(instance.getStartSyncBlockNum()).thenReturn(0L); + + Mockito.when(dynamicPropertiesStore.getLatestSolidifiedBlockNum()).thenReturn(0L); + Mockito.when(chainBaseManager.getBlockIdByNum(0L)) + .thenReturn(new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 0)); + historyEventService.init(); + + BlockEvent be2 = new BlockEvent(); + BlockCapsule.BlockId b2 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 2); + be2.setBlockId(b2); + + BlockEventGet blockEventGet = mock(BlockEventGet.class); + ReflectUtils.setFieldValue(historyEventService, "blockEventGet", blockEventGet); + Mockito.when(blockEventGet.getBlockEvent(1)).thenReturn(be2); + + Mockito.when(instance.getStartSyncBlockNum()).thenReturn(1L); + ReflectUtils.setFieldValue(historyEventService, "isRunning", true); + Mockito.when(dynamicPropertiesStore.getLatestSolidifiedBlockNum()).thenReturn(1L); + + Mockito.when(chainBaseManager.getBlockIdByNum(1L)) + .thenReturn(new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 1)); + + Method method1 = historyEventService.getClass().getDeclaredMethod("syncEvent"); + method1.setAccessible(true); + method1.invoke(historyEventService); + + } +} diff --git a/framework/src/test/java/org/tron/core/event/RealtimeEventServiceTest.java b/framework/src/test/java/org/tron/core/event/RealtimeEventServiceTest.java new file mode 100644 index 00000000000..91dcea71322 --- /dev/null +++ b/framework/src/test/java/org/tron/core/event/RealtimeEventServiceTest.java @@ -0,0 +1,118 @@ +package org.tron.core.event; + +import static org.mockito.Mockito.mock; + +import com.google.protobuf.ByteString; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import org.eclipse.jetty.util.BlockingArrayQueue; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.logsfilter.capsule.BlockLogTriggerCapsule; +import org.tron.common.logsfilter.capsule.TransactionLogTriggerCapsule; +import org.tron.common.logsfilter.capsule.TriggerCapsule; +import org.tron.common.logsfilter.trigger.ContractEventTrigger; +import org.tron.common.logsfilter.trigger.ContractLogTrigger; +import org.tron.common.utils.ReflectUtils; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.db.Manager; +import org.tron.core.services.event.BlockEventCache; +import org.tron.core.services.event.RealtimeEventService; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.services.event.bo.Event; +import org.tron.core.services.event.bo.SmartContractTrigger; + +public class RealtimeEventServiceTest { + + RealtimeEventService realtimeEventService = new RealtimeEventService(); + + @Test + public void test() throws Exception { + BlockEvent be1 = new BlockEvent(); + BlockCapsule.BlockId b1 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 1); + be1.setBlockId(b1); + be1.setParentId(b1); + be1.setSolidId(b1); + BlockEventCache.init(b1); + + BlockEvent be2 = new BlockEvent(); + BlockCapsule.BlockId b2 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 2); + be2.setBlockId(b2); + be2.setParentId(b1); + be2.setSolidId(b1); + BlockEventCache.add(be2); + Assert.assertEquals(be2, BlockEventCache.getHead()); + Assert.assertEquals(be2, BlockEventCache.getBlockEvent(b2)); + + Event event = new Event(be2, true); + + realtimeEventService.add(event); + realtimeEventService.work(); + + EventPluginLoader instance = mock(EventPluginLoader.class); + ReflectUtils.setFieldValue(realtimeEventService, "instance", instance); + + BlockingQueue queue = new BlockingArrayQueue<>(); + Manager manager = mock(Manager.class); + Mockito.when(manager.getTriggerCapsuleQueue()).thenReturn(queue); + ReflectUtils.setFieldValue(realtimeEventService, "manager", manager); + + BlockCapsule blockCapsule = new BlockCapsule(0L, Sha256Hash.ZERO_HASH, 0L, + ByteString.copyFrom(BlockEventCacheTest.getBlockId())); + be2.setBlockLogTriggerCapsule(new BlockLogTriggerCapsule(blockCapsule)); + Mockito.when(instance.isBlockLogTriggerEnable()).thenReturn(true); + Mockito.when(instance.isBlockLogTriggerSolidified()).thenReturn(false); + + realtimeEventService.add(event); + realtimeEventService.work(); + + Assert.assertEquals(0, queue.size()); + + event = new Event(be2, false); + realtimeEventService.add(event); + realtimeEventService.work(); + + Assert.assertEquals(1, queue.size()); + + be2.setBlockLogTriggerCapsule(null); + queue.poll(); + + List list = new ArrayList<>(); + list.add(mock(TransactionLogTriggerCapsule.class)); + be2.setTransactionLogTriggerCapsules(list); + + Mockito.when(instance.isTransactionLogTriggerEnable()).thenReturn(true); + Mockito.when(instance.isTransactionLogTriggerSolidified()).thenReturn(false); + realtimeEventService.flush(be2, event.isRemove()); + Assert.assertEquals(1, queue.size()); + + be2.setTransactionLogTriggerCapsules(null); + + SmartContractTrigger contractTrigger = new SmartContractTrigger(); + be2.setSmartContractTrigger(contractTrigger); + + contractTrigger.getContractEventTriggers().add(mock(ContractEventTrigger.class)); + Mockito.when(instance.isContractLogTriggerEnable()).thenReturn(true); + try { + realtimeEventService.flush(be2, event.isRemove()); + } catch (Exception e) { + Assert.assertTrue(e instanceof NullPointerException); + } + + contractTrigger.getContractEventTriggers().clear(); + + realtimeEventService.flush(be2, event.isRemove()); + + contractTrigger.getContractLogTriggers().add(mock(ContractLogTrigger.class)); + Mockito.when(instance.isContractEventTriggerEnable()).thenReturn(true); + try { + realtimeEventService.flush(be2, event.isRemove()); + } catch (Exception e) { + Assert.assertTrue(e instanceof NullPointerException); + } + } +} diff --git a/framework/src/test/java/org/tron/core/event/SolidEventServiceTest.java b/framework/src/test/java/org/tron/core/event/SolidEventServiceTest.java new file mode 100644 index 00000000000..ef71a3d39a9 --- /dev/null +++ b/framework/src/test/java/org/tron/core/event/SolidEventServiceTest.java @@ -0,0 +1,116 @@ +package org.tron.core.event; + +import static org.mockito.Mockito.mock; + +import com.google.protobuf.ByteString; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import org.eclipse.jetty.util.BlockingArrayQueue; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.logsfilter.capsule.BlockLogTriggerCapsule; +import org.tron.common.logsfilter.capsule.SolidityTriggerCapsule; +import org.tron.common.logsfilter.capsule.TransactionLogTriggerCapsule; +import org.tron.common.logsfilter.capsule.TriggerCapsule; +import org.tron.common.logsfilter.trigger.ContractEventTrigger; +import org.tron.common.logsfilter.trigger.ContractLogTrigger; +import org.tron.common.utils.ReflectUtils; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.db.Manager; +import org.tron.core.services.event.BlockEventCache; +import org.tron.core.services.event.SolidEventService; +import org.tron.core.services.event.bo.BlockEvent; +import org.tron.core.services.event.bo.SmartContractTrigger; + +public class SolidEventServiceTest { + + SolidEventService solidEventService = new SolidEventService(); + + @Test + public void test() throws Exception { + BlockEvent be1 = new BlockEvent(); + BlockCapsule.BlockId b1 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 1); + be1.setBlockId(b1); + be1.setParentId(b1); + be1.setSolidId(b1); + BlockEventCache.init(b1); + + BlockEvent be2 = new BlockEvent(); + BlockCapsule.BlockId b2 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 2); + be2.setBlockId(b2); + be2.setParentId(b1); + be2.setSolidId(b1); + BlockEventCache.add(be2); + Assert.assertEquals(be2, BlockEventCache.getHead()); + Assert.assertEquals(be2, BlockEventCache.getBlockEvent(b2)); + + solidEventService.flush(be2); + + EventPluginLoader instance = mock(EventPluginLoader.class); + ReflectUtils.setFieldValue(solidEventService, "instance", instance); + + BlockingQueue queue = new BlockingArrayQueue<>(); + Manager manager = mock(Manager.class); + Mockito.when(manager.getTriggerCapsuleQueue()).thenReturn(queue); + ReflectUtils.setFieldValue(solidEventService, "manager", manager); + + BlockCapsule blockCapsule = new BlockCapsule(0L, Sha256Hash.ZERO_HASH, 0L, + ByteString.copyFrom(BlockEventCacheTest.getBlockId())); + be2.setBlockLogTriggerCapsule(new BlockLogTriggerCapsule(blockCapsule)); + Mockito.when(instance.isBlockLogTriggerEnable()).thenReturn(true); + Mockito.when(instance.isBlockLogTriggerSolidified()).thenReturn(true); + + solidEventService.flush(be2); + + Assert.assertEquals(1, queue.size()); + + be2.setBlockLogTriggerCapsule(null); + queue.poll(); + + List list = new ArrayList<>(); + list.add(mock(TransactionLogTriggerCapsule.class)); + be2.setTransactionLogTriggerCapsules(list); + + Mockito.when(instance.isTransactionLogTriggerEnable()).thenReturn(true); + Mockito.when(instance.isTransactionLogTriggerSolidified()).thenReturn(true); + solidEventService.flush(be2); + Assert.assertEquals(1, queue.size()); + + be2.setTransactionLogTriggerCapsules(null); + + SmartContractTrigger contractTrigger = new SmartContractTrigger(); + be2.setSmartContractTrigger(contractTrigger); + + contractTrigger.getContractEventTriggers().add(mock(ContractEventTrigger.class)); + Mockito.when(instance.isSolidityLogTriggerEnable()).thenReturn(true); + try { + solidEventService.flush(be2); + } catch (Exception e) { + Assert.assertTrue(e instanceof NullPointerException); + } + + contractTrigger.getContractEventTriggers().clear(); + + solidEventService.flush(be2); + + contractTrigger.getContractLogTriggers().add(mock(ContractLogTrigger.class)); + Mockito.when(instance.isSolidityEventTriggerEnable()).thenReturn(true); + try { + solidEventService.flush(be2); + } catch (Exception e) { + Assert.assertTrue(e instanceof NullPointerException); + } + + be2.setSmartContractTrigger(null); + + Mockito.when(instance.isSolidityTriggerEnable()).thenReturn(true); + be2.setSolidityTriggerCapsule(new SolidityTriggerCapsule(1)); + queue.clear(); + solidEventService.flush(be2); + Assert.assertEquals(1, queue.size()); + } +} From ff44e093610613ed3e96cc3f079a19cb49fe0552 Mon Sep 17 00:00:00 2001 From: zeusoo001 Date: Mon, 17 Feb 2025 11:48:10 +0800 Subject: [PATCH 2/2] solve the sonar problem --- .../core/services/event/BlockEventGet.java | 63 ++++++++++--------- .../services/event/HistoryEventService.java | 7 ++- .../core/services/event/bo/BlockEvent.java | 20 ++++-- .../tron/core/services/event/bo/Event.java | 8 ++- .../event/bo/SmartContractTrigger.java | 10 ++- 5 files changed, 69 insertions(+), 39 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java b/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java index 8ffc333eda4..25fb54bce8c 100644 --- a/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java +++ b/framework/src/main/java/org/tron/core/services/event/BlockEventGet.java @@ -59,7 +59,6 @@ public BlockEvent getBlockEvent(long blockNum) throws Exception { blockEvent.setBlockId(block.getBlockId()); blockEvent.setParentId(block.getParentBlockId()); blockEvent.setSolidId(manager.getChainBaseManager().getBlockIdByNum(solidNum)); - blockEvent.setBlockTime(block.getTimeStamp()); if (instance.isBlockLogTriggerEnable()) { blockEvent.setBlockLogTriggerCapsule(getBlockLogTrigger(block, solidNum)); } @@ -145,11 +144,45 @@ private List parseLogs(Protocol.Transaction tx, Map addrMap = new HashMap<>(); Map abiMap = new HashMap<>(); + parseLogs(logs, originAddress, addrMap, abiMap); + int index = 1; for (Protocol.TransactionInfo.Log log : logs) { byte[] contractAddress = TransactionTrace .convertToTronAddress(log.getAddress().toByteArray()); + String strContractAddress = + ArrayUtils.isEmpty(contractAddress) ? "" : StringUtil.encode58Check(contractAddress); + SmartContractOuterClass.SmartContract.ABI abi = abiMap.get(strContractAddress); + ContractTrigger event = new ContractTrigger(); + String creatorAddr = addrMap.get(strContractAddress); + String txId = Hex.toHexString(txInfo.getId().toByteArray()); + event.setUniqueId(txId + "_" + index); + event.setTransactionId(txId); + event.setContractAddress(strContractAddress); + event.setOriginAddress(originAddress); + event.setCallerAddress(""); + event.setCreatorAddress(StringUtils.isEmpty(creatorAddr) ? "" : creatorAddr); + event.setBlockNumber(txInfo.getBlockNumber()); + event.setTimeStamp(txInfo.getBlockTimeStamp()); + event.setLogInfo(buildLogInfo(log)); + event.setAbi(abi); + + list.add(event); + index++; + } + + return list; + } + + private void parseLogs(List logs, + String originAddress, + Map addrMap, Map abiMap) { + for (Protocol.TransactionInfo.Log log : logs) { + + byte[] contractAddress = TransactionTrace + .convertToTronAddress(log.getAddress().toByteArray()); String strContractAddr = ArrayUtils.isEmpty(contractAddress) ? "" : StringUtil.encode58Check(contractAddress); if (addrMap.get(strContractAddr) != null) { @@ -175,34 +208,6 @@ private List parseLogs(Protocol.Transaction tx, addrMap.put(strContractAddr, creatorAddr); abiMap.put(strContractAddr, abi); } - - int index = 1; - for (Protocol.TransactionInfo.Log log : logs) { - - byte[] contractAddress = TransactionTrace - .convertToTronAddress(log.getAddress().toByteArray()); - String strContractAddress = - ArrayUtils.isEmpty(contractAddress) ? "" : StringUtil.encode58Check(contractAddress); - SmartContractOuterClass.SmartContract.ABI abi = abiMap.get(strContractAddress); - ContractTrigger event = new ContractTrigger(); - String creatorAddr = addrMap.get(strContractAddress); - String txId = Hex.toHexString(txInfo.getId().toByteArray()); - event.setUniqueId(txId + "_" + index); - event.setTransactionId(txId); - event.setContractAddress(strContractAddress); - event.setOriginAddress(originAddress); - event.setCallerAddress(""); - event.setCreatorAddress(StringUtils.isEmpty(creatorAddr) ? "" : creatorAddr); - event.setBlockNumber(txInfo.getBlockNumber()); - event.setTimeStamp(txInfo.getBlockTimeStamp()); - event.setLogInfo(buildLogInfo(log)); - event.setAbi(abi); - - list.add(event); - index++; - } - - return list; } private LogInfo buildLogInfo(Protocol.TransactionInfo.Log log) { diff --git a/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java b/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java index 0f26490d16b..af9b7818640 100644 --- a/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java +++ b/framework/src/main/java/org/tron/core/services/event/HistoryEventService.java @@ -61,8 +61,11 @@ private void syncEvent() { Thread.sleep(30); } initEventService(manager.getChainBaseManager().getBlockIdByNum(endNum)); - } catch (Exception e) { - logger.error("Sync event failed.", e); + } catch (InterruptedException e1) { + logger.warn("Sync event interrupted."); + Thread.currentThread().interrupt(); + } catch (Exception e2) { + logger.error("Sync event failed.", e2); } } diff --git a/framework/src/main/java/org/tron/core/services/event/bo/BlockEvent.java b/framework/src/main/java/org/tron/core/services/event/bo/BlockEvent.java index 9e314bc7d5a..f573a486738 100644 --- a/framework/src/main/java/org/tron/core/services/event/bo/BlockEvent.java +++ b/framework/src/main/java/org/tron/core/services/event/bo/BlockEvent.java @@ -1,22 +1,34 @@ package org.tron.core.services.event.bo; import java.util.List; -import lombok.Data; +import lombok.Getter; +import lombok.Setter; import org.tron.common.logsfilter.capsule.BlockLogTriggerCapsule; import org.tron.common.logsfilter.capsule.SolidityTriggerCapsule; import org.tron.common.logsfilter.capsule.TransactionLogTriggerCapsule; import org.tron.core.capsule.BlockCapsule; -@Data public class BlockEvent { + @Getter + @Setter private BlockCapsule.BlockId blockId; + @Getter + @Setter private BlockCapsule.BlockId parentId; + @Getter + @Setter private BlockCapsule.BlockId solidId; - private long blockTime; - + @Getter + @Setter private BlockLogTriggerCapsule blockLogTriggerCapsule; + @Getter + @Setter private List transactionLogTriggerCapsules; + @Getter + @Setter private SolidityTriggerCapsule solidityTriggerCapsule; + @Getter + @Setter private SmartContractTrigger smartContractTrigger; public BlockEvent() {} diff --git a/framework/src/main/java/org/tron/core/services/event/bo/Event.java b/framework/src/main/java/org/tron/core/services/event/bo/Event.java index 142e1be3c22..7d99747c087 100644 --- a/framework/src/main/java/org/tron/core/services/event/bo/Event.java +++ b/framework/src/main/java/org/tron/core/services/event/bo/Event.java @@ -1,10 +1,14 @@ package org.tron.core.services.event.bo; -import lombok.Data; +import lombok.Getter; +import lombok.Setter; -@Data public class Event { + @Getter + @Setter private boolean isRemove; + @Getter + @Setter private BlockEvent blockEvent; public Event(BlockEvent blockEvent, boolean isRemove) { diff --git a/framework/src/main/java/org/tron/core/services/event/bo/SmartContractTrigger.java b/framework/src/main/java/org/tron/core/services/event/bo/SmartContractTrigger.java index 7413c309169..e981d5db7da 100644 --- a/framework/src/main/java/org/tron/core/services/event/bo/SmartContractTrigger.java +++ b/framework/src/main/java/org/tron/core/services/event/bo/SmartContractTrigger.java @@ -2,13 +2,19 @@ import java.util.ArrayList; import java.util.List; -import lombok.Data; +import lombok.Getter; +import lombok.Setter; import org.tron.common.logsfilter.trigger.ContractEventTrigger; import org.tron.common.logsfilter.trigger.ContractLogTrigger; -@Data public class SmartContractTrigger { + @Getter + @Setter private List contractLogTriggers = new ArrayList<>(); + @Getter + @Setter private List contractEventTriggers = new ArrayList<>(); + @Getter + @Setter private List redundancies = new ArrayList<>(); }