diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml new file mode 100644 index 00000000000..12aa565e15e --- /dev/null +++ b/.github/workflows/dependency-check.yml @@ -0,0 +1,60 @@ +name: "Dependency Check" + +on: + push: + branches: [ 'develop', 'master', 'release_**', 'feat/state_root_sync' ] + pull_request: + branches: [ 'develop', "release_**" , 'feat/state_root_sync' ] + schedule: + - cron: '25 6 * * *' + workflow_dispatch: + +jobs: + dependency-check: + name: Dependency Check + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Cache ODC data + uses: actions/cache@v3 + with: + path: ~/.dependency-check/data + key: ${{ runner.os }}-odc-data-${{ hashFiles('**/build.gradle') }} + restore-keys: | + ${{ runner.os }}-odc-data- + + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'temurin' + + - name: Gradlew build + run: ./gradlew --no-daemon -S -Dorg.gradle.dependency.verification=off -Dorg.gradle.warning.mode=none build -x test + + - name: Dependency Check + uses: dependency-check/Dependency-Check_Action@1.1.0 + env: + # actions/setup-java@v1 changes JAVA_HOME, so it needs to be reset to match the depcheck image + JAVA_HOME: /opt/jdk + with: + project: 'java-tron' + path: '.' + format: 'HTML' + out: 'reports' + args: > + --failOnCVSS 7 + --enableRetired + - name: Generate timestamp + run: echo "BUILD_TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")" >> $GITHUB_ENV + - name: Get Repository Name + run: echo "REPO_NAME=$(echo '${{ github.repository }}' | cut -d'/' -f2)" >> $GITHUB_ENV + - name: Upload report + if: always() + uses: actions/upload-artifact@v4 + with: + name: dependency-check-${{ env.REPO_NAME }}-${{ env.BUILD_TIMESTAMP }} + path: ${{github.workspace}}/reports diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml new file mode 100644 index 00000000000..d8a5c8a416e --- /dev/null +++ b/.github/workflows/dependency-submission.yml @@ -0,0 +1,29 @@ +name: Dependency Submission + +on: + push: + branches: [ 'develop', 'master', 'release_**', 'feat/state_root_sync' ] + pull_request: + branches: [ 'develop', "release_**" , 'feat/state_root_sync' ] + + workflow_dispatch: + +permissions: + contents: write + +jobs: + dependency-submission: + runs-on: ubuntu-24.04-arm + + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 17 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@v4 \ No newline at end of file diff --git a/actuator/build.gradle b/actuator/build.gradle index 1143dc83618..923d0b7d2c5 100644 --- a/actuator/build.gradle +++ b/actuator/build.gradle @@ -33,8 +33,8 @@ test { jacocoTestReport { reports { - xml.enabled = true - html.enabled = true + xml.required.set(true) + html.required.set(true) } getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec")) afterEvaluate { diff --git a/actuator/src/main/java/org/tron/core/utils/TransactionRegister.java b/actuator/src/main/java/org/tron/core/utils/TransactionRegister.java index 867ea06bfe2..effacc378a7 100644 --- a/actuator/src/main/java/org/tron/core/utils/TransactionRegister.java +++ b/actuator/src/main/java/org/tron/core/utils/TransactionRegister.java @@ -1,24 +1,49 @@ package org.tron.core.utils; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import lombok.extern.slf4j.Slf4j; import org.reflections.Reflections; import org.tron.core.actuator.AbstractActuator; +import org.tron.core.exception.TronError; @Slf4j(topic = "TransactionRegister") public class TransactionRegister { + private static final AtomicBoolean REGISTERED = new AtomicBoolean(false); + private static final String PACKAGE_NAME = "org.tron.core.actuator"; + public static void registerActuator() { - Reflections reflections = new Reflections("org.tron"); - Set> subTypes = reflections - .getSubTypesOf(AbstractActuator.class); - for (Class _class : subTypes) { - try { - _class.newInstance(); - } catch (Exception e) { - logger.error("{} contract actuator register fail!", _class, e); + if (REGISTERED.get()) { + logger.info("Actuator already registered."); + return; + } + + synchronized (TransactionRegister.class) { + if (REGISTERED.get()) { + logger.info("Actuator already registered."); + return; + } + + logger.info("Register actuator start."); + Reflections reflections = new Reflections(PACKAGE_NAME); + Set> subTypes = reflections + .getSubTypesOf(AbstractActuator.class); + + for (Class clazz : subTypes) { + try { + logger.debug("Registering actuator: {} start", clazz.getName()); + clazz.getDeclaredConstructor().newInstance(); + logger.debug("Registering actuator: {} done", clazz.getName()); + } catch (Exception e) { + throw new TronError(clazz.getName() + ": " + + (e.getCause() == null ? e.getMessage() : e.getCause().getMessage()), + e, TronError.ErrCode.ACTUATOR_REGISTER); + } } + + REGISTERED.set(true); + logger.info("Register actuator done, total {}.", subTypes.size()); } } - } diff --git a/build.gradle b/build.gradle index 12a0622db99..9e55c23f4f8 100644 --- a/build.gradle +++ b/build.gradle @@ -40,14 +40,11 @@ ext.archInfo = [ VMOptions: isArm64 ? "${rootDir}/gradle/jdk17/java-tron.vmoptions" : "${rootDir}/gradle/java-tron.vmoptions" ] -if (!archInfo.java.is(archInfo.requires.JavaVersion)) { - throw new GradleException("Java ${archInfo.requires.JavaVersion} is required for ${archInfo.name}. Detected version ${archInfo.java}") -} - println "Building for architecture: ${archInfo.name}, Java version: ${archInfo.java}" subprojects { + apply plugin: "java" apply plugin: "jacoco" apply plugin: "maven-publish" @@ -108,14 +105,15 @@ subprojects { testImplementation "org.mockito:mockito-inline:4.11.0" } - task sourcesJar(type: Jar, dependsOn: classes) { - classifier = "sources" + tasks.register('sourcesJar', Jar) { + dependsOn classes + archiveClassifier.set('sources') from sourceSets.main.allSource duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates } - tasks.withType(AbstractArchiveTask) { + tasks.withType(AbstractArchiveTask).configureEach { preserveFileTimestamps = false reproducibleFileOrder = true duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates @@ -149,7 +147,7 @@ subprojects { } } -task copyToParent(type: Copy) { +tasks.register('copyToParent', Copy) { into "$buildDir/libs" subprojects { from tasks.withType(Jar) diff --git a/chainbase/build.gradle b/chainbase/build.gradle index 1a07ff95fa5..002cfa7acc5 100644 --- a/chainbase/build.gradle +++ b/chainbase/build.gradle @@ -42,8 +42,8 @@ test { jacocoTestReport { dependsOn(processResources) // explicit_dependency reports { - xml.enabled = true - html.enabled = true + xml.required.set(true) + html.required.set(true) } getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec")) afterEvaluate { diff --git a/chainbase/src/main/java/org/tron/core/ChainBaseManager.java b/chainbase/src/main/java/org/tron/core/ChainBaseManager.java index 21f0bac8d77..8346c05cc4b 100644 --- a/chainbase/src/main/java/org/tron/core/ChainBaseManager.java +++ b/chainbase/src/main/java/org/tron/core/ChainBaseManager.java @@ -59,6 +59,7 @@ import org.tron.core.store.NullifierStore; import org.tron.core.store.ProposalStore; import org.tron.core.store.SectionBloomStore; +import org.tron.core.store.StateRootStore; import org.tron.core.store.StorageRowStore; import org.tron.core.store.TransactionHistoryStore; import org.tron.core.store.TransactionRetStore; @@ -233,6 +234,10 @@ public class ChainBaseManager { @Getter private SectionBloomStore sectionBloomStore; + @Autowired + @Getter + private StateRootStore stateRootStore; + @Autowired private DbStatService dbStatService; @@ -315,7 +320,7 @@ public boolean containBlockInMainChain(BlockId blockId) { } } - public BlockCapsule getKhaosDbHead(){ + public BlockCapsule getKhaosDbHead() { return this.khaosDb.getHead(); } diff --git a/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java index 01ff7fb5365..a5053ccd2d6 100755 --- a/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java +++ b/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java @@ -335,6 +335,16 @@ public String toString() { return toStringBuff.toString(); } + public Sha256Hash getStateRoot() { + ByteString stateRoot = this.block.getBlockHeader().getStateRoot(); + return stateRoot.isEmpty() ? Sha256Hash.ZERO_HASH : Sha256Hash.wrap(stateRoot); + } + + public void clearStateRoot() { + BlockHeader blockHeader = this.block.getBlockHeader().toBuilder().clearStateRoot().build(); + this.block = this.block.toBuilder().setBlockHeader(blockHeader).build(); + } + public static class BlockId extends Sha256Hash { private long num; diff --git a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java index 40762568c82..f9ccf9a289c 100644 --- a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java +++ b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java @@ -60,6 +60,10 @@ public DbSourceInter getDbSource() { } public void updateByBatch(Map rows) { + this.updateByBatch(rows, writeOptions); + } + + public void updateByBatch(Map rows, WriteOptionsWrapper writeOptions) { this.dbSource.updateByBatch(rows, writeOptions); } diff --git a/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java b/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java index e20490d93c0..624bda23796 100644 --- a/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java +++ b/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java @@ -25,6 +25,7 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.tron.common.error.TronDBException; import org.tron.common.es.ExecutorServiceManager; @@ -49,7 +50,7 @@ public class SnapshotManager implements RevokingDatabase { public static final int DEFAULT_MIN_FLUSH_COUNT = 1; private static final int DEFAULT_STACK_MAX_SIZE = 256; - private static final long ONE_MINUTE_MILLS = 60*1000L; + private static final long ONE_MINUTE_MILLS = 60 * 1000L; private static final String CHECKPOINT_V2_DIR = "checkpoint"; @Getter private List dbs = new ArrayList<>(); @@ -79,6 +80,9 @@ public class SnapshotManager implements RevokingDatabase { @Getter private CheckTmpStore checkTmpStore; + @Autowired + private ObjectFactory checkPointV2Store; + @Setter private volatile int maxFlushCount = DEFAULT_MIN_FLUSH_COUNT; @@ -385,8 +389,7 @@ public void createCheckpoint() { } } if (isV2Open()) { - String dbName = String.valueOf(System.currentTimeMillis()); - checkPointStore = getCheckpointDB(dbName); + checkPointStore = checkPointV2Store.getObject(); } else { checkPointStore = checkTmpStore; } @@ -405,7 +408,7 @@ public void createCheckpoint() { } private TronDatabase getCheckpointDB(String dbName) { - return new CheckPointV2Store(CHECKPOINT_V2_DIR+"/"+dbName); + return new CheckPointV2Store(CHECKPOINT_V2_DIR, dbName); } public List getCheckpointList() { @@ -431,7 +434,7 @@ private void deleteCheckpoint() { for (Map.Entry e : checkTmpStore.getDbSource()) { hmap.put(e.getKey(), null); } - if (hmap.size() != 0) { + if (!hmap.isEmpty()) { checkTmpStore.getDbSource().updateByBatch(hmap); } } catch (Exception e) { @@ -450,10 +453,10 @@ private void pruneCheckpoint() { if (cpList.size() < 3) { return; } - long latestTimestamp = Long.parseLong(cpList.get(cpList.size()-1)); - for (String cp: cpList.subList(0, cpList.size()-3)) { + long latestTimestamp = Long.parseLong(cpList.get(cpList.size() - 1)); + for (String cp: cpList.subList(0, cpList.size() - 3)) { long timestamp = Long.parseLong(cp); - if (latestTimestamp - timestamp <= ONE_MINUTE_MILLS*2) { + if (latestTimestamp - timestamp <= ONE_MINUTE_MILLS * 2) { break; } String checkpointPath = Paths.get(StorageUtils.getOutputDirectoryByDbName(CHECKPOINT_V2_DIR), diff --git a/chainbase/src/main/java/org/tron/core/service/RootHashService.java b/chainbase/src/main/java/org/tron/core/service/RootHashService.java new file mode 100644 index 00000000000..f0ff7ba059a --- /dev/null +++ b/chainbase/src/main/java/org/tron/core/service/RootHashService.java @@ -0,0 +1,161 @@ +package org.tron.core.service; + +import com.google.common.collect.Sets; +import com.google.common.collect.Streams; +import com.google.common.primitives.Bytes; +import com.google.common.primitives.Ints; +import com.google.protobuf.ByteString; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.tron.common.context.GlobalContext; +import org.tron.common.error.TronDBException; +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.db.TronDatabase; +import org.tron.core.db2.common.Value; +import org.tron.core.store.AccountAssetStore; +import org.tron.core.store.CorruptedCheckpointStore; +import org.tron.protos.Protocol; + +@Slf4j(topic = "DB") +@Component +public class RootHashService { + + private static final byte[] HEADER_KEY = "latest_block_header_number".getBytes(); + + private static Optional corruptedCheckpointStore = Optional.empty(); + private static AccountAssetStore assetStore; + private static final List stateDbs = Arrays.asList( + "account", "asset-issue-v2", + "code", "contract", "contract-state", "storage-row", + "delegation", "DelegatedResource", "DelegatedResourceAccountIndex", + "exchange-v2", + "market_account", "market_order", "market_pair_price_to_order", "market_pair_to_price", + "properties", "proposal", + "votes", "witness", "witness_schedule" + ); + private static final byte[] CURRENT_SHUFFLED_WITNESSES = "current_shuffled_witnesses".getBytes(); + private static final String FORK_PREFIX = "FORK_VERSION_"; + private static final String DONE_SUFFIX = "_DONE"; + private static final String ACCOUNT_VOTE_SUFFIX = "-account-vote"; + private static final Set ignoredProperties = Sets.newHashSet( + "VOTE_REWARD_RATE", "SINGLE_REPEAT", "NON_EXISTENT_ACCOUNT_TRANSFER_MIN", + "ALLOW_TVM_ASSET_ISSUE", "ALLOW_TVM_STAKE", + "MAX_VOTE_NUMBER", "MAX_FROZEN_NUMBER", "MAINTENANCE_TIME_INTERVAL", + "LATEST_SOLIDIFIED_BLOCK_NUM", "BLOCK_NET_USAGE", + "VERSION_NUMBER", + "SHIELDED_TRANSACTION_FEE", + "BLOCK_FILLED_SLOTS_INDEX", "BLOCK_FILLED_SLOTS_NUMBER", "BLOCK_FILLED_SLOTS"); + + @Autowired + public RootHashService(@Autowired CorruptedCheckpointStore corruptedCheckpointStore, + @Autowired AccountAssetStore assetStore) { + RootHashService.corruptedCheckpointStore = Optional.ofNullable(corruptedCheckpointStore); + RootHashService.assetStore = assetStore; + } + + public static Pair, Sha256Hash> getRootHash(Map rows) { + try { + Map preparedStateData = preparedStateData(rows); + AtomicReference> height = new AtomicReference<>(Optional.empty()); + List ids = Streams.stream(preparedStateData.entrySet()).parallel().map(entry -> { + if (Arrays.equals(HEADER_KEY, entry.getKey())) { + height.set(Optional.of(ByteArray.toLong(entry.getValue()))); + } + return getHash(entry); + }).sorted().collect(Collectors.toList()); + Sha256Hash actual = MerkleRoot.root(ids); + long num = height.get().orElseThrow(() -> new TronDBException("blockNum is null")); + Optional expected = GlobalContext.popBlockHash(num); + if (expected.isPresent() && !Objects.equals(expected.get(), actual)) { + corruptedCheckpointStore.ifPresent(TronDatabase::reset); + corruptedCheckpointStore.ifPresent(store -> store.updateByBatch(rows)); + throw new TronDBException(String.format( + "Root hash mismatch for blockNum: %s, expected: %s, actual: %s", + num, expected, actual)); + } + return new Pair<>(height.get(), actual); + } catch (IOException e) { + throw new TronDBException(e); + } + } + + private static Map preparedStateData(Map rows) + throws IOException { + Map preparedStateData = new HashMap<>(rows.size()); + for (Map.Entry e : rows.entrySet()) { + byte[] key = e.getKey(); + String dbName = simpleDecode(key); + if (!stateDbs.contains(dbName)) { + continue; + } + byte[] realKey = Arrays.copyOfRange(key, dbName.getBytes().length + Integer.BYTES, + key.length); + if ("witness_schedule".equals(dbName) && Arrays.equals(realKey, CURRENT_SHUFFLED_WITNESSES)) { + continue; + } + if ("properties".equals(dbName)) { + String keyStr = new String(realKey); + if (ignoredProperties.contains(keyStr) + || keyStr.startsWith(FORK_PREFIX) || keyStr.endsWith(DONE_SUFFIX)) { + continue; + } + } + byte[] value = e.getValue(); + byte[] realValue = value.length == 1 ? null : Arrays.copyOfRange(value, 1, value.length); + if (realValue != null) { + if ("witness".equals(dbName)) { + realValue = Protocol.Witness.parseFrom(realValue) + .toBuilder().clearTotalMissed() + .build().toByteArray(); // ignore totalMissed + } + if ("account".equals(dbName)) { + Protocol.Account account = Protocol.Account.parseFrom(realValue); + Map assets = new TreeMap<>(assetStore.getAllAssets(account)); + assets.entrySet().removeIf(entry -> entry.getValue() == 0); + realValue = account.toBuilder().clearAsset().clearAssetV2().clearAssetOptimized() + .putAllAssetV2(assets) + .build().toByteArray(); + } + if ("delegation".equals(dbName) && new String(key).endsWith(ACCOUNT_VOTE_SUFFIX)) { + Protocol.Account account = Protocol.Account.parseFrom(realValue); + realValue = Protocol.Account.newBuilder().addAllVotes(account.getVotesList()) + .build().toByteArray(); + } + } + if (realValue != null) { + preparedStateData.put(realKey, realValue); + } else { + if (Value.Operator.DELETE.getValue() != value[0]) { + preparedStateData.put(realKey, ByteString.EMPTY.toByteArray()); + } + } + } + return preparedStateData; + } + + private static String simpleDecode(byte[] bytes) { + byte[] lengthBytes = Arrays.copyOf(bytes, Integer.BYTES); + int length = Ints.fromByteArray(lengthBytes); + byte[] value = Arrays.copyOfRange(bytes, Integer.BYTES, Integer.BYTES + length); + return new String(value); + } + + private static Sha256Hash getHash(Map.Entry entry) { + return Sha256Hash.of(true, Bytes.concat(entry.getKey(), entry.getValue())); + } +} diff --git a/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java b/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java index f027bd02664..5403bdd7262 100644 --- a/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java +++ b/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java @@ -2,25 +2,42 @@ import com.google.protobuf.InvalidProtocolBufferException; import java.util.Map; +import java.util.Optional; import java.util.Spliterator; import java.util.function.Consumer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; import org.tron.common.parameter.CommonParameter; import org.tron.common.storage.WriteOptionsWrapper; +import org.tron.common.utils.Pair; +import org.tron.common.utils.Sha256Hash; import org.tron.core.db.TronDatabase; import org.tron.core.exception.BadItemException; import org.tron.core.exception.ItemNotFoundException; +import org.tron.core.service.RootHashService; @Slf4j(topic = "DB") +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE/*, proxyMode = ScopedProxyMode.TARGET_CLASS*/) public class CheckPointV2Store extends TronDatabase { private final WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance() .sync(CommonParameter.getInstance().getStorage().isCheckpointSync()); @Autowired - public CheckPointV2Store(String dbPath) { - super(dbPath); + private StateRootStore stateRootStore; + + @Autowired + public CheckPointV2Store(@Value("checkpoint") String dbPath) { + this(dbPath, String.valueOf(System.currentTimeMillis())); + } + + public CheckPointV2Store(String dbPath, String name) { + super(dbPath + "/" + name); } @Override @@ -67,15 +84,22 @@ public void updateByBatch(Map rows) { */ @Override public void close() { - logger.debug("******** Begin to close {}. ********", getName()); + logger.debug("******** Begin to close {}. ********", getDbName()); try { writeOptions.close(); dbSource.closeDB(); } catch (Exception e) { - logger.warn("Failed to close {}.", getName(), e); + logger.warn("Failed to close {}.", getDbName(), e); } finally { - logger.debug("******** End to close {}. ********", getName()); + logger.debug("******** End to close {}. ********", getDbName()); } } + @Override + public void updateByBatch(Map rows, WriteOptionsWrapper writeOptions) { + Pair, Sha256Hash> ret = RootHashService.getRootHash(rows); + super.updateByBatch(rows, writeOptions); + ret.getKey().ifPresent(height -> stateRootStore.put(height, ret.getValue())); + } + } diff --git a/chainbase/src/main/java/org/tron/core/store/CheckTmpStore.java b/chainbase/src/main/java/org/tron/core/store/CheckTmpStore.java index 09f60c83898..bceab7c4630 100644 --- a/chainbase/src/main/java/org/tron/core/store/CheckTmpStore.java +++ b/chainbase/src/main/java/org/tron/core/store/CheckTmpStore.java @@ -1,18 +1,27 @@ package org.tron.core.store; import com.google.protobuf.InvalidProtocolBufferException; +import java.util.Map; +import java.util.Optional; import java.util.Spliterator; import java.util.function.Consumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; +import org.tron.common.storage.WriteOptionsWrapper; +import org.tron.common.utils.Pair; +import org.tron.common.utils.Sha256Hash; import org.tron.core.db.TronDatabase; import org.tron.core.exception.BadItemException; import org.tron.core.exception.ItemNotFoundException; +import org.tron.core.service.RootHashService; @Component public class CheckTmpStore extends TronDatabase { + @Autowired + private StateRootStore stateRootStore; + @Autowired public CheckTmpStore(ApplicationContext ctx) { super("tmp"); @@ -46,4 +55,11 @@ public void forEach(Consumer action) { public Spliterator spliterator() { return null; } -} \ No newline at end of file + + @Override + public void updateByBatch(Map rows, WriteOptionsWrapper writeOptions) { + Pair, Sha256Hash> ret = RootHashService.getRootHash(rows); + super.updateByBatch(rows, writeOptions); + ret.getKey().ifPresent(height -> stateRootStore.put(height, ret.getValue())); + } +} diff --git a/chainbase/src/main/java/org/tron/core/store/CorruptedCheckpointStore.java b/chainbase/src/main/java/org/tron/core/store/CorruptedCheckpointStore.java new file mode 100644 index 00000000000..efab9345871 --- /dev/null +++ b/chainbase/src/main/java/org/tron/core/store/CorruptedCheckpointStore.java @@ -0,0 +1,49 @@ +package org.tron.core.store; + +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.Spliterator; +import java.util.function.Consumer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.tron.core.db.TronDatabase; +import org.tron.core.exception.BadItemException; +import org.tron.core.exception.ItemNotFoundException; + +@Component +public class CorruptedCheckpointStore extends TronDatabase { + + @Autowired + public CorruptedCheckpointStore(@Value("corrupted-checkpoint") String dbName) { + super(dbName); + } + + @Override + public void put(byte[] key, byte[] item) { + } + + @Override + public void delete(byte[] key) { + } + + @Override + public byte[] get(byte[] key) + throws InvalidProtocolBufferException, ItemNotFoundException, BadItemException { + return null; + } + + @Override + public boolean has(byte[] key) { + return false; + } + + @Override + public void forEach(Consumer action) { + + } + + @Override + public Spliterator spliterator() { + return null; + } +} diff --git a/chainbase/src/main/java/org/tron/core/store/StateRootStore.java b/chainbase/src/main/java/org/tron/core/store/StateRootStore.java new file mode 100644 index 00000000000..8a5cfbe586d --- /dev/null +++ b/chainbase/src/main/java/org/tron/core/store/StateRootStore.java @@ -0,0 +1,54 @@ +package org.tron.core.store; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.tron.common.utils.ByteArray; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.db.TronDatabase; +import org.tron.core.db.common.iterator.DBIterator; + +@Slf4j(topic = "DB") +@Component +public class StateRootStore extends TronDatabase { + + @Autowired + private StateRootStore(@Value("state-root") String dbName) { + super(dbName); + } + + @Override + public byte[] get(byte[] key) { + return dbSource.getData(key); + } + + public byte[] get(long key) { + return dbSource.getData(ByteArray.fromLong(key)); + } + + @Override + public void put(byte[] key, byte[] item) { + dbSource.putData(key, item); + } + + public void put(long key, Sha256Hash root) { + logger.info("block: {}, stateRoot: {}", key, root); + this.put(ByteArray.fromLong(key), root.getBytes()); + } + + @Override + public void delete(byte[] key) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean has(byte[] key) { + return dbSource.getData(key) != null; + } + + @Override + public DBIterator iterator() { + return ((DBIterator) dbSource.iterator()); + } +} diff --git a/common/build.gradle b/common/build.gradle index 8ea91ecd5b1..572fb9e599e 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,11 +1,5 @@ -plugins { - id 'java' -} - version '1.0.0' -sourceCompatibility = 1.8 - dependencies { api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.18.3' // https://github.com/FasterXML/jackson-databind/issues/3627 @@ -45,8 +39,8 @@ dependencies { jacocoTestReport { reports { - xml.enabled = true - html.enabled = true + xml.required.set(true) + html.required.set(true) } getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec")) afterEvaluate { diff --git a/common/src/main/java/org/tron/common/context/GlobalContext.java b/common/src/main/java/org/tron/common/context/GlobalContext.java new file mode 100644 index 00000000000..91bc262ea46 --- /dev/null +++ b/common/src/main/java/org/tron/common/context/GlobalContext.java @@ -0,0 +1,47 @@ +package org.tron.common.context; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.tron.common.utils.Sha256Hash; + +public class GlobalContext { + + private static final ThreadLocal HEADER = new ThreadLocal<>(); + private static final ThreadLocal> BLOCK_HASHES = + ThreadLocal.withInitial(() -> Collections.synchronizedMap(new HashMap<>())); + + private GlobalContext() { + } + + public static Optional getHeader() { + return Optional.ofNullable(HEADER.get()); + } + + public static void setHeader(long header) { + HEADER.set(header); + } + + public static void removeHeader() { + HEADER.remove(); + } + + public static void putBlockHash(long blockNumber, Sha256Hash blockHash) { + BLOCK_HASHES.get().put(blockNumber, blockHash); + } + + public static Optional popBlockHash(long blockNumber) { + return Optional.ofNullable(BLOCK_HASHES.get().remove(blockNumber)); + } + + public static void clearBlockHashes() { + BLOCK_HASHES.get().clear(); + } + + public static void clear() { + removeHeader(); + clearBlockHashes(); + } + +} diff --git a/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java b/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java index 779a8edf75d..b009b8ea66e 100644 --- a/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java +++ b/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java @@ -84,6 +84,15 @@ public static void shutdownAndAwaitTermination(ExecutorService pool, String name logger.info("Pool {} shutdown done", name); } + public static void shutdownNow(ExecutorService pool, String name) { + if (pool == null) { + return; + } + logger.info("Pool {} shutdown...", name); + pool.shutdownNow(); + logger.info("Pool {} shutdown done", name); + } + public static Future submit(ExecutorService es, Runnable task) { return es.submit(() -> { try { diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index 0583962f266..b81fe18f477 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -753,6 +753,11 @@ public class CommonParameter { @Setter public long allowTvmBlob; + @Getter + @Setter + @Parameter(names = {"--no-check-root"}, description = "disable check state root") + public boolean noCheckRootHash = false; + private static double calcMaxTimeRatio() { //return max(2.0, min(5.0, 5 * 4.0 / max(Runtime.getRuntime().availableProcessors(), 1))); return 5.0; 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 655b6b779fe..79a238b5049 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 @@ -29,7 +29,6 @@ import org.apache.commons.lang3.StringUtils; import org.iq80.leveldb.CompressionType; import org.iq80.leveldb.Options; -import org.tron.common.arch.Arch; import org.tron.common.cache.CacheStrategies; import org.tron.common.cache.CacheType; import org.tron.common.utils.DbOptionalsUtils; @@ -175,13 +174,7 @@ public class Storage { private final Map dbRoots = Maps.newConcurrentMap(); public static String getDbEngineFromConfig(final Config config) { - if (Arch.isArm64()) { - // if is arm64 but config is leveldb, should throw exception? - logger.warn("Arm64 architecture detected, using RocksDB as db engine, ignore config."); - return ROCKS_DB_ENGINE; - } - return config.hasPath(DB_ENGINE_CONFIG_KEY) - ? config.getString(DB_ENGINE_CONFIG_KEY) : DEFAULT_DB_ENGINE; + return ROCKS_DB_ENGINE; } public static Boolean getDbVersionSyncFromConfig(final Config config) { diff --git a/common/src/main/java/org/tron/core/exception/TronError.java b/common/src/main/java/org/tron/core/exception/TronError.java index f407c6dfe3c..4ee7cdae916 100644 --- a/common/src/main/java/org/tron/core/exception/TronError.java +++ b/common/src/main/java/org/tron/core/exception/TronError.java @@ -49,6 +49,7 @@ public enum ErrCode { RATE_LIMITER_INIT(1), SOLID_NODE_INIT(0), PARAMETER_INIT(1), + ACTUATOR_REGISTER(1), JDK_VERSION(1); private final int code; diff --git a/consensus/build.gradle b/consensus/build.gradle index 04cc24be5fd..fae3d7cd245 100644 --- a/consensus/build.gradle +++ b/consensus/build.gradle @@ -32,8 +32,8 @@ test { jacocoTestReport { reports { - xml.enabled = true - html.enabled = true + xml.required.set(true) + html.required.set(true) } getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec")) afterEvaluate { diff --git a/crypto/build.gradle b/crypto/build.gradle index 82814af49e6..cacf872b5fa 100644 --- a/crypto/build.gradle +++ b/crypto/build.gradle @@ -1,11 +1,5 @@ -plugins { - id 'java' -} - version '1.0.0' -sourceCompatibility = 1.8 - repositories { mavenCentral() } @@ -16,8 +10,8 @@ dependencies { jacocoTestReport { reports { - xml.enabled = true - html.enabled = true + xml.required.set(true) + html.required.set(true) } getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec")) afterEvaluate { diff --git a/framework/build.gradle b/framework/build.gradle index 59d070e066d..d6f17d012d0 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -22,7 +22,7 @@ configurations { } -configurations.getByName('checkstyleConfig') { +configurations.named('checkstyleConfig') { transitive = false } @@ -30,7 +30,7 @@ static def isWindows() { return org.gradle.internal.os.OperatingSystem.current().isWindows() } -task version(type: Exec) { +tasks.register('version', Exec) { commandLine 'bash', '-c', '../ver.sh' } @@ -47,7 +47,7 @@ dependencies { // end http // https://mvnrepository.com/artifact/com.github.briandilley.jsonrpc4j/jsonrpc4j - implementation group: 'com.github.briandilley.jsonrpc4j', name: 'jsonrpc4j', version: '1.6' + implementation group: 'com.github.briandilley.jsonrpc4j', name: 'jsonrpc4j', version: '1.7' // https://mvnrepository.com/artifact/javax.portlet/portlet-api compileOnly group: 'javax.portlet', name: 'portlet-api', version: '3.0.1' @@ -76,7 +76,7 @@ checkstyleMain { source = 'src/main/java' } -task lint(type: Checkstyle) { +tasks.register('lint', Checkstyle) { // Cleaning the old log because of the creation of the new ones (not sure if totaly needed) delete fileTree(dir: "${project.rootDir}/app/build/reports") source 'src' @@ -136,27 +136,27 @@ test { exclude 'org/tron/core/ShieldedTRC20BuilderTest.class' exclude 'org/tron/common/runtime/vm/WithdrawRewardTest.class' } - maxHeapSize = "1024m" + maxHeapSize = "512m" + maxParallelForks = Math.max(1, Math.min(2, Runtime.runtime.availableProcessors())) doFirst { // Restart the JVM after every 100 tests to avoid memory leaks and ensure test isolation forkEvery = 100 - jvmArgs "-XX:MetaspaceSize=128m","-XX:MaxMetaspaceSize=256m", "-XX:+UseG1GC" } } jacocoTestReport { reports { - xml.enabled true - csv.enabled false + xml.required.set(true) + csv.required.set(false) html.destination file("${buildDir}/jacocoHtml") } getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec")) } def binaryRelease(taskName, jarName, mainClass) { - return tasks.create("${taskName}", Jar) { - baseName = jarName - version = null + return tasks.register("${taskName}", Jar) { + archiveBaseName.set(jarName) + archiveVersion.set('') from(sourceSets.main.output) { include "/**" } @@ -175,7 +175,9 @@ def binaryRelease(taskName, jarName, mainClass) { exclude "META-INF/*.SF" exclude "META-INF/*.DSA" exclude "META-INF/*.RSA" - + // for service SPI loader for dnsjava + // see https://issues.apache.org/jira/browse/HADOOP-19288 + exclude "META-INF/services/java.net.spi.InetAddressResolverProvider" manifest { attributes "Main-Class": "${mainClass}" } @@ -231,7 +233,7 @@ if (releaseBinary == 'true') { } } -task copyToParent(type: Copy) { +tasks.register('copyToParent', Copy) { into "../build/distributions" from "$buildDir/distributions" include "*.zip" 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 46695986c1f..c931ac4f5d6 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 @@ -55,6 +55,7 @@ import org.tron.common.args.GenesisBlock; import org.tron.common.args.Witness; import org.tron.common.config.DbBackupConfig; +import org.tron.common.context.GlobalContext; import org.tron.common.cron.CronExpression; import org.tron.common.logsfilter.EventPluginConfig; import org.tron.common.logsfilter.FilterQuery; @@ -256,6 +257,7 @@ public static void clearParam() { PARAMETER.allowTvmBlob = 0; PARAMETER.rpcMaxRstStream = 0; PARAMETER.rpcSecondsPerWindow = 0; + GlobalContext.removeHeader(); } /** 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 cd1a61c01fe..9f553f33043 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -49,7 +49,6 @@ import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.tron.api.GrpcAPI; import org.tron.api.GrpcAPI.TransactionInfoList; import org.tron.common.args.GenesisBlock; import org.tron.common.bloom.Bloom; @@ -1033,23 +1032,6 @@ public void eraseBlock() { } } - public void pushVerifiedBlock(BlockCapsule block) throws ContractValidateException, - ContractExeException, ValidateSignatureException, AccountResourceInsufficientException, - TransactionExpirationException, TooBigTransactionException, DupTransactionException, - TaposException, ValidateScheduleException, ReceiptCheckErrException, - VMIllegalException, TooBigTransactionResultException, UnLinkedBlockException, - NonCommonBlockException, BadNumberBlockException, BadBlockException, ZksnarkException, - EventBloomException { - block.generatedByMyself = true; - long start = System.currentTimeMillis(); - pushBlock(block); - logger.info("Push block cost: {} ms, blockNum: {}, blockHash: {}, trx count: {}.", - System.currentTimeMillis() - start, - block.getNum(), - block.getBlockId(), - block.getTransactions().size()); - } - private void applyBlock(BlockCapsule block) throws ContractValidateException, ContractExeException, ValidateSignatureException, AccountResourceInsufficientException, TransactionExpirationException, TooBigTransactionException, DupTransactionException, diff --git a/framework/src/main/java/org/tron/core/net/TronNetDelegate.java b/framework/src/main/java/org/tron/core/net/TronNetDelegate.java index 100bad179bf..bfdd038d97a 100644 --- a/framework/src/main/java/org/tron/core/net/TronNetDelegate.java +++ b/framework/src/main/java/org/tron/core/net/TronNetDelegate.java @@ -8,6 +8,7 @@ import io.prometheus.client.Histogram; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; import javax.annotation.PostConstruct; @@ -19,7 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.backup.socket.BackupServer; +import org.tron.common.context.GlobalContext; import org.tron.common.overlay.message.Message; +import org.tron.common.parameter.CommonParameter; import org.tron.common.prometheus.MetricKeys; import org.tron.common.prometheus.MetricLabels; import org.tron.common.prometheus.Metrics; @@ -307,6 +310,23 @@ public void processBlock(BlockCapsule block, boolean isSync) throws P2pException } } + public void pushVerifiedBlock(BlockCapsule block) throws P2pException { + block.generatedByMyself = true; + Sha256Hash stateRoot = block.getStateRoot(); + if (!CommonParameter.getInstance().isNoCheckRootHash() + && !Objects.equals(Sha256Hash.ZERO_HASH, stateRoot)) { + GlobalContext.putBlockHash(block.getNum(), stateRoot); + } + // clear stateRoot for block + block.clearStateRoot(); + try { + GlobalContext.setHeader(block.getNum()); + processBlock(block, true); + } finally { + GlobalContext.removeHeader(); + } + } + public void pushTransaction(TransactionCapsule trx) throws P2pException { try { trx.setTime(System.currentTimeMillis()); 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 63e7ba03fc7..927ee46bf35 100755 --- a/framework/src/main/java/org/tron/core/services/RpcApiService.java +++ b/framework/src/main/java/org/tron/core/services/RpcApiService.java @@ -317,6 +317,7 @@ public void getNowBlock(EmptyMessage request, StreamObserver responseObse Block block = null; try { block = chainBaseManager.getHead().getInstance(); + block = addStateRoot(block); } catch (StoreException e) { logger.error(e.getMessage()); } @@ -329,6 +330,7 @@ public void getBlockByNum(NumberMessage request, StreamObserver responseO Block block = null; try { block = chainBaseManager.getBlockByNum(request.getNum()).getInstance(); + block = addStateRoot(block); } catch (StoreException e) { logger.error(e.getMessage()); } @@ -346,6 +348,16 @@ public void getDynamicProperties(EmptyMessage request, responseObserver.onNext(dynamicProperties); responseObserver.onCompleted(); } + + private Block addStateRoot(Block block) { + byte[] stateRoot = chainBaseManager.getStateRootStore().get( + block.getBlockHeader().getRawData().getNumber()); + if (stateRoot != null) { + block = block.toBuilder().setBlockHeader(block.getBlockHeader().toBuilder() + .setStateRoot(ByteString.copyFrom(stateRoot))).build(); + } + return block; + } } /** diff --git a/framework/src/main/java/org/tron/program/SolidityNode.java b/framework/src/main/java/org/tron/program/SolidityNode.java index 3367141e2a5..f916fbdb04d 100644 --- a/framework/src/main/java/org/tron/program/SolidityNode.java +++ b/framework/src/main/java/org/tron/program/SolidityNode.java @@ -2,50 +2,86 @@ import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; +import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.client.DatabaseGrpcClient; +import org.tron.common.es.ExecutorServiceManager; import org.tron.common.parameter.CommonParameter; import org.tron.common.prometheus.Metrics; import org.tron.core.ChainBaseManager; import org.tron.core.capsule.BlockCapsule; import org.tron.core.config.DefaultConfig; +import org.tron.core.config.args.Args; import org.tron.core.db.Manager; import org.tron.core.exception.TronError; +import org.tron.core.net.TronNetDelegate; import org.tron.protos.Protocol.Block; @Slf4j(topic = "app") +@Component +@Conditional(value = {SolidityNode.SolidityCondition.class}) public class SolidityNode { + @Autowired private Manager dbManager; + @Autowired private ChainBaseManager chainBaseManager; + @Autowired + private TronNetDelegate tronNetDelegate; + private DatabaseGrpcClient databaseGrpcClient; - private AtomicLong ID = new AtomicLong(); + private final AtomicLong ID = new AtomicLong(); - private AtomicLong remoteBlockNum = new AtomicLong(); + private final AtomicLong remoteBlockNum = new AtomicLong(); - private LinkedBlockingDeque blockQueue = new LinkedBlockingDeque<>(100); + private final LinkedBlockingDeque blockQueue = new LinkedBlockingDeque<>(100); - private int exceptionSleepTime = 1000; + private final int exceptionSleepTime = 1000; private volatile boolean flag = true; - public SolidityNode(Manager dbManager) { - this.dbManager = dbManager; - this.chainBaseManager = dbManager.getChainBaseManager(); + private final String getBlockName = "get-block"; + private final String processBlockName = "process-block"; + + private ExecutorService getBlockExecutor; + + private ExecutorService processBlockExecutor; + + @PostConstruct + private void init() { resolveCompatibilityIssueIfUsingFullNodeDatabase(); ID.set(chainBaseManager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum()); databaseGrpcClient = new DatabaseGrpcClient(CommonParameter.getInstance().getTrustNodeAddr()); - remoteBlockNum.set(getLastSolidityBlockNum()); + getBlockExecutor = ExecutorServiceManager.newSingleThreadExecutor(getBlockName); + processBlockExecutor = ExecutorServiceManager.newSingleThreadExecutor(processBlockName); + } + + @PreDestroy + private void shutdown() { + flag = false; + ExecutorServiceManager.shutdownNow(getBlockExecutor, getBlockName); + ExecutorServiceManager.shutdownNow(processBlockExecutor, processBlockName); + if (databaseGrpcClient != null) { + databaseGrpcClient.shutdown(); + } } /** @@ -70,15 +106,16 @@ public static void start() { Application appT = ApplicationFactory.create(context); context.registerShutdownHook(); appT.startup(); - SolidityNode node = new SolidityNode(appT.getDbManager()); + SolidityNode node = context.getBean(SolidityNode.class); node.run(); appT.blockUntilShutdown(); } private void run() { try { - new Thread(this::getBlock).start(); - new Thread(this::processBlock).start(); + remoteBlockNum.set(getLastSolidityBlockNum()); + getBlockExecutor.submit(this::getBlock); + processBlockExecutor.submit(this::processBlock); logger.info("Success to start solid node, ID: {}, remoteBlockNum: {}.", ID.get(), remoteBlockNum); } catch (Exception e) { @@ -123,7 +160,7 @@ private void loopProcessBlock(Block block) { while (flag) { long blockNum = block.getBlockHeader().getRawData().getNumber(); try { - dbManager.pushVerifiedBlock(new BlockCapsule(block)); + tronNetDelegate.pushVerifiedBlock(new BlockCapsule(block)); chainBaseManager.getDynamicPropertiesStore().saveLatestSolidifiedBlockNum(blockNum); logger .info("Success to process block: {}, blockQueueSize: {}.", blockNum, blockQueue.size()); @@ -151,7 +188,8 @@ private Block getBlockByNum(long blockNum) { sleep(exceptionSleepTime); } } catch (Exception e) { - logger.error("Failed to get block: {}, reason: {}.", blockNum, e.getMessage()); + logger.error("Failed to get block: {}, reason: {}, trustNode: {}.", + blockNum, e.getMessage(), CommonParameter.getInstance().getTrustNodeAddr()); sleep(exceptionSleepTime); } } @@ -166,8 +204,8 @@ private long getLastSolidityBlockNum() { blockNum, remoteBlockNum, System.currentTimeMillis() - time); return blockNum; } catch (Exception e) { - logger.error("Failed to get last solid blockNum: {}, reason: {}.", remoteBlockNum.get(), - e.getMessage()); + logger.error("Failed to get last solid blockNum: {}, reason: {}, trustNode: {}.", + remoteBlockNum.get(), e.getMessage(), CommonParameter.getInstance().getTrustNodeAddr()); sleep(exceptionSleepTime); } } @@ -193,4 +231,12 @@ private void resolveCompatibilityIssueIfUsingFullNodeDatabase() { chainBaseManager.getDynamicPropertiesStore().saveLatestSolidifiedBlockNum(headBlockNum); } } + + + static class SolidityCondition implements Condition { + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return Args.getInstance().isSolidityNode(); + } + } } \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/db/ManagerTest.java b/framework/src/test/java/org/tron/core/db/ManagerTest.java index fc3b5c18265..0e28dbf616c 100755 --- a/framework/src/test/java/org/tron/core/db/ManagerTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerTest.java @@ -20,6 +20,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -139,7 +140,7 @@ public void init() throws IOException { chainManager = dbManager.getChainBaseManager(); localWitnesses = new LocalWitnesses(); - localWitnesses.setPrivateKeys(Arrays.asList(privateKey)); + localWitnesses.setPrivateKeys(Collections.singletonList(privateKey)); localWitnesses.initWitnessAccountAddress(null, true); Args.setLocalWitnesses(localWitnesses); @@ -333,7 +334,6 @@ public void transactionTest() { Assert.assertTrue(e instanceof TaposException); } try { - dbManager.pushVerifiedBlock(chainManager.getHead()); dbManager.getBlockChainHashesOnFork(chainManager.getHeadBlockId()); } catch (Exception e) { Assert.assertTrue(e instanceof TaposException); @@ -848,16 +848,16 @@ public void getVerifyTxsTest() { list.add(t1.getInstance()); BlockCapsule capsule = new BlockCapsule(0, ByteString.EMPTY, 0, list); List txs = dbManager.getVerifyTxs(capsule); - Assert.assertEquals(txs.size(), 1); + Assert.assertEquals(1, txs.size()); dbManager.getPendingTransactions().add(t1); txs = dbManager.getVerifyTxs(capsule); - Assert.assertEquals(txs.size(), 0); + Assert.assertEquals(0, txs.size()); list.add(t2.getInstance()); capsule = new BlockCapsule(0, ByteString.EMPTY, 0, list); txs = dbManager.getVerifyTxs(capsule); - Assert.assertEquals(txs.size(), 1); + Assert.assertEquals(1, txs.size()); dbManager.getPendingTransactions().add(t3); txs = dbManager.getVerifyTxs(capsule); @@ -868,7 +868,7 @@ public void getVerifyTxsTest() { dbManager.getPendingTransactions().add(t1); dbManager.getPendingTransactions().add(t2); txs = dbManager.getVerifyTxs(capsule); - Assert.assertEquals(txs.size(), 0); + Assert.assertEquals(0, txs.size()); dbManager.getPendingTransactions().clear(); Transaction t1Bak = t1.getInstance().toBuilder() @@ -876,7 +876,7 @@ public void getVerifyTxsTest() { dbManager.getPendingTransactions().add(new TransactionCapsule(t1Bak)); txs = dbManager.getVerifyTxs(capsule); Assert.assertEquals(t1.getTransactionId(), new TransactionCapsule(t1Bak).getTransactionId()); - Assert.assertEquals(txs.size(), 2); + Assert.assertEquals(2, txs.size()); dbManager.getPendingTransactions().clear(); list.clear(); @@ -890,7 +890,7 @@ public void getVerifyTxsTest() { new TransactionCapsule(t2Bak).getTransactionId()); dbManager.getPendingTransactions().add(new TransactionCapsule(t2Bak)); txs = dbManager.getVerifyTxs(capsule); - Assert.assertEquals(txs.size(), 1); + Assert.assertEquals(1, txs.size()); } @Test @@ -1018,7 +1018,7 @@ public void switchBack() Args.setParam(new String[]{}, Constant.TEST_CONF); long size = chainManager.getBlockStore().size(); System.out.print("block store size:" + size + "\n"); - String key = PublicMethod.getRandomPrivateKey();; + String key = PublicMethod.getRandomPrivateKey(); byte[] privateKey = ByteArray.fromHexString(key); final ECKey ecKey = ECKey.fromPrivate(privateKey); byte[] address = ecKey.getAddress(); diff --git a/framework/src/test/java/org/tron/core/db/api/AssetUpdateHelperTest.java b/framework/src/test/java/org/tron/core/db/api/AssetUpdateHelperTest.java index d1edd92c109..eb0eb6a7d0e 100644 --- a/framework/src/test/java/org/tron/core/db/api/AssetUpdateHelperTest.java +++ b/framework/src/test/java/org/tron/core/db/api/AssetUpdateHelperTest.java @@ -28,7 +28,6 @@ public class AssetUpdateHelperTest extends BaseTest { static { Args.setParam(new String[]{"-d", dbPath()}, "config-test-index.conf"); - Args.getInstance().setSolidityNode(true); } @Before diff --git a/framework/src/test/java/org/tron/core/db2/SnapshotManagerTest.java b/framework/src/test/java/org/tron/core/db2/SnapshotManagerTest.java index d6fd319e6a0..8c57ae09e62 100644 --- a/framework/src/test/java/org/tron/core/db2/SnapshotManagerTest.java +++ b/framework/src/test/java/org/tron/core/db2/SnapshotManagerTest.java @@ -18,8 +18,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.tron.common.application.Application; -import org.tron.common.application.ApplicationFactory; +import org.junit.rules.TestName; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.Sha256Hash; import org.tron.core.Constant; @@ -30,8 +29,6 @@ import org.tron.core.db2.SnapshotRootTest.ProtoCapsuleTest; import org.tron.core.db2.core.Chainbase; import org.tron.core.db2.core.SnapshotManager; -import org.tron.core.exception.BadItemException; -import org.tron.core.exception.ItemNotFoundException; import org.tron.core.exception.TronError; @Slf4j @@ -39,39 +36,37 @@ public class SnapshotManagerTest { private SnapshotManager revokingDatabase; private TronApplicationContext context; - private Application appT; private TestRevokingTronStore tronDatabase; @Rule - public final TemporaryFolder temporaryFolder = new TemporaryFolder(); - + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public TestName name = new TestName(); @Before public void init() throws IOException { Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); context = new TronApplicationContext(DefaultConfig.class); - appT = ApplicationFactory.create(context); revokingDatabase = context.getBean(SnapshotManager.class); revokingDatabase.enable(); - tronDatabase = new TestRevokingTronStore("testSnapshotManager-test"); - revokingDatabase.add(tronDatabase.getRevokingDB()); } @After public void removeDb() { - Args.clearParam(); - context.destroy(); tronDatabase.close(); + Args.clearParam(); + context.close(); } @Test - public synchronized void testRefresh() - throws BadItemException, ItemNotFoundException { + public synchronized void testRefresh() { + tronDatabase = new TestRevokingTronStore(name.getMethodName()); + revokingDatabase.add(tronDatabase.getRevokingDB()); while (revokingDatabase.size() != 0) { revokingDatabase.pop(); } - revokingDatabase.setMaxFlushCount(0); + revokingDatabase.setMaxFlushCount(1); revokingDatabase.setUnChecked(false); revokingDatabase.setMaxSize(5); List dbList = revokingDatabase.getDbs(); @@ -79,6 +74,7 @@ public synchronized void testRefresh() .map(db -> Maps.immutableEntry(db.getDbName(), db)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); ProtoCapsuleTest protoCapsule = new ProtoCapsuleTest("refresh".getBytes()); + dbMap.get("properties").put("latest_block_header_number".getBytes(), Longs.toByteArray(0)); for (int i = 1; i < 11; i++) { ProtoCapsuleTest testProtoCapsule = new ProtoCapsuleTest(("refresh" + i).getBytes()); try (ISession tmpSession = revokingDatabase.buildSession()) { @@ -86,6 +82,7 @@ public synchronized void testRefresh() BlockCapsule blockCapsule = new BlockCapsule(i, Sha256Hash.ZERO_HASH, System.currentTimeMillis(), ByteString.EMPTY); dbMap.get("block").put(Longs.toByteArray(i), blockCapsule.getData()); + dbMap.get("properties").put("latest_block_header_number".getBytes(), Longs.toByteArray(i)); tmpSession.commit(); } } @@ -97,6 +94,8 @@ public synchronized void testRefresh() @Test public synchronized void testClose() { + tronDatabase = new TestRevokingTronStore(name.getMethodName()); + revokingDatabase.add(tronDatabase.getRevokingDB()); while (revokingDatabase.size() != 0) { revokingDatabase.pop(); } @@ -111,13 +110,13 @@ public synchronized void testClose() { tronDatabase.put(protoCapsule.getData(), testProtoCapsule); } } - Assert.assertEquals(null, - tronDatabase.get(protoCapsule.getData())); + Assert.assertNull(tronDatabase.get(protoCapsule.getData())); } @Test public void testCheckError() { + tronDatabase = new TestRevokingTronStore(name.getMethodName()); SnapshotManager manager = spy(new SnapshotManager("")); when(manager.getCheckpointList()).thenReturn(Arrays.asList("check1", "check2")); TronError thrown = Assert.assertThrows(TronError.class, manager::check); @@ -126,6 +125,7 @@ public void testCheckError() { @Test public void testFlushError() { + tronDatabase = new TestRevokingTronStore(name.getMethodName()); SnapshotManager manager = spy(new SnapshotManager("")); manager.setUnChecked(false); when(manager.getCheckpointList()).thenReturn(Arrays.asList("check1", "check2")); diff --git a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java new file mode 100644 index 00000000000..e8fa4b1568f --- /dev/null +++ b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java @@ -0,0 +1,161 @@ +package org.tron.core.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedConstruction; +import org.mockito.junit.MockitoJUnitRunner; +import org.reflections.Reflections; +import org.tron.core.actuator.AbstractActuator; +import org.tron.core.actuator.TransferActuator; +import org.tron.core.config.args.Args; +import org.tron.core.exception.TronError; + +@RunWith(MockitoJUnitRunner.class) +public class TransactionRegisterTest { + + @Before + public void init() throws Exception { + Args.getInstance().setActuatorSet(new HashSet<>()); + resetRegisteredField(); + } + + @After + public void destroy() { + Args.clearParam(); + } + + private void resetRegisteredField() throws Exception { + Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED"); + registeredField.setAccessible(true); + AtomicBoolean registered = (AtomicBoolean) registeredField.get(null); + registered.set(false); + } + + @Test + public void testAlreadyRegisteredSkipRegistration() throws Exception { + + TransactionRegister.registerActuator(); + + Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED"); + registeredField.setAccessible(true); + AtomicBoolean registered = (AtomicBoolean) registeredField.get(null); + assertTrue("First registration should be completed", registered.get()); + + TransactionRegister.registerActuator(); + assertTrue("Registration should still be true", registered.get()); + } + + @Test + public void testConcurrentAccessThreadSafe() throws Exception { + final int threadCount = 5; + Thread[] threads = new Thread[threadCount]; + final AtomicBoolean testPassed = new AtomicBoolean(true); + + for (int i = 0; i < threadCount; i++) { + threads[i] = new Thread(() -> { + try { + TransactionRegister.registerActuator(); + } catch (Exception e) { + testPassed.set(false); + } + }); + } + + for (Thread thread : threads) { + thread.start(); + } + + for (Thread thread : threads) { + thread.join(); + } + + assertTrue("All threads should complete successfully", testPassed.get()); + + Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED"); + registeredField.setAccessible(true); + AtomicBoolean registered = (AtomicBoolean) registeredField.get(null); + assertTrue("Registration should be completed", registered.get()); + } + + @Test + public void testDoubleCheckLockingAtomicBoolean() throws Exception { + Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED"); + registeredField.setAccessible(true); + AtomicBoolean registered = (AtomicBoolean) registeredField.get(null); + + assertFalse("Initial registration state should be false", registered.get()); + + TransactionRegister.registerActuator(); + assertTrue("After first call, should be registered", registered.get()); + + TransactionRegister.registerActuator(); + assertTrue("After second call, should still be registered", registered.get()); + } + + @Test + public void testSynchronizationBlock() throws Exception { + final AtomicBoolean completedRegistration = new AtomicBoolean(false); + + Thread registrationThread = new Thread(() -> { + TransactionRegister.registerActuator(); + completedRegistration.set(true); + + }); + + registrationThread.start(); + registrationThread.join(); + + assertTrue("Registration should have completed", completedRegistration.get()); + + Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED"); + registeredField.setAccessible(true); + AtomicBoolean registered = (AtomicBoolean) registeredField.get(null); + assertTrue("Should be registered after completion", registered.get()); + } + + @Test + public void testMultipleCallsConsistency() throws Exception { + Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED"); + registeredField.setAccessible(true); + AtomicBoolean registered = (AtomicBoolean) registeredField.get(null); + + assertFalse("Should start unregistered", registered.get()); + + TransactionRegister.registerActuator(); + + assertTrue("Should be registered after first call", registered.get()); + + for (int i = 0; i < 5; i++) { + TransactionRegister.registerActuator(); + assertTrue("Should remain registered after call " + (i + 2), registered.get()); + } + } + + @Test + public void testThrowsTronError() { + try (MockedConstruction ignored = mockConstruction(Reflections.class, + (mock, context) -> when(mock.getSubTypesOf(AbstractActuator.class)) + .thenReturn(Collections.singleton(TransferActuator.class))); + MockedConstruction ignored1 = mockConstruction(TransferActuator.class, + (mock, context) -> { + throw new RuntimeException("boom"); + })) { + TronError error = assertThrows(TronError.class, TransactionRegister::registerActuator); + assertEquals(TronError.ErrCode.ACTUATOR_REGISTER, error.getErrCode()); + assertTrue(error.getMessage().contains("TransferActuator")); + } + } +} diff --git a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java index 5d403b54f90..4545ba9d630 100644 --- a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java @@ -29,6 +29,7 @@ import org.junit.Ignore; import org.junit.Test; import org.tron.common.BaseTest; +import org.tron.common.context.GlobalContext; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.common.zksnark.IncrementalMerkleTreeContainer; @@ -86,6 +87,7 @@ public static void init() { ); Args.getInstance().setAllowShieldedTransactionApi(true); ZksnarkInitService.librustzcashInitZksnarkParams(); + GlobalContext.setHeader(1); } private static int randomInt(int minInt, int maxInt) { diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index 943c73cb9ed..ef477bca821 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -38,6 +38,8 @@ public class SolidityNodeTest extends BaseTest { Args.setParam(new String[] {"-d", dbPath(), "--solidity"}, Constant.TEST_CONF); Args.getInstance().setRpcPort(rpcPort); Args.getInstance().setSolidityHttpPort(solidityHttpPort); + Args.getInstance().setTrustNodeAddr(String.format("%s:%d", Constant.LOCAL_HOST, + Args.getInstance().getRpcPort())); } @Test diff --git a/framework/src/test/resources/logback-test.xml b/framework/src/test/resources/logback-test.xml index 9cf4a04062f..318816f57da 100644 --- a/framework/src/test/resources/logback-test.xml +++ b/framework/src/test/resources/logback-test.xml @@ -7,13 +7,15 @@ %d{HH:mm:ss.SSS} %p [%c{1}] %m%n - INFO + ERROR ./logs/tron-test.log + 8192 + true diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 4d0bf1013d6..2df626b4875 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -588,6 +588,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b..1b33c55baab 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3994438e229..d4081da476b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 83f2acfdc31..23d15a93670 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,80 +15,115 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,92 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 24467a141f7..5eed7ee8452 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,10 +27,14 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -51,48 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/platform/build.gradle b/platform/build.gradle index a94aad3cf17..8245adb110b 100644 --- a/platform/build.gradle +++ b/platform/build.gradle @@ -1,17 +1,8 @@ description = "platform – a distributed consensus arithmetic for blockchain." -sourceSets { - main { - java.srcDirs = rootProject.archInfo.sourceSets.main.java.srcDirs - } - test { - java.srcDirs = rootProject.archInfo.sourceSets.test.java.srcDirs - } -} - dependencies { - api group: 'org.fusesource.leveldbjni', name: 'leveldbjni-all', version: '1.8' - api group: 'org.rocksdb', name: 'rocksdbjni', version: "${rootProject.archInfo.requires.RocksdbVersion}" + api group: 'com.halibobor', name: 'leveldbjni-all', version: '1.18.4' + api group: 'org.rocksdb', name: 'rocksdbjni', version: "9.7.4" api group: 'commons-io', name: 'commons-io', version: '2.18.0' api 'io.github.tronprotocol:zksnark-java-sdk:1.0.0' exclude(group: 'commons-io', module: 'commons-io') } diff --git a/platform/src/main/java/common/org/tron/common/arch/Arch.java b/platform/src/main/java/org/tron/common/arch/Arch.java similarity index 100% rename from platform/src/main/java/common/org/tron/common/arch/Arch.java rename to platform/src/main/java/org/tron/common/arch/Arch.java diff --git a/platform/src/main/java/arm/org/tron/common/math/MathWrapper.java b/platform/src/main/java/org/tron/common/math/MathWrapper.java similarity index 100% rename from platform/src/main/java/arm/org/tron/common/math/MathWrapper.java rename to platform/src/main/java/org/tron/common/math/MathWrapper.java diff --git a/platform/src/main/java/common/org/tron/common/utils/MarketComparator.java b/platform/src/main/java/org/tron/common/utils/MarketComparator.java similarity index 100% rename from platform/src/main/java/common/org/tron/common/utils/MarketComparator.java rename to platform/src/main/java/org/tron/common/utils/MarketComparator.java diff --git a/platform/src/main/java/common/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java b/platform/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java similarity index 100% rename from platform/src/main/java/common/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java rename to platform/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForLevelDB.java diff --git a/platform/src/main/java/arm/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java b/platform/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java similarity index 100% rename from platform/src/main/java/arm/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java rename to platform/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java diff --git a/platform/src/main/java/x86/org/tron/common/math/MathWrapper.java b/platform/src/main/java/x86/org/tron/common/math/MathWrapper.java deleted file mode 100644 index 758a0f18370..00000000000 --- a/platform/src/main/java/x86/org/tron/common/math/MathWrapper.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.tron.common.math; - -/** - * This class is deprecated and should not be used in new code, - * for cross-platform consistency, please use {@link StrictMathWrapper} instead, - * especially for floating-point calculations. - */ -@Deprecated -public class MathWrapper { - - public static double pow(double a, double b) { - return Math.pow(a, b); - } - - public static long addExact(long x, long y) { - return Math.addExact(x, y); - } - - public static int addExact(int x, int y) { - return Math.addExact(x, y); - } - - public static long floorDiv(long x, long y) { - return Math.floorDiv(x, y); - } - - public static int multiplyExact(int x, int y) { - return Math.multiplyExact(x, y); - } - - public static long multiplyExact(long x, long y) { - return Math.multiplyExact(x, y); - } - - public static long subtractExact(long x, long y) { - return Math.subtractExact(x, y); - } - - public static int min(int a, int b) { - return Math.min(a, b); - } - - public static long min(long a, long b) { - return Math.min(a, b); - } - - public static int max(int a, int b) { - return Math.max(a, b); - } - - public static long max(long a, long b) { - return Math.max(a, b); - } - - public static int round(float a) { - return Math.round(a); - } - - public static long round(double a) { - return Math.round(a); - } - - public static double ceil(double a) { - return Math.ceil(a); - } - - public static double signum(double a) { - return Math.signum(a); - } - - public static double random() { - return Math.random(); - } - - public static long abs(long a) { - return Math.abs(a); - } -} diff --git a/platform/src/main/java/x86/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java b/platform/src/main/java/x86/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java deleted file mode 100644 index be406ff658d..00000000000 --- a/platform/src/main/java/x86/org/tron/common/utils/MarketOrderPriceComparatorForRocksDB.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.tron.common.utils; - -import org.rocksdb.ComparatorOptions; -import org.rocksdb.DirectSlice; -import org.rocksdb.util.DirectBytewiseComparator; - -public class MarketOrderPriceComparatorForRocksDB extends DirectBytewiseComparator { - - public MarketOrderPriceComparatorForRocksDB(final ComparatorOptions copt) { - super(copt); - } - - @Override - public String name() { - return "MarketOrderPriceComparator"; - } - - @Override - public int compare(final DirectSlice a, final DirectSlice b) { - return MarketComparator.comparePriceKey(convertDataToBytes(a), convertDataToBytes(b)); - } - - /** - * DirectSlice.data().array will throw UnsupportedOperationException. - * */ - public byte[] convertDataToBytes(DirectSlice directSlice) { - int capacity = directSlice.data().capacity(); - byte[] bytes = new byte[capacity]; - - for (int i = 0; i < capacity; i++) { - bytes[i] = directSlice.get(i); - } - - return bytes; - } - -} diff --git a/plugins/build.gradle b/plugins/build.gradle index e03e9a7c49a..5b060e690da 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -38,18 +38,9 @@ dependencies { implementation group: 'com.typesafe', name: 'config', version: '1.3.2' implementation group: 'me.tongfei', name: 'progressbar', version: '0.9.3' implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: '1.79' - if (rootProject.archInfo.isArm64) { - testRuntimeOnly group: 'org.fusesource.hawtjni', name: 'hawtjni-runtime', version: '1.18' // for test - implementation project(":platform") - } else { - implementation project(":platform"), { - exclude(group: 'org.fusesource.leveldbjni', module: 'leveldbjni-all') - exclude(group: 'io.github.tronprotocol', module: 'zksnark-java-sdk') - exclude(group: 'commons-io', module: 'commons-io') - } - implementation 'io.github.tronprotocol:leveldbjni-all:1.18.2' - implementation 'io.github.tronprotocol:leveldb:1.18.2' - } + testRuntimeOnly group: 'org.fusesource.hawtjni', name: 'hawtjni-runtime', version: '1.18' + implementation 'io.github.tronprotocol:leveldb:1.18.2' + implementation project(":platform") implementation project(":protocol") } @@ -109,8 +100,8 @@ test { jacocoTestReport { reports { - xml.enabled true - csv.enabled false + xml.required.set(true) + csv.required.set(true) html.destination file("${buildDir}/jacocoHtml") } getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec")) @@ -118,8 +109,8 @@ jacocoTestReport { def binaryRelease(taskName, jarName, mainClass) { return tasks.create("${taskName}", Jar) { - baseName = jarName - version = null + archiveBaseName.set(jarName) + archiveVersion.set('') from(sourceSets.main.output) { include "/**" } diff --git a/plugins/src/main/java/common/org/tron/plugins/Db.java b/plugins/src/main/java/common/org/tron/plugins/Db.java index 84654dca934..7fffa70a12c 100644 --- a/plugins/src/main/java/common/org/tron/plugins/Db.java +++ b/plugins/src/main/java/common/org/tron/plugins/Db.java @@ -12,7 +12,10 @@ DbConvert.class, DbLite.class, DbCopy.class, - DbRoot.class + DbRoot.class, + DbCheckSum.class, + DbQueryProperties.class, + DbCompare.class, }, commandListHeading = "%nCommands:%n%nThe most commonly used db commands are:%n" ) diff --git a/plugins/src/main/java/common/org/tron/plugins/DbCheckSum.java b/plugins/src/main/java/common/org/tron/plugins/DbCheckSum.java new file mode 100644 index 00000000000..bc70822b51c --- /dev/null +++ b/plugins/src/main/java/common/org/tron/plugins/DbCheckSum.java @@ -0,0 +1,271 @@ +package org.tron.plugins; + +import com.google.common.collect.Sets; +import com.google.common.collect.Streams; +import com.google.common.primitives.Bytes; +import com.google.common.primitives.Longs; +import com.google.protobuf.InvalidProtocolBufferException; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import me.tongfei.progressbar.ProgressBar; +import org.rocksdb.RocksDBException; +import org.tron.plugins.utils.ByteArray; +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 org.tron.protos.Protocol; +import org.tron.protos.contract.SmartContractOuterClass; +import picocli.CommandLine; + +@Slf4j(topic = "db-root") +@CommandLine.Command(name = "checksum", + description = "compute checksum.", + exitCodeListHeading = "Exit Codes:%n", + exitCodeList = { + "0:Successful", + "n:query failed,please check toolkit.log"}) +public class DbCheckSum 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"}, split = ",", + description = "db name for show root") + private List dbs; + + @CommandLine.Option(names = {"--ignore-asset"}, + description = "ignore asset in account, default: ${DEFAULT-VALUE}") + private boolean ignoreAsset; + + @CommandLine.Option(names = {"-h", "--help"}, help = true, description = "display a help message") + private boolean help; + + AtomicReference> height = new AtomicReference<>(Optional.empty()); + private static final byte[] HEADER_KEY = "latest_block_header_number".getBytes(); + private static final List stateDbs = Arrays.asList( + "account", "asset-issue-v2", + "code", "contract", "contract-state", "storage-row", + "delegation", "DelegatedResource", "DelegatedResourceAccountIndex", + "exchange-v2", + "market_account", "market_order", "market_pair_price_to_order", "market_pair_to_price", + "properties", "proposal", + "votes", "witness", "witness_schedule" + ); + + DBIterator assetIterator; + + private static final byte[] CURRENT_SHUFFLED_WITNESSES = "current_shuffled_witnesses".getBytes(); + private static final String FORK_PREFIX = "FORK_VERSION_"; + private static final String DONE_SUFFIX = "_DONE"; + private static final String ACCOUNT_VOTE_SUFFIX = "-account-vote"; + private static final String LASTWITHDRAW_PREFIX = "lastWithdraw-"; + private static final String REMARK_SUFFIX = "-remark"; + private static final String REWARD_VOTE_SUFFIX = "-reward-vote"; + private static final String BLOCK_SUFFIX = "-block"; + private static final Set IGNORED_PROPERTIES = Sets.newHashSet( + "VOTE_REWARD_RATE", "SINGLE_REPEAT", "NON_EXISTENT_ACCOUNT_TRANSFER_MIN", + "ALLOW_TVM_ASSET_ISSUE", "ALLOW_TVM_STAKE", + "MAX_VOTE_NUMBER", "MAX_FROZEN_NUMBER", "MAINTENANCE_TIME_INTERVAL", + "LATEST_SOLIDIFIED_BLOCK_NUM", "BLOCK_NET_USAGE", + "VERSION_NUMBER", + "SHIELDED_TRANSACTION_FEE", + "BLOCK_FILLED_SLOTS_INDEX", "BLOCK_FILLED_SLOTS_NUMBER", "BLOCK_FILLED_SLOTS", + "CURRENT_CYCLE_TIMESTAMP"); + + @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()) { + dbs = stateDbs.stream() + .filter(s -> Paths.get(db.toString(), s).toFile().exists()) + .collect(Collectors.toList()); + } else if (!dbs.contains("properties")) { + dbs.add("properties"); + } + + if (dbs.contains("account")) { + dbs.remove("account-asset"); + assetIterator = DbTool.getDB(db, "account-asset").iterator(); + } + List task = ProgressBar.wrap(dbs.stream(), "root task").parallel() + .map(this::calcMerkleRoot).collect(Collectors.toList()); + long num = height.get().orElseThrow(() -> new IllegalArgumentException("blockNum is null")); + spec.commandLine().getOut().println("block number: " + num); + logger.info("block number: {}", num); + 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."); + if (assetIterator != null) { + assetIterator.close(); + } + 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(); + final AtomicReference root = new AtomicReference<>(Sha256Hash.ZERO_HASH); + Streams.stream(iterator).map(e -> map(e, name)).filter(Objects::nonNull).map(this::getHash) + .forEach(hash -> root.getAndUpdate(v -> MerkleRoot.computeHash(v, hash))); + logger.info("db: {}, checksum: {}", database.getName(), root.get()); + info.code = 0; + info.msg = String.format("db: %s, checksum: %s", database.getName(), root.get()); + } 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 Map getAllAssets(Protocol.Account account) { + Map assets = new TreeMap<>(); + if (ignoreAsset) { + return assets; + } + int addressSize = account.getAddress().toByteArray().length; + if (account.getAssetOptimized()) { + Map map = prefixQuery(account.getAddress().toByteArray()); + map.forEach((k, v) -> assets.put(ByteArray.toStr( + ByteArray.subArray(k, addressSize, k.length)), Longs.fromByteArray(v))); + } + assets.putAll(account.getAssetV2Map()); + assets.entrySet().removeIf(entry -> entry.getValue() <= 0); + return assets; + } + + public Map prefixQuery(byte[] key) { + Map result = new HashMap<>(); + for (assetIterator.seek(key); assetIterator.valid(); assetIterator.next()) { + if (Bytes.indexOf(assetIterator.getKey(), key) == 0) { + result.put(assetIterator.getKey(), assetIterator.getValue()); + } else { + return result; + } + } + return result; + } + + private Map.Entry map(Map.Entry entry, String name) { + try { + switch (name) { + case "account": { + Protocol.Account account = Protocol.Account.parseFrom(entry.getValue()); + return new AbstractMap.SimpleEntry<>(entry.getKey(), + account.toBuilder().clearAsset().clearAssetV2().clearAssetOptimized() + .putAllAssetV2(getAllAssets(account)) + .build().toByteArray()); + } + case "contract": { + SmartContractOuterClass.SmartContract contract = + SmartContractOuterClass.SmartContract.parseFrom(entry.getValue()); + return new AbstractMap.SimpleEntry<>(entry.getKey(), + contract.toBuilder().clearAbi().build().toByteArray()); + } + case "witness": { + return new AbstractMap.SimpleEntry<>(entry.getKey(), + Protocol.Witness.parseFrom(entry.getValue()) + .toBuilder().clearTotalMissed() + .build().toByteArray()); + } + case "delegation": { + String keyStr = new String(entry.getKey()); + if (keyStr.endsWith(REWARD_VOTE_SUFFIX) || keyStr.endsWith(BLOCK_SUFFIX) + || keyStr.startsWith(LASTWITHDRAW_PREFIX) || keyStr.endsWith(REMARK_SUFFIX)) { + return null; + } + if (keyStr.endsWith(ACCOUNT_VOTE_SUFFIX)) { + return new AbstractMap.SimpleEntry<>(entry.getKey(), + Protocol.Account.newBuilder().addAllVotes( + Protocol.Account.parseFrom( + entry.getValue()).getVotesList()).build().toByteArray()); + } else { + return entry; + } + } + case "properties": { + String keyStr = new String(entry.getKey()); + if (IGNORED_PROPERTIES.contains(keyStr) + || keyStr.startsWith(FORK_PREFIX) || keyStr.endsWith(DONE_SUFFIX)) { + return null; + } + if (Arrays.equals(HEADER_KEY, entry.getKey())) { + height.set(Optional.of(ByteArray.toLong(entry.getValue()))); + } + return entry; + } + case "witness_schedule": { + if (Arrays.equals(entry.getKey(), CURRENT_SHUFFLED_WITNESSES)) { + return null; + } + return entry; + } + default: + return entry; + } + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException("Failed to parse entry: " + entry, e); + } + } + + 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; + } +} \ No newline at end of file diff --git a/plugins/src/main/java/common/org/tron/plugins/DbCompare.java b/plugins/src/main/java/common/org/tron/plugins/DbCompare.java new file mode 100644 index 00000000000..867830a38f8 --- /dev/null +++ b/plugins/src/main/java/common/org/tron/plugins/DbCompare.java @@ -0,0 +1,245 @@ +package org.tron.plugins; + +import com.google.common.collect.Sets; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.Callable; +import lombok.extern.slf4j.Slf4j; +import org.rocksdb.RocksDBException; +import org.tron.plugins.utils.ByteArray; +import org.tron.plugins.utils.db.DBInterface; +import org.tron.plugins.utils.db.DBIterator; +import org.tron.plugins.utils.db.DbTool; +import org.tron.protos.Protocol; +import picocli.CommandLine; + +@Slf4j(topic = "compare") +@CommandLine.Command(name = "compare", + description = "compare data between two path for db.", + exitCodeListHeading = "Exit Codes:%n", + exitCodeList = { + "0:Successful", + "n:compare diff find,please check toolkit.log"}) +public class DbCompare implements Callable { + + @CommandLine.Spec + static CommandLine.Model.CommandSpec spec; + @CommandLine.Parameters(index = "0", + description = " ful input path for base") + private File base; + @CommandLine.Parameters(index = "1", + description = "ful input path for compare") + private File compare; + @CommandLine.Option(names = {"-h", "--help"}, help = true, description = "display a help message") + private boolean help; + + @CommandLine.Option(names = {"--continues"}) + private boolean continues; + + private static final String ACCOUNT_VOTE_SUFFIX = "-account-vote"; + private static final String LASTWITHDRAW_PREFIX = "lastWithdraw-"; + private static final String REMARK_SUFFIX = "-remark"; + private static final String REWARD_VOTE_SUFFIX = "-reward-vote"; + private static final String BLOCK_SUFFIX = "-block"; + + private static final String FORK_PREFIX = "FORK_VERSION_"; + private static final String DONE_SUFFIX = "_DONE"; + private static final Set IGNORED_PROPERTIES = Sets.newHashSet( + "VOTE_REWARD_RATE", "SINGLE_REPEAT", "NON_EXISTENT_ACCOUNT_TRANSFER_MIN", + "ALLOW_TVM_ASSET_ISSUE", "ALLOW_TVM_STAKE", + "MAX_VOTE_NUMBER", "MAX_FROZEN_NUMBER", "MAINTENANCE_TIME_INTERVAL", + "LATEST_SOLIDIFIED_BLOCK_NUM", "BLOCK_NET_USAGE", + "VERSION_NUMBER", + "SHIELDED_TRANSACTION_FEE", + "BLOCK_FILLED_SLOTS_INDEX", "BLOCK_FILLED_SLOTS_NUMBER", "BLOCK_FILLED_SLOTS", + "CURRENT_CYCLE_TIMESTAMP"); + + private static final byte[] CURRENT_SHUFFLED_WITNESSES = "current_shuffled_witnesses".getBytes(); + + @Override + public Integer call() throws Exception { + if (help) { + spec.commandLine().usage(System.out); + return 0; + } + if (!base.exists()) { + logger.info(" {} does not exist.", base); + spec.commandLine().getErr().println(spec.commandLine().getColorScheme() + .errorText(String.format("%s does not exist.", base))); + return 404; + } + if (!compare.exists()) { + logger.info(" {} does not exist.", compare); + spec.commandLine().getErr().println(spec.commandLine().getColorScheme() + .errorText(String.format("%s does not exist.", compare))); + return 404; + } + + Comparison service = new DbComparison(base.toPath(), compare.toPath(), continues); + return service.doCompare() ? 0 : 1; + + } + + interface Comparison { + boolean doCompare() throws Exception; + } + + static class DbComparison implements Comparison { + + private final Path basePath; + private final Path dstPath; + private final String name; + private final boolean continues; + + public DbComparison(Path srcDir, Path dstDir, boolean continues) { + this.basePath = srcDir.getParent(); + this.dstPath = dstDir.getParent(); + this.name = srcDir.getFileName().toString(); + this.continues = continues; + } + + @Override + public boolean doCompare() throws Exception { + return compare(); + } + + + + private boolean compare() throws RocksDBException, IOException { + try ( + DBInterface base = DbTool.getDB(this.basePath, this.name); + DBIterator baseIterator = base.iterator(); + DBInterface dst = DbTool.getDB(this.dstPath, this.name); + DBIterator dstIterator = dst.iterator()) { + + // check + logger.info("compare {} start", this.name); + spec.commandLine().getOut().println("compare " + this.name + " start"); + baseIterator.seekToFirst(); + dstIterator.seekToFirst(); + for (; baseIterator.hasNext() && dstIterator.hasNext(); + baseIterator.next(), dstIterator.next()) { + byte[] baseValue = baseIterator.getValue(); + byte[] baseKey = baseIterator.getKey(); + byte[] dstKey = dstIterator.getKey(); + byte[] dstValue = dstIterator.getValue(); + if (Arrays.equals(baseKey, dstKey) && !compareValue(baseKey, baseValue, dstValue)) { + spec.commandLine().getOut().format("%s\t%s\t%s.", + ByteArray.toHexString(baseKey), + ByteArray.toHexString(baseValue), ByteArray.toHexString(dstValue)).println(); + logger.info("{}\t{}\t{}", + ByteArray.toHexString(baseKey), + ByteArray.toHexString(baseValue), ByteArray.toHexString(dstValue)); + if (!continues) { + return false; + } + } + if (!Arrays.equals(baseKey, dstKey)) { + byte[] dstValueTmp = base.get(dstKey); + byte[] baseValueTmp = dst.get(baseKey); + if (!compareValue(baseKey, baseValue, baseValueTmp)) { + spec.commandLine().getOut().format("%s\t%s\t%s.", + ByteArray.toHexString(baseKey), + ByteArray.toHexString(baseValue), ByteArray.toHexString(baseValueTmp)).println(); + logger.info("{}\t{}\t{}", + ByteArray.toHexString(baseKey), + ByteArray.toHexString(baseValue), ByteArray.toHexString(baseValueTmp)); + if (!continues) { + return false; + } + } + if (!compareValue(dstKey, dstValueTmp, dstValue)) { + spec.commandLine().getOut().format("%s\t%s\t%s.", + ByteArray.toHexString(dstKey), + ByteArray.toHexString(dstValueTmp), ByteArray.toHexString(dstValue)).println(); + logger.info("{}\t{}\t{}", + ByteArray.toHexString(dstKey), + ByteArray.toHexString(dstValueTmp), ByteArray.toHexString(dstValue)); + if (!continues) { + return false; + } + } + } + } + for (; baseIterator.hasNext(); baseIterator.next()) { + byte[] key = baseIterator.getKey(); + byte[] baseValue = baseIterator.getValue(); + byte[] destValue = dst.get(key); + if (!compareValue(key, baseValue, destValue)) { + spec.commandLine().getOut().format("%s\t%s\t%s.", + ByteArray.toHexString(key), + ByteArray.toHexString(baseValue), ByteArray.toHexString(destValue)).println(); + logger.info("{}\t{}\t{}", + ByteArray.toHexString(key), + ByteArray.toHexString(baseValue), ByteArray.toHexString(destValue)); + } + } + for (; dstIterator.hasNext(); dstIterator.next()) { + byte[] key = dstIterator.getKey(); + byte[] destValue = dstIterator.getValue(); + byte[] baseValue = base.get(key); + if (!compareValue(key, baseValue, destValue)) { + spec.commandLine().getOut().format("%s\t%s\t%s.", + ByteArray.toHexString(key), + ByteArray.toHexString(baseValue), ByteArray.toHexString(destValue)).println(); + logger.info("{}\t{}\t{}", + ByteArray.toHexString(key), + ByteArray.toHexString(baseValue), ByteArray.toHexString(destValue)); + } + } + } + logger.info("compare {} end", this.name); + spec.commandLine().getOut().println("compare " + this.name + " end"); + return true; + } + + private boolean compareValue(byte[] key, byte[] base, byte[] exp) { + if ("account".equals(name)) { + Protocol.Account baseAccount = ByteArray.toAccount(base); + Protocol.Account expAccount = ByteArray.toAccount(exp); + if (baseAccount.getAssetOptimized() || expAccount.getAssetOptimized()) { + baseAccount = baseAccount.toBuilder().clearAsset().clearAssetV2() + .setAssetOptimized(true).build(); + expAccount = expAccount.toBuilder().clearAsset().clearAssetV2() + .setAssetOptimized(true).build(); + } + return baseAccount.equals(expAccount); + } else if ("properties".equals(name)) { + String keyStr = new String(key); + if (IGNORED_PROPERTIES.contains(keyStr) + || keyStr.startsWith(FORK_PREFIX) || keyStr.endsWith(DONE_SUFFIX)) { + return true; + } + return Arrays.equals(base, exp); + } else if ("delegation".equals(name)) { + String keyStr = new String(key); + if (keyStr.endsWith(REWARD_VOTE_SUFFIX) || keyStr.endsWith(BLOCK_SUFFIX) + || keyStr.startsWith(LASTWITHDRAW_PREFIX) || keyStr.endsWith(REMARK_SUFFIX)) { + return true; + } + if (keyStr.endsWith(ACCOUNT_VOTE_SUFFIX)) { + Protocol.Account baseAccount = ByteArray.toAccount(base); + Protocol.Account expAccount = ByteArray.toAccount(exp); + return baseAccount.getVotesList().equals(expAccount.getVotesList()); + } + return Arrays.equals(base, exp); + } else if ("witness".equals(name)) { + Protocol.Witness baseSR = ByteArray.toWitness(base) + .toBuilder().clearTotalMissed() + .build(); + Protocol.Witness expSR = ByteArray.toWitness(exp); + return baseSR.equals(expSR); + } else if ("witness_schedule".equals(name)) { + if (Arrays.equals(key, CURRENT_SHUFFLED_WITNESSES)) { + return true; + } + return Arrays.equals(base, exp); + } else { + return Arrays.equals(base, exp); + } + } + } +} \ No newline at end of file diff --git a/plugins/src/main/java/common/org/tron/plugins/DbQueryProperties.java b/plugins/src/main/java/common/org/tron/plugins/DbQueryProperties.java new file mode 100644 index 00000000000..39de23bde70 --- /dev/null +++ b/plugins/src/main/java/common/org/tron/plugins/DbQueryProperties.java @@ -0,0 +1,97 @@ +package org.tron.plugins; + +import com.google.common.collect.Sets; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import lombok.extern.slf4j.Slf4j; +import org.rocksdb.RocksDBException; +import org.tron.plugins.utils.ByteArray; +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 = "query") +@CommandLine.Command(name = "query-properties", + description = "query data from dynamicPropertiesStore.", + exitCodeListHeading = "Exit Codes:%n", + exitCodeList = { + "0:Successful", + "n:query failed,please check toolkit.log"}) +public class DbQueryProperties implements Callable { + + @CommandLine.Spec + CommandLine.Model.CommandSpec spec; + @CommandLine.Parameters(index = "0", + description = " db path for dynamicPropertiesStore") + private Path db; + @CommandLine.Option(names = { "--keys"}, + description = "key for query") + private List keys; + @CommandLine.Option(names = {"-h", "--help"}, help = true, description = "display a help message") + private boolean help; + + private static final String DB = "properties"; + + private static final String FORK_PREFIX = "FORK_VERSION_"; + private static final String DONE_SUFFIX = "_DONE"; + private static final Set IGNORED_PROPERTIES = Sets.newHashSet( + "VOTE_REWARD_RATE", "SINGLE_REPEAT", "NON_EXISTENT_ACCOUNT_TRANSFER_MIN", + "ALLOW_TVM_ASSET_ISSUE", "ALLOW_TVM_STAKE", + "MAX_VOTE_NUMBER", "MAX_FROZEN_NUMBER", "MAINTENANCE_TIME_INTERVAL", + "LATEST_SOLIDIFIED_BLOCK_NUM", "BLOCK_NET_USAGE", + "VERSION_NUMBER", + "SHIELDED_TRANSACTION_FEE", + "BLOCK_FILLED_SLOTS_INDEX", "BLOCK_FILLED_SLOTS_NUMBER", "BLOCK_FILLED_SLOTS"); + + @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; + } + return query(); + } + + + private int query() throws RocksDBException, IOException { + try ( + DBInterface database = DbTool.getDB(this.db, DB); + DBIterator iterator = database.iterator()) { + + if (keys != null && !keys.isEmpty()) { + keys.forEach(k -> print(k, database.get(k.getBytes()))); + } else { + long c = 0; + for (iterator.seekToFirst(); iterator.hasNext(); iterator.next()) { + String keyStr = new String(iterator.getKey()); + if (IGNORED_PROPERTIES.contains(keyStr) + || keyStr.startsWith(FORK_PREFIX) || keyStr.endsWith(DONE_SUFFIX)) { + continue; + } + c++; + print(keyStr, iterator.getValue()); + } + spec.commandLine().getOut().format("total key size: %d", c).println(); + logger.info("total key size: {}", c); + } + } + return 0; + } + + private void print(String key, byte[] b) { + String v = ByteArray.toHexString(b); + spec.commandLine().getOut().format("%s\t%s", key, v).println(); + logger.info("{}\t{}", key, v); + } +} \ No newline at end of file diff --git a/plugins/src/main/java/common/org/tron/plugins/utils/ByteArray.java b/plugins/src/main/java/common/org/tron/plugins/utils/ByteArray.java index 2922d110b7c..4e8cc01bf64 100644 --- a/plugins/src/main/java/common/org/tron/plugins/utils/ByteArray.java +++ b/plugins/src/main/java/common/org/tron/plugins/utils/ByteArray.java @@ -6,6 +6,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.util.encoders.Hex; +import org.tron.protos.Protocol; public class ByteArray { @@ -98,4 +99,35 @@ public static byte[] fromHexString(String data) { } return Hex.decode(data); } + + /** + * Generate a subarray of a given byte array. + * + * @param input the input byte array + * @param start the start index + * @param end the end index + * @return a subarray of input, ranging from start (inclusively) to end + * (exclusively) + */ + public static byte[] subArray(byte[] input, int start, int end) { + byte[] result = new byte[end - start]; + System.arraycopy(input, start, result, 0, end - start); + return result; + } + + public static Protocol.Account toAccount(byte[] base) { + try { + return Protocol.Account.parseFrom(base); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static Protocol.Witness toWitness(byte[] base) { + try { + return Protocol.Witness.parseFrom(base); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java b/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java index 6eb097cbec5..b5785b6d417 100644 --- a/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java +++ b/plugins/src/main/java/common/org/tron/plugins/utils/DBUtils.java @@ -14,7 +14,6 @@ import org.rocksdb.BloomFilter; import org.rocksdb.ComparatorOptions; import org.rocksdb.Options; -import org.tron.common.arch.Arch; import org.tron.common.utils.MarketOrderPriceComparatorForLevelDB; import org.tron.common.utils.MarketOrderPriceComparatorForRocksDB; import org.tron.protos.Protocol; @@ -64,7 +63,6 @@ static Operator valueOf(byte b) { public static final String ROCKSDB = "ROCKSDB"; public static DB newLevelDb(Path db) throws IOException { - Arch.throwIfUnsupportedArm64Exception(LEVELDB); File file = db.toFile(); org.iq80.leveldb.Options dbOptions = newDefaultLevelDbOptions(); if (MARKET_PAIR_PRICE_TO_ORDER.equalsIgnoreCase(file.getName())) { diff --git a/plugins/src/main/java/common/org/tron/plugins/utils/MerkleRoot.java b/plugins/src/main/java/common/org/tron/plugins/utils/MerkleRoot.java index 055f5dcdee0..4b444a938bd 100644 --- a/plugins/src/main/java/common/org/tron/plugins/utils/MerkleRoot.java +++ b/plugins/src/main/java/common/org/tron/plugins/utils/MerkleRoot.java @@ -55,7 +55,7 @@ private static Leaf createLeaf(Sha256Hash hash) { return leaf; } - private static Sha256Hash computeHash(Sha256Hash leftHash, Sha256Hash rightHash) { + public static Sha256Hash computeHash(Sha256Hash leftHash, Sha256Hash rightHash) { return Sha256Hash.of(true, leftHash.getByteString().concat(rightHash.getByteString()).toByteArray()); } diff --git a/protocol/src/main/protos/core/Tron.proto b/protocol/src/main/protos/core/Tron.proto index 2b104b86d34..4ab6f3b95b4 100644 --- a/protocol/src/main/protos/core/Tron.proto +++ b/protocol/src/main/protos/core/Tron.proto @@ -515,6 +515,7 @@ message BlockHeader { } raw raw_data = 1; bytes witness_signature = 2; + bytes state_root = 3; } // block diff --git a/start.sh.simple b/start.sh.simple index 52548dea62b..f4affc67a66 100644 --- a/start.sh.simple +++ b/start.sh.simple @@ -29,6 +29,16 @@ # ############################################################################## +## Set TCMalloc as the memory allocator + +export LD_PRELOAD="/usr/lib64/libtcmalloc.so" +export TCMALLOC_RELEASE_RATE=10 + +# JDK + #export JAVA_HOME=/home/java-tron/jdk17 + #export PATH=$JAVA_HOME/bin:$PATH + #export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar + #JDK # adjust JVM start # Set the maximum heap size to 9G, adjust as needed @@ -131,7 +141,7 @@ startService() { nohup "$JAVACMD" \ -Xmx"$VM_XMX" \ -XX:+UseZGC \ - -Xlog:gc,gc+heap:file=gc.log:time,tags,level:filecount=10,filesize=100M \ + -Xlog:gc,gc+heap:file=gc/gc.log:time,tags,level:filecount=10,filesize=100M \ -XX:ReservedCodeCacheSize=256m \ -XX:+UseCodeCacheFlushing \ -XX:MetaspaceSize=256m \