diff --git a/actuator/src/main/java/org/tron/core/actuator/AbstractActuator.java b/actuator/src/main/java/org/tron/core/actuator/AbstractActuator.java index c9f83c520bd..64a81e17c58 100644 --- a/actuator/src/main/java/org/tron/core/actuator/AbstractActuator.java +++ b/actuator/src/main/java/org/tron/core/actuator/AbstractActuator.java @@ -2,6 +2,7 @@ import com.google.protobuf.Any; import com.google.protobuf.GeneratedMessageV3; +import lombok.Getter; import org.tron.common.math.Maths; import org.tron.common.utils.Commons; import org.tron.common.utils.ForkController; @@ -16,9 +17,13 @@ public abstract class AbstractActuator implements Actuator { + @Getter protected Any any; + @Getter protected ChainBaseManager chainBaseManager; + @Getter protected Contract contract; + @Getter protected TransactionCapsule tx; protected ForkController forkController; @@ -26,38 +31,22 @@ public AbstractActuator(ContractType type, Class c TransactionFactory.register(type, getClass(), clazz); } - public Any getAny() { - return any; - } - public AbstractActuator setAny(Any any) { this.any = any; return this; } - public ChainBaseManager getChainBaseManager() { - return chainBaseManager; - } - public AbstractActuator setChainBaseManager(ChainBaseManager chainBaseManager) { this.chainBaseManager = chainBaseManager; return this; } - public Contract getContract() { - return contract; - } - public AbstractActuator setContract(Contract contract) { this.contract = contract; this.any = contract.getParameter(); return this; } - public TransactionCapsule getTx() { - return tx; - } - public AbstractActuator setTx(TransactionCapsule tx) { this.tx = tx; return this; diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java index 8254a862927..74d332c5611 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -886,6 +886,29 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, } break; } + case ALLOW_TVM_PRAGUE: { + if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) { + throw new ContractValidateException( + "Bad chain parameter id [ALLOW_TVM_PRAGUE]"); + } + // The deployed BlockHashHistory bytecode contains PUSH0 (0x5f), which + // is itself gated on ALLOW_TVM_SHANGHAI at execution time. Refuse the + // proposal until Shanghai is enacted so an out-of-order activation + // can't leave a contract whose every STATICCALL hits InvalidOpcode. + if (dynamicPropertiesStore.getAllowTvmShangHai() != 1) { + throw new ContractValidateException( + "[ALLOW_TVM_PRAGUE] requires [ALLOW_TVM_SHANGHAI] to be enacted first"); + } + if (dynamicPropertiesStore.getAllowTvmPrague() == 1) { + throw new ContractValidateException( + "[ALLOW_TVM_PRAGUE] has been valid, no need to propose again"); + } + if (value != 1) { + throw new ContractValidateException( + "This value[ALLOW_TVM_PRAGUE] is only allowed to be 1"); + } + break; + } case ALLOW_HARDEN_RESOURCE_CALCULATION: { if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) { throw new ContractValidateException( @@ -1003,6 +1026,7 @@ public enum ProposalType { // current value, value range ALLOW_TVM_BLOB(89), // 0, 1 PROPOSAL_EXPIRE_TIME(92), // (0, 31536003000) ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94), // 0, 1 + ALLOW_TVM_PRAGUE(95), // 0, 1 ALLOW_TVM_OSAKA(96), // 0, 1 ALLOW_HARDEN_RESOURCE_CALCULATION(97), // 0, 1 ALLOW_HARDEN_EXCHANGE_CALCULATION(98); // 0, 1 diff --git a/actuator/src/main/java/org/tron/core/vm/Op.java b/actuator/src/main/java/org/tron/core/vm/Op.java index ed2d8eb2f53..0a3fcc1dae3 100644 --- a/actuator/src/main/java/org/tron/core/vm/Op.java +++ b/actuator/src/main/java/org/tron/core/vm/Op.java @@ -64,6 +64,8 @@ public class Op { public static final int SHR = 0x1c; // (0x1d) Arithmetic shift right public static final int SAR = 0x1d; + // (0x1e) Count leading zeros + public static final int CLZ = 0x1e; /* Cryptographic Operations */ // (0x20) Compute SHA3-256 hash diff --git a/actuator/src/main/java/org/tron/core/vm/OperationActions.java b/actuator/src/main/java/org/tron/core/vm/OperationActions.java index 0d978743a5e..88c3c55899e 100644 --- a/actuator/src/main/java/org/tron/core/vm/OperationActions.java +++ b/actuator/src/main/java/org/tron/core/vm/OperationActions.java @@ -2,6 +2,7 @@ import static org.tron.common.crypto.Hash.sha3; import static org.tron.common.utils.ByteUtil.EMPTY_BYTE_ARRAY; +import static org.tron.common.utils.ByteUtil.numberOfLeadingZeros; import java.math.BigInteger; import java.util.ArrayList; @@ -287,6 +288,17 @@ public static void sarAction(Program program) { program.step(); } + public static void clzAction(Program program) { + DataWord word = program.stackPop(); + int clz = numberOfLeadingZeros(word.getData()); + if (clz == 256) { + program.stackPush(new DataWord(256)); + } else { + program.stackPush(DataWord.of((byte) clz)); + } + program.step(); + } + public static void sha3Action(Program program) { DataWord memOffsetData = program.stackPop(); DataWord lengthData = program.stackPop(); diff --git a/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java b/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java index f2d251ceee9..8c078e843a2 100644 --- a/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java +++ b/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java @@ -13,6 +13,7 @@ public enum Version { TRON_V1_2, TRON_V1_3, TRON_V1_4, + TRON_V1_5, // add more // TRON_V2, // ETH @@ -26,6 +27,7 @@ public enum Version { tableMap.put(Version.TRON_V1_2, newTronV12OperationSet()); tableMap.put(Version.TRON_V1_3, newTronV13OperationSet()); tableMap.put(Version.TRON_V1_4, newTronV14OperationSet()); + tableMap.put(Version.TRON_V1_5, newTronV15OperationSet()); } public static JumpTable newTronV10OperationSet() { @@ -63,12 +65,18 @@ public static JumpTable newTronV14OperationSet() { return table; } + public static JumpTable newTronV15OperationSet() { + JumpTable table = newTronV14OperationSet(); + appendOsakaOperations(table); + return table; + } + // Just for warming up class to avoid out_of_time public static void init() {} public static JumpTable getTable() { // always get the table which has the newest version - JumpTable table = tableMap.get(Version.TRON_V1_4); + JumpTable table = tableMap.get(Version.TRON_V1_5); // next make the corresponding changes, exclude activating opcode if (VMConfig.allowHigherLimitForMaxCpuTimeOfOneTx()) { @@ -704,6 +712,16 @@ public static void appendCancunOperations(JumpTable table) { tvmBlobProposal)); } + public static void appendOsakaOperations(JumpTable table) { + BooleanSupplier proposal = VMConfig::allowTvmOsaka; + + table.set(new Operation( + Op.CLZ, 1, 1, + EnergyCost::getLowTierCost, + OperationActions::clzAction, + proposal)); + } + public static void adjustSelfdestruct(JumpTable table) { table.set(new Operation( Op.SUICIDE, 1, 0, diff --git a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java index fa7459a069d..07bda61fd3c 100644 --- a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java +++ b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java @@ -58,6 +58,7 @@ import org.tron.common.crypto.zksnark.Fp; import org.tron.common.crypto.zksnark.PairingCheck; import org.tron.common.es.ExecutorServiceManager; +import org.tron.common.math.StrictMathWrapper; import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.ProgramResult; import org.tron.common.runtime.vm.DataWord; @@ -636,6 +637,11 @@ public static class ModExp extends PrecompiledContract { private static final int UPPER_BOUND = 1024; + private static final long MIN_ENERGY_TIP7883 = 500L; + + private static final BigInteger MIN_ENERGY_TIP7883_BI = + BigInteger.valueOf(MIN_ENERGY_TIP7883); + @Override public long getEnergyForData(byte[] data) { @@ -651,6 +657,10 @@ public long getEnergyForData(byte[] data) { byte[] expHighBytes = parseBytes(data, addSafely(ARGS_OFFSET, baseLen), min(expLen, 32, VMConfig.disableJavaLangMath())); + if (VMConfig.allowTvmOsaka()) { + return getEnergyTIP7883(baseLen, modLen, expHighBytes, expLen); + } + long multComplexity = getMultComplexity(max(baseLen, modLen, VMConfig.disableJavaLangMath())); long adjExpLen = getAdjustedExponentLength(expHighBytes, expLen); @@ -734,6 +744,66 @@ private long getAdjustedExponentLength(byte[] expHighBytes, long expLen) { } } + /** + * TIP-7883: ModExp gas cost increase. + * New pricing formula with higher minimum cost and no divisor. + */ + private long getEnergyTIP7883(int baseLen, int modLen, + byte[] expHighBytes, int expLen) { + long multComplexity = getMultComplexityTIP7883(baseLen, modLen); + long iterCount = getIterationCountTIP7883(expHighBytes, expLen); + + // use big numbers to stay safe in case of overflow + BigInteger energy = BigInteger.valueOf(multComplexity) + .multiply(BigInteger.valueOf(iterCount)); + + if (isLessThan(energy, MIN_ENERGY_TIP7883_BI)) { + return MIN_ENERGY_TIP7883; + } + + return isLessThan(energy, BigInteger.valueOf(Long.MAX_VALUE)) ? energy.longValueExact() + : Long.MAX_VALUE; + } + + /** + * TIP-7883: New multiplication complexity formula. + * Minimal complexity of 16; doubled complexity for base/modulus > 32 bytes. + */ + private long getMultComplexityTIP7883(int baseLen, int modLen) { + long maxLength = StrictMathWrapper.max(baseLen, modLen); + if (maxLength <= 32) { + return 16; + } + // ceil(maxLength / 8) + long words = StrictMathWrapper.floorDiv(StrictMathWrapper.addExact(maxLength, 7L), 8L); + return StrictMathWrapper.multiplyExact(2L, StrictMathWrapper.multiplyExact(words, words)); + } + + /** + * TIP-7883: New iteration count formula. + * Multiplier for exponents > 32 bytes increased from 8 to 16. + */ + private long getIterationCountTIP7883(byte[] expHighBytes, long expLen) { + int leadingZeros = numberOfLeadingZeros(expHighBytes); + long highestBit = StrictMathWrapper.subtractExact( + StrictMathWrapper.multiplyExact(8L, expHighBytes.length), leadingZeros); + + if (highestBit > 0) { + highestBit = StrictMathWrapper.subtractExact(highestBit, 1L); + } + + long iterCount; + if (expLen <= 32) { + iterCount = highestBit; + } else { + iterCount = StrictMathWrapper.addExact( + StrictMathWrapper.multiplyExact(16L, StrictMathWrapper.subtractExact(expLen, 32L)), + highestBit); + } + + return StrictMathWrapper.max(iterCount, 1L); + } + private int parseLen(byte[] data, int idx) { byte[] bytes = parseBytes(data, 32 * idx, 32); return new DataWord(bytes).intValueSafe(); diff --git a/actuator/src/main/java/org/tron/core/vm/trace/ProgramTrace.java b/actuator/src/main/java/org/tron/core/vm/trace/ProgramTrace.java index bd8bc18e78e..a7bb132e3a2 100644 --- a/actuator/src/main/java/org/tron/core/vm/trace/ProgramTrace.java +++ b/actuator/src/main/java/org/tron/core/vm/trace/ProgramTrace.java @@ -90,12 +90,8 @@ public void merge(ProgramTrace programTrace) { this.ops.addAll(programTrace.ops); } - public String asJsonString(boolean formatted) { - return serializeFieldsOnly(this, formatted); - } - @Override public String toString() { - return asJsonString(true); + return serializeFieldsOnly(this); } } diff --git a/actuator/src/main/java/org/tron/core/vm/trace/Serializers.java b/actuator/src/main/java/org/tron/core/vm/trace/Serializers.java index 69056d359c5..ddf18105941 100644 --- a/actuator/src/main/java/org/tron/core/vm/trace/Serializers.java +++ b/actuator/src/main/java/org/tron/core/vm/trace/Serializers.java @@ -1,73 +1,28 @@ package org.tron.core.vm.trace; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.introspect.VisibilityChecker; -import java.io.IOException; +import com.fasterxml.jackson.databind.json.JsonMapper; import lombok.extern.slf4j.Slf4j; -import org.bouncycastle.util.encoders.Hex; -import org.tron.common.runtime.vm.DataWord; -import org.tron.core.vm.Op; @Slf4j(topic = "VM") public final class Serializers { - public static String serializeFieldsOnly(Object value, boolean pretty) { - try { - ObjectMapper mapper = createMapper(pretty); - mapper.setVisibilityChecker(fieldsOnlyVisibilityChecker(mapper)); + private static final ObjectMapper mapper = JsonMapper.builder() + .enable(SerializationFeature.INDENT_OUTPUT) + .visibility(PropertyAccessor.FIELD, Visibility.ANY) + .visibility(PropertyAccessor.GETTER, Visibility.NONE) + .visibility(PropertyAccessor.IS_GETTER, Visibility.NONE) + .build(); + public static String serializeFieldsOnly(Object value) { + try { return mapper.writeValueAsString(value); } catch (Exception e) { logger.error("JSON serialization error: ", e); return "{}"; } } - - private static VisibilityChecker fieldsOnlyVisibilityChecker(ObjectMapper mapper) { - return mapper.getSerializationConfig().getDefaultVisibilityChecker() - .withFieldVisibility(JsonAutoDetect.Visibility.ANY) - .withGetterVisibility(JsonAutoDetect.Visibility.NONE) - .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE); - } - - public static ObjectMapper createMapper(boolean pretty) { - ObjectMapper mapper = new ObjectMapper(); - if (pretty) { - mapper.enable(SerializationFeature.INDENT_OUTPUT); - } - return mapper; - } - - public static class DataWordSerializer extends JsonSerializer { - - @Override - public void serialize(DataWord energy, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException { - jgen.writeString(energy.value().toString()); - } - } - - public static class ByteArraySerializer extends JsonSerializer { - - @Override - public void serialize(byte[] memory, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException { - jgen.writeString(Hex.toHexString(memory)); - } - } - - public static class OpCodeSerializer extends JsonSerializer { - - @Override - public void serialize(Byte op, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException { - jgen.writeString(Op.getNameOf(op)); - } - } } diff --git a/chainbase/src/main/java/org/tron/core/actuator/TransactionFactory.java b/chainbase/src/main/java/org/tron/core/actuator/TransactionFactory.java index d151812b19c..6e74f7f8a2b 100644 --- a/chainbase/src/main/java/org/tron/core/actuator/TransactionFactory.java +++ b/chainbase/src/main/java/org/tron/core/actuator/TransactionFactory.java @@ -2,17 +2,15 @@ import com.google.protobuf.GeneratedMessageV3; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.tron.common.parameter.CommonParameter; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract; import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract; public class TransactionFactory { - private static Map> actuatorMap = new ConcurrentHashMap<>(); - private static Map> contractMap = new ConcurrentHashMap<>(); + private static final Map> actuatorMap = new ConcurrentHashMap<>(); + private static final Map> contractMap = new ConcurrentHashMap<>(); static { register(ContractType.CreateSmartContract, null, CreateSmartContract.class); @@ -21,12 +19,6 @@ public class TransactionFactory { public static void register(ContractType type, Class actuatorClass, Class clazz) { - Set actuatorSet = CommonParameter.getInstance().getActuatorSet(); - if (actuatorClass != null && !actuatorSet.isEmpty() && !actuatorSet - .contains(actuatorClass.getSimpleName())) { - return; - } - if (type != null && actuatorClass != null) { actuatorMap.put(type, actuatorClass); } @@ -42,12 +34,4 @@ public static Class getActuator(ContractType type) { public static Class getContract(ContractType type) { return contractMap.get(type); } - - public static Map> getActuatorMap() { - return actuatorMap; - } - - public static Map> getContractMap() { - return contractMap; - } } diff --git a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java index 29864d65b87..0f74f20d379 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -240,6 +240,15 @@ public class DynamicPropertiesStore extends TronStoreWithRevoking private static final byte[] ALLOW_TVM_OSAKA = "ALLOW_TVM_OSAKA".getBytes(); + private static final byte[] ALLOW_TVM_PRAGUE = "ALLOW_TVM_PRAGUE".getBytes(); + + // TIP-2935 install marker — flipped to 1 inside HistoryBlockHashUtil.deploy() + // only after the three store writes succeed. Stays 0 when deploy() skips on + // foreign-state collision; HistoryBlockHashUtil.write() reads this to decide + // whether StorageRowStore at the canonical address is ours to mutate. + private static final byte[] BLOCK_HASH_HISTORY_INSTALLED = + "BLOCK_HASH_HISTORY_INSTALLED".getBytes(); + private static final byte[] ALLOW_HARDEN_RESOURCE_CALCULATION = "ALLOW_HARDEN_RESOURCE_CALCULATION".getBytes(); @@ -2995,13 +3004,43 @@ public long getAllowTvmOsaka() { return Optional.ofNullable(getUnchecked(ALLOW_TVM_OSAKA)) .map(BytesCapsule::getData) .map(ByteArray::toLong) - .orElse(CommonParameter.getInstance().getAllowTvmOsaka()); + .orElse(0L); } public void saveAllowTvmOsaka(long value) { this.put(ALLOW_TVM_OSAKA, new BytesCapsule(ByteArray.fromLong(value))); } + public long getAllowTvmPrague() { + return Optional.ofNullable(getUnchecked(ALLOW_TVM_PRAGUE)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElse(0L); + } + + public void saveAllowTvmPrague(long value) { + this.put(ALLOW_TVM_PRAGUE, new BytesCapsule(ByteArray.fromLong(value))); + } + + public boolean allowTvmPrague() { + return getAllowTvmPrague() == 1L; + } + + public long getBlockHashHistoryInstalled() { + return Optional.ofNullable(getUnchecked(BLOCK_HASH_HISTORY_INSTALLED)) + .map(BytesCapsule::getData) + .map(ByteArray::toLong) + .orElse(0L); + } + + public void saveBlockHashHistoryInstalled(long value) { + this.put(BLOCK_HASH_HISTORY_INSTALLED, new BytesCapsule(ByteArray.fromLong(value))); + } + + public boolean isBlockHashHistoryInstalled() { + return getBlockHashHistoryInstalled() == 1L; + } + public long getAllowHardenResourceCalculation() { return Optional.ofNullable(getUnchecked(ALLOW_HARDEN_RESOURCE_CALCULATION)) .map(BytesCapsule::getData) diff --git a/common/build.gradle b/common/build.gradle index 98fc3257190..acde43a1ea9 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -8,7 +8,7 @@ sourceCompatibility = 1.8 dependencies { - api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.18.3' // https://github.com/FasterXML/jackson-databind/issues/3627 + api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.18.6' // https://github.com/FasterXML/jackson-databind/issues/3627 api "com.cedarsoftware:java-util:3.2.0" api group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.1' api group: 'commons-codec', name: 'commons-codec', version: '1.11' diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index 1bb61c420f0..0028a5d50d0 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -5,7 +5,6 @@ import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; -import java.util.Set; import lombok.Getter; import lombok.Setter; import org.slf4j.bridge.SLF4JBridgeHandler; @@ -163,6 +162,9 @@ public class CommonParameter { @Getter @Setter public long syncFetchBatchNum; // clearParam: 2000 + @Getter + @Setter + public int maxPendingBlockSize; // If you are running a solidity node for java tron, // this flag is set to true @@ -392,9 +394,6 @@ public class CommonParameter { public long changedDelegation; @Getter @Setter - public Set actuatorSet; - @Getter - @Setter public RateLimiterInitialization rateLimiterInitialization; @Getter @Setter @@ -496,21 +495,6 @@ public class CommonParameter { public boolean nodeMetricsEnable = false; @Getter @Setter - public boolean metricsStorageEnable = false; - @Getter - @Setter - public String influxDbIp; - @Getter - @Setter - public int influxDbPort; - @Getter - @Setter - public String influxDbDatabase; - @Getter - @Setter - public int metricsReportInterval = 10; - @Getter - @Setter public boolean metricsPrometheusEnable = false; @Getter @Setter @@ -529,6 +513,12 @@ public class CommonParameter { public int pBFTHttpPort; @Getter @Setter + public int maxNestingDepth = 100; + @Getter + @Setter + public int maxTokenCount = 100_000; + @Getter + @Setter public long pBFTExpireNum; // clearParam: 20 @Getter @Setter @@ -658,10 +648,6 @@ public class CommonParameter { @Setter public long allowTvmBlob; - @Getter - @Setter - public long allowTvmOsaka; - private static double calcMaxTimeRatio() { return 5.0; } diff --git a/common/src/main/java/org/tron/common/utils/JsonUtil.java b/common/src/main/java/org/tron/common/utils/JsonUtil.java index 0847e18607b..e08b49e8c08 100644 --- a/common/src/main/java/org/tron/common/utils/JsonUtil.java +++ b/common/src/main/java/org/tron/common/utils/JsonUtil.java @@ -1,14 +1,16 @@ package org.tron.common.utils; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; import org.springframework.util.StringUtils; public class JsonUtil { + private static final ObjectMapper om = new JsonMapper(); + public static final T json2Obj(String jsonString, Class clazz) { if (!StringUtils.isEmpty(jsonString) && clazz != null) { try { - ObjectMapper om = new ObjectMapper(); return om.readValue(jsonString, clazz); } catch (Exception var3) { throw new RuntimeException(var3); @@ -22,7 +24,6 @@ public static final String obj2Json(Object obj) { if (obj == null) { return null; } else { - ObjectMapper om = new ObjectMapper(); try { return om.writeValueAsString(obj); } catch (Exception var3) { diff --git a/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java index 0f94e7a59eb..5cd9de842a0 100644 --- a/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java +++ b/common/src/main/java/org/tron/core/config/args/CommitteeConfig.java @@ -72,7 +72,6 @@ public class CommitteeConfig { private long consensusLogicOptimization = 0; private long allowTvmCancun = 0; private long allowTvmBlob = 0; - private long allowTvmOsaka = 0; private long unfreezeDelayDays = 0; private long allowReceiptsMerkleRoot = 0; private long allowAccountAssetOptimization = 0; diff --git a/common/src/main/java/org/tron/core/config/args/MetricsConfig.java b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java index 5b504acdd1c..5547dfa6d3a 100644 --- a/common/src/main/java/org/tron/core/config/args/MetricsConfig.java +++ b/common/src/main/java/org/tron/core/config/args/MetricsConfig.java @@ -8,16 +8,14 @@ /** * Metrics configuration bean. Field names match config.conf keys under "node.metrics". - * Contains nested sub-beans for prometheus and influxdb sections. + * Contains nested sub-bean for the prometheus section. */ @Slf4j @Getter @Setter public class MetricsConfig { - private boolean storageEnable = false; private PrometheusConfig prometheus = new PrometheusConfig(); - private InfluxDbConfig influxdb = new InfluxDbConfig(); @Getter @Setter @@ -26,15 +24,6 @@ public static class PrometheusConfig { private int port = 9527; } - @Getter - @Setter - public static class InfluxDbConfig { - private String ip = ""; - private int port = 8086; - private String database = "metrics"; - private int metricsReportInterval = 10; - } - // Defaults come from reference.conf (loaded globally via Configuration.java) /** diff --git a/common/src/main/java/org/tron/core/config/args/MiscConfig.java b/common/src/main/java/org/tron/core/config/args/MiscConfig.java index f6c3b200b80..0c6d3631ba8 100644 --- a/common/src/main/java/org/tron/core/config/args/MiscConfig.java +++ b/common/src/main/java/org/tron/core/config/args/MiscConfig.java @@ -1,19 +1,15 @@ package org.tron.core.config.args; import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.tron.core.Constant; /** * Miscellaneous small config domains that don't warrant their own bean class. - * Covers: storage (partial), trx, energy, crypto, seed, actuator. + * Covers: storage (partial), trx, energy, crypto, seed. * *

These use manual reads because they span multiple unrelated config.conf * top-level sections and some have non-standard key naming (e.g. "enery" typo). @@ -29,7 +25,6 @@ public class MiscConfig { private long blockNumForEnergyLimit = 4727890L; private String cryptoEngine = Constant.ECKey_ENGINE; private List seedNodeIpList = new ArrayList<>(); - private Set actuatorWhitelist = Collections.emptySet(); public static MiscConfig fromConfig(Config config) { MiscConfig mc = new MiscConfig(); @@ -61,10 +56,6 @@ public static MiscConfig fromConfig(Config config) { mc.seedNodeIpList = config.hasPath("seed.node.ip.list") ? config.getStringList("seed.node.ip.list") : new ArrayList<>(); - // actuator - mc.actuatorWhitelist = config.hasPath("actuator.whitelist") - ? new HashSet<>(config.getStringList("actuator.whitelist")) : Collections.emptySet(); - return mc; } } diff --git a/common/src/main/java/org/tron/core/config/args/NodeConfig.java b/common/src/main/java/org/tron/core/config/args/NodeConfig.java index 751fb81e4a1..620152a907a 100644 --- a/common/src/main/java/org/tron/core/config/args/NodeConfig.java +++ b/common/src/main/java/org/tron/core/config/args/NodeConfig.java @@ -28,6 +28,7 @@ public class NodeConfig { private String trustNode = ""; private boolean walletExtensionApi = false; private int syncFetchBatchNum = 2000; + private int maxPendingBlockSize = 500; private int validateSignThreadNum = 0; // 0 = auto (availableProcessors) private int maxConnections = 30; private int minConnections = 8; @@ -202,6 +203,8 @@ public static class HttpConfig { private boolean solidityEnable = true; private int solidityPort = 8091; private long maxMessageSize = 4194304; + private int maxNestingDepth = 100; + private int maxTokenCount = 100_000; // PBFT fields — handled manually (same naming issue as CommitteeConfig) // Default must match CommonParameter.pBFTHttpEnable = true @Getter(lombok.AccessLevel.NONE) @@ -449,6 +452,14 @@ private void postProcess() { syncFetchBatchNum = 100; } + // maxPendingBlockSize: clamp to [50, 2000] + if (maxPendingBlockSize > 2000) { + maxPendingBlockSize = 2000; + } + if (maxPendingBlockSize < 50) { + maxPendingBlockSize = 50; + } + // blockProducedTimeOut: clamp to [30, 100] if (blockProducedTimeOut < 30) { blockProducedTimeOut = 30; diff --git a/common/src/main/java/org/tron/json/JSON.java b/common/src/main/java/org/tron/json/JSON.java new file mode 100644 index 00000000000..88678c49a44 --- /dev/null +++ b/common/src/main/java/org/tron/json/JSON.java @@ -0,0 +1,157 @@ +package org.tron.json; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.StreamReadConstraints; +import com.fasterxml.jackson.core.json.JsonReadFeature; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.tron.common.parameter.CommonParameter; + +/** + * Drop-in replacement for {@code com.alibaba.fastjson.JSON}. + * + * @deprecated Compatibility shim from the fastjson removal. New code should use + * Jackson directly ({@link com.fasterxml.jackson.databind.ObjectMapper}, + * {@link com.fasterxml.jackson.databind.JsonNode}) instead of this helper. + */ +@Deprecated +public final class JSON { + + // Initialization-order invariant: this class must NOT be loaded before + // Args.setParam() completes. The factory's StreamReadConstraints are a + // one-shot snapshot of CommonParameter at class-init time. If JSON is + // touched too early — e.g. a stray reference in startup code or in a static + // initializer that runs before Args — the snapshot captures CommonParameter's + // hardcoded defaults (100 / 100_000) and any user override of + // node.http.maxNestingDepth / maxTokenCount is silently ignored. + // Current production startup (FullNode.main) calls Args.setParam first and + // no path in that call chain references this class, so the invariant holds. + static final ObjectMapper MAPPER = JsonMapper.builder(buildFactory()) + // Fastjson Feature.AllowUnQuotedFieldNames (default ON) + .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) + // Fastjson Feature.AllowSingleQuotes (default ON) + .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) + // Partial compatibility with Fastjson Feature.AllowArbitraryCommas: + // this only covers a single trailing comma like {"a":1,} or [1,2,]. + // Repeated/arbitrary commas like {"a":1,,,,} and [1,,2] remain rejected. + .enable(JsonReadFeature.ALLOW_TRAILING_COMMA) + // Fastjson accepts a leading plus sign for numbers (for example +123, +0.5) + .enable(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS) + // Partial compatibility for Fastjson's asymmetric decimal behavior: + // Fastjson accepts +.5 but rejects .5 by default. Jackson cannot model only + // the signed form, so enabling this also accepts .5. + .enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS) + // Fastjson accepts a trailing decimal point for numbers (for example 5.) + .enable(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS) + // Fastjson accepts leading zeros for numbers (for example 007) + .enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS) + // Fastjson accepts unescaped control chars in strings (for example raw tab/newline) + .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS) + // Fastjson accepts Java-style comments (// and /* */) + .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) + // Fastjson Feature.UseBigDecimal (default ON) + // https://github.com/alibaba/fastjson/wiki/deserialize_disable_bigdecimal_cn + .configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true) + // Fastjson Feature.IgnoreNotMatch (default ON) — unknown fields silently ignored + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + // Fastjson serializes empty beans as "{}" without error + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + // Fastjson omits null-valued fields by default (WriteMapNullValue is OFF by default) + // https://github.com/alibaba/fastjson/wiki/WriteNull_cn + .serializationInclusion(JsonInclude.Include.NON_NULL) + .build(); + + private static JsonFactory buildFactory() { + CommonParameter p = CommonParameter.getInstance(); + return JsonFactory.builder().streamReadConstraints(StreamReadConstraints.builder() + .maxNestingDepth(p.getMaxNestingDepth()).maxTokenCount(p.getMaxTokenCount()) + .build()).build(); + } + + private JSON() { + } + + /** + * Returns {@code true} when {@code text} is null, blank, or a + * lowercase {@code "null"} literal. + */ + static boolean isNullLiteral(String text) { + if (text == null) { + return true; + } + String trimmed = text.trim(); + return trimmed.isEmpty() || "null".equals(trimmed); + } + + public static JSONObject parseObject(String text) { + if (isNullLiteral(text)) { + return null; + } + try { + JsonNode node = MAPPER.readTree(text); + if (node == null || node.isNull()) { + return null; + } + if (!node.isObject()) { + throw new JSONException("can not cast to JSONObject."); + } + return new JSONObject((ObjectNode) node); + } catch (JSONException e) { + throw e; + } catch (Exception e) { + throw new JSONException(e.getMessage(), e); + } + } + + public static JsonNode parse(String text) { + if (isNullLiteral(text)) { + return null; + } + try { + JsonNode node = MAPPER.readTree(text); + if (node == null || node.isNull()) { + return null; + } + return node; + } catch (JSONException e) { + throw e; + } catch (Exception e) { + throw new JSONException(e.getMessage(), e); + } + } + + static JSONArray parseArray(String text) { + return JSONArray.parseArray(text); + } + + public static String toJSONString(Object obj) { + return toJSONString(obj, false); + } + + public static String toJSONString(Object obj, boolean pretty) { + if (obj == null) { + return "null"; + } + try { + if (obj instanceof JSONObject) { + return pretty ? MAPPER.writerWithDefaultPrettyPrinter() + .writeValueAsString(((JSONObject) obj).unwrap()) + : MAPPER.writeValueAsString(((JSONObject) obj).unwrap()); + } + if (obj instanceof JSONArray) { + return pretty ? MAPPER.writerWithDefaultPrettyPrinter() + .writeValueAsString(((JSONArray) obj).unwrap()) + : MAPPER.writeValueAsString(((JSONArray) obj).unwrap()); + } + return pretty ? MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj) + : MAPPER.writeValueAsString(obj); + } catch (Exception e) { + throw new JSONException(e.getMessage(), e); + } + } +} diff --git a/common/src/main/java/org/tron/json/JSONArray.java b/common/src/main/java/org/tron/json/JSONArray.java new file mode 100644 index 00000000000..f2f8082bec5 --- /dev/null +++ b/common/src/main/java/org/tron/json/JSONArray.java @@ -0,0 +1,151 @@ +package org.tron.json; + +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.annotations.VisibleForTesting; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Drop-in replacement for {@code com.alibaba.fastjson.JSONArray}. + * + * @deprecated Compatibility shim from the fastjson removal. New code should use + * Jackson directly ({@link com.fasterxml.jackson.databind.node.ArrayNode}) + * instead of this helper. + */ +@Deprecated +public class JSONArray implements Iterable { + + private final ArrayNode node; + + public JSONArray(ArrayNode node) { + this.node = node; + } + + public JSONArray() { + this.node = JSON.MAPPER.createArrayNode(); + } + + public static JSONArray parseArray(String text) { + if (JSON.isNullLiteral(text)) { + return null; + } + try { + JsonNode node = JSON.MAPPER.readTree(text); + if (node == null || node.isNull()) { + return null; + } + if (!node.isArray()) { + throw new JSONException("Expected JSON array but got: " + node.getNodeType()); + } + return new JSONArray((ArrayNode) node); + } catch (JSONException e) { + throw e; + } catch (Exception e) { + throw new JSONException(e.getMessage(), e); + } + } + + public int size() { + return node.size(); + } + + private void rangeCheck(int index) { + if (index < 0 || index >= node.size()) { + throw new IndexOutOfBoundsException( + "Index: " + index + ", Size: " + node.size()); + } + } + + @VisibleForTesting + public Object get(int index) { + rangeCheck(index); + return JSONObject.convertNode(node.get(index)); + } + + public JSONObject getJSONObject(int index) { + rangeCheck(index); + JsonNode child = node.get(index); + if (child.isNull()) { + return null; + } + if (child.isObject()) { + return new JSONObject((ObjectNode) child); + } + // Fastjson auto-parses stringified JSON objects + if (child.isTextual()) { + return JSON.parseObject(child.asText()); + } + throw new JSONException("Element at index " + index + " is not an object"); + } + + @VisibleForTesting + public String getString(int index) { + rangeCheck(index); + JsonNode child = node.get(index); + if (child.isNull()) { + return null; + } + if (child.isContainerNode()) { + try { + return JSON.MAPPER.writeValueAsString(child); + } catch (Exception e) { + throw new JSONException("Serialization failed: " + e.getMessage(), e); + } + } + return child.asText(null); + } + + // ------------------------------------------------------------------------- + // Mutation helpers + // ------------------------------------------------------------------------- + + public JSONArray add(JSONObject value) { + node.add(value == null ? node.nullNode() : value.unwrap()); + return this; + } + + @JsonValue + public ArrayNode unwrap() { + return node; + } + + @Override + public Iterator iterator() { + List list = new ArrayList<>(); + node.forEach(child -> list.add(JSONObject.convertNode(child))); + return list.iterator(); + } + + @Override + public String toString() { + try { + return JSON.MAPPER.writeValueAsString(node); + } catch (Exception e) { + throw new JSONException("Serialization failed: " + e.getMessage(), e); + } + } + + public String toJSONString() { + return toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof JSONArray)) { + return false; + } + return node.equals(((JSONArray) o).node); + } + + @Override + public int hashCode() { + return node.hashCode(); + } +} diff --git a/common/src/main/java/org/tron/json/JSONException.java b/common/src/main/java/org/tron/json/JSONException.java new file mode 100644 index 00000000000..079142ae011 --- /dev/null +++ b/common/src/main/java/org/tron/json/JSONException.java @@ -0,0 +1,21 @@ +package org.tron.json; + +/** + * Drop-in replacement for {@code com.alibaba.fastjson.JSONException}. + * + * @deprecated Compatibility shim from the fastjson removal. New code should + * handle Jackson's own exceptions + * ({@link com.fasterxml.jackson.core.JacksonException} and subclasses) + * instead of this helper. + */ +@Deprecated +public class JSONException extends RuntimeException { + + public JSONException(String message) { + super(message); + } + + public JSONException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/common/src/main/java/org/tron/json/JSONObject.java b/common/src/main/java/org/tron/json/JSONObject.java new file mode 100644 index 00000000000..b96c8f6e420 --- /dev/null +++ b/common/src/main/java/org/tron/json/JSONObject.java @@ -0,0 +1,343 @@ +package org.tron.json; + +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.annotations.VisibleForTesting; +import java.math.BigDecimal; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * Drop-in replacement for {@code com.alibaba.fastjson.JSONObject}. + * + *

Note: {@code put(key, null)} removes the key instead of storing a JSON + * {@code null}. This matches Fastjson's default serialization output + * ({@code WriteMapNullValue=OFF} omits null fields), but differs in + * {@link #containsKey(String)} / {@link #size()} after a null put. To emit an + * explicit {@code "key":null}, pass a Jackson {@code NullNode} via + * {@link #put(String, Object)}. + * + * @deprecated Compatibility shim from the fastjson removal. New code should use + * Jackson directly ({@link com.fasterxml.jackson.databind.node.ObjectNode}) + * instead of this helper. + */ +@Deprecated +public class JSONObject { + + private final ObjectNode node; + + public JSONObject(ObjectNode node) { + this.node = node; + } + + public JSONObject() { + this.node = JSON.MAPPER.createObjectNode(); + } + + public static JSONObject parseObject(String text) { + return JSON.parseObject(text); + } + + public boolean containsKey(String key) { + return node.has(key); + } + + @VisibleForTesting + public int size() { + return node.size(); + } + + @VisibleForTesting + public Set keySet() { + Set keys = new LinkedHashSet<>(node.size()); + Iterator names = node.fieldNames(); + while (names.hasNext()) { + keys.add(names.next()); + } + return keys; + } + + public String getString(String key) { + JsonNode child = node.get(key); + if (child == null || child.isNull()) { + return null; + } + if (child.isContainerNode()) { + try { + return JSON.MAPPER.writeValueAsString(child); + } catch (Exception e) { + throw new JSONException("Serialization failed: " + e.getMessage(), e); + } + } + return child.asText(null); + } + + public Boolean getBoolean(String key) { + return TypeUtils.castToBoolean(get(key)); + } + + public Integer getInteger(String key) { + return TypeUtils.castToInt(get(key)); + } + + @VisibleForTesting + public Long getLong(String key) { + return TypeUtils.castToLong(get(key)); + } + + @VisibleForTesting + public long getLongValue(String key) { + Long value = TypeUtils.castToLong(get(key)); + return value == null ? 0L : value; + } + + @VisibleForTesting + public int getIntValue(String key) { + Integer value = TypeUtils.castToInt(get(key)); + return value == null ? 0 : value; + } + + public BigDecimal getBigDecimal(String key) { + return TypeUtils.castToBigDecimal(get(key)); + } + + + public Object get(String key) { + return convertNode(node.get(key)); + } + + static Object convertNode(JsonNode child) { + if (child == null || child.isNull() || child.isMissingNode()) { + return null; + } + if (child.isObject()) { + return new JSONObject((ObjectNode) child); + } + if (child.isArray()) { + return new JSONArray((ArrayNode) child); + } + if (child.isTextual()) { + return child.asText(); + } + if (child.isInt()) { + return child.intValue(); + } + if (child.isShort()) { + return child.shortValue(); + } + if (child.isLong()) { + return child.longValue(); + } + if (child.isBigInteger()) { + return child.bigIntegerValue(); + } + if (child.isBigDecimal()) { + return child.decimalValue(); + } + if (child.isDouble() || child.isFloat()) { + return child.doubleValue(); + } + if (child.isBoolean()) { + return child.booleanValue(); + } + return child.asText(); + } + + public JSONObject getJSONObject(String key) { + JsonNode child = node.get(key); + if (child == null || child.isNull()) { + return null; + } + if (child.isObject()) { + return new JSONObject((ObjectNode) child); + } + // Fastjson auto-parses stringified JSON objects + if (child.isTextual()) { + return JSON.parseObject(child.asText()); + } + throw new JSONException("Field '" + key + "' is not an object"); + } + + public JSONArray getJSONArray(String key) { + JsonNode child = node.get(key); + if (child == null || child.isNull()) { + return null; + } + if (child.isArray()) { + return new JSONArray((ArrayNode) child); + } + if (child.isTextual()) { + return JSON.parseArray(child.asText()); + } + throw new JSONException("Field '" + key + "' is not an array"); + } + + @VisibleForTesting + public T getObject(String key, Class clazz) { + JsonNode child = node.get(key); + if (child == null || child.isNull()) { + return null; + } + try { + if (clazz == JSONObject.class) { + if (!child.isObject()) { + throw new JSONException( + "Field '" + key + "' is " + child.getNodeType() + ", cannot convert to JSONObject"); + } + return clazz.cast(new JSONObject((ObjectNode) child)); + } + if (clazz == JSONArray.class) { + if (!child.isArray()) { + throw new JSONException( + "Field '" + key + "' is " + child.getNodeType() + ", cannot convert to JSONArray"); + } + return clazz.cast(new JSONArray((ArrayNode) child)); + } + return JSON.MAPPER.treeToValue(child, clazz); + } catch (JSONException e) { + throw e; + } catch (Exception e) { + throw new JSONException( + "Failed to convert field '" + key + "' to " + clazz.getSimpleName(), e); + } + } + + public JSONObject put(String key, String value) { + if (value == null) { + node.remove(key); + } else { + node.put(key, value); + } + return this; + } + + public JSONObject put(String key, Boolean value) { + if (value == null) { + node.remove(key); + } else { + node.put(key, value); + } + return this; + } + + public JSONObject put(String key, Integer value) { + if (value == null) { + node.remove(key); + } else { + node.put(key, value); + } + return this; + } + + public JSONObject put(String key, Long value) { + if (value == null) { + node.remove(key); + } else { + node.put(key, value); + } + return this; + } + + public JSONObject put(String key, JSONObject value) { + if (value == null) { + node.remove(key); + } else { + node.set(key, value.unwrap()); + } + return this; + } + + public JSONObject put(String key, JSONArray value) { + if (value == null) { + node.remove(key); + } else { + node.set(key, value.unwrap()); + } + return this; + } + + public JSONObject put(String key, Object value) { + if (value == null) { + node.remove(key); + return this; + } + if (value instanceof JSONObject) { + return put(key, (JSONObject) value); + } + if (value instanceof JSONArray) { + return put(key, (JSONArray) value); + } + if (value instanceof JsonNode) { + node.set(key, (JsonNode) value); + return this; + } + node.set(key, JSON.MAPPER.valueToTree(value)); + return this; + } + + public JSONObject put(String key, List value) { + if (value == null) { + node.remove(key); + return this; + } + ArrayNode arr = JSON.MAPPER.createArrayNode(); + for (Object v : value) { + if (v == null) { + arr.addNull(); + } else if (v instanceof JSONObject) { + arr.add(((JSONObject) v).unwrap()); + } else if (v instanceof JSONArray) { + arr.add(((JSONArray) v).unwrap()); + } else if (v instanceof JsonNode) { + arr.add((JsonNode) v); + } else { + arr.add(JSON.MAPPER.valueToTree(v)); + } + } + node.set(key, arr); + return this; + } + + public Object remove(String key) { + JsonNode removed = node.remove(key); + return convertNode(removed); + } + + @JsonValue + public ObjectNode unwrap() { + return node; + } + + @Override + public String toString() { + try { + return JSON.MAPPER.writeValueAsString(node); + } catch (Exception e) { + throw new JSONException("Serialization failed: " + e.getMessage(), e); + } + } + + public String toJSONString() { + return toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof JSONObject)) { + return false; + } + return node.equals(((JSONObject) o).node); + } + + @Override + public int hashCode() { + return node.hashCode(); + } +} diff --git a/common/src/main/java/org/tron/json/TypeUtils.java b/common/src/main/java/org/tron/json/TypeUtils.java new file mode 100644 index 00000000000..a2d46177e9b --- /dev/null +++ b/common/src/main/java/org/tron/json/TypeUtils.java @@ -0,0 +1,209 @@ +package org.tron.json; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.regex.Pattern; + +/** + * Type coercion utilities ported from {@code com.alibaba.fastjson.util.TypeUtils} + * to maintain exact behavioral parity with Fastjson 1.x. + * + *

Key Fastjson behaviors preserved: + *

    + *
  • Comma stripping in numeric strings ({@code "1,000"} → {@code 1000})
  • + *
  • Trailing-zero removal ({@code "1.0"} → {@code 1} for int/long)
  • + *
  • Boolean coercion from {@code "Y"/"T"/"F"/"N"} strings
  • + *
  • Boolean from Number: {@code intValue() == 1} (only 1 is true)
  • + *
  • {@code NaN}/{@code Infinity} → {@code null} for BigDecimal
  • + *
  • {@code null}/{@code "null"}/{@code "NULL"}/empty → {@code null}
  • + *
+ */ +final class TypeUtils { + + private static final Pattern NUMBER_WITH_TRAILING_ZEROS_PATTERN = + Pattern.compile("\\.0*$"); + + private TypeUtils() { + } + + static Boolean castToBoolean(Object value) { + if (value == null) { + return null; + } + if (value instanceof Boolean) { + return (Boolean) value; + } + + if (value instanceof BigDecimal) { + return intValue((BigDecimal) value) == 1; + } + + if (value instanceof Number) { + return ((Number) value).intValue() == 1; + } + + if (value instanceof String) { + String strVal = (String) value; + if (strVal.isEmpty() + || "null".equals(strVal) + || "NULL".equals(strVal)) { + return null; + } + if ("true".equalsIgnoreCase(strVal) + || "1".equals(strVal)) { + return Boolean.TRUE; + } + if ("false".equalsIgnoreCase(strVal) + || "0".equals(strVal)) { + return Boolean.FALSE; + } + if ("Y".equalsIgnoreCase(strVal) + || "T".equals(strVal)) { + return Boolean.TRUE; + } + if ("F".equalsIgnoreCase(strVal) + || "N".equals(strVal)) { + return Boolean.FALSE; + } + } + throw new JSONException("can not cast to boolean, value : " + value); + } + + static Integer castToInt(Object value) { + if (value == null) { + return null; + } + + if (value instanceof Integer) { + return (Integer) value; + } + + if (value instanceof BigDecimal) { + return intValue((BigDecimal) value); + } + + if (value instanceof Number) { + return ((Number) value).intValue(); + } + + if (value instanceof String) { + String strVal = (String) value; + if (strVal.isEmpty() + || "null".equals(strVal) + || "NULL".equals(strVal)) { + return null; + } + if (strVal.indexOf(',') != -1) { + strVal = strVal.replaceAll(",", ""); + } + strVal = NUMBER_WITH_TRAILING_ZEROS_PATTERN.matcher(strVal).replaceAll(""); + return Integer.parseInt(strVal); + } + + if (value instanceof Boolean) { + return (Boolean) value ? 1 : 0; + } + + throw new JSONException("can not cast to int, value : " + value); + } + + static Long castToLong(Object value) { + if (value == null) { + return null; + } + + if (value instanceof BigDecimal) { + return longValue((BigDecimal) value); + } + + if (value instanceof Number) { + return ((Number) value).longValue(); + } + + if (value instanceof String) { + String strVal = (String) value; + if (strVal.isEmpty() + || "null".equals(strVal) + || "NULL".equals(strVal)) { + return null; + } + if (strVal.indexOf(',') != -1) { + strVal = strVal.replaceAll(",", ""); + } + try { + return Long.parseLong(strVal); + } catch (NumberFormatException ex) { + // Fastjson falls through to BigDecimal attempt + } + + strVal = NUMBER_WITH_TRAILING_ZEROS_PATTERN.matcher(strVal).replaceAll(""); + return Long.parseLong(strVal); + } + + if (value instanceof Boolean) { + return (Boolean) value ? 1L : 0L; + } + + throw new JSONException("can not cast to long, value : " + value); + } + + static BigDecimal castToBigDecimal(Object value) { + if (value == null) { + return null; + } + + if (value instanceof Float) { + if (Float.isNaN((Float) value) || Float.isInfinite((Float) value)) { + return null; + } + } else if (value instanceof Double) { + if (Double.isNaN((Double) value) || Double.isInfinite((Double) value)) { + return null; + } + } else if (value instanceof BigDecimal) { + return (BigDecimal) value; + } else if (value instanceof BigInteger) { + return new BigDecimal((BigInteger) value); + } + + String strVal = value.toString(); + + if (strVal.isEmpty() || "null".equalsIgnoreCase(strVal)) { + return null; + } + + if (strVal.length() > 65535) { + throw new JSONException("decimal overflow"); + } + + if (strVal.indexOf(',') != -1) { + strVal = strVal.replaceAll(",", ""); + } + + return new BigDecimal(strVal); + } + + // -- BigDecimal helper methods (ported from Fastjson) -- + + static int intValue(BigDecimal decimal) { + if (decimal == null) { + return 0; + } + int scale = decimal.scale(); + if (scale >= -100 && scale <= 100) { + return decimal.intValue(); + } + return decimal.intValueExact(); + } + + static long longValue(BigDecimal decimal) { + if (decimal == null) { + return 0; + } + int scale = decimal.scale(); + if (scale >= -100 && scale <= 100) { + return decimal.longValue(); + } + return decimal.longValueExact(); + } +} diff --git a/common/src/main/resources/reference.conf b/common/src/main/resources/reference.conf index 7bf1d24da5a..63e5d86a4af 100644 --- a/common/src/main/resources/reference.conf +++ b/common/src/main/resources/reference.conf @@ -166,25 +166,11 @@ crypto { # Energy limit block number (config key has typo "enery" preserved for backward compatibility) enery.limit.block.num = 4727890 -# Actuator whitelist — empty means all actuators allowed -actuator { - whitelist = [] -} - node.metrics = { prometheus { enable = false port = 9527 } - - storageEnable = false - - influxdb { - ip = "" - port = 8086 - database = "metrics" - metricsReportInterval = 10 - } } node { @@ -202,6 +188,8 @@ node { # Number of blocks to fetch in one batch during sync. Range: [100, 2000]. syncFetchBatchNum = 2000 + # Max in-flight (requested but not yet processed) blocks during sync. Range: [50, 2000]. + maxPendingBlockSize = 500 # Number of validate sign threads, default availableProcessors # Number of validate sign threads, 0 = auto (availableProcessors) @@ -274,6 +262,8 @@ node { # Maximum HTTP request body size, default 4MB. Independent from rpc.maxMessageSize. maxMessageSize = 4M + maxNestingDepth = 100 + maxTokenCount = 100000 } rpc { @@ -759,7 +749,6 @@ committee = { consensusLogicOptimization = 0 allowTvmCancun = 0 allowTvmBlob = 0 - allowTvmOsaka = 0 allowAccountAssetOptimization = 0 allowAssetOptimization = 0 allowNewReward = 0 diff --git a/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java b/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java deleted file mode 100644 index b641e4d1924..00000000000 --- a/common/src/test/java/org/tron/core/config/args/MetricsConfigTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.tron.core.config.args; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import org.junit.Test; - -public class MetricsConfigTest { - - private static Config withRef(String hocon) { - return ConfigFactory.parseString(hocon).withFallback(ConfigFactory.defaultReference()); - } - - private static Config withRef() { - return ConfigFactory.defaultReference(); - } - - @Test - public void testDefaults() { - Config empty = withRef(); - MetricsConfig mc = MetricsConfig.fromConfig(empty); - assertFalse(mc.isStorageEnable()); - assertFalse(mc.getPrometheus().isEnable()); - assertEquals(9527, mc.getPrometheus().getPort()); - assertEquals(8086, mc.getInfluxdb().getPort()); - } - - @Test - public void testFromConfig() { - Config config = withRef( - "node.metrics {" - + " storageEnable = true," - + " prometheus { enable = true, port = 9999 }," - + " influxdb { ip = \"10.0.0.1\", port = 9086, database = mydb," - + " metricsReportInterval = 30 } }"); - MetricsConfig mc = MetricsConfig.fromConfig(config); - assertTrue(mc.isStorageEnable()); - assertTrue(mc.getPrometheus().isEnable()); - assertEquals(9999, mc.getPrometheus().getPort()); - assertEquals("10.0.0.1", mc.getInfluxdb().getIp()); - assertEquals(9086, mc.getInfluxdb().getPort()); - assertEquals("mydb", mc.getInfluxdb().getDatabase()); - assertEquals(30, mc.getInfluxdb().getMetricsReportInterval()); - } -} diff --git a/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java b/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java index ed369d6c35f..89a2d6e7b3c 100644 --- a/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java +++ b/common/src/test/java/org/tron/core/config/args/MiscConfigTest.java @@ -32,7 +32,6 @@ public void testDefaults() { assertEquals("eckey", mc.getCryptoEngine()); // reference.conf has seed.node.ip.list with actual IPs assertFalse(mc.getSeedNodeIpList().isEmpty()); - assertTrue(mc.getActuatorWhitelist().isEmpty()); } @Test @@ -42,15 +41,12 @@ public void testFromConfig() { + " balance { history { lookup = true } } }\n" + "trx { reference { block = head } }\n" + "crypto { engine = sm2 }\n" - + "seed.node { ip.list = [\"1.2.3.4:18888\"] }\n" - + "actuator { whitelist = [\"CreateSmartContract\"] }"); + + "seed.node { ip.list = [\"1.2.3.4:18888\"] }"); MiscConfig mc = MiscConfig.fromConfig(config); assertFalse(mc.isNeedToUpdateAsset()); assertTrue(mc.isHistoryBalanceLookup()); assertEquals("head", mc.getTrxReferenceBlock()); assertEquals("sm2", mc.getCryptoEngine()); assertEquals(1, mc.getSeedNodeIpList().size()); - assertEquals(1, mc.getActuatorWhitelist().size()); - assertTrue(mc.getActuatorWhitelist().contains("CreateSmartContract")); } } diff --git a/framework/build.gradle b/framework/build.gradle index 82cde33fe7e..7b3e6ddb968 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -40,11 +40,9 @@ dependencies { // end local libraries implementation group: 'com.beust', name: 'jcommander', version: '1.78' implementation group: 'io.dropwizard.metrics', name: 'metrics-core', version: '3.1.2' - implementation group: 'com.github.davidb', name: 'metrics-influxdb', version: '0.8.2' // http implementation 'org.eclipse.jetty:jetty-server:9.4.58.v20250814' implementation 'org.eclipse.jetty:jetty-servlet:9.4.58.v20250814' - implementation 'com.alibaba:fastjson:1.2.83' // end http // https://mvnrepository.com/artifact/com.github.briandilley.jsonrpc4j/jsonrpc4j 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 2c41bedffb7..4f31b2815ad 100644 --- a/framework/src/main/java/org/tron/common/application/ApplicationImpl.java +++ b/framework/src/main/java/org/tron/common/application/ApplicationImpl.java @@ -8,7 +8,6 @@ import org.tron.core.config.args.Args; import org.tron.core.consensus.ConsensusService; 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; @@ -46,7 +45,6 @@ public void startup() { if ((!Args.getInstance().isSolidityNode()) && (!Args.getInstance().isP2pDisable())) { tronNetService.start(); } - MetricsUtil.init(); } @Override diff --git a/framework/src/main/java/org/tron/common/logsfilter/ContractEventParserJson.java b/framework/src/main/java/org/tron/common/logsfilter/ContractEventParserJson.java index 4236230ef18..4424e28f237 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/ContractEventParserJson.java +++ b/framework/src/main/java/org/tron/common/logsfilter/ContractEventParserJson.java @@ -1,7 +1,5 @@ package org.tron.common.logsfilter; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -9,6 +7,8 @@ import org.apache.commons.lang3.ArrayUtils; import org.bouncycastle.util.encoders.Hex; import org.pf4j.util.StringUtils; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; @Slf4j(topic = "Parser") public class ContractEventParserJson extends ContractEventParser { diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsule.java index cc5b2ce473c..3ebb5102bba 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsule.java @@ -135,11 +135,11 @@ public void processTrigger() { EventPluginLoader.getInstance().postContractEventTrigger((ContractEventTrigger) event); } - if (EventPluginLoader.getInstance().isSolidityEventTriggerEnable()) { + if (EventPluginLoader.getInstance().isSolidityEventTriggerEnable() + && !contractTrigger.isRemoved()) { boolean result = Args.getSolidityContractEventTriggerMap().computeIfAbsent(event .getBlockNumber(), listBlk -> new LinkedBlockingQueue()) .offer((ContractEventTrigger) event); - if (!result) { logger.info("too many triggers, solidity event trigger lost: {}", event.getUniqueId()); @@ -159,11 +159,11 @@ public void processTrigger() { EventPluginLoader.getInstance().postContractLogTrigger(logTrigger); } - if (EventPluginLoader.getInstance().isSolidityLogTriggerRedundancy()) { + if (EventPluginLoader.getInstance().isSolidityLogTriggerRedundancy() + && !contractTrigger.isRemoved()) { boolean result = Args.getSolidityContractLogTriggerMap().computeIfAbsent(event .getBlockNumber(), listBlk -> new LinkedBlockingQueue()) .offer(logTrigger); - if (!result) { logger.info("too many triggers, solidity log trigger lost: {}", logTrigger.getUniqueId()); @@ -175,11 +175,11 @@ public void processTrigger() { EventPluginLoader.getInstance().postContractLogTrigger((ContractLogTrigger) event); } - if (EventPluginLoader.getInstance().isSolidityLogTriggerEnable()) { + if (EventPluginLoader.getInstance().isSolidityLogTriggerEnable() + && !contractTrigger.isRemoved()) { boolean result = Args.getSolidityContractLogTriggerMap().computeIfAbsent(event .getBlockNumber(), listBlk -> new LinkedBlockingQueue()) .offer((ContractLogTrigger) event); - if (!result) { logger.info("too many triggers, solidity log trigger lost: {}", event.getUniqueId()); diff --git a/framework/src/main/java/org/tron/common/runtime/RuntimeImpl.java b/framework/src/main/java/org/tron/common/runtime/RuntimeImpl.java index 4ba53c7dc92..3dccfc5d146 100644 --- a/framework/src/main/java/org/tron/common/runtime/RuntimeImpl.java +++ b/framework/src/main/java/org/tron/common/runtime/RuntimeImpl.java @@ -2,11 +2,9 @@ import java.util.List; import java.util.Objects; -import java.util.Set; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.tron.common.parameter.CommonParameter; import org.tron.core.actuator.Actuator; import org.tron.core.actuator.Actuator2; import org.tron.core.actuator.ActuatorCreator; @@ -46,10 +44,6 @@ public void execute(TransactionContext context) switch (contractType.getNumber()) { case ContractType.TriggerSmartContract_VALUE: case ContractType.CreateSmartContract_VALUE: - Set actuatorSet = CommonParameter.getInstance().getActuatorSet(); - if (!actuatorSet.isEmpty() && !actuatorSet.contains(VMActuator.class.getSimpleName())) { - throw new ContractValidateException("not exist contract " + "SmartContract"); - } actuator2 = new VMActuator(context.isStatic()); break; default: diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index 279153115d9..0482643d8d0 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -1480,6 +1480,11 @@ public Protocol.ChainParameters getChainParameters() { .setValue(dbManager.getDynamicPropertiesStore().getAllowTvmOsaka()) .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() + .setKey("getAllowTvmPrague") + .setValue(dbManager.getDynamicPropertiesStore().getAllowTvmPrague()) + .build()); + builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder() .setKey("getAllowHardenResourceCalculation") .setValue(dbManager.getDynamicPropertiesStore().getAllowHardenResourceCalculation()) diff --git a/framework/src/main/java/org/tron/core/config/DefaultConfig.java b/framework/src/main/java/org/tron/core/config/DefaultConfig.java index d01820626c3..9bce903d411 100755 --- a/framework/src/main/java/org/tron/core/config/DefaultConfig.java +++ b/framework/src/main/java/org/tron/core/config/DefaultConfig.java @@ -1,6 +1,5 @@ package org.tron.core.config; -import com.alibaba.fastjson.parser.ParserConfig; import lombok.extern.slf4j.Slf4j; import org.rocksdb.RocksDB; import org.springframework.beans.factory.annotation.Autowired; @@ -27,7 +26,6 @@ public class DefaultConfig { static { RocksDB.loadLibrary(); - ParserConfig.getGlobalInstance().setSafeMode(true); } @Autowired 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 382c8cfc30c..652f37a90db 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 @@ -2,43 +2,27 @@ import static java.lang.System.exit; import static org.tron.common.math.Maths.max; -import static org.tron.common.math.Maths.min; import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET; -import static org.tron.core.Constant.DEFAULT_PROPOSAL_EXPIRE_TIME; -import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE; -import static org.tron.core.Constant.DYNAMIC_ENERGY_MAX_FACTOR_RANGE; import static org.tron.core.Constant.ENERGY_LIMIT_IN_CONSTANT_TX; -import static org.tron.core.Constant.MAX_PROPOSAL_EXPIRE_TIME; -import static org.tron.core.Constant.MIN_PROPOSAL_EXPIRE_TIME; -import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCE_TIMEOUT_PERCENT; -import static org.tron.core.config.Parameter.ChainConstant.MAX_ACTIVE_WITNESS_NUM; -import static org.tron.core.exception.TronError.ErrCode.PARAMETER_INIT; import com.beust.jcommander.JCommander; import com.beust.jcommander.ParameterDescription; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.typesafe.config.Config; -import com.typesafe.config.ConfigObject; -import io.grpc.internal.GrpcUtil; -import io.grpc.netty.NettyServerBuilder; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.concurrent.BlockingQueue; @@ -70,8 +54,6 @@ import org.tron.core.Constant; import org.tron.core.Wallet; import org.tron.core.config.Configuration; -import org.tron.core.config.Parameter.NetConstants; -import org.tron.core.config.Parameter.NodeConstant; import org.tron.core.exception.TronError; import org.tron.core.store.AccountStore; import org.tron.p2p.P2pConfig; @@ -327,8 +309,6 @@ private static void applyMiscConfig(MiscConfig mc) { PARAMETER.trxReferenceBlock = mc.getTrxReferenceBlock(); PARAMETER.trxExpirationTimeInMilliseconds = mc.getTrxExpirationTimeInMilliseconds(); PARAMETER.blockNumForEnergyLimit = mc.getBlockNumForEnergyLimit(); - PARAMETER.actuatorSet = mc.getActuatorWhitelist(); - // seed.node — top-level config section, not under "node" // Config structure is arguably misplaced but preserved for backward compatibility PARAMETER.seedNode = new SeedNode(); @@ -462,12 +442,6 @@ private static void applyEventConfig(EventConfig ec) { * Note: node.metricsEnable is handled in applyNodeConfig (it's a node-level field). */ private static void applyMetricsConfig(MetricsConfig mc) { - PARAMETER.metricsStorageEnable = mc.isStorageEnable(); - PARAMETER.influxDbIp = mc.getInfluxdb().getIp().isEmpty() - ? Constant.LOCAL_HOST : mc.getInfluxdb().getIp(); - PARAMETER.influxDbPort = mc.getInfluxdb().getPort(); - PARAMETER.influxDbDatabase = mc.getInfluxdb().getDatabase(); - PARAMETER.metricsReportInterval = mc.getInfluxdb().getMetricsReportInterval(); PARAMETER.metricsPrometheusEnable = mc.getPrometheus().isEnable(); PARAMETER.metricsPrometheusPort = mc.getPrometheus().getPort(); } @@ -512,7 +486,6 @@ private static void applyCommitteeConfig(CommitteeConfig cc) { PARAMETER.consensusLogicOptimization = cc.getConsensusLogicOptimization(); PARAMETER.allowTvmCancun = cc.getAllowTvmCancun(); PARAMETER.allowTvmBlob = cc.getAllowTvmBlob(); - PARAMETER.allowTvmOsaka = cc.getAllowTvmOsaka(); PARAMETER.unfreezeDelayDays = cc.getUnfreezeDelayDays(); // allowReceiptsMerkleRoot not in CommonParameter — skip for now PARAMETER.allowAccountAssetOptimization = cc.getAllowAccountAssetOptimization(); @@ -577,6 +550,8 @@ private static void applyNodeConfig(NodeConfig nc) { PARAMETER.solidityHttpPort = http.getSolidityPort(); PARAMETER.pBFTHttpPort = http.getPBFTPort(); PARAMETER.httpMaxMessageSize = http.getMaxMessageSize(); + PARAMETER.maxNestingDepth = http.getMaxNestingDepth(); + PARAMETER.maxTokenCount = http.getMaxTokenCount(); // ---- JSON-RPC sub-bean ---- NodeConfig.JsonRpcConfig jsonrpc = nc.getJsonrpc(); @@ -626,6 +601,7 @@ private static void applyNodeConfig(NodeConfig nc) { PARAMETER.nodeEnableIpv6 = nc.isEnableIpv6(); PARAMETER.syncFetchBatchNum = nc.getSyncFetchBatchNum(); + PARAMETER.maxPendingBlockSize = nc.getMaxPendingBlockSize(); PARAMETER.solidityThreads = nc.getSolidityThreads(); PARAMETER.blockProducedTimeOut = nc.getBlockProducedTimeOut(); @@ -790,8 +766,6 @@ public static void applyConfigParams( // Node backup: from NodeConfig bean applyNodeBackupConfig(nodeConfig); - // actuatorSet already set in applyMiscConfig - // Metrics config: bind from config.conf "node.metrics" section metricsConfig = MetricsConfig.fromConfig(config); applyMetricsConfig(metricsConfig); diff --git a/framework/src/main/java/org/tron/core/consensus/ProposalService.java b/framework/src/main/java/org/tron/core/consensus/ProposalService.java index c95ec1c657d..543deab2fc6 100644 --- a/framework/src/main/java/org/tron/core/consensus/ProposalService.java +++ b/framework/src/main/java/org/tron/core/consensus/ProposalService.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.tron.core.capsule.ProposalCapsule; import org.tron.core.config.Parameter.ForkBlockVersionEnum; +import org.tron.core.db.HistoryBlockHashUtil; import org.tron.core.db.Manager; import org.tron.core.store.DynamicPropertiesStore; import org.tron.core.utils.ProposalUtil; @@ -396,6 +397,11 @@ public static boolean process(Manager manager, ProposalCapsule proposalCapsule) manager.getDynamicPropertiesStore().saveAllowTvmOsaka(entry.getValue()); break; } + case ALLOW_TVM_PRAGUE: { + manager.getDynamicPropertiesStore().saveAllowTvmPrague(entry.getValue()); + HistoryBlockHashUtil.deploy(manager); + break; + } case ALLOW_HARDEN_RESOURCE_CALCULATION: { manager.getDynamicPropertiesStore() .saveAllowHardenResourceCalculation(entry.getValue()); diff --git a/framework/src/main/java/org/tron/core/db/HistoryBlockHashUtil.java b/framework/src/main/java/org/tron/core/db/HistoryBlockHashUtil.java new file mode 100644 index 00000000000..19a0e278e08 --- /dev/null +++ b/framework/src/main/java/org/tron/core/db/HistoryBlockHashUtil.java @@ -0,0 +1,158 @@ +package org.tron.core.db; + +import com.google.protobuf.ByteString; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.util.encoders.Hex; +import org.tron.common.runtime.vm.DataWord; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.CodeCapsule; +import org.tron.core.capsule.ContractCapsule; +import org.tron.core.vm.program.Storage; +import org.tron.protos.Protocol; +import org.tron.protos.Protocol.Account; +import org.tron.protos.contract.SmartContractOuterClass.SmartContract; + +/** + * TIP-2935 (EIP-2935): serve historical block hashes from state. + * + *

Approach A1 — at proposal activation, deploy the BlockHashHistory bytecode + * and minimal contract/account metadata via direct store writes; on every block + * (before the tx loop) write the parent block hash to slot + * {@code (blockNum - 1) % HISTORY_SERVE_WINDOW} via {@link Storage}. + * No VM execution is needed for {@code set()}; user contracts read via normal + * STATICCALL which executes the deployed bytecode. + */ +@Slf4j(topic = "DB") +public class HistoryBlockHashUtil { + + public static final long HISTORY_SERVE_WINDOW = 8191L; + + // 21-byte TRON address (0x41 prefix + 20-byte EVM address 0x0000F908...2935) + public static final byte[] HISTORY_STORAGE_ADDRESS = + Hex.decode("410000f90827f1c53a10cb7a02335b175320002935"); + + // Recovered sender of the EIP-2935 presigned (no-private-key) deploy + // transaction on Ethereum, in TRON 21-byte form. Used as {@code originAddress} + // on the deployed SmartContract so the deployer-of-record matches Ethereum + // byte-for-byte; cross-chain tooling that inspects this field sees the same + // address on both sides. + public static final byte[] HISTORY_DEPLOYER_ADDRESS = + Hex.decode("413462413af4609098e1e27a490f554f260213d685"); + + // TIP-2935 runtime bytecode (83 bytes, no constructor prefix). Identical to + // EIP-2935's so the same address resolves to the same code on both chains. + public static final byte[] HISTORY_STORAGE_CODE = Hex.decode( + "3373fffffffffffffffffffffffffffffffffffffffe" + + "14604657602036036042575f35600143038111604257" + + "611fff81430311604257611fff9006545f5260205ff3" + + "5b5f5ffd5b5f35611fff60014303065500"); + + public static final String HISTORY_STORAGE_NAME = "BlockHashHistory"; + + // Account template for the new-account branch of {@code deploy()} (no prior + // state at the canonical address). Equivalent to create2's + // {@code createAccount(addr, name, Contract)}: only type, accountName, and + // address are set. The pre-existing-account branch never uses this template + // — it mutates the existing capsule in place to preserve balance / asset + // state, mirroring the CREATE2 collision path. Safe to share: the proto is + // immutable, and AccountCapsule mutations rebuild via {@code toBuilder}. + private static final Account HISTORY_STORAGE_ACCOUNT = Account.newBuilder() + .setType(Protocol.AccountType.Contract) + .setAccountName(ByteString.copyFromUtf8(HISTORY_STORAGE_NAME)) + .setAddress(ByteString.copyFrom(HISTORY_STORAGE_ADDRESS)) + .build(); + + // SmartContract template: every field is fixed at activation time, so the + // proto is immutable and shared across calls. Mirrors the create2 path's + // shape (version=0, contractAddress, consumeUserResourcePercent=100, + // originAddress) plus a descriptive name. No trxHash since activation is + // not a transaction. + private static final SmartContract HISTORY_STORAGE_CONTRACT = SmartContract.newBuilder() + .setName(HISTORY_STORAGE_NAME) + .setContractAddress(ByteString.copyFrom(HISTORY_STORAGE_ADDRESS)) + .setOriginAddress(ByteString.copyFrom(HISTORY_DEPLOYER_ADDRESS)) + .setConsumeUserResourcePercent(100L) + .build(); + + private HistoryBlockHashUtil() { + } + + /** + * Deploy the TIP-2935 BlockHashHistory contract at {@code HISTORY_STORAGE_ADDRESS}. + * If foreign code or contract metadata already sits at the canonical address, + * logs a warning and returns without writing — the collision is deterministic + * across nodes (same pre-state ⇒ same decision), so the proposal flag still + * commits and chain consensus is intact. The foreign contract executes as-is + * on every node; TIP-2935 functionality is silently absent at this address. + * A SHA-3 pre-image of the address is the only realistic way that branch + * fires, so it's belt-and-braces. A pre-existing non-contract account at the + * address is the common case (anyone can transfer TRX there to activate it + * as an EOA), so we upgrade its type to {@code Contract} in place — matching + * the CREATE2 collision branch ({@code updateAccountType} + + * {@code clearDelegatedResource}) and preserving balance/asset state. + * + *

Called only from {@code ProposalService} inside maintenance-time block + * processing. Proposal validation rejects re-activation, so this runs at most + * once per chain history; the three store writes share the block's revoking + * session, so any node-local exception (RocksDB / IO) propagates and rolls + * the {@code saveAllowTvmPrague(1)} write back atomically. + */ + public static void deploy(Manager manager) { + if (manager.getCodeStore().has(HISTORY_STORAGE_ADDRESS) + || manager.getContractStore().has(HISTORY_STORAGE_ADDRESS)) { + logger.warn("TIP-2935: foreign state at {}, skipping deploy", + Hex.toHexString(HISTORY_STORAGE_ADDRESS)); + return; + } + + manager.getCodeStore().put(HISTORY_STORAGE_ADDRESS, + new CodeCapsule(HISTORY_STORAGE_CODE)); + manager.getContractStore().put(HISTORY_STORAGE_ADDRESS, + new ContractCapsule(HISTORY_STORAGE_CONTRACT)); + + AccountCapsule account = manager.getAccountStore().get(HISTORY_STORAGE_ADDRESS); + boolean accountExisting = account != null; + if (!accountExisting) { + account = new AccountCapsule(HISTORY_STORAGE_ACCOUNT); + } else { + account.updateAccountType(Protocol.AccountType.Contract); + account.clearDelegatedResource(); + } + manager.getAccountStore().put(HISTORY_STORAGE_ADDRESS, account); + + // Flip the install marker only after all three store writes succeed; this + // gates the per-block write() path so a skipped deploy never mutates + // foreign storage. Any node-local exception above propagates and rolls + // the marker back together with the partial writes via the revoking session. + manager.getDynamicPropertiesStore().saveBlockHashHistoryInstalled(1L); + + logger.info("TIP-2935: deployed BlockHashHistory at {} (preExistingAccount={})", + Hex.toHexString(HISTORY_STORAGE_ADDRESS), accountExisting); + } + + /** + * Write the parent block hash to storage at slot + * {@code (blockNum - 1) % HISTORY_SERVE_WINDOW}. Called from + * {@code Manager.processBlock} before the tx loop so transactions can SLOAD + * it via STATICCALL to the deployed bytecode. + */ + public static void write(Manager manager, BlockCapsule block) { + // Genesis has no parent; applyBlock never invokes this for block 0, but be + // explicit so (0-1) % 8191 = -1 in Java can never corrupt a slot. + if (block.getNum() <= 0) { + return; + } + // Defense-in-depth: deploy() skips on foreign state at the canonical + // address, but the proposal flag still commits. Gate on the install + // marker (set at the tail of a successful deploy()) so write() can never + // overwrite an unrelated contract's storage. Single store hit, cached. + if (!manager.getDynamicPropertiesStore().isBlockHashHistoryInstalled()) { + return; + } + long slot = (block.getNum() - 1) % HISTORY_SERVE_WINDOW; + Storage storage = new Storage(HISTORY_STORAGE_ADDRESS, manager.getStorageRowStore()); + storage.put(new DataWord(slot), new DataWord(block.getParentHash().getBytes())); + storage.commit(); + } +} 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 7d56a442141..2c188c90b30 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -1391,6 +1391,7 @@ public void pushBlock(final BlockCapsule block) } catch (Throwable throwable) { logger.error(throwable.getMessage(), throwable); khaosDb.removeBlk(block.getBlockId()); + clearSolidityContractTriggerCache(block.getNum()); throw throwable; } long newSolidNum = getDynamicPropertiesStore().getLatestSolidifiedBlockNum(); @@ -1638,6 +1639,7 @@ public BlockCapsule generateBlock(Miner miner, long blockTime, long timeout) { session.reset(); session.setValue(revokingStore.buildSession()); + HistoryBlockHashUtil.write(this, blockCapsule); accountStateCallBack.preExecute(blockCapsule); if (getDynamicPropertiesStore().getAllowMultiSign() == 1) { @@ -1867,6 +1869,7 @@ private void processBlock(BlockCapsule block, List txs) TransactionRetCapsule transactionRetCapsule = new TransactionRetCapsule(block); + HistoryBlockHashUtil.write(this, block); try { merkleContainer.resetCurrentMerkleTree(); accountStateCallBack.preExecute(block); @@ -2395,6 +2398,16 @@ private void reOrgContractTrigger() { getDynamicPropertiesStore().getLatestBlockHeaderHash()); } } + clearSolidityContractTriggerCache(getHeadBlockNum()); + } + + private void clearSolidityContractTriggerCache(long blockNum) { + if (eventPluginLoaded + && (EventPluginLoader.getInstance().isSolidityEventTriggerEnable() + || EventPluginLoader.getInstance().isSolidityLogTriggerEnable())) { + Args.getSolidityContractLogTriggerMap().remove(blockNum); + Args.getSolidityContractEventTriggerMap().remove(blockNum); + } } private void postContractTrigger(final TransactionTrace trace, boolean remove, String blockHash) { @@ -2414,9 +2427,14 @@ private void postContractTrigger(final TransactionTrace trace, boolean remove, S .getLatestSolidifiedBlockNum()); contractTriggerCapsule.setBlockHash(blockHash); - if (!triggerCapsuleQueue.offer(contractTriggerCapsule)) { - logger.info("Too many triggers, contract log trigger lost: {}.", - trigger.getTransactionId()); + // Process synchronously to avoid race condition between async queue and + // reOrgContractTrigger cache clearing. Performance is not impacted because + // processTrigger() only enqueues events into the plugin's internal queue + // without blocking on actual I/O. + try { + contractTriggerCapsule.processTrigger(); + } catch (Throwable throwable) { + logger.warn("Post contract trigger failed.", throwable); } } } diff --git a/framework/src/main/java/org/tron/core/metrics/MetricsUtil.java b/framework/src/main/java/org/tron/core/metrics/MetricsUtil.java index 51a0182eed8..6878f5a6b1e 100644 --- a/framework/src/main/java/org/tron/core/metrics/MetricsUtil.java +++ b/framework/src/main/java/org/tron/core/metrics/MetricsUtil.java @@ -3,18 +3,12 @@ import com.codahale.metrics.Counter; import com.codahale.metrics.Histogram; import com.codahale.metrics.Meter; -import com.codahale.metrics.MetricFilter; import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.ScheduledReporter; import java.util.SortedMap; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; -import metrics_influxdb.InfluxdbReporter; -import metrics_influxdb.api.protocols.InfluxdbProtocols; import org.tron.common.parameter.CommonParameter; -import org.tron.core.Constant; import org.tron.core.metrics.net.RateInfo; @Slf4j(topic = "metrics") @@ -22,26 +16,6 @@ public class MetricsUtil { private static MetricRegistry metricRegistry = new MetricRegistry(); - public static void init() { - if (CommonParameter.getInstance().isNodeMetricsEnable() - && CommonParameter.getInstance().isMetricsStorageEnable()) { - String ip = CommonParameter.getInstance().getInfluxDbIp(); - int port = CommonParameter.getInstance().getInfluxDbPort(); - String dataBase = CommonParameter.getInstance().getInfluxDbDatabase(); - ScheduledReporter influxReport = InfluxdbReporter - .forRegistry(metricRegistry) - .protocol(InfluxdbProtocols.http(ip, port, dataBase)) - .convertRatesTo(TimeUnit.SECONDS) - .convertDurationsTo(TimeUnit.MILLISECONDS) - .filter(MetricFilter.ALL) - .skipIdleMetrics(false) - .build(); - int interval = CommonParameter.getInstance().getMetricsReportInterval() - * Constant.ONE_THOUSAND; - influxReport.start(interval, TimeUnit.MILLISECONDS); - } - } - public static Histogram getHistogram(String key) { return metricRegistry.histogram(key); } diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java index 0436b48d374..d6bd439d7ff 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java @@ -3,6 +3,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import lombok.Getter; @@ -44,6 +45,7 @@ public class TransactionsMsgHandler implements TronMsgHandler { private BlockingQueue queue = new LinkedBlockingQueue(); + private volatile boolean isClosed = false; private int threadNum = Args.getInstance().getValidateSignThreadNum(); private final String trxEsName = "trx-msg-handler"; private ExecutorService trxHandlePool = ExecutorServiceManager.newThreadPoolExecutor( @@ -58,8 +60,14 @@ public void init() { } public void close() { - ExecutorServiceManager.shutdownAndAwaitTermination(trxHandlePool, trxEsName); + isClosed = true; + // Stop the scheduler first so no new tasks are drained from smartContractQueue. ExecutorServiceManager.shutdownAndAwaitTermination(smartContractExecutor, smartEsName); + // Then shutdown the worker pool to finish already-submitted tasks. + ExecutorServiceManager.shutdownAndAwaitTermination(trxHandlePool, trxEsName); + // Discard any remaining items and release references. + smartContractQueue.clear(); + queue.clear(); } public boolean isBusy() { @@ -68,6 +76,10 @@ public boolean isBusy() { @Override public void processMessage(PeerConnection peer, TronMessage msg) throws P2pException { + if (isClosed) { + logger.info("TransactionsMsgHandler is closed, drop message"); + return; + } TransactionsMessage transactionsMessage = (TransactionsMessage) msg; check(peer, transactionsMessage); for (Transaction trx : transactionsMessage.getTransactions().getTransactionsList()) { @@ -78,6 +90,10 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep int trxHandlePoolQueueSize = 0; int dropSmartContractCount = 0; for (Transaction trx : transactionsMessage.getTransactions().getTransactionsList()) { + if (isClosed) { + logger.info("TransactionsMsgHandler is closed during processing, stop submit"); + break; + } int type = trx.getRawData().getContract(0).getType().getNumber(); if (type == ContractType.TriggerSmartContract_VALUE || type == ContractType.CreateSmartContract_VALUE) { @@ -87,8 +103,13 @@ public void processMessage(PeerConnection peer, TronMessage msg) throws P2pExcep dropSmartContractCount++; } } else { - ExecutorServiceManager.submit( - trxHandlePool, () -> handleTransaction(peer, new TransactionMessage(trx))); + try { + ExecutorServiceManager.submit( + trxHandlePool, () -> handleTransaction(peer, new TransactionMessage(trx))); + } catch (RejectedExecutionException e) { + logger.warn("Submit task to {} failed", trxEsName); + break; + } } } diff --git a/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java b/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java index 75349bd4c19..32230612743 100644 --- a/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java +++ b/framework/src/main/java/org/tron/core/net/service/sync/SyncService.java @@ -5,6 +5,7 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -44,9 +45,9 @@ public class SyncService { @Autowired private PbftDataSyncHandler pbftDataSyncHandler; - private Map blockWaitToProcess = new ConcurrentHashMap<>(); + private Map blockWaitToProcess = new ConcurrentHashMap<>(); - private Map blockJustReceived = new ConcurrentHashMap<>(); + private Map blockJustReceived = new ConcurrentHashMap<>(); private long blockCacheTimeout = Args.getInstance().getBlockCacheTimeout(); private Cache requestBlockIds = CacheBuilder.newBuilder() @@ -69,6 +70,10 @@ public class SyncService { private final long syncFetchBatchNum = Args.getInstance().getSyncFetchBatchNum(); + private final int maxPendingBlockSize = Args.getInstance().getMaxPendingBlockSize(); + + private volatile long maxRequestedBlockNum = 0; + public void init() { ExecutorServiceManager.scheduleWithFixedDelay(fetchExecutor, () -> { try { @@ -135,7 +140,9 @@ public void syncNext(PeerConnection peer) { public void processBlock(PeerConnection peer, BlockMessage blockMessage) { synchronized (blockJustReceived) { - blockJustReceived.put(blockMessage, peer); + UnparsedBlock unparsedBlock = new UnparsedBlock( + blockMessage.getBlockId(), blockMessage.getData()); + blockJustReceived.put(unparsedBlock, peer); } handleFlag = true; if (peer.isSyncIdle()) { @@ -227,8 +234,18 @@ private BlockId getBlockIdByNum(long num) throws P2pException { } private void startFetchSyncBlock() { + Collection activePeers = tronNetDelegate.getActivePeer(); + int reqNum = activePeers.stream() + .mapToInt(p -> p.getSyncBlockRequested().size()).sum(); + int remainNum; + synchronized (blockJustReceived) { + remainNum = maxPendingBlockSize - reqNum + - blockJustReceived.size() - blockWaitToProcess.size(); + } + HashMap> send = new HashMap<>(); - tronNetDelegate.getActivePeer().stream() + int[] fetchingBlockSize = {0}; + activePeers.stream() .filter(peer -> peer.isNeedSyncFromPeer() && peer.isSyncIdle()) .filter(peer -> peer.isFetchAble()) .forEach(peer -> { @@ -238,9 +255,16 @@ private void startFetchSyncBlock() { for (BlockId blockId : peer.getSyncBlockToFetch()) { if (requestBlockIds.getIfPresent(blockId) == null && !peer.getSyncBlockInProcess().contains(blockId)) { + if (fetchingBlockSize[0] >= remainNum && blockId.getNum() > maxRequestedBlockNum) { + break; + } + if (blockId.getNum() > maxRequestedBlockNum) { + maxRequestedBlockNum = blockId.getNum(); + } requestBlockIds.put(blockId, peer); peer.getSyncBlockRequested().put(blockId, System.currentTimeMillis()); send.get(peer).add(blockId); + fetchingBlockSize[0]++; if (send.get(peer).size() >= MAX_BLOCK_FETCH_PER_PEER) { break; } @@ -269,29 +293,37 @@ private synchronized void handleSyncBlock() { isProcessed[0] = false; - blockWaitToProcess.forEach((msg, peerConnection) -> { + blockWaitToProcess.forEach((unparsedBlock, peerConnection) -> { synchronized (tronNetDelegate.getBlockLock()) { + BlockId blockId = unparsedBlock.getBlockId(); if (peerConnection.isDisconnect()) { - blockWaitToProcess.remove(msg); - invalid(msg.getBlockId(), peerConnection); + blockWaitToProcess.remove(unparsedBlock); + invalid(blockId, peerConnection); return; } - if (msg.getBlockId().getNum() <= solidNum) { - blockWaitToProcess.remove(msg); - peerConnection.getSyncBlockInProcess().remove(msg.getBlockId()); + if (blockId.getNum() <= solidNum) { + blockWaitToProcess.remove(unparsedBlock); + peerConnection.getSyncBlockInProcess().remove(blockId); return; } final boolean[] isFound = {false}; tronNetDelegate.getActivePeer().stream() - .filter(peer -> msg.getBlockId().equals(peer.getSyncBlockToFetch().peek())) + .filter(peer -> blockId.equals(peer.getSyncBlockToFetch().peek())) .forEach(peer -> { isFound[0] = true; }); if (isFound[0]) { - blockWaitToProcess.remove(msg); + blockWaitToProcess.remove(unparsedBlock); isProcessed[0] = true; - processSyncBlock(msg.getBlockCapsule(), peerConnection); - peerConnection.getSyncBlockInProcess().remove(msg.getBlockId()); + BlockCapsule block; + try { + block = new BlockCapsule(unparsedBlock.getData()); + } catch (Exception e) { + logger.warn("Deserialize block {} failed", blockId.getString(), e); + return; + } + processSyncBlock(block, peerConnection); + peerConnection.getSyncBlockInProcess().remove(blockId); } } }); diff --git a/framework/src/main/java/org/tron/core/net/service/sync/UnparsedBlock.java b/framework/src/main/java/org/tron/core/net/service/sync/UnparsedBlock.java new file mode 100644 index 00000000000..129c992ce7b --- /dev/null +++ b/framework/src/main/java/org/tron/core/net/service/sync/UnparsedBlock.java @@ -0,0 +1,46 @@ +package org.tron.core.net.service.sync; + +import org.tron.core.capsule.BlockCapsule; + +public class UnparsedBlock { + + private final BlockCapsule.BlockId blockId; + private final byte[] data; + + public UnparsedBlock(BlockCapsule.BlockId blockId, byte[] data) { + if (blockId == null) { + throw new IllegalArgumentException("blockId must not be null"); + } + this.blockId = blockId; + this.data = data; + } + + public BlockCapsule.BlockId getBlockId() { + return blockId; + } + + public byte[] getData() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof UnparsedBlock)) { + return false; + } + return blockId.equals(((UnparsedBlock) o).blockId); + } + + @Override + public int hashCode() { + return blockId.hashCode(); + } + + @Override + public String toString() { + return blockId.getString(); + } +} diff --git a/framework/src/main/java/org/tron/core/services/filter/HttpApiAccessFilter.java b/framework/src/main/java/org/tron/core/services/filter/HttpApiAccessFilter.java index f4994d9af08..e18e6541baa 100644 --- a/framework/src/main/java/org/tron/core/services/filter/HttpApiAccessFilter.java +++ b/framework/src/main/java/org/tron/core/services/filter/HttpApiAccessFilter.java @@ -1,6 +1,5 @@ package org.tron.core.services.filter; -import com.alibaba.fastjson.JSONObject; import java.net.URI; import java.util.List; import java.util.Locale; @@ -14,6 +13,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.tron.common.parameter.CommonParameter; +import org.tron.json.JSONObject; @Component @Slf4j(topic = "httpApiAccessFilter") diff --git a/framework/src/main/java/org/tron/core/services/http/AccountPermissionUpdateServlet.java b/framework/src/main/java/org/tron/core/services/http/AccountPermissionUpdateServlet.java index c8ddd93f103..6fe83eff8be 100644 --- a/framework/src/main/java/org/tron/core/services/http/AccountPermissionUpdateServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/AccountPermissionUpdateServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.AccountContract.AccountPermissionUpdateContract; diff --git a/framework/src/main/java/org/tron/core/services/http/BroadcastHexServlet.java b/framework/src/main/java/org/tron/core/services/http/BroadcastHexServlet.java index ec115b20a15..5d59df59678 100644 --- a/framework/src/main/java/org/tron/core/services/http/BroadcastHexServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/BroadcastHexServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -11,6 +10,7 @@ import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; import org.tron.core.capsule.TransactionCapsule; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; @Component diff --git a/framework/src/main/java/org/tron/core/services/http/BroadcastServlet.java b/framework/src/main/java/org/tron/core/services/http/BroadcastServlet.java index 6b265c35e02..370a81eef4e 100644 --- a/framework/src/main/java/org/tron/core/services/http/BroadcastServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/BroadcastServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -10,6 +9,7 @@ import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; import org.tron.core.capsule.TransactionCapsule; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; diff --git a/framework/src/main/java/org/tron/core/services/http/CancelAllUnfreezeV2Servlet.java b/framework/src/main/java/org/tron/core/services/http/CancelAllUnfreezeV2Servlet.java index 894126e50da..a2d4571be27 100644 --- a/framework/src/main/java/org/tron/core/services/http/CancelAllUnfreezeV2Servlet.java +++ b/framework/src/main/java/org/tron/core/services/http/CancelAllUnfreezeV2Servlet.java @@ -1,13 +1,13 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSON; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.CancelAllUnfreezeV2Contract; diff --git a/framework/src/main/java/org/tron/core/services/http/ClearABIServlet.java b/framework/src/main/java/org/tron/core/services/http/ClearABIServlet.java index c897b895c58..e0833052ce8 100644 --- a/framework/src/main/java/org/tron/core/services/http/ClearABIServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ClearABIServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.ClearABIContract; diff --git a/framework/src/main/java/org/tron/core/services/http/CreateAccountServlet.java b/framework/src/main/java/org/tron/core/services/http/CreateAccountServlet.java index 102d9ca80ce..b547fd00364 100644 --- a/framework/src/main/java/org/tron/core/services/http/CreateAccountServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/CreateAccountServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.AccountContract.AccountCreateContract; diff --git a/framework/src/main/java/org/tron/core/services/http/CreateAssetIssueServlet.java b/framework/src/main/java/org/tron/core/services/http/CreateAssetIssueServlet.java index 9d537ab641b..bc1a33509e0 100644 --- a/framework/src/main/java/org/tron/core/services/http/CreateAssetIssueServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/CreateAssetIssueServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract; diff --git a/framework/src/main/java/org/tron/core/services/http/CreateCommonTransactionServlet.java b/framework/src/main/java/org/tron/core/services/http/CreateCommonTransactionServlet.java index c3a515f84a5..f4b5e03db82 100644 --- a/framework/src/main/java/org/tron/core/services/http/CreateCommonTransactionServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/CreateCommonTransactionServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.GeneratedMessageV3; import com.google.protobuf.Message; import java.lang.reflect.Constructor; @@ -13,6 +12,7 @@ import org.tron.core.Wallet; import org.tron.core.actuator.TransactionFactory; import org.tron.core.exception.ContractValidateException; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; diff --git a/framework/src/main/java/org/tron/core/services/http/CreateShieldedTransactionWithoutSpendAuthSigServlet.java b/framework/src/main/java/org/tron/core/services/http/CreateShieldedTransactionWithoutSpendAuthSigServlet.java index b77fff77034..eb870bd1721 100644 --- a/framework/src/main/java/org/tron/core/services/http/CreateShieldedTransactionWithoutSpendAuthSigServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/CreateShieldedTransactionWithoutSpendAuthSigServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -9,6 +7,8 @@ import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI.PrivateParametersWithoutAsk; import org.tron.core.Wallet; +import org.tron.json.JSON; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; diff --git a/framework/src/main/java/org/tron/core/services/http/CreateWitnessServlet.java b/framework/src/main/java/org/tron/core/services/http/CreateWitnessServlet.java index 55e1b25ce3d..3258dbbe6b9 100644 --- a/framework/src/main/java/org/tron/core/services/http/CreateWitnessServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/CreateWitnessServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.WitnessContract.WitnessCreateContract; diff --git a/framework/src/main/java/org/tron/core/services/http/DelegateResourceServlet.java b/framework/src/main/java/org/tron/core/services/http/DelegateResourceServlet.java index 00994238988..25641ff093c 100644 --- a/framework/src/main/java/org/tron/core/services/http/DelegateResourceServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/DelegateResourceServlet.java @@ -1,13 +1,13 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSON; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.DelegateResourceContract; diff --git a/framework/src/main/java/org/tron/core/services/http/DeployContractServlet.java b/framework/src/main/java/org/tron/core/services/http/DeployContractServlet.java index 1209c6fb385..45a5be961e9 100644 --- a/framework/src/main/java/org/tron/core/services/http/DeployContractServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/DeployContractServlet.java @@ -3,7 +3,6 @@ import static org.tron.core.services.http.Util.getHexAddress; import static org.tron.core.services.http.Util.setTransactionPermissionId; -import com.alibaba.fastjson.JSONObject; import com.google.common.base.Strings; import com.google.protobuf.ByteString; import javax.servlet.http.HttpServletRequest; @@ -14,6 +13,7 @@ import org.springframework.stereotype.Component; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract; @@ -91,4 +91,4 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) Util.processError(e, response); } } -} \ No newline at end of file +} diff --git a/framework/src/main/java/org/tron/core/services/http/EstimateEnergyServlet.java b/framework/src/main/java/org/tron/core/services/http/EstimateEnergyServlet.java index d88f7dd1af1..91d673a2d08 100644 --- a/framework/src/main/java/org/tron/core/services/http/EstimateEnergyServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/EstimateEnergyServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -17,6 +16,7 @@ import org.tron.core.Wallet; import org.tron.core.capsule.TransactionCapsule; import org.tron.core.exception.ContractValidateException; +import org.tron.json.JSONObject; import org.tron.protos.Protocol; import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract; diff --git a/framework/src/main/java/org/tron/core/services/http/ExchangeCreateServlet.java b/framework/src/main/java/org/tron/core/services/http/ExchangeCreateServlet.java index b7e2d7beb50..84707c5586f 100644 --- a/framework/src/main/java/org/tron/core/services/http/ExchangeCreateServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ExchangeCreateServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.ExchangeContract.ExchangeCreateContract; diff --git a/framework/src/main/java/org/tron/core/services/http/ExchangeInjectServlet.java b/framework/src/main/java/org/tron/core/services/http/ExchangeInjectServlet.java index a6c8ebc2132..b4b2b31221e 100644 --- a/framework/src/main/java/org/tron/core/services/http/ExchangeInjectServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ExchangeInjectServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.ExchangeContract.ExchangeInjectContract; diff --git a/framework/src/main/java/org/tron/core/services/http/ExchangeTransactionServlet.java b/framework/src/main/java/org/tron/core/services/http/ExchangeTransactionServlet.java index b788e6bba9f..a143cd45c70 100644 --- a/framework/src/main/java/org/tron/core/services/http/ExchangeTransactionServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ExchangeTransactionServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.ExchangeContract.ExchangeTransactionContract; diff --git a/framework/src/main/java/org/tron/core/services/http/ExchangeWithdrawServlet.java b/framework/src/main/java/org/tron/core/services/http/ExchangeWithdrawServlet.java index f454e08df9c..159f23abb22 100644 --- a/framework/src/main/java/org/tron/core/services/http/ExchangeWithdrawServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ExchangeWithdrawServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.ExchangeContract.ExchangeWithdrawContract; diff --git a/framework/src/main/java/org/tron/core/services/http/FreezeBalanceServlet.java b/framework/src/main/java/org/tron/core/services/http/FreezeBalanceServlet.java index e73c294e023..2990755b928 100644 --- a/framework/src/main/java/org/tron/core/services/http/FreezeBalanceServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/FreezeBalanceServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.FreezeBalanceContract; diff --git a/framework/src/main/java/org/tron/core/services/http/FreezeBalanceV2Servlet.java b/framework/src/main/java/org/tron/core/services/http/FreezeBalanceV2Servlet.java index f1687a5bbb1..95d20233898 100644 --- a/framework/src/main/java/org/tron/core/services/http/FreezeBalanceV2Servlet.java +++ b/framework/src/main/java/org/tron/core/services/http/FreezeBalanceV2Servlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.FreezeBalanceV2Contract; diff --git a/framework/src/main/java/org/tron/core/services/http/GetAccountBalanceServlet.java b/framework/src/main/java/org/tron/core/services/http/GetAccountBalanceServlet.java index 159c3899666..db48fc11340 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetAccountBalanceServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetAccountBalanceServlet.java @@ -1,13 +1,11 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; -import org.tron.protos.Protocol.Account; import org.tron.protos.contract.BalanceContract; diff --git a/framework/src/main/java/org/tron/core/services/http/GetAccountByIdServlet.java b/framework/src/main/java/org/tron/core/services/http/GetAccountByIdServlet.java index 7387b801168..96243327c70 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetAccountByIdServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetAccountByIdServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Account; @@ -48,4 +48,4 @@ private void fillResponse(Account account, boolean visible, HttpServletResponse Account reply = wallet.getAccountById(account); Util.printAccount(reply, response, visible); } -} \ No newline at end of file +} diff --git a/framework/src/main/java/org/tron/core/services/http/GetAccountResourceServlet.java b/framework/src/main/java/org/tron/core/services/http/GetAccountResourceServlet.java index 98224334e1a..0e26a526a12 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetAccountResourceServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetAccountResourceServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -10,6 +9,7 @@ import org.tron.api.GrpcAPI.AccountResourceMessage; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONObject; @Component @Slf4j(topic = "API") diff --git a/framework/src/main/java/org/tron/core/services/http/GetAccountServlet.java b/framework/src/main/java/org/tron/core/services/http/GetAccountServlet.java index 7de95dab541..ee5bbdf3d3a 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetAccountServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetAccountServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Account; diff --git a/framework/src/main/java/org/tron/core/services/http/GetAssetIssueByIdServlet.java b/framework/src/main/java/org/tron/core/services/http/GetAssetIssueByIdServlet.java index 08f8227deee..b705c4ed0cd 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetAssetIssueByIdServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetAssetIssueByIdServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract; diff --git a/framework/src/main/java/org/tron/core/services/http/GetAssetIssueByNameServlet.java b/framework/src/main/java/org/tron/core/services/http/GetAssetIssueByNameServlet.java index a1cc0525514..da6a7243f41 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetAssetIssueByNameServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetAssetIssueByNameServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -10,6 +8,8 @@ import org.springframework.stereotype.Component; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSON; +import org.tron.json.JSONObject; import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract; diff --git a/framework/src/main/java/org/tron/core/services/http/GetAssetIssueListByNameServlet.java b/framework/src/main/java/org/tron/core/services/http/GetAssetIssueListByNameServlet.java index d9b7426011d..2c203ed983e 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetAssetIssueListByNameServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetAssetIssueListByNameServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import java.io.IOException; import javax.servlet.http.HttpServletRequest; @@ -12,6 +10,8 @@ import org.tron.api.GrpcAPI.AssetIssueList; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSON; +import org.tron.json.JSONObject; @Component diff --git a/framework/src/main/java/org/tron/core/services/http/GetBlockBalanceServlet.java b/framework/src/main/java/org/tron/core/services/http/GetBlockBalanceServlet.java index 0fc3256899c..5217b37907a 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetBlockBalanceServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetBlockBalanceServlet.java @@ -1,13 +1,11 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; -import org.tron.protos.Protocol.Account; import org.tron.protos.contract.BalanceContract.BlockBalanceTrace; diff --git a/framework/src/main/java/org/tron/core/services/http/GetBlockServlet.java b/framework/src/main/java/org/tron/core/services/http/GetBlockServlet.java index 0e0104e8014..2320fc87c7d 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetBlockServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetBlockServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.google.common.base.Strings; import java.io.IOException; import javax.servlet.http.HttpServletRequest; @@ -12,6 +10,8 @@ import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI.BlockReq; import org.tron.core.Wallet; +import org.tron.json.JSON; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Block; diff --git a/framework/src/main/java/org/tron/core/services/http/GetContractInfoServlet.java b/framework/src/main/java/org/tron/core/services/http/GetContractInfoServlet.java index 86ed7f6d9b6..6a1549bd398 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetContractInfoServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetContractInfoServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -9,7 +7,7 @@ import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI.BytesMessage; import org.tron.core.Wallet; -import org.tron.protos.contract.SmartContractOuterClass.SmartContract; +import org.tron.json.JSONObject; import org.tron.protos.contract.SmartContractOuterClass.SmartContractDataWrapper; diff --git a/framework/src/main/java/org/tron/core/services/http/GetContractServlet.java b/framework/src/main/java/org/tron/core/services/http/GetContractServlet.java index b9efd6c1520..3565d3121f6 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetContractServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetContractServlet.java @@ -2,7 +2,6 @@ import static org.tron.core.services.http.PostParams.S_VALUE; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -10,6 +9,7 @@ import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI.BytesMessage; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.contract.SmartContractOuterClass.SmartContract; diff --git a/framework/src/main/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexServlet.java b/framework/src/main/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexServlet.java index 035e20cb873..e022c523548 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import java.io.IOException; import javax.servlet.http.HttpServletRequest; @@ -11,6 +10,7 @@ import org.tron.api.GrpcAPI.BytesMessage; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.DelegatedResourceAccountIndex; diff --git a/framework/src/main/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexV2Servlet.java b/framework/src/main/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexV2Servlet.java index 3d5bff80941..7ff517256f9 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexV2Servlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexV2Servlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import java.io.IOException; import javax.servlet.http.HttpServletRequest; @@ -11,6 +10,7 @@ import org.tron.api.GrpcAPI.BytesMessage; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.DelegatedResourceAccountIndex; diff --git a/framework/src/main/java/org/tron/core/services/http/GetExchangeByIdServlet.java b/framework/src/main/java/org/tron/core/services/http/GetExchangeByIdServlet.java index 7a84c0ea8a4..1e87a4f188f 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetExchangeByIdServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetExchangeByIdServlet.java @@ -1,9 +1,7 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import java.io.IOException; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -11,6 +9,7 @@ import org.springframework.stereotype.Component; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONObject; @Component diff --git a/framework/src/main/java/org/tron/core/services/http/GetIncomingViewingKeyServlet.java b/framework/src/main/java/org/tron/core/services/http/GetIncomingViewingKeyServlet.java index b572cf348e5..4eb3a01693e 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetIncomingViewingKeyServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetIncomingViewingKeyServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -9,6 +8,7 @@ import org.tron.api.GrpcAPI; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONObject; @Component diff --git a/framework/src/main/java/org/tron/core/services/http/GetMarketOrderByAccountServlet.java b/framework/src/main/java/org/tron/core/services/http/GetMarketOrderByAccountServlet.java index 1c3190b62ea..1f011bb88ce 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetMarketOrderByAccountServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetMarketOrderByAccountServlet.java @@ -1,10 +1,7 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import java.io.IOException; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -12,6 +9,8 @@ import org.springframework.stereotype.Component; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSON; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.MarketOrderList; diff --git a/framework/src/main/java/org/tron/core/services/http/GetNodeInfoServlet.java b/framework/src/main/java/org/tron/core/services/http/GetNodeInfoServlet.java index 8516d1c51bb..0b8f7b9ce2b 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetNodeInfoServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetNodeInfoServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -9,6 +8,7 @@ import org.springframework.stereotype.Component; import org.tron.common.entity.NodeInfo; import org.tron.core.services.NodeInfoService; +import org.tron.json.JSON; @Component diff --git a/framework/src/main/java/org/tron/core/services/http/GetProposalByIdServlet.java b/framework/src/main/java/org/tron/core/services/http/GetProposalByIdServlet.java index 9d7805d4f98..f57bc8b1b3f 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetProposalByIdServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetProposalByIdServlet.java @@ -1,10 +1,7 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import java.io.IOException; -import java.util.HashMap; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -12,6 +9,7 @@ import org.springframework.stereotype.Component; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Proposal; diff --git a/framework/src/main/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServlet.java b/framework/src/main/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServlet.java index 587dd2d6613..5d0a09b1a68 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import java.io.IOException; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -12,6 +10,8 @@ import org.tron.api.GrpcAPI.NumberMessage; import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.core.Wallet; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.TransactionInfo; import org.tron.protos.Protocol.TransactionInfo.Log; diff --git a/framework/src/main/java/org/tron/core/services/http/GetZenPaymentAddressServlet.java b/framework/src/main/java/org/tron/core/services/http/GetZenPaymentAddressServlet.java index c78b663eec2..c4f81ea7f87 100644 --- a/framework/src/main/java/org/tron/core/services/http/GetZenPaymentAddressServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/GetZenPaymentAddressServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -12,6 +11,7 @@ import org.tron.core.Wallet; import org.tron.core.zen.address.DiversifierT; import org.tron.core.zen.address.IncomingViewingKey; +import org.tron.json.JSONObject; @Component diff --git a/framework/src/main/java/org/tron/core/services/http/JsonFormat.java b/framework/src/main/java/org/tron/core/services/http/JsonFormat.java index 8a8c66fb371..1dab6c7b941 100644 --- a/framework/src/main/java/org/tron/core/services/http/JsonFormat.java +++ b/framework/src/main/java/org/tron/core/services/http/JsonFormat.java @@ -29,7 +29,6 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import com.alibaba.fastjson.JSON; import com.google.common.collect.ImmutableSet; import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors; @@ -58,6 +57,7 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT import org.tron.common.utils.ByteArray; import org.tron.common.utils.Commons; import org.tron.common.utils.StringUtil; +import org.tron.json.JSON; import org.tron.protos.contract.BalanceContract; /** diff --git a/framework/src/main/java/org/tron/core/services/http/MarketCancelOrderServlet.java b/framework/src/main/java/org/tron/core/services/http/MarketCancelOrderServlet.java index 8d19d60ce5c..a9e27bfb8e3 100644 --- a/framework/src/main/java/org/tron/core/services/http/MarketCancelOrderServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/MarketCancelOrderServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.MarketContract.MarketCancelOrderContract; diff --git a/framework/src/main/java/org/tron/core/services/http/MarketSellAssetServlet.java b/framework/src/main/java/org/tron/core/services/http/MarketSellAssetServlet.java index 258dd270811..12bb7e3e078 100644 --- a/framework/src/main/java/org/tron/core/services/http/MarketSellAssetServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/MarketSellAssetServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.MarketContract.MarketSellAssetContract; diff --git a/framework/src/main/java/org/tron/core/services/http/MetricsServlet.java b/framework/src/main/java/org/tron/core/services/http/MetricsServlet.java index bb4bf18e58e..aaaebb22146 100644 --- a/framework/src/main/java/org/tron/core/services/http/MetricsServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/MetricsServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -8,6 +7,7 @@ import org.springframework.stereotype.Component; import org.tron.core.metrics.MetricsApiService; import org.tron.core.metrics.MetricsInfo; +import org.tron.json.JSON; @Component @Slf4j(topic = "API") diff --git a/framework/src/main/java/org/tron/core/services/http/ParticipateAssetIssueServlet.java b/framework/src/main/java/org/tron/core/services/http/ParticipateAssetIssueServlet.java index 1e864250bff..ec5e3d956f6 100644 --- a/framework/src/main/java/org/tron/core/services/http/ParticipateAssetIssueServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ParticipateAssetIssueServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.AssetIssueContractOuterClass.ParticipateAssetIssueContract; diff --git a/framework/src/main/java/org/tron/core/services/http/ProposalApproveServlet.java b/framework/src/main/java/org/tron/core/services/http/ProposalApproveServlet.java index 3fc60016780..dfeeb1acde5 100644 --- a/framework/src/main/java/org/tron/core/services/http/ProposalApproveServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ProposalApproveServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.ProposalContract.ProposalApproveContract; diff --git a/framework/src/main/java/org/tron/core/services/http/ProposalCreateServlet.java b/framework/src/main/java/org/tron/core/services/http/ProposalCreateServlet.java index 9660ee7f863..f1055e00396 100644 --- a/framework/src/main/java/org/tron/core/services/http/ProposalCreateServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ProposalCreateServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.ProposalContract.ProposalCreateContract; diff --git a/framework/src/main/java/org/tron/core/services/http/ProposalDeleteServlet.java b/framework/src/main/java/org/tron/core/services/http/ProposalDeleteServlet.java index 09c4c78a37a..8e7163f490b 100644 --- a/framework/src/main/java/org/tron/core/services/http/ProposalDeleteServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ProposalDeleteServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.ProposalContract.ProposalDeleteContract; diff --git a/framework/src/main/java/org/tron/core/services/http/RateLimiterServlet.java b/framework/src/main/java/org/tron/core/services/http/RateLimiterServlet.java index f173fbcaa82..3086cbb3619 100644 --- a/framework/src/main/java/org/tron/core/services/http/RateLimiterServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/RateLimiterServlet.java @@ -102,17 +102,15 @@ private static TronError rateLimiterInitError(String strategy, String params, St @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - - RuntimeData runtimeData = new RuntimeData(req); - GlobalRateLimiter.acquire(runtimeData); + RuntimeData runtimeData = new RuntimeData(req); IRateLimiter rateLimiter = container.get(KEY_PREFIX_HTTP, getClass().getSimpleName()); - boolean acquireResource = true; + // Check per-endpoint first to avoid consuming global IP/QPS quota for requests + // that would be rejected by the per-endpoint limiter anyway. + boolean perEndpointAcquired = rateLimiter == null || rateLimiter.tryAcquire(runtimeData); + boolean acquireResource = perEndpointAcquired && GlobalRateLimiter.tryAcquire(runtimeData); - if (rateLimiter != null) { - acquireResource = rateLimiter.acquire(runtimeData); - } String contextPath = req.getContextPath(); String url = Strings.isNullOrEmpty(req.getServletPath()) ? MetricLabels.UNDEFINED : contextPath + req.getServletPath(); @@ -143,7 +141,9 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) // will leak int64_as_string state across requests on reused Tomcat threads, // producing intermittent quoted/unquoted output that is very hard to debug. JsonFormat.clearInt64AsString(); - if (rateLimiter instanceof IPreemptibleRateLimiter && acquireResource) { + // Release whenever the per-endpoint permit was acquired (covers both the normal + // completion path and the case where GlobalRateLimiter rejected the request). + if (rateLimiter instanceof IPreemptibleRateLimiter && perEndpointAcquired) { ((IPreemptibleRateLimiter) rateLimiter).release(); } } diff --git a/framework/src/main/java/org/tron/core/services/http/ScanAndMarkNoteByIvkServlet.java b/framework/src/main/java/org/tron/core/services/http/ScanAndMarkNoteByIvkServlet.java index 692a1c2ecdc..b91fd394442 100644 --- a/framework/src/main/java/org/tron/core/services/http/ScanAndMarkNoteByIvkServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ScanAndMarkNoteByIvkServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -11,6 +9,8 @@ import org.tron.api.GrpcAPI.IvkDecryptAndMarkParameters; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; @Component @Slf4j(topic = "API") diff --git a/framework/src/main/java/org/tron/core/services/http/ScanNoteByIvkServlet.java b/framework/src/main/java/org/tron/core/services/http/ScanNoteByIvkServlet.java index c0076a705f8..c8e25a2fc37 100644 --- a/framework/src/main/java/org/tron/core/services/http/ScanNoteByIvkServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ScanNoteByIvkServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -12,6 +10,8 @@ import org.tron.api.GrpcAPI.IvkDecryptParameters; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; @Component @Slf4j(topic = "API") diff --git a/framework/src/main/java/org/tron/core/services/http/ScanShieldedTRC20NotesByIvkServlet.java b/framework/src/main/java/org/tron/core/services/http/ScanShieldedTRC20NotesByIvkServlet.java index 49e3246baaa..d9da2453c9b 100644 --- a/framework/src/main/java/org/tron/core/services/http/ScanShieldedTRC20NotesByIvkServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ScanShieldedTRC20NotesByIvkServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -11,6 +9,8 @@ import org.tron.api.GrpcAPI.IvkDecryptTRC20Parameters; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; @Component @Slf4j(topic = "API") diff --git a/framework/src/main/java/org/tron/core/services/http/SetAccountIdServlet.java b/framework/src/main/java/org/tron/core/services/http/SetAccountIdServlet.java index 36203749e0b..2f5b9fd9cd4 100644 --- a/framework/src/main/java/org/tron/core/services/http/SetAccountIdServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/SetAccountIdServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol; import org.tron.protos.contract.AccountContract.SetAccountIdContract; diff --git a/framework/src/main/java/org/tron/core/services/http/TransferAssetServlet.java b/framework/src/main/java/org/tron/core/services/http/TransferAssetServlet.java index af2699c4bf4..c8becdb9fde 100644 --- a/framework/src/main/java/org/tron/core/services/http/TransferAssetServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/TransferAssetServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.AssetIssueContractOuterClass.TransferAssetContract; diff --git a/framework/src/main/java/org/tron/core/services/http/TransferServlet.java b/framework/src/main/java/org/tron/core/services/http/TransferServlet.java index 6f575e4fe3f..11b2179800a 100644 --- a/framework/src/main/java/org/tron/core/services/http/TransferServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/TransferServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.TransferContract; diff --git a/framework/src/main/java/org/tron/core/services/http/TriggerConstantContractServlet.java b/framework/src/main/java/org/tron/core/services/http/TriggerConstantContractServlet.java index 8a46ee1ed74..634165911d1 100644 --- a/framework/src/main/java/org/tron/core/services/http/TriggerConstantContractServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/TriggerConstantContractServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -17,6 +16,7 @@ import org.tron.core.Wallet; import org.tron.core.capsule.TransactionCapsule; import org.tron.core.exception.ContractValidateException; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract; diff --git a/framework/src/main/java/org/tron/core/services/http/TriggerSmartContractServlet.java b/framework/src/main/java/org/tron/core/services/http/TriggerSmartContractServlet.java index 6577a9e5f24..bc4d9dc5f66 100644 --- a/framework/src/main/java/org/tron/core/services/http/TriggerSmartContractServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/TriggerSmartContractServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -18,6 +17,7 @@ import org.tron.core.Wallet; import org.tron.core.capsule.TransactionCapsule; import org.tron.core.exception.ContractValidateException; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UnDelegateResourceServlet.java b/framework/src/main/java/org/tron/core/services/http/UnDelegateResourceServlet.java index cf9c2c95dcd..140129d4e34 100644 --- a/framework/src/main/java/org/tron/core/services/http/UnDelegateResourceServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UnDelegateResourceServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.UnDelegateResourceContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UnFreezeAssetServlet.java b/framework/src/main/java/org/tron/core/services/http/UnFreezeAssetServlet.java index 6118b39f2cd..a2218547b4a 100644 --- a/framework/src/main/java/org/tron/core/services/http/UnFreezeAssetServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UnFreezeAssetServlet.java @@ -1,13 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.AssetIssueContractOuterClass.UnfreezeAssetContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UnFreezeBalanceServlet.java b/framework/src/main/java/org/tron/core/services/http/UnFreezeBalanceServlet.java index eb075b7139d..1f893003c20 100644 --- a/framework/src/main/java/org/tron/core/services/http/UnFreezeBalanceServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UnFreezeBalanceServlet.java @@ -1,13 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.UnfreezeBalanceContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UnFreezeBalanceV2Servlet.java b/framework/src/main/java/org/tron/core/services/http/UnFreezeBalanceV2Servlet.java index 614a7ca6b7a..05644c0b941 100644 --- a/framework/src/main/java/org/tron/core/services/http/UnFreezeBalanceV2Servlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UnFreezeBalanceV2Servlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.UnfreezeBalanceV2Contract; diff --git a/framework/src/main/java/org/tron/core/services/http/UpdateAccountServlet.java b/framework/src/main/java/org/tron/core/services/http/UpdateAccountServlet.java index 0251a46ec64..532d7acf658 100644 --- a/framework/src/main/java/org/tron/core/services/http/UpdateAccountServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UpdateAccountServlet.java @@ -1,13 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.AccountContract.AccountUpdateContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UpdateAssetServlet.java b/framework/src/main/java/org/tron/core/services/http/UpdateAssetServlet.java index d3f467ff7a8..e7e8179b1a4 100644 --- a/framework/src/main/java/org/tron/core/services/http/UpdateAssetServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UpdateAssetServlet.java @@ -1,13 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.AssetIssueContractOuterClass.UpdateAssetContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UpdateBrokerageServlet.java b/framework/src/main/java/org/tron/core/services/http/UpdateBrokerageServlet.java index 23daaef6072..e1073354ddc 100644 --- a/framework/src/main/java/org/tron/core/services/http/UpdateBrokerageServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UpdateBrokerageServlet.java @@ -1,13 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.StorageContract.UpdateBrokerageContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UpdateEnergyLimitServlet.java b/framework/src/main/java/org/tron/core/services/http/UpdateEnergyLimitServlet.java index aac6e8ff50e..cd349c7e153 100644 --- a/framework/src/main/java/org/tron/core/services/http/UpdateEnergyLimitServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UpdateEnergyLimitServlet.java @@ -1,13 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.UpdateEnergyLimitContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UpdateSettingServlet.java b/framework/src/main/java/org/tron/core/services/http/UpdateSettingServlet.java index 821054c7b4e..e58f02df157 100644 --- a/framework/src/main/java/org/tron/core/services/http/UpdateSettingServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UpdateSettingServlet.java @@ -1,12 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.SmartContractOuterClass.UpdateSettingContract; diff --git a/framework/src/main/java/org/tron/core/services/http/UpdateWitnessServlet.java b/framework/src/main/java/org/tron/core/services/http/UpdateWitnessServlet.java index 3584557d3b6..6b0c4449e88 100644 --- a/framework/src/main/java/org/tron/core/services/http/UpdateWitnessServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/UpdateWitnessServlet.java @@ -1,13 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.WitnessContract.WitnessUpdateContract; diff --git a/framework/src/main/java/org/tron/core/services/http/Util.java b/framework/src/main/java/org/tron/core/services/http/Util.java index e8972c5aa73..c4556e42c76 100644 --- a/framework/src/main/java/org/tron/core/services/http/Util.java +++ b/framework/src/main/java/org/tron/core/services/http/Util.java @@ -3,17 +3,12 @@ import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.tron.common.utils.Commons.decodeFromBase58Check; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONException; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.GeneratedMessageV3; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; import com.google.protobuf.ProtocolStringList; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -54,6 +49,10 @@ import org.tron.core.config.args.Args; import org.tron.core.db.TransactionTrace; import org.tron.core.services.http.JsonFormat.ParseException; +import org.tron.json.JSON; +import org.tron.json.JSONArray; +import org.tron.json.JSONException; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Account; import org.tron.protos.Protocol.Block; import org.tron.protos.Protocol.Transaction; diff --git a/framework/src/main/java/org/tron/core/services/http/ValidateAddressServlet.java b/framework/src/main/java/org/tron/core/services/http/ValidateAddressServlet.java index a65e2ce2ee0..07eecfc5466 100644 --- a/framework/src/main/java/org/tron/core/services/http/ValidateAddressServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/ValidateAddressServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import java.io.IOException; import java.util.Base64; import java.util.stream.Collectors; @@ -12,6 +10,8 @@ import org.tron.common.utils.ByteArray; import org.tron.common.utils.Commons; import org.tron.common.utils.DecodeUtil; +import org.tron.json.JSON; +import org.tron.json.JSONObject; @Component diff --git a/framework/src/main/java/org/tron/core/services/http/VoteWitnessAccountServlet.java b/framework/src/main/java/org/tron/core/services/http/VoteWitnessAccountServlet.java index 0a77a0e6fc1..f3695b83de8 100644 --- a/framework/src/main/java/org/tron/core/services/http/VoteWitnessAccountServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/VoteWitnessAccountServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.WitnessContract.VoteWitnessContract; diff --git a/framework/src/main/java/org/tron/core/services/http/WithdrawBalanceServlet.java b/framework/src/main/java/org/tron/core/services/http/WithdrawBalanceServlet.java index 27ea54d8ee2..33faa01866c 100644 --- a/framework/src/main/java/org/tron/core/services/http/WithdrawBalanceServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/WithdrawBalanceServlet.java @@ -1,6 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -8,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.WithdrawBalanceContract; diff --git a/framework/src/main/java/org/tron/core/services/http/WithdrawExpireUnfreezeServlet.java b/framework/src/main/java/org/tron/core/services/http/WithdrawExpireUnfreezeServlet.java index 4888ee42de4..7e5f3f96c57 100644 --- a/framework/src/main/java/org/tron/core/services/http/WithdrawExpireUnfreezeServlet.java +++ b/framework/src/main/java/org/tron/core/services/http/WithdrawExpireUnfreezeServlet.java @@ -1,7 +1,5 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -9,6 +7,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.core.Wallet; +import org.tron.json.JSON; +import org.tron.json.JSONObject; import org.tron.protos.Protocol.Transaction; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.WithdrawExpireUnfreezeContract; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java index 8e7c8615da4..f5707148724 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java @@ -1,6 +1,5 @@ package org.tron.core.services.jsonrpc; -import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.googlecode.jsonrpc4j.JsonRpcError; import com.googlecode.jsonrpc4j.JsonRpcErrors; @@ -30,6 +29,7 @@ import org.tron.core.services.jsonrpc.types.CallArguments; import org.tron.core.services.jsonrpc.types.TransactionReceipt; import org.tron.core.services.jsonrpc.types.TransactionResult; +import org.tron.json.JSONObject; /** * Error code refers to https://www.quicknode.com/docs/ethereum/error-references diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 663b39de290..40fafac535b 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -14,7 +14,6 @@ import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseBlockNumber; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.triggerCallContract; -import com.alibaba.fastjson.JSON; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.protobuf.ByteString; @@ -89,6 +88,7 @@ import org.tron.core.services.jsonrpc.types.TransactionResult; import org.tron.core.store.StorageRowStore; import org.tron.core.vm.program.Storage; +import org.tron.json.JSON; import org.tron.program.Version; import org.tron.protos.Protocol.Account; import org.tron.protos.Protocol.Block; diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java b/framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java index a3b1638ac95..4b3043274d2 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/GlobalRateLimiter.java @@ -5,8 +5,10 @@ import com.google.common.cache.CacheBuilder; import com.google.common.util.concurrent.RateLimiter; import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; import org.tron.core.config.args.Args; +@Slf4j public class GlobalRateLimiter { private static double QPS = Args.getInstance().getRateLimiterGlobalQps(); @@ -18,18 +20,24 @@ public class GlobalRateLimiter { private static RateLimiter rateLimiter = RateLimiter.create(QPS); - public static void acquire(RuntimeData runtimeData) { - rateLimiter.acquire(); + public static boolean tryAcquire(RuntimeData runtimeData) { String ip = runtimeData.getRemoteAddr(); - if (Strings.isNullOrEmpty(ip)) { - return; + if (!Strings.isNullOrEmpty(ip)) { + RateLimiter r; + try { + // cache.get is atomic: only one loader executes per key under concurrent requests, + // preventing multiple RateLimiter instances from being created for the same IP. + r = cache.get(ip, () -> RateLimiter.create(IP_QPS)); + } catch (Exception e) { + logger.warn("Failed to load IP rate limiter for {}, denying request: {}", + ip, e.getMessage()); + return false; + } + if (!r.tryAcquire()) { + return false; + } } - RateLimiter r = cache.getIfPresent(ip); - if (r == null) { - r = RateLimiter.create(IP_QPS); - cache.put(ip, r); - } - r.acquire(); + return rateLimiter.tryAcquire(); } } diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/RateLimiterInterceptor.java b/framework/src/main/java/org/tron/core/services/ratelimiter/RateLimiterInterceptor.java index 772e0b81433..a07cf955828 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/RateLimiterInterceptor.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/RateLimiterInterceptor.java @@ -104,49 +104,58 @@ public Listener interceptCall(ServerCall call, IRateLimiter rateLimiter = container .get(KEY_PREFIX_RPC, call.getMethodDescriptor().getFullMethodName()); - RuntimeData runtimeData = new RuntimeData(call); - GlobalRateLimiter.acquire(runtimeData); - - boolean acquireResource = true; + Listener listener = new ServerCall.Listener() {}; - if (rateLimiter != null) { - acquireResource = rateLimiter.acquire(runtimeData); + RuntimeData runtimeData = new RuntimeData(call); + // Check per-endpoint first to avoid consuming global IP/QPS quota for requests + // that would be rejected by the per-endpoint limiter anyway. + boolean perEndpointAcquired = rateLimiter == null || rateLimiter.tryAcquire(runtimeData); + boolean acquireResource = perEndpointAcquired && GlobalRateLimiter.tryAcquire(runtimeData); + + if (!acquireResource) { + // Release the per-endpoint permit when global rejected, to avoid semaphore leak. + if (rateLimiter instanceof IPreemptibleRateLimiter && perEndpointAcquired) { + ((IPreemptibleRateLimiter) rateLimiter).release(); + } + call.close(Status.fromCode(Code.RESOURCE_EXHAUSTED), new Metadata()); + return listener; } - Listener listener = new ServerCall.Listener() { - }; - try { - if (acquireResource) { - call.setMessageCompression(true); - ServerCall.Listener delegate = next.startCall(call, headers); - - listener = new SimpleForwardingServerCallListener(delegate) { - @Override - public void onComplete() { - // must release the permit to avoid the leak of permit. - if (rateLimiter instanceof IPreemptibleRateLimiter) { - ((IPreemptibleRateLimiter) rateLimiter).release(); - } + call.setMessageCompression(true); + ServerCall.Listener delegate = next.startCall(call, headers); + + listener = new SimpleForwardingServerCallListener(delegate) { + @Override + public void onComplete() { + // must release the permit to avoid the leak of permit. + if (rateLimiter instanceof IPreemptibleRateLimiter) { + ((IPreemptibleRateLimiter) rateLimiter).release(); } + } - @Override - public void onCancel() { - // must release the permit to avoid the leak of permit. - if (rateLimiter instanceof IPreemptibleRateLimiter) { - ((IPreemptibleRateLimiter) rateLimiter).release(); - } + @Override + public void onCancel() { + // must release the permit to avoid the leak of permit. + if (rateLimiter instanceof IPreemptibleRateLimiter) { + ((IPreemptibleRateLimiter) rateLimiter).release(); } - }; - } else { - call.close(Status.fromCode(Code.RESOURCE_EXHAUSTED), new Metadata()); - } + } + }; } catch (Exception e) { + // next.startCall() failed — release the permit that was already acquired. + if (rateLimiter instanceof IPreemptibleRateLimiter) { + ((IPreemptibleRateLimiter) rateLimiter).release(); + } String grpcFailMeterName = MetricsKey.NET_API_DETAIL_FAIL_QPS + call.getMethodDescriptor().getFullMethodName(); MetricsUtil.meterMark(MetricsKey.NET_API_FAIL_QPS); MetricsUtil.meterMark(grpcFailMeterName); logger.error("Rpc Api Error: {}", e.getMessage()); + // Close the call so the client gets an immediate INTERNAL status instead of + // hanging until the transport-level deadline fires. + call.close(Status.fromCode(Code.INTERNAL).withDescription("rpc handler init failed"), + new Metadata()); } return listener; diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/DefaultBaseQqsAdapter.java b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/DefaultBaseQqsAdapter.java index 18a1cd14726..8f5b5a487bf 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/DefaultBaseQqsAdapter.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/DefaultBaseQqsAdapter.java @@ -12,7 +12,7 @@ public DefaultBaseQqsAdapter(String paramString) { } @Override - public boolean acquire(RuntimeData data) { - return strategy.acquire(); + public boolean tryAcquire(RuntimeData data) { + return strategy.tryAcquire(); } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/GlobalPreemptibleAdapter.java b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/GlobalPreemptibleAdapter.java index 7f446d4f7e4..4adc142ed28 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/GlobalPreemptibleAdapter.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/GlobalPreemptibleAdapter.java @@ -17,8 +17,8 @@ public void release() { } @Override - public boolean acquire(RuntimeData data) { - return strategy.acquire(); + public boolean tryAcquire(RuntimeData data) { + return strategy.tryAcquire(); } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IPQPSRateLimiterAdapter.java b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IPQPSRateLimiterAdapter.java index a3d94ecea93..c6fb089063a 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IPQPSRateLimiterAdapter.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IPQPSRateLimiterAdapter.java @@ -12,8 +12,8 @@ public IPQPSRateLimiterAdapter(String paramString) { } @Override - public boolean acquire(RuntimeData data) { - return strategy.acquire(data.getRemoteAddr()); + public boolean tryAcquire(RuntimeData data) { + return strategy.tryAcquire(data.getRemoteAddr()); } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IRateLimiter.java b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IRateLimiter.java index 012e9857d65..46ed8beee92 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IRateLimiter.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/IRateLimiter.java @@ -4,6 +4,6 @@ public interface IRateLimiter { - boolean acquire(RuntimeData data); + boolean tryAcquire(RuntimeData data); } diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/QpsRateLimiterAdapter.java b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/QpsRateLimiterAdapter.java index fd45a4588f7..846a5eb1c4e 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/QpsRateLimiterAdapter.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/adapter/QpsRateLimiterAdapter.java @@ -12,8 +12,8 @@ public QpsRateLimiterAdapter(String paramString) { } @Override - public boolean acquire(RuntimeData data) { - return strategy.acquire(); + public boolean tryAcquire(RuntimeData data) { + return strategy.tryAcquire(); } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/GlobalPreemptibleStrategy.java b/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/GlobalPreemptibleStrategy.java index cad1a7ea87b..0a29183d762 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/GlobalPreemptibleStrategy.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/GlobalPreemptibleStrategy.java @@ -3,17 +3,11 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import lombok.extern.slf4j.Slf4j; - -@Slf4j public class GlobalPreemptibleStrategy extends Strategy { public static final String STRATEGY_PARAM_PERMIT = "permit"; public static final int DEFAULT_PERMIT_NUM = 1; - public static final int DEFAULT_ACQUIRE_TIMEOUT = 2; - private Semaphore sp; public GlobalPreemptibleStrategy(String paramString) { @@ -29,20 +23,13 @@ protected Map defaultParam() { return map; } - public boolean acquire() { - - try { - if (!sp.tryAcquire(DEFAULT_ACQUIRE_TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException(); - } - - } catch (InterruptedException e) { - logger.error("acquire permit with error: {}", e.getMessage()); - Thread.currentThread().interrupt(); - } catch (RuntimeException e1) { - return false; - } - return true; + // Non-blocking: immediately rejects if no permit is available. + // Intentional change from the previous tryAcquire(2, TimeUnit.SECONDS) behaviour: + // blocking the caller for up to 2 s ties up Netty IO / gRPC executor threads and + // masks overload rather than shedding it. All rate-limiting in this stack is now + // non-blocking to keep the thread model consistent with GlobalRateLimiter. + public boolean tryAcquire() { + return sp.tryAcquire(); } public void release() { diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java b/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java index 713666a05e3..6589c90fe1d 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/IPQpsStrategy.java @@ -6,7 +6,9 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class IPQpsStrategy extends Strategy { public static final String STRATEGY_PARAM_IPQPS = "qps"; @@ -19,14 +21,18 @@ public IPQpsStrategy(String paramString) { super(paramString); } - public boolean acquire(String ip) { - RateLimiter limiter = ipLimiter.getIfPresent(ip); - if (limiter == null) { - limiter = newRateLimiter(); - ipLimiter.put(ip, limiter); + public boolean tryAcquire(String ip) { + RateLimiter limiter; + try { + // cache.get is atomic: only one loader executes per key under concurrent requests, + // preventing multiple RateLimiter instances from being created for the same IP. + limiter = ipLimiter.get(ip, this::newRateLimiter); + } catch (Exception e) { + logger.warn("Failed to load IP rate limiter for {}, denying request: {}", + ip, e.getMessage()); + return false; } - limiter.acquire(); - return true; + return limiter.tryAcquire(); } private RateLimiter newRateLimiter() { diff --git a/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java b/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java index 34f2042cf99..7e0466448b3 100644 --- a/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java +++ b/framework/src/main/java/org/tron/core/services/ratelimiter/strategy/QpsStrategy.java @@ -26,8 +26,7 @@ protected Map defaultParam() { return map; } - public boolean acquire() { - rateLimiter.acquire(); - return true; + public boolean tryAcquire() { + return rateLimiter.tryAcquire(); } } \ No newline at end of file diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 296a4d4b32a..6c8f2082301 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -138,14 +138,6 @@ node.metrics = { port = 9527 } - # influxdb metrics - storageEnable = false # Whether write metrics data into InfluxDb. Default: false. - influxdb { - ip = "" - port = 8086 - database = "" - metricsReportInterval = 10 - } } node { @@ -163,6 +155,11 @@ node { fetchBlock.timeout = 200 # syncFetchBatchNum = 2000 + # Maximum number of blocks allowed in-flight (requested but not yet processed). + # Throttles block download to reduce memory pressure during sync. + # Range: [50, 2000], default: 500 + # maxPendingBlockSize = 500 + # Number of validate sign thread, default availableProcessors # validateSignThreadNum = 16 @@ -409,7 +406,7 @@ node { ## rate limiter config rate.limiter = { - # Every api could only set a specific rate limit strategy. Three blocking strategy are supported: + # Every api could only set a specific rate limit strategy. Three non-blocking strategy are supported: # GlobalPreemptibleAdapter: The number of preemptible resource or maximum concurrent requests globally. # QpsRateLimiterAdapter: qps is the average request count in one second supported by the server, it could be a Double or a Integer. # IPQPSRateLimiterAdapter: similar to the QpsRateLimiterAdapter, qps could be a Double or a Integer. @@ -780,7 +777,6 @@ committee = { # allowTvmBlob = 0 # consensusLogicOptimization = 0 # allowOptimizedReturnValueOfChainId = 0 - # allowTvmOsaka = 0 } event.subscribe = { diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java index d5dbced87fe..1e7bbc1545c 100644 --- a/framework/src/test/java/org/tron/common/ParameterTest.java +++ b/framework/src/test/java/org/tron/common/ParameterTest.java @@ -218,8 +218,6 @@ public void testCommonParameter() { assertEquals(1, parameter.getShieldedTransInPendingMaxCounts()); parameter.setChangedDelegation(1); assertEquals(1, parameter.getChangedDelegation()); - parameter.setActuatorSet(new HashSet<>()); - assertTrue(CollectionUtils.isEmpty(parameter.getActuatorSet())); parameter.setRateLimiterInitialization(new RateLimiterInitialization()); assertNotNull(parameter.getRateLimiterInitialization()); parameter.setRateLimiterGlobalQps(1000); @@ -241,16 +239,6 @@ public void testCommonParameter() { assertEquals(500, parameter.getPendingTransactionTimeout()); parameter.setNodeMetricsEnable(false); assertFalse(parameter.isNodeMetricsEnable()); - parameter.setMetricsStorageEnable(false); - assertFalse(parameter.isMetricsStorageEnable()); - parameter.setInfluxDbIp("127.0.0.1"); - assertEquals("127.0.0.1", parameter.getInfluxDbIp()); - parameter.setInfluxDbPort(90); - assertEquals(90, parameter.getInfluxDbPort()); - parameter.setInfluxDbDatabase("InfluxDb"); - assertEquals("InfluxDb", parameter.getInfluxDbDatabase()); - parameter.setMetricsReportInterval(100); - assertEquals(100, parameter.getMetricsReportInterval()); parameter.setMetricsPrometheusPort(3000); assertEquals(3000, parameter.getMetricsPrometheusPort()); parameter.setAgreeNodeCount(10); diff --git a/framework/src/test/java/org/tron/common/jetty/SizeLimitHandlerTest.java b/framework/src/test/java/org/tron/common/jetty/SizeLimitHandlerTest.java index 685a861bc92..64108943ad5 100644 --- a/framework/src/test/java/org/tron/common/jetty/SizeLimitHandlerTest.java +++ b/framework/src/test/java/org/tron/common/jetty/SizeLimitHandlerTest.java @@ -1,6 +1,5 @@ package org.tron.common.jetty; -import com.alibaba.fastjson.JSONObject; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; @@ -30,6 +29,7 @@ import org.tron.common.application.HttpService; import org.tron.common.utils.PublicMethod; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; /** * Tests {@link org.eclipse.jetty.server.handler.SizeLimitHandler} body-size diff --git a/framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java b/framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java index 9b0f17244a8..644cecc7da0 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java @@ -2,8 +2,6 @@ import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -12,6 +10,8 @@ import org.tron.common.crypto.Hash; import org.tron.common.utils.ByteArray; import org.tron.core.Wallet; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; public class EventParserJsonTest { @@ -60,7 +60,7 @@ public synchronized void testEventParser() { topicList.add(ByteArray .fromHexString("0xb7685f178b1c93df3422f7bfcb61ae2c6f66d0947bb9eb293259c231b986b81b")); - JSONArray entryArr = JSONObject.parseArray(abiStr); + JSONArray entryArr = JSONArray.parseArray(abiStr); JSONObject entry = new JSONObject(); for (int i = 0; i < entryArr.size(); i++) { diff --git a/framework/src/test/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsuleTest.java b/framework/src/test/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsuleTest.java index 898447b3a75..14b86510fea 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsuleTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/capsule/ContractTriggerCapsuleTest.java @@ -3,8 +3,11 @@ import static com.google.common.collect.Lists.newArrayList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.beust.jcommander.internal.Lists; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import lombok.extern.slf4j.Slf4j; @@ -12,9 +15,12 @@ import org.apache.commons.lang3.ArrayUtils; import org.junit.Before; import org.junit.Test; +import org.tron.common.logsfilter.EventPluginLoader; +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.core.config.args.Args; @Slf4j public class ContractTriggerCapsuleTest { @@ -58,6 +64,45 @@ public void testSetAndGetContractTrigger() { } } + @Test + public void testRemovedTriggerNotWrittenToSolidityMap() throws Exception { + Args.getSolidityContractLogTriggerMap().clear(); + Args.getSolidityContractEventTriggerMap().clear(); + + EventPluginLoader mockLoader = mock(EventPluginLoader.class); + when(mockLoader.isSolidityLogTriggerEnable()).thenReturn(true); + when(mockLoader.isSolidityEventTriggerEnable()).thenReturn(false); + when(mockLoader.isContractLogTriggerEnable()).thenReturn(false); + when(mockLoader.isContractEventTriggerEnable()).thenReturn(false); + when(mockLoader.isSolidityLogTriggerRedundancy()).thenReturn(false); + when(mockLoader.isContractLogTriggerRedundancy()).thenReturn(false); + + Field instanceField = EventPluginLoader.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + EventPluginLoader originalInstance = (EventPluginLoader) instanceField.get(null); + instanceField.set(null, mockLoader); + + try { + ContractLogTrigger trigger = new ContractLogTrigger(); + trigger.setRemoved(true); + trigger.setBlockNumber(100L); + trigger.setTransactionId("abc"); + trigger.setContractAddress("0x01"); + LogInfo logInfo = new LogInfo(new byte[0], new ArrayList<>(), new byte[0]); + trigger.setLogInfo(logInfo); + + ContractTriggerCapsule capsule = new ContractTriggerCapsule(trigger); + capsule.processTrigger(); + + assertTrue(Args.getSolidityContractLogTriggerMap().isEmpty()); + assertTrue(Args.getSolidityContractEventTriggerMap().isEmpty()); + } finally { + instanceField.set(null, originalInstance); + Args.getSolidityContractLogTriggerMap().clear(); + Args.getSolidityContractEventTriggerMap().clear(); + } + } + @Test public void testLogInfo() { logger.info("log info to string: {}, ", logInfo.toString()); diff --git a/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java b/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java index 25d7f353e2c..c7000175b00 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/AllowTvmOsakaTest.java @@ -4,7 +4,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.junit.Assert; import org.junit.Test; -import org.tron.common.runtime.vm.DataWord; +import org.tron.common.math.StrictMathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.core.vm.PrecompiledContracts; @@ -62,6 +62,123 @@ public void testEIP7823() { } } + /** + * Build ModExp input data for energy calculation testing. + */ + private static byte[] buildModExpData(int baseLen, int expLen, int modLen, byte[] expValue) { + byte[] base = new byte[baseLen]; + byte[] exp = new byte[expLen]; + if (expValue.length > 0 && expLen > 0) { + System.arraycopy(expValue, 0, exp, 0, StrictMathWrapper.min(expValue.length, expLen)); + } + byte[] mod = new byte[modLen]; + return ByteUtil.merge(toLenBytes(baseLen), toLenBytes(expLen), toLenBytes(modLen), + base, exp, mod); + } + + private static long getEnergy(int baseLen, int expLen, int modLen, byte[] expValue) { + return modExp.getEnergyForData(buildModExpData(baseLen, expLen, modLen, expValue)); + } + + @Test + public void testEIP7883ModExpPricing() { + ConfigLoader.disable = true; + VMConfig.initAllowTvmOsaka(1); + + try { + byte[] square = {0x02}; + byte[] qube = {0x03}; + byte[] pow0x10001 = {0x01, 0x00, 0x01}; + + // nagydani_1: baseLen=64, expLen=square/qube:1 pow:3, modLen=64 + Assert.assertEquals(500L, getEnergy(64, 1, 64, square)); + Assert.assertEquals(500L, getEnergy(64, 1, 64, qube)); + Assert.assertEquals(2048L, getEnergy(64, 3, 64, pow0x10001)); + + // nagydani_2: baseLen=128, modLen=128 + Assert.assertEquals(512L, getEnergy(128, 1, 128, square)); + Assert.assertEquals(512L, getEnergy(128, 1, 128, qube)); + Assert.assertEquals(8192L, getEnergy(128, 3, 128, pow0x10001)); + + // nagydani_3: baseLen=256, modLen=256 + Assert.assertEquals(2048L, getEnergy(256, 1, 256, square)); + Assert.assertEquals(2048L, getEnergy(256, 1, 256, qube)); + Assert.assertEquals(32768L, getEnergy(256, 3, 256, pow0x10001)); + + // nagydani_4: baseLen=512, modLen=512 + Assert.assertEquals(8192L, getEnergy(512, 1, 512, square)); + Assert.assertEquals(8192L, getEnergy(512, 1, 512, qube)); + Assert.assertEquals(131072L, getEnergy(512, 3, 512, pow0x10001)); + + // nagydani_5: baseLen=1024, modLen=1024 + Assert.assertEquals(32768L, getEnergy(1024, 1, 1024, square)); + Assert.assertEquals(32768L, getEnergy(1024, 1, 1024, qube)); + Assert.assertEquals(524288L, getEnergy(1024, 3, 1024, pow0x10001)); + + // Minimum energy: zero-length inputs + Assert.assertEquals(500L, getEnergy(0, 0, 0, new byte[]{})); + + // Small base/mod (<=32): complexity=16 + Assert.assertEquals(500L, getEnergy(1, 1, 1, square)); + Assert.assertEquals(500L, getEnergy(32, 1, 32, square)); + + // Boundary: base/mod at 33 (just over 32) uses doubled formula + // words = ceil(33/8) = 5, complexity = 2 * 25 = 50, iterCount = 1 + Assert.assertEquals(500L, getEnergy(33, 1, 33, square)); + + // Same boundary with expLen=64 forces a non-floor result so the + // 2*words² branch is observable: complexity=50, iterCount=16*(64-32)=512, + // energy = 50 * 512 = 25600. + Assert.assertEquals(25600L, getEnergy(33, 64, 33, new byte[]{})); + + // Exponent > 32 bytes: multiplier is 16 + // expLen=64, high bytes all zero → highestBit=0, iterCount = 16*(64-32)+0 = 512 + // baseLen=64, modLen=64 → complexity=128, energy=128*512=65536 + Assert.assertEquals(65536L, getEnergy(64, 64, 64, new byte[]{})); + + // Exponent > 32 bytes with non-zero high bytes + // expLen=64, first byte=0x01 → highestBit=248, iterCount = 16*32+248 = 760 + // baseLen=64, modLen=64 → complexity=128, energy=128*760=97280 + Assert.assertEquals(97280L, getEnergy(64, 64, 64, new byte[]{0x01})); + } finally { + VMConfig.initAllowTvmOsaka(0); + ConfigLoader.disable = false; + } + } + + @Test + public void testEIP7883DisabledPreservesOldPricing() { + ConfigLoader.disable = true; + VMConfig.initAllowTvmOsaka(0); + + try { + // When Osaka is disabled, the existing EIP-198-style pricing formula is used: + // nagydani_1_square: 4096 * 1 / 20 = 204. + long energy = getEnergy(64, 1, 64, new byte[]{0x02}); + Assert.assertEquals(204L, energy); + } finally { + ConfigLoader.disable = false; + } + } + + @Test + public void testEIP7883CanBeLowerThanLegacyPricing() { + ConfigLoader.disable = true; + + try { + byte[] square = {0x02}; + + VMConfig.initAllowTvmOsaka(0); + Assert.assertEquals(665L, getEnergy(128, 1, 128, square)); + + VMConfig.initAllowTvmOsaka(1); + Assert.assertEquals(512L, getEnergy(128, 1, 128, square)); + } finally { + VMConfig.initAllowTvmOsaka(0); + ConfigLoader.disable = false; + } + } + @Test public void testEIP7823DisabledShouldPass() { ConfigLoader.disable = true; diff --git a/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java b/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java index db5cdd3f21a..9e65b22c091 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/OperationsTest.java @@ -78,6 +78,7 @@ public static void destroy() { VMConfig.initAllowTvmIstanbul(0); VMConfig.initAllowTvmLondon(0); VMConfig.initAllowTvmCompatibleEvm(0); + VMConfig.initAllowTvmOsaka(0); } @Test @@ -905,6 +906,128 @@ public void testPush0() throws ContractValidateException { VMConfig.initAllowTvmShangHai(0); } + @Test + public void testCLZ() throws ContractValidateException { + VMConfig.initAllowTvmOsaka(1); + + try { + invoke = new ProgramInvokeMockImpl(); + Protocol.Transaction trx = Protocol.Transaction.getDefaultInstance(); + InternalTransaction interTrx = + new InternalTransaction(trx, InternalTransaction.TrxType.TRX_UNKNOWN_TYPE); + + // CLZ(0) = 256 + byte[] op = buildCLZBytecode(new byte[32]); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(256), program.getStack().pop()); + + // CLZ(0x80...00) = 0 (highest bit set) + byte[] val = new byte[32]; + val[0] = (byte) 0x80; + op = buildCLZBytecode(val); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(0), program.getStack().pop()); + + // CLZ(0xFF...FF) = 0 + val = new byte[32]; + for (int i = 0; i < 32; i++) { + val[i] = (byte) 0xFF; + } + op = buildCLZBytecode(val); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(0), program.getStack().pop()); + + // CLZ(0x40...00) = 1 + val = new byte[32]; + val[0] = (byte) 0x40; + op = buildCLZBytecode(val); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(1), program.getStack().pop()); + + // CLZ(0x7F...FF) = 1 + val = new byte[32]; + for (int i = 0; i < 32; i++) { + val[i] = (byte) 0xFF; + } + val[0] = (byte) 0x7F; + op = buildCLZBytecode(val); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(1), program.getStack().pop()); + + // CLZ(1) = 255 + val = new byte[32]; + val[31] = 0x01; + op = buildCLZBytecode(val); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(255), program.getStack().pop()); + + // Vectors with CLZ in [128, 254] — exercise the (byte) cast path in + // DataWord.of(byte) where the unsigned int would otherwise become a + // negative byte. Read-back goes through new BigInteger(1, data), so the + // bit pattern must round-trip as unsigned. + // CLZ = 128 (boundary): byte[16] high bit set + val = new byte[32]; + val[16] = (byte) 0x80; + op = buildCLZBytecode(val); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(128), program.getStack().pop()); + + // CLZ = 192 (mid-range): byte[24] high bit set + val = new byte[32]; + val[24] = (byte) 0x80; + op = buildCLZBytecode(val); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(192), program.getStack().pop()); + + // CLZ = 247 (near-upper): 30 zero bytes, then 0x01 + val = new byte[32]; + val[30] = 0x01; + op = buildCLZBytecode(val); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + Assert.assertEquals(new DataWord(247), program.getStack().pop()); + + // Verify energy cost = LOW_TIER(5) + PUSH32 cost(3) = 8 + Assert.assertEquals(8, program.getResult().getEnergyUsed()); + } finally { + VMConfig.initAllowTvmOsaka(0); + } + } + + @Test + public void testCLZRejectedWhenOsakaDisabled() throws ContractValidateException { + VMConfig.initAllowTvmOsaka(0); + + invoke = new ProgramInvokeMockImpl(); + Protocol.Transaction trx = Protocol.Transaction.getDefaultInstance(); + InternalTransaction interTrx = + new InternalTransaction(trx, InternalTransaction.TrxType.TRX_UNKNOWN_TYPE); + + byte[] op = buildCLZBytecode(new byte[32]); + program = new Program(op, op, invoke, interTrx); + testOperations(program); + + Assert.assertTrue(program.getResult().getException() + instanceof Program.IllegalOperationException); + } + + // Build bytecode: PUSH32 CLZ + private byte[] buildCLZBytecode(byte[] value) { + byte[] op = new byte[34]; + op[0] = 0x7f; // PUSH32 + System.arraycopy(value, 0, op, 1, 32); + op[33] = Op.CLZ; + return op; + } + @Test public void testSuicideCost() throws ContractValidateException { invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], new byte[21]); diff --git a/framework/src/test/java/org/tron/common/utils/client/utils/AbiUtil.java b/framework/src/test/java/org/tron/common/utils/client/utils/AbiUtil.java index 1d780bfacd4..5f28fb3e13a 100644 --- a/framework/src/test/java/org/tron/common/utils/client/utils/AbiUtil.java +++ b/framework/src/test/java/org/tron/common/utils/client/utils/AbiUtil.java @@ -3,6 +3,7 @@ import static org.tron.common.math.Maths.abs; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -19,6 +20,7 @@ public class AbiUtil { private static Pattern paramTypeBytes = Pattern.compile("^bytes([0-9]*)$"); private static Pattern paramTypeNumber = Pattern.compile("^(u?int)([0-9]*)$"); private static Pattern paramTypeArray = Pattern.compile("^(.*)\\[([0-9]*)]$"); + private static final ObjectMapper mapper = new ObjectMapper(); public static String[] getTypes(String methodSign) { int start = methodSign.indexOf('(') + 1; @@ -221,7 +223,6 @@ public static String parseSelector(String methodSign) { } public static byte[] encodeInput(String methodSign, String input) { - ObjectMapper mapper = new ObjectMapper(); input = "[" + input + "]"; List items; try { @@ -315,7 +316,6 @@ byte[] encode(String arrayValues) { List items; try { - ObjectMapper mapper = new ObjectMapper(); items = mapper.readValue(arrayValues, List.class); } catch (IOException e) { e.printStackTrace(); diff --git a/framework/src/test/java/org/tron/common/utils/client/utils/HttpMethed.java b/framework/src/test/java/org/tron/common/utils/client/utils/HttpMethed.java index 030fbd80dea..cea17b0c033 100644 --- a/framework/src/test/java/org/tron/common/utils/client/utils/HttpMethed.java +++ b/framework/src/test/java/org/tron/common/utils/client/utils/HttpMethed.java @@ -1,7 +1,5 @@ package org.tron.common.utils.client.utils; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.JsonArray; @@ -29,6 +27,8 @@ import org.tron.common.utils.ByteUtil; import org.tron.common.utils.PublicMethod; import org.tron.common.utils.client.Configuration; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; @Slf4j public class HttpMethed { diff --git a/framework/src/test/java/org/tron/common/utils/client/utils/JSONObjectWarp.java b/framework/src/test/java/org/tron/common/utils/client/utils/JSONObjectWarp.java index bab667dedf1..500d1f452b2 100644 --- a/framework/src/test/java/org/tron/common/utils/client/utils/JSONObjectWarp.java +++ b/framework/src/test/java/org/tron/common/utils/client/utils/JSONObjectWarp.java @@ -1,9 +1,34 @@ package org.tron.common.utils.client.utils; -import com.alibaba.fastjson.JSONObject; +import org.tron.json.JSONObject; public class JSONObjectWarp extends JSONObject { + @Override + public JSONObjectWarp put(String key, String value) { + super.put(key, value); + return this; + } + + @Override + public JSONObjectWarp put(String key, Boolean value) { + super.put(key, value); + return this; + } + + @Override + public JSONObjectWarp put(String key, Integer value) { + super.put(key, value); + return this; + } + + @Override + public JSONObjectWarp put(String key, Long value) { + super.put(key, value); + return this; + } + + @Override public JSONObjectWarp put(String key, Object value) { super.put(key, value); return this; diff --git a/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java b/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java index efe2c2b871b..16a3cb3a5bb 100644 --- a/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java +++ b/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java @@ -343,6 +343,8 @@ public void validateCheck() { testAllowTvmSelfdestructRestrictionProposal(); + testAllowTvmPragueProposal(); + testAllowHardenResourceCalculationProposal(); testAllowHardenExchangeCalculationProposal(); @@ -577,6 +579,8 @@ private void testAllowHardenResourceCalculationProposal() { byte[] stats = new byte[27]; forkUtils.getManager().getDynamicPropertiesStore() .statsByVersion(ForkBlockVersionEnum.VERSION_4_8_1.getValue(), stats); + forkUtils.getManager().getDynamicPropertiesStore() + .statsByVersion(ForkBlockVersionEnum.VERSION_4_8_2.getValue(), stats); ContractValidateException e1 = Assert.assertThrows(ContractValidateException.class, () -> ProposalUtil.validator(dynamicPropertiesStore, forkUtils, ProposalType.ALLOW_HARDEN_RESOURCE_CALCULATION.getCode(), 1)); @@ -615,6 +619,69 @@ private void testAllowHardenResourceCalculationProposal() { e3.getMessage()); } + private void testAllowTvmPragueProposal() { + byte[] stats = new byte[27]; + forkUtils.getManager().getDynamicPropertiesStore() + .statsByVersion(ForkBlockVersionEnum.VERSION_4_8_2.getValue(), stats); + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_TVM_PRAGUE.getCode(), 1); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "Bad chain parameter id [ALLOW_TVM_PRAGUE]", + e.getMessage()); + } + + long maintenanceTimeInterval = forkUtils.getManager().getDynamicPropertiesStore() + .getMaintenanceTimeInterval(); + long hardForkTime = + ((ForkBlockVersionEnum.VERSION_4_8_2.getHardForkTime() - 1) / maintenanceTimeInterval + 1) + * maintenanceTimeInterval; + forkUtils.getManager().getDynamicPropertiesStore() + .saveLatestBlockHeaderTimestamp(hardForkTime + 1); + + stats = new byte[27]; + Arrays.fill(stats, (byte) 1); + forkUtils.getManager().getDynamicPropertiesStore() + .statsByVersion(ForkBlockVersionEnum.VERSION_4_8_2.getValue(), stats); + + // Fork passed but Shanghai not yet enacted: prague validator must refuse, + // since the deployed bytecode uses PUSH0 (gated on ALLOW_TVM_SHANGHAI). + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_TVM_PRAGUE.getCode(), 1); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "[ALLOW_TVM_PRAGUE] requires [ALLOW_TVM_SHANGHAI] to be enacted first", + e.getMessage()); + } + + dynamicPropertiesStore.saveAllowTvmShangHai(1); + + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_TVM_PRAGUE.getCode(), 2); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "This value[ALLOW_TVM_PRAGUE] is only allowed to be 1", + e.getMessage()); + } + + dynamicPropertiesStore.saveAllowTvmPrague(1); + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_TVM_PRAGUE.getCode(), 1); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "[ALLOW_TVM_PRAGUE] has been valid, no need to propose again", + e.getMessage()); + } + } + private void testAllowHardenExchangeCalculationProposal() { long code = ProposalType.ALLOW_HARDEN_EXCHANGE_CALCULATION.getCode(); ThrowingRunnable proposeZero = () -> ProposalUtil.validator(dynamicPropertiesStore, forkUtils, diff --git a/framework/src/test/java/org/tron/core/actuator/vm/SerializersTest.java b/framework/src/test/java/org/tron/core/actuator/vm/SerializersTest.java index 52ee1eeb937..9e9b5d35fbe 100644 --- a/framework/src/test/java/org/tron/core/actuator/vm/SerializersTest.java +++ b/framework/src/test/java/org/tron/core/actuator/vm/SerializersTest.java @@ -1,5 +1,9 @@ package org.tron.core.actuator.vm; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import org.junit.Test; import org.tron.core.vm.trace.Serializers; @@ -7,6 +11,60 @@ public class SerializersTest { @Test public void testSerializeFieldsOnly() { - Serializers.serializeFieldsOnly("testString", true); + assertEquals("\"testString\"", Serializers.serializeFieldsOnly("testString")); + } + + @Test + public void testSerializeFieldsOnlyPojo() { + TestBean bean = new TestBean("hello", 42); + String json = Serializers.serializeFieldsOnly(bean); + assertTrue("Should contain name field", json.contains("\"name\"")); + assertTrue("Should contain name value", json.contains("\"hello\"")); + assertTrue("Should contain value field", json.contains("\"value\"")); + assertTrue("Should contain value 42", json.contains("42")); + } + + @Test + public void testSerializeFieldsOnlyIgnoresGetters() { + TestBean bean = new TestBean("hello", 42); + String json = Serializers.serializeFieldsOnly(bean); + // getComputedField() returns "computed" but should not appear + // because getter visibility is NONE + assertFalse("Should not serialize getter-only property", + json.contains("computed")); + } + + @Test + public void testSerializeFieldsOnlyNull() { + TestBean bean = new TestBean(null, 0); + String json = Serializers.serializeFieldsOnly(bean); + assertTrue("Should contain null name", json.replaceAll("\\s+", "") + .contains("\"name\":null")); + assertTrue("Should contain value 0", json.replaceAll("\\s+", "") + .contains("\"value\":0")); + } + + @Test + public void testSerializeFieldsOnlyReturnsEmptyOnError() { + // A non-serializable object should return "{}" + assertEquals("{}", Serializers.serializeFieldsOnly(new Object() { + // anonymous class with circular reference + Object self = this; + })); + } + + @SuppressWarnings("unused") + static class TestBean { + private String name; + private int value; + + TestBean(String name, int value) { + this.name = name; + this.value = value; + } + + public String getComputedField() { + return "computed"; + } } } diff --git a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java index 2a3c3e253ab..a67414bd388 100644 --- a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java +++ b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java @@ -39,7 +39,6 @@ import org.tron.common.utils.DecodeUtil; import org.tron.common.utils.LocalWitnesses; import org.tron.common.utils.PublicMethod; -import org.tron.core.config.Configuration; import org.tron.core.exception.TronError; @Slf4j @@ -408,6 +407,35 @@ public void testFetchBlockTimeoutClampedAboveMax() { Args.clearParam(); } + + @Test + public void testHttpJsonParseConstraints() { + Map override = new HashMap<>(); + override.put("storage.db.directory", "database"); + Config config = ConfigFactory.parseMap(override) + .withFallback(ConfigFactory.defaultReference()); + Args.applyConfigParams(config); + + Assert.assertEquals(100, Args.getInstance().getMaxNestingDepth()); + Assert.assertEquals(100_000, Args.getInstance().getMaxTokenCount()); + Args.clearParam(); + } + + @Test + public void testHttpJsonParseConstraintsApplied() { + Map override = new HashMap<>(); + override.put("storage.db.directory", "database"); + override.put("node.http.maxNestingDepth", "42"); + override.put("node.http.maxTokenCount", "12345"); + Config config = ConfigFactory.parseMap(override) + .withFallback(ConfigFactory.defaultReference()); + Args.applyConfigParams(config); + + Assert.assertEquals(42, Args.getInstance().getMaxNestingDepth()); + Assert.assertEquals(12345, Args.getInstance().getMaxTokenCount()); + Args.clearParam(); + } + @Test public void testFetchBlockTimeoutInRangeUnchanged() { Map override = new HashMap<>(); diff --git a/framework/src/test/java/org/tron/core/db/HistoryBlockHashIntegrationTest.java b/framework/src/test/java/org/tron/core/db/HistoryBlockHashIntegrationTest.java new file mode 100644 index 00000000000..186d897effa --- /dev/null +++ b/framework/src/test/java/org/tron/core/db/HistoryBlockHashIntegrationTest.java @@ -0,0 +1,473 @@ +package org.tron.core.db; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.protobuf.ByteString; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.tron.common.BaseTest; +import org.tron.common.TestConstants; +import org.tron.common.crypto.ECKey; +import org.tron.common.runtime.vm.DataWord; +import org.tron.common.utils.Sha256Hash; +import org.tron.consensus.base.Param; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.CodeCapsule; +import org.tron.core.capsule.ContractCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.db.accountstate.callback.AccountStateCallBack; +import org.tron.core.store.DynamicPropertiesStore; +import org.tron.core.store.StoreFactory; +import org.tron.core.vm.program.Storage; +import org.tron.core.vm.repository.RepositoryImpl; +import org.tron.protos.Protocol; +import org.tron.protos.contract.SmartContractOuterClass.SmartContract; + +/** + * TIP-2935 end-to-end: activation deploys the contract, subsequent blocks + * populate the ring buffer via the pre-tx hook, and the VM repository reads + * back written hashes through the same {@code Storage.compose()} layer that + * production {@code SLOAD} uses. + */ +public class HistoryBlockHashIntegrationTest extends BaseTest { + + static { + Args.setParam(new String[]{"--output-directory", dbPath()}, TestConstants.TEST_CONF); + } + + @Before + public void resetState() { + byte[] addr = HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS; + chainBaseManager.getDynamicPropertiesStore().saveAllowTvmPrague(0L); + chainBaseManager.getDynamicPropertiesStore().saveBlockHashHistoryInstalled(0L); + chainBaseManager.getCodeStore().delete(addr); + chainBaseManager.getContractStore().delete(addr); + chainBaseManager.getAccountStore().delete(addr); + // Storage.commit() translates a zero write into a row delete (see + // Storage#commit), so writing ZERO to every slot the suite touches is + // the cheapest way to clear leftover state between tests. + Storage storage = new Storage(addr, chainBaseManager.getStorageRowStore()); + for (long slot : new long[]{0L, 99L, 499L, 776L}) { + storage.put(new DataWord(slot), DataWord.ZERO()); + } + storage.commit(); + } + + private DataWord readSlot(long slot) { + Storage storage = new Storage( + HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS, + chainBaseManager.getStorageRowStore()); + return storage.getValue(new DataWord(slot)); + } + + @Test + public void activationDeploysContractAndFlagIsSet() { + DynamicPropertiesStore dps = chainBaseManager.getDynamicPropertiesStore(); + byte[] addr = HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS; + + assertEquals(0L, dps.getAllowTvmPrague()); + assertFalse(chainBaseManager.getCodeStore().has(addr)); + + dps.saveAllowTvmPrague(1L); + HistoryBlockHashUtil.deploy(dbManager); + + assertEquals(1L, dps.getAllowTvmPrague()); + assertTrue(chainBaseManager.getCodeStore().has(addr)); + CodeCapsule code = chainBaseManager.getCodeStore().get(addr); + assertNotNull(code); + assertArrayEquals(HistoryBlockHashUtil.HISTORY_STORAGE_CODE, code.getData()); + } + + @Test + public void writeAfterActivationFillsStorageSlot() { + chainBaseManager.getDynamicPropertiesStore().saveAllowTvmPrague(1L); + HistoryBlockHashUtil.deploy(dbManager); + + long blockNum = 500L; + byte[] parentHash = new byte[32]; + Arrays.fill(parentHash, (byte) 0x5a); + BlockCapsule block = new BlockCapsule( + blockNum, + Sha256Hash.wrap(parentHash), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + + HistoryBlockHashUtil.write(dbManager, block); + + DataWord readBack = readSlot(499L); + assertNotNull(readBack); + assertArrayEquals(parentHash, readBack.getData()); + } + + @Test + public void vmRepositoryReadsBackWrittenHash() { + // Full round-trip: direct-write through Storage -> VM Repository -> getStorageValue. + // Proves write and read go through the same Storage.compose() layer. + chainBaseManager.getDynamicPropertiesStore().saveAllowTvmPrague(1L); + HistoryBlockHashUtil.deploy(dbManager); + + long blockNum = 777L; + byte[] parentHash = new byte[32]; + Arrays.fill(parentHash, (byte) 0x77); + BlockCapsule block = new BlockCapsule( + blockNum, + Sha256Hash.wrap(parentHash), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + HistoryBlockHashUtil.write(dbManager, block); + + RepositoryImpl repo = RepositoryImpl.createRoot(StoreFactory.getInstance()); + + // (777 - 1) % 8191 = 776 + DataWord slotKey = new DataWord(776L); + DataWord readBack = repo.getStorageValue( + HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS, slotKey); + + assertNotNull("VM repository failed to read stored hash", readBack); + assertArrayEquals("VM read-back != direct-written hash", + parentHash, readBack.getData()); + } + + @Test + public void noWriteBeforeActivation() { + assertEquals(0L, + chainBaseManager.getDynamicPropertiesStore().getAllowTvmPrague()); + assertFalse(chainBaseManager.getDynamicPropertiesStore() + .isBlockHashHistoryInstalled()); + + long blockNum = 100L; + byte[] parentHash = new byte[32]; + Arrays.fill(parentHash, (byte) 0xff); + BlockCapsule block = new BlockCapsule( + blockNum, + Sha256Hash.wrap(parentHash), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + + // Manager calls write() unconditionally; the install marker stays 0 + // before activation, so write() must early-return. + HistoryBlockHashUtil.write(dbManager, block); + + assertNull(readSlot(99L)); + } + + /** + * Block 1 is the first block to go through {@code applyBlock -> processBlock}. + * Its parent is the genesis block, so slot 0 must hold the genesis block hash. + */ + @Test + public void writeForBlock1StoresGenesisHashAtSlot0() { + chainBaseManager.getDynamicPropertiesStore().saveAllowTvmPrague(1L); + HistoryBlockHashUtil.deploy(dbManager); + + byte[] genesisHash = new byte[32]; + Arrays.fill(genesisHash, (byte) 0x01); + BlockCapsule block1 = new BlockCapsule( + 1L, + Sha256Hash.wrap(genesisHash), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + + HistoryBlockHashUtil.write(dbManager, block1); + + DataWord readBack = readSlot(0L); + assertNotNull(readBack); + assertArrayEquals(genesisHash, readBack.getData()); + } + + /** + * Genesis never goes through {@code applyBlock}, but the guard keeps + * {@code (0 - 1) % 8191 = -1} from ever corrupting a slot if it ever did. + */ + @Test + public void writeIsNoOpForGenesisBlock() { + chainBaseManager.getDynamicPropertiesStore().saveAllowTvmPrague(1L); + HistoryBlockHashUtil.deploy(dbManager); + + byte[] zeroHash = new byte[32]; + BlockCapsule genesis = new BlockCapsule( + 0L, + Sha256Hash.wrap(zeroHash), + 0L, + ByteString.copyFrom(new byte[21])); + + HistoryBlockHashUtil.write(dbManager, genesis); + + assertNull(readSlot(0L)); + } + + /** + * Collision guard: if foreign bytecode already sits at the canonical address + * (theoretically impossible short of a hash pre-image), activation must skip + * the deploy entirely — leaving the foreign code intact and writing nothing + * to ContractStore / AccountStore — rather than silently merging into a + * broken contract. Same expectation applies to foreign contract metadata. + */ + @Test + public void deploySkipsWhenForeignBytecodePresent() { + byte[] addr = HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS; + byte[] foreignCode = new byte[]{0x60, 0x00}; + chainBaseManager.getCodeStore().put(addr, new CodeCapsule(foreignCode)); + + HistoryBlockHashUtil.deploy(dbManager); + + assertArrayEquals(foreignCode, + chainBaseManager.getCodeStore().get(addr).getData()); + assertFalse(chainBaseManager.getContractStore().has(addr)); + assertFalse(chainBaseManager.getAccountStore().has(addr)); + } + + @Test + public void deploySkipsWhenForeignContractPresent() { + byte[] addr = HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS; + SmartContract foreign = SmartContract.newBuilder() + .setName("NotBlockHashHistory") + .setContractAddress(ByteString.copyFrom(addr)) + .setOriginAddress(ByteString.copyFrom(addr)) + .build(); + chainBaseManager.getContractStore().put(addr, new ContractCapsule(foreign)); + + HistoryBlockHashUtil.deploy(dbManager); + + assertEquals("NotBlockHashHistory", + chainBaseManager.getContractStore().get(addr).getInstance().getName()); + assertFalse(chainBaseManager.getCodeStore().has(addr)); + assertFalse(chainBaseManager.getAccountStore().has(addr)); + } + + /** + * Anyone can transfer TRX to {@code HISTORY_STORAGE_ADDRESS} before the + * proposal fires, leaving an EOA at the canonical address. Activation must + * upgrade the type to {@code Contract} in place — preserving balance — + * rather than failing or zeroing the account. + */ + /** + * SR / validator parity: the producer's {@code generateBlock} simulation + * loop and the validator's {@code processBlock} apply loop must see the + * same storage state when transactions hit {@code HISTORY_STORAGE_ADDRESS}. + * That requires {@link HistoryBlockHashUtil#write} to run before the tx + * loop on both paths. {@code processBlock} writes at line 1858; this test + * pins the matching write inside {@code generateBlock}. + * + *

Spy {@code accountStateCallBack.preExecute} — called between the + * write and the tx loop on both paths — and snapshot the slot from inside + * the revoking session. Pre-fix the slot is empty (write never ran); + * post-fix it holds the parent block hash. + */ + @Test + public void generateBlockWritesParentHashBeforeTxLoop() throws Exception { + chainBaseManager.getDynamicPropertiesStore().saveAllowTvmPrague(1L); + HistoryBlockHashUtil.deploy(dbManager); + + byte[] expectedParentHash = chainBaseManager.getHeadBlockId().getBytes(); + long nextBlockNum = chainBaseManager.getHeadBlockNum() + 1; + long expectedSlot = + (nextBlockNum - 1) % HistoryBlockHashUtil.HISTORY_SERVE_WINDOW; + + Field cbField = Manager.class.getDeclaredField("accountStateCallBack"); + cbField.setAccessible(true); + AccountStateCallBack realCb = (AccountStateCallBack) cbField.get(dbManager); + AccountStateCallBack spy = Mockito.spy(realCb); + AtomicReference captured = new AtomicReference<>(); + Mockito.doAnswer(inv -> { + Storage st = new Storage( + HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS, + chainBaseManager.getStorageRowStore()); + captured.set(st.getValue(new DataWord(expectedSlot))); + return inv.callRealMethod(); + }).when(spy).preExecute(Mockito.any(BlockCapsule.class)); + cbField.set(dbManager, spy); + + try { + ECKey ecKey = new ECKey(); + ByteString witness = ByteString.copyFrom(ecKey.getAddress()); + Param.Miner miner = + Param.getInstance().new Miner(ecKey.getPrivKeyBytes(), witness, witness); + long blockTime = System.currentTimeMillis() / 3000 * 3000; + BlockCapsule generated = dbManager.generateBlock( + miner, blockTime, System.currentTimeMillis() + 1000); + assertNotNull("generateBlock returned null", generated); + } finally { + cbField.set(dbManager, realCb); + } + + assertNotNull( + "preExecute fired with an empty slot — write() must run before preExecute", + captured.get()); + assertArrayEquals( + "slot must hold the parent block hash before the tx loop runs", + expectedParentHash, captured.get().getData()); + } + + @Test + public void deployUpgradesPreExistingNormalAccountPreservingBalance() { + byte[] addr = HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS; + long balance = 12345L; + AccountCapsule eoa = new AccountCapsule( + ByteString.copyFrom(addr), Protocol.AccountType.Normal); + eoa.setBalance(balance); + chainBaseManager.getAccountStore().put(addr, eoa); + + HistoryBlockHashUtil.deploy(dbManager); + + AccountCapsule after = chainBaseManager.getAccountStore().get(addr); + assertEquals(Protocol.AccountType.Contract, after.getType()); + assertEquals(balance, after.getBalance()); + assertTrue(chainBaseManager.getCodeStore().has(addr)); + assertTrue(chainBaseManager.getContractStore().has(addr)); + } + + @Test + public void deployCreatesCodeContractAndAccount() { + HistoryBlockHashUtil.deploy(dbManager); + + byte[] addr = HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS; + + assertTrue(chainBaseManager.getCodeStore().has(addr)); + CodeCapsule code = chainBaseManager.getCodeStore().get(addr); + assertNotNull(code); + assertArrayEquals(HistoryBlockHashUtil.HISTORY_STORAGE_CODE, code.getData()); + + ContractCapsule contract = chainBaseManager.getContractStore().get(addr); + assertNotNull(contract); + SmartContract proto = contract.getInstance(); + assertEquals(HistoryBlockHashUtil.HISTORY_STORAGE_NAME, proto.getName()); + assertArrayEquals(addr, proto.getContractAddress().toByteArray()); + assertEquals("version must be 0", 0, proto.getVersion()); + assertEquals(100L, proto.getConsumeUserResourcePercent()); + assertArrayEquals("originAddress must be the EIP-2935 system caller", + HistoryBlockHashUtil.HISTORY_DEPLOYER_ADDRESS, + proto.getOriginAddress().toByteArray()); + + assertTrue(chainBaseManager.getAccountStore().has(addr)); + AccountCapsule account = chainBaseManager.getAccountStore().get(addr); + assertEquals(HistoryBlockHashUtil.HISTORY_STORAGE_NAME, + account.getAccountName().toStringUtf8()); + assertEquals(Protocol.AccountType.Contract, account.getType()); + assertTrue("install marker must flip after a successful deploy", + chainBaseManager.getDynamicPropertiesStore().isBlockHashHistoryInstalled()); + } + + @Test + public void writeStoresParentHashAtCorrectSlot() { + HistoryBlockHashUtil.deploy(dbManager); + + long blockNum = 100L; + byte[] parentHash = new byte[32]; + Arrays.fill(parentHash, (byte) 0xab); + + BlockCapsule block = new BlockCapsule( + blockNum, + Sha256Hash.wrap(parentHash), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + + HistoryBlockHashUtil.write(dbManager, block); + + DataWord readBack = readSlot(99L); + assertNotNull(readBack); + assertArrayEquals(parentHash, readBack.getData()); + } + + @Test + public void writeUsesRingBufferModulo() { + HistoryBlockHashUtil.deploy(dbManager); + + // (8192 - 1) % 8191 = 0 + long blockNum = 8192L; + byte[] parentHash = new byte[32]; + Arrays.fill(parentHash, (byte) 0xcd); + + BlockCapsule block = new BlockCapsule( + blockNum, + Sha256Hash.wrap(parentHash), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + + HistoryBlockHashUtil.write(dbManager, block); + + DataWord readBack = readSlot(0L); + assertNotNull(readBack); + assertArrayEquals(parentHash, readBack.getData()); + } + + @Test + public void beforeDeployNothingIsWritten() { + assertFalse(chainBaseManager.getCodeStore() + .has(HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS)); + assertFalse(chainBaseManager.getContractStore() + .has(HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS)); + assertFalse(chainBaseManager.getAccountStore() + .has(HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS)); + } + + /** + * If {@code deploy()} never ran (e.g. flag flipped without the deploy path), + * {@code write()} must not mutate {@code StorageRowStore} at the canonical + * address — otherwise the next call to {@code deploy()} would land on top of + * partially-written state. + */ + @Test + public void writeIsNoOpBeforeDeploy() { + long blockNum = 100L; + byte[] parentHash = new byte[32]; + Arrays.fill(parentHash, (byte) 0xab); + BlockCapsule block = new BlockCapsule( + blockNum, + Sha256Hash.wrap(parentHash), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + + HistoryBlockHashUtil.write(dbManager, block); + + assertNull("write() must be a no-op without an installed BlockHashHistory", + readSlot(99L)); + } + + /** + * Defense-in-depth: when foreign bytecode sits at the canonical address, + * {@code deploy()} skips and the install marker stays 0, so {@code write()} + * must refuse to overwrite that contract's storage every block. Triggering + * the collision in practice requires a SHA-3 pre-image of the address, but + * the marker check is a single cached store hit. + */ + @Test + public void writeIsNoOpOnForeignCode() { + byte[] addr = HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS; + byte[] foreignCode = Hex.decode("60016002"); + chainBaseManager.getCodeStore().put(addr, new CodeCapsule(foreignCode)); + + HistoryBlockHashUtil.deploy(dbManager); + + assertFalse("install marker must stay 0 when deploy skipped", + chainBaseManager.getDynamicPropertiesStore().isBlockHashHistoryInstalled()); + + long blockNum = 100L; + byte[] parentHash = new byte[32]; + Arrays.fill(parentHash, (byte) 0xcd); + BlockCapsule block = new BlockCapsule( + blockNum, + Sha256Hash.wrap(parentHash), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + + HistoryBlockHashUtil.write(dbManager, block); + + assertNull("write() must not overwrite a foreign contract's storage", + readSlot(99L)); + assertArrayEquals("foreign code must remain intact", + foreignCode, chainBaseManager.getCodeStore().get(addr).getData()); + } + +} diff --git a/framework/src/test/java/org/tron/core/db/HistoryBlockHashVmTest.java b/framework/src/test/java/org/tron/core/db/HistoryBlockHashVmTest.java new file mode 100644 index 00000000000..2dd15392684 --- /dev/null +++ b/framework/src/test/java/org/tron/core/db/HistoryBlockHashVmTest.java @@ -0,0 +1,243 @@ +package org.tron.core.db; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.protobuf.ByteString; +import java.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.common.TestConstants; +import org.tron.common.runtime.TVMTestResult; +import org.tron.common.runtime.TvmTestUtils; +import org.tron.common.runtime.vm.DataWord; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.store.DynamicPropertiesStore; +import org.tron.core.vm.config.ConfigLoader; +import org.tron.core.vm.program.Program.IllegalOperationException; +import org.tron.core.vm.program.Storage; +import org.tron.protos.Protocol; + +/** + * Real STATICCALL execution of the deployed TIP-2935 bytecode through the VM + * trace path. Complements {@link HistoryBlockHashIntegrationTest}, which only + * verifies that storage writes round-trip through {@code RepositoryImpl}. + * + *

Each test prepares storage and a {@code BlockCapsule} (which fixes the + * EVM {@code block.number}), invokes the deployed contract via + * {@code TvmTestUtils.triggerContract...}, and asserts the bytecode's + * documented branches: normal return, bootstrap zero, three revert paths, + * and PUSH0 not tripping {@code IllegalOperationException} under Shanghai. + */ +public class HistoryBlockHashVmTest extends BaseTest { + + static { + Args.setParam( + new String[]{"--output-directory", dbPath(), "--debug"}, + TestConstants.TEST_CONF); + } + + private static final byte[] OWNER = + Hex.decode("41abd4b9367799eaa3197fecb144eb71de1e049abc"); + private static final long FEE_LIMIT = 1_000_000_000L; + + @Before + public void init() { + // Some prior tests in the same Gradle JVM batch may flip ConfigLoader.disable + // to true, which would freeze VMConfig at whatever it last held. Reset so + // VMActuator picks up the DPS values we set below. + ConfigLoader.disable = false; + + DynamicPropertiesStore dps = chainBaseManager.getDynamicPropertiesStore(); + dps.saveAllowTvmConstantinople(1L); + dps.saveAllowTvmTransferTrc10(1L); + dps.saveAllowTvmSolidity059(1L); + dps.saveAllowTvmIstanbul(1L); + dps.saveAllowTvmLondon(1L); + dps.saveAllowTvmShangHai(1L); + dps.saveAllowTvmPrague(1L); + + AccountCapsule owner = new AccountCapsule( + ByteString.copyFrom(OWNER), Protocol.AccountType.Normal); + owner.setBalance(30_000_000_000_000L); + chainBaseManager.getAccountStore().put(OWNER, owner); + + HistoryBlockHashUtil.deploy(dbManager); + } + + @After + public void cleanup() { + // BaseTest shares the Spring context across @Test methods in this class, + // so reset every store we touched. + DynamicPropertiesStore dps = chainBaseManager.getDynamicPropertiesStore(); + dps.saveAllowTvmShangHai(0L); + dps.saveAllowTvmPrague(0L); + dps.saveBlockHashHistoryInstalled(0L); + + byte[] addr = HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS; + chainBaseManager.getCodeStore().delete(addr); + chainBaseManager.getContractStore().delete(addr); + chainBaseManager.getAccountStore().delete(addr); + + Storage storage = new Storage(addr, chainBaseManager.getStorageRowStore()); + for (long slot : new long[]{0L, 1L, 50L, 100L, 900L, 999L, 1000L}) { + storage.put(new DataWord(slot), DataWord.ZERO()); + } + storage.commit(); + } + + private void writeSlot(long slot, byte[] hash) { + Storage storage = new Storage( + HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS, + chainBaseManager.getStorageRowStore()); + storage.put(new DataWord(slot), new DataWord(hash)); + storage.commit(); + } + + private BlockCapsule blockAt(long num) { + BlockCapsule block = new BlockCapsule( + num, + Sha256Hash.wrap(new byte[32]), + System.currentTimeMillis(), + ByteString.copyFrom(new byte[21])); + // Skip the cpu-limit-ratio path that reads {@code trx.getRet(0)}; the + // bare TriggerSmartContract built by TvmTestUtils carries no Ret entry. + block.generatedByMyself = true; + return block; + } + + private static byte[] uint256(long n) { + return new DataWord(n).getData(); + } + + private TVMTestResult call(byte[] calldata, long currentBlockNum) throws Exception { + return TvmTestUtils.triggerContractAndReturnTvmTestResult( + OWNER, + HistoryBlockHashUtil.HISTORY_STORAGE_ADDRESS, + calldata, + 0L, + FEE_LIMIT, + dbManager, + blockAt(currentBlockNum)); + } + + /** + * Normal read: 32-byte calldata pointing at a block within the sliding + * window whose slot has been populated. The bytecode SLOAD-and-RETURNs + * the stored hash; this also exercises every PUSH0 in the read path. + */ + @Test + public void vmReturnsWrittenHashForBlockInWindow() throws Exception { + long current = 1000L; + long queried = current - 100L; + byte[] hash = new byte[32]; + Arrays.fill(hash, (byte) 0xab); + writeSlot(queried % HistoryBlockHashUtil.HISTORY_SERVE_WINDOW, hash); + + TVMTestResult result = call(uint256(queried), current); + + assertFalse("must not revert", result.getRuntime().getResult().isRevert()); + assertNull("must not throw", result.getRuntime().getResult().getException()); + byte[] hReturn = result.getRuntime().getResult().getHReturn(); + assertNotNull("must return data", hReturn); + assertArrayEquals(hash, hReturn); + } + + /** + * Bootstrap behavior: when a slot has not been written yet (pre-activation + * blocks within the sliding window, or fresh post-activation), the SLOAD + * returns 0 and the contract returns {@code bytes32(0)} — never reverts. + */ + @Test + public void vmReturnsZeroForUnwrittenSlot() throws Exception { + long current = 1000L; + long queried = current - 50L; + + TVMTestResult result = call(uint256(queried), current); + + assertFalse("must not revert", result.getRuntime().getResult().isRevert()); + assertNull("must not throw", result.getRuntime().getResult().getException()); + byte[] hReturn = result.getRuntime().getResult().getHReturn(); + assertNotNull("must return data", hReturn); + assertArrayEquals(new byte[32], hReturn); + } + + /** + * Out-of-range upper bound: querying the current block number (or any + * future block) is not serviceable — the bytecode reverts via 5f5ffd. + */ + @Test + public void vmRevertsForFutureBlock() throws Exception { + long current = 1000L; + long queried = current; + + TVMTestResult result = call(uint256(queried), current); + + assertTrue("must revert for queried >= current", + result.getRuntime().getResult().isRevert()); + } + + /** + * Out-of-range lower bound: once {@code queried + 8191 < current}, the slot + * has already been overwritten by a newer block in the ring buffer, so the + * bytecode reverts rather than returning a stale hash. + */ + @Test + public void vmRevertsForBlockOutsideWindow() throws Exception { + long current = 1000L + HistoryBlockHashUtil.HISTORY_SERVE_WINDOW + 1L; + long queried = 1000L; + + TVMTestResult result = call(uint256(queried), current); + + assertTrue("must revert for queried + window < current", + result.getRuntime().getResult().isRevert()); + } + + /** + * Calldata length guard: anything other than 32 bytes — including the + * 4-byte ABI selector shape Solidity callers might accidentally encode — + * reverts immediately at the {@code 60203603604257} preamble. + */ + @Test + public void vmRevertsForBadCalldataLength() throws Exception { + long current = 1000L; + byte[] shortCalldata = new byte[]{0x01, 0x02, 0x03, 0x04}; + + TVMTestResult result = call(shortCalldata, current); + + assertTrue("must revert for calldata.size != 32", + result.getRuntime().getResult().isRevert()); + } + + /** + * Shanghai gate: the bytecode contains four PUSH0 (0x5f) opcodes on the + * read path. With {@code ALLOW_TVM_SHANGHAI=1}, a normal call must reach + * the RETURN without {@code IllegalOperationException} — i.e., PUSH0 is + * recognized and not treated as an invalid opcode. + */ + @Test + public void vmExecutionDoesNotInvalidOpcodeUnderShanghai() throws Exception { + long current = 1000L; + long queried = current - 1L; + byte[] hash = new byte[32]; + Arrays.fill(hash, (byte) 0xcd); + writeSlot(queried % HistoryBlockHashUtil.HISTORY_SERVE_WINDOW, hash); + + TVMTestResult result = call(uint256(queried), current); + + Throwable ex = result.getRuntime().getResult().getException(); + assertFalse("PUSH0 must not be an invalid opcode under Shanghai", + ex instanceof IllegalOperationException); + assertFalse("normal read must not revert", + result.getRuntime().getResult().isRevert()); + } +} diff --git a/framework/src/test/java/org/tron/core/db/ManagerMockTest.java b/framework/src/test/java/org/tron/core/db/ManagerMockTest.java index 364b86c82b4..1e4b9a037ac 100644 --- a/framework/src/test/java/org/tron/core/db/ManagerMockTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerMockTest.java @@ -20,10 +20,14 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.junit.After; +import org.junit.Assert; import org.junit.Test; import org.mockito.MockedConstruction; import org.mockito.MockedStatic; @@ -32,8 +36,12 @@ import org.mockito.stubbing.Answer; import org.tron.common.cron.CronExpression; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.logsfilter.trigger.ContractLogTrigger; +import org.tron.common.logsfilter.trigger.ContractTrigger; import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.ProgramResult; +import org.tron.common.runtime.vm.LogInfo; import org.tron.common.utils.Sha256Hash; import org.tron.core.ChainBaseManager; import org.tron.core.capsule.BlockCapsule; @@ -438,4 +446,111 @@ public void testReOrgLogsFilter() throws Exception { privateMethod.invoke(dbManager); } + @Test + public void testPostContractTriggerProcessesSync() throws Exception { + Manager dbManager = spy(new Manager()); + Field eventLoadedField = Manager.class.getDeclaredField("eventPluginLoaded"); + eventLoadedField.setAccessible(true); + eventLoadedField.set(dbManager, true); + + ChainBaseManager cbm = mock(ChainBaseManager.class); + DynamicPropertiesStore dps = mock(DynamicPropertiesStore.class); + when(dps.getLatestSolidifiedBlockNum()).thenReturn(0L); + when(cbm.getDynamicPropertiesStore()).thenReturn(dps); + Field cbmField = Manager.class.getDeclaredField("chainBaseManager"); + cbmField.setAccessible(true); + cbmField.set(dbManager, cbm); + + EventPluginLoader mockLoader = mock(EventPluginLoader.class); + when(mockLoader.isContractLogTriggerEnable()).thenReturn(false); + when(mockLoader.isContractEventTriggerEnable()).thenReturn(false); + when(mockLoader.isSolidityLogTriggerEnable()).thenReturn(true); + when(mockLoader.isSolidityEventTriggerEnable()).thenReturn(false); + + Field instanceField = EventPluginLoader.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + EventPluginLoader original = (EventPluginLoader) instanceField.get(null); + instanceField.set(null, mockLoader); + + Args.getSolidityContractLogTriggerMap().clear(); + + try { + ContractLogTrigger trigger = new ContractLogTrigger(); + trigger.setBlockNumber(200L); + trigger.setTransactionId("tx-id"); + trigger.setContractAddress("0x01"); + trigger.setLogInfo(new LogInfo(new byte[0], new ArrayList<>(), new byte[0])); + + TransactionTrace traceMock = mock(TransactionTrace.class); + ProgramResult resultMock = mock(ProgramResult.class); + when(traceMock.getRuntimeResult()).thenReturn(resultMock); + List triggers = new ArrayList<>(); + triggers.add(trigger); + when(resultMock.getTriggerList()).thenReturn(triggers); + + Method method = Manager.class.getDeclaredMethod("postContractTrigger", + TransactionTrace.class, boolean.class, String.class); + method.setAccessible(true); + method.invoke(dbManager, traceMock, false, "blockhash"); + + Assert.assertNotNull( + "synchronous processTrigger should populate solidity log map", + Args.getSolidityContractLogTriggerMap().get(200L)); + } finally { + instanceField.set(null, original); + eventLoadedField.set(dbManager, false); + Args.getSolidityContractLogTriggerMap().clear(); + } + } + + @Test + public void testPostContractTriggerSwallowsThrowable() throws Exception { + Manager dbManager = spy(new Manager()); + Field eventLoadedField = Manager.class.getDeclaredField("eventPluginLoaded"); + eventLoadedField.setAccessible(true); + eventLoadedField.set(dbManager, true); + + ChainBaseManager cbm = mock(ChainBaseManager.class); + DynamicPropertiesStore dps = mock(DynamicPropertiesStore.class); + when(dps.getLatestSolidifiedBlockNum()).thenReturn(0L); + when(cbm.getDynamicPropertiesStore()).thenReturn(dps); + Field cbmField = Manager.class.getDeclaredField("chainBaseManager"); + cbmField.setAccessible(true); + cbmField.set(dbManager, cbm); + + EventPluginLoader mockLoader = mock(EventPluginLoader.class); + when(mockLoader.isContractLogTriggerEnable()).thenReturn(false); + when(mockLoader.isContractEventTriggerEnable()).thenReturn(false); + when(mockLoader.isSolidityLogTriggerEnable()).thenReturn(true); + when(mockLoader.isSolidityEventTriggerEnable()).thenReturn(false); + + Field instanceField = EventPluginLoader.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + EventPluginLoader original = (EventPluginLoader) instanceField.get(null); + instanceField.set(null, mockLoader); + + try { + // null logInfo → processTrigger throws NPE on logInfo.getTopics() + ContractLogTrigger trigger = new ContractLogTrigger(); + trigger.setBlockNumber(300L); + trigger.setTransactionId("tx-id"); + trigger.setContractAddress("0x01"); + + TransactionTrace traceMock = mock(TransactionTrace.class); + ProgramResult resultMock = mock(ProgramResult.class); + when(traceMock.getRuntimeResult()).thenReturn(resultMock); + when(resultMock.getTriggerList()) + .thenReturn(Collections.singletonList((ContractTrigger) trigger)); + + Method method = Manager.class.getDeclaredMethod("postContractTrigger", + TransactionTrace.class, boolean.class, String.class); + method.setAccessible(true); + // catch (Throwable) absorbs the NPE — invocation must complete normally + method.invoke(dbManager, traceMock, false, "blockhash"); + } finally { + instanceField.set(null, original); + eventLoadedField.set(dbManager, false); + } + } + } \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/db/ManagerTest.java b/framework/src/test/java/org/tron/core/db/ManagerTest.java index 639aa94593a..a0522417c59 100755 --- a/framework/src/test/java/org/tron/core/db/ManagerTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerTest.java @@ -3,7 +3,9 @@ import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import static org.tron.common.utils.Commons.adjustAssetBalanceV2; import static org.tron.common.utils.Commons.adjustTotalShieldedPoolValue; import static org.tron.common.utils.Commons.getExchangeStoreFinal; @@ -16,12 +18,15 @@ import com.google.common.collect.Sets; import com.google.protobuf.Any; import com.google.protobuf.ByteString; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -32,6 +37,8 @@ import org.tron.common.BaseMethodTest; import org.tron.common.TestConstants; import org.tron.common.crypto.ECKey; +import org.tron.common.logsfilter.EventPluginLoader; +import org.tron.common.logsfilter.trigger.ContractLogTrigger; import org.tron.common.runtime.RuntimeImpl; import org.tron.common.utils.ByteArray; import org.tron.common.utils.Commons; @@ -1339,6 +1346,79 @@ public void blockTrigger() { Assert.assertEquals(TronError.ErrCode.EVENT_SUBSCRIBE_ERROR, thrown.getErrCode()); } + @Test + public void testReOrgContractTriggerClearsMap() throws Exception { + ReflectUtils.setFieldValue(dbManager, "eventPluginLoaded", true); + EventPluginLoader mockLoader = mock(EventPluginLoader.class); + // Disable contract triggers so reOrgContractTrigger skips the old-block fetch + // branch and proceeds to clearSolidityContractTriggerCache(getHeadBlockNum()). + when(mockLoader.isContractEventTriggerEnable()).thenReturn(false); + when(mockLoader.isContractLogTriggerEnable()).thenReturn(false); + when(mockLoader.isSolidityLogTriggerEnable()).thenReturn(true); + when(mockLoader.isSolidityEventTriggerEnable()).thenReturn(false); + Field instanceField = EventPluginLoader.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + EventPluginLoader originalLoader = (EventPluginLoader) instanceField.get(null); + instanceField.set(null, mockLoader); + + long headBlockNum = dbManager.getHeadBlockNum(); + Args.getSolidityContractLogTriggerMap() + .computeIfAbsent(headBlockNum, k -> new LinkedBlockingQueue<>()) + .offer(new ContractLogTrigger()); + Args.getSolidityContractEventTriggerMap() + .computeIfAbsent(headBlockNum, k -> new LinkedBlockingQueue<>()) + .offer(new org.tron.common.logsfilter.trigger.ContractEventTrigger()); + + try { + Method method = Manager.class.getDeclaredMethod("reOrgContractTrigger"); + method.setAccessible(true); + method.invoke(dbManager); + + Assert.assertNull(Args.getSolidityContractLogTriggerMap().get(headBlockNum)); + Assert.assertNull(Args.getSolidityContractEventTriggerMap().get(headBlockNum)); + } finally { + instanceField.set(null, originalLoader); + ReflectUtils.setFieldValue(dbManager, "eventPluginLoaded", false); + Args.getSolidityContractLogTriggerMap().clear(); + Args.getSolidityContractEventTriggerMap().clear(); + } + } + + @Test + public void testClearSolidityContractTriggerCache() throws Exception { + long blockNum = 999L; + ReflectUtils.setFieldValue(dbManager, "eventPluginLoaded", true); + EventPluginLoader mockLoader = mock(EventPluginLoader.class); + when(mockLoader.isSolidityLogTriggerEnable()).thenReturn(true); + when(mockLoader.isSolidityEventTriggerEnable()).thenReturn(true); + Field instanceField = EventPluginLoader.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + EventPluginLoader originalLoader = (EventPluginLoader) instanceField.get(null); + instanceField.set(null, mockLoader); + + Args.getSolidityContractLogTriggerMap() + .computeIfAbsent(blockNum, k -> new LinkedBlockingQueue<>()) + .offer(new ContractLogTrigger()); + Args.getSolidityContractEventTriggerMap() + .computeIfAbsent(blockNum, k -> new LinkedBlockingQueue<>()); + Assert.assertFalse(Args.getSolidityContractLogTriggerMap().isEmpty()); + + try { + Method method = Manager.class.getDeclaredMethod("clearSolidityContractTriggerCache", + long.class); + method.setAccessible(true); + method.invoke(dbManager, blockNum); + + Assert.assertNull(Args.getSolidityContractLogTriggerMap().get(blockNum)); + Assert.assertNull(Args.getSolidityContractEventTriggerMap().get(blockNum)); + } finally { + instanceField.set(null, originalLoader); + ReflectUtils.setFieldValue(dbManager, "eventPluginLoaded", false); + Args.getSolidityContractLogTriggerMap().clear(); + Args.getSolidityContractEventTriggerMap().clear(); + } + } + public void adjustBalance(AccountStore accountStore, byte[] accountAddress, long amount) throws BalanceInsufficientException { Commons.adjustBalance(accountStore, accountAddress, amount, diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index e162ee917e9..9a1641f9e45 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -6,7 +6,6 @@ import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseBlockNumber; import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.parseBlockTag; -import com.alibaba.fastjson.JSON; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.protobuf.ByteString; @@ -59,6 +58,9 @@ import org.tron.core.services.jsonrpc.types.BlockResult; import org.tron.core.services.jsonrpc.types.TransactionReceipt; import org.tron.core.services.jsonrpc.types.TransactionResult; +import org.tron.json.JSON; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; import org.tron.protos.Protocol; import org.tron.protos.Protocol.Transaction.Contract.ContractType; import org.tron.protos.contract.BalanceContract.TransferContract; @@ -1339,6 +1341,53 @@ public void testGetBlockReceipts() { Assert.assertEquals("invalid block number", overflowReceiptsEx.getMessage()); } + @Test + public void testBuildTransactionTransfer() { + // End-to-end smoke test for the buildTransaction JSON-RPC path: + // posts through fullNodeJsonRpcHttpService and asserts the response's + // `transaction` field is a structurally valid JSON object with the + // expected protobuf-derived fields (type, amount). Coverage was + // missing before; not specific to the fastjson→jackson migration. + fullNodeJsonRpcHttpService.start(); + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + JsonObject buildArgs = new JsonObject(); + buildArgs.addProperty("from", "0xabd4b9367799eaa3197fecb144eb71de1e049abc"); + buildArgs.addProperty("to", "0x548794500882809695a8a687866e76d4271a1abc"); + buildArgs.addProperty("value", "0x1f4"); + JsonArray params = new JsonArray(); + params.add(buildArgs); + JsonObject requestBody = new JsonObject(); + requestBody.addProperty("jsonrpc", "2.0"); + requestBody.addProperty("method", "buildTransaction"); + requestBody.add("params", params); + requestBody.addProperty("id", 1); + + HttpPost httpPost = new HttpPost("http://127.0.0.1:" + + CommonParameter.getInstance().getJsonRpcHttpFullNodePort() + "/jsonrpc"); + httpPost.addHeader("Content-Type", "application/json"); + httpPost.setEntity(new StringEntity(requestBody.toString())); + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + String resp = EntityUtils.toString(response.getEntity()); + JSONObject tx = JSON.parseObject(resp).getJSONObject("result") + .getJSONObject("transaction"); + Assert.assertNotNull("transaction must be a JSON object", tx); + Assert.assertNotNull(tx.getString("txID")); + Assert.assertNotNull(tx.getString("raw_data_hex")); + + JSONArray contracts = tx.getJSONObject("raw_data").getJSONArray("contract"); + Assert.assertEquals(1, contracts.size()); + JSONObject contract = contracts.getJSONObject(0); + Assert.assertEquals("TransferContract", contract.getString("type")); + Assert.assertEquals(500L, contract.getJSONObject("parameter") + .getJSONObject("value").getLongValue("amount")); + } + } catch (Exception e) { + Assert.fail(e.getMessage()); + } finally { + fullNodeJsonRpcHttpService.stop(); + } + } + @Test public void testWeb3ClientVersion() { try { diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/PbftDataSyncHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/PbftDataSyncHandlerTest.java index e5d242a6c4d..7f3ad8a71e2 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/PbftDataSyncHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/PbftDataSyncHandlerTest.java @@ -1,6 +1,6 @@ package org.tron.core.net.messagehandler; -import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.protobuf.ByteString; import java.lang.reflect.Field; import java.util.ArrayList; @@ -52,7 +52,7 @@ public void testProcessMessage() throws Exception { pbftDataSyncHandler.processPBFTCommitData(blockCapsule); Field field1 = PbftDataSyncHandler.class.getDeclaredField("pbftCommitMessageCache"); field1.setAccessible(true); - Map map = JSON.parseObject(JSON.toJSONString(field1.get(pbftDataSyncHandler)), Map.class); + Map map = new ObjectMapper().convertValue(field1.get(pbftDataSyncHandler), Map.class); Assert.assertFalse(map.containsKey(0)); } } diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java index db8aac00c60..c14f9a9c86a 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java @@ -3,12 +3,15 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; import lombok.Getter; import org.joda.time.DateTime; @@ -20,7 +23,10 @@ import org.tron.common.TestConstants; import org.tron.common.runtime.TvmTestUtils; import org.tron.common.utils.ByteArray; +import org.tron.core.ChainBaseManager; import org.tron.core.config.args.Args; +import org.tron.core.exception.P2pException; +import org.tron.core.exception.P2pException.TypeEnum; import org.tron.core.net.TronNetDelegate; import org.tron.core.net.message.adv.TransactionMessage; import org.tron.core.net.message.adv.TransactionsMessage; @@ -80,7 +86,6 @@ public void testProcessMessage() { transactionsMsgHandler.processMessage(peer, new TransactionsMessage(transactionList)); Assert.assertNull(advInvRequest.get(item)); //Thread.sleep(10); - transactionsMsgHandler.close(); BlockingQueue smartContractQueue = new LinkedBlockingQueue(2); smartContractQueue.offer(new TrxEvent(null, null)); @@ -132,6 +137,159 @@ public void testProcessMessage() { } } + @Test + public void testProcessMessageAfterClose() throws Exception { + TransactionsMsgHandler handler = new TransactionsMsgHandler(); + handler.init(); + handler.close(); + + PeerConnection peer = Mockito.mock(PeerConnection.class); + TransactionsMessage msg = Mockito.mock(TransactionsMessage.class); + + handler.processMessage(peer, msg); + + Mockito.verify(msg, Mockito.never()).getTransactions(); + Mockito.verifyNoInteractions(peer); + } + + @Test + public void testRejectedExecution() throws Exception { + TransactionsMsgHandler handler = new TransactionsMsgHandler(); + try { + ExecutorService mockPool = Mockito.mock(ExecutorService.class); + Mockito.when(mockPool.submit(Mockito.any(Runnable.class))) + .thenThrow(new RejectedExecutionException("pool closed")); + Field poolField = TransactionsMsgHandler.class.getDeclaredField("trxHandlePool"); + poolField.setAccessible(true); + poolField.set(handler, mockPool); + + PeerConnection peer = Mockito.mock(PeerConnection.class); + TransactionsMessage msg = buildTransferMessage(2); + stubAdvInvRequest(peer, msg); + // 2 transfer transactions, submit throws on the first → catch + break, only called once + handler.processMessage(peer, msg); + + Mockito.verify(mockPool, Mockito.times(1)).submit(Mockito.any(Runnable.class)); + } finally { + handler.close(); + } + } + + @Test + public void testCloseDuringProcessing() throws Exception { + TransactionsMsgHandler handler = new TransactionsMsgHandler(); + try { + Field closedField = TransactionsMsgHandler.class.getDeclaredField("isClosed"); + closedField.setAccessible(true); + + ExecutorService mockPool = Mockito.mock(ExecutorService.class); + // on the first submit, flip isClosed to true so the second iteration breaks + Mockito.when(mockPool.submit(Mockito.any(Runnable.class))).thenAnswer(inv -> { + closedField.set(handler, true); + return null; + }); + Field poolField = TransactionsMsgHandler.class.getDeclaredField("trxHandlePool"); + poolField.setAccessible(true); + poolField.set(handler, mockPool); + + PeerConnection peer = Mockito.mock(PeerConnection.class); + TransactionsMessage msg = buildTransferMessage(2); + stubAdvInvRequest(peer, msg); + handler.processMessage(peer, msg); + + Mockito.verify(mockPool, Mockito.times(1)).submit(Mockito.any(Runnable.class)); + } finally { + handler.close(); + } + } + + private TransactionsMessage buildTransferMessage(int count) { + List txs = new ArrayList<>(); + for (int i = 0; i < count; i++) { + BalanceContract.TransferContract tc = BalanceContract.TransferContract.newBuilder() + .setAmount(10 + i) + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString("121212a9cf"))) + .setToAddress(ByteString.copyFrom(ByteArray.fromHexString("232323a9cf"))) + .build(); + txs.add(Protocol.Transaction.newBuilder().setRawData( + Protocol.Transaction.raw.newBuilder() + .setTimestamp(1_700_000_000_000L + i) + .setRefBlockNum(1) + .addContract(Protocol.Transaction.Contract.newBuilder() + .setType(Protocol.Transaction.Contract.ContractType.TransferContract) + .setParameter(Any.pack(tc)).build()).build()) + .build()); + } + return new TransactionsMessage(txs); + } + + private void stubAdvInvRequest(PeerConnection peer, TransactionsMessage msg) { + Map advInvRequest = new ConcurrentHashMap<>(); + for (Protocol.Transaction trx : msg.getTransactions().getTransactionsList()) { + Item item = new Item(new TransactionMessage(trx).getMessageId(), + Protocol.Inventory.InventoryType.TRX); + advInvRequest.put(item, 0L); + } + Mockito.when(peer.getAdvInvRequest()).thenReturn(advInvRequest); + } + + @Test + public void testHandleTransaction() throws Exception { + TransactionsMsgHandler handler = new TransactionsMsgHandler(); + try { + TronNetDelegate tronNetDelegate = Mockito.mock(TronNetDelegate.class); + AdvService advService = Mockito.mock(AdvService.class); + ChainBaseManager chainBaseManager = Mockito.mock(ChainBaseManager.class); + + Field f1 = TransactionsMsgHandler.class.getDeclaredField("tronNetDelegate"); + f1.setAccessible(true); + f1.set(handler, tronNetDelegate); + Field f2 = TransactionsMsgHandler.class.getDeclaredField("advService"); + f2.setAccessible(true); + f2.set(handler, advService); + Field f3 = TransactionsMsgHandler.class.getDeclaredField("chainBaseManager"); + f3.setAccessible(true); + f3.set(handler, chainBaseManager); + + PeerConnection peer = Mockito.mock(PeerConnection.class); + + BalanceContract.TransferContract tc = BalanceContract.TransferContract.newBuilder() + .setAmount(10) + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString("121212a9cf"))) + .setToAddress(ByteString.copyFrom(ByteArray.fromHexString("232323a9cf"))) + .build(); + long now = System.currentTimeMillis(); + Protocol.Transaction trx = Protocol.Transaction.newBuilder().setRawData( + Protocol.Transaction.raw.newBuilder() + .setTimestamp(now) + .setExpiration(now + 60_000) + .setRefBlockNum(1) + .addContract(Protocol.Transaction.Contract.newBuilder() + .setType(Protocol.Transaction.Contract.ContractType.TransferContract) + .setParameter(Any.pack(tc)).build()).build()) + .build(); + TransactionMessage trxMsg = new TransactionMessage(trx); + + Method handleTx = TransactionsMsgHandler.class.getDeclaredMethod( + "handleTransaction", PeerConnection.class, TransactionMessage.class); + handleTx.setAccessible(true); + + // happy path → push and broadcast + Mockito.when(chainBaseManager.getNextBlockSlotTime()).thenReturn(now); + handleTx.invoke(handler, peer, trxMsg); + Mockito.verify(advService).broadcast(trxMsg); + + // P2pException BAD_TRX → disconnect + Mockito.doThrow(new P2pException(TypeEnum.BAD_TRX, "bad")) + .when(tronNetDelegate).pushTransaction(Mockito.any()); + handleTx.invoke(handler, peer, trxMsg); + Mockito.verify(peer).setBadPeer(true); + Mockito.verify(peer).disconnect(Protocol.ReasonCode.BAD_TX); + } finally { + handler.close(); + } + } + class TrxEvent { @Getter diff --git a/framework/src/test/java/org/tron/core/net/services/SyncServiceTest.java b/framework/src/test/java/org/tron/core/net/services/SyncServiceTest.java index 45ecbe866ab..2366aab3ab5 100644 --- a/framework/src/test/java/org/tron/core/net/services/SyncServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/SyncServiceTest.java @@ -23,6 +23,7 @@ import org.tron.core.net.peer.PeerManager; import org.tron.core.net.peer.TronState; import org.tron.core.net.service.sync.SyncService; +import org.tron.core.net.service.sync.UnparsedBlock; import org.tron.p2p.connection.Channel; import org.tron.protos.Protocol; @@ -98,12 +99,22 @@ public void testProcessBlock() { ReflectUtils.setFieldValue(c1, "inetSocketAddress", inetSocketAddress); ReflectUtils.setFieldValue(c1, "inetAddress", inetSocketAddress.getAddress()); peer.setChannel(c1); - service.processBlock(peer, - new BlockMessage(new BlockCapsule(Protocol.Block.newBuilder().build()))); + + BlockCapsule blockCapsule = new BlockCapsule(Protocol.Block.newBuilder().build()); + BlockMessage blockMessage = new BlockMessage(blockCapsule); + service.processBlock(peer, blockMessage); + boolean fetchFlag = (boolean) ReflectUtils.getFieldObject(service, "fetchFlag"); boolean handleFlag = (boolean) ReflectUtils.getFieldObject(service, "handleFlag"); Assert.assertTrue(fetchFlag); Assert.assertTrue(handleFlag); + + Map blockJustReceived = + (Map) + ReflectUtils.getFieldObject(service, "blockJustReceived"); + Assert.assertEquals(1, blockJustReceived.size()); + UnparsedBlock stored = blockJustReceived.keySet().iterator().next(); + Assert.assertEquals(blockMessage.getBlockId(), stored.getBlockId()); } @Test @@ -169,6 +180,46 @@ public void testStartFetchSyncBlock() throws Exception { peer.getSyncBlockRequested().remove(blockId); method.invoke(service); Assert.assertTrue(peer.getSyncBlockRequested().get(blockId) == null); + + // reset maxRequestedBlockNum to 0 + Field maxRequestedBlockNumField = service.getClass().getDeclaredField("maxRequestedBlockNum"); + maxRequestedBlockNumField.setAccessible(true); + maxRequestedBlockNumField.set(service, 0L); + + Map blockWaitToProcess = + (Map) + ReflectUtils.getFieldObject(service, "blockWaitToProcess"); + + // target block has num=1, above maxRequestedBlockNum=0 so it can be throttled + BlockCapsule.BlockId highBlockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 1); + peer.getSyncBlockToFetch().clear(); + peer.getSyncBlockToFetch().add(highBlockId); + peer.getSyncBlockRequested().clear(); + requestBlockIds.invalidateAll(); + + // fill blockWaitToProcess to reach maxPendingBlockSize (default 500) + int maxPendingBlockSize = (int) ReflectUtils.getFieldObject(service, "maxPendingBlockSize"); + for (int i = 0; i < maxPendingBlockSize; i++) { + BlockCapsule.BlockId fillId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 10000 + i); + blockWaitToProcess.put(new UnparsedBlock(fillId, new byte[0]), peer); + } + method.invoke(service); + // highBlockId must NOT be requested: remainNum <= 0 and num > maxRequestedBlockNum + Assert.assertNull(peer.getSyncBlockRequested().get(highBlockId)); + + // Symmetric retry-exemption case: budget still saturated, but the target block's num + // is below maxRequestedBlockNum, so it must still be requested (deadlock-avoidance + // retry path — guards an explicit invariant of the throttling design). + maxRequestedBlockNumField.set(service, 100L); + BlockCapsule.BlockId retryBlockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 50); + peer.getSyncBlockToFetch().clear(); + peer.getSyncBlockToFetch().add(retryBlockId); + peer.getSyncBlockRequested().clear(); + requestBlockIds.invalidateAll(); + method.invoke(service); + // retryBlockId MUST be requested: remainNum <= 0 but num=50 <= maxRequestedBlockNum=100 + Assert.assertNotNull(peer.getSyncBlockRequested().get(retryBlockId)); + blockWaitToProcess.clear(); } @Test @@ -181,24 +232,19 @@ public void testHandleSyncBlock() throws Exception { Method method = service.getClass().getDeclaredMethod("handleSyncBlock"); method.setAccessible(true); - Map blockJustReceived = - (Map) + Map blockJustReceived = + (Map) ReflectUtils.getFieldObject(service, "blockJustReceived"); - Protocol.BlockHeader.raw.Builder blockHeaderRawBuild = Protocol.BlockHeader.raw.newBuilder(); - Protocol.BlockHeader.raw blockHeaderRaw = blockHeaderRawBuild + + Protocol.BlockHeader.raw blockHeaderRaw = Protocol.BlockHeader.raw.newBuilder() .setNumber(100000) .build(); - - // block header - Protocol.BlockHeader.Builder blockHeaderBuild = Protocol.BlockHeader.newBuilder(); - Protocol.BlockHeader blockHeader = blockHeaderBuild.setRawData(blockHeaderRaw).build(); - - BlockCapsule blockCapsule = new BlockCapsule(Protocol.Block.newBuilder() - .setBlockHeader(blockHeader).build()); - + Protocol.BlockHeader blockHeader = Protocol.BlockHeader.newBuilder() + .setRawData(blockHeaderRaw).build(); + BlockCapsule blockCapsule = new BlockCapsule( + Protocol.Block.newBuilder().setBlockHeader(blockHeader).build()); BlockCapsule.BlockId blockId = blockCapsule.getBlockId(); - InetSocketAddress a1 = new InetSocketAddress("127.0.0.1", 10001); Channel c1 = mock(Channel.class); Mockito.when(c1.getInetSocketAddress()).thenReturn(a1); @@ -206,14 +252,14 @@ public void testHandleSyncBlock() throws Exception { PeerManager.add(ctx, c1); peer = PeerManager.getPeers().get(0); - blockJustReceived.put(new BlockMessage(blockCapsule), peer); + UnparsedBlock unparsedBlock = new UnparsedBlock(blockId, blockCapsule.getData()); + blockJustReceived.put(unparsedBlock, peer); peer.getSyncBlockToFetch().add(blockId); Cache requestBlockIds = - (Cache) - ReflectUtils.getFieldObject(service, "requestBlockIds"); - + (Cache) + ReflectUtils.getFieldObject(service, "requestBlockIds"); requestBlockIds.put(blockId, peer); method.invoke(service); diff --git a/framework/src/test/java/org/tron/core/pbft/PbftApiTest.java b/framework/src/test/java/org/tron/core/pbft/PbftApiTest.java index 48af7408b6f..36253333a4e 100755 --- a/framework/src/test/java/org/tron/core/pbft/PbftApiTest.java +++ b/framework/src/test/java/org/tron/core/pbft/PbftApiTest.java @@ -1,7 +1,5 @@ package org.tron.core.pbft; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import java.io.IOException; import java.util.Objects; @@ -29,6 +27,8 @@ import org.tron.core.db2.ISession; import org.tron.core.services.interfaceOnPBFT.http.PBFT.HttpApiOnPBFTService; import org.tron.core.store.DynamicPropertiesStore; +import org.tron.json.JSON; +import org.tron.json.JSONObject; @Slf4j public class PbftApiTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java b/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java index 10e69258406..ce0a09da94a 100644 --- a/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java @@ -1,6 +1,5 @@ package org.tron.core.services; -import com.alibaba.fastjson.JSON; import com.google.protobuf.ByteString; import java.net.InetSocketAddress; import javax.annotation.Resource; @@ -21,6 +20,7 @@ import org.tron.core.net.P2pEventHandlerImpl; import org.tron.core.net.TronNetService; import org.tron.core.net.peer.PeerManager; +import org.tron.json.JSON; import org.tron.p2p.P2pConfig; import org.tron.p2p.connection.Channel; import org.tron.program.Version; diff --git a/framework/src/test/java/org/tron/core/services/http/AccountPermissionUpdateServletTest.java b/framework/src/test/java/org/tron/core/services/http/AccountPermissionUpdateServletTest.java new file mode 100644 index 00000000000..e93c41397a2 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/AccountPermissionUpdateServletTest.java @@ -0,0 +1,60 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.AccountContract; + +public class AccountPermissionUpdateServletTest extends BaseHttpTest { + + private AccountPermissionUpdateServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new AccountPermissionUpdateServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(AccountContract.AccountPermissionUpdateContract.class), + eq(Protocol.Transaction.Contract.ContractType.AccountPermissionUpdateContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testAccountPermissionUpdate() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"owner\": {\"type\": 0, \"permission_name\": \"owner\", \"threshold\": 1," + + " \"keys\": [{\"address\": \"" + ownerAddr + "\"," + + " \"weight\": 1}]}," + + "\"actives\": [{\"type\": 2, \"permission_name\": \"active\", \"threshold\": 1," + + " \"operations\": \"7fff1fc0033e0000000000000000000000000000000000000000000000000000\"," + + " \"keys\": [{\"address\": \"" + ownerAddr + "\"," + + " \"weight\": 1}]}]" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof AccountContract.AccountPermissionUpdateContract + && addressEquals(((AccountContract.AccountPermissionUpdateContract) c) + .getOwnerAddress(), ownerAddr) + && ((AccountContract.AccountPermissionUpdateContract) c) + .getOwner().getThreshold() == 1 + && ((AccountContract.AccountPermissionUpdateContract) c) + .getActivesCount() == 1), + eq(Protocol.Transaction.Contract.ContractType.AccountPermissionUpdateContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/BaseHttpTest.java b/framework/src/test/java/org/tron/core/services/http/BaseHttpTest.java new file mode 100644 index 00000000000..47710a8ca93 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/BaseHttpTest.java @@ -0,0 +1,127 @@ +package org.tron.core.services.http; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.protobuf.ByteString; +import java.lang.reflect.Field; +import javax.servlet.http.HttpServlet; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.TestConstants; +import org.tron.common.utils.ByteArray; +import org.tron.core.Wallet; +import org.tron.core.config.args.Args; +import org.tron.protos.Protocol.Transaction; + +/** + * Base class for HTTP servlet unit tests. + * + *

Manages {@link Args} lifecycle so that + * {@link org.tron.common.parameter.CommonParameter} + * (e.g. {@code maxMessageSize}) is properly initialised from + * {@code config-test.conf} before any servlet touches it, and + * cleaned up after the test class finishes. + */ +public abstract class BaseHttpTest { + + protected static final Transaction MINIMAL_TX = Transaction.newBuilder() + .setRawData(Transaction.raw.newBuilder().addContract(Transaction.Contract.newBuilder())) + .build(); + + @Mock + protected Wallet wallet; + private AutoCloseable closeable; + + @BeforeClass + public static void initArgs() { + Args.setParam(new String[]{}, TestConstants.TEST_CONF); + } + + @AfterClass + public static void clearArgs() { + Args.clearParam(); + } + + @Before + public void initMocks() throws Exception { + closeable = MockitoAnnotations.openMocks(this); + setUpMocks(); + } + + @After + public void closeMocks() throws Exception { + if (closeable != null) { + closeable.close(); + closeable = null; + } + } + + /** + * Override to configure mocks and inject the wallet into the servlet. + */ + protected abstract void setUpMocks() throws Exception; + + /** + * Injects the wallet mock into the servlet's private {@code wallet} field. + */ + protected void injectWallet(HttpServlet servlet) throws Exception { + Field f = servlet.getClass().getDeclaredField("wallet"); + f.setAccessible(true); + f.set(servlet, wallet); + } + + /** + * Creates a POST request with JSON body. + */ + protected static MockHttpServletRequest postRequest(String json) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + request.setContentType("application/json"); + request.setContent(json.getBytes(UTF_8)); + request.setCharacterEncoding(UTF_8.name()); + return request; + } + + /** + * Creates a GET request with optional query parameters (key, value pairs). + */ + protected static MockHttpServletRequest getRequest(String... params) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("GET"); + for (int i = 0; i < params.length - 1; i += 2) { + request.addParameter(params[i], params[i + 1]); + } + return request; + } + + protected static MockHttpServletResponse newResponse() { + return new MockHttpServletResponse(); + } + + /** + * Checks if a protobuf ByteString field matches the expected hex address. + */ + protected static boolean addressEquals(ByteString actual, String expectedHex) { + return ByteArray.toHexString(actual.toByteArray()).equals(expectedHex); + } + + /** + * Asserts that the servlet response represents a valid transaction: + * no error, contains txID and raw_data. + */ + protected static void assertTransactionResponse(MockHttpServletResponse response) + throws Exception { + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertTrue("Should contain txID", content.contains("txID")); + assertTrue("Should contain raw_data", content.contains("\"raw_data\"")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/CancelAllUnfreezeV2ServletTest.java b/framework/src/test/java/org/tron/core/services/http/CancelAllUnfreezeV2ServletTest.java new file mode 100644 index 00000000000..59a3f02256c --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/CancelAllUnfreezeV2ServletTest.java @@ -0,0 +1,47 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class CancelAllUnfreezeV2ServletTest extends BaseHttpTest { + + private CancelAllUnfreezeV2Servlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new CancelAllUnfreezeV2Servlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(BalanceContract.CancelAllUnfreezeV2Contract.class), + eq(Protocol.Transaction.Contract.ContractType.CancelAllUnfreezeV2Contract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testCancelAllUnfreezeV2() throws Exception { + String jsonParam = "{\"owner_address\": \"" + ownerAddr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.CancelAllUnfreezeV2Contract + && addressEquals(((BalanceContract.CancelAllUnfreezeV2Contract) c) + .getOwnerAddress(), ownerAddr)), + eq(Protocol.Transaction.Contract.ContractType.CancelAllUnfreezeV2Contract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ClearABIServletTest.java b/framework/src/test/java/org/tron/core/services/http/ClearABIServletTest.java index 48b538380c2..9d75226aa42 100644 --- a/framework/src/test/java/org/tron/core/services/http/ClearABIServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/ClearABIServletTest.java @@ -5,14 +5,10 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; - import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import org.apache.http.client.methods.HttpPost; - import org.junit.Assert; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; @@ -22,6 +18,7 @@ import org.tron.common.utils.ByteArray; import org.tron.core.capsule.ContractCapsule; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; import org.tron.protos.contract.SmartContractOuterClass; public class ClearABIServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/CreateAccountServletTest.java b/framework/src/test/java/org/tron/core/services/http/CreateAccountServletTest.java index 1c34d7b8a92..c9d6a4f2a63 100644 --- a/framework/src/test/java/org/tron/core/services/http/CreateAccountServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/CreateAccountServletTest.java @@ -4,12 +4,9 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; - import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import org.apache.http.client.methods.HttpPost; import org.junit.Assert; import org.junit.Before; @@ -21,6 +18,7 @@ import org.tron.common.utils.ByteArray; import org.tron.core.capsule.AccountCapsule; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; import org.tron.protos.Protocol; diff --git a/framework/src/test/java/org/tron/core/services/http/CreateAssetIssueServletTest.java b/framework/src/test/java/org/tron/core/services/http/CreateAssetIssueServletTest.java index 52ada9dd0d0..9a53814ea11 100644 --- a/framework/src/test/java/org/tron/core/services/http/CreateAssetIssueServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/CreateAssetIssueServletTest.java @@ -4,12 +4,9 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; - import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import org.apache.http.client.methods.HttpPost; import org.junit.Assert; import org.junit.Before; @@ -21,6 +18,7 @@ import org.tron.common.utils.ByteArray; import org.tron.core.capsule.AccountCapsule; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; import org.tron.protos.Protocol; public class CreateAssetIssueServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/CreateSpendAuthSigServletTest.java b/framework/src/test/java/org/tron/core/services/http/CreateSpendAuthSigServletTest.java index d3ebf26a261..2253fecfb52 100644 --- a/framework/src/test/java/org/tron/core/services/http/CreateSpendAuthSigServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/CreateSpendAuthSigServletTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.apache.http.client.methods.HttpPost; @@ -17,6 +16,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class CreateSpendAuthSigServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/CreateWitnessServletTest.java b/framework/src/test/java/org/tron/core/services/http/CreateWitnessServletTest.java index 62908c1563f..bd8145fed7b 100644 --- a/framework/src/test/java/org/tron/core/services/http/CreateWitnessServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/CreateWitnessServletTest.java @@ -4,12 +4,9 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; - import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import org.apache.http.client.methods.HttpPost; import org.junit.Assert; import org.junit.Before; @@ -24,6 +21,7 @@ import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; import org.tron.protos.Protocol; public class CreateWitnessServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/DelegateResourceServletTest.java b/framework/src/test/java/org/tron/core/services/http/DelegateResourceServletTest.java new file mode 100644 index 00000000000..07ca6750e31 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/DelegateResourceServletTest.java @@ -0,0 +1,59 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; +import org.tron.protos.contract.Common.ResourceCode; + +public class DelegateResourceServletTest extends BaseHttpTest { + + private DelegateResourceServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + private final String receiverAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new DelegateResourceServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(BalanceContract.DelegateResourceContract.class), + eq(Protocol.Transaction.Contract.ContractType.DelegateResourceContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testDelegateResource() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"receiver_address\": \"" + receiverAddr + "\"," + + "\"balance\": 1000000," + + "\"resource\": \"ENERGY\"" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.DelegateResourceContract + && addressEquals(((BalanceContract.DelegateResourceContract) c) + .getOwnerAddress(), ownerAddr) + && addressEquals(((BalanceContract.DelegateResourceContract) c) + .getReceiverAddress(), receiverAddr) + && ((BalanceContract.DelegateResourceContract) c).getBalance() == 1000000 + && ((BalanceContract.DelegateResourceContract) c) + .getResource() == ResourceCode.ENERGY), + eq(Protocol.Transaction.Contract.ContractType.DelegateResourceContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/DeployContractServletTest.java b/framework/src/test/java/org/tron/core/services/http/DeployContractServletTest.java new file mode 100644 index 00000000000..83fb64880c3 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/DeployContractServletTest.java @@ -0,0 +1,58 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract; + +public class DeployContractServletTest extends BaseHttpTest { + + private DeployContractServlet servlet; + + @Override + protected void setUpMocks() throws Exception { + servlet = new DeployContractServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(), eq(Protocol.Transaction.Contract.ContractType.CreateSmartContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testDeployContract() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"4199357684BC659F5166046B56C95A0E99F1265CD1\"," + + "\"name\": \"TestContract\"," + + "\"abi\": \"[{\\\"inputs\\\":[],\\\"name\\\":\\\"test\\\"," + + "\\\"outputs\\\":[],\\\"type\\\":\\\"function\\\"}]\"," + + "\"bytecode\": \"608060405234801561001057600080fd5b50\"," + + "\"fee_limit\": 1000000000," + + "\"call_value\": 0," + + "\"consume_user_resource_percent\": 100," + + "\"origin_energy_limit\": 10000000" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + assertEquals(200, response.getStatus()); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof CreateSmartContract + && addressEquals(((CreateSmartContract) c).getOwnerAddress(), + "4199357684bc659f5166046b56c95a0e99f1265cd1") + && ((CreateSmartContract) c).getNewContract().getName().equals("TestContract") + && ((CreateSmartContract) c).getNewContract() + .getOriginEnergyLimit() == 10000000), + eq(Protocol.Transaction.Contract.ContractType.CreateSmartContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ExchangeCreateServletTest.java b/framework/src/test/java/org/tron/core/services/http/ExchangeCreateServletTest.java new file mode 100644 index 00000000000..11840a895bd --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ExchangeCreateServletTest.java @@ -0,0 +1,58 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol.Transaction.Contract.ContractType; +import org.tron.protos.contract.ExchangeContract.ExchangeCreateContract; + +public class ExchangeCreateServletTest extends BaseHttpTest { + + private ExchangeCreateServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ExchangeCreateServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(ExchangeCreateContract.class), eq(ContractType.ExchangeCreateContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testExchangeCreate() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"first_token_id\": \"5f\"," + + "\"first_token_balance\": 100," + + "\"second_token_id\": \"61\"," + + "\"second_token_balance\": 200" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof ExchangeCreateContract + && addressEquals(((ExchangeCreateContract) c) + .getOwnerAddress(), ownerAddr) + && ((ExchangeCreateContract) c).getFirstTokenBalance() == 100 + && ((ExchangeCreateContract) c).getSecondTokenBalance() == 200 + && ((ExchangeCreateContract) c) + .getFirstTokenId().toStringUtf8().equals("_") + && ((ExchangeCreateContract) c) + .getSecondTokenId().toStringUtf8().equals("a")), + eq(ContractType.ExchangeCreateContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ExchangeInjectServletTest.java b/framework/src/test/java/org/tron/core/services/http/ExchangeInjectServletTest.java new file mode 100644 index 00000000000..f2f661732d2 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ExchangeInjectServletTest.java @@ -0,0 +1,56 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.ExchangeContract; + +public class ExchangeInjectServletTest extends BaseHttpTest { + + private ExchangeInjectServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ExchangeInjectServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(ExchangeContract.ExchangeInjectContract.class), + eq(Protocol.Transaction.Contract.ContractType.ExchangeInjectContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testExchangeInject() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"exchange_id\": 1," + + "\"token_id\": \"5f\"," + + "\"quant\": 100" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof ExchangeContract.ExchangeInjectContract + && addressEquals(((ExchangeContract.ExchangeInjectContract) c) + .getOwnerAddress(), ownerAddr) + && ((ExchangeContract.ExchangeInjectContract) c).getExchangeId() == 1 + && ((ExchangeContract.ExchangeInjectContract) c).getQuant() == 100 + && ((ExchangeContract.ExchangeInjectContract) c) + .getTokenId().toStringUtf8().equals("_")), + eq(Protocol.Transaction.Contract.ContractType.ExchangeInjectContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ExchangeTransactionServletTest.java b/framework/src/test/java/org/tron/core/services/http/ExchangeTransactionServletTest.java new file mode 100644 index 00000000000..6e986288b3c --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ExchangeTransactionServletTest.java @@ -0,0 +1,58 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.ExchangeContract; + +public class ExchangeTransactionServletTest extends BaseHttpTest { + + private ExchangeTransactionServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ExchangeTransactionServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(ExchangeContract.ExchangeTransactionContract.class), + eq(Protocol.Transaction.Contract.ContractType.ExchangeTransactionContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testExchangeTransaction() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"exchange_id\": 1," + + "\"token_id\": \"5f\"," + + "\"quant\": 100," + + "\"expected\": 10" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof ExchangeContract.ExchangeTransactionContract + && addressEquals(((ExchangeContract.ExchangeTransactionContract) c) + .getOwnerAddress(), ownerAddr) + && ((ExchangeContract.ExchangeTransactionContract) c).getExchangeId() == 1 + && ((ExchangeContract.ExchangeTransactionContract) c).getQuant() == 100 + && ((ExchangeContract.ExchangeTransactionContract) c).getExpected() == 10 + && ((ExchangeContract.ExchangeTransactionContract) c) + .getTokenId().toStringUtf8().equals("_")), + eq(Protocol.Transaction.Contract.ContractType.ExchangeTransactionContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ExchangeWithdrawServletTest.java b/framework/src/test/java/org/tron/core/services/http/ExchangeWithdrawServletTest.java new file mode 100644 index 00000000000..b1147b819dd --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ExchangeWithdrawServletTest.java @@ -0,0 +1,56 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.ExchangeContract; + +public class ExchangeWithdrawServletTest extends BaseHttpTest { + + private ExchangeWithdrawServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ExchangeWithdrawServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(ExchangeContract.ExchangeWithdrawContract.class), + eq(Protocol.Transaction.Contract.ContractType.ExchangeWithdrawContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testExchangeWithdraw() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"exchange_id\": 1," + + "\"token_id\": \"5f\"," + + "\"quant\": 50" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof ExchangeContract.ExchangeWithdrawContract + && addressEquals(((ExchangeContract.ExchangeWithdrawContract) c) + .getOwnerAddress(), ownerAddr) + && ((ExchangeContract.ExchangeWithdrawContract) c).getExchangeId() == 1 + && ((ExchangeContract.ExchangeWithdrawContract) c).getQuant() == 50 + && ((ExchangeContract.ExchangeWithdrawContract) c) + .getTokenId().toStringUtf8().equals("_")), + eq(Protocol.Transaction.Contract.ContractType.ExchangeWithdrawContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/FreezeBalanceServletTest.java b/framework/src/test/java/org/tron/core/services/http/FreezeBalanceServletTest.java new file mode 100644 index 00000000000..9d56381f9a8 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/FreezeBalanceServletTest.java @@ -0,0 +1,53 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class FreezeBalanceServletTest extends BaseHttpTest { + + private FreezeBalanceServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new FreezeBalanceServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(BalanceContract.FreezeBalanceContract.class), + eq(Protocol.Transaction.Contract.ContractType.FreezeBalanceContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testFreezeBalance() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"frozen_balance\": 1000000," + + "\"frozen_duration\": 3" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.FreezeBalanceContract + && addressEquals(((BalanceContract.FreezeBalanceContract) c) + .getOwnerAddress(), ownerAddr) + && ((BalanceContract.FreezeBalanceContract) c).getFrozenBalance() == 1000000 + && ((BalanceContract.FreezeBalanceContract) c).getFrozenDuration() == 3), + eq(Protocol.Transaction.Contract.ContractType.FreezeBalanceContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/FreezeBalanceV2ServletTest.java b/framework/src/test/java/org/tron/core/services/http/FreezeBalanceV2ServletTest.java new file mode 100644 index 00000000000..414054501e6 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/FreezeBalanceV2ServletTest.java @@ -0,0 +1,55 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; +import org.tron.protos.contract.Common.ResourceCode; + +public class FreezeBalanceV2ServletTest extends BaseHttpTest { + + private FreezeBalanceV2Servlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new FreezeBalanceV2Servlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(BalanceContract.FreezeBalanceV2Contract.class), + eq(Protocol.Transaction.Contract.ContractType.FreezeBalanceV2Contract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testFreezeBalanceV2() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"frozen_balance\": 1000000," + + "\"resource\": \"ENERGY\"" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.FreezeBalanceV2Contract + && addressEquals(((BalanceContract.FreezeBalanceV2Contract) c) + .getOwnerAddress(), ownerAddr) + && ((BalanceContract.FreezeBalanceV2Contract) c).getFrozenBalance() == 1000000 + && ((BalanceContract.FreezeBalanceV2Contract) c) + .getResource() == ResourceCode.ENERGY), + eq(Protocol.Transaction.Contract.ContractType.FreezeBalanceV2Contract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetAccountResourceServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetAccountResourceServletTest.java new file mode 100644 index 00000000000..cc5ed97b94b --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetAccountResourceServletTest.java @@ -0,0 +1,55 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.api.GrpcAPI.AccountResourceMessage; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; + +public class GetAccountResourceServletTest extends BaseHttpTest { + + private GetAccountResourceServlet servlet; + private final String addrStr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetAccountResourceServlet(); + injectWallet(servlet); + when(wallet.getAccountResource(any())) + .thenReturn(AccountResourceMessage.newBuilder().setFreeNetUsed(1L).build()); + } + + @Test + public void testGetAccountResourcePost() throws Exception { + String jsonParam = "{\"address\": \"" + addrStr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).getAccountResource(eq(ByteString.copyFrom(ByteArray.fromHexString(addrStr)))); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertTrue("Should contain freeNetUsed", content.contains("freeNetUsed")); + } + + @Test + public void testGetAccountResourceGet() throws Exception { + MockHttpServletRequest request = getRequest("address", addrStr); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getAccountResource(eq(ByteString.copyFrom(ByteArray.fromHexString(addrStr)))); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertTrue("Should contain freeNetUsed", content.contains("freeNetUsed")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetAccountServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetAccountServletTest.java new file mode 100644 index 00000000000..466917d0cd5 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetAccountServletTest.java @@ -0,0 +1,60 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.protos.Protocol.Account; + +public class GetAccountServletTest extends BaseHttpTest { + + private GetAccountServlet servlet; + private final byte[] address = new ECKey().getAddress(); + private final String addrStr = ByteArray.toHexString(address); + private final ByteString addr = ByteString.copyFrom(address); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetAccountServlet(); + injectWallet(servlet); + when(wallet.getAccount(any(Account.class))).thenReturn( + Account.newBuilder().setAddress(addr).build()); + } + + @Test + public void testGetAccountPost() throws Exception { + String jsonParam = "{\"address\": \"" + addrStr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + assertEquals(200, response.getStatus()); + verify(wallet).getAccount(argThat(req -> req != null && req.getAddress().equals(addr))); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertTrue("Should contain address", content.contains("address")); + } + + @Test + public void testGetAccountGet() throws Exception { + MockHttpServletRequest request = getRequest("address", addrStr); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + assertEquals(200, response.getStatus()); + verify(wallet).getAccount(argThat(req -> req != null && req.getAddress().equals(addr))); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertTrue("Should contain address", content.contains("address")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetAssetIssueByIdServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetAssetIssueByIdServletTest.java new file mode 100644 index 00000000000..b87331d6d61 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetAssetIssueByIdServletTest.java @@ -0,0 +1,70 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract; + +public class GetAssetIssueByIdServletTest extends BaseHttpTest { + + private GetAssetIssueByIdServlet servlet; + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetAssetIssueByIdServlet(); + injectWallet(servlet); + when(wallet.getAssetIssueById(any())).thenReturn(null); + } + + @Test + public void testGetAssetIssueByIdPost() throws Exception { + String jsonParam = "{\"value\": \"100001\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).getAssetIssueById(eq("100001")); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertEquals("{}" + System.lineSeparator(), content); + } + + @Test + public void testGetAssetIssueByIdGet() throws Exception { + MockHttpServletRequest request = getRequest("value", "100001"); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getAssetIssueById(eq("100001")); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertEquals("{}" + System.lineSeparator(), content); + } + + @Test + public void testPostReturnsAssetWhenFound() throws Exception { + AssetIssueContract asset = AssetIssueContract.newBuilder() + .setId("100001") + .setTotalSupply(1000L) + .build(); + when(wallet.getAssetIssueById(eq("100001"))).thenReturn(asset); + + MockHttpServletRequest request = postRequest("{\"value\": \"100001\"}"); + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertTrue("Should contain id", content.contains("100001")); + assertTrue("Should contain total_supply", content.contains("total_supply")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetAssetIssueByNameServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetAssetIssueByNameServletTest.java new file mode 100644 index 00000000000..ccfa3af2e56 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetAssetIssueByNameServletTest.java @@ -0,0 +1,71 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.utils.ByteArray; +import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract; + +public class GetAssetIssueByNameServletTest extends BaseHttpTest { + + private GetAssetIssueByNameServlet servlet; + private final ByteString data = + ByteString.copyFrom(ByteArray.fromHexString("74657374")); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetAssetIssueByNameServlet(); + injectWallet(servlet); + when(wallet.getAssetIssueByName(any())).thenReturn(null); + } + + @Test + public void testPost() throws Exception { + String jsonParam = "{\"value\": \"74657374\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).getAssetIssueByName(eq(data)); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertEquals("{}" + System.lineSeparator(), content); + } + + @Test + public void testGet() throws Exception { + MockHttpServletRequest request = getRequest("value", "74657374"); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getAssetIssueByName(eq(data)); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertEquals("{}" + System.lineSeparator(), content); + } + + @Test + public void testPostReturnsAssetWhenFound() throws Exception { + AssetIssueContract asset = AssetIssueContract.newBuilder() + .setName(data) + .setTotalSupply(5000L) + .build(); + when(wallet.getAssetIssueByName(eq(data))).thenReturn(asset); + + MockHttpServletRequest request = postRequest("{\"value\": \"74657374\"}"); + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertTrue("Should contain total_supply", content.contains("total_supply")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetAssetIssueListByNameServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetAssetIssueListByNameServletTest.java new file mode 100644 index 00000000000..e3055e21f99 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetAssetIssueListByNameServletTest.java @@ -0,0 +1,47 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.utils.ByteArray; + +public class GetAssetIssueListByNameServletTest extends BaseHttpTest { + + private GetAssetIssueListByNameServlet servlet; + private final ByteString data = ByteString.copyFrom(ByteArray.fromHexString("74657374")); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetAssetIssueListByNameServlet(); + injectWallet(servlet); + when(wallet.getAssetIssueListByName(any())).thenReturn(null); + } + + @Test + public void testPost() throws Exception { + String jsonParam = "{\"value\": \"74657374\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).getAssetIssueListByName(eq(data)); + assertEquals("{}" + System.lineSeparator(), response.getContentAsString()); + } + + @Test + public void testGet() throws Exception { + MockHttpServletRequest request = getRequest("value", "74657374"); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getAssetIssueListByName(eq(data)); + assertEquals("{}" + System.lineSeparator(), response.getContentAsString()); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetAssetIssueListServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetAssetIssueListServletTest.java index 935a3d1416d..2b9e997cf80 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetAssetIssueListServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetAssetIssueListServletTest.java @@ -3,7 +3,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.fail; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.junit.Test; @@ -12,6 +11,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetAssetIssueListServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetBandwidthPricesServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetBandwidthPricesServletTest.java index e4837610e23..2ddfda17bef 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetBandwidthPricesServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetBandwidthPricesServletTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.apache.http.client.methods.HttpGet; @@ -16,6 +15,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetBandwidthPricesServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetBlockByNumServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetBlockByNumServletTest.java index d4d6f33cf17..b28e1d33308 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetBlockByNumServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetBlockByNumServletTest.java @@ -3,11 +3,8 @@ import static org.junit.Assert.assertTrue; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; - import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import org.apache.http.client.methods.HttpPost; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; @@ -15,6 +12,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetBlockByNumServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetBlockServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetBlockServletTest.java new file mode 100644 index 00000000000..f48f9eb0fbc --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetBlockServletTest.java @@ -0,0 +1,45 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.protos.Protocol.Block; + +public class GetBlockServletTest extends BaseHttpTest { + + private GetBlockServlet servlet; + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetBlockServlet(); + injectWallet(servlet); + when(wallet.getBlock(org.mockito.ArgumentMatchers.argThat( + req -> req != null && "0".equals(req.getIdOrNum()) && !req.getDetail()))) + .thenReturn(Block.getDefaultInstance()); + } + + @Test + public void testGetBlockPost() throws Exception { + String jsonParam = "{\"id_or_num\": \"0\", \"detail\": false}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + assertEquals(200, response.getStatus()); + assertTrue(response.getContentAsString().contains("blockID")); + } + + @Test + public void testGetBlockGet() throws Exception { + MockHttpServletRequest request = getRequest("id_or_num", "0", "detail", "false"); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + assertEquals(200, response.getStatus()); + assertTrue(response.getContentAsString().contains("blockID")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetBrokerageServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetBrokerageServletTest.java index b5fa1914541..9b37c2e4205 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetBrokerageServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetBrokerageServletTest.java @@ -1,10 +1,7 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONObject; - import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import org.junit.Assert; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; @@ -12,6 +9,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetBrokerageServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetContractInfoServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetContractInfoServletTest.java new file mode 100644 index 00000000000..532bb42706f --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetContractInfoServletTest.java @@ -0,0 +1,60 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.api.GrpcAPI; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.protos.contract.SmartContractOuterClass.SmartContract; +import org.tron.protos.contract.SmartContractOuterClass.SmartContractDataWrapper; + +public class GetContractInfoServletTest extends BaseHttpTest { + + private GetContractInfoServlet servlet; + private final byte[] address = new ECKey().getAddress(); + private final String addrStr = ByteArray.toHexString(address); + private final GrpcAPI.BytesMessage expectedRequest = GrpcAPI.BytesMessage.newBuilder() + .setValue(ByteString.copyFrom(address)) + .build(); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetContractInfoServlet(); + injectWallet(servlet); + } + + @Test + public void testGetContractInfoPost() throws Exception { + when(wallet.getContractInfo(eq(expectedRequest))).thenReturn( + SmartContractDataWrapper.newBuilder() + .setSmartContract(SmartContract.newBuilder().setName("TestContract").build()) + .build()); + String jsonParam = "{\"value\": \"" + addrStr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + assertEquals(200, response.getStatus()); + assertTrue(response.getContentAsString().contains("TestContract")); + } + + @Test + public void testGetContractInfoGet() throws Exception { + when(wallet.getContractInfo(eq(expectedRequest))).thenReturn(null); + MockHttpServletRequest request = getRequest("value", addrStr); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getContractInfo(eq(expectedRequest)); + assertEquals(200, response.getStatus()); + assertEquals("{}", response.getContentAsString().trim()); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetContractServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetContractServletTest.java new file mode 100644 index 00000000000..074093de2a1 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetContractServletTest.java @@ -0,0 +1,71 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.api.GrpcAPI; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.protos.contract.SmartContractOuterClass.SmartContract; + +public class GetContractServletTest extends BaseHttpTest { + + private final byte[] address = new ECKey().getAddress(); + private final String addrStr = ByteArray.toHexString(address); + private final GrpcAPI.BytesMessage expectedRequest = GrpcAPI.BytesMessage.newBuilder() + .setValue(ByteString.copyFrom(address)) + .build(); + + private GetContractServlet servlet; + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetContractServlet(); + injectWallet(servlet); + } + + @Test + public void testPostFound() throws Exception { + when(wallet.getContract(eq(expectedRequest))).thenReturn( + SmartContract.newBuilder().setName("TestContract").build()); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(postRequest("{\"value\": \"" + addrStr + "\"}"), response); + verify(wallet).getContract(eq(expectedRequest)); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertTrue("Should contain contract name", content.contains("TestContract")); + } + + @Test + public void testPostNotFound() throws Exception { + when(wallet.getContract(eq(expectedRequest))).thenReturn(null); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(postRequest("{\"value\": \"" + addrStr + "\"}"), response); + verify(wallet).getContract(eq(expectedRequest)); + assertEquals(200, response.getStatus()); + assertEquals("{}" + System.lineSeparator(), response.getContentAsString()); + assertEquals("{}", response.getContentAsString().trim()); + } + + @Test + public void testGetNotFound() throws Exception { + when(wallet.getContract(eq(expectedRequest))).thenReturn(null); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(getRequest("value", addrStr), response); + verify(wallet).getContract(eq(expectedRequest)); + assertEquals(200, response.getStatus()); + assertEquals("{}" + System.lineSeparator(), response.getContentAsString()); + assertEquals("{}", response.getContentAsString().trim()); + + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexServletTest.java new file mode 100644 index 00000000000..21a455c108f --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexServletTest.java @@ -0,0 +1,56 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.protos.Protocol.DelegatedResourceAccountIndex; + +public class GetDelegatedResourceAccountIndexServletTest extends BaseHttpTest { + + private GetDelegatedResourceAccountIndexServlet servlet; + private final byte[] address = new ECKey().getAddress(); + private final String addrStr = ByteArray.toHexString(address); + private final ByteString expectedAddress = ByteString.copyFrom(address); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetDelegatedResourceAccountIndexServlet(); + injectWallet(servlet); + when(wallet.getDelegatedResourceAccountIndex(any())) + .thenReturn(DelegatedResourceAccountIndex.getDefaultInstance()); + } + + @Test + public void testGetDelegatedResourceAccountIndexPost() throws Exception { + String jsonParam = "{\"value\": \"" + addrStr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).getDelegatedResourceAccountIndex(eq(expectedAddress)); + String content = response.getContentAsString(); + assertFalse("Should not be empty", content.trim().isEmpty()); + assertFalse("Should not contain error", content.contains("\"Error\"")); + } + + @Test + public void testGetDelegatedResourceAccountIndexGet() throws Exception { + MockHttpServletRequest request = getRequest("value", addrStr); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getDelegatedResourceAccountIndex(eq(expectedAddress)); + String content = response.getContentAsString(); + assertFalse("Should not be empty", content.trim().isEmpty()); + assertFalse("Should not contain error", content.contains("\"Error\"")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexV2ServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexV2ServletTest.java new file mode 100644 index 00000000000..41be6db4d5a --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetDelegatedResourceAccountIndexV2ServletTest.java @@ -0,0 +1,57 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.utils.ByteArray; +import org.tron.protos.Protocol.DelegatedResourceAccountIndex; + +public class GetDelegatedResourceAccountIndexV2ServletTest extends BaseHttpTest { + + private GetDelegatedResourceAccountIndexV2Servlet servlet; + ByteString expectedAddress = ByteString.copyFrom( + ByteArray.fromHexString( + Util.getHexAddress("TBxSocpujP6UGKV5ydXNVTDQz7fAgdmoaB"))); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetDelegatedResourceAccountIndexV2Servlet(); + injectWallet(servlet); + when(wallet.getDelegatedResourceAccountIndexV2(any())) + .thenReturn(DelegatedResourceAccountIndex.getDefaultInstance()); + } + + @Test + public void testPost() throws Exception { + String jsonParam = "{\"visible\": true, \"value\": \"TBxSocpujP6UGKV5ydXNVTDQz7fAgdmoaB\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + + verify(wallet).getDelegatedResourceAccountIndexV2(eq(expectedAddress)); + String postContent = response.getContentAsString(); + assertFalse(postContent.contains("\"Error\"")); + assertFalse(postContent.trim().isEmpty()); + } + + @Test + public void testGet() throws Exception { + MockHttpServletRequest request = + getRequest("visible", "true", "value", "TBxSocpujP6UGKV5ydXNVTDQz7fAgdmoaB"); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getDelegatedResourceAccountIndexV2(eq(expectedAddress)); + String getContent = response.getContentAsString(); + assertFalse(getContent.contains("\"Error\"")); + assertFalse(getContent.trim().isEmpty()); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetEnergyPricesServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetEnergyPricesServletTest.java index 6c208c59d39..f0fe69fe450 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetEnergyPricesServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetEnergyPricesServletTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.apache.http.client.methods.HttpGet; @@ -16,6 +15,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetEnergyPricesServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetExchangeByIdServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetExchangeByIdServletTest.java new file mode 100644 index 00000000000..f67072e9856 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetExchangeByIdServletTest.java @@ -0,0 +1,52 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.protos.Protocol.Exchange; + +public class GetExchangeByIdServletTest extends BaseHttpTest { + + private GetExchangeByIdServlet servlet; + private final ByteString exchangeId = + ByteString.copyFrom(org.tron.common.utils.ByteArray.fromLong(1L)); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetExchangeByIdServlet(); + injectWallet(servlet); + when(wallet.getExchangeById(eq(exchangeId))) + .thenReturn(Exchange.newBuilder().setExchangeId(1L).build()); + } + + @Test + public void testPost() throws Exception { + String jsonParam = "{\"id\": 1}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).getExchangeById(eq(exchangeId)); + assertEquals(200, response.getStatus()); + assertTrue(response.getContentAsString().contains("exchange_id")); + } + + @Test + public void testGet() throws Exception { + MockHttpServletRequest request = getRequest("id", "1"); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getExchangeById(eq(exchangeId)); + assertEquals(200, response.getStatus()); + assertTrue(response.getContentAsString().contains("exchange_id")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetMarketOrderByAccountServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetMarketOrderByAccountServletTest.java new file mode 100644 index 00000000000..a1895231f8a --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetMarketOrderByAccountServletTest.java @@ -0,0 +1,55 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; + +public class GetMarketOrderByAccountServletTest extends BaseHttpTest { + + private GetMarketOrderByAccountServlet servlet; + private final byte[] address = new ECKey().getAddress(); + private final String addrStr = ByteArray.toHexString(address); + private final ByteString addrBytes = ByteString.copyFrom(address); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetMarketOrderByAccountServlet(); + injectWallet(servlet); + when(wallet.getMarketOrderByAccount(any())).thenReturn(null); + } + + @Test + public void testPost() throws Exception { + String jsonParam = "{\"value\": \"" + addrStr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).getMarketOrderByAccount(eq(addrBytes)); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertEquals("{}" + System.lineSeparator(), content); + } + + @Test + public void testGet() throws Exception { + MockHttpServletRequest request = getRequest("value", addrStr); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getMarketOrderByAccount(eq(addrBytes)); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertEquals("{}" + System.lineSeparator(), content); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetMemoFeePricesServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetMemoFeePricesServletTest.java index df8cda9e15e..b9440aa948f 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetMemoFeePricesServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetMemoFeePricesServletTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.apache.http.client.methods.HttpGet; @@ -16,6 +15,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetMemoFeePricesServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetNowBlockServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetNowBlockServletTest.java index bf5ab766fb1..1179e914d32 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetNowBlockServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetNowBlockServletTest.java @@ -7,7 +7,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.junit.Test; @@ -16,6 +15,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetNowBlockServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetProposalByIdServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetProposalByIdServletTest.java new file mode 100644 index 00000000000..cf64adcb1a1 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetProposalByIdServletTest.java @@ -0,0 +1,70 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.utils.ByteArray; +import org.tron.protos.Protocol.Proposal; + +public class GetProposalByIdServletTest extends BaseHttpTest { + + private GetProposalByIdServlet servlet; + private final ByteString proposalId = + ByteString.copyFrom(ByteArray.fromLong(1L)); + + @Override + protected void setUpMocks() throws Exception { + servlet = new GetProposalByIdServlet(); + injectWallet(servlet); + when(wallet.getProposalById(any())).thenReturn(null); + } + + @Test + public void testPost() throws Exception { + String jsonParam = "{\"id\": 1}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).getProposalById(eq(proposalId)); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertEquals("{}" + System.lineSeparator(), content); + } + + @Test + public void testGet() throws Exception { + MockHttpServletRequest request = getRequest("id", "1"); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + verify(wallet).getProposalById(eq(proposalId)); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertFalse("Should not contain error", content.contains("\"Error\"")); + assertEquals("{}" + System.lineSeparator(), content); + } + + @Test + public void testPostReturnsProposalWhenFound() throws Exception { + Proposal proposal = Proposal.newBuilder().setProposalId(1L).build(); + when(wallet.getProposalById(eq(proposalId))).thenReturn(proposal); + + MockHttpServletRequest request = postRequest("{\"id\": 1}"); + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertTrue("Should contain proposal_id", content.contains("proposal_id")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetRewardServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetRewardServletTest.java index 3de72eb3d45..76f85da5d8f 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetRewardServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetRewardServletTest.java @@ -2,15 +2,9 @@ import static org.tron.common.utils.Commons.decodeFromBase58Check; -import com.alibaba.fastjson.JSONObject; - -import java.io.File; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -18,11 +12,11 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.tron.common.BaseTest; import org.tron.common.TestConstants; -import org.tron.common.utils.FileUtil; import org.tron.core.config.args.Args; import org.tron.core.db.Manager; import org.tron.core.service.MortgageService; import org.tron.core.store.DelegationStore; +import org.tron.json.JSONObject; @Slf4j public class GetRewardServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServletTest.java index c2c02453cd4..1763e440b48 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServletTest.java @@ -3,12 +3,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; - import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import org.apache.http.client.methods.HttpPost; import org.junit.Assert; import org.junit.Before; @@ -18,11 +14,12 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.common.utils.ByteArray; - import org.tron.core.capsule.TransactionInfoCapsule; import org.tron.core.capsule.TransactionRetCapsule; import org.tron.core.config.args.Args; import org.tron.core.db.TransactionStoreTest; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; public class GetTransactionInfoByBlockNumServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByIdServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByIdServletTest.java index ed1bc19aed6..6793433371d 100644 --- a/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByIdServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByIdServletTest.java @@ -3,12 +3,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; - import java.io.UnsupportedEncodingException; import javax.annotation.Resource; - import org.apache.http.client.methods.HttpPost; import org.junit.Assert; import org.junit.Before; @@ -27,6 +24,7 @@ import org.tron.core.db.TransactionStore; import org.tron.core.db.TransactionStoreTest; import org.tron.core.store.TransactionRetStore; +import org.tron.json.JSONObject; import org.tron.protos.Protocol; import org.tron.protos.contract.BalanceContract; diff --git a/framework/src/test/java/org/tron/core/services/http/ListProposalsServletTest.java b/framework/src/test/java/org/tron/core/services/http/ListProposalsServletTest.java index 980c6617001..f600f704aa7 100644 --- a/framework/src/test/java/org/tron/core/services/http/ListProposalsServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/ListProposalsServletTest.java @@ -3,7 +3,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.fail; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.junit.Test; @@ -12,6 +11,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class ListProposalsServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/http/MarketCancelOrderServletTest.java b/framework/src/test/java/org/tron/core/services/http/MarketCancelOrderServletTest.java new file mode 100644 index 00000000000..7c8e529b275 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/MarketCancelOrderServletTest.java @@ -0,0 +1,50 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol.Transaction.Contract.ContractType; +import org.tron.protos.contract.MarketContract.MarketCancelOrderContract; + +public class MarketCancelOrderServletTest extends BaseHttpTest { + + private MarketCancelOrderServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new MarketCancelOrderServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(MarketCancelOrderContract.class), eq(ContractType.MarketCancelOrderContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testMarketCancelOrder() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"order_id\": \"0000000000000000000000000000000000000000000000000000000000000001\"" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof MarketCancelOrderContract + && addressEquals(((MarketCancelOrderContract) c) + .getOwnerAddress(), ownerAddr) + && ((MarketCancelOrderContract) c).getOrderId().size() == 32), + eq(ContractType.MarketCancelOrderContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/MarketSellAssetServletTest.java b/framework/src/test/java/org/tron/core/services/http/MarketSellAssetServletTest.java new file mode 100644 index 00000000000..e82178b909a --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/MarketSellAssetServletTest.java @@ -0,0 +1,59 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.MarketContract; + +public class MarketSellAssetServletTest extends BaseHttpTest { + + private MarketSellAssetServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new MarketSellAssetServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(MarketContract.MarketSellAssetContract.class), + eq(Protocol.Transaction.Contract.ContractType.MarketSellAssetContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testMarketSellAsset() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"sell_token_id\": \"5f\"," + + "\"sell_token_quantity\": 100," + + "\"buy_token_id\": \"60\"," + + "\"buy_token_quantity\": 200" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof MarketContract.MarketSellAssetContract + && addressEquals(((MarketContract.MarketSellAssetContract) c) + .getOwnerAddress(), ownerAddr) + && ((MarketContract.MarketSellAssetContract) c).getSellTokenQuantity() == 100 + && ((MarketContract.MarketSellAssetContract) c).getBuyTokenQuantity() == 200 + && ((MarketContract.MarketSellAssetContract) c) + .getSellTokenId().toStringUtf8().equals("_") + && ((MarketContract.MarketSellAssetContract) c) + .getBuyTokenId().toStringUtf8().equals("`")), + eq(Protocol.Transaction.Contract.ContractType.MarketSellAssetContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ParticipateAssetIssueServletTest.java b/framework/src/test/java/org/tron/core/services/http/ParticipateAssetIssueServletTest.java new file mode 100644 index 00000000000..0fbfe73d6fc --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ParticipateAssetIssueServletTest.java @@ -0,0 +1,59 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.AssetIssueContractOuterClass; + +public class ParticipateAssetIssueServletTest extends BaseHttpTest { + + private ParticipateAssetIssueServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + private final String toAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ParticipateAssetIssueServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(AssetIssueContractOuterClass.ParticipateAssetIssueContract.class), + eq(Protocol.Transaction.Contract.ContractType.ParticipateAssetIssueContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testParticipateAssetIssue() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"to_address\": \"" + toAddr + "\"," + + "\"asset_name\": \"74657374\"," + + "\"amount\": 100" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof AssetIssueContractOuterClass.ParticipateAssetIssueContract + && addressEquals(((AssetIssueContractOuterClass.ParticipateAssetIssueContract) c) + .getOwnerAddress(), ownerAddr) + && addressEquals(((AssetIssueContractOuterClass.ParticipateAssetIssueContract) c) + .getToAddress(), toAddr) + && ((AssetIssueContractOuterClass.ParticipateAssetIssueContract) c) + .getAmount() == 100 + && ((AssetIssueContractOuterClass.ParticipateAssetIssueContract) c) + .getAssetName().toStringUtf8().equals("test")), + eq(Protocol.Transaction.Contract.ContractType.ParticipateAssetIssueContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ProposalApproveServletTest.java b/framework/src/test/java/org/tron/core/services/http/ProposalApproveServletTest.java new file mode 100644 index 00000000000..f017b06de24 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ProposalApproveServletTest.java @@ -0,0 +1,53 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.ProposalContract; + +public class ProposalApproveServletTest extends BaseHttpTest { + + private ProposalApproveServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ProposalApproveServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(ProposalContract.ProposalApproveContract.class), + eq(Protocol.Transaction.Contract.ContractType.ProposalApproveContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testProposalApprove() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"proposal_id\": 1," + + "\"is_add_approval\": true" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof ProposalContract.ProposalApproveContract + && ((ProposalContract.ProposalApproveContract) c).getProposalId() == 1 + && ((ProposalContract.ProposalApproveContract) c).getIsAddApproval() + && addressEquals(((ProposalContract.ProposalApproveContract) c) + .getOwnerAddress(), ownerAddr)), + eq(Protocol.Transaction.Contract.ContractType.ProposalApproveContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ProposalCreateServletTest.java b/framework/src/test/java/org/tron/core/services/http/ProposalCreateServletTest.java new file mode 100644 index 00000000000..0057aa7bd2d --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ProposalCreateServletTest.java @@ -0,0 +1,53 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.ProposalContract; + +public class ProposalCreateServletTest extends BaseHttpTest { + + private ProposalCreateServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ProposalCreateServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(ProposalContract.ProposalCreateContract.class), + eq(Protocol.Transaction.Contract.ContractType.ProposalCreateContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testProposalCreate() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"parameters\": [{\"key\": 0, \"value\": 100000}]" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof ProposalContract.ProposalCreateContract + && addressEquals(((ProposalContract.ProposalCreateContract) c) + .getOwnerAddress(), ownerAddr) + && ((ProposalContract.ProposalCreateContract) c).getParametersMap().size() == 1 + && ((ProposalContract.ProposalCreateContract) c) + .getParametersMap().get(0L) == 100000), + eq(Protocol.Transaction.Contract.ContractType.ProposalCreateContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/ProposalDeleteServletTest.java b/framework/src/test/java/org/tron/core/services/http/ProposalDeleteServletTest.java new file mode 100644 index 00000000000..90a25e5bb68 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ProposalDeleteServletTest.java @@ -0,0 +1,51 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.ProposalContract; + +public class ProposalDeleteServletTest extends BaseHttpTest { + + private ProposalDeleteServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ProposalDeleteServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(ProposalContract.ProposalDeleteContract.class), + eq(Protocol.Transaction.Contract.ContractType.ProposalDeleteContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testProposalDelete() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"proposal_id\": 1" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof ProposalContract.ProposalDeleteContract + && addressEquals(((ProposalContract.ProposalDeleteContract) c) + .getOwnerAddress(), ownerAddr) + && ((ProposalContract.ProposalDeleteContract) c).getProposalId() == 1), + eq(Protocol.Transaction.Contract.ContractType.ProposalDeleteContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/RateLimiterServletTest.java b/framework/src/test/java/org/tron/core/services/http/RateLimiterServletTest.java index 4ae76f85dfb..1ae341696eb 100644 --- a/framework/src/test/java/org/tron/core/services/http/RateLimiterServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/RateLimiterServletTest.java @@ -5,13 +5,34 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import java.lang.reflect.Field; import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.junit.AfterClass; +import org.junit.Before; import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.TestConstants; +import org.tron.core.config.args.Args; import org.tron.core.exception.TronError; +import org.tron.core.services.ratelimiter.GlobalRateLimiter; +import org.tron.core.services.ratelimiter.RateLimiterContainer; +import org.tron.core.services.ratelimiter.RuntimeData; import org.tron.core.services.ratelimiter.adapter.DefaultBaseQqsAdapter; import org.tron.core.services.ratelimiter.adapter.GlobalPreemptibleAdapter; import org.tron.core.services.ratelimiter.adapter.IPQPSRateLimiterAdapter; +import org.tron.core.services.ratelimiter.adapter.IPreemptibleRateLimiter; import org.tron.core.services.ratelimiter.adapter.IRateLimiter; import org.tron.core.services.ratelimiter.adapter.QpsRateLimiterAdapter; @@ -19,12 +40,60 @@ * Verifies RateLimiterServlet's adapter resolution: strict whitelist * (no Class.forName arbitrary class loading), fail-fast on unknown or * empty names, and successful construction of every whitelisted adapter. + * + *

Also covers the rate-limiting logic in {@link RateLimiterServlet#service}: + *

    + *
  1. Per-endpoint check runs before the global check, so a per-endpoint + * rejection never consumes a global IP/QPS token.
  2. + *
  3. A {@link IPreemptibleRateLimiter} permit is always released — whether the + * global limiter rejects the request or the request handler completes normally.
  4. + *
*/ public class RateLimiterServletTest { private static final Map> allowedAdapters = RateLimiterServlet.ALLOWED_ADAPTERS; + private static final String KEY_HTTP = "http_"; + + private TestServlet servlet; + private RateLimiterContainer container; + private MockHttpServletRequest request; + private MockHttpServletResponse response; + + /** Minimal concrete subclass — only {@code doGet} is needed for the happy-path test. */ + static class TestServlet extends RateLimiterServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + // intentional no-op + } + } + + /** + * GlobalRateLimiter's static initializer calls Args.getInstance().getRateLimiterGlobalQps(). + * Without Args being initialized the default QPS is 0, causing RateLimiter.create(0) to throw. + * Initializing Args here (before the class is first loaded inside each test method) prevents + * the static initialization failure that would otherwise break mockStatic(). + */ + @Before + public void setUp() throws Exception { + Args.setParam(new String[0], TestConstants.TEST_CONF); + servlet = new TestServlet(); + container = new RateLimiterContainer(); + Field f = RateLimiterServlet.class.getDeclaredField("container"); + f.setAccessible(true); + f.set(servlet, container); + + request = new MockHttpServletRequest("GET", "/test"); + request.setRemoteAddr("10.0.0.1"); + response = new MockHttpServletResponse(); + } + + @AfterClass + public static void tearDown() { + Args.clearParam(); + } + @Test public void testWhitelistContents() { assertEquals(GlobalPreemptibleAdapter.class, @@ -90,4 +159,95 @@ public void testBuildsEachWhitelistedAdapter() { GlobalPreemptibleAdapter.class.getSimpleName(), "permit=1", "TestServlet") instanceof GlobalPreemptibleAdapter); } + + /** + * Per-endpoint rejects → GlobalRateLimiter must NOT be invoked. + * The global IP/QPS quota is fully preserved for other clients. + */ + @Test + public void testPerEndpointRejectedDoesNotConsumeGlobalQuota() throws Exception { + IPreemptibleRateLimiter perEndpoint = Mockito.mock(IPreemptibleRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(false); + container.add(KEY_HTTP, "TestServlet", perEndpoint); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + servlet.service(request, response); + + globalMock.verify(() -> GlobalRateLimiter.tryAcquire(any()), never()); + // tryAcquire returned false — no permit was taken, nothing to release + verify(perEndpoint, never()).release(); + } + } + + /** + * Per-endpoint (QPS-only, non-preemptible) rejects → global not called, + * and no release() attempt on a non-IPreemptibleRateLimiter. + */ + @Test + public void testNonPreemptiblePerEndpointRejectedDoesNotConsumeGlobal() throws Exception { + IRateLimiter perEndpoint = Mockito.mock(IRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(false); + container.add(KEY_HTTP, "TestServlet", perEndpoint); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + servlet.service(request, response); + + globalMock.verify(() -> GlobalRateLimiter.tryAcquire(any()), never()); + } + } + + /** + * Per-endpoint (IPreemptibleRateLimiter) acquires the permit, but the global limiter + * then rejects. The finally block must release the permit to avoid a semaphore leak. + */ + @Test + public void testGlobalRejectedReleasesPreemptiblePermit() throws Exception { + IPreemptibleRateLimiter perEndpoint = Mockito.mock(IPreemptibleRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(true); + container.add(KEY_HTTP, "TestServlet", perEndpoint); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + globalMock.when(() -> GlobalRateLimiter.tryAcquire(any())).thenReturn(false); + + servlet.service(request, response); + + // Permit was acquired but request blocked — must be returned + verify(perEndpoint, times(1)).release(); + } + } + + /** + * Both limiters pass → request executes and the permit is released exactly once + * in the finally block after the handler returns. + */ + @Test + public void testBothPassPermitReleasedAfterRequest() throws Exception { + IPreemptibleRateLimiter perEndpoint = Mockito.mock(IPreemptibleRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(true); + container.add(KEY_HTTP, "TestServlet", perEndpoint); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + globalMock.when(() -> GlobalRateLimiter.tryAcquire(any())).thenReturn(true); + + servlet.service(request, response); + + verify(perEndpoint, times(1)).release(); + } + } + + /** + * No per-endpoint limiter configured (null) → only GlobalRateLimiter is consulted, + * and nothing is released (no permit to hold). + */ + @Test + public void testNullRateLimiterConsultsOnlyGlobal() throws Exception { + // No entry added to container — container.get() returns null + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + globalMock.when(() -> GlobalRateLimiter.tryAcquire(any())).thenReturn(true); + + servlet.service(request, response); + + globalMock.verify(() -> GlobalRateLimiter.tryAcquire(any()), times(1)); + } + } } diff --git a/framework/src/test/java/org/tron/core/services/http/SetAccountIdServletTest.java b/framework/src/test/java/org/tron/core/services/http/SetAccountIdServletTest.java new file mode 100644 index 00000000000..6967372c9ac --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/SetAccountIdServletTest.java @@ -0,0 +1,51 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.AccountContract; + +public class SetAccountIdServletTest extends BaseHttpTest { + + private SetAccountIdServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new SetAccountIdServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule(any(AccountContract.SetAccountIdContract.class), eq( + Protocol.Transaction.Contract.ContractType.SetAccountIdContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testSetAccountId() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"account_id\": \"6161616162626262\"" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof AccountContract.SetAccountIdContract + && addressEquals(((AccountContract.SetAccountIdContract) c) + .getOwnerAddress(), ownerAddr) + && ((AccountContract.SetAccountIdContract) c) + .getAccountId().toStringUtf8().equals("aaaabbbb")), + eq(Protocol.Transaction.Contract.ContractType.SetAccountIdContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/TransferAssetServletTest.java b/framework/src/test/java/org/tron/core/services/http/TransferAssetServletTest.java new file mode 100644 index 00000000000..49652f3361d --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/TransferAssetServletTest.java @@ -0,0 +1,58 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.AssetIssueContractOuterClass; + +public class TransferAssetServletTest extends BaseHttpTest { + + private TransferAssetServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + private final String toAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new TransferAssetServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(AssetIssueContractOuterClass.TransferAssetContract.class), + eq(Protocol.Transaction.Contract.ContractType.TransferAssetContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testTransferAsset() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"to_address\": \"" + toAddr + "\"," + + "\"asset_name\": \"74657374\"," + + "\"amount\": 100" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof AssetIssueContractOuterClass.TransferAssetContract + && addressEquals(((AssetIssueContractOuterClass.TransferAssetContract) c) + .getOwnerAddress(), ownerAddr) + && addressEquals(((AssetIssueContractOuterClass.TransferAssetContract) c) + .getToAddress(), toAddr) + && ((AssetIssueContractOuterClass.TransferAssetContract) c).getAmount() == 100 + && ((AssetIssueContractOuterClass.TransferAssetContract) c) + .getAssetName().toStringUtf8().equals("test")), + eq(Protocol.Transaction.Contract.ContractType.TransferAssetContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/TransferServletTest.java b/framework/src/test/java/org/tron/core/services/http/TransferServletTest.java new file mode 100644 index 00000000000..b04c6255dac --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/TransferServletTest.java @@ -0,0 +1,55 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class TransferServletTest extends BaseHttpTest { + + private TransferServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + private final String toAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new TransferServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(BalanceContract.TransferContract.class), + eq(Protocol.Transaction.Contract.ContractType.TransferContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testTransfer() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"to_address\": \"" + toAddr + "\"," + + "\"amount\": 100" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.TransferContract + && addressEquals(((BalanceContract.TransferContract) c) + .getOwnerAddress(), ownerAddr) + && addressEquals(((BalanceContract.TransferContract) c) + .getToAddress(), toAddr) + && ((BalanceContract.TransferContract) c).getAmount() == 100), + eq(Protocol.Transaction.Contract.ContractType.TransferContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UnDelegateResourceServletTest.java b/framework/src/test/java/org/tron/core/services/http/UnDelegateResourceServletTest.java new file mode 100644 index 00000000000..fbdb0138c41 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UnDelegateResourceServletTest.java @@ -0,0 +1,59 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; +import org.tron.protos.contract.Common.ResourceCode; + +public class UnDelegateResourceServletTest extends BaseHttpTest { + + private UnDelegateResourceServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + private final String receiverAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UnDelegateResourceServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(BalanceContract.UnDelegateResourceContract.class), + eq(Protocol.Transaction.Contract.ContractType.UnDelegateResourceContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUnDelegateResource() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"receiver_address\": \"" + receiverAddr + "\"," + + "\"balance\": 1000000," + + "\"resource\": \"ENERGY\"" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.UnDelegateResourceContract + && addressEquals(((BalanceContract.UnDelegateResourceContract) c) + .getOwnerAddress(), ownerAddr) + && addressEquals(((BalanceContract.UnDelegateResourceContract) c) + .getReceiverAddress(), receiverAddr) + && ((BalanceContract.UnDelegateResourceContract) c).getBalance() == 1000000 + && ((BalanceContract.UnDelegateResourceContract) c) + .getResource() == ResourceCode.ENERGY), + eq(Protocol.Transaction.Contract.ContractType.UnDelegateResourceContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UnFreezeAssetServletTest.java b/framework/src/test/java/org/tron/core/services/http/UnFreezeAssetServletTest.java new file mode 100644 index 00000000000..a9c784c38e8 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UnFreezeAssetServletTest.java @@ -0,0 +1,47 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.AssetIssueContractOuterClass; + +public class UnFreezeAssetServletTest extends BaseHttpTest { + + private UnFreezeAssetServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UnFreezeAssetServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(AssetIssueContractOuterClass.UnfreezeAssetContract.class), + eq(Protocol.Transaction.Contract.ContractType.UnfreezeAssetContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUnFreezeAsset() throws Exception { + String jsonParam = "{\"owner_address\": \"" + ownerAddr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof AssetIssueContractOuterClass.UnfreezeAssetContract + && addressEquals(((AssetIssueContractOuterClass.UnfreezeAssetContract) c) + .getOwnerAddress(), ownerAddr)), + eq(Protocol.Transaction.Contract.ContractType.UnfreezeAssetContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UnFreezeBalanceServletTest.java b/framework/src/test/java/org/tron/core/services/http/UnFreezeBalanceServletTest.java new file mode 100644 index 00000000000..800e5e957dc --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UnFreezeBalanceServletTest.java @@ -0,0 +1,46 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class UnFreezeBalanceServletTest extends BaseHttpTest { + + private UnFreezeBalanceServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UnFreezeBalanceServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule(any(BalanceContract.UnfreezeBalanceContract.class), + eq(Protocol.Transaction.Contract.ContractType.UnfreezeBalanceContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUnFreezeBalance() throws Exception { + String jsonParam = "{\"owner_address\": \"" + ownerAddr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.UnfreezeBalanceContract + && addressEquals(((BalanceContract.UnfreezeBalanceContract) c) + .getOwnerAddress(), ownerAddr)), + eq(Protocol.Transaction.Contract.ContractType.UnfreezeBalanceContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UnFreezeBalanceV2ServletTest.java b/framework/src/test/java/org/tron/core/services/http/UnFreezeBalanceV2ServletTest.java new file mode 100644 index 00000000000..cb27bc0df69 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UnFreezeBalanceV2ServletTest.java @@ -0,0 +1,55 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; +import org.tron.protos.contract.Common.ResourceCode; + +public class UnFreezeBalanceV2ServletTest extends BaseHttpTest { + + private UnFreezeBalanceV2Servlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UnFreezeBalanceV2Servlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(BalanceContract.UnfreezeBalanceV2Contract.class), + eq(Protocol.Transaction.Contract.ContractType.UnfreezeBalanceV2Contract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUnFreezeBalanceV2() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"unfreeze_balance\": 1000000," + + "\"resource\": \"ENERGY\"" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.UnfreezeBalanceV2Contract + && addressEquals(((BalanceContract.UnfreezeBalanceV2Contract) c) + .getOwnerAddress(), ownerAddr) + && ((BalanceContract.UnfreezeBalanceV2Contract) c).getUnfreezeBalance() == 1000000 + && ((BalanceContract.UnfreezeBalanceV2Contract) c) + .getResource() == ResourceCode.ENERGY), + eq(Protocol.Transaction.Contract.ContractType.UnfreezeBalanceV2Contract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UpdateAssetServletTest.java b/framework/src/test/java/org/tron/core/services/http/UpdateAssetServletTest.java new file mode 100644 index 00000000000..18fbdb84a28 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UpdateAssetServletTest.java @@ -0,0 +1,59 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.AssetIssueContractOuterClass; + +public class UpdateAssetServletTest extends BaseHttpTest { + + private UpdateAssetServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UpdateAssetServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(AssetIssueContractOuterClass.UpdateAssetContract.class), + eq(Protocol.Transaction.Contract.ContractType.UpdateAssetContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUpdateAsset() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"description\": \"746573745f64657363\"," + + "\"url\": \"746573745f75726c\"," + + "\"new_limit\": 100," + + "\"new_public_limit\": 200" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof AssetIssueContractOuterClass.UpdateAssetContract + && addressEquals(((AssetIssueContractOuterClass.UpdateAssetContract) c) + .getOwnerAddress(), ownerAddr) + && ((AssetIssueContractOuterClass.UpdateAssetContract) c).getNewLimit() == 100 + && ((AssetIssueContractOuterClass.UpdateAssetContract) c).getNewPublicLimit() == 200 + && ((AssetIssueContractOuterClass.UpdateAssetContract) c) + .getDescription().toStringUtf8().equals("test_desc") + && ((AssetIssueContractOuterClass.UpdateAssetContract) c) + .getUrl().toStringUtf8().equals("test_url")), + eq(Protocol.Transaction.Contract.ContractType.UpdateAssetContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UpdateBrokerageServletTest.java b/framework/src/test/java/org/tron/core/services/http/UpdateBrokerageServletTest.java new file mode 100644 index 00000000000..378690e9bdc --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UpdateBrokerageServletTest.java @@ -0,0 +1,51 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.StorageContract; + +public class UpdateBrokerageServletTest extends BaseHttpTest { + + private UpdateBrokerageServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UpdateBrokerageServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(StorageContract.UpdateBrokerageContract.class), + eq(Protocol.Transaction.Contract.ContractType.UpdateBrokerageContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUpdateBrokerage() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"brokerage\": 20" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof StorageContract.UpdateBrokerageContract + && addressEquals(((StorageContract.UpdateBrokerageContract) c) + .getOwnerAddress(), ownerAddr) + && ((StorageContract.UpdateBrokerageContract) c).getBrokerage() == 20), + eq(Protocol.Transaction.Contract.ContractType.UpdateBrokerageContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UpdateEnergyLimitServletTest.java b/framework/src/test/java/org/tron/core/services/http/UpdateEnergyLimitServletTest.java new file mode 100644 index 00000000000..b54407c7ab0 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UpdateEnergyLimitServletTest.java @@ -0,0 +1,54 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol.Transaction.Contract.ContractType; +import org.tron.protos.contract.SmartContractOuterClass.UpdateEnergyLimitContract; + +public class UpdateEnergyLimitServletTest extends BaseHttpTest { + + private UpdateEnergyLimitServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + private final String contractAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UpdateEnergyLimitServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(UpdateEnergyLimitContract.class), eq(ContractType.UpdateEnergyLimitContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUpdateEnergyLimit() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"contract_address\": \"" + contractAddr + "\"," + + "\"origin_energy_limit\": 10000000" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof UpdateEnergyLimitContract + && addressEquals(((UpdateEnergyLimitContract) c) + .getOwnerAddress(), ownerAddr) + && addressEquals(((UpdateEnergyLimitContract) c) + .getContractAddress(), contractAddr) + && ((UpdateEnergyLimitContract) c).getOriginEnergyLimit() == 10000000), + eq(ContractType.UpdateEnergyLimitContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UpdateSettingServletTest.java b/framework/src/test/java/org/tron/core/services/http/UpdateSettingServletTest.java new file mode 100644 index 00000000000..cd33306f50c --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UpdateSettingServletTest.java @@ -0,0 +1,56 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.SmartContractOuterClass; + +public class UpdateSettingServletTest extends BaseHttpTest { + + private UpdateSettingServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + private final String contractAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UpdateSettingServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(SmartContractOuterClass.UpdateSettingContract.class), + eq(Protocol.Transaction.Contract.ContractType.UpdateSettingContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUpdateSetting() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"contract_address\": \"" + contractAddr + "\"," + + "\"consume_user_resource_percent\": 50" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof SmartContractOuterClass.UpdateSettingContract + && addressEquals(((SmartContractOuterClass.UpdateSettingContract) c) + .getOwnerAddress(), ownerAddr) + && addressEquals(((SmartContractOuterClass.UpdateSettingContract) c) + .getContractAddress(), contractAddr) + && ((SmartContractOuterClass.UpdateSettingContract) c) + .getConsumeUserResourcePercent() == 50), + eq(Protocol.Transaction.Contract.ContractType.UpdateSettingContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UpdateWitnessServletTest.java b/framework/src/test/java/org/tron/core/services/http/UpdateWitnessServletTest.java new file mode 100644 index 00000000000..52c0e5ede91 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UpdateWitnessServletTest.java @@ -0,0 +1,52 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.WitnessContract; + +public class UpdateWitnessServletTest extends BaseHttpTest { + + private UpdateWitnessServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new UpdateWitnessServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(WitnessContract.WitnessUpdateContract.class), + eq(Protocol.Transaction.Contract.ContractType.WitnessUpdateContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testUpdateWitness() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"update_url\": \"746573745f75726c\"" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof WitnessContract.WitnessUpdateContract + && addressEquals(((WitnessContract.WitnessUpdateContract) c) + .getOwnerAddress(), ownerAddr) + && ((WitnessContract.WitnessUpdateContract) c) + .getUpdateUrl().toStringUtf8().equals("test_url")), + eq(Protocol.Transaction.Contract.ContractType.WitnessUpdateContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/UtilMockTest.java b/framework/src/test/java/org/tron/core/services/http/UtilMockTest.java index 7c05e0e9cfe..d4124c90adf 100644 --- a/framework/src/test/java/org/tron/core/services/http/UtilMockTest.java +++ b/framework/src/test/java/org/tron/core/services/http/UtilMockTest.java @@ -1,16 +1,12 @@ package org.tron.core.services.http; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors; - import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Assert; @@ -20,6 +16,8 @@ import org.tron.common.utils.Sha256Hash; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionCapsule; +import org.tron.json.JSONArray; +import org.tron.json.JSONObject; import org.tron.p2p.utils.ByteArray; import org.tron.protos.Protocol; import org.tron.protos.contract.BalanceContract; @@ -386,4 +384,4 @@ public void testGetJsonString() { Assert.assertEquals(expect, ret2); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/services/http/ValidateAddressServletTest.java b/framework/src/test/java/org/tron/core/services/http/ValidateAddressServletTest.java new file mode 100644 index 00000000000..a74f04765a8 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ValidateAddressServletTest.java @@ -0,0 +1,58 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; + +public class ValidateAddressServletTest extends BaseHttpTest { + + private ValidateAddressServlet servlet; + private final String validAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new ValidateAddressServlet(); + } + + @Test + public void testValidateAddressGet() throws Exception { + MockHttpServletRequest request = getRequest("address", validAddr); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertTrue("Should report valid", content.contains("\"result\":true")); + assertFalse("Should not contain Error", content.contains("\"Error\"")); + } + + @Test + public void testValidateAddressPost() throws Exception { + String json = "{\"address\": \"" + validAddr + "\"}"; + MockHttpServletRequest request = postRequest(json); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertTrue("Should report valid", content.contains("\"result\":true")); + } + + @Test + public void testValidateInvalidAddress() throws Exception { + MockHttpServletRequest request = getRequest("address", "invalid_address"); + + MockHttpServletResponse response = newResponse(); + servlet.doGet(request, response); + assertEquals(200, response.getStatus()); + String content = response.getContentAsString(); + assertTrue("Should report invalid", content.contains("\"result\":false")); + assertFalse("Should not report valid", content.contains("\"result\":true")); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/VoteWitnessAccountServletTest.java b/framework/src/test/java/org/tron/core/services/http/VoteWitnessAccountServletTest.java new file mode 100644 index 00000000000..8166a001ce7 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/VoteWitnessAccountServletTest.java @@ -0,0 +1,54 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol.Transaction.Contract.ContractType; +import org.tron.protos.contract.WitnessContract.VoteWitnessContract; + +public class VoteWitnessAccountServletTest extends BaseHttpTest { + + private VoteWitnessAccountServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + private final String voteAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new VoteWitnessAccountServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(VoteWitnessContract.class), eq(ContractType.VoteWitnessContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testVoteWitnessAccount() throws Exception { + String jsonParam = "{" + + "\"owner_address\": \"" + ownerAddr + "\"," + + "\"votes\": [{\"vote_address\": \"" + voteAddr + "\"," + + "\"vote_count\": 1}]" + + "}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof VoteWitnessContract + && addressEquals(((VoteWitnessContract) c).getOwnerAddress(), ownerAddr) + && ((VoteWitnessContract) c).getVotesCount() == 1 + && ((VoteWitnessContract) c).getVotes(0).getVoteCount() == 1 + && addressEquals(((VoteWitnessContract) c).getVotes(0) + .getVoteAddress(), voteAddr)), + eq(ContractType.VoteWitnessContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/WithdrawBalanceServletTest.java b/framework/src/test/java/org/tron/core/services/http/WithdrawBalanceServletTest.java new file mode 100644 index 00000000000..f793265f9bc --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/WithdrawBalanceServletTest.java @@ -0,0 +1,46 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class WithdrawBalanceServletTest extends BaseHttpTest { + + private WithdrawBalanceServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new WithdrawBalanceServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule(any(BalanceContract.WithdrawBalanceContract.class), + eq(Protocol.Transaction.Contract.ContractType.WithdrawBalanceContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testWithdrawBalance() throws Exception { + String jsonParam = "{\"owner_address\": \"" + ownerAddr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.WithdrawBalanceContract + && addressEquals(((BalanceContract.WithdrawBalanceContract) c) + .getOwnerAddress(), ownerAddr)), + eq(Protocol.Transaction.Contract.ContractType.WithdrawBalanceContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/WithdrawExpireUnfreezeServletTest.java b/framework/src/test/java/org/tron/core/services/http/WithdrawExpireUnfreezeServletTest.java new file mode 100644 index 00000000000..9e733018452 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/WithdrawExpireUnfreezeServletTest.java @@ -0,0 +1,47 @@ +package org.tron.core.services.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class WithdrawExpireUnfreezeServletTest extends BaseHttpTest { + + private WithdrawExpireUnfreezeServlet servlet; + private final String ownerAddr = ByteArray.toHexString(new ECKey().getAddress()); + + @Override + protected void setUpMocks() throws Exception { + servlet = new WithdrawExpireUnfreezeServlet(); + injectWallet(servlet); + when(wallet.createTransactionCapsule( + any(BalanceContract.WithdrawExpireUnfreezeContract.class), + eq(Protocol.Transaction.Contract.ContractType.WithdrawExpireUnfreezeContract))) + .thenReturn(new TransactionCapsule(MINIMAL_TX)); + } + + @Test + public void testWithdrawExpireUnfreeze() throws Exception { + String jsonParam = "{\"owner_address\": \"" + ownerAddr + "\"}"; + MockHttpServletRequest request = postRequest(jsonParam); + + MockHttpServletResponse response = newResponse(); + servlet.doPost(request, response); + verify(wallet).createTransactionCapsule( + argThat(c -> c instanceof BalanceContract.WithdrawExpireUnfreezeContract + && addressEquals(((BalanceContract.WithdrawExpireUnfreezeContract) c) + .getOwnerAddress(), ownerAddr)), + eq(Protocol.Transaction.Contract.ContractType.WithdrawExpireUnfreezeContract)); + assertTransactionResponse(response); + } +} diff --git a/framework/src/test/java/org/tron/core/services/interfaceOnPBFT/http/GetBandwidthPricesOnPBFTServletTest.java b/framework/src/test/java/org/tron/core/services/interfaceOnPBFT/http/GetBandwidthPricesOnPBFTServletTest.java index 5f6558e0bd3..b37a792bc45 100644 --- a/framework/src/test/java/org/tron/core/services/interfaceOnPBFT/http/GetBandwidthPricesOnPBFTServletTest.java +++ b/framework/src/test/java/org/tron/core/services/interfaceOnPBFT/http/GetBandwidthPricesOnPBFTServletTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.apache.http.client.methods.HttpGet; @@ -16,6 +15,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetBandwidthPricesOnPBFTServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/interfaceOnPBFT/http/GetEnergyPricesOnPBFTServletTest.java b/framework/src/test/java/org/tron/core/services/interfaceOnPBFT/http/GetEnergyPricesOnPBFTServletTest.java index a2774095d94..71d7e7e4b0b 100644 --- a/framework/src/test/java/org/tron/core/services/interfaceOnPBFT/http/GetEnergyPricesOnPBFTServletTest.java +++ b/framework/src/test/java/org/tron/core/services/interfaceOnPBFT/http/GetEnergyPricesOnPBFTServletTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.apache.http.client.methods.HttpGet; @@ -16,6 +15,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetEnergyPricesOnPBFTServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/GetBandwidthPricesOnSolidityServletTest.java b/framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/GetBandwidthPricesOnSolidityServletTest.java index d1f2e33410f..890528b72e4 100644 --- a/framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/GetBandwidthPricesOnSolidityServletTest.java +++ b/framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/GetBandwidthPricesOnSolidityServletTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.apache.http.client.methods.HttpGet; @@ -16,6 +15,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetBandwidthPricesOnSolidityServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/GetEnergyPricesOnSolidityServletTest.java b/framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/GetEnergyPricesOnSolidityServletTest.java index 133a4b5cde5..b7310d065f3 100644 --- a/framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/GetEnergyPricesOnSolidityServletTest.java +++ b/framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/GetEnergyPricesOnSolidityServletTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.fail; import static org.tron.common.utils.client.utils.HttpMethed.createRequest; -import com.alibaba.fastjson.JSONObject; import java.io.UnsupportedEncodingException; import javax.annotation.Resource; import org.apache.http.client.methods.HttpGet; @@ -16,6 +15,7 @@ import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; public class GetEnergyPricesOnSolidityServletTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/BuildArgumentsTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/BuildArgumentsTest.java index 5ad2c85d181..753d93d47f4 100644 --- a/framework/src/test/java/org/tron/core/services/jsonrpc/BuildArgumentsTest.java +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/BuildArgumentsTest.java @@ -244,17 +244,6 @@ public void jacksonSerialize_doesNotExposeResolveDataOrThrowOnConflict() json.contains("resolveData")); } - /** Same guarantee for FastJSON, which also discovers bean getters. */ - @Test - public void fastjsonSerialize_doesNotExposeResolveDataOrThrowOnConflict() { - BuildArguments args = new BuildArguments(); - args.setInput("0xdeadbeef"); - args.setData("0xcafebabe"); - String json = com.alibaba.fastjson.JSON.toJSONString(args); - Assert.assertFalse("should not leak resolveData: " + json, - json.contains("resolveData")); - } - /** Validates the loser field too, not only the precedence winner. */ @Test public void resolveData_inputValidDataMalformed_throwsInvalidParams() { diff --git a/framework/src/test/java/org/tron/core/services/jsonrpc/CallArgumentsTest.java b/framework/src/test/java/org/tron/core/services/jsonrpc/CallArgumentsTest.java index 66fb8e0a0c7..4ebfc3c1872 100644 --- a/framework/src/test/java/org/tron/core/services/jsonrpc/CallArgumentsTest.java +++ b/framework/src/test/java/org/tron/core/services/jsonrpc/CallArgumentsTest.java @@ -231,16 +231,4 @@ public void jacksonSerialize_doesNotExposeResolveDataOrThrowOnConflict() Assert.assertFalse("should not leak resolveData: " + json, json.contains("resolveData")); } - - /** Same guarantee for FastJSON, which also discovers bean getters. */ - @Test - public void fastjsonSerialize_doesNotExposeResolveDataOrThrowOnConflict() { - CallArguments args = new CallArguments(); - args.setInput("0xdeadbeef"); - args.setData("0xcafebabe"); - String json = com.alibaba.fastjson.JSON.toJSONString(args); - Assert.assertFalse("should not leak resolveData: " + json, - json.contains("resolveData")); - } - } diff --git a/framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java b/framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java index 6a7aadaba01..c34d49d9009 100644 --- a/framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java +++ b/framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java @@ -1,27 +1,142 @@ package org.tron.core.services.ratelimiter; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.util.concurrent.RateLimiter; import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; import org.junit.AfterClass; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.tron.common.TestConstants; import org.tron.core.config.args.Args; public class GlobalRateLimiterTest { - @Test - public void testAcquire() throws Exception { + /** + * Reset GlobalRateLimiter's static state to known rates before each test. + * Static fields are initialized at class-load time from Args, so we must + * override them via reflection to guarantee test isolation. + */ + @Before + public void setUp() throws Exception { String[] a = new String[0]; - Args.setParam(a, "config.conf"); + Args.setParam(a, TestConstants.TEST_CONF); + resetGlobalRateLimiter(2.0, 1.0); + } + + private static void resetGlobalRateLimiter(double globalQps, double ipQps) throws Exception { + // Reset per-IP QPS value + Field ipQpsField = GlobalRateLimiter.class.getDeclaredField("IP_QPS"); + ipQpsField.setAccessible(true); + ipQpsField.set(null, ipQps); + + // Create a fresh rate limiter, then sleep one stable interval (1000/qps ms) so + // Guava's SmoothBursty accumulates exactly 1 stored permit. With 1 stored permit + // the first tryAcquire() consumes it (no advance of nextFreeTicket), and the second + // call pre-bills the next slot and still returns true — giving exactly floor(qps)=2 + // consecutive successes without touching Guava-internal fields. + RateLimiter rl = RateLimiter.create(globalQps); + Thread.sleep((long) (1000.0 / globalQps)); + + Field rateLimiterField = GlobalRateLimiter.class.getDeclaredField("rateLimiter"); + rateLimiterField.setAccessible(true); + rateLimiterField.set(null, rl); + + // Clear the per-IP cache so each test starts fresh + Field cacheField = GlobalRateLimiter.class.getDeclaredField("cache"); + cacheField.setAccessible(true); + Cache freshCache = CacheBuilder.newBuilder() + .maximumSize(10000).expireAfterWrite(1, TimeUnit.HOURS).build(); + cacheField.set(null, freshCache); + } + + private static RuntimeData runtimeDataFor(String ip) throws Exception { RuntimeData runtimeData = new RuntimeData(null); - Field field = runtimeData.getClass().getDeclaredField("address"); + Field field = runtimeData.getClass().getDeclaredField("address"); field.setAccessible(true); - field.set(runtimeData, "127.0.0.1"); - Assert.assertEquals(runtimeData.getRemoteAddr(), "127.0.0.1"); - GlobalRateLimiter.acquire(runtimeData); + field.set(runtimeData, ip == null ? "" : ip); + return runtimeData; + } + + /** + * Normal request: passes both IP and global limits. + */ + @Test + public void testNormalRequestPasses() throws Exception { + RuntimeData runtimeData = runtimeDataFor("10.0.0.1"); + Assert.assertTrue(GlobalRateLimiter.tryAcquire(runtimeData)); + } + + /** + * IP limit exhausted: second request from same IP is rejected without + * consuming a global token. A third request from a different IP must still + * pass because the global budget was not wasted. + * globalQps=2, ipQps=1 + */ + @Test + public void testIpLimitDoesNotWasteGlobalToken() throws Exception { + RuntimeData ip1 = runtimeDataFor("10.0.0.1"); + RuntimeData ip2 = runtimeDataFor("10.0.0.2"); + + // First request from 10.0.0.1: IP passes (1/1), global passes (1/2) + Assert.assertTrue(GlobalRateLimiter.tryAcquire(ip1)); + + // Second request from 10.0.0.1: IP exhausted → rejected, global NOT consumed + Assert.assertFalse(GlobalRateLimiter.tryAcquire(ip1)); + + // First request from 10.0.0.2: IP passes (1/1), global passes (2/2) + Assert.assertTrue(GlobalRateLimiter.tryAcquire(ip2)); + + // Any further request: global exhausted + Assert.assertFalse(GlobalRateLimiter.tryAcquire(runtimeDataFor("10.0.0.3"))); + } + + /** + * Multiple IPs each consume one global token and then hit their own IP limit. + * globalQps=2, ipQps=1: exactly 2 distinct IPs can succeed. + */ + @Test + public void testGlobalCapAcrossMultipleIps() throws Exception { + Assert.assertTrue(GlobalRateLimiter.tryAcquire(runtimeDataFor("1.1.1.1"))); + Assert.assertTrue(GlobalRateLimiter.tryAcquire(runtimeDataFor("1.1.1.2"))); + + // Global budget exhausted; a fresh IP is also rejected + Assert.assertFalse(GlobalRateLimiter.tryAcquire(runtimeDataFor("1.1.1.3"))); + } + + /** + * Request with no IP address bypasses the IP-level check and goes straight + * to the global limiter. + * globalQps=2: two no-IP requests succeed, third fails. + */ + @Test + public void testNoIpAddressFallsBackToGlobalOnly() throws Exception { + RuntimeData noIp = runtimeDataFor(""); + + Assert.assertTrue(GlobalRateLimiter.tryAcquire(noIp)); + Assert.assertTrue(GlobalRateLimiter.tryAcquire(noIp)); + Assert.assertFalse(GlobalRateLimiter.tryAcquire(noIp)); + } + + /** + * Per-IP limit is independent between different IPs. + * globalQps=10 (high), ipQps=1: each IP gets exactly one successful request. + */ + @Test + public void testPerIpLimitsAreIndependent() throws Exception { + resetGlobalRateLimiter(10.0, 1.0); + + Assert.assertTrue(GlobalRateLimiter.tryAcquire(runtimeDataFor("2.2.2.1"))); + Assert.assertFalse(GlobalRateLimiter.tryAcquire(runtimeDataFor("2.2.2.1"))); + + Assert.assertTrue(GlobalRateLimiter.tryAcquire(runtimeDataFor("2.2.2.2"))); + Assert.assertFalse(GlobalRateLimiter.tryAcquire(runtimeDataFor("2.2.2.2"))); } @AfterClass public static void destroy() { Args.clearParam(); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/services/ratelimiter/RateLimiterInterceptorTest.java b/framework/src/test/java/org/tron/core/services/ratelimiter/RateLimiterInterceptorTest.java new file mode 100644 index 00000000000..6cf02a25050 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/ratelimiter/RateLimiterInterceptorTest.java @@ -0,0 +1,235 @@ +package org.tron.core.services.ratelimiter; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.grpc.Attributes; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.Status; +import java.lang.reflect.Field; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.tron.common.TestConstants; +import org.tron.core.config.args.Args; +import org.tron.core.services.ratelimiter.adapter.IPreemptibleRateLimiter; +import org.tron.core.services.ratelimiter.adapter.IRateLimiter; + +/** + * Unit tests for the rate-limiting logic in + * {@link RateLimiterInterceptor#interceptCall}. + * + *

The key invariants under test: + *

    + *
  1. Per-endpoint check runs before the global check — a per-endpoint + * rejection must not consume any global IP/QPS token.
  2. + *
  3. A {@link IPreemptibleRateLimiter} permit is always released: + *
      + *
    • immediately, when the global limiter rejects after per-endpoint passes;
    • + *
    • in the catch block, when {@code next.startCall()} throws after both pass;
    • + *
    • via {@code onComplete()} / {@code onCancel()} on the returned listener + * for successful calls.
    • + *
    + *
  4. + *
+ */ +@SuppressWarnings("unchecked") +public class RateLimiterInterceptorTest { + + private static final String METHOD_NAME = "tron.api.Wallet/GetNowBlock"; + private static final String KEY_RPC = "rpc_"; + + private RateLimiterInterceptor interceptor; + private RateLimiterContainer container; + + private ServerCall call; + private Metadata headers; + private ServerCallHandler next; + + @AfterClass + public static void tearDown() { + Args.clearParam(); + } + + /** + * GlobalRateLimiter's static initializer calls Args.getInstance().getRateLimiterGlobalQps(). + * Without Args being initialized the default QPS is 0, causing RateLimiter.create(0) to throw. + * Initializing Args here (before the class is first loaded inside each test method) prevents + * the static initialization failure that would otherwise break mockStatic(). + */ + @Before + public void setUp() throws Exception { + Args.setParam(new String[0], TestConstants.TEST_CONF); + interceptor = new RateLimiterInterceptor(); + container = new RateLimiterContainer(); + Field f = RateLimiterInterceptor.class.getDeclaredField("container"); + f.setAccessible(true); + f.set(interceptor, container); + + call = Mockito.mock(ServerCall.class); + MethodDescriptor descriptor = Mockito.mock(MethodDescriptor.class); + when(call.getMethodDescriptor()).thenReturn(descriptor); + when(descriptor.getFullMethodName()).thenReturn(METHOD_NAME); + // Attributes.EMPTY causes RuntimeData to catch the NPE and set address="" + when(call.getAttributes()).thenReturn(Attributes.EMPTY); + + headers = new Metadata(); + next = Mockito.mock(ServerCallHandler.class); + } + + /** + * Per-endpoint rejects → GlobalRateLimiter must NOT be called. + * No permit was acquired, so release() must not be called either. + */ + @Test + public void testPerEndpointRejectedDoesNotConsumeGlobalQuota() { + IPreemptibleRateLimiter perEndpoint = Mockito.mock(IPreemptibleRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(false); + container.add(KEY_RPC, METHOD_NAME, perEndpoint); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + interceptor.interceptCall(call, headers, next); + + globalMock.verify(() -> GlobalRateLimiter.tryAcquire(any()), never()); + verify(perEndpoint, never()).release(); + } + } + + /** + * Non-preemptible per-endpoint rejects → global not called. + */ + @Test + public void testNonPreemptiblePerEndpointRejectedDoesNotConsumeGlobal() { + IRateLimiter perEndpoint = Mockito.mock(IRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(false); + container.add(KEY_RPC, METHOD_NAME, perEndpoint); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + interceptor.interceptCall(call, headers, next); + + globalMock.verify(() -> GlobalRateLimiter.tryAcquire(any()), never()); + } + } + + /** + * Per-endpoint (IPreemptibleRateLimiter) acquires, but global rejects. + * The early-return rejection path must release the permit immediately. + */ + @Test + public void testGlobalRejectedReleasesPreemptiblePermit() { + IPreemptibleRateLimiter perEndpoint = Mockito.mock(IPreemptibleRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(true); + container.add(KEY_RPC, METHOD_NAME, perEndpoint); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + globalMock.when(() -> GlobalRateLimiter.tryAcquire(any())).thenReturn(false); + + interceptor.interceptCall(call, headers, next); + + verify(perEndpoint, times(1)).release(); + } + } + + /** + * Both limiters pass but {@code next.startCall()} throws. + * The catch block must: + * - release the permit to prevent a permanent semaphore leak (the + * SimpleForwardingServerCallListener that holds the release logic is never + * assigned when the exception is thrown); + * - close the call with INTERNAL so the client fails immediately instead of + * waiting for the transport-level deadline. + */ + @Test + public void testStartCallExceptionReleasesPermitAndClosesCall() throws Exception { + IPreemptibleRateLimiter perEndpoint = Mockito.mock(IPreemptibleRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(true); + container.add(KEY_RPC, METHOD_NAME, perEndpoint); + when(next.startCall(any(), any())).thenThrow(new RuntimeException("handler crash")); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + globalMock.when(() -> GlobalRateLimiter.tryAcquire(any())).thenReturn(true); + + interceptor.interceptCall(call, headers, next); + + verify(perEndpoint, times(1)).release(); + ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(Status.class); + verify(call, times(1)).close(statusCaptor.capture(), any(Metadata.class)); + assertEquals(Status.Code.INTERNAL, statusCaptor.getValue().getCode()); + } + } + + /** + * Normal successful flow: both pass, {@code next.startCall()} succeeds. + * The returned listener's {@code onComplete()} must release the permit exactly once. + */ + @Test + public void testListenerReleasesPermitOnComplete() throws Exception { + IPreemptibleRateLimiter perEndpoint = Mockito.mock(IPreemptibleRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(true); + container.add(KEY_RPC, METHOD_NAME, perEndpoint); + + ServerCall.Listener delegate = Mockito.mock(ServerCall.Listener.class); + when(next.startCall(any(), any())).thenReturn(delegate); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + globalMock.when(() -> GlobalRateLimiter.tryAcquire(any())).thenReturn(true); + + ServerCall.Listener listener = interceptor.interceptCall(call, headers, next); + listener.onComplete(); + + verify(perEndpoint, times(1)).release(); + } + } + + /** + * Normal successful flow: both pass, {@code next.startCall()} succeeds. + * The returned listener's {@code onCancel()} must release the permit exactly once. + */ + @Test + public void testListenerReleasesPermitOnCancel() throws Exception { + IPreemptibleRateLimiter perEndpoint = Mockito.mock(IPreemptibleRateLimiter.class); + when(perEndpoint.tryAcquire(any(RuntimeData.class))).thenReturn(true); + container.add(KEY_RPC, METHOD_NAME, perEndpoint); + + ServerCall.Listener delegate = Mockito.mock(ServerCall.Listener.class); + when(next.startCall(any(), any())).thenReturn(delegate); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + globalMock.when(() -> GlobalRateLimiter.tryAcquire(any())).thenReturn(true); + + ServerCall.Listener listener = interceptor.interceptCall(call, headers, next); + listener.onCancel(); + + verify(perEndpoint, times(1)).release(); + } + } + + /** + * No per-endpoint limiter configured (null) → GlobalRateLimiter is still called once. + */ + @Test + public void testNullRateLimiterConsultsOnlyGlobal() throws Exception { + // Nothing registered in container — container.get() returns null + ServerCall.Listener delegate = Mockito.mock(ServerCall.Listener.class); + when(next.startCall(any(), any())).thenReturn(delegate); + + try (MockedStatic globalMock = mockStatic(GlobalRateLimiter.class)) { + globalMock.when(() -> GlobalRateLimiter.tryAcquire(any())).thenReturn(true); + + interceptor.interceptCall(call, headers, next); + + globalMock.verify(() -> GlobalRateLimiter.tryAcquire(any()), times(1)); + } + } +} diff --git a/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java b/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java index 22d8d50483f..69a6c688200 100644 --- a/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java +++ b/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java @@ -2,8 +2,6 @@ import com.google.common.cache.Cache; import com.google.common.util.concurrent.RateLimiter; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.junit.Assert; @@ -43,32 +41,24 @@ public void testStrategy() { @Test public void testIPQPSRateLimiterAdapter() { - String paramString = "qps=5"; + String paramString = "qps=1"; IPQPSRateLimiterAdapter adapter = new IPQPSRateLimiterAdapter(paramString); IPQpsStrategy strategy = (IPQpsStrategy) ReflectUtils.getFieldObject(adapter, "strategy"); - Assert.assertEquals(5.0d, Double + Assert.assertEquals(1.0d, Double .parseDouble(ReflectUtils.getFieldValue(strategy.getMapParams().get("qps"), "value").toString()), 0.0); - long t0 = System.currentTimeMillis(); - for (int i = 0; i < 20; i++) { - strategy.acquire("1.2.3.4"); - } - long t1 = System.currentTimeMillis(); - Assert.assertTrue(t1 - t0 > 3500); - - t0 = System.currentTimeMillis(); - for (int i = 0; i < 20; i++) { - if (i % 2 == 0) { - strategy.acquire("1.2.3.4"); - } else { - strategy.acquire("4.3.2.1"); - } - } - t1 = System.currentTimeMillis(); - Assert.assertTrue(t1 - t0 > 1500); + boolean flag = strategy.tryAcquire("1.2.3.4"); + Assert.assertTrue(flag); + + flag = strategy.tryAcquire("1.2.3.4"); + Assert.assertFalse(flag); + + flag = strategy.tryAcquire("1.2.3.5"); + Assert.assertTrue(flag); + Cache ipLimiter = (Cache) ReflectUtils .getFieldObject(strategy, "ipLimiter"); Assert.assertEquals(2, ipLimiter.size()); @@ -83,14 +73,14 @@ public void testGlobalPreemptibleAdapter() { Assert.assertEquals(1, Integer.parseInt( ReflectUtils.getFieldValue(strategy1.getMapParams().get("permit"), "value").toString())); - boolean first = strategy1.acquire(); + boolean first = strategy1.tryAcquire(); Assert.assertTrue(first); - boolean second = strategy1.acquire(); + boolean second = strategy1.tryAcquire(); Assert.assertFalse(second); strategy1.release(); - boolean secondAfterOneRelease = strategy1.acquire(); + boolean secondAfterOneRelease = strategy1.tryAcquire(); Assert.assertTrue(secondAfterOneRelease); String paramString2 = "permit=3"; @@ -101,18 +91,18 @@ public void testGlobalPreemptibleAdapter() { ReflectUtils.getFieldValue(strategy2.getMapParams().get("permit"), "value").toString())); - first = strategy2.acquire(); + first = strategy2.tryAcquire(); Assert.assertTrue(first); - second = strategy2.acquire(); + second = strategy2.tryAcquire(); Assert.assertTrue(second); - boolean third = strategy2.acquire(); + boolean third = strategy2.tryAcquire(); Assert.assertTrue(third); - boolean four = strategy2.acquire(); + boolean four = strategy2.tryAcquire(); Assert.assertFalse(four); strategy2.release(); - boolean fourAfterOneRelease = strategy2.acquire(); + boolean fourAfterOneRelease = strategy2.tryAcquire(); Assert.assertTrue(fourAfterOneRelease); Semaphore sp = (Semaphore) ReflectUtils.getFieldObject(strategy2, "sp"); @@ -121,35 +111,32 @@ public void testGlobalPreemptibleAdapter() { strategy2.release(); strategy2.release(); Assert.assertEquals(3, sp.availablePermits()); - } @Test - public void testQpsRateLimiterAdapter() { - String paramString = "qps=5"; + public void testQpsRateLimiterAdapter() throws Exception { + String paramString = "qps=1"; QpsRateLimiterAdapter adapter = new QpsRateLimiterAdapter(paramString); QpsStrategy strategy = (QpsStrategy) ReflectUtils.getFieldObject(adapter, "strategy"); - Assert.assertEquals(5, Double + Assert.assertEquals(1, Double .parseDouble(ReflectUtils.getFieldValue(strategy.getMapParams().get("qps"), "value").toString()), 0.0); - strategy.acquire(); - - long t0 = System.currentTimeMillis(); - CountDownLatch latch = new CountDownLatch(20); - ExecutorService pool = ExecutorServiceManager.newFixedThreadPool("adaptor-test", 20); - try { - for (int i = 0; i < 20; i++) { - pool.execute(new AdaptorThread(latch, strategy)); - } - latch.await(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } finally { - ExecutorServiceManager.shutdownAndAwaitTermination(pool, "adaptor-test"); - } - long t1 = System.currentTimeMillis(); - Assert.assertTrue(t1 - t0 > 4000); + + Thread.sleep(1000); + + boolean flag = strategy.tryAcquire(); + Assert.assertTrue(flag); + + // Guava SmoothBursty "pre-bills" the next slot when stored permits are + // consumed without cost: nextFreeTicketMicros stays at the resync time, + // so the immediately following call still passes (waitLength = 0) while + // advancing the ticket to 1 s in the future. + flag = strategy.tryAcquire(); + Assert.assertTrue(flag); + + flag = strategy.tryAcquire(); + Assert.assertFalse(flag); } } diff --git a/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorThread.java b/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorThread.java deleted file mode 100644 index afcf21e7510..00000000000 --- a/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorThread.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.tron.core.services.ratelimiter.adaptor; - -import java.util.concurrent.CountDownLatch; -import org.tron.core.services.ratelimiter.strategy.QpsStrategy; - -class AdaptorThread implements Runnable { - - private CountDownLatch latch; - private QpsStrategy strategy; - - public AdaptorThread(CountDownLatch latch, QpsStrategy strategy) { - this.latch = latch; - this.strategy = strategy; - } - - @Override - public void run() { - strategy.acquire(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - latch.countDown(); - } -} diff --git a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java index c92862921f8..8064011e21d 100644 --- a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java +++ b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java @@ -9,7 +9,6 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -36,7 +35,6 @@ public class TransactionRegisterTest { @Before public void init() { - Args.getInstance().setActuatorSet(new HashSet<>()); TransactionRegister.resetForTesting(); } diff --git a/framework/src/test/java/org/tron/core/zksnark/MerkleTreeTest.java b/framework/src/test/java/org/tron/core/zksnark/MerkleTreeTest.java index faea3780135..e21ba8010b5 100644 --- a/framework/src/test/java/org/tron/core/zksnark/MerkleTreeTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/MerkleTreeTest.java @@ -1,6 +1,5 @@ package org.tron.core.zksnark; -import com.alibaba.fastjson.JSONArray; import com.google.common.base.Charsets; import com.google.common.collect.Lists; import com.google.common.io.Files; @@ -21,6 +20,7 @@ import org.tron.core.capsule.IncrementalMerkleVoucherCapsule; import org.tron.core.capsule.PedersenHashCapsule; import org.tron.core.config.args.Args; +import org.tron.json.JSONArray; import org.tron.protos.contract.ShieldContract.PedersenHash; public class MerkleTreeTest extends BaseTest { diff --git a/framework/src/test/java/org/tron/core/zksnark/SendCoinShieldTest.java b/framework/src/test/java/org/tron/core/zksnark/SendCoinShieldTest.java index e7dfa06d094..8693bf0716d 100644 --- a/framework/src/test/java/org/tron/core/zksnark/SendCoinShieldTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/SendCoinShieldTest.java @@ -2,7 +2,6 @@ import static org.tron.core.capsule.TransactionCapsule.getShieldTransactionHashIgnoreTypeException; -import com.alibaba.fastjson.JSONArray; import com.google.common.base.Charsets; import com.google.common.collect.Lists; import com.google.common.io.Files; @@ -80,6 +79,7 @@ import org.tron.core.zen.note.NoteEncryption; import org.tron.core.zen.note.NoteEncryption.Encryption; import org.tron.core.zen.note.OutgoingPlaintext; +import org.tron.json.JSONArray; import org.tron.protos.Protocol; import org.tron.protos.Protocol.AccountType; import org.tron.protos.Protocol.Transaction.Contract.ContractType; diff --git a/framework/src/test/java/org/tron/json/JsonTest.java b/framework/src/test/java/org/tron/json/JsonTest.java new file mode 100644 index 00000000000..2a6d73931be --- /dev/null +++ b/framework/src/test/java/org/tron/json/JsonTest.java @@ -0,0 +1,403 @@ +package org.tron.json; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import com.fasterxml.jackson.core.StreamReadConstraints; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import org.junit.Test; + +/** + * Tests for Jackson {@code JsonReadFeature} compatibility with Fastjson 1.x. + */ +public class JsonTest { + + @Test + public void testUnquotedFieldNames() { + assertEquals(1, JSON.parseObject("{a:1}").getIntValue("a")); + } + + @Test + public void testDupFieldNames() { + assertEquals(2, JSON.parseObject("{a:1, a:2 }").getIntValue("a")); + assertEquals(1, JSON.parseObject("{a:2, a:1 }").getIntValue("a")); + } + + @Test + public void testSingleQuotes() { + assertEquals(1, JSON.parseObject("{'a':'1'}").getIntValue("a")); + } + + @Test + public void testTrailingComma() { + assertEquals(1, JSON.parseObject("{\"a\":1,}").getIntValue("a")); + assertEquals(2, JSON.parseArray("[1,2,]").size()); + assertThrows(JSONException.class, () -> JSON.parseObject("{c:'NULL',,,,,,}")); + assertThrows(JSONException.class, () -> JSON.parseObject("[1,,2]")); + } + + @Test + public void testNonNumericNumbers() { + assertThrows(JSONException.class, () -> JSON.parseObject("{\"a\":NaN}")); + assertThrows(JSONException.class, () -> JSON.parseArray("[1, NaN, 2]")); + assertThrows(JSONException.class, () -> JSON.parseObject("{outer:{inner:NaN}}")); + assertThrows(JSONException.class, () -> JSON.parseObject("{b:Infinity}")); + assertThrows(JSONException.class, () -> JSON.parseObject("{c:-Infinity}")); + assertThrows(JSONException.class, () -> JSON.parseArray("[Infinity]")); + } + + @Test + public void testLeadingNumbers() { + JSONObject o = JSON.parseObject("{'a':+1,b:-2,c:.3,d:-.4,e:+.5,f:+6.,h:007}"); + assertNotNull(o); + assertEquals(1, o.getIntValue("a")); + assertEquals(-2, o.getIntValue("b")); + assertEquals("0.3", o.getBigDecimal("c").toPlainString()); + assertEquals("-0.4", o.getBigDecimal("d").toPlainString()); + assertEquals("0.5", o.getBigDecimal("e").toPlainString()); + assertEquals(6, o.getIntValue("f")); + assertEquals(7, o.getIntValue("h")); + } + + @Test + public void testUnescapedControlChars() { + JSONObject obj = JSON.parseObject("{'a':'line1\n\tline2'}"); + assertNotNull(obj); + assertEquals("line1\n\tline2", obj.getString("a")); + obj = JSON.parseObject("{\"a\":\"\u0001\"}"); + assertNotNull(obj); + assertEquals("\u0001", obj.getString("a")); + } + + @Test + public void testBackslashEscapeAnyChar() { + assertThrows(JSONException.class, () -> JSON.parseObject("{\"a\":\"\\q\"}")); + } + + @Test + public void testComment() { + JSONObject obj = JSON.parseObject("{\"a\":1} \n\t // this is a comment"); + assertNotNull(obj); + assertEquals(1, obj.getIntValue("a")); + obj = JSON.parseObject("{/* comment */\"a\":1}"); + assertNotNull(obj); + assertEquals(1, obj.getIntValue("a")); + } + + + @Test + public void testParseNull() { + assertNull(JSON.parseObject(null)); + assertNull(JSON.parseObject("")); + assertNull(JSON.parseObject(" ")); + assertNull(JSON.parseObject("\n\t")); + assertNull(JSON.parseObject("null")); + assertThrows(JSONException.class, () -> JSON.parseObject("NULL")); + } + + @Test + public void testThrows() { + assertThrows(JSONException.class, () -> JSON.parseObject("{a:abc}")); + assertThrows(JSONException.class, () -> JSON.parseObject("{a:TRUE}")); + assertThrows(JSONException.class, () -> JSON.parseObject("{a:FALSE}")); + assertThrows(JSONException.class, () -> JSON.parseObject("[1,,3]")); + // valid JSON but wrong shape — exercises the single-arg JSONException constructor + assertThrows(JSONException.class, () -> JSON.parseObject("[1,2,3]")); + } + + @Test + public void testUppercaseNullRejected() { + assertThrows(JSONException.class, () -> JSON.parseObject("{\"a\":NULL,\"b\":1}")); + assertThrows(JSONException.class, () -> JSON.parseArray("[NULL, null]")); + assertThrows(JSONException.class, () -> JSON.parse("NULL")); + // String value containing the substring "NULL" must be preserved verbatim. + JSONObject q = JSON.parseObject("{\"k\":\"NULL\"}"); + assertEquals("NULL", q.getString("k")); + } + + @Test + public void testParseHelpers() { + assertNotNull(JSON.parse("{\"a\":1}")); + assertEquals(1, JSON.parse("{\"a\":1}").get("a").intValue()); + assertNull(JSON.parse(null)); + assertNull(JSON.parse("null")); + + // JSONObject.parseObject delegate + assertEquals(1, JSONObject.parseObject("{\"a\":1}").getIntValue("a")); + + // JSONArray.parseArray null inputs + assertNull(JSONArray.parseArray(null)); + assertNull(JSONArray.parseArray("null")); + } + + @Test + public void testToJSONString() { + assertEquals("null", JSON.toJSONString(null)); + assertEquals("{\"a\":1}", JSON.toJSONString(new JSONObject().put("a", 1))); + assertEquals("[1,2]", JSON.toJSONString(JSON.parseArray("[1,2]"))); + assertEquals("\"hi\"", JSON.toJSONString("hi")); + assertEquals("0", JSON.toJSONString(new Date(0))); + + // pretty variant differs from compact for containers (exercises the pretty branch) + JSONObject obj = new JSONObject().put("a", 1); + assertNotEquals(JSON.toJSONString(obj, false), JSON.toJSONString(obj, true)); + JSONArray arr = JSON.parseArray("[1,2]"); + assertNotEquals(JSON.toJSONString(arr, false), JSON.toJSONString(arr, true)); + // pretty for primitive types is just the JSON literal + assertEquals("\"hi\"", JSON.toJSONString("hi", true)); + } + + @Test + public void testJsonObjectGetters() { + JSONObject o = JSON.parseObject( + "{'b':true,'i':42,'l':9000000000,'s':'hi','nested':{'k':'v'},'arr':[1,2]}"); + assertNotNull(o); + + assertEquals(Boolean.TRUE, o.getBoolean("b")); + assertEquals(Integer.valueOf(42), o.getInteger("i")); + assertEquals(42L, o.getLongValue("i")); + assertEquals(Long.valueOf(9_000_000_000L), o.getLong("l")); + assertEquals("hi", o.getString("s")); + assertEquals("v", o.getJSONObject("nested").getString("k")); + assertEquals(2, o.getJSONArray("arr").size()); + + // getString on container serializes to JSON + assertTrue(o.getString("nested").contains("\"k\"")); + assertTrue(o.getString("arr").contains("1")); + + // missing keys → null / 0 for primitive accessors + assertNull(o.getString("missing")); + assertNull(o.getJSONObject("missing")); + assertNull(o.getJSONArray("missing")); + assertNull(o.getBoolean("missing")); + assertNull(o.getInteger("missing")); + assertNull(o.getLong("missing")); + assertNull(o.getBigDecimal("missing")); + assertEquals(0, o.getIntValue("missing")); + assertEquals(0L, o.getLongValue("missing")); + + // getObject(key, Class) — JSONObject/JSONArray short-circuits + POJO via Jackson + assertEquals("v", o.getObject("nested", JSONObject.class).getString("k")); + assertEquals(2, o.getObject("arr", JSONArray.class).size()); + assertNull(o.getObject("missing", Pojo.class)); + + Pojo nested = JSON.parseObject("{\"obj\":{\"name\":\"x\"}}").getObject("obj", Pojo.class); + assertEquals("x", nested.name); + + // Fastjson compat: getJSONObject / getJSONArray auto-parse stringified JSON + JSONObject autoParse = JSON.parseObject( + "{'jsonStr':'{\"k\":\"v\"}','arrStr':'[1,2,3]'}"); + assertEquals("v", autoParse.getJSONObject("jsonStr").getString("k")); + assertEquals(3, autoParse.getJSONArray("arrStr").size()); + } + + @Test + public void testJsonObjectPutAndEquality() { + JSONObject o = new JSONObject(); + o.put("s", "str"); + o.put("b", Boolean.TRUE); + o.put("i", Integer.valueOf(1)); + o.put("l", Long.valueOf(2L)); + o.put("nested", new JSONObject().put("k", "v")); + o.put("arr", new JSONArray().add(new JSONObject().put("x", 1))); + o.put("o_json", (Object) new JSONObject().put("k2", "v2")); + o.put("o_str", (Object) "fallthrough"); + o.put("list", Arrays.asList("raw", null, new JSONObject().put("a", 1), + new JSONArray())); + + assertEquals(9, o.size()); + assertEquals(9, o.keySet().size()); + assertTrue(o.containsKey("s")); + + // null put removes the key for every typed overload + o.put("s", (String) null); + o.put("b", (Boolean) null); + o.put("i", (Integer) null); + o.put("l", (Long) null); + o.put("nested", (JSONObject) null); + o.put("arr", (JSONArray) null); + o.put("o_json", (Object) null); + o.put("list", (List) null); + assertFalse(o.containsKey("s")); + assertEquals(1, o.size()); // only "o_str" remains + + // remove returns the converted value + o.put("k", 7); + assertEquals(7, o.remove("k")); + assertNull(o.remove("nonexistent")); + + // unwrap, toString, toJSONString + assertNotNull(o.unwrap()); + assertEquals(o.toString(), o.toJSONString()); + + // equals / hashCode round-trip + JSONObject copy = JSON.parseObject(o.toString()); + assertEquals(o, copy); + assertEquals(o.hashCode(), copy.hashCode()); + assertNotEquals(o, "not a json"); + assertNotEquals(o, null); + assertEquals(o, o); + } + + @Test + public void testJsonArrayOps() { + JSONArray arr = new JSONArray(); + arr.add(new JSONObject().put("k", "v")); + arr.add(null); // becomes a JSON null node + + assertEquals(2, arr.size()); + assertEquals("v", arr.getJSONObject(0).getString("k")); + assertNull(arr.getJSONObject(1)); + assertTrue(arr.getString(0).contains("\"k\"")); + assertNull(arr.getString(1)); + + // iterator covers JSONObject + null branches + int count = 0; + for (Object e : arr) { + if (count == 0) { + assertTrue(e instanceof JSONObject); + } else { + assertNull(e); + } + count++; + } + assertEquals(2, count); + + // toString / toJSONString equivalence + unwrap + assertEquals(arr.toString(), arr.toJSONString()); + assertNotNull(arr.unwrap()); + assertEquals(2, arr.unwrap().size()); + + // equals / hashCode round-trip + JSONArray copy = JSON.parseArray(arr.toString()); + assertEquals(arr, copy); + assertEquals(arr.hashCode(), copy.hashCode()); + assertNotEquals("not array", arr); + assertEquals(arr, arr); + + // Fastjson compat: getJSONObject auto-parses stringified JSON elements + JSONArray stringified = JSON.parseArray("[\"{\\\"k\\\":\\\"v\\\"}\"]"); + assertEquals("v", stringified.getJSONObject(0).getString("k")); + } + + @Test + public void testTypeUtilsCoercion() { + // null inputs across all coercers + assertNull(TypeUtils.castToBoolean(null)); + assertNull(TypeUtils.castToInt(null)); + assertNull(TypeUtils.castToLong(null)); + assertNull(TypeUtils.castToBigDecimal(null)); + + // direct passthrough (Boolean / Number / BigDecimal / BigInteger) + assertEquals(Boolean.TRUE, TypeUtils.castToBoolean(Boolean.TRUE)); + assertEquals(Boolean.TRUE, TypeUtils.castToBoolean(Integer.valueOf(1))); + assertEquals(Boolean.FALSE, TypeUtils.castToBoolean(Integer.valueOf(0))); + assertEquals(Boolean.TRUE, TypeUtils.castToBoolean(new BigDecimal("1"))); + assertEquals(Integer.valueOf(7), TypeUtils.castToInt(Integer.valueOf(7))); + assertEquals(Integer.valueOf(7), TypeUtils.castToInt(new BigDecimal("7"))); + assertEquals(Integer.valueOf(7), TypeUtils.castToInt(Long.valueOf(7L))); // Number branch + assertEquals(Long.valueOf(7L), TypeUtils.castToLong(Long.valueOf(7L))); + assertEquals(Long.valueOf(7L), TypeUtils.castToLong(new BigDecimal("7"))); + assertEquals(Long.valueOf(7L), TypeUtils.castToLong(Integer.valueOf(7))); // Number branch + + // string-null literals → null + assertNull(TypeUtils.castToBoolean("")); + assertNull(TypeUtils.castToBoolean("null")); + assertNull(TypeUtils.castToBoolean("NULL")); + assertNull(TypeUtils.castToInt("")); + assertNull(TypeUtils.castToInt("null")); + assertNull(TypeUtils.castToInt("NULL")); + assertNull(TypeUtils.castToLong("")); + assertNull(TypeUtils.castToLong("null")); + assertNull(TypeUtils.castToLong("NULL")); + assertNull(TypeUtils.castToBigDecimal("")); + assertNull(TypeUtils.castToBigDecimal("null")); + + // boolean string parsing — covers all token branches + assertEquals(Boolean.TRUE, TypeUtils.castToBoolean("true")); + assertEquals(Boolean.TRUE, TypeUtils.castToBoolean("TRUE")); + assertEquals(Boolean.TRUE, TypeUtils.castToBoolean("1")); + assertEquals(Boolean.FALSE, TypeUtils.castToBoolean("false")); + assertEquals(Boolean.FALSE, TypeUtils.castToBoolean("FALSE")); + assertEquals(Boolean.FALSE, TypeUtils.castToBoolean("0")); + assertEquals(Boolean.TRUE, TypeUtils.castToBoolean("Y")); + assertEquals(Boolean.TRUE, TypeUtils.castToBoolean("T")); + assertEquals(Boolean.FALSE, TypeUtils.castToBoolean("F")); + assertEquals(Boolean.FALSE, TypeUtils.castToBoolean("N")); + + // numeric string — comma stripping + trailing-zero stripping (Fastjson compat) + assertEquals(Integer.valueOf(1000), TypeUtils.castToInt("1,000")); + assertEquals(Integer.valueOf(1), TypeUtils.castToInt("1.0")); + assertEquals(Long.valueOf(2_000L), TypeUtils.castToLong("2,000")); + assertEquals(Long.valueOf(9_000_000_000L), TypeUtils.castToLong("9000000000")); + + // Boolean → numeric + assertEquals(Integer.valueOf(1), TypeUtils.castToInt(Boolean.TRUE)); + assertEquals(Integer.valueOf(0), TypeUtils.castToInt(Boolean.FALSE)); + assertEquals(Long.valueOf(1L), TypeUtils.castToLong(Boolean.TRUE)); + assertEquals(Long.valueOf(0L), TypeUtils.castToLong(Boolean.FALSE)); + + // BigDecimal: NaN / Infinity → null; BigInteger conversion; string with comma + assertEquals(new BigDecimal("3.14"), + TypeUtils.castToBigDecimal(new BigDecimal("3.14"))); + assertEquals(new BigDecimal(BigInteger.TEN), + TypeUtils.castToBigDecimal(BigInteger.TEN)); + assertNull(TypeUtils.castToBigDecimal(Float.NaN)); + assertNull(TypeUtils.castToBigDecimal(Float.POSITIVE_INFINITY)); + assertNull(TypeUtils.castToBigDecimal(Double.NaN)); + assertNull(TypeUtils.castToBigDecimal(Double.NEGATIVE_INFINITY)); + assertEquals(new BigDecimal("1000.5"), TypeUtils.castToBigDecimal("1,000.5")); + assertEquals(new BigDecimal("123"), TypeUtils.castToBigDecimal(Integer.valueOf(123))); + + // intValue / longValue helpers — null + normal scale + assertEquals(0, TypeUtils.intValue(null)); + assertEquals(0L, TypeUtils.longValue(null)); + assertEquals(7, TypeUtils.intValue(new BigDecimal("7"))); + assertEquals(7L, TypeUtils.longValue(new BigDecimal("7"))); + } + + @Test + public void testJsonMapperHasConfiguredConstraints() { + StreamReadConstraints sr = JSON.MAPPER.getFactory().streamReadConstraints(); + assertEquals(100, sr.getMaxNestingDepth()); + assertEquals(100_000L, sr.getMaxTokenCount()); + } + + @Test + public void testParseObjectRejectsOverDepth() { + StringBuilder open = new StringBuilder(); + StringBuilder close = new StringBuilder(); + for (int i = 0; i < 120; i++) { + open.append("{\"a\":"); + close.append("}"); + } + String deep = open + "1" + close; + JSONException e = assertThrows(JSONException.class, () -> JSON.parseObject(deep)); + assertTrue(e.getMessage().toLowerCase(Locale.ROOT).contains("depth")); + } + + @Test + public void testParseArrayRejectsOverTokenCount() { + StringBuilder sb = new StringBuilder("[0"); + for (int i = 1; i < 100_500; i++) { + sb.append(",0"); + } + sb.append(']'); + JSONException e = assertThrows(JSONException.class, () -> JSON.parseArray(sb.toString())); + assertTrue(e.getMessage().toLowerCase(Locale.ROOT).contains("token")); + } + + public static class Pojo { + public String name; + } +} diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 34e0bbce16c..11c585cab6c 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -70,14 +70,6 @@ - - - - - - - - @@ -187,9 +179,9 @@ - - - + + + @@ -197,9 +189,9 @@ - - - + + + @@ -207,9 +199,9 @@ - - - + + + @@ -217,9 +209,9 @@ - - - + + + @@ -227,15 +219,15 @@ - - - + + + - - + + - - + + @@ -243,15 +235,15 @@ - - - + + + - - + + - - + + @@ -259,15 +251,15 @@ - - - + + + - - + + - - + +