() {
+
+ @Override
+ protected int compareParameters(MediaType mediaType1, MediaType mediaType2) {
+ double quality1 = mediaType1.getQualityValue();
+ double quality2 = mediaType2.getQualityValue();
+ int qualityComparison = Double.compare(quality2, quality1);
+ if (qualityComparison != 0) {
+ return qualityComparison; // audio/*;q=0.7 < audio/*;q=0.3
+ }
+ return super.compareParameters(mediaType1, mediaType2);
+ }
+ };
+
+ static {
+ // Not using "valueOf' to avoid static init cost
+ ALL = new MediaType("*", "*");
+ APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
+ APPLICATION_CBOR = new MediaType("application", "cbor");
+ APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
+ APPLICATION_GRAPHQL = new MediaType("application", "graphql+json");
+ APPLICATION_JSON = new MediaType("application", "json");
+ APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
+ APPLICATION_NDJSON = new MediaType("application", "x-ndjson");
+ APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream");
+ APPLICATION_PDF = new MediaType("application", "pdf");
+ APPLICATION_PROBLEM_JSON = new MediaType("application", "problem+json");
+ APPLICATION_PROBLEM_JSON_UTF8 = new MediaType("application", "problem+json",
+ StandardCharsets.UTF_8);
+ APPLICATION_PROBLEM_XML = new MediaType("application", "problem+xml");
+ APPLICATION_RSS_XML = new MediaType("application", "rss+xml");
+ APPLICATION_STREAM_JSON = new MediaType("application", "stream+json");
+ APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml");
+ APPLICATION_XML = new MediaType("application", "xml");
+ IMAGE_GIF = new MediaType("image", "gif");
+ IMAGE_JPEG = new MediaType("image", "jpeg");
+ IMAGE_PNG = new MediaType("image", "png");
+ MULTIPART_FORM_DATA = new MediaType("multipart", "form-data");
+ MULTIPART_MIXED = new MediaType("multipart", "mixed");
+ MULTIPART_RELATED = new MediaType("multipart", "related");
+ TEXT_EVENT_STREAM = new MediaType("text", "event-stream");
+ TEXT_HTML = new MediaType("text", "html");
+ TEXT_MARKDOWN = new MediaType("text", "markdown");
+ TEXT_PLAIN = new MediaType("text", "plain");
+ TEXT_XML = new MediaType("text", "xml");
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given primary type.
+ * The {@linkplain #getSubtype() subtype} is set to "*", parameters empty.
+ *
+ * @param type the primary type
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type) {
+ super(type);
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given primary type and subtype.
+ *
The parameters are empty.
+ *
+ * @param type the primary type
+ * @param subtype the subtype
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type, String subtype) {
+ super(type, subtype, Collections.emptyMap());
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given type, subtype, and character set.
+ *
+ * @param type the primary type
+ * @param subtype the subtype
+ * @param charset the character set
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type, String subtype, Charset charset) {
+ super(type, subtype, charset);
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given type, subtype, and quality value.
+ *
+ * @param type the primary type
+ * @param subtype the subtype
+ * @param qualityValue the quality value
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type, String subtype, double qualityValue) {
+ this(type, subtype, Collections.singletonMap(PARAM_QUALITY_FACTOR,
+ Double.toString(qualityValue)));
+ }
+
+ /**
+ * Copy-constructor that copies the type, subtype and parameters of the given
+ * {@code MediaType}, and allows to set the specified character set.
+ *
+ * @param other the other media type
+ * @param charset the character set
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ * @since 4.3
+ */
+ public MediaType(MediaType other, Charset charset) {
+ super(other, charset);
+ }
+
+ /**
+ * Copy-constructor that copies the type and subtype of the given {@code MediaType},
+ * and allows for different parameters.
+ *
+ * @param other the other media type
+ * @param parameters the parameters, may be {@code null}
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(MediaType other, @Nullable Map parameters) {
+ super(other.getType(), other.getSubtype(), parameters);
+ }
+
+
+ /**
+ * Create a new {@code MediaType} for the given type, subtype, and parameters.
+ *
+ * @param type the primary type
+ * @param subtype the subtype
+ * @param parameters the parameters, may be {@code null}
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ */
+ public MediaType(String type, String subtype, @Nullable Map parameters) {
+ super(type, subtype, parameters);
+ }
+
+ /**
+ * Create a new {@code MediaType} for the given {@link MimeType}.
+ * The type, subtype and parameters information is copied and {@code MediaType}-specific
+ * checks on parameters are performed.
+ *
+ * @param mimeType the MIME type
+ * @throws IllegalArgumentException if any of the parameters contain illegal characters
+ * @since 5.3
+ */
+ public MediaType(MimeType mimeType) {
+ super(mimeType);
+ getParameters().forEach(this::checkParameters);
+ }
+
+ /**
+ * Parse the given String value into a {@code MediaType} object,
+ * with this method name following the 'valueOf' naming convention
+ * (as supported by {@link org.springframework.core.convert.ConversionService}.
+ *
+ * @param value the string to parse
+ * @throws InvalidMediaTypeException if the media type value cannot be parsed
+ * @see #parseMediaType(String)
+ */
+ public static MediaType valueOf(String value) {
+ return parseMediaType(value);
+ }
+
+ /**
+ * Parse the given String into a single {@code MediaType}.
+ *
+ * @param mediaType the string to parse
+ * @return the media type
+ * @throws InvalidMediaTypeException if the media type value cannot be parsed
+ */
+ public static MediaType parseMediaType(String mediaType) {
+ MimeType type;
+ try {
+ type = MimeTypeUtils.parseMimeType(mediaType);
+ } catch (InvalidMimeTypeException ex) {
+ throw new InvalidMediaTypeException(ex);
+ }
+ try {
+ return new MediaType(type);
+ } catch (IllegalArgumentException ex) {
+ throw new InvalidMediaTypeException(mediaType, ex.getMessage());
+ }
+ }
+
+ /**
+ * Parse the comma-separated string into a list of {@code MediaType} objects.
+ * This method can be used to parse an Accept or Content-Type header.
+ *
+ * @param mediaTypes the string to parse
+ * @return the list of media types
+ * @throws InvalidMediaTypeException if the media type value cannot be parsed
+ */
+ public static List parseMediaTypes(@Nullable String mediaTypes) {
+ if (!StringUtils.hasLength(mediaTypes)) {
+ return Collections.emptyList();
+ }
+ // Avoid using java.util.stream.Stream in hot paths
+ List tokenizedTypes = MimeTypeUtils.tokenize(mediaTypes);
+ List result = new ArrayList<>(tokenizedTypes.size());
+ for (String type : tokenizedTypes) {
+ if (StringUtils.hasText(type)) {
+ result.add(parseMediaType(type));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parse the given list of (potentially) comma-separated strings into a
+ * list of {@code MediaType} objects.
+ * This method can be used to parse an Accept or Content-Type header.
+ *
+ * @param mediaTypes the string to parse
+ * @return the list of media types
+ * @throws InvalidMediaTypeException if the media type value cannot be parsed
+ * @since 4.3.2
+ */
+ public static List parseMediaTypes(@Nullable List mediaTypes) {
+ if (CollectionUtils.isEmpty(mediaTypes)) {
+ return Collections.emptyList();
+ } else if (mediaTypes.size() == 1) {
+ return parseMediaTypes(mediaTypes.get(0));
+ } else {
+ List result = new ArrayList<>(8);
+ for (String mediaType : mediaTypes) {
+ result.addAll(parseMediaTypes(mediaType));
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Re-create the given mime types as media types.
+ *
+ * @since 5.0
+ */
+ public static List asMediaTypes(List mimeTypes) {
+ List mediaTypes = new ArrayList<>(mimeTypes.size());
+ for (MimeType mimeType : mimeTypes) {
+ mediaTypes.add(MediaType.asMediaType(mimeType));
+ }
+ return mediaTypes;
+ }
+
+ /**
+ * Re-create the given mime type as a media type.
+ *
+ * @since 5.0
+ */
+ public static MediaType asMediaType(MimeType mimeType) {
+ if (mimeType instanceof MediaType) {
+ return (MediaType) mimeType;
+ }
+ return new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters());
+ }
+
+ /**
+ * Return a string representation of the given list of {@code MediaType} objects.
+ * This method can be used to for an {@code Accept} or {@code Content-Type} header.
+ *
+ * @param mediaTypes the media types to create a string representation for
+ * @return the string representation
+ */
+ public static String toString(Collection mediaTypes) {
+ return MimeTypeUtils.toString(mediaTypes);
+ }
+
+ /**
+ * Sorts the given list of {@code MediaType} objects by specificity.
+ * Given two media types:
+ *
+ * - if either media type has a {@linkplain #isWildcardType() wildcard type},
+ * then the media type without the wildcard is ordered before the other.
+ * - if the two media types have different {@linkplain #getType() types},
+ * then they are considered equal and remain their current order.
+ * - if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype},
+ * then the media type without the wildcard is sorted before the other.
+ * - if the two media types have different {@linkplain #getSubtype() subtypes},
+ * then they are considered equal and remain their current order.
+ * - if the two media types have different {@linkplain #getQualityValue() quality value},
+ * then the media type with the highest quality value is ordered before the other.
+ * - if the two media types have a different amount of
+ * {@linkplain #getParameter(String) parameters}, then the
+ * media type with the most parameters is ordered before the other.
+ *
+ * For example:
+ *
audio/basic < audio/* < */*
+ * audio/* < audio/*;q=0.7; audio/*;q=0.3
+ * audio/basic;level=1 < audio/basic
+ * audio/basic == text/html
+ * audio/basic == audio/wave
+ *
+ * @param mediaTypes the list of media types to be sorted
+ * @see HTTP 1.1: Semantics
+ * and Content, section 5.3.2
+ */
+ public static void sortBySpecificity(List mediaTypes) {
+ Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
+ if (mediaTypes.size() > 1) {
+ mediaTypes.sort(SPECIFICITY_COMPARATOR);
+ }
+ }
+
+ /**
+ * Sorts the given list of {@code MediaType} objects by quality value.
+ * Given two media types:
+ *
+ * - if the two media types have different {@linkplain #getQualityValue() quality value},
+ * then the media type with the highest quality value is ordered before the other.
+ * - if either media type has a {@linkplain #isWildcardType() wildcard type},
+ * then the media type without the wildcard is ordered before the other.
+ * - if the two media types have different {@linkplain #getType() types},
+ * then they are considered equal and remain their current order.
+ * - if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype},
+ * then the media type without the wildcard is sorted before the other.
+ * - if the two media types have different {@linkplain #getSubtype() subtypes},
+ * then they are considered equal and remain their current order.
+ * - if the two media types have a different amount of
+ * {@linkplain #getParameter(String) parameters}, then the
+ * media type with the most parameters is ordered before the other.
+ *
+ *
+ * @param mediaTypes the list of media types to be sorted
+ * @see #getQualityValue()
+ */
+ public static void sortByQualityValue(List mediaTypes) {
+ Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
+ if (mediaTypes.size() > 1) {
+ mediaTypes.sort(QUALITY_VALUE_COMPARATOR);
+ }
+ }
+
+ /**
+ * Sorts the given list of {@code MediaType} objects by specificity as the
+ * primary criteria and quality value the secondary.
+ *
+ * @see MediaType#sortBySpecificity(List)
+ * @see MediaType#sortByQualityValue(List)
+ */
+ public static void sortBySpecificityAndQuality(List mediaTypes) {
+ Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
+ if (mediaTypes.size() > 1) {
+ mediaTypes.sort(
+ MediaType.SPECIFICITY_COMPARATOR.thenComparing(MediaType.QUALITY_VALUE_COMPARATOR));
+ }
+ }
+
+ @Override
+ protected void checkParameters(String parameter, String value) {
+ super.checkParameters(parameter, value);
+ if (PARAM_QUALITY_FACTOR.equals(parameter)) {
+ String unquotedValue = unquote(value);
+ double d = Double.parseDouble(unquotedValue);
+ Assert.isTrue(d >= 0D && d <= 1D,
+ () -> "Invalid quality value \"" + unquotedValue + "\": should be between 0.0 and 1.0");
+ }
+ }
+
+ /**
+ * Return the quality factor, as indicated by a {@code q} parameter, if any.
+ * Defaults to {@code 1.0}.
+ *
+ * @return the quality factor as double value
+ */
+ public double getQualityValue() {
+ String qualityFactor = getParameter(PARAM_QUALITY_FACTOR);
+ return (qualityFactor != null ? Double.parseDouble(unquote(qualityFactor)) : 1D);
+ }
+
+ /**
+ * Indicate whether this {@code MediaType} includes the given media type.
+ * For instance, {@code text/*} includes {@code text/plain} and {@code text/html},
+ * and {@code application/*+xml} includes {@code application/soap+xml}, etc.
+ * This method is not symmetric.
+ *
Simply calls {@link MimeType#includes(MimeType)} but declared with a
+ * {@code MediaType} parameter for binary backwards compatibility.
+ *
+ * @param other the reference media type with which to compare
+ * @return {@code true} if this media type includes the given media type;
+ * {@code false} otherwise
+ */
+ public boolean includes(@Nullable MediaType other) {
+ return super.includes(other);
+ }
+
+ /**
+ * Indicate whether this {@code MediaType} is compatible with the given media type.
+ *
For instance, {@code text/*} is compatible with {@code text/plain},
+ * {@code text/html}, and vice versa. In effect, this method is similar to
+ * {@link #includes}, except that it is symmetric.
+ *
Simply calls {@link MimeType#isCompatibleWith(MimeType)} but declared with a
+ * {@code MediaType} parameter for binary backwards compatibility.
+ *
+ * @param other the reference media type with which to compare
+ * @return {@code true} if this media type is compatible with the given media type;
+ * {@code false} otherwise
+ */
+ public boolean isCompatibleWith(@Nullable MediaType other) {
+ return super.isCompatibleWith(other);
+ }
+
+ /**
+ * Return a replica of this instance with the quality value of the given {@code MediaType}.
+ *
+ * @return the same instance if the given MediaType doesn't have a quality value,
+ * or a new one otherwise
+ */
+ public MediaType copyQualityValue(MediaType mediaType) {
+ if (!mediaType.getParameters().containsKey(PARAM_QUALITY_FACTOR)) {
+ return this;
+ }
+ Map params = new LinkedHashMap<>(getParameters());
+ params.put(PARAM_QUALITY_FACTOR, mediaType.getParameters().get(PARAM_QUALITY_FACTOR));
+ return new MediaType(this, params);
+ }
+
+ /**
+ * Return a replica of this instance with its quality value removed.
+ *
+ * @return the same instance if the media type doesn't contain a quality value,
+ * or a new one otherwise
+ */
+ public MediaType removeQualityValue() {
+ if (!getParameters().containsKey(PARAM_QUALITY_FACTOR)) {
+ return this;
+ }
+ Map params = new LinkedHashMap<>(getParameters());
+ params.remove(PARAM_QUALITY_FACTOR);
+ return new MediaType(this, params);
+ }
+
+}
\ No newline at end of file
diff --git a/framework/src/test/java/org/tron/common/EntityTest.java b/framework/src/test/java/org/tron/common/EntityTest.java
index 483475a453b..bbdc8631225 100644
--- a/framework/src/test/java/org/tron/common/EntityTest.java
+++ b/framework/src/test/java/org/tron/common/EntityTest.java
@@ -5,13 +5,16 @@
import static org.junit.Assert.assertTrue;
import com.google.common.collect.Lists;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import org.apache.commons.collections4.CollectionUtils;
import org.junit.Before;
import org.junit.Test;
import org.tron.common.entity.NodeInfo;
import org.tron.common.entity.NodeInfo.MachineInfo;
import org.tron.common.entity.NodeInfo.MachineInfo.DeadLockThreadInfo;
+import org.tron.common.entity.PeerInfo;
public class EntityTest {
@@ -54,6 +57,9 @@ public void testDeadLockThreadInfo() {
@Test
public void testNodeInfo() {
+ List peerInfoList = new ArrayList<>();
+ peerInfoList.add(getDefaultPeerInfo());
+
NodeInfo nodeInfo = new NodeInfo();
nodeInfo.setTotalFlow(1L);
nodeInfo.setCheatWitnessInfoMap(new HashMap<>());
@@ -62,6 +68,39 @@ public void testNodeInfo() {
nodeInfo.setMachineInfo(machineInfo);
nodeInfo.setBlock("block");
nodeInfo.setSolidityBlock("solidityBlock");
+ nodeInfo.setPeerList(peerInfoList);
nodeInfo.transferToProtoEntity();
}
+
+ private PeerInfo getDefaultPeerInfo() {
+ PeerInfo peerInfo = new PeerInfo();
+ peerInfo.setAvgLatency(peerInfo.getAvgLatency());
+ peerInfo.setBlockInPorcSize(peerInfo.getBlockInPorcSize());
+ peerInfo.setConnectTime(peerInfo.getConnectTime());
+ peerInfo.setDisconnectTimes(peerInfo.getDisconnectTimes());
+ peerInfo.setHeadBlockTimeWeBothHave(peerInfo.getHeadBlockTimeWeBothHave());
+ peerInfo.setHeadBlockWeBothHave(peerInfo.getHeadBlockWeBothHave());
+ peerInfo.setHost("host");
+ peerInfo.setInFlow(peerInfo.getInFlow());
+ peerInfo.setLastBlockUpdateTime(peerInfo.getLastBlockUpdateTime());
+ peerInfo.setLastSyncBlock("last");
+ peerInfo.setLocalDisconnectReason("localDisconnectReason");
+ peerInfo.setNodeCount(peerInfo.getNodeCount());
+ peerInfo.setNodeId("nodeId");
+ peerInfo.setHeadBlockWeBothHave("headBlockWeBothHave");
+ peerInfo.setRemainNum(peerInfo.getRemainNum());
+ peerInfo.setRemoteDisconnectReason("remoteDisconnectReason");
+ peerInfo.setScore(peerInfo.getScore());
+ peerInfo.setPort(peerInfo.getPort());
+ peerInfo.setSyncFlag(peerInfo.isSyncFlag());
+ peerInfo.setNeedSyncFromPeer(peerInfo.isNeedSyncFromPeer());
+ peerInfo.setNeedSyncFromUs(peerInfo.isNeedSyncFromUs());
+ peerInfo.setSyncToFetchSize(peerInfo.getSyncToFetchSize());
+ peerInfo.setSyncToFetchSizePeekNum(peerInfo.getSyncToFetchSizePeekNum());
+ peerInfo.setSyncBlockRequestedSize(peerInfo.getSyncBlockRequestedSize());
+ peerInfo.setUnFetchSynNum(peerInfo.getUnFetchSynNum());
+ peerInfo.setActive(peerInfo.isActive());
+
+ return peerInfo;
+ }
}
diff --git a/framework/src/test/java/org/tron/common/ParameterTest.java b/framework/src/test/java/org/tron/common/ParameterTest.java
index b16be405f61..2f65189ac1c 100644
--- a/framework/src/test/java/org/tron/common/ParameterTest.java
+++ b/framework/src/test/java/org/tron/common/ParameterTest.java
@@ -129,6 +129,12 @@ public void testCommonParameter() {
assertEquals(10, parameter.getMaxConcurrentCallsPerConnection());
parameter.setFlowControlWindow(20);
assertEquals(20, parameter.getFlowControlWindow());
+ assertEquals(0, parameter.getRpcMaxRstStream());
+ parameter.setRpcMaxRstStream(10);
+ assertEquals(10, parameter.getRpcMaxRstStream());
+ assertEquals(0, parameter.getRpcSecondsPerWindow());
+ parameter.setRpcSecondsPerWindow(5);
+ assertEquals(5, parameter.getRpcSecondsPerWindow());
parameter.setMaxConnectionIdleInMillis(1000);
assertEquals(1000, parameter.getMaxConnectionIdleInMillis());
parameter.setBlockProducedTimeOut(500);
diff --git a/framework/src/test/java/org/tron/common/backup/BackupServerTest.java b/framework/src/test/java/org/tron/common/backup/BackupServerTest.java
index c40aca7e17a..18e264eead2 100644
--- a/framework/src/test/java/org/tron/common/backup/BackupServerTest.java
+++ b/framework/src/test/java/org/tron/common/backup/BackupServerTest.java
@@ -10,6 +10,7 @@
import org.junit.rules.Timeout;
import org.tron.common.backup.socket.BackupServer;
import org.tron.common.parameter.CommonParameter;
+import org.tron.common.utils.PublicMethod;
import org.tron.core.Constant;
import org.tron.core.config.args.Args;
@@ -26,7 +27,7 @@ public class BackupServerTest {
@Before
public void setUp() throws Exception {
Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF);
- CommonParameter.getInstance().setBackupPort(80);
+ CommonParameter.getInstance().setBackupPort(PublicMethod.chooseRandomPort());
List members = new ArrayList<>();
members.add("127.0.0.2");
CommonParameter.getInstance().setBackupMembers(members);
diff --git a/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java b/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java
index 4e7d45ee8d7..273672e8342 100644
--- a/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java
+++ b/framework/src/test/java/org/tron/common/crypto/ECKeyTest.java
@@ -5,6 +5,7 @@
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.assertTrue;
import static org.junit.Assert.fail;
import static org.tron.common.utils.client.utils.AbiUtil.generateOccupationConstantPrivateKey;
@@ -67,6 +68,11 @@ public void testFromPrivateKey() {
assertTrue(key.isPubKeyCanonical());
assertTrue(key.hasPrivKey());
assertArrayEquals(pubKey, key.getPubKey());
+
+ key = ECKey.fromPrivate((byte[]) null);
+ assertNull(key);
+ key = ECKey.fromPrivate(new byte[0]);
+ assertNull(key);
}
@Test(expected = IllegalArgumentException.class)
diff --git a/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java b/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java
index b84026d2085..87e4e14698c 100644
--- a/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java
+++ b/framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java
@@ -4,6 +4,7 @@
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 static org.junit.Assert.fail;
import static org.tron.common.utils.client.utils.AbiUtil.generateOccupationConstantPrivateKey;
@@ -64,6 +65,11 @@ public void testFromPrivateKey() {
assertTrue(key.isPubKeyCanonical());
assertTrue(key.hasPrivKey());
assertArrayEquals(pubKey, key.getPubKey());
+
+ key = SM2.fromPrivate((byte[]) null);
+ assertNull(key);
+ key = SM2.fromPrivate(new byte[0]);
+ assertNull(key);
}
@Test(expected = IllegalArgumentException.class)
diff --git a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java
index e6bb407bb53..d356e43d66c 100644
--- a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java
+++ b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java
@@ -55,18 +55,19 @@ public void publishTrigger() {
public void startSubscribeThread() {
Thread thread = new Thread(() -> {
- ZContext context = new ZContext();
- ZMQ.Socket subscriber = context.createSocket(SocketType.SUB);
+ try (ZContext context = new ZContext()) {
+ ZMQ.Socket subscriber = context.createSocket(SocketType.SUB);
- Assert.assertEquals(true, subscriber.connect(String.format("tcp://localhost:%d", bindPort)));
- Assert.assertEquals(true, subscriber.subscribe(topic));
+ Assert.assertTrue(subscriber.connect(String.format("tcp://localhost:%d", bindPort)));
+ Assert.assertTrue(subscriber.subscribe(topic));
- while (!Thread.currentThread().isInterrupted()) {
- byte[] message = subscriber.recv();
- String triggerMsg = new String(message);
-
- Assert.assertEquals(true, triggerMsg.contains(dataToSend) || triggerMsg.contains(topic));
+ while (!Thread.currentThread().isInterrupted()) {
+ byte[] message = subscriber.recv();
+ String triggerMsg = new String(message);
+ Assert.assertTrue(triggerMsg.contains(dataToSend) || triggerMsg.contains(topic));
+ }
+ // ZMQ.Socket will be automatically closed when ZContext is closed
}
});
thread.start();
diff --git a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java
index f400b3215ee..6fa2801c51f 100644
--- a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java
+++ b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java
@@ -19,9 +19,9 @@
import org.tron.core.Wallet;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
-import org.tron.core.exception.JsonRpcInvalidParamsException;
import org.tron.core.exception.ReceiptCheckErrException;
import org.tron.core.exception.VMIllegalException;
+import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException;
import org.tron.core.services.NodeInfoService;
import org.tron.core.services.jsonrpc.TronJsonRpcImpl;
import org.tron.core.vm.config.ConfigLoader;
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 3315005b7d2..b447af87bc2 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
@@ -1,6 +1,8 @@
package org.tron.common.runtime.vm;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.tron.core.config.Parameter.ChainConstant.FROZEN_PERIOD;
import java.util.List;
import java.util.Random;
@@ -13,12 +15,14 @@
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
+import org.mockito.Mockito;
import org.springframework.util.StringUtils;
import org.tron.common.BaseTest;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.InternalTransaction;
import org.tron.common.utils.DecodeUtil;
import org.tron.core.Constant;
+import org.tron.core.capsule.AccountCapsule;
import org.tron.core.config.args.Args;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.store.StoreFactory;
@@ -26,12 +30,14 @@
import org.tron.core.vm.JumpTable;
import org.tron.core.vm.Op;
import org.tron.core.vm.Operation;
+import org.tron.core.vm.OperationActions;
import org.tron.core.vm.OperationRegistry;
import org.tron.core.vm.VM;
import org.tron.core.vm.config.ConfigLoader;
import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.program.Program;
import org.tron.core.vm.program.invoke.ProgramInvokeMockImpl;
+import org.tron.core.vm.repository.Repository;
import org.tron.protos.Protocol;
@Slf4j
@@ -886,6 +892,12 @@ public void testSuicideCost() throws ContractValidateException {
Assert.assertEquals(25000, EnergyCost.getSuicideCost2(program));
invoke.getDeposit().createAccount(receiver2, Protocol.AccountType.Normal);
Assert.assertEquals(0, EnergyCost.getSuicideCost2(program));
+
+ byte[] receiver3 = generateRandomAddress();
+ program.stackPush(new DataWord(receiver3));
+ Assert.assertEquals(30000, EnergyCost.getSuicideCost3(program));
+ invoke.getDeposit().createAccount(receiver3, Protocol.AccountType.Normal);
+ Assert.assertEquals(5000, EnergyCost.getSuicideCost3(program));
}
@Test
@@ -911,6 +923,85 @@ public void testSuicideAction() throws ContractValidateException {
VMConfig.initAllowEnergyAdjustment(0);
}
+ @Test
+ public void testCanSuicide2() throws ContractValidateException {
+ VMConfig.initAllowTvmFreeze(1);
+ VMConfig.initAllowTvmFreezeV2(1);
+
+ byte[] contractAddr = Hex.decode("41471fd3ad3e9eeadeec4608b92d16ce6b500704cc");
+ invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr);
+
+ program = new Program(null, null, invoke,
+ new InternalTransaction(
+ Protocol.Transaction.getDefaultInstance(),
+ InternalTransaction.TrxType.TRX_UNKNOWN_TYPE));
+ program.getContractState().createAccount(
+ program.getContextAddress(), Protocol.AccountType.Contract);
+ Assert.assertTrue(program.canSuicide2());
+
+ long nowInMs =
+ program.getContractState().getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ long expireTime = nowInMs + FROZEN_PERIOD;
+ AccountCapsule owner = program.getContractState().getAccount(program.getContextAddress());
+ owner.setFrozenForEnergy(1000000, expireTime);
+ program.getContractState().updateAccount(program.getContextAddress(), owner);
+ Assert.assertFalse(program.canSuicide2());
+
+ VMConfig.initAllowTvmFreeze(0);
+ VMConfig.initAllowTvmFreezeV2(0);
+ }
+
+ @Test
+ public void testSuicideAction2() throws ContractValidateException {
+ byte[] contractAddr = Hex.decode("41471fd3ad3e9eeadeec4608b92d16ce6b500704cc");
+ invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr);
+ Assert.assertTrue(invoke.getDeposit().isNewContract(contractAddr));
+
+ program = new Program(null, null, invoke,
+ new InternalTransaction(
+ Protocol.Transaction.getDefaultInstance(),
+ InternalTransaction.TrxType.TRX_UNKNOWN_TYPE));
+
+ VMConfig.initAllowEnergyAdjustment(1);
+ VMConfig.initAllowTvmSelfdestructRestriction(1);
+ VMConfig.initAllowTvmFreeze(1);
+ VMConfig.initAllowTvmFreezeV2(1);
+ VMConfig.initAllowTvmCompatibleEvm(1);
+ VMConfig.initAllowTvmVote(1);
+ byte prePrefixByte = DecodeUtil.addressPreFixByte;
+ DecodeUtil.addressPreFixByte = Constant.ADD_PRE_FIX_BYTE_MAINNET;
+
+ program.stackPush(new DataWord(
+ dbManager.getAccountStore().getBlackhole().getAddress().toByteArray()));
+ OperationActions.suicideAction2(program);
+
+ Assert.assertEquals(1, program.getResult().getDeleteAccounts().size());
+
+
+ invoke = new ProgramInvokeMockImpl(StoreFactory.getInstance(), new byte[0], contractAddr);
+ program = new Program(null, null, invoke,
+ new InternalTransaction(
+ Protocol.Transaction.getDefaultInstance(),
+ InternalTransaction.TrxType.TRX_UNKNOWN_TYPE));
+ Program spyProgram = Mockito.spy(program);
+ Repository realContractState = program.getContractState();
+ Repository spyContractState = Mockito.spy(realContractState);
+ Mockito.when(spyContractState.isNewContract(any(byte[].class))).thenReturn(false);
+ Mockito.when(spyProgram.getContractState()).thenReturn(spyContractState);
+ spyProgram.suicide2(new DataWord(
+ dbManager.getAccountStore().getBlackhole().getAddress().toByteArray()));
+
+ Assert.assertEquals(0, spyProgram.getResult().getDeleteAccounts().size());
+
+ DecodeUtil.addressPreFixByte = prePrefixByte;
+ VMConfig.initAllowEnergyAdjustment(0);
+ VMConfig.initAllowTvmSelfdestructRestriction(0);
+ VMConfig.initAllowTvmFreeze(0);
+ VMConfig.initAllowTvmFreezeV2(0);
+ VMConfig.initAllowTvmCompatibleEvm(0);
+ VMConfig.initAllowTvmVote(0);
+ }
+
@Test
public void testVoteWitnessCost() throws ContractValidateException {
// Build stack environment, the stack from top to bottom is 0x00, 0x80, 0x00, 0x80
diff --git a/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java b/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java
new file mode 100644
index 00000000000..90aac10c0b6
--- /dev/null
+++ b/framework/src/test/java/org/tron/common/storage/CheckOrInitEngineTest.java
@@ -0,0 +1,263 @@
+package org.tron.common.storage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mockStatic;
+import static org.tron.core.db.common.DbSourceInter.ENGINE_FILE;
+import static org.tron.core.db.common.DbSourceInter.ENGINE_KEY;
+import static org.tron.core.db.common.DbSourceInter.LEVELDB;
+import static org.tron.core.db.common.DbSourceInter.ROCKSDB;
+import static org.tron.core.db.common.DbSourceInter.checkOrInitEngine;
+
+import com.google.common.base.Strings;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.tron.common.utils.FileUtil;
+import org.tron.common.utils.PropUtil;
+import org.tron.core.exception.TronError;
+
+
+public class CheckOrInitEngineTest {
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private static final String ACCOUNT = "account";
+
+ @After
+ public void clearMocks() {
+ Mockito.clearAllCaches();
+ }
+
+ @Test
+ public void testLevelDbDetectedWhenExpectingRocksDb() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ File currentFile = new File(dir, "CURRENT");
+ assertTrue(currentFile.createNewFile());
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(ROCKSDB, dir, errCode));
+ assertEquals("Cannot open LEVELDB database with ROCKSDB engine.",
+ exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+ }
+
+ @Test
+ public void testCannotCreateDir() {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class)) {
+ String dir = "/invalid/path/that/cannot/be/created";
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(false);
+ TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT;
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(LEVELDB, dir, errCode));
+ assertEquals("Cannot create dir: " + dir + ".", exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+ }
+
+ @Test
+ public void testCannotCreateEngineFile() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class)) {
+ String dir = temporaryFolder.newFolder().toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(false);
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(ROCKSDB, dir, errCode));
+
+ assertEquals("Cannot create file: " + engineFile + ".", exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+ }
+
+ @Test
+ public void testCannotWritePropertyFile() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder().toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(null);
+ strings.when(() -> Strings.isNullOrEmpty(null)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.writeProperty(engineFile, ENGINE_KEY, ROCKSDB))
+ .thenReturn(false);
+
+ TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT;
+
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(ROCKSDB, dir, errCode));
+
+ assertEquals("Cannot write file: " + engineFile + ".", exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+
+ }
+
+ @Test
+ public void testEngineMismatch() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(LEVELDB);
+ strings.when(() -> Strings.isNullOrEmpty(LEVELDB)).thenReturn(false);
+
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+
+ TronError exception = assertThrows(TronError.class, () ->
+ checkOrInitEngine(ROCKSDB, dir, errCode));
+
+ assertEquals("Cannot open LEVELDB database with ROCKSDB engine.",
+ exception.getMessage());
+ assertEquals(errCode, exception.getErrCode());
+ }
+ }
+
+ @Test
+ public void testSuccessfulFirstTimeInit() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY))
+ .thenReturn(null)
+ .thenReturn(LEVELDB);
+ strings.when(() -> Strings.isNullOrEmpty(null)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.writeProperty(engineFile, ENGINE_KEY, LEVELDB))
+ .thenReturn(true);
+
+ TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT;
+ checkOrInitEngine(LEVELDB, dir, errCode);
+ }
+ }
+
+ @Test
+ public void testSuccessfulExistingEngine() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(ROCKSDB);
+ strings.when(() -> Strings.isNullOrEmpty(ROCKSDB)).thenReturn(false);
+
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+ checkOrInitEngine(ROCKSDB, dir, errCode);
+ }
+ }
+
+ @Test
+ /**
+ * 000003.log CURRENT LOCK MANIFEST-000002
+ */
+ public void testCurrentFileExistsWithNoEngineFile() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+ File currentFile = new File(dir, "CURRENT");
+ assertTrue(currentFile.createNewFile());
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(LEVELDB);
+ strings.when(() -> Strings.isNullOrEmpty(LEVELDB)).thenReturn(false);
+
+ TronError.ErrCode errCode = TronError.ErrCode.LEVELDB_INIT;
+
+ checkOrInitEngine(LEVELDB, dir, errCode);
+ }
+ }
+
+ @Test
+ /**
+ * 000003.log CURRENT LOCK MANIFEST-000002 engine.properties(RocksDB)
+ */
+ public void testCurrentFileExistsEngineFileExists() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder(ACCOUNT).toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ File currentFile = new File(dir, "CURRENT");
+ File engineFileObj = new File(engineFile);
+ assertTrue(currentFile.createNewFile());
+ assertTrue(engineFileObj.createNewFile());
+
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY)).thenReturn(ROCKSDB);
+ strings.when(() -> Strings.isNullOrEmpty(ROCKSDB)).thenReturn(false);
+
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+ checkOrInitEngine(ROCKSDB, dir, errCode);
+ }
+ }
+
+ @Test
+ public void testEmptyStringEngine() throws IOException {
+ try (MockedStatic fileUtil = mockStatic(FileUtil.class);
+ MockedStatic propUtil = mockStatic(PropUtil.class);
+ MockedStatic strings = mockStatic(Strings.class)) {
+
+ String dir = temporaryFolder.newFolder("account").toString();
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+
+ fileUtil.when(() -> FileUtil.createDirIfNotExists(dir)).thenReturn(true);
+ fileUtil.when(() -> FileUtil.createFileIfNotExists(engineFile)).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.readProperty(engineFile, ENGINE_KEY))
+ .thenReturn("").thenReturn(ROCKSDB);
+ strings.when(() -> Strings.isNullOrEmpty("")).thenReturn(true);
+
+ propUtil.when(() -> PropUtil.writeProperty(engineFile, ENGINE_KEY, ROCKSDB))
+ .thenReturn(true);
+ TronError.ErrCode errCode = TronError.ErrCode.ROCKSDB_INIT;
+ checkOrInitEngine(ROCKSDB, dir, errCode);
+ }
+ }
+}
diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java
index bf18b988f19..78cbba3d079 100644
--- a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java
+++ b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java
@@ -28,6 +28,7 @@
import com.google.common.collect.Sets;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -38,15 +39,24 @@
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
+import org.iq80.leveldb.DBException;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
+import org.rocksdb.RocksDB;
+import org.tron.common.parameter.CommonParameter;
+import org.tron.common.storage.WriteOptionsWrapper;
+import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl;
import org.tron.common.utils.ByteArray;
import org.tron.common.utils.FileUtil;
+import org.tron.common.utils.PropUtil;
import org.tron.common.utils.PublicMethod;
+import org.tron.common.utils.StorageUtils;
import org.tron.core.Constant;
import org.tron.core.config.args.Args;
import org.tron.core.db2.common.WrappedByteArray;
@@ -73,6 +83,14 @@ public class LevelDbDataSourceImplTest {
private byte[] key5 = "00000005aa".getBytes();
private byte[] key6 = "00000006aa".getBytes();
+
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
+
+ static {
+ RocksDB.loadLibrary();
+ }
+
/**
* Release resources.
*/
@@ -94,7 +112,6 @@ public void testPutGet() {
dataSourceTest.resetDb();
String key1 = PublicMethod.getRandomPrivateKey();
byte[] key = key1.getBytes();
- dataSourceTest.initDB();
String value1 = "50000";
byte[] value = value1.getBytes();
@@ -102,8 +119,19 @@ public void testPutGet() {
assertNotNull(dataSourceTest.getData(key));
assertEquals(1, dataSourceTest.allKeys().size());
+ assertEquals(1, dataSourceTest.getTotal());
+ assertEquals(1, dataSourceTest.allValues().size());
assertEquals("50000", ByteArray.toStr(dataSourceTest.getData(key1.getBytes())));
+ dataSourceTest.deleteData(key);
+ assertNull(dataSourceTest.getData(key));
+ assertEquals(0, dataSourceTest.getTotal());
+ dataSourceTest.iterator().forEachRemaining(entry -> Assert.fail("iterator should be empty"));
+ dataSourceTest.stream().forEach(entry -> Assert.fail("stream should be empty"));
+ dataSourceTest.stat();
dataSourceTest.closeDB();
+ dataSourceTest.stat(); // stat again
+ exception.expect(DBException.class);
+ dataSourceTest.deleteData(key);
}
@Test
@@ -126,8 +154,6 @@ public void testReset() {
public void testupdateByBatchInner() {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_updateByBatch");
- dataSource.initDB();
- dataSource.resetDb();
String key1 = PublicMethod.getRandomPrivateKey();
String value1 = "50000";
String key2 = PublicMethod.getRandomPrivateKey();
@@ -142,6 +168,25 @@ public void testupdateByBatchInner() {
assertEquals("50000", ByteArray.toStr(dataSource.getData(key1.getBytes())));
assertEquals("10000", ByteArray.toStr(dataSource.getData(key2.getBytes())));
assertEquals(2, dataSource.allKeys().size());
+
+ rows.clear();
+ rows.put(key1.getBytes(), null);
+ rows.put(key2.getBytes(), null);
+ try (WriteOptionsWrapper options = WriteOptionsWrapper.getInstance()) {
+ dataSource.updateByBatch(rows, options);
+ }
+ assertEquals(0, dataSource.allKeys().size());
+
+ rows.clear();
+ rows.put(key1.getBytes(), value1.getBytes());
+ rows.put(key2.getBytes(), null);
+ dataSource.updateByBatch(rows);
+ assertEquals("50000", ByteArray.toStr(dataSource.getData(key1.getBytes())));
+ assertEquals(1, dataSource.allKeys().size());
+ rows.clear();
+ rows.put(null, null);
+ exception.expect(RuntimeException.class);
+ dataSource.updateByBatch(rows);
dataSource.closeDB();
}
@@ -149,7 +194,6 @@ public void testupdateByBatchInner() {
public void testdeleteData() {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_delete");
- dataSource.initDB();
String key1 = PublicMethod.getRandomPrivateKey();
byte[] key = key1.getBytes();
dataSource.deleteData(key);
@@ -163,8 +207,6 @@ public void testdeleteData() {
public void testallKeys() {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_find_key");
- dataSource.initDB();
- dataSource.resetDb();
String key1 = PublicMethod.getRandomPrivateKey();
byte[] key = key1.getBytes();
@@ -187,7 +229,6 @@ public void testallKeys() {
@Test(timeout = 1000)
public void testLockReleased() {
- dataSourceTest.initDB();
// normal close
dataSourceTest.closeDB();
// closing already closed db.
@@ -202,8 +243,6 @@ public void testLockReleased() {
public void allKeysTest() {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_allKeysTest_key");
- dataSource.initDB();
- dataSource.resetDb();
byte[] key = "0000000987b10fbb7f17110757321".getBytes();
byte[] value = "50000".getBytes();
@@ -216,7 +255,6 @@ public void allKeysTest() {
logger.info(ByteArray.toStr(keyOne));
});
assertEquals(2, dataSource.allKeys().size());
- dataSource.resetDb();
dataSource.closeDB();
}
@@ -242,26 +280,10 @@ private void putSomeKeyValue(LevelDbDataSourceImpl dataSource) {
dataSource.putData(key4, value4);
}
- @Test
- public void seekTest() {
- LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
- Args.getInstance().getOutputDirectory(), "test_seek_key");
- dataSource.initDB();
- dataSource.resetDb();
-
- putSomeKeyValue(dataSource);
- Assert.assertTrue(true);
- dataSource.resetDb();
- dataSource.closeDB();
- }
-
@Test
public void getValuesNext() {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_getValuesNext_key");
- dataSource.initDB();
- dataSource.resetDb();
-
putSomeKeyValue(dataSource);
Set seekKeyLimitNext = dataSource.getValuesNext("0000000300".getBytes(), 2);
HashSet hashSet = Sets.newHashSet(ByteArray.toStr(value3), ByteArray.toStr(value4));
@@ -276,7 +298,6 @@ public void getValuesNext() {
public void testGetTotal() {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_getTotal_key");
- dataSource.initDB();
dataSource.resetDb();
Map dataMapset = Maps.newHashMap();
@@ -293,8 +314,6 @@ public void testGetTotal() {
public void getKeysNext() {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_getKeysNext_key");
- dataSource.initDB();
- dataSource.resetDb();
putSomeKeyValue(dataSource);
int limit = 2;
@@ -304,8 +323,6 @@ public void getKeysNext() {
for (int i = 0; i < limit; i++) {
Assert.assertArrayEquals(list.get(i), seekKeyLimitNext.get(i));
}
-
- dataSource.resetDb();
dataSource.closeDB();
}
@@ -313,9 +330,6 @@ public void getKeysNext() {
public void prefixQueryTest() {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_prefixQuery");
- dataSource.initDB();
- dataSource.resetDb();
-
putSomeKeyValue(dataSource);
// put a kv that will not be queried.
byte[] key7 = "0000001".getBytes();
@@ -341,23 +355,113 @@ public void prefixQueryTest() {
Assert.assertEquals(list.size(), result.size());
list.forEach(entry -> Assert.assertTrue(result.contains(entry)));
- dataSource.resetDb();
dataSource.closeDB();
}
@Test
public void initDbTest() {
makeExceptionDb("test_initDb");
- LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
- Args.getInstance().getOutputDirectory(), "test_initDb");
- TronError thrown = assertThrows(TronError.class, dataSource::initDB);
+ TronError thrown = assertThrows(TronError.class, () -> new LevelDbDataSourceImpl(
+ Args.getInstance().getOutputDirectory(), "test_initDb"));
assertEquals(TronError.ErrCode.LEVELDB_INIT, thrown.getErrCode());
}
+ @Test
+ public void testCheckOrInitEngine() {
+ String dir =
+ Args.getInstance().getOutputDirectory() + Args.getInstance().getStorage().getDbDirectory();
+ String enginePath = dir + File.separator + "test_engine" + File.separator + "engine.properties";
+ FileUtil.createDirIfNotExists(dir + File.separator + "test_engine");
+ FileUtil.createFileIfNotExists(enginePath);
+ PropUtil.writeProperty(enginePath, "ENGINE", "LEVELDB");
+ Assert.assertEquals("LEVELDB", PropUtil.readProperty(enginePath, "ENGINE"));
+
+ LevelDbDataSourceImpl dataSource;
+ dataSource = new LevelDbDataSourceImpl(dir, "test_engine");
+ dataSource.closeDB();
+
+ PropUtil.writeProperty(enginePath, "ENGINE", "ROCKSDB");
+ Assert.assertEquals("ROCKSDB", PropUtil.readProperty(enginePath, "ENGINE"));
+ try {
+ new LevelDbDataSourceImpl(dir, "test_engine");
+ } catch (TronError e) {
+ Assert.assertEquals("Cannot open ROCKSDB database with LEVELDB engine.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testLevelDbOpenRocksDb() {
+ String name = "test_openRocksDb";
+ String output = Paths
+ .get(StorageUtils.getOutputDirectoryByDbName(name), CommonParameter
+ .getInstance().getStorage().getDbDirectory()).toString();
+ RocksDbDataSourceImpl rocksDb = new RocksDbDataSourceImpl(output, name);
+ rocksDb.putData(key1, value1);
+ rocksDb.closeDB();
+ exception.expectMessage("Cannot open ROCKSDB database with LEVELDB engine.");
+ new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(name), name);
+ }
+
+ @Test
+ public void testNewInstance() {
+ dataSourceTest.closeDB();
+ LevelDbDataSourceImpl newInst = dataSourceTest.newInstance();
+ assertFalse(newInst.flush());
+ newInst.closeDB();
+ LevelDbDataSourceImpl empty = new LevelDbDataSourceImpl();
+ empty.setDBName("empty");
+ assertEquals("empty", empty.getDBName());
+ String name = "newInst2";
+ LevelDbDataSourceImpl newInst2 = new LevelDbDataSourceImpl(
+ StorageUtils.getOutputDirectoryByDbName(name),
+ name);
+ newInst2.closeDB();
+ }
+
+ @Test
+ public void testGetNext() {
+ LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
+ Args.getInstance().getOutputDirectory(), "test_getNext_key");
+ putSomeKeyValue(dataSource);
+ // case: normal
+ Map seekKvLimitNext = dataSource.getNext("0000000300".getBytes(), 2);
+ Map hashMap = Maps.newHashMap();
+ hashMap.put(ByteArray.toStr(key3), ByteArray.toStr(value3));
+ hashMap.put(ByteArray.toStr(key4), ByteArray.toStr(value4));
+ seekKvLimitNext.forEach((key, value) -> {
+ String keyStr = ByteArray.toStr(key);
+ Assert.assertTrue("getNext", hashMap.containsKey(keyStr));
+ Assert.assertEquals(ByteArray.toStr(value), hashMap.get(keyStr));
+ });
+ // case: targetKey greater than all existed keys
+ seekKvLimitNext = dataSource.getNext("0000000700".getBytes(), 2);
+ Assert.assertEquals(0, seekKvLimitNext.size());
+ // case: limit<=0
+ seekKvLimitNext = dataSource.getNext("0000000300".getBytes(), 0);
+ Assert.assertEquals(0, seekKvLimitNext.size());
+ dataSource.closeDB();
+ }
+
+ @Test
+ public void testGetlatestValues() {
+ LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
+ Args.getInstance().getOutputDirectory(), "test_getlatestValues_key");
+ putSomeKeyValue(dataSource);
+ // case: normal
+ Set seekKeyLimitNext = dataSource.getlatestValues(2);
+ Set hashSet = Sets.newHashSet(ByteArray.toStr(value5), ByteArray.toStr(value6));
+ seekKeyLimitNext.forEach(value -> {
+ Assert.assertTrue(hashSet.contains(ByteArray.toStr(value)));
+ });
+ // case: limit<=0
+ seekKeyLimitNext = dataSource.getlatestValues(0);
+ assertEquals(0, seekKeyLimitNext.size());
+ dataSource.closeDB();
+ }
+
private void makeExceptionDb(String dbName) {
LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_initDb");
- dataSource.initDB();
dataSource.closeDB();
FileUtil.saveData(dataSource.getDbPath().toString() + "/CURRENT",
"...", Boolean.FALSE);
diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java
similarity index 71%
rename from framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java
rename to framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java
index c6fce30e3af..86543db19fb 100644
--- a/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java
+++ b/framework/src/test/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImplTest.java
@@ -1,4 +1,4 @@
-package org.tron.common.storage.leveldb;
+package org.tron.common.storage.rocksdb;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -10,6 +10,8 @@
import com.google.common.collect.Sets;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -24,13 +26,20 @@
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
-import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl;
+import org.rocksdb.RocksDBException;
+import org.tron.common.error.TronDBException;
+import org.tron.common.parameter.CommonParameter;
+import org.tron.common.storage.WriteOptionsWrapper;
+import org.tron.common.storage.leveldb.LevelDbDataSourceImpl;
import org.tron.common.utils.ByteArray;
import org.tron.common.utils.FileUtil;
import org.tron.common.utils.PropUtil;
import org.tron.common.utils.PublicMethod;
+import org.tron.common.utils.StorageUtils;
import org.tron.core.config.args.Args;
import org.tron.core.db2.common.WrappedByteArray;
import org.tron.core.exception.TronError;
@@ -55,6 +64,9 @@ public class RocksDbDataSourceImplTest {
private byte[] key5 = "00000005aa".getBytes();
private byte[] key6 = "00000006aa".getBytes();
+ @Rule
+ public final ExpectedException expectedException = ExpectedException.none();
+
/**
* Release resources.
*/
@@ -76,7 +88,6 @@ public void testPutGet() {
dataSourceTest.resetDb();
String key1 = PublicMethod.getRandomPrivateKey();
byte[] key = key1.getBytes();
- dataSourceTest.initDB();
String value1 = "50000";
byte[] value = value1.getBytes();
@@ -84,8 +95,18 @@ public void testPutGet() {
assertNotNull(dataSourceTest.getData(key));
assertEquals(1, dataSourceTest.allKeys().size());
+ assertEquals(1, dataSourceTest.getTotal());
+ assertEquals(1, dataSourceTest.allValues().size());
assertEquals("50000", ByteArray.toStr(dataSourceTest.getData(key1.getBytes())));
+ dataSourceTest.deleteData(key);
+ assertNull(dataSourceTest.getData(key));
+ assertEquals(0, dataSourceTest.getTotal());
+ dataSourceTest.iterator().forEachRemaining(entry -> Assert.fail("iterator should be empty"));
+ dataSourceTest.stat();
dataSourceTest.closeDB();
+ dataSourceTest.stat(); // stat again
+ expectedException.expect(TronDBException.class);
+ dataSourceTest.deleteData(key);
}
@Test
@@ -108,8 +129,6 @@ public void testReset() {
public void testupdateByBatchInner() {
RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_updateByBatch");
- dataSource.initDB();
- dataSource.resetDb();
String key1 = PublicMethod.getRandomPrivateKey();
String value1 = "50000";
String key2 = PublicMethod.getRandomPrivateKey();
@@ -124,6 +143,25 @@ public void testupdateByBatchInner() {
assertEquals("50000", ByteArray.toStr(dataSource.getData(key1.getBytes())));
assertEquals("10000", ByteArray.toStr(dataSource.getData(key2.getBytes())));
assertEquals(2, dataSource.allKeys().size());
+
+ rows.clear();
+ rows.put(key1.getBytes(), null);
+ rows.put(key2.getBytes(), null);
+ try (WriteOptionsWrapper options = WriteOptionsWrapper.getInstance()) {
+ dataSource.updateByBatch(rows, options);
+ }
+ assertEquals(0, dataSource.allKeys().size());
+
+ rows.clear();
+ rows.put(key1.getBytes(), value1.getBytes());
+ rows.put(key2.getBytes(), null);
+ dataSource.updateByBatch(rows);
+ assertEquals("50000", ByteArray.toStr(dataSource.getData(key1.getBytes())));
+ assertEquals(1, dataSource.allKeys().size());
+ rows.clear();
+ rows.put(null, null);
+ expectedException.expect(RuntimeException.class);
+ dataSource.updateByBatch(rows);
dataSource.closeDB();
}
@@ -131,7 +169,6 @@ public void testupdateByBatchInner() {
public void testdeleteData() {
RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_delete");
- dataSource.initDB();
String key1 = PublicMethod.getRandomPrivateKey();
byte[] key = key1.getBytes();
dataSource.deleteData(key);
@@ -145,8 +182,6 @@ public void testdeleteData() {
public void testallKeys() {
RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_find_key");
- dataSource.initDB();
- dataSource.resetDb();
String key1 = PublicMethod.getRandomPrivateKey();
byte[] key = key1.getBytes();
@@ -163,13 +198,11 @@ public void testallKeys() {
dataSource.putData(key2, value2);
assertEquals(2, dataSource.allKeys().size());
- dataSource.resetDb();
dataSource.closeDB();
}
@Test(timeout = 1000)
public void testLockReleased() {
- dataSourceTest.initDB();
// normal close
dataSourceTest.closeDB();
// closing already closed db.
@@ -184,8 +217,6 @@ public void testLockReleased() {
public void allKeysTest() {
RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_allKeysTest_key");
- dataSource.initDB();
- dataSource.resetDb();
byte[] key = "0000000987b10fbb7f17110757321".getBytes();
byte[] value = "50000".getBytes();
@@ -198,7 +229,6 @@ public void allKeysTest() {
logger.info(ByteArray.toStr(keyOne));
});
assertEquals(2, dataSource.allKeys().size());
- dataSource.resetDb();
dataSource.closeDB();
}
@@ -224,31 +254,15 @@ private void putSomeKeyValue(RocksDbDataSourceImpl dataSource) {
dataSource.putData(key4, value4);
}
- @Test
- public void seekTest() {
- RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl(
- Args.getInstance().getOutputDirectory(), "test_seek_key");
- dataSource.initDB();
- dataSource.resetDb();
-
- putSomeKeyValue(dataSource);
- Assert.assertTrue(true);
- dataSource.resetDb();
- dataSource.closeDB();
- }
-
@Test
public void getValuesNext() {
RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl(
Args.getInstance().getOutputDirectory(), "test_getValuesNext_key");
- dataSource.initDB();
- dataSource.resetDb();
putSomeKeyValue(dataSource);
Set seekKeyLimitNext = dataSource.getValuesNext("0000000300".getBytes(), 2);
HashSet