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 cf013266bd0..cfec5b09d96 100644 --- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java +++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java @@ -733,19 +733,19 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, throw new ContractValidateException( "Bad chain parameter id [ALLOW_OLD_REWARD_OPT]"); } + if (dynamicPropertiesStore.allowOldRewardOpt()) { + throw new ContractValidateException( + "[ALLOW_OLD_REWARD_OPT] has been valid, no need to propose again"); + } if (value != 1) { throw new ContractValidateException( "This value[ALLOW_OLD_REWARD_OPT] is only allowed to be 1"); } if (!dynamicPropertiesStore.useNewRewardAlgorithm()) { throw new ContractValidateException( - "[ALLOW_NEW_REWARD] proposal must be approved " + "[ALLOW_NEW_REWARD] or [ALLOW_TVM_VOTE] proposal must be approved " + "before [ALLOW_OLD_REWARD_OPT] can be proposed"); } - if (dynamicPropertiesStore.useNewRewardAlgorithmFromStart()) { - throw new ContractValidateException( - "no need old reward opt, ALLOW_NEW_REWARD from start cycle 1"); - } break; } default: diff --git a/build.gradle b/build.gradle index e3611a27a73..a56be97afa1 100644 --- a/build.gradle +++ b/build.gradle @@ -55,7 +55,7 @@ subprojects { testAnnotationProcessor 'org.projectlombok:lombok:1.18.12' testImplementation group: 'junit', name: 'junit', version: '4.13.2' - testImplementation "org.mockito:mockito-core:2.1.0" + testImplementation "org.mockito:mockito-core:2.13.0" } task sourcesJar(type: Jar, dependsOn: classes) { diff --git a/chainbase/src/main/java/org/tron/common/utils/ForkController.java b/chainbase/src/main/java/org/tron/common/utils/ForkController.java index c3db883a011..7cbac28e781 100644 --- a/chainbase/src/main/java/org/tron/common/utils/ForkController.java +++ b/chainbase/src/main/java/org/tron/common/utils/ForkController.java @@ -78,7 +78,7 @@ private boolean passOld(int version) { private boolean passNew(int version) { ForkBlockVersionEnum versionEnum = ForkBlockVersionEnum.getForkBlockVersionEnum(version); if (versionEnum == null) { - logger.error("Not exist block version: {}.", version); + logger.warn("Not exist block version: {}.", version); return false; } long latestBlockTime = manager.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp(); diff --git a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java index 9598fd99a6b..a588b28c748 100755 --- a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java @@ -102,7 +102,6 @@ public class TransactionCapsule implements ProtoCapsule { @Setter private TransactionTrace trxTrace; - private StringBuilder toStringBuff = new StringBuilder(); @Getter @Setter private long time; @@ -738,8 +737,7 @@ public Transaction getInstance() { @Override public String toString() { - - toStringBuff.setLength(0); + StringBuilder toStringBuff = new StringBuilder(); toStringBuff.append("TransactionCapsule \n[ "); toStringBuff.append("hash=").append(getTransactionId()).append("\n"); diff --git a/chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java b/chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java index 47ac45c9fb8..94d22f4b474 100644 --- a/chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java +++ b/chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java @@ -5,10 +5,12 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.Getter; +import net.jcip.annotations.NotThreadSafe; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.Sha256Hash; @Getter +@NotThreadSafe public class MerkleTree { private static volatile MerkleTree instance; diff --git a/chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java b/chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java index ec8d7fd85be..afbacac35db 100755 --- a/chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java +++ b/chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java @@ -16,12 +16,6 @@ public interface DBIterator extends Iterator>, AutoCloseab void seekToLast(); - default UnmodifiableIterator> prefixQueryAfterThat - (byte[] key, byte[] afterThat) { - this.seek(afterThat == null ? key : afterThat); - return Iterators.filter(this, entry -> Bytes.indexOf(entry.getKey(), key) == 0); - } - /** * An iterator is either positioned at a key/value pair, or * not valid. This method returns true iff the iterator is valid. diff --git a/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java b/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java index 1438b9247b5..541f71348af 100644 --- a/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java +++ b/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java @@ -108,14 +108,12 @@ public boolean valid() { @Override public byte[] getKey() { - checkState(); checkValid(); return dbIterator.key(); } @Override public byte[] getValue() { - checkState(); checkValid(); return dbIterator.value(); } diff --git a/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java b/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java index 4c635660ea6..d771716a7e8 100755 --- a/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java +++ b/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java @@ -93,14 +93,12 @@ public boolean valid() { @Override public byte[] getKey() { - checkState(); checkValid(); return dbIterator.peekNext().getKey(); } @Override public byte[] getValue() { - checkState(); checkValid(); return dbIterator.peekNext().getValue(); } diff --git a/chainbase/src/main/java/org/tron/core/service/RewardViCalService.java b/chainbase/src/main/java/org/tron/core/service/RewardViCalService.java index acb16142b3f..e27990f0403 100644 --- a/chainbase/src/main/java/org/tron/core/service/RewardViCalService.java +++ b/chainbase/src/main/java/org/tron/core/service/RewardViCalService.java @@ -3,7 +3,6 @@ import static org.tron.core.store.DelegationStore.DECIMAL_OF_VI_REWARD; import static org.tron.core.store.DelegationStore.REMARK; -import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Streams; import com.google.common.primitives.Bytes; import com.google.protobuf.ByteString; @@ -17,9 +16,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.LongStream; -import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; @@ -28,9 +25,9 @@ import org.tron.common.es.ExecutorServiceManager; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; +import org.tron.common.utils.MerkleRoot; import org.tron.common.utils.Pair; import org.tron.common.utils.Sha256Hash; -import org.tron.core.capsule.utils.MerkleTree; import org.tron.core.db.common.iterator.DBIterator; import org.tron.core.db2.common.DB; import org.tron.core.store.DelegationStore; @@ -56,10 +53,11 @@ public class RewardViCalService { private volatile long lastBlockNumber = -1; - @VisibleForTesting - @Setter - private Sha256Hash rewardViRoot = Sha256Hash.wrap( - ByteString.fromHex("9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8")); + private static final String MAIN_NET_ROOT_HEX = + "9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8"; + + private final Sha256Hash rewardViRoot = CommonParameter.getInstance().getStorage().getDbRoot( + "reward-vi", Sha256Hash.wrap(ByteString.fromHex(MAIN_NET_ROOT_HEX))); private final CountDownLatch lock = new CountDownLatch(1); @@ -75,8 +73,14 @@ public RewardViCalService(@Autowired DynamicPropertiesStore propertiesStore, this.witnessStore = witnessStore.getDb(); } - @PostConstruct - private void init() { + public void init() { + // after init, we can get the latest block header number from db + this.newRewardCalStartCycle = this.getNewRewardAlgorithmEffectiveCycle(); + boolean ret = this.newRewardCalStartCycle != Long.MAX_VALUE; + if (ret) { + // checkpoint is flushed to db, we can start rewardViCalService immediately + lastBlockNumber = Long.MAX_VALUE; + } es.scheduleWithFixedDelay(this::maybeRun, 0, 3, TimeUnit.SECONDS); } @@ -94,24 +98,30 @@ private boolean isDone() { } private void maybeRun() { - if (enableNewRewardAlgorithm()) { - if (this.newRewardCalStartCycle > 1) { - if (isDone()) { - this.clearUp(true); - logger.info("rewardViCalService is already done"); - } else { - if (this.getLatestBlockHeaderNumber() > lastBlockNumber) { - // checkpoint is flushed to db, so we can start rewardViCalService - startRewardCal(); - clearUp(true); + try { + if (enableNewRewardAlgorithm()) { + if (this.newRewardCalStartCycle > 1) { + if (isDone()) { + this.clearUp(true); + logger.info("rewardViCalService is already done"); } else { - logger.info("startRewardCal will run after checkpoint is flushed to db"); + if (lastBlockNumber == Long.MAX_VALUE // start rewardViCalService immediately + || this.getLatestBlockHeaderNumber() > lastBlockNumber) { + // checkpoint is flushed to db, so we can start rewardViCalService + startRewardCal(); + clearUp(true); + } else { + logger.info("startRewardCal will run after checkpoint is flushed to db"); + } } + } else { + clearUp(false); + logger.info("rewardViCalService is no need to run"); } - } else { - clearUp(false); - logger.info("rewardViCalService is no need to run"); } + } catch (Exception e) { + logger.error(" Find fatal error, program will be exited soon.", e); + System.exit(1); } } @@ -131,7 +141,7 @@ private void destroy() { public long getNewRewardAlgorithmReward(long beginCycle, long endCycle, List> votes) { - if (!rewardViStore.has(IS_DONE_KEY)) { + if (!isDone()) { logger.warn("rewardViCalService is not done, wait for it"); try { lock.await(); @@ -168,10 +178,13 @@ private void calcMerkleRoot() { .map(this::getHash) .collect(Collectors.toCollection(ArrayList::new)); - Sha256Hash rewardViRootLocal = MerkleTree.getInstance().createTree(ids).getRoot().getHash(); + Sha256Hash rewardViRootLocal = MerkleRoot.root(ids); if (!Objects.equals(rewardViRoot, rewardViRootLocal)) { - logger.error("merkle root mismatch, expect: {}, actual: {}", - rewardViRoot, rewardViRootLocal); + logger.warn("Merkle root mismatch, expect: {}, actual: {}." + + " If you are quite sure that there is no miscalculation (not on the main network)" + + ", please configure 'storage.merkleRoot.reward-vi = {}'" + + "(for a specific network such as Nile, etc.) in config.conf to fix the hints", + rewardViRoot, rewardViRootLocal, rewardViRootLocal); } logger.info("calcMerkleRoot: {}", rewardViRootLocal); } 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 23693f9bab6..bf788232640 100644 --- a/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java +++ b/chainbase/src/main/java/org/tron/core/store/DynamicPropertiesStore.java @@ -2525,10 +2525,6 @@ public boolean useNewRewardAlgorithm() { return getNewRewardAlgorithmEffectiveCycle() != Long.MAX_VALUE; } - public boolean useNewRewardAlgorithmFromStart() { - return getNewRewardAlgorithmEffectiveCycle() == 1; - } - public void saveNewRewardAlgorithmEffectiveCycle() { if (getNewRewardAlgorithmEffectiveCycle() == Long.MAX_VALUE) { long currentCycle = getCurrentCycleNumber(); @@ -2839,19 +2835,8 @@ public boolean supportMaxDelegateLockPeriod() { getUnfreezeDelayDays() > 0; } - /** - * @require NEW_REWARD_ALGORITHM_EFFECTIVE_CYCLE != Long.MAX_VALUE - * @require NEW_REWARD_ALGORITHM_EFFECTIVE_CYCLE > 1 - */ public void saveAllowOldRewardOpt(long allowOldRewardOpt) { - if (useNewRewardAlgorithm()) { - if (useNewRewardAlgorithmFromStart()) { - throw new IllegalStateException("no need old reward opt, ALLOW_NEW_REWARD from start"); - } - this.put(ALLOW_OLD_REWARD_OPT, new BytesCapsule(ByteArray.fromLong(allowOldRewardOpt))); - } else { - throw new IllegalStateException("not support old reward opt, ALLOW_NEW_REWARD not set"); - } + this.put(ALLOW_OLD_REWARD_OPT, new BytesCapsule(ByteArray.fromLong(allowOldRewardOpt))); } public boolean allowOldRewardOpt() { diff --git a/common/build.gradle b/common/build.gradle index ba1c431e74c..6c1545e5d13 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -46,7 +46,7 @@ dependencies { compile 'org.aspectj:aspectjrt:1.8.13' compile 'org.aspectj:aspectjweaver:1.8.13' compile 'org.aspectj:aspectjtools:1.8.13' - compile group: 'com.github.tronprotocol', name: 'libp2p', version: 'test-v2.2.1',{ + compile group: 'io.github.tronprotocol', name: 'libp2p', version: '2.2.1',{ exclude group: 'io.grpc', module: 'grpc-context' exclude group: 'io.grpc', module: 'grpc-core' exclude group: 'io.grpc', module: 'grpc-netty' diff --git a/common/src/main/java/org/tron/common/prometheus/MetricKeys.java b/common/src/main/java/org/tron/common/prometheus/MetricKeys.java index 7e85771000e..87ab6fae0a3 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricKeys.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricKeys.java @@ -62,8 +62,6 @@ public static class Histogram { public static final String MESSAGE_PROCESS_LATENCY = "tron:message_process_latency_seconds"; public static final String BLOCK_FETCH_LATENCY = "tron:block_fetch_latency_seconds"; public static final String BLOCK_RECEIVE_DELAY = "tron:block_receive_delay_seconds"; - public static final String DO_REWARD_CAL_DELAY = "tron:do_reward_cal_seconds"; - private Histogram() { throw new IllegalStateException("Histogram"); diff --git a/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java b/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java index d776474c92e..556db10feb5 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java @@ -48,8 +48,6 @@ public class MetricsHistogram { init(MetricKeys.Histogram.BLOCK_FETCH_LATENCY, "fetch block latency."); init(MetricKeys.Histogram.BLOCK_RECEIVE_DELAY, "receive block delay time, receiveTime - blockTime."); - init(MetricKeys.Histogram.DO_REWARD_CAL_DELAY, - "do reward cal delay time.", "depth"); } private MetricsHistogram() { diff --git a/common/src/main/java/org/tron/common/utils/MerkleRoot.java b/common/src/main/java/org/tron/common/utils/MerkleRoot.java new file mode 100644 index 00000000000..ccd8905b6c5 --- /dev/null +++ b/common/src/main/java/org/tron/common/utils/MerkleRoot.java @@ -0,0 +1,68 @@ +package org.tron.common.utils; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import lombok.Getter; + +public class MerkleRoot { + + private MerkleRoot() { + + } + + public static Sha256Hash root(List hashList) { + List leaves = createLeaves(hashList); + while (leaves.size() > 1) { + leaves = createParentLeaves(leaves); + } + return leaves.isEmpty() ? Sha256Hash.ZERO_HASH : leaves.get(0).hash; + } + + private static List createParentLeaves(List leaves) { + int step = 2; + int len = leaves.size(); + return IntStream.iterate(0, i -> i + step) + .limit(len) + .filter(i -> i < len) + .mapToObj(i -> { + Leaf right = i + 1 < len ? leaves.get(i + 1) : null; + return createLeaf(leaves.get(i), right); + }).collect(Collectors.toList()); + } + + private static List createLeaves(List hashList) { + int step = 2; + int len = hashList.size(); + return IntStream.iterate(0, i -> i + step) + .limit(len) + .filter(i -> i < len) + .mapToObj(i -> { + Leaf right = i + 1 < len ? createLeaf(hashList.get(i + 1)) : null; + return createLeaf(createLeaf(hashList.get(i)), right); + }).collect(Collectors.toList()); + } + + private static Leaf createLeaf(Leaf left, Leaf right) { + Leaf leaf = new Leaf(); + leaf.hash = right == null ? left.hash : computeHash(left.hash, right.hash); + return leaf; + } + + private static Leaf createLeaf(Sha256Hash hash) { + Leaf leaf = new Leaf(); + leaf.hash = hash; + return leaf; + } + + private static Sha256Hash computeHash(Sha256Hash leftHash, Sha256Hash rightHash) { + return Sha256Hash.of(true, + leftHash.getByteString().concat(rightHash.getByteString()).toByteArray()); + } + + @Getter + private static class Leaf { + + private Sha256Hash hash; + } +} diff --git a/common/src/main/java/org/tron/core/config/args/Storage.java b/common/src/main/java/org/tron/core/config/args/Storage.java index 22dc239c167..9cf6eb6bab1 100644 --- a/common/src/main/java/org/tron/core/config/args/Storage.java +++ b/common/src/main/java/org/tron/core/config/args/Storage.java @@ -16,6 +16,7 @@ package org.tron.core.config.args; import com.google.common.collect.Maps; +import com.google.protobuf.ByteString; import com.typesafe.config.Config; import com.typesafe.config.ConfigObject; import java.io.File; @@ -32,6 +33,7 @@ import org.tron.common.utils.DbOptionalsUtils; import org.tron.common.utils.FileUtil; import org.tron.common.utils.Property; +import org.tron.common.utils.Sha256Hash; /** * Custom storage configurations @@ -79,6 +81,8 @@ public class Storage { private static final String CACHE_STRATEGIES = "storage.cache.strategies"; public static final String TX_CACHE_INIT_OPTIMIZATION = "storage.txCache.initOptimization"; + private static final String MERKLE_ROOT = "storage.merkleRoot"; + /** * Default values of directory */ @@ -163,6 +167,9 @@ public class Storage { @Getter private Map propertyMap; + // db root + private final Map dbRoots = Maps.newConcurrentMap(); + public static String getDbEngineFromConfig(final Config config) { return config.hasPath(DB_ENGINE_CONFIG_KEY) ? config.getString(DB_ENGINE_CONFIG_KEY) : DEFAULT_DB_ENGINE; @@ -258,6 +265,18 @@ public String getCacheStrategy(CacheType dbName) { return this.cacheStrategies.getOrDefault(dbName, CacheStrategies.getCacheStrategy(dbName)); } + public Sha256Hash getDbRoot(String dbName, Sha256Hash defaultV) { + return this.dbRoots.getOrDefault(dbName, defaultV); + } + + public void setDbRoots(Config config) { + if (config.hasPath(MERKLE_ROOT)) { + config.getConfig(MERKLE_ROOT).resolve().entrySet().forEach(c -> + this.dbRoots.put(c.getKey(), Sha256Hash.wrap( + ByteString.fromHex(c.getValue().unwrapped().toString())))); + } + } + private Property createProperty(final ConfigObject conf) { Property property = new Property(); diff --git a/consensus/src/main/java/org/tron/consensus/pbft/PbftMessageHandle.java b/consensus/src/main/java/org/tron/consensus/pbft/PbftMessageHandle.java index ea7b00802b2..523ffac4d61 100644 --- a/consensus/src/main/java/org/tron/consensus/pbft/PbftMessageHandle.java +++ b/consensus/src/main/java/org/tron/consensus/pbft/PbftMessageHandle.java @@ -262,7 +262,7 @@ public boolean isSyncing() { } //Cleanup related status - private void remove(String no) { + private synchronized void remove(String no) { String pre = String.valueOf(no) + "_"; preVotes.remove(no); pareVoteMap.keySet().removeIf(vp -> StringUtils.startsWith(vp, pre)); @@ -285,7 +285,7 @@ private void remove(String no) { //just try once if (srPbftMessage != null && StringUtils.equals(no, srPbftMessage.getNo())) { try { - Thread.sleep(100); + wait(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Exception e) { diff --git a/framework/build.gradle b/framework/build.gradle index 877ed4e9073..8c4fbfc4583 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -216,13 +216,11 @@ createScript(project, 'org.tron.program.SolidityNode', 'SolidityNode') createScript(project, 'org.tron.program.FullNode', 'FullNode') createScript(project, 'org.tron.program.KeystoreFactory', 'KeystoreFactory') createScript(project, 'org.tron.program.DBConvert', 'DBConvert') -createScript(project, 'org.tron.tool.litefullnode.LiteFullNodeTool', 'LiteFullNodeTool') def releaseBinary = hasProperty('binaryRelease') ? getProperty('binaryRelease') : 'true' def skipSolidity = hasProperty('skipSolidity') ? true : false def skipKeystore = hasProperty('skipKeystore') ? true : false def skipConvert = hasProperty('skipConvert') ? true : false -def skipLite = hasProperty('skipLite') ? true : false def skipAll = hasProperty('skipAll') ? true : false if (releaseBinary == 'true') { artifacts { @@ -241,10 +239,6 @@ if (releaseBinary == 'true') { artifacts { archives(binaryRelease('buildDBConvertJar', 'DBConvert', 'org.tron.program.DBConvert'))} } - if (!skipLite) { - artifacts { - archives(binaryRelease('buildLiteFullNodeToolJar', 'LiteFullNodeTool', 'org.tron.tool.litefullnode.LiteFullNodeTool'))} - } } } 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 9133fddf434..3cb75cb1e24 100644 --- a/framework/src/main/java/org/tron/common/application/ApplicationImpl.java +++ b/framework/src/main/java/org/tron/common/application/ApplicationImpl.java @@ -54,13 +54,13 @@ public void initServices(CommonParameter parameter) { * start up the app. */ public void startup() { + this.initServices(Args.getInstance()); + this.startServices(); if ((!Args.getInstance().isSolidityNode()) && (!Args.getInstance().isP2pDisable())) { tronNetService.start(); } consensusService.start(); MetricsUtil.init(); - this.initServices(Args.getInstance()); - this.startServices(); } @Override diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java index cd2e26aa6f7..96af6fc7476 100755 --- a/framework/src/main/java/org/tron/core/Wallet.java +++ b/framework/src/main/java/org/tron/core/Wallet.java @@ -4085,6 +4085,9 @@ private long[] checkPublicAmount(byte[] address, BigInteger fromAmount, BigInteg } catch (ContractExeException e) { throw new ContractExeException("Get shielded contract scalingFactor failed"); } + if (scalingFactor.compareTo(BigInteger.ZERO) <= 0) { + throw new ContractValidateException("scalingFactor must be positive"); + } // fromAmount and toAmount must be a multiple of scalingFactor if (!(fromAmount.mod(scalingFactor).equals(BigInteger.ZERO) 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 9694668af01..a8547b73948 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 @@ -229,8 +229,8 @@ public static void clearParam() { PARAMETER.dynamicConfigEnable = false; PARAMETER.dynamicConfigCheckInterval = 600; PARAMETER.allowTvmShangHai = 0; - PARAMETER.unsolidifiedBlockCheck = true; - PARAMETER.maxUnsolidifiedBlocks = 1000; + PARAMETER.unsolidifiedBlockCheck = false; + PARAMETER.maxUnsolidifiedBlocks = 54; PARAMETER.allowOldRewardOpt = 0; } @@ -530,6 +530,7 @@ public static void setParam(final String[] args, final String confFileName) { PARAMETER.storage.setDefaultDbOptions(config); PARAMETER.storage.setPropertyMapFromConfig(config); PARAMETER.storage.setCacheStrategies(config); + PARAMETER.storage.setDbRoots(config); PARAMETER.seedNode = new SeedNode(); PARAMETER.seedNode.setAddressList(loadSeeds(config)); @@ -1185,16 +1186,24 @@ public static void setParam(final String[] args, final String confFileName) { .getInt(Constant.COMMITTEE_ALLOW_TVM_SHANGHAI) : 0; PARAMETER.unsolidifiedBlockCheck = - !config.hasPath(Constant.UNSOLIDIFIED_BLOCK_CHECK) - || config.getBoolean(Constant.UNSOLIDIFIED_BLOCK_CHECK); + config.hasPath(Constant.UNSOLIDIFIED_BLOCK_CHECK) + && config.getBoolean(Constant.UNSOLIDIFIED_BLOCK_CHECK); PARAMETER.maxUnsolidifiedBlocks = config.hasPath(Constant.MAX_UNSOLIDIFIED_BLOCKS) ? config - .getInt(Constant.MAX_UNSOLIDIFIED_BLOCKS) : 1000; - - PARAMETER.allowOldRewardOpt = - config.hasPath(Constant.COMMITTEE_ALLOW_OLD_REWARD_OPT) ? config - .getInt(Constant.COMMITTEE_ALLOW_OLD_REWARD_OPT) : 0; + .getInt(Constant.MAX_UNSOLIDIFIED_BLOCKS) : 54; + + long allowOldRewardOpt = config.hasPath(Constant.COMMITTEE_ALLOW_OLD_REWARD_OPT) ? config + .getInt(Constant.COMMITTEE_ALLOW_OLD_REWARD_OPT) : 0; + if (allowOldRewardOpt == 1 && PARAMETER.allowNewRewardAlgorithm != 1 + && PARAMETER.allowNewReward != 1 && PARAMETER.allowTvmVote != 1) { + throw new IllegalArgumentException( + "At least one of the following proposals is required to be opened first: " + + "committee.allowNewRewardAlgorithm = 1" + + " or committee.allowNewReward = 1" + + " or committee.allowTvmVote = 1."); + } + PARAMETER.allowOldRewardOpt = allowOldRewardOpt; logConfig(); } 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 b0d902eb84a..63bbef9ff7f 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -130,6 +130,7 @@ import org.tron.core.metrics.MetricsKey; import org.tron.core.metrics.MetricsUtil; import org.tron.core.service.MortgageService; +import org.tron.core.service.RewardViCalService; import org.tron.core.store.AccountAssetStore; import org.tron.core.store.AccountIdIndexStore; import org.tron.core.store.AccountIndexStore; @@ -260,6 +261,9 @@ public class Manager { private ExecutorService filterEs; private static final String filterEsName = "filter"; + @Autowired + private RewardViCalService rewardViCalService; + /** * Cycle thread to rePush Transactions */ @@ -465,6 +469,7 @@ public void init() { revokingStore.disable(); revokingStore.check(); transactionCache.initCache(); + rewardViCalService.init(); this.setProposalController(ProposalController.createInstance(this)); this.setMerkleContainer( merkleContainer.createInstance(chainBaseManager.getMerkleTreeStore(), @@ -1843,6 +1848,7 @@ private void postSolidityLogContractTrigger(Long blockNum, Long lastSolidityNum) triggerCapsule.setTriggerName(Trigger.SOLIDITYLOG_TRIGGER_NAME); EventPluginLoader.getInstance().postSolidityLogTrigger(triggerCapsule); } else { + // when switch fork, block will be post to triggerCapsuleQueue, transaction may be not found logger.error("PostSolidityLogContractTrigger txId = {} not contains transaction.", triggerCapsule.getTransactionId()); } @@ -1908,6 +1914,10 @@ public long getSyncBeginNumber() { chainBaseManager.getDynamicPropertiesStore().getLatestBlockHeaderNumber() - revokingStore.size(), chainBaseManager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum()); + return this.fetchSyncBeginNumber(); + } + + public long fetchSyncBeginNumber() { return chainBaseManager.getDynamicPropertiesStore().getLatestBlockHeaderNumber() - revokingStore.size(); } diff --git a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java index b98d0e173fb..867ced5dbff 100755 --- a/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java +++ b/framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java @@ -4,6 +4,7 @@ import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.tron.common.utils.ByteArray; +import org.tron.common.utils.DecodeUtil; import org.tron.common.utils.StringUtil; import org.tron.core.ChainBaseManager; import org.tron.core.capsule.BlockCapsule; @@ -169,6 +170,22 @@ public boolean valid() { return false; } + int maxByteSize = 200; + ByteString address = this.helloMessage.getAddress(); + if (!address.isEmpty() && address.toByteArray().length > maxByteSize) { + return false; + } + + ByteString sig = this.helloMessage.getSignature(); + if (!sig.isEmpty() && sig.toByteArray().length > maxByteSize) { + return false; + } + + ByteString codeVersion = this.helloMessage.getCodeVersion(); + if (!codeVersion.isEmpty() && codeVersion.toByteArray().length > maxByteSize) { + return false; + } + return true; } diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/BlockMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/BlockMsgHandler.java index 9bb746346a6..14523df86a5 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/BlockMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/BlockMsgHandler.java @@ -125,6 +125,14 @@ private void check(PeerConnection peer, BlockMessage msg) throws P2pException { private void processBlock(PeerConnection peer, BlockCapsule block) throws P2pException { BlockId blockId = block.getBlockId(); + boolean flag = tronNetDelegate.validBlock(block); + if (!flag) { + logger.warn("Receive a bad block from {}, {}, {}", + peer.getInetSocketAddress(), blockId.getString(), + Hex.toHexString(block.getWitnessAddress().toByteArray())); + return; + } + if (!tronNetDelegate.containBlock(block.getParentBlockId())) { logger.warn("Get unlink block {} from {}, head is {}", blockId.getString(), peer.getInetAddress(), tronNetDelegate.getHeadBlockId().getString()); @@ -138,16 +146,10 @@ private void processBlock(PeerConnection peer, BlockCapsule block) throws P2pExc return; } - boolean flag = tronNetDelegate.validBlock(block); - if (flag) { - broadcast(new BlockMessage(block)); - } + broadcast(new BlockMessage(block)); try { tronNetDelegate.processBlock(block, false); - if (!flag) { - broadcast(new BlockMessage(block)); - } witnessProductBlockService.validWitnessProductTwoBlock(block); diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/PbftDataSyncHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/PbftDataSyncHandler.java index 60f614632a4..d66fa6d41f7 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/PbftDataSyncHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/PbftDataSyncHandler.java @@ -1,5 +1,7 @@ package org.tron.core.net.messagehandler; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.common.collect.Sets; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; @@ -8,12 +10,11 @@ import java.security.SignatureException; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -37,7 +38,9 @@ @Service public class PbftDataSyncHandler implements TronMsgHandler, Closeable { - private Map pbftCommitMessageCache = new ConcurrentHashMap<>(); + private static final Cache pbftCommitMessageCache = + CacheBuilder.newBuilder().initialCapacity(100).maximumSize(200) + .expireAfterWrite(10, TimeUnit.MINUTES).build(); private final String esName = "valid-header-pbft-sign"; @@ -51,6 +54,9 @@ public class PbftDataSyncHandler implements TronMsgHandler, Closeable { public void processMessage(PeerConnection peer, TronMessage msg) throws P2pException { PbftCommitMessage pbftCommitMessage = (PbftCommitMessage) msg; try { + if (!chainBaseManager.getDynamicPropertiesStore().allowPBFT()) { + return; + } Raw raw = Raw.parseFrom(pbftCommitMessage.getPBFTCommitResult().getData()); pbftCommitMessageCache.put(raw.getViewN(), pbftCommitMessage); } catch (InvalidProtocolBufferException e) { @@ -64,7 +70,8 @@ public void processPBFTCommitData(BlockCapsule block) { return; } long epoch = 0; - PbftCommitMessage pbftCommitMessage = pbftCommitMessageCache.remove(block.getNum()); + PbftCommitMessage pbftCommitMessage = pbftCommitMessageCache.getIfPresent(block.getNum()); + pbftCommitMessageCache.invalidate(block.getNum()); long maintenanceTimeInterval = chainBaseManager.getDynamicPropertiesStore() .getMaintenanceTimeInterval(); if (pbftCommitMessage == null) { @@ -75,7 +82,8 @@ public void processPBFTCommitData(BlockCapsule block) { Raw raw = Raw.parseFrom(pbftCommitMessage.getPBFTCommitResult().getData()); epoch = raw.getEpoch(); } - pbftCommitMessage = pbftCommitMessageCache.remove(epoch); + pbftCommitMessage = pbftCommitMessageCache.getIfPresent(epoch); + pbftCommitMessageCache.invalidate(epoch); if (pbftCommitMessage != null) { processPBFTCommitMessage(pbftCommitMessage); } diff --git a/framework/src/main/java/org/tron/core/net/messagehandler/PbftMsgHandler.java b/framework/src/main/java/org/tron/core/net/messagehandler/PbftMsgHandler.java index 4e4cc858898..d086cc28b6c 100644 --- a/framework/src/main/java/org/tron/core/net/messagehandler/PbftMsgHandler.java +++ b/framework/src/main/java/org/tron/core/net/messagehandler/PbftMsgHandler.java @@ -33,6 +33,9 @@ public class PbftMsgHandler { private TronNetDelegate tronNetDelegate; public void processMessage(PeerConnection peer, PbftMessage msg) throws Exception { + if (!tronNetDelegate.allowPBFT()) { + return; + } if (Param.getInstance().getPbftInterface().isSyncing()) { return; } diff --git a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java index aa6567d1cda..6cd117c83dd 100644 --- a/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java +++ b/framework/src/main/java/org/tron/core/net/service/handshake/HandshakeService.java @@ -48,12 +48,15 @@ public void processHelloMessage(PeerConnection peer, HelloMessage msg) { } if (!msg.valid()) { - logger.warn("Peer {} invalid hello message parameters, " - + "GenesisBlockId: {}, SolidBlockId: {}, HeadBlockId: {}", - peer.getInetSocketAddress(), - ByteArray.toHexString(msg.getInstance().getGenesisBlockId().getHash().toByteArray()), - ByteArray.toHexString(msg.getInstance().getSolidBlockId().getHash().toByteArray()), - ByteArray.toHexString(msg.getInstance().getHeadBlockId().getHash().toByteArray())); + logger.warn("Peer {} invalid hello message parameters, GenesisBlockId: {}, SolidBlockId: {}, " + + "HeadBlockId: {}, address: {}, sig: {}, codeVersion: {}", + peer.getInetSocketAddress(), + ByteArray.toHexString(msg.getInstance().getGenesisBlockId().getHash().toByteArray()), + ByteArray.toHexString(msg.getInstance().getSolidBlockId().getHash().toByteArray()), + ByteArray.toHexString(msg.getInstance().getHeadBlockId().getHash().toByteArray()), + msg.getInstance().getAddress().toByteArray().length, + msg.getInstance().getSignature().toByteArray().length, + msg.getInstance().getCodeVersion().toByteArray().length); peer.disconnect(ReasonCode.UNEXPECTED_IDENTITY); return; } diff --git a/framework/src/main/java/org/tron/core/services/NodeInfoService.java b/framework/src/main/java/org/tron/core/services/NodeInfoService.java index ddf72c64410..b55ce96748e 100644 --- a/framework/src/main/java/org/tron/core/services/NodeInfoService.java +++ b/framework/src/main/java/org/tron/core/services/NodeInfoService.java @@ -195,7 +195,7 @@ private void setConfigNodeInfo(NodeInfo nodeInfo) { } protected void setBlockInfo(NodeInfo nodeInfo) { - nodeInfo.setBeginSyncNum(dbManager.getSyncBeginNumber()); + nodeInfo.setBeginSyncNum(dbManager.fetchSyncBeginNumber()); nodeInfo.setBlock(chainBaseManager.getHeadBlockId().getString()); nodeInfo.setSolidityBlock(chainBaseManager.getSolidBlockId().getString()); } diff --git a/framework/src/main/java/org/tron/core/services/RpcApiService.java b/framework/src/main/java/org/tron/core/services/RpcApiService.java index c5077facf6f..85ad2dd18dd 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -89,6 +89,7 @@ import org.tron.core.config.args.Args; import org.tron.core.db.Manager; import org.tron.core.exception.BadItemException; +import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; import org.tron.core.exception.ItemNotFoundException; import org.tron.core.exception.NonUniqueObjectException; @@ -2475,7 +2476,7 @@ public void createShieldedContractParameters( ShieldedTRC20Parameters shieldedTRC20Parameters = wallet .createShieldedContractParameters(request); responseObserver.onNext(shieldedTRC20Parameters); - } catch (ZksnarkException | ContractValidateException e) { + } catch (ZksnarkException | ContractValidateException | ContractExeException e) { responseObserver.onError(getRunTimeException(e)); logger.info("createShieldedContractParameters: {}", e.getMessage()); return; diff --git a/framework/src/main/java/org/tron/program/Version.java b/framework/src/main/java/org/tron/program/Version.java index a01eb714297..483f5be713f 100644 --- a/framework/src/main/java/org/tron/program/Version.java +++ b/framework/src/main/java/org/tron/program/Version.java @@ -2,9 +2,9 @@ public class Version { - public static final String VERSION_NAME = "GreatVoyage-v4.7.3-5-g788136ebe"; - public static final String VERSION_CODE = "18180"; - private static final String VERSION = "4.7.3.1"; + public static final String VERSION_NAME = "GreatVoyage-v4.7.3.1-78-ge84a9e778"; + public static final String VERSION_CODE = "18260"; + private static final String VERSION = "4.7.4"; public static String getVersion() { return VERSION; diff --git a/framework/src/main/java/org/tron/tool/litefullnode/DbTool.java b/framework/src/main/java/org/tron/tool/litefullnode/DbTool.java deleted file mode 100644 index 5f43361eddf..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/DbTool.java +++ /dev/null @@ -1,199 +0,0 @@ -package org.tron.tool.litefullnode; - -import static org.fusesource.leveldbjni.JniDBFactory.factory; - -import com.google.common.collect.Maps; -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Iterator; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; -import org.iq80.leveldb.CompressionType; -import org.iq80.leveldb.DB; -import org.iq80.leveldb.DBIterator; -import org.iq80.leveldb.Options; -import org.iq80.leveldb.WriteOptions; -import org.rocksdb.BlockBasedTableConfig; -import org.rocksdb.BloomFilter; -import org.rocksdb.RocksDBException; -import org.tron.common.utils.PropUtil; -import org.tron.tool.litefullnode.db.DBInterface; -import org.tron.tool.litefullnode.db.LevelDBImpl; -import org.tron.tool.litefullnode.db.RocksDBImpl; - -@Slf4j(topic = "tool") -public class DbTool { - - private static final String KEY_ENGINE = "ENGINE"; - public static final String ENGINE_FILE = "engine.properties"; - private static final String FILE_SEPARATOR = File.separator; - private static final String ROCKSDB = "ROCKSDB"; - - private static Map dbMap = Maps.newHashMap(); - - enum DbType { - LevelDB, - RocksDB - } - - /** - * Get the DB object according to the specified path, - * create db object when not exists, otherwise get it from the dbMap. - * - * @param sourceDir the parent path of db - * @param dbName db dir name - * - * @return db object - * - * @throws IOException IOException - * @throws RocksDBException RocksDBException - */ - public static DBInterface getDB(String sourceDir, String dbName) - throws IOException, RocksDBException { - Path path = Paths.get(sourceDir, dbName); - if (dbMap.containsKey(path.toString())) { - return dbMap.get(path.toString()); - } - DbType type = getDbType(sourceDir, dbName); - DBInterface db; - switch (type) { - case LevelDB: - db = openLevelDb(path); - dbMap.put(path.toString(), db); - break; - case RocksDB: - db = openRocksDb(path); - dbMap.put(path.toString(), db); - break; - default: - throw new IllegalStateException("Unexpected value: " + type); - } - return db; - } - - /** - * Close db. - * @param sourceDir db parentPath - * @param dbName db dirname - * @throws IOException IOException - */ - public static void closeDB(String sourceDir, String dbName) - throws IOException { - Path path = Paths.get(sourceDir, dbName); - DBInterface db = dbMap.get(path.toString()); - if (db != null) { - try { - dbMap.remove(path.toString()); - db.close(); - } catch (IOException e) { - logger.error("close db {} error: {}", path, e); - throw e; - } - } - } - - /** - * Close all dbs. - */ - public static void close() { - Iterator> iterator = dbMap.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry next = iterator.next(); - try { - next.getValue().close(); - } catch (IOException e) { - logger.error("close db failed, db: {}", next.getKey(), e); - } - iterator.remove(); - } - } - - private static DbType getDbType(String sourceDir, String dbName) { - String engineFile = String.format("%s%s%s%s%s", sourceDir, FILE_SEPARATOR, - dbName, FILE_SEPARATOR, ENGINE_FILE); - if (!new File(engineFile).exists()) { - return DbType.LevelDB; - } - String engine = PropUtil.readProperty(engineFile, KEY_ENGINE); - if (engine.equalsIgnoreCase(ROCKSDB)) { - return DbType.RocksDB; - } else { - return DbType.LevelDB; - } - } - - private static LevelDBImpl openLevelDb(Path db) throws IOException { - DB database; - Options options = getLevelDbOptions(); - try { - database = factory.open(db.toFile(), options); - } catch (IOException e) { - if (e.getMessage().contains("Corruption:")) { - factory.repair(db.toFile(), options); - database = factory.open(db.toFile(), options); - } else { - throw e; - } - } - return new LevelDBImpl(database); - } - - private static RocksDBImpl openRocksDb(Path db) throws RocksDBException { - org.rocksdb.RocksDB database; - try (org.rocksdb.Options options = newDefaultRocksDbOptions()) { - database = org.rocksdb.RocksDB.open(options, db.toString()); - } catch (Exception e) { - throw e; - } - return new RocksDBImpl(database); - } - - private static org.rocksdb.Options newDefaultRocksDbOptions() { - org.rocksdb.Options options = new org.rocksdb.Options(); - - options.setCreateIfMissing(true); - options.setIncreaseParallelism(1); - options.setNumLevels(7); - options.setMaxOpenFiles(-1); - options.setTargetFileSizeBase(64 * 1024 * 1024); - options.setTargetFileSizeMultiplier(1); - options.setMaxBytesForLevelBase(512 * 1024 * 1024); - options.setMaxBackgroundCompactions(Math.max(1, Runtime.getRuntime().availableProcessors())); - options.setLevel0FileNumCompactionTrigger(4); - options.setLevelCompactionDynamicLevelBytes(true); - - BlockBasedTableConfig tableCfg = new BlockBasedTableConfig(); - tableCfg.setBlockSize(64 * 1024); - tableCfg.setBlockCacheSize(32 * 1024 * 1024); - tableCfg.setCacheIndexAndFilterBlocks(true); - tableCfg.setPinL0FilterAndIndexBlocksInCache(true); - tableCfg.setFilter(new BloomFilter(10, false)); - - options.setTableFormatConfig(tableCfg); - return options; - } - - private static Options getLevelDbOptions() { - CompressionType defaultCompressionType = CompressionType.SNAPPY; - int defaultBlockSize = 4 * 1024; - int defaultWriteBufferSize = 10 * 1024 * 1024; - long defaultCacheSize = 10 * 1024 * 1024L; - int defaultMaxOpenFiles = 100; - - Options dbOptions = new Options(); - - dbOptions.createIfMissing(true); - dbOptions.paranoidChecks(true); - dbOptions.verifyChecksums(true); - - dbOptions.compressionType(defaultCompressionType); - dbOptions.blockSize(defaultBlockSize); - dbOptions.writeBufferSize(defaultWriteBufferSize); - dbOptions.cacheSize(defaultCacheSize); - dbOptions.maxOpenFiles(defaultMaxOpenFiles); - - return dbOptions; - } -} diff --git a/framework/src/main/java/org/tron/tool/litefullnode/LiteFullNodeTool.java b/framework/src/main/java/org/tron/tool/litefullnode/LiteFullNodeTool.java deleted file mode 100644 index 40c371c58e0..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/LiteFullNodeTool.java +++ /dev/null @@ -1,637 +0,0 @@ -package org.tron.tool.litefullnode; - -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.internal.Lists; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Strings; -import com.google.common.collect.Maps; -import com.google.common.primitives.Bytes; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.rocksdb.RocksDBException; -import org.tron.common.parameter.CommonParameter; -import org.tron.common.utils.ByteArray; -import org.tron.common.utils.FileUtil; -import org.tron.common.utils.PropUtil; -import org.tron.core.Constant; -import org.tron.core.capsule.BlockCapsule; -import org.tron.core.capsule.TransactionCapsule; -import org.tron.core.db2.common.Value; -import org.tron.core.db2.core.SnapshotManager; -import org.tron.core.exception.BadItemException; -import org.tron.tool.litefullnode.db.DBInterface; -import org.tron.tool.litefullnode.iterator.DBIterator; - -@Slf4j(topic = "tool") -@Deprecated -public class LiteFullNodeTool { - - private static final long START_TIME = System.currentTimeMillis() / 1000; - - private static long RECENT_BLKS = 65536; - - private static final String SNAPSHOT_DIR_NAME = "snapshot"; - private static final String HISTORY_DIR_NAME = "history"; - private static final String INFO_FILE_NAME = "info.properties"; - private static final String BACKUP_DIR_PREFIX = ".bak_"; - private static final String CHECKPOINT_DB = "tmp"; - private static final String CHECKPOINT_DB_V2 = "checkpoint"; - private static final String BLOCK_DB_NAME = "block"; - private static final String BLOCK_INDEX_DB_NAME = "block-index"; - private static final String TRANS_DB_NAME = "trans"; - private static final String TRANSACTION_RET_DB_NAME = "transactionRetStore"; - private static final String TRANSACTION_HISTORY_DB_NAME = "transactionHistoryStore"; - private static final String PROPERTIES_DB_NAME = "properties"; - - private static final String DIR_FORMAT_STRING = "%s%s%s"; - - private static List archiveDbs = Arrays.asList( - BLOCK_DB_NAME, - BLOCK_INDEX_DB_NAME, - TRANS_DB_NAME, - TRANSACTION_RET_DB_NAME, - TRANSACTION_HISTORY_DB_NAME); - - /** - * Create the snapshot dataset. - * - * @param sourceDir the original fullnode database dir, - * same with {storage.db.directory} in conf file. - * @param snapshotDir the path that stores the snapshot dataset - */ - public void generateSnapshot(String sourceDir, String snapshotDir) { - logger.info("Start create snapshot."); - long start = System.currentTimeMillis(); - snapshotDir = Paths.get(snapshotDir, SNAPSHOT_DIR_NAME).toString(); - try { - hasEnoughBlock(sourceDir); - List snapshotDbs = getSnapshotDbs(sourceDir); - split(sourceDir, snapshotDir, snapshotDbs); - mergeCheckpoint2Snapshot(sourceDir, snapshotDir); - // write genesisBlock , latest recent blocks and trans - fillSnapshotBlockAndTransDb(sourceDir, snapshotDir); - generateInfoProperties(Paths.get(snapshotDir, INFO_FILE_NAME).toString(), sourceDir); - } catch (IOException | RocksDBException e) { - logger.error("Create snapshot failed, {}.", e.getMessage()); - return; - } - long end = System.currentTimeMillis(); - logger.info("Create snapshot finished, take {} s.\n", (end - start) / 1000); - } - - /** - * Create the history dataset. - * - * @param sourceDir the original fullnode database dir, - * same with {storage.db.directory} in conf file. - * @param historyDir the path that stores the history dataset - */ - public void generateHistory(String sourceDir, String historyDir) { - logger.info("Start create history."); - long start = System.currentTimeMillis(); - historyDir = Paths.get(historyDir, HISTORY_DIR_NAME).toString(); - try { - if (isLite(sourceDir)) { - throw new IllegalStateException( - String.format("Unavailable sourceDir: %s is not fullNode data.", sourceDir)); - } - hasEnoughBlock(sourceDir); - split(sourceDir, historyDir, archiveDbs); - mergeCheckpoint2History(sourceDir, historyDir); - generateInfoProperties(Paths.get(historyDir, INFO_FILE_NAME).toString(), sourceDir); - } catch (IOException | RocksDBException e) { - logger.error("Create history failed, {}.", e.getMessage()); - return; - } - long end = System.currentTimeMillis(); - logger.info("Create history finished, take {} s.\n", (end - start) / 1000); - } - - /** - * Merge the history dataset into database. - * - * @param historyDir the path that stores the history dataset - * - * @param databaseDir lite fullnode database path - */ - public void completeHistoryData(String historyDir, String databaseDir) { - logger.info("Start merge history to lite node."); - long start = System.currentTimeMillis(); - BlockNumInfo blockNumInfo = null; - try { - // check historyDir is from lite data - if (isLite(historyDir)) { - throw new IllegalStateException( - String.format("Unavailable history: %s is not generated by fullNode data.", - historyDir)); - } - // 1. check block number and genesis block are compatible, - // and return the block numbers of snapshot and history - blockNumInfo = checkAndGetBlockNumInfo(historyDir, databaseDir); - // 2. move archive dbs to bak - backupArchiveDbs(databaseDir); - // 3. copy history data to databaseDir - copyHistory2Database(historyDir, databaseDir); - // 4. delete the duplicate block data in history data - trimHistory(databaseDir, blockNumInfo); - // 5. merge bak to database - mergeBak2Database(databaseDir); - // 6. delete snapshot flag - deleteSnapshotFlag(databaseDir); - } catch (IOException | RocksDBException | BadItemException e) { - logger.error("Merge history data to database failed, {}.", e.getMessage()); - return; - } - long end = System.currentTimeMillis(); - logger.info("Merge history finished, take {} s. \n", (end - start) / 1000); - } - - private List getSnapshotDbs(String sourceDir) { - List snapshotDbs = Lists.newArrayList(); - File basePath = new File(sourceDir); - Arrays.stream(Objects.requireNonNull(basePath.listFiles())) - .filter(File::isDirectory) - .filter(dir -> !archiveDbs.contains(dir.getName())) - .forEach(dir -> snapshotDbs.add(dir.getName())); - return snapshotDbs; - } - - private void mergeCheckpoint2Snapshot(String sourceDir, String historyDir) { - List snapshotDbs = getSnapshotDbs(sourceDir); - mergeCheckpoint(sourceDir, historyDir, snapshotDbs); - } - - private void mergeCheckpoint2History(String sourceDir, String destDir) { - mergeCheckpoint(sourceDir, destDir, archiveDbs); - } - - private void split(String sourceDir, String destDir, List dbs) throws IOException { - logger.info("Begin to split the dbs."); - if (!new File(sourceDir).isDirectory()) { - throw new RuntimeException(String.format("sourceDir: %s must be a directory ", sourceDir)); - } - File destPath = new File(destDir); - if (new File(destDir).exists()) { - throw new RuntimeException(String.format( - "destDir: %s is already exist, please remove it first", destDir)); - } - if (!destPath.mkdirs()) { - throw new RuntimeException(String.format("destDir: %s create failed, please check", destDir)); - } - Util.copyDatabases(Paths.get(sourceDir), Paths.get(destDir), dbs); - } - - private void mergeCheckpoint(String sourceDir, String destDir, List destDbs) { - logger.info("Begin to merge checkpoint to dataset."); - try { - List cpList = getCheckpointV2List(sourceDir); - if (cpList.size() > 0) { - for (String cp: cpList) { - DBInterface checkpointDb = DbTool.getDB(sourceDir + "/" + CHECKPOINT_DB_V2, cp); - recover(checkpointDb, destDir, destDbs); - } - } else { - DBInterface tmpDb = DbTool.getDB(sourceDir, CHECKPOINT_DB); - recover(tmpDb, destDir, destDbs); - } - } catch (IOException | RocksDBException e) { - throw new RuntimeException(e); - } - } - - private void recover(DBInterface db, String destDir, List destDbs) - throws IOException, RocksDBException { - try (DBIterator iterator = db.iterator()) { - for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { - byte[] key = iterator.getKey(); - byte[] value = iterator.getValue(); - String dbName = SnapshotManager.simpleDecode(key); - byte[] realKey = Arrays.copyOfRange(key, dbName.getBytes().length + 4, key.length); - byte[] realValue = - value.length == 1 ? null : Arrays.copyOfRange(value, 1, value.length); - if (destDbs != null && destDbs.contains(dbName)) { - DBInterface destDb = DbTool.getDB(destDir, dbName); - if (realValue != null) { - destDb.put(realKey, realValue); - } else { - byte op = value[0]; - if (Value.Operator.DELETE.getValue() == op) { - destDb.delete(realKey); - } else { - destDb.put(realKey, new byte[0]); - } - } - } - } - } - } - - private void generateInfoProperties(String propertyfile, String databaseDir) - throws IOException, RocksDBException { - logger.info("Create {} for dataset.", INFO_FILE_NAME); - if (!FileUtil.createFileIfNotExists(propertyfile)) { - throw new RuntimeException("Create properties file failed."); - } - if (!PropUtil.writeProperty(propertyfile, Constant.SPLIT_BLOCK_NUM, - Long.toString(getLatestBlockHeaderNum(databaseDir)))) { - throw new RuntimeException("Write properties file failed."); - } - } - - private long getLatestBlockHeaderNum(String databaseDir) throws IOException, RocksDBException { - // query latest_block_header_number from checkpoint first - final String latestBlockHeaderNumber = "latest_block_header_number"; - List cpList = getCheckpointV2List(databaseDir); - DBInterface checkpointDb = null; - if (cpList.size() > 0) { - String lastestCp = cpList.get(cpList.size() - 1); - checkpointDb = DbTool.getDB(databaseDir + "/" + CHECKPOINT_DB_V2, lastestCp); - } else { - checkpointDb = DbTool.getDB(databaseDir, CHECKPOINT_DB); - } - Long blockNumber = getLatestBlockHeaderNumFromCP(checkpointDb, - latestBlockHeaderNumber.getBytes()); - if (blockNumber != null) { - return blockNumber; - } - // query from propertiesDb if checkpoint not contains latest_block_header_number - DBInterface propertiesDb = DbTool.getDB(databaseDir, PROPERTIES_DB_NAME); - return Optional.ofNullable(propertiesDb.get(ByteArray.fromString(latestBlockHeaderNumber))) - .map(ByteArray::toLong) - .orElseThrow( - () -> new IllegalArgumentException("not found latest block header number")); - } - - private Long getLatestBlockHeaderNumFromCP(DBInterface db, byte[] key) { - byte[] value = db.get(Bytes.concat(simpleEncode(PROPERTIES_DB_NAME), key)); - if (value != null && value.length > 1) { - return ByteArray.toLong(Arrays.copyOfRange(value, 1, value.length)); - } - return null; - } - - /** - * recent blocks, trans and genesis block. - */ - private void fillSnapshotBlockAndTransDb(String sourceDir, String snapshotDir) - throws IOException, RocksDBException { - logger.info("Begin to fill {} block, genesis block and trans to snapshot.", RECENT_BLKS); - DBInterface sourceBlockIndexDb = DbTool.getDB(sourceDir, BLOCK_INDEX_DB_NAME); - DBInterface sourceBlockDb = DbTool.getDB(sourceDir, BLOCK_DB_NAME); - DBInterface destBlockDb = DbTool.getDB(snapshotDir, BLOCK_DB_NAME); - DBInterface destBlockIndexDb = DbTool.getDB(snapshotDir, BLOCK_INDEX_DB_NAME); - DBInterface destTransDb = DbTool.getDB(snapshotDir, TRANS_DB_NAME); - // put genesis block and block-index into snapshot - long genesisBlockNum = 0L; - byte[] genesisBlockID = sourceBlockIndexDb.get(ByteArray.fromLong(genesisBlockNum)); - destBlockIndexDb.put(ByteArray.fromLong(genesisBlockNum), genesisBlockID); - destBlockDb.put(genesisBlockID, sourceBlockDb.get(genesisBlockID)); - - long latestBlockNum = getLatestBlockHeaderNum(sourceDir); - long startIndex = latestBlockNum - RECENT_BLKS + 1; - // put the recent blocks and trans in snapshot - for (long blockNum = startIndex; blockNum <= latestBlockNum; blockNum++) { - try { - byte[] blockId = getDataFromSourceDB(sourceDir, BLOCK_INDEX_DB_NAME, - Longs.toByteArray(blockNum)); - byte[] block = getDataFromSourceDB(sourceDir, BLOCK_DB_NAME, blockId); - // put block - destBlockDb.put(blockId, block); - // put block index - destBlockIndexDb.put(ByteArray.fromLong(blockNum), blockId); - // put trans - long finalBlockNum = blockNum; - new BlockCapsule(block).getTransactions().stream().map( - tc -> tc.getTransactionId().getBytes()) - .map(bytes -> Maps.immutableEntry(bytes, Longs.toByteArray(finalBlockNum))) - .forEach(e -> destTransDb.put(e.getKey(), e.getValue())); - } catch (IOException | RocksDBException | BadItemException e) { - throw new RuntimeException(e.getMessage()); - } - } - // copy engine.properties for block、block-index、trans from source if exist - copyEngineIfExist(sourceDir, snapshotDir, BLOCK_DB_NAME, BLOCK_INDEX_DB_NAME, TRANS_DB_NAME); - } - - private void copyEngineIfExist(String source, String dest, String... dbNames) { - for (String dbName : dbNames) { - Path ori = Paths.get(source, dbName, DbTool.ENGINE_FILE); - if (ori.toFile().exists()) { - Util.copy(ori, Paths.get(dest, dbName, DbTool.ENGINE_FILE)); - } - } - } - - private byte[] getGenesisBlockHash(String parentDir) throws IOException, RocksDBException { - long genesisBlockNum = 0L; - DBInterface blockIndexDb = DbTool.getDB(parentDir, BLOCK_INDEX_DB_NAME); - byte[] result = blockIndexDb.get(ByteArray.fromLong(genesisBlockNum)); - // when merge history, block-index db will be moved to bak dir and replaced by history - // so should close this db and reopen it. - DbTool.closeDB(parentDir, BLOCK_INDEX_DB_NAME); - return result; - } - - private static byte[] simpleEncode(String s) { - byte[] bytes = s.getBytes(); - byte[] length = Ints.toByteArray(bytes.length); - byte[] r = new byte[4 + bytes.length]; - System.arraycopy(length, 0, r, 0, 4); - System.arraycopy(bytes, 0, r, 4, bytes.length); - return r; - } - - private BlockNumInfo checkAndGetBlockNumInfo(String historyDir, String databaseDir) - throws IOException, RocksDBException { - logger.info("Check the compatibility of this history."); - String snapshotInfo = String.format( - DIR_FORMAT_STRING, databaseDir, File.separator, INFO_FILE_NAME); - String historyInfo = String.format( - DIR_FORMAT_STRING, historyDir, File.separator, INFO_FILE_NAME); - if (!FileUtil.isExists(snapshotInfo)) { - throw new FileNotFoundException( - "Snapshot property file is not found. maybe this is a complete fullnode?"); - } - if (!FileUtil.isExists(historyInfo)) { - throw new FileNotFoundException("history property file is not found."); - } - long snapshotBlkNum = Long.parseLong(PropUtil.readProperty(snapshotInfo, Constant - .SPLIT_BLOCK_NUM)); - long historyBlkNum = Long.parseLong(PropUtil.readProperty(historyInfo, Constant - .SPLIT_BLOCK_NUM)); - if (historyBlkNum < snapshotBlkNum) { - throw new RuntimeException( - String.format( - "History latest block number is lower than snapshot, history: %d, snapshot: %d", - historyBlkNum, snapshotBlkNum)); - } - // check genesis block is equal - if (!Arrays.equals(getGenesisBlockHash(databaseDir), getGenesisBlockHash(historyDir))) { - throw new RuntimeException(String.format( - "Genesis block hash is not equal, history: %s, database: %s", - Arrays.toString(getGenesisBlockHash(historyDir)), - Arrays.toString(getGenesisBlockHash(databaseDir)))); - } - return new BlockNumInfo(snapshotBlkNum, historyBlkNum); - } - - private void backupArchiveDbs(String databaseDir) throws IOException { - String bakDir = String.format("%s%s%s%d", - databaseDir, File.separator, BACKUP_DIR_PREFIX, START_TIME); - logger.info("Backup the archive dbs to {}.", bakDir); - if (!FileUtil.createDirIfNotExists(bakDir)) { - throw new RuntimeException(String.format("create bak dir %s failed", bakDir)); - } - Util.copyDatabases(Paths.get(databaseDir), Paths.get(bakDir), archiveDbs); - archiveDbs.forEach(db -> FileUtil.deleteDir(new File(databaseDir, db))); - } - - private void copyHistory2Database(String historyDir, String databaseDir) throws IOException { - logger.info("Begin to copy history to database."); - Util.copyDatabases(Paths.get(historyDir), Paths.get(databaseDir), archiveDbs); - } - - private void trimHistory(String databaseDir, BlockNumInfo blockNumInfo) - throws BadItemException, IOException, RocksDBException { - logger.info("Begin to trim the history data."); - DBInterface blockIndexDb = DbTool.getDB(databaseDir, BLOCK_INDEX_DB_NAME); - DBInterface blockDb = DbTool.getDB(databaseDir, BLOCK_DB_NAME); - DBInterface transDb = DbTool.getDB(databaseDir, TRANS_DB_NAME); - DBInterface tranRetDb = DbTool.getDB(databaseDir, TRANSACTION_RET_DB_NAME); - for (long n = blockNumInfo.getHistoryBlkNum(); n > blockNumInfo.getSnapshotBlkNum(); n--) { - byte[] blockIdHash = blockIndexDb.get(ByteArray.fromLong(n)); - BlockCapsule block = new BlockCapsule(blockDb.get(blockIdHash)); - // delete transactions - for (TransactionCapsule e : block.getTransactions()) { - transDb.delete(e.getTransactionId().getBytes()); - } - // delete transaction result - tranRetDb.delete(ByteArray.fromLong(n)); - // delete block - blockDb.delete(blockIdHash); - // delete block index - blockIndexDb.delete(ByteArray.fromLong(n)); - } - } - - private void mergeBak2Database(String databaseDir) throws IOException, RocksDBException { - String bakDir = String.format("%s%s%s%d", - databaseDir, File.separator, BACKUP_DIR_PREFIX, START_TIME); - logger.info("Begin to merge {} to database.", bakDir); - for (String dbName : archiveDbs) { - DBInterface bakDb = DbTool.getDB(bakDir, dbName); - DBInterface destDb = DbTool.getDB(databaseDir, dbName); - try (DBIterator iterator = bakDb.iterator()) { - for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { - destDb.put(iterator.getKey(), iterator.getValue()); - } - } - } - } - - private byte[] getDataFromSourceDB(String sourceDir, String dbName, byte[] key) - throws IOException, RocksDBException { - DBInterface sourceDb = DbTool.getDB(sourceDir, dbName); - DBInterface checkpointDb = DbTool.getDB(sourceDir, CHECKPOINT_DB); - // get data from tmp first. - byte[] valueFromTmp = checkpointDb.get(Bytes.concat(simpleEncode(dbName), key)); - byte[] value; - if (isEmptyBytes(valueFromTmp)) { - value = sourceDb.get(key); - } else { - value = valueFromTmp.length == 1 - ? null : Arrays.copyOfRange(valueFromTmp, 1, valueFromTmp.length); - } - if (isEmptyBytes(value)) { - throw new RuntimeException(String.format("data not found in store, dbName: %s, key: %s", - dbName, Arrays.toString(key))); - } - return value; - } - - /** - * return true if byte array is null or length is 0. - * @param b bytes - * @return true or false - */ - private static boolean isEmptyBytes(byte[] b) { - if (b != null) { - return b.length == 0; - } - return true; - } - - private void deleteSnapshotFlag(String databaseDir) throws IOException, RocksDBException { - logger.info("Delete the info file from {}.", databaseDir); - Files.delete(Paths.get(databaseDir, INFO_FILE_NAME)); - } - - private void hasEnoughBlock(String sourceDir) throws RocksDBException, IOException { - // check latest - long latest = getLatestBlockHeaderNum(sourceDir); - // check second ,skip 0; - long second = getSecondBlock(sourceDir); - if (latest - second + 1 < RECENT_BLKS) { - throw new NoSuchElementException( - String.format("At least %d blocks in block store, actual latestBlock:%d, firstBlock:%d.", - RECENT_BLKS, latest, second)); - } - } - - private boolean isLite(String databaseDir) throws RocksDBException, IOException { - return getSecondBlock(databaseDir) > 1; - } - - private long getSecondBlock(String databaseDir) throws RocksDBException, IOException { - long num = 0; - DBInterface sourceBlockIndexDb = DbTool.getDB(databaseDir, BLOCK_INDEX_DB_NAME); - DBIterator iterator = sourceBlockIndexDb.iterator(); - iterator.seek(ByteArray.fromLong(1)); - if (iterator.hasNext()) { - num = Longs.fromByteArray(iterator.getKey()); - } - return num; - } - - @VisibleForTesting - public static void setRecentBlks(long recentBlks) { - RECENT_BLKS = recentBlks; - } - - @VisibleForTesting - public static void reSetRecentBlks() { - RECENT_BLKS = 65536; - } - - private List getCheckpointV2List(String sourceDir) { - File file = new File(Paths.get(sourceDir, CHECKPOINT_DB_V2).toString()); - if (file.exists() && file.isDirectory() && file.list() != null) { - return Arrays.stream(file.list()).sorted().collect(Collectors.toList()); - } - return Lists.newArrayList(); - } - - private void run(Args argv) { - if (StringUtils.isBlank(argv.fnDataPath) || StringUtils.isBlank(argv.datasetPath)) { - throw new ParameterException("fnDataPath or datasetPath can't be null"); - } - switch (argv.operate) { - case "split": - if (Strings.isNullOrEmpty(argv.type)) { - throw new ParameterException("type can't be null when operate=split"); - } - if (SNAPSHOT_DIR_NAME.equals(argv.type)) { - generateSnapshot(argv.fnDataPath, argv.datasetPath); - } else if (HISTORY_DIR_NAME.equals(argv.type)) { - generateHistory(argv.fnDataPath, argv.datasetPath); - } else { - throw new ParameterException("not support type:" + argv.type); - } - break; - case "merge": - completeHistoryData(argv.datasetPath, argv.fnDataPath); - break; - default: - throw new ParameterException("not supportted operate:" + argv.operate); - } - DbTool.close(); - } - - /** - * main. - */ - public static void main(String[] args) { - logger.info("LiteFullNodeTool is deprecated and it will be removed in the next major release," - + " use Toolkit.jar db lite instead, parameters are fully compatible."); - Args argv = new Args(); - CommonParameter.getInstance().setValidContractProtoThreadNum(1); - LiteFullNodeTool tool = new LiteFullNodeTool(); - JCommander jct = JCommander.newBuilder() - .addObject(argv) - .build(); - jct.setProgramName("lite fullnode tool"); - try { - jct.parse(args); - if (argv.help) { - jct.usage(); - } else { - tool.run(argv); - } - } catch (Exception e) { - logger.error(e.getMessage()); - jct.usage(); - } - } - - static class Args { - @Parameter( - names = {"--operate", "-o"}, - help = true, required = true, - description = "operate: [ split | merge ]", - order = 1) - private String operate; - @Parameter(names = {"--type", "-t"}, - help = true, - description = "only used with operate=split: [ snapshot | history ]", - order = 2) - private String type; - @Parameter( - names = {"--fn-data-path"}, - help = true, required = true, - description = "the fullnode database path," - + " defined as ${storage.db.directory} in config.conf", - order = 3) - private String fnDataPath; - @Parameter( - names = {"--dataset-path"}, - help = true, required = true, - description = "dataset directory, when operation is `split`, " - + "`dataset-path` is the path that store the `Snapshot Dataset` or " - + "`History Dataset`, otherwise `dataset-path` should be " - + "the `History Dataset` path", - order = 4) - private String datasetPath; - @Parameter( - names = "--help", - help = true, - order = 5) - private boolean help; - } - - static class BlockNumInfo { - private long snapshotBlkNum; - private long historyBlkNum; - - public BlockNumInfo(long snapshotBlkNum, long historyBlkNum) { - this.snapshotBlkNum = snapshotBlkNum; - this.historyBlkNum = historyBlkNum; - } - - public long getSnapshotBlkNum() { - return snapshotBlkNum; - } - - public long getHistoryBlkNum() { - return historyBlkNum; - } - } -} - - - diff --git a/framework/src/main/java/org/tron/tool/litefullnode/README.md b/framework/src/main/java/org/tron/tool/litefullnode/README.md deleted file mode 100644 index 24357c2a99e..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# Lite FullNode Tool - -## Introduction - -Lite FullNode Tool is used to split the database of a FullNode into a `Snapshot dataset` and a `History dataset`. - -- `Snapshot dataset`: the minimum dataset for quick startup of the Lite FullNode. -- `History dataset`: the archive dataset that used for historical data queries. - -Remember stop the FullNode process before any operation. This tool provides the ability to specify which dataset to split. -The two datasets are split by the `latest_block_number`. Lite FullNode that startup by `Snapshot dataset` does not support query the historical data behind the `latest_block_number`, -this tool also provides a merge function that can merge `History dataset` into the database of Lite FullNode. For more API details: [HTTP&GRPC APIs](#HTTP&GRPC-APIs) - -For more design details, please refer to: [TIP128](https://github.com/tronprotocol/tips/issues/128) - -## Usage - -### Options - -This tool provides independent cutting of `Snapshot Dataset` and `History Dataset` and a merge function. - -- `--operation | -o`: [ split | merge ] specifies the operation as either to split or to merge -- `--type | -t`: [ snapshot | history ] is used only with `split` to specify the type of the dataset to be split; snapshot refers to Snapshot Dataset and history refers to History Dataset. -- `--fn-data-path`: FullNode database directory -- `--dataset-path`: dataset directory, when operation is `split`, `dataset-path` is the path that store the `Snapshot Dataset` or `History Dataset`, -otherwise `dataset-path` should be the `History Dataset` path. - -### Example - -Start a new FullNode using the default config, then an `output-directory` will be produced in the current directory. -`output-directory` contains a sub-directory named `database` which is the database to be split. - -#### Split and get a `Snapshot` - -First, stop the FullNode and execute: -``` -// just for simplify, locate the snapshot into `/tmp` directory, -java -jar LiteFullNodeTool.jar -o split -t snapshot --fn-data-path output-directory/database --dataset-path /tmp -``` -then a `snapshot` directory will be generated in `/tmp`, pack this directory and copy it to somewhere that is ready to run a Lite Fullnode. -Do not forget rename the directory from `snapshot` to `database`. -(the default value of the storage.db.directory is `database`, make sure rename the snapshot to the specified value) - -#### Split a `History` - -If historical data query is needed, `History dataset` should be generated and merged into Lite FullNode. -``` -// just for simplify, locate the history into `/tmp` directory, -java -jar LiteFullNodeTool.jar -o split -t history --fn-data-path output-directory/database --dataset-path /tmp -``` -A `history` directory will be generated in `/tmp`, pack this directory and copy it to a Lite Fullnode. -`History dataset` always take a large storage, make sure the disk has enough volume to store the `History dataset`. - -#### Merge - -Both `History Dataset` and `Snapshot Dataset` have an info.properties file to identify the block height from which they are segmented. -Make sure that the `split_block_num` in `History Dataset` is not less than the corresponding value in the `Snapshot Dataset`. - -After getting the `History dataset`, the Lite FullNode can merge the `History dataset` and become a real FullNode. -``` -// just for simplify, assume `History dataset` is locate in /tmp -java -jar LiteFullNodeTool.jar -o merge --fn-data-path output-directory/database --dataset-path /tmp/history -``` - -### HTTP&GRPC APIs - -Some APIs are not supported on lite fullnode, here is the list: - -#### Http - -| wallet/ | walletsolidity/ | -|---|---| -| getblockbyid | getblockbyid | -| getblockbylatestnum | getblockbylatestnum | -| getblockbylimitnext | getblockbylimitnext | -| getblockbynum | getblockbynum | -| getmerkletreevoucherinfo | getmerkletreevoucherinfo | -| gettransactionbyid | gettransactionbyid | -| gettransactioncountbyblocknum | gettransactioncountbyblocknum | -| gettransactioninfobyid | gettransactioninfobyid | -| gettransactionreceiptbyid | | -| isspend | isspend | -| scanandmarknotebyivk | scanandmarknotebyivk | -| scannotebyivk | scannotebyivk | -| scannotebyovk | scannotebyovk | -| totaltransaction | | - -#### GRPC - -| protocol.Wallet | protocol.WalletSolidity | protocol.Database | -|---|---|---| -| GetBlockById | | | -| GetBlockByLatestNum | | | -| GetBlockByLatestNum2 | | | -| GetBlockByLimitNext | | | -| GetBlockByLimitNext2 | | | -| GetBlockByNum | GetBlockByNum | GetBlockByNum | -| GetBlockByNum2 | GetBlockByNum2 | | -| GetMerkleTreeVoucherInfo | GetMerkleTreeVoucherInfo | | -| GetTransactionById | GetTransactionById | | -| GetTransactionCountByBlockNum | GetTransactionCountByBlockNum | | -| GetTransactionInfoById | GetTransactionInfoById | | -| IsSpend | IsSpend | | -| ScanAndMarkNoteByIvk | ScanAndMarkNoteByIvk | | -| ScanNoteByIvk | ScanNoteByIvk | | -| ScanNoteByOvk | ScanNoteByOvk | | -| TotalTransaction | | | - -These APIs can open forcibly by set `openHistoryQueryWhenLiteFN` = true, but not recommended. \ No newline at end of file diff --git a/framework/src/main/java/org/tron/tool/litefullnode/Util.java b/framework/src/main/java/org/tron/tool/litefullnode/Util.java deleted file mode 100644 index 0e4898b8031..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/Util.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.tron.tool.litefullnode; - -import java.io.IOException; -import java.nio.file.FileSystemException; -import java.nio.file.FileVisitOption; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.tron.common.utils.FileUtil; - -@Slf4j(topic = "tool") -public class Util { - - /** - * Copy src to dest, if dest is a directory and already exists, throw Exception. - * - *

Note: This method is not rigorous, because all the dirs that its FileName - * is contained in List(subDirs) will be filtered, this may result in unpredictable result. - * just used in LiteFullNodeTool. - * - * @param src Path or File - * @param dest Path or File - * @param subDirs only the subDirs in {@code src} will be copied - * @throws IOException IOException - */ - public static void copyDatabases(Path src, Path dest, List subDirs) - throws IOException { - // create subdirs, as using parallel() to run, so should create dirs first. - subDirs.forEach(dir -> { - if (FileUtil.isExists(Paths.get(src.toString(), dir).toString())) { - try { - Files.walk(Paths.get(src.toString(), dir), FileVisitOption.FOLLOW_LINKS) - .forEach(source -> copy(source, dest.resolve(src.relativize(source)))); - } catch (IOException e) { - logger.error("copy database failed, src: {}, dest: {}, error: {}", - Paths.get(src.toString(), dir), Paths.get(dest.toString(), dir), e.getMessage()); - throw new RuntimeException(e); - } - } - }); - } - - public static void copy(Path source, Path dest) { - try { - // create hard link when file is .sst - if (source.toString().endsWith(".sst")) { - try { - Files.createLink(dest, source); - } catch (FileSystemException e) { - Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING); - } - } else { - Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING); - } - } catch (Exception e) { - throw new RuntimeException(e.getMessage(), e); - } - } -} diff --git a/framework/src/main/java/org/tron/tool/litefullnode/db/DBInterface.java b/framework/src/main/java/org/tron/tool/litefullnode/db/DBInterface.java deleted file mode 100644 index 8f30cbc5026..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/db/DBInterface.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.tron.tool.litefullnode.db; - -import java.io.Closeable; -import java.io.IOException; -import org.tron.tool.litefullnode.iterator.DBIterator; - -public interface DBInterface extends Closeable { - - byte[] get(byte[] key); - - void put(byte[] key, byte[] value); - - void delete(byte[] key); - - DBIterator iterator(); - - long size(); - - void close() throws IOException; - -} diff --git a/framework/src/main/java/org/tron/tool/litefullnode/db/LevelDBImpl.java b/framework/src/main/java/org/tron/tool/litefullnode/db/LevelDBImpl.java deleted file mode 100644 index 774c9a86146..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/db/LevelDBImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.tron.tool.litefullnode.db; - -import com.google.common.collect.Streams; -import java.io.IOException; -import org.iq80.leveldb.DB; -import org.tron.tool.litefullnode.iterator.DBIterator; -import org.tron.tool.litefullnode.iterator.LevelDBIterator; - -public class LevelDBImpl implements DBInterface { - - private DB leveldb; - - public LevelDBImpl(DB leveldb) { - this.leveldb = leveldb; - } - - @Override - public byte[] get(byte[] key) { - return leveldb.get(key); - } - - @Override - public void put(byte[] key, byte[] value) { - leveldb.put(key, value); - } - - @Override - public void delete(byte[] key) { - leveldb.delete(key); - } - - @Override - public DBIterator iterator() { - return new LevelDBIterator(leveldb.iterator()); - } - - @Override - public long size() { - return Streams.stream(leveldb.iterator()).count(); - } - - @Override - public void close() throws IOException { - leveldb.close(); - } -} diff --git a/framework/src/main/java/org/tron/tool/litefullnode/db/RocksDBImpl.java b/framework/src/main/java/org/tron/tool/litefullnode/db/RocksDBImpl.java deleted file mode 100644 index d6dfc55f1bb..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/db/RocksDBImpl.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.tron.tool.litefullnode.db; - -import com.google.common.collect.Streams; -import java.io.IOException; -import org.rocksdb.RocksDBException; -import org.rocksdb.RocksIterator; -import org.tron.tool.litefullnode.iterator.DBIterator; -import org.tron.tool.litefullnode.iterator.RockDBIterator; - -public class RocksDBImpl implements DBInterface { - - private org.rocksdb.RocksDB rocksDB; - - public RocksDBImpl(org.rocksdb.RocksDB rocksDB) { - this.rocksDB = rocksDB; - } - - @Override - public byte[] get(byte[] key) { - try { - return rocksDB.get(key); - } catch (RocksDBException e) { - e.printStackTrace(); - } - return null; - } - - @Override - public void put(byte[] key, byte[] value) { - try { - rocksDB.put(key, value); - } catch (RocksDBException e) { - e.printStackTrace(); - } - } - - @Override - public void delete(byte[] key) { - try { - rocksDB.delete(key); - } catch (RocksDBException e) { - e.printStackTrace(); - } - } - - @Override - public DBIterator iterator() { - return new RockDBIterator(rocksDB.newIterator()); - } - - @Override - public long size() { - RocksIterator iterator = rocksDB.newIterator(); - long size = 0; - for (iterator.seekToFirst(); iterator.isValid(); iterator.next()) { - size++; - } - iterator.close(); - return size; - } - - @Override - public void close() throws IOException { - rocksDB.close(); - } -} diff --git a/framework/src/main/java/org/tron/tool/litefullnode/iterator/DBIterator.java b/framework/src/main/java/org/tron/tool/litefullnode/iterator/DBIterator.java deleted file mode 100644 index 363252e660e..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/iterator/DBIterator.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.tron.tool.litefullnode.iterator; - -import java.io.Closeable; - -public interface DBIterator extends Closeable { - - void seek(byte[] key); - - void seekToFirst(); - - boolean hasNext(); - - byte[] getKey(); - - byte[] getValue(); - - void next(); -} diff --git a/framework/src/main/java/org/tron/tool/litefullnode/iterator/LevelDBIterator.java b/framework/src/main/java/org/tron/tool/litefullnode/iterator/LevelDBIterator.java deleted file mode 100644 index d75b21ce67e..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/iterator/LevelDBIterator.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.tron.tool.litefullnode.iterator; - -import java.io.IOException; - -public class LevelDBIterator implements DBIterator { - - private org.iq80.leveldb.DBIterator iterator; - - public LevelDBIterator(org.iq80.leveldb.DBIterator iterator) { - this.iterator = iterator; - } - - @Override - public void seek(byte[] key) { - iterator.seek(key); - } - - @Override - public void seekToFirst() { - iterator.seekToFirst(); - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public byte[] getKey() { - return iterator.peekNext().getKey(); - } - - @Override - public byte[] getValue() { - return iterator.peekNext().getValue(); - } - - @Override - public void next() { - iterator.next(); - } - - @Override - public void close() throws IOException { - iterator.close(); - } -} diff --git a/framework/src/main/java/org/tron/tool/litefullnode/iterator/RockDBIterator.java b/framework/src/main/java/org/tron/tool/litefullnode/iterator/RockDBIterator.java deleted file mode 100644 index eb13330ebce..00000000000 --- a/framework/src/main/java/org/tron/tool/litefullnode/iterator/RockDBIterator.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.tron.tool.litefullnode.iterator; - -import java.io.IOException; -import org.rocksdb.RocksIterator; - -public class RockDBIterator implements DBIterator { - - private RocksIterator iterator; - - public RockDBIterator(RocksIterator iterator) { - this.iterator = iterator; - } - - @Override - public void seek(byte[] key) { - iterator.seek(key); - } - - @Override - public void seekToFirst() { - iterator.seekToFirst(); - } - - @Override - public boolean hasNext() { - return iterator.isValid(); - } - - @Override - public byte[] getKey() { - return iterator.key(); - } - - @Override - public byte[] getValue() { - return iterator.value(); - } - - @Override - public void next() { - iterator.next(); - } - - @Override - public void close() throws IOException { - iterator.close(); - } -} diff --git a/framework/src/main/resources/config-backup.conf b/framework/src/main/resources/config-backup.conf index 2a1579fdc18..bb3082e42c2 100644 --- a/framework/src/main/resources/config-backup.conf +++ b/framework/src/main/resources/config-backup.conf @@ -51,7 +51,6 @@ storage { node.discovery = { enable = true persist = true - bind.ip = "" external.ip = null } diff --git a/framework/src/main/resources/config-beta.conf b/framework/src/main/resources/config-beta.conf index 1d4a914ce20..050df1e45ad 100644 --- a/framework/src/main/resources/config-beta.conf +++ b/framework/src/main/resources/config-beta.conf @@ -51,7 +51,6 @@ storage { node.discovery = { enable = true persist = true - bind.ip = "" external.ip = null } diff --git a/framework/src/main/resources/config-localtest.conf b/framework/src/main/resources/config-localtest.conf index 5ce30aeb1a4..f1ac104c9ed 100644 --- a/framework/src/main/resources/config-localtest.conf +++ b/framework/src/main/resources/config-localtest.conf @@ -57,7 +57,6 @@ storage { node.discovery = { enable = true persist = true - bind.ip = "" external.ip = null } diff --git a/framework/src/main/resources/config-test-net.conf b/framework/src/main/resources/config-test-net.conf index 58c40587fcc..ff292a3951c 100644 --- a/framework/src/main/resources/config-test-net.conf +++ b/framework/src/main/resources/config-test-net.conf @@ -53,7 +53,6 @@ storage { node.discovery = { enable = true persist = true - bind.ip = "" external.ip = null } diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 019b6302911..78427c30f87 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -97,6 +97,13 @@ storage { # txCache.estimatedTransactions = 1000 # if true, transaction cache initialization will be faster. default false # txCache.initOptimization = true + + # data root setting, for check data, currently, only reward-vi is used. + + # merkleRoot = { + # reward-vi = 9debcb9924055500aaae98cdee10501c5c39d4daa75800a996f4bdda73dbccd8 // main-net, Sha256Hash, hexString + # } + } node.discovery = { @@ -213,7 +220,7 @@ node { dns { # dns urls to get nodes, url format tree://{pubkey}@{domain}, default empty treeUrls = [ - #"tree://AKMQMNAJJBL73LXWPXDI4I5ZWWIZ4AWO34DWQ636QOBBXNFXH3LQS@main.trondisco.net", //offical dns tree + #"tree://AKMQMNAJJBL73LXWPXDI4I5ZWWIZ4AWO34DWQ636QOBBXNFXH3LQS@main.trondisco.net", ] } @@ -243,6 +250,9 @@ node { # Transactions can only be broadcast if the number of effective connections is reached. minEffectiveConnection = 1 + + # The switch of the reflection service, effective for all gRPC services + # reflectionService = true } # number of solidity thread in the FullNode. @@ -285,8 +295,6 @@ node { # "getnowblock2" # ] - # The switch of the reflection service, effective for all gRPC services - # reflectionService = true } ## rate limiter config diff --git a/framework/src/test/java/org/tron/core/actuator/ActuatorFactoryTest.java b/framework/src/test/java/org/tron/core/actuator/ActuatorFactoryTest.java new file mode 100644 index 00000000000..8258fbf9a3e --- /dev/null +++ b/framework/src/test/java/org/tron/core/actuator/ActuatorFactoryTest.java @@ -0,0 +1,71 @@ +package org.tron.core.actuator; + +import com.google.protobuf.ByteString; +import java.util.List; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.core.Constant; +import org.tron.core.Wallet; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.config.args.Args; +import org.tron.protos.Protocol.AccountType; +import org.tron.protos.contract.BalanceContract.TransferContract; + +public class ActuatorFactoryTest extends BaseTest { + + private static final String OWNER_ADDRESS = Wallet.getAddressPreFixString() + + "548794500882809695a8a687866e76d4271a1abc"; + private static final String TO_ADDRESS = Wallet.getAddressPreFixString() + + "abd4b9367799eaa3197fecb144eb71de1e049abc"; + + static { + Args.setParam( + new String[] { + "--output-directory", dbPath() + }, + Constant.TEST_CONF + ); + } + + private TransferContract getContract(long count, String owneraddress, String toaddress) { + return TransferContract.newBuilder() + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(owneraddress))) + .setToAddress(ByteString.copyFrom(ByteArray.fromHexString(toaddress))) + .setAmount(count) + .build(); + } + + @Before + public void createCapsule() { + AccountCapsule ownerCapsule = + new AccountCapsule( + ByteString.copyFromUtf8("owner"), + ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS)), + AccountType.Normal, + 10000L); + AccountCapsule toAccountCapsule = + new AccountCapsule( + ByteString.copyFromUtf8("toAccount"), + ByteString.copyFrom(ByteArray.fromHexString(TO_ADDRESS)), + AccountType.Normal, + 10L); + dbManager.getAccountStore().put(ownerCapsule.getAddress().toByteArray(), ownerCapsule); + dbManager.getAccountStore().put(toAccountCapsule.getAddress().toByteArray(), toAccountCapsule); + } + + + @Test + public void testCreateActuator() { + TransferContract contract = getContract(10L, OWNER_ADDRESS, TO_ADDRESS); + TransactionCapsule trx = new TransactionCapsule(contract, + chainBaseManager.getAccountStore()); + List actList = ActuatorFactory.createActuator(trx, chainBaseManager); + + Assert.assertEquals(1, actList.size()); + } + +} 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 6f4df3cba8f..8e2b7647967 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 @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.BeforeClass; @@ -12,6 +13,7 @@ import org.tron.common.utils.ByteArray; import org.tron.common.utils.ForkController; import org.tron.core.Constant; +import org.tron.core.capsule.BytesCapsule; import org.tron.core.config.Parameter; import org.tron.core.config.Parameter.ForkBlockVersionEnum; import org.tron.core.config.args.Args; @@ -27,6 +29,11 @@ public class ProposalUtilTest extends BaseTest { private static final String LONG_VALUE_ERROR = "Bad chain parameter value, valid range is [0," + LONG_VALUE + "]"; + @Resource + private DynamicPropertiesStore dynamicPropertiesStore; + + ForkController forkUtils = ForkController.instance(); + /** * Init . */ @@ -60,8 +67,6 @@ public void validProposalTypeCheck() throws ContractValidateException { @Test public void validateCheck() { - DynamicPropertiesStore dynamicPropertiesStore = null; - ForkController forkUtils = ForkController.instance(); long invalidValue = -1; try { @@ -328,6 +333,54 @@ public void validateCheck() { e.getMessage()); } + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_OLD_REWARD_OPT.getCode(), 2); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "Bad chain parameter id [ALLOW_OLD_REWARD_OPT]", + e.getMessage()); + } + hardForkTime = + ((ForkBlockVersionEnum.VERSION_4_7_4.getHardForkTime() - 1) / maintenanceTimeInterval + 1) + * maintenanceTimeInterval; + forkUtils.getManager().getDynamicPropertiesStore() + .saveLatestBlockHeaderTimestamp(hardForkTime + 1); + forkUtils.getManager().getDynamicPropertiesStore() + .statsByVersion(ForkBlockVersionEnum.VERSION_4_7_4.getValue(), stats); + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_OLD_REWARD_OPT.getCode(), 2); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "This value[ALLOW_OLD_REWARD_OPT] is only allowed to be 1", + e.getMessage()); + } + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_OLD_REWARD_OPT.getCode(), 1); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "[ALLOW_NEW_REWARD] or [ALLOW_TVM_VOTE] proposal must be approved " + + "before [ALLOW_OLD_REWARD_OPT] can be proposed", + e.getMessage()); + } + dynamicPropertiesStore.put("NEW_REWARD_ALGORITHM_EFFECTIVE_CYCLE".getBytes(), + new BytesCapsule(ByteArray.fromLong(4000))); + dynamicPropertiesStore.saveAllowOldRewardOpt(1); + try { + ProposalUtil.validator(dynamicPropertiesStore, forkUtils, + ProposalType.ALLOW_OLD_REWARD_OPT.getCode(), 1); + Assert.fail(); + } catch (ContractValidateException e) { + Assert.assertEquals( + "[ALLOW_OLD_REWARD_OPT] has been valid, no need to propose again", + e.getMessage()); + } + forkUtils.getManager().getDynamicPropertiesStore() .statsByVersion(ForkBlockVersionEnum.ENERGY_LIMIT.getValue(), stats); forkUtils.reset(); diff --git a/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java b/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java index 9d56876a4da..3346a1aead5 100644 --- a/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java +++ b/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java @@ -14,6 +14,7 @@ import com.google.protobuf.ByteString; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; @@ -431,4 +432,22 @@ public void estimateConsumeBandWidthSizeCorner() { long actual = TransactionUtil.estimateConsumeBandWidthSize(dps, balance); Assert.assertEquals(expected, actual); } + + @Test + public void testConcurrentToString() throws InterruptedException { + Transaction.Builder builder = Transaction.newBuilder(); + TransactionCapsule trx = new TransactionCapsule(builder.build()); + List threadList = new ArrayList<>(); + int n = 10; + for (int i = 0; i < n; i++) { + threadList.add(new Thread(() -> trx.toString())); + } + for (int i = 0; i < n; i++) { + threadList.get(i).start(); + } + for (int i = 0; i < n; i++) { + threadList.get(i).join(); + } + Assert.assertTrue(true); + } } diff --git a/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java b/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java index 4e3273203c8..3662fb524b8 100644 --- a/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java +++ b/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java @@ -1,18 +1,28 @@ package org.tron.core.capsule.utils; +import com.google.protobuf.ByteString; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; +import org.tron.common.utils.MerkleRoot; import org.tron.common.utils.Sha256Hash; import org.tron.core.capsule.utils.MerkleTree.Leaf; @Slf4j public class MerkleTreeTest { + @Rule + public ExpectedException exception = ExpectedException.none(); + private static List getHash(int hashNum) { List hashList = new ArrayList(); for (int i = 0; i < hashNum; i++) { @@ -173,4 +183,27 @@ public void testAnyHashNum() { pareTree(root, hashList, maxRank, 0, 0); } } -} \ No newline at end of file + + @Test + public void testConcurrent() { + Sha256Hash root1 = Sha256Hash.wrap( + ByteString.fromHex("6cb38b4f493db8bacf26123cd4253bbfc530c708b97b3747e782f64097c3c482")); + Sha256Hash root2 = Sha256Hash.wrap( + ByteString.fromHex("4bfc60ea3de4f5d1476f839874df0aba38eec4e524d6fa63f5b19c4bf527eaf3")); + List list1 = IntStream.range(0, 10000).mapToObj(i -> + Sha256Hash.of(true, ("byte1-" + i).getBytes(StandardCharsets.UTF_8))) + .collect(Collectors.toList()); + List list2 = IntStream.range(0, 10000).mapToObj(i -> + Sha256Hash.of(true, ("byte2-" + i).getBytes(StandardCharsets.UTF_8))) + .collect(Collectors.toList()); + Assert.assertEquals(root1, MerkleTree.getInstance().createTree(list1).getRoot().getHash()); + Assert.assertEquals(root2, MerkleTree.getInstance().createTree(list2).getRoot().getHash()); + Assert.assertEquals(root1, MerkleRoot.root(list1)); + Assert.assertEquals(root2, MerkleRoot.root(list2)); + exception.expect(ArrayIndexOutOfBoundsException.class); + IntStream.range(0, 1000).parallel().forEach(i -> Assert.assertEquals( + MerkleTree.getInstance().createTree(i % 2 == 0 ? list1 : list2).getRoot().getHash(), + MerkleRoot.root(i % 2 == 0 ? list1 : list2)) + ); + } +} diff --git a/framework/src/test/java/org/tron/core/capsule/utils/RLPListTest.java b/framework/src/test/java/org/tron/core/capsule/utils/RLPListTest.java index 7d903591966..cba4a7d8040 100644 --- a/framework/src/test/java/org/tron/core/capsule/utils/RLPListTest.java +++ b/framework/src/test/java/org/tron/core/capsule/utils/RLPListTest.java @@ -1,7 +1,13 @@ package org.tron.core.capsule.utils; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.util.Random; +import org.bouncycastle.util.BigIntegers; import org.junit.Assert; import org.junit.Test; +import org.tron.common.utils.Value; public class RLPListTest { @@ -23,4 +29,53 @@ public void testGetRLPData() { Assert.assertEquals(new String(rlpList.getRLPData()), "rlpData"); } + @Test + public void testToBytes() + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Method method = RLP.class.getDeclaredMethod("toBytes", Object.class); + method.setAccessible(true); + + byte[] aBytes = new byte[10]; + byte[] bBytes = (byte[]) method.invoke(RLP.class, aBytes); + Assert.assertArrayEquals(aBytes, bBytes); + + int i = new Random().nextInt(); + byte[] cBytes = BigIntegers.asUnsignedByteArray(BigInteger.valueOf(i)); + byte[] dBytes = (byte[]) method.invoke(RLP.class, i); + Assert.assertArrayEquals(cBytes, dBytes); + + long j = new Random().nextInt(); + byte[] eBytes = BigIntegers.asUnsignedByteArray(BigInteger.valueOf(j)); + byte[] fBytes = (byte[]) method.invoke(RLP.class, j); + Assert.assertArrayEquals(eBytes, fBytes); + + String test = "testA"; + byte[] gBytes = test.getBytes(); + byte[] hBytes = (byte[]) method.invoke(RLP.class, test); + Assert.assertArrayEquals(gBytes, hBytes); + + BigInteger bigInteger = BigInteger.valueOf(100); + byte[] iBytes = BigIntegers.asUnsignedByteArray(bigInteger); + byte[] jBytes = (byte[]) method.invoke(RLP.class, bigInteger); + Assert.assertArrayEquals(iBytes, jBytes); + + Value v = new Value(new byte[0]); + byte[] kBytes = v.asBytes(); + byte[] lBytes = (byte[]) method.invoke(RLP.class, v); + Assert.assertArrayEquals(kBytes, lBytes); + + char c = 'a'; + try { + method.invoke(RLP.class, c); + Assert.fail(); + } catch (Exception e) { + Assert.assertTrue(true); + } + } + + @Test + public void testEncode() { + byte[] aBytes = RLP.encode(new byte[1]); + Assert.assertEquals(1, aBytes.length); + } } 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 59b684b338f..52307d9d294 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 @@ -25,7 +25,9 @@ import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.tron.common.args.GenesisBlock; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; @@ -41,6 +43,9 @@ public class ArgsTest { private String address; private LocalWitnesses localWitnesses; + @Rule + public ExpectedException thrown = ExpectedException.none(); + @After public void destroy() { Args.clearParam(); @@ -94,8 +99,8 @@ public void get() { Assert.assertEquals(0, parameter.getActiveNodes().size()); Assert.assertEquals(30, parameter.getMaxConnections()); Assert.assertEquals(43, parameter.getNodeP2pVersion()); - Assert.assertEquals(1000, parameter.getMaxUnsolidifiedBlocks()); - Assert.assertEquals(true, parameter.isUnsolidifiedBlockCheck()); + Assert.assertEquals(54, parameter.getMaxUnsolidifiedBlocks()); + Assert.assertEquals(false, parameter.isUnsolidifiedBlockCheck()); //Assert.assertEquals(30, args.getSyncNodeCount()); // gRPC network configs checking @@ -138,5 +143,11 @@ public void testIpFromLibP2p() Assert.assertNotEquals(configuredExternalIp, parameter.getNodeExternalIp()); } + + @Test + public void testOldRewardOpt() { + thrown.expect(IllegalArgumentException.class); + Args.setParam(new String[] {"-w"}, "args-test.conf"); + } } diff --git a/framework/src/test/java/org/tron/core/db/AccountTraceStoreTest.java b/framework/src/test/java/org/tron/core/db/AccountTraceStoreTest.java index aa87f903ad3..5a6d44a8c7c 100644 --- a/framework/src/test/java/org/tron/core/db/AccountTraceStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/AccountTraceStoreTest.java @@ -12,6 +12,7 @@ import org.tron.common.utils.ByteArray; import org.tron.core.Constant; import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.AccountTraceCapsule; import org.tron.core.config.args.Args; import org.tron.core.exception.BadItemException; import org.tron.core.exception.ItemNotFoundException; @@ -54,4 +55,16 @@ public void testGetPrevBalance() { Assert.assertEquals((long)pair2.getKey(),3L); Assert.assertEquals((long)pair2.getValue(), 99L); } + + @Test + public void testPut() { + long number = 2 ^ Long.MAX_VALUE; + long balance = 9999; + byte[] key = Bytes.concat(address, Longs.toByteArray(number)); + accountTraceStore.put(key, new AccountTraceCapsule(balance)); + Pair pair = accountTraceStore.getPrevBalance(address,2); + Assert.assertEquals((long)pair.getKey(),2L); + Assert.assertEquals((long)pair.getValue(), 0L); + } + } \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/db/BlockIndexStoreTest.java b/framework/src/test/java/org/tron/core/db/BlockIndexStoreTest.java new file mode 100644 index 00000000000..a5600b34b26 --- /dev/null +++ b/framework/src/test/java/org/tron/core/db/BlockIndexStoreTest.java @@ -0,0 +1,64 @@ +package org.tron.core.db; + +import com.google.protobuf.ByteString; +import javax.annotation.Resource; +import org.junit.Assert; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.Constant; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.BytesCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.exception.ItemNotFoundException; + +public class BlockIndexStoreTest extends BaseTest { + + @Resource + private BlockIndexStore blockIndexStore; + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath() + }, + Constant.TEST_CONF + ); + } + + private BlockCapsule getBlockCapsule(long number) { + return new BlockCapsule(number, Sha256Hash.ZERO_HASH, + System.currentTimeMillis(), ByteString.EMPTY); + } + + @Test + public void testPut() { + BlockCapsule blockCapsule = getBlockCapsule(1); + blockIndexStore.put(blockCapsule.getBlockId()); + byte[] key = ByteArray.fromLong(blockCapsule.getBlockId().getNum()); + Assert.assertTrue(blockIndexStore.has(key)); + } + + @Test + public void testGet() throws ItemNotFoundException { + BlockCapsule blockCapsule = getBlockCapsule(1); + blockIndexStore.put(blockCapsule.getBlockId()); + byte[] key = ByteArray.fromLong(blockCapsule.getBlockId().getNum()); + BytesCapsule bytesCapsule = blockIndexStore.get(key); + Assert.assertNotNull(bytesCapsule); + } + + @Test + public void testDelete() throws ItemNotFoundException { + BlockCapsule blockCapsule = getBlockCapsule(1); + blockIndexStore.put(blockCapsule.getBlockId()); + byte[] key = ByteArray.fromLong(blockCapsule.getBlockId().getNum()); + BytesCapsule bytesCapsule = blockIndexStore.get(key); + Assert.assertNotNull(bytesCapsule); + + blockIndexStore.delete(key); + BytesCapsule capsule = blockIndexStore.getUnchecked(key); + Assert.assertNull(capsule.getData()); + } +} diff --git a/framework/src/test/java/org/tron/core/db/BlockStoreTest.java b/framework/src/test/java/org/tron/core/db/BlockStoreTest.java index 4f706a97dcf..937a102193f 100644 --- a/framework/src/test/java/org/tron/core/db/BlockStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/BlockStoreTest.java @@ -1,21 +1,90 @@ package org.tron.core.db; +import com.google.protobuf.ByteString; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; import org.junit.Test; import org.tron.common.BaseTest; +import org.tron.common.utils.Sha256Hash; import org.tron.core.Constant; +import org.tron.core.capsule.BlockCapsule; import org.tron.core.config.args.Args; +import org.tron.core.exception.BadItemException; +import org.tron.core.exception.ItemNotFoundException; + @Slf4j public class BlockStoreTest extends BaseTest { + @Resource + private BlockStore blockStore; static { Args.setParam(new String[]{"--output-directory", dbPath()}, Constant.TEST_CONF); } + private BlockCapsule getBlockCapsule(long number) { + return new BlockCapsule(number, Sha256Hash.ZERO_HASH, + System.currentTimeMillis(), ByteString.EMPTY); + } + @Test public void testCreateBlockStore() { } + + @Test + public void testPut() { + long number = 1; + BlockCapsule blockCapsule = getBlockCapsule(number); + + byte[] blockId = blockCapsule.getBlockId().getBytes(); + blockStore.put(blockId, blockCapsule); + try { + BlockCapsule blockCapsule1 = blockStore.get(blockId); + Assert.assertNotNull(blockCapsule1); + Assert.assertEquals(number, blockCapsule1.getNum()); + } catch (ItemNotFoundException | BadItemException e) { + e.printStackTrace(); + } + } + + @Test + public void testGet() { + long number = 2; + BlockCapsule blockCapsule = getBlockCapsule(number); + byte[] blockId = blockCapsule.getBlockId().getBytes(); + blockStore.put(blockId, blockCapsule); + try { + boolean has = blockStore.has(blockId); + Assert.assertTrue(has); + BlockCapsule blockCapsule1 = blockStore.get(blockId); + + Assert.assertEquals(number, blockCapsule1.getNum()); + } catch (ItemNotFoundException | BadItemException e) { + e.printStackTrace(); + } + } + + @Test + public void testDelete() { + long number = 1; + BlockCapsule blockCapsule = getBlockCapsule(number); + + byte[] blockId = blockCapsule.getBlockId().getBytes(); + blockStore.put(blockId, blockCapsule); + try { + BlockCapsule blockCapsule1 = blockStore.get(blockId); + Assert.assertNotNull(blockCapsule1); + Assert.assertEquals(number, blockCapsule1.getNum()); + + blockStore.delete(blockId); + BlockCapsule blockCapsule2 = blockStore.getUnchecked(blockId); + Assert.assertNull(blockCapsule2); + } catch (ItemNotFoundException | BadItemException e) { + e.printStackTrace(); + } + } + } diff --git a/framework/src/test/java/org/tron/core/db/DelegatedResourceStoreTest.java b/framework/src/test/java/org/tron/core/db/DelegatedResourceStoreTest.java index 8878ff0dcf3..905ef0384f1 100644 --- a/framework/src/test/java/org/tron/core/db/DelegatedResourceStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/DelegatedResourceStoreTest.java @@ -57,6 +57,21 @@ public void testPut() { Assert.assertEquals(BALANCE, delegatedResourceCapsule1.getFrozenBalanceForEnergy()); } + @Test + public void testDelete() { + DelegatedResourceCapsule delegatedResourceCapsule = create("444444444"); + byte[] key = delegatedResourceCapsule.createDbKey(); + delegatedResourceStore.put(key, delegatedResourceCapsule); + DelegatedResourceCapsule delegatedResourceCapsule1 = delegatedResourceStore.get(key); + Assert.assertNotNull(delegatedResourceCapsule1); + Assert.assertEquals(BALANCE, delegatedResourceCapsule1.getFrozenBalanceForEnergy()); + + delegatedResourceStore.delete(key); + DelegatedResourceCapsule delegatedResourceCapsule2 = delegatedResourceStore.get(key); + Assert.assertNull(delegatedResourceCapsule2); + + } + public DelegatedResourceCapsule create(String address) { byte[] ownerAddress = ByteArray.fromHexString(address); byte[] receiverAddress = ByteArray.fromHexString(RECEIVER_ADDRESS); diff --git a/framework/src/test/java/org/tron/core/db/DelegationStoreTest.java b/framework/src/test/java/org/tron/core/db/DelegationStoreTest.java index d63015ae064..10e70a0a83b 100644 --- a/framework/src/test/java/org/tron/core/db/DelegationStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/DelegationStoreTest.java @@ -68,4 +68,21 @@ public void testPut() { Assert.assertEquals(value, actualValue); } + @Test + public void testDelete() { + long value = 20_000_000; + byte[] key = buildRewardKey(CYCLE, ByteArray.fromHexString("33333333")); + delegationStore.put(key, new BytesCapsule(ByteArray + .fromLong(20_000_000))); + + BytesCapsule bytesCapsule = delegationStore.get(key); + Assert.assertNotNull(bytesCapsule); + long actualValue = ByteArray.toLong(bytesCapsule.getData()); + Assert.assertEquals(value, actualValue); + + delegationStore.delete(key); + BytesCapsule bytesCapsule1 = delegationStore.getUnchecked(key); + Assert.assertNull(bytesCapsule1.getData()); + } + } diff --git a/framework/src/test/java/org/tron/core/db/ExchangeStoreTest.java b/framework/src/test/java/org/tron/core/db/ExchangeStoreTest.java index f48ac11301d..685c137422c 100644 --- a/framework/src/test/java/org/tron/core/db/ExchangeStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/ExchangeStoreTest.java @@ -10,6 +10,7 @@ import org.tron.core.Constant; import org.tron.core.capsule.ExchangeCapsule; import org.tron.core.config.args.Args; +import org.tron.core.exception.ItemNotFoundException; import org.tron.core.store.ExchangeStore; import org.tron.protos.Protocol; @@ -50,6 +51,30 @@ public void testGet() throws Exception { Assert.assertEquals(result.getID(), 1); } + @Test + public void testPut() throws ItemNotFoundException { + Protocol.Exchange.Builder builder = Protocol.Exchange.newBuilder(); + builder.setExchangeId(1L).setCreatorAddress(ByteString.copyFromUtf8("Address1")); + ExchangeCapsule exchangeCapsule = new ExchangeCapsule(builder.build()); + exchangeKey1 = exchangeCapsule.createDbKey(); + chainBaseManager.getExchangeStore().put(exchangeKey1, exchangeCapsule); + + final ExchangeCapsule result = exchangeStore.get(exchangeKey1); + Assert.assertNotNull(result); + Assert.assertEquals(result.getID(), 1); + } + + @Test + public void testDelete() throws Exception { + final ExchangeCapsule result = exchangeStore.get(exchangeKey1); + Assert.assertNotNull(result); + Assert.assertEquals(result.getID(), 1); + + exchangeStore.delete(exchangeKey1); + ExchangeCapsule exchangeCapsule = exchangeStore.getUnchecked(exchangeKey1); + Assert.assertNull(exchangeCapsule); + } + @Test public void testGetAllExchanges() { List exchangeCapsuleList = exchangeStore.getAllExchanges(); diff --git a/framework/src/test/java/org/tron/core/db/ExchangeV2StoreTest.java b/framework/src/test/java/org/tron/core/db/ExchangeV2StoreTest.java index 2b1e2ea31ea..97c5f599b6e 100644 --- a/framework/src/test/java/org/tron/core/db/ExchangeV2StoreTest.java +++ b/framework/src/test/java/org/tron/core/db/ExchangeV2StoreTest.java @@ -8,6 +8,7 @@ import org.tron.core.Constant; import org.tron.core.capsule.ExchangeCapsule; import org.tron.core.config.args.Args; +import org.tron.core.exception.ItemNotFoundException; import org.tron.core.store.ExchangeV2Store; import org.tron.protos.Protocol; @@ -33,6 +34,31 @@ public void testGet() throws Exception { Assert.assertEquals(result.getID(), 1); } + @Test + public void testPut() throws ItemNotFoundException { + Protocol.Exchange.Builder builder = Protocol.Exchange.newBuilder().setExchangeId(1L) + .setCreatorAddress(ByteString.copyFromUtf8("Address2")); + ExchangeCapsule exchangeCapsule = new ExchangeCapsule(builder.build()); + byte[] exchangeKey1 = exchangeCapsule.createDbKey(); + exchangeV2Store.put(exchangeKey1, exchangeCapsule); + + final ExchangeCapsule result = exchangeV2Store.get(exchangeKey1); + Assert.assertNotNull(result); + Assert.assertEquals(result.getID(), 1); + } + + @Test + public void testDelete() throws ItemNotFoundException { + Protocol.Exchange.Builder builder = Protocol.Exchange.newBuilder().setExchangeId(1L) + .setCreatorAddress(ByteString.copyFromUtf8("Address3")); + ExchangeCapsule exchangeCapsule = new ExchangeCapsule(builder.build()); + byte[] exchangeKey1 = exchangeCapsule.createDbKey(); + exchangeV2Store.put(exchangeKey1, exchangeCapsule); + exchangeV2Store.delete(exchangeKey1); + ExchangeCapsule result = exchangeV2Store.getUnchecked(exchangeKey1); + Assert.assertNull(result); + } + private byte[] putToExchangeV2() { Protocol.Exchange.Builder builder = Protocol.Exchange.newBuilder().setExchangeId(1L) .setCreatorAddress(ByteString.copyFromUtf8("Address1")); 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 bd79f7d9776..053647cc25a 100755 --- a/framework/src/test/java/org/tron/core/db/ManagerTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerTest.java @@ -580,14 +580,24 @@ public void pushSwitchFork() TaposException, ReceiptCheckErrException, TooBigTransactionException, AccountResourceInsufficientException, EventBloomException { - String key = "f31db24bfbd1a2ef19beddca0a0fa37632eded9ac666a05d3bd925f01dde1f62"; - String key2 = "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4"; + String key = PublicMethod.getRandomPrivateKey(); + String key2 = PublicMethod.getRandomPrivateKey(); byte[] privateKey = ByteArray.fromHexString(key); final ECKey ecKey = ECKey.fromPrivate(privateKey); byte[] address = ecKey.getAddress(); + + ByteString addressByte = ByteString.copyFrom(address); + AccountCapsule accountCapsule = + new AccountCapsule(Protocol.Account.newBuilder() + .setAddress(addressByte).build()); + chainManager.getAccountStore() + .put(addressByte.toByteArray(), accountCapsule); + WitnessCapsule sr1 = new WitnessCapsule( ByteString.copyFrom(address), "www.tron.net/first"); sr1.setVoteCount(1000000000L); + + byte[] privateKey2 = ByteArray.fromHexString(key2); final ECKey ecKey2 = ECKey.fromPrivate(privateKey2); byte[] address2 = ecKey2.getAddress(); @@ -703,13 +713,23 @@ public void fork() Args.setParam(new String[]{"--witness"}, Constant.TEST_CONF); long size = chainManager.getBlockStore().size(); // System.out.print("block store size:" + size + "\n"); - String key = "f31db24bfbd1a2ef19beddca0a0fa37632eded9ac666a05d3bd925f01dde1f62"; + String key = PublicMethod.getRandomPrivateKey(); byte[] privateKey = ByteArray.fromHexString(key); final ECKey ecKey = ECKey.fromPrivate(privateKey); byte[] address = ecKey.getAddress(); + + ByteString addressByte = ByteString.copyFrom(address); + AccountCapsule accountCapsule = + new AccountCapsule(Protocol.Account.newBuilder() + .setAddress(addressByte).build()); + chainManager.getAccountStore() + .put(addressByte.toByteArray(), accountCapsule); + + WitnessCapsule witnessCapsule = new WitnessCapsule(ByteString.copyFrom(address)); chainManager.getWitnessScheduleStore().saveActiveWitnesses(new ArrayList<>()); chainManager.addWitness(ByteString.copyFrom(address)); + chainManager.getWitnessStore().put(address, witnessCapsule); Block block = getSignedBlock(witnessCapsule.getAddress(), 1533529947843L, privateKey); @@ -822,14 +842,20 @@ public void doNotSwitch() Args.setParam(new String[]{"--witness"}, Constant.TEST_CONF); long size = chainManager.getBlockStore().size(); System.out.print("block store size:" + size + "\n"); - String key = "f31db24bfbd1a2ef19beddca0a0fa37632eded9ac666a05d3bd925f01dde1f62"; + String key = PublicMethod.getRandomPrivateKey(); byte[] privateKey = ByteArray.fromHexString(key); final ECKey ecKey = ECKey.fromPrivate(privateKey); byte[] address = ecKey.getAddress(); - + ByteString addressByte = ByteString.copyFrom(address); + AccountCapsule accountCapsule = + new AccountCapsule(Protocol.Account.newBuilder() + .setAddress(addressByte).build()); + chainManager.getAccountStore() + .put(addressByte.toByteArray(), accountCapsule); WitnessCapsule witnessCapsule = new WitnessCapsule(ByteString.copyFrom(address)); chainManager.getWitnessScheduleStore().saveActiveWitnesses(new ArrayList<>()); chainManager.addWitness(ByteString.copyFrom(address)); + chainManager.getWitnessStore().put(address, witnessCapsule); Block block = getSignedBlock(witnessCapsule.getAddress(), 1533529947843L, privateKey); dbManager.pushBlock(new BlockCapsule(block)); @@ -928,14 +954,22 @@ public void switchBack() Args.setParam(new String[]{"--witness"}, Constant.TEST_CONF); long size = chainManager.getBlockStore().size(); System.out.print("block store size:" + size + "\n"); - String key = "f31db24bfbd1a2ef19beddca0a0fa37632eded9ac666a05d3bd925f01dde1f62"; + String key = PublicMethod.getRandomPrivateKey();; byte[] privateKey = ByteArray.fromHexString(key); final ECKey ecKey = ECKey.fromPrivate(privateKey); byte[] address = ecKey.getAddress(); + + ByteString addressByte = ByteString.copyFrom(address); + AccountCapsule accountCapsule = + new AccountCapsule(Protocol.Account.newBuilder() + .setAddress(addressByte).build()); + chainManager.getAccountStore() + .put(addressByte.toByteArray(), accountCapsule); + WitnessCapsule witnessCapsule = new WitnessCapsule(ByteString.copyFrom(address)); chainManager.getWitnessScheduleStore().saveActiveWitnesses(new ArrayList<>()); chainManager.addWitness(ByteString.copyFrom(address)); - + chainManager.getWitnessStore().put(address, witnessCapsule); Block block = getSignedBlock(witnessCapsule.getAddress(), 1533529947843L, privateKey); dbManager.pushBlock(new BlockCapsule(block)); diff --git a/framework/src/test/java/org/tron/core/db/MarketAccountStoreTest.java b/framework/src/test/java/org/tron/core/db/MarketAccountStoreTest.java index 51bf1c5d7fc..ed94a64175d 100644 --- a/framework/src/test/java/org/tron/core/db/MarketAccountStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/MarketAccountStoreTest.java @@ -8,6 +8,7 @@ import org.tron.core.Constant; import org.tron.core.capsule.MarketAccountOrderCapsule; import org.tron.core.config.args.Args; +import org.tron.core.exception.ItemNotFoundException; import org.tron.core.store.MarketAccountStore; public class MarketAccountStoreTest extends BaseTest { @@ -27,6 +28,11 @@ public class MarketAccountStoreTest extends BaseTest { @Test public void testGet() throws Exception { String address = "Address1"; + + MarketAccountOrderCapsule marketAccountOrderCapsule = + marketAccountStore.getUnchecked(address.getBytes()); + Assert.assertNull(marketAccountOrderCapsule); + marketAccountStore.put(address.getBytes(), new MarketAccountOrderCapsule(ByteString.copyFrom(address.getBytes()))); final MarketAccountOrderCapsule result = marketAccountStore.get(address.getBytes()); @@ -34,4 +40,14 @@ public void testGet() throws Exception { Assert.assertEquals(result.getOwnerAddress(), ByteString.copyFrom(address.getBytes())); } + @Test + public void testPut() throws ItemNotFoundException { + String address = "Address1"; + marketAccountStore.put(address.getBytes(), + new MarketAccountOrderCapsule(ByteString.copyFrom(address.getBytes()))); + final MarketAccountOrderCapsule result = marketAccountStore.get(address.getBytes()); + Assert.assertNotNull(result); + Assert.assertEquals(result.getOwnerAddress(), ByteString.copyFrom(address.getBytes())); + } + } diff --git a/framework/src/test/java/org/tron/core/db/MarketOrderStoreTest.java b/framework/src/test/java/org/tron/core/db/MarketOrderStoreTest.java index 33126783e9a..1cfdb20da97 100644 --- a/framework/src/test/java/org/tron/core/db/MarketOrderStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/MarketOrderStoreTest.java @@ -8,6 +8,7 @@ import org.tron.core.Constant; import org.tron.core.capsule.MarketOrderCapsule; import org.tron.core.config.args.Args; +import org.tron.core.exception.ItemNotFoundException; import org.tron.core.store.MarketOrderStore; import org.tron.protos.Protocol; @@ -44,4 +45,20 @@ public void testGet() throws Exception { Assert.assertEquals(result.getBuyTokenQuantity(), 100L); } + @Test + public void testDelete() throws ItemNotFoundException { + byte[] orderId = "testDelete".getBytes(); + marketOrderStore.put(orderId, + new MarketOrderCapsule(Protocol.MarketOrder.newBuilder() + .setOrderId(ByteString.copyFrom(orderId)) + .setSellTokenId(ByteString.copyFrom("addr1".getBytes())) + .setSellTokenQuantity(200L) + .setBuyTokenId(ByteString.copyFrom("addr2".getBytes())) + .setBuyTokenQuantity(100L) + .build())); + marketOrderStore.delete(orderId); + final MarketOrderCapsule result = marketOrderStore.getUnchecked(orderId); + Assert.assertNull(result); + } + } diff --git a/framework/src/test/java/org/tron/core/db/RecentBlockStoreTest.java b/framework/src/test/java/org/tron/core/db/RecentBlockStoreTest.java new file mode 100644 index 00000000000..7856fe337a5 --- /dev/null +++ b/framework/src/test/java/org/tron/core/db/RecentBlockStoreTest.java @@ -0,0 +1,81 @@ +package org.tron.core.db; + +import com.google.protobuf.ByteString; +import javax.annotation.Resource; +import org.junit.Assert; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.Constant; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.BytesCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.exception.ItemNotFoundException; + +public class RecentBlockStoreTest extends BaseTest { + + @Resource + private RecentBlockStore recentBlockStore; + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath() + }, + Constant.TEST_CONF + ); + } + + private BlockCapsule getBlockCapsule() { + long number = 1; + return new BlockCapsule(number, + Sha256Hash.ZERO_HASH, + System.currentTimeMillis(), + ByteString.EMPTY); + } + + @Test + public void testPut() { + BlockCapsule blockCapsule = getBlockCapsule(); + byte[] key = ByteArray.subArray( + ByteArray.fromLong(blockCapsule.getNum()), 6, 8); + recentBlockStore.put(key, + new BytesCapsule(ByteArray.subArray(blockCapsule + .getBlockId().getBytes(), + 8, + 16))); + + Assert.assertTrue(recentBlockStore.has(key)); + } + + @Test + public void testGet() throws ItemNotFoundException { + BlockCapsule blockCapsule = getBlockCapsule(); + byte[] key = ByteArray.subArray( + ByteArray.fromLong(blockCapsule.getNum()), 6, 8); + BytesCapsule value = new BytesCapsule(ByteArray + .subArray(blockCapsule.getBlockId().getBytes(), + 8, + 16)); + recentBlockStore.put(key, value); + + BytesCapsule bytesCapsule = recentBlockStore.get(key); + Assert.assertNotNull(bytesCapsule); + Assert.assertArrayEquals(value.getData(), bytesCapsule.getData()); + } + + @Test + public void testDelete() { + BlockCapsule blockCapsule = getBlockCapsule(); + byte[] key = ByteArray.subArray( + ByteArray.fromLong(blockCapsule.getNum()), 6, 8); + recentBlockStore.put(key, + new BytesCapsule(ByteArray.subArray(blockCapsule + .getBlockId().getBytes(), + 8, + 16))); + recentBlockStore.delete(key); + Assert.assertFalse(recentBlockStore.has(key)); + } +} diff --git a/framework/src/test/java/org/tron/core/db/RecentTransactionStoreTest.java b/framework/src/test/java/org/tron/core/db/RecentTransactionStoreTest.java new file mode 100644 index 00000000000..20447dfc6a1 --- /dev/null +++ b/framework/src/test/java/org/tron/core/db/RecentTransactionStoreTest.java @@ -0,0 +1,72 @@ +package org.tron.core.db; + +import com.google.protobuf.ByteString; +import javax.annotation.Resource; + +import org.junit.Assert; +import org.junit.Test; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.core.Constant; +import org.tron.core.capsule.BytesCapsule; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.exception.ItemNotFoundException; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class RecentTransactionStoreTest extends BaseTest { + + @Resource + private RecentTransactionStore recentTransactionStore; + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath() + }, + Constant.TEST_CONF + ); + } + + private TransactionCapsule createTransaction() { + BalanceContract.TransferContract tc = + BalanceContract.TransferContract.newBuilder() + .setAmount(10) + .setOwnerAddress(ByteString.copyFromUtf8("aaa")) + .setToAddress(ByteString.copyFromUtf8("bbb")) + .build(); + return new TransactionCapsule(tc, + Protocol.Transaction.Contract.ContractType.TransferContract); + } + + + @Test + public void testPut() { + TransactionCapsule transaction = createTransaction(); + byte[] key = transaction.getTransactionId().getBytes(); + BytesCapsule value = new BytesCapsule(ByteArray.subArray(transaction + .getTransactionId().getBytes(), + 8, + 16)); + recentTransactionStore.put(key, value); + Assert.assertTrue(recentTransactionStore.has(key)); + } + + @Test + public void testGet() throws ItemNotFoundException { + TransactionCapsule transaction = createTransaction(); + byte[] key = transaction.getTransactionId().getBytes(); + BytesCapsule value = new BytesCapsule( + ByteArray.subArray(transaction + .getTransactionId().getBytes(), + 8, + 16)); + recentTransactionStore.put(key, value); + + BytesCapsule bytesCapsule = recentTransactionStore.get(key); + Assert.assertNotNull(bytesCapsule); + Assert.assertArrayEquals(value.getData(), bytesCapsule.getData()); + + } +} diff --git a/framework/src/test/java/org/tron/core/db/TransactionHistoryTest.java b/framework/src/test/java/org/tron/core/db/TransactionHistoryTest.java index eef168938b2..c5c249b6f70 100644 --- a/framework/src/test/java/org/tron/core/db/TransactionHistoryTest.java +++ b/framework/src/test/java/org/tron/core/db/TransactionHistoryTest.java @@ -59,4 +59,11 @@ public void get() throws BadItemException { Assert.assertEquals(ByteArray.toHexString(transactionId), ByteArray.toHexString(resultCapsule.getId())); } + + @Test + public void testDelete() throws BadItemException { + transactionHistoryStore.delete(transactionId); + TransactionInfoCapsule transactionInfoCapsule = transactionHistoryStore.get(transactionId); + Assert.assertNull(transactionInfoCapsule); + } } \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/db/TransactionStoreTest.java b/framework/src/test/java/org/tron/core/db/TransactionStoreTest.java index 1edc4aca756..deeb135373f 100644 --- a/framework/src/test/java/org/tron/core/db/TransactionStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/TransactionStoreTest.java @@ -112,7 +112,7 @@ private VoteWitnessContract getVoteWitnessContract(String address, String votead public void getTransactionTest() throws BadItemException, ItemNotFoundException { final BlockStore blockStore = chainBaseManager.getBlockStore(); final TransactionStore trxStore = chainBaseManager.getTransactionStore(); - String key = "f31db24bfbd1a2ef19beddca0a0fa37632eded9ac666a05d3bd925f01dde1f62"; + String key = PublicMethod.getRandomPrivateKey();; BlockCapsule blockCapsule = new BlockCapsule( diff --git a/framework/src/test/java/org/tron/core/db/ZKProofStoreTest.java b/framework/src/test/java/org/tron/core/db/ZKProofStoreTest.java new file mode 100644 index 00000000000..a8aa07c4342 --- /dev/null +++ b/framework/src/test/java/org/tron/core/db/ZKProofStoreTest.java @@ -0,0 +1,58 @@ +package org.tron.core.db; + +import com.google.protobuf.ByteString; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.tron.common.BaseTest; +import org.tron.core.Constant; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.store.ZKProofStore; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class ZKProofStoreTest extends BaseTest { + + static { + Args.setParam(new String[]{"--output-directory", dbPath()}, + Constant.TEST_CONF); + } + + @Autowired + private ZKProofStore proofStore; + + private TransactionCapsule getTransactionCapsule() { + BalanceContract.TransferContract transferContract = + BalanceContract.TransferContract.newBuilder() + .setAmount(10) + .setOwnerAddress(ByteString.copyFromUtf8("aaa")) + .setToAddress(ByteString.copyFromUtf8("bbb")) + .build(); + return new TransactionCapsule(transferContract, + Protocol.Transaction.Contract.ContractType.TransferContract); + } + + @Test + public void testPut() { + TransactionCapsule trx = getTransactionCapsule(); + proofStore.put(trx.getTransactionId().getBytes(), + true); + boolean has = proofStore.has(trx.getTransactionId().getBytes()); + Assert.assertTrue(has); + } + + @Test + public void testGet() { + TransactionCapsule trx = getTransactionCapsule(); + proofStore.put(trx.getTransactionId().getBytes(), + true); + Boolean result = proofStore.get(trx.getTransactionId().getBytes()); + Assert.assertEquals(true, result); + + proofStore.put(trx.getTransactionId().getBytes(), + false); + result = proofStore.get(trx.getTransactionId().getBytes()); + Assert.assertEquals(false, result); + } +} diff --git a/framework/src/test/java/org/tron/core/net/TronNetDelegateTest.java b/framework/src/test/java/org/tron/core/net/TronNetDelegateTest.java index c1ac3df1599..727aad9dccb 100644 --- a/framework/src/test/java/org/tron/core/net/TronNetDelegateTest.java +++ b/framework/src/test/java/org/tron/core/net/TronNetDelegateTest.java @@ -20,6 +20,7 @@ public void test() throws Exception { Args.setParam(new String[] {"-w"}, Constant.TEST_CONF); CommonParameter parameter = Args.getInstance(); Args.logConfig(); + parameter.setUnsolidifiedBlockCheck(true); BlockCapsule.BlockId blockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 10000L); 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 d1fdfaa5d90..e5d242a6c4d 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 @@ -32,9 +32,6 @@ public void testProcessMessage() throws Exception { Protocol.PBFTMessage.Raw raw = rawBuilder.build(); PbftSignCapsule pbftSignCapsule = new PbftSignCapsule(raw.toByteString(), new ArrayList<>()); PbftCommitMessage pbftCommitMessage = new PbftCommitMessage(pbftSignCapsule); - pbftDataSyncHandler.processMessage(null, pbftCommitMessage); - Assert.assertEquals(Protocol.PBFTMessage.Raw.parseFrom( - pbftCommitMessage.getPBFTCommitResult().getData()).getViewN(), 1); DynamicPropertiesStore dynamicPropertiesStore = Mockito.mock(DynamicPropertiesStore.class); PbftSignDataStore pbftSignDataStore = Mockito.mock(PbftSignDataStore.class); @@ -48,6 +45,10 @@ public void testProcessMessage() throws Exception { field.setAccessible(true); field.set(pbftDataSyncHandler, chainBaseManager); + pbftDataSyncHandler.processMessage(null, pbftCommitMessage); + Assert.assertEquals(Protocol.PBFTMessage.Raw.parseFrom( + pbftCommitMessage.getPBFTCommitResult().getData()).getViewN(), 1); + pbftDataSyncHandler.processPBFTCommitData(blockCapsule); Field field1 = PbftDataSyncHandler.class.getDeclaredField("pbftCommitMessageCache"); field1.setAccessible(true); diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/PbftMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/PbftMsgHandlerTest.java index 3fec10fc163..8b9d1969cfc 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/PbftMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/PbftMsgHandlerTest.java @@ -34,6 +34,7 @@ import org.tron.core.net.message.MessageTypes; import org.tron.core.net.peer.PeerConnection; import org.tron.core.net.peer.PeerManager; +import org.tron.core.store.DynamicPropertiesStore; import org.tron.p2p.P2pConfig; import org.tron.p2p.base.Parameter; import org.tron.p2p.connection.Channel; @@ -116,6 +117,14 @@ public void testPbft() throws Exception { Assert.assertEquals(P2pException.TypeEnum.BAD_MESSAGE, e.getType()); } + DynamicPropertiesStore dynamicPropertiesStore = context.getBean(DynamicPropertiesStore.class); + dynamicPropertiesStore.saveAllowPBFT(1); + try { + context.getBean(PbftMsgHandler.class).processMessage(peer, pbftMessage); + } catch (P2pException e) { + Assert.assertEquals(P2pException.TypeEnum.BAD_MESSAGE, e.getType()); + } + Assert.assertEquals(1, PeerManager.getPeers().size()); } } diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index ea2156ae713..f4fabce5d64 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -21,6 +21,7 @@ import org.mockito.Mockito; import org.springframework.context.ApplicationContext; import org.tron.common.application.TronApplicationContext; +import org.tron.common.utils.ByteArray; import org.tron.common.utils.ReflectUtils; import org.tron.common.utils.Sha256Hash; import org.tron.core.ChainBaseManager; @@ -137,6 +138,37 @@ public void testInvalidHelloMessage() { } } + @Test + public void testInvalidHelloMessage2() throws Exception { + Protocol.HelloMessage.Builder builder = getTestHelloMessageBuilder(); + Assert.assertTrue(new HelloMessage(builder.build().toByteArray()).valid()); + + builder.setAddress(ByteString.copyFrom(new byte[201])); + HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); + Assert.assertFalse(helloMessage.valid()); + + builder.setAddress(ByteString.copyFrom(new byte[200])); + helloMessage = new HelloMessage(builder.build().toByteArray()); + Assert.assertTrue(helloMessage.valid()); + + builder.setSignature(ByteString.copyFrom(new byte[201])); + helloMessage = new HelloMessage(builder.build().toByteArray()); + Assert.assertFalse(helloMessage.valid()); + + builder.setSignature(ByteString.copyFrom(new byte[200])); + helloMessage = new HelloMessage(builder.build().toByteArray()); + Assert.assertTrue(helloMessage.valid()); + + builder.setCodeVersion(ByteString.copyFrom(new byte[201])); + helloMessage = new HelloMessage(builder.build().toByteArray()); + Assert.assertFalse(helloMessage.valid()); + + builder.setCodeVersion(ByteString.copyFrom(new byte[200])); + helloMessage = new HelloMessage(builder.build().toByteArray()); + Assert.assertTrue(helloMessage.valid()); + } + + @Test public void testRelayHelloMessage() throws NoSuchMethodException { InetSocketAddress a1 = new InetSocketAddress("127.0.0.1", 10001); @@ -266,4 +298,13 @@ private Protocol.HelloMessage.Builder getHelloMessageBuilder(Node from, long tim return builder; } + + private Protocol.HelloMessage.Builder getTestHelloMessageBuilder() { + InetSocketAddress a1 = new InetSocketAddress("127.0.0.1", 10001); + Node node = new Node(NetUtil.getNodeId(), a1.getAddress().getHostAddress(), null, a1.getPort()); + Protocol.HelloMessage.Builder builder = + getHelloMessageBuilder(node, System.currentTimeMillis(), + ChainBaseManager.getChainBaseManager()); + return builder; + } } diff --git a/framework/src/test/java/org/tron/core/pbft/PbftTest.java b/framework/src/test/java/org/tron/core/pbft/PbftTest.java index a5d74ad7a37..33a46516988 100644 --- a/framework/src/test/java/org/tron/core/pbft/PbftTest.java +++ b/framework/src/test/java/org/tron/core/pbft/PbftTest.java @@ -29,6 +29,7 @@ public void testPbftSrMessage() { ByteString.copyFrom(ByteArray.fromHexString("41df309fef25b311e7895562bd9e11aab2a58816d2"))); PbftMessage pbftSrMessage = PbftMessage .prePrepareSRLMsg(blockCapsule, srList, 1, miner); + PbftMessage.fullNodePrePrepareSRLMsg(blockCapsule, srList, 1); System.out.println(pbftSrMessage); } diff --git a/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java b/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java index 17b2526d6d0..0082c8728da 100644 --- a/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java +++ b/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java @@ -17,14 +17,16 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.contrib.java.lang.system.ExpectedSystemExit; import org.junit.rules.TemporaryFolder; import org.tron.common.application.TronApplicationContext; import org.tron.common.error.TronDBException; import org.tron.common.es.ExecutorServiceManager; import org.tron.common.utils.ByteArray; -import org.tron.common.utils.Sha256Hash; +import org.tron.common.utils.ReflectUtils; import org.tron.core.Constant; import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.BytesCapsule; import org.tron.core.capsule.WitnessCapsule; import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; @@ -33,6 +35,7 @@ import org.tron.core.store.AccountStore; import org.tron.core.store.DelegationStore; import org.tron.core.store.DynamicPropertiesStore; +import org.tron.core.store.RewardViStore; import org.tron.core.store.WitnessStore; import org.tron.protos.Protocol; @@ -107,9 +110,13 @@ public class ComputeRewardTest { private static RewardViCalService rewardViCalService; private static WitnessStore witnessStore; private static MortgageService mortgageService; + private static RewardViStore rewardViStore; @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public final ExpectedSystemExit exit = ExpectedSystemExit.none(); + @After public void destroy() { context.destroy(); @@ -130,6 +137,7 @@ public void init() throws IOException { rewardViCalService = context.getBean(RewardViCalService.class); witnessStore = context.getBean(WitnessStore.class); mortgageService = context.getBean(MortgageService.class); + rewardViStore = context.getBean(RewardViStore.class); setUp(); } @@ -251,14 +259,36 @@ private void setUp() { } catch (ExecutionException e) { throw new TronDBException(e); } - - rewardViCalService.setRewardViRoot(Sha256Hash.wrap( - ByteString.fromHex("e0ebe2f3243391ed674dff816a07f589a3279420d6d88bc823b6a9d5778337ce"))); } @Test public void query() { + exit.expectSystemExitWithStatus(1); Assert.assertEquals(3189, mortgageService.queryReward(OWNER_ADDRESS)); + // mock root is error + rewardViStore.put("test".getBytes(), "test".getBytes()); + ReflectUtils.invokeMethod(rewardViCalService,"maybeRun"); + + // mock no need + propertiesStore.saveCurrentCycleNumber(0); + // reset + propertiesStore.put("NEW_REWARD_ALGORITHM_EFFECTIVE_CYCLE".getBytes(), + new BytesCapsule(ByteArray.fromLong(Long.MAX_VALUE))); + // set + propertiesStore.saveNewRewardAlgorithmEffectiveCycle(); + ReflectUtils.invokeMethod(rewardViCalService,"maybeRun"); + + // mock maybeRun exception + propertiesStore.saveCurrentCycleNumber(4); + // reset + propertiesStore.put("NEW_REWARD_ALGORITHM_EFFECTIVE_CYCLE".getBytes(), + new BytesCapsule(ByteArray.fromLong(Long.MAX_VALUE))); + // set + propertiesStore.saveNewRewardAlgorithmEffectiveCycle(); + propertiesStore.saveCurrentCycleNumber(5); + rewardViStore.close(); + ReflectUtils.invokeMethod(rewardViCalService,"maybeRun"); + } static class Vote { diff --git a/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java b/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java index e248af647c3..0ba32b27f2e 100644 --- a/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/ProposalServiceTest.java @@ -1,6 +1,5 @@ package org.tron.core.services; -import static org.tron.core.utils.ProposalUtil.ProposalType.ALLOW_OLD_REWARD_OPT; import static org.tron.core.utils.ProposalUtil.ProposalType.ENERGY_FEE; import static org.tron.core.utils.ProposalUtil.ProposalType.TRANSACTION_FEE; import static org.tron.core.utils.ProposalUtil.ProposalType.WITNESS_127_PAY_PER_BLOCK; @@ -11,9 +10,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.tron.common.BaseTest; import org.tron.core.Constant; import org.tron.core.capsule.ProposalCapsule; @@ -25,9 +22,6 @@ @Slf4j public class ProposalServiceTest extends BaseTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - private static boolean init; @BeforeClass @@ -69,9 +63,6 @@ public void test() { proposal = Proposal.newBuilder().putParameters(proposalType.getCode(), 1).build(); } proposalCapsule = new ProposalCapsule(proposal); - if (proposalType == ALLOW_OLD_REWARD_OPT) { - thrown.expect(IllegalStateException.class); - } result = ProposalService.process(dbManager, proposalCapsule); Assert.assertTrue(result); } 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 new file mode 100644 index 00000000000..a3a051ec6c9 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/ClearABIServletTest.java @@ -0,0 +1,92 @@ +package org.tron.core.services.http; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertTrue; +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; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.core.Constant; +import org.tron.core.capsule.ContractCapsule; +import org.tron.core.config.args.Args; +import org.tron.protos.contract.SmartContractOuterClass; + +public class ClearABIServletTest extends BaseTest { + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Resource + private ClearABIServlet clearABIServlet; + + private static final String SMART_CONTRACT_NAME = "smart_contract_test"; + private static String CONTRACT_ADDRESS = "A0B4750E2CD76E19DCA331BF5D089B71C3C2798548"; + private static String OWNER_ADDRESS; + private static final long SOURCE_ENERGY_LIMIT = 10L; + + + + private SmartContractOuterClass.SmartContract.Builder createContract( + String contractAddress, String contractName) { + OWNER_ADDRESS = + "A099357684BC659F5166046B56C95A0E99F1265CBD"; + SmartContractOuterClass.SmartContract.Builder builder = + SmartContractOuterClass.SmartContract.newBuilder(); + builder.setName(contractName); + builder.setOriginAddress(ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS))); + builder.setContractAddress(ByteString.copyFrom(ByteArray.fromHexString(contractAddress))); + builder.setOriginEnergyLimit(SOURCE_ENERGY_LIMIT); + return builder; + } + + @Test + public void testClearABI() { + chainBaseManager.getDynamicPropertiesStore() + .saveAllowTvmConstantinople(1); + SmartContractOuterClass.SmartContract.Builder contract = + createContract(CONTRACT_ADDRESS, SMART_CONTRACT_NAME); + chainBaseManager.getContractStore().put( + ByteArray.fromHexString(CONTRACT_ADDRESS), + new ContractCapsule(contract.build())); + + String jsonParam = "{" + + " \"owner_address\": \"A099357684BC659F5166046B56C95A0E99F1265CBD\"," + + " \"contract_address\": \"A0B4750E2CD76E19DCA331BF5D089B71C3C2798548\"" + + "}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes(UTF_8)); + + MockHttpServletResponse response = new MockHttpServletResponse(); + clearABIServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + try { + String contentAsString = response.getContentAsString(); + JSONObject result = JSONObject.parseObject(contentAsString); + assertTrue(result.containsKey("raw_data")); + assertTrue(result.containsKey("txID")); + } catch (UnsupportedEncodingException e) { + fail(e.getMessage()); + } + + } + +} 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 new file mode 100644 index 00000000000..bbc00ce81f0 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/CreateAccountServletTest.java @@ -0,0 +1,75 @@ +package org.tron.core.services.http; + +import static java.nio.charset.StandardCharsets.UTF_8; +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; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.core.Constant; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.config.args.Args; +import org.tron.protos.Protocol; + + +public class CreateAccountServletTest extends BaseTest { + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Resource + private CreateAccountServlet createAccountServlet; + + @Before + public void init() { + AccountCapsule accountCapsule = new AccountCapsule( + ByteString.copyFrom(ByteArray + .fromHexString("A099357684BC659F5166046B56C95A0E99F1265CD1")), + ByteString.copyFromUtf8("owner"), + Protocol.AccountType.forNumber(1)); + + chainBaseManager.getAccountStore().put(accountCapsule.createDbKey(), + accountCapsule); + } + + @Test + public void testCreate() { + String jsonParam = "{" + + "\"owner_address\": \"A099357684BC659F5166046B56C95A0E99F1265CD1\"," + + "\"account_address\": \"A0B4750E2CD76E19DCA331BF5D089B71C3C2798541\"" + + "}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes(UTF_8)); + + MockHttpServletResponse response = new MockHttpServletResponse(); + createAccountServlet.doPost(request, response); + + Assert.assertEquals(200, response.getStatus()); + try { + String contentAsString = response.getContentAsString(); + JSONObject result = JSONObject.parseObject(contentAsString); + Assert.assertTrue(result.containsKey("raw_data")); + Assert.assertTrue(result.containsKey("txID")); + } catch (UnsupportedEncodingException e) { + fail(e.getMessage()); + } + } +} 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 new file mode 100644 index 00000000000..4c70eb9252c --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/CreateAssetIssueServletTest.java @@ -0,0 +1,90 @@ +package org.tron.core.services.http; + +import static java.nio.charset.StandardCharsets.UTF_8; +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; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.core.Constant; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.config.args.Args; +import org.tron.protos.Protocol; + +public class CreateAssetIssueServletTest extends BaseTest { + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Resource + private CreateAssetIssueServlet createAssetIssueServlet; + + @Before + public void init() { + AccountCapsule accountCapsule = new AccountCapsule( + ByteString.copyFrom(ByteArray + .fromHexString("A099357684BC659F5166046B56C95A0E99F1265CD1")), + ByteString.copyFromUtf8("owner"), + Protocol.AccountType.forNumber(1)); + accountCapsule.setBalance(10000000000L); + + chainBaseManager.getAccountStore().put(accountCapsule.createDbKey(), + accountCapsule); + } + + @Test + public void testCreate() { + String jsonParam = "{" + + " \"owner_address\": \"A099357684BC659F5166046B56C95A0E99F1265CD1\"," + + " \"name\": \"0x6173736574497373756531353330383934333132313538\"," + + " \"abbr\": \"0x6162627231353330383934333132313538\"," + + " \"total_supply\": 4321," + + " \"trx_num\": 1," + + " \"num\": 1," + + " \"start_time\": 1530894315158," + + " \"end_time\": 1533894312158," + + " \"description\": \"007570646174654e616d6531353330363038383733343633\"," + + " \"url\": \"007570646174654e616d6531353330363038383733343633\"," + + " \"free_asset_net_limit\": 10000," + + " \"public_free_asset_net_limit\": 10000," + + " \"frozen_supply\": {" + + " \"frozen_amount\": 1," + + " \"frozen_days\": 2" + + " }" + + "}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes(UTF_8)); + + MockHttpServletResponse response = new MockHttpServletResponse(); + createAssetIssueServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + try { + String contentAsString = response.getContentAsString(); + JSONObject result = JSONObject.parseObject(contentAsString); + Assert.assertTrue(result.containsKey("raw_data")); + Assert.assertTrue(result.containsKey("txID")); + } catch (UnsupportedEncodingException e) { + fail(e.getMessage()); + } + + } + +} 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 new file mode 100644 index 00000000000..301e4472e69 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/CreateSpendAuthSigServletTest.java @@ -0,0 +1,60 @@ +package org.tron.core.services.http; + +import static java.nio.charset.StandardCharsets.UTF_8; +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; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; + +public class CreateSpendAuthSigServletTest extends BaseTest { + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Resource + private CreateSpendAuthSigServlet createSpendAuthSigServlet; + + @Test + public void testCreateSpendAuthSig() { + String jsonParam = "{" + + " \"ask\": \"e3ebcba1531f6d9158d9c162660c5d7c04dadf77d" + + "85d7436a9c98b291ff69a09\"," + + " \"tx_hash\": \"3b78fee6e956f915ffe082284c5f18640edca9" + + "c57a5f227e5f7d7eb65ad61502\"," + + " \"alpha\": \"2608999c3a97d005a879ecdaa16fd29ae434fb67" + + "b177c5e875b0c829e6a1db04\"" + + "}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes(UTF_8)); + + MockHttpServletResponse response = new MockHttpServletResponse(); + createSpendAuthSigServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + try { + String contentAsString = response.getContentAsString(); + JSONObject result = JSONObject.parseObject(contentAsString); + Assert.assertTrue(result.containsKey("value")); + String resultValue = (String) result.get("value"); + Assert.assertNotNull(resultValue); + } catch (UnsupportedEncodingException e) { + fail(e.getMessage()); + } + } + +} 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 new file mode 100644 index 00000000000..6cd6e9e2482 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/CreateWitnessServletTest.java @@ -0,0 +1,88 @@ +package org.tron.core.services.http; + +import static java.nio.charset.StandardCharsets.UTF_8; +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; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.common.crypto.ECKey; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.Utils; +import org.tron.core.Constant; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.WitnessCapsule; +import org.tron.core.config.args.Args; +import org.tron.protos.Protocol; + +public class CreateWitnessServletTest extends BaseTest { + + @Resource + private CreateWitnessServlet createWitnessServlet; + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + private static WitnessCapsule witnessCapsule; + private static AccountCapsule accountCapsule; + + @Before + public void init() { + ECKey ecKey = new ECKey(Utils.getRandom()); + ByteString address = ByteString.copyFrom(ecKey.getAddress()); + + accountCapsule = + new AccountCapsule(Protocol.Account + .newBuilder() + .setAddress(address).build()); + accountCapsule.setBalance(10000000L); + dbManager.getAccountStore().put(accountCapsule + .getAddress().toByteArray(), accountCapsule); + } + + @Test + public void testCreateWitness() { + chainBaseManager.getDynamicPropertiesStore() + .saveAccountUpgradeCost(1L); + String hexAddress = ByteArray + .toHexString(accountCapsule.getAddress().toByteArray()); + String jsonParam = "{\"owner_address\":\"" + + hexAddress + "\"," + + " \"url\": \"00757064617" + + "4654e616d6531353330363038383733343633\"}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes(UTF_8)); + + MockHttpServletResponse response = new MockHttpServletResponse(); + createWitnessServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + try { + String contentAsString = response.getContentAsString(); + JSONObject result = JSONObject.parseObject(contentAsString); + Assert.assertTrue(result.containsKey("raw_data")); + Assert.assertTrue(result.containsKey("txID")); + } catch (UnsupportedEncodingException e) { + fail(e.getMessage()); + } + } + +} + + diff --git a/framework/src/test/java/org/tron/core/services/http/GetAccountByIdServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetAccountByIdServletTest.java new file mode 100644 index 00000000000..38c23a971ff --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetAccountByIdServletTest.java @@ -0,0 +1,47 @@ +package org.tron.core.services.http; + +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + +import javax.annotation.Resource; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; + +public class GetAccountByIdServletTest extends BaseTest { + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Resource + private GetAccountByIdServlet getAccountByIdServlet; + + public MockHttpServletRequest createRequest(String contentType) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + if (isNotEmpty(contentType)) { + request.setContentType(contentType); + } + request.setCharacterEncoding("UTF-8"); + return request; + } + + @Test + public void testGetAccountById() { + String jsonParam = "{\"account_id\": \"6161616162626262\"}"; + MockHttpServletRequest request = createRequest("application/json"); + request.setContent(jsonParam.getBytes()); + MockHttpServletResponse response = new MockHttpServletResponse(); + + getAccountByIdServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetBlockByIdServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetBlockByIdServletTest.java new file mode 100644 index 00000000000..8b213e12640 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetBlockByIdServletTest.java @@ -0,0 +1,51 @@ +package org.tron.core.services.http; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.tron.common.utils.client.utils.HttpMethed.createRequest; + +import javax.annotation.Resource; + +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; + +public class GetBlockByIdServletTest extends BaseTest { + + @Resource + private GetBlockByIdServlet getBlockByIdServlet; + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Test + public void testGetBlockById() { + String jsonParam = "{\"value\": " + + "\"0000000002951a2f65db6725c2d0583f1ab9bdb1520eeedece99d9c98f3\"}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes(UTF_8)); + MockHttpServletResponse response = new MockHttpServletResponse(); + getBlockByIdServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + } + + @Test + public void testGet() { + MockHttpServletRequest request = createRequest(HttpGet.METHOD_NAME); + request.addParameter("value", "0000000002951a2f65db6725c2d0583f1ab9bdb1520eeedece99d9c98f3"); + MockHttpServletResponse response = new MockHttpServletResponse(); + getBlockByIdServlet.doGet(request, response); + Assert.assertEquals(200, response.getStatus()); + } +} 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 new file mode 100644 index 00000000000..5ff84c54dbe --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetBlockByNumServletTest.java @@ -0,0 +1,69 @@ +package org.tron.core.services.http; + +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; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; + +public class GetBlockByNumServletTest extends BaseTest { + + @Resource + private GetBlockByNumServlet getBlockByNumServlet; + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Test + public void testGetBlockByNum() { + String jsonParam = "{\"number\": 1}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes()); + MockHttpServletResponse response = new MockHttpServletResponse(); + + try { + getBlockByNumServlet.doPost(request, response); + String contentAsString = response.getContentAsString(); + JSONObject result = JSONObject.parseObject(contentAsString); + assertTrue(result.containsKey("blockID")); + assertTrue(result.containsKey("transactions")); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + @Test + public void testGet() { + String jsonParam = "{\"number\": 1}"; + MockHttpServletRequest request = createRequest("application/json"); + request.setContent(jsonParam.getBytes()); + MockHttpServletResponse response = new MockHttpServletResponse(); + + try { + getBlockByNumServlet.doPost(request, response); + String contentAsString = response.getContentAsString(); + JSONObject result = JSONObject.parseObject(contentAsString); + assertTrue(result.containsKey("blockID")); + assertTrue(result.containsKey("transactions")); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + +} 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 new file mode 100644 index 00000000000..0a1a2e4ac5a --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByBlockNumServletTest.java @@ -0,0 +1,79 @@ +package org.tron.core.services.http; + +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; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.core.Constant; + +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; + +public class GetTransactionInfoByBlockNumServletTest extends BaseTest { + + @Resource + private GetTransactionInfoByBlockNumServlet getTransactionInfoByBlockNumServlet; + private static final byte[] transactionId = TransactionStoreTest.randomBytes(32); + private static TransactionRetCapsule transactionRetCapsule; + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Before + public void init() { + byte[] blockNum = ByteArray.fromLong(100); + TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); + + transactionInfoCapsule.setId(transactionId); + transactionInfoCapsule.setFee(1000L); + transactionInfoCapsule.setBlockNumber(100L); + transactionInfoCapsule.setBlockTimeStamp(200L); + + transactionRetCapsule = new TransactionRetCapsule(); + transactionRetCapsule.addTransactionInfo(transactionInfoCapsule.getInstance()); + chainBaseManager.getTransactionRetStore() + .put(blockNum, transactionRetCapsule); + } + + @Test + public void testGetTransactionInfoByBlockNum() { + String jsonParam = "{\"num\" : 100}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes(UTF_8)); + MockHttpServletResponse response = new MockHttpServletResponse(); + + getTransactionInfoByBlockNumServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + try { + String contentAsString = response.getContentAsString(); + JSONArray array = JSONArray.parseArray(contentAsString); + Assert.assertEquals(1, array.size()); + JSONObject object = (JSONObject) array.get(0); + Assert.assertEquals(1000, object.get("fee")); + Assert.assertEquals(100, object.get("blockNumber")); + } catch (UnsupportedEncodingException e) { + Assert.fail(e.getMessage()); + } + } +} 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 new file mode 100644 index 00000000000..900c41c7df8 --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetTransactionInfoByIdServletTest.java @@ -0,0 +1,126 @@ +package org.tron.core.services.http; + +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; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.common.utils.ByteArray; +import org.tron.core.Constant; +import org.tron.core.Wallet; +import org.tron.core.capsule.AccountCapsule; +import org.tron.core.capsule.TransactionCapsule; +import org.tron.core.capsule.TransactionInfoCapsule; +import org.tron.core.capsule.TransactionRetCapsule; +import org.tron.core.config.args.Args; +import org.tron.core.db.TransactionStore; +import org.tron.core.db.TransactionStoreTest; +import org.tron.core.store.TransactionRetStore; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract; + +public class GetTransactionInfoByIdServletTest extends BaseTest { + + @Resource + private GetTransactionInfoByIdServlet getTransactionInfoByIdServlet; + @Resource + private TransactionStore transactionStore; + @Resource + private TransactionRetStore transactionRetStore; + + private static final String OWNER_ADDRESS = + Wallet.getAddressPreFixString() + "abd4b9367799eaa3197fecb144eb71de1e049abc"; + private static final String TO_ADDRESS = + Wallet.getAddressPreFixString() + "abd4b9367799eaa3197fecb144eb71de1e049abc"; + private static final long AMOUNT = 100; + private static final byte[] KEY_1 = TransactionStoreTest.randomBytes(21); + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Before + public void init() { + byte[] blockNum = ByteArray.fromLong(100); + TransactionInfoCapsule transactionInfoCapsule = new TransactionInfoCapsule(); + + transactionInfoCapsule.setId(KEY_1); + transactionInfoCapsule.setFee(1000L); + transactionInfoCapsule.setBlockNumber(100L); + transactionInfoCapsule.setBlockTimeStamp(200L); + + TransactionRetCapsule transactionRetCapsule = new TransactionRetCapsule(); + transactionRetCapsule.addTransactionInfo(transactionInfoCapsule.getInstance()); + chainBaseManager.getTransactionRetStore() + .put(blockNum, transactionRetCapsule); + transactionRetStore.put(blockNum, transactionRetCapsule); + + AccountCapsule owner = new AccountCapsule( + ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS)), + ByteString.copyFromUtf8("owner"), + Protocol.AccountType.forNumber(1)); + owner.setBalance(1000000L); + + AccountCapsule to = new AccountCapsule( + ByteString.copyFrom(ByteArray.fromHexString(TO_ADDRESS)), + ByteString.copyFromUtf8("to"), + Protocol.AccountType.forNumber(1)); + to.setBalance(1000000L); + + chainBaseManager.getAccountStore().put(owner.createDbKey(), + owner); + chainBaseManager.getAccountStore().put(to.createDbKey(), + to); + BalanceContract.TransferContract transferContract = + getContract(AMOUNT, OWNER_ADDRESS, TO_ADDRESS); + TransactionCapsule transactionCapsule = new TransactionCapsule(transferContract, + chainBaseManager.getAccountStore()); + transactionCapsule.setBlockNum(100L); + transactionStore.put(KEY_1, transactionCapsule); + } + + private BalanceContract.TransferContract getContract(long count, + String owneraddress, String toaddress) { + return BalanceContract.TransferContract.newBuilder() + .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(owneraddress))) + .setToAddress(ByteString.copyFrom(ByteArray.fromHexString(toaddress))) + .setAmount(count) + .build(); + } + + @Test + public void testGetInfoById() { + String jsonParam = "{\"value\" : " + + "\"" + ByteArray.toHexString(KEY_1) + "\"}"; + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes(UTF_8)); + MockHttpServletResponse response = new MockHttpServletResponse(); + + getTransactionInfoByIdServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + try { + String contentAsString = response.getContentAsString(); + JSONObject jsonObject = JSONObject.parseObject(contentAsString); + Assert.assertEquals(1000, jsonObject.get("fee")); + Assert.assertEquals(100, jsonObject.get("blockNumber")); + } catch (UnsupportedEncodingException e) { + Assert.fail(e.getMessage()); + } + } +} diff --git a/framework/src/test/java/org/tron/core/services/http/GetTransactionListFromPendingServletTest.java b/framework/src/test/java/org/tron/core/services/http/GetTransactionListFromPendingServletTest.java new file mode 100644 index 00000000000..614d520280d --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/GetTransactionListFromPendingServletTest.java @@ -0,0 +1,38 @@ +package org.tron.core.services.http; + +import static org.junit.Assert.assertEquals; +import static org.tron.common.utils.client.utils.HttpMethed.createRequest; + +import javax.annotation.Resource; + +import org.apache.http.client.methods.HttpGet; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; + + +public class GetTransactionListFromPendingServletTest extends BaseTest { + + @Resource + private GetTransactionListFromPendingServlet getTransactionListFromPendingServlet; + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Test + public void testGet() { + MockHttpServletRequest request = createRequest(HttpGet.METHOD_NAME); + MockHttpServletResponse response = new MockHttpServletResponse(); + getTransactionListFromPendingServlet.doPost(request, response); + assertEquals(200, response.getStatus()); + } + +} diff --git a/framework/src/test/java/org/tron/core/services/http/UpdateAccountServletTest.java b/framework/src/test/java/org/tron/core/services/http/UpdateAccountServletTest.java new file mode 100644 index 00000000000..e5064e2013b --- /dev/null +++ b/framework/src/test/java/org/tron/core/services/http/UpdateAccountServletTest.java @@ -0,0 +1,77 @@ +package org.tron.core.services.http; + +import static org.tron.common.utils.client.utils.HttpMethed.createRequest; + +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; +import org.springframework.mock.web.MockHttpServletResponse; +import org.tron.common.BaseTest; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; + +public class UpdateAccountServletTest extends BaseTest { + + static { + Args.setParam( + new String[]{ + "--output-directory", dbPath(), + }, Constant.TEST_CONF + ); + } + + @Resource + private UpdateAccountServlet updateAccountServlet; + + private String getParam() { + return "{" + + " \"owner_address\": \"TXmVpin5vq5gdZsciyyjdZgKRUju4st1wM\"," + + " \"owner\": {" + + " \"type\": 0," + + " \"permission_name\": \"owner\"," + + " \"threshold\": 1," + + " \"keys\": [{" + + " \"address\": \"TXmVpin5vq5gdZsciyyjdZgKRUju4st1wM\"," + + " \"weight\": 1" + + " }]" + + " }," + + " \"witness\": {" + + " \"type\": 1," + + " \"permission_name\": \"witness\"," + + " \"threshold\": 1," + + " \"keys\": [{" + + " \"address\": \"TXmVpin5vq5gdZsciyyjdZgKRUju4st1wM\"," + + " \"weight\": 1" + + " }]" + + " }," + + " \"actives\": [{" + + " \"type\": 2," + + " \"permission_name\": \"active12323\"," + + " \"threshold\": 2," + + " \"operations\": " + + "\"7fff1fc0033e0000000000000000000000000000000000000000000000000000\"," + + " \"keys\": [{" + + " \"address\": \"TXmVpin5vq5gdZsciyyjdZgKRUju4st1wM\"," + + " \"weight\": 1" + + " }, {" + + " \"address\": \"TXmVpin5vq5gdZsciyyjdZgKRUju4st1wM\"," + + " \"weight\": 1" + + " }]" + + " }]," + + " \"visible\": true}"; + } + + @Test + public void test() { + String jsonParam = getParam(); + MockHttpServletRequest request = createRequest(HttpPost.METHOD_NAME); + request.setContentType("application/json"); + request.setContent(jsonParam.getBytes()); + MockHttpServletResponse response = new MockHttpServletResponse(); + + updateAccountServlet.doPost(request, response); + Assert.assertEquals(200, response.getStatus()); + } +} diff --git a/framework/src/test/java/org/tron/core/zksnark/NoteEncDecryTest.java b/framework/src/test/java/org/tron/core/zksnark/NoteEncDecryTest.java index e59060540e9..3c3fb14b2b1 100644 --- a/framework/src/test/java/org/tron/core/zksnark/NoteEncDecryTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/NoteEncDecryTest.java @@ -23,7 +23,6 @@ public class NoteEncDecryTest extends BaseTest { private static final String FROM_ADDRESS; - private static final String ADDRESS_ONE_PRIVATE_KEY; private static final long OWNER_BALANCE = 100_000_000; private static final long FROM_AMOUNT = 110_000_000; private static final long tokenId = 1; @@ -42,7 +41,6 @@ public class NoteEncDecryTest extends BaseTest { static { Args.setParam(new String[]{"--output-directory", dbPath()}, "config-localtest.conf"); FROM_ADDRESS = Wallet.getAddressPreFixString() + "a7d8a35b260395c14aa456297662092ba3b76fc0"; - ADDRESS_ONE_PRIVATE_KEY = "7f7f701e94d4f1dd60ee5205e7ea8ee31121427210417b608a6b2e96433549a7"; } /** diff --git a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java index c963a92bb9d..f3ad1f36cd1 100755 --- a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java @@ -25,6 +25,7 @@ import org.tron.common.crypto.ECKey; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; +import org.tron.common.utils.PublicMethod; import org.tron.common.utils.Sha256Hash; import org.tron.common.utils.client.utils.TransactionUtils; import org.tron.common.zksnark.IncrementalMerkleTreeContainer; @@ -124,8 +125,8 @@ public class ShieldedReceiveTest extends BaseTest { static { Args.setParam(new String[]{"--output-directory", dbPath()}, "config-localtest.conf"); - FROM_ADDRESS = Wallet.getAddressPreFixString() + "a7d8a35b260395c14aa456297662092ba3b76fc0"; - ADDRESS_ONE_PRIVATE_KEY = "7f7f701e94d4f1dd60ee5205e7ea8ee31121427210417b608a6b2e96433549a7"; + ADDRESS_ONE_PRIVATE_KEY = PublicMethod.getRandomPrivateKey(); + FROM_ADDRESS = PublicMethod.getHexAddressByPrivateKey(ADDRESS_ONE_PRIVATE_KEY);; } /** diff --git a/framework/src/test/java/org/tron/program/LiteFullNodeToolTest.java b/framework/src/test/java/org/tron/program/LiteFullNodeToolTest.java deleted file mode 100644 index d1da0bf00d8..00000000000 --- a/framework/src/test/java/org/tron/program/LiteFullNodeToolTest.java +++ /dev/null @@ -1,202 +0,0 @@ -package org.tron.program; - -import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; -import java.io.File; -import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; -import lombok.extern.slf4j.Slf4j; -import org.junit.After; -import org.junit.Test; -import org.tron.api.WalletGrpc; -import org.tron.common.application.Application; -import org.tron.common.application.ApplicationFactory; -import org.tron.common.application.TronApplicationContext; -import org.tron.common.config.DbBackupConfig; -import org.tron.common.crypto.ECKey; -import org.tron.common.utils.FileUtil; -import org.tron.common.utils.PublicMethod; -import org.tron.common.utils.Utils; -import org.tron.core.config.DefaultConfig; -import org.tron.core.config.args.Args; -import org.tron.core.services.RpcApiService; -import org.tron.core.services.interfaceOnSolidity.RpcApiServiceOnSolidity; -import org.tron.tool.litefullnode.LiteFullNodeTool; - -@Slf4j -@Deprecated -public class LiteFullNodeToolTest { - - private TronApplicationContext context; - private WalletGrpc.WalletBlockingStub blockingStubFull = null; - private ManagedChannel channelFull; - private Application appTest; - private String databaseDir; - - private static String dbPath = "output_lite_fn"; - - /** - * init logic. - */ - public void startApp() { - context = new TronApplicationContext(DefaultConfig.class); - appTest = ApplicationFactory.create(context); - appTest.addService(context.getBean(RpcApiService.class)); - appTest.addService(context.getBean(RpcApiServiceOnSolidity.class)); - appTest.startup(); - - String fullNode = String.format("%s:%d", "127.0.0.1", - Args.getInstance().getRpcPort()); - channelFull = ManagedChannelBuilder.forTarget(fullNode) - .usePlaintext() - .build(); - blockingStubFull = WalletGrpc.newBlockingStub(channelFull); - } - - /** - * Delete the database when exited. - */ - public static void destroy(String dbPath) { - File f = new File(dbPath); - if (f.exists()) { - if (FileUtil.deleteDir(f)) { - logger.info("Release resources successful."); - } else { - logger.info("Release resources failure."); - } - } - } - - /** - * shutdown the fullNode. - */ - public void shutdown() throws InterruptedException { - if (channelFull != null) { - channelFull.shutdown().awaitTermination(5, TimeUnit.SECONDS); - } - context.close(); - } - - public void init() { - destroy(dbPath); // delete if prev failed - Args.setParam(new String[]{"-d", dbPath, "-w"}, "config-localtest.conf"); - // allow account root - Args.getInstance().setAllowAccountStateRoot(1); - Args.getInstance().setRpcPort(PublicMethod.chooseRandomPort()); - databaseDir = Args.getInstance().getStorage().getDbDirectory(); - // init dbBackupConfig to avoid NPE - Args.getInstance().dbBackupConfig = DbBackupConfig.getInstance(); - } - - @After - public void clear() { - destroy(dbPath); - Args.clearParam(); - dbPath = "output_lite_fn"; - } - - @Test - public void testToolsWithLevelDB() throws InterruptedException { - logger.info("testToolsWithLevelDB start"); - testTools("LEVELDB", 1); - } - - @Test - public void testToolsWithLevelDBV2() throws InterruptedException { - logger.info("testToolsWithLevelDB start"); - testTools("LEVELDB", 2); - } - - @Test - public void testToolsWithRocksDB() throws InterruptedException { - logger.info("testToolsWithRocksDB start"); - testTools("ROCKSDB", 1); - } - - private void testTools(String dbType, int checkpointVersion) - throws InterruptedException { - dbPath = String.format("%s_%s_%d", dbPath, dbType, System.currentTimeMillis()); - init(); - final String[] argsForSnapshot = - new String[]{"-o", "split", "-t", "snapshot", "--fn-data-path", - dbPath + File.separator + databaseDir, "--dataset-path", - dbPath}; - final String[] argsForHistory = - new String[]{"-o", "split", "-t", "history", "--fn-data-path", - dbPath + File.separator + databaseDir, "--dataset-path", - dbPath}; - final String[] argsForMerge = - new String[]{"-o", "merge", "--fn-data-path", dbPath + File.separator + databaseDir, - "--dataset-path", dbPath + File.separator + "history"}; - Args.getInstance().getStorage().setDbEngine(dbType); - Args.getInstance().getStorage().setCheckpointVersion(checkpointVersion); - LiteFullNodeTool.setRecentBlks(3); - // start fullNode - startApp(); - // produce transactions for 18 seconds - generateSomeTransactions(18); - // stop the node - shutdown(); - // delete tran-cache - FileUtil.deleteDir(Paths.get(dbPath, databaseDir, "trans-cache").toFile()); - // generate snapshot - LiteFullNodeTool.main(argsForSnapshot); - // start fullNode - startApp(); - // produce transactions for 6 seconds - generateSomeTransactions(6); - // stop the node - shutdown(); - // generate history - LiteFullNodeTool.main(argsForHistory); - // backup original database to database_bak - File database = new File(Paths.get(dbPath, databaseDir).toString()); - if (!database.renameTo(new File(Paths.get(dbPath, databaseDir + "_bak").toString()))) { - throw new RuntimeException( - String.format("rename %s to %s failed", database.getPath(), - Paths.get(dbPath, databaseDir))); - } - // change snapshot to the new database - File snapshot = new File(Paths.get(dbPath, "snapshot").toString()); - if (!snapshot.renameTo(new File(Paths.get(dbPath, databaseDir).toString()))) { - throw new RuntimeException( - String.format("rename snapshot to %s failed", - Paths.get(dbPath, databaseDir))); - } - // start and validate the snapshot - startApp(); - generateSomeTransactions(6); - // stop the node - shutdown(); - // merge history - LiteFullNodeTool.main(argsForMerge); - // start and validate - startApp(); - generateSomeTransactions(6); - shutdown(); - LiteFullNodeTool.reSetRecentBlks(); - } - - private void generateSomeTransactions(int during) { - during *= 1000; // ms - int runTime = 0; - int sleepOnce = 100; - while (true) { - ECKey ecKey2 = new ECKey(Utils.getRandom()); - byte[] address = ecKey2.getAddress(); - - String sunPri = "cba92a516ea09f620a16ff7ee95ce0df1d56550a8babe9964981a7144c8a784a"; - byte[] sunAddress = PublicMethod.getFinalAddress(sunPri); - PublicMethod.sendcoin(address, 1L, - sunAddress, sunPri, blockingStubFull); - try { - Thread.sleep(sleepOnce); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if ((runTime += sleepOnce) > during) { - return; - } - } - } -} diff --git a/framework/src/test/resources/args-test.conf b/framework/src/test/resources/args-test.conf new file mode 100644 index 00000000000..91913dfe32e --- /dev/null +++ b/framework/src/test/resources/args-test.conf @@ -0,0 +1,224 @@ +net { + // type = mainnet + type = testnet +} + + +storage { + # Directory for storing persistent data + + db.engine = "LEVELDB" + db.directory = "database", + index.directory = "index", + + # You can custom these 14 databases' configs: + + # account, account-index, asset-issue, block, block-index, + # block_KDB, peers, properties, recent-block, trans, + # utxo, votes, witness, witness_schedule. + + # Otherwise, db configs will remain defualt and data will be stored in + # the path of "output-directory" or which is set by "-d" ("--output-directory"). + + # Attention: name is a required field that must be set !!! + default = { + maxOpenFiles = 50 + } + defaultM = { + maxOpenFiles = 500 + } + defaultL = { + maxOpenFiles = 1000 + } + properties = [ + { + name = "account", + path = "storage_directory_test", + createIfMissing = true, + paranoidChecks = true, + verifyChecksums = true, + compressionType = 1, // compressed with snappy + blockSize = 4096, // 4 KB = 4 * 1024 B + writeBufferSize = 10485760, // 10 MB = 10 * 1024 * 1024 B + cacheSize = 10485760, // 10 MB = 10 * 1024 * 1024 B + maxOpenFiles = 100 + }, + { + name = "account-index", + path = "storage_directory_test", + createIfMissing = true, + paranoidChecks = true, + verifyChecksums = true, + compressionType = 1, // compressed with snappy + blockSize = 4096, // 4 KB = 4 * 1024 B + writeBufferSize = 10485760, // 10 MB = 10 * 1024 * 1024 B + cacheSize = 10485760, // 10 MB = 10 * 1024 * 1024 B + maxOpenFiles = 100 + }, + { # only for unit test + name = "test_name", + path = "test_path", + createIfMissing = false, + paranoidChecks = false, + verifyChecksums = false, + compressionType = 1, + blockSize = 2, + writeBufferSize = 3, + cacheSize = 4, + maxOpenFiles = 5 + }, + ] + + needToUpdateAsset = false + +} + +node.discovery = { + enable = true + persist = true + external.ip = "46.168.1.1" +} + +node { + + trustNode = "127.0.0.1:50051" + + listen.port = 18888 + + connection.timeout = 2 + + active = [] + + maxConnections = 30 + minConnections = 8 + minActiveConnections = 3 + + p2p { + version = 43 # 43: testnet; 101: debug + } + + rpc { + port = 50051 + } + +} + +sync { + node.count = 30 +} + +seed.node = { + ip.list = [ + ] +} + +genesis.block = { + # Reserve balance + assets = [ + { + accountName = "Devaccount" + accountType = "AssetIssue" + address = "27d3byPxZXKQWfXX7sJvemJJuv5M65F3vjS" + balance = "10000000000000000" + }, + { + accountName = "Zion" + accountType = "AssetIssue" + address = "27fXgQ46DcjEsZ444tjZPKULcxiUfDrDjqj" + balance = "15000000000000000" + }, + { + accountName = "Sun" + accountType = "AssetIssue" + address = "27SWXcHuQgFf9uv49FknBBBYBaH3DUk4JPx" + balance = "10000000000000000" + }, + { + accountName = "Blackhole" + accountType = "AssetIssue" + address = "27WtBq2KoSy5v8VnVZBZHHJcDuWNiSgjbE3" + balance = "-9223372036854775808" + } + ] + + witnesses = [ + { + address: 27Ssb1WE8FArwJVRRb8Dwy3ssVGuLY8L3S1 + url = "http://Mercury.org", + voteCount = 105 + }, + { + address: 27anh4TDZJGYpsn4BjXzb7uEArNALxwiZZW + url = "http://Venus.org", + voteCount = 104 + }, + { + address: 27Wkfa5iEJtsKAKdDzSmF1b2gDm5s49kvdZ + url = "http://Earth.org", + voteCount = 103 + }, + { + address: 27bqKYX9Bgv7dgTY7xBw5SUHZ8EGaPSikjx + url = "http://Mars.org", + voteCount = 102 + }, + { + address: 27fASUY6qKtsaAEPz6QxhZac2KYVz2ZRTXW + url = "http://Jupiter.org", + voteCount = 101 + }, + { + address: 27Q3RSbiqm59VXcF8shQWHKbyztfso5FwvP + url = "http://Saturn.org", + voteCount = 100 + }, + { + address: 27YkUVSuvCK3K84DbnFnxYUxozpi793PTqZ + url = "http://Uranus.org", + voteCount = 99 + }, + { + address: 27kdTBTDJ16hK3Xqr8PpCuQJmje1b94CDJU + url = "http://Neptune.org", + voteCount = 98 + }, + { + address: 27mw9UpRy7inTMQ5kUzsdTc2QZ6KvtCX4uB + url = "http://Pluto.org", + voteCount = 97 + }, + { + address: 27QzC4PeQZJ2kFMUXiCo4S8dx3VWN5U9xcg + url = "http://Altair.org", + voteCount = 96 + }, + { + address: 27VZHn9PFZwNh7o2EporxmLkpe157iWZVkh + url = "http://AlphaLyrae.org", + voteCount = 95 + } + ] + + timestamp = "0" #2017-8-26 12:00:00 + + parentHash = "0x0000000000000000000000000000000000000000000000000000000000000000" +} + + +localwitness = [ + +] + +block = { + needSyncCheck = true # first node : false, other : true +} + +vm = { + supportConstant = true + minTimeRatio = 0.0 + maxTimeRatio = 5.0 +} +committee = { + allowCreationOfContracts = 1 //mainnet:0 (reset by committee),test:1 + allowOldRewardOpt = 1 +} diff --git a/framework/src/test/resources/config-localtest.conf b/framework/src/test/resources/config-localtest.conf index ff0fe8bf9d9..d7f573fe90e 100644 --- a/framework/src/test/resources/config-localtest.conf +++ b/framework/src/test/resources/config-localtest.conf @@ -57,7 +57,6 @@ storage { node.discovery = { enable = true persist = true - bind.ip = "" external.ip = null } @@ -188,8 +187,6 @@ genesis.block = { accountType = "AssetIssue" address = "TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW" balance = "25000000000000000" - #priKey = D95611A9AF2A2A45359106222ED1AFED48853D9A44DEFF8DC7913F5CBA727366 - #password = 2VYRqa8qKkU1kQYiLtGv7UiFPZpE3v+Nx5E/XLpyc2Y= }, # the account of payment @@ -198,8 +195,6 @@ genesis.block = { accountType = "AssetIssue" address = "TGehVcNhud84JDCGrNHKVz9jEAVKUpbuiv" balance = "10000000000000000" - #priKey = cba92a516ea09f620a16ff7ee95ce0df1d56550a8babe9964981a7144c8a784a - #password = y6kqUW6gn2IKFv9+6Vzg3x1WVQqLq+mWSYGnFEyKeEo= }, # the account of coin burn @@ -208,8 +203,6 @@ genesis.block = { accountType = "AssetIssue" address = "THKrowiEfCe8evdbaBzDDvQjM5DGeB3s3F" balance = "-9223372036854775808" - #priKey = 8E812436A0E3323166E1F0E8BA79E19E217B2C4A53C970D4CCA0CFB1078979DF - #password = joEkNqDjMjFm4fDounnhniF7LEpTyXDUzKDPsQeJed8= } ] @@ -218,27 +211,23 @@ genesis.block = { address: TN3zfjYUmMFK3ZsHSsrdJoNRtGkQmZLBLz url = "http://Test.org", voteCount = 106 - #priKey = f4df789d3210ac881cb900464dd30409453044d2777060a0c391cbdf4c6a4f57 6666 }, // { // address: TPrLL5ckUdMaPNgJYmGv23qtYjBE34aBf8 // url = "http://Mercury.org", // voteCount = 105 - // #priKey = f5583fd20e13073900a513f333ed13db8c9e83e7e3cf37e74adacef96c5afeaa 7777 // }, // { // address: TEZBh76rouEQpB2zqYVopbRXGx7RfyWorT // #address: 27TfVERREG3FeWMHEAQ95tWHG4sb3ANn3Qe // url = "http://Venus.org", // voteCount = 104 - // #priKey = 9f5c5e48bf87cf92017313082e8cf0f58ccfce423097f0fcebf801695fc99bd4 8888 // }, // { // address: TN27wbfCLEN1gP2PZAxHgU3QZrntsLyxdj // #address: 27b8RUuyZnNPFNZGct2bZkNu9MnGWNAdH3Z // url = "http://Earth.org", // voteCount = 103 - // #priKey = 6781f44d9a2083b14fad1702b8e9ba82749162b795e2fc3f136192fc63f80de2 9999 // }, ] diff --git a/framework/src/test/resources/config-test-dbbackup.conf b/framework/src/test/resources/config-test-dbbackup.conf index bb64a87ef69..4f9ddf8d32b 100644 --- a/framework/src/test/resources/config-test-dbbackup.conf +++ b/framework/src/test/resources/config-test-dbbackup.conf @@ -60,7 +60,6 @@ storage { node.discovery = { enable = true persist = true - bind.ip = "" external.ip = null } diff --git a/framework/src/test/resources/config-test-index.conf b/framework/src/test/resources/config-test-index.conf index b9fbf5b2c43..6eb3621e747 100644 --- a/framework/src/test/resources/config-test-index.conf +++ b/framework/src/test/resources/config-test-index.conf @@ -54,7 +54,6 @@ storage { node.discovery = { enable = true persist = true - bind.ip = "" external.ip = null } diff --git a/framework/src/test/resources/config-test-storagetest.conf b/framework/src/test/resources/config-test-storagetest.conf index d2dc27b795a..25127cdab91 100644 --- a/framework/src/test/resources/config-test-storagetest.conf +++ b/framework/src/test/resources/config-test-storagetest.conf @@ -284,4 +284,6 @@ vm = { } committee = { allowCreationOfContracts = 1 //mainnet:0 (reset by committee),test:1 + allowOldRewardOpt = 1 + allowNewRewardAlgorithm = 1 } diff --git a/framework/src/test/resources/config-test.conf b/framework/src/test/resources/config-test.conf index 68a48ca64b2..db24bb2a8a0 100644 --- a/framework/src/test/resources/config-test.conf +++ b/framework/src/test/resources/config-test.conf @@ -69,6 +69,12 @@ storage { # if true, transaction cache initialization will be faster. default false txCache.initOptimization = true + # data root setting, for check data, currently, only reward-vi is used. + + merkleRoot = { + reward-vi = e0ebe2f3243391ed674dff816a07f589a3279420d6d88bc823b6a9d5778337ce + } + } node.discovery = { diff --git a/plugins/README.md b/plugins/README.md index 6807bfbb409..0db6f2e6143 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -66,7 +66,6 @@ DB copy provides a helper which can copy LevelDB or RocksDB data quickly on the java -jar Toolkit.jar db cp output-directory/database /tmp/databse ``` - ## DB Lite DB lite provides lite database, parameters are compatible with previous `LiteFullNodeTool`. @@ -135,3 +134,14 @@ Execute move command. java -jar Toolkit.jar db mv -c main_net_config.conf -d /data/tron/output-directory ``` +## DB Root + +DB root provides a helper which can compute merkle root for tiny db. + +NOTE: large db may GC overhead limit exceeded. + +### Available parameters: + +- ``: Source path for database. Default: output-directory/database +- `--db`: db name. +- `-h | --help`: provide the help info diff --git a/plugins/src/main/java/org/tron/plugins/Db.java b/plugins/src/main/java/org/tron/plugins/Db.java index c67c838a3d6..84654dca934 100644 --- a/plugins/src/main/java/org/tron/plugins/Db.java +++ b/plugins/src/main/java/org/tron/plugins/Db.java @@ -11,7 +11,8 @@ DbArchive.class, DbConvert.class, DbLite.class, - DbCopy.class + DbCopy.class, + DbRoot.class }, commandListHeading = "%nCommands:%n%nThe most commonly used db commands are:%n" ) diff --git a/plugins/src/main/java/org/tron/plugins/DbRoot.java b/plugins/src/main/java/org/tron/plugins/DbRoot.java new file mode 100644 index 00000000000..7c33219e180 --- /dev/null +++ b/plugins/src/main/java/org/tron/plugins/DbRoot.java @@ -0,0 +1,121 @@ +package org.tron.plugins; + +import com.google.common.collect.Streams; +import com.google.common.primitives.Bytes; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import me.tongfei.progressbar.ProgressBar; +import org.rocksdb.RocksDBException; +import org.tron.plugins.utils.MerkleRoot; +import org.tron.plugins.utils.Sha256Hash; +import org.tron.plugins.utils.db.DBInterface; +import org.tron.plugins.utils.db.DBIterator; +import org.tron.plugins.utils.db.DbTool; +import picocli.CommandLine; + +@Slf4j(topic = "db-root") +@CommandLine.Command(name = "root", + description = "compute merkle root for tiny db. NOTE: large db may GC overhead limit exceeded.", + exitCodeListHeading = "Exit Codes:%n", + exitCodeList = { + "0:Successful", + "n:query failed,please check toolkit.log"}) +public class DbRoot implements Callable { + + @CommandLine.Spec + CommandLine.Model.CommandSpec spec; + @CommandLine.Parameters(index = "0", defaultValue = "output-directory/database", + description = "Input path. Default: ${DEFAULT-VALUE}") + private Path db; + + @CommandLine.Option(names = { "--db"}, + description = "db name for show root") + private List dbs; + + @CommandLine.Option(names = {"-h", "--help"}, help = true, description = "display a help message") + private boolean help; + + + @Override + public Integer call() throws Exception { + if (help) { + spec.commandLine().usage(System.out); + return 0; + } + if (!db.toFile().exists()) { + logger.info(" {} does not exist.", db); + spec.commandLine().getErr().println(spec.commandLine().getColorScheme() + .errorText(String.format("%s does not exist.", db))); + return 404; + } + + // remove not exit + if (dbs != null) { + dbs.removeIf(s -> !Paths.get(db.toString(), s).toFile().exists()); + } + + if (dbs == null || dbs.isEmpty()) { + logger.info("Specify at least one exit database: --db dbName."); + spec.commandLine().getErr().println(spec.commandLine().getColorScheme() + .errorText("Specify at least one exit database: --db dbName.")); + return 404; + } + List task = ProgressBar.wrap(dbs.stream(), "root task").parallel() + .map(this::calcMerkleRoot).collect(Collectors.toList()); + task.forEach(this::printInfo); + int code = (int) task.stream().filter(r -> r.code == 1).count(); + if (code > 0) { + spec.commandLine().getErr().println(spec.commandLine().getColorScheme() + .errorText("There are some errors, please check toolkit.log for detail.")); + } + spec.commandLine().getOut().println("root task done."); + return code; + } + + private Ret calcMerkleRoot(String name) { + Ret info = new Ret(); + try (DBInterface database = DbTool.getDB(this.db, name)) { + DBIterator iterator = database.iterator(); + iterator.seekToFirst(); + ArrayList ids = Streams.stream(iterator) + .map(this::getHash) + .collect(Collectors.toCollection(ArrayList::new)); + Sha256Hash root = MerkleRoot.root(ids); + logger.info("db: {},root: {}", database.getName(), root); + info.code = 0; + info.msg = String.format("db: %s,root: %s", database.getName(), root); + } catch (RocksDBException | IOException e) { + logger.error("calc db {} fail", name, e); + info.code = 1; + info.msg = String.format("db: %s,fail: %s", + name, e.getMessage()); + } + return info; + } + + private Sha256Hash getHash(Map.Entry entry) { + return Sha256Hash.of(true, + Bytes.concat(entry.getKey(), entry.getValue())); + } + + private void printInfo(Ret ret) { + if (ret.code == 0) { + spec.commandLine().getOut().println(ret.msg); + } else { + spec.commandLine().getErr().println(spec.commandLine().getColorScheme() + .errorText(ret.msg)); + } + } + + private static class Ret { + private int code; + private String msg; + } +} diff --git a/plugins/src/main/java/org/tron/plugins/utils/MerkleRoot.java b/plugins/src/main/java/org/tron/plugins/utils/MerkleRoot.java new file mode 100644 index 00000000000..055f5dcdee0 --- /dev/null +++ b/plugins/src/main/java/org/tron/plugins/utils/MerkleRoot.java @@ -0,0 +1,68 @@ +package org.tron.plugins.utils; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import lombok.Getter; + +public class MerkleRoot { + + private MerkleRoot() { + + } + + public static Sha256Hash root(List hashList) { + List leaves = createLeaves(hashList); + while (leaves.size() > 1) { + leaves = createParentLeaves(leaves); + } + return leaves.isEmpty() ? Sha256Hash.ZERO_HASH : leaves.get(0).hash; + } + + private static List createParentLeaves(List leaves) { + int step = 2; + int len = leaves.size(); + return IntStream.iterate(0, i -> i + step) + .limit(len) + .filter(i -> i < len) + .mapToObj(i -> { + Leaf right = i + 1 < len ? leaves.get(i + 1) : null; + return createLeaf(leaves.get(i), right); + }).collect(Collectors.toList()); + } + + private static List createLeaves(List hashList) { + int step = 2; + int len = hashList.size(); + return IntStream.iterate(0, i -> i + step) + .limit(len) + .filter(i -> i < len) + .mapToObj(i -> { + Leaf right = i + 1 < len ? createLeaf(hashList.get(i + 1)) : null; + return createLeaf(createLeaf(hashList.get(i)), right); + }).collect(Collectors.toList()); + } + + private static Leaf createLeaf(Leaf left, Leaf right) { + Leaf leaf = new Leaf(); + leaf.hash = right == null ? left.hash : computeHash(left.hash, right.hash); + return leaf; + } + + private static Leaf createLeaf(Sha256Hash hash) { + Leaf leaf = new Leaf(); + leaf.hash = hash; + return leaf; + } + + private static Sha256Hash computeHash(Sha256Hash leftHash, Sha256Hash rightHash) { + return Sha256Hash.of(true, + leftHash.getByteString().concat(rightHash.getByteString()).toByteArray()); + } + + @Getter + private static class Leaf { + + private Sha256Hash hash; + } +} diff --git a/plugins/src/main/java/org/tron/plugins/utils/Sha256Hash.java b/plugins/src/main/java/org/tron/plugins/utils/Sha256Hash.java index 5fe80601b66..67e6e64ea79 100644 --- a/plugins/src/main/java/org/tron/plugins/utils/Sha256Hash.java +++ b/plugins/src/main/java/org/tron/plugins/utils/Sha256Hash.java @@ -21,6 +21,7 @@ import com.google.common.io.ByteStreams; import com.google.common.primitives.Ints; +import com.google.protobuf.ByteString; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -39,7 +40,7 @@ public class Sha256Hash implements Serializable, Comparable { public static final int LENGTH = 32; // bytes - + public static final Sha256Hash ZERO_HASH = wrap(new byte[LENGTH]); private final byte[] bytes; /** @@ -184,6 +185,13 @@ public byte[] getBytes() { return bytes; } + /** + * For pb return ByteString. + */ + public ByteString getByteString() { + return ByteString.copyFrom(bytes); + } + @Override public int compareTo(final Sha256Hash other) { diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/DBInterface.java b/plugins/src/main/java/org/tron/plugins/utils/db/DBInterface.java index b0f7c58c587..513e021c83c 100644 --- a/plugins/src/main/java/org/tron/plugins/utils/db/DBInterface.java +++ b/plugins/src/main/java/org/tron/plugins/utils/db/DBInterface.java @@ -18,4 +18,6 @@ public interface DBInterface extends Closeable { void close() throws IOException; + String getName(); + } diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/DbTool.java b/plugins/src/main/java/org/tron/plugins/utils/db/DbTool.java index 6e8df4c59fb..429025e8f8b 100644 --- a/plugins/src/main/java/org/tron/plugins/utils/db/DbTool.java +++ b/plugins/src/main/java/org/tron/plugins/utils/db/DbTool.java @@ -23,7 +23,7 @@ public class DbTool { private static final Map dbMap = Maps.newConcurrentMap(); - enum DbType { + public enum DbType { LevelDB, RocksDB } @@ -83,11 +83,11 @@ public static DBInterface getDB(String sourceDir, String dbName, DbType type) DBInterface db; switch (type) { case LevelDB: - db = openLevelDb(path); + db = openLevelDb(path, dbName); dbMap.put(path.toString(), db); break; case RocksDB: - db = openRocksDb(path); + db = openRocksDb(path, dbName); dbMap.put(path.toString(), db); break; default: @@ -114,9 +114,9 @@ public static DBInterface getDB(Path sourceDir, String dbName) DbType type = getDbType(sourceDir.toString(), dbName); switch (type) { case LevelDB: - return openLevelDb(path); + return openLevelDb(path, dbName); case RocksDB: - return openRocksDb(path); + return openRocksDb(path, dbName); default: throw new IllegalStateException("Unexpected value: " + type); } @@ -175,12 +175,12 @@ private static DbType getDbType(String sourceDir, String dbName) { } } - private static LevelDBImpl openLevelDb(Path db) throws IOException { - return new LevelDBImpl(DBUtils.newLevelDb(db)); + private static LevelDBImpl openLevelDb(Path db, String name) throws IOException { + return new LevelDBImpl(DBUtils.newLevelDb(db), name); } - private static RocksDBImpl openRocksDb(Path db) throws RocksDBException { - return new RocksDBImpl(DBUtils.newRocksDb(db)); + private static RocksDBImpl openRocksDb(Path db, String name) throws RocksDBException { + return new RocksDBImpl(DBUtils.newRocksDb(db), name); } diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/LevelDBImpl.java b/plugins/src/main/java/org/tron/plugins/utils/db/LevelDBImpl.java index 3a89e77af40..511f4dfd5b4 100644 --- a/plugins/src/main/java/org/tron/plugins/utils/db/LevelDBImpl.java +++ b/plugins/src/main/java/org/tron/plugins/utils/db/LevelDBImpl.java @@ -2,6 +2,7 @@ import com.google.common.collect.Streams; import java.io.IOException; +import lombok.Getter; import org.iq80.leveldb.DB; import org.iq80.leveldb.ReadOptions; @@ -10,8 +11,12 @@ public class LevelDBImpl implements DBInterface { private DB leveldb; - public LevelDBImpl(DB leveldb) { + @Getter + private final String name; + + public LevelDBImpl(DB leveldb, String name) { this.leveldb = leveldb; + this.name = name; } @Override diff --git a/plugins/src/main/java/org/tron/plugins/utils/db/RocksDBImpl.java b/plugins/src/main/java/org/tron/plugins/utils/db/RocksDBImpl.java index 88b25b0413f..50957bbe61b 100644 --- a/plugins/src/main/java/org/tron/plugins/utils/db/RocksDBImpl.java +++ b/plugins/src/main/java/org/tron/plugins/utils/db/RocksDBImpl.java @@ -1,6 +1,7 @@ package org.tron.plugins.utils.db; import java.io.IOException; +import lombok.Getter; import org.rocksdb.RocksDBException; import org.rocksdb.RocksIterator; @@ -8,8 +9,12 @@ public class RocksDBImpl implements DBInterface { private org.rocksdb.RocksDB rocksDB; - public RocksDBImpl(org.rocksdb.RocksDB rocksDB) { + @Getter + private final String name; + + public RocksDBImpl(org.rocksdb.RocksDB rocksDB, String name) { this.rocksDB = rocksDB; + this.name = name; } @Override diff --git a/plugins/src/test/java/org/tron/plugins/DbRootTest.java b/plugins/src/test/java/org/tron/plugins/DbRootTest.java new file mode 100644 index 00000000000..b86688f77d5 --- /dev/null +++ b/plugins/src/test/java/org/tron/plugins/DbRootTest.java @@ -0,0 +1,82 @@ +package org.tron.plugins; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.rocksdb.RocksDBException; +import org.tron.plugins.utils.DBUtils; +import org.tron.plugins.utils.db.DBInterface; +import org.tron.plugins.utils.db.DbTool; +import org.tron.plugins.utils.db.LevelDBImpl; +import picocli.CommandLine; + +@Slf4j +public class DbRootTest { + + @Rule + public final TemporaryFolder folder = new TemporaryFolder(); + + CommandLine cli = new CommandLine(new Toolkit()); + + private static final String NORMAL_DB = "normal"; + private static final String EMPTY_DB = "empty"; + private static final String ERROR_DB = "error"; + + @Test + public void testRoot() throws IOException, RocksDBException { + + File file = folder.newFolder(); + + File database = Paths.get(file.getPath(),"database").toFile(); + Assert.assertTrue(database.mkdirs()); + + + try (DBInterface normal = DbTool.getDB(database.toString(), NORMAL_DB, DbTool.DbType.LevelDB); + DBInterface empty = DbTool.getDB(database.toString(), EMPTY_DB, DbTool.DbType.RocksDB)) { + for (int i = 0; i < 10; i++) { + normal.put(("" + i).getBytes(), (NORMAL_DB + "-" + i).getBytes()); + } + } + + String[] args = new String[] {"db", "root", database.toString(), + "--db", NORMAL_DB, "--db", EMPTY_DB}; + Assert.assertEquals(0, cli.execute(args)); + args = new String[] {"db", "root", database.toString(), + "--db", NORMAL_DB}; + Assert.assertEquals(0, cli.execute(args)); + args = new String[] {"db", "root", database.toString(), + "--db", EMPTY_DB}; + Assert.assertEquals(0, cli.execute(args)); + + try (DBInterface errorDb = new LevelDBImpl( + DBUtils.newLevelDb(Paths.get(database.toString(), ERROR_DB)), ERROR_DB)) { + for (int i = 0; i < 10; i++) { + errorDb.put(("" + i).getBytes(), (ERROR_DB + "-" + i).getBytes()); + } + args = new String[] {"db", "root", database.toString(), "--db", ERROR_DB}; + Assert.assertEquals(1, cli.execute(args)); + } + + } + + @Test + public void testHelp() { + String[] args = new String[] {"db", "root", "-h"}; + Assert.assertEquals(0, cli.execute(args)); + } + + @Test + public void testEmpty() throws IOException { + File file = folder.newFolder(); + File database = Paths.get(file.getPath(),"database").toFile(); + String[] args = new String[] {"db", "root", database.toString()}; + Assert.assertEquals(404, cli.execute(args)); + Assert.assertTrue(database.mkdirs()); + Assert.assertEquals(404, cli.execute(args)); + } +}