diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 74940a5a754..00000000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-version: 2
-jobs:
- build:
- branches:
- only:
- - master
- - develop
- - addinCircleCI
-
- docker:
- - image: circleci/openjdk:8-jdk-browsers
- working_directory: ~/java-tron
- steps:
- - checkout
- - run:
- name: multi_os_result
- command: echo "curl http://60.205.215.34/multi_os_result"
-#
-# - run:
-# name: Daily Build Report
-# command: curl http://47.95.206.44:50080/Daily_Build_Task_Report
-#
-# - run:
-# name: Download Links
-# command: sh DownloadLinks.sh
-
diff --git a/.github/ISSUE_TEMPLATE/ask-a-question.md b/.github/ISSUE_TEMPLATE/ask-a-question.md
index a92239b39eb..1517046d7da 100644
--- a/.github/ISSUE_TEMPLATE/ask-a-question.md
+++ b/.github/ISSUE_TEMPLATE/ask-a-question.md
@@ -1,28 +1,51 @@
---
name: Ask a question
-about: Something is unclear
-title: ''
-labels: ''
+about: Something is unclear or needs clarification
+title: '[Question]'
+labels: 'type:docs'
assignees: ''
---
-
+
-### 1. What did you do?
-
+## Question
+
-### 2. What did you expect to see?
+## Context
+
+**What are you trying to achieve?**
+
-### 3. What did you see instead?
+**What have you tried so far?**
+
+**Relevant documentation or code**
+
+## Environment (if applicable)
+
+- Network:
+- java-tron version:
+- Operating System:
+- Java version:
+
+## Additional Information (Optional)
+
+
diff --git a/.github/ISSUE_TEMPLATE/report-a-bug.md b/.github/ISSUE_TEMPLATE/report-a-bug.md
index 649d7e97ed1..30a3b245862 100644
--- a/.github/ISSUE_TEMPLATE/report-a-bug.md
+++ b/.github/ISSUE_TEMPLATE/report-a-bug.md
@@ -1,31 +1,86 @@
---
name: Report a bug
about: Create a report to help us improve
-title: ''
-labels: ''
+title: '[Bug]'
+labels: 'type:bug'
assignees: ''
---
-#### System information
+
-java-tron version: `java -jar FullNode.jar -v`
-OS & Version: Windows/Linux/OSX
-Commit hash : (if `develop`)
+## Bug Description
-#### Expected behaviour
+
+## Environment
-#### Actual behaviour
+**Network**
+
+**Software Versions**
+
-#### Steps to reproduce the behaviour
+```
+OS:
+JVM:
+Git Commit:
+Version:
+Code:
+```
+**Configuration**
+
-#### Backtrace
+## Expected Behavior
-````
-[backtrace]
-````
+
-When submitting logs: please submit them as text and not screenshots.
+## Actual Behavior
+
+
+
+## Frequency
+
+
+- [ ] Always (100%)
+- [ ] Frequently (>50%)
+- [ ] Sometimes (10-50%)
+- [ ] Rarely (<10%)
+
+## Steps to Reproduce
+
+
+
+1.
+2.
+3.
+
+## Logs and Error Messages
+
+
+
+```
+[Paste error messages, stack traces, or relevant logs here]
+```
+
+## Additional Context (Optional)
+
+
+
+**Screenshots**
+
+
+**Related Issues**
+
+
+**Possible Solution**
+
diff --git a/.github/ISSUE_TEMPLATE/request-a-feature.md b/.github/ISSUE_TEMPLATE/request-a-feature.md
index 9047b7e6a87..d8234f92a25 100644
--- a/.github/ISSUE_TEMPLATE/request-a-feature.md
+++ b/.github/ISSUE_TEMPLATE/request-a-feature.md
@@ -1,18 +1,47 @@
---
name: Request a feature
about: Suggest an idea for this project
-title: ''
-labels: ''
+title: '[Feature]'
+labels: 'type:feature'
assignees: ''
---
-# Rationale
+# Summary
+
-Why should this feature exist?
-What are the use-cases?
+# Problem
+### Motivation
+
-# Implementation
+### Current State
+
-Do you have ideas regarding the implementation of this feature?
-Are you willing to implement this feature?
+### Limitations or Risks
+
+
+# Proposed Solution
+
+### Proposed Design
+
+
+### Key Changes
+
+
+# Impact
+
+
+# Compatibility
+
+
+# References (Optional)
+
+
+# Additional Notes
+- Do you have ideas regarding implementation? Yes / No
+- Are you willing to implement this feature? Yes / No
\ No newline at end of file
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 00000000000..9c3af93f787
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,56 @@
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ 'develop', 'master', 'release_**' ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ 'develop' ]
+ paths-ignore: [ '**/*.md', '.gitignore', '**/.gitignore', '.editorconfig',
+ '.gitattributes', 'docs/**', 'CHANGELOG', '.github/ISSUE_TEMPLATE/**',
+ '.github/PULL_REQUEST_TEMPLATE/**', '.github/CODEOWNERS' ]
+ schedule:
+ - cron: '6 10 * * 0'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'java' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Use only 'java' to analyze code written in Java, Kotlin or both
+ # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
+ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v4
+ with:
+ languages: ${{ matrix.language }}
+ build-mode: manual
+
+ - name: Set up JDK 8
+ uses: actions/setup-java@v5
+ with:
+ java-version: '8'
+ distribution: 'temurin'
+
+ - name: Build
+ run: ./gradlew build -x test --no-daemon
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v4
+ with:
+ category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/math-check.yml b/.github/workflows/math-check.yml
new file mode 100644
index 00000000000..a5db3351a94
--- /dev/null
+++ b/.github/workflows/math-check.yml
@@ -0,0 +1,93 @@
+name: Check Math Usage
+
+on:
+ push:
+ branches: [ 'master', 'release_**' ]
+ pull_request:
+ branches: [ 'develop', 'release_**' ]
+ workflow_dispatch:
+
+jobs:
+ check-math:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Check for java.lang.Math usage
+ id: check-math
+ shell: bash
+ run: |
+ echo "Checking for java.lang.Math usage..."
+
+ touch math_usage.txt
+
+ while IFS= read -r file; do
+ filename=$(basename "$file")
+ if [[ "$filename" == "StrictMathWrapper.java" || "$filename" == "MathWrapper.java" ]]; then
+ continue
+ fi
+
+ perl -0777 -ne '
+ s/"([^"\\]|\\.)*"//g;
+ s/'\''([^'\''\\]|\\.)*'\''//g;
+ s!/\*([^*]|\*[^/])*\*/!!g;
+ s!//[^\n]*!!g;
+ $hasMath = 0;
+ $hasMath = 1 if /^[\s]*import[\s]+java\.lang\.Math\b/m;
+ $hasMath = 1 if /\bjava\s*\.\s*lang\s*\.\s*Math\s*\./;
+ $hasMath = 1 if /(?> math_usage.txt
+ done < <(find . -type f -name "*.java")
+
+ sort -u math_usage.txt -o math_usage.txt
+
+ if [ -s math_usage.txt ]; then
+ echo "❌ Error: Forbidden Math usage found in the following files:"
+ cat math_usage.txt
+ echo "math_found=true" >> $GITHUB_OUTPUT
+ echo "Please use org.tron.common.math.StrictMathWrapper instead of direct Math usage."
+ else
+ echo "✅ No forbidden Math usage found"
+ echo "math_found=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Upload findings
+ if: steps.check-math.outputs.math_found == 'true'
+ uses: actions/upload-artifact@v6
+ with:
+ name: math-usage-report
+ path: math_usage.txt
+
+ - name: Create comment
+ if: github.event_name == 'pull_request' && steps.check-math.outputs.math_found == 'true'
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const fs = require('fs');
+ const findings = fs.readFileSync('math_usage.txt', 'utf8');
+ const body = `### ❌ Math Usage Detection Results
+
+ Found forbidden usage of \`java.lang.Math\` in the following files:
+
+ \`\`\`
+ ${findings}
+ \`\`\`
+
+ **Please review if this usage is intended.**
+ > [!CAUTION]
+ > Note: You should use \`org.tron.common.math.StrictMathWrapper\`.
+ > If you need to use \`java.lang.Math\`, please provide a justification.
+ `;
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ body: body
+ });
+
+ - name: Fail if Math usage found
+ if: steps.check-math.outputs.math_found == 'true'
+ run: exit 1
diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml
new file mode 100644
index 00000000000..dd005f98b74
--- /dev/null
+++ b/.github/workflows/pr-build.yml
@@ -0,0 +1,509 @@
+name: PR Build
+
+on:
+ pull_request:
+ branches: [ 'master','develop', 'release_**' ]
+ types: [ opened, synchronize, reopened ]
+ paths-ignore: [ '**/*.md', '.gitignore', '**/.gitignore', '.editorconfig',
+ '.gitattributes', 'docs/**', 'CHANGELOG', '.github/ISSUE_TEMPLATE/**',
+ '.github/PULL_REQUEST_TEMPLATE/**', '.github/CODEOWNERS' ]
+ workflow_dispatch:
+ inputs:
+ job:
+ description: 'Job to run: all / macos / ubuntu / rockylinux / debian11'
+ required: false
+ default: 'all'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+
+ build-macos:
+ name: Build macos26 (JDK ${{ matrix.java }} / ${{ matrix.arch }})
+ if: ${{ github.event_name == 'pull_request' || inputs.job == 'all' || inputs.job == 'macos' }}
+ runs-on: ${{ matrix.runner }}
+ timeout-minutes: 60
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - java: '17'
+ runner: macos-26
+ arch: aarch64
+
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Set up JDK ${{ matrix.java }}
+ uses: actions/setup-java@v5
+ with:
+ java-version: ${{ matrix.java }}
+ distribution: 'temurin'
+
+ - name: Cache Gradle packages
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: macos26-${{ matrix.arch }}-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }}
+ restore-keys: macos26-${{ matrix.arch }}-gradle-
+
+ - name: Build
+ run: ./gradlew clean build --no-daemon
+
+ - name: Toolkit jar smoke test
+ run: |
+ set -e
+ JAR=plugins/build/libs/Toolkit.jar
+ java -jar "$JAR" help
+ java -jar "$JAR" db --help
+ java -jar "$JAR" db archive -h
+ java -jar "$JAR" keystore --help
+
+ build-ubuntu:
+ name: Build ubuntu24 (JDK 17 / aarch64)
+ if: ${{ github.event_name == 'pull_request' || inputs.job == 'all' || inputs.job == 'ubuntu' }}
+ runs-on: ubuntu-24.04-arm
+ timeout-minutes: 60
+
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v5
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+
+ - name: Check Java version
+ run: java -version
+
+ - name: Cache Gradle packages
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ubuntu24-aarch64-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }}
+ restore-keys: ubuntu24-aarch64-gradle-
+
+ - name: Build
+ run: ./gradlew clean build --no-daemon
+
+ - name: Toolkit jar smoke test
+ run: |
+ set -e
+ JAR=plugins/build/libs/Toolkit.jar
+ java -jar "$JAR" help
+ java -jar "$JAR" db --help
+ java -jar "$JAR" db archive -h
+ java -jar "$JAR" keystore --help
+
+ docker-build-rockylinux:
+ name: Build rockylinux (JDK 8 / x86_64)
+ if: ${{ github.event_name == 'pull_request' || inputs.job == 'all' || inputs.job == 'rockylinux' }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+
+ container:
+ image: rockylinux:8
+
+ env:
+ GRADLE_USER_HOME: /github/home/.gradle
+ LANG: en_US.UTF-8
+ LC_ALL: en_US.UTF-8
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ - name: Install dependencies (Rocky 8 + JDK8)
+ run: |
+ set -euxo pipefail
+ dnf -y install java-1.8.0-openjdk-devel git wget unzip which jq bc curl glibc-langpack-en
+ dnf -y groupinstall "Development Tools"
+
+ - name: Check Java version
+ run: java -version
+
+ - name: Cache Gradle
+ uses: actions/cache@v4
+ with:
+ path: |
+ /github/home/.gradle/caches
+ /github/home/.gradle/wrapper
+ key: rockylinux-x86_64-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ rockylinux-x86_64-gradle-
+
+ - name: Stop Gradle daemon
+ run: ./gradlew --stop || true
+
+ - name: Build
+ run: ./gradlew clean build --no-daemon
+
+ - name: Toolkit jar smoke test
+ run: |
+ set -e
+ JAR=plugins/build/libs/Toolkit.jar
+ java -jar "$JAR" help
+ java -jar "$JAR" db --help
+ java -jar "$JAR" db archive -h
+ java -jar "$JAR" keystore --help
+
+ - name: Test with RocksDB engine
+ run: ./gradlew :framework:testWithRocksDb --no-daemon
+
+ docker-build-debian11:
+ name: Build debian11 (JDK 8 / x86_64)
+ if: ${{ github.event_name == 'pull_request' || inputs.job == 'all' || inputs.job == 'debian11' }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+
+ container:
+ image: eclipse-temurin:8-jdk # base image is Debian 11 (Bullseye)
+
+ defaults:
+ run:
+ shell: bash
+
+ env:
+ GRADLE_USER_HOME: /github/home/.gradle
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ - name: Install dependencies (Debian + build tools)
+ run: |
+ set -euxo pipefail
+ apt-get update
+ apt-get install -y git wget unzip build-essential curl jq
+
+ - name: Check Java version
+ run: java -version
+
+ - name: Cache Gradle
+ uses: actions/cache@v4
+ with:
+ path: |
+ /github/home/.gradle/caches
+ /github/home/.gradle/wrapper
+ key: debian11-x86_64-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ debian11-x86_64-gradle-
+
+ - name: Build
+ run: ./gradlew clean build --no-daemon --no-build-cache
+
+ - name: Toolkit jar smoke test
+ run: |
+ set -e
+ JAR=plugins/build/libs/Toolkit.jar
+ java -jar "$JAR" help
+ java -jar "$JAR" db --help
+ java -jar "$JAR" db archive -h
+ java -jar "$JAR" keystore --help
+
+ - name: Test with RocksDB engine
+ run: ./gradlew :framework:testWithRocksDb --no-daemon --no-build-cache
+
+ - name: Generate module coverage reports
+ run: ./gradlew jacocoTestReport --no-daemon
+
+ - name: Upload PR coverage reports
+ uses: actions/upload-artifact@v6
+ with:
+ name: jacoco-coverage-pr
+ path: |
+ **/build/reports/jacoco/test/jacocoTestReport.xml
+ if-no-files-found: error
+
+ coverage-base:
+ name: Coverage Base (JDK 8 / x86_64)
+ if: ${{ github.event_name == 'pull_request' }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ container:
+ image: eclipse-temurin:8-jdk # base image is Debian 11 (Bullseye)
+ defaults:
+ run:
+ shell: bash
+ env:
+ GRADLE_USER_HOME: /github/home/.gradle
+ permissions:
+ contents: read
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+ with:
+ ref: ${{ github.event.pull_request.base.sha }}
+
+ - name: Install dependencies (Debian + build tools)
+ run: |
+ set -euxo pipefail
+ apt-get update
+ apt-get install -y git wget unzip build-essential curl jq
+
+ - name: Cache Gradle packages
+ uses: actions/cache@v4
+ with:
+ path: |
+ /github/home/.gradle/caches
+ /github/home/.gradle/wrapper
+ key: coverage-base-x86_64-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ coverage-base-x86_64-gradle-
+
+ - name: Build (base)
+ run: ./gradlew clean build --no-daemon --no-build-cache
+
+ - name: Test with RocksDB engine (base)
+ run: ./gradlew :framework:testWithRocksDb --no-daemon --no-build-cache
+
+ - name: Generate module coverage reports (base)
+ run: ./gradlew jacocoTestReport --no-daemon
+
+ - name: Upload base coverage reports
+ uses: actions/upload-artifact@v6
+ with:
+ name: jacoco-coverage-base
+ path: |
+ **/build/reports/jacoco/test/jacocoTestReport.xml
+ if-no-files-found: error
+
+ coverage-gate:
+ name: Coverage Gate
+ needs: [docker-build-debian11, coverage-base]
+ if: ${{ github.event_name == 'pull_request' }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ permissions:
+ contents: read
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+
+ - name: Download base coverage reports
+ uses: actions/download-artifact@v8
+ with:
+ name: jacoco-coverage-base
+ path: coverage/base
+
+ - name: Download PR coverage reports
+ uses: actions/download-artifact@v8
+ with:
+ name: jacoco-coverage-pr
+ path: coverage/pr
+
+ - name: Collect coverage report paths
+ id: collect-xml
+ run: |
+ BASE_XMLS=$(find coverage/base -name "jacocoTestReport.xml" | sort | paste -sd, -)
+ PR_XMLS=$(find coverage/pr -name "jacocoTestReport.xml" | sort | paste -sd, -)
+ if [ -z "$BASE_XMLS" ] || [ -z "$PR_XMLS" ]; then
+ echo "Missing jacocoTestReport.xml files for base or PR."
+ exit 1
+ fi
+ echo "base_xmls=$BASE_XMLS" >> "$GITHUB_OUTPUT"
+ echo "pr_xmls=$PR_XMLS" >> "$GITHUB_OUTPUT"
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ - name: Changed-line coverage (diff-cover)
+ id: diff-cover
+ env:
+ BASE_REF: ${{ github.event.pull_request.base.ref }}
+ run: |
+ set -euo pipefail
+ pip install --quiet 'diff-cover==9.2.0'
+
+ # Ensure the base branch ref is available locally for diff-cover.
+ git fetch --no-tags origin "+refs/heads/${BASE_REF}:refs/remotes/origin/${BASE_REF}"
+
+ PR_XMLS=$(find coverage/pr -name "jacocoTestReport.xml" | sort)
+ SRC_ROOTS=$(find . -type d -path '*/src/main/java' \
+ -not -path './coverage/*' -not -path './.git/*' | sort)
+ if [ -z "$SRC_ROOTS" ]; then
+ echo "No src/main/java directories found; cannot run diff-cover." >&2
+ exit 1
+ fi
+
+ set +e
+ diff-cover $PR_XMLS \
+ --compare-branch="origin/${BASE_REF}" \
+ --src-roots $SRC_ROOTS \
+ --fail-under=0 \
+ --json-report=diff-cover.json \
+ --markdown-report=diff-cover.md
+ DIFF_RC=$?
+ set -e
+
+ if [ ! -f diff-cover.json ]; then
+ echo "diff-cover did not produce JSON report (exit=${DIFF_RC})." >&2
+ exit 1
+ fi
+
+ TOTAL_NUM_LINES=$(jq -r '.total_num_lines // 0' diff-cover.json)
+ if [ "${TOTAL_NUM_LINES}" = "0" ]; then
+ echo "No changed Java source lines; skipping changed-line gate."
+ echo "changed_line_coverage=NA" >> "$GITHUB_OUTPUT"
+ else
+ CHANGED_LINE_COVERAGE=$(jq -r '.total_percent_covered // empty' diff-cover.json)
+ if [ -z "$CHANGED_LINE_COVERAGE" ]; then
+ echo "Unable to parse changed-line coverage from diff-cover.json."
+ exit 1
+ fi
+ echo "changed_line_coverage=${CHANGED_LINE_COVERAGE}" >> "$GITHUB_OUTPUT"
+ fi
+
+ {
+ echo "### Changed-line Coverage (diff-cover)"
+ echo ""
+ if [ -f diff-cover.md ] && [ -s diff-cover.md ]; then
+ cat diff-cover.md
+ else
+ echo "_diff-cover produced no report._"
+ fi
+ } >> "$GITHUB_STEP_SUMMARY"
+
+ - name: Aggregate base coverage
+ id: jacoco-base
+ uses: madrapps/jacoco-report@v1.7.2
+ with:
+ paths: ${{ steps.collect-xml.outputs.base_xmls }}
+ token: ${{ secrets.GITHUB_TOKEN }}
+ min-coverage-overall: 0
+ min-coverage-changed-files: 0
+ skip-if-no-changes: true
+ comment-type: summary
+ title: '## Base Coverage Snapshot'
+ update-comment: false
+
+ - name: Aggregate PR coverage
+ id: jacoco-pr
+ uses: madrapps/jacoco-report@v1.7.2
+ with:
+ paths: ${{ steps.collect-xml.outputs.pr_xmls }}
+ token: ${{ secrets.GITHUB_TOKEN }}
+ min-coverage-overall: 0
+ min-coverage-changed-files: 0
+ skip-if-no-changes: true
+ comment-type: summary
+ title: '## PR Code Coverage Report'
+ update-comment: false
+
+ - name: Enforce coverage gates
+ env:
+ BASE_OVERALL_RAW: ${{ steps.jacoco-base.outputs.coverage-overall }}
+ PR_OVERALL_RAW: ${{ steps.jacoco-pr.outputs.coverage-overall }}
+ CHANGED_LINE_RAW: ${{ steps.diff-cover.outputs.changed_line_coverage }}
+ run: |
+ set -euo pipefail
+
+ MIN_CHANGED=60
+ MAX_DROP=-0.1
+
+ sanitize() {
+ echo "$1" | tr -d ' %'
+ }
+ is_number() {
+ [[ "$1" =~ ^-?[0-9]+([.][0-9]+)?$ ]]
+ }
+ compare_float() {
+ # Usage: compare_float ""
+ # Example: compare_float "1.2 >= -0.1"
+ awk "BEGIN { if ($1) print 1; else print 0 }"
+ }
+
+ # 1) Parse metrics from jacoco-report outputs
+ BASE_OVERALL="$(sanitize "$BASE_OVERALL_RAW")"
+ PR_OVERALL="$(sanitize "$PR_OVERALL_RAW")"
+ CHANGED_LINE="$(sanitize "$CHANGED_LINE_RAW")"
+
+ if ! is_number "$BASE_OVERALL" || ! is_number "$PR_OVERALL"; then
+ echo "Failed to parse coverage values: base='${BASE_OVERALL}', pr='${PR_OVERALL}'."
+ exit 1
+ fi
+
+ # 2) Compare metrics against thresholds
+ DELTA=$(awk -v pr="$PR_OVERALL" -v base="$BASE_OVERALL" 'BEGIN { printf "%.4f", pr - base }')
+ DELTA_OK=$(compare_float "${DELTA} >= ${MAX_DROP}")
+
+ if [ "$CHANGED_LINE" = "NA" ]; then
+ CHANGED_LINE_OK=1
+ CHANGED_LINE_STATUS="SKIPPED (no changed Java source lines)"
+ elif [ -z "$CHANGED_LINE" ] || [ "$CHANGED_LINE" = "NaN" ] || ! is_number "$CHANGED_LINE"; then
+ echo "Failed to parse changed-line coverage: changed-line='${CHANGED_LINE}'."
+ exit 1
+ else
+ CHANGED_LINE_OK=$(compare_float "${CHANGED_LINE} > ${MIN_CHANGED}")
+ if [ "$CHANGED_LINE_OK" -eq 1 ]; then
+ CHANGED_LINE_STATUS="PASS (> ${MIN_CHANGED}%)"
+ else
+ CHANGED_LINE_STATUS="FAIL (<= ${MIN_CHANGED}%)"
+ fi
+ fi
+
+ # 3) Output base metrics (always visible in logs + step summary)
+ OVERALL_STATUS="PASS (>= ${MAX_DROP}%)"
+ if [ "$DELTA_OK" -ne 1 ]; then
+ OVERALL_STATUS="FAIL (< ${MAX_DROP}%)"
+ fi
+
+ if [ "$CHANGED_LINE" = "NA" ]; then
+ CHANGED_LINE_DISPLAY="NA"
+ else
+ CHANGED_LINE_DISPLAY="${CHANGED_LINE}%"
+ fi
+
+ METRICS_TEXT=$(cat <> "$GITHUB_STEP_SUMMARY"
+
+ # 4) Decide CI pass/fail
+ if [ "$DELTA_OK" -ne 1 ]; then
+ echo "Coverage gate failed: overall coverage dropped more than 0.1%."
+ echo "base=${BASE_OVERALL}% pr=${PR_OVERALL}% delta=${DELTA}%"
+ exit 1
+ fi
+
+ if [ "$CHANGED_LINE_OK" -ne 1 ]; then
+ echo "Coverage gate failed: changed-line coverage must be > 60%."
+ echo "changed-line=${CHANGED_LINE}%"
+ exit 1
+ fi
+
+ echo "Coverage gates passed."
diff --git a/.github/workflows/pr-cancel.yml b/.github/workflows/pr-cancel.yml
new file mode 100644
index 00000000000..bbd0e68c235
--- /dev/null
+++ b/.github/workflows/pr-cancel.yml
@@ -0,0 +1,55 @@
+name: Cancel PR Workflows on Close
+
+on:
+ pull_request:
+ types: [ closed ]
+
+permissions:
+ actions: write
+
+jobs:
+ cancel:
+ name: Cancel In-Progress Workflows
+ if: github.event.pull_request.merged == false
+ runs-on: ubuntu-latest
+ steps:
+ - name: Cancel PR Build and System Test
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const workflows = ['pr-build.yml', 'system-test.yml', 'codeql.yml'];
+ const headSha = context.payload.pull_request.head.sha;
+ const prNumber = context.payload.pull_request.number;
+
+ for (const workflowId of workflows) {
+ for (const status of ['in_progress', 'queued']) {
+ const runs = await github.paginate(
+ github.rest.actions.listWorkflowRuns,
+ {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ workflow_id: workflowId,
+ status,
+ event: 'pull_request',
+ per_page: 100,
+ },
+ (response) => response.data.workflow_runs
+ );
+
+ for (const run of runs) {
+ if (!run) {
+ continue;
+ }
+ const prs = Array.isArray(run.pull_requests) ? run.pull_requests : [];
+ const isTargetPr = prs.length === 0 || prs.some((pr) => pr.number === prNumber);
+ if (run.head_sha === headSha && isTargetPr) {
+ await github.rest.actions.cancelWorkflowRun({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ run_id: run.id,
+ });
+ console.log(`Cancelled ${workflowId} run #${run.id} (${status})`);
+ }
+ }
+ }
+ }
diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml
new file mode 100644
index 00000000000..19425209bbc
--- /dev/null
+++ b/.github/workflows/pr-check.yml
@@ -0,0 +1,131 @@
+name: PR Check
+
+on:
+ push:
+ branches: [ 'master', 'release_**' ]
+ pull_request:
+ branches: [ 'develop', 'release_**' ]
+ types: [ opened, edited, synchronize, reopened ]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ pr-lint:
+ name: PR Lint
+ if: github.event_name == 'pull_request'
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Validate PR title and description
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const title = context.payload.pull_request.title;
+ const body = context.payload.pull_request.body;
+ const errors = [];
+ const warnings = [];
+
+ const allowedTypes = ['feat','fix','refactor','docs','style','test','chore','ci','perf','build','revert'];
+ const knownScopes = [
+ 'framework','chainbase','actuator','consensus','common','crypto','plugins','protocol',
+ 'net','db','vm','tvm','api','jsonrpc','rpc','http','event','config',
+ 'block','proposal','trie','log','metrics','test','docker','version',
+ 'freezeV2','DynamicEnergy','stable-coin','reward','lite','toolkit'
+ ];
+
+ // 1. Title length check
+ if (!title || title.trim().length < 10) {
+ errors.push('PR title is too short (minimum 10 characters).');
+ }
+ if (title && title.length > 72) {
+ errors.push(`PR title is too long (${title.length}/72 characters).`);
+ }
+
+ // 2. Conventional format check
+ const conventionalRegex = /^(feat|fix|refactor|docs|style|test|chore|ci|perf|build|revert)(\([^)]+\))?:\s\S.*/;
+ if (title && !conventionalRegex.test(title)) {
+ errors.push(
+ 'PR title must follow conventional format: `type(scope): description`\n' +
+ ' Allowed types: ' + allowedTypes.map(t => `\`${t}\``).join(', ') + '\n' +
+ ' Example: `feat(tvm): add blob opcodes`'
+ );
+ }
+
+ // 3. No trailing period
+ if (title && title.endsWith('.')) {
+ errors.push('PR title should not end with a period (.).');
+ }
+
+ // 4. Description part should not start with a capital letter
+ if (title) {
+ const descMatch = title.match(/^\w+(?:\([^)]+\))?:\s*(.+)/);
+ if (descMatch) {
+ const desc = descMatch[1];
+ if (/^[A-Z]/.test(desc)) {
+ errors.push('Description should not start with a capital letter.');
+ }
+ }
+ }
+
+ // 5. Scope validation (warning only)
+ if (title) {
+ const scopeMatch = title.match(/^\w+\(([^)]+)\):/);
+ if (scopeMatch && !knownScopes.includes(scopeMatch[1])) {
+ warnings.push(`Unknown scope \`${scopeMatch[1]}\`. See CONTRIBUTING.md for known scopes.`);
+ }
+ }
+
+ // 6. PR description check
+ if (!body || body.trim().length < 20) {
+ errors.push('PR description is too short or empty (minimum 20 characters). Please describe what this PR does and why.');
+ }
+
+ // Output warnings
+ for (const w of warnings) {
+ core.warning(w);
+ }
+
+ // Output result
+ if (errors.length > 0) {
+ const docLink = 'See [CONTRIBUTING.md](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/blob/develop/CONTRIBUTING.md#pull-request-guidelines) for details.';
+ const message = '### PR Lint Failed\n\n' + errors.map(e => `- ${e}`).join('\n') + '\n\n' + docLink;
+ core.setFailed(message);
+ } else {
+ core.info('PR lint passed.');
+ }
+
+ checkstyle:
+ name: Checkstyle
+ runs-on: ubuntu-24.04-arm
+
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v5
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+
+ - name: Cache Gradle packages
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }}
+ restore-keys: ${{ runner.os }}-gradle-
+
+ - name: Run Checkstyle
+ run: ./gradlew :framework:checkstyleMain :framework:checkstyleTest :plugins:checkstyleMain
+
+ - name: Upload Checkstyle reports
+ if: failure()
+ uses: actions/upload-artifact@v6
+ with:
+ name: checkstyle-reports
+ path: |
+ framework/build/reports/checkstyle/
+ plugins/build/reports/checkstyle/
diff --git a/.github/workflows/pr-reviewer.yml b/.github/workflows/pr-reviewer.yml
new file mode 100644
index 00000000000..bf124acf576
--- /dev/null
+++ b/.github/workflows/pr-reviewer.yml
@@ -0,0 +1,144 @@
+name: Auto Assign Reviewers
+
+on:
+ pull_request_target:
+ branches: [ 'develop', 'release_**' ]
+ types: [ opened, edited, reopened ]
+
+jobs:
+ assign-reviewers:
+ name: Assign Reviewers by Scope
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+
+ steps:
+ - name: Assign reviewers based on PR title scope
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const title = context.payload.pull_request.title;
+ const prAuthor = context.payload.pull_request.user.login;
+
+ // ── Scope → Reviewer mapping ──────────────────────────────
+ const scopeReviewers = {
+ 'framework': ['xxo1shine', '317787106'],
+ 'chainbase': ['halibobo1205', 'lvs0075'],
+ 'db': ['halibobo1205', 'xxo1shine'],
+ 'trie': ['halibobo1205', '317787106'],
+ 'actuator': ['yanghang8612', 'lxcmyf'],
+ 'consensus': ['lvs0075', 'xxo1shine'],
+ 'protocol': ['lvs0075', 'waynercheung'],
+ 'common': ['xxo1shine', 'lxcmyf'],
+ 'crypto': ['Federico2014', '3for'],
+ 'net': ['317787106', 'xxo1shine'],
+ 'vm': ['yanghang8612', 'CodeNinjaEvan'],
+ 'tvm': ['yanghang8612', 'CodeNinjaEvan'],
+ 'jsonrpc': ['0xbigapple', 'bladehan1'],
+ 'rpc': ['317787106', 'Sunny6889'],
+ 'http': ['Sunny6889', 'bladehan1'],
+ 'event': ['xxo1shine', '0xbigapple'],
+ 'config': ['317787106', 'halibobo1205'],
+ 'backup': ['xxo1shine', '317787106'],
+ 'lite': ['bladehan1', 'halibobo1205'],
+ 'toolkit': ['halibobo1205', 'Sunny6889'],
+ 'plugins': ['halibobo1205', 'Sunny6889'],
+ 'docker': ['3for', 'kuny0707'],
+ 'test': ['bladehan1', 'lxcmyf'],
+ 'metrics': ['halibobo1205', 'Sunny6889'],
+ 'api': ['0xbigapple', 'waynercheung', 'bladehan1'],
+ 'ci': ['bladehan1', 'halibobo1205'],
+ };
+ const defaultReviewers = ['halibobo1205', '317787106'];
+
+ // ── Normalize helper ─────────────────────────────────────
+ // Strip spaces, hyphens, underscores and lower-case so that
+ // "VM", " json rpc ", "chain-base", "Json_Rpc" all normalize
+ // to their canonical key form ("vm", "jsonrpc", "chainbase").
+ const normalize = s => s.toLowerCase().replace(/[\s\-_]/g, '');
+
+ // ── Extract scope from conventional commit title ──────────
+ // Format: type(scope): description
+ // Also supports: type(scope1,scope2): description
+ const scopeMatch = title.match(/^\w+\(([^)]+)\):/);
+ const rawScope = scopeMatch ? scopeMatch[1] : null;
+
+ core.info(`PR title : ${title}`);
+ core.info(`Raw scope: ${rawScope || '(none)'}`);
+
+ // ── Skip if reviewers already assigned ──────────────────
+ const pr = await github.rest.pulls.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.payload.pull_request.number,
+ });
+ const existing = pr.data.requested_reviewers || [];
+ if (existing.length > 0) {
+ core.info(`Reviewers already assigned (${existing.map(r => r.login).join(', ')}). Skipping.`);
+ return;
+ }
+
+ // ── Determine reviewers ───────────────────────────────────
+ // 1. Split by comma to support multi-scope: feat(vm,rpc): ...
+ // 2. Normalize each scope token
+ // 3. Match against keys: exact match first, then contains match
+ // (longest key wins to avoid "net" matching inside "jsonrpc")
+ let matched = new Set();
+ let matchedScopes = [];
+
+ if (rawScope) {
+ const tokens = rawScope.split(',').map(s => normalize(s.trim()));
+ // Pre-sort keys by length descending so longer keys match first
+ const sortedKeys = Object.keys(scopeReviewers)
+ .sort((a, b) => b.length - a.length);
+
+ for (const token of tokens) {
+ if (!token) continue;
+ // Exact match
+ if (scopeReviewers[token]) {
+ matchedScopes.push(token);
+ scopeReviewers[token].forEach(r => matched.add(r));
+ continue;
+ }
+ // Contains match: token contains a key, or key contains token
+ // Prefer longest key that matches
+ const found = sortedKeys.find(k => token.includes(k) || k.includes(token));
+ if (found) {
+ matchedScopes.push(`${token}→${found}`);
+ scopeReviewers[found].forEach(r => matched.add(r));
+ }
+ }
+ }
+
+ let reviewers = matched.size > 0
+ ? [...matched]
+ : defaultReviewers;
+
+ core.info(`Matched scopes: ${matchedScopes.length > 0 ? matchedScopes.join(', ') : '(none — using default)'}`);
+ core.info(`Candidate reviewers: ${reviewers.join(', ')}`);
+
+ // Exclude the PR author from the reviewer list
+ reviewers = reviewers.filter(r => r.toLowerCase() !== prAuthor.toLowerCase());
+
+ if (reviewers.length === 0) {
+ core.info('No eligible reviewers after excluding PR author. Skipping.');
+ return;
+ }
+
+ core.info(`Assigning reviewers: ${reviewers.join(', ')}`);
+
+ // ── Request reviews ───────────────────────────────────────
+ try {
+ await github.rest.pulls.requestReviewers({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.payload.pull_request.number,
+ reviewers: reviewers,
+ });
+ core.info('Reviewers assigned successfully.');
+ } catch (error) {
+ // If a reviewer is not a collaborator the API returns 422;
+ // log the error but do not fail the workflow.
+ core.warning(`Failed to assign some reviewers: ${error.message}`);
+ }
diff --git a/.github/workflows/system-test.yml b/.github/workflows/system-test.yml
new file mode 100644
index 00000000000..f6184fb0efc
--- /dev/null
+++ b/.github/workflows/system-test.yml
@@ -0,0 +1,95 @@
+name: System Test
+
+on:
+ push:
+ branches: [ 'master', 'release_**' ]
+ pull_request:
+ branches: [ 'develop', 'release_**' ]
+ types: [ opened, synchronize, reopened ]
+ paths-ignore: [ '**/*.md', '.gitignore', '**/.gitignore', '.editorconfig',
+ '.gitattributes', 'docs/**', 'CHANGELOG', '.github/ISSUE_TEMPLATE/**',
+ '.github/PULL_REQUEST_TEMPLATE/**', '.github/CODEOWNERS' ]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ system-test:
+ name: System Test (JDK 8 / x86_64)
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+
+ steps:
+ - name: Set up JDK 8
+ uses: actions/setup-java@v5
+ with:
+ java-version: '8'
+ distribution: 'temurin'
+
+ - name: Clone system-test
+ uses: actions/checkout@v5
+ with:
+ repository: tronprotocol/system-test
+ ref: release_workflow
+ path: system-test
+
+ - name: Checkout java-tron
+ uses: actions/checkout@v5
+ with:
+ path: java-tron
+
+ - name: Cache Gradle packages
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-system-test-${{ hashFiles('java-tron/**/*.gradle', 'java-tron/**/gradle-wrapper.properties') }}
+ restore-keys: ${{ runner.os }}-gradle-system-test-
+
+ - name: Build java-tron
+ working-directory: java-tron
+ run: ./gradlew clean build -x test --no-daemon
+
+ - name: Copy config and start FullNode
+ run: |
+ cp system-test/testcase/src/test/resources/config-system-test.conf java-tron/
+ cd java-tron
+ nohup java -jar build/libs/FullNode.jar --witness -c config-system-test.conf > fullnode.log 2>&1 &
+ echo "FullNode started, waiting for it to be ready..."
+
+ MAX_ATTEMPTS=60
+ INTERVAL=5
+ for i in $(seq 1 $MAX_ATTEMPTS); do
+ if curl -s --fail "http://localhost:8090/wallet/getblockbynum?num=1" > /dev/null 2>&1; then
+ echo "FullNode is ready! (attempt $i)"
+ exit 0
+ fi
+ echo "Waiting... (attempt $i/$MAX_ATTEMPTS)"
+ sleep $INTERVAL
+ done
+
+ echo "FullNode failed to start within $((MAX_ATTEMPTS * INTERVAL)) seconds."
+ echo "=== FullNode log (last 50 lines) ==="
+ tail -50 fullnode.log || true
+ exit 1
+
+ - name: Run system tests
+ working-directory: system-test
+ run: |
+ if [ ! -f solcDIR/solc-linux-0.8.6 ]; then
+ echo "ERROR: solc binary not found at solcDIR/solc-linux-0.8.6"
+ exit 1
+ fi
+ cp solcDIR/solc-linux-0.8.6 solcDIR/solc
+ ./gradlew clean --no-daemon
+ ./gradlew --info stest --no-daemon
+
+ - name: Upload FullNode log
+ if: always()
+ uses: actions/upload-artifact@v6
+ with:
+ name: fullnode-log
+ path: java-tron/fullnode.log
+ if-no-files-found: warn
diff --git a/.gitignore b/.gitignore
index b980800f353..3917bb44679 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,7 +31,6 @@ shareddata.*
# protobuf generated classes
src/main/gen
-
src/main/java/org/tron/core/bftconsensus
src/test/java/org/tron/consensus2
src/main/java/META-INF/
@@ -55,3 +54,6 @@ Wallet
# vm_trace
/vm_trace/
+
+/framework/propPath
+.cache
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d8dc83ad51b..ef67a81e3ee 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -16,24 +16,27 @@ Here are some guidelines to get started quickly and easily:
- [Commit Messages](#Commit-Messages)
- [Branch Naming Conventions](#Branch-Naming-Conventions)
- [Pull Request Guidelines](#Pull-Request-Guidelines)
+ - [PR Title Format](#PR-Title-Format)
+ - [Type and Scope Reference](#Type-and-Scope-Reference)
+ - [PR Description](#PR-Description)
- [Special Situations And How To Deal With Them](#Special-Situations-And-How-To-Deal-With-Them)
- [Conduct](#Conduct)
-### Reporting An Issue
+## Reporting An Issue
-If you're about to raise an issue because you think you've found a problem or bug with java-tron, please respect the following restrictions:
+If you have any question about java-tron, please search [existing issues](https://github.com/tronprotocol/java-tron/issues?q=is%3Aissue%20state%3Aclosed%20OR%20state%3Aopen) first to avoid duplicates. Your questions might already be under discussion or part of our roadmap. Checking first helps us streamline efforts and focus on new contributions.
-- Please search for existing issues. Help us keep duplicate issues to a minimum by checking to see if someone has already reported your problem or requested your idea.
+### Ask a question
+Feel free to ask any java-tron related question to solve your doubt. Please click **Ask a question** in GitHub Issues, using [Ask a question](.github/ISSUE_TEMPLATE/ask-a-question.md) template.
-- Use the Issue Report Template below.
- ```
- 1.What did you do?
+### Report a bug
- 2.What did you expect to see?
+If you think you've found a bug with java-tron, please click **Report a bug** in GitHub Issues, using [Report a bug](.github/ISSUE_TEMPLATE/report-a-bug.md) template.
- 3.What did you see instead?
- ```
+### Request a feature
+
+If you have any good feature suggestions for java-tron, please click **Request a feature** in GitHub Issues, using [Request a feature](.github/ISSUE_TEMPLATE/request-a-feature.md) template.
## Working on java-tron
@@ -66,43 +69,56 @@ java-tron only has `master`, `develop`, `release-*`, `feature-*`, and `hotfix-*`
### Submitting Code
-If you want to contribute codes to java-tron, please follow the following steps:
+If you want to contribute code to java-tron, please follow the following steps.
+
+* Fork the Repository
-* Fork code repository
- Fork a new repository from tronprotocol/java-tron to your personal code repository
+ Visit [tronprotocol/java-tron](https://github.com/tronprotocol/java-tron/) and click **Fork** to create a fork repository under your GitHub account.
-* Edit the code in the fork repository
+* Setup Local Environment
+
+ Clone your fork repository to local and add the official repository as **upstream**.
```
git clone https://github.com/yourname/java-tron.git
- git remote add upstream https://github.com/tronprotocol/java-tron.git ("upstream" refers to upstream projects repositories, namely tronprotocol's repositories, and can be named as you like it. We usually call it "upstream" for convenience)
+ cd java-tron
+
+ git remote add upstream https://github.com/tronprotocol/java-tron.git
```
- Before developing new features, please synchronize your fork repository with the upstream repository.
+
+* Synchronize and Develop
+
+ Before developing new features, please synchronize your local `develop` branch with the upstream repository and update to your fork repository.
```
- git fetch upstream
- git checkout develop
- git merge upstream/develop --no-ff (Add --no-ff to turn off the default fast merge mode)
+ git fetch upstream
+ git checkout develop
+ # `--no-ff` means to turn off the default fast merge mode
+ git merge upstream/develop --no-ff
+ git push origin develop
```
- Pull a new branch from the develop branch of your repository for local development. Please refer to [Branch Naming Conventions](#Branch-Naming-Conventions),
+ Create a new branch for development. Please refer to [Branch Naming Conventions](#Branch-Naming-Conventions).
```
git checkout -b feature/branch_name develop
```
- Write and commit the new code when it is completed. Please refer to [Commit Messages](#Commit-Messages)
+* Commit and Push
+
+ Write and commit the new code when it is completed. Please refer to [Commit Messages](#Commit-Messages).
```
git add .
git commit -m 'commit message'
```
- Commit the new branch to your personal remote repository
+
+ Push the new branch to your fork repository
```
git push origin feature/branch_name
```
-* Push code
+* Submit a pull request
- Submit a pull request (PR) from your repository to `tronprotocol/java-tron`.
- Please be sure to click on the link in the red box shown below. Select the base branch for tronprotocol and the compare branch for your personal fork repository.
+ Submit a pull request (PR) from your fork repository to `tronprotocol/java-tron`.
+ Please be sure to click on the link in the red box shown below. Select the base branch for `tronprotocol/java-tron` and the compare branch for your fork repository.

@@ -131,7 +147,7 @@ We would like all developers to follow a standard development flow and coding st
2. Review the code before submission.
3. Run standardized tests.
-`Sonar`-scanner and `Travis CI` continuous integration scanner will be automatically triggered when a pull request has been submitted. When a PR passes all the checks, the **java-tron** maintainers will then review the PR and offer feedback and modifications when necessary. Once adopted, the PR will be closed and merged into the `develop` branch.
+`Sonar`-scanner and CI checks (GitHub Actions) will be automatically triggered when a pull request has been submitted. When a PR passes all the checks, the **java-tron** maintainers will then review the PR and offer feedback and modifications when necessary. Once adopted, the PR will be closed and merged into the `develop` branch.
We are glad to receive your pull requests and will try our best to review them as soon as we can. Any pull request is welcome, even if it is for a typo.
@@ -145,13 +161,13 @@ Please make sure your submission meets the following code style:
- The code must have passed the Sonar scanner test.
- The code has to be pulled from the `develop` branch.
- The commit message should start with a verb, whose initial should not be capitalized.
-- The commit message should be less than 50 characters in length.
+- The commit message title should be between 10 and 72 characters in length.
### Commit Messages
-Commit messages should follow the rule below, we provide a template corresponding instructions.
+Commit messages should follow the rule below, we provide a template with corresponding instructions.
Template:
```
@@ -172,17 +188,21 @@ The message header is a single line that contains succinct description of the ch
* refactor (refactoring production code)
* test (adding or refactoring tests. no production code change)
* chore (updating grunt tasks etc. no production code change)
+* ci (CI/CD configuration)
+* perf (performance improvement)
+* build (build system changes)
+* revert (reverting a previous commit)
-The `scope` can be anything specifying place of the commit change. For example:`protobuf`,`api`,`test`,`docs`,`build`,`db`,`net`.You can use * if there isn't a more fitting scope.
+The `scope` can be anything specifying place of the commit change. For example: `framework`, `api`, `tvm`, `db`, `net`. For a full list of scopes, see [Type and Scope Reference](#type-and-scope-reference). You can use `*` if there isn't a more fitting scope.
The subject contains a succinct description of the change:
-1. Limit the subject line, which briefly describes the purpose of the commit, to 50 characters.
+1. Limit the subject line, which briefly describes the purpose of the commit, to 72 characters (minimum 10).
2. Start with a verb and use first-person present-tense (e.g., use "change" instead of "changed" or "changes").
3. Do not capitalize the first letter.
4. Do not end the subject line with a period.
5. Avoid meaningless commits. It is recommended to use the git rebase command.
-Message body use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
+Message body uses the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
Here is an example:
```
@@ -204,20 +224,103 @@ If the purpose of this submission is to modify one issue, you need to refer to t
4. Use `feature/` as the prefix of the `feature` branch, briefly describe the feature in the name, and connect words with underline (e.g., feature/new_resource_model, etc.).
### Pull Request Guidelines
+#### PR Title Format
+
+PR titles must follow the conventional commit format and will be checked by CI:
+
+```
+type(scope): description
+```
+
+| Rule | Requirement |
+|------|-------------|
+| Format | `type: description` or `type(scope): description` |
+| Length | 10 ~ 72 characters |
+| Type must be one of | `feat` `fix` `refactor` `docs` `style` `test` `chore` `ci` `perf` `build` `revert` |
+
+#### Type and Scope Reference
+
+**Type Reference**
+
+| Type | Purpose | Example |
+|------|---------|---------|
+| `feat` | New feature | `feat(tvm): add blob opcodes` |
+| `fix` | Bug fix | `fix(db): improve resource management` |
+| `docs` | Documentation only | `docs: fix formatting issues in README` |
+| `style` | Code style (no logic change) | `style: fix import order and line length` |
+| `refactor` | Code refactoring (no behavior change) | `refactor(config): simplify parameters` |
+| `test` | Adding or updating tests | `test(vm): add unit tests for opcodes` |
+| `chore` | Build tooling, dependencies, etc. | `chore(version): bump to v4.7.8` |
+| `ci` | CI/CD configuration | `ci: add PR check workflow` |
+| `perf` | Performance improvement | `perf(trie): optimize query performance` |
+| `build` | Build system changes | `build: add aarch64 support for RocksDB` |
+| `revert` | Reverting a previous commit | `revert: restore ApiUtilTest.java` |
+
+**Module Scopes**
+
+| Scope | Description |
+|-------|-------------|
+| `framework` | Core framework, services, APIs, RPC interfaces |
+| `chainbase` | Blockchain storage, state management, database layer |
+| `actuator` | Transaction execution engine, smart contract operations |
+| `consensus` | Consensus mechanism (DPoS, PBFT) |
+| `common` | Common utilities, configuration, shared infrastructure |
+| `crypto` | Cryptographic functions, key management, signatures |
+| `plugins` | Node tools (Toolkit, ArchiveManifest, database plugins) |
+| `protocol` | Protocol definitions, protobuf messages, gRPC contracts |
+
+**Functional Domain Scopes**
+
+| Scope | Description | Example |
+|-------|-------------|---------|
+| `net` | P2P networking, message handling, peer sync | `feat(net): optimize sync logic` |
+| `db` | Database operations, queries, persistence | `fix(db): handle null pointer in query` |
+| `vm` / `tvm` | Virtual machine, bytecode execution, EIP impl | `feat(tvm): implement eip-7823` |
+| `api` | HTTP/gRPC API endpoints | `fix(api): handle null response` |
+| `jsonrpc` | JSON-RPC interface (Ethereum-compatible) | `fix(jsonrpc): support blockHash param` |
+| `rpc` | gRPC services and methods | `fix(rpc): handle timeout correctly` |
+| `http` | HTTP server and endpoints | `feat(http): add new endpoint` |
+| `event` | Event logging and event service | `feat(event): optimize concurrent writes` |
+| `config` | Configuration management, feature flags | `refactor(config): simplify parameters` |
+| `block` | Block processing, validation, structure | `fix(block): validate block header` |
+| `proposal` | On-chain governance proposals | `feat(proposal): add Osaka proposal` |
+| `trie` | Merkle tree, state trie operations | `perf(trie): optimize tree query` |
+| `log` | Application logging | `refactor(log): reduce noise` |
+| `metrics` | Performance monitoring, Prometheus | `feat(metrics): add Prometheus support` |
+| `test` | Test infrastructure and utilities | `test(proposal): add unit test cases` |
+| `docker` | Docker containerization and deployment | `feat(docker): add ARM64 support` |
+| `version` | Version and release management | `chore(version): bump to v4.7.8` |
+
+**Feature Scopes**
+
+| Scope | Description |
+|-------|-------------|
+| `freezeV2` | Resource delegation / freeze-unfreeze V2 mechanism |
+| `DynamicEnergy` | Dynamic energy pricing mechanism |
+| `stable-coin` | Stable coin features and operations |
+| `reward` | Block producer rewards distribution |
+| `lite` | Lite fullnode functionality |
+| `toolkit` | Node maintenance tools (Toolkit.jar) |
+
+#### PR Description
+
+- PR description must not be empty, minimum **20 characters**.
+- Should explain **what** the PR does and **why**.
+
+#### General Rules
+
1. Create one PR for one issue.
2. Avoid massive PRs.
-3. Write an overview of the purpose of the PR in its title.
-4. Write a description of the PR for future reviewers.
-5. Elaborate on the feedback you need (if any).
-6. Do not capitalize the first letter.
-7. Do not put a period (.) in the end.
+3. Elaborate on the feedback you need (if any).
+4. Do not capitalize the first letter of the description.
+5. Do not put a period (.) at the end of the title.
### Special Situations And How To Deal With Them
-As a reviewer, you may find yourself in one of the sitations below. Here’s how to deal with those:
+As a reviewer, you may find yourself in one of the situations below. Here’s how to deal with those:
The author doesn’t follow up: ping them after a while (i.e. after a few days). If there is no further response, close the PR or complete the work yourself.
diff --git a/DownloadLinks.sh b/DownloadLinks.sh
deleted file mode 100644
index babc5a266f6..00000000000
--- a/DownloadLinks.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-PassFlag=`curl -s http://47.95.206.44:50080/Daily_Build_Task_Report | grep "Failed: 0" | wc -c`
-
-if [ $PassFlag -eq 0 ]; then
- echo "Daily Build Stest Fail"
- echo "To view Daily Replay and Stress Test logs please visit website below on browsers"
- echo "--- http://47.95.206.44:50080/latestReplayLog"
- echo "--- http://47.95.206.44:50080/latestStressLog"
-
-else
- echo "Daily Build Stest Pass"
- echo "Build on `date +"%Y-%m-%d"` 3:00:00 (CST), UTC +8"
- echo "Please visit following website to download java-tron.jar on browsers"
- echo "--- http://47.95.206.44:50080/Daily_Build/jFava-tron.jar"
- echo "To view Daily Replay and Stress Test logs please visit website below on browsers"
- echo "--- http://47.95.206.44:50080/latestReplayLog"
- echo "--- http://47.95.206.44:50080/latestStressLog"
- echo "The following compressed package is provided for user to set up Fullnode. Please use Linux OS to Download"
- echo "--- curl -# -O http://47.95.206.44:50080/Daily_Build/java-tron.tar.gz"
- echo "To unzip file use the command below"
- echo "--- tar -xzvf java-tron.tar.gz"
-fi
\ No newline at end of file
diff --git a/METRICS_CHANGELOG.md b/METRICS_CHANGELOG.md
new file mode 100644
index 00000000000..3c599796d7a
--- /dev/null
+++ b/METRICS_CHANGELOG.md
@@ -0,0 +1,94 @@
+Metrics Changelog
+=================
+
+This file tracks Prometheus metric additions, changes, and removals in java-tron. For the full set of metrics emitted today, see the references at the bottom.
+
+**4.8.2**
+
+### New Metrics
+
+#### Core
+
+- `tron:block_transaction_count` (Histogram, label `miner`) — per-block transaction count, sampled at the entry of `Manager#pushBlock` before any early return so duplicate, stale, and fork-switched pushes are observed alongside applied blocks. Primary use cases: empty-block detection per super representative, and per-SR TPS / throughput percentile interpolation. Default buckets `[0, 20, 50, 80, 100, 120, 140, 160, 180, 200, 230, 260, 300, 500, 2000, 5000, 10000]` are densified around 0–300 for percentile interpolation in the typical TPS range; 5000 and 10000 are retained as safety-net buckets to preserve resolution for outlier events such as stress tests or repush storms. ([#6624](https://github.com/tronprotocol/java-tron/pull/6624))
+
+ > **Operational note:** The effective upper bound is 10000; blocks exceeding that land in `+Inf`. Monitor the overflow ratio — e.g. `(rate(tron_block_transaction_count_bucket{le="+Inf"}[5m]) - rate(tron_block_transaction_count_bucket{le="10000"}[5m])) / rate(tron_block_transaction_count_count[5m]) > 0.01` — as a signal to re-tune the upper bound.
+
+#### Consensus
+
+- `tron:sr_set_change` (Counter, labels `action`, `witness`) — incremented once per witness whenever the active SR set rotates at a maintenance boundary. `action` is one of `add` / `remove`. Cardinality grows with the number of distinct witnesses that have ever entered or left the active set, not with the active set size at any given moment. ([#6624](https://github.com/tronprotocol/java-tron/pull/6624))
+
+**Pre-4.8.2 Baseline**
+
+Snapshot of metrics emitted prior to this changelog. Per-version provenance is not tracked here; consult `git log` on [`common/src/main/java/org/tron/common/prometheus/`](common/src/main/java/org/tron/common/prometheus/) for exact origin of each metric.
+
+### Existing Metrics
+
+#### Core (block / transaction processing)
+
+- `tron:header_height` (Gauge) — latest block height on this node.
+- `tron:header_time` (Gauge) — latest block timestamp on this node.
+- `tron:block_push_latency_seconds` (Histogram) — `Manager#pushBlock` latency.
+- `tron:block_process_latency_seconds` (Histogram, label `sync`) — `TronNetDelegate#processBlock` latency.
+- `tron:block_generate_latency_seconds` (Histogram, label `address`) — block generation latency per producer.
+- `tron:block_fetch_latency_seconds` (Histogram) — block fetch latency.
+- `tron:block_receive_delay_seconds` (Histogram) — `receiveTime - blockTime`.
+- `tron:block_fork` (Counter, label `type`) — fork events by type.
+- `tron:lock_acquire_latency_seconds` (Histogram, label `type`) — DB / chain lock acquisition latency.
+- `tron:miner` (Counter, labels `miner`, `type`) — blocks produced by an SR.
+- `tron:miner_latency_seconds` (Histogram, label `miner`) — block mining latency per producer.
+- `tron:miner_delay_seconds` (Histogram, label `miner`) — `actualTime - planTime` for block production.
+- `tron:txs` (Counter, labels `type`, `detail`) — transaction counts.
+- `tron:process_transaction_latency_seconds` (Histogram, labels `type`, `contract`) — transaction processing latency.
+- `tron:verify_sign_latency_seconds` (Histogram, label `type`) — signature verification latency for transactions and blocks.
+- `tron:tx_cache` (Gauge, label `type`) — transaction cache stats.
+- `tron:manager_queue_size` (Gauge, label `type`) — `Manager` queue sizes (pending / popped / queued / repush).
+
+#### Net (P2P)
+
+- `tron:peers` (Gauge, label `type`) — peer counts.
+- `tron:p2p_error` (Counter, label `type`) — P2P error events.
+- `tron:p2p_disconnect` (Counter, label `type`) — P2P disconnect events.
+- `tron:ping_pong_latency_seconds` (Histogram) — peer ping-pong RTT.
+- `tron:message_process_latency_seconds` (Histogram, label `type`) — peer message processing latency.
+- `tron:tcp_bytes` (Histogram, label `type`) — TCP traffic.
+- `tron:udp_bytes` (Histogram, label `type`) — UDP traffic.
+
+#### API
+
+- `tron:http_service_latency_seconds` (Histogram, label `url`) — HTTP endpoint latency.
+- `tron:http_bytes` (Histogram, labels `url`, `status`) — HTTP traffic.
+- `tron:grpc_service_latency_seconds` (Histogram, label `endpoint`) — gRPC endpoint latency.
+- `tron:jsonrpc_service_latency_seconds` (Histogram, label `method`) — JSON-RPC method latency.
+- `tron:internal_service_latency_seconds` (Histogram, labels `class`, `method`) — internal service-call latency.
+- `tron:internal_service_fail` (Counter, labels `class`, `method`) — internal service-call failure count.
+
+#### DB
+
+- `tron:db_size_bytes` (Gauge, labels `type`, `db`, `level`) — storage size in bytes per engine, database, and level; `type` is the storage engine (`LEVELDB` or `ROCKSDB`) depending on node configuration.
+- `tron:db_sst_level` (Gauge, labels `type`, `db`, `level`) — SST files per compaction level per engine and database; `type` is the storage engine (`LEVELDB` or `ROCKSDB`) depending on node configuration.
+- `tron:guava_cache_hit_rate` (Gauge, label `type`) — hit rate of a Guava cache; `type` is the cache name.
+- `tron:guava_cache_request` (Gauge, label `type`) — total request count of a Guava cache; `type` is the cache name.
+- `tron:guava_cache_eviction_count` (Gauge, label `type`) — eviction count of a Guava cache; `type` is the cache name.
+- (Registered via `GuavaCacheExports` for caches that opt in to `CacheManager`.)
+
+#### Logging
+
+- `tron:error_info` (Counter, labels `topic`, `type`) — incremented on every ERROR-level log line by `InstrumentedAppender`.
+
+#### System
+
+Emitted by `OperatingSystemExports` (no labels):
+
+- `system_available_cpus`, `process_cpu_load`, `system_cpu_load`, `system_load_average`, `system_total_physical_memory_bytes`, `system_free_physical_memory_bytes`, `system_total_swap_spaces_bytes`, `system_free_swap_spaces_bytes`.
+
+#### JVM / process
+
+Auto-emitted by the Prometheus client library via `DefaultExports.initialize()` (`simpleclient_hotspot`). The full list is owned by the upstream library and not enumerated here; see the [client_java](https://github.com/prometheus/client_java) docs. Common ones: `jvm_memory_bytes_*`, `jvm_gc_collection_seconds_*`, `jvm_threads_*`, `process_cpu_seconds_total`, `process_open_fds`, `process_resident_memory_bytes`.
+
+---
+
+**References**
+
+- [Official metrics documentation](https://tronprotocol.github.io/documentation-en/using_javatron/metrics/) — descriptions, configuration, and example queries.
+- [tron-docker `metric_monitor/README.md`](https://github.com/tronprotocol/tron-docker/blob/main/metric_monitor/README.md) — operator-oriented overview with deployment guidance.
+- [java-tron-server Grafana dashboard](https://github.com/tronprotocol/tron-docker/blob/main/metric_monitor/grafana_dashboard/java-tron-server.json) — maintained reference dashboard JSON.
diff --git a/README.md b/README.md
index d0ee5fc1f15..575409b3a96 100644
--- a/README.md
+++ b/README.md
@@ -1,179 +1,222 @@
-
- java-tron
-
-
- Java implementation of the Tron Protocol
+ Java implementation of the TRON Protocol
-
-
-
-
+
+
+
+
+
+
-
-
-
+## Table of Contents
-
-
-
+- [What’s TRON?](#whats-tron)
+- [Building the Source Code](#building-the-source-code)
+- [Executables](#executables)
+- [Running java-tron](#running-java-tron)
+- [Community](#community)
+- [Contribution](#contribution)
+- [Resources](#resources)
+- [Integrity Check](#integrity-check)
+- [License](#license)
-
-
-
+# What's TRON?
-
-
-
+TRON is building the foundational infrastructure for the decentralized internet ecosystem with a focus on high-performance, scalability, and security.
-
-
-
+- TRON Protocol: High-throughput (2000+ TPS), scalable blockchain OS (DPoS consensus) powering the TRON ecosystem.
+- TRON Virtual Machine (TVM): EVM-compatible smart-contract engine for fast smart-contract execution.
-
-
-
-
+# Building the Source Code
+Before building java-tron, make sure you have:
+- Hardware with at least 4 CPU cores, 16 GB RAM, 10 GB free disk space for a smooth compilation process.
+- Operating system: `Linux` or `macOS` (`Windows` is not supported).
+- Git and correct JDK (version `8` or `17`) installed based on your CPU architecture.
-## Table of Contents
-- [What’s TRON?](#What’s-TRON)
-- [Building the Source Code](#Building-the-source)
- - [Getting the Source Code](#Getting-the-Source-Code)
- - [Build](#Build)
-- [Running java-tron](#Running-java-tron)
-- [Community](#Community)
-- [Contribution](#Contribution)
-- [Resources](#Resources)
-- [License](#License)
+There are two ways to install the required dependencies:
-## What's TRON?
+- **Option 1: Automated script (recommended for quick setup)**
-TRON is a project dedicated to building the infrastructure for a truly decentralized Internet.
+ Use the provided [`install_dependencies.sh`](install_dependencies.sh) script:
-* Tron Protocol, one of the largest blockchain-based operating systems in the world, offers scalable, high-availability and high-throughput support that underlies all the decentralized applications in the TRON ecosystem.
+ ```bash
+ chmod +x install_dependencies.sh
+ ./install_dependencies.sh
+ ```
+ > **Note**: For production-grade stability with JDK 8 on x86_64 architecture, Oracle JDK 8 is strongly recommended (the script installs OpenJDK 8).
+
+- **Option 2: Manual installation**
+
+ Follow the [Prerequisites and Installation Guide](https://tronprotocol.github.io/documentation-en/using_javatron/installing_javatron/#prerequisites-before-compiling-java-tron) for step-by-step instructions.
-* Tron Virtual Machine (TVM) allows anyone to develop decentralized applications (DAPPs) for themselves or their communities with smart contracts thereby making decentralized crowdfunding and token issuance easier than ever.
+Once all dependencies have been installed, download and compile java-tron by executing:
+```bash
+git clone https://github.com/tronprotocol/java-tron.git
+cd java-tron
+git checkout -t origin/master
+./gradlew clean build -x test
+```
+* The parameter `-x test` indicates skipping the execution of test cases.
+* If you encounter any error please refer to the [Compiling java-tron Source Code](https://tronprotocol.github.io/documentation-en/using_javatron/installing_javatron/#compiling-java-tron-source-code) documentation for troubleshooting steps.
-TRON enables large-scale development and engagement. With over 2000 transactions per second (TPS), high concurrency, low latency, and massive data transmission. It is ideal for building decentralized entertainment applications. Free features and incentive systems allow developers to create premium app experiences for users.
+# Executables
-# Building the source
-Building java-tron requires `git` and `Oracle JDK 1.8` to be installed, other JDK versions are not supported yet. Make sure you operate on `Linux` and `MacOS` operating systems.
+The java-tron project comes with several runnable artifacts and helper scripts found in the project root and build directories.
-Clone the repo and switch to the `master` branch
+| Artifact/Script | Description |
+| :---------------------- | :---------- |
+| **`FullNode.jar`** | Main TRON node executable (generated in `build/libs/` after a successful build following the above guidance). Runs as a full node by default. `java -jar FullNode.jar --help` for command line options|
+| **`Toolkit.jar`** | Node management utility (generated in `build/libs/`): partition, prune, copy, convert DBs; shadow-fork tool. [Usage](https://tronprotocol.github.io/documentation-en/using_javatron/toolkit/#toolkit-a-java-tron-node-maintenance-suite) |
+| **`start.sh`** | Quick start script (x86_64, JDK 8) to download/build/run `FullNode.jar`. See the tool [guide](./shell.md). |
+| **`start.sh.simple`** | Quick start script template (ARM64, JDK 17). See usage notes inside the script. |
- ```bash
- $ git clone https://github.com/tronprotocol/java-tron.git
- $ cd java-tron
- $ git checkout -t origin/master
- ```
-then run the following command to build java-tron, the `FullNode.jar` file can be found in `java-tron/build/libs/` after build successful.
+# Running java-tron
+
+## Hardware Requirements for Mainnet
+
+| Deployment Tier | CPU Cores | Memory | High-performance SSD Storage | Network Downstream |
+|--------------------------|-------|--------|---------------------------|-----------------|
+| FullNode (Minimum) | 8 | 16 GB | 200 GB ([Lite](https://tronprotocol.github.io/documentation-en/using_javatron/litefullnode/#lite-fullnode)) | ≥ 5 MBit/sec |
+| FullNode (Stable) | 8 | 32 GB | 200 GB (Lite) / 3.5 TB (Full) | ≥ 5 MBit/sec |
+| FullNode (Recommend) | 16+ | 32 GB+ | 4 TB | ≥ 50 MBit/sec |
+| Super Representative | 32+ | 64 GB+ | 4 TB | ≥ 50 MBit/sec |
+
+> **Note**: For test networks, where transaction volume is significantly lower, you may operate with reduced hardware specifications.
+
+## Launching a full node
+
+A full node acts as a gateway to the TRON network, exposing comprehensive interfaces via HTTP and RPC APIs. Through these endpoints, clients may execute asset transfers, deploy smart contracts, and invoke on-chain logic. It must join a TRON network to participate in the network's consensus and transaction processing.
+
+### Network Types
+
+The TRON network is mainly divided into:
+
+- **Main Network (Mainnet)**
+ The primary public blockchain where real value (TRX, TRC-20 tokens, etc.) is transacted, secured by a massive decentralized network.
+
+- **[Nile Test Network (Testnet)](https://nileex.io/)**
+ A forward-looking testnet where new features and governance proposals are launched first for developers to experience. Consequently, its codebase is typically ahead of the Mainnet.
+
+- **[Shasta Testnet](https://shasta.tronex.io/)**
+ Closely mirrors the Mainnet’s features and governance proposals. Its network parameters and software versions are kept in sync with the Mainnet, providing developers with a highly realistic environment for final testing.
+
+- **Private Networks**
+ Customized TRON networks set up by private entities for testing, development, or specific use cases.
+
+Network selection is performed by specifying the appropriate configuration file upon full-node startup. Mainnet configuration: [config.conf](framework/src/main/resources/config.conf); Nile testnet configuration: [config-nile.conf](https://github.com/tron-nile-testnet/nile-testnet/blob/master/framework/src/main/resources/config-nile.conf)
+
+### 1. Join the TRON main network
+Launch a main-network full node with the built-in default configuration:
```bash
-$ ./gradlew clean build -x test
+java -jar ./build/libs/FullNode.jar
```
+> For production deployments or long-running Mainnet nodes, please refer to the [JVM Parameter Optimization for FullNode](https://tronprotocol.github.io/documentation-en/using_javatron/installing_javatron/#jvm-parameter-optimization-for-mainnet-fullnode-deployment) guide for the recommended Java command configuration.
-# Running java-tron
-Running java-tron requires `Oracle JDK 1.8` to be installed, other JDK versions are not supported yet. Make sure you operate on `Linux` and `MacOS` operating systems.
-
-Get the mainnet configurate file: [main_net_config.conf](https://github.com/tronprotocol/tron-deployment/blob/master/main_net_config.conf), other network configuration files can be find [here](https://github.com/tronprotocol/tron-deployment).
-## Hardware Requirements
-Minimum:
-* CPU with 8 cores
-* 16GB RAM
-* 1TB free storage space to sync the Mainnet
-
-Recommended:
-* CPU with 16+ cores(32+ cores for a super representative)
-* 32GB+ RAM(64GB+ for a super representative)
-* High Performance SSD with at least 1.5TB free space
-* 100+ MB/s download Internet service
-
-
-## Running a full node for mainnet
-Full node has full historical data, it is the entry point into the TRON network , it can be used by other processes as a gateway into the TRON network via HTTP and GRPC endpoints. You can interact with the TRON network through full node:transfer assets, deploy contracts, interact with contracts and so on. `-c ` parameter specifies a configuration file to run a full node:
- ```bash
- $ nohup java -Xms9G -Xmx9G -XX:ReservedCodeCacheSize=256m \
- -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
- -XX:MaxDirectMemorySize=1G -XX:+PrintGCDetails \
- -XX:+PrintGCDateStamps -Xloggc:gc.log \
- -XX:+UseConcMarkSweepGC -XX:NewRatio=2 \
- -XX:+CMSScavengeBeforeRemark -XX:+ParallelRefProcEnabled \
- -XX:+HeapDumpOnOutOfMemoryError \
- -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 \
- -jar FullNode.jar -c main_net_config.conf >> start.log 2>&1 &
- ```
-## Running a super representative node for mainnet
-Adding the `--witness` parameter to the startup command, full node will run as a super representative node. The super representative node supports all the functions of the full node and also supports block production. Before running, make sure you have a super representative account and get votes from others,once the number of obtained votes ranks in the top 27, your super representative node will participate in block production.
-
-Fill in the private key of super representative address into the `localwitness` list in the `main_net_config.conf`, here is an example:
- ```
- localwitness = [
- 650950B193DDDDB35B6E48912DD28F7AB0E7140C1BFDEFD493348F02295BD812
- ]
- ```
-
-then run the following command to start the node:
- ```bash
- $ nohup java -Xms9G -Xmx9G -XX:ReservedCodeCacheSize=256m \
- -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
- -XX:MaxDirectMemorySize=1G -XX:+PrintGCDetails \
- -XX:+PrintGCDateStamps -Xloggc:gc.log \
- -XX:+UseConcMarkSweepGC -XX:NewRatio=2 \
- -XX:+CMSScavengeBeforeRemark -XX:+ParallelRefProcEnabled \
- -XX:+HeapDumpOnOutOfMemoryError \
- -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 \
- -jar FullNode.jar --witness -c main_net_config.conf >> start.log 2>&1 &
- ```
-
-## Quick Start Tool
-An easier way to build and run java-tron is to use `start.sh`, `start.sh` is a quick start script written in shell language, you can use it to build and run java-tron quickly and easily.
-
-Here are some common use cases of the scripting tool
-* Use `start.sh` to start a full node with the downloaded `FullNode.jar`
-* Use `start.sh` to download the latest `FullNode.jar` and start a full node.
-* Use `start.sh` to download the latest source code and compile a `FullNode.jar` and then start a full node.
-
-For more details, please refer to the tool [guide](./shell.md).
-
-## Run inside Docker container
-
-One of the quickest ways to get `java-tron` up and running on your machine is by using Docker:
-```shell
-$ docker run -d --name="java-tron" \
- -v /your_path/output-directory:/java-tron/output-directory \
- -v /your_path/logs:/java-tron/logs \
- -p 8090:8090 -p 18888:18888 -p 50051:50051 \
- tronprotocol/java-tron \
- -c /java-tron/config/main_net_config.conf
+Using the below command, you can monitor the blocks syncing progress:
+```bash
+tail -f ./logs/tron.log
+```
+
+Use [TronScan](https://tronscan.org/#/), TRON's official block explorer, to view main network transactions, blocks, accounts, witness voting, and governance metrics, etc.
+
+### 2. Join Nile test network
+Utilize the `-c` flag to direct the node to the configuration file corresponding to the desired network. Since Nile Testnet may incorporate features not yet available on the Mainnet, it is **strongly advised** to compile the source code following the [Building the Source Code](https://github.com/tron-nile-testnet/nile-testnet/blob/master/README.md#building-the-source-code) instructions for the Nile Testnet.
+
+```bash
+java -jar ./build/libs/FullNode.jar -c config-nile.conf
```
-This will mount the `output-directory` and `logs` directories on the host, the docker.sh tool can also be used to simplify the use of docker, see more [here](docker/docker.md).
+Nile resources: explorer, faucet, wallet, developer docs, and network statistics at [nileex.io](https://nileex.io/).
+
+### 3. Access Shasta test network
+Shasta does not accept public node peers. Programmatic access is available via TronGrid endpoints; see [TronGrid Service](https://developers.tron.network/docs/trongrid) for details.
+
+Shasta resources: explorer, faucet, wallet, developer docs, and network statistics at [shasta.tronex.io](https://shasta.tronex.io/).
+
+### 4. Set up a private network
+To set up a private network for testing or development, follow the [Private Network guidance](https://tronprotocol.github.io/documentation-en/using_javatron/private_network/).
+
+## Running a super representative node
+
+To operate the node as a Super Representative (SR), append the `--witness` parameter to the standard launch command. An SR node inherits every capability of a FullNode and additionally participates in block production. Refer to the [Super Representative documentation](https://tronprotocol.github.io/documentation-en/mechanism-algorithm/sr/) for eligibility requirements.
+
+Fill in the private key of your SR account into the `localwitness` list in the configuration file. Here is an example:
+
+```
+ localwitness = [
+
+ ]
+```
+Check [Starting a Block Production Node](https://tronprotocol.github.io/documentation-en/using_javatron/installing_javatron/#starting-a-block-production-node) for more details.
+You could also test the process by connecting to a testnet or setting up a private network.
+
+## Programmatically interfacing FullNode
+
+Once the FullNode starts successfully, interaction with the TRON network is facilitated through a comprehensive suite of programmatic interfaces exposed by java-tron:
+- **HTTP API**: See the complete [HTTP API reference and endpoint list](https://tronprotocol.github.io/documentation-en/api/http/).
+- **gRPC**: High-performance APIs suitable for service-to-service integration. See the supported [gRPC reference](https://tronprotocol.github.io/documentation-en/api/rpc/).
+- **JSON-RPC**: Provides Ethereum-compatible JSON-RPC methods for logs, transactions and contract calls, etc. See the supported [JSON-RPC methods](https://tronprotocol.github.io/documentation-en/api/json-rpc/).
+
+Enable or disable each interface in the configuration file:
+
+```
+node {
+ http {
+ fullNodeEnable = true
+ fullNodePort = 8090
+ }
+
+ jsonrpc {
+ httpFullNodeEnable = true
+ httpFullNodePort = 8545
+ }
+
+ rpc {
+ enable = true
+ port = 9090
+ }
+}
+```
+When exposing any of these APIs to a public interface, ensure the node is protected with appropriate authentication, rate limiting, and network access controls in line with your security requirements.
+
+Public hosted HTTP endpoints for both mainnet and testnet are provided by TronGrid. Please refer to the [TRON Network HTTP Endpoints](https://developers.tron.network/docs/connect-to-the-tron-network#tron-network-http-endpoints) for the latest list. For supported methods and request formats, see the HTTP API reference above.
# Community
-[Tron Developers & SRs](https://discord.gg/hqKvyAM) is Tron's official Discord channel. Feel free to join this channel if you have any questions.
-[Core Devs Community](https://t.me/troncoredevscommunity) is the Telegram channel for java-tron community developers. If you want to contribute to java-tron, please join this channel.
+[TRON Developers & SRs](https://discord.gg/hqKvyAM) is TRON's official Discord channel. Feel free to join this channel if you have any questions.
-[tronprotocol/allcoredev](https://gitter.im/tronprotocol/allcoredev) is the official Gitter channel for developers.
+The [Core Devs Community](https://t.me/troncoredevscommunity) and [TRON Official Developer Group](https://t.me/TronOfficialDevelopersGroupEn) are Telegram channels specifically designed for java-tron community developers to engage in technical discussions.
# Contribution
-Thank you for considering to help out with the source code! If you'd like to contribute to java-tron, please see the [Contribution Guide](./CONTRIBUTING.md) for more details.
+Thank you for considering to help out with the source code! If you'd like to contribute to java-tron, please see the [Contribution Guide](./CONTRIBUTING.md) for more details.
# Resources
-* [Medium](https://medium.com/@coredevs) java-tron's official technical articles are published there.
-* [Documentation](https://tronprotocol.github.io/documentation-en/introduction/) java-tron's official technical documentation website.
-* [Test network](http://nileex.io/) A stable test network of TRON contributed by TRON community.
-* [Tronscan](https://tronscan.org/#/) TRON network blockchain browser.
-* [Wallet-cli](https://github.com/tronprotocol/wallet-cli) TRON network wallet using command line.
-* [TIP](https://github.com/tronprotocol/tips) TRON Improvement Proposal (TIP) describes standards for the TRON network.
-* [TP](https://github.com/tronprotocol/tips/tree/master/tp) TRON Protocol (TP) describes standards already implemented in TRON network but not published as a TIP.
+
+- [Medium](https://medium.com/@coredevs) — Official technical articles from the java-tron core development team.
+- [Documentation](https://tronprotocol.github.io/documentation-en/) and [TRON Developer Hub](https://developers.tron.network/) — Primary documentation for java-tron developers.
+- [TronScan](https://tronscan.org/#/) — TRON mainnet blockchain explorer.
+- [Nile Test Network](http://nileex.io/) — A stable test network for TRON development and testing.
+- [Shasta Test Network](https://shasta.tronex.io/) — A stable test network mirroring mainnet features.
+- [Wallet-cli](https://github.com/tronprotocol/wallet-cli) — Command-line wallet for the TRON network.
+- [TIP](https://github.com/tronprotocol/tips) — TRON Improvement Proposals describing standards for the TRON network.
+- [TP](https://github.com/tronprotocol/tips/tree/master/tp) — TRON Protocols already implemented but not yet published as TIPs.
+
+# Integrity Check
+
+- After January 3, 2023, the release files will be signed using a GPG key pair, and the correctness of the signature will be verified using the following public key:
+ ```
+ pub: 1254 F859 D2B1 BD9F 66E7 107D F859 BCB4 4A28 290B
+ uid: build@tron.network
+ ```
# License
+
java-tron is released under the [LGPLv3 license](https://github.com/tronprotocol/java-tron/blob/master/LICENSE).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000000..b3125ce6af1
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,7 @@
+## Supported Versions
+Please see [Releases](https://github.com/tronprotocol/java-tron/releases). We recommend using the [most recently released version](https://github.com/tronprotocol/java-tron/releases/latest).
+
+## Reporting a Vulnerability
+**Please do not file a public ticket** mentioning the vulnerability.
+To find out how to report a vulnerability in TRON, visit [https://hackerone.com/tron_dao](https://hackerone.com/tron_dao?type=team) or email [bounty@tron.network](mailto:bounty@tron.network).
+Please read the [disclosure policy](https://www.hackerone.com/disclosure-guidelines) for more information about publicly disclosed security vulnerabilities.
diff --git a/Tron protobuf protocol document.md b/Tron protobuf protocol document.md
index be9558a1d93..d8e621ed69a 100644
--- a/Tron protobuf protocol document.md
+++ b/Tron protobuf protocol document.md
@@ -546,11 +546,11 @@ Transaction and transaction-related messages.
message `raw`
- `ref_block_bytes`: Deprecated.
+ `ref_block_bytes`: intercepted part of the now block bytes in transaction head.
- `ref_block_num`: now block number in transaction head.
+ `ref_block_num`: Deprecated.
- `ref_block_hash`: now block hash in transaction head.
+ `ref_block_hash`:intercepted part of the now block hash in transaction head..
`expiration`: the expiration time in transaction head.
@@ -565,15 +565,15 @@ Transaction and transaction-related messages.
```java
message raw {
bytes ref_block_bytes = 1;
- int64 ref_block_num = 3;
+ int64 ref_block_num = 3;
bytes ref_block_hash = 4;
- int64 expiration = 8;
+ int64 expiration = 8;
repeated authority auths = 9;
- bytes data = 10;
+ bytes data = 10;
repeated Contract contract = 11;
- bytes scripts = 12;
+ bytes scripts = 12;
int64 timestamp = 14;
- int64 fee_limit = 18;
+ int64 fee_limit = 18;
}
```
@@ -620,6 +620,14 @@ Transaction and transaction-related messages.
ClearABIContract = 48;
UpdateBrokerageContract = 49;
ShieldedTransferContract = 51;
+ MarketSellAssetContract = 52;
+ MarketCancelOrderContract = 53;
+ FreezeBalanceV2Contract = 54;
+ UnfreezeBalanceV2Contract = 55;
+ WithdrawExpireUnfreezeContract = 56;
+ DelegateResourceContract = 57;
+ UnDelegateResourceContract = 58;
+ CancelAllUnfreezeV2Contract = 59;
}
ContractType type = 1;
google.protobuf.Any parameter = 2;
@@ -873,6 +881,14 @@ Contract and contract-related messages.
ClearABIContract = 48;
UpdateBrokerageContract = 49;
ShieldedTransferContract = 51;
+ MarketSellAssetContract = 52;
+ MarketCancelOrderContract = 53;
+ FreezeBalanceV2Contract = 54;
+ UnfreezeBalanceV2Contract = 55;
+ WithdrawExpireUnfreezeContract = 56;
+ DelegateResourceContract = 57;
+ UnDelegateResourceContract = 58;
+ CancelAllUnfreezeV2Contract = 59;
}
ContractType type = 1;
google.protobuf.Any parameter = 2;
@@ -959,7 +975,7 @@ Contract and contract-related messages.
- message `VoteAssetContract`
- `owner_address`: assress of contract owner.
+ `owner_address`: address of contract owner.
`vote_address`: voted address of asset.
@@ -1043,7 +1059,7 @@ Contract and contract-related messages.
`total_supply`: maximum of asset.
- `frozen_supply`: frozen supplt of asset.
+ `frozen_supply`: frozen supply of asset.
`trx_num`: trx num defines token price.
@@ -1063,11 +1079,11 @@ Contract and contract-related messages.
`free_asset_net_limit`: free bandwidth limit each account owns when transfers asset.
- `public_free_asset_net_limit`: free bandwidth limit for all acoounts.
+ `public_free_asset_net_limit`: free bandwidth limit for all accounts.
`public_free_asset_net_usage`: free bandwidth usage of all accounts.
- `public_latest_free_net_time`: the latest bandwidth consumption time fo token transfer.
+ `public_latest_free_net_time`: the latest bandwidth consumption time for token transfer.
```java
message AssetIssueContract {
@@ -1115,7 +1131,7 @@ Contract and contract-related messages.
`owner_address`: owner address.
- `to_address`: reveiver address.
+ `to_address`: receiver address.
`asset_name`: target asset name.
@@ -1445,7 +1461,7 @@ Contract and contract-related messages.
`owner_address`: address of owner.
- `owner`: autuority to execute all contracts.
+ `owner`: authority to execute all contracts.
`witness`: used by SR for generating blocks.
@@ -1498,7 +1514,7 @@ Contract and contract-related messages.
`binding_signature`: signature to verify transaction.
- `transparent_to_address`: transparent address of reveiver.
+ `transparent_to_address`: transparent address of receiver.
`to_amount`: amount to transparent to_address
@@ -1520,7 +1536,7 @@ Contract and contract-related messages.
### Smart Contract
-message `SmartContract` has mutiple attributes and nested message `ABI`
+message `SmartContract` has multiple attributes and nested message `ABI`
- message `SmartContract`
@@ -1543,7 +1559,7 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
- message `Param`
- `indexed`: `true` if the field is part of the log’s topics, `false` if it one of the log’s data segment.
+ `indexed`: `true` if the field is part of the log’s topics, `false` if it is one of the log’s data segment.
`name`: name of the parameter.
@@ -1741,7 +1757,7 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
`tree`: incremental merkle tree.
- `filled`: this is a array, it contains the root of the subtree which can be combined with the param tree to be a new merkle tree.
+ `filled`: this is an array, it contains the root of the subtree which can be combined with the param tree to be a new merkle tree.
`cursor`: the node that can be combined to a subtree, when they are combined to a subtree, compute its root and put it into the filled.
@@ -1766,7 +1782,7 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
`vouchers`: this is an array, each items represents the merklevoucher of the outputpoint.
- `paths`: his is an array each items represents the path of the outputpoint.
+ `paths`: this is an array each items represents the path of the outputpoint.
```java
message IncrementalMerkleVoucherInfo {
@@ -2108,13 +2124,13 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
- #### Node Information
- Node information is separaed into several parts and implemented by nested messages.
+ Node information is separated into several parts and implemented by nested messages.
- message `NodeInfo`
- `beginSyncNum`: beginning block height for synchornize.
+ `beginSyncNum`: beginning block height for synchronize.
`block`: head block id.
@@ -2138,13 +2154,13 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
- message `PeerInfo`:
- `lastSyncBlock`: last block id for synchornize.
+ `lastSyncBlock`: last block id for synchronize.
`remainNum`: number of remaining blocks.
`lastBlockUpdateTime`: latest block update time .
- `syncFlag`: is synchroniing or not.
+ `syncFlag`: is synchronizing or not.
`headBlockTimeWeBothHave`: timestamp of common head block.
@@ -2156,7 +2172,7 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
`port`: listening port.
- `nodeId`: ramdomly generated node ID
+ `nodeId`: randomly generated node ID
`connectTime`: connection time period from established.
diff --git a/actuator/build.gradle b/actuator/build.gradle
index 34bfc018500..1143dc83618 100644
--- a/actuator/build.gradle
+++ b/actuator/build.gradle
@@ -1,28 +1,9 @@
description = "actuator – a series of transactions for blockchain."
-// Dependency versions
-// ---------------------------------------
-
-def junitVersion = "4.12"
-def mockitoVersion = "2.1.0"
-def testNgVersion = "6.11"
-def slf4jVersion = "1.7.25"
-// --------------------------------------
-
dependencies {
- compile project(":chainbase")
- compile project(":protocol")
- compile project(":crypto")
- testImplementation "junit:junit:$junitVersion"
- testImplementation "org.mockito:mockito-core:$mockitoVersion"
-
- testImplementation "org.testng:testng:$testNgVersion"
-
- compile "org.slf4j:jcl-over-slf4j:$slf4jVersion"
- compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69'
- compile 'com.github.tronprotocol:zksnark-java-sdk:master-SNAPSHOT'
- compile group: 'commons-codec', name: 'commons-codec', version: '1.11'
- compile 'org.reflections:reflections:0.9.11'
+ api project(":chainbase")
+ api project(":protocol")
+ api project(":crypto")
}
test {
@@ -50,34 +31,15 @@ test {
}
}
-task testng(type: Test) {
- useTestNG()
- testLogging {
- events = ["skipped", "failed"]
- exceptionFormat = "full"
-
- debug.events = ["skipped", "failed"]
- debug.exceptionFormat = "full"
-
- info.events = ["failed", "skipped"]
- info.exceptionFormat = "full"
-
- warn.events = ["failed", "skipped"]
- warn.exceptionFormat = "full"
- }
-}
-
jacocoTestReport {
reports {
xml.enabled = true
html.enabled = true
}
- executionData.from = '../framework/build/jacoco/jacocoTest.exec'
+ getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec"))
afterEvaluate {
classDirectories.from = classDirectories.files.collect {
fileTree(dir: it,)
}
}
}
-
-check.dependsOn testng
diff --git a/actuator/src/main/java/org/tron/core/actuator/AbstractActuator.java b/actuator/src/main/java/org/tron/core/actuator/AbstractActuator.java
index c88baf9b3ca..64a81e17c58 100644
--- a/actuator/src/main/java/org/tron/core/actuator/AbstractActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/AbstractActuator.java
@@ -2,18 +2,28 @@
import com.google.protobuf.Any;
import com.google.protobuf.GeneratedMessageV3;
+import lombok.Getter;
+import org.tron.common.math.Maths;
+import org.tron.common.utils.Commons;
import org.tron.common.utils.ForkController;
import org.tron.core.ChainBaseManager;
+import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.TransactionCapsule;
+import org.tron.core.exception.BalanceInsufficientException;
+import org.tron.core.store.AccountStore;
import org.tron.protos.Protocol.Transaction.Contract;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
public abstract class AbstractActuator implements Actuator {
+ @Getter
protected Any any;
+ @Getter
protected ChainBaseManager chainBaseManager;
+ @Getter
protected Contract contract;
+ @Getter
protected TransactionCapsule tx;
protected ForkController forkController;
@@ -21,38 +31,22 @@ public AbstractActuator(ContractType type, Class extends GeneratedMessageV3> c
TransactionFactory.register(type, getClass(), clazz);
}
- public Any getAny() {
- return any;
- }
-
public AbstractActuator setAny(Any any) {
this.any = any;
return this;
}
- public ChainBaseManager getChainBaseManager() {
- return chainBaseManager;
- }
-
public AbstractActuator setChainBaseManager(ChainBaseManager chainBaseManager) {
this.chainBaseManager = chainBaseManager;
return this;
}
- public Contract getContract() {
- return contract;
- }
-
public AbstractActuator setContract(Contract contract) {
this.contract = contract;
this.any = contract.getParameter();
return this;
}
- public TransactionCapsule getTx() {
- return tx;
- }
-
public AbstractActuator setTx(TransactionCapsule tx) {
this.tx = tx;
return this;
@@ -63,4 +57,61 @@ public AbstractActuator setForkUtils(ForkController forkController) {
return this;
}
+ public long addExact(long x, long y) {
+ return Maths.addExact(x, y, this.disableJavaLangMath());
+ }
+
+ public long addExact(int x, int y) {
+ return Maths.addExact(x, y, this.disableJavaLangMath());
+ }
+
+ public long floorDiv(long x, long y) {
+ return Maths.floorDiv(x, y, this.disableJavaLangMath());
+ }
+
+ public long floorDiv(long x, int y) {
+ return this.floorDiv(x, (long) y);
+ }
+
+ public long multiplyExact(long x, long y) {
+ return Maths.multiplyExact(x, y, this.disableJavaLangMath());
+ }
+
+ public long multiplyExact(long x, int y) {
+ return this.multiplyExact(x, (long) y);
+ }
+
+ public int multiplyExact(int x, int y) {
+ return Maths.multiplyExact(x, y, this.disableJavaLangMath());
+ }
+
+ public long subtractExact(long x, long y) {
+ return Maths.subtractExact(x, y, this.disableJavaLangMath());
+ }
+
+ public int min(int a, int b) {
+ return Maths.min(a, b, this.disableJavaLangMath());
+ }
+
+ public long min(long a, long b) {
+ return Maths.min(a, b, this.disableJavaLangMath());
+ }
+
+ public void adjustBalance(AccountStore accountStore, byte[] accountAddress, long amount)
+ throws BalanceInsufficientException {
+ AccountCapsule account = accountStore.getUnchecked(accountAddress);
+ this.adjustBalance(accountStore, account, amount);
+ }
+
+ /**
+ * judge balance.
+ */
+ public void adjustBalance(AccountStore accountStore, AccountCapsule account, long amount)
+ throws BalanceInsufficientException {
+ Commons.adjustBalance(accountStore, account, amount, this.disableJavaLangMath());
+ }
+
+ boolean disableJavaLangMath() {
+ return chainBaseManager.getDynamicPropertiesStore().disableJavaLangMath();
+ }
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/AbstractExchangeActuator.java b/actuator/src/main/java/org/tron/core/actuator/AbstractExchangeActuator.java
new file mode 100644
index 00000000000..7b1febfcf61
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/AbstractExchangeActuator.java
@@ -0,0 +1,24 @@
+package org.tron.core.actuator;
+
+import com.google.protobuf.GeneratedMessageV3;
+import org.tron.common.math.StrictMathWrapper;
+import org.tron.protos.Protocol.Transaction.Contract.ContractType;
+
+public abstract class AbstractExchangeActuator extends AbstractActuator {
+
+ public AbstractExchangeActuator(ContractType type, Class extends GeneratedMessageV3> clazz) {
+ super(type, clazz);
+ }
+
+ protected boolean allowHarden() {
+ return chainBaseManager.getDynamicPropertiesStore().allowHardenExchangeCalculation();
+ }
+
+ public long subtractExact(long x, long y) {
+ return allowHarden() ? StrictMathWrapper.subtractExact(x, y) : x - y;
+ }
+
+ public long addExact(long x, long y) {
+ return allowHarden() ? StrictMathWrapper.addExact(x, y) : x + y;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java b/actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java
index fcc4d775d43..f2eafb20a5e 100644
--- a/actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java
@@ -8,7 +8,6 @@
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-import org.tron.common.utils.Commons;
import org.tron.common.utils.DecodeUtil;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
@@ -52,11 +51,11 @@ public boolean execute(Object object) throws ContractExeException {
accountPermissionUpdateContract.getActivesList());
accountStore.put(ownerAddress, account);
- Commons.adjustBalance(accountStore, ownerAddress, -fee);
+ adjustBalance(accountStore, ownerAddress, -fee);
if (chainBaseManager.getDynamicPropertiesStore().supportBlackHoleOptimization()) {
chainBaseManager.getDynamicPropertiesStore().burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
}
result.setStatus(fee, code.SUCESS);
@@ -111,7 +110,7 @@ private boolean checkPermission(Permission permission) throws ContractValidateEx
throw new ContractValidateException("key's weight should be greater than 0");
}
try {
- weightSum = Math.addExact(weightSum, key.getWeight());
+ weightSum = addExact(weightSum, key.getWeight());
} catch (ArithmeticException e) {
throw new ContractValidateException(e.getMessage());
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/AssetIssueActuator.java b/actuator/src/main/java/org/tron/core/actuator/AssetIssueActuator.java
index 55218897c5d..59fa6e0aaa9 100644
--- a/actuator/src/main/java/org/tron/core/actuator/AssetIssueActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/AssetIssueActuator.java
@@ -22,13 +22,15 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
-import org.tron.common.utils.Commons;
+import org.tron.common.math.StrictMathWrapper;
import org.tron.common.utils.DecodeUtil;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.AssetIssueCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.config.Parameter.ForkBlockVersionEnum;
import org.tron.core.exception.BalanceInsufficientException;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
@@ -84,11 +86,11 @@ public boolean execute(Object result) throws ContractExeException {
.put(assetIssueCapsuleV2.createDbV2Key(), assetIssueCapsuleV2);
}
- Commons.adjustBalance(accountStore, ownerAddress, -fee);
+ adjustBalance(accountStore, ownerAddress, -fee);
if (dynamicStore.supportBlackHoleOptimization()) {
dynamicStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), fee);//send to blackhole
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);//send to blackhole
}
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
List frozenSupplyList = assetIssueContract.getFrozenSupplyList();
@@ -165,7 +167,7 @@ public boolean validate() throws ContractValidateException {
}
if (dynamicStore.getAllowSameTokenName() != 0) {
- String name = assetIssueContract.getName().toStringUtf8().toLowerCase();
+ String name = assetIssueContract.getName().toStringUtf8().toLowerCase(Locale.ROOT);
if (("trx").equals(name)) {
throw new ContractValidateException("assetName can't be trx");
}
@@ -264,6 +266,16 @@ public boolean validate() throws ContractValidateException {
"frozenDuration must be less than " + maxFrozenSupplyTime + " days "
+ "and more than " + minFrozenSupplyTime + " days");
}
+ // make sure FrozenSupply.expireTime not overflow
+ if (chainBaseManager.getForkController().pass(ForkBlockVersionEnum.VERSION_4_8_1)) {
+ long frozenPeriod = next.getFrozenDays() * FROZEN_PERIOD;
+ try {
+ StrictMathWrapper.addExact(assetIssueContract.getStartTime(), frozenPeriod);
+ } catch (ArithmeticException e) {
+ throw new ContractValidateException(
+ "Start time and frozen days would cause expire time overflow");
+ }
+ }
remainSupply -= next.getFrozenAmount();
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/CancelAllUnfreezeV2Actuator.java b/actuator/src/main/java/org/tron/core/actuator/CancelAllUnfreezeV2Actuator.java
new file mode 100755
index 00000000000..048f703e9f7
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/CancelAllUnfreezeV2Actuator.java
@@ -0,0 +1,206 @@
+package org.tron.core.actuator;
+
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+import static org.tron.protos.contract.Common.ResourceCode.TRON_POWER;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.AccountStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.protos.Protocol.Account.UnFreezeV2;
+import org.tron.protos.Protocol.Transaction.Contract.ContractType;
+import org.tron.protos.Protocol.Transaction.Result.code;
+import org.tron.protos.contract.BalanceContract.CancelAllUnfreezeV2Contract;
+
+@Slf4j(topic = "actuator")
+public class CancelAllUnfreezeV2Actuator extends AbstractActuator {
+
+ public CancelAllUnfreezeV2Actuator() {
+ super(ContractType.CancelAllUnfreezeV2Contract, CancelAllUnfreezeV2Contract.class);
+ }
+
+ @Override
+ public boolean execute(Object result) throws ContractExeException {
+ TransactionResultCapsule ret = (TransactionResultCapsule) result;
+ if (Objects.isNull(ret)) {
+ throw new RuntimeException(ActuatorConstant.TX_RESULT_NULL);
+ }
+ long fee = calcFee();
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ byte[] ownerAddress;
+ try {
+ ownerAddress = getOwnerAddress().toByteArray();
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ ret.setStatus(fee, code.FAILED);
+ throw new ContractExeException(e.getMessage());
+ }
+ AccountCapsule ownerCapsule = accountStore.get(ownerAddress);
+ List unfrozenV2List = ownerCapsule.getUnfrozenV2List();
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ AtomicLong atomicWithdrawExpireBalance = new AtomicLong(0L);
+ /* The triple object is defined by resource type, with left representing the pair object
+ corresponding to bandwidth, middle representing the pair object corresponding to energy, and
+ right representing the pair object corresponding to tron power. The pair object for each
+ resource type, left represents resource weight, and right represents the number of unfreeze
+ resources for that resource type. */
+ Triple, Pair, Pair>
+ triple = Triple.of(
+ Pair.of(new AtomicLong(0L), new AtomicLong(0L)),
+ Pair.of(new AtomicLong(0L), new AtomicLong(0L)),
+ Pair.of(new AtomicLong(0L), new AtomicLong(0L)));
+ for (UnFreezeV2 unFreezeV2 : unfrozenV2List) {
+ updateAndCalculate(triple, ownerCapsule, now, atomicWithdrawExpireBalance, unFreezeV2);
+ }
+ ownerCapsule.clearUnfrozenV2();
+ addTotalResourceWeight(dynamicStore, triple);
+
+ long withdrawExpireBalance = atomicWithdrawExpireBalance.get();
+ if (withdrawExpireBalance > 0) {
+ ownerCapsule.setBalance(ownerCapsule.getBalance() + withdrawExpireBalance);
+ }
+
+ accountStore.put(ownerCapsule.createDbKey(), ownerCapsule);
+ ret.setWithdrawExpireAmount(withdrawExpireBalance);
+ Map cancelUnfreezeV2AmountMap = new HashMap<>();
+ cancelUnfreezeV2AmountMap.put(BANDWIDTH.name(), triple.getLeft().getRight().get());
+ cancelUnfreezeV2AmountMap.put(ENERGY.name(), triple.getMiddle().getRight().get());
+ cancelUnfreezeV2AmountMap.put(TRON_POWER.name(), triple.getRight().getRight().get());
+ ret.putAllCancelUnfreezeV2AmountMap(cancelUnfreezeV2AmountMap);
+ ret.setStatus(fee, code.SUCESS);
+ return true;
+ }
+
+ private void addTotalResourceWeight(DynamicPropertiesStore dynamicStore,
+ Triple,
+ Pair,
+ Pair> triple) {
+ dynamicStore.addTotalNetWeight(triple.getLeft().getLeft().get());
+ dynamicStore.addTotalEnergyWeight(triple.getMiddle().getLeft().get());
+ dynamicStore.addTotalTronPowerWeight(triple.getRight().getLeft().get());
+ }
+
+ private void updateAndCalculate(Triple, Pair,
+ Pair> triple,
+ AccountCapsule ownerCapsule, long now, AtomicLong atomicLong, UnFreezeV2 unFreezeV2) {
+ if (unFreezeV2.getUnfreezeExpireTime() > now) {
+ updateFrozenInfoAndTotalResourceWeight(ownerCapsule, unFreezeV2, triple);
+ } else {
+ atomicLong.addAndGet(unFreezeV2.getUnfreezeAmount());
+ }
+ }
+
+ @Override
+ public boolean validate() throws ContractValidateException {
+ if (Objects.isNull(this.any)) {
+ throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
+ }
+
+ if (Objects.isNull(chainBaseManager)) {
+ throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
+ }
+
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+
+ if (!this.any.is(CancelAllUnfreezeV2Contract.class)) {
+ throw new ContractValidateException("contract type error, expected type " +
+ "[CancelAllUnfreezeV2Contract], real type[" + any.getClass() + "]");
+ }
+
+ if (!dynamicStore.supportAllowCancelAllUnfreezeV2()) {
+ throw new ContractValidateException("Not support CancelAllUnfreezeV2 transaction,"
+ + " need to be opened by the committee");
+ }
+
+ byte[] ownerAddress;
+ try {
+ ownerAddress = getOwnerAddress().toByteArray();
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule accountCapsule = accountStore.get(ownerAddress);
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ if (Objects.isNull(accountCapsule)) {
+ throw new ContractValidateException(ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + NOT_EXIST_STR);
+ }
+
+ List unfrozenV2List = accountCapsule.getUnfrozenV2List();
+ if (unfrozenV2List.isEmpty()) {
+ throw new ContractValidateException("No unfreezeV2 list to cancel");
+ }
+
+ return true;
+ }
+
+ @Override
+ public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
+ return getCancelAllUnfreezeV2Contract().getOwnerAddress();
+ }
+
+ private CancelAllUnfreezeV2Contract getCancelAllUnfreezeV2Contract()
+ throws InvalidProtocolBufferException {
+ return any.unpack(CancelAllUnfreezeV2Contract.class);
+ }
+
+ @Override
+ public long calcFee() {
+ return 0;
+ }
+
+ public void updateFrozenInfoAndTotalResourceWeight(
+ AccountCapsule accountCapsule, UnFreezeV2 unFreezeV2,
+ Triple, Pair,
+ Pair> triple) {
+ switch (unFreezeV2.getType()) {
+ case BANDWIDTH:
+ long oldNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForBandwidthV2(unFreezeV2.getUnfreezeAmount());
+ long newNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ triple.getLeft().getLeft().addAndGet(newNetWeight - oldNetWeight);
+ triple.getLeft().getRight().addAndGet(unFreezeV2.getUnfreezeAmount());
+ break;
+ case ENERGY:
+ long oldEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForEnergyV2(unFreezeV2.getUnfreezeAmount());
+ long newEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ triple.getMiddle().getLeft().addAndGet(newEnergyWeight - oldEnergyWeight);
+ triple.getMiddle().getRight().addAndGet(unFreezeV2.getUnfreezeAmount());
+ break;
+ case TRON_POWER:
+ long oldTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ accountCapsule.addFrozenForTronPowerV2(unFreezeV2.getUnfreezeAmount());
+ long newTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ triple.getRight().getLeft().addAndGet(newTPWeight - oldTPWeight);
+ triple.getRight().getRight().addAndGet(unFreezeV2.getUnfreezeAmount());
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/actuator/CreateAccountActuator.java b/actuator/src/main/java/org/tron/core/actuator/CreateAccountActuator.java
index 1c6aca4d7d7..352f394d6cb 100755
--- a/actuator/src/main/java/org/tron/core/actuator/CreateAccountActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/CreateAccountActuator.java
@@ -6,7 +6,6 @@
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
-import org.tron.common.utils.Commons;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
@@ -48,13 +47,12 @@ public boolean execute(Object result)
accountStore
.put(accountCreateContract.getAccountAddress().toByteArray(), accountCapsule);
- Commons
- .adjustBalance(accountStore, accountCreateContract.getOwnerAddress().toByteArray(), -fee);
+ adjustBalance(accountStore, accountCreateContract.getOwnerAddress().toByteArray(), -fee);
// Add to blackhole address
if (dynamicStore.supportBlackHoleOptimization()) {
dynamicStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
}
ret.setStatus(fee, code.SUCESS);
} catch (BalanceInsufficientException | InvalidProtocolBufferException e) {
diff --git a/actuator/src/main/java/org/tron/core/actuator/DelegateResourceActuator.java b/actuator/src/main/java/org/tron/core/actuator/DelegateResourceActuator.java
new file mode 100755
index 00000000000..9e7b0efa5ce
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/DelegateResourceActuator.java
@@ -0,0 +1,327 @@
+package org.tron.core.actuator;
+
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL;
+import static org.tron.core.config.Parameter.ChainConstant.DELEGATE_PERIOD;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+import static org.tron.core.vm.utils.FreezeV2Util.getV2EnergyUsage;
+import static org.tron.core.vm.utils.FreezeV2Util.getV2NetUsage;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.Arrays;
+import java.util.Objects;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.DelegatedResourceCapsule;
+import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.db.BandwidthProcessor;
+import org.tron.core.db.EnergyProcessor;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.AccountStore;
+import org.tron.core.store.DelegatedResourceAccountIndexStore;
+import org.tron.core.store.DelegatedResourceStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.utils.TransactionUtil;
+import org.tron.protos.Protocol.AccountType;
+import org.tron.protos.Protocol.Transaction.Contract.ContractType;
+import org.tron.protos.Protocol.Transaction.Result.code;
+import org.tron.protos.contract.BalanceContract.DelegateResourceContract;
+
+@Slf4j(topic = "actuator")
+public class DelegateResourceActuator extends AbstractActuator {
+
+ public DelegateResourceActuator() {
+ super(ContractType.DelegateResourceContract, DelegateResourceContract.class);
+ }
+
+ @Override
+ public boolean execute(Object result) throws ContractExeException {
+ TransactionResultCapsule ret = (TransactionResultCapsule) result;
+ if (Objects.isNull(ret)) {
+ throw new RuntimeException(ActuatorConstant.TX_RESULT_NULL);
+ }
+
+ long fee = calcFee();
+ final DelegateResourceContract delegateResourceContract;
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ byte[] ownerAddress;
+ try {
+ delegateResourceContract = this.any.unpack(DelegateResourceContract.class);
+ ownerAddress = getOwnerAddress().toByteArray();
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ ret.setStatus(fee, code.FAILED);
+ throw new ContractExeException(e.getMessage());
+ }
+
+ AccountCapsule ownerCapsule = accountStore
+ .get(delegateResourceContract.getOwnerAddress().toByteArray());
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ long delegateBalance = delegateResourceContract.getBalance();
+ boolean lock = delegateResourceContract.getLock();
+ long lockPeriod = getLockPeriod(dynamicStore.supportMaxDelegateLockPeriod(),
+ delegateResourceContract);
+ byte[] receiverAddress = delegateResourceContract.getReceiverAddress().toByteArray();
+
+ // delegate resource to receiver
+ switch (delegateResourceContract.getResource()) {
+ case BANDWIDTH:
+ delegateResource(ownerAddress, receiverAddress, true,
+ delegateBalance, lock, lockPeriod);
+
+ ownerCapsule.addDelegatedFrozenV2BalanceForBandwidth(delegateBalance);
+ ownerCapsule.addFrozenBalanceForBandwidthV2(-delegateBalance);
+ break;
+ case ENERGY:
+ delegateResource(ownerAddress, receiverAddress, false,
+ delegateBalance, lock, lockPeriod);
+
+ ownerCapsule.addDelegatedFrozenV2BalanceForEnergy(delegateBalance);
+ ownerCapsule.addFrozenBalanceForEnergyV2(-delegateBalance);
+ break;
+ default:
+ logger.debug("Resource Code Error.");
+ }
+
+ accountStore.put(ownerCapsule.createDbKey(), ownerCapsule);
+
+ ret.setStatus(fee, code.SUCESS);
+
+ return true;
+ }
+
+
+ @Override
+ public boolean validate() throws ContractValidateException {
+ if (this.any == null) {
+ throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
+ }
+ if (chainBaseManager == null) {
+ throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
+ }
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ DelegatedResourceStore delegatedResourceStore = chainBaseManager.getDelegatedResourceStore();
+ if (!any.is(DelegateResourceContract.class)) {
+ throw new ContractValidateException(
+ "contract type error,expected type [DelegateResourceContract],real type["
+ + any.getClass() + "]");
+ }
+
+ if (!dynamicStore.supportDR()) {
+ throw new ContractValidateException("No support for resource delegate");
+ }
+
+ if (!dynamicStore.supportUnfreezeDelay()) {
+ throw new ContractValidateException("Not support Delegate resource transaction,"
+ + " need to be opened by the committee");
+ }
+
+ final DelegateResourceContract delegateResourceContract;
+ byte[] ownerAddress;
+ try {
+ delegateResourceContract = this.any.unpack(DelegateResourceContract.class);
+ ownerAddress = getOwnerAddress().toByteArray();
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+
+ AccountCapsule ownerCapsule = accountStore.get(ownerAddress);
+ if (ownerCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR + readableOwnerAddress + NOT_EXIST_STR);
+ }
+
+ long delegateBalance = delegateResourceContract.getBalance();
+ if (delegateBalance < TRX_PRECISION) {
+ throw new ContractValidateException("delegateBalance must be greater than or equal to 1 TRX");
+ }
+
+ switch (delegateResourceContract.getResource()) {
+ case BANDWIDTH: {
+ BandwidthProcessor processor = new BandwidthProcessor(chainBaseManager);
+ processor.updateUsageForDelegated(ownerCapsule);
+
+ long accountNetUsage = ownerCapsule.getNetUsage();
+ if (null != this.getTx() && this.getTx().isTransactionCreate()) {
+ accountNetUsage += TransactionUtil.estimateConsumeBandWidthSize(dynamicStore,
+ ownerCapsule.getFrozenV2BalanceForBandwidth());
+ }
+ long netUsage = (long) (accountNetUsage * TRX_PRECISION * ((double)
+ (dynamicStore.getTotalNetWeight()) / dynamicStore.getTotalNetLimit()));
+ long v2NetUsage = getV2NetUsage(ownerCapsule, netUsage,
+ this.disableJavaLangMath());
+ if (ownerCapsule.getFrozenV2BalanceForBandwidth() - v2NetUsage < delegateBalance) {
+ throw new ContractValidateException(
+ "delegateBalance must be less than or equal to available FreezeBandwidthV2 balance");
+ }
+ }
+ break;
+ case ENERGY: {
+ EnergyProcessor processor = new EnergyProcessor(dynamicStore, accountStore);
+ processor.updateUsage(ownerCapsule);
+
+ long energyUsage = (long) (ownerCapsule.getEnergyUsage() * TRX_PRECISION * ((double)
+ (dynamicStore.getTotalEnergyWeight()) / dynamicStore.getTotalEnergyCurrentLimit()));
+ long v2EnergyUsage = getV2EnergyUsage(ownerCapsule, energyUsage,
+ this.disableJavaLangMath());
+ if (ownerCapsule.getFrozenV2BalanceForEnergy() - v2EnergyUsage < delegateBalance) {
+ throw new ContractValidateException(
+ "delegateBalance must be less than or equal to available FreezeEnergyV2 balance");
+ }
+ }
+ break;
+ default:
+ throw new ContractValidateException(
+ "ResourceCode error, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+
+ byte[] receiverAddress = delegateResourceContract.getReceiverAddress().toByteArray();
+
+ if (!DecodeUtil.addressValid(receiverAddress)) {
+ throw new ContractValidateException("Invalid receiverAddress");
+ }
+
+
+ if (Arrays.equals(receiverAddress, ownerAddress)) {
+ throw new ContractValidateException(
+ "receiverAddress must not be the same as ownerAddress");
+ }
+
+ AccountCapsule receiverCapsule = accountStore.get(receiverAddress);
+ if (receiverCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(receiverAddress);
+ throw new ContractValidateException(
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + NOT_EXIST_STR);
+ }
+
+ boolean lock = delegateResourceContract.getLock();
+ if (lock && dynamicStore.supportMaxDelegateLockPeriod()) {
+ long lockPeriod = getLockPeriod(true, delegateResourceContract);
+ long maxDelegateLockPeriod = dynamicStore.getMaxDelegateLockPeriod();
+ if (lockPeriod < 0 || lockPeriod > maxDelegateLockPeriod) {
+ throw new ContractValidateException(
+ "The lock period of delegate resource cannot be less than 0 and cannot exceed "
+ + maxDelegateLockPeriod + "!");
+ }
+
+ byte[] key = DelegatedResourceCapsule.createDbKeyV2(ownerAddress, receiverAddress, true);
+ DelegatedResourceCapsule delegatedResourceCapsule = delegatedResourceStore.get(key);
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ if (delegatedResourceCapsule != null) {
+ switch (delegateResourceContract.getResource()) {
+ case BANDWIDTH: {
+ validRemainTime(BANDWIDTH, lockPeriod,
+ delegatedResourceCapsule.getExpireTimeForBandwidth(), now);
+ }
+ break;
+ case ENERGY: {
+ validRemainTime(ENERGY, lockPeriod,
+ delegatedResourceCapsule.getExpireTimeForEnergy(), now);
+ }
+ break;
+ default:
+ throw new ContractValidateException(
+ "ResourceCode error, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ }
+ }
+
+ if (receiverCapsule.getType() == AccountType.Contract) {
+ throw new ContractValidateException(
+ "Do not allow delegate resources to contract addresses");
+ }
+
+ return true;
+ }
+
+ private long getLockPeriod(boolean supportMaxDelegateLockPeriod,
+ DelegateResourceContract delegateResourceContract) {
+ long lockPeriod = delegateResourceContract.getLockPeriod();
+ if (supportMaxDelegateLockPeriod) {
+ return lockPeriod == 0 ? DELEGATE_PERIOD / BLOCK_PRODUCED_INTERVAL : lockPeriod;
+ } else {
+ return DELEGATE_PERIOD / BLOCK_PRODUCED_INTERVAL;
+ }
+ }
+
+ private void validRemainTime(ResourceCode resourceCode, long lockPeriod, long expireTime,
+ long now) throws ContractValidateException {
+ long remainTime = expireTime - now;
+ if (lockPeriod * BLOCK_PRODUCED_INTERVAL < remainTime) {
+ throw new ContractValidateException(
+ "The lock period for " + resourceCode.name() + " this time cannot be less than the "
+ + "remaining time[" + remainTime + "ms] of the last lock period for "
+ + resourceCode.name() + "!");
+ }
+ }
+
+ @Override
+ public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
+ return any.unpack(DelegateResourceContract.class).getOwnerAddress();
+ }
+
+ @Override
+ public long calcFee() {
+ return 0;
+ }
+
+ private void delegateResource(byte[] ownerAddress, byte[] receiverAddress, boolean isBandwidth,
+ long balance, boolean lock, long lockPeriod) {
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicPropertiesStore = chainBaseManager.getDynamicPropertiesStore();
+ DelegatedResourceStore delegatedResourceStore = chainBaseManager.getDelegatedResourceStore();
+ DelegatedResourceAccountIndexStore delegatedResourceAccountIndexStore = chainBaseManager
+ .getDelegatedResourceAccountIndexStore();
+
+ // 1. unlock the expired delegate resource
+ long now = chainBaseManager.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ delegatedResourceStore.unLockExpireResource(ownerAddress, receiverAddress, now);
+
+ //modify DelegatedResourceStore
+ long expireTime = 0;
+ if (lock) {
+ expireTime = now + lockPeriod * BLOCK_PRODUCED_INTERVAL;
+ }
+ byte[] key = DelegatedResourceCapsule.createDbKeyV2(ownerAddress, receiverAddress, lock);
+ DelegatedResourceCapsule delegatedResourceCapsule = delegatedResourceStore.get(key);
+ if (delegatedResourceCapsule == null) {
+ delegatedResourceCapsule = new DelegatedResourceCapsule(ByteString.copyFrom(ownerAddress),
+ ByteString.copyFrom(receiverAddress));
+ }
+
+ if (isBandwidth) {
+ delegatedResourceCapsule.addFrozenBalanceForBandwidth(balance, expireTime);
+ } else {
+ delegatedResourceCapsule.addFrozenBalanceForEnergy(balance, expireTime);
+ }
+ delegatedResourceStore.put(key, delegatedResourceCapsule);
+
+ //modify DelegatedResourceAccountIndexStore
+ delegatedResourceAccountIndexStore.delegateV2(ownerAddress, receiverAddress,
+ dynamicPropertiesStore.getLatestBlockHeaderTimestamp());
+
+ //modify AccountStore for receiver
+ AccountCapsule receiverCapsule = accountStore.get(receiverAddress);
+ if (isBandwidth) {
+ receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForBandwidth(balance);
+ } else {
+ receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForEnergy(balance);
+ }
+ accountStore.put(receiverCapsule.createDbKey(), receiverCapsule);
+ }
+
+}
diff --git a/actuator/src/main/java/org/tron/core/actuator/ExchangeCreateActuator.java b/actuator/src/main/java/org/tron/core/actuator/ExchangeCreateActuator.java
index b7f5b90da45..7edea48e12f 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ExchangeCreateActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeCreateActuator.java
@@ -9,7 +9,6 @@
import java.util.Arrays;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
-import org.tron.common.utils.Commons;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
@@ -28,7 +27,7 @@
import org.tron.protos.contract.ExchangeContract.ExchangeCreateContract;
@Slf4j(topic = "actuator")
-public class ExchangeCreateActuator extends AbstractActuator {
+public class ExchangeCreateActuator extends AbstractExchangeActuator {
public ExchangeCreateActuator() {
super(ContractType.ExchangeCreateContract, ExchangeCreateContract.class);
@@ -58,25 +57,25 @@ public boolean execute(Object object) throws ContractExeException {
long firstTokenBalance = exchangeCreateContract.getFirstTokenBalance();
long secondTokenBalance = exchangeCreateContract.getSecondTokenBalance();
- long newBalance = accountCapsule.getBalance() - fee;
+ long newBalance = subtractExact(accountCapsule.getBalance(), fee);
accountCapsule.setBalance(newBalance);
if (Arrays.equals(firstTokenID, TRX_SYMBOL_BYTES)) {
- accountCapsule.setBalance(newBalance - firstTokenBalance);
+ accountCapsule.setBalance(subtractExact(newBalance, firstTokenBalance));
} else {
accountCapsule
.reduceAssetAmountV2(firstTokenID, firstTokenBalance, dynamicStore, assetIssueStore);
}
if (Arrays.equals(secondTokenID, TRX_SYMBOL_BYTES)) {
- accountCapsule.setBalance(newBalance - secondTokenBalance);
+ accountCapsule.setBalance(subtractExact(newBalance, secondTokenBalance));
} else {
accountCapsule
.reduceAssetAmountV2(secondTokenID, secondTokenBalance, dynamicStore, assetIssueStore);
}
- long id = dynamicStore.getLatestExchangeNum() + 1;
+ long id = addExact(dynamicStore.getLatestExchangeNum(), 1);
long now = dynamicStore.getLatestBlockHeaderTimestamp();
if (dynamicStore.getAllowSameTokenName() == 0) {
//save to old asset store
@@ -121,11 +120,12 @@ public boolean execute(Object object) throws ContractExeException {
if (dynamicStore.supportBlackHoleOptimization()) {
dynamicStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
}
ret.setExchangeId(id);
ret.setStatus(fee, code.SUCESS);
- } catch (BalanceInsufficientException | InvalidProtocolBufferException e) {
+ } catch (BalanceInsufficientException | InvalidProtocolBufferException
+ | ArithmeticException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(fee, code.FAILED);
throw new ContractExeException(e.getMessage());
@@ -135,6 +135,14 @@ public boolean execute(Object object) throws ContractExeException {
@Override
public boolean validate() throws ContractValidateException {
+ try {
+ return doValidate();
+ } catch (ArithmeticException e) {
+ throw new ContractValidateException(e.getMessage());
+ }
+ }
+
+ private boolean doValidate() throws ContractValidateException {
if (this.any == null) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}
@@ -200,7 +208,7 @@ public boolean validate() throws ContractValidateException {
}
if (Arrays.equals(firstTokenID, TRX_SYMBOL_BYTES)) {
- if (accountCapsule.getBalance() < (firstTokenBalance + calcFee())) {
+ if (accountCapsule.getBalance() < addExact(firstTokenBalance, calcFee())) {
throw new ContractValidateException("balance is not enough");
}
} else {
@@ -210,7 +218,7 @@ public boolean validate() throws ContractValidateException {
}
if (Arrays.equals(secondTokenID, TRX_SYMBOL_BYTES)) {
- if (accountCapsule.getBalance() < (secondTokenBalance + calcFee())) {
+ if (accountCapsule.getBalance() < addExact(secondTokenBalance, calcFee())) {
throw new ContractValidateException("balance is not enough");
}
} else {
diff --git a/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java b/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java
index 7848f898ced..e9c27e33920 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java
@@ -24,13 +24,12 @@
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.core.store.ExchangeStore;
import org.tron.core.store.ExchangeV2Store;
-import org.tron.core.utils.TransactionUtil;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.contract.ExchangeContract.ExchangeInjectContract;
@Slf4j(topic = "actuator")
-public class ExchangeInjectActuator extends AbstractActuator {
+public class ExchangeInjectActuator extends AbstractExchangeActuator {
public ExchangeInjectActuator() {
super(ContractType.ExchangeInjectContract, ExchangeInjectContract.class);
@@ -56,8 +55,8 @@ public boolean execute(Object object) throws ContractExeException {
.get(exchangeInjectContract.getOwnerAddress().toByteArray());
ExchangeCapsule exchangeCapsule;
- exchangeCapsule = Commons.getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store).
- get(ByteArray.fromLong(exchangeInjectContract.getExchangeId()));
+ exchangeCapsule = Commons.getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store)
+ .get(ByteArray.fromLong(exchangeInjectContract.getExchangeId()));
byte[] firstTokenID = exchangeCapsule.getFirstTokenId();
byte[] secondTokenID = exchangeCapsule.getSecondTokenId();
long firstTokenBalance = exchangeCapsule.getFirstTokenBalance();
@@ -71,29 +70,29 @@ public boolean execute(Object object) throws ContractExeException {
if (Arrays.equals(tokenID, firstTokenID)) {
anotherTokenID = secondTokenID;
- anotherTokenQuant = Math
- .floorDiv(Math.multiplyExact(secondTokenBalance, tokenQuant), firstTokenBalance);
- exchangeCapsule.setBalance(firstTokenBalance + tokenQuant,
- secondTokenBalance + anotherTokenQuant);
+ anotherTokenQuant = floorDiv(multiplyExact(
+ secondTokenBalance, tokenQuant), firstTokenBalance);
+ exchangeCapsule.setBalance(addExact(firstTokenBalance, tokenQuant),
+ addExact(secondTokenBalance, anotherTokenQuant));
} else {
anotherTokenID = firstTokenID;
- anotherTokenQuant = Math
- .floorDiv(Math.multiplyExact(firstTokenBalance, tokenQuant), secondTokenBalance);
- exchangeCapsule.setBalance(firstTokenBalance + anotherTokenQuant,
- secondTokenBalance + tokenQuant);
+ anotherTokenQuant = floorDiv(multiplyExact(
+ firstTokenBalance, tokenQuant), secondTokenBalance);
+ exchangeCapsule.setBalance(addExact(firstTokenBalance, anotherTokenQuant),
+ addExact(secondTokenBalance, tokenQuant));
}
- long newBalance = accountCapsule.getBalance() - calcFee();
+ long newBalance = subtractExact(accountCapsule.getBalance(), calcFee());
accountCapsule.setBalance(newBalance);
if (Arrays.equals(tokenID, TRX_SYMBOL_BYTES)) {
- accountCapsule.setBalance(newBalance - tokenQuant);
+ accountCapsule.setBalance(subtractExact(newBalance, tokenQuant));
} else {
accountCapsule.reduceAssetAmountV2(tokenID, tokenQuant, dynamicStore, assetIssueStore);
}
if (Arrays.equals(anotherTokenID, TRX_SYMBOL_BYTES)) {
- accountCapsule.setBalance(newBalance - anotherTokenQuant);
+ accountCapsule.setBalance(subtractExact(newBalance, anotherTokenQuant));
} else {
accountCapsule
.reduceAssetAmountV2(anotherTokenID, anotherTokenQuant, dynamicStore, assetIssueStore);
@@ -105,7 +104,8 @@ public boolean execute(Object object) throws ContractExeException {
ret.setExchangeInjectAnotherAmount(anotherTokenQuant);
ret.setStatus(fee, code.SUCESS);
- } catch (ItemNotFoundException | InvalidProtocolBufferException e) {
+ } catch (ItemNotFoundException | InvalidProtocolBufferException
+ | ArithmeticException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(fee, code.FAILED);
throw new ContractExeException(e.getMessage());
@@ -115,6 +115,14 @@ public boolean execute(Object object) throws ContractExeException {
@Override
public boolean validate() throws ContractValidateException {
+ try {
+ return doValidate();
+ } catch (ArithmeticException e) {
+ throw new ContractValidateException(e.getMessage());
+ }
+ }
+
+ private boolean doValidate() throws ContractValidateException {
if (this.any == null) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}
@@ -156,8 +164,8 @@ public boolean validate() throws ContractValidateException {
ExchangeCapsule exchangeCapsule;
try {
- exchangeCapsule = Commons.getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store).
- get(ByteArray.fromLong(contract.getExchangeId()));
+ exchangeCapsule = Commons.getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store)
+ .get(ByteArray.fromLong(contract.getExchangeId()));
} catch (ItemNotFoundException ex) {
throw new ContractValidateException("Exchange[" + contract.getExchangeId() + ActuatorConstant
@@ -179,9 +187,9 @@ public boolean validate() throws ContractValidateException {
byte[] anotherTokenID;
long anotherTokenQuant;
- if (dynamicStore.getAllowSameTokenName() == 1 &&
- !Arrays.equals(tokenID, TRX_SYMBOL_BYTES) &&
- !isNumber(tokenID)) {
+ if (dynamicStore.getAllowSameTokenName() == 1
+ && !Arrays.equals(tokenID, TRX_SYMBOL_BYTES)
+ && !isNumber(tokenID)) {
throw new ContractValidateException("token id is not a valid number");
}
@@ -208,14 +216,14 @@ public boolean validate() throws ContractValidateException {
anotherTokenID = secondTokenID;
anotherTokenQuant = bigSecondTokenBalance.multiply(bigTokenQuant)
.divide(bigFirstTokenBalance).longValueExact();
- newTokenBalance = firstTokenBalance + tokenQuant;
- newAnotherTokenBalance = secondTokenBalance + anotherTokenQuant;
+ newTokenBalance = addExact(firstTokenBalance, tokenQuant);
+ newAnotherTokenBalance = addExact(secondTokenBalance, anotherTokenQuant);
} else {
anotherTokenID = firstTokenID;
anotherTokenQuant = bigFirstTokenBalance.multiply(bigTokenQuant)
.divide(bigSecondTokenBalance).longValueExact();
- newTokenBalance = secondTokenBalance + tokenQuant;
- newAnotherTokenBalance = firstTokenBalance + anotherTokenQuant;
+ newTokenBalance = addExact(secondTokenBalance, tokenQuant);
+ newAnotherTokenBalance = addExact(firstTokenBalance, anotherTokenQuant);
}
if (anotherTokenQuant <= 0) {
@@ -228,7 +236,7 @@ public boolean validate() throws ContractValidateException {
}
if (Arrays.equals(tokenID, TRX_SYMBOL_BYTES)) {
- if (accountCapsule.getBalance() < (tokenQuant + calcFee())) {
+ if (accountCapsule.getBalance() < addExact(tokenQuant, calcFee())) {
throw new ContractValidateException("balance is not enough");
}
} else {
@@ -238,7 +246,7 @@ public boolean validate() throws ContractValidateException {
}
if (Arrays.equals(anotherTokenID, TRX_SYMBOL_BYTES)) {
- if (accountCapsule.getBalance() < (anotherTokenQuant + calcFee())) {
+ if (accountCapsule.getBalance() < addExact(anotherTokenQuant, calcFee())) {
throw new ContractValidateException("balance is not enough");
}
} else {
diff --git a/actuator/src/main/java/org/tron/core/actuator/ExchangeTransactionActuator.java b/actuator/src/main/java/org/tron/core/actuator/ExchangeTransactionActuator.java
index 612f673832e..c5198224c5c 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ExchangeTransactionActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeTransactionActuator.java
@@ -24,13 +24,12 @@
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.core.store.ExchangeStore;
import org.tron.core.store.ExchangeV2Store;
-import org.tron.core.utils.TransactionUtil;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.contract.ExchangeContract.ExchangeTransactionContract;
@Slf4j(topic = "actuator")
-public class ExchangeTransactionActuator extends AbstractActuator {
+public class ExchangeTransactionActuator extends AbstractExchangeActuator {
public ExchangeTransactionActuator() {
super(ContractType.ExchangeTransactionContract, ExchangeTransactionContract.class);
@@ -56,8 +55,8 @@ public boolean execute(Object object) throws ContractExeException {
.get(exchangeTransactionContract.getOwnerAddress().toByteArray());
ExchangeCapsule exchangeCapsule = Commons
- .getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store).
- get(ByteArray.fromLong(exchangeTransactionContract.getExchangeId()));
+ .getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store)
+ .get(ByteArray.fromLong(exchangeTransactionContract.getExchangeId()));
byte[] firstTokenID = exchangeCapsule.getFirstTokenId();
byte[] secondTokenID = exchangeCapsule.getSecondTokenId();
@@ -66,7 +65,8 @@ public boolean execute(Object object) throws ContractExeException {
long tokenQuant = exchangeTransactionContract.getQuant();
byte[] anotherTokenID;
- long anotherTokenQuant = exchangeCapsule.transaction(tokenID, tokenQuant);
+ long anotherTokenQuant = exchangeCapsule.transaction(tokenID, tokenQuant,
+ dynamicStore.allowStrictMath(), allowHarden());
if (Arrays.equals(tokenID, firstTokenID)) {
anotherTokenID = secondTokenID;
@@ -74,17 +74,17 @@ public boolean execute(Object object) throws ContractExeException {
anotherTokenID = firstTokenID;
}
- long newBalance = accountCapsule.getBalance() - calcFee();
+ long newBalance = subtractExact(accountCapsule.getBalance(), calcFee());
accountCapsule.setBalance(newBalance);
if (Arrays.equals(tokenID, TRX_SYMBOL_BYTES)) {
- accountCapsule.setBalance(newBalance - tokenQuant);
+ accountCapsule.setBalance(subtractExact(newBalance, tokenQuant));
} else {
accountCapsule.reduceAssetAmountV2(tokenID, tokenQuant, dynamicStore, assetIssueStore);
}
if (Arrays.equals(anotherTokenID, TRX_SYMBOL_BYTES)) {
- accountCapsule.setBalance(newBalance + anotherTokenQuant);
+ accountCapsule.setBalance(addExact(newBalance, anotherTokenQuant));
} else {
accountCapsule
.addAssetAmountV2(anotherTokenID, anotherTokenQuant, dynamicStore, assetIssueStore);
@@ -97,7 +97,8 @@ public boolean execute(Object object) throws ContractExeException {
ret.setExchangeReceivedAmount(anotherTokenQuant);
ret.setStatus(fee, code.SUCESS);
- } catch (ItemNotFoundException | InvalidProtocolBufferException e) {
+ } catch (ItemNotFoundException | InvalidProtocolBufferException
+ | ContractValidateException | ArithmeticException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(fee, code.FAILED);
throw new ContractExeException(e.getMessage());
@@ -108,6 +109,14 @@ public boolean execute(Object object) throws ContractExeException {
@Override
public boolean validate() throws ContractValidateException {
+ try {
+ return doValidate();
+ } catch (ArithmeticException e) {
+ throw new ContractValidateException(e.getMessage());
+ }
+ }
+
+ private boolean doValidate() throws ContractValidateException {
if (this.any == null) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}
@@ -149,8 +158,8 @@ public boolean validate() throws ContractValidateException {
ExchangeCapsule exchangeCapsule;
try {
- exchangeCapsule = Commons.getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store).
- get(ByteArray.fromLong(contract.getExchangeId()));
+ exchangeCapsule = Commons.getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store)
+ .get(ByteArray.fromLong(contract.getExchangeId()));
} catch (ItemNotFoundException ex) {
throw new ContractValidateException("Exchange[" + contract.getExchangeId()
+ ActuatorConstant.NOT_EXIST_STR);
@@ -165,9 +174,9 @@ public boolean validate() throws ContractValidateException {
long tokenQuant = contract.getQuant();
long tokenExpected = contract.getExpected();
- if (dynamicStore.getAllowSameTokenName() == 1 &&
- !Arrays.equals(tokenID, TRX_SYMBOL_BYTES) &&
- !isNumber(tokenID)) {
+ if (dynamicStore.getAllowSameTokenName() == 1
+ && !Arrays.equals(tokenID, TRX_SYMBOL_BYTES)
+ && !isNumber(tokenID)) {
throw new ContractValidateException("token id is not a valid number");
}
if (!Arrays.equals(tokenID, firstTokenID) && !Arrays.equals(tokenID, secondTokenID)) {
@@ -190,13 +199,13 @@ public boolean validate() throws ContractValidateException {
long balanceLimit = dynamicStore.getExchangeBalanceLimit();
long tokenBalance = (Arrays.equals(tokenID, firstTokenID) ? firstTokenBalance
: secondTokenBalance);
- tokenBalance += tokenQuant;
+ tokenBalance = addExact(tokenBalance, tokenQuant);
if (tokenBalance > balanceLimit) {
throw new ContractValidateException("token balance must less than " + balanceLimit);
}
if (Arrays.equals(tokenID, TRX_SYMBOL_BYTES)) {
- if (accountCapsule.getBalance() < (tokenQuant + calcFee())) {
+ if (accountCapsule.getBalance() < addExact(tokenQuant, calcFee())) {
throw new ContractValidateException("balance is not enough");
}
} else {
@@ -205,7 +214,8 @@ public boolean validate() throws ContractValidateException {
}
}
- long anotherTokenQuant = exchangeCapsule.transaction(tokenID, tokenQuant);
+ long anotherTokenQuant = exchangeCapsule.transaction(tokenID, tokenQuant,
+ dynamicStore.allowStrictMath(), allowHarden());
if (anotherTokenQuant < tokenExpected) {
throw new ContractValidateException("token required must greater than expected");
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java b/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java
index 8929305d68c..d43d5053f67 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java
@@ -7,6 +7,7 @@
import com.google.protobuf.InvalidProtocolBufferException;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
@@ -25,13 +26,12 @@
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.core.store.ExchangeStore;
import org.tron.core.store.ExchangeV2Store;
-import org.tron.core.utils.TransactionUtil;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.contract.ExchangeContract.ExchangeWithdrawContract;
@Slf4j(topic = "actuator")
-public class ExchangeWithdrawActuator extends AbstractActuator {
+public class ExchangeWithdrawActuator extends AbstractExchangeActuator {
public ExchangeWithdrawActuator() {
super(ContractType.ExchangeWithdrawContract, ExchangeWithdrawContract.class);
@@ -57,8 +57,8 @@ public boolean execute(Object object) throws ContractExeException {
.get(exchangeWithdrawContract.getOwnerAddress().toByteArray());
ExchangeCapsule exchangeCapsule = Commons
- .getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store).
- get(ByteArray.fromLong(exchangeWithdrawContract.getExchangeId()));
+ .getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store)
+ .get(ByteArray.fromLong(exchangeWithdrawContract.getExchangeId()));
byte[] firstTokenID = exchangeCapsule.getFirstTokenId();
byte[] secondTokenID = exchangeCapsule.getSecondTokenId();
@@ -76,32 +76,28 @@ public boolean execute(Object object) throws ContractExeException {
BigInteger bigTokenQuant = new BigInteger(String.valueOf(tokenQuant));
if (Arrays.equals(tokenID, firstTokenID)) {
anotherTokenID = secondTokenID;
-// anotherTokenQuant = Math
-// .floorDiv(Math.multiplyExact(secondTokenBalance, tokenQuant), firstTokenBalance);
anotherTokenQuant = bigSecondTokenBalance.multiply(bigTokenQuant)
.divide(bigFirstTokenBalance).longValueExact();
- exchangeCapsule.setBalance(firstTokenBalance - tokenQuant,
- secondTokenBalance - anotherTokenQuant);
+ exchangeCapsule.setBalance(subtractExact(firstTokenBalance, tokenQuant),
+ subtractExact(secondTokenBalance, anotherTokenQuant));
} else {
anotherTokenID = firstTokenID;
-// anotherTokenQuant = Math
-// .floorDiv(Math.multiplyExact(firstTokenBalance, tokenQuant), secondTokenBalance);
anotherTokenQuant = bigFirstTokenBalance.multiply(bigTokenQuant)
.divide(bigSecondTokenBalance).longValueExact();
- exchangeCapsule.setBalance(firstTokenBalance - anotherTokenQuant,
- secondTokenBalance - tokenQuant);
+ exchangeCapsule.setBalance(subtractExact(firstTokenBalance, anotherTokenQuant),
+ subtractExact(secondTokenBalance, tokenQuant));
}
- long newBalance = accountCapsule.getBalance() - calcFee();
+ long newBalance = subtractExact(accountCapsule.getBalance(), calcFee());
if (Arrays.equals(tokenID, TRX_SYMBOL_BYTES)) {
- accountCapsule.setBalance(newBalance + tokenQuant);
+ accountCapsule.setBalance(addExact(newBalance, tokenQuant));
} else {
accountCapsule.addAssetAmountV2(tokenID, tokenQuant, dynamicStore, assetIssueStore);
}
if (Arrays.equals(anotherTokenID, TRX_SYMBOL_BYTES)) {
- accountCapsule.setBalance(newBalance + anotherTokenQuant);
+ accountCapsule.setBalance(addExact(newBalance, anotherTokenQuant));
} else {
accountCapsule
.addAssetAmountV2(anotherTokenID, anotherTokenQuant, dynamicStore, assetIssueStore);
@@ -114,7 +110,8 @@ public boolean execute(Object object) throws ContractExeException {
ret.setExchangeWithdrawAnotherAmount(anotherTokenQuant);
ret.setStatus(fee, code.SUCESS);
- } catch (ItemNotFoundException | InvalidProtocolBufferException e) {
+ } catch (ItemNotFoundException | InvalidProtocolBufferException
+ | ArithmeticException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(fee, code.FAILED);
throw new ContractExeException(e.getMessage());
@@ -125,6 +122,14 @@ public boolean execute(Object object) throws ContractExeException {
@Override
public boolean validate() throws ContractValidateException {
+ try {
+ return doValidate();
+ } catch (ArithmeticException e) {
+ throw new ContractValidateException(e.getMessage());
+ }
+ }
+
+ private boolean doValidate() throws ContractValidateException {
if (this.any == null) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}
@@ -166,8 +171,8 @@ public boolean validate() throws ContractValidateException {
ExchangeCapsule exchangeCapsule;
try {
- exchangeCapsule = Commons.getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store).
- get(ByteArray.fromLong(contract.getExchangeId()));
+ exchangeCapsule = Commons.getExchangeStoreFinal(dynamicStore, exchangeStore, exchangeV2Store)
+ .get(ByteArray.fromLong(contract.getExchangeId()));
} catch (ItemNotFoundException ex) {
throw new ContractValidateException("Exchange[" + contract.getExchangeId() + ActuatorConstant
.NOT_EXIST_STR);
@@ -187,9 +192,9 @@ public boolean validate() throws ContractValidateException {
long anotherTokenQuant;
- if (dynamicStore.getAllowSameTokenName() == 1 &&
- !Arrays.equals(tokenID, TRX_SYMBOL_BYTES) &&
- !isNumber(tokenID)) {
+ if (dynamicStore.getAllowSameTokenName() == 1
+ && !Arrays.equals(tokenID, TRX_SYMBOL_BYTES)
+ && !isNumber(tokenID)) {
throw new ContractValidateException("token id is not a valid number");
}
@@ -209,9 +214,8 @@ public boolean validate() throws ContractValidateException {
BigDecimal bigFirstTokenBalance = new BigDecimal(String.valueOf(firstTokenBalance));
BigDecimal bigSecondTokenBalance = new BigDecimal(String.valueOf(secondTokenBalance));
BigDecimal bigTokenQuant = new BigDecimal(String.valueOf(tokenQuant));
+ final boolean allowHarden = allowHarden();
if (Arrays.equals(tokenID, firstTokenID)) {
-// anotherTokenQuant = Math
-// .floorDiv(Math.multiplyExact(secondTokenBalance, tokenQuant), firstTokenBalance);
anotherTokenQuant = bigSecondTokenBalance.multiply(bigTokenQuant)
.divideToIntegralValue(bigFirstTokenBalance).longValueExact();
if (firstTokenBalance < tokenQuant || secondTokenBalance < anotherTokenQuant) {
@@ -221,17 +225,24 @@ public boolean validate() throws ContractValidateException {
if (anotherTokenQuant <= 0) {
throw new ContractValidateException("withdraw another token quant must greater than zero");
}
-
- double remainder = bigSecondTokenBalance.multiply(bigTokenQuant)
- .divide(bigFirstTokenBalance, 4, BigDecimal.ROUND_HALF_UP).doubleValue()
- - anotherTokenQuant;
- if (remainder / anotherTokenQuant > 0.0001) {
- throw new ContractValidateException("Not precise enough");
+ if (allowHarden) {
+ BigDecimal remainder = bigSecondTokenBalance.multiply(bigTokenQuant)
+ .divide(bigFirstTokenBalance, 4, RoundingMode.HALF_UP)
+ .subtract(BigDecimal.valueOf(anotherTokenQuant));
+ if (remainder.compareTo(
+ BigDecimal.valueOf(anotherTokenQuant).multiply(new BigDecimal("0.0001"))) > 0) {
+ throw new ContractValidateException("Not precise enough");
+ }
+ } else {
+ double remainder = bigSecondTokenBalance.multiply(bigTokenQuant)
+ .divide(bigFirstTokenBalance, 4, BigDecimal.ROUND_HALF_UP).doubleValue()
+ - anotherTokenQuant;
+ if (remainder / anotherTokenQuant > 0.0001) {
+ throw new ContractValidateException("Not precise enough");
+ }
}
} else {
-// anotherTokenQuant = Math
-// .floorDiv(Math.multiplyExact(firstTokenBalance, tokenQuant), secondTokenBalance);
anotherTokenQuant = bigFirstTokenBalance.multiply(bigTokenQuant)
.divideToIntegralValue(bigSecondTokenBalance).longValueExact();
if (secondTokenBalance < tokenQuant || firstTokenBalance < anotherTokenQuant) {
@@ -242,11 +253,21 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException("withdraw another token quant must greater than zero");
}
- double remainder = bigFirstTokenBalance.multiply(bigTokenQuant)
- .divide(bigSecondTokenBalance, 4, BigDecimal.ROUND_HALF_UP).doubleValue()
- - anotherTokenQuant;
- if (remainder / anotherTokenQuant > 0.0001) {
- throw new ContractValidateException("Not precise enough");
+ if (allowHarden) {
+ BigDecimal remainder = bigFirstTokenBalance.multiply(bigTokenQuant)
+ .divide(bigSecondTokenBalance, 4, RoundingMode.HALF_UP)
+ .subtract(BigDecimal.valueOf(anotherTokenQuant));
+ if (remainder.compareTo(
+ BigDecimal.valueOf(anotherTokenQuant).multiply(new BigDecimal("0.0001"))) > 0) {
+ throw new ContractValidateException("Not precise enough");
+ }
+ } else {
+ double remainder = bigFirstTokenBalance.multiply(bigTokenQuant)
+ .divide(bigSecondTokenBalance, 4, BigDecimal.ROUND_HALF_UP).doubleValue()
+ - anotherTokenQuant;
+ if (remainder / anotherTokenQuant > 0.0001) {
+ throw new ContractValidateException("Not precise enough");
+ }
}
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceActuator.java b/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceActuator.java
index af36795035f..421b4e3013a 100755
--- a/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceActuator.java
@@ -189,7 +189,7 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException("frozenBalance must be positive");
}
if (frozenBalance < TRX_PRECISION) {
- throw new ContractValidateException("frozenBalance must be more than 1TRX");
+ throw new ContractValidateException("frozenBalance must be greater than or equal to 1 TRX");
}
int frozenCount = accountCapsule.getFrozenCount();
@@ -197,7 +197,7 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException("frozenCount must be 0 or 1");
}
if (frozenBalance > accountCapsule.getBalance()) {
- throw new ContractValidateException("frozenBalance must be less than accountBalance");
+ throw new ContractValidateException("frozenBalance must be less than or equal to accountBalance");
}
long frozenDuration = freezeBalanceContract.getFrozenDuration();
@@ -244,8 +244,7 @@ public boolean validate() throws ContractValidateException {
//If the receiver is included in the contract, the receiver will receive the resource.
if (!ArrayUtils.isEmpty(receiverAddress) && dynamicStore.supportDR()) {
if (Arrays.equals(receiverAddress, ownerAddress)) {
- throw new ContractValidateException(
- "receiverAddress must not be the same as ownerAddress");
+ throw new ContractValidateException("receiverAddress must not be the same as ownerAddress");
}
if (!DecodeUtil.addressValid(receiverAddress)) {
@@ -269,6 +268,11 @@ public boolean validate() throws ContractValidateException {
}
+ if (dynamicStore.supportUnfreezeDelay()) {
+ throw new ContractValidateException(
+ "freeze v2 is open, old freeze is closed");
+ }
+
return true;
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceV2Actuator.java b/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceV2Actuator.java
new file mode 100755
index 00000000000..f0e5505be9c
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceV2Actuator.java
@@ -0,0 +1,176 @@
+package org.tron.core.actuator;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.AccountStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.protos.Protocol.Transaction.Contract.ContractType;
+import org.tron.protos.Protocol.Transaction.Result.code;
+import org.tron.protos.contract.BalanceContract.FreezeBalanceV2Contract;
+
+import java.util.Objects;
+
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+
+@Slf4j(topic = "actuator")
+public class FreezeBalanceV2Actuator extends AbstractActuator {
+
+ public FreezeBalanceV2Actuator() {
+ super(ContractType.FreezeBalanceV2Contract, FreezeBalanceV2Contract.class);
+ }
+
+ @Override
+ public boolean execute(Object result) throws ContractExeException {
+ TransactionResultCapsule ret = (TransactionResultCapsule) result;
+ if (Objects.isNull(ret)) {
+ throw new RuntimeException(ActuatorConstant.TX_RESULT_NULL);
+ }
+
+ long fee = calcFee();
+ final FreezeBalanceV2Contract freezeBalanceV2Contract;
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ try {
+ freezeBalanceV2Contract = any.unpack(FreezeBalanceV2Contract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ ret.setStatus(fee, code.FAILED);
+ throw new ContractExeException(e.getMessage());
+ }
+ AccountCapsule accountCapsule = accountStore.get(freezeBalanceV2Contract.getOwnerAddress().toByteArray());
+
+ if (dynamicStore.supportAllowNewResourceModel()
+ && accountCapsule.oldTronPowerIsNotInitialized()) {
+ accountCapsule.initializeOldTronPower();
+ }
+
+ long frozenBalance = freezeBalanceV2Contract.getFrozenBalance();
+ long newBalance = accountCapsule.getBalance() - frozenBalance;
+
+ switch (freezeBalanceV2Contract.getResource()) {
+ case BANDWIDTH:
+ long oldNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForBandwidthV2(frozenBalance);
+ long newNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ dynamicStore.addTotalNetWeight(newNetWeight - oldNetWeight);
+ break;
+ case ENERGY:
+ long oldEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForEnergyV2(frozenBalance);
+ long newEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ dynamicStore.addTotalEnergyWeight(newEnergyWeight - oldEnergyWeight);
+ break;
+ case TRON_POWER:
+ long oldTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ accountCapsule.addFrozenForTronPowerV2(frozenBalance);
+ long newTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ dynamicStore.addTotalTronPowerWeight(newTPWeight - oldTPWeight);
+ break;
+ default:
+ logger.debug("Resource Code Error.");
+ }
+
+ accountCapsule.setBalance(newBalance);
+ accountStore.put(accountCapsule.createDbKey(), accountCapsule);
+
+ ret.setStatus(fee, code.SUCESS);
+
+ return true;
+ }
+
+ @Override
+ public boolean validate() throws ContractValidateException {
+ if (this.any == null) {
+ throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
+ }
+ if (chainBaseManager == null) {
+ throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
+ }
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ if (!any.is(FreezeBalanceV2Contract.class)) {
+ throw new ContractValidateException(
+ "contract type error,expected type [FreezeBalanceV2Contract],real type[" + any
+ .getClass() + "]");
+ }
+
+ if (!dynamicStore.supportUnfreezeDelay()) {
+ throw new ContractValidateException("Not support FreezeV2 transaction,"
+ + " need to be opened by the committee");
+ }
+
+ final FreezeBalanceV2Contract freezeBalanceV2Contract;
+ try {
+ freezeBalanceV2Contract = this.any.unpack(FreezeBalanceV2Contract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+ byte[] ownerAddress = freezeBalanceV2Contract.getOwnerAddress().toByteArray();
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+
+ AccountCapsule accountCapsule = accountStore.get(ownerAddress);
+ if (accountCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR + readableOwnerAddress + NOT_EXIST_STR);
+ }
+
+ long frozenBalance = freezeBalanceV2Contract.getFrozenBalance();
+ if (frozenBalance <= 0) {
+ throw new ContractValidateException("frozenBalance must be positive");
+ }
+ if (frozenBalance < TRX_PRECISION) {
+ throw new ContractValidateException("frozenBalance must be greater than or equal to 1 TRX");
+ }
+
+ if (frozenBalance > accountCapsule.getBalance()) {
+ throw new ContractValidateException("frozenBalance must be less than or equal to accountBalance");
+ }
+
+ switch (freezeBalanceV2Contract.getResource()) {
+ case BANDWIDTH:
+ case ENERGY:
+ break;
+ case TRON_POWER:
+ if (!dynamicStore.supportAllowNewResourceModel()) {
+ throw new ContractValidateException(
+ "ResourceCode error, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ break;
+ default:
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ throw new ContractValidateException(
+ "ResourceCode error, valid ResourceCode[BANDWIDTH、ENERGY、TRON_POWER]");
+ } else {
+ throw new ContractValidateException(
+ "ResourceCode error, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
+ return any.unpack(FreezeBalanceV2Contract.class).getOwnerAddress();
+ }
+
+ @Override
+ public long calcFee() {
+ return 0;
+ }
+
+}
\ No newline at end of file
diff --git a/actuator/src/main/java/org/tron/core/actuator/MarketCancelOrderActuator.java b/actuator/src/main/java/org/tron/core/actuator/MarketCancelOrderActuator.java
index f859e580253..d4260e38163 100644
--- a/actuator/src/main/java/org/tron/core/actuator/MarketCancelOrderActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/MarketCancelOrderActuator.java
@@ -23,7 +23,6 @@
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
-import org.tron.common.utils.Commons;
import org.tron.common.utils.DecodeUtil;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.MarketOrderCapsule;
@@ -44,7 +43,6 @@
import org.tron.protos.Protocol.MarketOrder.State;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
-import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract;
import org.tron.protos.contract.MarketContract.MarketCancelOrderContract;
@Slf4j(topic = "actuator")
@@ -100,7 +98,7 @@ public boolean execute(Object object) throws ContractExeException {
if (dynamicStore.supportBlackHoleOptimization()) {
dynamicStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
}
// 1. return balance and token
MarketUtils
@@ -222,7 +220,7 @@ public boolean validate() throws ContractValidateException {
@Override
public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
- return any.unpack(AssetIssueContract.class).getOwnerAddress();
+ return any.unpack(MarketCancelOrderContract.class).getOwnerAddress();
}
@Override
diff --git a/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java b/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java
index 15e5a98f86a..369857ae6c1 100644
--- a/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java
@@ -54,7 +54,6 @@
import org.tron.protos.Protocol.MarketPrice;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
-import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract;
import org.tron.protos.contract.MarketContract.MarketSellAssetContract;
@Slf4j(topic = "actuator")
@@ -129,7 +128,7 @@ public boolean execute(Object object) throws ContractExeException {
if (dynamicStore.supportBlackHoleOptimization()) {
dynamicStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
}
// 1. transfer of balance
transferBalanceOrToken(accountCapsule);
@@ -244,7 +243,7 @@ public boolean validate() throws ContractValidateException {
long fee = calcFee();
if (Arrays.equals(sellTokenID, "_".getBytes())) {
- if (ownerAccount.getBalance() < Math.addExact(sellTokenQuantity, fee)) {
+ if (ownerAccount.getBalance() < addExact(sellTokenQuantity, fee)) {
throw new ContractValidateException("No enough balance !");
}
} else {
@@ -283,7 +282,7 @@ public boolean validate() throws ContractValidateException {
@Override
public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
- return any.unpack(AssetIssueContract.class).getOwnerAddress();
+ return any.unpack(MarketSellAssetContract.class).getOwnerAddress();
}
@Override
@@ -401,7 +400,8 @@ private void matchSingleOrder(MarketOrderCapsule takerOrderCapsule,
// makerSellTokenQuantity_A/makerBuyTokenQuantity_TRX
long takerBuyTokenQuantityRemain = MarketUtils
- .multiplyAndDivide(takerSellRemainQuantity, makerSellQuantity, makerBuyQuantity);
+ .multiplyAndDivide(takerSellRemainQuantity, makerSellQuantity, makerBuyQuantity,
+ this.disableJavaLangMath());
if (takerBuyTokenQuantityRemain == 0) {
// quantity too small, return sellToken to user
@@ -424,7 +424,8 @@ private void matchSingleOrder(MarketOrderCapsule takerOrderCapsule,
// makerBuyTokenQuantity_TRX / makerSellTokenQuantity_A
makerBuyTokenQuantityReceive = MarketUtils
- .multiplyAndDivide(makerSellRemainQuantity, makerBuyQuantity, makerSellQuantity);
+ .multiplyAndDivide(makerSellRemainQuantity, makerBuyQuantity, makerSellQuantity,
+ this.disableJavaLangMath());
takerBuyTokenQuantityReceive = makerOrderCapsule.getSellTokenQuantityRemain();
long takerSellTokenLeft =
@@ -447,7 +448,7 @@ private void matchSingleOrder(MarketOrderCapsule takerOrderCapsule,
takerOrderCapsule.setSellTokenQuantityRemain(0);
MarketUtils.updateOrderState(takerOrderCapsule, State.INACTIVE, marketAccountStore);
- makerOrderCapsule.setSellTokenQuantityRemain(Math.subtractExact(
+ makerOrderCapsule.setSellTokenQuantityRemain(subtractExact(
makerOrderCapsule.getSellTokenQuantityRemain(), takerBuyTokenQuantityRemain));
} else {
// taker > maker
@@ -458,7 +459,8 @@ private void matchSingleOrder(MarketOrderCapsule takerOrderCapsule,
// makerSellTokenQuantityRemain_A/makerBuyTokenQuantityCurrent_TRX =
// makerSellTokenQuantity_A/makerBuyTokenQuantity_TRX
makerBuyTokenQuantityReceive = MarketUtils
- .multiplyAndDivide(makerSellRemainQuantity, makerBuyQuantity, makerSellQuantity);
+ .multiplyAndDivide(makerSellRemainQuantity, makerBuyQuantity, makerSellQuantity,
+ this.disableJavaLangMath());
MarketUtils.updateOrderState(makerOrderCapsule, State.INACTIVE, marketAccountStore);
if (makerBuyTokenQuantityReceive == 0) {
@@ -475,7 +477,7 @@ private void matchSingleOrder(MarketOrderCapsule takerOrderCapsule,
return;
} else {
makerOrderCapsule.setSellTokenQuantityRemain(0);
- takerOrderCapsule.setSellTokenQuantityRemain(Math.subtractExact(
+ takerOrderCapsule.setSellTokenQuantityRemain(subtractExact(
takerOrderCapsule.getSellTokenQuantityRemain(), makerBuyTokenQuantityReceive));
}
}
@@ -524,7 +526,8 @@ private MarketOrderCapsule createAndSaveOrder(AccountCapsule accountCapsule,
private void transferBalanceOrToken(AccountCapsule accountCapsule) {
if (Arrays.equals(sellTokenID, "_".getBytes())) {
- accountCapsule.setBalance(Math.subtractExact(accountCapsule.getBalance(), sellTokenQuantity));
+ accountCapsule.setBalance(subtractExact(
+ accountCapsule.getBalance(), sellTokenQuantity));
} else {
accountCapsule
.reduceAssetAmountV2(sellTokenID, sellTokenQuantity, dynamicStore, assetIssueStore);
@@ -537,7 +540,7 @@ private void addTrxOrToken(MarketOrderCapsule orderCapsule, long num,
byte[] buyTokenId = orderCapsule.getBuyTokenId();
if (Arrays.equals(buyTokenId, "_".getBytes())) {
- accountCapsule.setBalance(Math.addExact(accountCapsule.getBalance(), num));
+ accountCapsule.setBalance(addExact(accountCapsule.getBalance(), num));
} else {
accountCapsule
.addAssetAmountV2(buyTokenId, num, dynamicStore, assetIssueStore);
@@ -550,7 +553,7 @@ private void addTrxOrToken(MarketOrderCapsule orderCapsule, long num) {
byte[] buyTokenId = orderCapsule.getBuyTokenId();
if (Arrays.equals(buyTokenId, "_".getBytes())) {
- accountCapsule.setBalance(Math.addExact(accountCapsule.getBalance(), num));
+ accountCapsule.setBalance(addExact(accountCapsule.getBalance(), num));
} else {
accountCapsule
.addAssetAmountV2(buyTokenId, num, dynamicStore, assetIssueStore);
diff --git a/actuator/src/main/java/org/tron/core/actuator/ParticipateAssetIssueActuator.java b/actuator/src/main/java/org/tron/core/actuator/ParticipateAssetIssueActuator.java
index 77e345b2a92..7fdf15acd18 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ParticipateAssetIssueActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ParticipateAssetIssueActuator.java
@@ -64,8 +64,8 @@ public boolean execute(Object object) throws ContractExeException {
//subtract from owner address
byte[] ownerAddress = participateAssetIssueContract.getOwnerAddress().toByteArray();
AccountCapsule ownerAccount = accountStore.get(ownerAddress);
- long balance = Math.subtractExact(ownerAccount.getBalance(), cost);
- balance = Math.subtractExact(balance, fee);
+ long balance = subtractExact(ownerAccount.getBalance(), cost);
+ balance = subtractExact(balance, fee);
ownerAccount.setBalance(balance);
byte[] key = participateAssetIssueContract.getAssetName().toByteArray();
@@ -74,14 +74,14 @@ public boolean execute(Object object) throws ContractExeException {
assetIssueCapsule = Commons
.getAssetIssueStoreFinal(dynamicStore, assetIssueStore, assetIssueV2Store).get(key);
- long exchangeAmount = Math.multiplyExact(cost, assetIssueCapsule.getNum());
- exchangeAmount = Math.floorDiv(exchangeAmount, assetIssueCapsule.getTrxNum());
+ long exchangeAmount = multiplyExact(cost, assetIssueCapsule.getNum());
+ exchangeAmount = floorDiv(exchangeAmount, assetIssueCapsule.getTrxNum());
ownerAccount.addAssetAmountV2(key, exchangeAmount, dynamicStore, assetIssueStore);
//add to to_address
byte[] toAddress = participateAssetIssueContract.getToAddress().toByteArray();
AccountCapsule toAccount = accountStore.get(toAddress);
- toAccount.setBalance(Math.addExact(toAccount.getBalance(), cost));
+ toAccount.setBalance(addExact(toAccount.getBalance(), cost));
if (!toAccount.reduceAssetAmountV2(key, exchangeAmount, dynamicStore, assetIssueStore)) {
throw new ContractExeException("reduceAssetAmount failed !");
}
@@ -156,7 +156,7 @@ public boolean validate() throws ContractValidateException {
try {
//Whether the balance is enough
long fee = calcFee();
- if (ownerAccount.getBalance() < Math.addExact(amount, fee)) {
+ if (ownerAccount.getBalance() < addExact(amount, fee)) {
throw new ContractValidateException("No enough balance !");
}
@@ -181,8 +181,8 @@ public boolean validate() throws ContractValidateException {
int trxNum = assetIssueCapsule.getTrxNum();
int num = assetIssueCapsule.getNum();
- long exchangeAmount = Math.multiplyExact(amount, num);
- exchangeAmount = Math.floorDiv(exchangeAmount, trxNum);
+ long exchangeAmount = multiplyExact(amount, num);
+ exchangeAmount = floorDiv(exchangeAmount, trxNum);
if (exchangeAmount <= 0) {
throw new ContractValidateException("Can not process the exchange!");
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java b/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java
index a3639cca07f..e0044c4958d 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ProposalCreateActuator.java
@@ -9,7 +9,6 @@
import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
-import org.tron.common.parameter.CommonParameter;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.ProposalCapsule;
@@ -53,7 +52,7 @@ public boolean execute(Object result) throws ContractExeException {
long currentMaintenanceTime =
chainBaseManager.getDynamicPropertiesStore().getNextMaintenanceTime();
- long now3 = now + CommonParameter.getInstance().getProposalExpireTime();
+ long now3 = now + chainBaseManager.getDynamicPropertiesStore().getProposalExpireTime();
long round = (now3 - currentMaintenanceTime) / maintenanceTimeInterval;
long expirationTime =
currentMaintenanceTime + (round + 1) * maintenanceTimeInterval;
diff --git a/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java b/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java
index 284650f1ffb..338e948f304 100644
--- a/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java
@@ -96,9 +96,9 @@ public boolean execute(Object result)
//adjust and verify total shielded pool value
try {
- Commons.adjustTotalShieldedPoolValue(
- Math.addExact(Math.subtractExact(shieldedTransferContract.getToAmount(),
- shieldedTransferContract.getFromAmount()), fee), dynamicStore);
+ Commons.adjustTotalShieldedPoolValue(addExact(subtractExact(
+ shieldedTransferContract.getToAmount(),
+ shieldedTransferContract.getFromAmount()), fee), dynamicStore);
} catch (ArithmeticException | BalanceInsufficientException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(0, code.FAILED);
@@ -170,28 +170,27 @@ private void executeShielded(List spends, List spendDescriptions,
long totalShieldedPoolValue = dynamicStore
.getTotalShieldedPoolValue();
try {
- valueBalance = Math.addExact(Math.subtractExact(shieldedTransferContract.getToAmount(),
+ valueBalance = addExact(subtractExact(
+ shieldedTransferContract.getToAmount(),
shieldedTransferContract.getFromAmount()), fee);
- totalShieldedPoolValue = Math.subtractExact(totalShieldedPoolValue, valueBalance);
+ totalShieldedPoolValue = subtractExact(
+ totalShieldedPoolValue, valueBalance);
} catch (ArithmeticException e) {
logger.debug(e.getMessage(), e);
throw new ZkProofValidateException(e.getMessage(), true);
@@ -452,7 +452,7 @@ private void validateTransparent(ShieldedTransferContract shieldedTransferContra
AccountCapsule toAccount = accountStore.get(toAddress);
if (toAccount != null) {
try {
- Math.addExact(getZenBalance(toAccount), toAmount);
+ addExact(getZenBalance(toAccount), toAmount);
} catch (ArithmeticException e) {
logger.debug(e.getMessage(), e);
throw new ContractValidateException(e.getMessage());
diff --git a/actuator/src/main/java/org/tron/core/actuator/TransferActuator.java b/actuator/src/main/java/org/tron/core/actuator/TransferActuator.java
index 5e3d605aed7..a0deabf8f00 100755
--- a/actuator/src/main/java/org/tron/core/actuator/TransferActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/TransferActuator.java
@@ -7,7 +7,6 @@
import java.util.Arrays;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
-import org.tron.common.utils.Commons;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
@@ -58,13 +57,13 @@ public boolean execute(Object object) throws ContractExeException {
fee = fee + dynamicStore.getCreateNewAccountFeeInSystemContract();
}
- Commons.adjustBalance(accountStore, ownerAddress, -(Math.addExact(fee, amount)));
+ adjustBalance(accountStore, ownerAddress, -(addExact(fee, amount)));
if (dynamicStore.supportBlackHoleOptimization()) {
dynamicStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
}
- Commons.adjustBalance(accountStore, toAddress, amount);
+ adjustBalance(accountStore, toAddress, amount);
ret.setStatus(fee, code.SUCESS);
} catch (BalanceInsufficientException | ArithmeticException | InvalidProtocolBufferException e) {
logger.debug(e.getMessage(), e);
@@ -156,7 +155,7 @@ public boolean validate() throws ContractValidateException {
}
}
- if (balance < Math.addExact(amount, fee)) {
+ if (balance < addExact(amount, fee)) {
logger.warn("Balance is not sufficient. Account: {}, balance: {}, amount: {}, fee: {}.",
StringUtil.encode58Check(ownerAddress), balance, amount, fee);
throw new ContractValidateException(
@@ -164,7 +163,7 @@ public boolean validate() throws ContractValidateException {
}
if (toAccount != null) {
- Math.addExact(toAccount.getBalance(), amount);
+ addExact(toAccount.getBalance(), amount);
}
} catch (ArithmeticException e) {
logger.debug(e.getMessage(), e);
diff --git a/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java b/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java
index de2b2faec86..d93263055eb 100644
--- a/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java
@@ -83,11 +83,11 @@ public boolean execute(Object result) throws ContractExeException {
.addAssetAmountV2(assetName.toByteArray(), amount, dynamicStore, assetIssueStore);
accountStore.put(toAddress, toAccountCapsule);
- Commons.adjustBalance(accountStore, ownerAccountCapsule, -fee);
+ adjustBalance(accountStore, ownerAccountCapsule, -fee);
if (dynamicStore.supportBlackHoleOptimization()) {
dynamicStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
}
ret.setStatus(fee, code.SUCESS);
} catch (BalanceInsufficientException e) {
@@ -177,7 +177,7 @@ public boolean validate() throws ContractValidateException {
assetBalance = toAccount.getAsset(dynamicStore, ByteArray.toStr(assetName));
if (assetBalance != null) {
try {
- assetBalance = Math.addExact(assetBalance, amount); //check if overflow
+ assetBalance = addExact(assetBalance, amount); //check if overflow
} catch (Exception e) {
logger.debug(e.getMessage(), e);
throw new ContractValidateException(e.getMessage());
diff --git a/actuator/src/main/java/org/tron/core/actuator/UnDelegateResourceActuator.java b/actuator/src/main/java/org/tron/core/actuator/UnDelegateResourceActuator.java
new file mode 100755
index 00000000000..2f2eed7fded
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/UnDelegateResourceActuator.java
@@ -0,0 +1,319 @@
+package org.tron.core.actuator;
+
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.Arrays;
+import java.util.Objects;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.DelegatedResourceCapsule;
+import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.db.BandwidthProcessor;
+import org.tron.core.db.EnergyProcessor;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.AccountStore;
+import org.tron.core.store.DelegatedResourceAccountIndexStore;
+import org.tron.core.store.DelegatedResourceStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.protos.Protocol.Transaction.Contract.ContractType;
+import org.tron.protos.Protocol.Transaction.Result.code;
+import org.tron.protos.contract.BalanceContract.UnDelegateResourceContract;
+
+@Slf4j(topic = "actuator")
+public class UnDelegateResourceActuator extends AbstractActuator {
+
+ public UnDelegateResourceActuator() {
+ super(ContractType.UnDelegateResourceContract, UnDelegateResourceContract.class);
+ }
+
+ @Override
+ public boolean execute(Object result) throws ContractExeException {
+ TransactionResultCapsule ret = (TransactionResultCapsule) result;
+ if (Objects.isNull(ret)) {
+ throw new RuntimeException(ActuatorConstant.TX_RESULT_NULL);
+ }
+
+ long fee = calcFee();
+ final UnDelegateResourceContract unDelegateResourceContract;
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ DelegatedResourceStore delegatedResourceStore = chainBaseManager.getDelegatedResourceStore();
+ DelegatedResourceAccountIndexStore delegatedResourceAccountIndexStore = chainBaseManager
+ .getDelegatedResourceAccountIndexStore();
+ try {
+ unDelegateResourceContract = any.unpack(UnDelegateResourceContract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ ret.setStatus(fee, code.FAILED);
+ throw new ContractExeException(e.getMessage());
+ }
+
+
+ final long unDelegateBalance = unDelegateResourceContract.getBalance();
+ byte[] ownerAddress = unDelegateResourceContract.getOwnerAddress().toByteArray();
+ byte[] receiverAddress = unDelegateResourceContract.getReceiverAddress().toByteArray();
+
+ AccountCapsule receiverCapsule = accountStore.get(receiverAddress);
+
+ long transferUsage = 0;
+ // modify receiver Account
+ if (receiverCapsule != null) {
+ long now = chainBaseManager.getHeadSlot();
+ switch (unDelegateResourceContract.getResource()) {
+ case BANDWIDTH:
+ BandwidthProcessor bandwidthProcessor = new BandwidthProcessor(chainBaseManager);
+ bandwidthProcessor.updateUsageForDelegated(receiverCapsule);
+
+ if (receiverCapsule.getAcquiredDelegatedFrozenV2BalanceForBandwidth()
+ < unDelegateBalance) {
+ // A TVM contract suicide, re-create will produce this situation
+ receiverCapsule.setAcquiredDelegatedFrozenV2BalanceForBandwidth(0);
+ } else {
+ // calculate usage
+ long unDelegateMaxUsage = (long) ((double) unDelegateBalance / TRX_PRECISION
+ * ((double) (dynamicStore.getTotalNetLimit()) / dynamicStore.getTotalNetWeight()));
+ transferUsage = (long) (receiverCapsule.getNetUsage()
+ * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForBandwidth()));
+ transferUsage = min(unDelegateMaxUsage, transferUsage);
+
+ receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForBandwidth(-unDelegateBalance);
+ }
+
+ long newNetUsage = receiverCapsule.getNetUsage() - transferUsage;
+ receiverCapsule.setNetUsage(newNetUsage);
+ receiverCapsule.setLatestConsumeTime(now);
+ break;
+ case ENERGY:
+ EnergyProcessor energyProcessor = new EnergyProcessor(dynamicStore, accountStore);
+ energyProcessor.updateUsage(receiverCapsule);
+
+ if (receiverCapsule.getAcquiredDelegatedFrozenV2BalanceForEnergy()
+ < unDelegateBalance) {
+ // A TVM contract receiver, re-create will produce this situation
+ receiverCapsule.setAcquiredDelegatedFrozenV2BalanceForEnergy(0);
+ } else {
+ // calculate usage
+ long unDelegateMaxUsage = (long) ((double) unDelegateBalance / TRX_PRECISION
+ * ((double) (dynamicStore.getTotalEnergyCurrentLimit()) / dynamicStore.getTotalEnergyWeight()));
+ transferUsage = (long) (receiverCapsule.getEnergyUsage()
+ * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForEnergy()));
+ transferUsage = min(unDelegateMaxUsage, transferUsage);
+
+ receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForEnergy(-unDelegateBalance);
+ }
+
+ long newEnergyUsage = receiverCapsule.getEnergyUsage() - transferUsage;
+ receiverCapsule.setEnergyUsage(newEnergyUsage);
+ receiverCapsule.setLatestConsumeTimeForEnergy(now);
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+ accountStore.put(receiverCapsule.createDbKey(), receiverCapsule);
+ }
+
+ // transfer lock delegate to unlock
+ delegatedResourceStore.unLockExpireResource(ownerAddress, receiverAddress,
+ dynamicStore.getLatestBlockHeaderTimestamp());
+
+ byte[] unlockKey = DelegatedResourceCapsule
+ .createDbKeyV2(ownerAddress, receiverAddress, false);
+ DelegatedResourceCapsule unlockResource = delegatedResourceStore
+ .get(unlockKey);
+
+ // modify owner Account
+ AccountCapsule ownerCapsule = accountStore.get(ownerAddress);
+ switch (unDelegateResourceContract.getResource()) {
+ case BANDWIDTH: {
+ unlockResource.addFrozenBalanceForBandwidth(-unDelegateBalance, 0);
+
+ ownerCapsule.addDelegatedFrozenV2BalanceForBandwidth(-unDelegateBalance);
+ ownerCapsule.addFrozenBalanceForBandwidthV2(unDelegateBalance);
+
+ BandwidthProcessor processor = new BandwidthProcessor(chainBaseManager);
+
+ long now = chainBaseManager.getHeadSlot();
+ if (Objects.nonNull(receiverCapsule) && transferUsage > 0) {
+ processor.unDelegateIncrease(ownerCapsule, receiverCapsule,
+ transferUsage, BANDWIDTH, now);
+ }
+ }
+ break;
+ case ENERGY: {
+ unlockResource.addFrozenBalanceForEnergy(-unDelegateBalance, 0);
+
+ ownerCapsule.addDelegatedFrozenV2BalanceForEnergy(-unDelegateBalance);
+ ownerCapsule.addFrozenBalanceForEnergyV2(unDelegateBalance);
+
+ EnergyProcessor processor = new EnergyProcessor(dynamicStore, accountStore);
+
+ long now = chainBaseManager.getHeadSlot();
+ if (Objects.nonNull(receiverCapsule) && transferUsage > 0) {
+ processor.unDelegateIncrease(ownerCapsule, receiverCapsule, transferUsage, ENERGY, now);
+ }
+ }
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+
+ if (unlockResource.getFrozenBalanceForBandwidth() == 0
+ && unlockResource.getFrozenBalanceForEnergy() == 0) {
+ delegatedResourceStore.delete(unlockKey);
+ unlockResource = null;
+ } else {
+ delegatedResourceStore.put(unlockKey, unlockResource);
+ }
+
+ byte[] lockKey = DelegatedResourceCapsule
+ .createDbKeyV2(ownerAddress, receiverAddress, true);
+ DelegatedResourceCapsule lockResource = delegatedResourceStore
+ .get(lockKey);
+ if (lockResource == null && unlockResource == null) {
+ //modify DelegatedResourceAccountIndexStore
+ delegatedResourceAccountIndexStore.unDelegateV2(ownerAddress, receiverAddress);
+ }
+
+ accountStore.put(ownerAddress, ownerCapsule);
+
+ ret.setStatus(fee, code.SUCESS);
+
+ return true;
+ }
+
+ @Override
+ public boolean validate() throws ContractValidateException {
+ if (this.any == null) {
+ throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
+ }
+ if (chainBaseManager == null) {
+ throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
+ }
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ DelegatedResourceStore delegatedResourceStore = chainBaseManager.getDelegatedResourceStore();
+ if (!dynamicStore.supportDR()) {
+ throw new ContractValidateException("No support for resource delegate");
+ }
+
+ if (!dynamicStore.supportUnfreezeDelay()) {
+ throw new ContractValidateException("Not support unDelegate resource transaction,"
+ + " need to be opened by the committee");
+ }
+
+ if (!this.any.is(UnDelegateResourceContract.class)) {
+ throw new ContractValidateException(
+ "contract type error, expected type [UnDelegateResourceContract], real type[" + any
+ .getClass() + "]");
+ }
+ final UnDelegateResourceContract unDelegateResourceContract;
+ try {
+ unDelegateResourceContract = this.any.unpack(UnDelegateResourceContract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+
+ byte[] ownerAddress = unDelegateResourceContract.getOwnerAddress().toByteArray();
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule ownerCapsule = accountStore.get(ownerAddress);
+ if (ownerCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
+ }
+
+ byte[] receiverAddress = unDelegateResourceContract.getReceiverAddress().toByteArray();
+ if (!DecodeUtil.addressValid(receiverAddress)) {
+ throw new ContractValidateException("Invalid receiverAddress");
+ }
+ if (Arrays.equals(receiverAddress, ownerAddress)) {
+ throw new ContractValidateException(
+ "receiverAddress must not be the same as ownerAddress");
+ }
+
+ // TVM contract suicide can result in no receiving account
+ // AccountCapsule receiverCapsule = accountStore.get(receiverAddress);
+ // if (receiverCapsule == null) {
+ // String readableReceiverAddress = StringUtil.createReadableString(receiverAddress);
+ // throw new ContractValidateException(
+ // "Receiver Account[" + readableReceiverAddress + "] does not exist");
+ // }
+
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ byte[] key = DelegatedResourceCapsule.createDbKeyV2(ownerAddress, receiverAddress, false);
+ DelegatedResourceCapsule unlockResourceCapsule = delegatedResourceStore.get(key);
+ byte[] lockKey = DelegatedResourceCapsule.createDbKeyV2(ownerAddress, receiverAddress, true);
+ DelegatedResourceCapsule lockResourceCapsule = delegatedResourceStore.get(lockKey);
+ if (unlockResourceCapsule == null && lockResourceCapsule == null) {
+ throw new ContractValidateException(
+ "delegated Resource does not exist");
+ }
+
+ long unDelegateBalance = unDelegateResourceContract.getBalance();
+ if (unDelegateBalance <= 0) {
+ throw new ContractValidateException("unDelegateBalance must be more than 0 TRX");
+ }
+ switch (unDelegateResourceContract.getResource()) {
+ case BANDWIDTH: {
+ long delegateBalance = 0;
+ if (unlockResourceCapsule != null) {
+ delegateBalance += unlockResourceCapsule.getFrozenBalanceForBandwidth();
+ }
+ if (lockResourceCapsule != null
+ && lockResourceCapsule.getExpireTimeForBandwidth() < now) {
+ delegateBalance += lockResourceCapsule.getFrozenBalanceForBandwidth();
+ }
+ if (delegateBalance < unDelegateBalance) {
+ throw new ContractValidateException(
+ "insufficient delegatedFrozenBalance(BANDWIDTH), request="
+ + unDelegateBalance + ", unlock_balance=" + delegateBalance);
+ }
+ }
+ break;
+ case ENERGY: {
+ long delegateBalance = 0;
+ if (unlockResourceCapsule != null) {
+ delegateBalance += unlockResourceCapsule.getFrozenBalanceForEnergy();
+ }
+ if (lockResourceCapsule != null
+ && lockResourceCapsule.getExpireTimeForEnergy() < now) {
+ delegateBalance += lockResourceCapsule.getFrozenBalanceForEnergy();
+ }
+ if (delegateBalance < unDelegateBalance) {
+ throw new ContractValidateException("insufficient delegateFrozenBalance(Energy), request="
+ + unDelegateBalance + ", unlock_balance=" + delegateBalance);
+ }
+ }
+ break;
+ default:
+ throw new ContractValidateException(
+ "ResourceCode error.valid ResourceCode[BANDWIDTH、Energy]");
+ }
+
+ return true;
+ }
+
+ @Override
+ public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
+ return any.unpack(UnDelegateResourceContract.class).getOwnerAddress();
+ }
+
+ @Override
+ public long calcFee() {
+ return 0;
+ }
+
+}
diff --git a/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceActuator.java b/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceActuator.java
index befcefd32d2..80334f1fc49 100755
--- a/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceActuator.java
@@ -38,6 +38,9 @@
@Slf4j(topic = "actuator")
public class UnfreezeBalanceActuator extends AbstractActuator {
+ private static final String INVALID_RESOURCE_CODE =
+ "ResourceCode error.valid ResourceCode[BANDWIDTH、Energy]";
+
public UnfreezeBalanceActuator() {
super(ContractType.UnfreezeBalanceContract, UnfreezeBalanceContract.class);
}
@@ -426,8 +429,7 @@ public boolean validate() throws ContractValidateException {
}
break;
default:
- throw new ContractValidateException(
- "ResourceCode error.valid ResourceCode[BANDWIDTH、Energy]");
+ throw new ContractValidateException(INVALID_RESOURCE_CODE);
}
} else {
@@ -464,8 +466,7 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException("It's not time to unfreeze(TronPower).");
}
} else {
- throw new ContractValidateException(
- "ResourceCode error.valid ResourceCode[BANDWIDTH、Energy]");
+ throw new ContractValidateException(INVALID_RESOURCE_CODE);
}
break;
default:
@@ -473,8 +474,7 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException(
"ResourceCode error.valid ResourceCode[BANDWIDTH、Energy、TRON_POWER]");
} else {
- throw new ContractValidateException(
- "ResourceCode error.valid ResourceCode[BANDWIDTH、Energy]");
+ throw new ContractValidateException(INVALID_RESOURCE_CODE);
}
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceV2Actuator.java b/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceV2Actuator.java
new file mode 100755
index 00000000000..fb41c97f7ed
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceV2Actuator.java
@@ -0,0 +1,389 @@
+package org.tron.core.actuator;
+
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.config.Parameter.ChainConstant.FROZEN_PERIOD;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+import static org.tron.protos.contract.Common.ResourceCode.TRON_POWER;
+
+import com.google.common.collect.Lists;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.capsule.VotesCapsule;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.service.MortgageService;
+import org.tron.core.store.AccountStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.store.VotesStore;
+import org.tron.protos.Protocol;
+import org.tron.protos.Protocol.Account.FreezeV2;
+import org.tron.protos.Protocol.Account.UnFreezeV2;
+import org.tron.protos.Protocol.Transaction.Contract.ContractType;
+import org.tron.protos.Protocol.Transaction.Result.code;
+import org.tron.protos.Protocol.Vote;
+import org.tron.protos.contract.BalanceContract.UnfreezeBalanceV2Contract;
+
+@Slf4j(topic = "actuator")
+public class UnfreezeBalanceV2Actuator extends AbstractActuator {
+
+ @Getter
+ private static final int UNFREEZE_MAX_TIMES = 32;
+
+ public UnfreezeBalanceV2Actuator() {
+ super(ContractType.UnfreezeBalanceV2Contract, UnfreezeBalanceV2Contract.class);
+ }
+
+ @Override
+ public boolean execute(Object result) throws ContractExeException {
+ TransactionResultCapsule ret = (TransactionResultCapsule) result;
+ if (Objects.isNull(ret)) {
+ throw new RuntimeException(ActuatorConstant.TX_RESULT_NULL);
+ }
+
+ long fee = calcFee();
+ final UnfreezeBalanceV2Contract unfreezeBalanceV2Contract;
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ MortgageService mortgageService = chainBaseManager.getMortgageService();
+ try {
+ unfreezeBalanceV2Contract = any.unpack(UnfreezeBalanceV2Contract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ ret.setStatus(fee, code.FAILED);
+ throw new ContractExeException(e.getMessage());
+ }
+ byte[] ownerAddress = unfreezeBalanceV2Contract.getOwnerAddress().toByteArray();
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+
+ mortgageService.withdrawReward(ownerAddress);
+
+ AccountCapsule accountCapsule = accountStore.get(ownerAddress);
+ long unfreezeAmount = this.unfreezeExpire(accountCapsule, now);
+ long unfreezeBalance = unfreezeBalanceV2Contract.getUnfreezeBalance();
+
+ if (dynamicStore.supportAllowNewResourceModel()
+ && accountCapsule.oldTronPowerIsNotInitialized()) {
+ accountCapsule.initializeOldTronPower();
+ }
+
+ ResourceCode freezeType = unfreezeBalanceV2Contract.getResource();
+
+ long expireTime = this.calcUnfreezeExpireTime(now);
+ accountCapsule.addUnfrozenV2List(freezeType, unfreezeBalance, expireTime);
+
+ this.updateTotalResourceWeight(accountCapsule, unfreezeBalanceV2Contract, unfreezeBalance);
+ this.updateVote(accountCapsule, unfreezeBalanceV2Contract, ownerAddress);
+
+ if (dynamicStore.supportAllowNewResourceModel()
+ && !accountCapsule.oldTronPowerIsInvalid()) {
+ accountCapsule.invalidateOldTronPower();
+ }
+
+ accountStore.put(ownerAddress, accountCapsule);
+
+ ret.setWithdrawExpireAmount(unfreezeAmount);
+ ret.setStatus(fee, code.SUCESS);
+ return true;
+ }
+
+ @Override
+ public boolean validate() throws ContractValidateException {
+ if (this.any == null) {
+ throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
+ }
+ if (chainBaseManager == null) {
+ throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
+ }
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ if (!this.any.is(UnfreezeBalanceV2Contract.class)) {
+ throw new ContractValidateException(
+ "contract type error, expected type [UnfreezeBalanceContract], real type[" + any
+ .getClass() + "]");
+ }
+
+ if (!dynamicStore.supportUnfreezeDelay()) {
+ throw new ContractValidateException("Not support UnfreezeV2 transaction,"
+ + " need to be opened by the committee");
+ }
+
+ final UnfreezeBalanceV2Contract unfreezeBalanceV2Contract;
+ try {
+ unfreezeBalanceV2Contract = this.any.unpack(UnfreezeBalanceV2Contract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+
+ byte[] ownerAddress = unfreezeBalanceV2Contract.getOwnerAddress().toByteArray();
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+
+ AccountCapsule accountCapsule = accountStore.get(ownerAddress);
+ if (accountCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
+ }
+
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ switch (unfreezeBalanceV2Contract.getResource()) {
+ case BANDWIDTH:
+ if (!checkExistFrozenBalance(accountCapsule, BANDWIDTH)) {
+ throw new ContractValidateException("no frozenBalance(BANDWIDTH)");
+ }
+ break;
+ case ENERGY:
+ if (!checkExistFrozenBalance(accountCapsule, ENERGY)) {
+ throw new ContractValidateException("no frozenBalance(Energy)");
+ }
+ break;
+ case TRON_POWER:
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ if (!checkExistFrozenBalance(accountCapsule, TRON_POWER)) {
+ throw new ContractValidateException("no frozenBalance(TronPower)");
+ }
+ } else {
+ throw new ContractValidateException("ResourceCode error.valid ResourceCode[BANDWIDTH、Energy]");
+ }
+ break;
+ default:
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ throw new ContractValidateException("ResourceCode error.valid ResourceCode[BANDWIDTH、Energy、TRON_POWER]");
+ } else {
+ throw new ContractValidateException("ResourceCode error.valid ResourceCode[BANDWIDTH、Energy]");
+ }
+ }
+
+ if (!checkUnfreezeBalance(accountCapsule, unfreezeBalanceV2Contract, unfreezeBalanceV2Contract.getResource())) {
+ throw new ContractValidateException(
+ "Invalid unfreeze_balance, [" + unfreezeBalanceV2Contract.getUnfreezeBalance() + "] is error"
+ );
+ }
+
+ int unfreezingCount = accountCapsule.getUnfreezingV2Count(now);
+ if (UNFREEZE_MAX_TIMES <= unfreezingCount) {
+ throw new ContractValidateException("Invalid unfreeze operation, unfreezing times is over limit");
+ }
+
+ return true;
+ }
+
+ @Override
+ public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
+ return any.unpack(UnfreezeBalanceV2Contract.class).getOwnerAddress();
+ }
+
+ @Override
+ public long calcFee() {
+ return 0;
+ }
+
+ public boolean checkExistFrozenBalance(AccountCapsule accountCapsule, ResourceCode freezeType) {
+ List frozenV2List = accountCapsule.getFrozenV2List();
+ for (FreezeV2 frozenV2 : frozenV2List) {
+ if (frozenV2.getType().equals(freezeType) && frozenV2.getAmount() > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean checkUnfreezeBalance(AccountCapsule accountCapsule,
+ final UnfreezeBalanceV2Contract unfreezeBalanceV2Contract,
+ ResourceCode freezeType) {
+ boolean checkOk = false;
+
+ long frozenAmount = 0L;
+ List freezeV2List = accountCapsule.getFrozenV2List();
+ for (FreezeV2 freezeV2 : freezeV2List) {
+ if (freezeV2.getType().equals(freezeType)) {
+ frozenAmount = freezeV2.getAmount();
+ break;
+ }
+ }
+
+ if (unfreezeBalanceV2Contract.getUnfreezeBalance() > 0
+ && unfreezeBalanceV2Contract.getUnfreezeBalance() <= frozenAmount) {
+ checkOk = true;
+ }
+
+ return checkOk;
+ }
+
+ public long calcUnfreezeExpireTime(long now) {
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ long unfreezeDelayDays = dynamicStore.getUnfreezeDelayDays();
+
+ return now + unfreezeDelayDays * FROZEN_PERIOD;
+ }
+
+ public void updateAccountFrozenInfo(ResourceCode freezeType, AccountCapsule accountCapsule, long unfreezeBalance) {
+ List freezeV2List = accountCapsule.getFrozenV2List();
+ for (int i = 0; i < freezeV2List.size(); i++) {
+ if (freezeV2List.get(i).getType().equals(freezeType)) {
+ FreezeV2 freezeV2 = FreezeV2.newBuilder()
+ .setAmount(freezeV2List.get(i).getAmount() - unfreezeBalance)
+ .setType(freezeV2List.get(i).getType())
+ .build();
+ accountCapsule.updateFrozenV2List(i, freezeV2);
+ break;
+ }
+ }
+ }
+
+ public long unfreezeExpire(AccountCapsule accountCapsule, long now) {
+ long unfreezeBalance = 0L;
+
+ List unFrozenV2List = Lists.newArrayList();
+ unFrozenV2List.addAll(accountCapsule.getUnfrozenV2List());
+ Iterator iterator = unFrozenV2List.iterator();
+
+ while (iterator.hasNext()) {
+ UnFreezeV2 next = iterator.next();
+ if (next.getUnfreezeExpireTime() <= now) {
+ unfreezeBalance += next.getUnfreezeAmount();
+ iterator.remove();
+ }
+ }
+
+ accountCapsule.setInstance(
+ accountCapsule.getInstance().toBuilder()
+ .setBalance(accountCapsule.getBalance() + unfreezeBalance)
+ .clearUnfrozenV2()
+ .addAllUnfrozenV2(unFrozenV2List).build()
+ );
+ return unfreezeBalance;
+ }
+
+ public void updateTotalResourceWeight(AccountCapsule accountCapsule,
+ final UnfreezeBalanceV2Contract unfreezeBalanceV2Contract,
+ long unfreezeBalance) {
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ switch (unfreezeBalanceV2Contract.getResource()) {
+ case BANDWIDTH:
+ long oldNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForBandwidthV2(-unfreezeBalance);
+ long newNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ dynamicStore.addTotalNetWeight(newNetWeight - oldNetWeight);
+ break;
+ case ENERGY:
+ long oldEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForEnergyV2(-unfreezeBalance);
+ long newEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ dynamicStore.addTotalEnergyWeight(newEnergyWeight - oldEnergyWeight);
+ break;
+ case TRON_POWER:
+ long oldTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ accountCapsule.addFrozenForTronPowerV2(-unfreezeBalance);
+ long newTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ dynamicStore.addTotalTronPowerWeight(newTPWeight - oldTPWeight);
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+ }
+
+ private void updateVote(AccountCapsule accountCapsule,
+ final UnfreezeBalanceV2Contract unfreezeBalanceV2Contract,
+ byte[] ownerAddress) {
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ VotesStore votesStore = chainBaseManager.getVotesStore();
+
+ if (accountCapsule.getVotesList().isEmpty()) {
+ return;
+ }
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ if (accountCapsule.oldTronPowerIsInvalid()) {
+ switch (unfreezeBalanceV2Contract.getResource()) {
+ case BANDWIDTH:
+ case ENERGY:
+ // there is no need to change votes
+ return;
+ default:
+ break;
+ }
+ } else {
+ // clear all votes at once when new resource model start
+ VotesCapsule votesCapsule;
+ if (!votesStore.has(ownerAddress)) {
+ votesCapsule = new VotesCapsule(
+ unfreezeBalanceV2Contract.getOwnerAddress(),
+ accountCapsule.getVotesList()
+ );
+ } else {
+ votesCapsule = votesStore.get(ownerAddress);
+ }
+ accountCapsule.clearVotes();
+ votesCapsule.clearNewVotes();
+ votesStore.put(ownerAddress, votesCapsule);
+ return;
+ }
+ }
+
+ long totalVote = 0;
+ for (Protocol.Vote vote : accountCapsule.getVotesList()) {
+ totalVote += vote.getVoteCount();
+ }
+ long ownedTronPower;
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ ownedTronPower = accountCapsule.getAllTronPower();
+ } else {
+ ownedTronPower = accountCapsule.getTronPower();
+ }
+
+ // tron power is enough to total votes
+ if (ownedTronPower >= totalVote * TRX_PRECISION) {
+ return;
+ }
+ if (totalVote == 0) {
+ return;
+ }
+
+ VotesCapsule votesCapsule;
+ if (!votesStore.has(ownerAddress)) {
+ votesCapsule = new VotesCapsule(
+ unfreezeBalanceV2Contract.getOwnerAddress(),
+ accountCapsule.getVotesList()
+ );
+ } else {
+ votesCapsule = votesStore.get(ownerAddress);
+ }
+
+ // Update Owner Voting
+ List addVotes = new ArrayList<>();
+ for (Vote vote : accountCapsule.getVotesList()) {
+ long newVoteCount = (long)
+ ((double) vote.getVoteCount() / totalVote * ownedTronPower / TRX_PRECISION);
+ if (newVoteCount > 0) {
+ Vote newVote = Vote.newBuilder()
+ .setVoteAddress(vote.getVoteAddress())
+ .setVoteCount(newVoteCount)
+ .build();
+ addVotes.add(newVote);
+ }
+ }
+ votesCapsule.clearNewVotes();
+ votesCapsule.addAllNewVotes(addVotes);
+ votesStore.put(ownerAddress, votesCapsule);
+
+ accountCapsule.clearVotes();
+ accountCapsule.addAllVotes(addVotes);
+ }
+}
\ No newline at end of file
diff --git a/actuator/src/main/java/org/tron/core/actuator/UpdateAssetActuator.java b/actuator/src/main/java/org/tron/core/actuator/UpdateAssetActuator.java
index 193d352a72d..36ec42cb8ba 100644
--- a/actuator/src/main/java/org/tron/core/actuator/UpdateAssetActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/UpdateAssetActuator.java
@@ -17,7 +17,6 @@
import org.tron.core.utils.TransactionUtil;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
-import org.tron.protos.contract.AccountContract.AccountUpdateContract;
import org.tron.protos.contract.AssetIssueContractOuterClass.UpdateAssetContract;
@Slf4j(topic = "actuator")
@@ -171,7 +170,7 @@ public boolean validate() throws ContractValidateException {
@Override
public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
- return any.unpack(AccountUpdateContract.class).getOwnerAddress();
+ return any.unpack(UpdateAssetContract.class).getOwnerAddress();
}
@Override
diff --git a/actuator/src/main/java/org/tron/core/actuator/VMActuator.java b/actuator/src/main/java/org/tron/core/actuator/VMActuator.java
index 84b31a9297e..1b0e8a6637f 100644
--- a/actuator/src/main/java/org/tron/core/actuator/VMActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/VMActuator.java
@@ -1,9 +1,12 @@
package org.tron.core.actuator;
-import static java.lang.Math.max;
-import static java.lang.Math.min;
import static org.apache.commons.lang3.ArrayUtils.getLength;
import static org.apache.commons.lang3.ArrayUtils.isNotEmpty;
+import static org.tron.common.math.Maths.addExact;
+import static org.tron.common.math.Maths.floorDiv;
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.min;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
import com.google.protobuf.ByteString;
import java.math.BigInteger;
@@ -25,16 +28,19 @@
import org.tron.common.utils.StorageUtils;
import org.tron.common.utils.StringUtil;
import org.tron.common.utils.WalletUtil;
+import org.tron.core.ChainBaseManager;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.BlockCapsule;
import org.tron.core.capsule.ContractCapsule;
import org.tron.core.capsule.ReceiptCapsule;
+import org.tron.core.db.EnergyProcessor;
import org.tron.core.db.TransactionContext;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.utils.TransactionUtil;
import org.tron.core.vm.EnergyCost;
import org.tron.core.vm.LogInfoTriggerParser;
+import org.tron.core.vm.Op;
import org.tron.core.vm.OperationRegistry;
import org.tron.core.vm.VM;
import org.tron.core.vm.VMConstant;
@@ -84,6 +90,8 @@ public class VMActuator implements Actuator2 {
@Setter
private boolean isConstantCall;
+ private long maxEnergyLimit;
+
@Setter
private boolean enableEventListener;
@@ -91,6 +99,7 @@ public class VMActuator implements Actuator2 {
public VMActuator(boolean isConstantCall) {
this.isConstantCall = isConstantCall;
+ this.maxEnergyLimit = CommonParameter.getInstance().maxEnergyLimitForConstant;
}
private static long getEnergyFee(long callerEnergyUsage, long callerEnergyFrozen,
@@ -115,8 +124,15 @@ public void validate(Object object) throws ContractValidateException {
// Warm up registry class
OperationRegistry.init();
trx = context.getTrxCap().getInstance();
+ // If tx`s fee limit is set, use it to calc max energy limit for constant call
+ if (isConstantCall && trx.getRawData().getFeeLimit() > 0) {
+ maxEnergyLimit = min(maxEnergyLimit, trx.getRawData().getFeeLimit()
+ / context.getStoreFactory().getChainBaseManager()
+ .getDynamicPropertiesStore().getEnergyFee(), VMConfig.disableJavaLangMath());
+ }
blockCap = context.getBlockCap();
- if (VMConfig.allowTvmFreeze() && context.getTrxCap().getTrxTrace() != null) {
+ if ((VMConfig.allowTvmFreeze() || VMConfig.allowTvmFreezeV2())
+ && context.getTrxCap().getTrxTrace() != null) {
receipt = context.getTrxCap().getTrxTrace().getReceipt();
}
//Route Type
@@ -176,6 +192,13 @@ public void execute(Object object) throws ContractExeException {
VM.play(program, OperationRegistry.getTable());
result = program.getResult();
+ if (VMConfig.allowEnergyAdjustment()) {
+ // If the last op consumed too much execution time, the CPU time limit for the whole tx can be exceeded.
+ // This is not fair for other txs in the same block.
+ // So when allowFairEnergyAdjustment is on, the CPU time limit will be checked at the end of tx execution.
+ program.checkCPUTimeLimit(Op.getNameOf(program.getLastOp()) + "(TX_LAST_OP)");
+ }
+
if (TrxType.TRX_CONTRACT_CREATION_TYPE == trxType && !result.isRevert()) {
byte[] code = program.getResult().getHReturn();
if (code.length != 0 && VMConfig.allowTvmLondon() && code[0] == (byte) 0xEF) {
@@ -352,7 +375,7 @@ private void create()
// according to version
if (isConstantCall) {
- energyLimit = CommonParameter.getInstance().maxEnergyLimitForConstant;
+ energyLimit = maxEnergyLimit;
} else {
if (StorageUtils.getEnergyLimitHardFork()) {
if (callValue < 0) {
@@ -375,9 +398,9 @@ private void create()
byte[] ops = newSmartContract.getBytecode().toByteArray();
rootInternalTx = new InternalTransaction(trx, trxType);
- long maxCpuTimeOfOneTx = rootRepository.getDynamicPropertiesStore()
- .getMaxCpuTimeOfOneTx() * VMConstant.ONE_THOUSAND;
- long thisTxCPULimitInUs = (long) (maxCpuTimeOfOneTx * getCpuLimitInUsRatio());
+ long thisTxCPULimitInUs = calculateCpuLimitInUs(isConstantCall,
+ rootRepository.getDynamicPropertiesStore().getMaxCpuTimeOfOneTx(),
+ getCpuLimitInUsRatio(), CommonParameter.getInstance().getConstantCallTimeoutMs());
long vmStartInUs = System.nanoTime() / VMConstant.ONE_THOUSAND;
long vmShouldEndInUs = vmStartInUs + thisTxCPULimitInUs;
ProgramInvoke programInvoke = ProgramInvokeFactory
@@ -482,17 +505,16 @@ private void call()
AccountCapsule caller = rootRepository.getAccount(callerAddress);
long energyLimit;
if (isConstantCall) {
- energyLimit = CommonParameter.getInstance().maxEnergyLimitForConstant;
+ energyLimit = maxEnergyLimit;
} else {
AccountCapsule creator = rootRepository
.getAccount(deployedContract.getInstance().getOriginAddress().toByteArray());
energyLimit = getTotalEnergyLimit(creator, caller, contract, feeLimit, callValue);
}
- long maxCpuTimeOfOneTx = rootRepository.getDynamicPropertiesStore()
- .getMaxCpuTimeOfOneTx() * VMConstant.ONE_THOUSAND;
- long thisTxCPULimitInUs =
- (long) (maxCpuTimeOfOneTx * getCpuLimitInUsRatio());
+ long thisTxCPULimitInUs = calculateCpuLimitInUs(isConstantCall,
+ rootRepository.getDynamicPropertiesStore().getMaxCpuTimeOfOneTx(),
+ getCpuLimitInUsRatio(), CommonParameter.getInstance().getConstantCallTimeoutMs());
long vmStartInUs = System.nanoTime() / VMConstant.ONE_THOUSAND;
long vmShouldEndInUs = vmStartInUs + thisTxCPULimitInUs;
ProgramInvoke programInvoke = ProgramInvokeFactory
@@ -538,15 +560,36 @@ public long getAccountEnergyLimitWithFixRatio(AccountCapsule account, long feeLi
}
long leftFrozenEnergy = rootRepository.getAccountLeftEnergyFromFreeze(account);
- if (VMConfig.allowTvmFreeze()) {
+ if (VMConfig.allowTvmFreeze() || VMConfig.allowTvmFreezeV2()) {
receipt.setCallerEnergyLeft(leftFrozenEnergy);
}
- long energyFromBalance = max(account.getBalance() - callValue, 0) / sunPerEnergy;
- long availableEnergy = Math.addExact(leftFrozenEnergy, energyFromBalance);
+ long energyFromBalance = max(account.getBalance() - callValue, 0,
+ VMConfig.disableJavaLangMath()) / sunPerEnergy;
+ long availableEnergy = addExact(leftFrozenEnergy, energyFromBalance,
+ VMConfig.disableJavaLangMath());
long energyFromFeeLimit = feeLimit / sunPerEnergy;
- return min(availableEnergy, energyFromFeeLimit);
+ if (VMConfig.allowTvmFreezeV2()) {
+ long now = rootRepository.getHeadSlot();
+ EnergyProcessor energyProcessor =
+ new EnergyProcessor(
+ rootRepository.getDynamicPropertiesStore(),
+ ChainBaseManager.getInstance().getAccountStore());
+ energyProcessor.updateUsage(account);
+ account.setLatestConsumeTimeForEnergy(now);
+ receipt.setCallerEnergyUsage(account.getEnergyUsage());
+ receipt.setCallerEnergyWindowSize(account.getWindowSize(ENERGY));
+ receipt.setCallerEnergyWindowSizeV2(account.getWindowSizeV2(ENERGY));
+ account.setEnergyUsage(
+ energyProcessor.increase(account, ENERGY,
+ account.getEnergyUsage(), min(leftFrozenEnergy, energyFromFeeLimit,
+ VMConfig.disableJavaLangMath()), now, now));
+ receipt.setCallerEnergyMergedUsage(account.getEnergyUsage());
+ receipt.setCallerEnergyMergedWindowSize(account.getWindowSize(ENERGY));
+ rootRepository.updateAccount(account.createDbKey(), account);
+ }
+ return min(availableEnergy, energyFromFeeLimit, VMConfig.disableJavaLangMath());
}
@@ -559,9 +602,10 @@ private long getAccountEnergyLimitWithFloatRatio(AccountCapsule account, long fe
}
// can change the calc way
long leftEnergyFromFreeze = rootRepository.getAccountLeftEnergyFromFreeze(account);
- callValue = max(callValue, 0);
- long energyFromBalance = Math
- .floorDiv(max(account.getBalance() - callValue, 0), sunPerEnergy);
+ callValue = max(callValue, 0, VMConfig.disableJavaLangMath());
+ long energyFromBalance = floorDiv(max(
+ account.getBalance() - callValue, 0, VMConfig.disableJavaLangMath()), sunPerEnergy,
+ VMConfig.disableJavaLangMath());
long energyFromFeeLimit;
long totalBalanceForEnergyFreeze = account.getAllFrozenBalanceForEnergy();
@@ -580,13 +624,14 @@ private long getAccountEnergyLimitWithFloatRatio(AccountCapsule account, long fe
.multiply(BigInteger.valueOf(feeLimit))
.divide(BigInteger.valueOf(totalBalanceForEnergyFreeze)).longValueExact();
} else {
- energyFromFeeLimit = Math
- .addExact(leftEnergyFromFreeze,
- (feeLimit - leftBalanceForEnergyFreeze) / sunPerEnergy);
+ energyFromFeeLimit = addExact(
+ leftEnergyFromFreeze, (feeLimit - leftBalanceForEnergyFreeze) / sunPerEnergy,
+ VMConfig.disableJavaLangMath());
}
}
- return min(Math.addExact(leftEnergyFromFreeze, energyFromBalance), energyFromFeeLimit);
+ return min(addExact(leftEnergyFromFreeze, energyFromBalance,
+ VMConfig.disableJavaLangMath()), energyFromFeeLimit, VMConfig.disableJavaLangMath());
}
public long getTotalEnergyLimit(AccountCapsule creator, AccountCapsule caller,
@@ -646,6 +691,14 @@ private double getCpuLimitInUsRatio() {
return cpuLimitRatio;
}
+ static long calculateCpuLimitInUs(boolean isConstantCall, long maxCpuTimeOfOneTxMs,
+ double cpuLimitInUsRatio, long constantCallTimeoutMs) {
+ if (isConstantCall && constantCallTimeoutMs > 0L) {
+ return constantCallTimeoutMs * VMConstant.ONE_THOUSAND;
+ }
+ return (long) (maxCpuTimeOfOneTxMs * VMConstant.ONE_THOUSAND * cpuLimitInUsRatio);
+ }
+
public long getTotalEnergyLimitWithFixRatio(AccountCapsule creator, AccountCapsule caller,
TriggerSmartContract contract, long feeLimit, long callValue)
throws ContractValidateException {
@@ -661,7 +714,8 @@ public long getTotalEnergyLimitWithFixRatio(AccountCapsule creator, AccountCapsu
long creatorEnergyLimit = 0;
ContractCapsule contractCapsule = rootRepository
.getContract(contract.getContractAddress().toByteArray());
- long consumeUserResourcePercent = contractCapsule.getConsumeUserResourcePercent();
+ long consumeUserResourcePercent = contractCapsule.getConsumeUserResourcePercent(
+ VMConfig.disableJavaLangMath());
long originEnergyLimit = contractCapsule.getOriginEnergyLimit();
if (originEnergyLimit < 0) {
@@ -671,12 +725,13 @@ public long getTotalEnergyLimitWithFixRatio(AccountCapsule creator, AccountCapsu
long originEnergyLeft = 0;
if (consumeUserResourcePercent < VMConstant.ONE_HUNDRED) {
originEnergyLeft = rootRepository.getAccountLeftEnergyFromFreeze(creator);
- if (VMConfig.allowTvmFreeze()) {
+ if (VMConfig.allowTvmFreeze() || VMConfig.allowTvmFreezeV2()) {
receipt.setOriginEnergyLeft(originEnergyLeft);
}
}
if (consumeUserResourcePercent <= 0) {
- creatorEnergyLimit = min(originEnergyLeft, originEnergyLimit);
+ creatorEnergyLimit = min(originEnergyLeft, originEnergyLimit,
+ VMConfig.disableJavaLangMath());
} else {
if (consumeUserResourcePercent < VMConstant.ONE_HUNDRED) {
// creatorEnergyLimit =
@@ -687,11 +742,30 @@ public long getTotalEnergyLimitWithFixRatio(AccountCapsule creator, AccountCapsu
BigInteger.valueOf(callerEnergyLimit)
.multiply(BigInteger.valueOf(VMConstant.ONE_HUNDRED - consumeUserResourcePercent))
.divide(BigInteger.valueOf(consumeUserResourcePercent)).longValueExact(),
- min(originEnergyLeft, originEnergyLimit)
- );
+ min(originEnergyLeft, originEnergyLimit, VMConfig.disableJavaLangMath()),
+ VMConfig.disableJavaLangMath());
}
}
- return Math.addExact(callerEnergyLimit, creatorEnergyLimit);
+ if (VMConfig.allowTvmFreezeV2()) {
+ long now = rootRepository.getHeadSlot();
+ EnergyProcessor energyProcessor =
+ new EnergyProcessor(
+ rootRepository.getDynamicPropertiesStore(),
+ ChainBaseManager.getInstance().getAccountStore());
+ energyProcessor.updateUsage(creator);
+ creator.setLatestConsumeTimeForEnergy(now);
+ receipt.setOriginEnergyUsage(creator.getEnergyUsage());
+ receipt.setOriginEnergyWindowSize(creator.getWindowSize(ENERGY));
+ receipt.setOriginEnergyWindowSizeV2(creator.getWindowSizeV2(ENERGY));
+ creator.setEnergyUsage(
+ energyProcessor.increase(creator, ENERGY,
+ creator.getEnergyUsage(), creatorEnergyLimit, now, now));
+ receipt.setOriginEnergyMergedUsage(creator.getEnergyUsage());
+ receipt.setOriginEnergyMergedWindowSize(creator.getWindowSize(ENERGY));
+ rootRepository.updateAccount(creator.createDbKey(), creator);
+ }
+ return addExact(callerEnergyLimit, creatorEnergyLimit,
+ VMConfig.disableJavaLangMath());
}
private long getTotalEnergyLimitWithFloatRatio(AccountCapsule creator, AccountCapsule caller,
@@ -707,13 +781,17 @@ private long getTotalEnergyLimitWithFloatRatio(AccountCapsule creator, AccountCa
ContractCapsule contractCapsule = rootRepository
.getContract(contract.getContractAddress().toByteArray());
- long consumeUserResourcePercent = contractCapsule.getConsumeUserResourcePercent();
+ long consumeUserResourcePercent = contractCapsule.getConsumeUserResourcePercent(
+ VMConfig.disableJavaLangMath());
if (creatorEnergyLimit * consumeUserResourcePercent
> (VMConstant.ONE_HUNDRED - consumeUserResourcePercent) * callerEnergyLimit) {
- return Math.floorDiv(callerEnergyLimit * VMConstant.ONE_HUNDRED, consumeUserResourcePercent);
+ return floorDiv(
+ callerEnergyLimit * VMConstant.ONE_HUNDRED, consumeUserResourcePercent,
+ VMConfig.disableJavaLangMath());
} else {
- return Math.addExact(callerEnergyLimit, creatorEnergyLimit);
+ return addExact(callerEnergyLimit, creatorEnergyLimit,
+ VMConfig.disableJavaLangMath());
}
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/WithdrawExpireUnfreezeActuator.java b/actuator/src/main/java/org/tron/core/actuator/WithdrawExpireUnfreezeActuator.java
new file mode 100755
index 00000000000..fd71ebf1404
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/WithdrawExpireUnfreezeActuator.java
@@ -0,0 +1,148 @@
+package org.tron.core.actuator;
+
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+
+import com.google.common.math.LongMath;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.AccountStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.protos.Protocol.Account.UnFreezeV2;
+import org.tron.protos.Protocol.Transaction.Contract.ContractType;
+import org.tron.protos.Protocol.Transaction.Result.code;
+import org.tron.protos.contract.BalanceContract.WithdrawExpireUnfreezeContract;
+
+
+@Slf4j(topic = "actuator")
+public class WithdrawExpireUnfreezeActuator extends AbstractActuator {
+
+ public WithdrawExpireUnfreezeActuator() {
+ super(ContractType.WithdrawExpireUnfreezeContract, WithdrawExpireUnfreezeContract.class);
+ }
+
+ @Override
+ public boolean execute(Object result) throws ContractExeException {
+ TransactionResultCapsule ret = (TransactionResultCapsule) result;
+ if (Objects.isNull(ret)) {
+ throw new RuntimeException(ActuatorConstant.TX_RESULT_NULL);
+ }
+ long fee = calcFee();
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ final WithdrawExpireUnfreezeContract withdrawExpireUnfreezeContract;
+ try {
+ withdrawExpireUnfreezeContract = any.unpack(WithdrawExpireUnfreezeContract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ ret.setStatus(fee, code.FAILED);
+ throw new ContractExeException(e.getMessage());
+ }
+ AccountCapsule accountCapsule = accountStore.get(
+ withdrawExpireUnfreezeContract.getOwnerAddress().toByteArray());
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ List unfrozenV2List = accountCapsule.getInstance().getUnfrozenV2List();
+ long totalWithdrawUnfreeze = getTotalWithdrawUnfreeze(unfrozenV2List, now);
+ accountCapsule.setInstance(accountCapsule.getInstance().toBuilder()
+ .setBalance(accountCapsule.getBalance() + totalWithdrawUnfreeze)
+ .build());
+ List newUnFreezeList = getRemainWithdrawList(unfrozenV2List, now);
+ accountCapsule.clearUnfrozenV2();
+ accountCapsule.addAllUnfrozenV2(newUnFreezeList);
+ accountStore.put(accountCapsule.createDbKey(), accountCapsule);
+ ret.setWithdrawExpireAmount(totalWithdrawUnfreeze);
+ ret.setStatus(fee, code.SUCESS);
+ return true;
+ }
+
+ @Override
+ public boolean validate() throws ContractValidateException {
+ if (Objects.isNull(this.any)) {
+ throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
+ }
+ if (Objects.isNull(chainBaseManager)) {
+ throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
+ }
+ AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ if (!this.any.is(WithdrawExpireUnfreezeContract.class)) {
+ throw new ContractValidateException(
+ "contract type error, expected type [WithdrawExpireUnfreezeContract], real type[" + any
+ .getClass() + "]");
+ }
+
+ if (!dynamicStore.supportUnfreezeDelay()) {
+ throw new ContractValidateException("Not support WithdrawExpireUnfreeze transaction,"
+ + " need to be opened by the committee");
+ }
+
+ final WithdrawExpireUnfreezeContract withdrawExpireUnfreezeContract;
+ try {
+ withdrawExpireUnfreezeContract = this.any.unpack(WithdrawExpireUnfreezeContract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+ byte[] ownerAddress = withdrawExpireUnfreezeContract.getOwnerAddress().toByteArray();
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule accountCapsule = accountStore.get(ownerAddress);
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ if (Objects.isNull(accountCapsule)) {
+ throw new ContractValidateException(ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + NOT_EXIST_STR);
+ }
+
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ List unfrozenV2List = accountCapsule.getInstance().getUnfrozenV2List();
+ long totalWithdrawUnfreeze = getTotalWithdrawUnfreeze(unfrozenV2List, now);
+ if (totalWithdrawUnfreeze <= 0) {
+ throw new ContractValidateException("no unFreeze balance to withdraw ");
+ }
+ try {
+ LongMath.checkedAdd(accountCapsule.getBalance(), totalWithdrawUnfreeze);
+ } catch (ArithmeticException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+ return true;
+ }
+
+ private long getTotalWithdrawUnfreeze(List unfrozenV2List, long now) {
+ return getTotalWithdrawList(unfrozenV2List, now).stream()
+ .mapToLong(UnFreezeV2::getUnfreezeAmount).sum();
+ }
+
+ private List getTotalWithdrawList(List unfrozenV2List, long now) {
+ return unfrozenV2List.stream().filter(unfrozenV2 -> unfrozenV2.getUnfreezeExpireTime() <= now)
+ .collect(Collectors.toList());
+ }
+
+ private List getRemainWithdrawList(List unfrozenV2List, long now) {
+ return unfrozenV2List.stream()
+ .filter(unfrozenV2 -> unfrozenV2.getUnfreezeExpireTime() > now)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
+ return any.unpack(WithdrawExpireUnfreezeContract.class).getOwnerAddress();
+ }
+
+ @Override
+ public long calcFee() {
+ return 0;
+ }
+
+}
diff --git a/actuator/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java b/actuator/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java
index ee223a32ffa..fc908a1713f 100755
--- a/actuator/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java
@@ -6,7 +6,6 @@
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
-import org.tron.common.utils.Commons;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
@@ -140,12 +139,11 @@ private void createWitness(final WitnessCreateContract witnessCreateContract)
}
accountStore.put(accountCapsule.createDbKey(), accountCapsule);
long cost = dynamicStore.getAccountUpgradeCost();
- Commons
- .adjustBalance(accountStore, witnessCreateContract.getOwnerAddress().toByteArray(), -cost);
+ adjustBalance(accountStore, witnessCreateContract.getOwnerAddress().toByteArray(), -cost);
if (dynamicStore.supportBlackHoleOptimization()) {
dynamicStore.burnTrx(cost);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole(), +cost);
+ adjustBalance(accountStore, accountStore.getBlackhole(), +cost);
}
dynamicStore.addTotalCreateWitnessCost(cost);
}
diff --git a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java
index 9a7491ebe0b..74d332c5611 100644
--- a/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java
+++ b/actuator/src/main/java/org/tron/core/utils/ProposalUtil.java
@@ -1,5 +1,13 @@
package org.tron.core.utils;
+import static org.tron.core.Constant.CREATE_ACCOUNT_TRANSACTION_MAX_BYTE_SIZE;
+import static org.tron.core.Constant.CREATE_ACCOUNT_TRANSACTION_MIN_BYTE_SIZE;
+import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE;
+import static org.tron.core.Constant.DYNAMIC_ENERGY_MAX_FACTOR_RANGE;
+import static org.tron.core.Constant.MAX_PROPOSAL_EXPIRE_TIME;
+import static org.tron.core.Constant.MIN_PROPOSAL_EXPIRE_TIME;
+import static org.tron.core.config.Parameter.ChainConstant.ONE_YEAR_BLOCK_NUMBERS;
+
import org.tron.common.utils.ForkController;
import org.tron.core.config.Parameter.ForkBlockVersionConsts;
import org.tron.core.config.Parameter.ForkBlockVersionEnum;
@@ -348,7 +356,8 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore,
break;
}
case ALLOW_MARKET_TRANSACTION: {
- if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1)) {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1)
+ || forkController.pass(ForkBlockVersionEnum.VERSION_4_8_1)) {
throw new ContractValidateException(
"Bad chain parameter id [ALLOW_MARKET_TRANSACTION]");
}
@@ -597,6 +606,341 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore,
}
break;
}
+ case UNFREEZE_DELAY_DAYS: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [UNFREEZE_DELAY_DAYS]");
+ }
+ if (value < 1 || value > 365) {
+ throw new ContractValidateException(
+ "This value[UNFREEZE_DELAY_DAYS] is only allowed to be in the range 1-365");
+ }
+ break;
+ }
+ case ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID] is only allowed to be 1");
+ }
+ break;
+ }
+
+ case ALLOW_DYNAMIC_ENERGY: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_DYNAMIC_ENERGY]");
+ }
+ if (value < 0 || value > 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_DYNAMIC_ENERGY] is only allowed to be in the range 0-1"
+ );
+ }
+ if (value == 1 && dynamicPropertiesStore.getChangeDelegation() == 0) {
+ throw new ContractValidateException(
+ "[ALLOW_CHANGE_DELEGATION] proposal must be approved "
+ + "before [ALLOW_DYNAMIC_ENERGY] can be opened");
+ }
+ break;
+ }
+ case DYNAMIC_ENERGY_THRESHOLD: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [DYNAMIC_ENERGY_THRESHOLD]");
+ }
+
+ if (value < 0 || value > LONG_VALUE) {
+ throw new ContractValidateException(LONG_VALUE_ERROR);
+ }
+ break;
+ }
+ case DYNAMIC_ENERGY_INCREASE_FACTOR: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [DYNAMIC_ENERGY_INCREASE_FACTOR]");
+ }
+
+ if (value < 0 || value > DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE) {
+ throw new ContractValidateException(
+ "This value[DYNAMIC_ENERGY_INCREASE_FACTOR] "
+ + "is only allowed to be in the range 0-"
+ + DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE
+ );
+ }
+ break;
+ }
+ case DYNAMIC_ENERGY_MAX_FACTOR: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [DYNAMIC_ENERGY_MAX_FACTOR]");
+ }
+
+ if (value < 0 || value > DYNAMIC_ENERGY_MAX_FACTOR_RANGE) {
+ throw new ContractValidateException(
+ "This value[DYNAMIC_ENERGY_MAX_FACTOR] "
+ + "is only allowed to be in the range 0-"
+ + DYNAMIC_ENERGY_MAX_FACTOR_RANGE
+ );
+ }
+ break;
+ }
+ case ALLOW_TVM_SHANGHAI: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_SHANGHAI]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_SHANGHAI] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_CANCEL_ALL_UNFREEZE_V2: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_CANCEL_ALL_UNFREEZE_V2]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_CANCEL_ALL_UNFREEZE_V2] is only allowed to be 1");
+ }
+ if (dynamicPropertiesStore.getUnfreezeDelayDays() == 0) {
+ throw new ContractValidateException(
+ "[UNFREEZE_DELAY_DAYS] proposal must be approved "
+ + "before [ALLOW_CANCEL_ALL_UNFREEZE_V2] can be proposed");
+ }
+ break;
+ }
+ case MAX_DELEGATE_LOCK_PERIOD: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [MAX_DELEGATE_LOCK_PERIOD]");
+ }
+ long maxDelegateLockPeriod = dynamicPropertiesStore.getMaxDelegateLockPeriod();
+ if (value <= maxDelegateLockPeriod || value > ONE_YEAR_BLOCK_NUMBERS) {
+ throw new ContractValidateException(
+ "This value[MAX_DELEGATE_LOCK_PERIOD] is only allowed to be greater than "
+ + maxDelegateLockPeriod + " and less than or equal to " + ONE_YEAR_BLOCK_NUMBERS
+ + " !");
+ }
+ if (dynamicPropertiesStore.getUnfreezeDelayDays() == 0) {
+ throw new ContractValidateException(
+ "[UNFREEZE_DELAY_DAYS] proposal must be approved "
+ + "before [MAX_DELEGATE_LOCK_PERIOD] can be proposed");
+ }
+ break;
+ }
+ case ALLOW_OLD_REWARD_OPT: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7_4)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_OLD_REWARD_OPT]");
+ }
+ if (dynamicPropertiesStore.allowOldRewardOpt()) {
+ throw new ContractValidateException(
+ "[ALLOW_OLD_REWARD_OPT] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_OLD_REWARD_OPT] is only allowed to be 1");
+ }
+ if (!dynamicPropertiesStore.useNewRewardAlgorithm()) {
+ throw new ContractValidateException(
+ "[ALLOW_NEW_REWARD] or [ALLOW_TVM_VOTE] proposal must be approved "
+ + "before [ALLOW_OLD_REWARD_OPT] can be proposed");
+ }
+ break;
+ }
+ case ALLOW_ENERGY_ADJUSTMENT: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7_5)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_ENERGY_ADJUSTMENT]");
+ }
+ if (dynamicPropertiesStore.getAllowEnergyAdjustment() == 1) {
+ throw new ContractValidateException(
+ "[ALLOW_ENERGY_ADJUSTMENT] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_ENERGY_ADJUSTMENT] is only allowed to be 1");
+ }
+ break;
+ }
+ case MAX_CREATE_ACCOUNT_TX_SIZE: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7_5)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [MAX_CREATE_ACCOUNT_TX_SIZE]");
+ }
+ if (value < CREATE_ACCOUNT_TRANSACTION_MIN_BYTE_SIZE
+ || value > CREATE_ACCOUNT_TRANSACTION_MAX_BYTE_SIZE) {
+ throw new ContractValidateException(
+ "This value[MAX_CREATE_ACCOUNT_TX_SIZE] is only allowed to be greater than or equal "
+ + "to " + CREATE_ACCOUNT_TRANSACTION_MIN_BYTE_SIZE + " and less than or equal to "
+ + CREATE_ACCOUNT_TRANSACTION_MAX_BYTE_SIZE + "!");
+ }
+ break;
+ }
+ case ALLOW_STRICT_MATH: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_7_7)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_STRICT_MATH]");
+ }
+ if (dynamicPropertiesStore.allowStrictMath()) {
+ throw new ContractValidateException(
+ "[ALLOW_STRICT_MATH] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_STRICT_MATH] is only allowed to be 1");
+ }
+ break;
+ }
+ case CONSENSUS_LOGIC_OPTIMIZATION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_0)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [CONSENSUS_LOGIC_OPTIMIZATION]");
+ }
+ if (dynamicPropertiesStore.getConsensusLogicOptimization() == 1) {
+ throw new ContractValidateException(
+ "[CONSENSUS_LOGIC_OPTIMIZATION] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[CONSENSUS_LOGIC_OPTIMIZATION] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_TVM_CANCUN: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_0)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_CANCUN]");
+ }
+ if (dynamicPropertiesStore.getAllowTvmCancun() == 1) {
+ throw new ContractValidateException(
+ "[ALLOW_TVM_CANCUN] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_CANCUN] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_TVM_BLOB: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_0)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_BLOB]");
+ }
+ if (dynamicPropertiesStore.getAllowTvmBlob() == 1) {
+ throw new ContractValidateException(
+ "[ALLOW_TVM_BLOB] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_BLOB] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_TVM_SELFDESTRUCT_RESTRICTION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_1)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_SELFDESTRUCT_RESTRICTION]");
+ }
+ if (dynamicPropertiesStore.allowTvmSelfdestructRestriction()) {
+ throw new ContractValidateException(
+ "[ALLOW_TVM_SELFDESTRUCT_RESTRICTION] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_SELFDESTRUCT_RESTRICTION] is only allowed to be 1");
+ }
+ break;
+ }
+ case PROPOSAL_EXPIRE_TIME: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_1)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [PROPOSAL_EXPIRE_TIME]");
+ }
+ if (value <= MIN_PROPOSAL_EXPIRE_TIME
+ || value >= MAX_PROPOSAL_EXPIRE_TIME) {
+ throw new ContractValidateException(
+ "This value[PROPOSAL_EXPIRE_TIME] is only allowed to be greater than "
+ + MIN_PROPOSAL_EXPIRE_TIME + " and less than "
+ + MAX_PROPOSAL_EXPIRE_TIME + "!");
+ }
+ break;
+ }
+ case ALLOW_TVM_OSAKA: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_OSAKA]");
+ }
+ if (dynamicPropertiesStore.getAllowTvmOsaka() == 1) {
+ throw new ContractValidateException(
+ "[ALLOW_TVM_OSAKA] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_OSAKA] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_TVM_PRAGUE: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_PRAGUE]");
+ }
+ // The deployed BlockHashHistory bytecode contains PUSH0 (0x5f), which
+ // is itself gated on ALLOW_TVM_SHANGHAI at execution time. Refuse the
+ // proposal until Shanghai is enacted so an out-of-order activation
+ // can't leave a contract whose every STATICCALL hits InvalidOpcode.
+ if (dynamicPropertiesStore.getAllowTvmShangHai() != 1) {
+ throw new ContractValidateException(
+ "[ALLOW_TVM_PRAGUE] requires [ALLOW_TVM_SHANGHAI] to be enacted first");
+ }
+ if (dynamicPropertiesStore.getAllowTvmPrague() == 1) {
+ throw new ContractValidateException(
+ "[ALLOW_TVM_PRAGUE] has been valid, no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_PRAGUE] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_HARDEN_RESOURCE_CALCULATION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_HARDEN_RESOURCE_CALCULATION]");
+ }
+ if (dynamicPropertiesStore.getAllowHardenResourceCalculation() == 1) {
+ throw new ContractValidateException(
+ "[ALLOW_HARDEN_RESOURCE_CALCULATION] has been valid, "
+ + "no need to propose again");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_HARDEN_RESOURCE_CALCULATION] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_HARDEN_EXCHANGE_CALCULATION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_8_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_HARDEN_EXCHANGE_CALCULATION]");
+ }
+ if (value != 0 && value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_HARDEN_EXCHANGE_CALCULATION] is only allowed to be 0 or 1");
+ }
+ if (dynamicPropertiesStore.getAllowHardenExchangeCalculation() == value) {
+ throw new ContractValidateException(
+ "[ALLOW_HARDEN_EXCHANGE_CALCULATION] has been set to " + value
+ + ", no need to propose again");
+ }
+ break;
+ }
default:
break;
}
@@ -663,8 +1007,29 @@ public enum ProposalType { // current value, value range
ALLOW_ASSET_OPTIMIZATION(66), // 0, 1
ALLOW_NEW_REWARD(67), // 0, 1
MEMO_FEE(68), // 0, [0, 1000_000_000]
- ALLOW_DELEGATE_OPTIMIZATION(69); // 0, 1
-
+ ALLOW_DELEGATE_OPTIMIZATION(69),
+ UNFREEZE_DELAY_DAYS(70), // 0, [1, 365]
+ ALLOW_OPTIMIZED_RETURN_VALUE_OF_CHAIN_ID(71), // 0, 1
+ ALLOW_DYNAMIC_ENERGY(72), // 0, 1
+ DYNAMIC_ENERGY_THRESHOLD(73), // 0, [0, LONG]
+ DYNAMIC_ENERGY_INCREASE_FACTOR(74), // 0, [0, 10_000]
+ DYNAMIC_ENERGY_MAX_FACTOR(75), // 0, [0, 100_000]
+ ALLOW_TVM_SHANGHAI(76), // 0, 1
+ ALLOW_CANCEL_ALL_UNFREEZE_V2(77), // 0, 1
+ MAX_DELEGATE_LOCK_PERIOD(78), // (86400, 10512000]
+ ALLOW_OLD_REWARD_OPT(79), // 0, 1
+ ALLOW_ENERGY_ADJUSTMENT(81), // 0, 1
+ MAX_CREATE_ACCOUNT_TX_SIZE(82), // [500, 10000]
+ ALLOW_TVM_CANCUN(83), // 0, 1
+ ALLOW_STRICT_MATH(87), // 0, 1
+ CONSENSUS_LOGIC_OPTIMIZATION(88), // 0, 1
+ ALLOW_TVM_BLOB(89), // 0, 1
+ PROPOSAL_EXPIRE_TIME(92), // (0, 31536003000)
+ ALLOW_TVM_SELFDESTRUCT_RESTRICTION(94), // 0, 1
+ ALLOW_TVM_PRAGUE(95), // 0, 1
+ ALLOW_TVM_OSAKA(96), // 0, 1
+ ALLOW_HARDEN_RESOURCE_CALCULATION(97), // 0, 1
+ ALLOW_HARDEN_EXCHANGE_CALCULATION(98); // 0, 1
private long code;
ProposalType(long code) {
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..f7359031d92 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,62 @@
package org.tron.core.utils;
+import java.lang.reflect.Modifier;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
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.debug("Actuator already registered.");
+ return;
+ }
+
+ synchronized (TransactionRegister.class) {
+ if (REGISTERED.get()) {
+ logger.debug("Actuator already registered.");
+ return;
+ }
+ logger.debug("Register actuator start.");
+ Reflections reflections = new Reflections(PACKAGE_NAME);
+ Set> subTypes = reflections
+ .getSubTypesOf(AbstractActuator.class).stream()
+ .filter(c -> !Modifier.isAbstract(c.getModifiers()))
+ .collect(Collectors.toSet());
+
+ for (Class extends AbstractActuator> clazz : subTypes) {
+ try {
+ logger.debug("Registering actuator: {} start", clazz.getName());
+ clazz.getDeclaredConstructor().newInstance();
+ logger.debug("Registering actuator: {} done", clazz.getName());
+ } catch (Exception e) {
+ Throwable cause = e.getCause() != null ? e.getCause() : e;
+ String detail = cause.getMessage() != null ? cause.getMessage() : cause.toString();
+ throw new TronError(clazz.getName() + ": " + detail,
+ e, TronError.ErrCode.ACTUATOR_REGISTER);
+ }
}
+
+ REGISTERED.set(true);
+ logger.debug("Register actuator done, total {}.", subTypes.size());
}
}
+ static boolean isRegistered() {
+ return REGISTERED.get();
+ }
+
+ // For testing only — resets registration state between tests.
+ static void resetForTesting() {
+ REGISTERED.set(false);
+ }
}
diff --git a/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java b/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java
index 5ca4f36ab12..53d6caf5691 100644
--- a/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java
+++ b/actuator/src/main/java/org/tron/core/utils/TransactionUtil.java
@@ -16,6 +16,9 @@
package org.tron.core.utils;
import static org.tron.common.crypto.Hash.sha3omit12;
+import static org.tron.common.math.Maths.max;
+import static org.tron.core.config.Parameter.ChainConstant.DELEGATE_COST_BASE_SIZE;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
import com.google.common.base.CaseFormat;
import com.google.common.primitives.Longs;
@@ -41,14 +44,15 @@
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.exception.PermissionException;
import org.tron.core.exception.SignatureFormatException;
+import org.tron.core.store.DynamicPropertiesStore;
import org.tron.protos.Protocol.Permission;
import org.tron.protos.Protocol.Permission.PermissionType;
import org.tron.protos.Protocol.Transaction;
import org.tron.protos.Protocol.Transaction.Contract;
import org.tron.protos.Protocol.Transaction.Result.contractResult;
-import org.tron.protos.Protocol.TransactionSign;
import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract;
import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract;
+import org.tron.protos.contract.BalanceContract.DelegateResourceContract;
@Slf4j(topic = "capsule")
@Component
@@ -179,21 +183,6 @@ public static String makeUpperCamelMethod(String originName) {
.replace("_", "");
}
- public static TransactionCapsule getTransactionSign(TransactionSign transactionSign) {
- byte[] privateKey = transactionSign.getPrivateKey().toByteArray();
- TransactionCapsule trx = new TransactionCapsule(transactionSign.getTransaction());
- trx.sign(privateKey);
- return trx;
- }
-
- public TransactionCapsule addSign(TransactionSign transactionSign)
- throws PermissionException, SignatureException, SignatureFormatException {
- byte[] privateKey = transactionSign.getPrivateKey().toByteArray();
- TransactionCapsule trx = new TransactionCapsule(transactionSign.getTransaction());
- trx.addSign(privateKey, chainBaseManager.getAccountStore());
- return trx;
- }
-
public TransactionSignWeight getTransactionSignWeight(Transaction trx) {
TransactionSignWeight.Builder tswBuilder = TransactionSignWeight.newBuilder();
TransactionExtention.Builder trxExBuilder = TransactionExtention.newBuilder();
@@ -233,7 +222,7 @@ public TransactionSignWeight getTransactionSignWeight(Transaction trx) {
}
tswBuilder.setPermission(permission);
if (trx.getSignatureCount() > 0) {
- List approveList = new ArrayList();
+ List approveList = new ArrayList<>();
long currentWeight = TransactionCapsule.checkWeight(permission, trx.getSignatureList(),
Sha256Hash.hash(CommonParameter.getInstance()
.isECKeyCryptoEngine(), trx.getRawData().toByteArray()), approveList);
@@ -264,4 +253,24 @@ public TransactionSignWeight getTransactionSignWeight(Transaction trx) {
return tswBuilder.build();
}
+ public static long estimateConsumeBandWidthSize(DynamicPropertiesStore dps, long balance) {
+ DelegateResourceContract.Builder builder;
+ if (dps.supportMaxDelegateLockPeriod()) {
+ builder = DelegateResourceContract.newBuilder()
+ .setLock(true)
+ .setLockPeriod(dps.getMaxDelegateLockPeriod())
+ .setBalance(balance);
+ } else {
+ builder = DelegateResourceContract.newBuilder()
+ .setLock(true)
+ .setBalance(balance);
+ }
+ long builderSize = builder.build().getSerializedSize();
+ DelegateResourceContract.Builder builder2 = DelegateResourceContract.newBuilder()
+ .setBalance(TRX_PRECISION);
+ long builder2Size = builder2.build().getSerializedSize();
+ long addSize = max(builderSize - builder2Size, 0L, dps.disableJavaLangMath());
+
+ return DELEGATE_COST_BASE_SIZE + addSize;
+ }
}
diff --git a/actuator/src/main/java/org/tron/core/vm/ChainParameterEnum.java b/actuator/src/main/java/org/tron/core/vm/ChainParameterEnum.java
new file mode 100644
index 00000000000..0d9df91ef6c
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/ChainParameterEnum.java
@@ -0,0 +1,50 @@
+package org.tron.core.vm;
+
+import java.util.function.Function;
+import org.tron.core.vm.repository.Repository;
+
+public enum ChainParameterEnum {
+
+ INVALID_PARAMETER_KEY(0, ignored -> 0L),
+
+ TOTAL_NET_LIMIT(1, repository -> repository.getDynamicPropertiesStore().getTotalNetLimit()),
+
+ TOTAL_NET_WEIGHT(2, Repository::getTotalNetWeight),
+
+ TOTAL_ENERGY_CURRENT_LIMIT(3,
+ repository -> repository.getDynamicPropertiesStore().getTotalEnergyCurrentLimit()),
+
+ TOTAL_ENERGY_WEIGHT(4, Repository::getTotalEnergyWeight),
+
+ UNFREEZE_DELAY_DAYS(5,
+ repository -> repository.getDynamicPropertiesStore().getUnfreezeDelayDays()),
+
+ ;
+
+ private final long code;
+
+ private final Function action;
+
+ ChainParameterEnum(long code, Function action) {
+ this.code = code;
+ this.action = action;
+ }
+
+ public static ChainParameterEnum fromCode(long code) {
+ for (ChainParameterEnum each : values()) {
+ if (each.code == code) {
+ return each;
+ }
+ }
+
+ return INVALID_PARAMETER_KEY;
+ }
+
+ public long getCode() {
+ return code;
+ }
+
+ public Function getAction() {
+ return action;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/EnergyCost.java b/actuator/src/main/java/org/tron/core/vm/EnergyCost.java
index 8253e4bf7e6..3641548b3e5 100644
--- a/actuator/src/main/java/org/tron/core/vm/EnergyCost.java
+++ b/actuator/src/main/java/org/tron/core/vm/EnergyCost.java
@@ -2,9 +2,12 @@
import java.math.BigInteger;
import org.tron.common.runtime.vm.DataWord;
+import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.program.Program;
import org.tron.core.vm.program.Stack;
+import static org.tron.core.Constant.DYNAMIC_ENERGY_FACTOR_DECIMAL;
+
public class EnergyCost {
private static final long ZERO_TIER = 0;
@@ -36,6 +39,12 @@ public class EnergyCost {
private static final long NEW_ACCT_CALL = 25000;
private static final long UNFREEZE = 20000;
private static final long FREEZE_EXPIRE_TIME = 50;
+ private static final long FREEZE_V2 = 10000;
+ private static final long UNFREEZE_V2 = 10000;
+ private static final long WITHDRAW_EXPIRE_UNFREEZE = 10000;
+ private static final long CANCEL_ALL_UNFREEZE_V2 = 10000;
+ private static final long DELEGATE_RESOURCE = 10000;
+ private static final long UN_DELEGATE_RESOURCE = 10000;
private static final long VOTE_WITNESS = 30000;
private static final long WITHDRAW_REWARD = 20000;
private static final long CREATE = 32000;
@@ -46,8 +55,11 @@ public class EnergyCost {
private static final long EXT_CODE_SIZE = 20;
private static final long EXT_CODE_HASH = 400;
private static final long SUICIDE = 0;
+ private static final long SUICIDE_V2 = 5000;
private static final long STOP = 0;
private static final long CREATE_DATA = 200;
+ private static final long TLOAD = 100;
+ private static final long TSTORE = 100;
public static long getZeroTierCost(Program ignored) {
return ZERO_TIER;
@@ -223,6 +235,26 @@ public static long getSstoreCost(Program program) {
}
+ public static long getTLoadCost(Program ignored) {
+ return TLOAD;
+ }
+
+ public static long getTStoreCost(Program ignored) {
+ return TSTORE;
+ }
+
+ public static long getMCopyCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+
+ DataWord dstOffset = stack.peek();
+ DataWord srcOffset = stack.get(stack.size() - 2);
+ DataWord maxOffset = dstOffset.compareTo(srcOffset) > 0 ? dstOffset : srcOffset;
+ return VERY_LOW_TIER + calcMemEnergy(oldMemSize,
+ memNeeded(maxOffset, stack.get(stack.size() - 3)),
+ stack.get(stack.size() - 3).longValueSafe(), Op.MCOPY);
+ }
+
public static long getLogCost(Program program) {
Stack stack = program.getStack();
long oldMemSize = program.getMemSize();
@@ -250,12 +282,27 @@ public static long getSuicideCost(Program ignored) {
return SUICIDE;
}
+ public static long getSuicideCost2(Program program) {
+ DataWord inheritorAddress = program.getStack().peek();
+ if (isDeadAccount(program, inheritorAddress)) {
+ return getSuicideCost(program) + NEW_ACCT_CALL;
+ }
+ return getSuicideCost(program);
+ }
+
+ public static long getSuicideCost3(Program program) {
+ DataWord inheritorAddress = program.getStack().peek();
+ if (isDeadAccount(program, inheritorAddress)) {
+ return SUICIDE_V2 + NEW_ACCT_CALL;
+ }
+ return SUICIDE_V2;
+ }
+
public static long getBalanceCost(Program ignored) {
return BALANCE;
}
public static long getFreezeCost(Program program) {
-
Stack stack = program.getStack();
DataWord receiverAddressWord = stack.get(stack.size() - 3);
if (isDeadAccount(program, receiverAddressWord)) {
@@ -272,8 +319,52 @@ public static long getFreezeExpireTimeCost(Program ignored) {
return FREEZE_EXPIRE_TIME;
}
+ public static long getFreezeBalanceV2Cost(Program ignored) {
+ return FREEZE_V2;
+ }
+
+ public static long getUnfreezeBalanceV2Cost(Program ignored) {
+ return UNFREEZE_V2;
+ }
+
+ public static long getWithdrawExpireUnfreezeCost(Program ignored) {
+ return WITHDRAW_EXPIRE_UNFREEZE;
+ }
+
+ public static long getCancelAllUnfreezeV2Cost(Program ignored) {
+ return CANCEL_ALL_UNFREEZE_V2;
+ }
+
+ public static long getDelegateResourceCost(Program ignored) {
+ return DELEGATE_RESOURCE;
+ }
+
+ public static long getUnDelegateResourceCost(Program ignored) {
+ return UN_DELEGATE_RESOURCE;
+ }
+
public static long getVoteWitnessCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ DataWord amountArrayLength = stack.get(stack.size() - 1).clone();
+ DataWord amountArrayOffset = stack.get(stack.size() - 2);
+ DataWord witnessArrayLength = stack.get(stack.size() - 3).clone();
+ DataWord witnessArrayOffset = stack.get(stack.size() - 4);
+
+ DataWord wordSize = new DataWord(DataWord.WORD_SIZE);
+
+ amountArrayLength.mul(wordSize);
+ BigInteger amountArrayMemoryNeeded = memNeeded(amountArrayOffset, amountArrayLength);
+
+ witnessArrayLength.mul(wordSize);
+ BigInteger witnessArrayMemoryNeeded = memNeeded(witnessArrayOffset, witnessArrayLength);
+
+ return VOTE_WITNESS + calcMemEnergy(oldMemSize,
+ (amountArrayMemoryNeeded.compareTo(witnessArrayMemoryNeeded) > 0
+ ? amountArrayMemoryNeeded : witnessArrayMemoryNeeded), 0, Op.VOTEWITNESS);
+ }
+ public static long getVoteWitnessCost2(Program program) {
Stack stack = program.getStack();
long oldMemSize = program.getMemSize();
DataWord amountArrayLength = stack.get(stack.size() - 1).clone();
@@ -284,9 +375,11 @@ public static long getVoteWitnessCost(Program program) {
DataWord wordSize = new DataWord(DataWord.WORD_SIZE);
amountArrayLength.mul(wordSize);
+ amountArrayLength.add(wordSize); // dynamic array length is at least 32 bytes
BigInteger amountArrayMemoryNeeded = memNeeded(amountArrayOffset, amountArrayLength);
witnessArrayLength.mul(wordSize);
+ witnessArrayLength.add(wordSize); // dynamic array length is at least 32 bytes
BigInteger witnessArrayMemoryNeeded = memNeeded(witnessArrayOffset, witnessArrayLength);
return VOTE_WITNESS + calcMemEnergy(oldMemSize,
@@ -294,6 +387,27 @@ public static long getVoteWitnessCost(Program program) {
? amountArrayMemoryNeeded : witnessArrayMemoryNeeded), 0, Op.VOTEWITNESS);
}
+ public static long getVoteWitnessCost3(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ BigInteger amountArrayLength = stack.get(stack.size() - 1).value();
+ BigInteger amountArrayOffset = stack.get(stack.size() - 2).value();
+ BigInteger witnessArrayLength = stack.get(stack.size() - 3).value();
+ BigInteger witnessArrayOffset = stack.get(stack.size() - 4).value();
+
+ BigInteger wordSize = BigInteger.valueOf(DataWord.WORD_SIZE);
+
+ BigInteger amountArraySize = amountArrayLength.multiply(wordSize).add(wordSize);
+ BigInteger amountArrayMemoryNeeded = memNeeded(amountArrayOffset, amountArraySize);
+
+ BigInteger witnessArraySize = witnessArrayLength.multiply(wordSize).add(wordSize);
+ BigInteger witnessArrayMemoryNeeded = memNeeded(witnessArrayOffset, witnessArraySize);
+
+ return VOTE_WITNESS + calcMemEnergy(oldMemSize,
+ (amountArrayMemoryNeeded.compareTo(witnessArrayMemoryNeeded) > 0
+ ? amountArrayMemoryNeeded : witnessArrayMemoryNeeded), 0, Op.VOTEWITNESS);
+ }
+
public static long getWithdrawRewardCost(Program ignored) {
return WITHDRAW_REWARD;
}
@@ -388,6 +502,18 @@ public static long getCalculateCallCost(Stack stack, Program program,
energyCost += calcMemEnergy(oldMemSize, in.max(out),
0, op);
+ if (VMConfig.allowDynamicEnergy()) {
+ long factor = program.getContextContractFactor();
+ if (factor > DYNAMIC_ENERGY_FACTOR_DECIMAL) {
+ long penalty = energyCost * factor / DYNAMIC_ENERGY_FACTOR_DECIMAL - energyCost;
+ if (penalty < 0) {
+ penalty = 0;
+ }
+ program.setCallPenaltyEnergy(penalty);
+ energyCost += penalty;
+ }
+ }
+
if (energyCost > program.getEnergyLimitLeft().longValueSafe()) {
throw new Program.OutOfEnergyException(
"Not enough energy for '%s' operation executing: opEnergy[%d], programEnergy[%d]",
@@ -445,6 +571,10 @@ private static BigInteger memNeeded(DataWord offset, DataWord size) {
return size.isZero() ? BigInteger.ZERO : offset.value().add(size.value());
}
+ private static BigInteger memNeeded(BigInteger offset, BigInteger size) {
+ return size.equals(BigInteger.ZERO) ? BigInteger.ZERO : offset.add(size);
+ }
+
private static boolean isDeadAccount(Program program, DataWord address) {
return program.getContractState().getAccount(address.toTronAddress()) == null;
}
diff --git a/actuator/src/main/java/org/tron/core/vm/Op.java b/actuator/src/main/java/org/tron/core/vm/Op.java
index 6abf6a04632..0a3fcc1dae3 100644
--- a/actuator/src/main/java/org/tron/core/vm/Op.java
+++ b/actuator/src/main/java/org/tron/core/vm/Op.java
@@ -64,6 +64,8 @@ public class Op {
public static final int SHR = 0x1c;
// (0x1d) Arithmetic shift right
public static final int SAR = 0x1d;
+ // (0x1e) Count leading zeros
+ public static final int CLZ = 0x1e;
/* Cryptographic Operations */
// (0x20) Compute SHA3-256 hash
@@ -120,6 +122,10 @@ public class Op {
public static final int SELFBALANCE = 0x47;
// (0x48) Get block's basefee
public static final int BASEFEE = 0x48;
+ // (0x49) Get blob hash
+ public static final int BLOBHASH = 0x49;
+ // (0x4a) Get block's blob basefee
+ public static final int BLOBBASEFEE = 0x4a;
/* Memory, Storage and Flow Operations */
// (0x50) Remove item from stack
@@ -145,9 +151,16 @@ public class Op {
// (0x5a) Get the amount of available gas
public static final int GAS = 0x5a;
public static final int JUMPDEST = 0x5b;
+ // (0x5c) Load word from transient storage
+ public static final int TLOAD = 0x5c;
+ // (0x5d) Save word to transient storage
+ public static final int TSTORE = 0x5d;
+ // (0x5e) Copy word from memory
+ public static final int MCOPY = 0x5e;
/* Push Operations */
// Place item on stack
+ public static final int PUSH0 = 0x5f;
public static final int PUSH1 = 0x60;
public static final int PUSH2 = 0x61;
public static final int PUSH3 = 0x62;
@@ -236,6 +249,12 @@ public class Op {
public static final int FREEZEEXPIRETIME = 0xd7;
public static final int VOTEWITNESS = 0xd8;
public static final int WITHDRAWREWARD = 0xd9;
+ public static final int FREEZEBALANCEV2 = 0xda;
+ public static final int UNFREEZEBALANCEV2 = 0xdb;
+ public static final int CANCELALLUNFREEZEV2 = 0xdc;
+ public static final int WITHDRAWEXPIREUNFREEZE = 0xdd;
+ public static final int DELEGATERESOURCE = 0xde;
+ public static final int UNDELEGATERESOURCE = 0xdf;
// (0xf0) Create a new account with associated code
public static final int CREATE = 0xf0;
diff --git a/actuator/src/main/java/org/tron/core/vm/OperationActions.java b/actuator/src/main/java/org/tron/core/vm/OperationActions.java
index c7a9c2cc2e3..88c3c55899e 100644
--- a/actuator/src/main/java/org/tron/core/vm/OperationActions.java
+++ b/actuator/src/main/java/org/tron/core/vm/OperationActions.java
@@ -2,11 +2,11 @@
import static org.tron.common.crypto.Hash.sha3;
import static org.tron.common.utils.ByteUtil.EMPTY_BYTE_ARRAY;
+import static org.tron.common.utils.ByteUtil.numberOfLeadingZeros;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
-
import org.tron.common.runtime.vm.DataWord;
import org.tron.common.runtime.vm.LogInfo;
import org.tron.core.vm.config.VMConfig;
@@ -288,6 +288,17 @@ public static void sarAction(Program program) {
program.step();
}
+ public static void clzAction(Program program) {
+ DataWord word = program.stackPop();
+ int clz = numberOfLeadingZeros(word.getData());
+ if (clz == 256) {
+ program.stackPush(new DataWord(256));
+ } else {
+ program.stackPush(DataWord.of((byte) clz));
+ }
+ program.step();
+ }
+
public static void sha3Action(Program program) {
DataWord memOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
@@ -644,6 +655,56 @@ public static void jumpDestAction(Program program) {
program.step();
}
+ public static void tLoadAction(Program program) {
+ DataWord key = program.stackPop();
+ DataWord address = program.getContractAddress();
+
+ byte[] data =
+ program.getContractState().getTransientStorageValue(address.getData(), key.getData());
+ DataWord value = data != null ? new DataWord(data).clone() : DataWord.ZERO();
+
+ program.stackPush(value);
+ program.step();
+ }
+
+ public static void tStoreAction(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+ DataWord key = program.stackPop();
+ DataWord value = program.stackPop();
+ DataWord address = program.getContractAddress();
+
+ program.getContractState()
+ .updateTransientStorageValue(address.getData(), key.getData(), value.getData());
+ program.step();
+ }
+
+ public static void mCopyAction(Program program) {
+ int dstOffset = program.stackPop().intValueSafe();
+ int srcOffset = program.stackPop().intValueSafe();
+ int length = program.stackPop().intValueSafe();
+
+ program.memoryCopy(dstOffset, srcOffset, length);
+ program.step();
+ }
+
+ public static void blobHashAction(Program program) {
+ program.stackPop();
+ program.stackPush(DataWord.ZERO());
+ program.step();
+ }
+
+ public static void blobBaseFeeAction(Program program) {
+ program.stackPush(DataWord.ZERO());
+ program.step();
+ }
+
+ public static void push0Action(Program program) {
+ program.stackPush(DataWord.ZERO());
+ program.step();
+ }
+
public static void pushAction(Program program) {
int n = program.getCurrentOpIntValue() - Op.PUSH1 + 1;
program.step();
@@ -737,8 +798,13 @@ public static void freezeAction(Program program) {
DataWord frozenBalance = program.stackPop();
DataWord receiverAddress = program.stackPop();
- boolean result = program.freeze(receiverAddress, frozenBalance, resourceType );
- program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ if (VMConfig.allowTvmFreezeV2()) {
+ // after v2 activated, we just push zero to stack and do nothing
+ program.stackPush(DataWord.ZERO());
+ } else {
+ boolean result = program.freeze(receiverAddress, frozenBalance, resourceType );
+ program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ }
program.step();
}
@@ -764,6 +830,78 @@ public static void freezeExpireTimeAction(Program program) {
program.step();
}
+ public static void freezeBalanceV2Action(Program program) {
+ // after allow vote, check static
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+ DataWord resourceType = program.stackPop();
+ DataWord frozenBalance = program.stackPop();
+
+ boolean result = program.freezeBalanceV2(frozenBalance, resourceType);
+ program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ program.step();
+ }
+
+ public static void unfreezeBalanceV2Action(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ DataWord resourceType = program.stackPop();
+ DataWord unfreezeBalance = program.stackPop();
+
+ boolean result = program.unfreezeBalanceV2(unfreezeBalance, resourceType);
+ program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ program.step();
+ }
+
+ public static void withdrawExpireUnfreezeAction(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ long expireUnfreezeBalance = program.withdrawExpireUnfreeze();
+ program.stackPush(new DataWord(expireUnfreezeBalance));
+ program.step();
+ }
+
+ public static void cancelAllUnfreezeV2Action(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ boolean result = program.cancelAllUnfreezeV2Action();
+ program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ program.step();
+ }
+
+ public static void delegateResourceAction(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+ DataWord resourceType = program.stackPop();
+ DataWord delegateBalance = program.stackPop();
+ DataWord receiverAddress = program.stackPop();
+
+ boolean result = program.delegateResource(receiverAddress, delegateBalance, resourceType);
+ program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ program.step();
+ }
+
+ public static void unDelegateResourceAction(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+ DataWord resourceType = program.stackPop();
+ DataWord unDelegateBalance = program.stackPop();
+ DataWord receiverAddress = program.stackPop();
+
+ boolean result = program.unDelegateResource(receiverAddress, unDelegateBalance, resourceType);
+ program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ program.step();
+ }
+
public static void voteWitnessAction(Program program) {
if (program.isStaticCall()) {
throw new Program.StaticCallModificationException();
@@ -891,7 +1029,7 @@ public static void staticCallAction(Program program) {
}
public static void exeCall(Program program, DataWord adjustedCallEnergy,
- DataWord codeAddress, DataWord value, DataWord tokenId, boolean isTokenTransferMsg) {
+ DataWord codeAddress, DataWord value, DataWord tokenId, boolean isTokenTransferMsg) {
DataWord inDataOffs = program.stackPop();
DataWord inDataSize = program.stackPop();
@@ -908,6 +1046,9 @@ public static void exeCall(Program program, DataWord adjustedCallEnergy,
PrecompiledContracts.PrecompiledContract contract =
PrecompiledContracts.getContractForAddress(codeAddress);
if (contract != null) {
+ if (program.isConstantCall()) {
+ contract = PrecompiledContracts.getOptimizedContractForConstant(contract);
+ }
program.callToPrecompiledAddress(msg, contract);
} else {
program.callToAddress(msg);
@@ -943,4 +1084,19 @@ public static void suicideAction(Program program) {
program.stop();
}
+ public static void suicideAction2(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ if (!program.canSuicide2()) {
+ program.getResult().setRevert();
+ } else {
+ DataWord address = program.stackPop();
+ program.suicide2(address);
+ }
+
+ program.stop();
+ }
+
}
diff --git a/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java b/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java
index fb66f69d1e4..8c078e843a2 100644
--- a/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java
+++ b/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java
@@ -10,6 +10,10 @@ public class OperationRegistry {
public enum Version {
TRON_V1_0,
TRON_V1_1,
+ TRON_V1_2,
+ TRON_V1_3,
+ TRON_V1_4,
+ TRON_V1_5,
// add more
// TRON_V2,
// ETH
@@ -20,6 +24,10 @@ public enum Version {
static {
tableMap.put(Version.TRON_V1_0, newTronV10OperationSet());
tableMap.put(Version.TRON_V1_1, newTronV11OperationSet());
+ tableMap.put(Version.TRON_V1_2, newTronV12OperationSet());
+ tableMap.put(Version.TRON_V1_3, newTronV13OperationSet());
+ tableMap.put(Version.TRON_V1_4, newTronV14OperationSet());
+ tableMap.put(Version.TRON_V1_5, newTronV15OperationSet());
}
public static JumpTable newTronV10OperationSet() {
@@ -35,23 +43,59 @@ public static JumpTable newTronV10OperationSet() {
}
public static JumpTable newTronV11OperationSet() {
- JumpTable table = newTronV10OperationSet();
- adjustMemOperations(table);
+ return newTronV10OperationSet();
+ }
+
+ public static JumpTable newTronV12OperationSet() {
+ JumpTable table = newTronV11OperationSet();
+ appendFreezeV2Operations(table);
+ appendDelegateOperations(table);
return table;
}
- // Just for warming up class to avoid out_of_time
- public static void init() {
+ public static JumpTable newTronV13OperationSet() {
+ JumpTable table = newTronV12OperationSet();
+ appendShangHaiOperations(table);
+ return table;
+ }
+
+ public static JumpTable newTronV14OperationSet() {
+ JumpTable table = newTronV13OperationSet();
+ appendCancunOperations(table);
+ return table;
+ }
+
+ public static JumpTable newTronV15OperationSet() {
+ JumpTable table = newTronV14OperationSet();
+ appendOsakaOperations(table);
+ return table;
}
+ // Just for warming up class to avoid out_of_time
+ public static void init() {}
+
public static JumpTable getTable() {
- // implement as needed
- // switch (tx.getType()) {
- // }
+ // always get the table which has the newest version
+ JumpTable table = tableMap.get(Version.TRON_V1_5);
+
+ // next make the corresponding changes, exclude activating opcode
if (VMConfig.allowHigherLimitForMaxCpuTimeOfOneTx()) {
- return tableMap.get(Version.TRON_V1_1);
+ adjustMemOperations(table);
+ }
+
+ if (VMConfig.allowEnergyAdjustment()) {
+ adjustForFairEnergy(table);
}
- return tableMap.get(Version.TRON_V1_0);
+
+ if (VMConfig.allowTvmSelfdestructRestriction()) {
+ adjustSelfdestruct(table);
+ }
+
+ if (VMConfig.allowTvmOsaka()) {
+ adjustVoteWitnessCost(table);
+ }
+
+ return table;
}
public static JumpTable newBaseOperationSet() {
@@ -565,4 +609,131 @@ public static void adjustMemOperations(JumpTable table) {
EnergyCost::getMStore8Cost2,
OperationActions::mStore8Action));
}
+
+ public static void appendFreezeV2Operations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmFreezeV2;
+
+ table.set(new Operation(
+ Op.FREEZEBALANCEV2, 2, 1,
+ EnergyCost::getFreezeBalanceV2Cost,
+ OperationActions::freezeBalanceV2Action,
+ proposal));
+
+ table.set(new Operation(
+ Op.UNFREEZEBALANCEV2, 2, 1,
+ EnergyCost::getUnfreezeBalanceV2Cost,
+ OperationActions::unfreezeBalanceV2Action,
+ proposal));
+
+ table.set(new Operation(
+ Op.WITHDRAWEXPIREUNFREEZE, 0, 1,
+ EnergyCost::getWithdrawExpireUnfreezeCost,
+ OperationActions::withdrawExpireUnfreezeAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.CANCELALLUNFREEZEV2, 0, 1,
+ EnergyCost::getCancelAllUnfreezeV2Cost,
+ OperationActions::cancelAllUnfreezeV2Action,
+ proposal));
+ }
+
+ public static void appendDelegateOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmFreezeV2;
+
+ table.set(new Operation(
+ Op.DELEGATERESOURCE, 3, 1,
+ EnergyCost::getDelegateResourceCost,
+ OperationActions::delegateResourceAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.UNDELEGATERESOURCE, 3, 1,
+ EnergyCost::getUnDelegateResourceCost,
+ OperationActions::unDelegateResourceAction,
+ proposal));
+ }
+
+ public static void appendShangHaiOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmShanghai;
+
+ table.set(new Operation(
+ Op.PUSH0, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::push0Action,
+ proposal));
+ }
+
+ public static void adjustForFairEnergy(JumpTable table) {
+ table.set(new Operation(
+ Op.VOTEWITNESS, 4, 1,
+ EnergyCost::getVoteWitnessCost2,
+ OperationActions::voteWitnessAction,
+ VMConfig::allowTvmVote));
+
+ table.set(new Operation(
+ Op.SUICIDE, 1, 0,
+ EnergyCost::getSuicideCost2,
+ OperationActions::suicideAction));
+ }
+
+ public static void appendCancunOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmCancun;
+ BooleanSupplier tvmBlobProposal = VMConfig::allowTvmBlob;
+
+ table.set(new Operation(
+ Op.TLOAD, 1, 1,
+ EnergyCost::getTLoadCost,
+ OperationActions::tLoadAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.TSTORE, 2, 0,
+ EnergyCost::getTStoreCost,
+ OperationActions::tStoreAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.MCOPY, 3, 0,
+ EnergyCost::getMCopyCost,
+ OperationActions::mCopyAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.BLOBHASH, 1, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::blobHashAction,
+ tvmBlobProposal));
+
+ table.set(new Operation(
+ Op.BLOBBASEFEE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::blobBaseFeeAction,
+ tvmBlobProposal));
+ }
+
+ public static void appendOsakaOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmOsaka;
+
+ table.set(new Operation(
+ Op.CLZ, 1, 1,
+ EnergyCost::getLowTierCost,
+ OperationActions::clzAction,
+ proposal));
+ }
+
+ public static void adjustSelfdestruct(JumpTable table) {
+ table.set(new Operation(
+ Op.SUICIDE, 1, 0,
+ EnergyCost::getSuicideCost3,
+ OperationActions::suicideAction2));
+ }
+
+ public static void adjustVoteWitnessCost(JumpTable table) {
+ table.set(new Operation(
+ Op.VOTEWITNESS, 4, 1,
+ EnergyCost::getVoteWitnessCost3,
+ OperationActions::voteWitnessAction,
+ VMConfig::allowTvmVote));
+ }
}
diff --git a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java
index 437ecdd1d8f..1ac96b9d59d 100644
--- a/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java
+++ b/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java
@@ -1,6 +1,10 @@
package org.tron.core.vm;
import static java.util.Arrays.copyOfRange;
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.min;
+import static org.tron.common.math.StrictMathWrapper.multiplyExact;
+import static org.tron.common.math.StrictMathWrapper.subtractExact;
import static org.tron.common.runtime.vm.DataWord.WORD_SIZE;
import static org.tron.common.utils.BIUtil.addSafely;
import static org.tron.common.utils.BIUtil.isLessThan;
@@ -14,8 +18,11 @@
import static org.tron.common.utils.ByteUtil.parseWord;
import static org.tron.common.utils.ByteUtil.stripLeadingZeroes;
import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.core.vm.VMConstant.SIG_LENGTH;
import com.google.protobuf.ByteString;
+
+import java.lang.reflect.Constructor;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.ArrayList;
@@ -26,7 +33,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import lombok.AllArgsConstructor;
@@ -35,8 +41,16 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.math.ec.ECPoint;
import org.tron.common.crypto.Blake2bfMessageDigest;
import org.tron.common.crypto.Hash;
+import org.tron.common.crypto.Rsv;
import org.tron.common.crypto.SignUtils;
import org.tron.common.crypto.SignatureInterface;
import org.tron.common.crypto.zksnark.BN128;
@@ -45,6 +59,8 @@
import org.tron.common.crypto.zksnark.BN128G2;
import org.tron.common.crypto.zksnark.Fp;
import org.tron.common.crypto.zksnark.PairingCheck;
+import org.tron.common.es.ExecutorServiceManager;
+import org.tron.common.math.StrictMathWrapper;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.ProgramResult;
import org.tron.common.runtime.vm.DataWord;
@@ -61,9 +77,11 @@
import org.tron.core.exception.ZksnarkException;
import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.program.Program;
+import org.tron.core.vm.program.Program.OutOfTimeException;
import org.tron.core.vm.repository.Repository;
+import org.tron.core.vm.utils.FreezeV2Util;
+import org.tron.core.vm.utils.MUtil;
import org.tron.core.vm.utils.VoteRewardUtil;
-
import org.tron.protos.Protocol;
import org.tron.protos.Protocol.Permission;
@@ -97,6 +115,20 @@ public class PrecompiledContracts {
private static final EthRipemd160 ethRipemd160 = new EthRipemd160();
private static final Blake2F blake2F = new Blake2F();
+ private static final P256Verify p256Verify = new P256Verify();
+
+ // FreezeV2 PrecompileContracts
+ private static final GetChainParameter getChainParameter = new GetChainParameter();
+ private static final AvailableUnfreezeV2Size availableUnfreezeV2Size = new AvailableUnfreezeV2Size();
+ private static final UnfreezableBalanceV2 unfreezableBalanceV2 = new UnfreezableBalanceV2();
+ private static final ExpireUnfreezeBalanceV2 expireUnfreezeBalanceV2 = new ExpireUnfreezeBalanceV2();
+ private static final DelegatableResource delegatableResource = new DelegatableResource();
+ private static final ResourceV2 resourceV2 = new ResourceV2();
+ private static final CheckUnDelegateResource checkUnDelegateResource = new CheckUnDelegateResource();
+ private static final ResourceUsage resourceUsage = new ResourceUsage();
+ private static final TotalResource totalResource = new TotalResource();
+ private static final TotalDelegatedResource totalDelegatedResource = new TotalDelegatedResource();
+ private static final TotalAcquiredResource totalAcquiredResource = new TotalAcquiredResource();
private static final DataWord ecRecoverAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000001");
@@ -138,11 +170,56 @@ public class PrecompiledContracts {
"0000000000000000000000000000000000000000000000000000000001000009");
private static final DataWord totalVoteCountAddr = new DataWord(
"000000000000000000000000000000000000000000000000000000000100000a");
+
+ // FreezeV2 PrecompileContracts
+ private static final DataWord getChainParameterAddr = new DataWord(
+ "000000000000000000000000000000000000000000000000000000000100000b");
+
+ private static final DataWord availableUnfreezeV2SizeAddr = new DataWord(
+ "000000000000000000000000000000000000000000000000000000000100000c");
+
+ private static final DataWord unfreezableBalanceV2Addr = new DataWord(
+ "000000000000000000000000000000000000000000000000000000000100000d");
+
+ private static final DataWord expireUnfreezeBalanceV2Addr = new DataWord(
+ "000000000000000000000000000000000000000000000000000000000100000e");
+
+ private static final DataWord delegatableResourceAddr = new DataWord(
+ "000000000000000000000000000000000000000000000000000000000100000f");
+
+ private static final DataWord resourceV2Addr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000010");
+
+ private static final DataWord checkUnDelegateResourceAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000011");
+
+ private static final DataWord resourceUsageAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000012");
+
+ private static final DataWord totalResourceAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000013");
+
+ private static final DataWord totalDelegatedResourceAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000014");
+
+ private static final DataWord totalAcquiredResourceAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000015");
+
private static final DataWord ethRipemd160Addr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000020003");
private static final DataWord blake2FAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000020009");
+ private static final DataWord p256VerifyAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000000000100");
+ public static PrecompiledContract getOptimizedContractForConstant(PrecompiledContract contract) {
+ try {
+ Constructor> constructor = contract.getClass().getDeclaredConstructor();
+ return (PrecompiledContracts.PrecompiledContract) constructor.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
public static PrecompiledContract getContractForAddress(DataWord address) {
@@ -216,6 +293,45 @@ public static PrecompiledContract getContractForAddress(DataWord address) {
if (VMConfig.allowTvmCompatibleEvm() && address.equals(blake2FAddr)) {
return blake2F;
}
+ if (VMConfig.allowTvmOsaka() && address.equals(p256VerifyAddr)) {
+ return p256Verify;
+ }
+
+ if (VMConfig.allowTvmFreezeV2()) {
+ if (address.equals(getChainParameterAddr)) {
+ return getChainParameter;
+ }
+ if (address.equals(availableUnfreezeV2SizeAddr)) {
+ return availableUnfreezeV2Size;
+ }
+ if (address.equals(unfreezableBalanceV2Addr)) {
+ return unfreezableBalanceV2;
+ }
+ if (address.equals(expireUnfreezeBalanceV2Addr)) {
+ return expireUnfreezeBalanceV2;
+ }
+ if (address.equals(delegatableResourceAddr)) {
+ return delegatableResource;
+ }
+ if (address.equals(resourceV2Addr)) {
+ return resourceV2;
+ }
+ if (address.equals(checkUnDelegateResourceAddr)) {
+ return checkUnDelegateResource;
+ }
+ if (address.equals(resourceUsageAddr)) {
+ return resourceUsage;
+ }
+ if (address.equals(totalResourceAddr)) {
+ return totalResource;
+ }
+ if (address.equals(totalDelegatedResourceAddr)) {
+ return totalDelegatedResource;
+ }
+ if (address.equals(totalAcquiredResourceAddr)) {
+ return totalAcquiredResource;
+ }
+ }
return null;
}
@@ -233,23 +349,33 @@ private static byte[] encodeRes(byte[] w1, byte[] w2) {
return res;
}
+ private static byte[] encodeMultiRes(byte[]... words) {
+ if (words == null) {
+ return null;
+ }
+ if (words.length == 1) {
+ return words[0];
+ }
+
+ byte[] res = new byte[words.length * 32];
+
+ for (int i = 0; i < words.length; i++) {
+ byte[] word = stripLeadingZeroes(words[i]);
+
+ System.arraycopy(word, 0, res, 32 * (i + 1) - word.length, word.length);
+ }
+
+ return res;
+ }
+
private static byte[] recoverAddrBySign(byte[] sign, byte[] hash) {
- byte v;
- byte[] r;
- byte[] s;
byte[] out = null;
if (ArrayUtils.isEmpty(sign) || sign.length < 65) {
return new byte[0];
}
try {
- r = Arrays.copyOfRange(sign, 0, 32);
- s = Arrays.copyOfRange(sign, 32, 64);
- v = sign[64];
- if (v < 27) {
- v += 27;
- }
-
- SignatureInterface signature = SignUtils.fromComponents(r, s, v,
+ Rsv rsv = Rsv.fromSignature(sign);
+ SignatureInterface signature = SignUtils.fromComponents(rsv.getR(), rsv.getS(), rsv.getV(),
CommonParameter.getInstance().isECKeyCryptoEngine());
if (signature.validateComponents()) {
out = SignUtils.signatureToAddress(hash, signature,
@@ -285,10 +411,32 @@ private static byte[][] extractBytesArray(DataWord[] words, int offset, byte[] d
return bytesArray;
}
+ private static byte[][] extractSigArray(DataWord[] words, int offset, byte[] data) {
+ if (offset > words.length - 1) {
+ return new byte[0][];
+ }
+ int len = words[offset].intValueSafe();
+ byte[][] bytesArray = new byte[len][];
+ for (int i = 0; i < len; i++) {
+ int bytesOffset = words[offset + i + 1].intValueSafe() / WORD_SIZE;
+ bytesArray[i] = extractBytes(data, (bytesOffset + offset + 2) * WORD_SIZE,
+ SIG_LENGTH);
+ }
+ return bytesArray;
+ }
+
private static byte[] extractBytes(byte[] data, int offset, int len) {
return Arrays.copyOfRange(data, offset, offset + len);
}
+ private static boolean isValidAbiEncoding(byte[] data, int headerWords, int itemWords) {
+ if (data == null || data.length % WORD_SIZE != 0) {
+ return false;
+ }
+ long tail = subtractExact(data.length, multiplyExact(headerWords, WORD_SIZE));
+ return tail > 0 && tail % multiplyExact(itemWords, WORD_SIZE) == 0;
+ }
+
public abstract static class PrecompiledContract {
protected static final byte[] DATA_FALSE = new byte[WORD_SIZE];
@@ -497,6 +645,13 @@ public static class ModExp extends PrecompiledContract {
private static final int ARGS_OFFSET = 32 * 3; // addresses length part
+ private static final int UPPER_BOUND = 1024;
+
+ private static final long MIN_ENERGY_TIP7883 = 500L;
+
+ private static final BigInteger MIN_ENERGY_TIP7883_BI =
+ BigInteger.valueOf(MIN_ENERGY_TIP7883);
+
@Override
public long getEnergyForData(byte[] data) {
@@ -508,14 +663,20 @@ public long getEnergyForData(byte[] data) {
int expLen = parseLen(data, 1);
int modLen = parseLen(data, 2);
- byte[] expHighBytes = parseBytes(data, addSafely(ARGS_OFFSET, baseLen), Math.min(expLen, 32));
- long multComplexity = getMultComplexity(Math.max(baseLen, modLen));
+ byte[] expHighBytes = parseBytes(data, addSafely(ARGS_OFFSET, baseLen), min(expLen, 32,
+ VMConfig.disableJavaLangMath()));
+
+ if (VMConfig.allowTvmOsaka()) {
+ return getEnergyTIP7883(baseLen, modLen, expHighBytes, expLen);
+ }
+
+ long multComplexity = getMultComplexity(max(baseLen, modLen, VMConfig.disableJavaLangMath()));
long adjExpLen = getAdjustedExponentLength(expHighBytes, expLen);
// use big numbers to stay safe in case of overflow
BigInteger energy = BigInteger.valueOf(multComplexity)
- .multiply(BigInteger.valueOf(Math.max(adjExpLen, 1)))
+ .multiply(BigInteger.valueOf(max(adjExpLen, 1, VMConfig.disableJavaLangMath())))
.divide(GQUAD_DIVISOR);
return isLessThan(energy, BigInteger.valueOf(Long.MAX_VALUE)) ? energy.longValueExact()
@@ -533,6 +694,11 @@ public Pair execute(byte[] data) {
int expLen = parseLen(data, 1);
int modLen = parseLen(data, 2);
+ if (VMConfig.allowTvmOsaka()
+ && (baseLen > UPPER_BOUND || expLen > UPPER_BOUND || modLen > UPPER_BOUND)) {
+ return Pair.of(false, EMPTY_BYTE_ARRAY);
+ }
+
BigInteger base = parseArg(data, ARGS_OFFSET, baseLen);
BigInteger exp = parseArg(data, addSafely(ARGS_OFFSET, baseLen), expLen);
BigInteger mod = parseArg(data, addSafely(addSafely(ARGS_OFFSET, baseLen), expLen), modLen);
@@ -588,6 +754,66 @@ private long getAdjustedExponentLength(byte[] expHighBytes, long expLen) {
}
}
+ /**
+ * TIP-7883: ModExp gas cost increase.
+ * New pricing formula with higher minimum cost and no divisor.
+ */
+ private long getEnergyTIP7883(int baseLen, int modLen,
+ byte[] expHighBytes, int expLen) {
+ long multComplexity = getMultComplexityTIP7883(baseLen, modLen);
+ long iterCount = getIterationCountTIP7883(expHighBytes, expLen);
+
+ // use big numbers to stay safe in case of overflow
+ BigInteger energy = BigInteger.valueOf(multComplexity)
+ .multiply(BigInteger.valueOf(iterCount));
+
+ if (isLessThan(energy, MIN_ENERGY_TIP7883_BI)) {
+ return MIN_ENERGY_TIP7883;
+ }
+
+ return isLessThan(energy, BigInteger.valueOf(Long.MAX_VALUE)) ? energy.longValueExact()
+ : Long.MAX_VALUE;
+ }
+
+ /**
+ * TIP-7883: New multiplication complexity formula.
+ * Minimal complexity of 16; doubled complexity for base/modulus > 32 bytes.
+ */
+ private long getMultComplexityTIP7883(int baseLen, int modLen) {
+ long maxLength = StrictMathWrapper.max(baseLen, modLen);
+ if (maxLength <= 32) {
+ return 16;
+ }
+ // ceil(maxLength / 8)
+ long words = StrictMathWrapper.floorDiv(StrictMathWrapper.addExact(maxLength, 7L), 8L);
+ return StrictMathWrapper.multiplyExact(2L, StrictMathWrapper.multiplyExact(words, words));
+ }
+
+ /**
+ * TIP-7883: New iteration count formula.
+ * Multiplier for exponents > 32 bytes increased from 8 to 16.
+ */
+ private long getIterationCountTIP7883(byte[] expHighBytes, long expLen) {
+ int leadingZeros = numberOfLeadingZeros(expHighBytes);
+ long highestBit = StrictMathWrapper.subtractExact(
+ StrictMathWrapper.multiplyExact(8L, expHighBytes.length), leadingZeros);
+
+ if (highestBit > 0) {
+ highestBit = StrictMathWrapper.subtractExact(highestBit, 1L);
+ }
+
+ long iterCount;
+ if (expLen <= 32) {
+ iterCount = highestBit;
+ } else {
+ iterCount = StrictMathWrapper.addExact(
+ StrictMathWrapper.multiplyExact(16L, StrictMathWrapper.subtractExact(expLen, 32L)),
+ highestBit);
+ }
+
+ return StrictMathWrapper.max(iterCount, 1L);
+ }
+
private int parseLen(byte[] data, int idx) {
byte[] bytes = parseBytes(data, 32 * idx, 32);
return new DataWord(bytes).intValueSafe();
@@ -804,6 +1030,8 @@ public static class ValidateMultiSign extends PrecompiledContract {
private static final int ENGERYPERSIGN = 1500;
private static final int MAX_SIZE = 5;
+ private static final int ABI_HEADER_WORDS = 5;
+ private static final int ABI_ITEM_WORDS = 5;
@Override
@@ -815,6 +1043,10 @@ public long getEnergyForData(byte[] data) {
@Override
public Pair execute(byte[] rawData) {
+ if (VMConfig.allowTvmOsaka()
+ && !isValidAbiEncoding(rawData, ABI_HEADER_WORDS, ABI_ITEM_WORDS)) {
+ return Pair.of(false, EMPTY_BYTE_ARRAY);
+ }
DataWord[] words = DataWord.parseArray(rawData);
byte[] address = words[0].toTronAddress();
int permissionId = words[1].intValueSafe();
@@ -824,8 +1056,15 @@ public Pair execute(byte[] rawData) {
byte[] hash = Sha256Hash.hash(CommonParameter
.getInstance().isECKeyCryptoEngine(), combine);
- byte[][] signatures = extractBytesArray(
- words, words[3].intValueSafe() / WORD_SIZE, rawData);
+ if (VMConfig.allowTvmSelfdestructRestriction()) {
+ int sigArraySize = words[words[3].intValueSafe() / WORD_SIZE].intValueSafe();
+ if (sigArraySize > MAX_SIZE) {
+ return Pair.of(true, DATA_FALSE);
+ }
+ }
+ byte[][] signatures = VMConfig.allowTvmSelfdestructRestriction() ?
+ extractSigArray(words, words[3].intValueSafe() / WORD_SIZE, rawData) :
+ extractBytesArray(words, words[3].intValueSafe() / WORD_SIZE, rawData);
if (signatures.length == 0 || signatures.length > MAX_SIZE) {
return Pair.of(true, DATA_FALSE);
@@ -840,10 +1079,15 @@ public Pair execute(byte[] rawData) {
long totalWeight = 0L;
List executedSignList = new ArrayList<>();
for (byte[] sign : signatures) {
- if (ByteArray.matrixContains(executedSignList, sign)) {
- continue;
- }
byte[] recoveredAddr = recoverAddrBySign(sign, hash);
+
+ sign = merge(recoveredAddr, sign);
+ if (ByteArray.matrixContains(executedSignList, recoveredAddr)) {
+ if (ByteArray.matrixContains(executedSignList, sign)) {
+ continue;
+ }
+ MUtil.checkCPUTime();
+ }
long weight = TransactionCapsule.getWeight(permission, recoveredAddr);
if (weight == 0) {
//incorrect sign
@@ -851,6 +1095,7 @@ public Pair execute(byte[] rawData) {
}
totalWeight += weight;
executedSignList.add(sign);
+ executedSignList.add(recoveredAddr);
}
if (totalWeight >= permission.getThreshold()) {
@@ -858,6 +1103,9 @@ public Pair execute(byte[] rawData) {
}
}
} catch (Throwable t) {
+ if (t instanceof OutOfTimeException) {
+ throw t;
+ }
logger.info("ValidateMultiSign error:{}", t.getMessage());
}
}
@@ -868,11 +1116,15 @@ public Pair execute(byte[] rawData) {
public static class BatchValidateSign extends PrecompiledContract {
private static final ExecutorService workers;
+ private static final String workersName = "validate-sign-contract";
private static final int ENGERYPERSIGN = 1500;
private static final int MAX_SIZE = 16;
+ private static final int ABI_HEADER_WORDS = 5;
+ private static final int ABI_ITEM_WORDS = 6;
static {
- workers = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2 + 1);
+ workers = ExecutorServiceManager.newFixedThreadPool(workersName,
+ Runtime.getRuntime().availableProcessors() / 2 + 1);
}
@Override
@@ -896,10 +1148,24 @@ public Pair execute(byte[] data) {
private Pair doExecute(byte[] data)
throws InterruptedException, ExecutionException {
+ if (VMConfig.allowTvmOsaka()
+ && !isValidAbiEncoding(data, ABI_HEADER_WORDS, ABI_ITEM_WORDS)) {
+ return Pair.of(false, EMPTY_BYTE_ARRAY);
+ }
DataWord[] words = DataWord.parseArray(data);
byte[] hash = words[0].getData();
- byte[][] signatures = extractBytesArray(
- words, words[1].intValueSafe() / WORD_SIZE, data);
+
+ if (VMConfig.allowTvmSelfdestructRestriction()) {
+ int sigArraySize = words[words[1].intValueSafe() / WORD_SIZE].intValueSafe();
+ int addrArraySize = words[words[2].intValueSafe() / WORD_SIZE].intValueSafe();
+ if (sigArraySize > MAX_SIZE || addrArraySize > MAX_SIZE) {
+ return Pair.of(true, DATA_FALSE);
+ }
+ }
+
+ byte[][] signatures = VMConfig.allowTvmSelfdestructRestriction() ?
+ extractSigArray(words, words[1].intValueSafe() / WORD_SIZE, data) :
+ extractBytesArray(words, words[1].intValueSafe() / WORD_SIZE, data);
byte[][] addresses = extractBytes32Array(
words, words[2].intValueSafe() / WORD_SIZE);
int cnt = signatures.length;
@@ -1175,10 +1441,12 @@ public static class VerifyTransferProof extends VerifyProof {
private static final Integer[] SIZE = {2080, 2368, 2464, 2752};
private static final ExecutorService workersInConstantCall;
private static final ExecutorService workersInNonConstantCall;
+ private static final String constantCallName = "verify-transfer-constant-call";
+ private static final String nonConstantCallName = "verify-transfer-non-constant-call";
static {
- workersInConstantCall = Executors.newFixedThreadPool(5);
- workersInNonConstantCall = Executors.newFixedThreadPool(5);
+ workersInConstantCall = ExecutorServiceManager.newFixedThreadPool(constantCallName, 5);
+ workersInNonConstantCall = ExecutorServiceManager.newFixedThreadPool(nonConstantCallName, 5);
}
@Override
@@ -1683,9 +1951,18 @@ public Pair execute(byte[] data) {
byte[] address = new DataWord(data).toTronAddress();
AccountCapsule accountCapsule = this.getDeposit().getAccount(address);
- long tronPower = accountCapsule != null
- ? accountCapsule.getTronPower() / TRX_PRECISION : 0;
- return Pair.of(true, longTo32Bytes(tronPower));
+ long tronPower;
+ if (accountCapsule == null) {
+ tronPower = 0;
+ } else {
+ if (getDeposit().getDynamicPropertiesStore().supportUnfreezeDelay()
+ && getDeposit().getDynamicPropertiesStore().supportAllowNewResourceModel()) {
+ tronPower = accountCapsule.getAllTronPower();
+ } else {
+ tronPower = accountCapsule.getTronPower();
+ }
+ }
+ return Pair.of(true, longTo32Bytes(tronPower / TRX_PRECISION));
}
}
@@ -1744,4 +2021,363 @@ public Pair execute(byte[] data) {
return Pair.of(true, result);
}
}
+
+ public static class GetChainParameter extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ long code = new DataWord(data).longValueSafe();
+
+ long res = ChainParameterEnum.fromCode(code).getAction().apply(
+ getDeposit());
+
+ return Pair.of(true, longTo32Bytes(res));
+ }
+ }
+
+ public static class AvailableUnfreezeV2Size extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ byte[] address = new DataWord(data).toTronAddress();
+
+ long result = FreezeV2Util.queryAvailableUnfreezeV2Size(address, getDeposit());
+ return Pair.of(true, longTo32Bytes(result));
+ }
+ }
+
+ public static class UnfreezableBalanceV2 extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 2 * WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] address = words[0].toTronAddress();
+ long type = words[1].longValueSafe();
+
+ long balance = FreezeV2Util.queryUnfreezableBalanceV2(address, type, getDeposit());
+ return Pair.of(true, longTo32Bytes(balance));
+ }
+ }
+
+ public static class ExpireUnfreezeBalanceV2 extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 2 * WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] address = words[0].toTronAddress();
+ long time = words[1].longValueSafe();
+
+ if (time < 0) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ if (time >= Long.MAX_VALUE / 1_000) {
+ time = Long.MAX_VALUE;
+ } else {
+ time = time * 1_000;
+ }
+
+ long balance = FreezeV2Util.queryExpireUnfreezeBalanceV2(address, time, getDeposit());
+ return Pair.of(true, longTo32Bytes(balance));
+ }
+ }
+
+ public static class DelegatableResource extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 2 * WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] address = words[0].toTronAddress();
+ long type = words[1].longValueSafe();
+
+ long result = FreezeV2Util.queryDelegatableResource(address, type, getDeposit());
+ return Pair.of(true, longTo32Bytes(result));
+ }
+ }
+
+ public static class ResourceV2 extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 3 * WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] target = words[0].toTronAddress();
+ byte[] from = words[1].toTronAddress();
+ long type = words[2].longValueSafe();
+
+ long balance;
+ if (Arrays.equals(from, target)) {
+ balance = FreezeV2Util.queryUnfreezableBalanceV2(from, type, getDeposit());
+ } else {
+ balance = FreezeV2Util.queryResourceV2(from, target, type, getDeposit());
+ }
+ return Pair.of(true, longTo32Bytes(balance));
+ }
+ }
+
+ public static class CheckUnDelegateResource extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 3 * WORD_SIZE) {
+ return Pair.of(true, encodeMultiRes(
+ DataWord.ZERO().getData(), DataWord.ZERO().getData(), DataWord.ZERO().getData()));
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] target = words[0].toTronAddress();
+ long amount = words[1].longValueSafe();
+ long type = words[2].longValueSafe();
+
+ Triple values =
+ FreezeV2Util.checkUndelegateResource(target, amount, type, getDeposit());
+ if (values == null || values.getLeft() == null
+ || values.getMiddle() == null || values.getRight() == null) {
+ return Pair.of(true, encodeMultiRes(
+ DataWord.ZERO().getData(), DataWord.ZERO().getData(), DataWord.ZERO().getData()));
+ }
+
+ return Pair.of(true, encodeMultiRes(longTo32Bytes(values.getLeft()),
+ longTo32Bytes(values.getMiddle()), longTo32Bytes(values.getRight())));
+ }
+ }
+
+ public static class ResourceUsage extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 2 * WORD_SIZE) {
+ return Pair.of(true, encodeRes(DataWord.ZERO().getData(), DataWord.ZERO().getData()));
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] address = words[0].toTronAddress();
+ long type = words[1].longValueSafe();
+
+ Pair values = FreezeV2Util.queryFrozenBalanceUsage(address, type, getDeposit());
+ if (values == null || values.getLeft() == null || values.getRight() == null) {
+ return Pair.of(true, encodeRes(DataWord.ZERO().getData(), DataWord.ZERO().getData()));
+ }
+
+ return Pair.of(true, encodeRes(
+ longTo32Bytes(values.getLeft()), longTo32Bytes(values.getRight())));
+ }
+ }
+
+ public static class TotalResource extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 2 * WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] address = words[0].toTronAddress();
+ long type = words[1].longValueSafe();
+
+ AccountCapsule accountCapsule = getDeposit().getAccount(address);
+ if (accountCapsule == null) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ long totalResource = 0;
+ if (type == 0) {
+ totalResource = accountCapsule.getAllFrozenBalanceForBandwidth();
+ } else if (type == 1) {
+ totalResource = accountCapsule.getAllFrozenBalanceForEnergy();
+ }
+
+ return Pair.of(true, longTo32Bytes(totalResource));
+ }
+ }
+
+ public static class TotalDelegatedResource extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 2 * WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] address = words[0].toTronAddress();
+ long type = words[1].longValueSafe();
+
+ AccountCapsule accountCapsule = getDeposit().getAccount(address);
+ if (accountCapsule == null) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ long delegatedResource = 0;
+ if (type == 0) {
+ delegatedResource = accountCapsule.getTotalDelegatedFrozenBalanceForBandwidth();
+ } else if (type == 1) {
+ delegatedResource = accountCapsule.getTotalDelegatedFrozenBalanceForEnergy();
+ }
+
+ return Pair.of(true, longTo32Bytes(delegatedResource));
+ }
+ }
+
+ public static class TotalAcquiredResource extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 50;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 2 * WORD_SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] address = words[0].toTronAddress();
+ long type = words[1].longValueSafe();
+
+ AccountCapsule accountCapsule = getDeposit().getAccount(address);
+ if (accountCapsule == null) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ long acquiredResource = 0;
+ if (type == 0) {
+ acquiredResource = accountCapsule.getTotalAcquiredDelegatedFrozenBalanceForBandwidth();
+ } else if (type == 1) {
+ acquiredResource = accountCapsule.getTotalAcquiredDelegatedFrozenBalanceForEnergy();
+ }
+
+ return Pair.of(true, longTo32Bytes(acquiredResource));
+ }
+ }
+
+ public static class P256Verify extends PrecompiledContract {
+
+ private static final X9ECParameters CURVE = SECNamedCurves.getByName("secp256r1");
+ private static final ECDomainParameters DOMAIN = new ECDomainParameters(
+ CURVE.getCurve(), CURVE.getG(), CURVE.getN(), CURVE.getH());
+ private static final BigInteger N = CURVE.getN();
+ private static final BigInteger P = CURVE.getCurve().getField().getCharacteristic();
+ private static final int INPUT_LEN = 160;
+ private static final long ENERGY = 6900L;
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return ENERGY;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != INPUT_LEN) {
+ return Pair.of(true, EMPTY_BYTE_ARRAY);
+ }
+ try {
+ byte[] hash = copyOfRange(data, 0, 32);
+ BigInteger r = bytesToBigInteger(copyOfRange(data, 32, 64));
+ BigInteger s = bytesToBigInteger(copyOfRange(data, 64, 96));
+ BigInteger qx = bytesToBigInteger(copyOfRange(data, 96, 128));
+ BigInteger qy = bytesToBigInteger(copyOfRange(data, 128, 160));
+
+ if (r.signum() <= 0 || r.compareTo(N) >= 0
+ || s.signum() <= 0 || s.compareTo(N) >= 0) {
+ return Pair.of(true, EMPTY_BYTE_ARRAY);
+ }
+ if (qx.signum() < 0 || qx.compareTo(P) >= 0
+ || qy.signum() < 0 || qy.compareTo(P) >= 0) {
+ return Pair.of(true, EMPTY_BYTE_ARRAY);
+ }
+ if (qx.signum() == 0 && qy.signum() == 0) {
+ return Pair.of(true, EMPTY_BYTE_ARRAY);
+ }
+
+ ECPoint point = CURVE.getCurve().createPoint(qx, qy);
+ DOMAIN.validatePublicPoint(point);
+
+ ECDSASigner verifier = new ECDSASigner();
+ verifier.init(false, new ECPublicKeyParameters(point, DOMAIN));
+ boolean ok = verifier.verifySignature(hash, r, s);
+ return Pair.of(true, ok ? dataOne() : EMPTY_BYTE_ARRAY);
+ } catch (Exception e) {
+ // Off-curve point: createPoint / validatePublicPoint throw IllegalArgumentException.
+ // Crafted signature: BouncyCastle has a known NPE bug inside verifySignature.
+ // EIP-7951 mandates the precompile never reverts; map any failure to (true, empty).
+ return Pair.of(true, EMPTY_BYTE_ARRAY);
+ }
+ }
+ }
+
}
diff --git a/actuator/src/main/java/org/tron/core/vm/VM.java b/actuator/src/main/java/org/tron/core/vm/VM.java
index 410e7bad311..b1d7b027601 100644
--- a/actuator/src/main/java/org/tron/core/vm/VM.java
+++ b/actuator/src/main/java/org/tron/core/vm/VM.java
@@ -1,5 +1,9 @@
package org.tron.core.vm;
+import static org.tron.core.Constant.DYNAMIC_ENERGY_FACTOR_DECIMAL;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.util.StringUtils;
@@ -12,8 +16,18 @@
@Slf4j(topic = "VM")
public class VM {
+ private static final Set CALL_OPS = ImmutableSet.of(Op.CALL, Op.STATICCALL,
+ Op.DELEGATECALL, Op.CALLCODE, Op.CALLTOKEN);
+
public static void play(Program program, JumpTable jumpTable) {
try {
+ long factor = DYNAMIC_ENERGY_FACTOR_DECIMAL;
+ long energyUsage = 0L;
+
+ if (VMConfig.allowDynamicEnergy()) {
+ factor = program.updateContextContractFactor();
+ }
+
while (!program.isStopped()) {
if (VMConfig.vmTrace()) {
program.saveOpTrace();
@@ -32,7 +46,40 @@ public static void play(Program program, JumpTable jumpTable) {
String opName = Op.getNameOf(op.getOpcode());
/* spend energy before execution */
- program.spendEnergy(op.getEnergyCost(program), opName);
+ long energy = op.getEnergyCost(program);
+ if (VMConfig.allowDynamicEnergy()) {
+ long actualEnergy = energy;
+ // CALL Ops have special calculation on energy.
+ if (CALL_OPS.contains(op.getOpcode())) {
+ actualEnergy = energy
+ - program.getAdjustedCallEnergy().longValueSafe()
+ - program.getCallPenaltyEnergy();
+ }
+ energyUsage += actualEnergy;
+
+ if (factor > DYNAMIC_ENERGY_FACTOR_DECIMAL) {
+ long penalty;
+
+ // CALL Ops have special calculation on energy.
+ if (CALL_OPS.contains(op.getOpcode())) {
+ penalty = program.getCallPenaltyEnergy();
+ } else {
+ penalty = energy * factor / DYNAMIC_ENERGY_FACTOR_DECIMAL - energy;
+ if (penalty < 0) {
+ penalty = 0;
+ }
+ energy += penalty;
+ }
+
+ program.spendEnergyWithPenalty(energy, penalty, opName);
+ } else {
+ program.spendEnergy(energy, opName);
+ }
+
+ } else {
+ program.spendEnergy(energy, opName);
+ }
+
/* check if cpu time out */
program.checkCPUTimeLimit(opName);
@@ -53,10 +100,18 @@ public static void play(Program program, JumpTable jumpTable) {
program.fullTrace();
}
}
+
+ if (VMConfig.allowDynamicEnergy()) {
+ program.addContextContractUsage(energyUsage);
+ }
+
} catch (JVMStackOverFlowException | OutOfTimeException e) {
throw e;
} catch (RuntimeException e) {
- if (StringUtils.isEmpty(e.getMessage())) {
+ // https://openjdk.org/jeps/358
+ // https://bugs.openjdk.org/browse/JDK-8220715
+ // since jdk 14, the NullPointerExceptions message is not empty
+ if (e instanceof NullPointerException || StringUtils.isEmpty(e.getMessage())) {
logger.warn("Unknown Exception occurred, tx id: {}",
Hex.toHexString(program.getRootTransactionId()), e);
program.setRuntimeFailure(new RuntimeException("Unknown Exception"));
diff --git a/actuator/src/main/java/org/tron/core/vm/VMConstant.java b/actuator/src/main/java/org/tron/core/vm/VMConstant.java
index 4e7f6b29e3f..266224a1502 100644
--- a/actuator/src/main/java/org/tron/core/vm/VMConstant.java
+++ b/actuator/src/main/java/org/tron/core/vm/VMConstant.java
@@ -4,12 +4,15 @@ public class VMConstant {
public static final int CONTRACT_NAME_LENGTH = 32;
public static final int MIN_TOKEN_ID = 1_000_000;
+ public static final int SIG_LENGTH = 65;
// Numbers
public static final int ONE_HUNDRED = 100;
public static final int ONE_THOUSAND = 1000;
public static final long SUN_PER_ENERGY = 100;
+ public static final String WITHDRAW_EXPIRE_BALANCE = "WithdrawExpireBalance";
+
private VMConstant() {
}
}
diff --git a/actuator/src/main/java/org/tron/core/vm/VMUtils.java b/actuator/src/main/java/org/tron/core/vm/VMUtils.java
index abdf6c7fe4c..2f469e0579a 100644
--- a/actuator/src/main/java/org/tron/core/vm/VMUtils.java
+++ b/actuator/src/main/java/org/tron/core/vm/VMUtils.java
@@ -2,6 +2,7 @@
import static java.lang.String.format;
import static org.apache.commons.codec.binary.Base64.encodeBase64String;
+import static org.tron.common.math.Maths.addExact;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -12,7 +13,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
-import java.util.Map;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import lombok.extern.slf4j.Slf4j;
@@ -20,6 +20,7 @@
import org.tron.common.utils.ByteUtil;
import org.tron.common.utils.Commons;
import org.tron.common.utils.DecodeUtil;
+import org.tron.core.Constant;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.vm.config.VMConfig;
@@ -33,6 +34,11 @@ public final class VMUtils {
private VMUtils() {
}
+ public static int getAddressSize() {
+ return VMConfig.allowEnergyAdjustment() ?
+ Constant.TRON_ADDRESS_SIZE : Constant.STANDARD_ADDRESS_SIZE;
+ }
+
public static void closeQuietly(Closeable closeable) {
try {
if (closeable != null) {
@@ -164,7 +170,7 @@ public static boolean validateForSmartContract(Repository deposit, byte[] ownerA
"Validate InternalTransfer error, balance is not sufficient.");
}
- Math.addExact(toAccount.getBalance(), amount);
+ addExact(toAccount.getBalance(), amount, VMConfig.disableJavaLangMath());
} catch (ArithmeticException e) {
logger.debug(e.getMessage(), e);
throw new ContractValidateException(e.getMessage());
@@ -225,7 +231,8 @@ public static boolean validateForSmartContract(Repository deposit, byte[] ownerA
ByteArray.toStr(tokenIdWithoutLeadingZero));
if (assetBalance != null) {
try {
- assetBalance = Math.addExact(assetBalance, amount); //check if overflow
+ addExact(assetBalance, amount,
+ VMConfig.disableJavaLangMath()); //check if overflow
} catch (Exception e) {
logger.debug(e.getMessage(), e);
throw new ContractValidateException(e.getMessage());
diff --git a/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java b/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java
index 93781e749a4..881eb861bea 100644
--- a/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java
+++ b/actuator/src/main/java/org/tron/core/vm/config/ConfigLoader.java
@@ -31,6 +31,22 @@ public static void load(StoreFactory storeFactory) {
VMConfig.initAllowTvmCompatibleEvm(ds.getAllowTvmCompatibleEvm());
VMConfig.initAllowHigherLimitForMaxCpuTimeOfOneTx(
ds.getAllowHigherLimitForMaxCpuTimeOfOneTx());
+ VMConfig.initAllowTvmFreezeV2(ds.supportUnfreezeDelay() ? 1 : 0);
+ VMConfig.initAllowOptimizedReturnValueOfChainId(
+ ds.getAllowOptimizedReturnValueOfChainId());
+ VMConfig.initAllowDynamicEnergy(ds.getAllowDynamicEnergy());
+ VMConfig.initDynamicEnergyThreshold(ds.getDynamicEnergyThreshold());
+ VMConfig.initDynamicEnergyIncreaseFactor(ds.getDynamicEnergyIncreaseFactor());
+ VMConfig.initDynamicEnergyMaxFactor(ds.getDynamicEnergyMaxFactor());
+ VMConfig.initAllowTvmShangHai(ds.getAllowTvmShangHai());
+ VMConfig.initAllowEnergyAdjustment(ds.getAllowEnergyAdjustment());
+ VMConfig.initAllowStrictMath(ds.getAllowStrictMath());
+ VMConfig.initAllowTvmCancun(ds.getAllowTvmCancun());
+ VMConfig.initDisableJavaLangMath(ds.getConsensusLogicOptimization());
+ VMConfig.initAllowTvmBlob(ds.getAllowTvmBlob());
+ VMConfig.initAllowTvmSelfdestructRestriction(ds.getAllowTvmSelfdestructRestriction());
+ VMConfig.initAllowTvmOsaka(ds.getAllowTvmOsaka());
+ VMConfig.initAllowHardenResourceCalculation(ds.getAllowHardenResourceCalculation());
}
}
}
diff --git a/actuator/src/main/java/org/tron/core/vm/config/VMConfig.java b/actuator/src/main/java/org/tron/core/vm/config/VMConfig.java
deleted file mode 100644
index aa7ca241df5..00000000000
--- a/actuator/src/main/java/org/tron/core/vm/config/VMConfig.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package org.tron.core.vm.config;
-
-import lombok.Setter;
-import org.tron.common.parameter.CommonParameter;
-
-/**
- * For developer only
- */
-public class VMConfig {
-
- private static boolean vmTraceCompressed = false;
-
- @Setter
- private static boolean vmTrace = false;
-
- private static boolean ALLOW_TVM_TRANSFER_TRC10 = false;
-
- private static boolean ALLOW_TVM_CONSTANTINOPLE = false;
-
- private static boolean ALLOW_MULTI_SIGN = false;
-
- private static boolean ALLOW_TVM_SOLIDITY_059 = false;
-
- private static boolean ALLOW_SHIELDED_TRC20_TRANSACTION = false;
-
- private static boolean ALLOW_TVM_ISTANBUL = false;
-
- private static boolean ALLOW_TVM_FREEZE = false;
-
- private static boolean ALLOW_TVM_VOTE = false;
-
- private static boolean ALLOW_TVM_LONDON = false;
-
- private static boolean ALLOW_TVM_COMPATIBLE_EVM = false;
-
- private static boolean ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX = false;
-
- private VMConfig() {
- }
-
- public static boolean vmTrace() {
- return vmTrace;
- }
-
- public static boolean vmTraceCompressed() {
- return vmTraceCompressed;
- }
-
- public static void initVmHardFork(boolean pass) {
- CommonParameter.ENERGY_LIMIT_HARD_FORK = pass;
- }
-
- public static void initAllowMultiSign(long allow) {
- ALLOW_MULTI_SIGN = allow == 1;
- }
-
- public static void initAllowTvmTransferTrc10(long allow) {
- ALLOW_TVM_TRANSFER_TRC10 = allow == 1;
- }
-
- public static void initAllowTvmConstantinople(long allow) {
- ALLOW_TVM_CONSTANTINOPLE = allow == 1;
- }
-
- public static void initAllowTvmSolidity059(long allow) {
- ALLOW_TVM_SOLIDITY_059 = allow == 1;
- }
-
- public static void initAllowShieldedTRC20Transaction(long allow) {
- ALLOW_SHIELDED_TRC20_TRANSACTION = allow == 1;
- }
-
- public static void initAllowTvmIstanbul(long allow) {
- ALLOW_TVM_ISTANBUL = allow == 1;
- }
-
- public static void initAllowTvmFreeze(long allow) {
- ALLOW_TVM_FREEZE = allow == 1;
- }
-
- public static void initAllowTvmVote(long allow) {
- ALLOW_TVM_VOTE = allow == 1;
- }
-
- public static void initAllowTvmLondon(long allow) {
- ALLOW_TVM_LONDON = allow == 1;
- }
-
- public static void initAllowTvmCompatibleEvm(long allow) {
- ALLOW_TVM_COMPATIBLE_EVM = allow == 1;
- }
-
- public static void initAllowHigherLimitForMaxCpuTimeOfOneTx(long allow) {
- ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX = allow == 1;
- }
-
- public static boolean getEnergyLimitHardFork() {
- return CommonParameter.ENERGY_LIMIT_HARD_FORK;
- }
-
- public static boolean allowTvmTransferTrc10() {
- return ALLOW_TVM_TRANSFER_TRC10;
- }
-
- public static boolean allowTvmConstantinople() {
- return ALLOW_TVM_CONSTANTINOPLE;
- }
-
- public static boolean allowMultiSign() {
- return ALLOW_MULTI_SIGN;
- }
-
- public static boolean allowTvmSolidity059() {
- return ALLOW_TVM_SOLIDITY_059;
- }
-
- public static boolean allowShieldedTRC20Transaction() {
- return ALLOW_SHIELDED_TRC20_TRANSACTION;
- }
-
- public static boolean allowTvmIstanbul() {
- return ALLOW_TVM_ISTANBUL;
- }
-
- public static boolean allowTvmFreeze() {
- return ALLOW_TVM_FREEZE;
- }
-
- public static boolean allowTvmVote() {
- return ALLOW_TVM_VOTE;
- }
-
- public static boolean allowTvmLondon() {
- return ALLOW_TVM_LONDON;
- }
-
- public static boolean allowTvmCompatibleEvm() {
- return ALLOW_TVM_COMPATIBLE_EVM;
- }
-
- public static boolean allowHigherLimitForMaxCpuTimeOfOneTx() {
- return ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX;
- }
-}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/CancelAllUnfreezeV2Processor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/CancelAllUnfreezeV2Processor.java
new file mode 100644
index 00000000000..ec1f4363205
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/CancelAllUnfreezeV2Processor.java
@@ -0,0 +1,98 @@
+package org.tron.core.vm.nativecontract;
+
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.vm.VMConstant;
+import org.tron.core.vm.nativecontract.param.CancelAllUnfreezeV2Param;
+import org.tron.core.vm.repository.Repository;
+import org.tron.protos.Protocol;
+
+@Slf4j(topic = "VMProcessor")
+public class CancelAllUnfreezeV2Processor {
+
+ public void validate(CancelAllUnfreezeV2Param param, Repository repo) throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+ if (Objects.isNull(accountCapsule)) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + NOT_EXIST_STR);
+ }
+ }
+
+ public Map execute(CancelAllUnfreezeV2Param param, Repository repo) throws ContractExeException {
+ Map result = new HashMap<>();
+ byte[] ownerAddress = param.getOwnerAddress();
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddress);
+ long now = repo.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ long withdrawExpireBalance = 0L;
+ for (Protocol.Account.UnFreezeV2 unFreezeV2: ownerCapsule.getUnfrozenV2List()) {
+ if (unFreezeV2.getUnfreezeExpireTime() > now) {
+ String resourceName = unFreezeV2.getType().name();
+ result.put(resourceName, result.getOrDefault(resourceName, 0L) + unFreezeV2.getUnfreezeAmount());
+
+ updateFrozenInfoAndTotalResourceWeight(ownerCapsule, unFreezeV2, repo);
+ } else {
+ // withdraw
+ withdrawExpireBalance += unFreezeV2.getUnfreezeAmount();
+ }
+ }
+ if (withdrawExpireBalance > 0) {
+ ownerCapsule.setBalance(ownerCapsule.getBalance() + withdrawExpireBalance);
+ }
+ ownerCapsule.clearUnfrozenV2();
+
+ repo.updateAccount(ownerCapsule.createDbKey(), ownerCapsule);
+
+ result.put(VMConstant.WITHDRAW_EXPIRE_BALANCE, withdrawExpireBalance);
+ return result;
+ }
+
+ public void updateFrozenInfoAndTotalResourceWeight(
+ AccountCapsule accountCapsule, Protocol.Account.UnFreezeV2 unFreezeV2, Repository repo) {
+ switch (unFreezeV2.getType()) {
+ case BANDWIDTH:
+ long oldNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForBandwidthV2(unFreezeV2.getUnfreezeAmount());
+ long newNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ repo.addTotalNetWeight(newNetWeight - oldNetWeight);
+ break;
+ case ENERGY:
+ long oldEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForEnergyV2(unFreezeV2.getUnfreezeAmount());
+ long newEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ repo.addTotalEnergyWeight(newEnergyWeight - oldEnergyWeight);
+ break;
+ case TRON_POWER:
+ long oldTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ accountCapsule.addFrozenForTronPowerV2(unFreezeV2.getUnfreezeAmount());
+ long newTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ repo.addTotalTronPowerWeight(newTPWeight - oldTPWeight);
+ break;
+ default:
+ // this should never happen
+ break;
+ }
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/DelegateResourceProcessor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/DelegateResourceProcessor.java
new file mode 100644
index 00000000000..d03af04aaf3
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/DelegateResourceProcessor.java
@@ -0,0 +1,192 @@
+package org.tron.core.vm.nativecontract;
+
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.core.vm.utils.FreezeV2Util.getV2EnergyUsage;
+import static org.tron.core.vm.utils.FreezeV2Util.getV2NetUsage;
+
+import com.google.common.primitives.Bytes;
+import com.google.protobuf.ByteString;
+import java.util.Arrays;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.ChainBaseManager;
+import org.tron.core.actuator.ActuatorConstant;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.DelegatedResourceAccountIndexCapsule;
+import org.tron.core.capsule.DelegatedResourceCapsule;
+import org.tron.core.db.BandwidthProcessor;
+import org.tron.core.db.EnergyProcessor;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.DelegatedResourceAccountIndexStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.vm.config.VMConfig;
+import org.tron.core.vm.nativecontract.param.DelegateResourceParam;
+import org.tron.core.vm.repository.Repository;
+import org.tron.protos.Protocol;
+
+@Slf4j(topic = "VMProcessor")
+public class DelegateResourceProcessor {
+
+ public void validate(DelegateResourceParam param, Repository repo) throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+ if (!dynamicStore.supportDR()) {
+ throw new ContractValidateException("No support for resource delegate");
+ }
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddress);
+ if (ownerCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR + readableOwnerAddress + NOT_EXIST_STR);
+ }
+ long delegateBalance = param.getDelegateBalance();
+ if (delegateBalance < TRX_PRECISION) {
+ throw new ContractValidateException("delegateBalance must be greater than or equal to 1 TRX");
+ }
+
+ boolean disableJavaLangMath = VMConfig.disableJavaLangMath();
+ switch (param.getResourceType()) {
+ case BANDWIDTH: {
+ BandwidthProcessor processor = new BandwidthProcessor(ChainBaseManager.getInstance());
+ processor.updateUsageForDelegated(ownerCapsule);
+
+ long netUsage = (long) (ownerCapsule.getNetUsage() * TRX_PRECISION * ((double)
+ (repo.getTotalNetWeight()) / dynamicStore.getTotalNetLimit()));
+
+ long v2NetUsage = getV2NetUsage(ownerCapsule, netUsage, disableJavaLangMath);
+
+ if (ownerCapsule.getFrozenV2BalanceForBandwidth() - v2NetUsage < delegateBalance) {
+ throw new ContractValidateException(
+ "delegateBalance must be less than or equal to available FreezeBandwidthV2 balance");
+ }
+ }
+ break;
+ case ENERGY: {
+ EnergyProcessor processor =
+ new EnergyProcessor(dynamicStore, ChainBaseManager.getInstance().getAccountStore());
+ processor.updateUsage(ownerCapsule);
+
+ long energyUsage = (long) (ownerCapsule.getEnergyUsage() * TRX_PRECISION * ((double)
+ (repo.getTotalEnergyWeight()) / dynamicStore.getTotalEnergyCurrentLimit()));
+
+ long v2EnergyUsage = getV2EnergyUsage(ownerCapsule, energyUsage, disableJavaLangMath);
+
+ if (ownerCapsule.getFrozenV2BalanceForEnergy() - v2EnergyUsage < delegateBalance) {
+ throw new ContractValidateException(
+ "delegateBalance must be less than or equal to available FreezeEnergyV2 balance");
+ }
+ }
+ break;
+ default:
+ throw new ContractValidateException(
+ "Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+
+ byte[] receiverAddress = param.getReceiverAddress();
+
+ if (!DecodeUtil.addressValid(receiverAddress)) {
+ throw new ContractValidateException("Invalid receiverAddress");
+ }
+ if (Arrays.equals(receiverAddress, ownerAddress)) {
+ throw new ContractValidateException(
+ "receiverAddress must not be the same as ownerAddress");
+ }
+ AccountCapsule receiverCapsule = repo.getAccount(receiverAddress);
+ if (receiverCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(receiverAddress);
+ throw new ContractValidateException(
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + NOT_EXIST_STR);
+ }
+ if (receiverCapsule.getType() == Protocol.AccountType.Contract) {
+ throw new ContractValidateException(
+ "Do not allow delegate resources to contract addresses");
+ }
+ }
+
+ public void execute(DelegateResourceParam param, Repository repo) {
+ byte[] ownerAddress = param.getOwnerAddress();
+ AccountCapsule ownerCapsule = repo.getAccount(param.getOwnerAddress());
+ long delegateBalance = param.getDelegateBalance();
+ byte[] receiverAddress = param.getReceiverAddress();
+
+ // delegate resource to receiver
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ delegateResource(ownerAddress, receiverAddress, true,
+ delegateBalance, repo);
+
+ ownerCapsule.addDelegatedFrozenV2BalanceForBandwidth(delegateBalance);
+ ownerCapsule.addFrozenBalanceForBandwidthV2(-delegateBalance);
+ break;
+ case ENERGY:
+ delegateResource(ownerAddress, receiverAddress, false,
+ delegateBalance, repo);
+
+ ownerCapsule.addDelegatedFrozenV2BalanceForEnergy(delegateBalance);
+ ownerCapsule.addFrozenBalanceForEnergyV2(-delegateBalance);
+ break;
+ default:
+ logger.debug("Resource Code Error.");
+ }
+
+ repo.updateAccount(ownerCapsule.createDbKey(), ownerCapsule);
+ }
+
+ private void delegateResource(
+ byte[] ownerAddress,
+ byte[] receiverAddress,
+ boolean isBandwidth,
+ long delegateBalance,
+ Repository repo) {
+ //modify DelegatedResourceStore
+ byte[] key = DelegatedResourceCapsule.createDbKeyV2(ownerAddress, receiverAddress, false);
+ DelegatedResourceCapsule delegatedResourceCapsule = repo.getDelegatedResource(key);
+ if (delegatedResourceCapsule == null) {
+ delegatedResourceCapsule = new DelegatedResourceCapsule(
+ ByteString.copyFrom(ownerAddress),
+ ByteString.copyFrom(receiverAddress));
+ }
+ if (isBandwidth) {
+ delegatedResourceCapsule.addFrozenBalanceForBandwidth(delegateBalance, 0);
+ } else {
+ delegatedResourceCapsule.addFrozenBalanceForEnergy(delegateBalance, 0);
+ }
+
+ //modify DelegatedResourceAccountIndex
+ long now = repo.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ byte[] fromKey = Bytes.concat(
+ DelegatedResourceAccountIndexStore.getV2_FROM_PREFIX(), ownerAddress, receiverAddress);
+ DelegatedResourceAccountIndexCapsule toIndexCapsule =
+ new DelegatedResourceAccountIndexCapsule(ByteString.copyFrom(receiverAddress));
+ toIndexCapsule.setTimestamp(now);
+ repo.updateDelegatedResourceAccountIndex(fromKey, toIndexCapsule);
+
+ byte[] toKey = Bytes.concat(
+ DelegatedResourceAccountIndexStore.getV2_TO_PREFIX(), receiverAddress, ownerAddress);
+ DelegatedResourceAccountIndexCapsule fromIndexCapsule =
+ new DelegatedResourceAccountIndexCapsule(ByteString.copyFrom(ownerAddress));
+ fromIndexCapsule.setTimestamp(now);
+ repo.updateDelegatedResourceAccountIndex(toKey, fromIndexCapsule);
+
+ //update Account for receiver
+ AccountCapsule receiverCapsule = repo.getAccount(receiverAddress);
+ if (isBandwidth) {
+ receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForBandwidth(delegateBalance);
+ } else {
+ receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForEnergy(delegateBalance);
+ }
+ repo.updateDelegatedResource(key, delegatedResourceCapsule);
+ repo.updateAccount(receiverCapsule.createDbKey(), receiverCapsule);
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/FreezeBalanceProcessor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/FreezeBalanceProcessor.java
index 9a1af3c9cec..3088527ace6 100644
--- a/actuator/src/main/java/org/tron/core/vm/nativecontract/FreezeBalanceProcessor.java
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/FreezeBalanceProcessor.java
@@ -30,9 +30,9 @@ public void validate(FreezeBalanceParam param, Repository repo) throws ContractV
if (frozenBalance <= 0) {
throw new ContractValidateException("FrozenBalance must be positive");
} else if (frozenBalance < TRX_PRECISION) {
- throw new ContractValidateException("FrozenBalance must be more than 1TRX");
+ throw new ContractValidateException("FrozenBalance must be greater than or equal to 1 TRX");
} else if (frozenBalance > ownerCapsule.getBalance()) {
- throw new ContractValidateException("FrozenBalance must be less than accountBalance");
+ throw new ContractValidateException("FrozenBalance must be less than or equal to accountBalance");
}
// validate frozen count of owner account
@@ -48,7 +48,7 @@ public void validate(FreezeBalanceParam param, Repository repo) throws ContractV
break;
default:
throw new ContractValidateException(
- "ResourceCode error,valid ResourceCode[BANDWIDTH、ENERGY]");
+ "Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY]");
}
// validate for delegating resource
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/FreezeBalanceV2Processor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/FreezeBalanceV2Processor.java
new file mode 100644
index 00000000000..e7e932194ed
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/FreezeBalanceV2Processor.java
@@ -0,0 +1,106 @@
+package org.tron.core.vm.nativecontract;
+
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.vm.nativecontract.param.FreezeBalanceV2Param;
+import org.tron.core.vm.repository.Repository;
+
+@Slf4j(topic = "VMProcessor")
+public class FreezeBalanceV2Processor {
+
+ public void validate(FreezeBalanceV2Param param, Repository repo) throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddress);
+ if (ownerCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
+ }
+ long frozenBalance = param.getFrozenBalance();
+ if (frozenBalance <= 0) {
+ throw new ContractValidateException("FrozenBalance must be positive");
+ } else if (frozenBalance < TRX_PRECISION) {
+ throw new ContractValidateException("FrozenBalance must be greater than or equal to 1 TRX");
+ } else if (frozenBalance > ownerCapsule.getBalance()) {
+ throw new ContractValidateException(
+ "FrozenBalance must be less than or equal to accountBalance");
+ }
+
+ // validate arg @resourceType
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ case ENERGY:
+ break;
+ case TRON_POWER:
+ if (!repo.getDynamicPropertiesStore().supportAllowNewResourceModel()) {
+ throw new ContractValidateException(
+ "Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ break;
+ default:
+ if (repo.getDynamicPropertiesStore().supportAllowNewResourceModel()) {
+ throw new ContractValidateException(
+ "Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY、TRON_POWER]");
+ } else {
+ throw new ContractValidateException(
+ "Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ }
+ }
+
+ public void execute(FreezeBalanceV2Param param, Repository repo) {
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ long frozenBalance = param.getFrozenBalance();
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+ if (dynamicStore.supportAllowNewResourceModel()
+ && accountCapsule.oldTronPowerIsNotInitialized()) {
+ accountCapsule.initializeOldTronPower();
+ }
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ long oldNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForBandwidthV2(frozenBalance);
+ long newNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ repo.addTotalNetWeight(newNetWeight - oldNetWeight);
+ break;
+ case ENERGY:
+ long oldEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForEnergyV2(frozenBalance);
+ long newEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ repo.addTotalEnergyWeight(newEnergyWeight - oldEnergyWeight);
+ break;
+ case TRON_POWER:
+ long oldTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ accountCapsule.addFrozenForTronPowerV2(frozenBalance);
+ long newTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ repo.addTotalTronPowerWeight(newTPWeight - oldTPWeight);
+ break;
+ default:
+ logger.debug("Resource Code Error.");
+ }
+
+ // deduce balance of owner account
+ long newBalance = accountCapsule.getBalance() - frozenBalance;
+ accountCapsule.setBalance(newBalance);
+ repo.updateAccount(accountCapsule.createDbKey(), accountCapsule);
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/UnDelegateResourceProcessor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnDelegateResourceProcessor.java
new file mode 100644
index 00000000000..99bcecdbd44
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnDelegateResourceProcessor.java
@@ -0,0 +1,211 @@
+package org.tron.core.vm.nativecontract;
+
+import static org.tron.common.math.Maths.min;
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+
+import com.google.common.primitives.Bytes;
+import java.util.Arrays;
+import java.util.Objects;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.ChainBaseManager;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.DelegatedResourceAccountIndexCapsule;
+import org.tron.core.capsule.DelegatedResourceCapsule;
+import org.tron.core.db.BandwidthProcessor;
+import org.tron.core.db.EnergyProcessor;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.DelegatedResourceAccountIndexStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.vm.config.VMConfig;
+import org.tron.core.vm.nativecontract.param.UnDelegateResourceParam;
+import org.tron.core.vm.repository.Repository;
+
+@Slf4j(topic = "VMProcessor")
+public class UnDelegateResourceProcessor {
+
+ public void validate(UnDelegateResourceParam param, Repository repo) throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+ if (!dynamicStore.supportDR()) {
+ throw new ContractValidateException("No support for resource delegate");
+ }
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddress);
+ if (ownerCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
+ }
+
+ byte[] receiverAddress = param.getReceiverAddress();
+ if (!DecodeUtil.addressValid(receiverAddress)) {
+ throw new ContractValidateException("Invalid receiverAddress");
+ }
+ if (Arrays.equals(receiverAddress, ownerAddress)) {
+ throw new ContractValidateException(
+ "receiverAddress must not be the same as ownerAddress");
+ }
+
+ byte[] key = DelegatedResourceCapsule.createDbKeyV2(ownerAddress, receiverAddress, false);
+ DelegatedResourceCapsule delegatedResourceCapsule = repo.getDelegatedResource(key);
+ if (delegatedResourceCapsule == null) {
+ throw new ContractValidateException(
+ "delegated Resource does not exist");
+ }
+
+ long unDelegateBalance = param.getUnDelegateBalance();
+ if (unDelegateBalance <= 0) {
+ throw new ContractValidateException("unDelegateBalance must be more than 0 TRX");
+ }
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ if (delegatedResourceCapsule.getFrozenBalanceForBandwidth() < unDelegateBalance) {
+ throw new ContractValidateException("insufficient delegatedFrozenBalance(BANDWIDTH), request="
+ + unDelegateBalance + ", balance=" + delegatedResourceCapsule.getFrozenBalanceForBandwidth());
+ }
+ break;
+ case ENERGY:
+ if (delegatedResourceCapsule.getFrozenBalanceForEnergy() < unDelegateBalance) {
+ throw new ContractValidateException("insufficient delegateFrozenBalance(ENERGY), request="
+ + unDelegateBalance + ", balance=" + delegatedResourceCapsule.getFrozenBalanceForEnergy());
+ }
+ break;
+ default:
+ throw new ContractValidateException(
+ "Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ }
+
+ public void execute(UnDelegateResourceParam param, Repository repo) {
+ byte[] ownerAddress = param.getOwnerAddress();
+ byte[] receiverAddress = param.getReceiverAddress();
+ long unDelegateBalance = param.getUnDelegateBalance();
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddress);
+ AccountCapsule receiverCapsule = repo.getAccount(receiverAddress);
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+ long now = repo.getHeadSlot();
+
+ long transferUsage = 0;
+ // modify receiver Account
+ if (receiverCapsule != null) {
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ BandwidthProcessor bandwidthProcessor = new BandwidthProcessor(ChainBaseManager.getInstance());
+ bandwidthProcessor.updateUsageForDelegated(receiverCapsule);
+ /* For example, in a scenario where a regular account can be upgraded to a contract
+ account through an interface, the account information will be cleared after the
+ contract suicide, and this account will be converted to a regular account in the future */
+ if (receiverCapsule.getAcquiredDelegatedFrozenV2BalanceForBandwidth()
+ < unDelegateBalance) {
+ // A TVM contract suicide, re-create will produce this situation
+ receiverCapsule.setAcquiredDelegatedFrozenV2BalanceForBandwidth(0);
+ } else {
+ // calculate usage
+ long unDelegateMaxUsage = (long) ((double) unDelegateBalance / TRX_PRECISION
+ * dynamicStore.getTotalNetLimit() / repo.getTotalNetWeight());
+ transferUsage = (long) (receiverCapsule.getNetUsage()
+ * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForBandwidth()));
+ transferUsage = min(unDelegateMaxUsage, transferUsage, VMConfig.disableJavaLangMath());
+
+ receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForBandwidth(-unDelegateBalance);
+ }
+
+ long newNetUsage = receiverCapsule.getNetUsage() - transferUsage;
+ receiverCapsule.setNetUsage(newNetUsage);
+ receiverCapsule.setLatestConsumeTime(now);
+ break;
+ case ENERGY:
+ EnergyProcessor energyProcessor =
+ new EnergyProcessor(dynamicStore, ChainBaseManager.getInstance().getAccountStore());
+ energyProcessor.updateUsage(receiverCapsule);
+
+ if (receiverCapsule.getAcquiredDelegatedFrozenV2BalanceForEnergy()
+ < unDelegateBalance) {
+ // A TVM contract receiver, re-create will produce this situation
+ receiverCapsule.setAcquiredDelegatedFrozenV2BalanceForEnergy(0);
+ } else {
+ // calculate usage
+ long unDelegateMaxUsage = (long) ((double) unDelegateBalance / TRX_PRECISION
+ * dynamicStore.getTotalEnergyCurrentLimit() / repo.getTotalEnergyWeight());
+ transferUsage = (long) (receiverCapsule.getEnergyUsage()
+ * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForEnergy()));
+ transferUsage = min(unDelegateMaxUsage, transferUsage, VMConfig.disableJavaLangMath());
+
+ receiverCapsule.addAcquiredDelegatedFrozenV2BalanceForEnergy(-unDelegateBalance);
+ }
+
+ long newEnergyUsage = receiverCapsule.getEnergyUsage() - transferUsage;
+ receiverCapsule.setEnergyUsage(newEnergyUsage);
+ receiverCapsule.setLatestConsumeTimeForEnergy(now);
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+ repo.updateAccount(receiverCapsule.createDbKey(), receiverCapsule);
+ }
+
+ // modify owner Account
+ byte[] key = DelegatedResourceCapsule.createDbKeyV2(ownerAddress, receiverAddress, false);
+ DelegatedResourceCapsule delegatedResourceCapsule = repo.getDelegatedResource(key);
+ switch (param.getResourceType()) {
+ case BANDWIDTH: {
+ delegatedResourceCapsule.addFrozenBalanceForBandwidth(-unDelegateBalance, 0);
+
+ ownerCapsule.addDelegatedFrozenV2BalanceForBandwidth(-unDelegateBalance);
+ ownerCapsule.addFrozenBalanceForBandwidthV2(unDelegateBalance);
+
+ BandwidthProcessor processor = new BandwidthProcessor(ChainBaseManager.getInstance());
+ if (Objects.nonNull(receiverCapsule) && transferUsage > 0) {
+ processor.unDelegateIncrease(ownerCapsule, receiverCapsule,
+ transferUsage, BANDWIDTH, now);
+ }
+ }
+ break;
+ case ENERGY: {
+ delegatedResourceCapsule.addFrozenBalanceForEnergy(-unDelegateBalance, 0);
+
+ ownerCapsule.addDelegatedFrozenV2BalanceForEnergy(-unDelegateBalance);
+ ownerCapsule.addFrozenBalanceForEnergyV2(unDelegateBalance);
+
+ EnergyProcessor processor =
+ new EnergyProcessor(dynamicStore, ChainBaseManager.getInstance().getAccountStore());
+ if (Objects.nonNull(receiverCapsule) && transferUsage > 0) {
+ processor.unDelegateIncrease(ownerCapsule, receiverCapsule, transferUsage, ENERGY, now);
+ }
+ }
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+
+ if (delegatedResourceCapsule.getFrozenBalanceForBandwidth() == 0
+ && delegatedResourceCapsule.getFrozenBalanceForEnergy() == 0) {
+ //modify DelegatedResourceAccountIndex
+ byte[] fromKey = Bytes.concat(
+ DelegatedResourceAccountIndexStore.getV2_FROM_PREFIX(), ownerAddress, receiverAddress);
+ repo.updateDelegatedResourceAccountIndex(
+ fromKey, new DelegatedResourceAccountIndexCapsule(new byte[0]));
+ byte[] toKey = Bytes.concat(
+ DelegatedResourceAccountIndexStore.getV2_TO_PREFIX(), receiverAddress, ownerAddress);
+ repo.updateDelegatedResourceAccountIndex(
+ toKey, new DelegatedResourceAccountIndexCapsule(new byte[0]));
+ }
+
+ repo.updateDelegatedResource(key, delegatedResourceCapsule);
+ repo.updateAccount(ownerCapsule.createDbKey(), ownerCapsule);
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/UnfreezeBalanceProcessor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnfreezeBalanceProcessor.java
index 0eda888d3ca..53981a22d34 100644
--- a/actuator/src/main/java/org/tron/core/vm/nativecontract/UnfreezeBalanceProcessor.java
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnfreezeBalanceProcessor.java
@@ -65,8 +65,8 @@ public void validate(UnfreezeBalanceParam param, Repository repo)
}
break;
default:
- throw new ContractValidateException("ResourceCode error."
- + "valid ResourceCode[BANDWIDTH、Energy]");
+ throw new ContractValidateException("Unknown ResourceCode, "
+ + "valid ResourceCode[BANDWIDTH、ENERGY]");
}
} else {
switch (param.getResourceType()) {
@@ -95,8 +95,8 @@ public void validate(UnfreezeBalanceParam param, Repository repo)
}
break;
default:
- throw new ContractValidateException("ResourceCode error."
- + "valid ResourceCode[BANDWIDTH、Energy]");
+ throw new ContractValidateException("Unknown ResourceCode, "
+ + "valid ResourceCode[BANDWIDTH、ENERGY]");
}
}
}
@@ -136,10 +136,12 @@ public long execute(UnfreezeBalanceParam param, Repository repo) {
if (receiverCapsule != null) {
switch (param.getResourceType()) {
case BANDWIDTH:
- receiverCapsule.safeAddAcquiredDelegatedFrozenBalanceForBandwidth(-unfreezeBalance);
+ receiverCapsule.safeAddAcquiredDelegatedFrozenBalanceForBandwidth(-unfreezeBalance,
+ VMConfig.disableJavaLangMath());
break;
case ENERGY:
- receiverCapsule.safeAddAcquiredDelegatedFrozenBalanceForEnergy(-unfreezeBalance);
+ receiverCapsule.safeAddAcquiredDelegatedFrozenBalanceForEnergy(-unfreezeBalance,
+ VMConfig.disableJavaLangMath());
break;
default:
//this should never happen
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/UnfreezeBalanceV2Processor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnfreezeBalanceV2Processor.java
new file mode 100644
index 00000000000..af2cbf63a43
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnfreezeBalanceV2Processor.java
@@ -0,0 +1,286 @@
+package org.tron.core.vm.nativecontract;
+
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+import static org.tron.core.config.Parameter.ChainConstant.FROZEN_PERIOD;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+
+import com.google.common.collect.Lists;
+import com.google.protobuf.ByteString;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.actuator.UnfreezeBalanceV2Actuator;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.VotesCapsule;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.vm.config.VMConfig;
+import org.tron.core.vm.nativecontract.param.UnfreezeBalanceV2Param;
+import org.tron.core.vm.repository.Repository;
+import org.tron.core.vm.utils.VoteRewardUtil;
+import org.tron.protos.Protocol;
+import org.tron.protos.contract.Common;
+
+@Slf4j(topic = "VMProcessor")
+public class UnfreezeBalanceV2Processor {
+
+ public void validate(UnfreezeBalanceV2Param param, Repository repo)
+ throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+ if (accountCapsule == null) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
+ }
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ int unfreezingCount = accountCapsule.getUnfreezingV2Count(now);
+ if (UnfreezeBalanceV2Actuator.getUNFREEZE_MAX_TIMES() <= unfreezingCount) {
+ throw new ContractValidateException("Invalid unfreeze operation, unfreezing times is over limit");
+ }
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ // validate frozen balance
+ if (!this.checkExistFrozenBalance(accountCapsule, Common.ResourceCode.BANDWIDTH)) {
+ throw new ContractValidateException("no frozenBalance(BANDWIDTH)");
+ }
+ break;
+ case ENERGY:
+ // validate frozen balance
+ if (!this.checkExistFrozenBalance(accountCapsule, Common.ResourceCode.ENERGY)) {
+ throw new ContractValidateException("no frozenBalance(ENERGY)");
+ }
+ break;
+ case TRON_POWER:
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ if (!this.checkExistFrozenBalance(accountCapsule, Common.ResourceCode.TRON_POWER)) {
+ throw new ContractValidateException("no frozenBalance(TRON_POWER)");
+ }
+ } else {
+ throw new ContractValidateException("Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ break;
+ default:
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ throw new ContractValidateException("Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY、TRON_POWER]");
+ } else {
+ throw new ContractValidateException("Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ }
+
+ if (!checkUnfreezeBalance(accountCapsule, param.getUnfreezeBalance(), param.getResourceType())) {
+ throw new ContractValidateException(
+ "Invalid unfreeze_balance, [" + param.getUnfreezeBalance() + "] is invalid");
+ }
+ }
+
+ private boolean checkUnfreezeBalance(
+ AccountCapsule accountCapsule, long unfreezeBalance, Common.ResourceCode freezeType) {
+ if (unfreezeBalance <= 0) {
+ return false;
+ }
+ long frozenBalance = 0L;
+ List freezeV2List = accountCapsule.getFrozenV2List();
+ for (Protocol.Account.FreezeV2 freezeV2 : freezeV2List) {
+ if (freezeV2.getType().equals(freezeType)) {
+ frozenBalance = freezeV2.getAmount();
+ break;
+ }
+ }
+
+ return unfreezeBalance <= frozenBalance;
+ }
+
+ private boolean checkExistFrozenBalance(AccountCapsule accountCapsule, Common.ResourceCode freezeType) {
+ List frozenV2List = accountCapsule.getFrozenV2List();
+ for (Protocol.Account.FreezeV2 frozenV2 : frozenV2List) {
+ if (frozenV2.getType().equals(freezeType) && frozenV2.getAmount() > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long execute(UnfreezeBalanceV2Param param, Repository repo) {
+ byte[] ownerAddress = param.getOwnerAddress();
+ long unfreezeBalance = param.getUnfreezeBalance();
+ VoteRewardUtil.withdrawReward(ownerAddress, repo);
+
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+ long now = repo.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+
+ long unfreezeExpireBalance = this.unfreezeExpire(accountCapsule, now);
+
+ if (repo.getDynamicPropertiesStore().supportAllowNewResourceModel()
+ && accountCapsule.oldTronPowerIsNotInitialized()) {
+ accountCapsule.initializeOldTronPower();
+ }
+
+ long expireTime = this.calcUnfreezeExpireTime(now, repo);
+ accountCapsule.addUnfrozenV2List(param.getResourceType(), unfreezeBalance, expireTime);
+
+ this.updateTotalResourceWeight(accountCapsule, param.getResourceType(), unfreezeBalance, repo);
+ this.updateVote(accountCapsule, param.getResourceType(), ownerAddress, repo);
+
+ if (repo.getDynamicPropertiesStore().supportAllowNewResourceModel()
+ && !accountCapsule.oldTronPowerIsInvalid()) {
+ accountCapsule.invalidateOldTronPower();
+ }
+
+ repo.updateAccount(accountCapsule.createDbKey(), accountCapsule);
+ return unfreezeExpireBalance;
+ }
+
+ private long unfreezeExpire(AccountCapsule accountCapsule, long now) {
+ long unfreezeBalance = 0L;
+
+ List unFrozenV2List = Lists.newArrayList();
+ unFrozenV2List.addAll(accountCapsule.getUnfrozenV2List());
+ Iterator iterator = unFrozenV2List.iterator();
+
+ while (iterator.hasNext()) {
+ Protocol.Account.UnFreezeV2 next = iterator.next();
+ if (next.getUnfreezeExpireTime() <= now) {
+ unfreezeBalance += next.getUnfreezeAmount();
+ iterator.remove();
+ }
+ }
+
+ accountCapsule.setInstance(
+ accountCapsule.getInstance().toBuilder()
+ .setBalance(accountCapsule.getBalance() + unfreezeBalance)
+ .clearUnfrozenV2()
+ .addAllUnfrozenV2(unFrozenV2List).build()
+ );
+ return unfreezeBalance;
+ }
+
+ private long calcUnfreezeExpireTime(long now, Repository repo) {
+ long unfreezeDelayDays = repo.getDynamicPropertiesStore().getUnfreezeDelayDays();
+
+ return now + unfreezeDelayDays * FROZEN_PERIOD;
+ }
+
+ public void updateTotalResourceWeight(AccountCapsule accountCapsule,
+ Common.ResourceCode freezeType,
+ long unfreezeBalance,
+ Repository repo) {
+ switch (freezeType) {
+ case BANDWIDTH:
+ long oldNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForBandwidthV2(-unfreezeBalance);
+ long newNetWeight = accountCapsule.getFrozenV2BalanceWithDelegated(BANDWIDTH) / TRX_PRECISION;
+ repo.addTotalNetWeight(newNetWeight - oldNetWeight);
+ break;
+ case ENERGY:
+ long oldEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ accountCapsule.addFrozenBalanceForEnergyV2(-unfreezeBalance);
+ long newEnergyWeight = accountCapsule.getFrozenV2BalanceWithDelegated(ENERGY) / TRX_PRECISION;
+ repo.addTotalEnergyWeight(newEnergyWeight - oldEnergyWeight);
+ break;
+ case TRON_POWER:
+ long oldTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ accountCapsule.addFrozenForTronPowerV2(-unfreezeBalance);
+ long newTPWeight = accountCapsule.getTronPowerFrozenV2Balance() / TRX_PRECISION;
+ repo.addTotalTronPowerWeight(newTPWeight - oldTPWeight);
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+ }
+
+ private void updateVote(
+ AccountCapsule accountCapsule,
+ Common.ResourceCode freezeType,
+ byte[] ownerAddress,
+ Repository repo) {
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+
+ if (!VMConfig.allowTvmVote() || accountCapsule.getVotesList().isEmpty()) {
+ return;
+ }
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ if (accountCapsule.oldTronPowerIsInvalid()) {
+ switch (freezeType) {
+ case BANDWIDTH:
+ case ENERGY:
+ // there is no need to change votes
+ return;
+ default:
+ break;
+ }
+ } else {
+ // clear all votes at once when new resource model start
+ VotesCapsule votesCapsule = repo.getVotes(ownerAddress);
+ if (votesCapsule == null) {
+ votesCapsule =
+ new VotesCapsule(ByteString.copyFrom(ownerAddress), accountCapsule.getVotesList());
+ }
+ accountCapsule.clearVotes();
+ votesCapsule.clearNewVotes();
+ repo.updateVotes(ownerAddress, votesCapsule);
+ return;
+ }
+ }
+
+ long totalVote = 0;
+ for (Protocol.Vote vote : accountCapsule.getVotesList()) {
+ totalVote += vote.getVoteCount();
+ }
+ if (totalVote == 0) {
+ return;
+ }
+
+ long ownedTronPower;
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ ownedTronPower = accountCapsule.getAllTronPower();
+ } else {
+ ownedTronPower = accountCapsule.getTronPower();
+ }
+ // tron power is enough to total votes
+ if (ownedTronPower >= totalVote * TRX_PRECISION) {
+ return;
+ }
+
+ VotesCapsule votesCapsule = repo.getVotes(ownerAddress);
+ if (votesCapsule == null) {
+ votesCapsule =
+ new VotesCapsule(ByteString.copyFrom(ownerAddress), accountCapsule.getVotesList());
+ }
+
+ // Update Owner Voting
+ List votesToAdd = new ArrayList<>();
+ for (Protocol.Vote vote : accountCapsule.getVotesList()) {
+ long newVoteCount =
+ (long) ((double) vote.getVoteCount() / totalVote * ownedTronPower / TRX_PRECISION);
+ if (newVoteCount > 0) {
+ votesToAdd.add(
+ Protocol.Vote.newBuilder()
+ .setVoteAddress(vote.getVoteAddress())
+ .setVoteCount(newVoteCount)
+ .build());
+ }
+ }
+ votesCapsule.clearNewVotes();
+ votesCapsule.addAllNewVotes(votesToAdd);
+ repo.updateVotes(ownerAddress, votesCapsule);
+
+ accountCapsule.clearVotes();
+ accountCapsule.addAllVotes(votesToAdd);
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/VoteWitnessProcessor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/VoteWitnessProcessor.java
index ddc28bc2a41..8e17ffe8b13 100644
--- a/actuator/src/main/java/org/tron/core/vm/nativecontract/VoteWitnessProcessor.java
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/VoteWitnessProcessor.java
@@ -8,11 +8,9 @@
import com.google.common.math.LongMath;
import com.google.protobuf.ByteString;
-
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-
import lombok.extern.slf4j.Slf4j;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
@@ -87,7 +85,13 @@ public void execute(VoteWitnessParam param, Repository repo) throws ContractExeE
}
}
- long tronPower = accountCapsule.getTronPower();
+ long tronPower;
+ if (repo.getDynamicPropertiesStore().supportUnfreezeDelay()
+ && repo.getDynamicPropertiesStore().supportAllowNewResourceModel()) {
+ tronPower = accountCapsule.getAllTronPower();
+ } else {
+ tronPower = accountCapsule.getTronPower();
+ }
sum = LongMath.checkedMultiply(sum, TRX_PRECISION);
if (sum > tronPower) {
throw new ContractExeException(
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/WithdrawExpireUnfreezeProcessor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/WithdrawExpireUnfreezeProcessor.java
new file mode 100644
index 00000000000..0bcdb10d46f
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/WithdrawExpireUnfreezeProcessor.java
@@ -0,0 +1,92 @@
+package org.tron.core.vm.nativecontract;
+
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+
+import com.google.common.math.LongMath;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.vm.nativecontract.param.WithdrawExpireUnfreezeParam;
+import org.tron.core.vm.repository.Repository;
+import org.tron.protos.Protocol;
+
+@Slf4j(topic = "VMProcessor")
+public class WithdrawExpireUnfreezeProcessor {
+
+ public void validate(WithdrawExpireUnfreezeParam param, Repository repo) throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+ if (Objects.isNull(accountCapsule)) {
+ String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
+ throw new ContractValidateException(ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + NOT_EXIST_STR);
+ }
+
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ List unfrozenV2List = accountCapsule.getInstance()
+ .getUnfrozenV2List();
+ long totalWithdrawUnfreeze = getTotalWithdrawUnfreeze(unfrozenV2List, now);
+ if (totalWithdrawUnfreeze < 0) {
+ throw new ContractValidateException("no unFreeze balance to withdraw ");
+ }
+ try {
+ LongMath.checkedAdd(accountCapsule.getBalance(), totalWithdrawUnfreeze);
+ } catch (ArithmeticException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+ }
+
+ private long getTotalWithdrawUnfreeze(List unfrozenV2List, long now) {
+ return getTotalWithdrawList(unfrozenV2List, now).stream()
+ .mapToLong(Protocol.Account.UnFreezeV2::getUnfreezeAmount).sum();
+ }
+
+ private List getTotalWithdrawList(List unfrozenV2List, long now) {
+ return unfrozenV2List.stream().filter(unfrozenV2 -> unfrozenV2.getUnfreezeExpireTime() <= now)
+ .collect(Collectors.toList());
+ }
+
+ public long execute(WithdrawExpireUnfreezeParam param, Repository repo) throws ContractExeException {
+ byte[] ownerAddress = param.getOwnerAddress();
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddress);
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ List unfrozenV2List = ownerCapsule.getInstance().getUnfrozenV2List();
+ long totalWithdrawUnfreeze = getTotalWithdrawUnfreeze(unfrozenV2List, now);
+ if (totalWithdrawUnfreeze <= 0) {
+ return 0;
+ }
+ ownerCapsule.setInstance(ownerCapsule.getInstance().toBuilder()
+ .setBalance(ownerCapsule.getBalance() + totalWithdrawUnfreeze)
+ .build());
+ List newUnFreezeList = getRemainWithdrawList(unfrozenV2List, now);
+ ownerCapsule.clearUnfrozenV2();
+ ownerCapsule.addAllUnfrozenV2(newUnFreezeList);
+ repo.updateAccount(ownerCapsule.createDbKey(), ownerCapsule);
+ return totalWithdrawUnfreeze;
+ }
+
+ private List getRemainWithdrawList(List unfrozenV2List, long now) {
+ return unfrozenV2List.stream()
+ .filter(unfrozenV2 -> unfrozenV2.getUnfreezeExpireTime() > now)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/param/CancelAllUnfreezeV2Param.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/CancelAllUnfreezeV2Param.java
new file mode 100644
index 00000000000..f345fa0b344
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/CancelAllUnfreezeV2Param.java
@@ -0,0 +1,14 @@
+package org.tron.core.vm.nativecontract.param;
+
+public class CancelAllUnfreezeV2Param {
+
+ private byte[] ownerAddress;
+
+ public byte[] getOwnerAddress() {
+ return ownerAddress;
+ }
+
+ public void setOwnerAddress(byte[] ownerAddress) {
+ this.ownerAddress = ownerAddress;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/param/DelegateResourceParam.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/DelegateResourceParam.java
new file mode 100644
index 00000000000..f7d897800b5
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/DelegateResourceParam.java
@@ -0,0 +1,46 @@
+package org.tron.core.vm.nativecontract.param;
+
+import org.tron.protos.contract.Common;
+
+public class DelegateResourceParam {
+
+ private byte[] ownerAddress;
+
+ private byte[] receiverAddress;
+
+ private long delegateBalance;
+
+ private Common.ResourceCode resourceType;
+
+ public byte[] getOwnerAddress() {
+ return ownerAddress;
+ }
+
+ public void setOwnerAddress(byte[] ownerAddress) {
+ this.ownerAddress = ownerAddress;
+ }
+
+ public byte[] getReceiverAddress() {
+ return receiverAddress;
+ }
+
+ public void setReceiverAddress(byte[] receiverAddress) {
+ this.receiverAddress = receiverAddress;
+ }
+
+ public long getDelegateBalance() {
+ return delegateBalance;
+ }
+
+ public void setDelegateBalance(long delegateBalance) {
+ this.delegateBalance = delegateBalance;
+ }
+
+ public Common.ResourceCode getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(Common.ResourceCode resourceType) {
+ this.resourceType = resourceType;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/param/FreezeBalanceV2Param.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/FreezeBalanceV2Param.java
new file mode 100644
index 00000000000..8aaf00da975
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/FreezeBalanceV2Param.java
@@ -0,0 +1,36 @@
+package org.tron.core.vm.nativecontract.param;
+
+import org.tron.protos.contract.Common;
+
+public class FreezeBalanceV2Param {
+
+ private byte[] ownerAddress;
+
+ private long frozenBalance;
+
+ private Common.ResourceCode resourceType;
+
+ public byte[] getOwnerAddress() {
+ return ownerAddress;
+ }
+
+ public void setOwnerAddress(byte[] ownerAddress) {
+ this.ownerAddress = ownerAddress;
+ }
+
+ public long getFrozenBalance() {
+ return frozenBalance;
+ }
+
+ public void setFrozenBalance(long frozenBalance) {
+ this.frozenBalance = frozenBalance;
+ }
+
+ public Common.ResourceCode getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(Common.ResourceCode resourceType) {
+ this.resourceType = resourceType;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/param/UnDelegateResourceParam.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/UnDelegateResourceParam.java
new file mode 100644
index 00000000000..7a1c76b3c32
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/UnDelegateResourceParam.java
@@ -0,0 +1,46 @@
+package org.tron.core.vm.nativecontract.param;
+
+import org.tron.protos.contract.Common;
+
+public class UnDelegateResourceParam {
+
+ private byte[] ownerAddress;
+
+ private byte[] receiverAddress;
+
+ private long unDelegateBalance;
+
+ private Common.ResourceCode resourceType;
+
+ public byte[] getOwnerAddress() {
+ return ownerAddress;
+ }
+
+ public void setOwnerAddress(byte[] ownerAddress) {
+ this.ownerAddress = ownerAddress;
+ }
+
+ public byte[] getReceiverAddress() {
+ return receiverAddress;
+ }
+
+ public void setReceiverAddress(byte[] receiverAddress) {
+ this.receiverAddress = receiverAddress;
+ }
+
+ public long getUnDelegateBalance() {
+ return unDelegateBalance;
+ }
+
+ public void setUnDelegateBalance(long unDelegateBalance) {
+ this.unDelegateBalance = unDelegateBalance;
+ }
+
+ public Common.ResourceCode getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(Common.ResourceCode resourceType) {
+ this.resourceType = resourceType;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/param/UnfreezeBalanceV2Param.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/UnfreezeBalanceV2Param.java
new file mode 100644
index 00000000000..81ae0f8d222
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/UnfreezeBalanceV2Param.java
@@ -0,0 +1,36 @@
+package org.tron.core.vm.nativecontract.param;
+
+import org.tron.protos.contract.Common;
+
+public class UnfreezeBalanceV2Param {
+
+ private byte[] ownerAddress;
+
+ private long unfreezeBalance;
+
+ private Common.ResourceCode resourceType;
+
+ public byte[] getOwnerAddress() {
+ return ownerAddress;
+ }
+
+ public void setOwnerAddress(byte[] ownerAddress) {
+ this.ownerAddress = ownerAddress;
+ }
+
+ public long getUnfreezeBalance() {
+ return unfreezeBalance;
+ }
+
+ public void setUnfreezeBalance(long unfreezeBalance) {
+ this.unfreezeBalance = unfreezeBalance;
+ }
+
+ public Common.ResourceCode getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(Common.ResourceCode resourceType) {
+ this.resourceType = resourceType;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/nativecontract/param/WithdrawExpireUnfreezeParam.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/WithdrawExpireUnfreezeParam.java
new file mode 100644
index 00000000000..db3cece4126
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/WithdrawExpireUnfreezeParam.java
@@ -0,0 +1,15 @@
+package org.tron.core.vm.nativecontract.param;
+
+public class WithdrawExpireUnfreezeParam {
+
+ // Account address which want to withdraw its reward
+ private byte[] ownerAddress;
+
+ public byte[] getOwnerAddress() {
+ return ownerAddress;
+ }
+
+ public void setOwnerAddress(byte[] ownerAddress) {
+ this.ownerAddress = ownerAddress;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/program/ContractState.java b/actuator/src/main/java/org/tron/core/vm/program/ContractState.java
index 458d83f5110..c6347b9a072 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/ContractState.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/ContractState.java
@@ -1,11 +1,14 @@
package org.tron.core.vm.program;
+import org.apache.commons.lang3.tuple.Pair;
import org.tron.common.runtime.vm.DataWord;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.AssetIssueCapsule;
import org.tron.core.capsule.BlockCapsule;
import org.tron.core.capsule.BytesCapsule;
import org.tron.core.capsule.ContractCapsule;
+import org.tron.core.capsule.ContractStateCapsule;
+import org.tron.core.capsule.DelegatedResourceAccountIndexCapsule;
import org.tron.core.capsule.DelegatedResourceCapsule;
import org.tron.core.capsule.VotesCapsule;
import org.tron.core.capsule.WitnessCapsule;
@@ -103,11 +106,31 @@ public ContractCapsule getContract(byte[] codeHash) {
return repository.getContract(codeHash);
}
+ @Override
+ public ContractStateCapsule getContractState(byte[] address) {
+ return repository.getContractState(address);
+ }
+
@Override
public void updateContract(byte[] address, ContractCapsule contractCapsule) {
repository.updateContract(address, contractCapsule);
}
+ @Override
+ public void updateContractState(byte[] address, ContractStateCapsule contractStateCapsule) {
+ repository.updateContractState(address, contractStateCapsule);
+ }
+
+ @Override
+ public void putNewContract(byte[] address) {
+ repository.putNewContract(address);
+ }
+
+ @Override
+ public boolean isNewContract(byte[] address) {
+ return repository.isNewContract(address);
+ }
+
@Override
public void updateAccount(byte[] address, AccountCapsule accountCapsule) {
repository.updateAccount(address, accountCapsule);
@@ -181,6 +204,10 @@ public void putContract(Key key, Value value) {
repository.putContract(key, value);
}
+ @Override
+ public void putContractState(Key key, Value value) {
+ repository.putContractState(key, value);
+ }
public void putStorage(Key key, Storage cache) {
repository.putStorage(key, cache);
@@ -207,6 +234,16 @@ public void putDelegation(Key key, Value value) {
repository.putDelegation(key, value);
}
+ @Override
+ public void putDelegatedResourceAccountIndex(Key key, Value value) {
+ repository.putDelegatedResourceAccountIndex(key, value);
+ }
+
+ @Override
+ public void putTransientStorageValue(Key address, Key key, Value value) {
+ repository.putTransientStorageValue(address, key, value);
+ }
+
@Override
public long addTokenBalance(byte[] address, byte[] tokenId, long value) {
return repository.addTokenBalance(address, tokenId, value);
@@ -222,6 +259,21 @@ public long getAccountLeftEnergyFromFreeze(AccountCapsule accountCapsule) {
return repository.getAccountLeftEnergyFromFreeze(accountCapsule);
}
+ @Override
+ public long getAccountEnergyUsage(AccountCapsule accountCapsule) {
+ return repository.getAccountEnergyUsage(accountCapsule);
+ }
+
+ @Override
+ public Pair getAccountEnergyUsageBalanceAndRestoreSeconds(AccountCapsule accountCapsule) {
+ return repository.getAccountEnergyUsageBalanceAndRestoreSeconds(accountCapsule);
+ }
+
+ @Override
+ public Pair getAccountNetUsageBalanceAndRestoreSeconds(AccountCapsule accountCapsule) {
+ return repository.getAccountNetUsageBalanceAndRestoreSeconds(accountCapsule);
+ }
+
@Override
public long calculateGlobalEnergyLimit(AccountCapsule accountCapsule) {
return repository.calculateGlobalEnergyLimit(accountCapsule);
@@ -272,6 +324,16 @@ public BytesCapsule getDelegation(Key key) {
return repository.getDelegation(key);
}
+ @Override
+ public DelegatedResourceAccountIndexCapsule getDelegatedResourceAccountIndex(byte[] key) {
+ return repository.getDelegatedResourceAccountIndex(key);
+ }
+
+ @Override
+ public byte[] getTransientStorageValue(byte[] address, byte[] key) {
+ return repository.getTransientStorageValue(address, key);
+ }
+
@Override
public void updateDynamicProperty(byte[] word, BytesCapsule bytesCapsule) {
repository.updateDynamicProperty(word, bytesCapsule);
@@ -307,6 +369,16 @@ public void updateDelegation(byte[] word, BytesCapsule bytesCapsule) {
repository.updateDelegation(word, bytesCapsule);
}
+ @Override
+ public void updateDelegatedResourceAccountIndex(byte[] word, DelegatedResourceAccountIndexCapsule delegatedResourceAccountIndexCapsule) {
+ repository.updateDelegatedResourceAccountIndex(word, delegatedResourceAccountIndexCapsule);
+ }
+
+ @Override
+ public void updateTransientStorageValue(byte[] address, byte[] key, byte[] value) {
+ repository.updateTransientStorageValue(address, key, value);
+ }
+
@Override
public void putDynamicProperty(Key key, Value value) {
repository.putDynamicProperty(key, value);
@@ -327,6 +399,11 @@ public void addTotalEnergyWeight(long amount) {
repository.addTotalEnergyWeight(amount);
}
+ @Override
+ public void addTotalTronPowerWeight(long amount) {
+ repository.addTotalTronPowerWeight(amount);
+ }
+
@Override
public void saveTotalNetWeight(long totalNetWeight) {
repository.saveTotalNetWeight(totalNetWeight);
@@ -337,6 +414,11 @@ public void saveTotalEnergyWeight(long totalEnergyWeight) {
repository.saveTotalEnergyWeight(totalEnergyWeight);
}
+ @Override
+ public void saveTotalTronPowerWeight(long totalTronPowerWeight) {
+ repository.saveTotalTronPowerWeight(totalTronPowerWeight);
+ }
+
@Override
public long getTotalNetWeight() {
return repository.getTotalNetWeight();
@@ -347,4 +429,19 @@ public long getTotalEnergyWeight() {
return repository.getTotalEnergyWeight();
}
+ @Override
+ public long getTotalTronPowerWeight() {
+ return repository.getTotalTronPowerWeight();
+ }
+
+ @Override
+ public long getHeadSlot() {
+ return repository.getHeadSlot();
+ }
+
+ @Override
+ public long getSlotByTimestampMs(long timestamp) {
+ return repository.getSlotByTimestampMs(timestamp);
+ }
+
}
diff --git a/actuator/src/main/java/org/tron/core/vm/program/Memory.java b/actuator/src/main/java/org/tron/core/vm/program/Memory.java
index e5cbebad2b9..2c43c02e138 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/Memory.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/Memory.java
@@ -1,14 +1,16 @@
package org.tron.core.vm.program;
-import static java.lang.Math.ceil;
-import static java.lang.Math.min;
import static java.lang.String.format;
+import static org.tron.common.math.Maths.addExact;
+import static org.tron.common.math.Maths.ceil;
+import static org.tron.common.math.Maths.min;
import static org.tron.common.utils.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.tron.common.utils.ByteUtil.oneByteToHexString;
import java.util.LinkedList;
import java.util.List;
import org.tron.common.runtime.vm.DataWord;
+import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.program.listener.ProgramListener;
import org.tron.core.vm.program.listener.ProgramListenerAware;
@@ -96,24 +98,24 @@ public void write(int address, byte[] data, int dataSize, boolean limited) {
public void extendAndWrite(int address, int allocSize, byte[] data) {
extend(address, allocSize);
- write(address, data, data.length, false);
+ write(address, data, allocSize, false);
}
public void extend(int address, int size) {
if (size <= 0) {
return;
}
-
- final int newSize = Math.addExact(address, size);
+ final int newSize = addExact(address, size, VMConfig.disableJavaLangMath());
int toAllocate = newSize - internalSize();
if (toAllocate > 0) {
- addChunks((int) ceil((double) toAllocate / CHUNK_SIZE));
+ addChunks((int) ceil((double) toAllocate / CHUNK_SIZE, VMConfig.disableJavaLangMath()));
}
toAllocate = newSize - softSize;
if (toAllocate > 0) {
- toAllocate = (int) ceil((double) toAllocate / WORD_SIZE) * WORD_SIZE;
- softSize = Math.addExact(softSize, toAllocate);
+ toAllocate = (int) ceil((double) toAllocate / WORD_SIZE,
+ VMConfig.disableJavaLangMath()) * WORD_SIZE;
+ softSize = addExact(softSize, toAllocate, VMConfig.disableJavaLangMath());
if (programListener != null) {
programListener.onMemoryExtend(toAllocate);
@@ -181,10 +183,18 @@ public List getChunks() {
return new LinkedList<>(chunks);
}
+ public void copy(int destPos, int srcPos, int size) {
+ if (size <= 0) {
+ return;
+ }
+ byte[] data = read(srcPos, size);
+ write(destPos, data, size, false);
+ }
+
private int captureMax(int chunkIndex, int chunkOffset, int size, byte[] src, int srcPos) {
byte[] chunk = chunks.get(chunkIndex);
- int toCapture = min(size, chunk.length - chunkOffset);
+ int toCapture = min(size, chunk.length - chunkOffset, VMConfig.disableJavaLangMath());
System.arraycopy(src, srcPos, chunk, chunkOffset, toCapture);
return toCapture;
@@ -193,7 +203,7 @@ private int captureMax(int chunkIndex, int chunkOffset, int size, byte[] src, in
private int grabMax(int chunkIndex, int chunkOffset, int size, byte[] dest, int destPos) {
byte[] chunk = chunks.get(chunkIndex);
- int toGrab = min(size, chunk.length - chunkOffset);
+ int toGrab = min(size, chunk.length - chunkOffset, VMConfig.disableJavaLangMath());
System.arraycopy(chunk, chunkOffset, dest, destPos, toGrab);
diff --git a/actuator/src/main/java/org/tron/core/vm/program/Program.java b/actuator/src/main/java/org/tron/core/vm/program/Program.java
index cfb42fcde6a..80d972041dc 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/Program.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/Program.java
@@ -1,14 +1,21 @@
package org.tron.core.vm.program;
-import static java.lang.StrictMath.min;
import static java.lang.String.format;
import static org.apache.commons.lang3.ArrayUtils.EMPTY_BYTE_ARRAY;
import static org.apache.commons.lang3.ArrayUtils.getLength;
import static org.apache.commons.lang3.ArrayUtils.isEmpty;
import static org.apache.commons.lang3.ArrayUtils.isNotEmpty;
import static org.apache.commons.lang3.ArrayUtils.nullToEmpty;
+import static org.tron.common.math.Maths.addExact;
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.min;
+import static org.tron.common.math.Maths.multiplyExact;
import static org.tron.common.utils.ByteUtil.stripLeadingZeroes;
import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+import static org.tron.protos.contract.Common.ResourceCode.TRON_POWER;
+import static org.tron.protos.contract.Common.ResourceCode.UNRECOGNIZED;
import com.google.protobuf.ByteString;
import java.math.BigInteger;
@@ -16,7 +23,11 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.stream.Collectors;
+import lombok.Getter;
+import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.map.LRUMap;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
@@ -31,12 +42,17 @@
import org.tron.common.utils.FastByteComparisons;
import org.tron.common.utils.Utils;
import org.tron.common.utils.WalletUtil;
+import org.tron.core.ChainBaseManager;
+import org.tron.core.Constant;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.BlockCapsule;
import org.tron.core.capsule.ContractCapsule;
+import org.tron.core.capsule.ContractStateCapsule;
import org.tron.core.capsule.DelegatedResourceCapsule;
import org.tron.core.capsule.VotesCapsule;
import org.tron.core.capsule.WitnessCapsule;
+import org.tron.core.db.BandwidthProcessor;
+import org.tron.core.db.EnergyProcessor;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.exception.TronException;
@@ -50,13 +66,25 @@
import org.tron.core.vm.VMConstant;
import org.tron.core.vm.VMUtils;
import org.tron.core.vm.config.VMConfig;
+import org.tron.core.vm.nativecontract.CancelAllUnfreezeV2Processor;
+import org.tron.core.vm.nativecontract.DelegateResourceProcessor;
import org.tron.core.vm.nativecontract.FreezeBalanceProcessor;
+import org.tron.core.vm.nativecontract.FreezeBalanceV2Processor;
+import org.tron.core.vm.nativecontract.UnDelegateResourceProcessor;
import org.tron.core.vm.nativecontract.UnfreezeBalanceProcessor;
+import org.tron.core.vm.nativecontract.UnfreezeBalanceV2Processor;
import org.tron.core.vm.nativecontract.VoteWitnessProcessor;
+import org.tron.core.vm.nativecontract.WithdrawExpireUnfreezeProcessor;
import org.tron.core.vm.nativecontract.WithdrawRewardProcessor;
+import org.tron.core.vm.nativecontract.param.CancelAllUnfreezeV2Param;
+import org.tron.core.vm.nativecontract.param.DelegateResourceParam;
import org.tron.core.vm.nativecontract.param.FreezeBalanceParam;
+import org.tron.core.vm.nativecontract.param.FreezeBalanceV2Param;
+import org.tron.core.vm.nativecontract.param.UnDelegateResourceParam;
import org.tron.core.vm.nativecontract.param.UnfreezeBalanceParam;
+import org.tron.core.vm.nativecontract.param.UnfreezeBalanceV2Param;
import org.tron.core.vm.nativecontract.param.VoteWitnessParam;
+import org.tron.core.vm.nativecontract.param.WithdrawExpireUnfreezeParam;
import org.tron.core.vm.nativecontract.param.WithdrawRewardParam;
import org.tron.core.vm.program.invoke.ProgramInvoke;
import org.tron.core.vm.program.invoke.ProgramInvokeFactory;
@@ -112,6 +140,12 @@ public class Program {
private ProgramPrecompile programPrecompile;
private int contractVersion;
private DataWord adjustedCallEnergy;
+ @Getter
+ @Setter
+ private long contextContractFactor;
+ @Getter
+ @Setter
+ private long callPenaltyEnergy;
public Program(byte[] ops, byte[] codeAddress, ProgramInvoke programInvoke,
InternalTransaction internalTransaction) {
@@ -128,11 +162,13 @@ public Program(byte[] ops, byte[] codeAddress, ProgramInvoke programInvoke,
this.nonce = internalTransaction.getNonce();
}
+ @SuppressWarnings("unused")
static String formatBinData(byte[] binData, int startPC) {
StringBuilder ret = new StringBuilder();
for (int i = 0; i < binData.length; i += 16) {
ret.append(Utils.align("" + Integer.toHexString(startPC + (i)) + ":", ' ', 8, false));
- ret.append(Hex.toHexString(binData, i, min(16, binData.length - i))).append('\n');
+ ret.append(Hex.toHexString(binData, i, min(16, binData.length - i,
+ VMConfig.disableJavaLangMath()))).append('\n');
}
return ret.toString();
}
@@ -244,6 +280,10 @@ public void setLastOp(byte op) {
this.lastOp = op;
}
+ public byte getLastOp() {
+ return this.lastOp;
+ }
+
/**
* Returns the last fully executed OP.
*/
@@ -394,6 +434,10 @@ public byte[] memoryChunk(int offset, int size) {
return memory.read(offset, size);
}
+ public void memoryCopy(int dst, int src, int size) {
+ memory.copy(dst, src, size);
+ }
+
/**
* . Allocates extra memory in the program for a specified size, calculated from a given offset
*
@@ -423,10 +467,11 @@ public void suicide(DataWord obtainerAddress) {
increaseNonce();
- addInternalTx(null, owner, obtainer, balance, null, "suicide", nonce,
- getContractState().getAccount(owner).getAssetMapV2());
+ InternalTransaction internalTx = addInternalTx(null, owner, obtainer, balance, null,
+ "suicide", nonce, getContractState().getAccount(owner).getAssetMapV2());
- if (FastByteComparisons.compareTo(owner, 0, 20, obtainer, 0, 20) == 0) {
+ int ADDRESS_SIZE = VMUtils.getAddressSize();
+ if (FastByteComparisons.compareTo(owner, 0, ADDRESS_SIZE, obtainer, 0, ADDRESS_SIZE) == 0) {
// if owner == obtainer just zeroing account according to Yellow Paper
getContractState().addBalance(owner, -balance);
byte[] blackHoleAddress = getContractState().getBlackHoleAddress();
@@ -457,9 +502,85 @@ public void suicide(DataWord obtainerAddress) {
transferDelegatedResourceToInheritor(owner, obtainer, getContractState());
}
}
+ if (VMConfig.allowTvmFreezeV2()) {
+ byte[] Inheritor =
+ FastByteComparisons.isEqual(owner, obtainer)
+ ? getContractState().getBlackHoleAddress()
+ : obtainer;
+ long expireUnfrozenBalance = transferFrozenV2BalanceToInheritor(owner, Inheritor, getContractState());
+ if (expireUnfrozenBalance > 0 && internalTx != null) {
+ internalTx.setValue(internalTx.getValue() + expireUnfrozenBalance);
+ }
+ }
getResult().addDeleteAccount(this.getContractAddress());
}
+ public void suicide2(DataWord obtainerAddress) {
+
+ byte[] owner = getContextAddress();
+ boolean isNewContract = getContractState().isNewContract(owner);
+ if (isNewContract) {
+ suicide(obtainerAddress);
+ return;
+ }
+
+ byte[] obtainer = obtainerAddress.toTronAddress();
+
+ long balance = getContractState().getBalance(owner);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Transfer to: [{}] heritage: [{}]",
+ Hex.toHexString(obtainer),
+ balance);
+ }
+
+ increaseNonce();
+
+ InternalTransaction internalTx = addInternalTx(null, owner, obtainer, balance, null,
+ "suicide", nonce, getContractState().getAccount(owner).getAssetMapV2());
+
+ if (FastByteComparisons.isEqual(owner, obtainer)) {
+ return;
+ }
+
+ if (VMConfig.allowTvmVote()) {
+ withdrawRewardAndCancelVote(owner, getContractState());
+ balance = getContractState().getBalance(owner);
+ if (internalTx != null && balance != internalTx.getValue()) {
+ internalTx.setValue(balance);
+ }
+ }
+
+ // transfer balance and trc10
+ createAccountIfNotExist(getContractState(), obtainer);
+ try {
+ MUtil.transfer(getContractState(), owner, obtainer, balance);
+ if (VMConfig.allowTvmTransferTrc10()) {
+ MUtil.transferAllToken(getContractState(), owner, obtainer);
+ }
+ } catch (ContractValidateException e) {
+ if (VMConfig.allowTvmConstantinople()) {
+ throw new TransferException(
+ "transfer all token or transfer all trx failed in suicide: %s", e.getMessage());
+ }
+ throw new BytecodeExecutionException("transfer failure");
+ }
+
+ // transfer freeze
+ if (VMConfig.allowTvmFreeze()) {
+ transferDelegatedResourceToInheritor(owner, obtainer, getContractState());
+ }
+
+ // transfer freezeV2
+ if (VMConfig.allowTvmFreezeV2()) {
+ long expireUnfrozenBalance =
+ transferFrozenV2BalanceToInheritor(owner, obtainer, getContractState());
+ if (expireUnfrozenBalance > 0 && internalTx != null) {
+ internalTx.setValue(internalTx.getValue() + expireUnfrozenBalance);
+ }
+ }
+ }
+
public Repository getContractState() {
return this.contractState;
}
@@ -489,6 +610,88 @@ private void transferDelegatedResourceToInheritor(byte[] ownerAddr, byte[] inher
// transfer all kinds of frozen balance to BlackHole
repo.addBalance(inheritorAddr, frozenBalanceForBandwidthOfOwner + frozenBalanceForEnergyOfOwner);
+
+ if (VMConfig.allowTvmSelfdestructRestriction()) {
+ clearOwnerFreeze(ownerCapsule);
+ repo.updateAccount(ownerAddr, ownerCapsule);
+ }
+ }
+
+ private long transferFrozenV2BalanceToInheritor(byte[] ownerAddr, byte[] inheritorAddr, Repository repo) {
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddr);
+ AccountCapsule inheritorCapsule = repo.getAccount(inheritorAddr);
+ long now = repo.getHeadSlot();
+
+ // transfer frozen resource
+ ownerCapsule.getFrozenV2List().stream()
+ .filter(freezeV2 -> freezeV2.getAmount() > 0)
+ .forEach(
+ freezeV2 -> {
+ switch (freezeV2.getType()) {
+ case BANDWIDTH:
+ inheritorCapsule.addFrozenBalanceForBandwidthV2(freezeV2.getAmount());
+ break;
+ case ENERGY:
+ inheritorCapsule.addFrozenBalanceForEnergyV2(freezeV2.getAmount());
+ break;
+ case TRON_POWER:
+ inheritorCapsule.addFrozenForTronPowerV2(freezeV2.getAmount());
+ break;
+ }
+ });
+
+ // merge usage
+ BandwidthProcessor bandwidthProcessor = new BandwidthProcessor(ChainBaseManager.getInstance());
+ bandwidthProcessor.updateUsageForDelegated(ownerCapsule);
+ ownerCapsule.setLatestConsumeTime(now);
+ if (ownerCapsule.getNetUsage() > 0) {
+ bandwidthProcessor.unDelegateIncrease(inheritorCapsule, ownerCapsule,
+ ownerCapsule.getNetUsage(), BANDWIDTH, now);
+ }
+
+ EnergyProcessor energyProcessor =
+ new EnergyProcessor(
+ repo.getDynamicPropertiesStore(), ChainBaseManager.getInstance().getAccountStore());
+ energyProcessor.updateUsage(ownerCapsule);
+ ownerCapsule.setLatestConsumeTimeForEnergy(now);
+ if (ownerCapsule.getEnergyUsage() > 0) {
+ energyProcessor.unDelegateIncrease(inheritorCapsule, ownerCapsule,
+ ownerCapsule.getEnergyUsage(), ENERGY, now);
+ }
+
+ // withdraw expire unfrozen balance
+ long nowTimestamp = repo.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ long expireUnfrozenBalance =
+ ownerCapsule.getUnfrozenV2List().stream()
+ .filter(
+ unFreezeV2 ->
+ unFreezeV2.getUnfreezeAmount() > 0 && unFreezeV2.getUnfreezeExpireTime() <= nowTimestamp)
+ .mapToLong(Protocol.Account.UnFreezeV2::getUnfreezeAmount)
+ .sum();
+ if (expireUnfrozenBalance > 0) {
+ inheritorCapsule.setBalance(inheritorCapsule.getBalance() + expireUnfrozenBalance);
+ increaseNonce();
+ addInternalTx(null, ownerAddr, inheritorAddr, expireUnfrozenBalance, null,
+ "withdrawExpireUnfreezeWhileSuiciding", nonce, null);
+ }
+ clearOwnerFreezeV2(ownerCapsule);
+ repo.updateAccount(ownerCapsule.createDbKey(), ownerCapsule);
+ repo.updateAccount(inheritorCapsule.createDbKey(), inheritorCapsule);
+ return expireUnfrozenBalance;
+ }
+
+ private void clearOwnerFreeze(AccountCapsule ownerCapsule) {
+ ownerCapsule.setFrozenForBandwidth(0, 0);
+ ownerCapsule.setFrozenForEnergy(0, 0);
+ }
+
+ private void clearOwnerFreezeV2(AccountCapsule ownerCapsule) {
+ ownerCapsule.clearFrozenV2();
+ ownerCapsule.setNetUsage(0);
+ ownerCapsule.setNewWindowSize(BANDWIDTH, 0);
+ ownerCapsule.setEnergyUsage(0);
+ ownerCapsule.setNewWindowSize(ENERGY, 0);
+ ownerCapsule.clearUnfrozenV2();
}
private void withdrawRewardAndCancelVote(byte[] owner, Repository repo) {
@@ -504,13 +707,14 @@ private void withdrawRewardAndCancelVote(byte[] owner, Repository repo) {
votesCapsule.clearNewVotes();
}
ownerCapsule.clearVotes();
+ ownerCapsule.setOldTronPower(0);
repo.updateVotes(owner, votesCapsule);
}
try {
long balance = ownerCapsule.getBalance();
long allowance = ownerCapsule.getAllowance();
ownerCapsule.setInstance(ownerCapsule.getInstance().toBuilder()
- .setBalance(Math.addExact(balance, allowance))
+ .setBalance(addExact(balance, allowance, VMConfig.disableJavaLangMath()))
.setAllowance(0)
.setLatestWithdrawTime(getTimestamp().longValue() * 1000)
.build());
@@ -523,9 +727,13 @@ private void withdrawRewardAndCancelVote(byte[] owner, Repository repo) {
public boolean canSuicide() {
byte[] owner = getContextAddress();
AccountCapsule accountCapsule = getContractState().getAccount(owner);
- return !VMConfig.allowTvmFreeze()
+
+ boolean freezeCheck = !VMConfig.allowTvmFreeze()
|| (accountCapsule.getDelegatedFrozenBalanceForBandwidth() == 0
&& accountCapsule.getDelegatedFrozenBalanceForEnergy() == 0);
+
+ boolean freezeV2Check = freezeV2Check(accountCapsule);
+ return freezeCheck && freezeV2Check;
// boolean voteCheck = !VMConfig.allowTvmVote()
// || (accountCapsule.getVotesList().size() == 0
// && VoteRewardUtil.queryReward(owner, getContractState()) == 0
@@ -534,6 +742,56 @@ public boolean canSuicide() {
// return freezeCheck && voteCheck;
}
+ public boolean canSuicide2() {
+ byte[] owner = getContextAddress();
+ AccountCapsule accountCapsule = getContractState().getAccount(owner);
+
+ return freezeV1Check(accountCapsule) && freezeV2Check(accountCapsule);
+ }
+
+ private boolean freezeV1Check(AccountCapsule accountCapsule) {
+ if (!VMConfig.allowTvmFreeze()) {
+ return true;
+ }
+
+ // check freeze
+ long now = getContractState().getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ // bandwidth
+ if (accountCapsule.getFrozenCount() > 0
+ && accountCapsule.getFrozenList().stream()
+ .anyMatch(frozen -> frozen.getExpireTime() > now)) {
+ return false;
+ }
+ // energy
+ Protocol.Account.Frozen frozenEnergy =
+ accountCapsule.getAccountResource().getFrozenBalanceForEnergy();
+ if (frozenEnergy.getFrozenBalance() > 0 && frozenEnergy.getExpireTime() > now) {
+ return false;
+ }
+
+ // check delegate
+ return accountCapsule.getDelegatedFrozenBalanceForBandwidth() == 0
+ && accountCapsule.getDelegatedFrozenBalanceForEnergy() == 0;
+ }
+
+ private boolean freezeV2Check(AccountCapsule accountCapsule) {
+ if (!VMConfig.allowTvmFreezeV2()) {
+ return true;
+ }
+ long now = getContractState().getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+
+ boolean isDelegatedResourceEmpty =
+ accountCapsule.getDelegatedFrozenV2BalanceForBandwidth() == 0
+ && accountCapsule.getDelegatedFrozenV2BalanceForEnergy() == 0;
+ boolean isUnFrozenV2ListEmpty =
+ CollectionUtils.isEmpty(
+ accountCapsule.getUnfrozenV2List().stream()
+ .filter(unFreezeV2 -> unFreezeV2.getUnfreezeExpireTime() > now)
+ .collect(Collectors.toList()));
+
+ return isDelegatedResourceEmpty && isUnFrozenV2ListEmpty;
+ }
+
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
public void createContract(DataWord value, DataWord memStart, DataWord memSize) {
returnDataBuffer = null; // reset return buffer right before the call
@@ -970,6 +1228,16 @@ public void spendEnergy(long energyValue, String opName) {
getResult().spendEnergy(energyValue);
}
+ public void spendEnergyWithPenalty(long total, long penalty, String opName) {
+ if (getEnergylimitLeftLong() < total) {
+ throw new OutOfEnergyException(
+ "Not enough energy for '%s' operation executing: curInvokeEnergyLimit[%d],"
+ + " curOpEnergy[%d], penaltyEnergy[%d], usedEnergy[%d]",
+ opName, invoke.getEnergyLimit(), total - penalty, penalty, getResult().getEnergyUsed());
+ }
+ getResult().spendEnergyWithPenalty(total, penalty);
+ }
+
public void checkCPUTimeLimit(String opName) {
if (CommonParameter.getInstance().isDebug()) {
@@ -1076,7 +1344,8 @@ public DataWord getContractAddress() {
public DataWord getBlockHash(int index) {
if (index < this.getNumber().longValue()
- && index >= Math.max(256, this.getNumber().longValue()) - 256) {
+ && index >= max(256, this.getNumber().longValue(),
+ VMConfig.disableJavaLangMath()) - 256) {
BlockCapsule blockCapsule = contractState.getBlockByNum(index);
@@ -1121,7 +1390,7 @@ public DataWord getCallerAddress() {
public DataWord getChainId() {
byte[] chainId = getContractState().getBlockByNum(0).getBlockId().getBytes();
- if (VMConfig.allowTvmCompatibleEvm()) {
+ if (VMConfig.allowTvmCompatibleEvm() || VMConfig.allowOptimizedReturnValueOfChainId()) {
chainId = Arrays.copyOfRange(chainId, chainId.length - 4, chainId.length);
}
return new DataWord(chainId).clone();
@@ -1389,7 +1658,6 @@ public void callToPrecompiledAddress(MessageCall msg,
return;
}
-
Repository deposit = getContractState().newRepositoryChild();
byte[] senderAddress = getContextAddress();
@@ -1478,7 +1746,11 @@ public void callToPrecompiledAddress(MessageCall msg,
}
}
- this.memorySave(msg.getOutDataOffs().intValue(), out.getRight());
+ if (VMConfig.allowTvmSelfdestructRestriction()) {
+ this.memorySave(msg.getOutDataOffs().intValueSafe(), msg.getOutDataSize().intValueSafe(), out.getRight());
+ } else {
+ this.memorySave(msg.getOutDataOffs().intValue(), out.getRight());
+ }
}
}
@@ -1659,9 +1931,9 @@ public boolean freeze(DataWord receiverAddress, DataWord frozenBalance, DataWord
repository.commit();
return true;
} catch (ContractValidateException e) {
- logger.error("TVM Freeze: validate failure. Reason: {}", e.getMessage());
+ logger.warn("TVM Freeze: validate failure. Reason: {}", e.getMessage());
} catch (ArithmeticException e) {
- logger.error("TVM Freeze: frozenBalance out of long range.");
+ logger.warn("TVM Freeze: frozenBalance out of long range.");
}
if (internalTx != null) {
internalTx.reject();
@@ -1692,7 +1964,7 @@ public boolean unfreeze(DataWord receiverAddress, DataWord resourceType) {
}
return true;
} catch (ContractValidateException e) {
- logger.error("TVM Unfreeze: validate failure. Reason: {}", e.getMessage());
+ logger.warn("TVM Unfreeze: validate failure. Reason: {}", e.getMessage());
}
if (internalTx != null) {
internalTx.reject();
@@ -1734,14 +2006,241 @@ public long freezeExpireTime(DataWord targetAddress, DataWord resourceType) {
return 0;
}
+ public boolean freezeBalanceV2(DataWord frozenBalance, DataWord resourceType) {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, owner,
+ frozenBalance.longValue(), null,
+ "freezeBalanceV2For" + convertResourceToString(resourceType), nonce, null);
+
+ try {
+ FreezeBalanceV2Param param = new FreezeBalanceV2Param();
+ param.setOwnerAddress(owner);
+ param.setResourceType(parseResourceCodeV2(resourceType));
+ param.setFrozenBalance(frozenBalance.sValue().longValueExact());
+
+ FreezeBalanceV2Processor processor = new FreezeBalanceV2Processor();
+ processor.validate(param, repository);
+ processor.execute(param, repository);
+ repository.commit();
+ return true;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM FreezeBalanceV2: validate failure. Reason: {}", e.getMessage());
+ } catch (ArithmeticException e) {
+ logger.warn("TVM FreezeBalanceV2: frozenBalance out of long range.");
+ }
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+ return false;
+ }
+
+ public boolean unfreezeBalanceV2(DataWord unfreezeBalance, DataWord resourceType) {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, owner,
+ unfreezeBalance.longValue(), null,
+ "unfreezeBalanceV2For" + convertResourceToString(resourceType), nonce, null);
+
+ try {
+ UnfreezeBalanceV2Param param = new UnfreezeBalanceV2Param();
+ param.setOwnerAddress(owner);
+ param.setUnfreezeBalance(unfreezeBalance.sValue().longValueExact());
+ param.setResourceType(parseResourceCodeV2(resourceType));
+
+ UnfreezeBalanceV2Processor processor = new UnfreezeBalanceV2Processor();
+ processor.validate(param, repository);
+ long unfreezeExpireBalance = processor.execute(param, repository);
+ repository.commit();
+ if (unfreezeExpireBalance > 0) {
+ increaseNonce();
+ addInternalTx(null, owner, owner, unfreezeExpireBalance, null,
+ "withdrawExpireUnfreezeWhileUnfreezing", nonce, null);
+ }
+ return true;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM UnfreezeBalanceV2: validate failure. Reason: {}", e.getMessage());
+ } catch (ArithmeticException e) {
+ logger.warn("TVM UnfreezeBalanceV2: balance out of long range.");
+ }
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+ return false;
+ }
+
+ public long withdrawExpireUnfreeze() {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, owner, 0, null,
+ "withdrawExpireUnfreeze", nonce, null);
+
+ try {
+ WithdrawExpireUnfreezeParam param = new WithdrawExpireUnfreezeParam();
+ param.setOwnerAddress(owner);
+
+ WithdrawExpireUnfreezeProcessor processor = new WithdrawExpireUnfreezeProcessor();
+ processor.validate(param, repository);
+ long expireUnfreezeBalance = processor.execute(param, repository);
+ repository.commit();
+ if (internalTx != null) {
+ internalTx.setValue(expireUnfreezeBalance);
+ }
+ return expireUnfreezeBalance;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM WithdrawExpireUnfreeze: validate failure. Reason: {}", e.getMessage());
+ } catch (ContractExeException e) {
+ logger.warn("TVM WithdrawExpireUnfreeze: execute failure. Reason: {}", e.getMessage());
+ }
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+ return 0;
+ }
+
+ public boolean cancelAllUnfreezeV2Action() {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, owner, 0, null,
+ "cancelAllUnfreezeV2", nonce, null);
+
+ try {
+ CancelAllUnfreezeV2Param param = new CancelAllUnfreezeV2Param();
+ param.setOwnerAddress(owner);
+
+ CancelAllUnfreezeV2Processor processor = new CancelAllUnfreezeV2Processor();
+ processor.validate(param, repository);
+ Map result = processor.execute(param, repository);
+ repository.commit();
+
+ if (result.get(VMConstant.WITHDRAW_EXPIRE_BALANCE) > 0) {
+ increaseNonce();
+ addInternalTx(null, owner, owner, result.get(VMConstant.WITHDRAW_EXPIRE_BALANCE), null,
+ "withdrawExpireUnfreezeWhileCanceling", nonce, null);
+ }
+
+ if (internalTx != null && CommonParameter.getInstance().saveCancelAllUnfreezeV2Details) {
+ internalTx.setExtra(String.format("{\"%s\":%d,\"%s\":%d,\"%s\":%d}",
+ BANDWIDTH.name(), result.getOrDefault(BANDWIDTH.name(), 0L),
+ ENERGY.name(), result.getOrDefault(ENERGY.name(), 0L),
+ TRON_POWER.name(), result.getOrDefault(TRON_POWER.name(), 0L)));
+ }
+
+ return true;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM CancelAllUnfreezeV2: validate failure. Reason: {}", e.getMessage());
+ } catch (ContractExeException e) {
+ logger.warn("TVM CancelAllUnfreezeV2: execute failure. Reason: {}", e.getMessage());
+ }
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+ return false;
+ }
+
+ public boolean delegateResource(
+ DataWord receiverAddress, DataWord delegateBalance, DataWord resourceType) {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+ byte[] receiver = receiverAddress.toTronAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, receiver,
+ delegateBalance.longValue(), null,
+ "delegateResourceOf" + convertResourceToString(resourceType), nonce, null);
+
+ try {
+ DelegateResourceParam param = new DelegateResourceParam();
+ param.setOwnerAddress(owner);
+ param.setReceiverAddress(receiver);
+ param.setDelegateBalance(delegateBalance.sValue().longValueExact());
+ param.setResourceType(parseResourceCodeV2(resourceType));
+
+ DelegateResourceProcessor processor = new DelegateResourceProcessor();
+ processor.validate(param, repository);
+ processor.execute(param, repository);
+ repository.commit();
+ return true;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM DelegateResource: validate failure. Reason: {}", e.getMessage());
+ } catch (ArithmeticException e) {
+ logger.warn("TVM DelegateResource: balance out of long range.");
+ }
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+ return false;
+ }
+
+ public boolean unDelegateResource(
+ DataWord receiverAddress, DataWord unDelegateBalance, DataWord resourceType) {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+ byte[] receiver = receiverAddress.toTronAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, receiver,
+ unDelegateBalance.longValue(), null,
+ "unDelegateResourceOf" + convertResourceToString(resourceType), nonce, null);
+
+ try {
+ UnDelegateResourceParam param = new UnDelegateResourceParam();
+ param.setOwnerAddress(owner);
+ param.setReceiverAddress(receiver);
+ param.setUnDelegateBalance(unDelegateBalance.sValue().longValueExact());
+ param.setResourceType(parseResourceCodeV2(resourceType));
+
+ UnDelegateResourceProcessor processor = new UnDelegateResourceProcessor();
+ processor.validate(param, repository);
+ processor.execute(param, repository);
+ repository.commit();
+ return true;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM UnDelegateResource: validate failure. Reason: {}", e.getMessage());
+ } catch (ArithmeticException e) {
+ logger.warn("TVM UnDelegateResource: balance out of long range.");
+ }
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+ return false;
+ }
+
private Common.ResourceCode parseResourceCode(DataWord resourceType) {
switch (resourceType.intValue()) {
case 0:
- return Common.ResourceCode.BANDWIDTH;
+ return BANDWIDTH;
case 1:
- return Common.ResourceCode.ENERGY;
+ return ENERGY;
default:
- return Common.ResourceCode.UNRECOGNIZED;
+ return UNRECOGNIZED;
+ }
+ }
+
+ private Common.ResourceCode parseResourceCodeV2(DataWord resourceType) {
+ try {
+ byte type = resourceType.sValue().byteValueExact();
+ switch (type) {
+ case 0:
+ return BANDWIDTH;
+ case 1:
+ return ENERGY;
+ case 2:
+ return TRON_POWER;
+ default:
+ return UNRECOGNIZED;
+ }
+ } catch (ArithmeticException e) {
+ logger.warn("TVM ParseResourceCodeV2: invalid resource code: {}", resourceType.sValue());
+ return Common.ResourceCode.UNRECOGNIZED;
}
}
@@ -1751,6 +2250,8 @@ private String convertResourceToString(DataWord resourceType) {
return "Bandwidth";
case 1:
return "Energy";
+ case 2:
+ return "TronPower";
default:
return "UnknownType";
}
@@ -1781,11 +2282,12 @@ public boolean voteWitness(int witnessArrayOffset, int witnessArrayLength,
try {
VoteWitnessParam param = new VoteWitnessParam();
param.setVoterAddress(owner);
-
- byte[] witnessArrayData = memoryChunk(Math.addExact(witnessArrayOffset, DataWord.WORD_SIZE),
- Math.multiplyExact(witnessArrayLength, DataWord.WORD_SIZE));
- byte[] amountArrayData = memoryChunk(Math.addExact(amountArrayOffset, DataWord.WORD_SIZE),
- Math.multiplyExact(amountArrayLength, DataWord.WORD_SIZE));
+ byte[] witnessArrayData = memoryChunk(
+ addExact(witnessArrayOffset, DataWord.WORD_SIZE, VMConfig.disableJavaLangMath()),
+ multiplyExact(witnessArrayLength, DataWord.WORD_SIZE, VMConfig.disableJavaLangMath()));
+ byte[] amountArrayData = memoryChunk(
+ addExact(amountArrayOffset, DataWord.WORD_SIZE, VMConfig.disableJavaLangMath()),
+ multiplyExact(amountArrayLength, DataWord.WORD_SIZE, VMConfig.disableJavaLangMath()));
for (int i = 0; i < witnessArrayLength; i++) {
DataWord witness = new DataWord(Arrays.copyOfRange(witnessArrayData,
@@ -1804,11 +2306,11 @@ public boolean voteWitness(int witnessArrayOffset, int witnessArrayLength,
repository.commit();
return true;
} catch (ContractValidateException e) {
- logger.error("TVM VoteWitness: validate failure. Reason: {}", e.getMessage());
+ logger.warn("TVM VoteWitness: validate failure. Reason: {}", e.getMessage());
} catch (ContractExeException e) {
- logger.error("TVM VoteWitness: execute failure. Reason: {}", e.getMessage());
+ logger.warn("TVM VoteWitness: execute failure. Reason: {}", e.getMessage());
} catch (ArithmeticException e) {
- logger.error("TVM VoteWitness: int or long out of range. caused by: {}", e.getMessage());
+ logger.warn("TVM VoteWitness: int or long out of range. caused by: {}", e.getMessage());
}
if (internalTx != null) {
internalTx.reject();
@@ -1837,9 +2339,9 @@ public long withdrawReward() {
}
return allowance;
} catch (ContractValidateException e) {
- logger.error("TVM WithdrawReward: validate failure. Reason: {}", e.getMessage());
+ logger.warn("TVM WithdrawReward: validate failure. Reason: {}", e.getMessage());
} catch (ContractExeException e) {
- logger.error("TVM WithdrawReward: execute failure. Reason: {}", e.getMessage());
+ logger.warn("TVM WithdrawReward: execute failure. Reason: {}", e.getMessage());
}
if (internalTx != null) {
internalTx.reject();
@@ -1847,6 +2349,39 @@ public long withdrawReward() {
return 0;
}
+ public long updateContextContractFactor() {
+ ContractStateCapsule contractStateCapsule =
+ contractState.getContractState(getContextAddress());
+
+ if (contractStateCapsule == null) {
+ contractStateCapsule = new ContractStateCapsule(
+ contractState.getDynamicPropertiesStore().getCurrentCycleNumber());
+ contractState.updateContractState(getContextAddress(), contractStateCapsule);
+ } else {
+ if (contractStateCapsule.catchUpToCycle(
+ contractState.getDynamicPropertiesStore().getCurrentCycleNumber(),
+ VMConfig.getDynamicEnergyThreshold(),
+ VMConfig.getDynamicEnergyIncreaseFactor(),
+ VMConfig.getDynamicEnergyMaxFactor(),
+ VMConfig.allowStrictMath(),
+ VMConfig.disableJavaLangMath())) {
+ contractState.updateContractState(getContextAddress(), contractStateCapsule
+ );
+ }
+ }
+ contextContractFactor = contractStateCapsule.getEnergyFactor()
+ + Constant.DYNAMIC_ENERGY_FACTOR_DECIMAL;
+ return contextContractFactor;
+ }
+
+ public void addContextContractUsage(long value) {
+ ContractStateCapsule contractStateCapsule =
+ contractState.getContractState(getContextAddress());
+
+ contractStateCapsule.addEnergyUsage(value);
+ contractState.updateContractState(getContextAddress(), contractStateCapsule);
+ }
+
/**
* Denotes problem when executing Ethereum bytecode. From blockchain and peer perspective this is
* quite normal situation and doesn't mean exceptional situation in terms of the program
diff --git a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeImpl.java b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeImpl.java
index 7997aaedcd5..ede20103609 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeImpl.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeImpl.java
@@ -314,7 +314,7 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
- return new Integer(Boolean.valueOf(byTestingSuite).hashCode()
+ return Boolean.valueOf(byTestingSuite).hashCode()
+ Boolean.valueOf(byTransaction).hashCode()
+ address.hashCode()
+ balance.hashCode()
@@ -326,8 +326,7 @@ public int hashCode() {
+ origin.hashCode()
+ prevHash.hashCode()
+ deposit.hashCode()
- + timestamp.hashCode()
- ).hashCode();
+ + timestamp.hashCode();
}
@Override
diff --git a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeMockImpl.java b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeMockImpl.java
index 8674608f22a..567ac72931a 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeMockImpl.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeMockImpl.java
@@ -8,6 +8,7 @@
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.vm.DataWord;
import org.tron.core.capsule.ContractCapsule;
+import org.tron.core.store.StoreFactory;
import org.tron.core.vm.repository.Repository;
import org.tron.core.vm.repository.RepositoryImpl;
import org.tron.protos.Protocol;
@@ -45,7 +46,11 @@ public ProgramInvokeMockImpl() {
}
public ProgramInvokeMockImpl(byte[] op, byte[] opAddress) {
- this.deposit = RepositoryImpl.createRoot(null);
+ this(null, op, opAddress);
+ }
+
+ public ProgramInvokeMockImpl(StoreFactory storeFactory, byte[] op, byte[] opAddress) {
+ this.deposit = RepositoryImpl.createRoot(storeFactory);
this.deposit.createAccount(opAddress, Protocol.AccountType.Normal);
this.deposit.createAccount(opAddress, Protocol.AccountType.Contract);
@@ -205,6 +210,10 @@ public void setOwnerAddress(byte[] ownerAddress) {
this.ownerAddress = Arrays.clone(ownerAddress);
}
+ public void setStaticCall(boolean isStatic) {
+ isStaticCall = isStatic;
+ }
+
@Override
public boolean isStaticCall() {
return isStaticCall;
diff --git a/actuator/src/main/java/org/tron/core/vm/repository/Repository.java b/actuator/src/main/java/org/tron/core/vm/repository/Repository.java
index efbbdac1a05..8f91d59d0b8 100644
--- a/actuator/src/main/java/org/tron/core/vm/repository/Repository.java
+++ b/actuator/src/main/java/org/tron/core/vm/repository/Repository.java
@@ -1,5 +1,6 @@
package org.tron.core.vm.repository;
+import org.apache.commons.lang3.tuple.Pair;
import org.tron.common.runtime.vm.DataWord;
import org.tron.core.capsule.*;
import org.tron.core.store.*;
@@ -38,14 +39,26 @@ public interface Repository {
BytesCapsule getDelegation(Key key);
+ DelegatedResourceAccountIndexCapsule getDelegatedResourceAccountIndex(byte[] key);
+
+ byte[] getTransientStorageValue(byte[] address, byte[] key);
+
void deleteContract(byte[] address);
void createContract(byte[] address, ContractCapsule contractCapsule);
ContractCapsule getContract(byte[] address);
+ ContractStateCapsule getContractState(byte[] address);
+
void updateContract(byte[] address, ContractCapsule contractCapsule);
+ void updateContractState(byte[] address, ContractStateCapsule contractStateCapsule);
+
+ void putNewContract(byte[] address);
+
+ boolean isNewContract(byte[] address);
+
void updateAccount(byte[] address, AccountCapsule accountCapsule);
void updateDynamicProperty(byte[] word, BytesCapsule bytesCapsule);
@@ -62,6 +75,10 @@ public interface Repository {
void updateDelegation(byte[] word, BytesCapsule bytesCapsule);
+ void updateDelegatedResourceAccountIndex(byte[] word, DelegatedResourceAccountIndexCapsule delegatedResourceAccountIndexCapsule);
+
+ void updateTransientStorageValue(byte[] address, byte[] key, byte[] value);
+
void saveCode(byte[] address, byte[] code);
byte[] getCode(byte[] address);
@@ -88,6 +105,8 @@ public interface Repository {
void putContract(Key key, Value value);
+ void putContractState(Key key, Value value);
+
void putStorage(Key key, Storage cache);
void putAccountValue(byte[] address, AccountCapsule accountCapsule);
@@ -100,12 +119,22 @@ public interface Repository {
void putDelegation(Key key, Value value);
+ void putDelegatedResourceAccountIndex(Key key, Value value);
+
+ void putTransientStorageValue(Key address, Key key, Value value);
+
long addTokenBalance(byte[] address, byte[] tokenId, long value);
long getTokenBalance(byte[] address, byte[] tokenId);
long getAccountLeftEnergyFromFreeze(AccountCapsule accountCapsule);
+ long getAccountEnergyUsage(AccountCapsule accountCapsule);
+
+ Pair getAccountEnergyUsageBalanceAndRestoreSeconds(AccountCapsule accountCapsule);
+
+ Pair getAccountNetUsageBalanceAndRestoreSeconds(AccountCapsule accountCapsule);
+
long calculateGlobalEnergyLimit(AccountCapsule accountCapsule);
byte[] getBlackHoleAddress();
@@ -120,12 +149,22 @@ public interface Repository {
void addTotalEnergyWeight(long amount);
+ void addTotalTronPowerWeight(long amount);
+
void saveTotalNetWeight(long totalNetWeight);
void saveTotalEnergyWeight(long totalEnergyWeight);
+ void saveTotalTronPowerWeight(long totalTronPowerWeight);
+
long getTotalNetWeight();
long getTotalEnergyWeight();
+ long getTotalTronPowerWeight();
+
+ long getHeadSlot();
+
+ long getSlotByTimestampMs(long timestamp);
+
}
diff --git a/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java b/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java
index 10c81d3bae1..62e7ce6ec08 100644
--- a/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java
+++ b/actuator/src/main/java/org/tron/core/vm/repository/RepositoryImpl.java
@@ -1,16 +1,24 @@
package org.tron.core.vm.repository;
-import static java.lang.Long.max;
+import static org.tron.common.math.Maths.addExact;
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.round;
import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import com.google.common.collect.HashBasedTable;
import com.google.protobuf.ByteString;
+import java.math.BigInteger;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Optional;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.tuple.Pair;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;
import org.tron.common.crypto.Hash;
+import org.tron.common.math.StrictMathWrapper;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.vm.DataWord;
import org.tron.common.utils.ByteArray;
@@ -28,6 +36,8 @@
import org.tron.core.capsule.BytesCapsule;
import org.tron.core.capsule.CodeCapsule;
import org.tron.core.capsule.ContractCapsule;
+import org.tron.core.capsule.ContractStateCapsule;
+import org.tron.core.capsule.DelegatedResourceAccountIndexCapsule;
import org.tron.core.capsule.DelegatedResourceCapsule;
import org.tron.core.capsule.VotesCapsule;
import org.tron.core.capsule.WitnessCapsule;
@@ -44,7 +54,9 @@
import org.tron.core.store.AssetIssueStore;
import org.tron.core.store.AssetIssueV2Store;
import org.tron.core.store.CodeStore;
+import org.tron.core.store.ContractStateStore;
import org.tron.core.store.ContractStore;
+import org.tron.core.store.DelegatedResourceAccountIndexStore;
import org.tron.core.store.DelegatedResourceStore;
import org.tron.core.store.DelegationStore;
import org.tron.core.store.DynamicPropertiesStore;
@@ -60,17 +72,19 @@
import org.tron.protos.Protocol.AccountType;
import org.tron.protos.Protocol.DelegatedResource;
import org.tron.protos.Protocol.Votes;
+import org.tron.protos.Protocol.DelegatedResourceAccountIndex;
import org.tron.protos.contract.AssetIssueContractOuterClass.AssetIssueContract;
+import org.tron.protos.contract.Common;
+import org.tron.protos.contract.SmartContractOuterClass.ContractState;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract;
@Slf4j(topic = "Repository")
public class RepositoryImpl implements Repository {
private final long precision = Parameter.ChainConstant.PRECISION;
- private final long windowSize = Parameter.ChainConstant.WINDOW_SIZE_MS
- / BLOCK_PRODUCED_INTERVAL;
private static final byte[] TOTAL_NET_WEIGHT = "TOTAL_NET_WEIGHT".getBytes();
private static final byte[] TOTAL_ENERGY_WEIGHT = "TOTAL_ENERGY_WEIGHT".getBytes();
+ private static final byte[] TOTAL_TRON_POWER_WEIGHT = "TOTAL_TRON_POWER_WEIGHT".getBytes();
private StoreFactory storeFactory;
@Getter
@@ -88,6 +102,8 @@ public class RepositoryImpl implements Repository {
@Getter
private ContractStore contractStore;
@Getter
+ private ContractStateStore contractStateStore;
+ @Getter
private StorageRowStore storageRowStore;
@Getter
private BlockStore blockStore;
@@ -103,12 +119,16 @@ public class RepositoryImpl implements Repository {
private VotesStore votesStore;
@Getter
private DelegationStore delegationStore;
+ @Getter
+ private DelegatedResourceAccountIndexStore delegatedResourceAccountIndexStore;
private Repository parent = null;
private final HashMap> accountCache = new HashMap<>();
private final HashMap> codeCache = new HashMap<>();
private final HashMap> contractCache = new HashMap<>();
+ private final HashMap> contractStateCache
+ = new HashMap<>();
private final HashMap storageCache = new HashMap<>();
private final HashMap> assetIssueCache = new HashMap<>();
@@ -116,6 +136,9 @@ public class RepositoryImpl implements Repository {
private final HashMap> delegatedResourceCache = new HashMap<>();
private final HashMap> votesCache = new HashMap<>();
private final HashMap> delegationCache = new HashMap<>();
+ private final HashMap> delegatedResourceAccountIndexCache = new HashMap<>();
+ private final HashBasedTable> transientStorage = HashBasedTable.create();
+ private final HashSet newContractCache = new HashSet<>();
public static void removeLruCache(byte[] address) {
}
@@ -137,6 +160,7 @@ protected void init(StoreFactory storeFactory, RepositoryImpl parent) {
abiStore = manager.getAbiStore();
codeStore = manager.getCodeStore();
contractStore = manager.getContractStore();
+ contractStateStore = manager.getContractStateStore();
assetIssueStore = manager.getAssetIssueStore();
assetIssueV2Store = manager.getAssetIssueV2Store();
storageRowStore = manager.getStorageRowStore();
@@ -147,6 +171,7 @@ protected void init(StoreFactory storeFactory, RepositoryImpl parent) {
delegatedResourceStore = manager.getDelegatedResourceStore();
votesStore = manager.getVotesStore();
delegationStore = manager.getDelegationStore();
+ delegatedResourceAccountIndexStore = manager.getDelegatedResourceAccountIndexStore();
}
this.parent = parent;
}
@@ -164,9 +189,79 @@ public long getAccountLeftEnergyFromFreeze(AccountCapsule accountCapsule) {
long latestConsumeTime = accountCapsule.getAccountResource().getLatestConsumeTimeForEnergy();
long energyLimit = calculateGlobalEnergyLimit(accountCapsule);
- long newEnergyUsage = increase(energyUsage, 0, latestConsumeTime, now);
+ long windowSize = accountCapsule.getWindowSize(Common.ResourceCode.ENERGY);
+
+ long newEnergyUsage = recover(energyUsage, latestConsumeTime, now, windowSize);
+
+ return max(energyLimit - newEnergyUsage, 0, VMConfig.disableJavaLangMath()); // us
+ }
+
+ @Override
+ public long getAccountEnergyUsage(AccountCapsule accountCapsule) {
+ long now = getHeadSlot();
+ long energyUsage = accountCapsule.getEnergyUsage();
+ long latestConsumeTime = accountCapsule.getAccountResource().getLatestConsumeTimeForEnergy();
+
+ long accountWindowSize = accountCapsule.getWindowSize(Common.ResourceCode.ENERGY);
+
+ return recover(energyUsage, latestConsumeTime, now, accountWindowSize);
+ }
+
+ public Pair getAccountEnergyUsageBalanceAndRestoreSeconds(AccountCapsule accountCapsule) {
+ long now = getHeadSlot();
+
+ long energyUsage = accountCapsule.getEnergyUsage();
+ long latestConsumeTime = accountCapsule.getAccountResource().getLatestConsumeTimeForEnergy();
+ long accountWindowSize = accountCapsule.getWindowSize(Common.ResourceCode.ENERGY);
+
+ if (now >= latestConsumeTime + accountWindowSize) {
+ return Pair.of(0L, 0L);
+ }
+
+ long restoreSlots = latestConsumeTime + accountWindowSize - now;
+
+ long newEnergyUsage = recover(energyUsage, latestConsumeTime, now, accountWindowSize);
+
+ long totalEnergyLimit = getDynamicPropertiesStore().getTotalEnergyCurrentLimit();
+ long totalEnergyWeight = getTotalEnergyWeight();
+
+ long balance = usageToBalance(newEnergyUsage, totalEnergyWeight, totalEnergyLimit);
+
+ return Pair.of(balance, restoreSlots * BLOCK_PRODUCED_INTERVAL / 1_000);
+ }
+
+ public Pair getAccountNetUsageBalanceAndRestoreSeconds(AccountCapsule accountCapsule) {
+ long now = getHeadSlot();
+
+ long netUsage = accountCapsule.getNetUsage();
+ long latestConsumeTime = accountCapsule.getLatestConsumeTime();
+ long accountWindowSize = accountCapsule.getWindowSize(Common.ResourceCode.BANDWIDTH);
+
+ if (now >= latestConsumeTime + accountWindowSize) {
+ return Pair.of(0L, 0L);
+ }
+
+ long restoreSlots = latestConsumeTime + accountWindowSize - now;
+
+ long newNetUsage = recover(netUsage, latestConsumeTime, now, accountWindowSize);
+
+ long totalNetLimit = getDynamicPropertiesStore().getTotalNetLimit();
+ long totalNetWeight = getTotalNetWeight();
+
+ long balance = usageToBalance(newNetUsage, totalNetWeight, totalNetLimit);
- return max(energyLimit - newEnergyUsage, 0); // us
+ return Pair.of(balance, restoreSlots * BLOCK_PRODUCED_INTERVAL / 1_000);
+ }
+
+ private long usageToBalance(long usage, long totalWeight, long totalLimit) {
+ if (hardenResourceCalculation()) {
+ return BigInteger.valueOf(usage)
+ .multiply(BigInteger.valueOf(totalWeight))
+ .multiply(BigInteger.valueOf(TRX_PRECISION))
+ .divide(BigInteger.valueOf(totalLimit))
+ .longValueExact();
+ }
+ return (long) ((double) usage * totalWeight / totalLimit * TRX_PRECISION);
}
@Override
@@ -344,6 +439,49 @@ public BytesCapsule getDelegation(Key key) {
return bytesCapsule;
}
+ @Override
+ public DelegatedResourceAccountIndexCapsule getDelegatedResourceAccountIndex(byte[] key) {
+ Key cacheKey = new Key(key);
+ if (delegatedResourceAccountIndexCache.containsKey(cacheKey)) {
+ return new DelegatedResourceAccountIndexCapsule(
+ delegatedResourceAccountIndexCache.get(cacheKey).getValue());
+ }
+
+ DelegatedResourceAccountIndexCapsule delegatedResourceAccountIndexCapsule;
+ if (parent != null) {
+ delegatedResourceAccountIndexCapsule = parent.getDelegatedResourceAccountIndex(key);
+ } else {
+ delegatedResourceAccountIndexCapsule = getDelegatedResourceAccountIndexStore().get(key);
+ }
+
+ if (delegatedResourceAccountIndexCapsule != null) {
+ delegatedResourceAccountIndexCache.put(
+ cacheKey, Value.create(delegatedResourceAccountIndexCapsule));
+ }
+ return delegatedResourceAccountIndexCapsule;
+ }
+
+ public byte[] getTransientStorageValue(byte[] address, byte[] key) {
+ Key cacheAddress = new Key(address);
+ Key cacheKey = new Key(key);
+ if (transientStorage.contains(cacheAddress, cacheKey)) {
+ return transientStorage.get(cacheAddress, cacheKey).getValue();
+ }
+
+ byte[] value;
+ if (parent != null) {
+ value = parent.getTransientStorageValue(address, key);
+ } else {
+ value = null;
+ }
+
+ if (value != null) {
+ transientStorage.put(cacheAddress, cacheKey, Value.create(value));
+ }
+
+ return value;
+ }
+
@Override
public void deleteContract(byte[] address) {
@@ -356,6 +494,7 @@ public void deleteContract(byte[] address) {
public void createContract(byte[] address, ContractCapsule contractCapsule) {
contractCache.put(Key.create(address),
Value.create(contractCapsule, Type.CREATE));
+ putNewContract(address);
}
@Override
@@ -378,12 +517,61 @@ public ContractCapsule getContract(byte[] address) {
return contractCapsule;
}
+ @Override
+ public ContractStateCapsule getContractState(byte[] address) {
+ Key key = Key.create(address);
+ if (contractStateCache.containsKey(key)) {
+ return new ContractStateCapsule(contractStateCache.get(key).getValue());
+ }
+
+ ContractStateCapsule contractStateCapsule;
+ if (parent != null) {
+ contractStateCapsule = parent.getContractState(address);
+ } else {
+ contractStateCapsule = getContractStateStore().get(address);
+ }
+
+ if (contractStateCapsule != null) {
+ contractStateCache.put(key, Value.create(contractStateCapsule));
+ }
+ return contractStateCapsule;
+ }
+
@Override
public void updateContract(byte[] address, ContractCapsule contractCapsule) {
contractCache.put(Key.create(address),
Value.create(contractCapsule, Type.DIRTY));
}
+ @Override
+ public void updateContractState(byte[] address, ContractStateCapsule contractStateCapsule) {
+ contractStateCache.put(Key.create(address),
+ Value.create(contractStateCapsule, Type.DIRTY));
+ }
+
+ @Override
+ public void putNewContract(byte[] address) {
+ newContractCache.add(Key.create(address));
+ }
+
+ @Override
+ public boolean isNewContract(byte[] address) {
+ Key key = Key.create(address);
+ if (newContractCache.contains(key)) {
+ return true;
+ }
+
+ if (parent != null) {
+ boolean isNew = parent.isNewContract(address);
+ if (isNew) {
+ newContractCache.add(key);
+ }
+ return isNew;
+ } else {
+ return false;
+ }
+ }
+
@Override
public void updateAccount(byte[] address, AccountCapsule accountCapsule) {
accountCache.put(Key.create(address),
@@ -434,6 +622,18 @@ public void updateDelegation(byte[] word, BytesCapsule bytesCapsule) {
Value.create(bytesCapsule.getData(), Type.DIRTY));
}
+ @Override
+ public void updateDelegatedResourceAccountIndex(
+ byte[] word, DelegatedResourceAccountIndexCapsule delegatedResourceAccountIndexCapsule) {
+ delegatedResourceAccountIndexCache.put(
+ Key.create(word), Value.create(delegatedResourceAccountIndexCapsule, Type.DIRTY));
+ }
+
+ @Override
+ public void updateTransientStorageValue(byte[] address, byte[] key, byte[] value) {
+ transientStorage.put(Key.create(address), Key.create(key), Value.create(value, Type.DIRTY));
+ }
+
@Override
public void saveCode(byte[] address, byte[] code) {
codeCache.put(Key.create(address), Value.create(code, Type.CREATE));
@@ -550,7 +750,7 @@ public long addBalance(byte[] address, long value) {
StringUtil.createReadableString(accountCapsule.createDbKey())
+ " insufficient balance");
}
- accountCapsule.setBalance(Math.addExact(balance, value));
+ accountCapsule.setBalance(addExact(balance, value, VMConfig.disableJavaLangMath()));
Key key = Key.create(address);
accountCache.put(key, Value.create(accountCapsule,
accountCache.get(key).getType().addType(Type.DIRTY)));
@@ -571,11 +771,15 @@ public void commit() {
commitAccountCache(repository);
commitCodeCache(repository);
commitContractCache(repository);
+ commitContractStateCache(repository);
commitStorageCache(repository);
commitDynamicCache(repository);
commitDelegatedResourceCache(repository);
commitVotesCache(repository);
commitDelegationCache(repository);
+ commitDelegatedResourceAccountIndexCache(repository);
+ commitTransientStorage(repository);
+ commitNewContractCache(repository);
}
@Override
@@ -593,6 +797,11 @@ public void putContract(Key key, Value value) {
contractCache.put(key, value);
}
+ @Override
+ public void putContractState(Key key, Value value) {
+ contractStateCache.put(key, value);
+ }
+
@Override
public void putStorage(Key key, Storage cache) {
storageCache.put(key, cache);
@@ -624,6 +833,16 @@ public void putDelegation(Key key, Value value) {
delegationCache.put(key, value);
}
+ @Override
+ public void putDelegatedResourceAccountIndex(Key key, Value value) {
+ delegatedResourceAccountIndexCache.put(key, value);
+ }
+
+ @Override
+ public void putTransientStorageValue(Key address, Key key, Value value) {
+ transientStorage.put(address, key, value);
+ }
+
@Override
public long addTokenBalance(byte[] address, byte[] tokenId, long value) {
byte[] tokenIdWithoutLeadingZero = ByteUtil.stripLeadingZeroes(tokenId);
@@ -684,20 +903,32 @@ public BlockCapsule getBlockByNum(long num) {
}
}
- private long increase(long lastUsage, long usage, long lastTime, long now) {
- return increase(lastUsage, usage, lastTime, now, windowSize);
+ // new recover method, use personal window size.
+ private long recover(long lastUsage, long lastTime, long now, long personalWindowSize) {
+ return increase(lastUsage, 0, lastTime, now, personalWindowSize);
}
private long increase(long lastUsage, long usage, long lastTime, long now, long windowSize) {
- long averageLastUsage = divideCeil(lastUsage * precision, windowSize);
- long averageUsage = divideCeil(usage * precision, windowSize);
+ long averageLastUsage;
+ long averageUsage;
+ if (hardenResourceCalculation()) {
+ BigInteger biPrecision = BigInteger.valueOf(precision);
+ BigInteger biWindowSize = BigInteger.valueOf(windowSize);
+ averageLastUsage = divideCeilExact(
+ BigInteger.valueOf(lastUsage).multiply(biPrecision), biWindowSize);
+ averageUsage = divideCeilExact(
+ BigInteger.valueOf(usage).multiply(biPrecision), biWindowSize);
+ } else {
+ averageLastUsage = divideCeil(lastUsage * precision, windowSize);
+ averageUsage = divideCeil(usage * precision, windowSize);
+ }
if (lastTime != now) {
assert now > lastTime;
if (lastTime + windowSize > now) {
long delta = now - lastTime;
double decay = (windowSize - delta) / (double) windowSize;
- averageLastUsage = Math.round(averageLastUsage * decay);
+ averageLastUsage = round(averageLastUsage * decay, VMConfig.disableJavaLangMath());
} else {
averageLastUsage = 0;
}
@@ -710,27 +941,55 @@ private long divideCeil(long numerator, long denominator) {
return (numerator / denominator) + ((numerator % denominator) > 0 ? 1 : 0);
}
+ private long divideCeilExact(BigInteger numerator, BigInteger denominator) {
+ BigInteger[] divRem = numerator.divideAndRemainder(denominator);
+ long result = divRem[0].longValueExact();
+ if (divRem[1].signum() > 0) {
+ result = StrictMathWrapper.addExact(result, 1);
+ }
+ return result;
+ }
+
private long getUsage(long usage, long windowSize) {
+ if (hardenResourceCalculation()) {
+ return BigInteger.valueOf(usage)
+ .multiply(BigInteger.valueOf(windowSize))
+ .divide(BigInteger.valueOf(precision))
+ .longValueExact();
+ }
return usage * windowSize / precision;
}
+ private boolean hardenResourceCalculation() {
+ return VMConfig.allowHardenResourceCalculation();
+ }
+
public long calculateGlobalEnergyLimit(AccountCapsule accountCapsule) {
long frozeBalance = accountCapsule.getAllFrozenBalanceForEnergy();
- if (frozeBalance < 1_000_000L) {
+ if (frozeBalance < TRX_PRECISION) {
return 0;
}
- long energyWeight = frozeBalance / 1_000_000L;
+ long energyWeight = frozeBalance / TRX_PRECISION;
long totalEnergyLimit = getDynamicPropertiesStore().getTotalEnergyCurrentLimit();
long totalEnergyWeight = getDynamicPropertiesStore().getTotalEnergyWeight();
assert totalEnergyWeight > 0;
+ if (hardenResourceCalculation()) {
+ return BigInteger.valueOf(energyWeight)
+ .multiply(BigInteger.valueOf(totalEnergyLimit))
+ .divide(BigInteger.valueOf(totalEnergyWeight))
+ .longValueExact();
+ }
return (long) (energyWeight * ((double) totalEnergyLimit / totalEnergyWeight));
}
public long getHeadSlot() {
- return (getDynamicPropertiesStore().getLatestBlockHeaderTimestamp()
- - Long.parseLong(CommonParameter.getInstance()
+ return getSlotByTimestampMs(getDynamicPropertiesStore().getLatestBlockHeaderTimestamp());
+ }
+
+ public long getSlotByTimestampMs(long timestamp) {
+ return (timestamp - Long.parseLong(CommonParameter.getInstance()
.getGenesisBlock().getTimestamp()))
/ BLOCK_PRODUCED_INTERVAL;
}
@@ -775,6 +1034,19 @@ private void commitContractCache(Repository deposit) {
}));
}
+ private void commitContractStateCache(Repository deposit) {
+ contractStateCache.forEach(((key, value) -> {
+ if (value.getType().isDirty() || value.getType().isCreate()) {
+ if (deposit != null) {
+ deposit.putContractState(key, value);
+ } else {
+ ContractStateCapsule contractStateCapsule = new ContractStateCapsule(value.getValue());
+ getContractStateStore().put(key.getData(), contractStateCapsule);
+ }
+ }
+ }));
+ }
+
private void commitStorageCache(Repository deposit) {
storageCache.forEach((Key address, Storage storage) -> {
if (deposit != null) {
@@ -836,6 +1108,40 @@ private void commitDelegationCache(Repository deposit) {
});
}
+ private void commitDelegatedResourceAccountIndexCache(Repository deposit) {
+ delegatedResourceAccountIndexCache.forEach(((key, value) -> {
+ if (value.getType().isDirty() || value.getType().isCreate()) {
+ if (deposit != null) {
+ deposit.putDelegatedResourceAccountIndex(key, value);
+ } else {
+ if (ByteUtil.isNullOrZeroArray(value.getValue().toByteArray())) {
+ getDelegatedResourceAccountIndexStore().delete(key.getData());
+ } else {
+ getDelegatedResourceAccountIndexStore().put(key.getData(),
+ new DelegatedResourceAccountIndexCapsule(value.getValue()));
+ }
+ }
+ }
+ }));
+ }
+
+ public void commitTransientStorage(Repository deposit) {
+ if (deposit != null) {
+ transientStorage.cellSet().forEach(cell -> {
+ if (cell.getValue().getType().isDirty() || cell.getValue().getType().isCreate()) {
+ deposit.putTransientStorageValue(
+ cell.getRowKey(), cell.getColumnKey(), cell.getValue());
+ }
+ });
+ }
+ }
+
+ public void commitNewContractCache(Repository deposit) {
+ if (deposit != null) {
+ newContractCache.forEach(key -> deposit.putNewContract(key.getData()));
+ }
+ }
+
/**
* Get the block id from the number.
*/
@@ -872,6 +1178,13 @@ public void addTotalEnergyWeight(long amount) {
saveTotalEnergyWeight(totalEnergyWeight);
}
+ @Override
+ public void addTotalTronPowerWeight(long amount) {
+ long totalTronPowerWeight = getTotalTronPowerWeight();
+ totalTronPowerWeight += amount;
+ saveTotalTronPowerWeight(totalTronPowerWeight);
+ }
+
@Override
public void saveTotalNetWeight(long totalNetWeight) {
updateDynamicProperty(TOTAL_NET_WEIGHT,
@@ -884,6 +1197,12 @@ public void saveTotalEnergyWeight(long totalEnergyWeight) {
new BytesCapsule(ByteArray.fromLong(totalEnergyWeight)));
}
+ @Override
+ public void saveTotalTronPowerWeight(long totalTronPowerWeight) {
+ updateDynamicProperty(TOTAL_TRON_POWER_WEIGHT,
+ new BytesCapsule(ByteArray.fromLong(totalTronPowerWeight)));
+ }
+
@Override
public long getTotalNetWeight() {
return Optional.ofNullable(getDynamicProperty(TOTAL_NET_WEIGHT))
@@ -902,4 +1221,13 @@ public long getTotalEnergyWeight() {
() -> new IllegalArgumentException("not found TOTAL_ENERGY_WEIGHT"));
}
+ @Override
+ public long getTotalTronPowerWeight() {
+ return Optional.ofNullable(getDynamicProperty(TOTAL_TRON_POWER_WEIGHT))
+ .map(BytesCapsule::getData)
+ .map(ByteArray::toLong)
+ .orElseThrow(
+ () -> new IllegalArgumentException("not found TOTAL_TRON_POWER_WEIGHT"));
+ }
+
}
diff --git a/actuator/src/main/java/org/tron/core/vm/repository/Type.java b/actuator/src/main/java/org/tron/core/vm/repository/Type.java
index e0842e6a593..9dfbd69f9ae 100644
--- a/actuator/src/main/java/org/tron/core/vm/repository/Type.java
+++ b/actuator/src/main/java/org/tron/core/vm/repository/Type.java
@@ -73,7 +73,7 @@ public boolean equals(Object obj) {
@Override
public int hashCode() {
- return new Integer(type).hashCode();
+ return type;
}
@Override
diff --git a/actuator/src/main/java/org/tron/core/vm/repository/Value.java b/actuator/src/main/java/org/tron/core/vm/repository/Value.java
index bf5d99c9c94..1df758f0b3e 100644
--- a/actuator/src/main/java/org/tron/core/vm/repository/Value.java
+++ b/actuator/src/main/java/org/tron/core/vm/repository/Value.java
@@ -58,6 +58,6 @@ public boolean equals(Object obj) {
@Override
public int hashCode() {
- return new Integer(type.hashCode() + Objects.hashCode(value)).hashCode();
+ return type.hashCode() + Objects.hashCode(value);
}
}
diff --git a/actuator/src/main/java/org/tron/core/vm/repository/WriteOptionsWrapper.java b/actuator/src/main/java/org/tron/core/vm/repository/WriteOptionsWrapper.java
deleted file mode 100644
index f9e819f9716..00000000000
--- a/actuator/src/main/java/org/tron/core/vm/repository/WriteOptionsWrapper.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.tron.core.vm.repository;
-
-import lombok.Getter;
-
-public class WriteOptionsWrapper {
-
- @Getter
- private org.rocksdb.WriteOptions rocks = null;
- @Getter
- private org.iq80.leveldb.WriteOptions level = null;
-
- public static WriteOptionsWrapper getInstance() {
- WriteOptionsWrapper wrapper = new WriteOptionsWrapper();
- wrapper.level = new org.iq80.leveldb.WriteOptions();
- wrapper.rocks = new org.rocksdb.WriteOptions();
- return wrapper;
- }
-
- public WriteOptionsWrapper sync(boolean bool) {
- this.level.sync(bool);
- this.rocks.setSync(bool);
- return this;
- }
-}
diff --git a/actuator/src/main/java/org/tron/core/vm/trace/ProgramTrace.java b/actuator/src/main/java/org/tron/core/vm/trace/ProgramTrace.java
index bd8bc18e78e..a7bb132e3a2 100644
--- a/actuator/src/main/java/org/tron/core/vm/trace/ProgramTrace.java
+++ b/actuator/src/main/java/org/tron/core/vm/trace/ProgramTrace.java
@@ -90,12 +90,8 @@ public void merge(ProgramTrace programTrace) {
this.ops.addAll(programTrace.ops);
}
- public String asJsonString(boolean formatted) {
- return serializeFieldsOnly(this, formatted);
- }
-
@Override
public String toString() {
- return asJsonString(true);
+ return serializeFieldsOnly(this);
}
}
diff --git a/actuator/src/main/java/org/tron/core/vm/trace/Serializers.java b/actuator/src/main/java/org/tron/core/vm/trace/Serializers.java
index 69056d359c5..ddf18105941 100644
--- a/actuator/src/main/java/org/tron/core/vm/trace/Serializers.java
+++ b/actuator/src/main/java/org/tron/core/vm/trace/Serializers.java
@@ -1,73 +1,28 @@
package org.tron.core.vm.trace;
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
-import java.io.IOException;
+import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.extern.slf4j.Slf4j;
-import org.bouncycastle.util.encoders.Hex;
-import org.tron.common.runtime.vm.DataWord;
-import org.tron.core.vm.Op;
@Slf4j(topic = "VM")
public final class Serializers {
- public static String serializeFieldsOnly(Object value, boolean pretty) {
- try {
- ObjectMapper mapper = createMapper(pretty);
- mapper.setVisibilityChecker(fieldsOnlyVisibilityChecker(mapper));
+ private static final ObjectMapper mapper = JsonMapper.builder()
+ .enable(SerializationFeature.INDENT_OUTPUT)
+ .visibility(PropertyAccessor.FIELD, Visibility.ANY)
+ .visibility(PropertyAccessor.GETTER, Visibility.NONE)
+ .visibility(PropertyAccessor.IS_GETTER, Visibility.NONE)
+ .build();
+ public static String serializeFieldsOnly(Object value) {
+ try {
return mapper.writeValueAsString(value);
} catch (Exception e) {
logger.error("JSON serialization error: ", e);
return "{}";
}
}
-
- private static VisibilityChecker> fieldsOnlyVisibilityChecker(ObjectMapper mapper) {
- return mapper.getSerializationConfig().getDefaultVisibilityChecker()
- .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
- .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
- .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE);
- }
-
- public static ObjectMapper createMapper(boolean pretty) {
- ObjectMapper mapper = new ObjectMapper();
- if (pretty) {
- mapper.enable(SerializationFeature.INDENT_OUTPUT);
- }
- return mapper;
- }
-
- public static class DataWordSerializer extends JsonSerializer {
-
- @Override
- public void serialize(DataWord energy, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonProcessingException {
- jgen.writeString(energy.value().toString());
- }
- }
-
- public static class ByteArraySerializer extends JsonSerializer {
-
- @Override
- public void serialize(byte[] memory, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonProcessingException {
- jgen.writeString(Hex.toHexString(memory));
- }
- }
-
- public static class OpCodeSerializer extends JsonSerializer {
-
- @Override
- public void serialize(Byte op, JsonGenerator jgen, SerializerProvider provider)
- throws IOException, JsonProcessingException {
- jgen.writeString(Op.getNameOf(op));
- }
- }
}
diff --git a/actuator/src/main/java/org/tron/core/vm/utils/FreezeV2Util.java b/actuator/src/main/java/org/tron/core/vm/utils/FreezeV2Util.java
new file mode 100644
index 00000000000..762d9318e7b
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/utils/FreezeV2Util.java
@@ -0,0 +1,263 @@
+package org.tron.core.vm.utils;
+
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.min;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.tron.core.actuator.UnfreezeBalanceV2Actuator;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.DelegatedResourceCapsule;
+import org.tron.core.vm.config.VMConfig;
+import org.tron.core.vm.repository.Repository;
+import org.tron.protos.Protocol;
+
+public class FreezeV2Util {
+
+ private FreezeV2Util() {
+ }
+
+ public static long queryExpireUnfreezeBalanceV2(byte[] address, long time, Repository repository) {
+ if (!VMConfig.allowTvmFreezeV2()) {
+ return 0;
+ }
+
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ if (accountCapsule == null) {
+ return 0;
+ }
+
+ List unfrozenV2List =
+ accountCapsule.getInstance().getUnfrozenV2List();
+
+ return getTotalWithdrawUnfreeze(unfrozenV2List, time);
+ }
+
+ public static long queryUnfreezableBalanceV2(byte[] address, long type, Repository repository) {
+ if (!VMConfig.allowTvmFreezeV2()) {
+ return 0;
+ }
+
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ if (accountCapsule == null) {
+ return 0;
+ }
+
+ // BANDWIDTH
+ if (type == 0) {
+ return accountCapsule.getFrozenV2BalanceForBandwidth();
+ }
+
+ // ENERGY
+ if (type == 1) {
+ return accountCapsule.getFrozenV2BalanceForEnergy();
+ }
+
+ // POWER
+ if (type == 2) {
+ return accountCapsule.getTronPowerFrozenV2Balance();
+ }
+
+ return 0;
+ }
+
+ // only freezeV2.
+ public static long queryResourceV2(byte[] from, byte[] to, long type, Repository repository) {
+ if (!VMConfig.allowTvmFreezeV2()) {
+ return 0;
+ }
+
+ byte[] key = DelegatedResourceCapsule.createDbKeyV2(from, to, false);
+ byte[] lockKey = DelegatedResourceCapsule.createDbKeyV2(from, to, true);
+ DelegatedResourceCapsule delegatedResource = repository.getDelegatedResource(key);
+ DelegatedResourceCapsule lockDelegateResource = repository.getDelegatedResource(lockKey);
+ if (delegatedResource == null && lockDelegateResource == null) {
+ return 0;
+ }
+
+ long amount = 0;
+ // BANDWIDTH
+ if (type == 0) {
+ if (delegatedResource != null) {
+ amount += delegatedResource.getFrozenBalanceForBandwidth();
+ }
+ if (lockDelegateResource != null) {
+ amount += lockDelegateResource.getFrozenBalanceForBandwidth();
+ }
+ return amount;
+ }
+
+ // ENERGY
+ if (type == 1) {
+ if (delegatedResource != null) {
+ amount += delegatedResource.getFrozenBalanceForEnergy();
+ }
+ if (lockDelegateResource != null) {
+ amount += lockDelegateResource.getFrozenBalanceForEnergy();
+ }
+ return amount;
+ }
+
+ return 0;
+ }
+
+ public static Pair queryFrozenBalanceUsage(byte[] address, long type, Repository repository) {
+ if (!VMConfig.allowTvmFreezeV2()) {
+ return Pair.of(0L, 0L);
+ }
+
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ if (accountCapsule == null) {
+ return Pair.of(0L, 0L);
+ }
+
+ if (type == 0) {
+ return repository.getAccountNetUsageBalanceAndRestoreSeconds(accountCapsule);
+ } else if (type == 1) {
+ return repository.getAccountEnergyUsageBalanceAndRestoreSeconds(accountCapsule);
+ }
+
+ return Pair.of(0L, 0L);
+ }
+
+ public static long queryAvailableUnfreezeV2Size(byte[] address, Repository repository) {
+ if (!VMConfig.allowTvmFreezeV2()) {
+ return 0L;
+ }
+
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ if (accountCapsule == null) {
+ return 0L;
+ }
+
+ long now = repository.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ int unfreezingV2Count = accountCapsule.getUnfreezingV2Count(now);
+ return max(UnfreezeBalanceV2Actuator.getUNFREEZE_MAX_TIMES() - unfreezingV2Count, 0L,
+ VMConfig.disableJavaLangMath());
+ }
+
+ public static long queryDelegatableResource(byte[] address, long type, Repository repository) {
+ if (!VMConfig.allowTvmFreezeV2()) {
+ return 0L;
+ }
+
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ if (accountCapsule == null) {
+ return 0L;
+ }
+
+ if (type == 0) {
+ // self frozenV2 resource
+ long frozenV2Resource = accountCapsule.getFrozenV2BalanceForBandwidth();
+
+ // total Usage.
+ Pair usagePair =
+ repository.getAccountNetUsageBalanceAndRestoreSeconds(accountCapsule);
+ if (usagePair == null || usagePair.getLeft() == null) {
+ return frozenV2Resource;
+ }
+
+ long usage = usagePair.getLeft();
+ if (usage <= 0) {
+ return frozenV2Resource;
+ }
+
+ long v2NetUsage = getV2NetUsage(accountCapsule, usage, VMConfig.disableJavaLangMath());
+ return max(0L, frozenV2Resource - v2NetUsage, VMConfig.disableJavaLangMath());
+ }
+
+ if (type == 1) {
+ // self frozenV2 resource
+ long frozenV2Resource = accountCapsule.getFrozenV2BalanceForEnergy();
+
+ // total Usage.
+ Pair usagePair =
+ repository.getAccountEnergyUsageBalanceAndRestoreSeconds(accountCapsule);
+ if (usagePair == null || usagePair.getLeft() == null) {
+ return frozenV2Resource;
+ }
+
+ long usage = usagePair.getLeft();
+ if (usage <= 0) {
+ return frozenV2Resource;
+ }
+
+ long v2EnergyUsage = getV2EnergyUsage(accountCapsule, usage, VMConfig.disableJavaLangMath());
+ return max(0L, frozenV2Resource - v2EnergyUsage, VMConfig.disableJavaLangMath());
+ }
+
+ return 0L;
+ }
+
+ public static Triple checkUndelegateResource(byte[] address, long amount, long type, Repository repository) {
+ if (!VMConfig.allowTvmFreezeV2()) {
+ return Triple.of(0L, 0L, 0L);
+ }
+
+ if (amount <= 0) {
+ return Triple.of(0L, 0L, 0L);
+ }
+
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ if (accountCapsule == null) {
+ return Triple.of(0L, 0L, 0L);
+ }
+
+ Pair usagePair;
+ long resourceLimit;
+ if (type == 0) {
+ usagePair = repository.getAccountNetUsageBalanceAndRestoreSeconds(accountCapsule);
+ resourceLimit = accountCapsule.getAllFrozenBalanceForBandwidth();
+ } else if (type == 1) {
+ usagePair = repository.getAccountEnergyUsageBalanceAndRestoreSeconds(accountCapsule);
+ resourceLimit = accountCapsule.getAllFrozenBalanceForEnergy();
+ } else {
+ return Triple.of(0L, 0L, 0L);
+ }
+
+ if (usagePair == null || usagePair.getLeft() == null || usagePair.getRight() == null) {
+ return Triple.of(0L, 0L, 0L);
+ }
+
+ amount = min(amount, resourceLimit, VMConfig.disableJavaLangMath());
+ if (resourceLimit <= usagePair.getLeft()) {
+ return Triple.of(0L, amount, usagePair.getRight());
+ }
+
+ long clean = (long) (amount * ((double) (resourceLimit - usagePair.getLeft()) / resourceLimit));
+
+ return Triple.of(clean, amount - clean, usagePair.getRight());
+ }
+
+ private static long getTotalWithdrawUnfreeze(List unfrozenV2List, long time) {
+ return getTotalWithdrawList(unfrozenV2List, time).stream()
+ .mapToLong(Protocol.Account.UnFreezeV2::getUnfreezeAmount).sum();
+ }
+
+ private static List getTotalWithdrawList(List unfrozenV2List, long now) {
+ return unfrozenV2List.stream().filter(unfrozenV2 -> unfrozenV2.getUnfreezeExpireTime() <= now)
+ .collect(Collectors.toList());
+ }
+
+ public static long getV2NetUsage(AccountCapsule ownerCapsule, long netUsage, boolean
+ disableJavaLangMath) {
+ long v2NetUsage= netUsage
+ - ownerCapsule.getFrozenBalance()
+ - ownerCapsule.getAcquiredDelegatedFrozenBalanceForBandwidth()
+ - ownerCapsule.getAcquiredDelegatedFrozenV2BalanceForBandwidth();
+ return max(0, v2NetUsage, disableJavaLangMath);
+ }
+
+ public static long getV2EnergyUsage(AccountCapsule ownerCapsule, long energyUsage, boolean
+ disableJavaLangMath) {
+ long v2EnergyUsage= energyUsage
+ - ownerCapsule.getEnergyFrozenBalance()
+ - ownerCapsule.getAcquiredDelegatedFrozenBalanceForEnergy()
+ - ownerCapsule.getAcquiredDelegatedFrozenV2BalanceForEnergy();
+ return max(0, v2EnergyUsage, disableJavaLangMath);
+ }
+
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/utils/MUtil.java b/actuator/src/main/java/org/tron/core/vm/utils/MUtil.java
index 6d002c4db81..c94f28b3a2f 100644
--- a/actuator/src/main/java/org/tron/core/vm/utils/MUtil.java
+++ b/actuator/src/main/java/org/tron/core/vm/utils/MUtil.java
@@ -1,8 +1,11 @@
package org.tron.core.vm.utils;
+import org.tron.common.utils.ForkController;
import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.config.Parameter;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.vm.VMUtils;
+import org.tron.core.vm.program.Program.OutOfTimeException;
import org.tron.core.vm.repository.Repository;
import org.tron.protos.Protocol;
@@ -55,4 +58,10 @@ public static boolean isNullOrEmpty(String str) {
public static boolean isNotNullOrEmpty(String str) {
return !isNullOrEmpty(str);
}
+
+ public static void checkCPUTime() {
+ if (ForkController.instance().pass(Parameter.ForkBlockVersionEnum.VERSION_4_7_1)) {
+ throw new OutOfTimeException("CPU timeout for 0x0a executing");
+ }
+ }
}
diff --git a/actuator/src/test/java/org/tron/core/actuator/VMActuatorTest.java b/actuator/src/test/java/org/tron/core/actuator/VMActuatorTest.java
new file mode 100644
index 00000000000..240c606e2e9
--- /dev/null
+++ b/actuator/src/test/java/org/tron/core/actuator/VMActuatorTest.java
@@ -0,0 +1,23 @@
+package org.tron.core.actuator;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class VMActuatorTest {
+
+ @Test
+ public void testConstantCallUsesConfiguredTimeoutVerbatim() {
+ assertEquals(123_000L, VMActuator.calculateCpuLimitInUs(true, 80L, 5.0, 123L));
+ }
+
+ @Test
+ public void testConstantCallWithoutConfiguredTimeoutUsesNetworkDeadline() {
+ assertEquals(400_000L, VMActuator.calculateCpuLimitInUs(true, 80L, 5.0, 0L));
+ }
+
+ @Test
+ public void testNonConstantCallIgnoresConfiguredTimeout() {
+ assertEquals(400_000L, VMActuator.calculateCpuLimitInUs(false, 80L, 5.0, 123L));
+ }
+}
diff --git a/build.gradle b/build.gradle
index de4fb03e532..e143ab3a947 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,82 +1,194 @@
-allprojects {
- version = "1.0.0"
- apply plugin: "java"
-}
-
-subprojects {
- apply plugin: "java"
- apply plugin: "jacoco"
- apply plugin: "maven"
- apply plugin: "maven-publish"
-
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
-
- [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
-
- buildscript {
- repositories {
- mavenCentral()
- jcenter()
- maven { url 'https://jitpack.io' }
- }
- dependencies {
- classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
- classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
- }
- }
-
- repositories {
- mavenLocal()
- mavenCentral()
- maven { url 'https://repo.spring.io/plugins-release' }
- maven { url 'https://jitpack.io' }
- }
-
- dependencies {
- compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25'
- compile group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.25'
- compile "org.slf4j:jcl-over-slf4j:1.7.25"
- compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.9'
- compileOnly 'org.projectlombok:lombok:1.18.12'
- annotationProcessor 'org.projectlombok:lombok:1.18.12'
- testCompileOnly 'org.projectlombok:lombok:1.18.12'
- testAnnotationProcessor 'org.projectlombok:lombok:1.18.12'
- compile group: 'com.google.guava', name: 'guava', version: '24.1-jre'
- compile "com.google.code.findbugs:jsr305:3.0.0"
- compile group: 'org.springframework', name: 'spring-context', version: '4.2.4.RELEASE'
- compile group: 'org.springframework', name: 'spring-tx', version: '4.2.4.RELEASE'
- compile "org.apache.commons:commons-lang3:3.4"
- compile group: 'org.apache.commons', name: 'commons-math', version: '2.2'
- compile "org.apache.commons:commons-collections4:4.0"
- compile group: 'joda-time', name: 'joda-time', version: '2.3'
-
- }
-
- task sourcesJar(type: Jar, dependsOn: classes) {
- classifier = "sources"
- from sourceSets.main.allSource
- }
-
-
- tasks.withType(AbstractArchiveTask) {
- preserveFileTimestamps = false
- reproducibleFileOrder = true
- }
-
- configurations.all {
- resolutionStrategy {
- force group: 'com.google.guava', name: 'guava', version: '30.1-jre'
- }
- }
-}
-
-task copyToParent(type: Copy) {
- into "$buildDir/libs"
- subprojects {
- from tasks.withType(Jar)
- }
-}
-
-build.finalizedBy(copyToParent)
-
+import org.gradle.nativeplatform.platform.internal.Architectures
+import org.gradle.internal.os.OperatingSystem
+
+plugins {
+ id 'net.ltgt.errorprone' version '5.0.0' apply false
+}
+
+allprojects {
+ version = "1.0.0"
+ apply plugin: "java-library"
+ ext {
+ springVersion = "5.3.39"
+ errorproneVersion = "2.42.0"
+ }
+}
+def arch = System.getProperty("os.arch").toLowerCase(Locale.ROOT)
+def javaVersion = JavaVersion.current()
+def isArm64 = Architectures.AARCH64.isAlias(arch)
+def archSource = isArm64 ? "arm" : "x86"
+def isMac = OperatingSystem.current().isMacOsX()
+
+ext.archInfo = [
+ name : arch,
+ java : javaVersion,
+ isArm64 : isArm64,
+ sourceSets: [
+ main: [
+ java: [
+ srcDirs: ["src/main/java/common", "src/main/java/${archSource}"]
+ ]
+ ],
+ test: [
+ java: [
+ srcDirs: ["src/test/java"]
+ ]
+ ]
+ ],
+ requires: [
+ JavaVersion: isArm64 ? JavaVersion.VERSION_17 : JavaVersion.VERSION_1_8,
+ RocksdbVersion: isArm64 ? '9.7.4' : '5.15.10',
+ // https://github.com/grpc/grpc-java/issues/7690
+ // https://github.com/grpc/grpc-java/pull/12319, Add support for macOS aarch64 with universal binary
+ // https://github.com/grpc/grpc-java/pull/11371 , 1.64.x is not supported CentOS 7.
+ ProtocGenVersion: isArm64 || isMac ? '1.81.0' : '1.60.0'
+ ],
+ 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: "jacoco"
+ apply plugin: "maven-publish"
+
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.current()
+
+ [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
+ jacoco {
+ toolVersion = "0.8.12" // see https://www.jacoco.org/jacoco/trunk/doc/changes.html
+ }
+
+ buildscript {
+ repositories {
+ mavenCentral()
+ maven { url 'https://jitpack.io' }
+ maven {
+ url "https://plugins.gradle.org/m2/"
+ }
+ }
+ dependencies {
+ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.1'
+ classpath "gradle.plugin.com.github.johnrengelman:shadow:7.1.2"
+ }
+ }
+
+ repositories {
+ mavenLocal()
+ mavenCentral()
+ maven { url 'https://repo.spring.io/plugins-release' }
+ maven { url 'https://jitpack.io' }
+ }
+
+ dependencies {
+ implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.36'
+ implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.36'
+ implementation group: 'org.slf4j', name: 'jul-to-slf4j', version: '1.7.36'
+ implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.13'
+ implementation "com.google.code.findbugs:jsr305:3.0.0"
+ implementation group: 'org.springframework', name: 'spring-context', version: "${springVersion}"
+ implementation "org.apache.commons:commons-lang3:3.4"
+ implementation group: 'org.apache.commons', name: 'commons-math', version: '2.2'
+ implementation "org.apache.commons:commons-collections4:4.1"
+ implementation group: 'joda-time', name: 'joda-time', version: '2.3'
+ implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: '1.84'
+
+ compileOnly 'org.projectlombok:lombok:1.18.34'
+ annotationProcessor 'org.projectlombok:lombok:1.18.34'
+ testCompileOnly 'org.projectlombok:lombok:1.18.34'
+ testAnnotationProcessor 'org.projectlombok:lombok:1.18.34'
+
+ // https://www.oracle.com/java/technologies/javase/11-relnote-issues.html#JDK-8190378
+ implementation group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
+ // for json-rpc, see https://github.com/briandilley/jsonrpc4j/issues/278
+ implementation group: 'javax.jws', name: 'javax.jws-api', version: '1.1'
+ annotationProcessor group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
+
+ testImplementation group: 'junit', name: 'junit', version: '4.13.2'
+ testImplementation "org.mockito:mockito-core:4.11.0"
+ testImplementation "org.mockito:mockito-inline:4.11.0"
+ }
+ if (project.name != 'protocol' && project.name != 'errorprone'
+ && javaVersion.isJava11Compatible()) {
+ apply plugin: 'net.ltgt.errorprone'
+ dependencies {
+ errorprone "com.google.errorprone:error_prone_core:${errorproneVersion}"
+ errorprone rootProject.project(':errorprone')
+ }
+ tasks.withType(JavaCompile).configureEach {
+ options.errorprone {
+ enabled = true
+ disableWarningsInGeneratedCode = true
+ disableAllChecks = true
+ excludedPaths = '.*/generated/.*'
+ errorproneArgs.addAll([
+ '-Xep:StringCaseLocaleUsage:ERROR',
+ '-Xep:StringCaseLocaleUsageMethodRef:ERROR',
+ ])
+ }
+ }
+ }
+
+ task sourcesJar(type: Jar, dependsOn: classes) {
+ classifier = "sources"
+ from sourceSets.main.allSource
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates
+ }
+
+
+ tasks.withType(AbstractArchiveTask) {
+ preserveFileTimestamps = false
+ reproducibleFileOrder = true
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates
+ }
+ tasks.withType(Test).configureEach {
+ // https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:environment
+ environment 'CI', 'true'
+ }
+
+ publishing {
+ publications {
+ mavenJava(MavenPublication) {
+ from components.java
+ }
+ }
+ }
+ configurations.configureEach {
+ resolutionStrategy {
+ eachDependency { details ->
+ if (details.requested.group == 'com.google.guava' &&
+ details.requested.name == 'guava') {
+ def requestedVersion = details.requested.version
+ if (requestedVersion.matches(/.*-android$/)) {
+ def jreVersion = requestedVersion.replaceAll(/-android$/, '-jre')
+ details.useVersion(jreVersion)
+ details.because("Automatically replace android guava with jre version: ${requestedVersion} -> ${jreVersion}")
+ }
+ }
+ }
+ }
+ }
+}
+
+task copyToParent(type: Copy) {
+ into "$buildDir/libs"
+ subprojects {
+ from tasks.withType(Jar)
+ }
+}
+
+build.finalizedBy(copyToParent)
+
+gradle.buildFinished {
+ if (project.hasProperty('cleanSubBuild')) {
+ subprojects {
+ buildDir.deleteDir()
+ }
+ }
+}
diff --git a/build.md b/build.md
deleted file mode 100644
index 1f3671b2c7d..00000000000
--- a/build.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# How to Build
-
-## Prepare dependencies
-
-* JDK 1.8 (JDK 1.9+ are not supported yet)
-* On Linux Ubuntu system (e.g. Ubuntu 16.04.4 LTS), ensure that the machine has [__Oracle JDK 8__](https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-get-on-ubuntu-16-04), instead of having __Open JDK 8__ in the system. If you are building the source code by using __Open JDK 8__, you will get [__Build Failed__](https://github.com/tronprotocol/java-tron/issues/337) result.
-* Open **UDP** ports for connection to the network
-* **Minimum** 2 CPU Cores
-
-## Build and Deploy automatically using scripts
-
-- Please take a look at the [Tron Deployment Scripts](https://github.com/tronprotocol/TronDeployment) repository.
-
-## Getting the code with git
-
-* Use Git from the console, see the [Setting up Git](https://help.github.com/articles/set-up-git/) and [Fork a Repo](https://help.github.com/articles/fork-a-repo/) articles.
-* `develop` branch: the newest code
-* `master` branch: more stable than develop.
-In the shell command, type:
- ```bash
- git clone https://github.com/tronprotocol/java-tron.git
- git checkout -t origin/master
- ```
-
-* For Mac, you can also install **[GitHub for Mac](https://mac.github.com/)** then **[fork and clone our repository](https://guides.github.com/activities/forking/)**.
-
-* If you'd rather not use Git, **[Download the ZIP](https://github.com/tronprotocol/java-tron/archive/develop.zip)**
-
-## Including java-tron as dependency
-
-If you don't want to checkout the code and build the project, you can include it directly as a dependency.
-
-**Using gradle:**
-
-```
-repositories {
- maven { url 'https://jitpack.io' }
-}
-dependencies {
- implementation 'com.github.tronprotocol:java-tron:develop-SNAPSHOT'
-}
-```
-
-**Using maven:**
-
-```xml
-
-
- jitpack.io
- https://jitpack.io
-
-
-
-
- com.github.tronprotocol
- java-tron
- develop-SNAPSHOT
-
-
-```
-
-## Building from source code
-
-- **Building using the console:**
-
- ```bash
- cd java-tron
- ./gradlew build
- ```
-
-- **Building using [IntelliJ IDEA](https://www.jetbrains.com/idea/) (community version is enough):**
-
- **Please run `./gradlew build` once to build the protocol files**
-
- 1. Start IntelliJ.
- Select `File` -> `Open`, then locate to the java-tron folder which you have git cloned to your local drive. Then click `Open` button on the right bottom.
- 2. Check on `Use auto-import` on the `Import Project from Gradle` dialog. Select JDK 1.8 in the `Gradle JVM` option. Then click `OK`.
- 3. IntelliJ will import the project and start gradle syncing, which will take several minutes, depending on your network connection and your IntelliJ configuration
- 4. Enable Annotations, `Preferences` -> Search `annotations` -> check `Enable Annotation Processing`.
- 5. When the syncing finishes, select `Gradle` -> `Tasks` -> `build`, and then double click `build` option.
-
diff --git a/chainbase/build.gradle b/chainbase/build.gradle
index 499ef014b32..1a07ff95fa5 100644
--- a/chainbase/build.gradle
+++ b/chainbase/build.gradle
@@ -2,50 +2,15 @@ description = "chainbase – a decentralized database for blockchain."
// Dependency versions
// ---------------------------------------
-
-def junitVersion = "4.12"
-def mockitoVersion = "2.1.0"
-def testNgVersion = "6.11"
-def jacocoVersion = "0.8.0"
-def leveldbVersion = "1.8"
def jansiVersion = "1.16"
// --------------------------------------
-static def isWindows() {
- return org.gradle.internal.os.OperatingSystem.current().isWindows()
-}
-
-if (isWindows()) {
- ext {
- leveldbGroup = "org.ethereum"
- leveldbName = "leveldbjni-all"
- leveldbVersion = "1.18.3"
- }
-} else {
- ext {
- leveldbGroup = "org.fusesource.leveldbjni"
- leveldbName = "leveldbjni-all"
- leveldbVersion = "1.8"
- }
-}
-
dependencies {
- testImplementation "junit:junit:$junitVersion"
- testImplementation "org.mockito:mockito-core:$mockitoVersion"
-
- testImplementation "org.testng:testng:$testNgVersion"
-
- compile group: leveldbGroup, name: leveldbName, version: leveldbVersion
- compile "org.fusesource.jansi:jansi:$jansiVersion"
- compile group: 'org.rocksdb', name: 'rocksdbjni', version: '5.15.10'
- compile group: 'com.typesafe', name: 'config', version: '1.3.2'
- compile 'com.github.tronprotocol:zksnark-java-sdk:master-SNAPSHOT'
- compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.10.3'
- compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.8.5'
- compile project(":protocol")
- compile project(":common")
- compile project(":crypto")
- compile 'org.reflections:reflections:0.9.11'
+ api project(":protocol")
+ api project(":common")
+ api project(":crypto")
+ api "org.fusesource.jansi:jansi:$jansiVersion"
+ api 'org.reflections:reflections:0.9.11'
}
@@ -74,35 +39,13 @@ test {
}
}
-task testng(type: Test) {
- useTestNG()
- testLogging {
- events = ["skipped", "failed"]
- exceptionFormat = "full"
-
- debug.events = ["skipped", "failed"]
- debug.exceptionFormat = "full"
-
- info.events = ["failed", "skipped"]
- info.exceptionFormat = "full"
-
- warn.events = ["failed", "skipped"]
- warn.exceptionFormat = "full"
- }
-}
-
-check.dependsOn testng
-
-jacoco {
- toolVersion = jacocoVersion // See http://www.eclemma.org/jacoco/.
-}
-
jacocoTestReport {
+ dependsOn(processResources) // explicit_dependency
reports {
xml.enabled = true
html.enabled = true
}
- executionData.from = '../framework/build/jacoco/jacocoTest.exec'
+ getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec"))
afterEvaluate {
classDirectories.from = classDirectories.files.collect {
fileTree(dir: it,)
diff --git a/chainbase/src/main/java/org/tron/common/overlay/message/Message.java b/chainbase/src/main/java/org/tron/common/overlay/message/Message.java
index a0269d0481f..84c3f695686 100644
--- a/chainbase/src/main/java/org/tron/common/overlay/message/Message.java
+++ b/chainbase/src/main/java/org/tron/common/overlay/message/Message.java
@@ -69,6 +69,10 @@ public ByteBuf getSendData() {
return Unpooled.wrappedBuffer(ArrayUtils.add(this.getData(), 0, type));
}
+ public byte[] getSendBytes() {
+ return ArrayUtils.add(this.getData(), 0, type);
+ }
+
public Sha256Hash getMessageId() {
return Sha256Hash.of(CommonParameter.getInstance().isECKeyCryptoEngine(),
getData());
diff --git a/chainbase/src/main/java/org/tron/common/runtime/ProgramResult.java b/chainbase/src/main/java/org/tron/common/runtime/ProgramResult.java
index 112b7f921af..04228d1316a 100644
--- a/chainbase/src/main/java/org/tron/common/runtime/ProgramResult.java
+++ b/chainbase/src/main/java/org/tron/common/runtime/ProgramResult.java
@@ -16,7 +16,6 @@
import org.tron.common.logsfilter.trigger.ContractTrigger;
import org.tron.common.runtime.vm.DataWord;
import org.tron.common.runtime.vm.LogInfo;
-import org.tron.common.utils.ByteArraySet;
import org.tron.core.capsule.TransactionResultCapsule;
import org.tron.protos.Protocol.Transaction.Result.contractResult;
@@ -25,6 +24,9 @@ public class ProgramResult {
private long energyUsed = 0;
//private long futureRefund = 0;
+ @Getter
+ private long energyPenaltyTotal = 0;
+
private byte[] hReturn = EMPTY_BYTE_ARRAY;
private byte[] contractAddress = EMPTY_BYTE_ARRAY;
private RuntimeException exception;
@@ -64,6 +66,11 @@ public void spendEnergy(long energy) {
energyUsed += energy;
}
+ public void spendEnergyWithPenalty(long total, long penalty) {
+ energyPenaltyTotal += penalty;
+ energyUsed += total;
+ }
+
public void setRevert() {
this.revert = true;
}
@@ -76,6 +83,10 @@ public void refundEnergy(long energy) {
energyUsed -= energy;
}
+ public void addTotalPenalty(long penalty) {
+ energyPenaltyTotal += penalty;
+ }
+
public byte[] getContractAddress() {
return Arrays.copyOf(contractAddress, contractAddress.length);
}
@@ -227,6 +238,7 @@ public void reset() {
public void merge(ProgramResult another) {
addInternalTransactions(another.getInternalTransactions());
+ addTotalPenalty(another.getEnergyPenaltyTotal());
if (another.getException() == null && !another.isRevert()) {
addDeleteAccounts(another.getDeleteAccounts());
addLogInfos(another.getLogInfoList());
diff --git a/chainbase/src/main/java/org/tron/common/storage/WriteOptionsWrapper.java b/chainbase/src/main/java/org/tron/common/storage/WriteOptionsWrapper.java
index 11277eafe75..bd6cacc6481 100644
--- a/chainbase/src/main/java/org/tron/common/storage/WriteOptionsWrapper.java
+++ b/chainbase/src/main/java/org/tron/common/storage/WriteOptionsWrapper.java
@@ -1,6 +1,8 @@
package org.tron.common.storage;
-public class WriteOptionsWrapper {
+import java.io.Closeable;
+
+public class WriteOptionsWrapper implements Closeable {
public org.rocksdb.WriteOptions rocks = null;
public org.iq80.leveldb.WriteOptions level = null;
@@ -9,6 +11,23 @@ private WriteOptionsWrapper() {
}
+ /**
+ * Returns an WriteOptionsWrapper.
+ *
+ * CRITICAL: The returned WriteOptionsWrapper holds native resources
+ * and MUST be closed
+ * after use to prevent memory leaks. It is strongly recommended to use a try-with-resources
+ * statement.
+ *
+ *
Example of correct usage:
+ *
{@code
+ * try ( WriteOptionsWrapper readOptions = WriteOptionsWrapper.getInstance()) {
+ * // do something
+ * }
+ * }
+ *
+ * @return a new WriteOptionsWrapper that must be closed.
+ */
public static WriteOptionsWrapper getInstance() {
WriteOptionsWrapper wrapper = new WriteOptionsWrapper();
wrapper.level = new org.iq80.leveldb.WriteOptions();
@@ -23,4 +42,12 @@ public WriteOptionsWrapper sync(boolean bool) {
this.rocks.setSync(bool);
return this;
}
+
+ @Override
+ public void close() {
+ if (rocks != null) {
+ rocks.close();
+ }
+ // leveldb WriteOptions has no close method, and does not need to be closed
+ }
}
diff --git a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java
index 054648eb467..aa85ac08f45 100644
--- a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java
+++ b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java
@@ -17,8 +17,9 @@
import static org.fusesource.leveldbjni.JniDBFactory.factory;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
-import java.io.File;
+import com.google.common.primitives.Bytes;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -30,24 +31,24 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
-
-import com.google.common.primitives.Bytes;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.iq80.leveldb.CompressionType;
import org.iq80.leveldb.DB;
import org.iq80.leveldb.DBIterator;
import org.iq80.leveldb.Options;
import org.iq80.leveldb.ReadOptions;
import org.iq80.leveldb.WriteBatch;
import org.iq80.leveldb.WriteOptions;
+import org.tron.common.es.ExecutorServiceManager;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.storage.WriteOptionsWrapper;
import org.tron.common.storage.metric.DbStat;
@@ -57,35 +58,30 @@
import org.tron.core.db.common.iterator.StoreIterator;
import org.tron.core.db2.common.Instance;
import org.tron.core.db2.common.WrappedByteArray;
+import org.tron.core.exception.TronError;
@Slf4j(topic = "DB")
@NoArgsConstructor
public class LevelDbDataSourceImpl extends DbStat implements DbSourceInter,
Iterable>, Instance {
+ /** First watchdog WARN fires this many seconds after factory.open() begins. */
+ private static final long OPEN_WATCHDOG_INITIAL_DELAY_SEC = 60;
+ /** Subsequent watchdog WARN lines are emitted on this interval. */
+ private static final long OPEN_WATCHDOG_PERIOD_SEC = 30;
+
private String dataBaseName;
private DB database;
- private boolean alive;
+ private volatile boolean alive;
private String parentPath;
private Options options;
private WriteOptions writeOptions;
private ReadWriteLock resetDbLock = new ReentrantReadWriteLock();
- private static final String LEVELDB = "LEVELDB";
+
/**
* constructor.
*/
- public LevelDbDataSourceImpl(String parentPath, String dataBaseName, Options options,
- WriteOptions writeOptions) {
- this.parentPath = Paths.get(
- parentPath,
- CommonParameter.getInstance().getStorage().getDbDirectory()
- ).toString();
- this.dataBaseName = dataBaseName;
- this.options = options;
- this.writeOptions = writeOptions;
- initDB();
- }
public LevelDbDataSourceImpl(String parentPath, String dataBaseName) {
this.parentPath = Paths.get(
@@ -94,12 +90,13 @@ public LevelDbDataSourceImpl(String parentPath, String dataBaseName) {
).toString();
this.dataBaseName = dataBaseName;
- options = new Options();
- writeOptions = new WriteOptions();
+ this.options = StorageUtils.getOptionsByDbName(dataBaseName);
+ this.writeOptions = new WriteOptions().sync(CommonParameter.getInstance()
+ .getStorage().isDbSync());
+ initDB();
}
- @Override
- public void initDB() {
+ private void initDB() {
resetDbLock.writeLock().lock();
try {
logger.debug("Init DB: {}.", dataBaseName);
@@ -133,37 +130,55 @@ private void openDatabase(Options dbOptions) throws IOException {
if (!Files.isSymbolicLink(dbPath.getParent())) {
Files.createDirectories(dbPath.getParent());
}
+ final long openStartNs = System.nanoTime();
+ ScheduledExecutorService watchdog = ExecutorServiceManager
+ .newSingleThreadScheduledExecutor("db-open-watchdog-" + dataBaseName, true);
+ ScheduledFuture> watchdogTask = watchdog.scheduleAtFixedRate(
+ () -> logSlowOpen(dbPath, openStartNs),
+ OPEN_WATCHDOG_INITIAL_DELAY_SEC,
+ OPEN_WATCHDOG_PERIOD_SEC,
+ TimeUnit.SECONDS);
try {
+ DbSourceInter.checkOrInitEngine(getEngine(), dbPath.toString(),
+ TronError.ErrCode.LEVELDB_INIT);
database = factory.open(dbPath.toFile(), dbOptions);
if (!this.getDBName().startsWith("checkpoint")) {
- logger.info("DB {} open success with writeBufferSize {} M, cacheSize {} M, maxOpenFiles {}.",
- this.getDBName(), dbOptions.writeBufferSize() / 1024 / 1024,
- dbOptions.cacheSize() / 1024 / 1024, dbOptions.maxOpenFiles());
+ logger
+ .info("DB {} open success with writeBufferSize {} M, cacheSize {} M, maxOpenFiles {}.",
+ this.getDBName(), dbOptions.writeBufferSize() / 1024 / 1024,
+ dbOptions.cacheSize() / 1024 / 1024, dbOptions.maxOpenFiles());
}
} catch (IOException e) {
if (e.getMessage().contains("Corruption:")) {
- logger.warn("DB {} corruption detected, try to repair it.", this.getDBName(), e);
- factory.repair(dbPath.toFile(), dbOptions);
- logger.warn("DB {} corruption detected, repair done.", this.getDBName());
- database = factory.open(dbPath.toFile(), dbOptions);
+ logger.error("Database {} corrupted, please delete database directory({}) and restart.",
+ dataBaseName, parentPath, e);
} else {
- throw e;
+ logger.error("Open Database {} failed", dataBaseName, e);
}
+ throw new TronError(e, TronError.ErrCode.LEVELDB_INIT);
+ } finally {
+ watchdogTask.cancel(false);
+ watchdog.shutdownNow();
}
}
- @Deprecated
- private Options createDbOptions() {
- Options dbOptions = new Options();
- dbOptions.createIfMissing(true);
- dbOptions.compressionType(CompressionType.NONE);
- dbOptions.blockSize(10 * 1024 * 1024);
- dbOptions.writeBufferSize(10 * 1024 * 1024);
- dbOptions.cacheSize(0);
- dbOptions.paranoidChecks(true);
- dbOptions.verifyChecksums(true);
- dbOptions.maxOpenFiles(32);
- return dbOptions;
+ /**
+ * Emits a WARN when factory.open() is still blocked — usually because the
+ * MANIFEST has grown large enough to make replay expensive.
+ */
+ void logSlowOpen(Path dbPath, long startNs) {
+ try {
+ long elapsedSec = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startNs);
+ logger.warn("DB {} open still in progress after {}s. path={}. "
+ + "This startup will complete; to speed up future restarts, run "
+ + "`java -jar Toolkit.jar db archive -d {}` before the next startup "
+ + "to rebuild the MANIFEST (the tool requires an exclusive DB lock, "
+ + "so it cannot run while the node is up).",
+ dataBaseName, elapsedSec, dbPath, parentPath);
+ } catch (Exception e) {
+ // Purely observational - never let the watchdog disrupt startup.
+ logger.debug("db-open-watchdog failure for {}: {}", dataBaseName, e.getMessage());
+ }
}
public Path getDbPath() {
@@ -189,24 +204,6 @@ public boolean isAlive() {
return alive;
}
- /**
- * destroy database.
- */
- public void destroyDb(File fileLocation) {
- resetDbLock.writeLock().lock();
- try {
- logger.debug("Destroying existing database: " + fileLocation);
- Options options = new Options();
- try {
- factory.destroy(fileLocation, options);
- } catch (IOException e) {
- logger.error(e.getMessage(), e);
- }
- } finally {
- resetDbLock.writeLock().unlock();
- }
- }
-
@Override
public String getDBName() {
return dataBaseName;
@@ -248,6 +245,7 @@ public void deleteData(byte[] key) {
}
@Deprecated
+ @VisibleForTesting
@Override
public Set allKeys() {
resetDbLock.readLock().lock();
@@ -265,6 +263,7 @@ public Set allKeys() {
}
@Deprecated
+ @VisibleForTesting
@Override
public Set allValues() {
resetDbLock.readLock().lock();
@@ -384,6 +383,8 @@ public Map prefixQuery(byte[] key) {
}
}
+ @Deprecated
+ @VisibleForTesting
@Override
public long getTotal() throws RuntimeException {
resetDbLock.readLock().lock();
@@ -400,13 +401,6 @@ public long getTotal() throws RuntimeException {
}
}
- private void updateByBatchInner(Map rows) throws Exception {
- try (WriteBatch batch = database.createWriteBatch()) {
- innerBatchUpdate(rows,batch);
- database.write(batch, writeOptions);
- }
- }
-
private void updateByBatchInner(Map rows, WriteOptions options) throws Exception {
try (WriteBatch batch = database.createWriteBatch()) {
innerBatchUpdate(rows,batch);
@@ -426,30 +420,23 @@ private void innerBatchUpdate(Map rows, WriteBatch batch) {
@Override
public void updateByBatch(Map rows, WriteOptionsWrapper options) {
- resetDbLock.readLock().lock();
- try {
- updateByBatchInner(rows, options.level);
- } catch (Exception e) {
- try {
- updateByBatchInner(rows, options.level);
- } catch (Exception e1) {
- throw new RuntimeException(e);
- }
- } finally {
- resetDbLock.readLock().unlock();
- }
+ this.updateByBatch(rows, options.level);
}
@Override
public void updateByBatch(Map rows) {
+ this.updateByBatch(rows, writeOptions);
+ }
+
+ private void updateByBatch(Map rows, WriteOptions options) {
resetDbLock.readLock().lock();
try {
- updateByBatchInner(rows);
+ updateByBatchInner(rows, options);
} catch (Exception e) {
try {
- updateByBatchInner(rows);
+ updateByBatchInner(rows, options);
} catch (Exception e1) {
- throw new RuntimeException(e);
+ throw new RuntimeException(e1);
}
} finally {
resetDbLock.readLock().unlock();
@@ -477,6 +464,24 @@ public void closeDB() {
}
}
+ /**
+ * Returns an iterator over the database.
+ *
+ * CRITICAL: The returned iterator holds native resources and MUST be closed
+ * after use to prevent memory leaks. It is strongly recommended to use a try-with-resources
+ * statement.
+ *
+ *
Example of correct usage:
+ *
{@code
+ * try (DBIterator iterator = db.iterator()) {
+ * while (iterator.hasNext()) {
+ * // ... process entry
+ * }
+ * }
+ * }
+ *
+ * @return a new database iterator that must be closed.
+ */
@Override
public org.tron.core.db.common.iterator.DBIterator iterator() {
return new StoreIterator(getDBIterator());
@@ -486,14 +491,10 @@ public Stream> stream() {
return StreamSupport.stream(spliterator(), false);
}
- public Stream> parallelStream() {
- return StreamSupport.stream(spliterator(), true);
- }
-
@Override
public LevelDbDataSourceImpl newInstance() {
return new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dataBaseName),
- dataBaseName, options, writeOptions);
+ dataBaseName);
}
private DBIterator getDBIterator() {
diff --git a/chainbase/src/main/java/org/tron/common/storage/metric/DbStat.java b/chainbase/src/main/java/org/tron/common/storage/metric/DbStat.java
index c7fecf2a351..eb0362ad2e9 100644
--- a/chainbase/src/main/java/org/tron/common/storage/metric/DbStat.java
+++ b/chainbase/src/main/java/org/tron/common/storage/metric/DbStat.java
@@ -17,7 +17,7 @@ protected void statProperty() {
double size = Double.parseDouble(tmp[2]) * 1048576.0;
Metrics.gaugeSet(MetricKeys.Gauge.DB_SST_LEVEL, files, getEngine(), getName(), level);
Metrics.gaugeSet(MetricKeys.Gauge.DB_SIZE_BYTES, size, getEngine(), getName(), level);
- logger.info("DB {}, level:{},files:{},size:{} M",
+ logger.debug("DB {}, level:{},files:{},size:{} M",
getName(), level, files, size / 1048576.0);
});
} catch (Exception e) {
diff --git a/chainbase/src/main/java/org/tron/common/storage/metric/DbStatService.java b/chainbase/src/main/java/org/tron/common/storage/metric/DbStatService.java
index 402ab087808..b6fa25d5901 100644
--- a/chainbase/src/main/java/org/tron/common/storage/metric/DbStatService.java
+++ b/chainbase/src/main/java/org/tron/common/storage/metric/DbStatService.java
@@ -1,11 +1,10 @@
package org.tron.common.storage.metric;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
+import org.tron.common.es.ExecutorServiceManager;
import org.tron.common.prometheus.Metrics;
import org.tron.core.db.common.DbSourceInter;
import org.tron.core.db2.common.DB;
@@ -13,10 +12,9 @@
@Slf4j(topic = "metrics")
@Component
public class DbStatService {
- private static final ScheduledExecutorService statExecutor =
- Executors.newSingleThreadScheduledExecutor(
- new ThreadFactoryBuilder().setNameFormat("db-stats-thread-%d").build());
-
+ private final String esName = "db-stats";
+ private final ScheduledExecutorService statExecutor =
+ ExecutorServiceManager.newSingleThreadScheduledExecutor(esName);
public void register(DB db) {
if (Metrics.enabled()) {
@@ -32,11 +30,7 @@ public void register(DbSourceInter db) {
public void shutdown() {
if (Metrics.enabled()) {
- try {
- statExecutor.shutdown();
- } catch (Exception e) {
- logger.error("{}", e.getMessage());
- }
+ ExecutorServiceManager.shutdownAndAwaitTermination(statExecutor, esName);
}
}
}
diff --git a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java
index 10c8060ca8d..c7ca698cc3d 100644
--- a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java
+++ b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java
@@ -1,5 +1,6 @@
package org.tron.common.storage.rocksdb;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import com.google.common.primitives.Bytes;
import java.io.File;
@@ -20,27 +21,25 @@
import java.util.stream.Collectors;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.rocksdb.BlockBasedTableConfig;
-import org.rocksdb.BloomFilter;
import org.rocksdb.Checkpoint;
-import org.rocksdb.DirectComparator;
import org.rocksdb.Options;
import org.rocksdb.ReadOptions;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
import org.rocksdb.RocksIterator;
-import org.rocksdb.Statistics;
+import org.rocksdb.Status;
import org.rocksdb.WriteBatch;
import org.rocksdb.WriteOptions;
+import org.tron.common.error.TronDBException;
import org.tron.common.setting.RocksDbSettings;
import org.tron.common.storage.WriteOptionsWrapper;
import org.tron.common.storage.metric.DbStat;
import org.tron.common.utils.FileUtil;
-import org.tron.common.utils.PropUtil;
import org.tron.core.db.common.DbSourceInter;
import org.tron.core.db.common.iterator.RockStoreIterator;
import org.tron.core.db2.common.Instance;
import org.tron.core.db2.common.WrappedByteArray;
+import org.tron.core.exception.TronError;
@Slf4j(topic = "DB")
@@ -48,37 +47,19 @@
public class RocksDbDataSourceImpl extends DbStat implements DbSourceInter,
Iterable>, Instance {
- ReadOptions readOpts;
private String dataBaseName;
private RocksDB database;
- private boolean alive;
+ private volatile boolean alive;
private String parentPath;
private ReadWriteLock resetDbLock = new ReentrantReadWriteLock();
- private static final String KEY_ENGINE = "ENGINE";
- private static final String ROCKSDB = "ROCKSDB";
- private DirectComparator comparator;
+ private Options options;
- public RocksDbDataSourceImpl(String parentPath, String name, RocksDbSettings settings,
- DirectComparator comparator) {
- this.dataBaseName = name;
- this.parentPath = parentPath;
- this.comparator = comparator;
- RocksDbSettings.setRocksDbSettings(settings);
- initDB();
- }
-
- public RocksDbDataSourceImpl(String parentPath, String name, RocksDbSettings settings) {
+ public RocksDbDataSourceImpl(String parentPath, String name) {
this.dataBaseName = name;
this.parentPath = parentPath;
- RocksDbSettings.setRocksDbSettings(settings);
initDB();
}
- public RocksDbDataSourceImpl(String parentPath, String name) {
- this.parentPath = parentPath;
- this.dataBaseName = name;
- }
-
public Path getDbPath() {
return Paths.get(parentPath, dataBaseName);
}
@@ -98,6 +79,9 @@ public void closeDB() {
if (!isAlive()) {
return;
}
+ if (this.options != null) {
+ this.options.close();
+ }
database.close();
alive = false;
} catch (Exception e) {
@@ -119,21 +103,27 @@ public void resetDb() {
}
}
- private boolean quitIfNotAlive() {
+ private void throwIfNotAlive() {
if (!isAlive()) {
- logger.warn("DB {} is not alive.", dataBaseName);
+ throw new TronDBException("DB " + this.getDBName() + " is closed.");
}
- return !isAlive();
}
+ /** copy from {@link org.fusesource.leveldbjni.internal#checkArgNotNull} */
+ private static void checkArgNotNull(Object value, String name) {
+ if (value == null) {
+ throw new IllegalArgumentException("The " + name + " argument cannot be null");
+ }
+ }
+
+ @Deprecated
+ @VisibleForTesting
@Override
public Set allKeys() throws RuntimeException {
- if (quitIfNotAlive()) {
- return null;
- }
resetDbLock.readLock().lock();
- Set result = Sets.newHashSet();
- try (final RocksIterator iter = getRocksIterator()) {
+ try (final ReadOptions readOptions = getReadOptions();
+ final RocksIterator iter = getRocksIterator(readOptions)) {
+ Set result = Sets.newHashSet();
for (iter.seekToFirst(); iter.isValid(); iter.next()) {
result.add(iter.key());
}
@@ -143,14 +133,38 @@ public Set allKeys() throws RuntimeException {
}
}
+ @Deprecated
+ @VisibleForTesting
@Override
public Set allValues() throws RuntimeException {
- return null;
+ resetDbLock.readLock().lock();
+ try (final ReadOptions readOptions = getReadOptions();
+ final RocksIterator iter = getRocksIterator(readOptions)) {
+ Set result = Sets.newHashSet();
+ for (iter.seekToFirst(); iter.isValid(); iter.next()) {
+ result.add(iter.value());
+ }
+ return result;
+ } finally {
+ resetDbLock.readLock().unlock();
+ }
}
+ @Deprecated
+ @VisibleForTesting
@Override
public long getTotal() throws RuntimeException {
- return 0;
+ resetDbLock.readLock().lock();
+ try (final ReadOptions readOptions = getReadOptions();
+ final RocksIterator iter = getRocksIterator(readOptions)) {
+ long total = 0;
+ for (iter.seekToFirst(); iter.isValid(); iter.next()) {
+ total++;
+ }
+ return total;
+ } finally {
+ resetDbLock.readLock().unlock();
+ }
}
@Override
@@ -160,39 +174,10 @@ public String getDBName() {
@Override
public void setDBName(String name) {
+ this.dataBaseName = name;
}
- public boolean checkOrInitEngine() {
- String dir = getDbPath().toString();
- String enginePath = dir + File.separator + "engine.properties";
-
- if (FileUtil.createDirIfNotExists(dir)) {
- if (!FileUtil.createFileIfNotExists(enginePath)) {
- return false;
- }
- } else {
- return false;
- }
-
- // for the first init engine
- String engine = PropUtil.readProperty(enginePath, KEY_ENGINE);
- if (engine.isEmpty() && !PropUtil.writeProperty(enginePath, KEY_ENGINE, ROCKSDB)) {
- return false;
- }
- engine = PropUtil.readProperty(enginePath, KEY_ENGINE);
-
- return ROCKSDB.equals(engine);
- }
-
- public void initDB() {
- if (!checkOrInitEngine()) {
- throw new RuntimeException(
- String.format("failed to check database: %s, engine do not match", dataBaseName));
- }
- initDB(RocksDbSettings.getSettings());
- }
-
- public void initDB(RocksDbSettings settings) {
+ private void initDB() {
resetDbLock.writeLock().lock();
try {
if (isAlive()) {
@@ -202,69 +187,40 @@ public void initDB(RocksDbSettings settings) {
throw new IllegalArgumentException("No name set to the dbStore");
}
- try (Options options = new Options()) {
-
- // most of these options are suggested by https://github.com/facebook/rocksdb/wiki/Set-Up-Options
+ try {
+ logger.debug("Opening database {}.", dataBaseName);
+ final Path dbPath = getDbPath();
- // general options
- if (settings.isEnableStatistics()) {
- options.setStatistics(new Statistics());
- options.setStatsDumpPeriodSec(60);
+ if (!Files.isSymbolicLink(dbPath.getParent())) {
+ Files.createDirectories(dbPath.getParent());
}
- options.setCreateIfMissing(true);
- options.setIncreaseParallelism(1);
- options.setLevelCompactionDynamicLevelBytes(true);
- options.setMaxOpenFiles(settings.getMaxOpenFiles());
-
- // general options supported user config
- options.setNumLevels(settings.getLevelNumber());
- options.setMaxBytesForLevelMultiplier(settings.getMaxBytesForLevelMultiplier());
- options.setMaxBytesForLevelBase(settings.getMaxBytesForLevelBase());
- options.setMaxBackgroundCompactions(settings.getCompactThreads());
- options.setLevel0FileNumCompactionTrigger(settings.getLevel0FileNumCompactionTrigger());
- options.setTargetFileSizeMultiplier(settings.getTargetFileSizeMultiplier());
- options.setTargetFileSizeBase(settings.getTargetFileSizeBase());
- if (comparator != null) {
- options.setComparator(comparator);
- }
-
- // table options
- final BlockBasedTableConfig tableCfg;
- options.setTableFormatConfig(tableCfg = new BlockBasedTableConfig());
- tableCfg.setBlockSize(settings.getBlockSize());
- tableCfg.setBlockCache(RocksDbSettings.getCache());
- tableCfg.setCacheIndexAndFilterBlocks(true);
- tableCfg.setPinL0FilterAndIndexBlocksInCache(true);
- tableCfg.setFilter(new BloomFilter(10, false));
-
- // read options
- readOpts = new ReadOptions();
- readOpts = readOpts.setPrefixSameAsStart(true)
- .setVerifyChecksums(false);
try {
- logger.debug("Opening database {}.", dataBaseName);
- final Path dbPath = getDbPath();
-
- if (!Files.isSymbolicLink(dbPath.getParent())) {
- Files.createDirectories(dbPath.getParent());
+ DbSourceInter.checkOrInitEngine(getEngine(), dbPath.toString(),
+ TronError.ErrCode.ROCKSDB_INIT);
+ this.options = RocksDbSettings.getOptionsByDbName(dataBaseName);
+ database = RocksDB.open(this.options, dbPath.toString());
+ } catch (RocksDBException e) {
+ if (Objects.equals(e.getStatus().getCode(), Status.Code.Corruption)) {
+ logger.error("Database {} corrupted, please delete database directory({}) "
+ + "and restart.", dataBaseName, parentPath, e);
+ } else {
+ logger.error("Open Database {} failed", dataBaseName, e);
}
- try {
- database = RocksDB.open(options, dbPath.toString());
- } catch (RocksDBException e) {
- throw new RuntimeException(
- String.format("failed to open database: %s", dataBaseName), e);
+ if (this.options != null) {
+ this.options.close();
}
-
- alive = true;
- } catch (IOException ioe) {
- throw new RuntimeException(
- String.format("failed to init database: %s", dataBaseName), ioe);
+ throw new TronError(e, TronError.ErrCode.ROCKSDB_INIT);
}
- logger.debug("Init DB {} done.", dataBaseName);
+ alive = true;
+ } catch (IOException ioe) {
+ throw new RuntimeException(
+ String.format("failed to init database: %s", dataBaseName), ioe);
}
+
+ logger.debug("Init DB {} done.", dataBaseName);
} finally {
resetDbLock.writeLock().unlock();
}
@@ -272,11 +228,11 @@ public void initDB(RocksDbSettings settings) {
@Override
public void putData(byte[] key, byte[] value) {
- if (quitIfNotAlive()) {
- return;
- }
resetDbLock.readLock().lock();
try {
+ throwIfNotAlive();
+ checkArgNotNull(key, "key");
+ checkArgNotNull(value, "value");
database.put(key, value);
} catch (RocksDBException e) {
throw new RuntimeException(dataBaseName, e);
@@ -287,11 +243,10 @@ public void putData(byte[] key, byte[] value) {
@Override
public byte[] getData(byte[] key) {
- if (quitIfNotAlive()) {
- return null;
- }
resetDbLock.readLock().lock();
try {
+ throwIfNotAlive();
+ checkArgNotNull(key, "key");
return database.get(key);
} catch (RocksDBException e) {
throw new RuntimeException(dataBaseName, e);
@@ -302,11 +257,10 @@ public byte[] getData(byte[] key) {
@Override
public void deleteData(byte[] key) {
- if (quitIfNotAlive()) {
- return;
- }
resetDbLock.readLock().lock();
try {
+ throwIfNotAlive();
+ checkArgNotNull(key, "key");
database.delete(key);
} catch (RocksDBException e) {
throw new RuntimeException(dataBaseName, e);
@@ -320,74 +274,65 @@ public boolean flush() {
return false;
}
+ /**
+ * Returns an iterator over the database.
+ *
+ * CRITICAL: The returned iterator holds native resources and MUST be closed
+ * after use to prevent memory leaks. It is strongly recommended to use a try-with-resources
+ * statement.
+ *
+ *
Example of correct usage:
+ *
{@code
+ * try (DBIterator iterator = db.iterator()) {
+ * while (iterator.hasNext()) {
+ * // ... process entry
+ * }
+ * }
+ * }
+ *
+ * @return a new database iterator that must be closed.
+ */
@Override
public org.tron.core.db.common.iterator.DBIterator iterator() {
- return new RockStoreIterator(getRocksIterator());
- }
-
- private void updateByBatchInner(Map rows) throws Exception {
- if (quitIfNotAlive()) {
- return;
- }
- try (WriteBatch batch = new WriteBatch()) {
- for (Map.Entry entry : rows.entrySet()) {
- if (entry.getValue() == null) {
- batch.delete(entry.getKey());
- } else {
- batch.put(entry.getKey(), entry.getValue());
- }
- }
- database.write(new WriteOptions(), batch);
- }
+ ReadOptions readOptions = getReadOptions();
+ return new RockStoreIterator(getRocksIterator(readOptions), readOptions);
}
private void updateByBatchInner(Map rows, WriteOptions options)
throws Exception {
- if (quitIfNotAlive()) {
- return;
- }
try (WriteBatch batch = new WriteBatch()) {
for (Map.Entry entry : rows.entrySet()) {
+ checkArgNotNull(entry.getKey(), "key");
if (entry.getValue() == null) {
batch.delete(entry.getKey());
} else {
batch.put(entry.getKey(), entry.getValue());
}
}
+ throwIfNotAlive();
database.write(options, batch);
}
}
@Override
public void updateByBatch(Map rows, WriteOptionsWrapper optionsWrapper) {
- if (quitIfNotAlive()) {
- return;
- }
- resetDbLock.readLock().lock();
- try {
- updateByBatchInner(rows, optionsWrapper.rocks);
- } catch (Exception e) {
- try {
- updateByBatchInner(rows);
- } catch (Exception e1) {
- throw new RuntimeException(dataBaseName, e1);
- }
- } finally {
- resetDbLock.readLock().unlock();
- }
+ this.updateByBatch(rows, optionsWrapper.rocks);
}
@Override
public void updateByBatch(Map rows) {
- if (quitIfNotAlive()) {
- return;
+ try (WriteOptions writeOptions = new WriteOptions()) {
+ this.updateByBatch(rows, writeOptions);
}
+ }
+
+ private void updateByBatch(Map rows, WriteOptions options) {
resetDbLock.readLock().lock();
try {
- updateByBatchInner(rows);
+ updateByBatchInner(rows, options);
} catch (Exception e) {
try {
- updateByBatchInner(rows);
+ updateByBatchInner(rows, options);
} catch (Exception e1) {
throw new RuntimeException(dataBaseName, e1);
}
@@ -397,14 +342,12 @@ public void updateByBatch(Map rows) {
}
public List getKeysNext(byte[] key, long limit) {
- if (quitIfNotAlive()) {
- return new ArrayList<>();
- }
if (limit <= 0) {
return new ArrayList<>();
}
resetDbLock.readLock().lock();
- try (RocksIterator iter = getRocksIterator()) {
+ try (final ReadOptions readOptions = getReadOptions();
+ final RocksIterator iter = getRocksIterator(readOptions)) {
List result = new ArrayList<>();
long i = 0;
for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) {
@@ -417,14 +360,12 @@ public List getKeysNext(byte[] key, long limit) {
}
public Map getNext(byte[] key, long limit) {
- if (quitIfNotAlive()) {
- return null;
- }
if (limit <= 0) {
return Collections.emptyMap();
}
resetDbLock.readLock().lock();
- try (RocksIterator iter = getRocksIterator()) {
+ try (final ReadOptions readOptions = getReadOptions();
+ final RocksIterator iter = getRocksIterator(readOptions)) {
Map result = new HashMap<>();
long i = 0;
for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) {
@@ -438,11 +379,9 @@ public Map getNext(byte[] key, long limit) {
@Override
public Map prefixQuery(byte[] key) {
- if (quitIfNotAlive()) {
- return null;
- }
resetDbLock.readLock().lock();
- try (RocksIterator iterator = getRocksIterator()) {
+ try (final ReadOptions readOptions = getReadOptions();
+ final RocksIterator iterator = getRocksIterator(readOptions)) {
Map result = new HashMap<>();
for (iterator.seek(key); iterator.isValid(); iterator.next()) {
if (Bytes.indexOf(iterator.key(), key) == 0) {
@@ -458,14 +397,12 @@ public Map prefixQuery(byte[] key) {
}
public Set getlatestValues(long limit) {
- if (quitIfNotAlive()) {
- return null;
- }
if (limit <= 0) {
return Sets.newHashSet();
}
resetDbLock.readLock().lock();
- try (RocksIterator iter = getRocksIterator()) {
+ try (final ReadOptions readOptions = getReadOptions();
+ final RocksIterator iter = getRocksIterator(readOptions)) {
Set result = Sets.newHashSet();
long i = 0;
for (iter.seekToLast(); iter.isValid() && i < limit; iter.prev(), i++) {
@@ -477,16 +414,13 @@ public Set getlatestValues(long limit) {
}
}
-
public Set getValuesNext(byte[] key, long limit) {
- if (quitIfNotAlive()) {
- return null;
- }
if (limit <= 0) {
return Sets.newHashSet();
}
resetDbLock.readLock().lock();
- try (RocksIterator iter = getRocksIterator()) {
+ try (final ReadOptions readOptions = getReadOptions();
+ final RocksIterator iter = getRocksIterator(readOptions)) {
Set result = Sets.newHashSet();
long i = 0;
for (iter.seek(key); iter.isValid() && i < limit; iter.next(), i++) {
@@ -499,14 +433,56 @@ public Set getValuesNext(byte[] key, long limit) {
}
public void backup(String dir) throws RocksDBException {
- Checkpoint cp = Checkpoint.create(database);
- cp.createCheckpoint(dir + this.getDBName());
+ throwIfNotAlive();
+ try (Checkpoint cp = Checkpoint.create(database)) {
+ cp.createCheckpoint(dir + this.getDBName());
+ }
}
- private RocksIterator getRocksIterator() {
- try ( ReadOptions readOptions = new ReadOptions().setFillCache(false)) {
- return database.newIterator(readOptions);
- }
+ /**
+ * Returns an iterator over the database.
+ *
+ * CRITICAL: The returned iterator holds native resources and MUST be closed
+ * after use to prevent memory leaks. It is strongly recommended to use a try-with-resources
+ * statement.
+ *
+ *
Example of correct usage:
+ *
{@code
+ * try ( ReadOptions readOptions = new ReadOptions().setFillCache(false);
+ * RocksIterator iterator = getRocksIterator(readOptions)) {
+ * iterator.seekToFirst();
+ * // do something
+ * }
+ * }
+ *
+ * @return a new database iterator that must be closed.
+ */
+ private RocksIterator getRocksIterator(ReadOptions readOptions) {
+ throwIfNotAlive();
+ return database.newIterator(readOptions);
+ }
+
+ /**
+ * Returns an ReadOptions.
+ *
+ * CRITICAL: The returned ReadOptions holds native resources and MUST be closed
+ * after use to prevent memory leaks. It is strongly recommended to use a try-with-resources
+ * statement.
+ *
+ *
Example of correct usage:
+ *
{@code
+ * try (ReadOptions readOptions = getReadOptions();
+ * RocksIterator iterator = getRocksIterator(readOptions)) {
+ * iterator.seekToFirst();
+ * // do something
+ * }
+ * }
+ *
+ * @return a new database iterator that must be closed.
+ */
+ private ReadOptions getReadOptions() {
+ throwIfNotAlive();
+ return new ReadOptions().setFillCache(false);
}
public boolean deleteDbBakPath(String dir) {
@@ -515,7 +491,7 @@ public boolean deleteDbBakPath(String dir) {
@Override
public RocksDbDataSourceImpl newInstance() {
- return new RocksDbDataSourceImpl(parentPath, dataBaseName, RocksDbSettings.getSettings());
+ return new RocksDbDataSourceImpl(parentPath, dataBaseName);
}
diff --git a/chainbase/src/main/java/org/tron/common/utils/Commons.java b/chainbase/src/main/java/org/tron/common/utils/Commons.java
index 55542d494b4..b121e84ecfe 100644
--- a/chainbase/src/main/java/org/tron/common/utils/Commons.java
+++ b/chainbase/src/main/java/org/tron/common/utils/Commons.java
@@ -1,5 +1,8 @@
package org.tron.common.utils;
+import static org.tron.common.math.Maths.addExact;
+import static org.tron.common.math.Maths.subtractExact;
+
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.tron.common.parameter.CommonParameter;
@@ -55,16 +58,18 @@ public static byte[] decodeFromBase58Check(String addressBase58) {
return address;
}
- public static void adjustBalance(AccountStore accountStore, byte[] accountAddress, long amount)
+ public static void adjustBalance(AccountStore accountStore, byte[] accountAddress, long amount,
+ boolean useStrict)
throws BalanceInsufficientException {
AccountCapsule account = accountStore.getUnchecked(accountAddress);
- adjustBalance(accountStore, account, amount);
+ adjustBalance(accountStore, account, amount, useStrict);
}
/**
* judge balance.
*/
- public static void adjustBalance(AccountStore accountStore, AccountCapsule account, long amount)
+ public static void adjustBalance(AccountStore accountStore, AccountCapsule account, long amount,
+ boolean useStrict)
throws BalanceInsufficientException {
long balance = account.getBalance();
@@ -77,7 +82,7 @@ public static void adjustBalance(AccountStore accountStore, AccountCapsule accou
String.format("%s insufficient balance, balance: %d, amount: %d",
StringUtil.createReadableString(account.createDbKey()), balance, -amount));
}
- account.setBalance(Math.addExact(balance, amount));
+ account.setBalance(addExact(balance, amount, useStrict));
accountStore.put(account.getAddress().toByteArray(), account);
}
@@ -137,8 +142,9 @@ public static void adjustAssetBalanceV2(AccountCapsule account, String AssetID,
public static void adjustTotalShieldedPoolValue(long valueBalance,
DynamicPropertiesStore dynamicPropertiesStore) throws BalanceInsufficientException {
- long totalShieldedPoolValue = Math
- .subtractExact(dynamicPropertiesStore.getTotalShieldedPoolValue(), valueBalance);
+ long totalShieldedPoolValue = subtractExact(
+ dynamicPropertiesStore.getTotalShieldedPoolValue(), valueBalance,
+ dynamicPropertiesStore.disableJavaLangMath());
if (totalShieldedPoolValue < 0) {
throw new BalanceInsufficientException(String.format(
"total shielded pool value can not below 0, actual: %d", totalShieldedPoolValue));
diff --git a/chainbase/src/main/java/org/tron/common/utils/ForkController.java b/chainbase/src/main/java/org/tron/common/utils/ForkController.java
index db4d77fd809..a702234f4cd 100644
--- a/chainbase/src/main/java/org/tron/common/utils/ForkController.java
+++ b/chainbase/src/main/java/org/tron/common/utils/ForkController.java
@@ -1,5 +1,6 @@
package org.tron.common.utils;
+import static org.tron.common.math.Maths.ceil;
import static org.tron.common.utils.StringUtil.encode58Check;
import com.google.common.collect.Maps;
@@ -19,6 +20,7 @@
import org.tron.core.capsule.BlockCapsule;
import org.tron.core.config.Parameter.ForkBlockVersionConsts;
import org.tron.core.config.Parameter.ForkBlockVersionEnum;
+import org.tron.core.store.DynamicPropertiesStore;
@Slf4j(topic = "utils")
public class ForkController {
@@ -37,6 +39,17 @@ public class ForkController {
public void init(ChainBaseManager manager) {
this.manager = manager;
+ DynamicPropertiesStore store = manager.getDynamicPropertiesStore();
+ int latestVersion = store.getLatestVersion();
+ if (latestVersion == 0) {
+ for (ForkBlockVersionEnum version : ForkBlockVersionEnum.values()) {
+ int v = version.getValue();
+ if (pass(v) && latestVersion < v) {
+ latestVersion = v;
+ }
+ }
+ store.saveLatestVersion(latestVersion);
+ }
}
public boolean pass(ForkBlockVersionEnum forkBlockVersionEnum) {
@@ -44,6 +57,9 @@ public boolean pass(ForkBlockVersionEnum forkBlockVersionEnum) {
}
public synchronized boolean pass(int version) {
+ if (manager == null) {
+ throw new IllegalStateException("not inited");
+ }
if (version > ForkBlockVersionEnum.VERSION_4_0.getValue()) {
return passNew(version);
} else {
@@ -63,7 +79,7 @@ private boolean passOld(int version) {
private boolean passNew(int version) {
ForkBlockVersionEnum versionEnum = ForkBlockVersionEnum.getForkBlockVersionEnum(version);
if (versionEnum == null) {
- logger.error("Not exist block version: {}.", version);
+ logger.warn("Not exist block version: {}.", version);
return false;
}
long latestBlockTime = manager.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
@@ -83,8 +99,8 @@ private boolean passNew(int version) {
++count;
}
}
- return count >= Math
- .ceil((double) versionEnum.getHardForkRate() * manager.getWitnesses().size() / 100);
+ return count >= ceil((double) versionEnum.getHardForkRate() * stats.length / 100,
+ manager.getDynamicPropertiesStore().disableJavaLangMath());
}
@@ -113,9 +129,9 @@ private boolean check(byte[] stats) {
private void downgrade(int version, int slot) {
for (ForkBlockVersionEnum versionEnum : ForkBlockVersionEnum.values()) {
int versionValue = versionEnum.getValue();
- if (versionValue > version) {
+ if (versionValue > version && !pass(versionValue)) {
byte[] stats = manager.getDynamicPropertiesStore().statsByVersion(versionValue);
- if (!check(stats) && Objects.nonNull(stats)) {
+ if (Objects.nonNull(stats)) {
stats[slot] = VERSION_DOWNGRADE;
manager.getDynamicPropertiesStore().statsByVersion(versionValue, stats);
}
@@ -126,15 +142,13 @@ private void downgrade(int version, int slot) {
private void upgrade(int version, int slotSize) {
for (ForkBlockVersionEnum versionEnum : ForkBlockVersionEnum.values()) {
int versionValue = versionEnum.getValue();
- if (versionValue < version) {
+ if (versionValue < version && !pass(versionValue)) {
byte[] stats = manager.getDynamicPropertiesStore().statsByVersion(versionValue);
- if (!check(stats)) {
- if (stats == null || stats.length == 0) {
- stats = new byte[slotSize];
- }
- Arrays.fill(stats, VERSION_UPGRADE);
- manager.getDynamicPropertiesStore().statsByVersion(versionValue, stats);
+ if (stats == null || stats.length == 0) {
+ stats = new byte[slotSize];
}
+ Arrays.fill(stats, VERSION_UPGRADE);
+ manager.getDynamicPropertiesStore().statsByVersion(versionValue, stats);
}
}
}
@@ -152,6 +166,10 @@ public synchronized void update(BlockCapsule blockCapsule) {
return;
}
+ if (manager.getDynamicPropertiesStore().getLatestVersion() >= version) {
+ return;
+ }
+
downgrade(version, slot);
byte[] stats = manager.getDynamicPropertiesStore().statsByVersion(version);
@@ -159,8 +177,9 @@ public synchronized void update(BlockCapsule blockCapsule) {
stats = new byte[witnesses.size()];
}
- if (check(stats)) {
+ if (pass(version)) {
upgrade(version, stats.length);
+ manager.getDynamicPropertiesStore().saveLatestVersion(version);
return;
}
@@ -182,11 +201,12 @@ public synchronized void update(BlockCapsule blockCapsule) {
}
public synchronized void reset() {
+ int size = manager.getWitnessScheduleStore().getActiveWitnesses().size();
for (ForkBlockVersionEnum versionEnum : ForkBlockVersionEnum.values()) {
int versionValue = versionEnum.getValue();
byte[] stats = manager.getDynamicPropertiesStore().statsByVersion(versionValue);
if (Objects.nonNull(stats) && !pass(versionValue)) {
- Arrays.fill(stats, VERSION_DOWNGRADE);
+ stats = new byte[size];
manager.getDynamicPropertiesStore().statsByVersion(versionValue, stats);
}
}
@@ -209,4 +229,5 @@ private ForkController getInstance() {
return instance;
}
}
+
}
diff --git a/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java b/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java
index 940a107a2ac..7179045ea7e 100644
--- a/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java
+++ b/chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java
@@ -25,6 +25,7 @@
import org.tron.common.crypto.SignInterface;
import org.tron.common.crypto.SignUtils;
import org.tron.core.config.Parameter.ChainConstant;
+import org.tron.core.exception.TronError;
@Slf4j(topic = "app")
public class LocalWitnesses {
@@ -32,6 +33,7 @@ public class LocalWitnesses {
@Getter
private List privateKeys = Lists.newArrayList();
+ @Getter
private byte[] witnessAccountAddress;
public LocalWitnesses() {
@@ -45,21 +47,11 @@ public LocalWitnesses(List privateKeys) {
setPrivateKeys(privateKeys);
}
- public byte[] getWitnessAccountAddress(boolean isECKeyCryptoEngine) {
- if (witnessAccountAddress == null) {
- byte[] privateKey = ByteArray.fromHexString(getPrivateKey());
- final SignInterface cryptoEngine = SignUtils.fromPrivate(privateKey, isECKeyCryptoEngine);
- this.witnessAccountAddress = cryptoEngine.getAddress();
- }
- return witnessAccountAddress;
- }
-
- public void setWitnessAccountAddress(final byte[] localWitnessAccountAddress) {
- this.witnessAccountAddress = localWitnessAccountAddress;
- }
-
- public void initWitnessAccountAddress(boolean isECKeyCryptoEngine) {
- if (witnessAccountAddress == null) {
+ public void initWitnessAccountAddress(final byte[] witnessAddress,
+ boolean isECKeyCryptoEngine) {
+ if (witnessAddress != null) {
+ this.witnessAccountAddress = witnessAddress;
+ } else if (!CollectionUtils.isEmpty(privateKeys)) {
byte[] privateKey = ByteArray.fromHexString(getPrivateKey());
final SignInterface ecKey = SignUtils.fromPrivate(privateKey,
isECKeyCryptoEngine);
@@ -85,11 +77,16 @@ private void validate(String privateKey) {
privateKey = privateKey.substring(2);
}
- if (StringUtils.isNotBlank(privateKey)
- && privateKey.length() != ChainConstant.PRIVATE_KEY_LENGTH) {
- throw new IllegalArgumentException(
- String.format("private key must be %d-bits hex string, actual: %d",
- ChainConstant.PRIVATE_KEY_LENGTH, privateKey.length()));
+ if (StringUtils.isBlank(privateKey)
+ || privateKey.length() != ChainConstant.PRIVATE_KEY_LENGTH) {
+ throw new TronError(String.format("private key must be %d hex string, actual: %d",
+ ChainConstant.PRIVATE_KEY_LENGTH,
+ StringUtils.isBlank(privateKey) ? 0 : privateKey.length()),
+ TronError.ErrCode.WITNESS_INIT);
+ }
+ if (!StringUtil.isHexadecimal(privateKey)) {
+ throw new TronError("private key must be hex string",
+ TronError.ErrCode.WITNESS_INIT);
}
}
diff --git a/chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRockDB.java b/chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRockDB.java
deleted file mode 100644
index d3812f0f6bc..00000000000
--- a/chainbase/src/main/java/org/tron/common/utils/MarketOrderPriceComparatorForRockDB.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.tron.common.utils;
-
-import org.rocksdb.ComparatorOptions;
-import org.rocksdb.DirectSlice;
-import org.rocksdb.util.DirectBytewiseComparator;
-import org.tron.core.capsule.utils.MarketUtils;
-
-public class MarketOrderPriceComparatorForRockDB extends DirectBytewiseComparator {
-
- public MarketOrderPriceComparatorForRockDB(final ComparatorOptions copt) {
- super(copt);
- }
-
- @Override
- public String name() {
- return "MarketOrderPriceComparator";
- }
-
- @Override
- public int compare(final DirectSlice a, final DirectSlice b) {
- return MarketUtils.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/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java b/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java
index 16df43f1534..0c7c77bd23f 100644
--- a/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java
+++ b/chainbase/src/main/java/org/tron/common/utils/StorageUtils.java
@@ -1,15 +1,20 @@
package org.tron.common.utils;
import static org.tron.common.parameter.CommonParameter.ENERGY_LIMIT_HARD_FORK;
+import static org.tron.core.db.common.DbSourceInter.LEVELDB;
import java.io.File;
import org.apache.commons.lang3.StringUtils;
import org.iq80.leveldb.Options;
+import org.slf4j.LoggerFactory;
import org.tron.common.parameter.CommonParameter;
+import org.tron.core.Constant;
public class StorageUtils {
+ private static final org.slf4j.Logger levelDbLogger = LoggerFactory.getLogger(LEVELDB);
+
public static boolean getEnergyLimitHardFork() {
return ENERGY_LIMIT_HARD_FORK;
}
@@ -52,9 +57,16 @@ public static String getOutputDirectory() {
}
public static Options getOptionsByDbName(String dbName) {
+ Options options;
if (hasProperty(dbName)) {
- return getProperty(dbName).getDbOptions();
+ options = getProperty(dbName).getDbOptions();
+ } else {
+ options = CommonParameter.getInstance().getStorage().newDefaultDbOptions(dbName);
+ }
+ if (Constant.MARKET_PAIR_PRICE_TO_ORDER.equals(dbName)) {
+ options.comparator(new MarketOrderPriceComparatorForLevelDB());
}
- return CommonParameter.getInstance().getStorage().newDefaultDbOptions(dbName);
+ options.logger(message -> levelDbLogger.info("{} {}", dbName, message));
+ return options;
}
}
diff --git a/chainbase/src/main/java/org/tron/common/zksnark/JLibrustzcash.java b/chainbase/src/main/java/org/tron/common/zksnark/JLibrustzcash.java
index f7b8c17b57f..3700d300411 100644
--- a/chainbase/src/main/java/org/tron/common/zksnark/JLibrustzcash.java
+++ b/chainbase/src/main/java/org/tron/common/zksnark/JLibrustzcash.java
@@ -29,65 +29,42 @@
@Slf4j
public class JLibrustzcash {
- private static Librustzcash INSTANCE;
+ private static Librustzcash INSTANCE = LibrustzcashWrapper.getInstance();
public static void librustzcashZip32XskMaster(Zip32XskMasterParams params) {
- if (!isOpenZen()) {
- return;
- }
INSTANCE.librustzcashZip32XskMaster(params.getData(), params.getSize(), params.getM_bytes());
}
public static void librustzcashInitZksnarkParams(InitZksnarkParams params) {
- if (!isOpenZen()) {
- return;
- }
INSTANCE.librustzcashInitZksnarkParams(params.getSpend_path(),
params.getSpend_hash(), params.getOutput_path(), params.getOutput_hash());
}
public static void librustzcashZip32XskDerive(Zip32XskDeriveParams params) {
- if (!isOpenZen()) {
- return;
- }
INSTANCE.librustzcashZip32XskDerive(params.getData(), params.getSize(), params.getM_bytes());
}
public static boolean librustzcashZip32XfvkAddress(Zip32XfvkAddressParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashZip32XfvkAddress(params.getXfvk(), params.getJ(),
params.getJ_ret(), params.getAddr_ret());
}
public static void librustzcashCrhIvk(CrhIvkParams params) {
- if (!isOpenZen()) {
- return;
- }
INSTANCE.librustzcashCrhIvk(params.getAk(), params.getNk(), params.getIvk());
}
public static boolean librustzcashKaAgree(KaAgreeParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingKaAgree(params.getP(), params.getSk(), params.getResult());
}
public static boolean librustzcashComputeCm(ComputeCmParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingComputeCm(params.getD(), params.getPkD(),
params.getValue(), params.getR(), params.getCm());
}
public static boolean librustzcashComputeNf(ComputeNfParams params) {
- if (isOpenZen()) {
- INSTANCE.librustzcashSaplingComputeNf(params.getD(), params.getPkD(), params.getValue(),
- params.getR(), params.getAk(), params.getNk(), params.getPosition(), params.getResult());
- }
+ INSTANCE.librustzcashSaplingComputeNf(params.getD(), params.getPkD(), params.getValue(),
+ params.getR(), params.getAk(), params.getNk(), params.getPosition(), params.getResult());
return true;
}
@@ -96,9 +73,6 @@ public static boolean librustzcashComputeNf(ComputeNfParams params) {
* @return ak 32 bytes
*/
public static byte[] librustzcashAskToAk(byte[] ask) throws ZksnarkException {
- if (!isOpenZen()) {
- return ByteUtil.EMPTY_BYTE_ARRAY;
- }
LibrustzcashParam.valid32Params(ask);
byte[] ak = new byte[32];
INSTANCE.librustzcashAskToAk(ask, ak);
@@ -110,9 +84,6 @@ public static byte[] librustzcashAskToAk(byte[] ask) throws ZksnarkException {
* @return 32 bytes
*/
public static byte[] librustzcashNskToNk(byte[] nsk) throws ZksnarkException {
- if (!isOpenZen()) {
- return ByteUtil.EMPTY_BYTE_ARRAY;
- }
LibrustzcashParam.valid32Params(nsk);
byte[] nk = new byte[32];
INSTANCE.librustzcashNskToNk(nsk, nk);
@@ -125,26 +96,17 @@ public static byte[] librustzcashNskToNk(byte[] nsk) throws ZksnarkException {
* @return r: random number, less than r_J, 32 bytes
*/
public static byte[] librustzcashSaplingGenerateR(byte[] r) throws ZksnarkException {
- if (!isOpenZen()) {
- return ByteUtil.EMPTY_BYTE_ARRAY;
- }
LibrustzcashParam.valid32Params(r);
INSTANCE.librustzcashSaplingGenerateR(r);
return r;
}
public static boolean librustzcashSaplingKaDerivepublic(KaDerivepublicParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingKaDerivepublic(params.getDiversifier(), params.getEsk(),
params.getResult());
}
public static long librustzcashSaplingProvingCtxInit() {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE.librustzcashSaplingProvingCtxInit();
}
@@ -154,17 +116,11 @@ public static long librustzcashSaplingProvingCtxInit() {
* @param d 11 bytes
*/
public static boolean librustzcashCheckDiversifier(byte[] d) throws ZksnarkException {
- if (!isOpenZen()) {
- return true;
- }
LibrustzcashParam.valid11Params(d);
return INSTANCE.librustzcashCheckDiversifier(d);
}
public static boolean librustzcashSaplingSpendProof(SpendProofParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingSpendProof(params.getCtx(), params.getAk(),
params.getNsk(), params.getD(), params.getR(), params.getAlpha(), params.getValue(),
params.getAnchor(), params.getVoucherPath(), params.getCv(), params.getRk(),
@@ -172,26 +128,17 @@ public static boolean librustzcashSaplingSpendProof(SpendProofParams params) {
}
public static boolean librustzcashSaplingOutputProof(OutputProofParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingOutputProof(params.getCtx(), params.getEsk(),
params.getD(), params.getPkD(), params.getR(), params.getValue(), params.getCv(),
params.getZkproof());
}
public static boolean librustzcashSaplingSpendSig(SpendSigParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingSpendSig(params.getAsk(), params.getAlpha(),
params.getSigHash(), params.getResult());
}
public static boolean librustzcashSaplingBindingSig(BindingSigParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingBindingSig(params.getCtx(),
params.getValueBalance(), params.getSighash(), params.getResult());
}
@@ -203,74 +150,47 @@ public static boolean librustzcashSaplingBindingSig(BindingSigParams params) {
* @param data 32 bytes
*/
public static void librustzcashToScalar(byte[] value, byte[] data) throws ZksnarkException {
- if (!isOpenZen()) {
- return;
- }
LibrustzcashParam.validParamLength(value, 64);
LibrustzcashParam.valid32Params(data);
INSTANCE.librustzcashToScalar(value, data);
}
public static void librustzcashSaplingProvingCtxFree(long ctx) {
- if (!isOpenZen()) {
- return;
- }
INSTANCE.librustzcashSaplingProvingCtxFree(ctx);
}
public static long librustzcashSaplingVerificationCtxInit() {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE.librustzcashSaplingVerificationCtxInit();
}
public static boolean librustzcashSaplingCheckSpend(CheckSpendParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingCheckSpend(params.getCtx(), params.getCv(),
params.getAnchor(), params.getNullifier(), params.getRk(), params.getZkproof(),
params.getSpendAuthSig(), params.getSighashValue());
}
public static boolean librustzcashSaplingCheckOutput(CheckOutputParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingCheckOutput(params.getCtx(), params.getCv(),
params.getCm(), params.getEphemeralKey(), params.getZkproof());
}
public static boolean librustzcashSaplingFinalCheck(FinalCheckParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingFinalCheck(params.getCtx(),
params.getValueBalance(), params.getBindingSig(), params.getSighashValue());
}
public static boolean librustzcashSaplingCheckSpendNew(CheckSpendNewParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingCheckSpendNew(params.getCv(),
params.getAnchor(), params.getNullifier(), params.getRk(), params.getZkproof(),
params.getSpendAuthSig(), params.getSighashValue());
}
public static boolean librustzcashSaplingCheckOutputNew(CheckOutputNewParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashSaplingCheckOutputNew(params.getCv(), params.getCm(),
params.getEphemeralKey(), params.getZkproof());
}
public static boolean librustzcashSaplingFinalCheckNew(FinalCheckNewParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE
.librustzcashSaplingFinalCheckNew(params.getValueBalance(), params.getBindingSig(),
params.getSighashValue(), params.getSpendCv(), params.getSpendCvLen(),
@@ -278,23 +198,14 @@ public static boolean librustzcashSaplingFinalCheckNew(FinalCheckNewParams param
}
public static void librustzcashSaplingVerificationCtxFree(long ctx) {
- if (!isOpenZen()) {
- return;
- }
INSTANCE.librustzcashSaplingVerificationCtxFree(ctx);
}
public static boolean librustzcashIvkToPkd(IvkToPkdParams params) {
- if (!isOpenZen()) {
- return true;
- }
return INSTANCE.librustzcashIvkToPkd(params.getIvk(), params.getD(), params.getPkD());
}
public static void librustzcashMerkleHash(MerkleHashParams params) {
- if (!isOpenZen()) {
- return;
- }
INSTANCE.librustzcashMerkleHash(params.getDepth(), params.getA(), params.getB(),
params.getResult());
}
@@ -303,19 +214,7 @@ public static void librustzcashMerkleHash(MerkleHashParams params) {
* @param result uncommitted value, 32 bytes
*/
public static void librustzcashTreeUncommitted(byte[] result) throws ZksnarkException {
- if (!isOpenZen()) {
- return;
- }
LibrustzcashParam.valid32Params(result);
INSTANCE.librustzcashTreeUncommitted(result);
}
-
- public static boolean isOpenZen() {
- boolean res = CommonParameter.getInstance().isFullNodeAllowShieldedTransactionArgs();
- if (res) {
- INSTANCE = LibrustzcashWrapper.getInstance();
- }
- return res;
- }
-
}
diff --git a/chainbase/src/main/java/org/tron/common/zksnark/JLibsodium.java b/chainbase/src/main/java/org/tron/common/zksnark/JLibsodium.java
index 0159ba0bf6b..0713d74b7bd 100644
--- a/chainbase/src/main/java/org/tron/common/zksnark/JLibsodium.java
+++ b/chainbase/src/main/java/org/tron/common/zksnark/JLibsodium.java
@@ -12,37 +12,25 @@ public class JLibsodium {
public static final int CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES = 16;
public static final int CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES = 12;
- private static Libsodium INSTANCE;
+ private static Libsodium INSTANCE = LibsodiumWrapper.getInstance();
public static int cryptoGenerichashBlake2bInitSaltPersonal(Blake2bInitSaltPersonalParams params) {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE
.cryptoGenerichashBlake2BInitSaltPersonal(params.getState(), params.getKey(),
params.getKeyLen(), params.getOutLen(), params.getSalt(), params.getPersonal());
}
public static int cryptoGenerichashBlake2bUpdate(Blake2bUpdateParams params) {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE
.cryptoGenerichashBlake2BUpdate(params.getState(), params.getIn(), params.getInLen());
}
public static int cryptoGenerichashBlake2bFinal(Blake2bFinalParams params) {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE.cryptoGenerichashBlake2BFinal(params.getState(),
params.getOut(), params.getOutLen());
}
public static int cryptoGenerichashBlack2bSaltPersonal(Black2bSaltPersonalParams params) {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE.cryptoGenerichashBlake2BSaltPersonal(params.getOut(), params.getOutLen(),
params.getIn(), params.getInLen(), params.getKey(), params.getKeyLen(),
params.getSalt(),
@@ -51,9 +39,6 @@ public static int cryptoGenerichashBlack2bSaltPersonal(Black2bSaltPersonalParams
public static int cryptoAeadChacha20poly1305IetfDecrypt(
Chacha20poly1305IetfDecryptParams params) {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE
.cryptoAeadChacha20Poly1305IetfDecrypt(params.getM(), params.getMLenP(),
params.getNSec(),
@@ -63,9 +48,6 @@ public static int cryptoAeadChacha20poly1305IetfDecrypt(
public static int cryptoAeadChacha20Poly1305IetfEncrypt(
Chacha20Poly1305IetfEncryptParams params) {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE
.cryptoAeadChacha20Poly1305IetfEncrypt(params.getC(), params.getCLenP(), params.getM(),
params.getMLen(), params.getAd(), params.getAdLen(),
@@ -73,25 +55,10 @@ public static int cryptoAeadChacha20Poly1305IetfEncrypt(
}
public static long initState() {
- if (!isOpenZen()) {
- return 0;
- }
return INSTANCE.cryptoGenerichashBlake2BStateInit();
}
public static void freeState(long state) {
- if (!isOpenZen()) {
- return;
- }
INSTANCE.cryptoGenerichashBlake2BStateFree(state);
}
-
- private static boolean isOpenZen() {
- boolean res = CommonParameter.getInstance()
- .isFullNodeAllowShieldedTransactionArgs();
- if (res) {
- INSTANCE = LibsodiumWrapper.getInstance();
- }
- return res;
- }
}
diff --git a/chainbase/src/main/java/org/tron/core/ChainBaseManager.java b/chainbase/src/main/java/org/tron/core/ChainBaseManager.java
index a2f3cd67b8e..21f0bac8d77 100644
--- a/chainbase/src/main/java/org/tron/core/ChainBaseManager.java
+++ b/chainbase/src/main/java/org/tron/core/ChainBaseManager.java
@@ -4,6 +4,7 @@
import com.google.protobuf.ByteString;
import java.util.List;
+import javax.annotation.PostConstruct;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -28,7 +29,6 @@
import org.tron.core.db.RecentBlockStore;
import org.tron.core.db.RecentTransactionStore;
import org.tron.core.db.TransactionStore;
-import org.tron.core.db2.core.ITronChainBase;
import org.tron.core.exception.BadItemException;
import org.tron.core.exception.HeaderNotFound;
import org.tron.core.exception.ItemNotFoundException;
@@ -43,6 +43,7 @@
import org.tron.core.store.AssetIssueV2Store;
import org.tron.core.store.BalanceTraceStore;
import org.tron.core.store.CodeStore;
+import org.tron.core.store.ContractStateStore;
import org.tron.core.store.ContractStore;
import org.tron.core.store.DelegatedResourceAccountIndexStore;
import org.tron.core.store.DelegatedResourceStore;
@@ -143,6 +144,9 @@ public class ChainBaseManager {
private ContractStore contractStore;
@Autowired
@Getter
+ private ContractStateStore contractStateStore;
+ @Autowired
+ @Getter
private DelegatedResourceStore delegatedResourceStore;
@Autowired
@Getter
@@ -232,52 +236,17 @@ public class ChainBaseManager {
@Autowired
private DbStatService dbStatService;
- public void closeOneStore(ITronChainBase database) {
- logger.info("******** Begin to close {}. ********", database.getName());
- try {
- database.close();
- } catch (Exception e) {
- logger.info("Failed to close {}.", database.getName(), e);
- } finally {
- logger.info("******** End to close {}. ********", database.getName());
- }
- }
+ @Getter
+ @Setter
+ private NodeType nodeType;
- public void closeAllStore() {
- dbStatService.shutdown();
- closeOneStore(transactionRetStore);
- closeOneStore(recentBlockStore);
- closeOneStore(transactionHistoryStore);
- closeOneStore(transactionStore);
- closeOneStore(accountStore);
- closeOneStore(blockStore);
- closeOneStore(blockIndexStore);
- closeOneStore(accountIdIndexStore);
- closeOneStore(accountIndexStore);
- closeOneStore(witnessScheduleStore);
- closeOneStore(assetIssueStore);
- closeOneStore(dynamicPropertiesStore);
- closeOneStore(abiStore);
- closeOneStore(codeStore);
- closeOneStore(contractStore);
- closeOneStore(storageRowStore);
- closeOneStore(exchangeStore);
- closeOneStore(proposalStore);
- closeOneStore(votesStore);
- closeOneStore(delegatedResourceStore);
- closeOneStore(delegatedResourceAccountIndexStore);
- closeOneStore(assetIssueV2Store);
- closeOneStore(exchangeV2Store);
- closeOneStore(nullifierStore);
- closeOneStore(merkleTreeStore);
- closeOneStore(delegationStore);
- closeOneStore(proofStore);
- closeOneStore(commonStore);
- closeOneStore(commonDataBase);
- closeOneStore(pbftSignDataStore);
- closeOneStore(sectionBloomStore);
- closeOneStore(accountAssetStore);
- }
+ @Getter
+ @Setter
+ private long lowestBlockNum = -1; // except num = 0.
+
+ @Getter
+ @Setter
+ private long latestSaveBlockTime;
// for test only
public List getWitnesses() {
@@ -302,9 +271,7 @@ public BlockCapsule getHead() throws HeaderNotFound {
}
public synchronized BlockId getHeadBlockId() {
- return new BlockId(
- dynamicPropertiesStore.getLatestBlockHeaderHash(),
- dynamicPropertiesStore.getLatestBlockHeaderNumber());
+ return new BlockId(dynamicPropertiesStore.getLatestBlockHeaderHash());
}
public long getHeadBlockNum() {
@@ -348,6 +315,9 @@ public boolean containBlockInMainChain(BlockId blockId) {
}
}
+ public BlockCapsule getKhaosDbHead(){
+ return this.khaosDb.getHead();
+ }
/**
* Get a BlockCapsule by id.
@@ -409,5 +379,42 @@ public static synchronized void init(ChainBaseManager manager) {
AssetUtil.setAccountAssetStore(manager.getAccountAssetStore());
AssetUtil.setDynamicPropertiesStore(manager.getDynamicPropertiesStore());
}
+
+ public long getNextBlockSlotTime() {
+ long slotCount = 1;
+ if (dynamicPropertiesStore.getStateFlag() == 1) {
+ slotCount += dynamicPropertiesStore.getMaintenanceSkipSlots();
+ }
+ return dynamicPropertiesStore.getLatestBlockHeaderTimestamp()
+ + slotCount * BLOCK_PRODUCED_INTERVAL;
+ }
+
+ @PostConstruct
+ private void init() {
+ this.lowestBlockNum = this.blockIndexStore.getLimitNumber(1, 1).stream()
+ .map(BlockId::getNum).findFirst().orElse(0L);
+ this.nodeType = getLowestBlockNum() > 1 ? NodeType.LITE : NodeType.FULL;
+ this.latestSaveBlockTime = System.currentTimeMillis();
+ }
+
+ public void shutdown() {
+ dbStatService.shutdown();
+ }
+
+ public boolean isLiteNode() {
+ return getNodeType() == NodeType.LITE;
+ }
+
+ public enum NodeType {
+ FULL(0),
+ LITE(1);
+
+ @Getter
+ private final int type;
+
+ NodeType(int type) {
+ this.type = type;
+ }
+ }
}
diff --git a/chainbase/src/main/java/org/tron/core/actuator/TransactionFactory.java b/chainbase/src/main/java/org/tron/core/actuator/TransactionFactory.java
index d151812b19c..6e74f7f8a2b 100644
--- a/chainbase/src/main/java/org/tron/core/actuator/TransactionFactory.java
+++ b/chainbase/src/main/java/org/tron/core/actuator/TransactionFactory.java
@@ -2,17 +2,15 @@
import com.google.protobuf.GeneratedMessageV3;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.tron.common.parameter.CommonParameter;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract;
import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract;
public class TransactionFactory {
- private static Map> actuatorMap = new ConcurrentHashMap<>();
- private static Map> contractMap = new ConcurrentHashMap<>();
+ private static final Map> actuatorMap = new ConcurrentHashMap<>();
+ private static final Map> contractMap = new ConcurrentHashMap<>();
static {
register(ContractType.CreateSmartContract, null, CreateSmartContract.class);
@@ -21,12 +19,6 @@ public class TransactionFactory {
public static void register(ContractType type, Class extends Actuator> actuatorClass,
Class extends GeneratedMessageV3> clazz) {
- Set actuatorSet = CommonParameter.getInstance().getActuatorSet();
- if (actuatorClass != null && !actuatorSet.isEmpty() && !actuatorSet
- .contains(actuatorClass.getSimpleName())) {
- return;
- }
-
if (type != null && actuatorClass != null) {
actuatorMap.put(type, actuatorClass);
}
@@ -42,12 +34,4 @@ public static Class extends Actuator> getActuator(ContractType type) {
public static Class extends GeneratedMessageV3> getContract(ContractType type) {
return contractMap.get(type);
}
-
- public static Map> getActuatorMap() {
- return actuatorMap;
- }
-
- public static Map> getContractMap() {
- return contractMap;
- }
}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/AccountCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/AccountCapsule.java
index 2c30baa8d4e..1af7b55c8b2 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/AccountCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/AccountCapsule.java
@@ -15,13 +15,25 @@
package org.tron.core.capsule;
-import com.google.common.collect.Lists;
+import static org.tron.common.math.Maths.addExact;
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.subtractExact;
+import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL;
+import static org.tron.core.config.Parameter.ChainConstant.WINDOW_SIZE_MS;
+import static org.tron.core.config.Parameter.ChainConstant.WINDOW_SIZE_PRECISION;
+import static org.tron.protos.contract.Common.ResourceCode;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+import static org.tron.protos.contract.Common.ResourceCode.TRON_POWER;
+
import com.google.common.collect.Maps;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
import org.tron.common.utils.ByteArray;
import org.tron.core.capsule.utils.AssetUtil;
import org.tron.core.store.AssetIssueStore;
@@ -29,7 +41,9 @@
import org.tron.protos.Protocol.Account;
import org.tron.protos.Protocol.Account.AccountResource;
import org.tron.protos.Protocol.Account.Builder;
+import org.tron.protos.Protocol.Account.FreezeV2;
import org.tron.protos.Protocol.Account.Frozen;
+import org.tron.protos.Protocol.Account.UnFreezeV2;
import org.tron.protos.Protocol.AccountType;
import org.tron.protos.Protocol.Key;
import org.tron.protos.Protocol.Permission;
@@ -321,24 +335,24 @@ public long getLatestOperationTime() {
return this.account.getLatestOprationTime();
}
- public void setLatestOperationTime(long latest_time) {
- this.account = this.account.toBuilder().setLatestOprationTime(latest_time).build();
+ public void setLatestOperationTime(long latestTime) {
+ this.account = this.account.toBuilder().setLatestOprationTime(latestTime).build();
}
public long getLatestConsumeTime() {
return this.account.getLatestConsumeTime();
}
- public void setLatestConsumeTime(long latest_time) {
- this.account = this.account.toBuilder().setLatestConsumeTime(latest_time).build();
+ public void setLatestConsumeTime(long latestTime) {
+ this.account = this.account.toBuilder().setLatestConsumeTime(latestTime).build();
}
public long getLatestConsumeFreeTime() {
return this.account.getLatestConsumeFreeTime();
}
- public void setLatestConsumeFreeTime(long latest_time) {
- this.account = this.account.toBuilder().setLatestConsumeFreeTime(latest_time).build();
+ public void setLatestConsumeFreeTime(long latestTime) {
+ this.account = this.account.toBuilder().setLatestConsumeFreeTime(latestTime).build();
}
public void addDelegatedFrozenBalanceForBandwidth(long balance) {
@@ -346,31 +360,75 @@ public void addDelegatedFrozenBalanceForBandwidth(long balance) {
this.account.getDelegatedFrozenBalanceForBandwidth() + balance).build();
}
+ public void addDelegatedFrozenV2BalanceForBandwidth(long balance) {
+ this.account = this.account.toBuilder().setDelegatedFrozenV2BalanceForBandwidth(
+ this.account.getDelegatedFrozenV2BalanceForBandwidth() + balance).build();
+ }
+
public long getAcquiredDelegatedFrozenBalanceForBandwidth() {
return this.account.getAcquiredDelegatedFrozenBalanceForBandwidth();
}
+ public long getAcquiredDelegatedFrozenV2BalanceForBandwidth() {
+ return this.account.getAcquiredDelegatedFrozenV2BalanceForBandwidth();
+ }
+
+ public long getTotalAcquiredDelegatedFrozenBalanceForBandwidth() {
+ return getAcquiredDelegatedFrozenBalanceForBandwidth() + getAcquiredDelegatedFrozenV2BalanceForBandwidth();
+ }
+
+ public void addFrozenBalanceForBandwidthV2(long balance) {
+ this.addFrozenBalanceForResource(BANDWIDTH, balance);
+ }
+
public void setAcquiredDelegatedFrozenBalanceForBandwidth(long balance) {
this.account = this.account.toBuilder().setAcquiredDelegatedFrozenBalanceForBandwidth(balance)
.build();
}
+ public void setAcquiredDelegatedFrozenV2BalanceForBandwidth(long balance) {
+ this.account = this.account.toBuilder().setAcquiredDelegatedFrozenV2BalanceForBandwidth(balance)
+ .build();
+ }
+
public void addAcquiredDelegatedFrozenBalanceForBandwidth(long balance) {
this.account = this.account.toBuilder().setAcquiredDelegatedFrozenBalanceForBandwidth(
this.account.getAcquiredDelegatedFrozenBalanceForBandwidth() + balance)
.build();
}
- public void safeAddAcquiredDelegatedFrozenBalanceForBandwidth(long balance) {
+ public void addAcquiredDelegatedFrozenV2BalanceForBandwidth(long balance) {
+ this.account = this.account.toBuilder().setAcquiredDelegatedFrozenV2BalanceForBandwidth(
+ this.account.getAcquiredDelegatedFrozenV2BalanceForBandwidth() + balance).build();
+ }
+
+ public void safeAddAcquiredDelegatedFrozenBalanceForBandwidth(long balance, boolean useStrict) {
this.account = this.account.toBuilder().setAcquiredDelegatedFrozenBalanceForBandwidth(
- Math.max(0, this.account.getAcquiredDelegatedFrozenBalanceForBandwidth() + balance))
+ max(0, this.account.getAcquiredDelegatedFrozenBalanceForBandwidth() + balance,
+ useStrict))
.build();
}
+ @SuppressWarnings("unused")
+ public void safeAddAcquiredDelegatedFrozenV2BalanceForBandwidth(long balance, boolean useStrict) {
+ this.account = this.account.toBuilder().setAcquiredDelegatedFrozenV2BalanceForBandwidth(
+ max(0, this.account.getAcquiredDelegatedFrozenV2BalanceForBandwidth() + balance,
+ useStrict))
+ .build();
+ }
+
public long getAcquiredDelegatedFrozenBalanceForEnergy() {
return getAccountResource().getAcquiredDelegatedFrozenBalanceForEnergy();
}
+ public long getAcquiredDelegatedFrozenV2BalanceForEnergy() {
+ return getAccountResource().getAcquiredDelegatedFrozenV2BalanceForEnergy();
+ }
+
+ public long getTotalAcquiredDelegatedFrozenBalanceForEnergy() {
+ return getAcquiredDelegatedFrozenBalanceForEnergy() + getAcquiredDelegatedFrozenV2BalanceForEnergy();
+ }
+
public void setAcquiredDelegatedFrozenBalanceForEnergy(long balance) {
AccountResource newAccountResource = getAccountResource().toBuilder()
.setAcquiredDelegatedFrozenBalanceForEnergy(balance).build();
@@ -380,14 +438,36 @@ public void setAcquiredDelegatedFrozenBalanceForEnergy(long balance) {
.build();
}
+ public void setAcquiredDelegatedFrozenV2BalanceForEnergy(long balance) {
+ AccountResource newAccountResource = getAccountResource().toBuilder()
+ .setAcquiredDelegatedFrozenV2BalanceForEnergy(balance).build();
+ this.account = this.account.toBuilder().setAccountResource(newAccountResource).build();
+ }
+
public long getDelegatedFrozenBalanceForEnergy() {
return getAccountResource().getDelegatedFrozenBalanceForEnergy();
}
+ public long getDelegatedFrozenV2BalanceForEnergy() {
+ return getAccountResource().getDelegatedFrozenV2BalanceForEnergy();
+ }
+
+ public long getTotalDelegatedFrozenBalanceForEnergy() {
+ return getDelegatedFrozenBalanceForEnergy() + getDelegatedFrozenV2BalanceForEnergy();
+ }
+
public long getDelegatedFrozenBalanceForBandwidth() {
return this.account.getDelegatedFrozenBalanceForBandwidth();
}
+ public long getDelegatedFrozenV2BalanceForBandwidth() {
+ return this.account.getDelegatedFrozenV2BalanceForBandwidth();
+ }
+
+ public long getTotalDelegatedFrozenBalanceForBandwidth() {
+ return getDelegatedFrozenBalanceForBandwidth() + getDelegatedFrozenV2BalanceForBandwidth();
+ }
+
public void setDelegatedFrozenBalanceForBandwidth(long balance) {
this.account = this.account.toBuilder()
.setDelegatedFrozenBalanceForBandwidth(balance)
@@ -413,10 +493,18 @@ public void addAcquiredDelegatedFrozenBalanceForEnergy(long balance) {
.build();
}
- public void safeAddAcquiredDelegatedFrozenBalanceForEnergy(long balance) {
+ public void addAcquiredDelegatedFrozenV2BalanceForEnergy(long balance) {
+ AccountResource newAccountResource = getAccountResource().toBuilder()
+ .setAcquiredDelegatedFrozenV2BalanceForEnergy(getAccountResource()
+ .getAcquiredDelegatedFrozenV2BalanceForEnergy() + balance).build();
+ this.account = this.account.toBuilder().setAccountResource(newAccountResource).build();
+ }
+
+ public void safeAddAcquiredDelegatedFrozenBalanceForEnergy(long balance, boolean useStrict) {
AccountResource newAccountResource = getAccountResource().toBuilder()
.setAcquiredDelegatedFrozenBalanceForEnergy(
- Math.max(0, getAccountResource().getAcquiredDelegatedFrozenBalanceForEnergy() + balance))
+ max(0, getAccountResource().getAcquiredDelegatedFrozenBalanceForEnergy() + balance,
+ useStrict))
.build();
this.account = this.account.toBuilder()
@@ -424,6 +512,14 @@ public void safeAddAcquiredDelegatedFrozenBalanceForEnergy(long balance) {
.build();
}
+ @SuppressWarnings("unused")
+ public void safeAddAcquiredDelegatedFrozenV2BalanceForEnergy(long balance, boolean useStrict) {
+ AccountResource newAccountResource = getAccountResource().toBuilder()
+ .setAcquiredDelegatedFrozenV2BalanceForEnergy(max(0, getAccountResource()
+ .getAcquiredDelegatedFrozenV2BalanceForEnergy() + balance, useStrict)).build();
+ this.account = this.account.toBuilder().setAccountResource(newAccountResource).build();
+ }
+
public void addDelegatedFrozenBalanceForEnergy(long balance) {
AccountResource newAccountResource = getAccountResource().toBuilder()
.setDelegatedFrozenBalanceForEnergy(
@@ -434,6 +530,42 @@ public void addDelegatedFrozenBalanceForEnergy(long balance) {
.build();
}
+ public void addDelegatedFrozenV2BalanceForEnergy(long balance) {
+ AccountResource newAccountResource = getAccountResource().toBuilder()
+ .setDelegatedFrozenV2BalanceForEnergy(
+ getAccountResource().getDelegatedFrozenV2BalanceForEnergy() + balance).build();
+
+ this.account = this.account.toBuilder().setAccountResource(newAccountResource).build();
+ }
+
+ public void addFrozenBalanceForEnergyV2(long balance) {
+ this.addFrozenBalanceForResource(ENERGY, balance);
+ }
+
+ private void addFrozenBalanceForResource(ResourceCode type, long balance) {
+ boolean doUpdate = false;
+ for (int i = 0; i < this.account.getFrozenV2List().size(); i++) {
+ if (this.account.getFrozenV2List().get(i).getType().equals(type)) {
+ long newAmount = this.account.getFrozenV2(i).getAmount() + balance;
+ FreezeV2 freezeV2 = FreezeV2.newBuilder()
+ .setType(type)
+ .setAmount(newAmount)
+ .build();
+ this.updateFrozenV2List(i, freezeV2);
+ doUpdate = true;
+ break;
+ }
+ }
+
+ if (!doUpdate) {
+ FreezeV2 freezeV2 = FreezeV2.newBuilder()
+ .setType(type)
+ .setAmount(balance)
+ .build();
+ this.addFrozenV2List(freezeV2);
+ }
+ }
+
@Override
public String toString() {
return this.account.toString();
@@ -448,6 +580,10 @@ public void addVotes(ByteString voteAddress, long voteAdd) {
.build();
}
+ public void addAllVotes(List votesToAdd) {
+ this.account = this.account.toBuilder().addAllVotes(votesToAdd).build();
+ }
+
public void clearLatestAssetOperationTimeV2() {
this.account = this.account.toBuilder()
.clearLatestAssetOperationTimeV2()
@@ -470,19 +606,14 @@ public void clearVotes() {
* get votes.
*/
public List getVotesList() {
- if (this.account.getVotesList() != null) {
- return this.account.getVotesList();
- } else {
- return Lists.newArrayList();
- }
+ return this.account.getVotesList();
}
public long getTronPowerUsage() {
- if (this.account.getVotesList() != null) {
- return this.account.getVotesList().stream().mapToLong(Vote::getVoteCount).sum();
- } else {
+ if (getVotesList().isEmpty()) {
return 0L;
}
+ return this.account.getVotesList().stream().mapToLong(Vote::getVoteCount).sum();
}
//tp:Tron_Power
@@ -495,19 +626,68 @@ public long getTronPower() {
tp += account.getAccountResource().getFrozenBalanceForEnergy().getFrozenBalance();
tp += account.getDelegatedFrozenBalanceForBandwidth();
tp += account.getAccountResource().getDelegatedFrozenBalanceForEnergy();
+
+ tp += getFrozenV2List().stream().filter(o -> o.getType() != TRON_POWER)
+ .mapToLong(FreezeV2::getAmount).sum();
+ tp += account.getDelegatedFrozenV2BalanceForBandwidth();
+ tp += account.getAccountResource().getDelegatedFrozenV2BalanceForEnergy();
return tp;
}
public long getAllTronPower() {
if (account.getOldTronPower() == -1) {
- return getTronPowerFrozenBalance();
+ return getTronPowerFrozenBalance() + getTronPowerFrozenV2Balance();
} else if (account.getOldTronPower() == 0) {
- return getTronPower() + getTronPowerFrozenBalance();
+ return getTronPower() + getTronPowerFrozenBalance() + getTronPowerFrozenV2Balance();
} else {
- return account.getOldTronPower() + getTronPowerFrozenBalance();
+ return account.getOldTronPower() + getTronPowerFrozenBalance()
+ + getTronPowerFrozenV2Balance();
+ }
+ }
+
+
+
+ public List getFrozenV2List() {
+ return account.getFrozenV2List();
+ }
+
+ public List getUnfrozenV2List() {
+ return account.getUnfrozenV2List();
+ }
+
+ public void updateFrozenV2List(int index, FreezeV2 frozenV2) {
+ if (Objects.isNull(frozenV2)) {
+ return;
+ }
+ this.account = this.account.toBuilder().setFrozenV2(index, frozenV2).build();
+ }
+
+ public void addFrozenV2List(FreezeV2 frozenV2) {
+ this.account = this.account.toBuilder().addFrozenV2(frozenV2).build();
+ }
+
+ public void addUnfrozenV2List(ResourceCode type, long unfreezeAmount, long expireTime) {
+ UnFreezeV2 unFreezeV2 = UnFreezeV2.newBuilder()
+ .setType(type)
+ .setUnfreezeAmount(unfreezeAmount)
+ .setUnfreezeExpireTime(expireTime)
+ .build();
+ this.account = this.account.toBuilder().addUnfrozenV2(unFreezeV2).build();
+ }
+
+
+ public int getUnfreezingV2Count(long now) {
+ int count = 0;
+ List unFreezeV2List = account.getUnfrozenV2List();
+ for (UnFreezeV2 item : unFreezeV2List) {
+ if (item.getUnfreezeExpireTime() > now) {
+ count++;
+ }
}
+ return count;
}
+
/*************************** start asset ****************************************/
public boolean getAssetOptimized() {
@@ -537,14 +717,15 @@ public boolean assetBalanceEnoughV2(byte[] key, long amount,
return amount > 0 && null != currentAmount && amount <= currentAmount;
}
- public boolean addAssetAmount(byte[] key, long amount) {
+ public boolean addAssetAmount(byte[] key, long amount, boolean useStrict) {
Map assetMap = this.account.getAssetMap();
String nameKey = ByteArray.toStr(key);
Long currentAmount = assetMap.get(nameKey);
if (currentAmount == null) {
currentAmount = 0L;
}
- this.account = this.account.toBuilder().putAsset(nameKey, Math.addExact(currentAmount, amount))
+ this.account = this.account.toBuilder().putAsset(nameKey,
+ addExact(currentAmount, amount, useStrict))
.build();
return true;
}
@@ -552,6 +733,7 @@ public boolean addAssetAmount(byte[] key, long amount) {
public boolean addAssetAmountV2(byte[] key, long amount,
DynamicPropertiesStore dynamicPropertiesStore, AssetIssueStore assetIssueStore) {
importAsset(key);
+ boolean disableJavaLangMath = dynamicPropertiesStore.disableJavaLangMath();
//key is token name
if (dynamicPropertiesStore.getAllowSameTokenName() == 0) {
Map assetMap = this.account.getAssetMap();
@@ -563,8 +745,8 @@ public boolean addAssetAmountV2(byte[] key, long amount,
currentAmount = 0L;
}
this.account = this.account.toBuilder()
- .putAsset(nameKey, Math.addExact(currentAmount, amount))
- .putAssetV2(tokenID, Math.addExact(currentAmount, amount))
+ .putAsset(nameKey, addExact(currentAmount, amount, disableJavaLangMath))
+ .putAssetV2(tokenID, addExact(currentAmount, amount, disableJavaLangMath))
.build();
}
//key is token id
@@ -576,19 +758,19 @@ public boolean addAssetAmountV2(byte[] key, long amount,
currentAmount = 0L;
}
this.account = this.account.toBuilder()
- .putAssetV2(tokenIDStr, Math.addExact(currentAmount, amount))
+ .putAssetV2(tokenIDStr, addExact(currentAmount, amount, disableJavaLangMath))
.build();
}
return true;
}
- public boolean reduceAssetAmount(byte[] key, long amount) {
+ public boolean reduceAssetAmount(byte[] key, long amount, boolean disableJavaLangMath) {
Map assetMap = this.account.getAssetMap();
String nameKey = ByteArray.toStr(key);
Long currentAmount = assetMap.get(nameKey);
if (amount > 0 && null != currentAmount && amount <= currentAmount) {
this.account = this.account.toBuilder()
- .putAsset(nameKey, Math.subtractExact(currentAmount, amount)).build();
+ .putAsset(nameKey, subtractExact(currentAmount, amount, disableJavaLangMath)).build();
return true;
}
@@ -599,6 +781,7 @@ public boolean reduceAssetAmountV2(byte[] key, long amount,
DynamicPropertiesStore dynamicPropertiesStore, AssetIssueStore assetIssueStore) {
importAsset(key);
//key is token name
+ boolean disableJavaLangMath = dynamicPropertiesStore.disableJavaLangMath();
if (dynamicPropertiesStore.getAllowSameTokenName() == 0) {
Map assetMap = this.account.getAssetMap();
AssetIssueCapsule assetIssueCapsule = assetIssueStore.get(key);
@@ -607,8 +790,8 @@ public boolean reduceAssetAmountV2(byte[] key, long amount,
Long currentAmount = assetMap.get(nameKey);
if (amount > 0 && null != currentAmount && amount <= currentAmount) {
this.account = this.account.toBuilder()
- .putAsset(nameKey, Math.subtractExact(currentAmount, amount))
- .putAssetV2(tokenID, Math.subtractExact(currentAmount, amount))
+ .putAsset(nameKey, subtractExact(currentAmount, amount, disableJavaLangMath))
+ .putAssetV2(tokenID, subtractExact(currentAmount, amount, disableJavaLangMath))
.build();
return true;
}
@@ -620,7 +803,7 @@ public boolean reduceAssetAmountV2(byte[] key, long amount,
Long currentAmount = assetMapV2.get(tokenID);
if (amount > 0 && null != currentAmount && amount <= currentAmount) {
this.account = this.account.toBuilder()
- .putAssetV2(tokenID, Math.subtractExact(currentAmount, amount))
+ .putAssetV2(tokenID, subtractExact(currentAmount, amount, disableJavaLangMath))
.build();
return true;
}
@@ -663,9 +846,8 @@ public boolean addAssetV2(byte[] key, long value) {
return true;
}
- public boolean addAssetMapV2(Map assetMap) {
+ public void addAssetMapV2(Map assetMap) {
this.account = this.account.toBuilder().putAllAssetV2(assetMap).build();
- return true;
}
public Long getAsset(DynamicPropertiesStore dynamicStore, String key) {
@@ -712,9 +894,8 @@ public Map getAssetV2MapForTest() {
/*************************** end asset ****************************************/
- public boolean addAllLatestAssetOperationTimeV2(Map map) {
+ public void addAllLatestAssetOperationTimeV2(Map map) {
this.account = this.account.toBuilder().putAllLatestAssetOperationTimeV2(map).build();
- return true;
}
public Map getLatestAssetOperationTimeMap() {
@@ -757,8 +938,18 @@ public long getFrozenBalance() {
return frozenBalance[0];
}
+ public long getFrozenV2BalanceForBandwidth() {
+ List frozenList = getFrozenV2List();
+ if (frozenList.isEmpty()) {
+ return 0;
+ }
+ return frozenList.stream().filter(o -> o.getType() == BANDWIDTH)
+ .mapToLong(FreezeV2::getAmount).sum();
+ }
+
public long getAllFrozenBalanceForBandwidth() {
- return getFrozenBalance() + getAcquiredDelegatedFrozenBalanceForBandwidth();
+ return getFrozenBalance() + getAcquiredDelegatedFrozenBalanceForBandwidth()
+ + getFrozenV2BalanceForBandwidth() + getAcquiredDelegatedFrozenV2BalanceForBandwidth();
}
public int getFrozenSupplyCount() {
@@ -866,6 +1057,14 @@ public long getNetUsage() {
return this.account.getNetUsage();
}
+ public long getUsage(ResourceCode resourceCode) {
+ if (resourceCode == BANDWIDTH) {
+ return this.account.getNetUsage();
+ } else {
+ return this.account.getAccountResource().getEnergyUsage();
+ }
+ }
+
public void setNetUsage(long netUsage) {
this.account = this.account.toBuilder()
.setNetUsage(netUsage).build();
@@ -893,6 +1092,15 @@ public long getEnergyFrozenBalance() {
return this.account.getAccountResource().getFrozenBalanceForEnergy().getFrozenBalance();
}
+ public long getFrozenV2BalanceForEnergy() {
+ List frozenList = getFrozenV2List();
+ if (frozenList.isEmpty()) {
+ return 0;
+ }
+ return frozenList.stream().filter(o -> o.getType() == ENERGY)
+ .mapToLong(FreezeV2::getAmount).sum();
+ }
+
public boolean oldTronPowerIsNotInitialized() {
return this.account.getOldTronPower() == 0;
}
@@ -935,10 +1143,19 @@ public void setFrozenForTronPower(long frozenBalance, long expireTime) {
.build());
}
+ public void addFrozenForTronPowerV2(long balance) {
+ this.addFrozenBalanceForResource(TRON_POWER, balance);
+ }
+
public long getTronPowerFrozenBalance() {
return this.account.getTronPower().getFrozenBalance();
}
+ public long getTronPowerFrozenV2Balance() {
+ return getFrozenV2List().stream().filter(o-> o.getType() == TRON_POWER)
+ .mapToLong(FreezeV2::getAmount).sum();
+ }
+
public long getEnergyUsage() {
return this.account.getAccountResource().getEnergyUsage();
}
@@ -951,17 +1168,18 @@ public void setEnergyUsage(long energyUsage) {
}
public long getAllFrozenBalanceForEnergy() {
- return getEnergyFrozenBalance() + getAcquiredDelegatedFrozenBalanceForEnergy();
+ return getEnergyFrozenBalance() + getAcquiredDelegatedFrozenBalanceForEnergy()
+ + getFrozenV2BalanceForEnergy() + getAcquiredDelegatedFrozenV2BalanceForEnergy();
}
public long getLatestConsumeTimeForEnergy() {
return this.account.getAccountResource().getLatestConsumeTimeForEnergy();
}
- public void setLatestConsumeTimeForEnergy(long latest_time) {
+ public void setLatestConsumeTimeForEnergy(long latestTime) {
this.account = this.account.toBuilder()
.setAccountResource(
- this.account.getAccountResource().toBuilder().setLatestConsumeTimeForEnergy(latest_time)
+ this.account.getAccountResource().toBuilder().setLatestConsumeTimeForEnergy(latestTime)
.build()).build();
}
@@ -970,13 +1188,11 @@ public long getFreeNetUsage() {
}
public void setFreeNetUsage(long freeNetUsage) {
- this.account = this.account.toBuilder()
- .setFreeNetUsage(freeNetUsage).build();
+ this.account = this.account.toBuilder().setFreeNetUsage(freeNetUsage).build();
}
- public boolean addAllFreeAssetNetUsageV2(Map map) {
+ public void addAllFreeAssetNetUsageV2(Map map) {
this.account = this.account.toBuilder().putAllFreeAssetNetUsageV2(map).build();
- return true;
}
public long getFreeAssetNetUsage(String assetName) {
@@ -1111,9 +1327,12 @@ public void updateAccountType(AccountType accountType) {
public void clearDelegatedResource() {
Builder builder = account.toBuilder();
AccountResource newAccountResource = getAccountResource().toBuilder()
- .setAcquiredDelegatedFrozenBalanceForEnergy(0L).build();
+ .setAcquiredDelegatedFrozenBalanceForEnergy(0L)
+ .setAcquiredDelegatedFrozenV2BalanceForEnergy(0L)
+ .build();
builder.setAccountResource(newAccountResource);
- builder.setAcquiredDelegatedFrozenBalanceForBandwidth(0L);
+ builder.setAcquiredDelegatedFrozenBalanceForBandwidth(0L)
+ .setAcquiredDelegatedFrozenV2BalanceForBandwidth(0L);
this.account = builder.build();
}
@@ -1128,4 +1347,134 @@ public void importAllAsset() {
}
}
+ public void addUnfrozenV2(UnFreezeV2 unfrozenV2) {
+ if (Objects.isNull(unfrozenV2)) {
+ return;
+ }
+ this.account = this.account.toBuilder().addUnfrozenV2(unfrozenV2).build();
+ }
+
+ public void addAllUnfrozenV2(List unFreezeV2List) {
+ if (CollectionUtils.isEmpty(unFreezeV2List)) {
+ return;
+ }
+ this.account = this.account.toBuilder().addAllUnfrozenV2(unFreezeV2List).build();
+ }
+
+ public void clearUnfrozenV2() {
+ this.account = this.account.toBuilder().clearUnfrozenV2().build();
+ }
+
+ public void clearFrozenV2() {
+ this.account = this.account.toBuilder().clearFrozenV2().build();
+ }
+
+ public void setNewWindowSize(ResourceCode resourceCode, long newWindowSize) {
+ if (resourceCode == BANDWIDTH) {
+ this.account = this.account.toBuilder().setNetWindowSize(newWindowSize).build();
+ } else {
+ this.account = this.account.toBuilder().setAccountResource(this.account.getAccountResource()
+ .toBuilder().setEnergyWindowSize(newWindowSize).build()).build();
+ }
+ }
+
+ public long getWindowSize(ResourceCode resourceCode) {
+ long windowSize;
+ boolean windowOptimized;
+ if (resourceCode == BANDWIDTH) {
+ windowSize = this.account.getNetWindowSize();
+ windowOptimized = this.account.getNetWindowOptimized();
+ } else {
+ windowSize = this.account.getAccountResource().getEnergyWindowSize();
+ windowOptimized = this.account.getAccountResource().getEnergyWindowOptimized();
+ }
+ if (windowSize == 0) {
+ return WINDOW_SIZE_MS / BLOCK_PRODUCED_INTERVAL;
+ }
+ if (windowOptimized) {
+ return windowSize < WINDOW_SIZE_PRECISION ? WINDOW_SIZE_MS / BLOCK_PRODUCED_INTERVAL :
+ windowSize / WINDOW_SIZE_PRECISION;
+ } else {
+ return windowSize;
+ }
+ }
+
+ public long getWindowSizeV2(ResourceCode resourceCode) {
+ long windowSize;
+ boolean windowOptimized;
+ if (resourceCode == BANDWIDTH) {
+ windowSize = this.account.getNetWindowSize();
+ windowOptimized = this.account.getNetWindowOptimized();
+ } else {
+ windowSize = this.account.getAccountResource().getEnergyWindowSize();
+ windowOptimized = this.account.getAccountResource().getEnergyWindowOptimized();
+ }
+ if (windowSize == 0) {
+ return WINDOW_SIZE_MS / BLOCK_PRODUCED_INTERVAL * WINDOW_SIZE_PRECISION;
+ }
+ if (windowOptimized) {
+ return windowSize;
+ } else {
+ return windowSize * WINDOW_SIZE_PRECISION;
+ }
+ }
+
+ public boolean getWindowOptimized(ResourceCode resourceCode) {
+ boolean windowOptimized;
+ if (resourceCode == BANDWIDTH) {
+ windowOptimized = this.account.getNetWindowOptimized();
+ } else {
+ windowOptimized = this.account.getAccountResource().getEnergyWindowOptimized();
+ }
+ return windowOptimized;
+ }
+
+ public void setWindowOptimized(ResourceCode resourceCode, boolean windowOptimized) {
+ if (resourceCode == BANDWIDTH) {
+ this.account = this.account.toBuilder().setNetWindowOptimized(windowOptimized).build();
+ } else {
+ this.account = this.account.toBuilder().setAccountResource(this.account.getAccountResource()
+ .toBuilder().setEnergyWindowOptimized(windowOptimized).build()).build();
+ }
+ }
+
+ public long getLastConsumeTime(ResourceCode resourceCode) {
+ if (resourceCode == BANDWIDTH) {
+ return this.account.getLatestConsumeTime();
+ } else {
+ return this.account.getAccountResource().getLatestConsumeTimeForEnergy();
+ }
+ }
+
+ public long getFrozenV2BalanceWithDelegated(ResourceCode resourceCode) {
+ if (resourceCode == BANDWIDTH) {
+ return getFrozenV2BalanceForBandwidth() + getDelegatedFrozenV2BalanceForBandwidth();
+ } else {
+ return getFrozenV2BalanceForEnergy() + getDelegatedFrozenV2BalanceForEnergy();
+ }
+ }
+
+ public void setNewWindowSizeV2( ResourceCode resourceCode, long newWindowSize) {
+ this.setNewWindowSize(resourceCode, newWindowSize);
+ if (!this.getWindowOptimized(resourceCode)) {
+ this.setWindowOptimized(resourceCode, true);
+ }
+ }
+
+ public void setUsage(ResourceCode resourceCode, long usage) {
+ if (resourceCode == BANDWIDTH) {
+ setNetUsage(usage);
+ } else {
+ setEnergyUsage(usage);
+ }
+ }
+
+ public void setLatestTime(ResourceCode resourceCode, long time) {
+ if (resourceCode == BANDWIDTH) {
+ setLatestConsumeTime(time);
+ } else {
+ setLatestConsumeTimeForEnergy(time);
+ }
+ }
+
}
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 4853de64116..34b7853d4d1 100755
--- a/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java
@@ -15,6 +15,8 @@
package org.tron.core.capsule;
+import static org.tron.core.exception.BadBlockException.TypeEnum.CALC_MERKLE_ROOT_FAILED;
+
import com.google.common.primitives.Longs;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
@@ -37,6 +39,7 @@
import org.tron.common.utils.Time;
import org.tron.core.capsule.utils.MerkleTree;
import org.tron.core.config.Parameter.ChainConstant;
+import org.tron.core.exception.BadBlockException;
import org.tron.core.exception.BadItemException;
import org.tron.core.exception.ValidateSignatureException;
import org.tron.core.store.AccountStore;
@@ -49,6 +52,7 @@
public class BlockCapsule implements ProtoCapsule {
public boolean generatedByMyself = false;
+ private volatile boolean merkleValidated = false;
@Getter
@Setter
private TransactionRetCapsule result;
@@ -56,7 +60,6 @@ public class BlockCapsule implements ProtoCapsule {
private Block block;
private List transactions = new ArrayList<>();
- private StringBuilder toStringBuff = new StringBuilder();
private boolean isSwitch;
@Getter
@Setter
@@ -226,6 +229,19 @@ public Sha256Hash calcMerkleRoot() {
return MerkleTree.getInstance().createTree(ids).getRoot().getHash();
}
+ public void validateMerkleRoot() throws BadBlockException {
+ if (merkleValidated) {
+ return;
+ }
+ Sha256Hash actual = calcMerkleRoot();
+ if (!actual.equals(getMerkleRoot())) {
+ throw new BadBlockException(CALC_MERKLE_ROOT_FAILED,
+ String.format("merkle root mismatch for block %d: expected %s, actual %s",
+ getNum(), getMerkleRoot(), actual));
+ }
+ merkleValidated = true;
+ }
+
public void setMerkleRoot() {
BlockHeader.raw blockHeaderRaw =
this.block.getBlockHeader().getRawData().toBuilder()
@@ -284,6 +300,10 @@ public Block getInstance() {
return this.block;
}
+ public long getSerializedSize() {
+ return this.block.getSerializedSize();
+ }
+
public Sha256Hash getParentHash() {
return Sha256Hash.wrap(this.block.getBlockHeader().getRawData().getParentHash());
}
@@ -310,7 +330,7 @@ public boolean hasWitnessSignature() {
@Override
public String toString() {
- toStringBuff.setLength(0);
+ StringBuilder toStringBuff = new StringBuilder();
toStringBuff.append("BlockCapsule \n[ ");
toStringBuff.append("hash=").append(getBlockId()).append("\n");
diff --git a/chainbase/src/main/java/org/tron/core/capsule/ContractCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/ContractCapsule.java
index 2204b6446a3..f566a128d6b 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/ContractCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/ContractCapsule.java
@@ -15,8 +15,8 @@
package org.tron.core.capsule;
-import static java.lang.Math.max;
-import static java.lang.Math.min;
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.min;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
@@ -109,9 +109,9 @@ public byte[] getOriginAddress() {
return this.smartContract.getOriginAddress().toByteArray();
}
- public long getConsumeUserResourcePercent() {
+ public long getConsumeUserResourcePercent(boolean disableMath) {
long percent = this.smartContract.getConsumeUserResourcePercent();
- return max(0, min(percent, Constant.ONE_HUNDRED));
+ return max(0, min(percent, Constant.ONE_HUNDRED, disableMath), disableMath);
}
public long getOriginEnergyLimit() {
@@ -133,4 +133,5 @@ public byte[] getTrxHash() {
public int getContractVersion() {
return this.smartContract.getVersion();
}
+
}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/ContractStateCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/ContractStateCapsule.java
new file mode 100644
index 00000000000..bd932ea50ae
--- /dev/null
+++ b/chainbase/src/main/java/org/tron/core/capsule/ContractStateCapsule.java
@@ -0,0 +1,154 @@
+package org.tron.core.capsule;
+
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.min;
+import static org.tron.common.math.Maths.pow;
+import static org.tron.core.Constant.DYNAMIC_ENERGY_DECREASE_DIVISION;
+import static org.tron.core.Constant.DYNAMIC_ENERGY_FACTOR_DECIMAL;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.protos.contract.SmartContractOuterClass;
+import org.tron.protos.contract.SmartContractOuterClass.ContractState;
+
+@Slf4j(topic = "capsule")
+public class ContractStateCapsule implements ProtoCapsule {
+
+ private ContractState contractState;
+
+ public ContractStateCapsule(ContractState contractState) {
+ this.contractState = contractState;
+ }
+
+ public ContractStateCapsule(byte[] data) {
+ try {
+ this.contractState = SmartContractOuterClass.ContractState.parseFrom(data);
+ } catch (InvalidProtocolBufferException e) {
+ // logger.debug(e.getMessage());
+ }
+ }
+
+ public ContractStateCapsule(long currentCycle) {
+ reset(currentCycle);
+ }
+
+ @Override
+ public byte[] getData() {
+ return this.contractState.toByteArray();
+ }
+
+ @Override
+ public ContractState getInstance() {
+ return this.contractState;
+ }
+
+ public long getEnergyUsage() {
+ return this.contractState.getEnergyUsage();
+ }
+
+ public void setEnergyUsage(long value) {
+ this.contractState = this.contractState.toBuilder().setEnergyUsage(value).build();
+ }
+
+ public void addEnergyUsage(long toAdd) {
+ setEnergyUsage(getEnergyUsage() + toAdd);
+ }
+
+ public long getEnergyFactor() {
+ return this.contractState.getEnergyFactor();
+ }
+
+ public void setEnergyFactor(long value) {
+ this.contractState = this.contractState.toBuilder().setEnergyFactor(value).build();
+ }
+
+ public long getUpdateCycle() {
+ return this.contractState.getUpdateCycle();
+ }
+
+ public void setUpdateCycle(long value) {
+ this.contractState = this.contractState.toBuilder().setUpdateCycle(value).build();
+ }
+
+ public void addUpdateCycle(long toAdd) {
+ setUpdateCycle(getUpdateCycle() + toAdd);
+ }
+
+ public boolean catchUpToCycle(DynamicPropertiesStore dps) {
+ return catchUpToCycle(
+ dps.getCurrentCycleNumber(),
+ dps.getDynamicEnergyThreshold(),
+ dps.getDynamicEnergyIncreaseFactor(),
+ dps.getDynamicEnergyMaxFactor(),
+ dps.allowStrictMath(),
+ dps.disableJavaLangMath()
+ );
+ }
+
+ public boolean catchUpToCycle(
+ long newCycle, long threshold, long increaseFactor, long maxFactor,
+ boolean useStrictMath, boolean disableMath
+ ) {
+ long lastCycle = getUpdateCycle();
+
+ // Updated within this cycle
+ if (lastCycle == newCycle) {
+ return false;
+ }
+
+ // Guard judge and uninitialized state
+ if (lastCycle > newCycle || lastCycle == 0L) {
+ reset(newCycle);
+ return true;
+ }
+
+ final long precisionFactor = DYNAMIC_ENERGY_FACTOR_DECIMAL;
+
+ // Increase the last cycle
+ // fix the threshold = 0 caused incompatible
+ if (getEnergyUsage() > threshold) {
+ lastCycle += 1;
+ double increasePercent = 1 + (double) increaseFactor / precisionFactor;
+ this.contractState = ContractState.newBuilder()
+ .setUpdateCycle(lastCycle)
+ .setEnergyFactor(min(
+ maxFactor,
+ (long) ((getEnergyFactor() + precisionFactor) * increasePercent) - precisionFactor,
+ disableMath))
+ .build();
+ }
+
+ // No need to decrease
+ long cycleCount = newCycle - lastCycle;
+ if (cycleCount <= 0) {
+ return true;
+ }
+
+ // Calc the decrease percent (decrease factor [75% ~ 100%])
+ double decreasePercent = pow(
+ 1 - (double) increaseFactor / DYNAMIC_ENERGY_DECREASE_DIVISION / precisionFactor,
+ cycleCount, useStrictMath
+ );
+
+ // Decrease to this cycle
+ // (If long time no tx and factor is 100%,
+ // we just calc it again and result factor is still 100%.
+ // That means we merge this special case to normal cases)
+ this.contractState = ContractState.newBuilder()
+ .setUpdateCycle(newCycle)
+ .setEnergyFactor(max(
+ 0,
+ (long) ((getEnergyFactor() + precisionFactor) * decreasePercent) - precisionFactor,
+ disableMath))
+ .build();
+
+ return true;
+ }
+
+ public void reset(long latestCycle) {
+ this.contractState = ContractState.newBuilder()
+ .setUpdateCycle(latestCycle)
+ .build();
+ }
+}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/DelegatedResourceCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/DelegatedResourceCapsule.java
index c24c2e72edf..05324bb267e 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/DelegatedResourceCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/DelegatedResourceCapsule.java
@@ -1,5 +1,6 @@
package org.tron.core.capsule;
+import com.google.common.primitives.Bytes;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.extern.slf4j.Slf4j;
@@ -9,6 +10,9 @@
@Slf4j(topic = "capsule")
public class DelegatedResourceCapsule implements ProtoCapsule {
+ protected static final byte[] V2_PREFIX = new byte[]{0x01};
+ protected static final byte[] V2_LOCK_PREFIX = new byte[]{0x02};
+
private DelegatedResource delegatedResource;
public DelegatedResourceCapsule(final DelegatedResource delegatedResource) {
@@ -37,6 +41,13 @@ public static byte[] createDbKey(byte[] from, byte[] to) {
return key;
}
+ public static byte[] createDbKeyV2(byte[] from, byte[] to, boolean lock) {
+ if (lock) {
+ return Bytes.concat(V2_LOCK_PREFIX, from, to);
+ }
+ return Bytes.concat(V2_PREFIX, from, to);
+ }
+
public ByteString getFrom() {
return this.delegatedResource.getFrom();
}
@@ -76,17 +87,17 @@ public long getFrozenBalance(boolean isBandwidth) {
}
- public void setFrozenBalanceForBandwidth(long Bandwidth, long expireTime) {
+ public void setFrozenBalanceForBandwidth(long bandwidth, long expireTime) {
this.delegatedResource = this.delegatedResource.toBuilder()
- .setFrozenBalanceForBandwidth(Bandwidth)
+ .setFrozenBalanceForBandwidth(bandwidth)
.setExpireTimeForBandwidth(expireTime)
.build();
}
- public void addFrozenBalanceForBandwidth(long Bandwidth, long expireTime) {
+ public void addFrozenBalanceForBandwidth(long bandwidth, long expireTime) {
this.delegatedResource = this.delegatedResource.toBuilder()
.setFrozenBalanceForBandwidth(this.delegatedResource.getFrozenBalanceForBandwidth()
- + Bandwidth)
+ + bandwidth)
.setExpireTimeForBandwidth(expireTime)
.build();
}
@@ -99,9 +110,9 @@ public long getExpireTimeForEnergy() {
return this.delegatedResource.getExpireTimeForEnergy();
}
- public void setExpireTimeForBandwidth(long ExpireTime) {
+ public void setExpireTimeForBandwidth(long expireTime) {
this.delegatedResource = this.delegatedResource.toBuilder()
- .setExpireTimeForBandwidth(ExpireTime)
+ .setExpireTimeForBandwidth(expireTime)
.build();
}
@@ -113,9 +124,9 @@ public long getExpireTimeForEnergy(DynamicPropertiesStore dynamicPropertiesStore
}
}
- public void setExpireTimeForEnergy(long ExpireTime) {
+ public void setExpireTimeForEnergy(long expireTime) {
this.delegatedResource = this.delegatedResource.toBuilder()
- .setExpireTimeForEnergy(ExpireTime)
+ .setExpireTimeForEnergy(expireTime)
.build();
}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/ExchangeCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/ExchangeCapsule.java
index 1cf91301b43..7d7edebfc6e 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/ExchangeCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/ExchangeCapsule.java
@@ -2,11 +2,14 @@
import static org.tron.core.config.Parameter.ChainSymbol.TRX_SYMBOL_BYTES;
+import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Arrays;
import lombok.extern.slf4j.Slf4j;
+import org.tron.common.math.StrictMathWrapper;
import org.tron.common.utils.ByteArray;
+import org.tron.core.exception.ContractValidateException;
import org.tron.core.store.AssetIssueStore;
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.protos.Protocol.Exchange;
@@ -112,32 +115,56 @@ public byte[] createDbKey() {
return calculateDbKey(getID());
}
- public long transaction(byte[] sellTokenID, long sellTokenQuant) {
+ @VisibleForTesting
+ public long transaction(byte[] sellTokenID, long sellTokenQuant, boolean useStrictMath)
+ throws ContractValidateException {
+ return transaction(sellTokenID, sellTokenQuant, useStrictMath, false);
+ }
+
+ public long transaction(byte[] sellTokenID, long sellTokenQuant, boolean useStrictMath,
+ boolean hardenedCalc) throws ContractValidateException {
long supply = 1_000_000_000_000_000_000L;
- ExchangeProcessor processor = new ExchangeProcessor(supply);
+ Processor processor = hardenedCalc
+ ? SafeExchangeProcessor.INSTANCE : new ExchangeProcessor(supply, useStrictMath);
long buyTokenQuant = 0;
long firstTokenBalance = this.exchange.getFirstTokenBalance();
long secondTokenBalance = this.exchange.getSecondTokenBalance();
+ long newFirstTokenBalance;
+ long newSecondTokenBalance;
if (this.exchange.getFirstTokenId().equals(ByteString.copyFrom(sellTokenID))) {
buyTokenQuant = processor.exchange(firstTokenBalance,
secondTokenBalance,
sellTokenQuant);
- this.exchange = this.exchange.toBuilder()
- .setFirstTokenBalance(firstTokenBalance + sellTokenQuant)
- .setSecondTokenBalance(secondTokenBalance - buyTokenQuant)
- .build();
+ newFirstTokenBalance = hardenedCalc
+ ? StrictMathWrapper.addExact(firstTokenBalance, sellTokenQuant)
+ : firstTokenBalance + sellTokenQuant;
+ newSecondTokenBalance = hardenedCalc
+ ? StrictMathWrapper.subtractExact(secondTokenBalance, buyTokenQuant)
+ : secondTokenBalance - buyTokenQuant;
+
} else {
buyTokenQuant = processor.exchange(secondTokenBalance,
firstTokenBalance,
sellTokenQuant);
- this.exchange = this.exchange.toBuilder()
- .setFirstTokenBalance(firstTokenBalance - buyTokenQuant)
- .setSecondTokenBalance(secondTokenBalance + sellTokenQuant)
- .build();
+ newFirstTokenBalance = hardenedCalc
+ ? StrictMathWrapper.subtractExact(firstTokenBalance, buyTokenQuant)
+ : firstTokenBalance - buyTokenQuant;
+ newSecondTokenBalance = hardenedCalc
+ ? StrictMathWrapper.addExact(secondTokenBalance, sellTokenQuant)
+ : secondTokenBalance + sellTokenQuant;
+
}
+ if (hardenedCalc && (newFirstTokenBalance < 0 || newSecondTokenBalance < 0)) {
+ throw new ContractValidateException("Exchange balance must be >=0 after transaction");
+ }
+ this.exchange = this.exchange.toBuilder()
+ .setFirstTokenBalance(newFirstTokenBalance)
+ .setSecondTokenBalance(newSecondTokenBalance)
+ .build();
+
return buyTokenQuant;
}
@@ -172,4 +199,9 @@ public Exchange getInstance() {
return this.exchange;
}
+ public interface Processor {
+
+ long exchange(long sellTokenBalance, long buyTokenBalance, long sellTokenQuant);
+ }
+
}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/ExchangeProcessor.java b/chainbase/src/main/java/org/tron/core/capsule/ExchangeProcessor.java
index e1b536b3e7a..845ed37d455 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/ExchangeProcessor.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/ExchangeProcessor.java
@@ -1,14 +1,17 @@
package org.tron.core.capsule;
import lombok.extern.slf4j.Slf4j;
+import org.tron.common.math.Maths;
@Slf4j(topic = "capsule")
-public class ExchangeProcessor {
+public class ExchangeProcessor implements ExchangeCapsule.Processor {
private long supply;
+ private final boolean useStrictMath;
- public ExchangeProcessor(long supply) {
+ public ExchangeProcessor(long supply, boolean useStrictMath) {
this.supply = supply;
+ this.useStrictMath = useStrictMath;
}
private long exchangeToSupply(long balance, long quant) {
@@ -16,7 +19,8 @@ private long exchangeToSupply(long balance, long quant) {
long newBalance = balance + quant;
logger.debug("balance + quant: " + newBalance);
- double issuedSupply = -supply * (1.0 - Math.pow(1.0 + (double) quant / newBalance, 0.0005));
+ double issuedSupply = -supply * (1.0
+ - Maths.pow(1.0 + (double) quant / newBalance, 0.0005, this.useStrictMath));
logger.debug("issuedSupply: " + issuedSupply);
long out = (long) issuedSupply;
supply += out;
@@ -27,13 +31,14 @@ private long exchangeToSupply(long balance, long quant) {
private long exchangeFromSupply(long balance, long supplyQuant) {
supply -= supplyQuant;
- double exchangeBalance =
- balance * (Math.pow(1.0 + (double) supplyQuant / supply, 2000.0) - 1.0);
+ double exchangeBalance = balance
+ * (Maths.pow(1.0 + (double) supplyQuant / supply, 2000.0, this.useStrictMath) - 1.0);
logger.debug("exchangeBalance: " + exchangeBalance);
return (long) exchangeBalance;
}
+ @Override
public long exchange(long sellTokenBalance, long buyTokenBalance, long sellTokenQuant) {
long relay = exchangeToSupply(sellTokenBalance, sellTokenQuant);
return exchangeFromSupply(buyTokenBalance, relay);
diff --git a/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java
index 30fd1f7ecd3..06513a0edc7 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/ReceiptCapsule.java
@@ -1,5 +1,8 @@
package org.tron.core.capsule;
+import static org.tron.common.math.Maths.min;
+import static org.tron.common.math.Maths.multiplyExact;
+
import java.util.Objects;
import lombok.Getter;
import lombok.Setter;
@@ -20,6 +23,7 @@
public class ReceiptCapsule {
private ResourceReceipt receipt;
+
@Getter
@Setter
private long multiSignFee;
@@ -27,6 +31,7 @@ public class ReceiptCapsule {
@Getter
@Setter
private long memoFee;
+
/**
* Available energy of contract deployer before executing transaction
*/
@@ -39,6 +44,63 @@ public class ReceiptCapsule {
@Setter
private long callerEnergyLeft;
+ /**
+ * Energy usage of caller before merging frozen energy
+ */
+ @Getter
+ @Setter
+ private long callerEnergyUsage;
+
+ /**
+ * Energy usage of caller after merging frozen energy
+ */
+ @Getter
+ @Setter
+ private long callerEnergyMergedUsage;
+
+ /**
+ * Energy usage of origin after merging frozen energy
+ */
+ @Getter
+ @Setter
+ private long originEnergyMergedUsage;
+
+ /**
+ * Window size of caller before merging frozen energy
+ */
+ @Getter
+ @Setter
+ private long callerEnergyWindowSize;
+
+ @Getter
+ @Setter
+ private long callerEnergyWindowSizeV2;
+
+ /**
+ * Window size of caller after merging frozen energy
+ */
+ @Getter
+ @Setter
+ private long callerEnergyMergedWindowSize;
+
+ /**
+ * Window size of origin before merging frozen energy
+ */
+ @Getter
+ @Setter
+ private long originEnergyWindowSize;
+
+ @Getter
+ @Setter
+ private long originEnergyWindowSizeV2;
+
+ /**
+ * Window size of origin after merging frozen energy
+ */
+ @Getter
+ @Setter
+ private long originEnergyMergedWindowSize;
+
private Sha256Hash receiptAddress;
public ReceiptCapsule(ResourceReceipt data, Sha256Hash receiptAddress) {
@@ -109,6 +171,14 @@ public void setEnergyUsageTotal(long energyUsage) {
this.receipt = this.receipt.toBuilder().setEnergyUsageTotal(energyUsage).build();
}
+ public long getEnergyPenaltyTotal() {
+ return this.receipt.getEnergyPenaltyTotal();
+ }
+
+ public void setEnergyPenaltyTotal(long penalty) {
+ this.receipt = this.receipt.toBuilder().setEnergyPenaltyTotal(penalty).build();
+ }
+
public long getNetUsage() {
return this.receipt.getNetUsage();
}
@@ -133,6 +203,12 @@ public void payEnergyBill(DynamicPropertiesStore dynamicPropertiesStore,
AccountCapsule caller,
long percent, long originEnergyLimit, EnergyProcessor energyProcessor, long now)
throws BalanceInsufficientException {
+
+ // Reset origin energy usage here! Because after stake 2.0, this field are reused for
+ // recording pre-merge frozen energy for origin account. If total energy usage is zero, this
+ // field will be a dirty record.
+ this.setOriginEnergyUsage(0);
+
if (receipt.getEnergyUsageTotal() <= 0) {
return;
}
@@ -142,12 +218,14 @@ public void payEnergyBill(DynamicPropertiesStore dynamicPropertiesStore,
receipt.getEnergyUsageTotal(), receipt.getResult(), energyProcessor, now);
return;
}
+ boolean disableJavaLangMath = dynamicPropertiesStore.disableJavaLangMath();
- if ((!Objects.isNull(origin))&&caller.getAddress().equals(origin.getAddress())) {
+ if ((!Objects.isNull(origin)) && caller.getAddress().equals(origin.getAddress())) {
payEnergyBill(dynamicPropertiesStore, accountStore, forkController, caller,
receipt.getEnergyUsageTotal(), receipt.getResult(), energyProcessor, now);
} else {
- long originUsage = Math.multiplyExact(receipt.getEnergyUsageTotal(), percent) / 100;
+ long originUsage = multiplyExact(receipt.getEnergyUsageTotal(), percent, disableJavaLangMath)
+ / 100;
originUsage = getOriginUsage(dynamicPropertiesStore, origin, originEnergyLimit,
energyProcessor,
originUsage);
@@ -163,16 +241,20 @@ public void payEnergyBill(DynamicPropertiesStore dynamicPropertiesStore,
private long getOriginUsage(DynamicPropertiesStore dynamicPropertiesStore, AccountCapsule origin,
long originEnergyLimit,
EnergyProcessor energyProcessor, long originUsage) {
-
- if (dynamicPropertiesStore.getAllowTvmFreeze() == 1) {
- return Math.min(originUsage, Math.min(originEnergyLeft, originEnergyLimit));
+ boolean disableJavaLangMath = dynamicPropertiesStore.disableJavaLangMath();
+ if (dynamicPropertiesStore.getAllowTvmFreeze() == 1
+ || dynamicPropertiesStore.supportUnfreezeDelay()) {
+ return min(originUsage, min(originEnergyLeft, originEnergyLimit, disableJavaLangMath),
+ disableJavaLangMath);
}
if (checkForEnergyLimit(dynamicPropertiesStore)) {
- return Math.min(originUsage,
- Math.min(energyProcessor.getAccountLeftEnergyFromFreeze(origin), originEnergyLimit));
+ return min(originUsage,
+ min(energyProcessor.getAccountLeftEnergyFromFreeze(origin), originEnergyLimit,
+ disableJavaLangMath), disableJavaLangMath);
}
- return Math.min(originUsage, energyProcessor.getAccountLeftEnergyFromFreeze(origin));
+ return min(originUsage, energyProcessor.getAccountLeftEnergyFromFreeze(origin),
+ disableJavaLangMath);
}
private void payEnergyBill(
@@ -184,7 +266,8 @@ private void payEnergyBill(
EnergyProcessor energyProcessor,
long now) throws BalanceInsufficientException {
long accountEnergyLeft;
- if (dynamicPropertiesStore.getAllowTvmFreeze() == 1) {
+ if (dynamicPropertiesStore.getAllowTvmFreeze() == 1
+ || dynamicPropertiesStore.supportUnfreezeDelay()) {
accountEnergyLeft = callerEnergyLeft;
} else {
accountEnergyLeft = energyProcessor.getAccountLeftEnergyFromFreeze(account);
@@ -226,7 +309,7 @@ private void payEnergyBill(
} else {
//send to blackHole
Commons.adjustBalance(accountStore, accountStore.getBlackhole(),
- energyFee);
+ energyFee, dynamicPropertiesStore.disableJavaLangMath());
}
}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/SafeExchangeProcessor.java b/chainbase/src/main/java/org/tron/core/capsule/SafeExchangeProcessor.java
new file mode 100644
index 00000000000..8af999a34cf
--- /dev/null
+++ b/chainbase/src/main/java/org/tron/core/capsule/SafeExchangeProcessor.java
@@ -0,0 +1,45 @@
+package org.tron.core.capsule;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.math.StrictMathWrapper;
+
+@Slf4j(topic = "capsule")
+public class SafeExchangeProcessor implements ExchangeCapsule.Processor {
+
+ private static final BigDecimal SUPPLY = BigDecimal.valueOf(1_000_000_000_000_000_000L);
+
+ public static final SafeExchangeProcessor INSTANCE = new SafeExchangeProcessor();
+
+ private SafeExchangeProcessor() {
+
+ }
+
+ private BigDecimal exchangeToSupply(long balance, long quant) {
+ long newBalance = StrictMathWrapper.addExact(balance, quant);
+ BigDecimal bdQuant = BigDecimal.valueOf(quant);
+ BigDecimal bdNewBalance = BigDecimal.valueOf(newBalance);
+ BigDecimal base = BigDecimal.ONE.add(
+ bdQuant.divide(bdNewBalance, 18, RoundingMode.HALF_UP));
+ double powResult = StrictMathWrapper.pow(base.doubleValue(), 0.0005);
+ return SUPPLY.negate().multiply(
+ BigDecimal.ONE.subtract(BigDecimal.valueOf(powResult))).setScale(0, RoundingMode.DOWN);
+ }
+
+ private long exchangeFromSupply(long balance, BigDecimal supplyQuant) {
+ BigDecimal bdBalance = BigDecimal.valueOf(balance);
+ BigDecimal base = BigDecimal.ONE.add(
+ supplyQuant.divide(SUPPLY, 18, RoundingMode.HALF_UP));
+ double powResult = StrictMathWrapper.pow(base.doubleValue(), 2000.0);
+ BigDecimal exchangeBalance = bdBalance.multiply(
+ BigDecimal.valueOf(powResult).subtract(BigDecimal.ONE));
+ return exchangeBalance.setScale(0, RoundingMode.DOWN).longValueExact();
+ }
+
+ @Override
+ public long exchange(long sellTokenBalance, long buyTokenBalance, long sellTokenQuant) {
+ BigDecimal relay = exchangeToSupply(sellTokenBalance, sellTokenQuant);
+ return exchangeFromSupply(buyTokenBalance, relay);
+ }
+}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java
index afba80333cd..bb4b70cde1b 100755
--- a/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java
@@ -17,12 +17,14 @@
import static org.tron.common.utils.StringUtil.encode58Check;
import static org.tron.common.utils.WalletUtil.checkPermissionOperations;
+import static org.tron.core.Constant.MAX_CONTRACT_RESULT_SIZE;
import static org.tron.core.exception.P2pException.TypeEnum.PROTOBUF_ERROR;
import com.google.common.primitives.Bytes;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.GeneratedMessageV3;
import com.google.protobuf.Internal;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -32,22 +34,26 @@
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.tron.common.crypto.ECKey.ECDSASignature;
+import org.tron.common.crypto.Rsv;
import org.tron.common.crypto.SignInterface;
import org.tron.common.crypto.SignUtils;
+import org.tron.common.es.ExecutorServiceManager;
import org.tron.common.overlay.message.Message;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.utils.ByteArray;
+import org.tron.common.utils.ForkController;
import org.tron.common.utils.ReflectUtils;
import org.tron.common.utils.Sha256Hash;
import org.tron.core.actuator.TransactionFactory;
+import org.tron.core.config.Parameter;
import org.tron.core.db.TransactionContext;
import org.tron.core.db.TransactionTrace;
import org.tron.core.exception.BadItemException;
@@ -55,6 +61,7 @@
import org.tron.core.exception.P2pException;
import org.tron.core.exception.PermissionException;
import org.tron.core.exception.SignatureFormatException;
+import org.tron.core.exception.TransactionExpirationException;
import org.tron.core.exception.ValidateSignatureException;
import org.tron.core.store.AccountStore;
import org.tron.core.store.DynamicPropertiesStore;
@@ -83,10 +90,13 @@
@Slf4j(topic = "capsule")
public class TransactionCapsule implements ProtoCapsule {
- private static final ExecutorService executorService = Executors
- .newFixedThreadPool(CommonParameter.getInstance()
+ private static final String esName = "valid-contract-proto";
+ private static final ExecutorService executorService = ExecutorServiceManager
+ .newFixedThreadPool(esName, CommonParameter.getInstance()
.getValidContractProtoThreadNum());
private static final String OWNER_ADDRESS = "ownerAddress_";
+ // 2-6 ms in general, so we set 50 ms as the threshold for slow signature verification.
+ private static final long SLOW_SIG_VERIFY_MS = 50;
private Transaction transaction;
@Setter
@@ -98,7 +108,6 @@ public class TransactionCapsule implements ProtoCapsule {
@Setter
private TransactionTrace trxTrace;
- private StringBuilder toStringBuff = new StringBuilder();
@Getter
@Setter
private long time;
@@ -108,6 +117,13 @@ public class TransactionCapsule implements ProtoCapsule {
private byte[] ownerAddress;
private Sha256Hash id;
+ @Getter
+ @Setter
+ private boolean isTransactionCreate = false;
+ @Getter
+ @Setter
+ private boolean isInBlock = false;
+
public byte[] getOwnerAddress() {
if (this.ownerAddress == null) {
this.ownerAddress = getOwner(this.transaction.getRawData().getContract(0));
@@ -208,6 +224,11 @@ public static long getWeight(Permission permission, byte[] address) {
return 0;
}
+ /**
+ * make sure ForkController.init(ChainBaseManager) is invoked before invoke this method.
+ *
+ * @see ForkController#init(org.tron.core.ChainBaseManager)
+ */
public static long checkWeight(Permission permission, List sigs, byte[] hash,
List approveList)
throws SignatureException, PermissionException, SignatureFormatException {
@@ -232,6 +253,9 @@ public static long checkWeight(Permission permission, List sigs, byt
ByteArray.toHexString(sig.toByteArray()) + " is signed by " + encode58Check(address)
+ " but it is not contained of permission.");
}
+ if (ForkController.instance().pass(Parameter.ForkBlockVersionEnum.VERSION_4_7_1)) {
+ base64 = encode58Check(address);
+ }
if (addMap.containsKey(base64)) {
throw new PermissionException(encode58Check(address) + " has signed twice!");
}
@@ -318,19 +342,23 @@ public static byte[] getOwner(Transaction.Contract contract) {
Class extends GeneratedMessageV3> clazz = TransactionFactory
.getContract(contract.getType());
if (clazz == null) {
- logger.error("not exist {}", contract.getType());
+ logger.warn("not exist {}", contract.getType());
return new byte[0];
}
GeneratedMessageV3 generatedMessageV3 = contractParameter.unpack(clazz);
owner = ReflectUtils.getFieldValue(generatedMessageV3, OWNER_ADDRESS);
if (owner == null) {
- logger.error("not exist [{}] field,{}", OWNER_ADDRESS, clazz);
+ logger.warn("not exist [{}] field,{}", OWNER_ADDRESS, clazz);
return new byte[0];
}
break;
}
}
return owner.toByteArray();
+ } catch (InvalidProtocolBufferException invalidProtocolBufferException) {
+ logger.warn("InvalidProtocolBufferException occurred because {}, please verify the interface "
+ + "input parameters", invalidProtocolBufferException.getMessage());
+ return new byte[0];
} catch (Exception ex) {
logger.error(ex.getMessage());
return new byte[0];
@@ -432,14 +460,8 @@ public static long getCallValue(Transaction.Contract contract) {
}
public static String getBase64FromByteString(ByteString sign) {
- byte[] r = sign.substring(0, 32).toByteArray();
- byte[] s = sign.substring(32, 64).toByteArray();
- byte v = sign.byteAt(64);
- if (v < 27) {
- v += 27; //revId -> v
- }
- ECDSASignature signature = ECDSASignature.fromComponents(r, s, v);
- return signature.toBase64();
+ Rsv rsv = Rsv.fromSignature(sign.toByteArray());
+ return ECDSASignature.fromComponents(rsv.getR(), rsv.getS(), rsv.getV()).toBase64();
}
public static boolean validateSignature(Transaction transaction,
@@ -523,6 +545,17 @@ public long getTimestamp() {
return transaction.getRawData().getTimestamp();
}
+ public void setFeeLimit(long feeLimit) {
+ Transaction.raw rawData = this.transaction.getRawData().toBuilder()
+ .setFeeLimit(feeLimit)
+ .build();
+ setRawData(rawData);
+ }
+
+ public long getFeeLimit() {
+ return transaction.getRawData().getFeeLimit();
+ }
+
@Deprecated
public void createTransaction(com.google.protobuf.Message message, ContractType contractType) {
Transaction.raw.Builder transactionBuilder = Transaction.raw.newBuilder().addContract(
@@ -618,6 +651,7 @@ public boolean validatePubSignature(AccountStore accountStore,
byte[] hash = getTransactionId().getBytes();
+ long startNs = System.nanoTime();
try {
if (!validateSignature(this.transaction, hash, accountStore, dynamicPropertiesStore)) {
isVerified = false;
@@ -626,12 +660,27 @@ public boolean validatePubSignature(AccountStore accountStore,
} catch (SignatureException | PermissionException | SignatureFormatException e) {
isVerified = false;
throw new ValidateSignatureException(e.getMessage());
+ } finally {
+ logSlowSigVerify(startNs);
}
isVerified = true;
}
return true;
}
+ /**
+ * WARN-logs when a single signature verification exceeds
+ * {@link #SLOW_SIG_VERIFY_MS}. Package-private so it can be exercised from
+ * tests without forcing a real slow crypto path.
+ */
+ void logSlowSigVerify(long startNs) {
+ long costMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
+ if (costMs > SLOW_SIG_VERIFY_MS) {
+ logger.warn("slow verify: txId={}, sigCount={}, cost={} ms",
+ getTransactionId(), this.transaction.getSignatureCount(), costMs);
+ }
+ }
+
/**
* validate signature
*/
@@ -680,6 +729,18 @@ public long getSerializedSize() {
return this.transaction.getSerializedSize();
}
+ /**
+ * Compute the number of bytes that would be needed to encode an embedded message field, including
+ * tag.
+ * message Block {
+ * repeated Transaction transactions = 1;
+ * BlockHeader block_header = 2;
+ * }
+ */
+ public long computeTrxSizeForBlockMessage() {
+ return CodedOutputStream.computeMessageSize(1, this.transaction);
+ }
+
public long getResultSerializedSize() {
long size = 0;
for (Result result : this.transaction.getRetList()) {
@@ -688,6 +749,15 @@ public long getResultSerializedSize() {
return size;
}
+ public long getResultSizeWithMaxContractRet() {
+ long size = 0;
+ for (Result result : this.transaction.getRetList()) {
+ size += result.toBuilder().clearContractRet().build().getSerializedSize()
+ + MAX_CONTRACT_RESULT_SIZE;
+ }
+ return size;
+ }
+
@Override
public Transaction getInstance() {
return this.transaction;
@@ -695,8 +765,7 @@ public Transaction getInstance() {
@Override
public String toString() {
-
- toStringBuff.setLength(0);
+ StringBuilder toStringBuff = new StringBuilder();
toStringBuff.append("TransactionCapsule \n[ ");
toStringBuff.append("hash=").append(getTransactionId()).append("\n");
@@ -801,4 +870,39 @@ public BalanceContract.TransferContract getTransferContract() {
return null;
}
}
+
+ public void removeRedundantRet() {
+ Transaction tx = this.getInstance();
+ List tmpList = new ArrayList<>(tx.getRetList());
+ int contractCount = tx.getRawData().getContractCount();
+ if (tx.getRetCount() > contractCount && contractCount > 0) {
+ Transaction.Builder transactionBuilder = tx.toBuilder().clearRet();
+ for (int i = 0; i < contractCount; i++) {
+ Result result = tmpList.get(i);
+ transactionBuilder.addRet(result);
+ }
+ this.transaction = transactionBuilder.build();
+ }
+ }
+
+ public void checkExpiration(long nextSlotTime) throws TransactionExpirationException {
+ if (getExpiration() < nextSlotTime) {
+ throw new TransactionExpirationException(String.format(
+ "Transaction expiration time is %d, but next slot time is %d",
+ getExpiration(), nextSlotTime));
+ }
+ }
+
+ public boolean retCountIsGreatThanContractCount() {
+ int contractCount = getContractCount();
+ return getRetCount() > contractCount && contractCount > 0;
+ }
+
+ public int getRetCount() {
+ return this.getInstance().getRetCount();
+ }
+
+ public int getContractCount() {
+ return this.getInstance().getRawData().getContractCount();
+ }
}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/TransactionResultCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/TransactionResultCapsule.java
index f82ec427a0f..8ff3064b73c 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/TransactionResultCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/TransactionResultCapsule.java
@@ -3,6 +3,7 @@
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.List;
+import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.tron.core.exception.BadItemException;
import org.tron.protos.Protocol.MarketOrderDetail;
@@ -80,6 +81,24 @@ public void setWithdrawAmount(long amount) {
this.transactionResult = this.transactionResult.toBuilder().setWithdrawAmount(amount).build();
}
+ public long getWithdrawExpireAmount() {
+ return transactionResult.getWithdrawExpireAmount();
+ }
+
+ public void setWithdrawExpireAmount(long amount) {
+ this.transactionResult = this.transactionResult.toBuilder()
+ .setWithdrawExpireAmount(amount).build();
+ }
+
+ public Map getCancelUnfreezeV2AmountMap() {
+ return transactionResult.getCancelUnfreezeV2AmountMap();
+ }
+
+ public void putAllCancelUnfreezeV2AmountMap(Map map) {
+ this.transactionResult = this.transactionResult.toBuilder()
+ .putAllCancelUnfreezeV2Amount(map).build();
+ }
+
public long getExchangeReceivedAmount() {
return transactionResult.getExchangeReceivedAmount();
}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/VotesCapsule.java b/chainbase/src/main/java/org/tron/core/capsule/VotesCapsule.java
index a3692d52627..6e92dff56d4 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/VotesCapsule.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/VotesCapsule.java
@@ -84,6 +84,10 @@ public void addNewVotes(ByteString voteAddress, long voteCount) {
.build();
}
+ public void addAllNewVotes(List votesToAdd) {
+ this.votes = this.votes.toBuilder().addAllNewVotes(votesToAdd).build();
+ }
+
public void addOldVotes(ByteString voteAddress, long voteCount) {
this.votes = this.votes.toBuilder()
.addOldVotes(Vote.newBuilder().setVoteAddress(voteAddress).setVoteCount(voteCount).build())
diff --git a/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java b/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java
index f345a96df81..e98bba9db08 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/utils/MarketUtils.java
@@ -15,12 +15,17 @@
package org.tron.core.capsule.utils;
+import static org.tron.common.math.Maths.addExact;
+import static org.tron.common.math.Maths.floorDiv;
+import static org.tron.common.math.Maths.multiplyExact;
+
import com.google.protobuf.ByteString;
import java.math.BigInteger;
import java.util.Arrays;
import org.tron.common.crypto.Hash;
import org.tron.common.utils.ByteArray;
import org.tron.common.utils.ByteUtil;
+import org.tron.common.utils.MarketComparator;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.MarketAccountOrderCapsule;
import org.tron.core.capsule.MarketOrderCapsule;
@@ -223,48 +228,6 @@ public static byte[] createPairKey(byte[] sellTokenId, byte[] buyTokenId) {
return result;
}
- /**
- * Note: the params should be the same token pair, or you should change the order.
- * All the quantity should be bigger than 0.
- * */
- public static int comparePrice(long price1SellQuantity, long price1BuyQuantity,
- long price2SellQuantity, long price2BuyQuantity) {
- try {
- return Long.compare(Math.multiplyExact(price1BuyQuantity, price2SellQuantity),
- Math.multiplyExact(price2BuyQuantity, price1SellQuantity));
-
- } catch (ArithmeticException ex) {
- // do nothing here, because we will use BigInteger to compute again
- }
-
- BigInteger price1BuyQuantityBI = BigInteger.valueOf(price1BuyQuantity);
- BigInteger price1SellQuantityBI = BigInteger.valueOf(price1SellQuantity);
- BigInteger price2BuyQuantityBI = BigInteger.valueOf(price2BuyQuantity);
- BigInteger price2SellQuantityBI = BigInteger.valueOf(price2SellQuantity);
-
- return price1BuyQuantityBI.multiply(price2SellQuantityBI)
- .compareTo(price2BuyQuantityBI.multiply(price1SellQuantityBI));
- }
-
- /**
- * ex.
- * for sellToken is A, buyToken is TRX.
- * price_A_maker * sellQuantity_maker = Price_TRX * buyQuantity_maker
- * ==> price_A_maker = Price_TRX * buyQuantity_maker/sellQuantity_maker
- *
- * price_A_maker_1 < price_A_maker_2
- * ==> buyQuantity_maker_1/sellQuantity_maker_1 < buyQuantity_maker_2/sellQuantity_maker_2
- * ==> buyQuantity_maker_1*sellQuantity_maker_2 < buyQuantity_maker_2 * sellQuantity_maker_1
- */
- public static int comparePrice(MarketPrice price1, MarketPrice price2) {
- return comparePrice(price1.getSellTokenQuantity(), price1.getBuyTokenQuantity(),
- price2.getSellTokenQuantity(), price2.getBuyTokenQuantity());
- }
-
- public static boolean isLowerPrice(MarketPrice price1, MarketPrice price2) {
- return comparePrice(price1, price2) == -1;
- }
-
/**
* if takerPrice >= makerPrice, return True
* note: here are two different token pairs
@@ -280,7 +243,8 @@ public static boolean priceMatch(MarketPrice takerPrice, MarketPrice makerPrice)
// ==> Price_TRX * sellQuantity_taker/buyQuantity_taker >= Price_TRX * buyQuantity_maker/sellQuantity_maker
// ==> sellQuantity_taker * sellQuantity_maker > buyQuantity_taker * buyQuantity_maker
- return comparePrice(takerPrice.getBuyTokenQuantity(), takerPrice.getSellTokenQuantity(),
+ return MarketComparator.comparePrice(takerPrice.getBuyTokenQuantity(),
+ takerPrice.getSellTokenQuantity(),
makerPrice.getSellTokenQuantity(), makerPrice.getBuyTokenQuantity()) >= 0;
}
@@ -297,10 +261,10 @@ public static void updateOrderState(MarketOrderCapsule orderCapsule,
}
}
- public static long multiplyAndDivide(long a, long b, long c) {
+ public static long multiplyAndDivide(long a, long b, long c, boolean disableMath) {
try {
- long tmp = Math.multiplyExact(a, b);
- return Math.floorDiv(tmp, c);
+ long tmp = multiplyExact(a, b, disableMath);
+ return floorDiv(tmp, c, disableMath);
} catch (ArithmeticException ex) {
// do nothing here, because we will use BigInteger to compute again
}
@@ -320,8 +284,9 @@ public static void returnSellTokenRemain(MarketOrderCapsule orderCapsule,
byte[] sellTokenId = orderCapsule.getSellTokenId();
long sellTokenQuantityRemain = orderCapsule.getSellTokenQuantityRemain();
if (Arrays.equals(sellTokenId, "_".getBytes())) {
- accountCapsule.setBalance(Math.addExact(
- accountCapsule.getBalance(), sellTokenQuantityRemain));
+ accountCapsule.setBalance(addExact(
+ accountCapsule.getBalance(), sellTokenQuantityRemain,
+ dynamicStore.disableJavaLangMath()));
} else {
accountCapsule
.addAssetAmountV2(sellTokenId, sellTokenQuantityRemain, dynamicStore, assetIssueStore);
@@ -330,57 +295,7 @@ public static void returnSellTokenRemain(MarketOrderCapsule orderCapsule,
}
public static int comparePriceKey(byte[] o1, byte[] o2) {
- //compare pair
- byte[] pair1 = new byte[TOKEN_ID_LENGTH * 2];
- byte[] pair2 = new byte[TOKEN_ID_LENGTH * 2];
-
- System.arraycopy(o1, 0, pair1, 0, TOKEN_ID_LENGTH * 2);
- System.arraycopy(o2, 0, pair2, 0, TOKEN_ID_LENGTH * 2);
-
- int pairResult = org.bouncycastle.util.Arrays.compareUnsigned(pair1, pair2);
- if (pairResult != 0) {
- return pairResult;
- }
-
- //compare price
- byte[] getSellTokenQuantity1 = new byte[8];
- byte[] getBuyTokenQuantity1 = new byte[8];
-
- byte[] getSellTokenQuantity2 = new byte[8];
- byte[] getBuyTokenQuantity2 = new byte[8];
-
- int longByteNum = 8;
-
- System.arraycopy(o1, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH,
- getSellTokenQuantity1, 0, longByteNum);
- System.arraycopy(o1, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH + longByteNum,
- getBuyTokenQuantity1, 0, longByteNum);
-
- System.arraycopy(o2, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH,
- getSellTokenQuantity2, 0, longByteNum);
- System.arraycopy(o2, TOKEN_ID_LENGTH + TOKEN_ID_LENGTH + longByteNum,
- getBuyTokenQuantity2, 0, longByteNum);
-
- long sellTokenQuantity1 = ByteArray.toLong(getSellTokenQuantity1);
- long buyTokenQuantity1 = ByteArray.toLong(getBuyTokenQuantity1);
- long sellTokenQuantity2 = ByteArray.toLong(getSellTokenQuantity2);
- long buyTokenQuantity2 = ByteArray.toLong(getBuyTokenQuantity2);
-
- if ((sellTokenQuantity1 == 0 || buyTokenQuantity1 == 0)
- && (sellTokenQuantity2 == 0 || buyTokenQuantity2 == 0)) {
- return 0;
- }
-
- if (sellTokenQuantity1 == 0 || buyTokenQuantity1 == 0) {
- return -1;
- }
-
- if (sellTokenQuantity2 == 0 || buyTokenQuantity2 == 0) {
- return 1;
- }
-
- return comparePrice(sellTokenQuantity1, buyTokenQuantity1,
- sellTokenQuantity2, buyTokenQuantity2);
+ return MarketComparator.comparePriceKey(o1, o2);
}
diff --git a/chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java b/chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java
index 47ac45c9fb8..94d22f4b474 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/utils/MerkleTree.java
@@ -5,10 +5,12 @@
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import lombok.Getter;
+import net.jcip.annotations.NotThreadSafe;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.utils.Sha256Hash;
@Getter
+@NotThreadSafe
public class MerkleTree {
private static volatile MerkleTree instance;
diff --git a/chainbase/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java b/chainbase/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java
index fc164bb9e36..c9e0d30b1e6 100644
--- a/chainbase/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java
+++ b/chainbase/src/main/java/org/tron/core/capsule/utils/TransactionUtil.java
@@ -90,15 +90,17 @@ public static TransactionInfoCapsule buildTransactionInfoInstance(TransactionCap
}
ByteString contractResult = ByteString.copyFrom(programResult.getHReturn());
- ByteString ContractAddress = ByteString.copyFrom(programResult.getContractAddress());
+ ByteString contractAddress = ByteString.copyFrom(programResult.getContractAddress());
builder.setFee(fee);
builder.addContractResult(contractResult);
- builder.setContractAddress(ContractAddress);
+ builder.setContractAddress(contractAddress);
builder.setUnfreezeAmount(programResult.getRet().getUnfreezeAmount());
builder.setAssetIssueID(programResult.getRet().getAssetIssueID());
builder.setExchangeId(programResult.getRet().getExchangeId());
builder.setWithdrawAmount(programResult.getRet().getWithdrawAmount());
+ builder.setWithdrawExpireAmount(programResult.getRet().getWithdrawExpireAmount());
+ builder.putAllCancelUnfreezeV2Amount(programResult.getRet().getCancelUnfreezeV2AmountMap());
builder.setExchangeReceivedAmount(programResult.getRet().getExchangeReceivedAmount());
builder.setExchangeInjectAnotherAmount(programResult.getRet().getExchangeInjectAnotherAmount());
builder.setExchangeWithdrawAnotherAmount(
@@ -123,8 +125,18 @@ public static TransactionInfoCapsule buildTransactionInfoInstance(TransactionCap
builder.setReceipt(traceReceipt.getReceipt());
if (CommonParameter.getInstance().isSaveInternalTx()) {
- programResult.getInternalTransactions().forEach(it ->
- builder.addInternalTransactions(buildInternalTransaction(it)));
+ if (CommonParameter.getInstance().isSaveFeaturedInternalTx()) {
+ programResult.getInternalTransactions().forEach(it ->
+ builder.addInternalTransactions(buildInternalTransaction(it)));
+ } else {
+ programResult.getInternalTransactions().stream()
+ .filter(it ->
+ "call".equals(it.getNote())
+ || "create".equals(it.getNote())
+ || "suicide".equals(it.getNote()))
+ .forEach(it ->
+ builder.addInternalTransactions(buildInternalTransaction(it)));
+ }
}
return new TransactionInfoCapsule(builder.build());
diff --git a/chainbase/src/main/java/org/tron/core/db/AbstractRevokingStore.java b/chainbase/src/main/java/org/tron/core/db/AbstractRevokingStore.java
deleted file mode 100644
index 725456ce39e..00000000000
--- a/chainbase/src/main/java/org/tron/core/db/AbstractRevokingStore.java
+++ /dev/null
@@ -1,477 +0,0 @@
-package org.tron.core.db;
-
-import static org.tron.core.db2.core.SnapshotManager.simpleDecode;
-
-import com.google.common.collect.Maps;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.ToString;
-import lombok.extern.slf4j.Slf4j;
-import org.iq80.leveldb.Options;
-import org.iq80.leveldb.WriteOptions;
-import org.tron.common.parameter.CommonParameter;
-import org.tron.common.storage.WriteOptionsWrapper;
-import org.tron.common.storage.leveldb.LevelDbDataSourceImpl;
-import org.tron.common.utils.FileUtil;
-import org.tron.common.utils.StorageUtils;
-import org.tron.common.utils.Utils;
-import org.tron.core.db.common.SourceInter;
-import org.tron.core.db2.ISession;
-import org.tron.core.db2.common.IRevokingDB;
-import org.tron.core.db2.core.Chainbase;
-import org.tron.core.db2.core.RevokingDBWithCachingOldValue;
-import org.tron.core.exception.RevokingStoreIllegalStateException;
-
-@Slf4j(topic = "DB")
-@Getter // only for unit test
-public abstract class AbstractRevokingStore implements RevokingDatabase {
-
- private static final int DEFAULT_STACK_MAX_SIZE = 256;
- private static String ACTIVE_DIALOG_POSITIVE = "activeDialog has to be greater than 0";
- private Deque stack = new LinkedList<>();
- private boolean disabled = true;
- private int activeDialog = 0;
- private AtomicInteger maxSize = new AtomicInteger(DEFAULT_STACK_MAX_SIZE);
- private WriteOptionsWrapper writeOptionsWrapper = WriteOptionsWrapper.getInstance()
- .sync(CommonParameter.getInstance().getStorage().isDbSync());
- private List dbs = new ArrayList<>();
-
- @Override
- public ISession buildSession() {
- return buildSession(false);
- }
-
- @Override
- public synchronized ISession buildSession(boolean forceEnable) {
- if (disabled && !forceEnable) {
- return new Dialog(this);
- }
-
- boolean disableOnExit = disabled && forceEnable;
- if (forceEnable) {
- disabled = false;
- }
-
- while (stack.size() > maxSize.get()) {
- stack.poll();
- }
-
- stack.add(new RevokingState());
- ++activeDialog;
- return new Dialog(this, disableOnExit);
- }
-
- @Override
- public void setCursor(Chainbase.Cursor cursor) {
-
- }
-
- @Override
- public void setCursor(Chainbase.Cursor cursor, long offset) {
-
- }
-
- @Override
- public synchronized void check() {
- LevelDbDataSourceImpl check =
- new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName("tmp"), "tmp",
- new Options(),
- new WriteOptions());
- check.initDB();
-
- if (!check.allKeys().isEmpty()) {
- Map dbMap = dbs.stream()
- .map(db -> Maps.immutableEntry(db.getDBName(), db))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
-
- for (Map.Entry e : check) {
- byte[] key = e.getKey();
- byte[] value = e.getValue();
- String db = simpleDecode(key);
- if (dbMap.get(db) == null) {
- continue;
- }
- byte[] realKey = Arrays.copyOfRange(key, db.getBytes().length + 4, key.length);
-
- byte[] realValue = value.length == 1 ? null : Arrays.copyOfRange(value, 1, value.length);
- if (realValue != null) {
- dbMap.get(db).putData(realKey, realValue);
- } else {
- dbMap.get(db).deleteData(realKey);
- }
- }
- }
-
- check.closeDB();
- FileUtil.recursiveDelete(check.getDbPath().toString());
- }
-
- @Override
- public void add(IRevokingDB revokingDB) {
- dbs.add(((RevokingDBWithCachingOldValue) revokingDB).getDbSource());
- }
-
- public synchronized void onCreate(RevokingTuple tuple, byte[] value) {
- if (disabled) {
- return;
- }
-
- addIfEmpty();
- RevokingState state = stack.peekLast();
- state.newIds.add(tuple);
- }
-
- public synchronized void onModify(RevokingTuple tuple, byte[] value) {
- if (disabled) {
- return;
- }
-
- addIfEmpty();
- RevokingState state = stack.peekLast();
- if (state.newIds.contains(tuple) || state.oldValues.containsKey(tuple)) {
- return;
- }
-
- state.oldValues.put(tuple, Utils.clone(value));
- }
-
- public synchronized void onRemove(RevokingTuple tuple, byte[] value) {
- if (disabled) {
- return;
- }
-
- addIfEmpty();
- RevokingState state = stack.peekLast();
- if (state.newIds.contains(tuple)) {
- state.newIds.remove(tuple);
- return;
- }
-
- if (state.oldValues.containsKey(tuple)) {
- state.removed.put(tuple, state.oldValues.get(tuple));
- state.oldValues.remove(tuple);
- return;
- }
-
- if (state.removed.containsKey(tuple)) {
- return;
- }
-
- state.removed.put(tuple, Utils.clone(value));
- }
-
- @Override
- public synchronized void merge() {
- if (activeDialog <= 0) {
- throw new RevokingStoreIllegalStateException(ACTIVE_DIALOG_POSITIVE);
- }
-
- if (activeDialog == 1 && stack.size() == 1) {
- stack.pollLast();
- --activeDialog;
- return;
- }
-
- if (stack.size() < 2) {
- return;
- }
-
- RevokingState state = stack.peekLast();
- @SuppressWarnings("unchecked")
- List list = (List) stack;
- RevokingState prevState = list.get(stack.size() - 2);
-
- state.oldValues.entrySet().stream()
- .filter(e -> !prevState.newIds.contains(e.getKey()))
- .filter(e -> !prevState.oldValues.containsKey(e.getKey()))
- .forEach(e -> prevState.oldValues.put(e.getKey(), e.getValue()));
-
- prevState.newIds.addAll(state.newIds);
-
- state.removed.entrySet().stream()
- .filter(e -> {
- boolean has = prevState.newIds.contains(e.getKey());
- if (has) {
- prevState.newIds.remove(e.getKey());
- }
-
- return !has;
- })
- .filter(e -> {
- boolean has = prevState.oldValues.containsKey(e.getKey());
- if (has) {
- prevState.removed.put(e.getKey(), prevState.oldValues.get(e.getKey()));
- prevState.oldValues.remove(e.getKey());
- }
-
- return !has;
- })
- .forEach(e -> prevState.removed.put(e.getKey(), e.getValue()));
-
- stack.pollLast();
- --activeDialog;
- }
-
- @Override
- public synchronized void revoke() {
- if (disabled) {
- return;
- }
-
- if (activeDialog <= 0) {
- throw new RevokingStoreIllegalStateException(ACTIVE_DIALOG_POSITIVE);
- }
-
- disabled = true;
-
- try {
- RevokingState state = stack.peekLast();
- if (Objects.isNull(state)) {
- return;
- }
-
- state.oldValues.forEach((k, v) -> k.database.putData(k.key, v));
- state.newIds.forEach(e -> e.database.deleteData(e.key));
- state.removed.forEach((k, v) -> k.database.putData(k.key, v));
- stack.pollLast();
- } finally {
- disabled = false;
- }
- --activeDialog;
- }
-
- @Override
- public synchronized void commit() {
- if (activeDialog <= 0) {
- throw new RevokingStoreIllegalStateException(ACTIVE_DIALOG_POSITIVE);
- }
-
- --activeDialog;
- }
-
- @Override
- public synchronized void pop() {
- prune(writeOptionsWrapper);
- }
-
- @Override
- public synchronized void fastPop() {
- prune(WriteOptionsWrapper.getInstance());
- }
-
- private synchronized void prune(WriteOptionsWrapper optionsWrapper) {
- if (activeDialog != 0) {
- throw new RevokingStoreIllegalStateException("activeDialog has to be equal 0");
- }
-
- if (stack.isEmpty()) {
- throw new RevokingStoreIllegalStateException("stack is empty");
- }
-
- disabled = true;
-
- try {
- RevokingState state = stack.peekLast();
- state.oldValues.forEach((k, v) -> k.database.putData(k.key, v));
- state.newIds.forEach(e -> e.database.deleteData(e.key));
- state.removed.forEach((k, v) -> k.database.putData(k.key, v));
- stack.pollLast();
- } finally {
- disabled = false;
- }
- }
-
- @Override
- public synchronized void enable() {
- disabled = false;
- }
-
- @Override
- public synchronized void disable() {
- disabled = true;
- }
-
- private void addIfEmpty() {
- if (stack.isEmpty()) {
- stack.add(new RevokingState());
- }
- }
-
- @Override
- public synchronized int size() {
- return stack.size();
- }
-
- public int getMaxSize() {
- return maxSize.get();
- }
-
- @Override
- public void setMaxSize(int maxSize) {
- this.maxSize.set(maxSize);
- }
-
- @Override
- public void setMaxFlushCount(int maxFlushCount) {
- }
-
- public synchronized void shutdown() {
- System.err.println("******** begin to pop revokingDb ********");
- System.err.println("******** before revokingDb size:" + size());
- try {
- disable();
- while (true) {
- try {
- commit();
- } catch (RevokingStoreIllegalStateException e) {
- break;
- }
- if (activeDialog <= 0) {
- break;
- }
- }
-
- while (true) {
- try {
- pop();
- } catch (RevokingStoreIllegalStateException e) {
- break;
- }
- if (activeDialog != 0) {
- break;
- }
- if (stack.isEmpty()) {
- break;
- }
- }
- } catch (Exception e) {
- System.err.println("******** failed to pop revokingStore. " + e);
- } finally {
- System.err.println("******** after revokingStore size:" + stack.size());
- System.err.println("******** after revokingStore contains:" + stack);
- System.err.println("******** end to pop revokingStore ********");
- }
- }
-
- @Slf4j(topic = "DB")
- @Getter // only for unit test
- public static class Dialog implements ISession {
-
- private RevokingDatabase revokingDatabase;
- private boolean applyRevoking = true;
- private boolean disableOnExit = false;
-
- public Dialog(Dialog dialog) {
- this.revokingDatabase = dialog.revokingDatabase;
- this.applyRevoking = dialog.applyRevoking;
- dialog.applyRevoking = false;
- }
-
- public Dialog(RevokingDatabase revokingDatabase) {
- this(revokingDatabase, false);
- }
-
- public Dialog(RevokingDatabase revokingDatabase, boolean disableOnExit) {
- this.revokingDatabase = revokingDatabase;
- this.disableOnExit = disableOnExit;
- }
-
- @Override
- public void commit() {
- applyRevoking = false;
- revokingDatabase.commit();
- }
-
- @Override
- public void revoke() {
- if (applyRevoking) {
- revokingDatabase.revoke();
- }
-
- applyRevoking = false;
- }
-
- @Override
- public void merge() {
- if (applyRevoking) {
- revokingDatabase.merge();
- }
-
- applyRevoking = false;
- }
-
- void copy(Dialog dialog) {
- if (this.equals(dialog)) {
- return;
- }
-
- if (applyRevoking) {
- revokingDatabase.revoke();
- }
- applyRevoking = dialog.applyRevoking;
- dialog.applyRevoking = false;
- }
-
- @Override
- public void destroy() {
- try {
- if (applyRevoking) {
- revokingDatabase.revoke();
- }
- } catch (Exception e) {
- logger.error("revoke database error.", e);
- }
- if (disableOnExit) {
- revokingDatabase.disable();
- }
- }
-
- @Override
- public void close() {
- try {
- if (applyRevoking) {
- revokingDatabase.revoke();
- }
- } catch (Exception e) {
- logger.error("revoke database error.", e);
- throw new RevokingStoreIllegalStateException(e);
- }
- if (disableOnExit) {
- revokingDatabase.disable();
- }
- }
- }
-
- @ToString
- @Getter // only for unit test
- static class RevokingState {
-
- private Map oldValues = new HashMap<>();
- private Set newIds = new HashSet<>();
- private Map removed = new HashMap<>();
- }
-
- @AllArgsConstructor
- @EqualsAndHashCode
- @Getter
- @ToString
- public static class RevokingTuple {
-
- private SourceInter database;
- private byte[] key;
- }
-
-}
diff --git a/framework/src/main/java/org/tron/core/db/BandwidthProcessor.java b/chainbase/src/main/java/org/tron/core/db/BandwidthProcessor.java
similarity index 79%
rename from framework/src/main/java/org/tron/core/db/BandwidthProcessor.java
rename to chainbase/src/main/java/org/tron/core/db/BandwidthProcessor.java
index c349c6bf148..ece16b25819 100644
--- a/framework/src/main/java/org/tron/core/db/BandwidthProcessor.java
+++ b/chainbase/src/main/java/org/tron/core/db/BandwidthProcessor.java
@@ -1,8 +1,10 @@
package org.tron.core.db;
+import static org.tron.core.Constant.PER_SIGN_LENGTH;
import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
import static org.tron.protos.Protocol.Transaction.Contract.ContractType.ShieldedTransferContract;
import static org.tron.protos.Protocol.Transaction.Contract.ContractType.TransferAssetContract;
+import static org.tron.protos.contract.Common.ResourceCode.BANDWIDTH;
import com.google.protobuf.ByteString;
import java.util.HashMap;
@@ -19,6 +21,7 @@
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.exception.AccountResourceInsufficientException;
import org.tron.core.exception.ContractValidateException;
+import org.tron.core.exception.TooBigTransactionException;
import org.tron.core.exception.TooBigTransactionResultException;
import org.tron.protos.Protocol.Transaction.Contract;
import org.tron.protos.contract.AssetIssueContractOuterClass.TransferAssetContract;
@@ -34,16 +37,19 @@ public BandwidthProcessor(ChainBaseManager chainBaseManager) {
this.chainBaseManager = chainBaseManager;
}
- @Override
- public void updateUsage(AccountCapsule accountCapsule) {
+ public void updateUsageForDelegated(AccountCapsule ac) {
long now = chainBaseManager.getHeadSlot();
- updateUsage(accountCapsule, now);
+ long oldNetUsage = ac.getNetUsage();
+ long latestConsumeTime = ac.getLatestConsumeTime();
+ ac.setNetUsage(increase(ac, BANDWIDTH, oldNetUsage, 0, latestConsumeTime, now));
}
- private void updateUsage(AccountCapsule accountCapsule, long now) {
+ public void updateUsage(AccountCapsule accountCapsule) {
+ long now = chainBaseManager.getHeadSlot();
long oldNetUsage = accountCapsule.getNetUsage();
long latestConsumeTime = accountCapsule.getLatestConsumeTime();
- accountCapsule.setNetUsage(increase(oldNetUsage, 0, latestConsumeTime, now));
+ accountCapsule.setNetUsage(increase(accountCapsule, BANDWIDTH,
+ oldNetUsage, 0, latestConsumeTime, now));
long oldFreeNetUsage = accountCapsule.getFreeNetUsage();
long latestConsumeFreeTime = accountCapsule.getLatestConsumeFreeTime();
accountCapsule.setFreeNetUsage(increase(oldFreeNetUsage, 0, latestConsumeFreeTime, now));
@@ -58,8 +64,7 @@ private void updateUsage(AccountCapsule accountCapsule, long now) {
});
}
Map assetMapV2 = accountCapsule.getAssetMapV2();
- Map map = new HashMap<>();
- map.putAll(assetMapV2);
+ Map map = new HashMap<>(assetMapV2);
accountCapsule.getAllFreeAssetNetUsageV2().forEach((k, v) -> {
if (!map.containsKey(k)) {
map.put(k, 0L);
@@ -91,8 +96,17 @@ public void updateUsage(AssetIssueCapsule assetIssueCapsule, long now) {
@Override
public void consume(TransactionCapsule trx, TransactionTrace trace)
throws ContractValidateException, AccountResourceInsufficientException,
- TooBigTransactionResultException {
+ TooBigTransactionResultException, TooBigTransactionException {
List contracts = trx.getInstance().getRawData().getContractList();
+ long resultSizeWithMaxContractRet = trx.getResultSizeWithMaxContractRet();
+ boolean optimizeTxs = !trx.isInBlock() || chainBaseManager
+ .getDynamicPropertiesStore().allowConsensusLogicOptimization();
+ if (!trx.isInBlock() && resultSizeWithMaxContractRet >
+ Constant.MAX_RESULT_SIZE_IN_TX * contracts.size()) {
+ throw new TooBigTransactionResultException(String.format(
+ "Too big transaction result, TxId %s, the result size is %d bytes, maxResultSize %d",
+ trx.getTransactionId(), resultSizeWithMaxContractRet, Constant.MAX_RESULT_SIZE_IN_TX));
+ }
if (trx.getResultSerializedSize() > Constant.MAX_RESULT_SIZE_IN_TX * contracts.size()) {
throw new TooBigTransactionResultException();
}
@@ -123,6 +137,17 @@ public void consume(TransactionCapsule trx, TransactionTrace trace)
}
long now = chainBaseManager.getHeadSlot();
if (contractCreateNewAccount(contract)) {
+ if (optimizeTxs) {
+ long maxCreateAccountTxSize = dynamicPropertiesStore.getMaxCreateAccountTxSize();
+ int signatureCount = trx.getInstance().getSignatureCount();
+ long createAccountBytesSize = trx.getInstance().toBuilder().clearRet()
+ .build().getSerializedSize() - (signatureCount * PER_SIGN_LENGTH);
+ if (createAccountBytesSize > maxCreateAccountTxSize) {
+ throw new TooBigTransactionException(String.format(
+ "Too big new account transaction, TxId %s, the size is %d bytes, maxTxSize %d",
+ trx.getTransactionId(), createAccountBytesSize, maxCreateAccountTxSize));
+ }
+ }
consumeForCreateNewAccount(accountCapsule, bytesSize, now, trace);
continue;
}
@@ -189,14 +214,25 @@ public boolean consumeBandwidthForCreateNewAccount(AccountCapsule accountCapsule
long netUsage = accountCapsule.getNetUsage();
long latestConsumeTime = accountCapsule.getLatestConsumeTime();
long netLimit = calculateGlobalNetLimit(accountCapsule);
- long newNetUsage = increase(netUsage, 0, latestConsumeTime, now);
+ long newNetUsage;
+ if (!dynamicPropertiesStore.supportUnfreezeDelay()) {
+ newNetUsage = increase(netUsage, 0, latestConsumeTime, now);
+ } else {
+ // only participate in the calculation as a temporary variable, without disk flushing
+ newNetUsage = recovery(accountCapsule, BANDWIDTH, netUsage, latestConsumeTime, now);
+ }
long netCost = bytes * createNewAccountBandwidthRatio;
if (netCost <= (netLimit - newNetUsage)) {
- latestConsumeTime = now;
long latestOperationTime = chainBaseManager.getHeadBlockTimeStamp();
- newNetUsage = increase(newNetUsage, netCost, latestConsumeTime, now);
- accountCapsule.setLatestConsumeTime(latestConsumeTime);
+ if (!dynamicPropertiesStore.supportUnfreezeDelay()) {
+ newNetUsage = increase(newNetUsage, netCost, now, now);
+ } else {
+ // Participate in calculation and flush disk persistence
+ newNetUsage = increase(accountCapsule, BANDWIDTH,
+ netUsage, netCost, latestConsumeTime, now);
+ }
+ accountCapsule.setLatestConsumeTime(now);
accountCapsule.setLatestOperationTime(latestOperationTime);
accountCapsule.setNetUsage(newNetUsage);
@@ -322,8 +358,14 @@ private boolean useAssetAccountNet(Contract contract, AccountCapsule accountCaps
long issuerNetUsage = issuerAccountCapsule.getNetUsage();
long latestConsumeTime = issuerAccountCapsule.getLatestConsumeTime();
long issuerNetLimit = calculateGlobalNetLimit(issuerAccountCapsule);
-
- long newIssuerNetUsage = increase(issuerNetUsage, 0, latestConsumeTime, now);
+ long newIssuerNetUsage;
+ if (!dynamicPropertiesStore.supportUnfreezeDelay()) {
+ newIssuerNetUsage = increase(issuerNetUsage, 0, latestConsumeTime, now);
+ } else {
+ // only participate in the calculation as a temporary variable, without disk flushing
+ newIssuerNetUsage = recovery(issuerAccountCapsule, BANDWIDTH, issuerNetUsage,
+ latestConsumeTime, now);
+ }
if (bytes > (issuerNetLimit - newIssuerNetUsage)) {
logger.debug("The {} issuer's bandwidth is not enough."
@@ -332,19 +374,24 @@ private boolean useAssetAccountNet(Contract contract, AccountCapsule accountCaps
return false;
}
- latestConsumeTime = now;
latestAssetOperationTime = now;
publicLatestFreeNetTime = now;
long latestOperationTime = chainBaseManager.getHeadBlockTimeStamp();
+ if (!dynamicPropertiesStore.supportUnfreezeDelay()) {
+ newIssuerNetUsage = increase(newIssuerNetUsage, bytes, now, now);
+ } else {
+ // Participate in calculation and flush disk persistence
+ newIssuerNetUsage = increase(issuerAccountCapsule, BANDWIDTH,
+ issuerNetUsage, bytes, latestConsumeTime, now);
+ }
- newIssuerNetUsage = increase(newIssuerNetUsage, bytes, latestConsumeTime, now);
newFreeAssetNetUsage = increase(newFreeAssetNetUsage,
bytes, latestAssetOperationTime, now);
newPublicFreeAssetNetUsage = increase(newPublicFreeAssetNetUsage, bytes,
publicLatestFreeNetTime, now);
issuerAccountCapsule.setNetUsage(newIssuerNetUsage);
- issuerAccountCapsule.setLatestConsumeTime(latestConsumeTime);
+ issuerAccountCapsule.setLatestConsumeTime(now);
assetIssueCapsule.setPublicFreeAssetNetUsage(newPublicFreeAssetNetUsage);
assetIssueCapsule.setPublicLatestFreeNetTime(publicLatestFreeNetTime);
@@ -384,10 +431,12 @@ private boolean useAssetAccountNet(Contract contract, AccountCapsule accountCaps
public long calculateGlobalNetLimit(AccountCapsule accountCapsule) {
long frozeBalance = accountCapsule.getAllFrozenBalanceForBandwidth();
+ if (dynamicPropertiesStore.supportUnfreezeDelay()) {
+ return calculateGlobalNetLimitV2(frozeBalance);
+ }
if (frozeBalance < TRX_PRECISION) {
return 0;
}
- long netWeight = frozeBalance / TRX_PRECISION;
long totalNetLimit = chainBaseManager.getDynamicPropertiesStore().getTotalNetLimit();
long totalNetWeight = chainBaseManager.getDynamicPropertiesStore().getTotalNetWeight();
if (dynamicPropertiesStore.allowNewReward() && totalNetWeight <= 0) {
@@ -396,6 +445,23 @@ public long calculateGlobalNetLimit(AccountCapsule accountCapsule) {
if (totalNetWeight == 0) {
return 0;
}
+ if (hardenCalculation()) {
+ return calculateGlobalLimitV1(frozeBalance, totalNetLimit, totalNetWeight);
+ }
+ long netWeight = frozeBalance / TRX_PRECISION;
+ return (long) (netWeight * ((double) totalNetLimit / totalNetWeight));
+ }
+
+ public long calculateGlobalNetLimitV2(long frozeBalance) {
+ long totalNetLimit = dynamicPropertiesStore.getTotalNetLimit();
+ long totalNetWeight = dynamicPropertiesStore.getTotalNetWeight();
+ if (totalNetWeight == 0) {
+ return 0;
+ }
+ if (hardenCalculation()) {
+ return calculateGlobalLimitV2(frozeBalance, totalNetLimit, totalNetWeight);
+ }
+ double netWeight = (double) frozeBalance / TRX_PRECISION;
return (long) (netWeight * ((double) totalNetLimit / totalNetWeight));
}
@@ -405,7 +471,14 @@ private boolean useAccountNet(AccountCapsule accountCapsule, long bytes, long no
long latestConsumeTime = accountCapsule.getLatestConsumeTime();
long netLimit = calculateGlobalNetLimit(accountCapsule);
- long newNetUsage = increase(netUsage, 0, latestConsumeTime, now);
+ long newNetUsage;
+ if (!dynamicPropertiesStore.supportUnfreezeDelay()) {
+ newNetUsage = increase(netUsage, 0, latestConsumeTime, now);
+ } else {
+ // only participate in the calculation as a temporary variable, without disk flushing
+ newNetUsage = recovery(accountCapsule, BANDWIDTH, netUsage, latestConsumeTime, now);
+ }
+
if (bytes > (netLimit - newNetUsage)) {
logger.debug("Net usage is running out, now use free net usage."
@@ -414,12 +487,17 @@ private boolean useAccountNet(AccountCapsule accountCapsule, long bytes, long no
return false;
}
- latestConsumeTime = now;
long latestOperationTime = chainBaseManager.getHeadBlockTimeStamp();
- newNetUsage = increase(newNetUsage, bytes, latestConsumeTime, now);
+ if (!dynamicPropertiesStore.supportUnfreezeDelay()) {
+ newNetUsage = increase(newNetUsage, bytes, now, now);
+ } else {
+ // Participate in calculation and flush disk persistence
+ newNetUsage = increase(accountCapsule, BANDWIDTH, netUsage, bytes, latestConsumeTime, now);
+ }
+
accountCapsule.setNetUsage(newNetUsage);
accountCapsule.setLatestOperationTime(latestOperationTime);
- accountCapsule.setLatestConsumeTime(latestConsumeTime);
+ accountCapsule.setLatestConsumeTime(now);
chainBaseManager.getAccountStore().put(accountCapsule.createDbKey(), accountCapsule);
return true;
diff --git a/chainbase/src/main/java/org/tron/core/db/BlockIndexStore.java b/chainbase/src/main/java/org/tron/core/db/BlockIndexStore.java
index 50402002f88..5722d3506af 100644
--- a/chainbase/src/main/java/org/tron/core/db/BlockIndexStore.java
+++ b/chainbase/src/main/java/org/tron/core/db/BlockIndexStore.java
@@ -1,6 +1,10 @@
package org.tron.core.db;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -12,6 +16,7 @@
import org.tron.core.exception.ItemNotFoundException;
@Component
+@Slf4j(topic = "DB")
public class BlockIndexStore extends TronStoreWithRevoking {
@@ -44,4 +49,17 @@ public BytesCapsule get(byte[] key)
}
return new BytesCapsule(value);
}
-}
\ No newline at end of file
+
+ public List getLimitNumber(long startNumber, long limit) {
+ return pack(revokingDB.getValuesNext(ByteArray.fromLong(startNumber), limit));
+ }
+
+ private List pack(Set values) {
+ List blocks = new ArrayList<>();
+ for (byte[] bytes : values) {
+ blocks.add(new BlockId(Sha256Hash.wrap(bytes)));
+ }
+ blocks.sort(Comparator.comparing(BlockId::getNum));
+ return blocks;
+ }
+}
diff --git a/chainbase/src/main/java/org/tron/core/db/CommonStore.java b/chainbase/src/main/java/org/tron/core/db/CommonStore.java
index a22406f5f90..acc137bdb2a 100644
--- a/chainbase/src/main/java/org/tron/core/db/CommonStore.java
+++ b/chainbase/src/main/java/org/tron/core/db/CommonStore.java
@@ -3,16 +3,11 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
-import org.tron.common.utils.ByteArray;
-import org.tron.core.Constant;
import org.tron.core.capsule.BytesCapsule;
@Component
public class CommonStore extends TronDatabase {
- private static final byte[] DB_KEY_LOWEST_BLOCK_NUM = "lowest_block_num".getBytes();
- private static final byte[] DB_KEY_NODE_TYPE = "node_type".getBytes();
-
@Autowired
public CommonStore(ApplicationContext ctx) {
super("common");
@@ -37,31 +32,4 @@ public BytesCapsule get(byte[] key) {
public boolean has(byte[] key) {
return dbSource.getData(key) != null;
}
-
- public int getNodeType() {
- int nodeType = 0;
- byte[] bytes = get(DB_KEY_NODE_TYPE).getData();
- if (bytes != null) {
- nodeType = ByteArray.toInt(bytes);
- }
- return nodeType;
- }
-
- public void setNodeType(int nodeType) {
- put(DB_KEY_NODE_TYPE, new BytesCapsule(ByteArray.fromInt(nodeType)));
- }
-
- public long getLowestBlockNum() {
- long lowestBlockNum = 0;
- byte[] bytes = get(DB_KEY_LOWEST_BLOCK_NUM).getData();
- if (bytes != null) {
- lowestBlockNum = ByteArray.toLong(bytes);
- }
- return lowestBlockNum;
- }
-
- public void setLowestBlockNum(long lowestBlockNum) {
- put(DB_KEY_LOWEST_BLOCK_NUM, new BytesCapsule(ByteArray.fromLong(lowestBlockNum)));
- }
-
}
diff --git a/chainbase/src/main/java/org/tron/core/db/EnergyProcessor.java b/chainbase/src/main/java/org/tron/core/db/EnergyProcessor.java
index 5576896bc12..0c429178636 100644
--- a/chainbase/src/main/java/org/tron/core/db/EnergyProcessor.java
+++ b/chainbase/src/main/java/org/tron/core/db/EnergyProcessor.java
@@ -1,9 +1,11 @@
package org.tron.core.db;
-import static java.lang.Long.max;
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.min;
import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL;
import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import java.math.BigInteger;
import lombok.extern.slf4j.Slf4j;
import org.tron.common.parameter.CommonParameter;
import org.tron.core.capsule.AccountCapsule;
@@ -15,6 +17,8 @@
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.protos.Protocol.Account.AccountResource;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
+
@Slf4j(topic = "DB")
public class EnergyProcessor extends ResourceProcessor {
@@ -29,7 +33,6 @@ public static long getHeadSlot(DynamicPropertiesStore dynamicPropertiesStore) {
/ BLOCK_PRODUCED_INTERVAL;
}
- @Override
public void updateUsage(AccountCapsule accountCapsule) {
long now = getHeadSlot();
updateUsage(accountCapsule, now);
@@ -41,7 +44,8 @@ private void updateUsage(AccountCapsule accountCapsule, long now) {
long oldEnergyUsage = accountResource.getEnergyUsage();
long latestConsumeTime = accountResource.getLatestConsumeTimeForEnergy();
- accountCapsule.setEnergyUsage(increase(oldEnergyUsage, 0, latestConsumeTime, now));
+ accountCapsule.setEnergyUsage(increase(accountCapsule, ENERGY,
+ oldEnergyUsage, 0, latestConsumeTime, now));
}
public void updateTotalEnergyAverageUsage() {
@@ -68,19 +72,20 @@ public void updateAdaptiveTotalEnergyLimit() {
long result;
if (totalEnergyAverageUsage > targetTotalEnergyLimit) {
- result = totalEnergyCurrentLimit * AdaptiveResourceLimitConstants.CONTRACT_RATE_NUMERATOR
- / AdaptiveResourceLimitConstants.CONTRACT_RATE_DENOMINATOR;
- // logger.info(totalEnergyAverageUsage + ">" + targetTotalEnergyLimit + "\n" + result);
+ result = scaleByRate(totalEnergyCurrentLimit,
+ AdaptiveResourceLimitConstants.CONTRACT_RATE_NUMERATOR,
+ AdaptiveResourceLimitConstants.CONTRACT_RATE_DENOMINATOR);
} else {
- result = totalEnergyCurrentLimit * AdaptiveResourceLimitConstants.EXPAND_RATE_NUMERATOR
- / AdaptiveResourceLimitConstants.EXPAND_RATE_DENOMINATOR;
- // logger.info(totalEnergyAverageUsage + "<" + targetTotalEnergyLimit + "\n" + result);
+ result = scaleByRate(totalEnergyCurrentLimit,
+ AdaptiveResourceLimitConstants.EXPAND_RATE_NUMERATOR,
+ AdaptiveResourceLimitConstants.EXPAND_RATE_DENOMINATOR);
}
-
- result = Math.min(
- Math.max(result, totalEnergyLimit),
- totalEnergyLimit * dynamicPropertiesStore.getAdaptiveResourceLimitMultiplier()
- );
+ long upperBound = hardenCalculation()
+ ? BigInteger.valueOf(totalEnergyLimit).multiply(BigInteger.valueOf(
+ dynamicPropertiesStore.getAdaptiveResourceLimitMultiplier())).longValueExact()
+ : totalEnergyLimit * dynamicPropertiesStore.getAdaptiveResourceLimitMultiplier();
+ result = min(max(result, totalEnergyLimit, this.disableJavaLangMath()),
+ upperBound, this.disableJavaLangMath());
dynamicPropertiesStore.saveTotalEnergyCurrentLimit(result);
logger.debug("Adjust totalEnergyCurrentLimit, old: {}, new: {}.",
@@ -99,20 +104,33 @@ public boolean useEnergy(AccountCapsule accountCapsule, long energy, long now) {
long energyUsage = accountCapsule.getEnergyUsage();
long latestConsumeTime = accountCapsule.getAccountResource().getLatestConsumeTimeForEnergy();
long energyLimit = calculateGlobalEnergyLimit(accountCapsule);
-
- long newEnergyUsage = increase(energyUsage, 0, latestConsumeTime, now);
+ long newEnergyUsage;
+ if (!dynamicPropertiesStore.supportUnfreezeDelay()) {
+ newEnergyUsage = increase(energyUsage, 0, latestConsumeTime, now);
+ } else {
+ // only participate in the calculation as a temporary variable, without disk flushing
+ newEnergyUsage = recovery(accountCapsule, ENERGY, energyUsage,
+ latestConsumeTime, now);
+ }
if (energy > (energyLimit - newEnergyUsage)
- && dynamicPropertiesStore.getAllowTvmFreeze() == 0) {
+ && dynamicPropertiesStore.getAllowTvmFreeze() == 0
+ && !dynamicPropertiesStore.supportUnfreezeDelay()) {
return false;
}
- latestConsumeTime = now;
long latestOperationTime = dynamicPropertiesStore.getLatestBlockHeaderTimestamp();
- newEnergyUsage = increase(newEnergyUsage, energy, latestConsumeTime, now);
+ if (!dynamicPropertiesStore.supportUnfreezeDelay()) {
+ newEnergyUsage = increase(newEnergyUsage, energy, now, now);
+ } else {
+ // Participate in calculation and flush disk persistence
+ newEnergyUsage = increase(accountCapsule, ENERGY, energyUsage, energy,
+ latestConsumeTime, now);
+ }
+
accountCapsule.setEnergyUsage(newEnergyUsage);
accountCapsule.setLatestOperationTime(latestOperationTime);
- accountCapsule.setLatestConsumeTimeForEnergy(latestConsumeTime);
+ accountCapsule.setLatestConsumeTimeForEnergy(now);
accountStore.put(accountCapsule.createDbKey(), accountCapsule);
@@ -126,11 +144,13 @@ public boolean useEnergy(AccountCapsule accountCapsule, long energy, long now) {
public long calculateGlobalEnergyLimit(AccountCapsule accountCapsule) {
long frozeBalance = accountCapsule.getAllFrozenBalanceForEnergy();
+ if (dynamicPropertiesStore.supportUnfreezeDelay()) {
+ return calculateGlobalEnergyLimitV2(frozeBalance);
+ }
if (frozeBalance < TRX_PRECISION) {
return 0;
}
- long energyWeight = frozeBalance / TRX_PRECISION;
long totalEnergyLimit = dynamicPropertiesStore.getTotalEnergyCurrentLimit();
long totalEnergyWeight = dynamicPropertiesStore.getTotalEnergyWeight();
if (dynamicPropertiesStore.allowNewReward() && totalEnergyWeight <= 0) {
@@ -138,25 +158,51 @@ public long calculateGlobalEnergyLimit(AccountCapsule accountCapsule) {
} else {
assert totalEnergyWeight > 0;
}
+ if (hardenCalculation()) {
+ return calculateGlobalLimitV1(frozeBalance, totalEnergyLimit, totalEnergyWeight);
+ }
+ long energyWeight = frozeBalance / TRX_PRECISION;
return (long) (energyWeight * ((double) totalEnergyLimit / totalEnergyWeight));
}
+ public long calculateGlobalEnergyLimitV2(long frozeBalance) {
+ long totalEnergyLimit = dynamicPropertiesStore.getTotalEnergyCurrentLimit();
+ long totalEnergyWeight = dynamicPropertiesStore.getTotalEnergyWeight();
+ if (totalEnergyWeight == 0) {
+ return 0;
+ }
+ if (hardenCalculation()) {
+ return calculateGlobalLimitV2(frozeBalance, totalEnergyLimit, totalEnergyWeight);
+ }
+ double energyWeight = (double) frozeBalance / TRX_PRECISION;
+ return (long) (energyWeight * ((double) totalEnergyLimit / totalEnergyWeight));
+ }
+
+
public long getAccountLeftEnergyFromFreeze(AccountCapsule accountCapsule) {
long now = getHeadSlot();
long energyUsage = accountCapsule.getEnergyUsage();
long latestConsumeTime = accountCapsule.getAccountResource().getLatestConsumeTimeForEnergy();
long energyLimit = calculateGlobalEnergyLimit(accountCapsule);
- long newEnergyUsage = increase(energyUsage, 0, latestConsumeTime, now);
+ long newEnergyUsage = recovery(accountCapsule, ENERGY, energyUsage, latestConsumeTime, now);
- return max(energyLimit - newEnergyUsage, 0); // us
+ return max(energyLimit - newEnergyUsage, 0, this.disableJavaLangMath()); // us
}
private long getHeadSlot() {
return getHeadSlot(dynamicPropertiesStore);
}
-
+ private long scaleByRate(long value, long numerator, long denominator) {
+ if (hardenCalculation()) {
+ return BigInteger.valueOf(value)
+ .multiply(BigInteger.valueOf(numerator))
+ .divide(BigInteger.valueOf(denominator))
+ .longValueExact();
+ }
+ return value * numerator / denominator;
+ }
}
diff --git a/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java b/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java
index 09bac96e0ae..6706c430084 100644
--- a/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java
+++ b/chainbase/src/main/java/org/tron/core/db/ResourceProcessor.java
@@ -1,7 +1,13 @@
package org.tron.core.db;
+import static org.tron.common.math.Maths.min;
+import static org.tron.common.math.Maths.round;
import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+import static org.tron.core.config.Parameter.ChainConstant.WINDOW_SIZE_PRECISION;
+import java.math.BigInteger;
+import org.tron.common.math.StrictMathWrapper;
import org.tron.common.utils.Commons;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.TransactionCapsule;
@@ -10,9 +16,11 @@
import org.tron.core.exception.AccountResourceInsufficientException;
import org.tron.core.exception.BalanceInsufficientException;
import org.tron.core.exception.ContractValidateException;
+import org.tron.core.exception.TooBigTransactionException;
import org.tron.core.exception.TooBigTransactionResultException;
import org.tron.core.store.AccountStore;
import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.protos.contract.Common.ResourceCode;
abstract class ResourceProcessor {
@@ -22,7 +30,7 @@ abstract class ResourceProcessor {
protected long windowSize;
protected long averageWindowSize;
- public ResourceProcessor(DynamicPropertiesStore dynamicPropertiesStore,
+ protected ResourceProcessor(DynamicPropertiesStore dynamicPropertiesStore,
AccountStore accountStore) {
this.dynamicPropertiesStore = dynamicPropertiesStore;
this.accountStore = accountStore;
@@ -32,25 +40,35 @@ public ResourceProcessor(DynamicPropertiesStore dynamicPropertiesStore,
AdaptiveResourceLimitConstants.PERIODS_MS / BLOCK_PRODUCED_INTERVAL;
}
- abstract void updateUsage(AccountCapsule accountCapsule);
-
abstract void consume(TransactionCapsule trx, TransactionTrace trace)
- throws ContractValidateException, AccountResourceInsufficientException, TooBigTransactionResultException;
+ throws ContractValidateException, AccountResourceInsufficientException, TooBigTransactionResultException, TooBigTransactionException;
protected long increase(long lastUsage, long usage, long lastTime, long now) {
return increase(lastUsage, usage, lastTime, now, windowSize);
}
protected long increase(long lastUsage, long usage, long lastTime, long now, long windowSize) {
- long averageLastUsage = divideCeil(lastUsage * precision, windowSize);
- long averageUsage = divideCeil(usage * precision, windowSize);
+ long averageLastUsage;
+ long averageUsage;
+ if (hardenCalculation()) {
+ BigInteger biPrecision = BigInteger.valueOf(precision);
+ BigInteger biWindowSize = BigInteger.valueOf(windowSize);
+ averageLastUsage = divideCeilExact(
+ BigInteger.valueOf(lastUsage).multiply(biPrecision), biWindowSize);
+ averageUsage = divideCeilExact(
+ BigInteger.valueOf(usage).multiply(biPrecision), biWindowSize);
+ } else {
+ averageLastUsage = divideCeil(lastUsage * precision, windowSize);
+ averageUsage = divideCeil(usage * precision, windowSize);
+ }
if (lastTime != now) {
assert now > lastTime;
if (lastTime + windowSize > now) {
long delta = now - lastTime;
double decay = (windowSize - delta) / (double) windowSize;
- averageLastUsage = Math.round(averageLastUsage * decay);
+ averageLastUsage = round(averageLastUsage * decay,
+ this.disableJavaLangMath());
} else {
averageLastUsage = 0;
}
@@ -59,25 +77,241 @@ protected long increase(long lastUsage, long usage, long lastTime, long now, lon
return getUsage(averageLastUsage, windowSize);
}
+ public long recovery(AccountCapsule accountCapsule, ResourceCode resourceCode,
+ long lastUsage, long lastTime, long now) {
+ long oldWindowSize = accountCapsule.getWindowSize(resourceCode);
+ return increase(lastUsage, 0, lastTime, now, oldWindowSize);
+ }
+
+ public long increase(AccountCapsule accountCapsule, ResourceCode resourceCode,
+ long lastUsage, long usage, long lastTime, long now) {
+ if (dynamicPropertiesStore.supportAllowCancelAllUnfreezeV2()) {
+ return increaseV2(accountCapsule, resourceCode, lastUsage, usage, lastTime, now);
+ }
+ long oldWindowSize = accountCapsule.getWindowSize(resourceCode);
+ long averageLastUsage;
+ long averageUsage;
+ if (hardenCalculation()) {
+ BigInteger biPrecision = BigInteger.valueOf(this.precision);
+ averageLastUsage = divideCeilExact(
+ BigInteger.valueOf(lastUsage).multiply(biPrecision),
+ BigInteger.valueOf(oldWindowSize));
+ averageUsage = divideCeilExact(
+ BigInteger.valueOf(usage).multiply(biPrecision),
+ BigInteger.valueOf(this.windowSize));
+ } else {
+ averageLastUsage = divideCeil(lastUsage * this.precision, oldWindowSize);
+ averageUsage = divideCeil(usage * this.precision, this.windowSize);
+ }
+
+ if (lastTime != now) {
+ if (lastTime + oldWindowSize > now) {
+ long delta = now - lastTime;
+ double decay = (oldWindowSize - delta) / (double) oldWindowSize;
+ averageLastUsage = round(averageLastUsage * decay,
+ this.disableJavaLangMath());
+ } else {
+ averageLastUsage = 0;
+ }
+ }
+
+ long newUsage = getUsage(averageLastUsage, oldWindowSize, averageUsage, this.windowSize);
+ if (dynamicPropertiesStore.supportUnfreezeDelay()) {
+ long remainUsage = getUsage(averageLastUsage, oldWindowSize);
+ if (remainUsage == 0) {
+ accountCapsule.setNewWindowSize(resourceCode, this.windowSize);
+ return newUsage;
+ }
+ long remainWindowSize = oldWindowSize - (now - lastTime);
+ long newWindowSize = getNewWindowSize(remainUsage, remainWindowSize, usage,
+ windowSize, newUsage);
+ accountCapsule.setNewWindowSize(resourceCode, newWindowSize);
+ }
+ return newUsage;
+ }
+
+ public long increaseV2(AccountCapsule accountCapsule, ResourceCode resourceCode,
+ long lastUsage, long usage, long lastTime, long now) {
+ long oldWindowSizeV2 = accountCapsule.getWindowSizeV2(resourceCode);
+ long oldWindowSize = accountCapsule.getWindowSize(resourceCode);
+ long averageLastUsage;
+ long averageUsage;
+ if (hardenCalculation()) {
+ BigInteger biPrecision = BigInteger.valueOf(this.precision);
+ averageLastUsage = divideCeilExact(
+ BigInteger.valueOf(lastUsage).multiply(biPrecision),
+ BigInteger.valueOf(oldWindowSize));
+ averageUsage = divideCeilExact(
+ BigInteger.valueOf(usage).multiply(biPrecision),
+ BigInteger.valueOf(this.windowSize));
+ } else {
+ averageLastUsage = divideCeil(lastUsage * this.precision, oldWindowSize);
+ averageUsage = divideCeil(usage * this.precision, this.windowSize);
+ }
+
+ if (lastTime != now) {
+ if (lastTime + oldWindowSize > now) {
+ long delta = now - lastTime;
+ double decay = (oldWindowSize - delta) / (double) oldWindowSize;
+ averageLastUsage = round(averageLastUsage * decay,
+ this.disableJavaLangMath());
+ } else {
+ averageLastUsage = 0;
+ }
+ }
+
+ long newUsage = getUsage(averageLastUsage, oldWindowSize, averageUsage, this.windowSize);
+ long remainUsage = getUsage(averageLastUsage, oldWindowSize);
+ if (remainUsage == 0) {
+ accountCapsule.setNewWindowSizeV2(resourceCode, this.windowSize * WINDOW_SIZE_PRECISION);
+ return newUsage;
+ }
+
+ long remainWindowSize = oldWindowSizeV2 - (now - lastTime) * WINDOW_SIZE_PRECISION;
+ long newWindowSize;
+ if (hardenCalculation()) {
+ BigInteger biNewWindowSize = BigInteger.valueOf(remainUsage)
+ .multiply(BigInteger.valueOf(remainWindowSize))
+ .add(BigInteger.valueOf(usage)
+ .multiply(BigInteger.valueOf(this.windowSize))
+ .multiply(BigInteger.valueOf(WINDOW_SIZE_PRECISION)));
+ newWindowSize = divideCeilExact(biNewWindowSize, BigInteger.valueOf(newUsage));
+ } else {
+ newWindowSize = divideCeil(
+ remainUsage * remainWindowSize + usage * this.windowSize * WINDOW_SIZE_PRECISION,
+ newUsage);
+ }
+ newWindowSize = min(newWindowSize, this.windowSize * WINDOW_SIZE_PRECISION,
+ this.disableJavaLangMath());
+ accountCapsule.setNewWindowSizeV2(resourceCode, newWindowSize);
+ return newUsage;
+ }
+
+ public void unDelegateIncrease(AccountCapsule owner, final AccountCapsule receiver,
+ long transferUsage, ResourceCode resourceCode, long now) {
+ if (dynamicPropertiesStore.supportAllowCancelAllUnfreezeV2()) {
+ unDelegateIncreaseV2(owner, receiver, transferUsage, resourceCode, now);
+ return;
+ }
+ long lastOwnerTime = owner.getLastConsumeTime(resourceCode);
+ long ownerUsage = owner.getUsage(resourceCode);
+ // Update itself first
+ ownerUsage = increase(owner, resourceCode, ownerUsage, 0, lastOwnerTime, now);
+
+ long remainOwnerWindowSize = owner.getWindowSize(resourceCode);
+ long remainReceiverWindowSize = receiver.getWindowSize(resourceCode);
+ remainOwnerWindowSize = remainOwnerWindowSize < 0 ? 0 : remainOwnerWindowSize;
+ remainReceiverWindowSize = remainReceiverWindowSize < 0 ? 0 : remainReceiverWindowSize;
+
+ long newOwnerUsage = ownerUsage + transferUsage;
+ // mean ownerUsage == 0 and transferUsage == 0
+ if (newOwnerUsage == 0) {
+ owner.setNewWindowSize(resourceCode, this.windowSize);
+ owner.setUsage(resourceCode, 0);
+ owner.setLatestTime(resourceCode, now);
+ return;
+ }
+ // calculate new windowSize
+ long newOwnerWindowSize = getNewWindowSize(ownerUsage, remainOwnerWindowSize, transferUsage,
+ remainReceiverWindowSize, newOwnerUsage);
+ owner.setNewWindowSize(resourceCode, newOwnerWindowSize);
+ owner.setUsage(resourceCode, newOwnerUsage);
+ owner.setLatestTime(resourceCode, now);
+ }
+
+ public void unDelegateIncreaseV2(AccountCapsule owner, final AccountCapsule receiver,
+ long transferUsage, ResourceCode resourceCode, long now) {
+ long lastOwnerTime = owner.getLastConsumeTime(resourceCode);
+ long ownerUsage = owner.getUsage(resourceCode);
+ // Update itself first
+ ownerUsage = increase(owner, resourceCode, ownerUsage, 0, lastOwnerTime, now);
+ long newOwnerUsage = ownerUsage + transferUsage;
+ // mean ownerUsage == 0 and transferUsage == 0
+ if (newOwnerUsage == 0) {
+ owner.setNewWindowSizeV2(resourceCode, this.windowSize * WINDOW_SIZE_PRECISION);
+ owner.setUsage(resourceCode, 0);
+ owner.setLatestTime(resourceCode, now);
+ return;
+ }
+
+ long remainOwnerWindowSizeV2 = owner.getWindowSizeV2(resourceCode);
+ long remainReceiverWindowSizeV2 = receiver.getWindowSizeV2(resourceCode);
+ remainOwnerWindowSizeV2 = remainOwnerWindowSizeV2 < 0 ? 0 : remainOwnerWindowSizeV2;
+ remainReceiverWindowSizeV2 = remainReceiverWindowSizeV2 < 0 ? 0 : remainReceiverWindowSizeV2;
+
+ // calculate new windowSize
+ long newOwnerWindowSize;
+ if (hardenCalculation()) {
+ BigInteger bi = BigInteger.valueOf(ownerUsage)
+ .multiply(BigInteger.valueOf(remainOwnerWindowSizeV2))
+ .add(BigInteger.valueOf(transferUsage)
+ .multiply(BigInteger.valueOf(remainReceiverWindowSizeV2)));
+ newOwnerWindowSize = divideCeilExact(bi, BigInteger.valueOf(newOwnerUsage));
+ } else {
+ newOwnerWindowSize = divideCeil(
+ ownerUsage * remainOwnerWindowSizeV2 + transferUsage * remainReceiverWindowSizeV2,
+ newOwnerUsage);
+ }
+ newOwnerWindowSize = min(newOwnerWindowSize, this.windowSize * WINDOW_SIZE_PRECISION,
+ this.disableJavaLangMath());
+ owner.setNewWindowSizeV2(resourceCode, newOwnerWindowSize);
+ owner.setUsage(resourceCode, newOwnerUsage);
+ owner.setLatestTime(resourceCode, now);
+ }
+
+ private long getNewWindowSize(long lastUsage, long lastWindowSize, long usage,
+ long windowSize, long newUsage) {
+ if (hardenCalculation()) {
+ BigInteger bi = BigInteger.valueOf(lastUsage).multiply(BigInteger.valueOf(lastWindowSize))
+ .add(BigInteger.valueOf(usage).multiply(BigInteger.valueOf(windowSize)));
+ return bi.divide(BigInteger.valueOf(newUsage)).longValueExact();
+ }
+ return (lastUsage * lastWindowSize + usage * windowSize) / newUsage;
+ }
+
private long divideCeil(long numerator, long denominator) {
return (numerator / denominator) + ((numerator % denominator) > 0 ? 1 : 0);
}
+ private long divideCeilExact(BigInteger numerator, BigInteger denominator) {
+ BigInteger[] divRem = numerator.divideAndRemainder(denominator);
+ long result = divRem[0].longValueExact();
+ if (divRem[1].signum() > 0) {
+ result = StrictMathWrapper.addExact(result, 1);
+ }
+ return result;
+ }
+
private long getUsage(long usage, long windowSize) {
+ if (hardenCalculation()) {
+ return BigInteger.valueOf(usage).multiply(BigInteger.valueOf(windowSize))
+ .divide(BigInteger.valueOf(precision)).longValueExact();
+ }
return usage * windowSize / precision;
}
+ private long getUsage(long oldUsage, long oldWindowSize, long newUsage, long newWindowSize) {
+ if (hardenCalculation()) {
+ BigInteger bi = BigInteger.valueOf(oldUsage).multiply(BigInteger.valueOf(oldWindowSize))
+ .add(BigInteger.valueOf(newUsage).multiply(BigInteger.valueOf(newWindowSize)));
+ return bi.divide(BigInteger.valueOf(precision)).longValueExact();
+ }
+ return (oldUsage * oldWindowSize + newUsage * newWindowSize) / precision;
+ }
+
protected boolean consumeFeeForBandwidth(AccountCapsule accountCapsule, long fee) {
try {
long latestOperationTime = dynamicPropertiesStore.getLatestBlockHeaderTimestamp();
accountCapsule.setLatestOperationTime(latestOperationTime);
- Commons.adjustBalance(accountStore, accountCapsule, -fee);
+ Commons.adjustBalance(accountStore, accountCapsule, -fee,
+ this.disableJavaLangMath());
if (dynamicPropertiesStore.supportTransactionFeePool()) {
dynamicPropertiesStore.addTransactionFeePool(fee);
} else if (dynamicPropertiesStore.supportBlackHoleOptimization()) {
dynamicPropertiesStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), +fee);
+ Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), +fee,
+ this.disableJavaLangMath());
}
return true;
@@ -86,16 +320,17 @@ protected boolean consumeFeeForBandwidth(AccountCapsule accountCapsule, long fee
}
}
-
protected boolean consumeFeeForNewAccount(AccountCapsule accountCapsule, long fee) {
try {
long latestOperationTime = dynamicPropertiesStore.getLatestBlockHeaderTimestamp();
accountCapsule.setLatestOperationTime(latestOperationTime);
- Commons.adjustBalance(accountStore, accountCapsule, -fee);
+ Commons.adjustBalance(accountStore, accountCapsule, -fee,
+ this.disableJavaLangMath());
if (dynamicPropertiesStore.supportBlackHoleOptimization()) {
dynamicPropertiesStore.burnTrx(fee);
} else {
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), +fee);
+ Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), +fee,
+ this.disableJavaLangMath());
}
return true;
@@ -103,4 +338,42 @@ protected boolean consumeFeeForNewAccount(AccountCapsule accountCapsule, long fe
return false;
}
}
+
+ protected boolean disableJavaLangMath() {
+ return dynamicPropertiesStore.disableJavaLangMath();
+ }
+
+ protected boolean hardenCalculation() {
+ return dynamicPropertiesStore.allowHardenResourceCalculation();
+ }
+
+ protected long calculateGlobalLimitV1(long frozeBalance,
+ long totalLimit, long totalWeight) {
+ long weight = frozeBalance / TRX_PRECISION;
+ return BigInteger.valueOf(weight)
+ .multiply(BigInteger.valueOf(totalLimit))
+ .divide(BigInteger.valueOf(totalWeight))
+ .longValueExact();
+ }
+
+ /**
+ * Hardened replacement of legacy V2 formula
+ * {@code (long)(((double) frozeBalance / TRX_PRECISION)
+ * * ((double) totalLimit / totalWeight))}.
+ *
+ * Preserves V2 semantics: equivalent to
+ * {@code (frozeBalance * totalLimit) / (TRX_PRECISION * totalWeight)} with
+ * a single integer truncation at the end. Critically, fractional weight
+ * (i.e. {@code frozeBalance < TRX_PRECISION}) is preserved through the
+ * multiplication and only truncated at the final divide, so small balances
+ * yield the same proportional result as the double-arithmetic path.
+ */
+ protected long calculateGlobalLimitV2(long frozeBalance,
+ long totalLimit, long totalWeight) {
+ return BigInteger.valueOf(frozeBalance)
+ .multiply(BigInteger.valueOf(totalLimit))
+ .divide(BigInteger.valueOf(TRX_PRECISION)
+ .multiply(BigInteger.valueOf(totalWeight)))
+ .longValueExact();
+ }
}
diff --git a/chainbase/src/main/java/org/tron/core/db/RevokingStore.java b/chainbase/src/main/java/org/tron/core/db/RevokingStore.java
deleted file mode 100644
index 7b641cf6bc3..00000000000
--- a/chainbase/src/main/java/org/tron/core/db/RevokingStore.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.tron.core.db;
-
-public class RevokingStore extends AbstractRevokingStore {
-
- public RevokingStore() {
- }
-
- public static RevokingStore getInstance() {
- return RevokingEnum.INSTANCE.getInstance();
- }
-
- private enum RevokingEnum {
- INSTANCE;
-
- private RevokingStore instance;
-
- RevokingEnum() {
- instance = new RevokingStore();
- }
-
- private RevokingStore getInstance() {
- return instance;
- }
- }
-}
diff --git a/chainbase/src/main/java/org/tron/core/db/StorageMarket.java b/chainbase/src/main/java/org/tron/core/db/StorageMarket.java
deleted file mode 100644
index 10a3b656565..00000000000
--- a/chainbase/src/main/java/org/tron/core/db/StorageMarket.java
+++ /dev/null
@@ -1,234 +0,0 @@
-package org.tron.core.db;
-
-import lombok.extern.slf4j.Slf4j;
-import org.tron.core.capsule.AccountCapsule;
-import org.tron.core.store.AccountStore;
-import org.tron.core.store.DynamicPropertiesStore;
-
-@Slf4j(topic = "DB")
-public class StorageMarket {
- private static final String LOG_MSG = "NewTotalPool: {}, newTotalReserved: {}.";
- private static final long MS_PER_YEAR = 365 * 24 * 3600 * 1000L;
- private AccountStore accountStore;
- private DynamicPropertiesStore dynamicPropertiesStore;
- private long supply = 1_000_000_000_000_000L;
-
-
- public StorageMarket(AccountStore accountStore, DynamicPropertiesStore dynamicPropertiesStore) {
- this.accountStore = accountStore;
- this.dynamicPropertiesStore = dynamicPropertiesStore;
- }
-
- private long exchangeToSupply(boolean isTRX, long quant) {
- logger.info("IsTRX: {}.", isTRX);
- long balance = isTRX ? dynamicPropertiesStore.getTotalStoragePool() :
- dynamicPropertiesStore.getTotalStorageReserved();
- logger.info("Balance: {}.", balance);
- long newBalance = balance + quant;
- logger.info("Balance + quant: {}.", balance + quant);
-
-// if (isTRX) {
-// dbManager.getDynamicPropertiesStore().saveTotalStoragePool(newBalance);
-// } else {
-// dbManager.getDynamicPropertiesStore().saveTotalStorageReserved(newBalance);
-// }
-
- double issuedSupply = -supply * (1.0 - Math.pow(1.0 + (double) quant / newBalance, 0.0005));
- logger.info("IssuedSupply: {}.", issuedSupply);
- long out = (long) issuedSupply;
- supply += out;
-
- return out;
- }
-
- private long exchangeToSupply2(boolean isTRX, long quant) {
- logger.info("IsTRX: {}.", isTRX);
- long balance = isTRX ? dynamicPropertiesStore.getTotalStoragePool() :
- dynamicPropertiesStore.getTotalStorageReserved();
- logger.info("Balance: {}.", balance);
- long newBalance = balance - quant;
- logger.info("Balance - quant: {}.", balance - quant);
-
-// if (isTRX) {
-// dbManager.getDynamicPropertiesStore().saveTotalStoragePool(newBalance);
-// } else {
-// dbManager.getDynamicPropertiesStore().saveTotalStorageReserved(newBalance);
-// }
-
- double issuedSupply = -supply * (1.0 - Math.pow(1.0 + (double) quant / newBalance, 0.0005));
- logger.info("IssuedSupply: {}.", issuedSupply);
- long out = (long) issuedSupply;
- supply += out;
-
- return out;
- }
-
- private long exchange_from_supply(boolean isTRX, long supplyQuant) {
- long balance = isTRX ? dynamicPropertiesStore.getTotalStoragePool() :
- dynamicPropertiesStore.getTotalStorageReserved();
- supply -= supplyQuant;
-
- double exchangeBalance =
- balance * (Math.pow(1.0 + (double) supplyQuant / supply, 2000.0) - 1.0);
- logger.info("ExchangeBalance: {}.", exchangeBalance);
- long out = (long) exchangeBalance;
-
- if (isTRX) {
- out = Math.round(exchangeBalance / 100000) * 100000;
- logger.info("Out: {}.", out);
- }
-
- return out;
- }
-
- public long exchange(long from, boolean isTRX) {
- long relay = exchangeToSupply(isTRX, from);
- return exchange_from_supply(!isTRX, relay);
- }
-
- public long calculateTax(long duration, long limit) {
- // todo: Support for change by the committee
- double ratePerYear = dynamicPropertiesStore.getStorageExchangeTaxRate() / 100.0;
- double millisecondPerYear = MS_PER_YEAR;
- double feeRate = duration / millisecondPerYear * ratePerYear;
- long storageTax = (long) (limit * feeRate);
- logger.info("StorageTax: {}.", storageTax);
- return storageTax;
- }
-
-
- public long tryPayTax(long duration, long limit) {
- long storageTax = calculateTax(duration, limit);
- long tax = exchange(storageTax, false);
- logger.info("Tax: {}.", tax);
-
- long newTotalTax = dynamicPropertiesStore.getTotalStorageTax() + tax;
- long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() - tax;
- long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved()
- + storageTax;
- logger.info("Reserved: {}.", dynamicPropertiesStore.getTotalStorageReserved());
- boolean eq = dynamicPropertiesStore.getTotalStorageReserved()
- == 128L * 1024 * 1024 * 1024;
- logger.info("Reserved == 128GB: {}.", eq);
- logger.info("NewTotalTax: {}, newTotalPool: {}, newTotalReserved: {}.",
- newTotalTax, newTotalPool, newTotalReserved);
-
- return storageTax;
- }
-
- public long payTax(long duration, long limit) {
- long storageTax = calculateTax(duration, limit);
- long tax = exchange(storageTax, false);
- logger.info("Tax: {}.", tax);
-
- long newTotalTax = dynamicPropertiesStore.getTotalStorageTax() + tax;
- long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() - tax;
- long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved()
- + storageTax;
- logger.info("Reserved: {}.", dynamicPropertiesStore.getTotalStorageReserved());
- boolean eq = dynamicPropertiesStore.getTotalStorageReserved()
- == 128L * 1024 * 1024 * 1024;
- logger.info("Reserved == 128GB: {}.", eq);
- logger.info("NewTotalTax: {}, newTotalPool: {}, newTotalReserved: {}.",
- newTotalTax, newTotalPool, newTotalReserved);
- dynamicPropertiesStore.saveTotalStorageTax(newTotalTax);
- dynamicPropertiesStore.saveTotalStoragePool(newTotalPool);
- dynamicPropertiesStore.saveTotalStorageReserved(newTotalReserved);
-
- return storageTax;
- }
-
- public long tryBuyStorageBytes(long storageBought) {
- long relay = exchangeToSupply2(false, storageBought);
- return exchange_from_supply(true, relay);
- }
-
- public long tryBuyStorage(long quant) {
- return exchange(quant, true);
- }
-
- public long trySellStorage(long bytes) {
- return exchange(bytes, false);
- }
-
- public AccountCapsule buyStorageBytes(AccountCapsule accountCapsule, long storageBought) {
- long now = dynamicPropertiesStore.getLatestBlockHeaderTimestamp();
- long currentStorageLimit = accountCapsule.getStorageLimit();
-
- long relay = exchangeToSupply2(false, storageBought);
- long quant = exchange_from_supply(true, relay);
-
- long newBalance = accountCapsule.getBalance() - quant;
- logger.info("New balance: {}.", newBalance);
-
- long newStorageLimit = currentStorageLimit + storageBought;
- logger.info("StorageBought: {}, newStorageLimit: {}.", storageBought, newStorageLimit);
-
- accountCapsule.setLatestExchangeStorageTime(now);
- accountCapsule.setStorageLimit(newStorageLimit);
- accountCapsule.setBalance(newBalance);
- accountStore.put(accountCapsule.createDbKey(), accountCapsule);
-
- long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() + quant;
- long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved()
- - storageBought;
- logger.info(LOG_MSG, newTotalPool, newTotalReserved);
- dynamicPropertiesStore.saveTotalStoragePool(newTotalPool);
- dynamicPropertiesStore.saveTotalStorageReserved(newTotalReserved);
- return accountCapsule;
- }
-
-
- public void buyStorage(AccountCapsule accountCapsule, long quant) {
- long now = dynamicPropertiesStore.getLatestBlockHeaderTimestamp();
- long currentStorageLimit = accountCapsule.getStorageLimit();
-
- long newBalance = accountCapsule.getBalance() - quant;
- logger.info("New balance: {}.", newBalance);
-
- long storageBought = exchange(quant, true);
- long newStorageLimit = currentStorageLimit + storageBought;
- logger.info("StorageBought: {}, newStorageLimit: {}.", storageBought, newStorageLimit);
-
- accountCapsule.setLatestExchangeStorageTime(now);
- accountCapsule.setStorageLimit(newStorageLimit);
- accountCapsule.setBalance(newBalance);
- accountStore.put(accountCapsule.createDbKey(), accountCapsule);
-
- long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() + quant;
- long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved()
- - storageBought;
- logger.info(LOG_MSG, newTotalPool, newTotalReserved);
- dynamicPropertiesStore.saveTotalStoragePool(newTotalPool);
- dynamicPropertiesStore.saveTotalStorageReserved(newTotalReserved);
-
- }
-
- public void sellStorage(AccountCapsule accountCapsule, long bytes) {
- long now = dynamicPropertiesStore.getLatestBlockHeaderTimestamp();
- long currentStorageLimit = accountCapsule.getStorageLimit();
-
- long quant = exchange(bytes, false);
- long newBalance = accountCapsule.getBalance() + quant;
-
- long newStorageLimit = currentStorageLimit - bytes;
- logger.info("Quant: {}, newStorageLimit: {}.", quant, newStorageLimit);
-
- accountCapsule.setLatestExchangeStorageTime(now);
- accountCapsule.setStorageLimit(newStorageLimit);
- accountCapsule.setBalance(newBalance);
- accountStore.put(accountCapsule.createDbKey(), accountCapsule);
-
- long newTotalPool = dynamicPropertiesStore.getTotalStoragePool() - quant;
- long newTotalReserved = dynamicPropertiesStore.getTotalStorageReserved()
- + bytes;
- logger.info(LOG_MSG, newTotalPool, newTotalReserved);
- dynamicPropertiesStore.saveTotalStoragePool(newTotalPool);
- dynamicPropertiesStore.saveTotalStorageReserved(newTotalReserved);
-
- }
-
- public long getAccountLeftStorageInByteFromBought(AccountCapsule accountCapsule) {
- return accountCapsule.getStorageLimit() - accountCapsule.getStorageUsage();
- }
-}
diff --git a/chainbase/src/main/java/org/tron/core/db/TransactionCache.java b/chainbase/src/main/java/org/tron/core/db/TransactionCache.java
new file mode 100644
index 00000000000..58ed9be9145
--- /dev/null
+++ b/chainbase/src/main/java/org/tron/core/db/TransactionCache.java
@@ -0,0 +1,25 @@
+package org.tron.core.db;
+
+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.core.capsule.BytesCapsule;
+import org.tron.core.db2.common.TxCacheDB;
+import org.tron.core.store.DynamicPropertiesStore;
+
+@Slf4j
+@Component
+public class TransactionCache extends TronStoreWithRevoking {
+
+ @Autowired
+ public TransactionCache(@Value("trans-cache") String dbName,
+ @Autowired RecentTransactionStore recentTransactionStore,
+ @Autowired DynamicPropertiesStore dynamicPropertiesStore) {
+ super(new TxCacheDB(dbName, recentTransactionStore, dynamicPropertiesStore));
+ }
+
+ public void initCache() {
+ ((TxCacheDB) getDb()).init();
+ }
+}
diff --git a/chainbase/src/main/java/org/tron/core/db/TransactionTrace.java b/chainbase/src/main/java/org/tron/core/db/TransactionTrace.java
index 4df135ce310..c3776921244 100644
--- a/chainbase/src/main/java/org/tron/core/db/TransactionTrace.java
+++ b/chainbase/src/main/java/org/tron/core/db/TransactionTrace.java
@@ -1,13 +1,15 @@
package org.tron.core.db;
+import static org.tron.common.math.Maths.max;
+import static org.tron.common.math.Maths.min;
import static org.tron.common.runtime.InternalTransaction.TrxType.TRX_CONTRACT_CALL_TYPE;
import static org.tron.common.runtime.InternalTransaction.TrxType.TRX_CONTRACT_CREATION_TYPE;
+import static org.tron.protos.contract.Common.ResourceCode.ENERGY;
import java.util.Objects;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import org.bouncycastle.util.encoders.Hex;
import org.springframework.util.StringUtils;
import org.tron.common.runtime.InternalTransaction.TrxType;
import org.tron.common.runtime.ProgramResult;
@@ -158,6 +160,13 @@ public void setBill(long energyUsage) {
receipt.setEnergyUsageTotal(energyUsage);
}
+ public void setPenalty(long energyPenalty) {
+ if (energyPenalty < 0) {
+ energyPenalty = 0L;
+ }
+ receipt.setEnergyPenaltyTotal(energyPenalty);
+ }
+
//set net bill
public void setNetBill(long netUsage, long netFee) {
receipt.setNetUsage(netUsage);
@@ -179,6 +188,7 @@ public void exec()
/* VM execute */
runtime.execute(transactionContext);
setBill(transactionContext.getProgramResult().getEnergyUsed());
+ setPenalty(transactionContext.getProgramResult().getEnergyPenaltyTotal());
// if (TrxType.TRX_PRECOMPILED_TYPE != trxType) {
// if (contractResult.OUT_OF_TIME
@@ -234,9 +244,11 @@ public void pay() throws BalanceInsufficientException {
callerAccount = callContract.getOwnerAddress().toByteArray();
originAccount = contractCapsule.getOriginAddress();
- percent = Math
- .max(Constant.ONE_HUNDRED - contractCapsule.getConsumeUserResourcePercent(), 0);
- percent = Math.min(percent, Constant.ONE_HUNDRED);
+ boolean disableJavaLangMath = dynamicPropertiesStore.disableJavaLangMath();
+ percent = max(Constant.ONE_HUNDRED - contractCapsule.getConsumeUserResourcePercent(
+ disableJavaLangMath), 0, disableJavaLangMath);
+ percent = min(percent, Constant.ONE_HUNDRED,
+ disableJavaLangMath);
originEnergyLimit = contractCapsule.getOriginEnergyLimit();
break;
default:
@@ -246,6 +258,26 @@ public void pay() throws BalanceInsufficientException {
// originAccount Percent = 30%
AccountCapsule origin = accountStore.get(originAccount);
AccountCapsule caller = accountStore.get(callerAccount);
+ if (dynamicPropertiesStore.supportUnfreezeDelay()
+ && getRuntimeResult().getException() == null && !getRuntimeResult().isRevert()) {
+
+ // just fo caller is not origin, we set the related field for origin account
+ if (origin != null && !caller.getAddress().equals(origin.getAddress())) {
+ resetAccountUsage(origin,
+ receipt.getOriginEnergyUsage(),
+ receipt.getOriginEnergyWindowSize(),
+ receipt.getOriginEnergyMergedUsage(),
+ receipt.getOriginEnergyMergedWindowSize(),
+ receipt.getOriginEnergyWindowSizeV2());
+ }
+
+ resetAccountUsage(caller,
+ receipt.getCallerEnergyUsage(),
+ receipt.getCallerEnergyWindowSize(),
+ receipt.getCallerEnergyMergedUsage(),
+ receipt.getCallerEnergyMergedWindowSize(),
+ receipt.getCallerEnergyWindowSizeV2());
+ }
receipt.payEnergyBill(
dynamicPropertiesStore, accountStore, forkController,
origin,
@@ -255,6 +287,43 @@ public void pay() throws BalanceInsufficientException {
EnergyProcessor.getHeadSlot(dynamicPropertiesStore));
}
+ private void resetAccountUsage(AccountCapsule accountCap,
+ long usage, long size, long mergedUsage, long mergedSize, long size2) {
+ if (dynamicPropertiesStore.supportAllowCancelAllUnfreezeV2()) {
+ resetAccountUsageV2(accountCap, usage, size, mergedUsage, mergedSize, size2);
+ return;
+ }
+ long currentSize = accountCap.getWindowSize(ENERGY);
+ long currentUsage = accountCap.getEnergyUsage();
+ // Drop the pre consumed frozen energy
+ long newArea = currentUsage * currentSize
+ - (mergedUsage * mergedSize - usage * size);
+ // If area merging happened during suicide, use the current window size
+ long newSize = mergedSize == currentSize ? size : currentSize;
+ // Calc new usage by fixed x-axes
+ long newUsage = max(0, newArea / newSize, dynamicPropertiesStore.disableJavaLangMath());
+ // Reset account usage and window size
+ accountCap.setEnergyUsage(newUsage);
+ accountCap.setNewWindowSize(ENERGY, newUsage == 0 ? 0L : newSize);
+ }
+
+ private void resetAccountUsageV2(AccountCapsule accountCap,
+ long usage, long size, long mergedUsage, long mergedSize, long size2) {
+ long currentSize = accountCap.getWindowSize(ENERGY);
+ long currentSize2 = accountCap.getWindowSizeV2(ENERGY);
+ long currentUsage = accountCap.getEnergyUsage();
+ // Drop the pre consumed frozen energy
+ long newArea = currentUsage * currentSize - (mergedUsage * mergedSize - usage * size);
+ // If area merging happened during suicide, use the current window size
+ long newSize = mergedSize == currentSize ? size : currentSize;
+ long newSize2 = mergedSize == currentSize ? size2 : currentSize2;
+ // Calc new usage by fixed x-axes
+ long newUsage = max(0, newArea / newSize, dynamicPropertiesStore.disableJavaLangMath());
+ // Reset account usage and window size
+ accountCap.setEnergyUsage(newUsage);
+ accountCap.setNewWindowSizeV2(ENERGY, newUsage == 0 ? 0L : newSize2);
+ }
+
public boolean checkNeedRetry() {
if (!needVM()) {
return false;
@@ -311,7 +380,7 @@ public void deleteContract(byte[] address) {
public static byte[] convertToTronAddress(byte[] address) {
if (address.length == 20) {
byte[] newAddress = new byte[21];
- byte[] temp = new byte[]{DecodeUtil.addressPreFixByte};
+ byte[] temp = new byte[] {DecodeUtil.addressPreFixByte};
System.arraycopy(temp, 0, newAddress, 0, temp.length);
System.arraycopy(address, 0, newAddress, temp.length, address.length);
address = newAddress;
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 b96d7543302..0a78570b8ed 100644
--- a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java
+++ b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java
@@ -3,13 +3,12 @@
import com.google.protobuf.InvalidProtocolBufferException;
import java.nio.file.Paths;
import java.util.Iterator;
+import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-import org.iq80.leveldb.WriteOptions;
-import org.rocksdb.DirectComparator;
import org.springframework.beans.factory.annotation.Autowired;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.storage.WriteOptionsWrapper;
@@ -29,7 +28,7 @@ public abstract class TronDatabase implements ITronChainBase {
protected DbSourceInter dbSource;
@Getter
private String dbName;
- private WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance()
+ private final WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance()
.sync(CommonParameter.getInstance().getStorage().isDbSync());
@Autowired
@@ -39,23 +38,14 @@ protected TronDatabase(String dbName) {
this.dbName = dbName;
if ("LEVELDB".equals(CommonParameter.getInstance().getStorage()
- .getDbEngine().toUpperCase())) {
- dbSource =
- new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName),
- dbName,
- getOptionsByDbNameForLevelDB(dbName),
- new WriteOptions().sync(CommonParameter.getInstance()
- .getStorage().isDbSync()));
+ .getDbEngine().toUpperCase(Locale.ROOT))) {
+ dbSource = new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName), dbName);
} else if ("ROCKSDB".equals(CommonParameter.getInstance()
- .getStorage().getDbEngine().toUpperCase())) {
+ .getStorage().getDbEngine().toUpperCase(Locale.ROOT))) {
String parentName = Paths.get(StorageUtils.getOutputDirectoryByDbName(dbName),
CommonParameter.getInstance().getStorage().getDbDirectory()).toString();
- dbSource =
- new RocksDbDataSourceImpl(parentName, dbName, CommonParameter.getInstance()
- .getRocksDBCustomSettings(), getDirectComparator());
+ dbSource = new RocksDbDataSourceImpl(parentName, dbName);
}
-
- dbSource.initDB();
}
@PostConstruct
@@ -66,14 +56,6 @@ protected void init() {
protected TronDatabase() {
}
- protected org.iq80.leveldb.Options getOptionsByDbNameForLevelDB(String dbName) {
- return StorageUtils.getOptionsByDbName(dbName);
- }
-
- protected DirectComparator getDirectComparator() {
- return null;
- }
-
public DbSourceInter getDbSource() {
return dbSource;
}
@@ -94,7 +76,27 @@ public void reset() {
*/
@Override
public void close() {
- dbSource.closeDB();
+ logger.info("******** Begin to close {}. ********", getName());
+ doClose();
+ logger.info("******** End to close {}. ********", getName());
+ }
+
+ /**
+ * Releases writeOptions and dbSource (best-effort, exceptions logged at WARN).
+ * Subclasses with extra resources should override {@link #close()} and call
+ * {@code doClose()} directly — not {@code super.close()} — to avoid duplicated logs.
+ */
+ protected void doClose() {
+ try {
+ writeOptions.close();
+ } catch (Exception e) {
+ logger.warn("Failed to close writeOptions in {}.", getName(), e);
+ }
+ try {
+ dbSource.closeDB();
+ } catch (Exception e) {
+ logger.warn("Failed to close dbSource in {}.", getName(), e);
+ }
}
public abstract void put(byte[] key, T item);
diff --git a/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java b/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java
index be005a1da76..72e7a1cd82f 100644
--- a/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java
+++ b/chainbase/src/main/java/org/tron/core/db/TronStoreWithRevoking.java
@@ -9,15 +9,13 @@
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Paths;
import java.util.Iterator;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-import org.iq80.leveldb.Options;
-import org.iq80.leveldb.WriteOptions;
-import org.rocksdb.DirectComparator;
import org.springframework.beans.factory.annotation.Autowired;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.storage.leveldb.LevelDbDataSourceImpl;
@@ -33,7 +31,6 @@
import org.tron.core.db2.common.WrappedByteArray;
import org.tron.core.db2.core.Chainbase;
import org.tron.core.db2.core.ITronChainBase;
-import org.tron.core.db2.core.RevokingDBWithCachingOldValue;
import org.tron.core.db2.core.SnapshotRoot;
import org.tron.core.exception.BadItemException;
import org.tron.core.exception.ItemNotFoundException;
@@ -42,7 +39,7 @@
@Slf4j(topic = "DB")
public abstract class TronStoreWithRevoking implements ITronChainBase {
- @Getter // only for unit test
+ @Getter
protected IRevokingDB revokingDB;
private TypeToken token = new TypeToken(getClass()) {
};
@@ -53,69 +50,28 @@ public abstract class TronStoreWithRevoking implements I
@Autowired
private DbStatService dbStatService;
- private DB db;
+ @Getter
+ private final DB db;
protected TronStoreWithRevoking(String dbName) {
- int dbVersion = CommonParameter.getInstance().getStorage().getDbVersion();
String dbEngine = CommonParameter.getInstance().getStorage().getDbEngine();
- if (dbVersion == 1) {
- this.revokingDB = new RevokingDBWithCachingOldValue(dbName,
- getOptionsByDbNameForLevelDB(dbName));
- } else if (dbVersion == 2) {
- if ("LEVELDB".equals(dbEngine.toUpperCase())) {
- this.db = new LevelDB(
- new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName),
- dbName,
- getOptionsByDbNameForLevelDB(dbName),
- new WriteOptions().sync(CommonParameter.getInstance()
- .getStorage().isDbSync())));
- } else if ("ROCKSDB".equals(dbEngine.toUpperCase())) {
- String parentPath = Paths
- .get(StorageUtils.getOutputDirectoryByDbName(dbName), CommonParameter
- .getInstance().getStorage().getDbDirectory()).toString();
- this.db = new RocksDB(
- new RocksDbDataSourceImpl(parentPath,
- dbName, CommonParameter.getInstance()
- .getRocksDBCustomSettings(), getDirectComparator()));
- } else {
- throw new RuntimeException(String.format("db engine %s is error", dbEngine));
- }
- this.revokingDB = new Chainbase(new SnapshotRoot(this.db));
-
+ if ("LEVELDB".equals(dbEngine.toUpperCase(Locale.ROOT))) {
+ this.db = new LevelDB(
+ new LevelDbDataSourceImpl(StorageUtils.getOutputDirectoryByDbName(dbName), dbName));
+ } else if ("ROCKSDB".equals(dbEngine.toUpperCase(Locale.ROOT))) {
+ String parentPath = Paths
+ .get(StorageUtils.getOutputDirectoryByDbName(dbName), CommonParameter
+ .getInstance().getStorage().getDbDirectory()).toString();
+ this.db = new RocksDB(new RocksDbDataSourceImpl(parentPath, dbName));
} else {
- throw new RuntimeException(String.format("db version %d is error", dbVersion));
+ throw new RuntimeException(String.format("db engine %s is error", dbEngine));
}
- }
-
- protected org.iq80.leveldb.Options getOptionsByDbNameForLevelDB(String dbName) {
- return StorageUtils.getOptionsByDbName(dbName);
- }
-
- protected DirectComparator getDirectComparator() {
- return null;
+ this.revokingDB = new Chainbase(new SnapshotRoot(this.db));
}
protected TronStoreWithRevoking(DB db) {
- int dbVersion = CommonParameter.getInstance().getStorage().getDbVersion();
- if (dbVersion == 2) {
- this.db = db;
- this.revokingDB = new Chainbase(new SnapshotRoot(db));
- } else {
- throw new RuntimeException(String.format("db version is only 2, actual: %d", dbVersion));
- }
- }
-
- // only for test
- protected TronStoreWithRevoking(String dbName, RevokingDatabase revokingDatabase) {
- this.revokingDB = new RevokingDBWithCachingOldValue(dbName,
- (AbstractRevokingStore) revokingDatabase);
- }
-
- // only for test
- protected TronStoreWithRevoking(String dbName, Options options,
- RevokingDatabase revokingDatabase) {
- this.revokingDB = new RevokingDBWithCachingOldValue(dbName, options,
- (AbstractRevokingStore) revokingDatabase);
+ this.db = db;
+ this.revokingDB = new Chainbase(new SnapshotRoot(db));
}
@Override
@@ -210,7 +166,14 @@ public String getName() {
@Override
public void close() {
- revokingDB.close();
+ logger.info("******** Begin to close {}. ********", getName());
+ try {
+ revokingDB.close();
+ } catch (Exception e) {
+ logger.warn("Failed to close {}.", getName(), e);
+ } finally {
+ logger.info("******** End to close {}. ********", getName());
+ }
}
@Override
diff --git a/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java b/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java
index 0823b7a7cf4..21c0a0dff2a 100755
--- a/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java
+++ b/chainbase/src/main/java/org/tron/core/db/common/DbSourceInter.java
@@ -15,37 +15,82 @@
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see .
*/
-package org.tron.core.db.common;
-import org.tron.core.db2.common.WrappedByteArray;
+package org.tron.core.db.common;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import java.io.File;
+import java.nio.file.Paths;
import java.util.Map;
import java.util.Set;
-
+import org.tron.common.utils.FileUtil;
+import org.tron.common.utils.PropUtil;
+import org.tron.core.db2.common.WrappedByteArray;
+import org.tron.core.exception.TronError;
public interface DbSourceInter extends BatchSourceInter,
Iterable> {
+ String ENGINE_KEY = "ENGINE";
+ String ENGINE_FILE = "engine.properties";
+ String ROCKSDB = "ROCKSDB";
+ String LEVELDB = "LEVELDB";
+
String getDBName();
void setDBName(String name);
- void initDB();
-
boolean isAlive();
void closeDB();
void resetDb();
+ @VisibleForTesting
+ @Deprecated
Set allKeys() throws RuntimeException;
+ @VisibleForTesting
+ @Deprecated
Set allValues() throws RuntimeException;
+ @VisibleForTesting
+ @Deprecated
long getTotal() throws RuntimeException;
void stat();
Map prefixQuery(byte[] key);
+ static void checkOrInitEngine(String expectedEngine, String dir, TronError.ErrCode errCode) {
+ String engineFile = Paths.get(dir, ENGINE_FILE).toString();
+ File currentFile = new File(dir, "CURRENT");
+ if (ROCKSDB.equals(expectedEngine) && currentFile.exists()
+ && !Paths.get(engineFile).toFile().exists()) {
+ // if the CURRENT file exists, but the engine.properties file does not exist, it is LevelDB
+ // 000003.log CURRENT LOCK MANIFEST-000002
+ throw new TronError(
+ String.format("Cannot open %s database with %s engine.", LEVELDB, ROCKSDB), errCode);
+ }
+ if (FileUtil.createDirIfNotExists(dir)) {
+ if (!FileUtil.createFileIfNotExists(engineFile)) {
+ throw new TronError(String.format("Cannot create file: %s.", engineFile), errCode);
+ }
+ } else {
+ throw new TronError(String.format("Cannot create dir: %s.", dir), errCode);
+ }
+ String actualEngine = PropUtil.readProperty(engineFile, ENGINE_KEY);
+ // engine init
+ if (Strings.isNullOrEmpty(actualEngine)
+ && !PropUtil.writeProperty(engineFile, ENGINE_KEY, expectedEngine)) {
+ throw new TronError(String.format("Cannot write file: %s.", engineFile), errCode);
+ }
+ actualEngine = PropUtil.readProperty(engineFile, ENGINE_KEY);
+ if (!expectedEngine.equals(actualEngine)) {
+ throw new TronError(String.format(
+ "Cannot open %s database with %s engine.",
+ actualEngine, expectedEngine), errCode);
+ }
+ }
}
diff --git a/chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java b/chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java
index f706623693f..afbacac35db 100755
--- a/chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java
+++ b/chainbase/src/main/java/org/tron/core/db/common/iterator/DBIterator.java
@@ -1,9 +1,71 @@
package org.tron.core.db.common.iterator;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.UnmodifiableIterator;
+import com.google.common.primitives.Bytes;
import java.io.Closeable;
import java.util.Iterator;
import java.util.Map.Entry;
+import java.util.NoSuchElementException;
-public interface DBIterator extends Iterator>, Closeable {
+public interface DBIterator extends Iterator>, AutoCloseable, Closeable {
+ void seek(byte[] key);
+
+ void seekToFirst();
+
+ void seekToLast();
+
+ /**
+ * An iterator is either positioned at a key/value pair, or
+ * not valid. This method returns true iff the iterator is valid.
+ *
+ * REQUIRES: iterator not closed
+ *
+ * @throws IllegalStateException if the iterator is closed.
+ * @return an iterator is either positioned at a key/value pair
+ */
+ boolean valid();
+
+ /**
+ * The underlying storage for
+ * the returned slice is valid only until the next modification of
+ * the iterator.
+ *
+ * REQUIRES: valid() && !closed
+ *
+ * @throws IllegalStateException if the iterator is closed.
+ * @throws NoSuchElementException if the iterator is not valid.
+ *
+ * @return the key for the current entry
+ */
+ byte[] getKey();
+
+ /**
+ * The underlying storage for
+ * the returned slice is valid only until the next modification of
+ * the iterator.
+ *
+ * REQUIRES: valid() && !closed
+ *
+ * @throws IllegalStateException if the iterator is closed.
+ * @throws NoSuchElementException if the iterator is not valid.
+ *
+ * @return the value for the current entry
+ */
+ byte[] getValue();
+
+ /**
+ * @throws IllegalStateException if the iterator is closed.
+ */
+ void checkState();
+
+ /**
+ * @throws NoSuchElementException if the iterator is not valid.
+ */
+ default void checkValid() {
+ if (!valid()) {
+ throw new NoSuchElementException();
+ }
+ }
}
diff --git a/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java b/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java
index a8c4cff2066..cf9a5ff1e22 100644
--- a/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java
+++ b/chainbase/src/main/java/org/tron/core/db/common/iterator/RockStoreIterator.java
@@ -3,7 +3,9 @@
import java.io.IOException;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
import lombok.extern.slf4j.Slf4j;
+import org.rocksdb.ReadOptions;
import org.rocksdb.RocksIterator;
@@ -13,20 +15,25 @@ public final class RockStoreIterator implements DBIterator {
private final RocksIterator dbIterator;
private boolean first = true;
- private boolean valid = true;
+ private final AtomicBoolean close = new AtomicBoolean(false);
+ private final ReadOptions readOptions;
- public RockStoreIterator(RocksIterator dbIterator) {
+ public RockStoreIterator(RocksIterator dbIterator, ReadOptions readOptions) {
+ this.readOptions = readOptions;
this.dbIterator = dbIterator;
}
@Override
public void close() throws IOException {
- dbIterator.close();
+ if (close.compareAndSet(false, true)) {
+ readOptions.close();
+ dbIterator.close();
+ }
}
@Override
public boolean hasNext() {
- if (!valid) {
+ if (close.get()) {
return false;
}
boolean hasNext = false;
@@ -37,15 +44,14 @@ public boolean hasNext() {
first = false;
}
if (!(hasNext = dbIterator.isValid())) { // false is last item
- dbIterator.close();
- valid = false;
+ close();
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
try {
- dbIterator.close();
+ close();
} catch (Exception e1) {
- logger.error(e.getMessage(), e);
+ logger.error(e1.getMessage(), e1);
}
}
return hasNext;
@@ -53,7 +59,7 @@ public boolean hasNext() {
@Override
public Entry next() {
- if (!valid) {
+ if (close.get()) {
throw new NoSuchElementException();
}
byte[] key = dbIterator.key();
@@ -76,4 +82,55 @@ public byte[] setValue(byte[] value) {
}
};
}
-}
\ No newline at end of file
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void seek(byte[] key) {
+ checkState();
+ dbIterator.seek(key);
+ this.first = false;
+ }
+
+ @Override
+ public void seekToFirst() {
+ checkState();
+ dbIterator.seekToFirst();
+ this.first = false;
+ }
+
+ @Override
+ public void seekToLast() {
+ checkState();
+ dbIterator.seekToLast();
+ this.first = false;
+ }
+
+ @Override
+ public boolean valid() {
+ checkState();
+ return dbIterator.isValid();
+ }
+
+ @Override
+ public byte[] getKey() {
+ checkValid();
+ return dbIterator.key();
+ }
+
+ @Override
+ public byte[] getValue() {
+ checkValid();
+ return dbIterator.value();
+ }
+
+ @Override
+ public void checkState() {
+ if (close.get()) {
+ throw new IllegalStateException("iterator has been closed");
+ }
+ }
+}
diff --git a/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java b/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java
index 292bb421e54..c2803f99637 100755
--- a/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java
+++ b/chainbase/src/main/java/org/tron/core/db/common/iterator/StoreIterator.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
import lombok.extern.slf4j.Slf4j;
import org.iq80.leveldb.DBIterator;
@@ -13,7 +14,7 @@ public final class StoreIterator implements org.tron.core.db.common.iterator.DBI
private final DBIterator dbIterator;
private boolean first = true;
- private boolean valid = true;
+ private final AtomicBoolean close = new AtomicBoolean(false);
public StoreIterator(DBIterator dbIterator) {
this.dbIterator = dbIterator;
@@ -21,12 +22,14 @@ public StoreIterator(DBIterator dbIterator) {
@Override
public void close() throws IOException {
- dbIterator.close();
+ if (close.compareAndSet(false, true)) {
+ dbIterator.close();
+ }
}
@Override
public boolean hasNext() {
- if (!valid) {
+ if (close.get()) {
return false;
}
@@ -39,11 +42,15 @@ public boolean hasNext() {
}
if (!(hasNext = dbIterator.hasNext())) { // false is last item
- dbIterator.close();
- valid = false;
+ close();
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
+ try {
+ close();
+ } catch (Exception e1) {
+ logger.error(e1.getMessage(), e1);
+ }
}
return hasNext;
@@ -51,7 +58,7 @@ public boolean hasNext() {
@Override
public Entry next() {
- if (!valid) {
+ if (close.get()) {
throw new NoSuchElementException();
}
return dbIterator.next();
@@ -61,4 +68,51 @@ public Entry