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/.dockerignore b/.dockerignore
new file mode 100644
index 00000000000..d171944d877
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+./*
+!docker-entrypoint.sh
+
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 6d82a3ae6a9..00000000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-### 1. What did you do?
-
-
-
-### 2. What did you expect to see?
-
-
-
-### 3. What did you see instead?
-
-
diff --git a/.github/ISSUE_TEMPLATE/ask-a-question.md b/.github/ISSUE_TEMPLATE/ask-a-question.md
new file mode 100644
index 00000000000..1517046d7da
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/ask-a-question.md
@@ -0,0 +1,51 @@
+---
+name: Ask a question
+about: Something is unclear or needs clarification
+title: '[Question]'
+labels: 'type:docs'
+assignees: ''
+
+---
+
+
+
+## Question
+
+
+
+## Context
+
+
+
+**What are you trying to achieve?**
+
+
+**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
new file mode 100644
index 00000000000..30a3b245862
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/report-a-bug.md
@@ -0,0 +1,86 @@
+---
+name: Report a bug
+about: Create a report to help us improve
+title: '[Bug]'
+labels: 'type:bug'
+assignees: ''
+
+---
+
+
+
+## Bug Description
+
+
+
+## Environment
+
+**Network**
+
+
+**Software Versions**
+
+
+```
+OS:
+JVM:
+Git Commit:
+Version:
+Code:
+```
+
+**Configuration**
+
+
+## Expected Behavior
+
+
+
+## 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
new file mode 100644
index 00000000000..d8234f92a25
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/request-a-feature.md
@@ -0,0 +1,47 @@
+---
+name: Request a feature
+about: Suggest an idea for this project
+title: '[Feature]'
+labels: 'type:feature'
+assignees: ''
+
+---
+
+# Summary
+
+
+# Problem
+### Motivation
+
+
+### Current State
+
+
+### 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/.travis.yml b/.travis.yml
deleted file mode 100644
index 7a3b00d2ece..00000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-sudo: required
-dist: trusty
-language: java
-dist: trusty
-jdk: oraclejdk8
-addons:
- ssh_known_hosts:
- - 47.93.42.145:22008
- - 47.93.42.145:22008
- - 47.93.42.145:22008
- - 47.93.18.60:22008
- - 47.93.18.60:22008
- - 47.93.18.60:22008
- sonarcloud:
- organization: tron-zhaohong
- token:
- secure: "KXWEeQ1elAoQ0XfR54TQWfhrIdDP0+2CYPv2X9aWpU/YDl7hqZBAjcCOAUGvlbyM54jtG6YMaWwG48jlrOwwl5l/VjWSnUXF+7ixQy5ki0Ci9s7Y1pTQwV9SpL8TLIK2TYqabN8Mw+FJULASXLjYr9GerbbcUbCPTmcL6mQKw6ivxxpNPmz4eNoKAEuOzruO9fTXGAV3yr8Nn85c+mVxV8EUhkR17zpE9O8fvzOtSnYArWNOSCgDBn0EG45UNNPF2vn44s1c3h3gX1m3WK6PeCXPgy3hPqRn3wTG+xglnbqthGpo2wt1rJ83af+BwdYwvPEcUq84yLgXcE/aMkTKcVAfPWBP/6vblaoI90jxCeFji+MdMimKZAqIXt7oLqDZVmIq65de5YC5s7QTSbzJNY/tsAu3dqzSfbUJY6CRNFDcoenVpvgQkqb37TkDah4mJM8EUjbu2A9Q2HSFbyCVsQJtdasuu9cBOf6azK3U0XgFNBc0y2aziZrTnX30q7bi+5L/mbTnRdXrDqBOqyPeGtT77UZfcajHHmEWU/e6gYWiA/c+K25n13DD53Au6gpnnQ6ALeUl/1gDwz3fPBebJ5bVWrkIcLj7bbysjzfOvQmDS6G13RNz58Hm0/B7bVtZTr1E1q6Z1zEJwbuJYEJASNcezAfK5x/hIfZTGNqT3M8="
-cache:
- directories:
- - '$HOME/.sonar/cache'
-matrix:
- include:
- - name: build
- script:
- - sh tron.sh
- - "./gradlew build"
- - "./gradlew jacocoTestReport"
-
-
- - name: stest
- script:
- - sh tron.sh
- - bash deploy.sh
-
-
-after_success:
- - "bash <(curl -s https://codecov.io/bash)"
-
-skip_build:
- - README.md:
- - LICENSE
-
-
-
-
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5e2830a2473..ef67a81e3ee 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,83 +1,348 @@
# Contributing to java-tron
-java-tron is an open source project.
+java-tron is an open-source project which needs the support of open-source contributors.
-It is the work of contributors. We appreciate your help!
+Below are the instructions. We understand that there is much left to be desired, and if you see any room for improvement, please let us know. Thank you.
-Here are instructions to get you started. They are not perfect, so
-please let us know if anything feels wrong or incomplete.
+Here are some guidelines to get started quickly and easily:
+- [Reporting An Issue](#Reporting-An-Issue)
+- [Working on java-tron](#Working-on-java-tron)
+ - [Key Branches](#Key-Branches)
+ - [Submitting Code](#Submitting-Code)
+- [Code Review Guidelines](#Code-Review-Guidelines)
+ - [Terminology](#Terminology)
+ - [The Process](#The-Process)
+ - [Code Style](#Code-Style)
+ - [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)
-## Contribution guidelines
-First of all, java-tron follows GitFlow, the branches description in the java-tron project are listed as follow:
-``master`` branch:
-This branch contains the latest code released to the production environment. It can only be merged, and can not be modified directly in this branch.
+## Reporting An Issue
-``develop`` branch:
-This branch is the main development branch. It contains the complete code that is going to release. It can only be merged, and can not be modified directly in this branch.
+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.
-``feature`` branch:
-This branch is used to develop new features. It is created based on ``develop`` branch. Once the development is finished, it should be merged into ``develop`` branch, and then delete the branch.
+### 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.
-``release`` branch:
-This is the branch that is going to be released. It is created based on ``develop`` branch. In this branch, small fix and modification of final version of metadata is allowed. When the code is released, this branch should be merged into ``master`` branch(tag needed) and ``develop`` branch. The final test before release uses this branch.
+### Report a bug
-``hotfix`` branch:
-This branch is used to fix a bug when an online bug is found. It is created based on ``master`` branch. When bug fix is done, it should be merged into ``master`` branch(as a new release) and ``develop`` and then delete the branch. branch.
+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.
-### Pull requests
+### Request a feature
-If you'd like to contribute to java-tron, you should follow the steps below:
-- **Fork** a repository from **tronprotocol/java-tron** allows you to freely experiment with changes without affecting the original project
-- **Fix** some code and **Commit** your modified code.
-- **Send** a Pull Request(PR)for the maintainers to review and merge into the main code base.
- *notice*:When you create a new PR,please choose the **tronprotocol/java-tron** as the base repository and choose **your fork/java-tron** as the head repository.
- And you must choose **develop** as the base repository branch, which means we will merge the PR into our **develop** branch when reviewed and approved.
- Additionally, if you are writing a new feature, please ensure you add appropriate test cases under ``/src/test``.
+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.
-After the PR is checked by our Sonar check procedure and Travis CI continuous-integration check procedure automaticly,
-we maintainers will review the code changed and give some advices for modifying if necessary.Once approved,
-we will close the PR and merge into the protocol/java-tron's develop branch.
-We are always happy to receive pull requests, and do our best to
-review them as fast as possible. Not sure if that typo is worth a pull
-request? Do it! We would appreciate it.
+## Working on java-tron
+Thank you for considering to help out with the source code! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes!
-If your pull request is not accepted on the first try, don't be
-discouraged as it can be a possible oversight. Please explain your code as
-detailed as possible to make it easier for us to understand.
+If you’d like to contribute to java-tron, for small fixes, we recommend that you send a pull request (PR) for the maintainers to review and merge into the main code base, make sure the PR contains a detailed description. For more complex changes, you need to submit an issue to the TIP repository to detail your motive and implementation plan, etc. For how to submit a TIP issue, please refer to [TIP Specification](https://github.com/tronprotocol/tips#to-submit-a-tip).
-Please make sure your contributions adhere to our coding guidelines:
-- Code must be documented adhering to the [Google Style](https://google.github.io/styleguide/javaguide.html)
-- Code must pass Sonar detection.
-- Pull requests need to be based on and opened against the develop branch.
-- Commit messages should be started with verb, and the first letter should be a lowercase.The length of commit message
-must be limited in 50 words.
-### Create issues
+As the author of TIP issue, you are expected to encourage developers to discuss this issue, flesh out your issue by collecting their feedback, and eventually put your issue into practice.
-Any significant improvement should be documented as [a GitHub
-issue](https://github.com/tronprotocol/java-tron/issues) before anyone
-starts working on it.
-When filing an issue, make sure to answer these three questions:
+### Key Branches
+java-tron only has `master`, `develop`, `release-*`, `feature-*`, and `hotfix-*` branches, which are described below:
-- What did you do?
-- What did you expect to see?
-- What did you see instead?
+- ``develop`` branch
+ The `develop` branch only accept merge request from other forked branches or`release_*` branches. It is not allowed to directly push changes to the `develop` branch. A `release_*` branch has to be pulled from the develop branch when a new build is to be released.
-### Please check existing issues and docs first!
+- ``master`` branch
+ `release_*` branches and `hotfix/*` branches should only be merged into the `master` branch when a new build is released.
-Please take a moment to check that your bug report or improvement proposal
-doesn't already exist. If it does, please add a quick "+1" or "I have this problem too".
-This will help prioritize the most common problems and requests.
+- ``release`` branch
+ `release_*` is a branch pulled from the `develop` branch for release. It should be merged into `master` after a regression test and will be permanently kept in the repository. If a bug is identified in a `release_*` branch, its fixes should be directly merged into the branch. After passing the regression test, the `release_*` branch should be merged back into the `develop` branch. Essentially, a `release_*` branch serves as a snapshot for each release.
-## Community Developers Incentives Programme
+- ``feature`` branch
+ `feature/*` is an important feature branch pulled from the `develop` branch. After the `feature/*` branch is code-complete, it should be merged back to the `develop` branch. The `feature/*` branch is maintainable.
-Bonus point applies in TRON incentives programme. Developers can earn points by contributing to TRON.
+- ``hotfix`` branch
+ It is pulled from the `master` branch and should be merged back into the master branch and the `develop` branch. Only pull requests of the fork repository (pull requests for bug fixes) should be merged into the `hotfix/` branch. `hotfix/` branches are used only for fixing bugs found after release.
-You can find your points ranking at [Tronscan](https://tronscan.org/#/developersreward).
-The Top 5 scored developers (for every month, quarter and year) can win a cash reward.
+### Submitting Code
-For more details, please visit [Incentives Policy](https://tronprotocol.github.io/documentation-en/developers/incentives/).
+If you want to contribute code to java-tron, please follow the following steps.
+
+* Fork the Repository
+
+ Visit [tronprotocol/java-tron](https://github.com/tronprotocol/java-tron/) and click **Fork** to create a fork repository under your GitHub account.
+
+* 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
+
+ cd java-tron
+
+ git remote add upstream https://github.com/tronprotocol/java-tron.git
+ ```
+
+* 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
+ # `--no-ff` means to turn off the default fast merge mode
+ git merge upstream/develop --no-ff
+ git push origin develop
+ ```
+
+ Create a new branch for development. Please refer to [Branch Naming Conventions](#Branch-Naming-Conventions).
+ ```
+ git checkout -b feature/branch_name develop
+ ```
+
+* 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'
+ ```
+
+ Push the new branch to your fork repository
+ ```
+ git push origin feature/branch_name
+ ```
+
+* Submit a pull request
+
+ 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.
+ 
+
+
+
+## Code Review Guidelines
+The only way to get code into java-tron is to send a pull request. Those pull requests need to be reviewed by someone. The following guide explains our expectations around PRs for both authors and reviewers.
+
+### Terminology
+- The author of a pull request is the entity who wrote the diff and submitted it to GitHub.
+- The team consists of people with commit rights on the java-tron repository.
+- The reviewer is the person assigned to review the diff. The reviewer must be a team member.
+- The code owner is the person responsible for the subsystem being modified by the PR.
+
+### The Process
+The first decision to make for any PR is whether it’s worth including at all. This decision lies primarily with the code owner, but may be negotiated with team members.
+
+To make the decision we must understand what the PR is about. If there isn’t enough description content or the diff is too large, request an explanation. Anyone can do this part.
+
+We expect that reviewers check the style and functionality of the PR, providing comments to the author using the GitHub review system. Reviewers should follow up with the PR until it is in good shape, then approve the PR. Approved PRs can be merged by any code owner.
+
+When communicating with authors, be polite and respectful.
+
+### Code Style
+We would like all developers to follow a standard development flow and coding style. Therefore, we suggest the following:
+1. Review the code with coding style checkers.
+2. Review the code before submission.
+3. Run standardized tests.
+
+`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.
+
+Please kindly address the issue you find. We would appreciate your contribution.
+
+Please do not be discouraged if your pull request is not accepted, as it may be an oversight. Please explain your code as detailed as possible to make it easier to understand.
+
+Please make sure your submission meets the following code style:
+
+- The code must conform to [Google Code Style](https://google.github.io/styleguide/javaguide.html).
+- 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 title should be between 10 and 72 characters in length.
+
+
+
+### Commit Messages
+
+Commit messages should follow the rule below, we provide a template with corresponding instructions.
+
+Template:
+```
+():
+
+
+
+
+# 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.
-
+There are two ways to install the required dependencies:
+
+- **Option 1: Automated script (recommended for quick setup)**
+
+ Use the provided [`install_dependencies.sh`](install_dependencies.sh) script:
+
+ ```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.
+
+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.
+
+# Executables
+
+The java-tron project comes with several runnable artifacts and helper scripts found in the project root and build directories.
+
+| 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. |
+
+# 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
-## What's TRON?
+The TRON network is mainly divided into:
-TRON is a project dedicated to building the infrastructure for a truly decentralized Internet.
+- **Main Network (Mainnet)**
+ The primary public blockchain where real value (TRX, TRC-20 tokens, etc.) is transacted, secured by a massive decentralized network.
-* 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.
+- **[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.
-* 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.
+- **[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.
-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.
+- **Private Networks**
+ Customized TRON networks set up by private entities for testing, development, or specific use cases.
-# Quick Start
-This guide walks the user through the TRON Quickstart (v2.0.0) image setup.
-[TRON Quick Start](./quickstart.md)
+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)
-# Deploy
-* [Build](./build.md) Please build java-tron after cloning the project
-* [Run](./run.md) Run java-tron
+### 1. Join the TRON main network
+Launch a main-network full node with the built-in default configuration:
+```bash
+java -jar ./build/libs/FullNode.jar
+```
-# Deployment
-[Deployment Guide](https://tronprotocol.github.io/documentation-en/developers/deployment/)
- walks the user through how to deploy a FullNode and an SR node.
+> 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.
-# 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.
+Using the below command, you can monitor the blocks syncing progress:
+```bash
+tail -f ./logs/tron.log
+```
-[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.
+Use [TronScan](https://tronscan.org/#/), TRON's official block explorer, to view main network transactions, blocks, accounts, witness voting, and governance metrics, etc.
-[tronprotocol/allcoredev](https://gitter.im/tronprotocol/allcoredev) is the official Gitter channel for developers.
+### 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
+```
+
+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.
+
+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
-If you'd like to contribute to java-tron, please read the following instructions.
-- [Contribution](./CONTRIBUTING.md)
-- [Community Developers Incentives Programme](./CONTRIBUTING.md#community-developers-incentives-programme)
+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.
-* [White Paper](https://tron.network/resources?lng=&name=1) White paper of TRON network.
+
+- [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).
\ No newline at end of file
+
+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 ecc52b1b70c..d8e621ed69a 100644
--- a/Tron protobuf protocol document.md
+++ b/Tron protobuf protocol document.md
@@ -6,21 +6,21 @@ This is the description of Google Protobuf implementation of Tron's protocol.
## Contents
-#### [1.Account](#account)
+#### [1. Account](#account)
-#### [2.Witness](#witness)
+#### [2. Witness](#witness)
-#### [3.Block](#block)
+#### [3. Block](#block)
-#### [4.Transaction](#trans)
+#### [4. Transaction](#trans)
-#### [5.Contract](#contract)
+#### [5. Contract](#contract)
-#### [6.Network](#net)
+#### [6. Network](#net)
## Protocols
-### 1.Account
+### 1. Account
Account and account-related messages.
@@ -90,7 +90,7 @@ enum AccountType {
`allowance`: the allowance of this account.
- `latest_withdrew_time`: the latest operation time of this account.
+ `latest_withdraw_time`: the latest operation time of this account.
`code`: reserved
@@ -186,7 +186,7 @@ message Account {
- Message `AccountId`
- `name`: the name ofthis account.
+ `name`: the name of this account.
`address`: the address of this account.
@@ -202,7 +202,7 @@ message Account {
-### 2.Witness
+### 2. Witness
Witness and witness-related messages.
@@ -241,7 +241,7 @@ Witness and witness-related messages.
```
-### 3.Block
+### 3. Block
- message `Block`
@@ -314,7 +314,7 @@ message BlockHeader {
-### 4.Transaction
+### 4. Transaction
Transaction and transaction-related messages.
@@ -412,7 +412,7 @@ Transaction and transaction-related messages.
`net_fee`: consume yourself trx of net usage.
- `result`: he result of executing transaction.
+ `result`: the result of executing transaction.
```java
message ResourceReceipt {
@@ -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;
@@ -817,7 +825,7 @@ Transaction and transaction-related messages.
-### 5.Contract
+### 5. Contract
Contract and contract-related messages.
@@ -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`
@@ -1537,12 +1553,13 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
Function = 2;
Event = 3;
Fallback = 4;
+ Receive = 5;
}
```
- 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.
@@ -1617,6 +1634,7 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
Function = 2;
Event = 3;
Fallback = 4;
+ Receive = 5;
}
message Param {
bool indexed = 1;
@@ -1739,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.
@@ -1764,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 {
@@ -1851,7 +1869,7 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
}
```
-### 6.Network
+### 6. Network
- #### Inventory
@@ -2017,7 +2035,7 @@ message `SmartContract` has mutiple attributes and nested message `ABI`
TOO_MANY_PEERS = 0x04;
DUPLICATE_PEER = 0x05;
INCOMPATIBLE_PROTOCOL = 0x06;
- NULL_IDENTITY = 0x07;
+ RANDOM_ELIMINATION = 0x07;
PEER_QUITING = 0x08;
UNEXPECTED_IDENTITY = 0x09;
LOCAL_IDENTITY = 0x0A;
@@ -2106,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.
@@ -2136,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.
@@ -2154,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 594c6db8dad..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 "com.madgag.spongycastle:core:1.58.0.0"
- 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 = files('../framework/build/jacoco/jacocoTest.exec')
+ getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec"))
afterEvaluate {
- classDirectories = files(classDirectories.files.collect {
+ 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 a54a0b8a906..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,50 +31,87 @@ 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;
}
- public ForkController getForkUtils() {
- return forkController;
- }
-
public AbstractActuator setForkUtils(ForkController forkController) {
this.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 27be61bfff4..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,8 +51,12 @@ public boolean execute(Object object) throws ContractExeException {
accountPermissionUpdateContract.getActivesList());
accountStore.put(ownerAddress, account);
- Commons.adjustBalance(accountStore, ownerAddress, -fee);
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), fee);
+ adjustBalance(accountStore, ownerAddress, -fee);
+ if (chainBaseManager.getDynamicPropertiesStore().supportBlackHoleOptimization()) {
+ chainBaseManager.getDynamicPropertiesStore().burnTrx(fee);
+ } else {
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ }
result.setStatus(fee, code.SUCESS);
} catch (BalanceInsufficientException | InvalidProtocolBufferException e) {
@@ -107,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());
}
@@ -209,24 +212,18 @@ public boolean validate() throws ContractValidateException {
if (owner.getType() != PermissionType.Owner) {
throw new ContractValidateException("owner permission type is error");
}
- if (!checkPermission(owner)) {
- return false;
- }
+ checkPermission(owner);
if (accountCapsule.getIsWitness()) {
if (witness.getType() != PermissionType.Witness) {
throw new ContractValidateException("witness permission type is error");
}
- if (!checkPermission(witness)) {
- return false;
- }
+ checkPermission(witness);
}
for (Permission permission : actives) {
if (permission.getType() != PermissionType.Active) {
throw new ContractValidateException("active permission type is error");
}
- if (!checkPermission(permission)) {
- return false;
- }
+ checkPermission(permission);
}
return true;
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/ActuatorConstant.java b/actuator/src/main/java/org/tron/core/actuator/ActuatorConstant.java
index 7dcca1dc8af..ed5ca2f5a77 100644
--- a/actuator/src/main/java/org/tron/core/actuator/ActuatorConstant.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ActuatorConstant.java
@@ -9,5 +9,7 @@ public class ActuatorConstant {
public static final String TX_RESULT_NULL = "TransactionResultCapsule is null";
public static final String CONTRACT_NOT_EXIST = "No contract!";
public static final String STORE_NOT_EXIST = "No account store or dynamic store!";
+ public static final int ONE_HUNDRED = 100;
+ public static final int PRECISION_DECIMAL = 6;
}
diff --git a/framework/src/main/java/org/tron/core/actuator/ActuatorCreator.java b/actuator/src/main/java/org/tron/core/actuator/ActuatorCreator.java
similarity index 100%
rename from framework/src/main/java/org/tron/core/actuator/ActuatorCreator.java
rename to actuator/src/main/java/org/tron/core/actuator/ActuatorCreator.java
diff --git a/framework/src/main/java/org/tron/core/actuator/ActuatorFactory.java b/actuator/src/main/java/org/tron/core/actuator/ActuatorFactory.java
similarity index 82%
rename from framework/src/main/java/org/tron/core/actuator/ActuatorFactory.java
rename to actuator/src/main/java/org/tron/core/actuator/ActuatorFactory.java
index 35bfa90e548..34031150ad1 100644
--- a/framework/src/main/java/org/tron/core/actuator/ActuatorFactory.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ActuatorFactory.java
@@ -4,8 +4,8 @@
import com.google.common.collect.Lists;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
+import org.tron.core.ChainBaseManager;
import org.tron.core.capsule.TransactionCapsule;
-import org.tron.core.db.Manager;
import org.tron.protos.Protocol;
import org.tron.protos.Protocol.Transaction.Contract;
@@ -25,20 +25,20 @@ public static ActuatorFactory getInstance() {
* create actuator.
*/
public static List createActuator(TransactionCapsule transactionCapsule,
- Manager manager) {
+ ChainBaseManager chainBaseManager) {
List actuatorList = Lists.newArrayList();
if (null == transactionCapsule || null == transactionCapsule.getInstance()) {
logger.info("TransactionCapsule or Transaction is null");
return actuatorList;
}
- Preconditions.checkNotNull(manager, "manager is null");
+ Preconditions.checkNotNull(chainBaseManager, "manager is null");
Protocol.Transaction.raw rawData = transactionCapsule.getInstance().getRawData();
rawData.getContractList()
.forEach(contract -> {
try {
actuatorList
- .add(getActuatorByContract(contract, manager, transactionCapsule));
+ .add(getActuatorByContract(contract, chainBaseManager, transactionCapsule));
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
@@ -46,11 +46,11 @@ public static List createActuator(TransactionCapsule transactionCapsul
return actuatorList;
}
- private static Actuator getActuatorByContract(Contract contract, Manager manager,
+ private static Actuator getActuatorByContract(Contract contract, ChainBaseManager manager,
TransactionCapsule tx) throws IllegalAccessException, InstantiationException {
Class extends Actuator> clazz = TransactionFactory.getActuator(contract.getType());
AbstractActuator abstractActuator = (AbstractActuator) clazz.newInstance();
- abstractActuator.setChainBaseManager(manager.getChainBaseManager()).setContract(contract)
+ abstractActuator.setChainBaseManager(manager).setContract(contract)
.setForkUtils(manager.getForkController()).setTx(tx);
return abstractActuator;
}
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 e36d9b8024b..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,10 +86,12 @@ public boolean execute(Object result) throws ContractExeException {
.put(assetIssueCapsuleV2.createDbV2Key(), assetIssueCapsuleV2);
}
- Commons.adjustBalance(accountStore, ownerAddress, -fee);
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().getAddress().toByteArray(),
- fee);//send to blackhole
-
+ adjustBalance(accountStore, ownerAddress, -fee);
+ if (dynamicStore.supportBlackHoleOptimization()) {
+ dynamicStore.burnTrx(fee);
+ } else {
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);//send to blackhole
+ }
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
List frozenSupplyList = assetIssueContract.getFrozenSupplyList();
Iterator iterator = frozenSupplyList.iterator();
@@ -119,8 +123,7 @@ public boolean execute(Object result) throws ContractExeException {
ret.setAssetIssueID(Long.toString(tokenIdNum));
ret.setStatus(fee, code.SUCESS);
- }
- catch (InvalidProtocolBufferException | BalanceInsufficientException | ArithmeticException e){
+ } catch (InvalidProtocolBufferException | BalanceInsufficientException | ArithmeticException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(fee, code.FAILED);
throw new ContractExeException(e.getMessage());
@@ -164,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");
}
@@ -173,7 +176,7 @@ public boolean validate() throws ContractValidateException {
int precision = assetIssueContract.getPrecision();
if (precision != 0
&& dynamicStore.getAllowSameTokenName() != 0
- && (precision < 0 || precision > 6)) {
+ && (precision < 0 || precision > ActuatorConstant.PRECISION_DECIMAL)) {
throw new ContractValidateException("precision cannot exceed 6");
}
@@ -263,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/ClearABIContractActuator.java b/actuator/src/main/java/org/tron/core/actuator/ClearABIContractActuator.java
index 9309cf92551..7ce96d3318c 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ClearABIContractActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ClearABIContractActuator.java
@@ -1,24 +1,27 @@
package org.tron.core.actuator;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+
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.Commons;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
+import org.tron.core.capsule.AbiCapsule;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.ContractCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.AbiStore;
import org.tron.core.store.AccountStore;
import org.tron.core.store.ContractStore;
-import org.tron.core.vm.config.VMConfig;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.contract.SmartContractOuterClass.ClearABIContract;
+import org.tron.protos.contract.SmartContractOuterClass.SmartContract.ABI;
@Slf4j(topic = "actuator")
public class ClearABIContractActuator extends AbstractActuator {
@@ -35,15 +38,12 @@ public boolean execute(Object result) throws ContractExeException {
}
long fee = calcFee();
- ContractStore contractStore = chainBaseManager.getContractStore();
+ AbiStore abiStore = chainBaseManager.getAbiStore();
try {
ClearABIContract usContract = any.unpack(ClearABIContract.class);
byte[] contractAddress = usContract.getContractAddress().toByteArray();
- ContractCapsule deployedContract = contractStore.get(contractAddress);
-
- deployedContract.clearABI();
- contractStore.put(contractAddress, deployedContract);
+ abiStore.put(contractAddress, new AbiCapsule(ABI.getDefaultInstance()));
ret.setStatus(fee, code.SUCESS);
} catch (InvalidProtocolBufferException e) {
@@ -56,17 +56,16 @@ public boolean execute(Object result) throws ContractExeException {
@Override
public boolean validate() throws ContractValidateException {
- if (!VMConfig.allowTvmConstantinople()) {
- throw new ContractValidateException(
- "contract type error,unexpected type [ClearABIContract]");
- }
-
if (this.any == null) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}
if (chainBaseManager == null) {
throw new ContractValidateException("No account store or contract store!");
}
+ if (chainBaseManager.getDynamicPropertiesStore().getAllowTvmConstantinople() == 0) {
+ throw new ContractValidateException(
+ "contract type error,unexpected type [ClearABIContract]");
+ }
AccountStore accountStore = chainBaseManager.getAccountStore();
ContractStore contractStore = chainBaseManager.getContractStore();
if (!this.any.is(ClearABIContract.class)) {
@@ -90,7 +89,8 @@ public boolean validate() throws ContractValidateException {
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
if (accountCapsule == null) {
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] not exists");
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + NOT_EXIST_STR);
}
byte[] contractAddress = contract.getContractAddress().toByteArray();
@@ -106,7 +106,8 @@ public boolean validate() throws ContractValidateException {
if (!Arrays.equals(ownerAddress, deployedContractOwnerAddress)) {
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] is not the owner of the contract");
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + "] is not the owner of the contract");
}
return true;
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 25b7cb2902f..352f394d6cb 100755
--- a/actuator/src/main/java/org/tron/core/actuator/CreateAccountActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/CreateAccountActuator.java
@@ -1,10 +1,11 @@
package org.tron.core.actuator;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+
import com.google.protobuf.ByteString;
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;
@@ -46,11 +47,13 @@ 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
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), fee);
-
+ if (dynamicStore.supportBlackHoleOptimization()) {
+ dynamicStore.burnTrx(fee);
+ } else {
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ }
ret.setStatus(fee, code.SUCESS);
} catch (BalanceInsufficientException | InvalidProtocolBufferException e) {
logger.debug(e.getMessage(), e);
@@ -94,7 +97,8 @@ public boolean validate() throws ContractValidateException {
if (accountCapsule == null) {
String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] not exists");
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + NOT_EXIST_STR);
}
final long fee = calcFee();
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 3cdceaed922..7edea48e12f 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ExchangeCreateActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeCreateActuator.java
@@ -1,5 +1,7 @@
package org.tron.core.actuator;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+import static org.tron.core.capsule.utils.TransactionUtil.isNumber;
import static org.tron.core.config.Parameter.ChainSymbol.TRX_SYMBOL_BYTES;
import com.google.protobuf.ByteString;
@@ -7,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;
@@ -21,13 +22,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.ExchangeCreateContract;
@Slf4j(topic = "actuator")
-public class ExchangeCreateActuator extends AbstractActuator {
+public class ExchangeCreateActuator extends AbstractExchangeActuator {
public ExchangeCreateActuator() {
super(ContractType.ExchangeCreateContract, ExchangeCreateContract.class);
@@ -57,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
@@ -117,12 +117,15 @@ public boolean execute(Object object) throws ContractExeException {
accountStore.put(accountCapsule.createDbKey(), accountCapsule);
dynamicStore.saveLatestExchangeNum(id);
-
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), fee);
-
+ if (dynamicStore.supportBlackHoleOptimization()) {
+ dynamicStore.burnTrx(fee);
+ } else {
+ 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());
@@ -132,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);
}
@@ -160,7 +171,7 @@ public boolean validate() throws ContractValidateException {
}
if (!accountStore.has(ownerAddress)) {
- throw new ContractValidateException("account[" + readableOwnerAddress + "] not exists");
+ throw new ContractValidateException("account[" + readableOwnerAddress + NOT_EXIST_STR);
}
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
@@ -175,11 +186,10 @@ public boolean validate() throws ContractValidateException {
long secondTokenBalance = contract.getSecondTokenBalance();
if (dynamicStore.getAllowSameTokenName() == 1) {
- if (!Arrays.equals(firstTokenID, TRX_SYMBOL_BYTES) && !TransactionUtil.isNumber(firstTokenID)) {
+ if (!Arrays.equals(firstTokenID, TRX_SYMBOL_BYTES) && !isNumber(firstTokenID)) {
throw new ContractValidateException("first token id is not a valid number");
}
- if (!Arrays.equals(secondTokenID, TRX_SYMBOL_BYTES) && !TransactionUtil
- .isNumber(secondTokenID)) {
+ if (!Arrays.equals(secondTokenID, TRX_SYMBOL_BYTES) && !isNumber(secondTokenID)) {
throw new ContractValidateException("second token id is not a valid number");
}
}
@@ -198,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 {
@@ -208,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 9f85788011b..e9c27e33920 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeInjectActuator.java
@@ -1,5 +1,6 @@
package org.tron.core.actuator;
+import static org.tron.core.capsule.utils.TransactionUtil.isNumber;
import static org.tron.core.config.Parameter.ChainSymbol.TRX_SYMBOL_BYTES;
import com.google.protobuf.ByteString;
@@ -23,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);
@@ -55,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();
@@ -70,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);
@@ -104,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());
@@ -114,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);
}
@@ -155,11 +164,12 @@ 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() + "] not exists");
+ throw new ContractValidateException("Exchange[" + contract.getExchangeId() + ActuatorConstant
+ .NOT_EXIST_STR);
}
if (!accountCapsule.getAddress().equals(exchangeCapsule.getCreatorAddress())) {
@@ -177,9 +187,9 @@ public boolean validate() throws ContractValidateException {
byte[] anotherTokenID;
long anotherTokenQuant;
- if (dynamicStore.getAllowSameTokenName() == 1 &&
- !Arrays.equals(tokenID, TRX_SYMBOL_BYTES) &&
- !TransactionUtil.isNumber(tokenID)) {
+ if (dynamicStore.getAllowSameTokenName() == 1
+ && !Arrays.equals(tokenID, TRX_SYMBOL_BYTES)
+ && !isNumber(tokenID)) {
throw new ContractValidateException("token id is not a valid number");
}
@@ -201,19 +211,19 @@ public boolean validate() throws ContractValidateException {
BigInteger bigTokenQuant = new BigInteger(String.valueOf(tokenQuant));
long newTokenBalance;
long newAnotherTokenBalance;
-
+
if (Arrays.equals(tokenID, firstTokenID)) {
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) {
@@ -226,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 {
@@ -236,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 c240595f7cd..c5198224c5c 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ExchangeTransactionActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeTransactionActuator.java
@@ -1,5 +1,7 @@
package org.tron.core.actuator;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
+import static org.tron.core.capsule.utils.TransactionUtil.isNumber;
import static org.tron.core.config.Parameter.ChainSymbol.TRX_SYMBOL_BYTES;
import com.google.protobuf.ByteString;
@@ -22,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);
@@ -54,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();
@@ -64,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;
@@ -72,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);
@@ -95,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());
@@ -106,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);
}
@@ -136,7 +147,7 @@ public boolean validate() throws ContractValidateException {
}
if (!accountStore.has(ownerAddress)) {
- throw new ContractValidateException("account[" + readableOwnerAddress + "] not exists");
+ throw new ContractValidateException("account[" + readableOwnerAddress + NOT_EXIST_STR);
}
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
@@ -147,10 +158,11 @@ 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() + "] not exists");
+ throw new ContractValidateException("Exchange[" + contract.getExchangeId()
+ + ActuatorConstant.NOT_EXIST_STR);
}
byte[] firstTokenID = exchangeCapsule.getFirstTokenId();
@@ -162,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) &&
- !TransactionUtil.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)) {
@@ -187,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 {
@@ -202,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 c24bc890131..d43d5053f67 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ExchangeWithdrawActuator.java
@@ -1,11 +1,13 @@
package org.tron.core.actuator;
+import static org.tron.core.capsule.utils.TransactionUtil.isNumber;
import static org.tron.core.config.Parameter.ChainSymbol.TRX_SYMBOL_BYTES;
import com.google.protobuf.ByteString;
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;
@@ -24,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);
@@ -56,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();
@@ -75,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);
@@ -113,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());
@@ -124,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);
}
@@ -165,10 +171,11 @@ 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() + "] not exists");
+ throw new ContractValidateException("Exchange[" + contract.getExchangeId() + ActuatorConstant
+ .NOT_EXIST_STR);
}
if (!accountCapsule.getAddress().equals(exchangeCapsule.getCreatorAddress())) {
@@ -185,9 +192,9 @@ public boolean validate() throws ContractValidateException {
long anotherTokenQuant;
- if (dynamicStore.getAllowSameTokenName() == 1 &&
- !Arrays.equals(tokenID, TRX_SYMBOL_BYTES) &&
- !TransactionUtil.isNumber(tokenID)) {
+ if (dynamicStore.getAllowSameTokenName() == 1
+ && !Arrays.equals(tokenID, TRX_SYMBOL_BYTES)
+ && !isNumber(tokenID)) {
throw new ContractValidateException("token id is not a valid number");
}
@@ -207,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) {
@@ -219,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) {
@@ -240,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 4f98dfd5d76..421b4e3013a 100755
--- a/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/FreezeBalanceActuator.java
@@ -1,7 +1,12 @@
package org.tron.core.actuator;
+import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_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.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -23,7 +28,6 @@
import org.tron.core.store.DelegatedResourceAccountIndexStore;
import org.tron.core.store.DelegatedResourceStore;
import org.tron.core.store.DynamicPropertiesStore;
-import org.tron.core.vm.config.VMConfig;
import org.tron.protos.Protocol.AccountType;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
@@ -57,6 +61,11 @@ public boolean execute(Object result) throws ContractExeException {
AccountCapsule accountCapsule = accountStore
.get(freezeBalanceContract.getOwnerAddress().toByteArray());
+ if (dynamicStore.supportAllowNewResourceModel()
+ && accountCapsule.oldTronPowerIsNotInitialized()) {
+ accountCapsule.initializeOldTronPower();
+ }
+
long now = dynamicStore.getLatestBlockHeaderTimestamp();
long duration = freezeBalanceContract.getFrozenDuration() * FROZEN_PERIOD;
@@ -67,36 +76,48 @@ public boolean execute(Object result) throws ContractExeException {
byte[] ownerAddress = freezeBalanceContract.getOwnerAddress().toByteArray();
byte[] receiverAddress = freezeBalanceContract.getReceiverAddress().toByteArray();
+ long increment;
switch (freezeBalanceContract.getResource()) {
case BANDWIDTH:
if (!ArrayUtils.isEmpty(receiverAddress)
&& dynamicStore.supportDR()) {
- delegateResource(ownerAddress, receiverAddress, true,
- frozenBalance, expireTime);
+ increment = delegateResource(ownerAddress, receiverAddress, true,
+ frozenBalance, expireTime);
accountCapsule.addDelegatedFrozenBalanceForBandwidth(frozenBalance);
} else {
+ long oldNetWeight = accountCapsule.getFrozenBalance() / TRX_PRECISION;
long newFrozenBalanceForBandwidth =
frozenBalance + accountCapsule.getFrozenBalance();
accountCapsule.setFrozenForBandwidth(newFrozenBalanceForBandwidth, expireTime);
+ long newNetWeight = accountCapsule.getFrozenBalance() / TRX_PRECISION;
+ increment = newNetWeight - oldNetWeight;
}
- dynamicStore
- .addTotalNetWeight(frozenBalance / TRX_PRECISION);
+ addTotalWeight(BANDWIDTH, dynamicStore, frozenBalance, increment);
break;
case ENERGY:
if (!ArrayUtils.isEmpty(receiverAddress)
&& dynamicStore.supportDR()) {
- delegateResource(ownerAddress, receiverAddress, false,
- frozenBalance, expireTime);
+ increment = delegateResource(ownerAddress, receiverAddress, false,
+ frozenBalance, expireTime);
accountCapsule.addDelegatedFrozenBalanceForEnergy(frozenBalance);
} else {
+ long oldEnergyWeight = accountCapsule.getEnergyFrozenBalance() / TRX_PRECISION;
long newFrozenBalanceForEnergy =
- frozenBalance + accountCapsule.getAccountResource()
- .getFrozenBalanceForEnergy()
- .getFrozenBalance();
+ frozenBalance + accountCapsule.getEnergyFrozenBalance();
accountCapsule.setFrozenForEnergy(newFrozenBalanceForEnergy, expireTime);
+ long newEnergyWeight = accountCapsule.getEnergyFrozenBalance() / TRX_PRECISION;
+ increment = newEnergyWeight - oldEnergyWeight;
}
- dynamicStore
- .addTotalEnergyWeight(frozenBalance / TRX_PRECISION);
+ addTotalWeight(ENERGY, dynamicStore, frozenBalance, increment);
+ break;
+ case TRON_POWER:
+ long oldTPWeight = accountCapsule.getTronPowerFrozenBalance() / TRX_PRECISION;
+ long newFrozenBalanceForTronPower =
+ frozenBalance + accountCapsule.getTronPowerFrozenBalance();
+ accountCapsule.setFrozenForTronPower(newFrozenBalanceForTronPower, expireTime);
+ long newTPWeight = accountCapsule.getTronPowerFrozenBalance() / TRX_PRECISION;
+ increment = newTPWeight - oldTPWeight;
+ addTotalWeight(TRON_POWER, dynamicStore, frozenBalance, increment);
break;
default:
logger.debug("Resource Code Error.");
@@ -110,6 +131,23 @@ public boolean execute(Object result) throws ContractExeException {
return true;
}
+ private void addTotalWeight(ResourceCode resourceCode, DynamicPropertiesStore dynamicStore,
+ long frozenBalance, long increment) {
+ long weight = dynamicStore.allowNewReward() ? increment : frozenBalance / TRX_PRECISION;
+ switch (resourceCode) {
+ case BANDWIDTH:
+ dynamicStore.addTotalNetWeight(weight);
+ break;
+ case ENERGY:
+ dynamicStore.addTotalEnergyWeight(weight);
+ break;
+ case TRON_POWER:
+ dynamicStore.addTotalTronPowerWeight(weight);
+ break;
+ default:
+ logger.debug("Resource Code Error.");
+ }
+ }
@Override
public boolean validate() throws ContractValidateException {
@@ -143,7 +181,7 @@ public boolean validate() throws ContractValidateException {
if (accountCapsule == null) {
String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] not exists");
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR + readableOwnerAddress + NOT_EXIST_STR);
}
long frozenBalance = freezeBalanceContract.getFrozenBalance();
@@ -151,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();
@@ -159,14 +197,9 @@ 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 maxFrozenNumber = dbManager.getDynamicPropertiesStore().getMaxFrozenNumber();
-// if (accountCapsule.getFrozenCount() >= maxFrozenNumber) {
-// throw new ContractValidateException("max frozen number is: " + maxFrozenNumber);
-// }
-
long frozenDuration = freezeBalanceContract.getFrozenDuration();
long minFrozenTime = dynamicStore.getMinFrozenTime();
long maxFrozenTime = dynamicStore.getMaxFrozenTime();
@@ -182,12 +215,28 @@ public boolean validate() throws ContractValidateException {
switch (freezeBalanceContract.getResource()) {
case BANDWIDTH:
- break;
case ENERGY:
break;
+ case TRON_POWER:
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ byte[] receiverAddress = freezeBalanceContract.getReceiverAddress().toByteArray();
+ if (!ArrayUtils.isEmpty(receiverAddress)) {
+ throw new ContractValidateException(
+ "TRON_POWER is not allowed to delegate to other accounts.");
+ }
+ } else {
+ throw new ContractValidateException(
+ "ResourceCode error, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ break;
default:
- throw new ContractValidateException(
- "ResourceCode error,valid ResourceCode[BANDWIDTH、ENERGY]");
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ throw new ContractValidateException(
+ "ResourceCode error, valid ResourceCode[BANDWIDTH、ENERGY、TRON_POWER]");
+ } else {
+ throw new ContractValidateException(
+ "ResourceCode error, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
}
//todo:need version control and config for delegating resource
@@ -195,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)) {
@@ -207,7 +255,8 @@ public boolean validate() throws ContractValidateException {
if (receiverCapsule == null) {
String readableOwnerAddress = StringUtil.createReadableString(receiverAddress);
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] not exists");
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + NOT_EXIST_STR);
}
if (dynamicStore.getAllowTvmConstantinople() == 1
@@ -219,6 +268,11 @@ public boolean validate() throws ContractValidateException {
}
+ if (dynamicStore.supportUnfreezeDelay()) {
+ throw new ContractValidateException(
+ "freeze v2 is open, old freeze is closed");
+ }
+
return true;
}
@@ -232,9 +286,10 @@ public long calcFee() {
return 0;
}
- private void delegateResource(byte[] ownerAddress, byte[] receiverAddress, boolean isBandwidth,
+ private long delegateResource(byte[] ownerAddress, byte[] receiverAddress, boolean isBandwidth,
long balance, long expireTime) {
AccountStore accountStore = chainBaseManager.getAccountStore();
+ DynamicPropertiesStore dynamicPropertiesStore = chainBaseManager.getDynamicPropertiesStore();
DelegatedResourceStore delegatedResourceStore = chainBaseManager.getDelegatedResourceStore();
DelegatedResourceAccountIndexStore delegatedResourceAccountIndexStore = chainBaseManager
.getDelegatedResourceAccountIndexStore();
@@ -262,46 +317,56 @@ private void delegateResource(byte[] ownerAddress, byte[] receiverAddress, boole
delegatedResourceStore.put(key, delegatedResourceCapsule);
//modify DelegatedResourceAccountIndexStore
- {
- DelegatedResourceAccountIndexCapsule delegatedResourceAccountIndexCapsule = delegatedResourceAccountIndexStore
- .get(ownerAddress);
- if (delegatedResourceAccountIndexCapsule == null) {
- delegatedResourceAccountIndexCapsule = new DelegatedResourceAccountIndexCapsule(
+ if (!dynamicPropertiesStore.supportAllowDelegateOptimization()) {
+
+ DelegatedResourceAccountIndexCapsule ownerIndexCapsule =
+ delegatedResourceAccountIndexStore.get(ownerAddress);
+ if (ownerIndexCapsule == null) {
+ ownerIndexCapsule = new DelegatedResourceAccountIndexCapsule(
ByteString.copyFrom(ownerAddress));
}
- List toAccountsList = delegatedResourceAccountIndexCapsule.getToAccountsList();
+ List toAccountsList = ownerIndexCapsule.getToAccountsList();
if (!toAccountsList.contains(ByteString.copyFrom(receiverAddress))) {
- delegatedResourceAccountIndexCapsule.addToAccount(ByteString.copyFrom(receiverAddress));
+ ownerIndexCapsule.addToAccount(ByteString.copyFrom(receiverAddress));
}
- delegatedResourceAccountIndexStore
- .put(ownerAddress, delegatedResourceAccountIndexCapsule);
- }
+ delegatedResourceAccountIndexStore.put(ownerAddress, ownerIndexCapsule);
- {
- DelegatedResourceAccountIndexCapsule delegatedResourceAccountIndexCapsule = delegatedResourceAccountIndexStore
- .get(receiverAddress);
- if (delegatedResourceAccountIndexCapsule == null) {
- delegatedResourceAccountIndexCapsule = new DelegatedResourceAccountIndexCapsule(
+ DelegatedResourceAccountIndexCapsule receiverIndexCapsule
+ = delegatedResourceAccountIndexStore.get(receiverAddress);
+ if (receiverIndexCapsule == null) {
+ receiverIndexCapsule = new DelegatedResourceAccountIndexCapsule(
ByteString.copyFrom(receiverAddress));
}
- List fromAccountsList = delegatedResourceAccountIndexCapsule
+ List fromAccountsList = receiverIndexCapsule
.getFromAccountsList();
if (!fromAccountsList.contains(ByteString.copyFrom(ownerAddress))) {
- delegatedResourceAccountIndexCapsule.addFromAccount(ByteString.copyFrom(ownerAddress));
+ receiverIndexCapsule.addFromAccount(ByteString.copyFrom(ownerAddress));
}
- delegatedResourceAccountIndexStore
- .put(receiverAddress, delegatedResourceAccountIndexCapsule);
+ delegatedResourceAccountIndexStore.put(receiverAddress, receiverIndexCapsule);
+
+ } else {
+ // modify DelegatedResourceAccountIndexStore new
+ delegatedResourceAccountIndexStore.convert(ownerAddress);
+ delegatedResourceAccountIndexStore.convert(receiverAddress);
+ delegatedResourceAccountIndexStore.delegate(ownerAddress, receiverAddress,
+ dynamicPropertiesStore.getLatestBlockHeaderTimestamp());
}
//modify AccountStore
AccountCapsule receiverCapsule = accountStore.get(receiverAddress);
+ long oldWeight;
+ long newWeight;
if (isBandwidth) {
+ oldWeight = receiverCapsule.getAcquiredDelegatedFrozenBalanceForBandwidth() / TRX_PRECISION;
receiverCapsule.addAcquiredDelegatedFrozenBalanceForBandwidth(balance);
+ newWeight = receiverCapsule.getAcquiredDelegatedFrozenBalanceForBandwidth() / TRX_PRECISION;
} else {
+ oldWeight = receiverCapsule.getAcquiredDelegatedFrozenBalanceForEnergy() / TRX_PRECISION;
receiverCapsule.addAcquiredDelegatedFrozenBalanceForEnergy(balance);
+ newWeight = receiverCapsule.getAcquiredDelegatedFrozenBalanceForEnergy() / TRX_PRECISION;
}
-
accountStore.put(receiverCapsule.createDbKey(), receiverCapsule);
+ return newWeight - oldWeight;
}
}
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
new file mode 100644
index 00000000000..d4260e38163
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/MarketCancelOrderActuator.java
@@ -0,0 +1,231 @@
+/*
+ * java-tron is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * java-tron is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.tron.core.actuator;
+
+import static org.tron.core.actuator.ActuatorConstant.CONTRACT_NOT_EXIST;
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+import static org.tron.core.actuator.ActuatorConstant.TX_RESULT_NULL;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.Objects;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.DecodeUtil;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.MarketOrderCapsule;
+import org.tron.core.capsule.MarketOrderIdListCapsule;
+import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.capsule.utils.MarketUtils;
+import org.tron.core.exception.BalanceInsufficientException;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.exception.ItemNotFoundException;
+import org.tron.core.store.AccountStore;
+import org.tron.core.store.AssetIssueStore;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.store.MarketAccountStore;
+import org.tron.core.store.MarketOrderStore;
+import org.tron.core.store.MarketPairPriceToOrderStore;
+import org.tron.core.store.MarketPairToPriceStore;
+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.MarketContract.MarketCancelOrderContract;
+
+@Slf4j(topic = "actuator")
+public class MarketCancelOrderActuator extends AbstractActuator {
+
+ private AccountStore accountStore;
+ private DynamicPropertiesStore dynamicStore;
+ private AssetIssueStore assetIssueStore;
+
+ private MarketAccountStore marketAccountStore;
+ private MarketOrderStore orderStore;
+ private MarketPairToPriceStore pairToPriceStore;
+ private MarketPairPriceToOrderStore pairPriceToOrderStore;
+
+ public MarketCancelOrderActuator() {
+ super(ContractType.MarketCancelOrderContract, MarketCancelOrderContract.class);
+ }
+
+ private void initStores() {
+ accountStore = chainBaseManager.getAccountStore();
+ dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ assetIssueStore = chainBaseManager.getAssetIssueStore();
+
+ marketAccountStore = chainBaseManager.getMarketAccountStore();
+ orderStore = chainBaseManager.getMarketOrderStore();
+ pairToPriceStore = chainBaseManager.getMarketPairToPriceStore();
+ pairPriceToOrderStore = chainBaseManager.getMarketPairPriceToOrderStore();
+ }
+
+ @Override
+ public boolean execute(Object object) throws ContractExeException {
+
+ initStores();
+
+ TransactionResultCapsule ret = (TransactionResultCapsule) object;
+ if (Objects.isNull(ret)) {
+ throw new RuntimeException(TX_RESULT_NULL);
+ }
+ long fee = calcFee();
+
+ try {
+ final MarketCancelOrderContract contract = this.any
+ .unpack(MarketCancelOrderContract.class);
+
+ AccountCapsule accountCapsule = accountStore
+ .get(contract.getOwnerAddress().toByteArray());
+
+ byte[] orderId = contract.getOrderId().toByteArray();
+ MarketOrderCapsule orderCapsule = orderStore.get(orderId);
+
+ // fee
+ accountCapsule.setBalance(accountCapsule.getBalance() - fee);
+ if (dynamicStore.supportBlackHoleOptimization()) {
+ dynamicStore.burnTrx(fee);
+ } else {
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ }
+ // 1. return balance and token
+ MarketUtils
+ .returnSellTokenRemain(orderCapsule, accountCapsule, dynamicStore, assetIssueStore);
+
+ MarketUtils.updateOrderState(orderCapsule, State.CANCELED, marketAccountStore);
+ accountStore.put(orderCapsule.getOwnerAddress().toByteArray(), accountCapsule);
+ orderStore.put(orderCapsule.getID().toByteArray(), orderCapsule);
+
+ // 2. clear orderList
+ byte[] pairPriceKey = MarketUtils.createPairPriceKey(
+ orderCapsule.getSellTokenId(),
+ orderCapsule.getBuyTokenId(),
+ orderCapsule.getSellTokenQuantity(),
+ orderCapsule.getBuyTokenQuantity()
+ );
+ MarketOrderIdListCapsule orderIdListCapsule = pairPriceToOrderStore.get(pairPriceKey);
+
+ // delete order
+ orderIdListCapsule.removeOrder(orderCapsule, orderStore, pairPriceKey, pairPriceToOrderStore);
+
+ if (orderIdListCapsule.isOrderEmpty()) {
+ // if orderList is empty, delete
+ pairPriceToOrderStore.delete(pairPriceKey);
+
+ // 3. modify priceList
+ // decrease price number
+ // if empty, delete token pair
+ byte[] makerPair = MarketUtils
+ .createPairKey(orderCapsule.getSellTokenId(), orderCapsule.getBuyTokenId());
+ long remainCount = pairToPriceStore.getPriceNum(makerPair) - 1;
+ if (remainCount == 0) {
+ pairToPriceStore.delete(makerPair);
+ } else {
+ pairToPriceStore.setPriceNum(makerPair, remainCount);
+ }
+ }
+
+ ret.setStatus(fee, code.SUCESS);
+ } catch (ItemNotFoundException
+ | InvalidProtocolBufferException
+ | BalanceInsufficientException e) {
+ logger.debug(e.getMessage(), e);
+ ret.setStatus(fee, code.FAILED);
+ throw new ContractExeException(e.getMessage());
+ }
+ return true;
+ }
+
+ @Override
+ public boolean validate() throws ContractValidateException {
+ if (this.any == null) {
+ throw new ContractValidateException(CONTRACT_NOT_EXIST);
+ }
+ if (chainBaseManager == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ initStores();
+
+ if (!this.any.is(MarketCancelOrderContract.class)) {
+ throw new ContractValidateException(
+ "contract type error,expected type [MarketCancelOrderContract],real type[" + any
+ .getClass() + "]");
+ }
+
+ if (!dynamicStore.supportAllowMarketTransaction()) {
+ throw new ContractValidateException("Not support Market Transaction, need to be opened by"
+ + " the committee");
+ }
+
+ final MarketCancelOrderContract contract;
+ try {
+ contract =
+ this.any.unpack(MarketCancelOrderContract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+ // Parameters check
+ byte[] ownerAddress = contract.getOwnerAddress().toByteArray();
+ ByteString orderId = contract.getOrderId();
+
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+
+ // Whether the account exist
+ AccountCapsule ownerAccount = accountStore.get(ownerAddress);
+ if (ownerAccount == null) {
+ throw new ContractValidateException("Account does not exist!");
+ }
+
+ // Whether the order exist
+ MarketOrderCapsule marketOrderCapsule;
+ try {
+ marketOrderCapsule = orderStore.get(orderId.toByteArray());
+ } catch (ItemNotFoundException ex) {
+ throw new ContractValidateException(
+ "orderId not exists");
+ }
+
+ if (!marketOrderCapsule.isActive()) {
+ throw new ContractValidateException("Order is not active!");
+ }
+
+ if (!marketOrderCapsule.getOwnerAddress().equals(ownerAccount.getAddress())) {
+ throw new ContractValidateException("Order does not belong to the account!");
+ }
+
+ // Whether the balance is enough
+ long fee = calcFee();
+ if (ownerAccount.getBalance() < fee) {
+ throw new ContractValidateException("No enough balance !");
+ }
+
+ return true;
+ }
+
+ @Override
+ public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
+ return any.unpack(MarketCancelOrderContract.class).getOwnerAddress();
+ }
+
+ @Override
+ public long calcFee() {
+ return dynamicStore.getMarketCancelFee();
+ }
+
+}
diff --git a/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java b/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java
new file mode 100644
index 00000000000..369857ae6c1
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/actuator/MarketSellAssetActuator.java
@@ -0,0 +1,596 @@
+/*
+ * java-tron is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * java-tron is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.tron.core.actuator;
+
+import static org.tron.core.actuator.ActuatorConstant.CONTRACT_NOT_EXIST;
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+import static org.tron.core.actuator.ActuatorConstant.TX_RESULT_NULL;
+import static org.tron.core.capsule.utils.TransactionUtil.isNumber;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import lombok.Getter;
+import lombok.Setter;
+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.AssetIssueCapsule;
+import org.tron.core.capsule.MarketAccountOrderCapsule;
+import org.tron.core.capsule.MarketOrderCapsule;
+import org.tron.core.capsule.MarketOrderIdListCapsule;
+import org.tron.core.capsule.TransactionResultCapsule;
+import org.tron.core.capsule.utils.MarketUtils;
+import org.tron.core.exception.BalanceInsufficientException;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.exception.ItemNotFoundException;
+import org.tron.core.store.AccountStore;
+import org.tron.core.store.AssetIssueStore;
+import org.tron.core.store.AssetIssueV2Store;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.store.MarketAccountStore;
+import org.tron.core.store.MarketOrderStore;
+import org.tron.core.store.MarketPairPriceToOrderStore;
+import org.tron.core.store.MarketPairToPriceStore;
+import org.tron.protos.Protocol.MarketOrder.State;
+import org.tron.protos.Protocol.MarketOrderDetail;
+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.MarketContract.MarketSellAssetContract;
+
+@Slf4j(topic = "actuator")
+public class MarketSellAssetActuator extends AbstractActuator {
+
+ @Getter
+ @Setter
+ private static int MAX_ACTIVE_ORDER_NUM = 100;
+ @Getter
+ private static int MAX_MATCH_NUM = 20;
+
+ private AccountStore accountStore;
+ private DynamicPropertiesStore dynamicStore;
+ private AssetIssueStore assetIssueStore;
+ private AssetIssueV2Store assetIssueV2Store;
+
+ private MarketAccountStore marketAccountStore;
+ private MarketOrderStore orderStore;
+ private MarketPairToPriceStore pairToPriceStore;
+ private MarketPairPriceToOrderStore pairPriceToOrderStore;
+
+ private byte[] sellTokenID = null;
+ private byte[] buyTokenID = null;
+ private long sellTokenQuantity;
+ private long buyTokenQuantity;
+
+ public MarketSellAssetActuator() {
+ super(ContractType.MarketSellAssetContract, MarketSellAssetContract.class);
+ }
+
+ private void initStores() {
+ accountStore = chainBaseManager.getAccountStore();
+ dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ assetIssueStore = chainBaseManager.getAssetIssueStore();
+ assetIssueV2Store = chainBaseManager.getAssetIssueV2Store();
+
+ marketAccountStore = chainBaseManager.getMarketAccountStore();
+ orderStore = chainBaseManager.getMarketOrderStore();
+ pairToPriceStore = chainBaseManager.getMarketPairToPriceStore();
+ pairPriceToOrderStore = chainBaseManager.getMarketPairPriceToOrderStore();
+ }
+
+ @Override
+ public boolean execute(Object object) throws ContractExeException {
+ initStores();
+
+ TransactionResultCapsule ret = (TransactionResultCapsule) object;
+ if (Objects.isNull(ret)) {
+ throw new RuntimeException(TX_RESULT_NULL);
+ }
+
+ long fee = calcFee();
+
+ try {
+ final MarketSellAssetContract contract = this.any
+ .unpack(MarketSellAssetContract.class);
+
+ AccountCapsule accountCapsule = accountStore
+ .get(contract.getOwnerAddress().toByteArray());
+
+ sellTokenID = contract.getSellTokenId().toByteArray();
+ buyTokenID = contract.getBuyTokenId().toByteArray();
+ sellTokenQuantity = contract.getSellTokenQuantity();
+ buyTokenQuantity = contract.getBuyTokenQuantity();
+ MarketPrice takerPrice = MarketPrice.newBuilder()
+ .setSellTokenQuantity(sellTokenQuantity)
+ .setBuyTokenQuantity(buyTokenQuantity).build();
+
+ // fee
+ accountCapsule.setBalance(accountCapsule.getBalance() - fee);
+ // add to blackhole address
+ if (dynamicStore.supportBlackHoleOptimization()) {
+ dynamicStore.burnTrx(fee);
+ } else {
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ }
+ // 1. transfer of balance
+ transferBalanceOrToken(accountCapsule);
+
+ // 2. create and save order
+ MarketOrderCapsule orderCapsule = createAndSaveOrder(accountCapsule, contract);
+
+ // 3. match order
+ matchOrder(orderCapsule, takerPrice, ret, accountCapsule);
+
+ // 4. save remain order into order book
+ if (orderCapsule.getSellTokenQuantityRemain() != 0) {
+ saveRemainOrder(orderCapsule);
+ }
+
+ orderStore.put(orderCapsule.getID().toByteArray(), orderCapsule);
+ accountStore.put(accountCapsule.createDbKey(), accountCapsule);
+
+ ret.setOrderId(orderCapsule.getID());
+ ret.setStatus(fee, code.SUCESS);
+ } catch (ItemNotFoundException
+ | InvalidProtocolBufferException
+ | BalanceInsufficientException
+ | ContractValidateException e) {
+ logger.debug(e.getMessage(), e);
+ ret.setStatus(fee, code.FAILED);
+ throw new ContractExeException(e.getMessage());
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean validate() throws ContractValidateException {
+ if (this.any == null) {
+ throw new ContractValidateException(CONTRACT_NOT_EXIST);
+ }
+ if (chainBaseManager == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ initStores();
+
+ if (!this.any.is(MarketSellAssetContract.class)) {
+ throw new ContractValidateException(
+ "contract type error,expected type [MarketSellAssetContract],real type[" + any
+ .getClass() + "]");
+ }
+
+ if (!dynamicStore.supportAllowMarketTransaction()) {
+ throw new ContractValidateException("Not support Market Transaction, need to be opened by"
+ + " the committee");
+ }
+
+ final MarketSellAssetContract contract;
+ try {
+ contract =
+ this.any.unpack(MarketSellAssetContract.class);
+ } catch (InvalidProtocolBufferException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+
+ // Parameters check
+ byte[] ownerAddress = contract.getOwnerAddress().toByteArray();
+ sellTokenID = contract.getSellTokenId().toByteArray();
+ buyTokenID = contract.getBuyTokenId().toByteArray();
+ sellTokenQuantity = contract.getSellTokenQuantity();
+ buyTokenQuantity = contract.getBuyTokenQuantity();
+
+ if (!DecodeUtil.addressValid(ownerAddress)) {
+ throw new ContractValidateException("Invalid address");
+ }
+
+ // Whether the accountStore exist
+ AccountCapsule ownerAccount = accountStore.get(ownerAddress);
+ if (ownerAccount == null) {
+ throw new ContractValidateException("Account does not exist!");
+ }
+
+ if (!Arrays.equals(sellTokenID, "_".getBytes()) && !isNumber(sellTokenID)) {
+ throw new ContractValidateException("sellTokenId is not a valid number");
+ }
+ if (!Arrays.equals(buyTokenID, "_".getBytes()) && !isNumber(buyTokenID)) {
+ throw new ContractValidateException("buyTokenId is not a valid number");
+ }
+
+ if (Arrays.equals(sellTokenID, buyTokenID)) {
+ throw new ContractValidateException("cannot exchange same tokens");
+ }
+
+ if (sellTokenQuantity <= 0 || buyTokenQuantity <= 0) {
+ throw new ContractValidateException("token quantity must greater than zero");
+ }
+
+ long quantityLimit = dynamicStore.getMarketQuantityLimit();
+ if (sellTokenQuantity > quantityLimit || buyTokenQuantity > quantityLimit) {
+ throw new ContractValidateException("token quantity must less than " + quantityLimit);
+ }
+
+ // check order num
+ MarketAccountOrderCapsule marketAccountOrderCapsule = marketAccountStore
+ .getUnchecked(ownerAddress);
+ if (marketAccountOrderCapsule != null
+ && marketAccountOrderCapsule.getCount() >= MAX_ACTIVE_ORDER_NUM) {
+ throw new ContractValidateException(
+ "Maximum number of orders exceeded," + MAX_ACTIVE_ORDER_NUM);
+ }
+
+ try {
+ // Whether the balance is enough
+ long fee = calcFee();
+
+ if (Arrays.equals(sellTokenID, "_".getBytes())) {
+ if (ownerAccount.getBalance() < addExact(sellTokenQuantity, fee)) {
+ throw new ContractValidateException("No enough balance !");
+ }
+ } else {
+ if (ownerAccount.getBalance() < fee) {
+ throw new ContractValidateException("No enough balance !");
+ }
+
+ AssetIssueCapsule assetIssueCapsule = Commons
+ .getAssetIssueStoreFinal(dynamicStore, assetIssueStore, assetIssueV2Store)
+ .get(sellTokenID);
+ if (assetIssueCapsule == null) {
+ throw new ContractValidateException("No sellTokenId !");
+ }
+ if (!ownerAccount.assetBalanceEnoughV2(sellTokenID, sellTokenQuantity,
+ dynamicStore)) {
+ throw new ContractValidateException("SellToken balance is not enough !");
+ }
+ }
+
+ if (!Arrays.equals(buyTokenID, "_".getBytes())) {
+ // Whether have the token
+ AssetIssueCapsule assetIssueCapsule = Commons
+ .getAssetIssueStoreFinal(dynamicStore, assetIssueStore, assetIssueV2Store)
+ .get(buyTokenID);
+ if (assetIssueCapsule == null) {
+ throw new ContractValidateException("No buyTokenId !");
+ }
+ }
+ } catch (ArithmeticException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractValidateException(e.getMessage());
+ }
+
+ return true;
+ }
+
+ @Override
+ public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
+ return any.unpack(MarketSellAssetContract.class).getOwnerAddress();
+ }
+
+ @Override
+ public long calcFee() {
+ return dynamicStore.getMarketSellFee();
+ }
+
+ /**
+ * return marketPrice if matched, otherwise null
+ */
+ private MarketPrice hasMatch(List priceKeysList, MarketPrice takerPrice) {
+ if (priceKeysList.isEmpty()) {
+ return null;
+ }
+
+ // get the first key which is the lowest price
+ MarketPrice bestPrice = MarketUtils.decodeKeyToMarketPrice(priceKeysList.get(0));
+
+ return MarketUtils.priceMatch(takerPrice, bestPrice) ? bestPrice : null;
+ }
+
+ private void matchOrder(MarketOrderCapsule takerCapsule, MarketPrice takerPrice,
+ TransactionResultCapsule ret, AccountCapsule takerAccountCapsule)
+ throws ItemNotFoundException, ContractValidateException {
+
+ byte[] makerSellTokenID = buyTokenID;
+ byte[] makerBuyTokenID = sellTokenID;
+ byte[] makerPair = MarketUtils.createPairKey(makerSellTokenID, makerBuyTokenID);
+
+ // makerPair not exists
+ long makerPriceNumber = pairToPriceStore.getPriceNum(makerPair);
+ if (makerPriceNumber == 0) {
+ return;
+ }
+ long remainCount = makerPriceNumber;
+
+ // get maker price list
+ List priceKeysList = pairPriceToOrderStore
+ .getPriceKeysList(MarketUtils.getPairPriceHeadKey(makerSellTokenID, makerBuyTokenID),
+ (long) (MAX_MATCH_NUM + 1), makerPriceNumber, true);
+
+ int matchOrderCount = 0;
+ // match different price
+ while (takerCapsule.getSellTokenQuantityRemain() != 0) {
+ // get lowest ordersList
+ MarketPrice makerPrice = hasMatch(priceKeysList, takerPrice);
+ if (makerPrice == null) {
+ return;
+ }
+
+ byte[] pairPriceKey = priceKeysList.get(0);
+
+ // if not exists
+ MarketOrderIdListCapsule orderIdListCapsule = pairPriceToOrderStore.get(pairPriceKey);
+
+ // match different orders which have the same price
+ while (takerCapsule.getSellTokenQuantityRemain() != 0
+ && !orderIdListCapsule.isOrderEmpty()) {
+ byte[] orderId = orderIdListCapsule.getHead();
+ MarketOrderCapsule makerOrderCapsule = orderStore.get(orderId);
+
+ matchSingleOrder(takerCapsule, makerOrderCapsule, ret, takerAccountCapsule);
+
+ // remove order
+ if (makerOrderCapsule.getSellTokenQuantityRemain() == 0) {
+ // remove from market order list
+ orderIdListCapsule.removeOrder(makerOrderCapsule, orderStore,
+ pairPriceKey, pairPriceToOrderStore);
+ }
+
+ matchOrderCount++;
+ if (matchOrderCount > MAX_MATCH_NUM) {
+ throw new ContractValidateException("Too many matches. MAX_MATCH_NUM = " + MAX_MATCH_NUM);
+ }
+ }
+
+ // the orders of makerPrice have been all consumed
+ if (orderIdListCapsule.isOrderEmpty()) {
+ pairPriceToOrderStore.delete(pairPriceKey);
+
+ // need to delete marketPair if no more price(priceKeysList is empty after deleting)
+ priceKeysList.remove(0);
+
+ // update priceInfo's count
+ remainCount = remainCount - 1;
+ // if really empty, need to delete token pair from pairToPriceStore
+ if (remainCount == 0) {
+ pairToPriceStore.delete(makerPair);
+ break;
+ } else {
+ pairToPriceStore.setPriceNum(makerPair, remainCount);
+ }
+ }
+ } // end while
+ }
+
+ // return all match or not
+ private void matchSingleOrder(MarketOrderCapsule takerOrderCapsule,
+ MarketOrderCapsule makerOrderCapsule, TransactionResultCapsule ret,
+ AccountCapsule takerAccountCapsule)
+ throws ItemNotFoundException {
+
+ long takerSellRemainQuantity = takerOrderCapsule.getSellTokenQuantityRemain();
+ long makerSellQuantity = makerOrderCapsule.getSellTokenQuantity();
+ long makerBuyQuantity = makerOrderCapsule.getBuyTokenQuantity();
+ long makerSellRemainQuantity = makerOrderCapsule.getSellTokenQuantityRemain();
+
+ // according to the price of maker, calculate the quantity of taker can buy
+ // for makerPrice,sellToken is A,buyToken is TRX.
+ // for takerPrice,buyToken is A,sellToken is TRX.
+
+ // makerSellTokenQuantity_A/makerBuyTokenQuantity_TRX =
+ // takerBuyTokenQuantityCurrent_A/takerSellTokenQuantityRemain_TRX
+ // => takerBuyTokenQuantityCurrent_A = takerSellTokenQuantityRemain_TRX *
+ // makerSellTokenQuantity_A/makerBuyTokenQuantity_TRX
+
+ long takerBuyTokenQuantityRemain = MarketUtils
+ .multiplyAndDivide(takerSellRemainQuantity, makerSellQuantity, makerBuyQuantity,
+ this.disableJavaLangMath());
+
+ if (takerBuyTokenQuantityRemain == 0) {
+ // quantity too small, return sellToken to user
+ takerOrderCapsule.setSellTokenQuantityReturn();
+ MarketUtils.returnSellTokenRemain(takerOrderCapsule, takerAccountCapsule,
+ dynamicStore, assetIssueStore);
+ MarketUtils.updateOrderState(takerOrderCapsule, State.INACTIVE, marketAccountStore);
+ return;
+ }
+
+ long takerBuyTokenQuantityReceive; // In this match, the token obtained by taker
+ long makerBuyTokenQuantityReceive; // the token obtained by maker
+
+ if (takerBuyTokenQuantityRemain == makerOrderCapsule.getSellTokenQuantityRemain()) {
+ // taker == maker
+
+ // makerSellTokenQuantityRemain_A/makerBuyTokenQuantityCurrent_TRX =
+ // makerSellTokenQuantity_A/makerBuyTokenQuantity_TRX
+ // => makerBuyTokenQuantityCurrent_TRX = makerSellTokenQuantityRemain_A *
+ // makerBuyTokenQuantity_TRX / makerSellTokenQuantity_A
+
+ makerBuyTokenQuantityReceive = MarketUtils
+ .multiplyAndDivide(makerSellRemainQuantity, makerBuyQuantity, makerSellQuantity,
+ this.disableJavaLangMath());
+ takerBuyTokenQuantityReceive = makerOrderCapsule.getSellTokenQuantityRemain();
+
+ long takerSellTokenLeft =
+ takerOrderCapsule.getSellTokenQuantityRemain() - makerBuyTokenQuantityReceive;
+ takerOrderCapsule.setSellTokenQuantityRemain(takerSellTokenLeft);
+ makerOrderCapsule.setSellTokenQuantityRemain(0);
+
+ if (takerSellTokenLeft == 0) {
+ MarketUtils.updateOrderState(takerOrderCapsule, State.INACTIVE, marketAccountStore);
+ }
+ MarketUtils.updateOrderState(makerOrderCapsule, State.INACTIVE, marketAccountStore);
+ } else if (takerBuyTokenQuantityRemain < makerOrderCapsule.getSellTokenQuantityRemain()) {
+ // taker < maker
+ // if the quantity of taker want to buy is smaller than the remain of maker want to sell,
+ // consume the order of the taker
+
+ takerBuyTokenQuantityReceive = takerBuyTokenQuantityRemain;
+ makerBuyTokenQuantityReceive = takerOrderCapsule.getSellTokenQuantityRemain();
+
+ takerOrderCapsule.setSellTokenQuantityRemain(0);
+ MarketUtils.updateOrderState(takerOrderCapsule, State.INACTIVE, marketAccountStore);
+
+ makerOrderCapsule.setSellTokenQuantityRemain(subtractExact(
+ makerOrderCapsule.getSellTokenQuantityRemain(), takerBuyTokenQuantityRemain));
+ } else {
+ // taker > maker
+ takerBuyTokenQuantityReceive = makerOrderCapsule.getSellTokenQuantityRemain();
+
+ // if the quantity of taker want to buy is bigger than the remain of maker want to sell,
+ // consume the order of maker
+ // makerSellTokenQuantityRemain_A/makerBuyTokenQuantityCurrent_TRX =
+ // makerSellTokenQuantity_A/makerBuyTokenQuantity_TRX
+ makerBuyTokenQuantityReceive = MarketUtils
+ .multiplyAndDivide(makerSellRemainQuantity, makerBuyQuantity, makerSellQuantity,
+ this.disableJavaLangMath());
+
+ MarketUtils.updateOrderState(makerOrderCapsule, State.INACTIVE, marketAccountStore);
+ if (makerBuyTokenQuantityReceive == 0) {
+ // the quantity is too small, return the remain of sellToken to maker
+ // it would not happen here
+ // for the maker, when sellQuantity < buyQuantity, it will get at least one buyToken
+ // even when sellRemain = 1.
+ // so if sellQuantity=200,buyQuantity=100, when sellRemain=1, it needs to be satisfied
+ // the following conditions:
+ // makerOrderCapsule.getSellTokenQuantityRemain() - takerBuyTokenQuantityRemain = 1
+ // 200 - 200/100 * X = 1 ===> X = 199/2,and this comports with the fact that X is integer.
+ makerOrderCapsule.setSellTokenQuantityReturn();
+ returnSellTokenRemain(makerOrderCapsule);
+ return;
+ } else {
+ makerOrderCapsule.setSellTokenQuantityRemain(0);
+ takerOrderCapsule.setSellTokenQuantityRemain(subtractExact(
+ takerOrderCapsule.getSellTokenQuantityRemain(), makerBuyTokenQuantityReceive));
+ }
+ }
+
+ // save makerOrderCapsule
+ orderStore.put(makerOrderCapsule.getID().toByteArray(), makerOrderCapsule);
+
+ // add token into account
+ addTrxOrToken(takerOrderCapsule, takerBuyTokenQuantityReceive, takerAccountCapsule);
+ addTrxOrToken(makerOrderCapsule, makerBuyTokenQuantityReceive);
+
+ MarketOrderDetail orderDetail = MarketOrderDetail.newBuilder()
+ .setMakerOrderId(makerOrderCapsule.getID())
+ .setTakerOrderId(takerOrderCapsule.getID())
+ .setFillSellQuantity(makerBuyTokenQuantityReceive)
+ .setFillBuyQuantity(takerBuyTokenQuantityReceive)
+ .build();
+ ret.addOrderDetails(orderDetail);
+ }
+
+ private MarketOrderCapsule createAndSaveOrder(AccountCapsule accountCapsule,
+ MarketSellAssetContract contract) {
+ MarketAccountOrderCapsule marketAccountOrderCapsule = marketAccountStore
+ .getUnchecked(contract.getOwnerAddress().toByteArray());
+ if (marketAccountOrderCapsule == null) {
+ marketAccountOrderCapsule = new MarketAccountOrderCapsule(contract.getOwnerAddress());
+ }
+
+ // note: here use total_count
+ byte[] orderId = MarketUtils
+ .calculateOrderId(contract.getOwnerAddress(), sellTokenID, buyTokenID,
+ marketAccountOrderCapsule.getTotalCount());
+ MarketOrderCapsule orderCapsule = new MarketOrderCapsule(orderId, contract);
+
+ long now = dynamicStore.getLatestBlockHeaderTimestamp();
+ orderCapsule.setCreateTime(now);
+
+ marketAccountOrderCapsule.addOrders(orderCapsule.getID());
+ marketAccountOrderCapsule.setCount(marketAccountOrderCapsule.getCount() + 1);
+ marketAccountOrderCapsule.setTotalCount(marketAccountOrderCapsule.getTotalCount() + 1);
+ marketAccountStore.put(accountCapsule.createDbKey(), marketAccountOrderCapsule);
+ orderStore.put(orderId, orderCapsule);
+
+ return orderCapsule;
+ }
+
+ private void transferBalanceOrToken(AccountCapsule accountCapsule) {
+ if (Arrays.equals(sellTokenID, "_".getBytes())) {
+ accountCapsule.setBalance(subtractExact(
+ accountCapsule.getBalance(), sellTokenQuantity));
+ } else {
+ accountCapsule
+ .reduceAssetAmountV2(sellTokenID, sellTokenQuantity, dynamicStore, assetIssueStore);
+ }
+ }
+
+ // for taker
+ private void addTrxOrToken(MarketOrderCapsule orderCapsule, long num,
+ AccountCapsule accountCapsule) {
+
+ byte[] buyTokenId = orderCapsule.getBuyTokenId();
+ if (Arrays.equals(buyTokenId, "_".getBytes())) {
+ accountCapsule.setBalance(addExact(accountCapsule.getBalance(), num));
+ } else {
+ accountCapsule
+ .addAssetAmountV2(buyTokenId, num, dynamicStore, assetIssueStore);
+ }
+ }
+
+ private void addTrxOrToken(MarketOrderCapsule orderCapsule, long num) {
+ AccountCapsule accountCapsule = accountStore
+ .get(orderCapsule.getOwnerAddress().toByteArray());
+
+ byte[] buyTokenId = orderCapsule.getBuyTokenId();
+ if (Arrays.equals(buyTokenId, "_".getBytes())) {
+ accountCapsule.setBalance(addExact(accountCapsule.getBalance(), num));
+ } else {
+ accountCapsule
+ .addAssetAmountV2(buyTokenId, num, dynamicStore, assetIssueStore);
+ }
+ accountStore.put(orderCapsule.getOwnerAddress().toByteArray(), accountCapsule);
+ }
+
+ private void returnSellTokenRemain(MarketOrderCapsule orderCapsule) {
+ AccountCapsule accountCapsule = accountStore
+ .get(orderCapsule.getOwnerAddress().toByteArray());
+
+ MarketUtils.returnSellTokenRemain(orderCapsule, accountCapsule, dynamicStore, assetIssueStore);
+ accountStore.put(orderCapsule.getOwnerAddress().toByteArray(), accountCapsule);
+ }
+
+ private void saveRemainOrder(MarketOrderCapsule orderCapsule)
+ throws ItemNotFoundException {
+ // add order into orderList
+ byte[] pairPriceKey = MarketUtils.createPairPriceKey(
+ sellTokenID,
+ buyTokenID,
+ sellTokenQuantity,
+ buyTokenQuantity
+ );
+
+ MarketOrderIdListCapsule orderIdListCapsule = pairPriceToOrderStore.getUnchecked(pairPriceKey);
+ if (orderIdListCapsule == null) {
+ orderIdListCapsule = new MarketOrderIdListCapsule();
+
+ // pairPriceKey not exists, increase price count:
+ // if pair not exits, add token pair, set count = 1, add headKey to pairPriceToOrderStore
+ // if pair exists, increase count
+ pairToPriceStore.addNewPriceKey(sellTokenID, buyTokenID, pairPriceToOrderStore);
+ }
+
+ orderIdListCapsule.addOrder(orderCapsule, orderStore);
+ pairPriceToOrderStore.put(pairPriceKey, orderIdListCapsule);
+ }
+
+}
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/ProposalApproveActuator.java b/actuator/src/main/java/org/tron/core/actuator/ProposalApproveActuator.java
index c49b5971e5e..4cda552e2a7 100755
--- a/actuator/src/main/java/org/tron/core/actuator/ProposalApproveActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ProposalApproveActuator.java
@@ -66,10 +66,10 @@ public boolean execute(Object result) throws ContractExeException {
@Override
public boolean validate() throws ContractValidateException {
- if (this.any == null) {
+ if (Objects.isNull(this.any)) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}
- if (chainBaseManager == null) {
+ if (Objects.isNull(chainBaseManager)) {
throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
}
AccountStore accountStore = chainBaseManager.getAccountStore();
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 1455f84ffa4..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;
@@ -120,8 +119,9 @@ public boolean validate() throws ContractValidateException {
}
private void validateValue(Map.Entry entry) throws ContractValidateException {
- ProposalUtil.validator(chainBaseManager.getDynamicPropertiesStore(), forkController, entry.getKey(),
- entry.getValue());
+ ProposalUtil
+ .validator(chainBaseManager.getDynamicPropertiesStore(), forkController, entry.getKey(),
+ entry.getValue());
}
@Override
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 74b87140352..338e948f304 100644
--- a/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/ShieldedTransferActuator.java
@@ -35,7 +35,6 @@
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.core.store.NullifierStore;
import org.tron.core.store.ZKProofStore;
-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;
@@ -77,7 +76,7 @@ public boolean execute(Object result)
executeTransparentFrom(shieldedTransferContract.getTransparentFromAddress().toByteArray(),
shieldedTransferContract.getFromAmount(), ret, fee);
}
- Commons.adjustAssetBalanceV2(accountStore.getBlackhole().createDbKey(),
+ Commons.adjustAssetBalanceV2(accountStore.getBlackhole(),
CommonParameter.getInstance().getZenTokenId(), fee,
accountStore, assetIssueStore, dynamicStore);
} catch (BalanceInsufficientException e) {
@@ -97,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);
@@ -171,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);
@@ -453,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());
@@ -463,13 +462,7 @@ private void validateTransparent(ShieldedTransferContract shieldedTransferContra
}
private long getZenBalance(AccountCapsule account) {
- if (account.getAssetMapV2().get(CommonParameter
- .getInstance().getZenTokenId()) == null) {
- return 0L;
- } else {
- return account.getAssetMapV2().get(CommonParameter
- .getInstance().getZenTokenId());
- }
+ return account.getAssetV2(CommonParameter.getInstance().getZenTokenId());
}
@Override
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 2e87aad7d55..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,9 +7,10 @@
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;
+import org.tron.core.capsule.ContractCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
import org.tron.core.exception.BalanceInsufficientException;
import org.tron.core.exception.ContractExeException;
@@ -55,11 +56,15 @@ public boolean execute(Object object) throws ContractExeException {
fee = fee + dynamicStore.getCreateNewAccountFeeInSystemContract();
}
- Commons.adjustBalance(accountStore, ownerAddress, -fee);
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), fee);
+
+ adjustBalance(accountStore, ownerAddress, -(addExact(fee, amount)));
+ if (dynamicStore.supportBlackHoleOptimization()) {
+ dynamicStore.burnTrx(fee);
+ } else {
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ }
+ adjustBalance(accountStore, toAddress, amount);
ret.setStatus(fee, code.SUCESS);
- Commons.adjustBalance(accountStore, ownerAddress, -amount);
- Commons.adjustBalance(accountStore, toAddress, amount);
} catch (BalanceInsufficientException | ArithmeticException | InvalidProtocolBufferException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(fee, code.FAILED);
@@ -133,13 +138,32 @@ public boolean validate() throws ContractValidateException {
}
- if (balance < Math.addExact(amount, fee)) {
+ // after AllowTvmCompatibleEvm proposal, send trx to smartContract which version is one
+ // by actuator is not allowed.
+ if (dynamicStore.getAllowTvmCompatibleEvm() == 1
+ && toAccount != null
+ && toAccount.getType() == AccountType.Contract) {
+
+ ContractCapsule contractCapsule = chainBaseManager.getContractStore().get(toAddress);
+ if (contractCapsule == null) { // this can not happen
+ throw new ContractValidateException(
+ "Account type is Contract, but it is not exist in contract store.");
+ } else if (contractCapsule.getContractVersion() == 1) {
+ throw new ContractValidateException(
+ "Cannot transfer TRX to a smartContract which version is one. "
+ + "Instead please use TriggerSmartContract ");
+ }
+ }
+
+ if (balance < addExact(amount, fee)) {
+ logger.warn("Balance is not sufficient. Account: {}, balance: {}, amount: {}, fee: {}.",
+ StringUtil.encode58Check(ownerAddress), balance, amount, fee);
throw new ContractValidateException(
"Validate TransferContract error, balance is not sufficient.");
}
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 1c9969becc4..d93263055eb 100644
--- a/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/TransferAssetActuator.java
@@ -18,7 +18,6 @@
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Arrays;
-import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.tron.common.utils.ByteArray;
@@ -73,9 +72,6 @@ public boolean execute(Object result) throws ContractExeException {
ByteString assetName = transferAssetContract.getAssetName();
long amount = transferAssetContract.getAmount();
- Commons.adjustBalance(accountStore, ownerAddress, -fee);
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), fee);
-
AccountCapsule ownerAccountCapsule = accountStore.get(ownerAddress);
if (!ownerAccountCapsule
.reduceAssetAmountV2(assetName.toByteArray(), amount, dynamicStore, assetIssueStore)) {
@@ -87,6 +83,12 @@ public boolean execute(Object result) throws ContractExeException {
.addAssetAmountV2(assetName.toByteArray(), amount, dynamicStore, assetIssueStore);
accountStore.put(toAddress, toAccountCapsule);
+ adjustBalance(accountStore, ownerAccountCapsule, -fee);
+ if (dynamicStore.supportBlackHoleOptimization()) {
+ dynamicStore.burnTrx(fee);
+ } else {
+ adjustBalance(accountStore, accountStore.getBlackhole(), fee);
+ }
ret.setStatus(fee, code.SUCESS);
} catch (BalanceInsufficientException e) {
logger.debug(e.getMessage(), e);
@@ -156,17 +158,7 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException("No asset!");
}
- Map asset;
- if (dynamicStore.getAllowSameTokenName() == 0) {
- asset = ownerAccount.getAssetMap();
- } else {
- asset = ownerAccount.getAssetMapV2();
- }
- if (asset.isEmpty()) {
- throw new ContractValidateException("Owner has no asset!");
- }
-
- Long assetBalance = asset.get(ByteArray.toStr(assetName));
+ Long assetBalance = ownerAccount.getAsset(dynamicStore, ByteArray.toStr(assetName));
if (null == assetBalance || assetBalance <= 0) {
throw new ContractValidateException("assetBalance must be greater than 0.");
}
@@ -182,14 +174,10 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException("Cannot transfer asset to smartContract.");
}
- if (dynamicStore.getAllowSameTokenName() == 0) {
- assetBalance = toAccount.getAssetMap().get(ByteArray.toStr(assetName));
- } else {
- assetBalance = toAccount.getAssetMapV2().get(ByteArray.toStr(assetName));
- }
+ 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/UnfreezeAssetActuator.java b/actuator/src/main/java/org/tron/core/actuator/UnfreezeAssetActuator.java
index fde468eccdb..434b9151609 100755
--- a/actuator/src/main/java/org/tron/core/actuator/UnfreezeAssetActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/UnfreezeAssetActuator.java
@@ -1,5 +1,7 @@
package org.tron.core.actuator;
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+
import com.google.common.collect.Lists;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -112,7 +114,7 @@ public boolean validate() throws ContractValidateException {
if (accountCapsule == null) {
String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] does not exist");
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
}
if (accountCapsule.getFrozenSupplyCount() <= 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 8a0a9ce7041..80334f1fc49 100755
--- a/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/UnfreezeBalanceActuator.java
@@ -1,5 +1,6 @@
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 com.google.common.collect.Lists;
@@ -12,7 +13,6 @@
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
-import org.tron.common.utils.Commons;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
@@ -20,9 +20,9 @@
import org.tron.core.capsule.DelegatedResourceCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
import org.tron.core.capsule.VotesCapsule;
-import org.tron.core.db.DelegationService;
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.DelegatedResourceAccountIndexStore;
import org.tron.core.store.DelegatedResourceStore;
@@ -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);
}
@@ -57,7 +60,7 @@ public boolean execute(Object result) throws ContractExeException {
DelegatedResourceAccountIndexStore delegatedResourceAccountIndexStore = chainBaseManager
.getDelegatedResourceAccountIndexStore();
VotesStore votesStore = chainBaseManager.getVotesStore();
- DelegationService delegationService = chainBaseManager.getDelegationService();
+ MortgageService mortgageService = chainBaseManager.getMortgageService();
try {
unfreezeBalanceContract = any.unpack(UnfreezeBalanceContract.class);
} catch (InvalidProtocolBufferException e) {
@@ -68,16 +71,22 @@ public boolean execute(Object result) throws ContractExeException {
byte[] ownerAddress = unfreezeBalanceContract.getOwnerAddress().toByteArray();
//
- delegationService.withdrawReward(ownerAddress);
+ mortgageService.withdrawReward(ownerAddress);
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
long oldBalance = accountCapsule.getBalance();
long unfreezeBalance = 0L;
+ if (dynamicStore.supportAllowNewResourceModel()
+ && accountCapsule.oldTronPowerIsNotInitialized()) {
+ accountCapsule.initializeOldTronPower();
+ }
+
byte[] receiverAddress = unfreezeBalanceContract.getReceiverAddress().toByteArray();
//If the receiver is not included in the contract, unfreeze frozen balance for this account.
//otherwise,unfreeze delegated frozen balance provided this account.
+ long decrease = 0;
if (!ArrayUtils.isEmpty(receiverAddress) && dynamicStore.supportDR()) {
byte[] key = DelegatedResourceCapsule
.createDbKey(unfreezeBalanceContract.getOwnerAddress().toByteArray(),
@@ -102,31 +111,46 @@ public boolean execute(Object result) throws ContractExeException {
}
AccountCapsule receiverCapsule = accountStore.get(receiverAddress);
+
if (dynamicStore.getAllowTvmConstantinople() == 0 ||
(receiverCapsule != null && receiverCapsule.getType() != AccountType.Contract)) {
switch (unfreezeBalanceContract.getResource()) {
case BANDWIDTH:
+ long oldNetWeight = receiverCapsule.getAcquiredDelegatedFrozenBalanceForBandwidth() /
+ TRX_PRECISION;
if (dynamicStore.getAllowTvmSolidity059() == 1
&& receiverCapsule.getAcquiredDelegatedFrozenBalanceForBandwidth()
< unfreezeBalance) {
+ oldNetWeight = unfreezeBalance / TRX_PRECISION;
receiverCapsule.setAcquiredDelegatedFrozenBalanceForBandwidth(0);
} else {
receiverCapsule.addAcquiredDelegatedFrozenBalanceForBandwidth(-unfreezeBalance);
}
+ long newNetWeight = receiverCapsule.getAcquiredDelegatedFrozenBalanceForBandwidth() /
+ TRX_PRECISION;
+ decrease = newNetWeight - oldNetWeight;
break;
case ENERGY:
+ long oldEnergyWeight = receiverCapsule.getAcquiredDelegatedFrozenBalanceForEnergy() /
+ TRX_PRECISION;
if (dynamicStore.getAllowTvmSolidity059() == 1
&& receiverCapsule.getAcquiredDelegatedFrozenBalanceForEnergy() < unfreezeBalance) {
+ oldEnergyWeight = unfreezeBalance / TRX_PRECISION;
receiverCapsule.setAcquiredDelegatedFrozenBalanceForEnergy(0);
} else {
receiverCapsule.addAcquiredDelegatedFrozenBalanceForEnergy(-unfreezeBalance);
}
+ long newEnergyWeight = receiverCapsule.getAcquiredDelegatedFrozenBalanceForEnergy() /
+ TRX_PRECISION;
+ decrease = newEnergyWeight - oldEnergyWeight;
break;
default:
//this should never happen
break;
}
accountStore.put(receiverCapsule.createDbKey(), receiverCapsule);
+ } else {
+ decrease = -unfreezeBalance / TRX_PRECISION;
}
accountCapsule.setBalance(oldBalance + unfreezeBalance);
@@ -136,39 +160,39 @@ public boolean execute(Object result) throws ContractExeException {
delegatedResourceStore.delete(key);
//modify DelegatedResourceAccountIndexStore
- {
- DelegatedResourceAccountIndexCapsule delegatedResourceAccountIndexCapsule = delegatedResourceAccountIndexStore
- .get(ownerAddress);
- if (delegatedResourceAccountIndexCapsule != null) {
- List toAccountsList = new ArrayList<>(delegatedResourceAccountIndexCapsule
+ if (!dynamicStore.supportAllowDelegateOptimization()) {
+ DelegatedResourceAccountIndexCapsule ownerIndexCapsule =
+ delegatedResourceAccountIndexStore.get(ownerAddress);
+ if (ownerIndexCapsule != null) {
+ List toAccountsList = new ArrayList<>(ownerIndexCapsule
.getToAccountsList());
toAccountsList.remove(ByteString.copyFrom(receiverAddress));
- delegatedResourceAccountIndexCapsule.setAllToAccounts(toAccountsList);
- delegatedResourceAccountIndexStore
- .put(ownerAddress, delegatedResourceAccountIndexCapsule);
+ ownerIndexCapsule.setAllToAccounts(toAccountsList);
+ delegatedResourceAccountIndexStore.put(ownerAddress, ownerIndexCapsule);
}
- }
- {
- DelegatedResourceAccountIndexCapsule delegatedResourceAccountIndexCapsule = delegatedResourceAccountIndexStore
- .get(receiverAddress);
- if (delegatedResourceAccountIndexCapsule != null) {
- List fromAccountsList = new ArrayList<>(delegatedResourceAccountIndexCapsule
+ DelegatedResourceAccountIndexCapsule receiverIndexCapsule =
+ delegatedResourceAccountIndexStore.get(receiverAddress);
+ if (receiverIndexCapsule != null) {
+ List fromAccountsList = new ArrayList<>(receiverIndexCapsule
.getFromAccountsList());
fromAccountsList.remove(ByteString.copyFrom(ownerAddress));
- delegatedResourceAccountIndexCapsule.setAllFromAccounts(fromAccountsList);
- delegatedResourceAccountIndexStore
- .put(receiverAddress, delegatedResourceAccountIndexCapsule);
+ receiverIndexCapsule.setAllFromAccounts(fromAccountsList);
+ delegatedResourceAccountIndexStore.put(receiverAddress, receiverIndexCapsule);
}
+ } else {
+ //modify DelegatedResourceAccountIndexStore new
+ delegatedResourceAccountIndexStore.convert(ownerAddress);
+ delegatedResourceAccountIndexStore.convert(receiverAddress);
+ delegatedResourceAccountIndexStore.unDelegate(ownerAddress, receiverAddress);
}
-
} else {
delegatedResourceStore.put(key, delegatedResourceCapsule);
}
} else {
switch (unfreezeBalanceContract.getResource()) {
case BANDWIDTH:
-
+ long oldNetWeight = accountCapsule.getFrozenBalance() / TRX_PRECISION;
List frozenList = Lists.newArrayList();
frozenList.addAll(accountCapsule.getFrozenList());
Iterator iterator = frozenList.iterator();
@@ -184,9 +208,11 @@ public boolean execute(Object result) throws ContractExeException {
accountCapsule.setInstance(accountCapsule.getInstance().toBuilder()
.setBalance(oldBalance + unfreezeBalance)
.clearFrozen().addAllFrozen(frozenList).build());
-
+ long newNetWeight = accountCapsule.getFrozenBalance() / TRX_PRECISION;
+ decrease = newNetWeight - oldNetWeight;
break;
case ENERGY:
+ long oldEnergyWeight = accountCapsule.getEnergyFrozenBalance() / TRX_PRECISION;
unfreezeBalance = accountCapsule.getAccountResource().getFrozenBalanceForEnergy()
.getFrozenBalance();
@@ -195,7 +221,17 @@ public boolean execute(Object result) throws ContractExeException {
accountCapsule.setInstance(accountCapsule.getInstance().toBuilder()
.setBalance(oldBalance + unfreezeBalance)
.setAccountResource(newAccountResource).build());
-
+ long newEnergyWeight = accountCapsule.getEnergyFrozenBalance() / TRX_PRECISION;
+ decrease = newEnergyWeight - oldEnergyWeight;
+ break;
+ case TRON_POWER:
+ long oldTPWeight = accountCapsule.getTronPowerFrozenBalance() / TRX_PRECISION;
+ unfreezeBalance = accountCapsule.getTronPowerFrozenBalance();
+ accountCapsule.setInstance(accountCapsule.getInstance().toBuilder()
+ .setBalance(oldBalance + unfreezeBalance)
+ .clearTronPower().build());
+ long newTPWeight = accountCapsule.getTronPowerFrozenBalance() / TRX_PRECISION;
+ decrease = newTPWeight - oldTPWeight;
break;
default:
//this should never happen
@@ -203,34 +239,58 @@ public boolean execute(Object result) throws ContractExeException {
}
}
-
+
+ long weight = dynamicStore.allowNewReward() ? decrease : -unfreezeBalance / TRX_PRECISION;
switch (unfreezeBalanceContract.getResource()) {
case BANDWIDTH:
dynamicStore
- .addTotalNetWeight(-unfreezeBalance / TRX_PRECISION);
+ .addTotalNetWeight(weight);
break;
case ENERGY:
dynamicStore
- .addTotalEnergyWeight(-unfreezeBalance / TRX_PRECISION);
+ .addTotalEnergyWeight(weight);
+ break;
+ case TRON_POWER:
+ dynamicStore
+ .addTotalTronPowerWeight(weight);
break;
default:
//this should never happen
break;
}
- VotesCapsule votesCapsule;
- if (!votesStore.has(ownerAddress)) {
- votesCapsule = new VotesCapsule(unfreezeBalanceContract.getOwnerAddress(),
- accountCapsule.getVotesList());
- } else {
- votesCapsule = votesStore.get(ownerAddress);
+ boolean needToClearVote = true;
+ if (dynamicStore.supportAllowNewResourceModel()
+ && accountCapsule.oldTronPowerIsInvalid()) {
+ switch (unfreezeBalanceContract.getResource()) {
+ case BANDWIDTH:
+ case ENERGY:
+ needToClearVote = false;
+ break;
+ default:
+ break;
+ }
}
- accountCapsule.clearVotes();
- votesCapsule.clearNewVotes();
- accountStore.put(ownerAddress, accountCapsule);
+ if (needToClearVote) {
+ VotesCapsule votesCapsule;
+ if (!votesStore.has(ownerAddress)) {
+ votesCapsule = new VotesCapsule(unfreezeBalanceContract.getOwnerAddress(),
+ accountCapsule.getVotesList());
+ } else {
+ votesCapsule = votesStore.get(ownerAddress);
+ }
+ accountCapsule.clearVotes();
+ votesCapsule.clearNewVotes();
+ votesStore.put(ownerAddress, votesCapsule);
+ }
- votesStore.put(ownerAddress, votesCapsule);
+ if (dynamicStore.supportAllowNewResourceModel()
+ && !accountCapsule.oldTronPowerIsInvalid()) {
+ accountCapsule.invalidateOldTronPower();
+ }
+
+ accountStore.put(ownerAddress, accountCapsule);
ret.setUnfreezeAmount(unfreezeBalance);
ret.setStatus(fee, code.SUCESS);
@@ -270,7 +330,7 @@ public boolean validate() throws ContractValidateException {
if (accountCapsule == null) {
String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] does not exist");
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
}
long now = dynamicStore.getLatestBlockHeaderTimestamp();
byte[] receiverAddress = unfreezeBalanceContract.getReceiverAddress().toByteArray();
@@ -369,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 {
@@ -396,10 +455,27 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException("It's not time to unfreeze(Energy).");
}
+ break;
+ case TRON_POWER:
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ Frozen frozenBalanceForTronPower = accountCapsule.getInstance().getTronPower();
+ if (frozenBalanceForTronPower.getFrozenBalance() <= 0) {
+ throw new ContractValidateException("no frozenBalance(TronPower)");
+ }
+ if (frozenBalanceForTronPower.getExpireTime() > now) {
+ throw new ContractValidateException("It's not time to unfreeze(TronPower).");
+ }
+ } else {
+ throw new ContractValidateException(INVALID_RESOURCE_CODE);
+ }
break;
default:
- throw new ContractValidateException(
- "ResourceCode error.valid ResourceCode[BANDWIDTH、Energy]");
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ throw new ContractValidateException(
+ "ResourceCode error.valid ResourceCode[BANDWIDTH、Energy、TRON_POWER]");
+ } else {
+ 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/UpdateBrokerageActuator.java b/actuator/src/main/java/org/tron/core/actuator/UpdateBrokerageActuator.java
index 523d786899a..f9584e3b0bd 100644
--- a/actuator/src/main/java/org/tron/core/actuator/UpdateBrokerageActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/UpdateBrokerageActuator.java
@@ -4,7 +4,7 @@
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
-import org.spongycastle.util.encoders.Hex;
+import org.bouncycastle.util.encoders.Hex;
import org.tron.common.utils.DecodeUtil;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
@@ -90,7 +90,7 @@ public boolean validate() throws ContractValidateException {
throw new ContractValidateException("Invalid ownerAddress");
}
- if (brokerage < 0 || brokerage > 100) {
+ if (brokerage < 0 || brokerage > ActuatorConstant.ONE_HUNDRED) {
throw new ContractValidateException("Invalid brokerage");
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/UpdateEnergyLimitContractActuator.java b/actuator/src/main/java/org/tron/core/actuator/UpdateEnergyLimitContractActuator.java
index 63a515f28a0..f6b67e02891 100755
--- a/actuator/src/main/java/org/tron/core/actuator/UpdateEnergyLimitContractActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/UpdateEnergyLimitContractActuator.java
@@ -6,15 +6,16 @@
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.tron.common.utils.DecodeUtil;
-import org.tron.common.utils.StorageUtils;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.ContractCapsule;
+import org.tron.core.capsule.ReceiptCapsule;
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.ContractStore;
+import org.tron.core.vm.repository.RepositoryImpl;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.contract.SmartContractOuterClass.UpdateEnergyLimitContract;
@@ -44,6 +45,7 @@ public boolean execute(Object object) throws ContractExeException {
contractStore.put(contractAddress, new ContractCapsule(
deployedContract.getInstance().toBuilder().setOriginEnergyLimit(newOriginEnergyLimit)
.build()));
+ RepositoryImpl.removeLruCache(contractAddress);
ret.setStatus(fee, code.SUCESS);
} catch (InvalidProtocolBufferException e) {
@@ -56,16 +58,16 @@ public boolean execute(Object object) throws ContractExeException {
@Override
public boolean validate() throws ContractValidateException {
- if (!StorageUtils.getEnergyLimitHardFork()) {
- throw new ContractValidateException(
- "contract type error, unexpected type [UpdateEnergyLimitContract]");
- }
if (this.any == null) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}
if (chainBaseManager == null) {
throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
}
+ if (!ReceiptCapsule.checkForEnergyLimit(chainBaseManager.getDynamicPropertiesStore())) {
+ throw new ContractValidateException(
+ "contract type error, unexpected type [UpdateEnergyLimitContract]");
+ }
AccountStore accountStore = chainBaseManager.getAccountStore();
ContractStore contractStore = chainBaseManager.getContractStore();
if (!this.any.is(UpdateEnergyLimitContract.class)) {
@@ -89,7 +91,7 @@ public boolean validate() throws ContractValidateException {
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
if (accountCapsule == null) {
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] does not exist");
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
}
long newOriginEnergyLimit = contract.getOriginEnergyLimit();
@@ -111,7 +113,8 @@ public boolean validate() throws ContractValidateException {
if (!Arrays.equals(ownerAddress, deployedContractOwnerAddress)) {
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] is not the owner of the contract");
+ ActuatorConstant.ACCOUNT_EXCEPTION_STR
+ + readableOwnerAddress + "] is not the owner of the contract");
}
return true;
diff --git a/actuator/src/main/java/org/tron/core/actuator/UpdateSettingContractActuator.java b/actuator/src/main/java/org/tron/core/actuator/UpdateSettingContractActuator.java
index daa3100ed94..d0025f14ef6 100755
--- a/actuator/src/main/java/org/tron/core/actuator/UpdateSettingContractActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/UpdateSettingContractActuator.java
@@ -1,5 +1,7 @@
package org.tron.core.actuator;
+import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
+
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Arrays;
@@ -14,6 +16,7 @@
import org.tron.core.exception.ContractValidateException;
import org.tron.core.store.AccountStore;
import org.tron.core.store.ContractStore;
+import org.tron.core.vm.repository.RepositoryImpl;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.contract.SmartContractOuterClass.UpdateSettingContract;
@@ -43,6 +46,7 @@ public boolean execute(Object object) throws ContractExeException {
contractStore.put(contractAddress, new ContractCapsule(
deployedContract.getInstance().toBuilder().setConsumeUserResourcePercent(newPercent)
.build()));
+ RepositoryImpl.removeLruCache(contractAddress);
ret.setStatus(fee, code.SUCESS);
} catch (InvalidProtocolBufferException e) {
@@ -83,11 +87,11 @@ public boolean validate() throws ContractValidateException {
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
if (accountCapsule == null) {
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] does not exist");
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] does not exist");
}
long newPercent = contract.getConsumeUserResourcePercent();
- if (newPercent > 100 || newPercent < 0) {
+ if (newPercent > ActuatorConstant.ONE_HUNDRED || newPercent < 0) {
throw new ContractValidateException(
"percent not in [0, 100]");
}
@@ -105,7 +109,7 @@ public boolean validate() throws ContractValidateException {
if (!Arrays.equals(ownerAddress, deployedContractOwnerAddress)) {
throw new ContractValidateException(
- "Account[" + readableOwnerAddress + "] is not the owner of the contract");
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] is not the owner of the contract");
}
return true;
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 6e0a17d08d4..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,11 +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.core.vm.utils.MUtil.transfer;
-import static org.tron.core.vm.utils.MUtil.transferToken;
+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;
@@ -16,26 +17,31 @@
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-import org.spongycastle.util.encoders.Hex;
+import org.bouncycastle.util.encoders.Hex;
import org.tron.common.logsfilter.trigger.ContractTrigger;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.InternalTransaction;
import org.tron.common.runtime.InternalTransaction.ExecutorType;
import org.tron.common.runtime.InternalTransaction.TrxType;
import org.tron.common.runtime.ProgramResult;
+import org.tron.common.runtime.vm.DataWord;
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.TransactionCapsule;
+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;
import org.tron.core.vm.VMUtils;
@@ -48,7 +54,6 @@
import org.tron.core.vm.program.ProgramPrecompile;
import org.tron.core.vm.program.invoke.ProgramInvoke;
import org.tron.core.vm.program.invoke.ProgramInvokeFactory;
-import org.tron.core.vm.program.invoke.ProgramInvokeFactoryImpl;
import org.tron.core.vm.repository.Repository;
import org.tron.core.vm.repository.RepositoryImpl;
import org.tron.core.vm.utils.MUtil;
@@ -64,16 +69,17 @@
@Slf4j(topic = "VM")
public class VMActuator implements Actuator2 {
+ /* tx and block info */
private Transaction trx;
private BlockCapsule blockCap;
- private Repository repository;
- private InternalTransaction rootInternalTransaction;
- private ProgramInvokeFactory programInvokeFactory;
-
- private VM vm;
+ /* tvm execution context */
+ private Repository rootRepository;
private Program program;
- private VMConfig vmConfig = VMConfig.getInstance();
+ private InternalTransaction rootInternalTx;
+
+ /* tx receipt */
+ private ReceiptCapsule receipt;
@Getter
@Setter
@@ -82,17 +88,18 @@ public class VMActuator implements Actuator2 {
@Getter
@Setter
- private boolean isConstantCall = false;
+ private boolean isConstantCall;
+
+ private long maxEnergyLimit;
@Setter
private boolean enableEventListener;
private LogInfoTriggerParser logInfoTriggerParser;
-
public VMActuator(boolean isConstantCall) {
this.isConstantCall = isConstantCall;
- programInvokeFactory = new ProgramInvokeFactoryImpl();
+ this.maxEnergyLimit = CommonParameter.getInstance().maxEnergyLimitForConstant;
}
private static long getEnergyFee(long callerEnergyUsage, long callerEnergyFrozen,
@@ -112,14 +119,26 @@ public void validate(Object object) throws ContractValidateException {
throw new RuntimeException("TransactionContext is null");
}
- //Load Config
+ // Load Config
ConfigLoader.load(context.getStoreFactory());
+ // 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() || VMConfig.allowTvmFreezeV2())
+ && context.getTrxCap().getTrxTrace() != null) {
+ receipt = context.getTrxCap().getTrxTrace().getReceipt();
+ }
//Route Type
ContractType contractType = this.trx.getRawData().getContract(0).getType();
//Prepare Repository
- repository = RepositoryImpl.createRoot(context.getStoreFactory());
+ rootRepository = RepositoryImpl.createRoot(context.getStoreFactory());
enableEventListener = context.isEventPluginLoaded();
@@ -157,7 +176,7 @@ public void execute(Object object) throws ContractExeException {
ProgramResult result = context.getProgramResult();
try {
- if (vm != null) {
+ if (program != null) {
if (null != blockCap && blockCap.generatedByMyself && blockCap.hasWitnessSignature()
&& null != TransactionUtil.getContractRet(trx)
&& contractResult.OUT_OF_TIME == TransactionUtil.getContractRet(trx)) {
@@ -170,28 +189,24 @@ public void execute(Object object) throws ContractExeException {
throw e;
}
- vm.play(program);
+ VM.play(program, OperationRegistry.getTable());
result = program.getResult();
- if (isConstantCall) {
- long callValue = TransactionCapsule.getCallValue(trx.getRawData().getContract(0));
- long callTokenValue = TransactionUtil
- .getCallTokenValue(trx.getRawData().getContract(0));
- if (callValue > 0 || callTokenValue > 0) {
- result.setRuntimeError("constant cannot set call value or call token value.");
- result.rejectInternalTransactions();
- }
- if (result.getException() != null) {
- result.setRuntimeError(result.getException().getMessage());
- result.rejectInternalTransactions();
- }
- context.setProgramResult(result);
- return;
+ 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();
- long saveCodeEnergy = (long) getLength(code) * EnergyCost.getInstance().getCREATE_DATA();
+ if (code.length != 0 && VMConfig.allowTvmLondon() && code[0] == (byte) 0xEF) {
+ if (null == result.getException()) {
+ result.setException(Program.Exception.invalidCodeException());
+ }
+ }
+ long saveCodeEnergy = (long) getLength(code) * EnergyCost.getCreateData();
long afterSpend = program.getEnergyLimitLeft().longValue() - saveCodeEnergy;
if (afterSpend < 0) {
if (null == result.getException()) {
@@ -202,15 +217,24 @@ public void execute(Object object) throws ContractExeException {
} else {
result.spendEnergy(saveCodeEnergy);
if (VMConfig.allowTvmConstantinople()) {
- repository.saveCode(program.getContractAddress().getNoLeadZeroesData(), code);
+ rootRepository.saveCode(program.getContractAddress().getNoLeadZeroesData(), code);
}
}
}
+ if (isConstantCall) {
+ if (result.getException() != null) {
+ result.setRuntimeError(result.getException().getMessage());
+ result.rejectInternalTransactions();
+ }
+ context.setProgramResult(result);
+ return;
+ }
+
if (result.getException() != null || result.isRevert()) {
result.getDeleteAccounts().clear();
result.getLogInfoList().clear();
- result.resetFutureRefund();
+ //result.resetFutureRefund();
result.rejectInternalTransactions();
if (result.getException() != null) {
@@ -223,17 +247,20 @@ public void execute(Object object) throws ContractExeException {
result.setRuntimeError("REVERT opcode executed");
}
} else {
- repository.commit();
+ rootRepository.commit();
if (logInfoTriggerParser != null) {
List triggers = logInfoTriggerParser
- .parseLogInfos(program.getResult().getLogInfoList(), repository);
+ .parseLogInfos(program.getResult().getLogInfoList(), rootRepository);
program.getResult().setTriggerList(triggers);
}
}
} else {
- repository.commit();
+ rootRepository.commit();
+ }
+ for (DataWord account : result.getDeleteAccounts()) {
+ RepositoryImpl.removeLruCache(account.toTronAddress());
}
} catch (JVMStackOverFlowException e) {
program.spendAllEnergy();
@@ -277,7 +304,7 @@ public void execute(Object object) throws ContractExeException {
traceContent = VMUtils.zipAndEncode(traceContent);
}
- String txHash = Hex.toHexString(rootInternalTransaction.getHash());
+ String txHash = Hex.toHexString(rootInternalTx.getHash());
VMUtils.saveProgramTraceFile(txHash, traceContent);
}
@@ -285,7 +312,7 @@ public void execute(Object object) throws ContractExeException {
private void create()
throws ContractValidateException {
- if (!repository.getDynamicPropertiesStore().supportVM()) {
+ if (!rootRepository.getDynamicPropertiesStore().supportVM()) {
throw new ContractValidateException("vm work is off, need to be opened by the committee");
}
@@ -293,7 +320,12 @@ private void create()
if (contract == null) {
throw new ContractValidateException("Cannot get CreateSmartContract from transaction");
}
- SmartContract newSmartContract = contract.getNewContract();
+ SmartContract newSmartContract;
+ if (VMConfig.allowTvmCompatibleEvm()) {
+ newSmartContract = contract.getNewContract().toBuilder().setVersion(1).build();
+ } else {
+ newSmartContract = contract.getNewContract().toBuilder().clearVersion().build();
+ }
if (!contract.getOwnerAddress().equals(newSmartContract.getOriginAddress())) {
logger.info("OwnerAddress not equals OriginAddress");
throw new ContractValidateException("OwnerAddress is not equals OriginAddress");
@@ -312,7 +344,7 @@ private void create()
byte[] contractAddress = WalletUtil.generateContractAddress(trx);
// insure the new contract address haven't exist
- if (repository.getAccount(contractAddress) != null) {
+ if (rootRepository.getAccount(contractAddress) != null) {
throw new ContractValidateException(
"Trying to create a contract with existing contract address: " + StringUtil
.encode58Check(contractAddress));
@@ -331,49 +363,57 @@ private void create()
// create vm to constructor smart contract
try {
long feeLimit = trx.getRawData().getFeeLimit();
- if (feeLimit < 0 || feeLimit > VMConfig.MAX_FEE_LIMIT) {
+ if (feeLimit < 0 || feeLimit > rootRepository.getDynamicPropertiesStore().getMaxFeeLimit()) {
logger.info("invalid feeLimit {}", feeLimit);
- throw new ContractValidateException(
- "feeLimit must be >= 0 and <= " + VMConfig.MAX_FEE_LIMIT);
+ throw new ContractValidateException("feeLimit must be >= 0 and <= "
+ + rootRepository.getDynamicPropertiesStore().getMaxFeeLimit());
}
- AccountCapsule creator = this.repository
+ AccountCapsule creator = rootRepository
.getAccount(newSmartContract.getOriginAddress().toByteArray());
long energyLimit;
// according to version
- if (StorageUtils.getEnergyLimitHardFork()) {
- if (callValue < 0) {
- throw new ContractValidateException("callValue must be >= 0");
- }
- if (tokenValue < 0) {
- throw new ContractValidateException("tokenValue must be >= 0");
- }
- if (newSmartContract.getOriginEnergyLimit() <= 0) {
- throw new ContractValidateException("The originEnergyLimit must be > 0");
- }
- energyLimit = getAccountEnergyLimitWithFixRatio(creator, feeLimit, callValue);
+ if (isConstantCall) {
+ energyLimit = maxEnergyLimit;
} else {
- energyLimit = getAccountEnergyLimitWithFloatRatio(creator, feeLimit, callValue);
+ if (StorageUtils.getEnergyLimitHardFork()) {
+ if (callValue < 0) {
+ throw new ContractValidateException("callValue must be >= 0");
+ }
+ if (tokenValue < 0) {
+ throw new ContractValidateException("tokenValue must be >= 0");
+ }
+ if (newSmartContract.getOriginEnergyLimit() <= 0) {
+ throw new ContractValidateException("The originEnergyLimit must be > 0");
+ }
+ energyLimit = getAccountEnergyLimitWithFixRatio(creator, feeLimit, callValue);
+ } else {
+ energyLimit = getAccountEnergyLimitWithFloatRatio(creator, feeLimit, callValue);
+ }
}
checkTokenValueAndId(tokenValue, tokenId);
byte[] ops = newSmartContract.getBytecode().toByteArray();
- rootInternalTransaction = new InternalTransaction(trx, trxType);
+ rootInternalTx = new InternalTransaction(trx, trxType);
- long maxCpuTimeOfOneTx = repository.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
+ ProgramInvoke programInvoke = ProgramInvokeFactory
.createProgramInvoke(TrxType.TRX_CONTRACT_CREATION_TYPE, executorType, trx,
- tokenValue, tokenId, blockCap.getInstance(), repository, vmStartInUs,
+ tokenValue, tokenId, blockCap.getInstance(), rootRepository, vmStartInUs,
vmShouldEndInUs, energyLimit);
- this.vm = new VM();
- this.program = new Program(ops, programInvoke, rootInternalTransaction, vmConfig
- );
+ if (isConstantCall) {
+ programInvoke.setConstantCall();
+ }
+ this.program = new Program(ops, contractAddress, programInvoke, rootInternalTx);
+ if (VMConfig.allowTvmCompatibleEvm()) {
+ this.program.setContractVersion(1);
+ }
byte[] txId = TransactionUtil.getTransactionId(trx).getBytes();
this.program.setRootTransactionId(txId);
if (enableEventListener && isCheckTransaction()) {
@@ -386,20 +426,20 @@ private void create()
}
program.getResult().setContractAddress(contractAddress);
- repository.createAccount(contractAddress, newSmartContract.getName(),
+ rootRepository.createAccount(contractAddress, newSmartContract.getName(),
Protocol.AccountType.Contract);
- repository.createContract(contractAddress, new ContractCapsule(newSmartContract));
+ rootRepository.createContract(contractAddress, new ContractCapsule(newSmartContract));
byte[] code = newSmartContract.getBytecode().toByteArray();
if (!VMConfig.allowTvmConstantinople()) {
- repository.saveCode(contractAddress, ProgramPrecompile.getCode(code));
+ rootRepository.saveCode(contractAddress, ProgramPrecompile.getCode(code));
}
// transfer from callerAddress to contractAddress according to callValue
if (callValue > 0) {
- transfer(this.repository, callerAddress, contractAddress, callValue);
+ MUtil.transfer(rootRepository, callerAddress, contractAddress, callValue);
}
if (VMConfig.allowTvmTransferTrc10() && tokenValue > 0) {
- transferToken(this.repository, callerAddress, contractAddress, String.valueOf(tokenId),
+ MUtil.transferToken(rootRepository, callerAddress, contractAddress, String.valueOf(tokenId),
tokenValue);
}
@@ -412,7 +452,7 @@ private void create()
private void call()
throws ContractValidateException {
- if (!repository.getDynamicPropertiesStore().supportVM()) {
+ if (!rootRepository.getDynamicPropertiesStore().supportVM()) {
logger.info("vm work is off, need to be opened by the committee");
throw new ContractValidateException("VM work is off, need to be opened by the committee");
}
@@ -428,7 +468,7 @@ private void call()
byte[] contractAddress = contract.getContractAddress().toByteArray();
- ContractCapsule deployedContract = repository.getContract(contractAddress);
+ ContractCapsule deployedContract = rootRepository.getContract(contractAddress);
if (null == deployedContract) {
logger.info("No contract or not a smart contract");
throw new ContractValidateException("No contract or not a smart contract");
@@ -454,41 +494,41 @@ private void call()
byte[] callerAddress = contract.getOwnerAddress().toByteArray();
checkTokenValueAndId(tokenValue, tokenId);
- byte[] code = repository.getCode(contractAddress);
+ byte[] code = rootRepository.getCode(contractAddress);
if (isNotEmpty(code)) {
-
long feeLimit = trx.getRawData().getFeeLimit();
- if (feeLimit < 0 || feeLimit > VMConfig.MAX_FEE_LIMIT) {
+ if (feeLimit < 0 || feeLimit > rootRepository.getDynamicPropertiesStore().getMaxFeeLimit()) {
logger.info("invalid feeLimit {}", feeLimit);
- throw new ContractValidateException(
- "feeLimit must be >= 0 and <= " + VMConfig.MAX_FEE_LIMIT);
+ throw new ContractValidateException("feeLimit must be >= 0 and <= "
+ + rootRepository.getDynamicPropertiesStore().getMaxFeeLimit());
}
- AccountCapsule caller = repository.getAccount(callerAddress);
+ AccountCapsule caller = rootRepository.getAccount(callerAddress);
long energyLimit;
if (isConstantCall) {
- energyLimit = VMConstant.ENERGY_LIMIT_IN_CONSTANT_TX;
+ energyLimit = maxEnergyLimit;
} else {
- AccountCapsule creator = repository
+ AccountCapsule creator = rootRepository
.getAccount(deployedContract.getInstance().getOriginAddress().toByteArray());
energyLimit = getTotalEnergyLimit(creator, caller, contract, feeLimit, callValue);
}
- long maxCpuTimeOfOneTx = repository.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
+ ProgramInvoke programInvoke = ProgramInvokeFactory
.createProgramInvoke(TrxType.TRX_CONTRACT_CALL_TYPE, executorType, trx,
- tokenValue, tokenId, blockCap.getInstance(), repository, vmStartInUs,
+ tokenValue, tokenId, blockCap.getInstance(), rootRepository, vmStartInUs,
vmShouldEndInUs, energyLimit);
if (isConstantCall) {
programInvoke.setConstantCall();
}
- this.vm = new VM();
- rootInternalTransaction = new InternalTransaction(trx, trxType);
- this.program = new Program(code, programInvoke, rootInternalTransaction, vmConfig);
+ rootInternalTx = new InternalTransaction(trx, trxType);
+ this.program = new Program(code, contractAddress, programInvoke, rootInternalTx);
+ if (VMConfig.allowTvmCompatibleEvm()) {
+ this.program.setContractVersion(deployedContract.getContractVersion());
+ }
byte[] txId = TransactionUtil.getTransactionId(trx).getBytes();
this.program.setRootTransactionId(txId);
@@ -502,10 +542,10 @@ private void call()
//transfer from callerAddress to targetAddress according to callValue
if (callValue > 0) {
- transfer(this.repository, callerAddress, contractAddress, callValue);
+ MUtil.transfer(rootRepository, callerAddress, contractAddress, callValue);
}
if (VMConfig.allowTvmTransferTrc10() && tokenValue > 0) {
- transferToken(this.repository, callerAddress, contractAddress, String.valueOf(tokenId),
+ MUtil.transferToken(rootRepository, callerAddress, contractAddress, String.valueOf(tokenId),
tokenValue);
}
@@ -515,17 +555,41 @@ public long getAccountEnergyLimitWithFixRatio(AccountCapsule account, long feeLi
long callValue) {
long sunPerEnergy = VMConstant.SUN_PER_ENERGY;
- if (repository.getDynamicPropertiesStore().getEnergyFee() > 0) {
- sunPerEnergy = repository.getDynamicPropertiesStore().getEnergyFee();
+ if (rootRepository.getDynamicPropertiesStore().getEnergyFee() > 0) {
+ sunPerEnergy = rootRepository.getDynamicPropertiesStore().getEnergyFee();
}
- long leftFrozenEnergy = repository.getAccountLeftEnergyFromFreeze(account);
+ long leftFrozenEnergy = rootRepository.getAccountLeftEnergyFromFreeze(account);
+ 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());
}
@@ -533,14 +597,15 @@ private long getAccountEnergyLimitWithFloatRatio(AccountCapsule account, long fe
long callValue) {
long sunPerEnergy = VMConstant.SUN_PER_ENERGY;
- if (repository.getDynamicPropertiesStore().getEnergyFee() > 0) {
- sunPerEnergy = repository.getDynamicPropertiesStore().getEnergyFee();
+ if (rootRepository.getDynamicPropertiesStore().getEnergyFee() > 0) {
+ sunPerEnergy = rootRepository.getDynamicPropertiesStore().getEnergyFee();
}
// can change the calc way
- long leftEnergyFromFreeze = repository.getAccountLeftEnergyFromFreeze(account);
- callValue = max(callValue, 0);
- long energyFromBalance = Math
- .floorDiv(max(account.getBalance() - callValue, 0), sunPerEnergy);
+ long leftEnergyFromFreeze = rootRepository.getAccountLeftEnergyFromFreeze(account);
+ 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();
@@ -548,7 +613,7 @@ private long getAccountEnergyLimitWithFloatRatio(AccountCapsule account, long fe
energyFromFeeLimit =
feeLimit / sunPerEnergy;
} else {
- long totalEnergyFromFreeze = repository
+ long totalEnergyFromFreeze = rootRepository
.calculateGlobalEnergyLimit(account);
long leftBalanceForEnergyFreeze = getEnergyFee(totalBalanceForEnergyFreeze,
leftEnergyFromFreeze,
@@ -559,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,
@@ -593,8 +659,8 @@ public void checkTokenValueAndId(long tokenValue, long tokenId) throws ContractV
// tokenid can only be 0 when tokenvalue = 0,
// or (MIN_TOKEN_ID, Long.Max]
if (tokenValue > 0 && tokenId == 0) {
- throw new ContractValidateException("invalid arguments with tokenValue = " + tokenValue +
- ", tokenId = " + tokenId);
+ throw new ContractValidateException("invalid arguments with tokenValue = "
+ + tokenValue + ", tokenId = " + tokenId);
}
}
}
@@ -606,8 +672,8 @@ private double getCpuLimitInUsRatio() {
if (ExecutorType.ET_NORMAL_TYPE == executorType) {
// self witness generates block
- if (this.blockCap != null && blockCap.generatedByMyself &&
- !this.blockCap.hasWitnessSignature()) {
+ if (blockCap != null && blockCap.generatedByMyself
+ && !blockCap.hasWitnessSignature()) {
cpuLimitRatio = 1.0;
} else {
// self witness or other witness or fullnode verifies block
@@ -625,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 {
@@ -638,32 +712,60 @@ public long getTotalEnergyLimitWithFixRatio(AccountCapsule creator, AccountCapsu
}
long creatorEnergyLimit = 0;
- ContractCapsule contractCapsule = repository
+ ContractCapsule contractCapsule = rootRepository
.getContract(contract.getContractAddress().toByteArray());
- long consumeUserResourcePercent = contractCapsule.getConsumeUserResourcePercent();
+ long consumeUserResourcePercent = contractCapsule.getConsumeUserResourcePercent(
+ VMConfig.disableJavaLangMath());
long originEnergyLimit = contractCapsule.getOriginEnergyLimit();
if (originEnergyLimit < 0) {
throw new ContractValidateException("originEnergyLimit can't be < 0");
}
+ long originEnergyLeft = 0;
+ if (consumeUserResourcePercent < VMConstant.ONE_HUNDRED) {
+ originEnergyLeft = rootRepository.getAccountLeftEnergyFromFreeze(creator);
+ if (VMConfig.allowTvmFreeze() || VMConfig.allowTvmFreezeV2()) {
+ receipt.setOriginEnergyLeft(originEnergyLeft);
+ }
+ }
if (consumeUserResourcePercent <= 0) {
- creatorEnergyLimit = min(repository.getAccountLeftEnergyFromFreeze(creator),
- originEnergyLimit);
+ creatorEnergyLimit = min(originEnergyLeft, originEnergyLimit,
+ VMConfig.disableJavaLangMath());
} else {
if (consumeUserResourcePercent < VMConstant.ONE_HUNDRED) {
// creatorEnergyLimit =
- // min(callerEnergyLimit * (100 - percent) / percent, creatorLeftFrozenEnergy, originEnergyLimit)
+ // min(callerEnergyLimit * (100 - percent) / percent,
+ // creatorLeftFrozenEnergy, originEnergyLimit)
creatorEnergyLimit = min(
BigInteger.valueOf(callerEnergyLimit)
.multiply(BigInteger.valueOf(VMConstant.ONE_HUNDRED - consumeUserResourcePercent))
.divide(BigInteger.valueOf(consumeUserResourcePercent)).longValueExact(),
- min(repository.getAccountLeftEnergyFromFreeze(creator), 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,
@@ -675,17 +777,21 @@ private long getTotalEnergyLimitWithFloatRatio(AccountCapsule creator, AccountCa
}
// creatorEnergyFromFreeze
- long creatorEnergyLimit = repository.getAccountLeftEnergyFromFreeze(creator);
+ long creatorEnergyLimit = rootRepository.getAccountLeftEnergyFromFreeze(creator);
- ContractCapsule contractCapsule = repository
+ 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());
}
}
@@ -694,5 +800,4 @@ private boolean isCheckTransaction() {
.getWitnessSignature().isEmpty();
}
-
}
diff --git a/actuator/src/main/java/org/tron/core/actuator/VoteWitnessActuator.java b/actuator/src/main/java/org/tron/core/actuator/VoteWitnessActuator.java
index 29d1680ca81..9ea812ac751 100755
--- a/actuator/src/main/java/org/tron/core/actuator/VoteWitnessActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/VoteWitnessActuator.java
@@ -18,10 +18,11 @@
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
import org.tron.core.capsule.VotesCapsule;
-import org.tron.core.db.DelegationService;
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.core.store.WitnessStore;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
@@ -125,9 +126,16 @@ public boolean validate() throws ContractValidateException {
ACCOUNT_EXCEPTION_STR + readableOwnerAddress + NOT_EXIST_STR);
}
- long tronPower = accountCapsule.getTronPower();
+ long tronPower;
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ if (dynamicStore.supportAllowNewResourceModel()) {
+ tronPower = accountCapsule.getAllTronPower();
+ } else {
+ tronPower = accountCapsule.getTronPower();
+ }
- sum = LongMath.checkedMultiply(sum, TRX_PRECISION); //trx -> drop. The vote count is based on TRX
+ sum = LongMath
+ .checkedMultiply(sum, TRX_PRECISION); //trx -> drop. The vote count is based on TRX
if (sum > tronPower) {
throw new ContractValidateException(
"The total number of votes[" + sum + "] is greater than the tronPower[" + tronPower
@@ -144,16 +152,22 @@ public boolean validate() throws ContractValidateException {
private void countVoteAccount(VoteWitnessContract voteContract) {
AccountStore accountStore = chainBaseManager.getAccountStore();
VotesStore votesStore = chainBaseManager.getVotesStore();
- DelegationService delegationService = chainBaseManager.getDelegationService();
+ MortgageService mortgageService = chainBaseManager.getMortgageService();
byte[] ownerAddress = voteContract.getOwnerAddress().toByteArray();
VotesCapsule votesCapsule;
//
- delegationService.withdrawReward(ownerAddress);
+ mortgageService.withdrawReward(ownerAddress);
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
+ DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
+ if (dynamicStore.supportAllowNewResourceModel()
+ && accountCapsule.oldTronPowerIsNotInitialized()) {
+ accountCapsule.initializeOldTronPower();
+ }
+
if (!votesStore.has(ownerAddress)) {
votesCapsule = new VotesCapsule(voteContract.getOwnerAddress(),
accountCapsule.getVotesList());
diff --git a/actuator/src/main/java/org/tron/core/actuator/WithdrawBalanceActuator.java b/actuator/src/main/java/org/tron/core/actuator/WithdrawBalanceActuator.java
index 0ae90b42f22..cea71c9d8ed 100755
--- a/actuator/src/main/java/org/tron/core/actuator/WithdrawBalanceActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/WithdrawBalanceActuator.java
@@ -1,6 +1,7 @@
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.FROZEN_PERIOD;
import com.google.common.math.LongMath;
@@ -10,14 +11,13 @@
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.tron.common.parameter.CommonParameter;
-import org.tron.common.utils.Commons;
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.db.DelegationService;
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.protos.Protocol.Transaction.Contract.ContractType;
@@ -42,7 +42,7 @@ public boolean execute(Object result) throws ContractExeException {
final WithdrawBalanceContract withdrawBalanceContract;
AccountStore accountStore = chainBaseManager.getAccountStore();
DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
- DelegationService delegationService = chainBaseManager.getDelegationService();
+ MortgageService mortgageService = chainBaseManager.getMortgageService();
try {
withdrawBalanceContract = any.unpack(WithdrawBalanceContract.class);
} catch (InvalidProtocolBufferException e) {
@@ -51,13 +51,9 @@ public boolean execute(Object result) throws ContractExeException {
throw new ContractExeException(e.getMessage());
}
- delegationService.withdrawReward(withdrawBalanceContract.getOwnerAddress()
+ mortgageService.withdrawReward(withdrawBalanceContract.getOwnerAddress()
.toByteArray());
- delegationService.getDelegationStore().setLastWithdrawCycle(
- dynamicStore.getCurrentCycleNumber(), withdrawBalanceContract.getOwnerAddress()
- .toByteArray());
-
AccountCapsule accountCapsule = accountStore.
get(withdrawBalanceContract.getOwnerAddress().toByteArray());
long oldBalance = accountCapsule.getBalance();
@@ -86,7 +82,7 @@ public boolean validate() throws ContractValidateException {
}
AccountStore accountStore = chainBaseManager.getAccountStore();
DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
- DelegationService delegationService = chainBaseManager.getDelegationService();
+ MortgageService mortgageService = chainBaseManager.getMortgageService();
if (!this.any.is(WithdrawBalanceContract.class)) {
throw new ContractValidateException(
"contract type error, expected type [WithdrawBalanceContract], real type[" + any
@@ -108,14 +104,14 @@ public boolean validate() throws ContractValidateException {
if (accountCapsule == null) {
String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
throw new ContractValidateException(
- ACCOUNT_EXCEPTION_STR + readableOwnerAddress + "] not exists");
+ ACCOUNT_EXCEPTION_STR + readableOwnerAddress + NOT_EXIST_STR);
}
String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
boolean isGP = CommonParameter.getInstance()
.getGenesisBlock().getWitnesses().stream().anyMatch(witness ->
- Arrays.equals(ownerAddress, witness.getAddress()));
+ Arrays.equals(ownerAddress, witness.getAddress()));
if (isGP) {
throw new ContractValidateException(
ACCOUNT_EXCEPTION_STR + readableOwnerAddress
@@ -132,7 +128,7 @@ public boolean validate() throws ContractValidateException {
}
if (accountCapsule.getAllowance() <= 0 &&
- delegationService.queryReward(ownerAddress) <= 0) {
+ mortgageService.queryReward(ownerAddress) <= 0) {
throw new ContractValidateException("witnessAccount does not have any reward");
}
try {
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 8ce36f3d064..fc908a1713f 100755
--- a/actuator/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java
+++ b/actuator/src/main/java/org/tron/core/actuator/WitnessCreateActuator.java
@@ -1,10 +1,11 @@
package org.tron.core.actuator;
+import static org.tron.core.actuator.ActuatorConstant.WITNESS_EXCEPTION_STR;
+
import com.google.protobuf.ByteString;
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;
@@ -86,7 +87,8 @@ public boolean validate() throws ContractValidateException {
AccountCapsule accountCapsule = accountStore.get(ownerAddress);
if (accountCapsule == null) {
- throw new ContractValidateException("account[" + readableOwnerAddress + "] not exists");
+ throw new ContractValidateException("account[" + readableOwnerAddress
+ + ActuatorConstant.NOT_EXIST_STR);
}
/* todo later
if (ArrayUtils.isEmpty(accountCapsule.getAccountName().toByteArray())) {
@@ -94,7 +96,8 @@ public boolean validate() throws ContractValidateException {
} */
if (witnessStore.has(ownerAddress)) {
- throw new ContractValidateException("Witness[" + readableOwnerAddress + "] has existed");
+ throw new ContractValidateException(
+ WITNESS_EXCEPTION_STR + readableOwnerAddress + "] has existed");
}
if (accountCapsule.getBalance() < dynamicStore
@@ -136,11 +139,12 @@ private void createWitness(final WitnessCreateContract witnessCreateContract)
}
accountStore.put(accountCapsule.createDbKey(), accountCapsule);
long cost = dynamicStore.getAccountUpgradeCost();
- Commons
- .adjustBalance(accountStore, witnessCreateContract.getOwnerAddress().toByteArray(), -cost);
-
- Commons.adjustBalance(accountStore, accountStore.getBlackhole().createDbKey(), +cost);
-
+ adjustBalance(accountStore, witnessCreateContract.getOwnerAddress().toByteArray(), -cost);
+ if (dynamicStore.supportBlackHoleOptimization()) {
+ dynamicStore.burnTrx(cost);
+ } else {
+ 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 1dd03844be5..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;
@@ -14,8 +22,12 @@ public class ProposalUtil {
"Bad chain parameter value, valid range is [0," + LONG_VALUE + "]";
private static final String PRE_VALUE_NOT_ONE_ERROR = "This value[";
private static final String VALUE_NOT_ONE_ERROR = "] is only allowed to be 1";
+ private static final long MAX_SUPPLY = 100_000_000_000L;
+ private static final String MAX_SUPPLY_ERROR
+ = "Bad chain parameter value, valid range is [0, 100_000_000_000L]";
- public static void validator(DynamicPropertiesStore dynamicPropertiesStore, ForkController forkController,
+ public static void validator(DynamicPropertiesStore dynamicPropertiesStore,
+ ForkController forkController,
long code, long value)
throws ContractValidateException {
ProposalType proposalType = ProposalType.getEnum(code);
@@ -43,7 +55,7 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
case ALLOW_CREATION_OF_CONTRACTS: {
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_CREATION_OF_CONTRACTS" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_CREATION_OF_CONTRACTS" + VALUE_NOT_ONE_ERROR);
}
break;
}
@@ -55,7 +67,7 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "REMOVE_THE_POWER_OF_THE_GR" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "REMOVE_THE_POWER_OF_THE_GR" + VALUE_NOT_ONE_ERROR);
}
break;
}
@@ -63,29 +75,36 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
case EXCHANGE_CREATE_FEE:
break;
case MAX_CPU_TIME_OF_ONE_TX:
- if (value < 10 || value > 100) {
- throw new ContractValidateException(
- "Bad chain parameter value, valid range is [10,100]");
+ if (dynamicPropertiesStore.getAllowHigherLimitForMaxCpuTimeOfOneTx() == 1) {
+ if (value < 10 || value > 400) {
+ throw new ContractValidateException(
+ "Bad chain parameter value, valid range is [10,400]");
+ }
+ } else {
+ if (value < 10 || value > 100) {
+ throw new ContractValidateException(
+ "Bad chain parameter value, valid range is [10,100]");
+ }
}
break;
case ALLOW_UPDATE_ACCOUNT_NAME: {
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_UPDATE_ACCOUNT_NAME" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_UPDATE_ACCOUNT_NAME" + VALUE_NOT_ONE_ERROR);
}
break;
}
case ALLOW_SAME_TOKEN_NAME: {
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_SAME_TOKEN_NAME" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_SAME_TOKEN_NAME" + VALUE_NOT_ONE_ERROR);
}
break;
}
case ALLOW_DELEGATE_RESOURCE: {
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_DELEGATE_RESOURCE" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_DELEGATE_RESOURCE" + VALUE_NOT_ONE_ERROR);
}
break;
}
@@ -104,7 +123,7 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
case ALLOW_TVM_TRANSFER_TRC10: {
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_TVM_TRANSFER_TRC10" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_TVM_TRANSFER_TRC10" + VALUE_NOT_ONE_ERROR);
}
if (dynamicPropertiesStore.getAllowSameTokenName() == 0) {
throw new ContractValidateException("[ALLOW_SAME_TOKEN_NAME] proposal must be approved "
@@ -127,7 +146,7 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
}
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_MULTI_SIGN" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_MULTI_SIGN" + VALUE_NOT_ONE_ERROR);
}
break;
}
@@ -137,7 +156,7 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
}
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_ADAPTIVE_ENERGY" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_ADAPTIVE_ENERGY" + VALUE_NOT_ONE_ERROR);
}
break;
}
@@ -146,9 +165,8 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
throw new ContractValidateException(
"Bad chain parameter id: UPDATE_ACCOUNT_PERMISSION_FEE");
}
- if (value < 0 || value > 100_000_000_000L) {
- throw new ContractValidateException(
- "Bad chain parameter value, valid range is [0,100_000_000_000L]");
+ if (value < 0 || value > MAX_SUPPLY) {
+ throw new ContractValidateException(MAX_SUPPLY_ERROR);
}
break;
}
@@ -156,9 +174,8 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
if (!forkController.pass(ForkBlockVersionEnum.VERSION_3_5)) {
throw new ContractValidateException("Bad chain parameter id: MULTI_SIGN_FEE");
}
- if (value < 0 || value > 100_000_000_000L) {
- throw new ContractValidateException(
- "Bad chain parameter value, valid range is [0,100_000_000_000L]");
+ if (value < 0 || value > MAX_SUPPLY) {
+ throw new ContractValidateException(MAX_SUPPLY_ERROR);
}
break;
}
@@ -188,7 +205,7 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
}
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_TVM_CONSTANTINOPLE" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_TVM_CONSTANTINOPLE" + VALUE_NOT_ONE_ERROR);
}
if (dynamicPropertiesStore.getAllowTvmTransferTrc10() == 0) {
throw new ContractValidateException(
@@ -204,7 +221,7 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
}
if (value != 1) {
throw new ContractValidateException(
- PRE_VALUE_NOT_ONE_ERROR + "ALLOW_TVM_SOLIDITY_059" + VALUE_NOT_ONE_ERROR);
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_TVM_SOLIDITY_059" + VALUE_NOT_ONE_ERROR);
}
if (dynamicPropertiesStore.getAllowCreationOfContracts() == 0) {
throw new ContractValidateException(
@@ -306,7 +323,7 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
break;
}
case ALLOW_PBFT: {
- if (!forkController.pass(ForkBlockVersionEnum.VERSION_3_8)) {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1)) {
throw new ContractValidateException(
"Bad chain parameter id [ALLOW_PBFT]");
}
@@ -316,6 +333,614 @@ public static void validator(DynamicPropertiesStore dynamicPropertiesStore, Fork
}
break;
}
+ case ALLOW_TVM_ISTANBUL: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_ISTANBUL]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_ISTANBUL] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_SHIELDED_TRC20_TRANSACTION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_0_1)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_SHIELDED_TRC20_TRANSACTION]");
+ }
+ if (value != 1 && value != 0) {
+ throw new ContractValidateException(
+ "This value[ALLOW_SHIELDED_TRC20_TRANSACTION] is only allowed to be 1 or 0");
+ }
+ break;
+ }
+ case ALLOW_MARKET_TRANSACTION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1)
+ || forkController.pass(ForkBlockVersionEnum.VERSION_4_8_1)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_MARKET_TRANSACTION]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_MARKET_TRANSACTION] is only allowed to be 1");
+ }
+ break;
+ }
+ case MARKET_SELL_FEE: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1)) {
+ throw new ContractValidateException("Bad chain parameter id [MARKET_SELL_FEE]");
+ }
+ if (!dynamicPropertiesStore.supportAllowMarketTransaction()) {
+ throw new ContractValidateException(
+ "Market Transaction is not activated, can not set Market Sell Fee");
+ }
+ if (value < 0 || value > 10_000_000_000L) {
+ throw new ContractValidateException(
+ "Bad MARKET_SELL_FEE parameter value, valid range is [0,10_000_000_000L]");
+ }
+ break;
+ }
+ case MARKET_CANCEL_FEE: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1)) {
+ throw new ContractValidateException("Bad chain parameter id [MARKET_CANCEL_FEE]");
+ }
+ if (!dynamicPropertiesStore.supportAllowMarketTransaction()) {
+ throw new ContractValidateException(
+ "Market Transaction is not activated, can not set Market Cancel Fee");
+ }
+ if (value < 0 || value > 10_000_000_000L) {
+ throw new ContractValidateException(
+ "Bad MARKET_CANCEL_FEE parameter value, valid range is [0,10_000_000_000L]");
+ }
+ break;
+ }
+ case MAX_FEE_LIMIT: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1_2)) {
+ throw new ContractValidateException("Bad chain parameter id [MAX_FEE_LIMIT]");
+ }
+ if (value < 0) {
+ throw new ContractValidateException(
+ "Bad MAX_FEE_LIMIT parameter value, value must not be negative");
+ } else if (value > 10_000_000_000L) {
+ if (dynamicPropertiesStore.getAllowTvmLondon() == 0) {
+ throw new ContractValidateException(
+ "Bad MAX_FEE_LIMIT parameter value, valid range is [0,10_000_000_000L]");
+ }
+ if (value > LONG_VALUE) {
+ throw new ContractValidateException(LONG_VALUE_ERROR);
+ }
+ }
+ break;
+ }
+ case ALLOW_TRANSACTION_FEE_POOL: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TRANSACTION_FEE_POOL]");
+ }
+ if (value != 1 && value != 0) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TRANSACTION_FEE_POOL] is only allowed to be 1 or 0");
+ }
+ break;
+ }
+ case ALLOW_BLACKHOLE_OPTIMIZATION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_1_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_REMOVE_BLACKHOLE]");
+ }
+ if (value != 1 && value != 0) {
+ throw new ContractValidateException(
+ "This value[ALLOW_REMOVE_BLACKHOLE] is only allowed to be 1 or 0");
+ }
+ break;
+ }
+ case ALLOW_NEW_RESOURCE_MODEL: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_NEW_RESOURCE_MODEL]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_NEW_RESOURCE_MODEL] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_TVM_FREEZE: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_2)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_FREEZE]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_TVM_FREEZE" + VALUE_NOT_ONE_ERROR);
+ }
+ if (dynamicPropertiesStore.getAllowDelegateResource() == 0) {
+ throw new ContractValidateException(
+ "[ALLOW_DELEGATE_RESOURCE] proposal must be approved "
+ + "before [ALLOW_TVM_FREEZE] can be proposed");
+ }
+ if (dynamicPropertiesStore.getAllowMultiSign() == 0) {
+ throw new ContractValidateException(
+ "[ALLOW_MULTI_SIGN] proposal must be approved "
+ + "before [ALLOW_TVM_FREEZE] can be proposed");
+ }
+ if (dynamicPropertiesStore.getAllowTvmConstantinople() == 0) {
+ throw new ContractValidateException(
+ "[ALLOW_TVM_CONSTANTINOPLE] proposal must be approved "
+ + "before [ALLOW_TVM_FREEZE] can be proposed");
+ }
+ if (dynamicPropertiesStore.getAllowTvmSolidity059() == 0) {
+ throw new ContractValidateException(
+ "[ALLOW_TVM_SOLIDITY_059] proposal must be approved "
+ + "before [ALLOW_TVM_FREEZE] can be proposed");
+ }
+ break;
+ }
+ case ALLOW_TVM_VOTE: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_3)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_VOTE]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ PRE_VALUE_NOT_ONE_ERROR + "ALLOW_TVM_VOTE" + VALUE_NOT_ONE_ERROR);
+ }
+ if (dynamicPropertiesStore.getChangeDelegation() == 0) {
+ throw new ContractValidateException(
+ "[ALLOW_CHANGE_DELEGATION] proposal must be approved "
+ + "before [ALLOW_TVM_VOTE] can be proposed");
+ }
+ break;
+ }
+ case FREE_NET_LIMIT: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_3)) {
+ throw new ContractValidateException("Bad chain parameter id [FREE_NET_LIMIT]");
+ }
+ if (value < 0 || value > 100_000L) {
+ throw new ContractValidateException(
+ "Bad chain parameter value, valid range is [0,100_000]");
+ }
+ break;
+ }
+ case TOTAL_NET_LIMIT: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_3)) {
+ throw new ContractValidateException("Bad chain parameter id [TOTAL_NET_LIMIT]");
+ }
+ if (value < 0 || value > 1_000_000_000_000L) {
+ throw new ContractValidateException(
+ "Bad chain parameter value, valid range is [0, 1_000_000_000_000L]");
+ }
+ break;
+ }
+
+ case ALLOW_ACCOUNT_ASSET_OPTIMIZATION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_3)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_ACCOUNT_ASSET_OPTIMIZATION]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_ACCOUNT_ASSET_OPTIMIZATION] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_TVM_LONDON: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_4)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_LONDON]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_LONDON] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_TVM_COMPATIBLE_EVM: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_4)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_TVM_COMPATIBLE_EVM]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_TVM_COMPATIBLE_EVM] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_5)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_ASSET_OPTIMIZATION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_5)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_ASSET_OPTIMIZATION]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_ASSET_OPTIMIZATION] is only allowed to be 1");
+ }
+ break;
+ }
+ case ALLOW_NEW_REWARD: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_6)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_NEW_REWARD]");
+ }
+ if (dynamicPropertiesStore.allowNewReward()) {
+ throw new ContractValidateException(
+ "New reward has been valid.");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_NEW_REWARD] is only allowed to be 1");
+ }
+ break;
+ }
+ case MEMO_FEE: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_6)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [MEMO_FEE]");
+ }
+ if (value < 0 || value > 1_000_000_000) {
+ throw new ContractValidateException(
+ "This value[MEMO_FEE] is only allowed to be in the range 0-1000_000_000");
+ }
+ break;
+ }
+ case ALLOW_DELEGATE_OPTIMIZATION: {
+ if (!forkController.pass(ForkBlockVersionEnum.VERSION_4_6)) {
+ throw new ContractValidateException(
+ "Bad chain parameter id [ALLOW_DELEGATE_OPTIMIZATION]");
+ }
+ if (value != 1) {
+ throw new ContractValidateException(
+ "This value[ALLOW_DELEGATE_OPTIMIZATION] is only allowed to be 1");
+ }
+ 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;
}
@@ -349,17 +974,62 @@ public enum ProposalType { // current value, value range
ALLOW_PROTO_FILTER_NUM(24), // 0, {0, 1}
ALLOW_ACCOUNT_STATE_ROOT(25), // 1, {0, 1}
ALLOW_TVM_CONSTANTINOPLE(26), // 1, {0, 1}
- // ALLOW_SHIELDED_TRANSACTION(27), // 0, {0, 1}
- // SHIELDED_TRANSACTION_FEE(28), // 10 TRX, [0, 10000] TRX
+ // ALLOW_SHIELDED_TRANSACTION(27), // 0, {0, 1}
+ // SHIELDED_TRANSACTION_FEE(28), // 10 TRX, [0, 10000] TRX
ADAPTIVE_RESOURCE_LIMIT_MULTIPLIER(29), // 1000, [1, 10000]
ALLOW_CHANGE_DELEGATION(30), // 1, {0, 1}
WITNESS_127_PAY_PER_BLOCK(31), // 160 TRX, [0, 100000000000] TRX
ALLOW_TVM_SOLIDITY_059(32), // 1, {0, 1}
ADAPTIVE_RESOURCE_LIMIT_TARGET_RATIO(33), // 10, [1, 1000]
- // SHIELDED_TRANSACTION_CREATE_ACCOUNT_FEE(34), // 1 TRX, [0, 10000] TRX
+ // SHIELDED_TRANSACTION_CREATE_ACCOUNT_FEE(34), // 1 TRX, [0, 10000] TRX
FORBID_TRANSFER_TO_CONTRACT(35), // 1, {0, 1}
- ALLOW_PBFT(40);// 1,40
-
+ ALLOW_SHIELDED_TRC20_TRANSACTION(39), // 1, 39
+ ALLOW_PBFT(40),// 1,40
+ ALLOW_TVM_ISTANBUL(41),//1, {0,1}
+ // ALLOW_TVM_ASSET_ISSUE(42), // 0, 1
+ // ALLOW_TVM_STAKE(43), // 0, 1
+ ALLOW_MARKET_TRANSACTION(44), // {0, 1}
+ MARKET_SELL_FEE(45), // 0 [0,10_000_000_000]
+ MARKET_CANCEL_FEE(46), // 0 [0,10_000_000_000]
+ MAX_FEE_LIMIT(47), // [0, 100_000_000_000] TRX
+ ALLOW_TRANSACTION_FEE_POOL(48), // 0, 1
+ ALLOW_BLACKHOLE_OPTIMIZATION(49),// 0,1
+ ALLOW_NEW_RESOURCE_MODEL(51),// 0,1
+ ALLOW_TVM_FREEZE(52), // 0, 1
+ ALLOW_ACCOUNT_ASSET_OPTIMIZATION(53), // 1
+ // ALLOW_NEW_REWARD_ALGORITHM(58), // 0, 1
+ ALLOW_TVM_VOTE(59), // 0, 1
+ ALLOW_TVM_COMPATIBLE_EVM(60), // 0, 1
+ FREE_NET_LIMIT(61), // 5000, [0, 100_000]
+ TOTAL_NET_LIMIT(62), // 43_200_000_000L, [0, 1000_000_000_000L]
+ ALLOW_TVM_LONDON(63), // 0, 1
+ ALLOW_HIGHER_LIMIT_FOR_MAX_CPU_TIME_OF_ONE_TX(65), // 0, 1
+ ALLOW_ASSET_OPTIMIZATION(66), // 0, 1
+ ALLOW_NEW_REWARD(67), // 0, 1
+ MEMO_FEE(68), // 0, [0, 1000_000_000]
+ 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 9d96ffcb719..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 clazz : subTypes) {
- try {
- clazz.newInstance();
- } catch (Exception e) {
- logger.error("{} contract actuator register fail!", clazz, 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 92519ceb066..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;
@@ -24,6 +27,7 @@
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -40,52 +44,53 @@
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
public class TransactionUtil {
- private static final int maxAccountNameLen = 200;
- private static final int maxAccountIdLen = 32;
- private static final int minAccountIdLen = 8;
- private static final int maxAssetNameLen = 32;
- private static final int maxTokenAbbrNameLen = 5;
- private static final int maxAssetDescriptionLen = 200;
- private static final int maxUrlLen = 256;
+ private static final int MAX_ACCOUNT_NAME_LEN = 200;
+ private static final int MAX_ACCOUNT_ID_LEN = 32;
+ private static final int MIN_ACCOUNT_ID_LEN = 8;
+ private static final int MAX_ASSET_NAME_LEN = 32;
+ private static final int MAX_TOKEN_ABBR_NAME_LEN = 5;
+ private static final int MAX_ASSET_DESCRIPTION_LEN = 200;
+ private static final int MAX_URL_LEN = 256;
@Autowired
private ChainBaseManager chainBaseManager;
public static boolean validAccountName(byte[] accountName) {
- return validBytes(accountName, maxAccountNameLen, true);
+ return validBytes(accountName, MAX_ACCOUNT_NAME_LEN, true);
}
public static boolean validAssetDescription(byte[] description) {
- return validBytes(description, maxAssetDescriptionLen, true);
+ return validBytes(description, MAX_ASSET_DESCRIPTION_LEN, true);
}
public static boolean validUrl(byte[] url) {
- return validBytes(url, maxUrlLen, false);
+ return validBytes(url, MAX_URL_LEN, false);
}
public static boolean validAccountId(byte[] accountId) {
- return validReadableBytes(accountId, maxAccountIdLen) && accountId.length >= minAccountIdLen;
+ return validReadableBytes(accountId, MAX_ACCOUNT_ID_LEN) && accountId.length >= MIN_ACCOUNT_ID_LEN;
}
public static boolean validAssetName(byte[] assetName) {
- return validReadableBytes(assetName, maxAssetNameLen);
+ return validReadableBytes(assetName, MAX_ASSET_NAME_LEN);
}
public static boolean validTokenAbbrName(byte[] abbrName) {
- return validReadableBytes(abbrName, maxTokenAbbrNameLen);
+ return validReadableBytes(abbrName, MAX_TOKEN_ABBR_NAME_LEN);
}
private static boolean validBytes(byte[] bytes, int maxLength, boolean allowEmpty) {
@@ -111,19 +116,6 @@ private static boolean validReadableBytes(byte[] bytes, int maxLength) {
return true;
}
- public static boolean isNumber(byte[] id) {
- if (ArrayUtils.isEmpty(id)) {
- return false;
- }
- for (byte b : id) {
- if (b < '0' || b > '9') {
- return false;
- }
- }
-
- return !(id.length > 1 && id[0] == '0');
- }
-
public static Sha256Hash getTransactionId(Transaction transaction) {
return Sha256Hash.of(CommonParameter.getInstance().isECKeyCryptoEngine(),
transaction.getRawData().toByteArray());
@@ -191,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();
@@ -217,56 +194,83 @@ public TransactionSignWeight getTransactionSignWeight(Transaction trx) {
trxExBuilder.setResult(retBuilder);
tswBuilder.setTransaction(trxExBuilder);
Result.Builder resultBuilder = Result.newBuilder();
- try {
- Contract contract = trx.getRawData().getContract(0);
- byte[] owner = TransactionCapsule.getOwner(contract);
- AccountCapsule account = chainBaseManager.getAccountStore().get(owner);
- if (account == null) {
- throw new PermissionException("Account does not exist!");
- }
- int permissionId = contract.getPermissionId();
- Permission permission = account.getPermissionById(permissionId);
- if (permission == null) {
- throw new PermissionException("Permission for this, does not exist!");
- }
- if (permissionId != 0) {
- if (permission.getType() != PermissionType.Active) {
- throw new PermissionException("Permission type is wrong!");
+
+ if (trx.getRawData().getContractCount() == 0) {
+ resultBuilder.setCode(Result.response_code.OTHER_ERROR);
+ resultBuilder.setMessage("Invalid transaction: no valid contract");
+ } else {
+ try {
+ Contract contract = trx.getRawData().getContract(0);
+ byte[] owner = TransactionCapsule.getOwner(contract);
+ AccountCapsule account = chainBaseManager.getAccountStore().get(owner);
+ if (Objects.isNull(account)) {
+ throw new PermissionException("Account does not exist!");
}
- //check operations
- if (!checkPermissionOperations(permission, contract)) {
- throw new PermissionException("Permission denied!");
+ int permissionId = contract.getPermissionId();
+ Permission permission = account.getPermissionById(permissionId);
+ if (permission == null) {
+ throw new PermissionException("Permission for this, does not exist!");
}
+ if (permissionId != 0) {
+ if (permission.getType() != PermissionType.Active) {
+ throw new PermissionException("Permission type is wrong!");
+ }
+ //check operations
+ if (!checkPermissionOperations(permission, contract)) {
+ throw new PermissionException("Permission denied!");
+ }
+ }
+ tswBuilder.setPermission(permission);
+ if (trx.getSignatureCount() > 0) {
+ List approveList = new ArrayList<>();
+ long currentWeight = TransactionCapsule.checkWeight(permission, trx.getSignatureList(),
+ Sha256Hash.hash(CommonParameter.getInstance()
+ .isECKeyCryptoEngine(), trx.getRawData().toByteArray()), approveList);
+ tswBuilder.addAllApprovedList(approveList);
+ tswBuilder.setCurrentWeight(currentWeight);
+ }
+ if (tswBuilder.getCurrentWeight() >= permission.getThreshold()) {
+ resultBuilder.setCode(Result.response_code.ENOUGH_PERMISSION);
+ } else {
+ resultBuilder.setCode(Result.response_code.NOT_ENOUGH_PERMISSION);
+ }
+ } catch (SignatureFormatException signEx) {
+ resultBuilder.setCode(Result.response_code.SIGNATURE_FORMAT_ERROR);
+ resultBuilder.setMessage(signEx.getMessage());
+ } catch (SignatureException signEx) {
+ resultBuilder.setCode(Result.response_code.COMPUTE_ADDRESS_ERROR);
+ resultBuilder.setMessage(signEx.getMessage());
+ } catch (PermissionException permEx) {
+ resultBuilder.setCode(Result.response_code.PERMISSION_ERROR);
+ resultBuilder.setMessage(permEx.getMessage());
+ } catch (Exception ex) {
+ resultBuilder.setCode(Result.response_code.OTHER_ERROR);
+ resultBuilder.setMessage(ex.getClass() + " : " + ex.getMessage());
}
- tswBuilder.setPermission(permission);
- if (trx.getSignatureCount() > 0) {
- List approveList = new ArrayList();
- long currentWeight = TransactionCapsule.checkWeight(permission, trx.getSignatureList(),
- Sha256Hash.hash(CommonParameter.getInstance()
- .isECKeyCryptoEngine(), trx.getRawData().toByteArray()), approveList);
- tswBuilder.addAllApprovedList(approveList);
- tswBuilder.setCurrentWeight(currentWeight);
- }
- if (tswBuilder.getCurrentWeight() >= permission.getThreshold()) {
- resultBuilder.setCode(Result.response_code.ENOUGH_PERMISSION);
- } else {
- resultBuilder.setCode(Result.response_code.NOT_ENOUGH_PERMISSION);
- }
- } catch (SignatureFormatException signEx) {
- resultBuilder.setCode(Result.response_code.SIGNATURE_FORMAT_ERROR);
- resultBuilder.setMessage(signEx.getMessage());
- } catch (SignatureException signEx) {
- resultBuilder.setCode(Result.response_code.COMPUTE_ADDRESS_ERROR);
- resultBuilder.setMessage(signEx.getMessage());
- } catch (PermissionException permEx) {
- resultBuilder.setCode(Result.response_code.PERMISSION_ERROR);
- resultBuilder.setMessage(permEx.getMessage());
- } catch (Exception ex) {
- resultBuilder.setCode(Result.response_code.OTHER_ERROR);
- resultBuilder.setMessage(ex.getClass() + " : " + ex.getMessage());
}
+
tswBuilder.setResult(resultBuilder);
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 b8768a999d6..3641548b3e5 100644
--- a/actuator/src/main/java/org/tron/core/vm/EnergyCost.java
+++ b/actuator/src/main/java/org/tron/core/vm/EnergyCost.java
@@ -1,282 +1,581 @@
package org.tron.core.vm;
+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;
-public class EnergyCost {
+import static org.tron.core.Constant.DYNAMIC_ENERGY_FACTOR_DECIMAL;
- private static EnergyCost instance = null;
- /* backwards compatibility, remove eventually */
- private final int STEP = 1;
- /* backwards compatibility, remove eventually */
- private final int SSTORE = 300;
- private final int ZEROSTEP = 0;
- private final int QUICKSTEP = 2;
- private final int FASTESTSTEP = 3;
- private final int FASTSTEP = 5;
- private final int MIDSTEP = 8;
- private final int SLOWSTEP = 10;
- private final int EXTSTEP = 20;
- private final int GENESISENERGYLIMIT = 1000000;
- private final int MINENERGYLIMIT = 125000;
- private final int BALANCE = 20;
- private final int SHA3 = 30;
- private final int SHA3_WORD = 6;
- private final int SLOAD = 50;
- private final int STOP = 0;
- private final int SUICIDE = 0;
- private final int CLEAR_SSTORE = 5000;
- private final int SET_SSTORE = 20000;
- private final int RESET_SSTORE = 5000;
- private final int REFUND_SSTORE = 15000;
- private final int CREATE = 32000;
- private final int JUMPDEST = 1;
- private final int CREATE_DATA_BYTE = 5;
- private final int CALL = 40;
- private final int STIPEND_CALL = 2300;
- private final int VT_CALL = 9000; //value transfer call
- private final int NEW_ACCT_CALL = 25000; //new account call
- private final int MEMORY = 3;
- private final int SUICIDE_REFUND = 24000;
- private final int QUAD_COEFF_DIV = 512;
- private final int CREATE_DATA = 200;
- private final int TX_NO_ZERO_DATA = 68;
- private final int TX_ZERO_DATA = 4;
- private final int TRANSACTION = 21000;
- private final int TRANSACTION_CREATE_CONTRACT = 53000;
- private final int LOG_ENERGY = 375;
- private final int LOG_DATA_ENERGY = 8;
- private final int LOG_TOPIC_ENERGY = 375;
- private final int COPY_ENERGY = 3;
- private final int EXP_ENERGY = 10;
- private final int EXP_BYTE_ENERGY = 10;
- private final int IDENTITY = 15;
- private final int IDENTITY_WORD = 3;
- private final int RIPEMD160 = 600;
- private final int RIPEMD160_WORD = 120;
- private final int SHA256 = 60;
- private final int SHA256_WORD = 12;
- private final int EC_RECOVER = 3000;
- private final int EXT_CODE_SIZE = 20;
- private final int EXT_CODE_COPY = 20;
- private final int EXT_CODE_HASH = 400;
- private final int NEW_ACCT_SUICIDE = 0;
-
- public static EnergyCost getInstance() {
- if (instance == null) {
- instance = new EnergyCost();
- }
+public class EnergyCost {
- return instance;
+ private static final long ZERO_TIER = 0;
+ private static final long BASE_TIER = 2;
+ private static final long VERY_LOW_TIER = 3;
+ private static final long LOW_TIER = 5;
+ private static final long MID_TIER = 8;
+ private static final long HIGH_TIER = 10;
+ private static final long EXT_TIER = 20;
+ private static final long SPECIAL_TIER = 1;
+
+ private static final long EXP_ENERGY = 10;
+ private static final long EXP_BYTE_ENERGY = 10;
+ private static final long SHA3 = 30;
+ // 3MB
+ private static final BigInteger MEM_LIMIT = BigInteger.valueOf(3L * 1024 * 1024);
+ private static final long MEMORY = 3;
+ private static final long COPY_ENERGY = 3;
+ private static final long SHA3_WORD = 6;
+ private static final long SLOAD = 50;
+ private static final long CLEAR_SSTORE = 5000;
+ private static final long SET_SSTORE = 20000;
+ private static final long RESET_SSTORE = 5000;
+ private static final long LOG_DATA_ENERGY = 8;
+ private static final long LOG_ENERGY = 375;
+ private static final long LOG_TOPIC_ENERGY = 375;
+ private static final long BALANCE = 20;
+ private static final long FREEZE = 20000;
+ 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;
+ private static final long CALL_ENERGY = 40;
+ private static final long VT_CALL = 9000;
+ private static final long STIPEND_CALL = 2300;
+ private static final long EXT_CODE_COPY = 20;
+ 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;
+ }
+
+ public static long getVeryLowTierCost(Program ignored) {
+ return VERY_LOW_TIER;
+ }
+
+ public static long getLowTierCost(Program ignored) {
+ return LOW_TIER;
+ }
+
+ public static long getMidTierCost(Program ignored) {
+ return MID_TIER;
+ }
+
+ public static long getBaseTierCost(Program ignored) {
+ return BASE_TIER;
+ }
+
+ public static long getExtTierCost(Program ignored) {
+ return EXT_TIER;
+ }
+
+ public static long getHighTierCost(Program ignored) {
+ return HIGH_TIER;
+ }
+
+ public static long getSpecialTierCost(Program ignored) {
+ return SPECIAL_TIER;
+ }
+
+ public static long getStipendCallCost() {
+ return STIPEND_CALL;
}
- public int getSTEP() {
- return STEP;
+ public static long getExpCost(Program program) {
+ Stack stack = program.getStack();
+ DataWord exp = stack.get(stack.size() - 2);
+ int bytesOccupied = exp.bytesOccupied();
+ return EXP_ENERGY + EXP_BYTE_ENERGY * bytesOccupied;
}
- public int getSSTORE() {
- return SSTORE;
+ public static long getExtCodeSizeCost(Program ignored) {
+ return EXT_CODE_SIZE;
}
- public int getZEROSTEP() {
- return ZEROSTEP;
+ public static long getSha3Cost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ long energyCost = SHA3 + calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), stack.get(stack.size() - 2)), 0, Op.SHA3);
+ DataWord size = stack.get(stack.size() - 2);
+ long chunkUsed = (size.longValueSafe() + 31) / 32;
+ energyCost += chunkUsed * SHA3_WORD;
+ return energyCost;
}
- public int getQUICKSTEP() {
- return QUICKSTEP;
+ public static long getCodeCopyCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ long energyCost = calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), stack.get(stack.size() - 3)),
+ stack.get(stack.size() - 3).longValueSafe(), Op.CODECOPY);
+ return energyCost;
}
- public int getFASTESTSTEP() {
- return FASTESTSTEP;
+ public static long getReturnDataCopyCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ long energyCost = calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), stack.get(stack.size() - 3)),
+ stack.get(stack.size() - 3).longValueSafe(), Op.RETURNDATACOPY);
+ return energyCost;
}
- public int getFASTSTEP() {
- return FASTSTEP;
+ public static long getCallDataCopyCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ long energyCost = calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), stack.get(stack.size() - 3)),
+ stack.get(stack.size() - 3).longValueSafe(), Op.CALLDATACOPY);
+ return energyCost;
}
- public int getMIDSTEP() {
- return MIDSTEP;
+ public static long getExtCodeCopyCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ long energyCost = EXT_CODE_COPY + calcMemEnergy(oldMemSize,
+ memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 4)),
+ stack.get(stack.size() - 4).longValueSafe(), Op.EXTCODECOPY);
+ return energyCost;
}
- public int getSLOWSTEP() {
- return SLOWSTEP;
+ public static long getExtCodeHashCost(Program ignored) {
+ return EXT_CODE_HASH;
}
- public int getEXTSTEP() {
- return EXTSTEP;
+ public static long getMloadCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ return calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), new DataWord(32)),
+ 0, Op.MLOAD);
}
- public int getGENESISENERGYLIMIT() {
- return GENESISENERGYLIMIT;
+ public static long getMloadCost2(Program program) {
+ return SPECIAL_TIER + getMloadCost(program);
}
- public int getMINENERGYLIMIT() {
- return MINENERGYLIMIT;
+ public static long getMStoreCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ return calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), new DataWord(32)),
+ 0, Op.MSTORE);
}
- public int getBALANCE() {
- return BALANCE;
+ public static long getMStoreCost2(Program program) {
+ return SPECIAL_TIER + getMStoreCost(program);
}
- public int getSHA3() {
- return SHA3;
+ public static long getMStore8Cost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ return calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), DataWord.ONE()),
+ 0, Op.MSTORE8);
}
- public int getSHA3_WORD() {
- return SHA3_WORD;
+ public static long getMStore8Cost2(Program program) {
+ return SPECIAL_TIER + getMStore8Cost(program);
}
- public int getSLOAD() {
+ public static long getSloadCost(Program ignored) {
return SLOAD;
}
- public int getSTOP() {
- return STOP;
+ public static long getReturnCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ long energyCost = STOP + calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), stack.get(stack.size() - 2)), 0, Op.RETURN);
+ return energyCost;
}
- public int getSUICIDE() {
- return SUICIDE;
+ public static long getRevertCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ long energyCost = STOP + calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), stack.get(stack.size() - 2)), 0, Op.REVERT);
+ return energyCost;
}
- public int getCLEAR_SSTORE() {
- return CLEAR_SSTORE;
- }
+ public static long getSstoreCost(Program program) {
+ Stack stack = program.getStack();
+ DataWord newValue = stack.get(stack.size() - 2);
+ DataWord oldValue = program.storageLoad(stack.peek());
+
+ if (oldValue == null && !newValue.isZero()) {
+ // set a new not-zero value
+ return SET_SSTORE;
+ }
+ if (oldValue != null && newValue.isZero()) {
+ // set zero to an old value
+ return CLEAR_SSTORE;
+ }
+ // include:
+ // [1] oldValue == null && newValue == 0
+ // [2] oldValue != null && newValue != 0
+ return RESET_SSTORE;
- public int getSET_SSTORE() {
- return SET_SSTORE;
}
- public int getRESET_SSTORE() {
- return RESET_SSTORE;
+ public static long getTLoadCost(Program ignored) {
+ return TLOAD;
}
- public int getREFUND_SSTORE() {
- return REFUND_SSTORE;
+ public static long getTStoreCost(Program ignored) {
+ return TSTORE;
}
- public int getCREATE() {
- return CREATE;
+ 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 int getJUMPDEST() {
- return JUMPDEST;
+ public static long getLogCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ int opIntValue = program.getCurrentOpIntValue();
+ int nTopics = opIntValue - Op.LOG0;
+ BigInteger dataSize = stack.get(stack.size() - 2).value();
+ BigInteger dataCost = dataSize
+ .multiply(BigInteger.valueOf(LOG_DATA_ENERGY));
+ if (program.getEnergyLimitLeft().value().compareTo(dataCost) < 0) {
+ throw new Program.OutOfEnergyException(
+ "Not enough energy for '%s' operation executing: opEnergy[%d], programEnergy[%d]",
+ Op.getNameOf(opIntValue),
+ dataCost.longValueExact(), program.getEnergyLimitLeft().longValueSafe());
+ }
+ long energyCost = LOG_ENERGY + LOG_TOPIC_ENERGY * nTopics
+ + LOG_DATA_ENERGY * stack.get(stack.size() - 2).longValue()
+ + calcMemEnergy(oldMemSize,
+ memNeeded(stack.peek(), stack.get(stack.size() - 2)), 0, opIntValue);
+
+ checkMemorySize(opIntValue, memNeeded(stack.peek(), stack.get(stack.size() - 2)));
+ return energyCost;
}
- public int getCREATE_DATA_BYTE() {
- return CREATE_DATA_BYTE;
+ public static long getSuicideCost(Program ignored) {
+ return SUICIDE;
}
- public int getCALL() {
- return CALL;
+ 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 int getSTIPEND_CALL() {
- return STIPEND_CALL;
+ 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 int getVT_CALL() {
- return VT_CALL;
+ public static long getBalanceCost(Program ignored) {
+ return BALANCE;
}
- public int getNEW_ACCT_CALL() {
- return NEW_ACCT_CALL;
+ public static long getFreezeCost(Program program) {
+ Stack stack = program.getStack();
+ DataWord receiverAddressWord = stack.get(stack.size() - 3);
+ if (isDeadAccount(program, receiverAddressWord)) {
+ return FREEZE + NEW_ACCT_CALL;
+ }
+ return FREEZE;
}
- public int getNEW_ACCT_SUICIDE() {
- return NEW_ACCT_SUICIDE;
+ public static long getUnfreezeCost(Program ignored) {
+ return UNFREEZE;
}
- public int getMEMORY() {
- return MEMORY;
+ public static long getFreezeExpireTimeCost(Program ignored) {
+ return FREEZE_EXPIRE_TIME;
}
- public int getSUICIDE_REFUND() {
- return SUICIDE_REFUND;
+ public static long getFreezeBalanceV2Cost(Program ignored) {
+ return FREEZE_V2;
}
- public int getQUAD_COEFF_DIV() {
- return QUAD_COEFF_DIV;
+ public static long getUnfreezeBalanceV2Cost(Program ignored) {
+ return UNFREEZE_V2;
}
- public int getCREATE_DATA() {
- return CREATE_DATA;
+ public static long getWithdrawExpireUnfreezeCost(Program ignored) {
+ return WITHDRAW_EXPIRE_UNFREEZE;
}
- public int getTX_NO_ZERO_DATA() {
- return TX_NO_ZERO_DATA;
+ public static long getCancelAllUnfreezeV2Cost(Program ignored) {
+ return CANCEL_ALL_UNFREEZE_V2;
}
- public int getTX_ZERO_DATA() {
- return TX_ZERO_DATA;
+ public static long getDelegateResourceCost(Program ignored) {
+ return DELEGATE_RESOURCE;
}
- public int getTRANSACTION() {
- return TRANSACTION;
+ public static long getUnDelegateResourceCost(Program ignored) {
+ return UN_DELEGATE_RESOURCE;
}
- public int getTRANSACTION_CREATE_CONTRACT() {
- return TRANSACTION_CREATE_CONTRACT;
+ 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 int getLOG_ENERGY() {
- return LOG_ENERGY;
+ public static long getVoteWitnessCost2(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);
+ 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,
+ (amountArrayMemoryNeeded.compareTo(witnessArrayMemoryNeeded) > 0
+ ? amountArrayMemoryNeeded : witnessArrayMemoryNeeded), 0, Op.VOTEWITNESS);
}
- public int getLOG_DATA_ENERGY() {
- return LOG_DATA_ENERGY;
+ 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 int getLOG_TOPIC_ENERGY() {
- return LOG_TOPIC_ENERGY;
+ public static long getWithdrawRewardCost(Program ignored) {
+ return WITHDRAW_REWARD;
}
- public int getCOPY_ENERGY() {
- return COPY_ENERGY;
+ public static long getCreateCost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ return CREATE + calcMemEnergy(oldMemSize,
+ memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 3)), 0, Op.CREATE);
}
- public int getEXP_ENERGY() {
- return EXP_ENERGY;
+ public static long getCreate2Cost(Program program) {
+ Stack stack = program.getStack();
+ long oldMemSize = program.getMemSize();
+ DataWord codeSize = stack.get(stack.size() - 3);
+ long energyCost = CREATE;
+ energyCost += calcMemEnergy(oldMemSize,
+ memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 3)),
+ 0, Op.CREATE2);
+ return energyCost + DataWord.sizeInWords(codeSize.intValueSafe()) * SHA3_WORD;
}
- public int getEXP_BYTE_ENERGY() {
- return EXP_BYTE_ENERGY;
+ public static long getCallCost(Program program) {
+ Stack stack = program.getStack();
+ // here, contract call an other contract, or a library, and so on
+ long energyCost = CALL_ENERGY;
+ DataWord callAddressWord = stack.get(stack.size() - 2);
+ DataWord value = stack.get(stack.size() - 3);
+ int opOff = 4;
+ //check to see if account does not exist and is not a precompiled contract
+ if (!value.isZero()) {
+ energyCost += VT_CALL;
+ if (isDeadAccount(program, callAddressWord)) {
+ energyCost += NEW_ACCT_CALL;
+ }
+ }
+ return getCalculateCallCost(stack, program, energyCost, opOff);
}
- public int getIDENTITY() {
- return IDENTITY;
+ public static long getStaticCallCost(Program program) {
+ Stack stack = program.getStack();
+ long energyCost = CALL_ENERGY;
+ int opOff = 3;
+ return getCalculateCallCost(stack, program, energyCost, opOff);
}
- public int getIDENTITY_WORD() {
- return IDENTITY_WORD;
+ public static long getDelegateCallCost(Program program) {
+ Stack stack = program.getStack();
+ long energyCost = CALL_ENERGY;
+ int opOff = 3;
+ return getCalculateCallCost(stack, program, energyCost, opOff);
}
- public int getRIPEMD160() {
- return RIPEMD160;
+ public static long getCallCodeCost(Program program) {
+ Stack stack = program.getStack();
+ long energyCost = CALL_ENERGY;
+ DataWord value = stack.get(stack.size() - 3);
+ int opOff = 4;
+ if (!value.isZero()) {
+ energyCost += VT_CALL;
+ }
+ return getCalculateCallCost(stack, program, energyCost, opOff);
+ }
+
+ public static long getCallTokenCost(Program program) {
+ Stack stack = program.getStack();
+ long energyCost = CALL_ENERGY;
+ DataWord callAddressWord = stack.get(stack.size() - 2);
+ DataWord value = stack.get(stack.size() - 3);
+ int opOff = 5;
+ //check to see if account does not exist and is not a precompiled contract
+ if (!value.isZero()) {
+ energyCost += VT_CALL;
+ if (isDeadAccount(program, callAddressWord)) {
+ energyCost += NEW_ACCT_CALL;
+ }
+ }
+ return getCalculateCallCost(stack, program, energyCost, opOff);
+ }
+
+ public static long getCalculateCallCost(Stack stack, Program program,
+ long energyCost, int opOff) {
+ int op = program.getCurrentOpIntValue();
+ long oldMemSize = program.getMemSize();
+ DataWord callEnergyWord = stack.get(stack.size() - 1);
+ // in offset+size
+ BigInteger in = memNeeded(stack.get(stack.size() - opOff),
+ stack.get(stack.size() - opOff - 1));
+ // out offset+size
+ BigInteger out = memNeeded(stack.get(stack.size() - opOff - 2),
+ stack.get(stack.size() - opOff - 3));
+ 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]",
+ Op.getNameOf(op),
+ energyCost, program.getEnergyLimitLeft().longValueSafe());
+ }
+ DataWord getEnergyLimitLeft = program.getEnergyLimitLeft().clone();
+ getEnergyLimitLeft.sub(new DataWord(energyCost));
+
+ DataWord adjustedCallEnergy = program.getCallEnergy(callEnergyWord, getEnergyLimitLeft);
+ program.setAdjustedCallEnergy(adjustedCallEnergy);
+ energyCost += adjustedCallEnergy.longValueSafe();
+ return energyCost;
}
- public int getRIPEMD160_WORD() {
- return RIPEMD160_WORD;
+ public static long getNewAcctCall() {
+ return NEW_ACCT_CALL;
}
- public int getSHA256() {
- return SHA256;
+ public static long getCreateData() {
+ return CREATE_DATA;
}
- public int getSHA256_WORD() {
- return SHA256_WORD;
+
+ private static long calcMemEnergy(long oldMemSize, BigInteger newMemSize,
+ long copySize, int op) {
+ long energyCost = 0;
+
+ checkMemorySize(op, newMemSize);
+
+ // memory SUN consume calc
+ long memoryUsage = (newMemSize.longValueExact() + 31) / 32 * 32;
+ if (memoryUsage > oldMemSize) {
+ long memWords = (memoryUsage / 32);
+ long memWordsOld = (oldMemSize / 32);
+ long memEnergy = (MEMORY * memWords + memWords * memWords / 512)
+ - (MEMORY * memWordsOld + memWordsOld * memWordsOld / 512);
+ energyCost += memEnergy;
+ }
+
+ if (copySize > 0) {
+ long copyEnergy = COPY_ENERGY * ((copySize + 31) / 32);
+ energyCost += copyEnergy;
+ }
+ return energyCost;
}
- public int getEC_RECOVER() {
- return EC_RECOVER;
+ private static void checkMemorySize(int op, BigInteger newMemSize) {
+ if (newMemSize.compareTo(MEM_LIMIT) > 0) {
+ throw Program.Exception.memoryOverflow(op);
+ }
}
- public int getEXT_CODE_SIZE() {
- return EXT_CODE_SIZE;
+ private static BigInteger memNeeded(DataWord offset, DataWord size) {
+ return size.isZero() ? BigInteger.ZERO : offset.value().add(size.value());
}
- public int getEXT_CODE_COPY() {
- return EXT_CODE_COPY;
+ private static BigInteger memNeeded(BigInteger offset, BigInteger size) {
+ return size.equals(BigInteger.ZERO) ? BigInteger.ZERO : offset.add(size);
}
- public int getEXT_CODE_HASH() {
- return EXT_CODE_HASH;
+ 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/JumpTable.java b/actuator/src/main/java/org/tron/core/vm/JumpTable.java
new file mode 100644
index 00000000000..3a6625ac15e
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/JumpTable.java
@@ -0,0 +1,28 @@
+package org.tron.core.vm;
+
+import java.util.Arrays;
+
+public class JumpTable {
+
+ private static final Operation UNDEFINED =
+ new Operation(
+ -1, 0, 0,
+ p -> 0L,
+ p -> { },
+ () -> false);
+
+ private final Operation[] table = new Operation[256];
+
+ public JumpTable() {
+ // fill all op slots to undefined
+ Arrays.fill(table, UNDEFINED);
+ }
+
+ public Operation get(int op) {
+ return table[op];
+ }
+
+ public void set(Operation op) {
+ table[op.getOpcode()] = op;
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/LogInfoTriggerParser.java b/actuator/src/main/java/org/tron/core/vm/LogInfoTriggerParser.java
index 7e8f11536bd..7d2f5db953f 100644
--- a/actuator/src/main/java/org/tron/core/vm/LogInfoTriggerParser.java
+++ b/actuator/src/main/java/org/tron/core/vm/LogInfoTriggerParser.java
@@ -7,15 +7,15 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
-import org.spongycastle.util.encoders.Hex;
+import org.bouncycastle.util.encoders.Hex;
import org.tron.common.logsfilter.trigger.ContractTrigger;
import org.tron.common.runtime.vm.LogInfo;
import org.tron.common.utils.StringUtil;
-import org.tron.common.utils.WalletUtil;
+import org.tron.core.capsule.AbiCapsule;
import org.tron.core.capsule.ContractCapsule;
import org.tron.core.db.TransactionTrace;
+import org.tron.core.store.StoreFactory;
import org.tron.core.vm.repository.Repository;
-import org.tron.core.vm.utils.MUtil;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract.ABI;
@Slf4j
@@ -76,9 +76,17 @@ public List parseLogInfos(List logInfos, Repository de
abiMap.put(strContractAddr, ABI.getDefaultInstance());
continue;
}
- ABI abi = contract.getInstance().getAbi();
+ AbiCapsule abiCapsule = StoreFactory.getInstance().getChainBaseManager()
+ .getAbiStore().get(contractAddress);
+ ABI abi;
+ if (abiCapsule == null || abiCapsule.getInstance() == null) {
+ abi = ABI.getDefaultInstance();
+ } else {
+ abi = abiCapsule.getInstance();
+ }
String creatorAddr = StringUtil.encode58Check(
- TransactionTrace.convertToTronAddress(contract.getInstance().getOriginAddress().toByteArray()));
+ TransactionTrace
+ .convertToTronAddress(contract.getInstance().getOriginAddress().toByteArray()));
addrMap.put(strContractAddr, creatorAddr);
abiMap.put(strContractAddr, abi);
}
diff --git a/actuator/src/main/java/org/tron/core/vm/MessageCall.java b/actuator/src/main/java/org/tron/core/vm/MessageCall.java
index e4897f76e47..ca68cba56dd 100644
--- a/actuator/src/main/java/org/tron/core/vm/MessageCall.java
+++ b/actuator/src/main/java/org/tron/core/vm/MessageCall.java
@@ -1,20 +1,3 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm;
import org.tron.common.runtime.vm.DataWord;
@@ -28,7 +11,7 @@ public class MessageCall {
/**
* Type of internal call. Either CALL, CALLCODE or POST
*/
- private final OpCode type;
+ private final int opCode;
/**
* energy to pay for the call, remaining energy will be refunded to the caller
@@ -63,10 +46,10 @@ public class MessageCall {
private boolean isTokenTransferMsg;
- public MessageCall(OpCode type, DataWord energy, DataWord codeAddress,
+ public MessageCall(int opCode, DataWord energy, DataWord codeAddress,
DataWord endowment, DataWord inDataOffs, DataWord inDataSize, DataWord tokenId,
boolean isTokenTransferMsg) {
- this.type = type;
+ this.opCode = opCode;
this.energy = energy;
this.codeAddress = codeAddress;
this.endowment = endowment;
@@ -76,16 +59,16 @@ public MessageCall(OpCode type, DataWord energy, DataWord codeAddress,
this.isTokenTransferMsg = isTokenTransferMsg;
}
- public MessageCall(OpCode type, DataWord energy, DataWord codeAddress,
+ public MessageCall(int opCode, DataWord energy, DataWord codeAddress,
DataWord endowment, DataWord inDataOffs, DataWord inDataSize,
DataWord outDataOffs, DataWord outDataSize, DataWord tokenId, boolean isTokenTransferMsg) {
- this(type, energy, codeAddress, endowment, inDataOffs, inDataSize, tokenId, isTokenTransferMsg);
+ this(opCode, energy, codeAddress, endowment, inDataOffs, inDataSize, tokenId, isTokenTransferMsg);
this.outDataOffs = outDataOffs;
this.outDataSize = outDataSize;
}
- public OpCode getType() {
- return type;
+ public int getOpCode() {
+ return opCode;
}
public DataWord getEnergy() {
diff --git a/actuator/src/main/java/org/tron/core/vm/Op.java b/actuator/src/main/java/org/tron/core/vm/Op.java
new file mode 100644
index 00000000000..0a3fcc1dae3
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/Op.java
@@ -0,0 +1,311 @@
+package org.tron.core.vm;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j(topic = "VM")
+public class Op {
+
+ // Halts execution (0x00)
+ public static final int STOP = 0x00;
+
+ /* Arithmetic Operations */
+ // (0x01) Addition operation
+ public static final int ADD = 0x01;
+ // (0x02) Multiplication operation
+ public static final int MUL = 0x02;
+ // (0x03) Subtraction operations
+ public static final int SUB = 0x03;
+ // (0x04) Integer division operation
+ public static final int DIV = 0x04;
+ // (0x05) Signed integer division operation
+ public static final int SDIV = 0x05;
+ // (0x06) Modulo remainder operation
+ public static final int MOD = 0x06;
+ // (0x07) Signed modulo remainder operation
+ public static final int SMOD = 0x07;
+ // (0x08) Addition combined with modulo remainder operation
+ public static final int ADDMOD = 0x08;
+ // (0x09) Multiplication combined with modulo remainder operation
+ public static final int MULMOD = 0x09;
+ // (0x0a) Exponential operation
+ public static final int EXP = 0x0a;
+ // (0x0b) Extend length of signed integer
+ public static final int SIGNEXTEND = 0x0b;
+
+ /* Bitwise Logic & Comparison Operations */
+ // (0x10) Less-than comparison
+ public static final int LT = 0X10;
+ // (0x11) Greater-than comparison
+ public static final int GT = 0X11;
+ // (0x12) Signed less-than comparison
+ public static final int SLT = 0X12;
+ // (0x13) Signed greater-than comparison
+ public static final int SGT = 0X13;
+ // (0x14) Equality comparison
+ public static final int EQ = 0X14;
+ // (0x15) Negation operation
+ public static final int ISZERO = 0x15;
+ // (0x16) Bitwise AND operation
+ public static final int AND = 0x16;
+ // (0x17) Bitwise OR operation
+ public static final int OR = 0x17;
+ // (0x18) Bitwise XOR operation
+ public static final int XOR = 0x18;
+ // (0x19) Bitwise NOT operation
+ public static final int NOT = 0x19;
+ // (0x1a) Retrieve single byte from word
+ public static final int BYTE = 0x1a;
+ // (0x1b) Shift left
+ public static final int SHL = 0x1b;
+ // (0x1c) Logical shift right
+ 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
+ public static final int SHA3 = 0x20;
+
+ /* Environmental Information */
+ // (0x30) Get address of currently executing account
+ public static final int ADDRESS = 0x30;
+ // (0x31) Get balance of the given account
+ public static final int BALANCE = 0x31;
+ // (0x32) Get execution origination address
+ public static final int ORIGIN = 0x32;
+ // (0x33) Get caller address
+ public static final int CALLER = 0x33;
+ // (0x34) Get deposited value by the instruction/transaction responsible for this execution
+ public static final int CALLVALUE = 0x34;
+ // (0x35) Get input data of current environment
+ public static final int CALLDATALOAD = 0x35;
+ // (0x36) Get size of input data in current environment
+ public static final int CALLDATASIZE = 0x36;
+ // (0x37) Copy input data in current environment to memory
+ public static final int CALLDATACOPY = 0x37;
+ // (0x38) Get size of code running in current environment
+ public static final int CODESIZE = 0x38;
+ // (0x39) Copy code running in current environment to memory
+ public static final int CODECOPY = 0x39;
+ public static final int RETURNDATASIZE = 0x3d;
+ public static final int RETURNDATACOPY = 0x3e;
+ // (0x3a) Get price of gas in current environment
+ public static final int GASPRICE = 0x3a;
+ // (0x3b) Get size of code running in current environment with given offset
+ public static final int EXTCODESIZE = 0x3b;
+ // (0x3c) Copy code running in current environment to memory with given offset
+ public static final int EXTCODECOPY = 0x3c;
+ // (0x3f) Returns the keccak256 hash of a contract’s code
+ public static final int EXTCODEHASH = 0x3f;
+
+ /* Block Information */
+ // (0x40) Get hash of most recent complete block
+ public static final int BLOCKHASH = 0x40;
+ // (0x41) Get the block’s coinbase address
+ public static final int COINBASE = 0x41;
+ // (x042) Get the block’s timestamp
+ public static final int TIMESTAMP = 0x42;
+ // (0x43) Get the block’s number
+ public static final int NUMBER = 0x43;
+ // (0x44) Get the block’s difficulty
+ public static final int DIFFICULTY = 0x44;
+ // (0x45) Get the block’s gas limit
+ public static final int GASLIMIT = 0x45;
+ // (0x46) Get the chain id
+ public static final int CHAINID = 0x46;
+ // (0x47) Get current account balance
+ 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
+ public static final int POP = 0x50;
+ // (0x51) Load word from memory
+ public static final int MLOAD = 0x51;
+ // (0x52) Save word to memory
+ public static final int MSTORE = 0x52;
+ // (0x53) Save byte to memory
+ public static final int MSTORE8 = 0x53;
+ // (0x54) Load word from storage
+ public static final int SLOAD = 0x54;
+ // (0x55) Save word to storage
+ public static final int SSTORE = 0x55;
+ // (0x56) Alter the program counter
+ public static final int JUMP = 0x56;
+ // (0x57) Conditionally alter the program counter
+ public static final int JUMPI = 0x57;
+ // (0x58) Get the program counter
+ public static final int PC = 0x58;
+ // (0x59) Get the size of active memory
+ public static final int MSIZE = 0x59;
+ // (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;
+ public static final int PUSH4 = 0x63;
+ public static final int PUSH5 = 0x64;
+ public static final int PUSH6 = 0x65;
+ public static final int PUSH7 = 0x66;
+ public static final int PUSH8 = 0x67;
+ public static final int PUSH9 = 0x68;
+ public static final int PUSH10 = 0x69;
+ public static final int PUSH11 = 0x6a;
+ public static final int PUSH12 = 0x6b;
+ public static final int PUSH13 = 0x6c;
+ public static final int PUSH14 = 0x6d;
+ public static final int PUSH15 = 0x6e;
+ public static final int PUSH16 = 0x6f;
+ public static final int PUSH17 = 0x70;
+ public static final int PUSH18 = 0x71;
+ public static final int PUSH19 = 0x72;
+ public static final int PUSH20 = 0x73;
+ public static final int PUSH21 = 0x74;
+ public static final int PUSH22 = 0x75;
+ public static final int PUSH23 = 0x76;
+ public static final int PUSH24 = 0x77;
+ public static final int PUSH25 = 0x78;
+ public static final int PUSH26 = 0x79;
+ public static final int PUSH27 = 0x7a;
+ public static final int PUSH28 = 0x7b;
+ public static final int PUSH29 = 0x7c;
+ public static final int PUSH30 = 0x7d;
+ public static final int PUSH31 = 0x7e;
+ public static final int PUSH32 = 0x7f;
+
+ /* Duplicate Nth item from the stack */
+ public static final int DUP1 = 0x80;
+ public static final int DUP2 = 0x81;
+ public static final int DUP3 = 0x82;
+ public static final int DUP4 = 0x83;
+ public static final int DUP5 = 0x84;
+ public static final int DUP6 = 0x85;
+ public static final int DUP7 = 0x86;
+ public static final int DUP8 = 0x87;
+ public static final int DUP9 = 0x88;
+ public static final int DUP10 = 0x89;
+ public static final int DUP11 = 0x8a;
+ public static final int DUP12 = 0x8b;
+ public static final int DUP13 = 0x8c;
+ public static final int DUP14 = 0x8d;
+ public static final int DUP15 = 0x8e;
+ public static final int DUP16 = 0x8f;
+
+ /* Swap the Nth item from the stack with the top */
+ public static final int SWAP1 = 0x90;
+ public static final int SWAP2 = 0x91;
+ public static final int SWAP3 = 0x92;
+ public static final int SWAP4 = 0x93;
+ public static final int SWAP5 = 0x94;
+ public static final int SWAP6 = 0x95;
+ public static final int SWAP7 = 0x96;
+ public static final int SWAP8 = 0x97;
+ public static final int SWAP9 = 0x98;
+ public static final int SWAP10 = 0x99;
+ public static final int SWAP11 = 0x9a;
+ public static final int SWAP12 = 0x9b;
+ public static final int SWAP13 = 0x9c;
+ public static final int SWAP14 = 0x9d;
+ public static final int SWAP15 = 0x9e;
+ public static final int SWAP16 = 0x9f;
+
+ // (0xa[n]) log some data for some addres with 0..n tags [addr [tag0..tagn] data]
+ public static final int LOG0 = 0xa0;
+ public static final int LOG1 = 0xa1;
+ public static final int LOG2 = 0xa2;
+ public static final int LOG3 = 0xa3;
+ public static final int LOG4 = 0xa4;
+
+
+ /* System operations */
+ public static final int CALLTOKEN = 0xd0;
+ public static final int TOKENBALANCE = 0xd1;
+ public static final int CALLTOKENVALUE = 0xd2;
+ public static final int CALLTOKENID = 0xd3;
+ public static final int ISCONTRACT = 0xd4;
+ public static final int FREEZE = 0xd5;
+ public static final int UNFREEZE = 0xd6;
+ 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;
+ // Message-call into an account
+ public static final int CALL = 0xf1;
+ public static final int CALLCODE = 0xf2;
+ public static final int DELEGATECALL = 0xf4;
+ public static final int STATICCALL = 0xfa;
+ // (0xf3) Halt execution returning output data
+ public static final int RETURN = 0xf3;
+ // (0xf5) Skinny CREATE2, same as CREATE but with deterministic address
+ public static final int CREATE2 = 0xf5;
+ /*
+ * (0xfd) The `REVERT` instruction will stop execution, roll back all state changes done so far
+ * and provide a pointer to a memory section, which can be interpreted as an error code or
+ * message. While doing so, it will not consume all the remaining gas.
+ */
+ public static final int REVERT = 0xfd;
+ // (0xff) Halt execution and register account for later deletion
+ public static final int SUICIDE = 0xff;
+
+ private static final String[] OpName = new String[256];
+
+ private static final Map stringToByteMap = new HashMap<>();
+
+ static {
+ Field[] fields = Op.class.getDeclaredFields();
+ for (Field field : fields) {
+ try {
+ int op;
+ if (field.getType() == int.class) {
+ op = field.getInt(Op.class);
+ OpName[op] = field.getName();
+ stringToByteMap.put(field.getName(), (byte) op);
+ }
+ } catch (IllegalAccessException e) {
+ logger.error(e.getMessage());
+ }
+ }
+ }
+
+ public static String getNameOf(int opCode) {
+ return OpName[opCode];
+ }
+
+ public static String getNameOf(byte opCode) {
+ return OpName[opCode & 0xff];
+ }
+
+ public static byte getOpOf(String opCode) {
+ return stringToByteMap.get(opCode);
+ }
+
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/OpCode.java b/actuator/src/main/java/org/tron/core/vm/OpCode.java
deleted file mode 100644
index c14bce6bfc7..00000000000
--- a/actuator/src/main/java/org/tron/core/vm/OpCode.java
+++ /dev/null
@@ -1,797 +0,0 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm;
-
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-
-
-/**
- * Instruction set for the Ethereum Virtual Machine See Yellow Paper:
- * http://www.gavwood.com/Paper.pdf - Appendix G. Virtual Machine Specification
- */
-public enum OpCode {
- // TODO #POC9 Need to make tiers more accurate
- /**
- * Halts execution (0x00)
- */
- STOP(0x00, 0, 0, OpCode.Tier.ZeroTier),
-
- /* Arithmetic Operations */
-
- /**
- * (0x01) Addition operation
- */
- ADD(0x01, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x02) Multiplication operation
- */
- MUL(0x02, 2, 1, OpCode.Tier.LowTier),
- /**
- * (0x03) Subtraction operations
- */
- SUB(0x03, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x04) Integer division operation
- */
- DIV(0x04, 2, 1, OpCode.Tier.LowTier),
- /**
- * (0x05) Signed integer division operation
- */
- SDIV(0x05, 2, 1, OpCode.Tier.LowTier),
- /**
- * (0x06) Modulo remainder operation
- */
- MOD(0x06, 2, 1, OpCode.Tier.LowTier),
- /**
- * (0x07) Signed modulo remainder operation
- */
- SMOD(0x07, 2, 1, OpCode.Tier.LowTier),
- /**
- * (0x08) Addition combined with modulo remainder operation
- */
- ADDMOD(0x08, 3, 1, OpCode.Tier.MidTier),
- /**
- * (0x09) Multiplication combined with modulo remainder operation
- */
- MULMOD(0x09, 3, 1, OpCode.Tier.MidTier),
- /**
- * (0x0a) Exponential operation
- */
- EXP(0x0a, 2, 1, OpCode.Tier.SpecialTier),
- /**
- * (0x0b) Extend length of signed integer
- */
- SIGNEXTEND(0x0b, 2, 1, OpCode.Tier.LowTier),
-
- /* Bitwise Logic & Comparison Operations */
-
- /**
- * (0x10) Less-than comparison
- */
- LT(0X10, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x11) Greater-than comparison
- */
- GT(0X11, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x12) Signed less-than comparison
- */
- SLT(0X12, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x13) Signed greater-than comparison
- */
- SGT(0X13, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x14) Equality comparison
- */
- EQ(0X14, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x15) Negation operation
- */
- ISZERO(0x15, 1, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x16) Bitwise AND operation
- */
- AND(0x16, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x17) Bitwise OR operation
- */
- OR(0x17, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x18) Bitwise XOR operation
- */
- XOR(0x18, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x19) Bitwise NOT operationr
- */
- NOT(0x19, 1, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x1a) Retrieve single byte from word
- */
- BYTE(0x1a, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x1b) Shift left
- */
- SHL(0x1b, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x1c) Logical shift right
- */
- SHR(0x1c, 2, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x1d) Arithmetic shift right
- */
- SAR(0x1d, 2, 1, OpCode.Tier.VeryLowTier),
-
- /* Cryptographic Operations */
-
- /**
- * (0x20) Compute SHA3-256 hash
- */
- SHA3(0x20, 2, 1, OpCode.Tier.SpecialTier),
-
- /* Environmental Information */
-
- /**
- * (0x30) Get address of currently executing account
- */
- ADDRESS(0x30, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x31) Get balance of the given account
- */
- BALANCE(0x31, 1, 1, OpCode.Tier.ExtTier),
- /**
- * (0x32) Get execution origination address
- */
- ORIGIN(0x32, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x33) Get caller address
- */
- CALLER(0x33, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x34) Get deposited value by the instruction/transaction responsible for this execution
- */
- CALLVALUE(0x34, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x35) Get input data of current environment
- */
- CALLDATALOAD(0x35, 1, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x36) Get size of input data in current environment
- */
- CALLDATASIZE(0x36, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x37) Copy input data in current environment to memory
- */
- CALLDATACOPY(0x37, 3, 0, OpCode.Tier.VeryLowTier),
- /**
- * (0x38) Get size of code running in current environment
- */
- CODESIZE(0x38, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x39) Copy code running in current environment to memory
- */
- CODECOPY(0x39, 3, 0, OpCode.Tier.VeryLowTier), // [len code_start mem_start CODECOPY]
-
- RETURNDATASIZE(0x3d, 0, 1, OpCode.Tier.BaseTier),
-
- RETURNDATACOPY(0x3e, 3, 0, OpCode.Tier.VeryLowTier),
- /**
- * (0x3a) Get price of gas in current environment
- */
- GASPRICE(0x3a, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x3b) Get size of code running in current environment with given offset
- */
- EXTCODESIZE(0x3b, 1, 1, OpCode.Tier.ExtTier),
- /**
- * (0x3c) Copy code running in current environment to memory with given offset
- */
- EXTCODECOPY(0x3c, 4, 0, OpCode.Tier.ExtTier),
- /**
- * (0x3f) Returns the keccak256 hash of a contract’s code
- */
- EXTCODEHASH(0x3f, 1, 1, OpCode.Tier.ExtTier),
-
- /* Block Information */
-
- /**
- * (0x40) Get hash of most recent complete block
- */
- BLOCKHASH(0x40, 1, 1, OpCode.Tier.ExtTier),
- /**
- * (0x41) Get the block’s coinbase address
- */
- COINBASE(0x41, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (x042) Get the block’s timestamp
- */
- TIMESTAMP(0x42, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x43) Get the block’s number
- */
- NUMBER(0x43, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x44) Get the block’s difficulty
- */
- DIFFICULTY(0x44, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x45) Get the block’s gas limit
- */
- GASLIMIT(0x45, 0, 1, OpCode.Tier.BaseTier),
-
- /* Memory, Storage and Flow Operations */
-
- /**
- * (0x50) Remove item from stack
- */
- POP(0x50, 1, 0, OpCode.Tier.BaseTier),
- /**
- * (0x51) Load word from memory
- */
- MLOAD(0x51, 1, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x52) Save word to memory
- */
- MSTORE(0x52, 2, 0, OpCode.Tier.VeryLowTier),
- /**
- * (0x53) Save byte to memory
- */
- MSTORE8(0x53, 2, 0, OpCode.Tier.VeryLowTier),
- /**
- * (0x54) Load word from storage
- */
- SLOAD(0x54, 1, 1, OpCode.Tier.SpecialTier),
- /**
- * (0x55) Save word to storage
- */
- SSTORE(0x55, 2, 0, OpCode.Tier.SpecialTier),
- /**
- * (0x56) Alter the program counter
- */
- JUMP(0x56, 1, 0, OpCode.Tier.MidTier),
- /**
- * (0x57) Conditionally alter the program counter
- */
- JUMPI(0x57, 2, 0, OpCode.Tier.HighTier),
- /**
- * (0x58) Get the program counter
- */
- PC(0x58, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x59) Get the size of active memory
- */
- MSIZE(0x59, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x5a) Get the amount of available gas
- */
- GAS(0x5a, 0, 1, OpCode.Tier.BaseTier),
- /**
- * (0x5b)
- */
- JUMPDEST(0x5b, 0, 0, OpCode.Tier.SpecialTier),
-
- /* Push Operations */
-
- /**
- * (0x60) Place 1-byte item on stack
- */
- PUSH1(0x60, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x61) Place 2-byte item on stack
- */
- PUSH2(0x61, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x62) Place 3-byte item on stack
- */
- PUSH3(0x62, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x63) Place 4-byte item on stack
- */
- PUSH4(0x63, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x64) Place 5-byte item on stack
- */
- PUSH5(0x64, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x65) Place 6-byte item on stack
- */
- PUSH6(0x65, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x66) Place 7-byte item on stack
- */
- PUSH7(0x66, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x67) Place 8-byte item on stack
- */
- PUSH8(0x67, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x68) Place 9-byte item on stack
- */
- PUSH9(0x68, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x69) Place 10-byte item on stack
- */
- PUSH10(0x69, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x6a) Place 11-byte item on stack
- */
- PUSH11(0x6a, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x6b) Place 12-byte item on stack
- */
- PUSH12(0x6b, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x6c) Place 13-byte item on stack
- */
- PUSH13(0x6c, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x6d) Place 14-byte item on stack
- */
- PUSH14(0x6d, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x6e) Place 15-byte item on stack
- */
- PUSH15(0x6e, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x6f) Place 16-byte item on stack
- */
- PUSH16(0x6f, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x70) Place 17-byte item on stack
- */
- PUSH17(0x70, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x71) Place 18-byte item on stack
- */
- PUSH18(0x71, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x72) Place 19-byte item on stack
- */
- PUSH19(0x72, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x73) Place 20-byte item on stack
- */
- PUSH20(0x73, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x74) Place 21-byte item on stack
- */
- PUSH21(0x74, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x75) Place 22-byte item on stack
- */
- PUSH22(0x75, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x76) Place 23-byte item on stack
- */
- PUSH23(0x76, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x77) Place 24-byte item on stack
- */
- PUSH24(0x77, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x78) Place 25-byte item on stack
- */
- PUSH25(0x78, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x79) Place 26-byte item on stack
- */
- PUSH26(0x79, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x7a) Place 27-byte item on stack
- */
- PUSH27(0x7a, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x7b) Place 28-byte item on stack
- */
- PUSH28(0x7b, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x7c) Place 29-byte item on stack
- */
- PUSH29(0x7c, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x7d) Place 30-byte item on stack
- */
- PUSH30(0x7d, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x7e) Place 31-byte item on stack
- */
- PUSH31(0x7e, 0, 1, OpCode.Tier.VeryLowTier),
- /**
- * (0x7f) Place 32-byte (full word) item on stack
- */
- PUSH32(0x7f, 0, 1, OpCode.Tier.VeryLowTier),
-
- /* Duplicate Nth item from the stack */
-
- /**
- * (0x80) Duplicate 1st item on stack
- */
- DUP1(0x80, 1, 2, OpCode.Tier.VeryLowTier),
- /**
- * (0x81) Duplicate 2nd item on stack
- */
- DUP2(0x81, 2, 3, OpCode.Tier.VeryLowTier),
- /**
- * (0x82) Duplicate 3rd item on stack
- */
- DUP3(0x82, 3, 4, OpCode.Tier.VeryLowTier),
- /**
- * (0x83) Duplicate 4th item on stack
- */
- DUP4(0x83, 4, 5, OpCode.Tier.VeryLowTier),
- /**
- * (0x84) Duplicate 5th item on stack
- */
- DUP5(0x84, 5, 6, OpCode.Tier.VeryLowTier),
- /**
- * (0x85) Duplicate 6th item on stack
- */
- DUP6(0x85, 6, 7, OpCode.Tier.VeryLowTier),
- /**
- * (0x86) Duplicate 7th item on stack
- */
- DUP7(0x86, 7, 8, OpCode.Tier.VeryLowTier),
- /**
- * (0x87) Duplicate 8th item on stack
- */
- DUP8(0x87, 8, 9, OpCode.Tier.VeryLowTier),
- /**
- * (0x88) Duplicate 9th item on stack
- */
- DUP9(0x88, 9, 10, OpCode.Tier.VeryLowTier),
- /**
- * (0x89) Duplicate 10th item on stack
- */
- DUP10(0x89, 10, 11, OpCode.Tier.VeryLowTier),
- /**
- * (0x8a) Duplicate 11th item on stack
- */
- DUP11(0x8a, 11, 12, OpCode.Tier.VeryLowTier),
- /**
- * (0x8b) Duplicate 12th item on stack
- */
- DUP12(0x8b, 12, 13, OpCode.Tier.VeryLowTier),
- /**
- * (0x8c) Duplicate 13th item on stack
- */
- DUP13(0x8c, 13, 14, OpCode.Tier.VeryLowTier),
- /**
- * (0x8d) Duplicate 14th item on stack
- */
- DUP14(0x8d, 14, 15, OpCode.Tier.VeryLowTier),
- /**
- * (0x8e) Duplicate 15th item on stack
- */
- DUP15(0x8e, 15, 16, OpCode.Tier.VeryLowTier),
- /**
- * (0x8f) Duplicate 16th item on stack
- */
- DUP16(0x8f, 16, 17, OpCode.Tier.VeryLowTier),
-
- /* Swap the Nth item from the stack with the top */
-
- /**
- * (0x90) Exchange 2nd item from stack with the top
- */
- SWAP1(0x90, 2, 2, OpCode.Tier.VeryLowTier),
- /**
- * (0x91) Exchange 3rd item from stack with the top
- */
- SWAP2(0x91, 3, 3, OpCode.Tier.VeryLowTier),
- /**
- * (0x92) Exchange 4th item from stack with the top
- */
- SWAP3(0x92, 4, 4, OpCode.Tier.VeryLowTier),
- /**
- * (0x93) Exchange 5th item from stack with the top
- */
- SWAP4(0x93, 5, 5, OpCode.Tier.VeryLowTier),
- /**
- * (0x94) Exchange 6th item from stack with the top
- */
- SWAP5(0x94, 6, 6, OpCode.Tier.VeryLowTier),
- /**
- * (0x95) Exchange 7th item from stack with the top
- */
- SWAP6(0x95, 7, 7, OpCode.Tier.VeryLowTier),
- /**
- * (0x96) Exchange 8th item from stack with the top
- */
- SWAP7(0x96, 8, 8, OpCode.Tier.VeryLowTier),
- /**
- * (0x97) Exchange 9th item from stack with the top
- */
- SWAP8(0x97, 9, 9, OpCode.Tier.VeryLowTier),
- /**
- * (0x98) Exchange 10th item from stack with the top
- */
- SWAP9(0x98, 10, 10, OpCode.Tier.VeryLowTier),
- /**
- * (0x99) Exchange 11th item from stack with the top
- */
- SWAP10(0x99, 11, 11, OpCode.Tier.VeryLowTier),
- /**
- * (0x9a) Exchange 12th item from stack with the top
- */
- SWAP11(0x9a, 12, 12, OpCode.Tier.VeryLowTier),
- /**
- * (0x9b) Exchange 13th item from stack with the top
- */
- SWAP12(0x9b, 13, 13, OpCode.Tier.VeryLowTier),
- /**
- * (0x9c) Exchange 14th item from stack with the top
- */
- SWAP13(0x9c, 14, 14, OpCode.Tier.VeryLowTier),
- /**
- * (0x9d) Exchange 15th item from stack with the top
- */
- SWAP14(0x9d, 15, 15, OpCode.Tier.VeryLowTier),
- /**
- * (0x9e) Exchange 16th item from stack with the top
- */
- SWAP15(0x9e, 16, 16, OpCode.Tier.VeryLowTier),
- /**
- * (0x9f) Exchange 17th item from stack with the top
- */
- SWAP16(0x9f, 17, 17, OpCode.Tier.VeryLowTier),
-
- /**
- * (0xa[n]) log some data for some addres with 0..n tags [addr [tag0..tagn] data]
- */
- LOG0(0xa0, 2, 0, OpCode.Tier.SpecialTier),
- LOG1(0xa1, 3, 0, OpCode.Tier.SpecialTier),
- LOG2(0xa2, 4, 0, OpCode.Tier.SpecialTier),
- LOG3(0xa3, 5, 0, OpCode.Tier.SpecialTier),
- LOG4(0xa4, 6, 0, OpCode.Tier.SpecialTier),
-
- /* System operations */
-
- /**
- * (0xd0) Message-call into an account with trc10 token
- */
- CALLTOKEN(0xd0, 8, 1, OpCode.Tier.SpecialTier, CallFlags.Call, CallFlags.HasValue),
-
- TOKENBALANCE(0xd1, 2, 1, OpCode.Tier.ExtTier),
-
- CALLTOKENVALUE(0xd2, 0, 1, OpCode.Tier.BaseTier),
-
- CALLTOKENID(0xd3, 0, 1, OpCode.Tier.BaseTier),
-
- ISCONTRACT(0xd4, 1, 1, OpCode.Tier.ExtTier),
-
- /**
- * (0xf0) Create a new account with associated code
- */
- CREATE(0xf0, 3, 1, OpCode.Tier.SpecialTier), // [in_size] [in_offs] [gas_val] CREATE
- /**
- * (cxf1) Message-call into an account
- */
- CALL(0xf1, 7, 1, OpCode.Tier.SpecialTier, CallFlags.Call, CallFlags.HasValue),
- // [out_data_size] [out_data_start] [in_data_size] [in_data_start] [value] [to_addr]
- // [gas] CALL
- /**
- * (0xf2) Calls self, but grabbing the code from the TO argument instead of from one's own
- * address
- */
- CALLCODE(0xf2, 7, 1, OpCode.Tier.SpecialTier, CallFlags.Call, CallFlags.HasValue,
- CallFlags.Stateless),
- /**
- * (0xf3) Halt execution returning output data
- */
- RETURN(0xf3, 2, 0, OpCode.Tier.ZeroTier),
-
- /**
- * (0xf4) similar in idea to CALLCODE, except that it propagates the sender and value from the
- * parent scope to the child scope, ie. the call created has the same sender and value as the
- * original call. also the Value parameter is omitted for this opCode
- */
- DELEGATECALL(0xf4, 6, 1, OpCode.Tier.SpecialTier, CallFlags.Call, CallFlags.Stateless,
- CallFlags.Delegate),
-
- /**
- * (0xf5) Skinny CREATE2, same as CREATE but with deterministic address
- */
- CREATE2(0xf5, 4, 1, OpCode.Tier.SpecialTier),
-
- /**
- * opcode that can be used to call another contract (or itself) while disallowing any
- * modifications to the state during the call (and its subcalls, if present). Any opcode that
- * attempts to perform such a modification (see below for details) will result in an exception
- * instead of performing the modification.
- */
- STATICCALL(0xfa, 6, 1, OpCode.Tier.SpecialTier, CallFlags.Call, CallFlags.Static),
-
- /**
- * (0xfd) The `REVERT` instruction will stop execution, roll back all state changes done so far
- * and provide a pointer to a memory section, which can be interpreted as an error code or
- * message. While doing so, it will not consume all the remaining gas.
- */
- REVERT(0xfd, 2, 0, OpCode.Tier.ZeroTier),
- /**
- * (0xff) Halt execution and register account for later deletion
- */
- SUICIDE(0xff, 1, 0, OpCode.Tier.ZeroTier);
-
- private static final OpCode[] intToTypeMap = new OpCode[256];
- private static final Map stringToByteMap = new HashMap<>();
-
- static {
- for (OpCode type : OpCode.values()) {
- intToTypeMap[type.opcode & 0xFF] = type;
- stringToByteMap.put(type.name(), type.opcode);
- }
- }
-
- private final byte opcode;
- private final int require;
- private final Tier tier;
- private final int ret;
- private final EnumSet callFlags;
-
- //require = required args
- //return = required return
- private OpCode(int op, int require, int ret, Tier tier, CallFlags... callFlags) {
- this.opcode = (byte) op;
- this.require = require;
- this.tier = tier;
- this.ret = ret;
- this.callFlags = callFlags.length == 0 ? EnumSet.noneOf(CallFlags.class) :
- EnumSet.copyOf(Arrays.asList(callFlags));
- }
-
- public static boolean contains(String code) {
- return stringToByteMap.containsKey(code.trim());
- }
-
- public static byte byteVal(String code) {
- return stringToByteMap.get(code);
- }
-
- public static OpCode code(byte code) {
- return intToTypeMap[code & 0xFF];
- }
-
- public byte val() {
- return opcode;
- }
-
- /**
- * Returns the mininum amount of items required on the stack for this operation
- *
- * @return minimum amount of expected items on the stack
- */
- public int require() {
- return require;
- }
-
- public int ret() {
- return ret;
- }
-
- public int asInt() {
- return opcode;
- }
-
- private EnumSet getCallFlags() {
- return callFlags;
- }
-
- /**
- * Indicates that opcode is a call
- */
- public boolean isCall() {
- return getCallFlags().contains(CallFlags.Call);
- }
-
- private void checkCall() {
- if (!isCall()) {
- throw new RuntimeException("Opcode is not a call: " + this);
- }
- }
-
- /**
- * Indicates that the code is executed in the context of the caller
- */
- public boolean callIsStateless() {
- checkCall();
- return getCallFlags().contains(CallFlags.Stateless);
- }
-
- /**
- * Indicates that the opcode has value parameter (3rd on stack)
- */
- public boolean callHasValue() {
- checkCall();
- return getCallFlags().contains(CallFlags.HasValue);
- }
-
- /**
- * Indicates that any state modifications are disallowed during the call
- */
- public boolean callIsStatic() {
- checkCall();
- return getCallFlags().contains(CallFlags.Static);
- }
-
- /**
- * Indicates that value and message sender are propagated from parent to child scope
- */
- public boolean callIsDelegate() {
- checkCall();
- return getCallFlags().contains(CallFlags.Delegate);
- }
-
- public Tier getTier() {
- return this.tier;
- }
-
- public enum Tier {
- ZeroTier(0),
- BaseTier(2),
- VeryLowTier(3),
- LowTier(5),
- MidTier(8),
- HighTier(10),
- ExtTier(20),
- SpecialTier(1), //TODO #POC9 is this correct?? "multiparam" from cpp
- InvalidTier(0);
-
-
- private final int level;
-
- private Tier(int level) {
- this.level = level;
- }
-
- public int asInt() {
- return level;
- }
- }
-
- private enum CallFlags {
- /**
- * Indicates that opcode is a call
- */
- Call,
-
- /**
- * Indicates that the code is executed in the context of the caller
- */
- Stateless,
-
- /**
- * Indicates that the opcode has value parameter (3rd on stack)
- */
- HasValue,
-
- /**
- * Indicates that any state modifications are disallowed during the call
- */
- Static,
-
- /**
- * Indicates that value and message sender are propagated from parent to child scope
- */
- Delegate
- }
-}
-
-
diff --git a/actuator/src/main/java/org/tron/core/vm/Operation.java b/actuator/src/main/java/org/tron/core/vm/Operation.java
new file mode 100644
index 00000000000..87ff8fce749
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/Operation.java
@@ -0,0 +1,55 @@
+package org.tron.core.vm;
+
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.tron.core.vm.program.Program;
+
+public class Operation {
+
+ private final int opcode;
+ private final int require;
+ private final int ret;
+ private final Function cost;
+ private final Consumer action;
+ private final BooleanSupplier enabled;
+
+ public Operation(int opcode, int require, int ret,
+ Function cost, Consumer action) {
+ this(opcode, require, ret, cost, action, () -> true);
+ }
+
+ public Operation(int opcode, int require, int ret,
+ Function cost, Consumer action, BooleanSupplier enabled) {
+ this.opcode = opcode;
+ this.require = require;
+ this.ret = ret;
+ this.cost = cost;
+ this.action = action;
+ this.enabled = enabled;
+ }
+
+ public int getOpcode() {
+ return opcode;
+ }
+
+ public int getRequire() {
+ return require;
+ }
+
+ public int getRet() {
+ return ret;
+ }
+
+ public long getEnergyCost(Program program) {
+ return this.cost.apply(program);
+ }
+
+ public void execute(Program program) {
+ this.action.accept(program);
+ }
+
+ public boolean isEnabled() {
+ return enabled.getAsBoolean();
+ }
+}
diff --git a/actuator/src/main/java/org/tron/core/vm/OperationActions.java b/actuator/src/main/java/org/tron/core/vm/OperationActions.java
new file mode 100644
index 00000000000..88c3c55899e
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/OperationActions.java
@@ -0,0 +1,1102 @@
+package org.tron.core.vm;
+
+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;
+import org.tron.core.vm.program.Program;
+import org.tron.core.vm.program.Stack;
+
+public class OperationActions {
+
+ private static final BigInteger _32_ = BigInteger.valueOf(32);
+
+ public static void stopAction(Program program) {
+ program.setHReturn(EMPTY_BYTE_ARRAY);
+ program.stop();
+ }
+
+ public static void addAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.add(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void mulAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.mul(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void subAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.sub(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void divAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.div(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void sdivAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.sDiv(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void modAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.mod(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void sModAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.sMod(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void addModAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+ DataWord word3 = program.stackPop();
+
+ word1.addmod(word2, word3);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void mulModAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+ DataWord word3 = program.stackPop();
+
+ word1.mulmod(word2, word3);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void expAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.exp(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void signExtendAction(Program program) {
+ DataWord word1 = program.stackPop();
+ BigInteger k = word1.value();
+
+ if (k.compareTo(_32_) < 0) {
+ DataWord word2 = program.stackPop();
+ word2.signExtend(k.byteValue());
+ program.stackPush(word2);
+ }
+ program.step();
+ }
+
+ public static void ltAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ if (word1.value().compareTo(word2.value()) < 0) {
+ word1.and(DataWord.ZERO);
+ word1.getData()[31] = 1;
+ } else {
+ word1.and(DataWord.ZERO);
+ }
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void gtAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ if (word1.value().compareTo(word2.value()) > 0) {
+ word1.and(DataWord.ZERO);
+ word1.getData()[31] = 1;
+ } else {
+ word1.and(DataWord.ZERO);
+ }
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void sltAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ if (word1.sValue().compareTo(word2.sValue()) < 0) {
+ word1.and(DataWord.ZERO);
+ word1.getData()[31] = 1;
+ } else {
+ word1.and(DataWord.ZERO);
+ }
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void sgtAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ if (word1.sValue().compareTo(word2.sValue()) > 0) {
+ word1.and(DataWord.ZERO);
+ word1.getData()[31] = 1;
+ } else {
+ word1.and(DataWord.ZERO);
+ }
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void eqAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ if (word1.xor(word2).isZero()) {
+ word1.and(DataWord.ZERO);
+ word1.getData()[31] = 1;
+ } else {
+ word1.and(DataWord.ZERO);
+ }
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void isZeroAction(Program program) {
+ DataWord word1 = program.stackPop();
+ if (word1.isZero()) {
+ word1.getData()[31] = 1;
+ } else {
+ word1.and(DataWord.ZERO);
+ }
+
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void andAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.and(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void orAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.or(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void xorAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ word1.xor(word2);
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void notAction(Program program) {
+ DataWord word1 = program.stackPop();
+ word1.bnot();
+
+ program.stackPush(word1);
+ program.step();
+ }
+
+ public static void byteAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ final DataWord result;
+ if (word1.value().compareTo(_32_) < 0) {
+ byte tmp = word2.getData()[word1.intValue()];
+ word2.and(DataWord.ZERO);
+ word2.getData()[31] = tmp;
+ result = word2;
+ } else {
+ result = new DataWord();
+ }
+
+ program.stackPush(result);
+ program.step();
+ }
+
+ public static void shlAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ final DataWord result = word2.shiftLeft(word1);
+ program.stackPush(result);
+ program.step();
+ }
+
+ public static void shrAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ final DataWord result = word2.shiftRight(word1);
+ program.stackPush(result);
+ program.step();
+ }
+
+ public static void sarAction(Program program) {
+ DataWord word1 = program.stackPop();
+ DataWord word2 = program.stackPop();
+
+ final DataWord result = word2.shiftRightSigned(word1);
+ program.stackPush(result);
+ 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();
+ byte[] buffer = program
+ .memoryChunk(memOffsetData.intValueSafe(), lengthData.intValueSafe());
+
+ byte[] encoded = sha3(buffer);
+ DataWord word = new DataWord(encoded);
+
+ program.stackPush(word);
+ program.step();
+ }
+
+ public static void addressAction(Program program) {
+ DataWord address = program.getContractAddress();
+ // allowMultiSigns proposal
+ if (VMConfig.allowMultiSign()) {
+ address = new DataWord(address.getLast20Bytes());
+ }
+
+ program.stackPush(address);
+ program.step();
+ }
+
+ public static void balanceAction(Program program) {
+ DataWord address = program.stackPop();
+ DataWord balance = program.getBalance(address);
+
+ program.stackPush(balance);
+ program.step();
+ }
+
+ public static void originAction(Program program) {
+ DataWord originAddress = program.getOriginAddress();
+ //allowMultiSign proposal
+ if (VMConfig.allowMultiSign()) {
+ originAddress = new DataWord(originAddress.getLast20Bytes());
+ }
+
+ program.stackPush(originAddress);
+ program.step();
+ }
+
+ public static void callerAction(Program program) {
+ DataWord callerAddress = program.getCallerAddress();
+ /*
+ since we use 21 bytes address instead of 20 as etherum, we need to make sure
+ the address length in vm is matching with 20
+ */
+ callerAddress = new DataWord(callerAddress.getLast20Bytes());
+
+ program.stackPush(callerAddress);
+ program.step();
+ }
+
+ public static void callValueAction(Program program) {
+ DataWord callValue = program.getCallValue();
+
+ program.stackPush(callValue);
+ program.step();
+ }
+
+ public static void callDataLoadAction(Program program) {
+ DataWord dataOffs = program.stackPop();
+ DataWord value = program.getDataValue(dataOffs);
+
+ program.stackPush(value);
+ program.step();
+ }
+
+ public static void callDataSizeAction(Program program) {
+ DataWord dataSize = program.getDataSize();
+
+ program.stackPush(dataSize);
+ program.step();
+ }
+
+ public static void callDataCopyAction(Program program) {
+ DataWord memOffsetData = program.stackPop();
+ DataWord dataOffsetData = program.stackPop();
+ DataWord lengthData = program.stackPop();
+
+ byte[] msgData = program.getDataCopy(dataOffsetData, lengthData);
+
+ program.memorySave(memOffsetData.intValueSafe(), msgData);
+ program.step();
+ }
+
+ public static void codeSizeAction(Program program) {
+ int length = program.getCode().length;
+
+ DataWord codeLength = new DataWord(length);
+ program.stackPush(codeLength);
+ program.step();
+ }
+
+ public static void codeCopyAction(Program program) {
+ byte[] fullCode = program.getCode();
+
+ int memOffset = program.stackPop().intValueSafe();
+ int codeOffset = program.stackPop().intValueSafe();
+ int lengthData = program.stackPop().intValueSafe();
+
+ int sizeToBeCopied = lengthData;
+ if ((long) codeOffset + lengthData > fullCode.length) {
+ sizeToBeCopied = fullCode.length < codeOffset ? 0 : fullCode.length - codeOffset;
+ }
+
+ byte[] codeCopy = new byte[lengthData];
+
+ if (codeOffset < fullCode.length) {
+ System.arraycopy(fullCode, codeOffset, codeCopy, 0, sizeToBeCopied);
+ }
+
+ program.memorySave(memOffset, codeCopy);
+ program.step();
+ }
+
+ public static void returnDataSizeAction(Program program) {
+ DataWord dataSize = program.getReturnDataBufferSize();
+
+ program.stackPush(dataSize);
+ program.step();
+ }
+
+ public static void returnDataCopyAction(Program program) {
+ DataWord memOffsetData = program.stackPop();
+ DataWord dataOffsetData = program.stackPop();
+ DataWord lengthData = program.stackPop();
+
+ byte[] msgData = program.getReturnDataBufferData(dataOffsetData, lengthData);
+
+ if (msgData == null) {
+ throw new Program.ReturnDataCopyIllegalBoundsException(dataOffsetData, lengthData,
+ program.getReturnDataBufferSize().longValueSafe());
+ }
+
+ program.memorySave(memOffsetData.intValueSafe(), msgData);
+ program.step();
+ }
+
+ public static void gasPriceAction(Program program) {
+ DataWord energyPrice = DataWord.ZERO();
+ if (VMConfig.allowTvmCompatibleEvm() && program.getContractVersion() == 1) {
+ energyPrice = new DataWord(program.getContractState()
+ .getDynamicPropertiesStore().getEnergyFee());
+ }
+ program.stackPush(energyPrice);
+ program.step();
+ }
+
+ public static void extCodeSizeAction(Program program) {
+ DataWord address = program.stackPop();
+
+ int length = program.getCodeAt(address).length;
+ DataWord codeLength = new DataWord(length);
+
+ program.stackPush(codeLength);
+ program.step();
+ }
+
+ public static void extCodeCopyAction(Program program) {
+ DataWord address = program.stackPop();
+ byte[] fullCode = program.getCodeAt(address);
+
+ int memOffset = program.stackPop().intValueSafe();
+ int codeOffset = program.stackPop().intValueSafe();
+ int lengthData = program.stackPop().intValueSafe();
+
+ int sizeToBeCopied = lengthData;
+ if ((long) codeOffset + lengthData > fullCode.length) {
+ sizeToBeCopied = fullCode.length < codeOffset ? 0 : fullCode.length - codeOffset;
+ }
+
+ byte[] codeCopy = new byte[lengthData];
+
+ if (codeOffset < fullCode.length) {
+ System.arraycopy(fullCode, codeOffset, codeCopy, 0, sizeToBeCopied);
+ }
+
+ program.memorySave(memOffset, codeCopy);
+ program.step();
+ }
+
+ public static void extCodeHashAction(Program program) {
+ DataWord address = program.stackPop();
+ byte[] codeHash = program.getCodeHashAt(address);
+ program.stackPush(new DataWord(codeHash));
+ program.step();
+ }
+
+ public static void blockHashAction(Program program) {
+ int blockIndex = program.stackPop().intValueSafe();
+ DataWord blockHash = program.getBlockHash(blockIndex);
+
+ program.stackPush(blockHash);
+ program.step();
+ }
+
+ public static void coinBaseAction(Program program) {
+ DataWord coinbase = program.getCoinbase();
+
+ program.stackPush(coinbase);
+ program.step();
+ }
+
+ public static void timeStampAction(Program program) {
+ DataWord timestamp = program.getTimestamp();
+
+ program.stackPush(timestamp);
+ program.step();
+ }
+
+ public static void numberAction(Program program) {
+ DataWord number = program.getNumber();
+
+ program.stackPush(number);
+ program.step();
+ }
+
+ public static void difficultyAction(Program program) {
+ DataWord result = DataWord.ZERO();
+
+ program.stackPush(result);
+ program.step();
+ }
+
+ public static void gasLimitAction(Program program) {
+ DataWord result = DataWord.ZERO();
+
+ program.stackPush(result);
+ program.step();
+ }
+
+ public static void chainIdAction(Program program) {
+ DataWord chainId = program.getChainId();
+
+ program.stackPush(chainId);
+ program.step();
+ }
+
+ public static void selfBalanceAction(Program program) {
+ DataWord selfBalance = program.getBalance(program.getContractAddress());
+
+ program.stackPush(selfBalance);
+ program.step();
+ }
+
+ public static void baseFeeAction(Program program) {
+ DataWord energyFee =
+ new DataWord(program.getContractState().getDynamicPropertiesStore().getEnergyFee());
+
+ program.stackPush(energyFee);
+ program.step();
+ }
+
+ public static void popAction(Program program) {
+ program.stackPop();
+ program.step();
+ }
+
+ public static void mLoadAction(Program program) {
+ DataWord addr = program.stackPop();
+ DataWord data = program.memoryLoad(addr);
+
+ program.stackPush(data);
+ program.step();
+ }
+
+ public static void mStoreAction(Program program) {
+ DataWord addr = program.stackPop();
+ DataWord value = program.stackPop();
+
+ program.memorySave(addr, value);
+ program.step();
+ }
+
+ public static void mStore8Action(Program program) {
+ DataWord addr = program.stackPop();
+ DataWord value = program.stackPop();
+
+ byte[] byteVal = {value.getData()[31]};
+ program.memorySave(addr.intValueSafe(), byteVal);
+ program.step();
+ }
+
+ public static void sLoadAction(Program program) {
+ DataWord key = program.stackPop();
+ DataWord val = program.storageLoad(key);
+
+ if (val == null) {
+ val = key.and(DataWord.ZERO);
+ }
+
+ program.stackPush(val);
+ program.step();
+ }
+
+ public static void sStoreAction(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ DataWord addr = program.stackPop();
+ DataWord value = program.stackPop();
+
+ program.storageSave(addr, value);
+ program.step();
+ }
+
+ public static void jumpAction(Program program) {
+ DataWord pos = program.stackPop();
+ int nextPC = program.verifyJumpDest(pos);
+
+ program.setPC(nextPC);
+ }
+
+ public static void jumpIAction(Program program) {
+ DataWord pos = program.stackPop();
+ DataWord cond = program.stackPop();
+
+ if (!cond.isZero()) {
+ int nextPC = program.verifyJumpDest(pos);
+ program.setPC(nextPC);
+ } else {
+ program.step();
+ }
+ }
+
+ public static void pcAction(Program program) {
+ int pc = program.getPC();
+ DataWord pcWord = new DataWord(pc);
+
+ program.stackPush(pcWord);
+ program.step();
+ }
+
+ public static void mSizeAction(Program program) {
+ int memSize = program.getMemSize();
+ DataWord wordMemSize = new DataWord(memSize);
+
+ program.stackPush(wordMemSize);
+ program.step();
+ }
+
+ public static void gasAction(Program program) {
+ DataWord energy = program.getEnergyLimitLeft();
+
+ program.stackPush(energy);
+ program.step();
+ }
+
+ 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();
+ byte[] data = program.sweep(n);
+
+ program.stackPush(new DataWord(data));
+ }
+
+ public static void dupAction(Program program) {
+ Stack stack = program.getStack();
+ int n = program.getCurrentOpIntValue() - Op.DUP1 + 1;
+ DataWord word_1 = stack.get(stack.size() - n);
+
+ program.stackPush(word_1.clone());
+ program.step();
+ }
+
+ public static void swapAction(Program program) {
+ Stack stack = program.getStack();
+ int n = program.getCurrentOpIntValue() - Op.SWAP1 + 2;
+ stack.swap(stack.size() - 1, stack.size() - n);
+
+ program.step();
+ }
+
+ public static void logAction(Program program) {
+ Stack stack = program.getStack();
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+ DataWord address = program.getContractAddress();
+
+ DataWord memStart = stack.pop();
+ DataWord memOffset = stack.pop();
+
+ int nTopics = program.getCurrentOpIntValue() - Op.LOG0;
+
+ List topics = new ArrayList<>();
+ for (int i = 0; i < nTopics; ++i) {
+ DataWord topic = stack.pop();
+ topics.add(topic);
+ }
+
+ byte[] data = program.memoryChunk(memStart.intValueSafe(), memOffset.intValueSafe());
+
+ LogInfo logInfo =
+ new LogInfo(address.getLast20Bytes(), topics, data);
+
+ program.getResult().addLogInfo(logInfo);
+ program.step();
+ }
+
+ public static void tokenBalanceAction(Program program) {
+ DataWord tokenId = program.stackPop();
+ DataWord address = program.stackPop();
+ DataWord tokenBalance = program.getTokenBalance(address, tokenId);
+
+ program.stackPush(tokenBalance);
+ program.step();
+ }
+
+ public static void callTokenValueAction(Program program) {
+ DataWord tokenValue = program.getTokenValue();
+
+ program.stackPush(tokenValue);
+ program.step();
+ }
+
+ public static void callTokenIdAction(Program program) {
+ DataWord _tokenId = program.getTokenId();
+
+ program.stackPush(_tokenId);
+ program.step();
+ }
+
+ public static void isContractAction(Program program) {
+ DataWord address = program.stackPop();
+ DataWord isContract = program.isContract(address);
+
+ program.stackPush(isContract);
+ program.step();
+ }
+
+ public static void freezeAction(Program program) {
+ // after allow vote, check static
+ if (VMConfig.allowTvmVote() && program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+ // 0 as bandwidth, 1 as energy
+ DataWord resourceType = program.stackPop();
+ DataWord frozenBalance = program.stackPop();
+ DataWord receiverAddress = program.stackPop();
+
+ 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();
+ }
+
+ public static void unfreezeAction(Program program) {
+ if (VMConfig.allowTvmVote() && program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ DataWord resourceType = program.stackPop();
+ DataWord receiverAddress = program.stackPop();
+
+ boolean result = program.unfreeze(receiverAddress, resourceType);
+ program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ program.step();
+ }
+
+ public static void freezeExpireTimeAction(Program program) {
+ DataWord resourceType = program.stackPop();
+ DataWord targetAddress = program.stackPop();
+
+ long expireTime = program.freezeExpireTime(targetAddress, resourceType);
+ program.stackPush(new DataWord(expireTime / 1000));
+ 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();
+ }
+
+ int amountArrayLength = program.stackPop().intValueSafe();
+ int amountArrayOffset = program.stackPop().intValueSafe();
+ int witnessArrayLength = program.stackPop().intValueSafe();
+ int witnessArrayOffset = program.stackPop().intValueSafe();
+
+ boolean result = program.voteWitness(witnessArrayOffset, witnessArrayLength,
+ amountArrayOffset, amountArrayLength);
+ program.stackPush(result ? DataWord.ONE() : DataWord.ZERO());
+ program.step();
+ }
+
+ public static void withdrawRewardAction(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ long allowance = program.withdrawReward();
+ program.stackPush(new DataWord(allowance));
+ program.step();
+ }
+
+ public static void createAction(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ DataWord value = program.stackPop();
+ DataWord inOffset = program.stackPop();
+ DataWord inSize = program.stackPop();
+
+ program.createContract(value, inOffset, inSize);
+ program.step();
+ }
+
+ public static void returnAction(Program program) {
+ DataWord offset = program.stackPop();
+ DataWord size = program.stackPop();
+
+ byte[] hReturn = program.memoryChunk(offset.intValueSafe(), size.intValueSafe());
+ program.setHReturn(hReturn);
+
+ program.step();
+ program.stop();
+ }
+
+ public static void create2Action(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ DataWord value = program.stackPop();
+ DataWord inOffset = program.stackPop();
+ DataWord inSize = program.stackPop();
+ DataWord salt = program.stackPop();
+
+ program.createContract2(value, inOffset, inSize, salt);
+ program.step();
+ }
+
+ public static void callAction(Program program) {
+ // use adjustedCallEnergy instead of requested
+ program.stackPop();
+ DataWord codeAddress = program.stackPop();
+ DataWord value = program.stackPop();
+
+ if (program.isStaticCall() && !value.isZero()) {
+ throw new Program.StaticCallModificationException();
+ }
+ DataWord adjustedCallEnergy = program.getAdjustedCallEnergy();
+ if (!value.isZero()) {
+ adjustedCallEnergy.add(new DataWord(EnergyCost.getStipendCallCost()));
+ }
+ exeCall(program, adjustedCallEnergy, codeAddress, value, DataWord.ZERO(), false);
+ }
+
+ public static void callTokenAction(Program program) {
+ program.stackPop();
+ DataWord codeAddress = program.stackPop();
+ DataWord value = program.stackPop();
+
+ if (program.isStaticCall() && !value.isZero()) {
+ throw new Program.StaticCallModificationException();
+ }
+ DataWord adjustedCallEnergy = program.getAdjustedCallEnergy();
+ if (!value.isZero()) {
+ adjustedCallEnergy.add(new DataWord(EnergyCost.getStipendCallCost()));
+ }
+ DataWord tokenId = program.stackPop();
+ exeCall(program, adjustedCallEnergy, codeAddress, value, tokenId, VMConfig.allowMultiSign());
+ }
+
+ public static void callCodeAction(Program program) {
+ program.stackPop();
+ DataWord codeAddress = program.stackPop();
+ DataWord value = program.stackPop();
+
+ DataWord adjustedCallEnergy = program.getAdjustedCallEnergy();
+ if (!value.isZero()) {
+ adjustedCallEnergy.add(new DataWord(EnergyCost.getStipendCallCost()));
+ }
+ exeCall(program, adjustedCallEnergy, codeAddress, value, DataWord.ZERO(), false);
+ }
+
+ public static void delegateCallAction(Program program) {
+ program.stackPop();
+ DataWord codeAddress = program.stackPop();
+ DataWord value = DataWord.ZERO;
+
+ DataWord adjustedCallEnergy = program.getAdjustedCallEnergy();
+ exeCall(program, adjustedCallEnergy, codeAddress, value, DataWord.ZERO(), false);
+ }
+
+ public static void staticCallAction(Program program) {
+ program.stackPop();
+ DataWord codeAddress = program.stackPop();
+ DataWord value = DataWord.ZERO;
+
+ DataWord adjustedCallEnergy = program.getAdjustedCallEnergy();
+ exeCall(program, adjustedCallEnergy, codeAddress, value, DataWord.ZERO(), false);
+ }
+
+ public static void exeCall(Program program, DataWord adjustedCallEnergy,
+ DataWord codeAddress, DataWord value, DataWord tokenId, boolean isTokenTransferMsg) {
+
+ DataWord inDataOffs = program.stackPop();
+ DataWord inDataSize = program.stackPop();
+
+ DataWord outDataOffs = program.stackPop();
+ DataWord outDataSize = program.stackPop();
+
+ program.memoryExpand(outDataOffs, outDataSize);
+ int op = program.getCurrentOpIntValue();
+ MessageCall msg = new MessageCall(
+ op, adjustedCallEnergy, codeAddress, value, inDataOffs, inDataSize,
+ outDataOffs, outDataSize, tokenId, isTokenTransferMsg);
+
+ PrecompiledContracts.PrecompiledContract contract =
+ PrecompiledContracts.getContractForAddress(codeAddress);
+ if (contract != null) {
+ if (program.isConstantCall()) {
+ contract = PrecompiledContracts.getOptimizedContractForConstant(contract);
+ }
+ program.callToPrecompiledAddress(msg, contract);
+ } else {
+ program.callToAddress(msg);
+ }
+ program.step();
+ }
+
+ public static void revertAction(Program program) {
+ DataWord offset = program.stackPop();
+ DataWord size = program.stackPop();
+
+ byte[] hReturn = program.memoryChunk(offset.intValueSafe(), size.intValueSafe());
+ program.setHReturn(hReturn);
+
+ program.step();
+ program.stop();
+
+ program.getResult().setRevert();
+ }
+
+ public static void suicideAction(Program program) {
+ if (program.isStaticCall()) {
+ throw new Program.StaticCallModificationException();
+ }
+
+ if (!program.canSuicide()) {
+ program.getResult().setRevert();
+ } else {
+ DataWord address = program.stackPop();
+ program.suicide(address);
+ }
+
+ 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
new file mode 100644
index 00000000000..8c078e843a2
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/OperationRegistry.java
@@ -0,0 +1,739 @@
+package org.tron.core.vm;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BooleanSupplier;
+import org.tron.core.vm.config.VMConfig;
+
+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
+ }
+
+ private static final Map tableMap = new HashMap<>();
+
+ 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() {
+ JumpTable table = newBaseOperationSet();
+ appendTransferTrc10Operations(table);
+ appendConstantinopleOperations(table);
+ appendSolidity059Operations(table);
+ appendIstanbulOperations(table);
+ appendFreezeOperations(table);
+ appendVoteOperations(table);
+ appendLondonOperations(table);
+ return table;
+ }
+
+ public static JumpTable newTronV11OperationSet() {
+ return newTronV10OperationSet();
+ }
+
+ public static JumpTable newTronV12OperationSet() {
+ JumpTable table = newTronV11OperationSet();
+ appendFreezeV2Operations(table);
+ appendDelegateOperations(table);
+ return table;
+ }
+
+ 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() {
+ // 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()) {
+ adjustMemOperations(table);
+ }
+
+ if (VMConfig.allowEnergyAdjustment()) {
+ adjustForFairEnergy(table);
+ }
+
+ if (VMConfig.allowTvmSelfdestructRestriction()) {
+ adjustSelfdestruct(table);
+ }
+
+ if (VMConfig.allowTvmOsaka()) {
+ adjustVoteWitnessCost(table);
+ }
+
+ return table;
+ }
+
+ public static JumpTable newBaseOperationSet() {
+ JumpTable table = new JumpTable();
+
+ table.set(new Operation(
+ Op.STOP, 0, 0,
+ EnergyCost::getZeroTierCost,
+ OperationActions::stopAction));
+
+ table.set(new Operation(
+ Op.ADD, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::addAction));
+
+ table.set(new Operation(
+ Op.MUL, 2, 1,
+ EnergyCost::getLowTierCost,
+ OperationActions::mulAction));
+
+ table.set(new Operation(
+ Op.SUB, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::subAction));
+
+ table.set(new Operation(
+ Op.DIV, 2, 1,
+ EnergyCost::getLowTierCost,
+ OperationActions::divAction));
+
+ table.set(new Operation(
+ Op.SDIV, 2, 1,
+ EnergyCost::getLowTierCost,
+ OperationActions::sdivAction));
+
+ table.set(new Operation(
+ Op.MOD, 2, 1,
+ EnergyCost::getLowTierCost,
+ OperationActions::modAction));
+
+ table.set(new Operation(
+ Op.SMOD, 2, 1,
+ EnergyCost::getLowTierCost,
+ OperationActions::sModAction));
+
+ table.set(new Operation(
+ Op.ADDMOD, 3, 1,
+ EnergyCost::getMidTierCost,
+ OperationActions::addModAction));
+
+ table.set(new Operation(
+ Op.MULMOD, 3, 1,
+ EnergyCost::getMidTierCost,
+ OperationActions::mulModAction));
+
+ table.set(new Operation(
+ Op.EXP, 2, 1,
+ EnergyCost::getExpCost,
+ OperationActions::expAction));
+
+ table.set(new Operation(
+ Op.SIGNEXTEND, 2, 1,
+ EnergyCost::getLowTierCost,
+ OperationActions::signExtendAction));
+
+ table.set(new Operation(
+ Op.LT, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::ltAction));
+
+ table.set(new Operation(
+ Op.GT, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::gtAction));
+
+ table.set(new Operation(
+ Op.SLT, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::sltAction));
+
+ table.set(new Operation(
+ Op.SGT, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::sgtAction));
+
+ table.set(new Operation(
+ Op.EQ, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::eqAction));
+
+ table.set(new Operation(
+ Op.ISZERO, 1, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::isZeroAction));
+
+ table.set(new Operation(
+ Op.AND, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::andAction));
+
+ table.set(new Operation(
+ Op.OR, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::orAction));
+
+ table.set(new Operation(
+ Op.XOR, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::xorAction));
+
+ table.set(new Operation(
+ Op.NOT, 1, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::notAction));
+
+ table.set(new Operation(
+ Op.BYTE, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::byteAction));
+
+ table.set(new Operation(
+ Op.SHA3, 2, 1,
+ EnergyCost::getSha3Cost,
+ OperationActions::sha3Action));
+
+ table.set(new Operation(
+ Op.ADDRESS, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::addressAction));
+
+ table.set(new Operation(
+ Op.BALANCE, 1, 1,
+ EnergyCost::getBalanceCost,
+ OperationActions::balanceAction));
+
+ table.set(new Operation(
+ Op.ORIGIN, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::originAction));
+
+ table.set(new Operation(
+ Op.CALLER, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::callerAction));
+
+ table.set(new Operation(
+ Op.CALLVALUE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::callValueAction));
+
+ table.set(new Operation(
+ Op.CALLDATALOAD, 1, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::callDataLoadAction));
+
+ table.set(new Operation(
+ Op.CALLDATASIZE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::callDataSizeAction));
+
+ table.set(new Operation(
+ Op.CALLDATACOPY, 3, 0,
+ EnergyCost::getCallDataCopyCost,
+ OperationActions::callDataCopyAction));
+
+ table.set(new Operation(
+ Op.CODESIZE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::codeSizeAction));
+
+ table.set(new Operation(
+ Op.CODECOPY, 3, 0,
+ EnergyCost::getCodeCopyCost,
+ OperationActions::codeCopyAction));
+
+ table.set(new Operation(
+ Op.RETURNDATASIZE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::returnDataSizeAction));
+
+ table.set(new Operation(
+ Op.RETURNDATACOPY, 3, 0,
+ EnergyCost::getReturnDataCopyCost,
+ OperationActions::returnDataCopyAction));
+
+ table.set(new Operation(
+ Op.GASPRICE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::gasPriceAction));
+
+ table.set(new Operation(
+ Op.EXTCODESIZE, 1, 1,
+ EnergyCost::getExtCodeSizeCost,
+ OperationActions::extCodeSizeAction));
+
+ table.set(new Operation(
+ Op.EXTCODECOPY, 4, 0,
+ EnergyCost::getExtCodeCopyCost,
+ OperationActions::extCodeCopyAction));
+
+ table.set(new Operation(
+ Op.BLOCKHASH, 1, 1,
+ EnergyCost::getExtTierCost,
+ OperationActions::blockHashAction));
+
+ table.set(new Operation(
+ Op.COINBASE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::coinBaseAction));
+
+ table.set(new Operation(
+ Op.TIMESTAMP, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::timeStampAction));
+
+ table.set(new Operation(
+ Op.NUMBER, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::numberAction));
+
+ table.set(new Operation(
+ Op.DIFFICULTY, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::difficultyAction));
+
+ table.set(new Operation(
+ Op.GASLIMIT, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::gasLimitAction));
+
+ table.set(new Operation(
+ Op.POP, 1, 0,
+ EnergyCost::getBaseTierCost,
+ OperationActions::popAction));
+
+ table.set(new Operation(
+ Op.MLOAD, 1, 1,
+ EnergyCost::getMloadCost,
+ OperationActions::mLoadAction));
+
+ table.set(new Operation(
+ Op.MSTORE, 2, 0,
+ EnergyCost::getMStoreCost,
+ OperationActions::mStoreAction));
+
+ table.set(new Operation(
+ Op.MSTORE8, 2, 0,
+ EnergyCost::getMStore8Cost,
+ OperationActions::mStore8Action));
+
+ table.set(new Operation(
+ Op.SLOAD, 1, 1,
+ EnergyCost::getSloadCost,
+ OperationActions::sLoadAction));
+
+ table.set(new Operation(
+ Op.SSTORE, 2, 0,
+ EnergyCost::getSstoreCost,
+ OperationActions::sStoreAction));
+
+ table.set(new Operation(
+ Op.JUMP, 1, 0,
+ EnergyCost::getMidTierCost,
+ OperationActions::jumpAction));
+
+ table.set(new Operation(
+ Op.JUMPI, 2, 0,
+ EnergyCost::getHighTierCost,
+ OperationActions::jumpIAction));
+
+ table.set(new Operation(
+ Op.PC, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::pcAction));
+
+ table.set(new Operation(
+ Op.MSIZE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::mSizeAction));
+
+ table.set(new Operation(
+ Op.GAS, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::gasAction));
+
+ table.set(new Operation(
+ Op.JUMPDEST, 0, 0,
+ EnergyCost::getSpecialTierCost,
+ OperationActions::jumpDestAction));
+
+ for (int i = 0; i < 32; i++) {
+ table.set(new Operation(
+ Op.PUSH1 + i, 0, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::pushAction));
+ }
+
+ for (int i = 0; i < 16; i++) {
+ table.set(new Operation(
+ Op.DUP1 + i, 1 + i, 2 + i,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::dupAction));
+ }
+
+ for (int i = 0; i < 16; i++) {
+ table.set(new Operation(
+ Op.SWAP1 + i, 2 + i, 2 + i,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::swapAction));
+ }
+
+ for (int i = 0; i <= 4; i++) {
+ table.set(new Operation(
+ Op.LOG0 + i, 2 + i, 0,
+ EnergyCost::getLogCost,
+ OperationActions::logAction));
+ }
+
+ table.set(new Operation(
+ Op.CREATE, 3, 1,
+ EnergyCost::getCreateCost,
+ OperationActions::createAction));
+
+ table.set(new Operation(
+ Op.CALL, 7, 1,
+ EnergyCost::getCallCost,
+ OperationActions::callAction));
+
+ table.set(new Operation(
+ Op.CALLCODE, 7, 1,
+ EnergyCost::getCallCodeCost,
+ OperationActions::callCodeAction));
+
+ table.set(new Operation(
+ Op.RETURN, 2, 0,
+ EnergyCost::getReturnCost,
+ OperationActions::returnAction));
+
+ table.set(new Operation(
+ Op.DELEGATECALL, 6, 1,
+ EnergyCost::getDelegateCallCost,
+ OperationActions::delegateCallAction));
+
+ table.set(new Operation(
+ Op.STATICCALL, 6, 1,
+ EnergyCost::getStaticCallCost,
+ OperationActions::staticCallAction));
+
+ table.set(new Operation(
+ Op.REVERT, 2, 0,
+ EnergyCost::getRevertCost,
+ OperationActions::revertAction));
+
+ table.set(new Operation(
+ Op.SUICIDE, 1, 0,
+ EnergyCost::getSuicideCost,
+ OperationActions::suicideAction));
+
+ return table;
+ }
+
+ public static void appendTransferTrc10Operations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmTransferTrc10;
+
+ table.set(new Operation(
+ Op.CALLTOKEN, 8, 1,
+ EnergyCost::getCallTokenCost,
+ OperationActions::callTokenAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.TOKENBALANCE, 2, 1,
+ EnergyCost::getBalanceCost,
+ OperationActions::tokenBalanceAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.CALLTOKENVALUE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::callTokenValueAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.CALLTOKENID, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::callTokenIdAction,
+ proposal));
+ }
+
+ public static void appendConstantinopleOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmConstantinople;
+
+ table.set(new Operation(
+ Op.SHL, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::shlAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.SHR, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::shrAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.SAR, 2, 1,
+ EnergyCost::getVeryLowTierCost,
+ OperationActions::sarAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.CREATE2, 4, 1,
+ EnergyCost::getCreate2Cost,
+ OperationActions::create2Action,
+ proposal));
+
+ table.set(new Operation(
+ Op.EXTCODEHASH, 1, 1,
+ EnergyCost::getExtCodeHashCost,
+ OperationActions::extCodeHashAction,
+ proposal));
+ }
+
+ public static void appendSolidity059Operations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmSolidity059;
+
+ table.set(new Operation(
+ Op.ISCONTRACT, 1, 1,
+ EnergyCost::getBalanceCost,
+ OperationActions::isContractAction,
+ proposal));
+ }
+
+ public static void appendIstanbulOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmIstanbul;
+
+ table.set(new Operation(
+ Op.CHAINID, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::chainIdAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.SELFBALANCE, 0, 1,
+ EnergyCost::getLowTierCost,
+ OperationActions::selfBalanceAction,
+ proposal));
+ }
+
+ public static void appendFreezeOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmFreeze;
+
+ table.set(new Operation(
+ Op.FREEZE, 3, 1,
+ EnergyCost::getFreezeCost,
+ OperationActions::freezeAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.UNFREEZE, 2, 1,
+ EnergyCost::getUnfreezeCost,
+ OperationActions::unfreezeAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.FREEZEEXPIRETIME, 2, 1,
+ EnergyCost::getFreezeExpireTimeCost,
+ OperationActions::freezeExpireTimeAction,
+ proposal));
+ }
+
+ public static void appendVoteOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmVote;
+
+ table.set(new Operation(
+ Op.VOTEWITNESS, 4, 1,
+ EnergyCost::getVoteWitnessCost,
+ OperationActions::voteWitnessAction,
+ proposal));
+
+ table.set(new Operation(
+ Op.WITHDRAWREWARD, 0, 1,
+ EnergyCost::getWithdrawRewardCost,
+ OperationActions::withdrawRewardAction,
+ proposal));
+ }
+
+ public static void appendLondonOperations(JumpTable table) {
+ BooleanSupplier proposal = VMConfig::allowTvmLondon;
+
+ table.set(new Operation(
+ Op.BASEFEE, 0, 1,
+ EnergyCost::getBaseTierCost,
+ OperationActions::baseFeeAction,
+ proposal));
+ }
+
+ public static void adjustMemOperations(JumpTable table) {
+ table.set(new Operation(
+ Op.MLOAD, 1, 1,
+ EnergyCost::getMloadCost2,
+ OperationActions::mLoadAction));
+
+ table.set(new Operation(
+ Op.MSTORE, 2, 0,
+ EnergyCost::getMStoreCost2,
+ OperationActions::mStoreAction));
+
+ table.set(new Operation(
+ Op.MSTORE8, 2, 0,
+ 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 5beed1638af..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,43 +1,38 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.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;
import static org.tron.common.utils.BIUtil.isZero;
import static org.tron.common.utils.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.tron.common.utils.ByteUtil.bytesToBigInteger;
+import static org.tron.common.utils.ByteUtil.longTo32Bytes;
+import static org.tron.common.utils.ByteUtil.merge;
import static org.tron.common.utils.ByteUtil.numberOfLeadingZeros;
import static org.tron.common.utils.ByteUtil.parseBytes;
import static org.tron.common.utils.ByteUtil.parseWord;
import static org.tron.common.utils.ByteUtil.stripLeadingZeroes;
-import static org.tron.core.db.TransactionTrace.convertToTronAddress;
+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;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Callable;
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;
@@ -46,7 +41,16 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
-import org.tron.common.crypto.ECKey;
+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;
@@ -55,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;
@@ -62,18 +68,22 @@
import org.tron.common.utils.ByteArray;
import org.tron.common.utils.ByteUtil;
import org.tron.common.utils.Sha256Hash;
+import org.tron.common.zksnark.JLibrustzcash;
+import org.tron.common.zksnark.LibrustzcashParam;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.TransactionCapsule;
+import org.tron.core.capsule.WitnessCapsule;
+import org.tron.core.db.TransactionTrace;
+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;
-import org.tron.common.crypto.SignatureInterface;
-
-/**
- * @author Roman Mandeleil
- * @since 09.01.2015
- */
@Slf4j(topic = "VM")
public class PrecompiledContracts {
@@ -90,6 +100,35 @@ public class PrecompiledContracts {
private static final BatchValidateSign batchValidateSign = new BatchValidateSign();
private static final ValidateMultiSign validateMultiSign = new ValidateMultiSign();
+ private static final VerifyMintProof verifyMintProof = new VerifyMintProof();
+ private static final VerifyTransferProof verifyTransferProof = new VerifyTransferProof();
+ private static final VerifyBurnProof verifyBurnProof = new VerifyBurnProof();
+
+ private static final MerkleHash merkleHash = new MerkleHash();
+
+ private static final RewardBalance rewardBalance = new RewardBalance();
+ private static final IsSrCandidate isSrCandidate = new IsSrCandidate();
+ private static final VoteCount voteCount = new VoteCount();
+ private static final UsedVoteCount usedVoteCount = new UsedVoteCount();
+ private static final ReceivedVoteCount receivedVoteCount = new ReceivedVoteCount();
+ private static final TotalVoteCount totalVoteCount = new TotalVoteCount();
+
+ 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");
@@ -111,6 +150,76 @@ public class PrecompiledContracts {
"0000000000000000000000000000000000000000000000000000000000000009");
private static final DataWord validateMultiSignAddr = new DataWord(
"000000000000000000000000000000000000000000000000000000000000000a");
+ private static final DataWord verifyMintProofAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000001");
+ private static final DataWord verifyTransferProofAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000002");
+ private static final DataWord verifyBurnProofAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000003");
+ private static final DataWord merkleHashAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000004");
+ private static final DataWord rewardBalanceAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000005");
+ private static final DataWord isSrCandidateAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000006");
+ private static final DataWord voteCountAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000007");
+ private static final DataWord usedVoteCountAddr = new DataWord(
+ "0000000000000000000000000000000000000000000000000000000001000008");
+ private static final DataWord receivedVoteCountAddr = new DataWord(
+ "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) {
@@ -148,6 +257,81 @@ public static PrecompiledContract getContractForAddress(DataWord address) {
if (VMConfig.allowTvmSolidity059() && address.equals(validateMultiSignAddr)) {
return validateMultiSign;
}
+ if (VMConfig.allowShieldedTRC20Transaction() && address.equals(verifyMintProofAddr)) {
+ return verifyMintProof;
+ }
+ if (VMConfig.allowShieldedTRC20Transaction() && address.equals(verifyTransferProofAddr)) {
+ return verifyTransferProof;
+ }
+ if (VMConfig.allowShieldedTRC20Transaction() && address.equals(verifyBurnProofAddr)) {
+ return verifyBurnProof;
+ }
+ if (VMConfig.allowShieldedTRC20Transaction() && address.equals(merkleHashAddr)) {
+ return merkleHash;
+ }
+ if (VMConfig.allowTvmVote() && address.equals(rewardBalanceAddr)) {
+ return rewardBalance;
+ }
+ if (VMConfig.allowTvmVote() && address.equals(isSrCandidateAddr)) {
+ return isSrCandidate;
+ }
+ if (VMConfig.allowTvmVote() && address.equals(voteCountAddr)) {
+ return voteCount;
+ }
+ if (VMConfig.allowTvmVote() && address.equals(usedVoteCountAddr)) {
+ return usedVoteCount;
+ }
+ if (VMConfig.allowTvmVote() && address.equals(receivedVoteCountAddr)) {
+ return receivedVoteCount;
+ }
+ if (VMConfig.allowTvmVote() && address.equals(totalVoteCountAddr)) {
+ return totalVoteCount;
+ }
+ if (VMConfig.allowTvmCompatibleEvm() && address.equals(ethRipemd160Addr)) {
+ return ethRipemd160;
+ }
+ 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;
}
@@ -165,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,
@@ -217,11 +411,33 @@ 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);
}
- public static abstract class PrecompiledContract {
+ 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];
private byte[] callerAddress;
@@ -277,6 +493,12 @@ protected byte[] dataOne() {
return ret;
}
+ protected byte[] dataBoolean(boolean result) {
+ if (result) {
+ return DataWord.ONE().getData();
+ }
+ return DataWord.ZERO().getData();
+ }
}
public static class Identity extends PrecompiledContract {
@@ -423,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) {
@@ -434,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()
@@ -459,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);
@@ -514,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();
@@ -539,9 +839,16 @@ public static class BN128Addition extends PrecompiledContract {
@Override
public long getEnergyForData(byte[] data) {
+ if (VMConfig.allowTvmIstanbul()) {
+ return getEnergyForDataIstanbul(data);
+ }
return 500;
}
+ private long getEnergyForDataIstanbul(byte[] data) {
+ return 150;
+ }
+
@Override
public Pair execute(byte[] data) {
@@ -586,9 +893,16 @@ public static class BN128Multiplication extends PrecompiledContract {
@Override
public long getEnergyForData(byte[] data) {
+ if (VMConfig.allowTvmIstanbul()) {
+ return getEnergyForDataIstanbul(data);
+ }
return 40000;
}
+ private long getEnergyForDataIstanbul(byte[] data) {
+ return 6000;
+ }
+
@Override
public Pair execute(byte[] data) {
@@ -632,14 +946,22 @@ public static class BN128Pairing extends PrecompiledContract {
@Override
public long getEnergyForData(byte[] data) {
-
+ if (VMConfig.allowTvmIstanbul()) {
+ return getEnergyForDataIstanbul(data);
+ }
if (data == null) {
return 100000;
}
-
return 80000L * (data.length / PAIR_SIZE) + 100000;
}
+ private long getEnergyForDataIstanbul(byte[] data) {
+ if (data == null) {
+ return 45000;
+ }
+ return 34000L * (data.length / PAIR_SIZE) + 45000;
+ }
+
@Override
public Pair execute(byte[] data) {
@@ -708,35 +1030,47 @@ 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
public long getEnergyForData(byte[] data) {
- int cnt = (data.length / WORD_SIZE - 5) / 5;
+ long cnt = (data.length / WORD_SIZE - 5) / 5;
// one sign 1500, half of ecrecover
- return (long) (cnt * ENGERYPERSIGN);
+ return cnt * ENGERYPERSIGN;
}
@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[] addr = words[0].getLast20Bytes();
+ byte[] address = words[0].toTronAddress();
int permissionId = words[1].intValueSafe();
byte[] data = words[2].getData();
- byte[] combine = ByteUtil
- .merge(convertToTronAddress(addr), ByteArray.fromInt(permissionId), data);
+ byte[] combine = ByteUtil.merge(address, ByteArray.fromInt(permissionId), data);
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);
}
- AccountCapsule account = this.getDeposit().getAccount(convertToTronAddress(addr));
+ AccountCapsule account = this.getDeposit().getAccount(address);
if (account != null) {
try {
Permission permission = account.getPermissionById(permissionId);
@@ -745,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
@@ -756,6 +1095,7 @@ public Pair execute(byte[] rawData) {
}
totalWeight += weight;
executedSignList.add(sign);
+ executedSignList.add(recoveredAddr);
}
if (totalWeight >= permission.getThreshold()) {
@@ -763,6 +1103,9 @@ public Pair execute(byte[] rawData) {
}
}
} catch (Throwable t) {
+ if (t instanceof OutOfTimeException) {
+ throw t;
+ }
logger.info("ValidateMultiSign error:{}", t.getMessage());
}
}
@@ -773,18 +1116,22 @@ 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);
+ workers = ExecutorServiceManager.newFixedThreadPool(workersName,
+ Runtime.getRuntime().availableProcessors() / 2 + 1);
}
@Override
public long getEnergyForData(byte[] data) {
- int cnt = (data.length / WORD_SIZE - 5) / 6;
+ long cnt = (data.length / WORD_SIZE - 5) / 6;
// one sign 1500, half of ecrecover
- return (long) (cnt * ENGERYPERSIGN);
+ return cnt * ENGERYPERSIGN;
}
@Override
@@ -792,16 +1139,33 @@ public Pair execute(byte[] data) {
try {
return doExecute(data);
} catch (Throwable t) {
+ if (t instanceof InterruptedException){
+ Thread.currentThread().interrupt();
+ }
return Pair.of(true, new byte[WORD_SIZE]);
}
}
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;
@@ -874,4 +1238,1146 @@ private static class RecoverAddrResult {
}
+ public abstract static class VerifyProof extends PrecompiledContract {
+
+ protected static final long TREE_WIDTH = 1L << 32;
+ protected static final byte[][] UNCOMMITTED = new byte[32][32];
+
+ static {
+ UNCOMMITTED[0] = ByteArray.fromHexString(
+ "0100000000000000000000000000000000000000000000000000000000000000");
+ try {
+ for (int i = 0; i < 31; i++) {
+ JLibrustzcash.librustzcashMerkleHash(
+ new LibrustzcashParam.MerkleHashParams(
+ i, UNCOMMITTED[i], UNCOMMITTED[i], UNCOMMITTED[i + 1]));
+ }
+ } catch (Throwable any) {
+ logger.info("Initialize UNCOMMITTED array failed:{}", any.getMessage());
+ }
+ }
+
+ protected long parseLong(byte[] data, int idx) {
+ byte[] bytes = parseBytes(data, idx, 32);
+ return new DataWord(bytes).longValueSafe();
+ }
+
+ protected int parseInt(byte[] data, int idx) {
+ byte[] bytes = parseBytes(data, idx, 32);
+ return new DataWord(bytes).intValueSafe();
+ }
+
+ private int getFrontierSlot(long leafIndex) {
+ int slot = 0;
+ if (leafIndex % 2 != 0) {
+ int exp1 = 1;
+ long pow1 = 2;
+ long pow2 = pow1 << 1;
+ while (slot == 0) {
+ if ((leafIndex + 1 - pow1) % pow2 == 0) {
+ slot = exp1;
+ } else {
+ pow1 = pow2;
+ pow2 = pow2 << 1;
+ exp1++;
+ }
+ }
+ }
+ return slot;
+ }
+
+ protected Pair insertLeaves(
+ byte[][] frontier, long leafCount, byte[][] leafValue) {
+ long nodeIndex = 0;
+ boolean success = true;
+ byte[] leftInput;
+ byte[] rightInput;
+ byte[] hash = new byte[32];
+ byte[] nodeValue = new byte[32];
+ int cmCount = leafValue.length;
+ int[] slot = new int[cmCount];
+ for (int i = 0; i < cmCount; i++) {
+ slot[i] = getFrontierSlot(leafCount + i);
+ }
+ int resultArrayLength = 32;
+ for (int i = 0; i < cmCount; i++) {
+ resultArrayLength += (slot[i] + 1) * 32;
+ }
+
+ byte[] result = new byte[resultArrayLength];
+ try {
+ int offset = 0;
+ for (int i = 0; i < cmCount; i++) {
+ byte[] slotArray = DataWord.of((byte) (slot[i] & 0xFF)).getData();
+ System.arraycopy(slotArray, 0, result, offset, 32);
+ offset += 32;
+ nodeIndex = i + leafCount + TREE_WIDTH - 1;
+ System.arraycopy(leafValue[i], 0, nodeValue, 0, 32);
+ if (slot[i] == 0) {
+ System.arraycopy(nodeValue, 0, frontier[0], 0, 32);
+ continue;
+ }
+ for (int level = 1; level <= slot[i]; level++) {
+ if (nodeIndex % 2 == 0) {
+ leftInput = frontier[level - 1];
+ rightInput = nodeValue;
+ nodeIndex = (nodeIndex - 1) / 2;
+ } else {
+ leftInput = nodeValue;
+ rightInput = UNCOMMITTED[level - 1];
+ nodeIndex = nodeIndex / 2;
+ }
+ JLibrustzcash.librustzcashMerkleHash(new LibrustzcashParam.MerkleHashParams(
+ level - 1, leftInput, rightInput, hash));
+ System.arraycopy(hash, 0, nodeValue, 0, 32);
+ System.arraycopy(hash, 0, result, offset, 32);
+ offset += 32;
+ }
+ System.arraycopy(nodeValue, 0, frontier[slot[i]], 0, 32);
+ }
+
+ for (int level = slot[cmCount - 1] + 1; level <= 32; level++) {
+ if (nodeIndex % 2 == 0) {
+ leftInput = frontier[level - 1];
+ rightInput = nodeValue;
+ nodeIndex = (nodeIndex - 1) / 2;
+ } else {
+ leftInput = nodeValue;
+ rightInput = UNCOMMITTED[level - 1];
+ nodeIndex = nodeIndex / 2;
+ }
+ JLibrustzcash.librustzcashMerkleHash(new LibrustzcashParam.MerkleHashParams(
+ level - 1, leftInput, rightInput, hash));
+ System.arraycopy(hash, 0, nodeValue, 0, 32);
+ }
+ System.arraycopy(nodeValue, 0, result, offset, 32);
+ } catch (Throwable any) {
+ success = false;
+ String errorMsg = any.getMessage();
+ if (errorMsg == null && any.getCause() != null) {
+ errorMsg = any.getCause().getMessage();
+ }
+ logger.info("Insert leaves failed: " + errorMsg);
+ }
+ if (success) {
+ return Pair.of(true, merge(DataWord.ONE().getData(), result));
+ } else {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ }
+ }
+
+ public static class VerifyMintProof extends VerifyProof {
+
+ private static final int SIZE = 1504;
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 150000;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ if (data.length != SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ boolean result;
+ long ctx = JLibrustzcash.librustzcashSaplingVerificationCtxInit();
+ try {
+ byte[] cm = new byte[32];
+ byte[] cv = new byte[32];
+ byte[] epk = new byte[32];
+ byte[] proof = new byte[192];
+ byte[] bindingSig = new byte[64];
+ byte[] signHash = new byte[32];
+ byte[][] frontier = new byte[33][32];
+
+ System.arraycopy(data, 0, cm, 0, 32);
+ System.arraycopy(data, 32, cv, 0, 32);
+ System.arraycopy(data, 64, epk, 0, 32);
+ System.arraycopy(data, 96, proof, 0, 192);
+ System.arraycopy(data, 288, bindingSig, 0, 64);
+ long value = parseLong(data, 352);
+ System.arraycopy(data, 384, signHash, 0, 32);
+ for (int i = 0; i < 33; i++) {
+ System.arraycopy(data, i * 32 + 416, frontier[i], 0, 32);
+ }
+ long leafCount = parseLong(data, 1472);
+ if (leafCount >= TREE_WIDTH) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ result = JLibrustzcash.librustzcashSaplingCheckOutput(
+ new LibrustzcashParam.CheckOutputParams(ctx, cv, cm, epk, proof));
+ long valueBalance = -value;
+ result = result && JLibrustzcash.librustzcashSaplingFinalCheck(
+ new LibrustzcashParam.FinalCheckParams(ctx, valueBalance, bindingSig, signHash));
+
+ if (result) {
+ byte[][] leafValue = new byte[1][32];
+ System.arraycopy(cm, 0, leafValue[0], 0, 32);
+ return insertLeaves(frontier, leafCount, leafValue);
+ } else {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ } catch (Throwable any) {
+ String errorMsg = any.getMessage();
+ if (errorMsg == null && any.getCause() != null) {
+ errorMsg = any.getCause().getMessage();
+ }
+ logger.info("VerifyMintProof exception " + errorMsg);
+ } finally {
+ JLibrustzcash.librustzcashSaplingVerificationCtxFree(ctx);
+ }
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ }
+
+ 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 = ExecutorServiceManager.newFixedThreadPool(constantCallName, 5);
+ workersInNonConstantCall = ExecutorServiceManager.newFixedThreadPool(nonConstantCallName, 5);
+ }
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 200000;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ if (!Arrays.asList(SIZE).contains(data.length)) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ try {
+ byte[] bindingSig = new byte[64];
+ byte[] signHash = new byte[32];
+ byte[][] frontier = new byte[33][32];
+ //parse unfixed field offset
+ int spendOffset = parseInt(data, 0);
+ int spendAuthSigOffset = parseInt(data, 32);
+ int receiveOffset = parseInt(data, 64);
+ System.arraycopy(data, 96, bindingSig, 0, 64);
+ System.arraycopy(data, 160, signHash, 0, 32);
+ //parse value
+ long value = parseLong(data, 192);
+ for (int i = 0; i < 33; i++) {
+ System.arraycopy(data, i * 32 + 224, frontier[i], 0, 32);
+ }
+ long leafCount = parseLong(data, 1280);
+ if (leafCount >= TREE_WIDTH - 1) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ int spendCount = parseInt(data, spendOffset);
+ int spendAuthSigCount = parseInt(data, spendAuthSigOffset);
+ int receiveCount = parseInt(data, receiveOffset);
+
+ if (spendCount != spendAuthSigCount || spendCount < 1
+ || spendCount > 2 || receiveCount < 1 || receiveCount > 2) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ byte[][] anchor = new byte[spendCount][32];
+ byte[][] nullifier = new byte[spendCount][32];
+ byte[][] spendCv = new byte[spendCount][32];
+ byte[][] rk = new byte[spendCount][32];
+ byte[][] spendProof = new byte[spendCount][192];
+ byte[][] spendAuthSig = new byte[spendCount][64];
+ byte[][] receiveCm = new byte[receiveCount][32];
+ byte[][] receiveCv = new byte[receiveCount][32];
+ byte[][] receiveEpk = new byte[receiveCount][32];
+ byte[][] receiveProof = new byte[receiveCount][192];
+
+ //spend
+ spendOffset += 32;
+ for (int i = 0; i < spendCount; i++) {
+ System.arraycopy(data, spendOffset + 320 * i, nullifier[i], 0, 32);
+ System.arraycopy(data, spendOffset + 320 * i + 32, anchor[i], 0, 32);
+ System.arraycopy(data, spendOffset + 320 * i + 64, spendCv[i], 0, 32);
+ System.arraycopy(data, spendOffset + 320 * i + 96, rk[i], 0, 32);
+ System.arraycopy(data, spendOffset + 320 * i + 128, spendProof[i], 0, 192);
+ }
+ spendAuthSigOffset += 32;
+ for (int i = 0; i < spendCount; i++) {
+ System.arraycopy(data, spendAuthSigOffset + 64 * i, spendAuthSig[i], 0, 64);
+ }
+ //output
+ receiveOffset += 32;
+ for (int i = 0; i < receiveCount; i++) {
+ System.arraycopy(data, receiveOffset + 288 * i, receiveCm[i], 0, 32);
+ System.arraycopy(data, receiveOffset + 288 * i + 32, receiveCv[i], 0, 32);
+ System.arraycopy(data, receiveOffset + 288 * i + 64, receiveEpk[i], 0, 32);
+ System.arraycopy(data, receiveOffset + 288 * i + 96, receiveProof[i], 0, 192);
+ }
+
+ //copy each spendCv(receiveCv) into spendCvs(receiveCvs)
+ byte[] spendCvs = new byte[spendCount * 32];
+ byte[] receiveCvs = new byte[receiveCount * 32];
+ for (int i = 0; i < spendCount; i++) {
+ System.arraycopy(spendCv[i], 0, spendCvs, 32 * i, 32);
+ }
+ for (int i = 0; i < receiveCount; i++) {
+ System.arraycopy(receiveCv[i], 0, receiveCvs, 32 * i, 32);
+ }
+ //check duplicate nullifiers
+ HashSet nfSet = new HashSet<>();
+ for (byte[] nf : nullifier) {
+ if (nfSet.contains(ByteArray.toHexString(nf))) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ nfSet.add(ByteArray.toHexString(nf));
+ }
+ //check duplicate output note
+ HashSet cmSet = new HashSet<>();
+ for (byte[] cm : receiveCm) {
+ if (cmSet.contains(ByteArray.toHexString(cm))) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ cmSet.add(ByteArray.toHexString(cm));
+ }
+
+ int threadCount = spendCount + receiveCount + 1;
+ CountDownLatch countDownLatch = new CountDownLatch(threadCount);
+ List> futures = new ArrayList<>(threadCount);
+ ExecutorService workers;
+ if (isConstantCall()) {
+ workers = workersInConstantCall;
+ } else {
+ workers = workersInNonConstantCall;
+ }
+
+ // submit check spend task
+ for (int i = 0; i < spendCount; i++) {
+ Future futureCheckSpend = workers
+ .submit(new SaplingCheckSpendTask(countDownLatch, spendCv[i], anchor[i],
+ nullifier[i], rk[i], spendProof[i], spendAuthSig[i], signHash));
+ futures.add(futureCheckSpend);
+ }
+ //submit check output task
+ for (int i = 0; i < receiveCount; i++) {
+ Future futureCheckOutput = workers
+ .submit(new SaplingCheckOutputTask(countDownLatch, receiveCv[i], receiveCm[i],
+ receiveEpk[i], receiveProof[i]));
+ futures.add(futureCheckOutput);
+ }
+ // submit check binding signature
+ Future futureCheckBindingSig = workers
+ .submit(new SaplingCheckBingdingSig(countDownLatch, value, bindingSig,
+ signHash, spendCvs, spendCount * 32, receiveCvs, receiveCount * 32));
+ futures.add(futureCheckBindingSig);
+
+ boolean withNoTimeout = countDownLatch.await(getCPUTimeLeftInNanoSecond(),
+ TimeUnit.NANOSECONDS);
+ boolean checkResult = true;
+ for (Future future : futures) {
+ boolean eachTaskResult = future.get();
+ checkResult = checkResult && eachTaskResult;
+ }
+ if (checkResult) {
+ return insertLeaves(frontier, leafCount, receiveCm);
+ } else {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ logger.info("VerifyTransferProof exception: " + e.getMessage());
+ } catch (Throwable any) {
+ String errorMsg = any.getMessage();
+ if (errorMsg == null && any.getCause() != null) {
+ errorMsg = any.getCause().getMessage();
+ }
+ logger.info("VerifyTransferProof exception: " + errorMsg);
+ }
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+
+ private static class SaplingCheckSpendTask implements Callable {
+
+ private byte[] cv;
+ private byte[] anchor;
+ private byte[] nullifier;
+ private byte[] rk;
+ private byte[] zkproof;
+ private byte[] spendAuthSig;
+ private byte[] signHash;
+
+ private CountDownLatch countDownLatch;
+
+ SaplingCheckSpendTask(CountDownLatch countDownLatch,
+ byte[] cv, byte[] anchor, byte[] nullifier, byte[] rk,
+ byte[] zkproof, byte[] spendAuthSig, byte[] signHash) {
+ this.cv = cv;
+ this.anchor = anchor;
+ this.nullifier = nullifier;
+ this.rk = rk;
+ this.zkproof = zkproof;
+ this.spendAuthSig = spendAuthSig;
+ this.signHash = signHash;
+ this.countDownLatch = countDownLatch;
+ }
+
+ @Override
+ public Boolean call() throws ZksnarkException {
+ boolean result;
+ try {
+ result = JLibrustzcash.librustzcashSaplingCheckSpendNew(
+ new LibrustzcashParam.CheckSpendNewParams(this.cv, this.anchor, this.nullifier,
+ this.rk, this.zkproof, this.spendAuthSig, this.signHash));
+ } catch (ZksnarkException e) {
+ throw e;
+ } finally {
+ countDownLatch.countDown();
+ }
+ return result;
+ }
+ }
+
+ private static class SaplingCheckOutputTask implements Callable {
+
+ private byte[] cv;
+ private byte[] cm;
+ private byte[] ephemeralKey;
+ private byte[] zkproof;
+
+ private CountDownLatch countDownLatch;
+
+ SaplingCheckOutputTask(CountDownLatch countDownLatch, byte[] cv, byte[] cm,
+ byte[] ephemeralKey, byte[] zkproof) {
+ this.cv = cv;
+ this.cm = cm;
+ this.ephemeralKey = ephemeralKey;
+ this.zkproof = zkproof;
+ this.countDownLatch = countDownLatch;
+ }
+
+ @Override
+ public Boolean call() throws ZksnarkException {
+ boolean result;
+ try {
+ result = JLibrustzcash.librustzcashSaplingCheckOutputNew(
+ new LibrustzcashParam.CheckOutputNewParams(this.cv, this.cm,
+ this.ephemeralKey, this.zkproof));
+ } catch (ZksnarkException e) {
+ throw e;
+ } finally {
+ countDownLatch.countDown();
+ }
+ return result;
+ }
+ }
+
+ private static class SaplingCheckBingdingSig implements Callable {
+
+ private long valueBalance;
+ private int spendCvLen;
+ private int receiveCvLen;
+ private byte[] bindingSig;
+ private byte[] signHash;
+ private byte[] spendCvs;
+ private byte[] receiveCvs;
+
+ private CountDownLatch countDownLatch;
+
+ SaplingCheckBingdingSig(CountDownLatch countDownLatch, long valueBalance, byte[] bindingSig,
+ byte[] signHash, byte[] spendCvs, int spendCvLen,
+ byte[] receiveCvs, int receiveCvLen) {
+ this.valueBalance = valueBalance;
+ this.bindingSig = bindingSig;
+ this.signHash = signHash;
+ this.spendCvs = spendCvs;
+ this.spendCvLen = spendCvLen;
+ this.receiveCvs = receiveCvs;
+ this.receiveCvLen = receiveCvLen;
+ this.countDownLatch = countDownLatch;
+ }
+
+ @Override
+ public Boolean call() throws ZksnarkException {
+ boolean result;
+ try {
+ result = JLibrustzcash.librustzcashSaplingFinalCheckNew(
+ new LibrustzcashParam.FinalCheckNewParams(this.valueBalance, this.bindingSig,
+ this.signHash, this.spendCvs, this.spendCvLen,
+ this.receiveCvs, this.receiveCvLen));
+ } catch (ZksnarkException e) {
+ throw e;
+ } finally {
+ countDownLatch.countDown();
+ }
+ return result;
+ }
+ }
+ }
+
+ public static class VerifyBurnProof extends VerifyProof {
+
+ private static final int SIZE = 512;
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 150000;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ if (data.length != SIZE) {
+ return Pair.of(true, DataWord.ZERO().getData());
+ }
+ boolean result;
+ long ctx = JLibrustzcash.librustzcashSaplingVerificationCtxInit();
+ try {
+ byte[] nullifier = new byte[32];
+ byte[] anchor = new byte[32];
+ byte[] cv = new byte[32];
+ byte[] rk = new byte[32];
+ byte[] proof = new byte[192];
+ byte[] spendAuthSig = new byte[64];
+ byte[] bindingSig = new byte[64];
+ byte[] signHash = new byte[32];
+ //spend
+ System.arraycopy(data, 0, nullifier, 0, 32);
+ System.arraycopy(data, 32, anchor, 0, 32);
+ System.arraycopy(data, 64, cv, 0, 32);
+ System.arraycopy(data, 96, rk, 0, 32);
+ System.arraycopy(data, 128, proof, 0, 192);
+ System.arraycopy(data, 320, spendAuthSig, 0, 64);
+ long value = parseLong(data, 384);
+ System.arraycopy(data, 416, bindingSig, 0, 64);
+ System.arraycopy(data, 480, signHash, 0, 32);
+
+ result = JLibrustzcash.librustzcashSaplingCheckSpend(
+ new LibrustzcashParam.CheckSpendParams(
+ ctx, cv, anchor, nullifier, rk, proof, spendAuthSig, signHash));
+ result = result && JLibrustzcash.librustzcashSaplingFinalCheck(
+ new LibrustzcashParam.FinalCheckParams(ctx, value, bindingSig, signHash));
+ } catch (Throwable any) {
+ result = false;
+ String errorMsg = any.getMessage();
+ if (errorMsg == null && any.getCause() != null) {
+ errorMsg = any.getCause().getMessage();
+ }
+ logger.info("VerifyBurnProof exception " + errorMsg);
+ } finally {
+ JLibrustzcash.librustzcashSaplingVerificationCtxFree(ctx);
+ }
+ return Pair.of(true, dataBoolean(result));
+ }
+ }
+
+ // compute Merkle Hash
+ public static class MerkleHash extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 500;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ byte[] left = new byte[32];
+ byte[] right = new byte[32];
+ byte[] hash = new byte[32];
+ boolean res = true;
+ try {
+ int level = parseInt(data);
+ System.arraycopy(data, 32, left, 0, 32);
+ System.arraycopy(data, 64, right, 0, 32);
+ JLibrustzcash.librustzcashMerkleHash(
+ new LibrustzcashParam.MerkleHashParams(level, left, right, hash));
+ } catch (Throwable any) {
+ res = false;
+ logger.info("Compute MerkleHash failed:{}", any.getMessage());
+ }
+ if (res) {
+ return Pair.of(true, hash);
+ } else {
+ return Pair.of(false, EMPTY_BYTE_ARRAY);
+ }
+ }
+
+ private int parseInt(byte[] data) {
+ byte[] bytes = parseBytes(data, 0, 32);
+ return new DataWord(bytes).intValueSafe();
+ }
+ }
+
+ public static class RewardBalance extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 500;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+
+ long rewardBalance = VoteRewardUtil.queryReward(
+ TransactionTrace.convertToTronAddress(getCallerAddress()), getDeposit());
+ return Pair.of(true, longTo32Bytes(rewardBalance));
+ }
+ }
+
+ public static class IsSrCandidate extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 20;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != WORD_SIZE) {
+ return Pair.of(true, dataBoolean(false));
+ }
+
+ byte[] address = new DataWord(data).toTronAddress();
+ WitnessCapsule witnessCapsule = this.getDeposit().getWitness(address);
+ if (witnessCapsule != null) {
+ return Pair.of(true, dataBoolean(true));
+ } else {
+ return Pair.of(true, dataBoolean(false));
+ }
+ }
+ }
+
+ public static class VoteCount extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 500;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != 2 * WORD_SIZE) {
+ return Pair.of(true, longTo32Bytes(0L));
+ }
+
+ DataWord[] words = DataWord.parseArray(data);
+ byte[] address = words[0].toTronAddress();
+ AccountCapsule accountCapsule = this.getDeposit().getAccount(address);
+
+ long voteCount = 0;
+ if (accountCapsule != null && !accountCapsule.getVotesList().isEmpty()) {
+ ByteString witness = ByteString.copyFrom(words[1].toTronAddress());
+ for (Protocol.Vote vote : accountCapsule.getVotesList()) {
+ if (witness.equals(vote.getVoteAddress())) {
+ voteCount += vote.getVoteCount();
+ }
+ }
+ }
+
+ return Pair.of(true, longTo32Bytes(voteCount));
+ }
+ }
+
+ public static class UsedVoteCount extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 20;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != WORD_SIZE) {
+ return Pair.of(true, longTo32Bytes(0L));
+ }
+
+ byte[] address = new DataWord(data).toTronAddress();
+ AccountCapsule accountCapsule = this.getDeposit().getAccount(address);
+
+ long usedVoteCount = 0;
+ if (accountCapsule != null && !accountCapsule.getVotesList().isEmpty()) {
+ for (Protocol.Vote vote : accountCapsule.getVotesList()) {
+ usedVoteCount += vote.getVoteCount();
+ }
+ }
+
+ return Pair.of(true, longTo32Bytes(usedVoteCount));
+ }
+ }
+
+ public static class ReceivedVoteCount extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 20;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != WORD_SIZE) {
+ return Pair.of(true, longTo32Bytes(0L));
+ }
+
+ byte[] address = new DataWord(data).toTronAddress();
+ WitnessCapsule witnessCapsule = this.getDeposit().getWitness(address);
+
+ long voteCount = witnessCapsule != null ? witnessCapsule.getVoteCount() : 0;
+ return Pair.of(true, longTo32Bytes(voteCount));
+ }
+ }
+
+ public static class TotalVoteCount extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ return 20;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data == null || data.length != WORD_SIZE) {
+ return Pair.of(true, longTo32Bytes(0L));
+ }
+
+ byte[] address = new DataWord(data).toTronAddress();
+ AccountCapsule accountCapsule = this.getDeposit().getAccount(address);
+
+ 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));
+ }
+ }
+
+ public static class EthRipemd160 extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ if (data == null) {
+ return 600;
+ }
+ return 600L + (data.length + 31) / 32 * 120L;
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ byte[] result;
+ if (data == null) {
+ result = Hash.ripemd160(EMPTY_BYTE_ARRAY);
+ } else {
+ result = Hash.ripemd160(data);
+ }
+ return Pair.of(true, new DataWord(result).getData());
+ }
+ }
+
+ public static class Blake2F extends PrecompiledContract {
+
+ @Override
+ public long getEnergyForData(byte[] data) {
+ if (data.length != 213 || (data[212] & 0xFE) != 0) {
+ return 0;
+ }
+ final byte[] roundsBytes = copyOfRange(data, 0, 4);
+ final BigInteger rounds = new BigInteger(1, roundsBytes);
+ return rounds.longValue();
+ }
+
+ @Override
+ public Pair execute(byte[] data) {
+ if (data.length != 213) {
+ logger.warn("Incorrect input length. Expected {} and got {}", 213, data.length);
+ return Pair.of(false, DataWord.ZERO().getData());
+ }
+ if ((data[212] & 0xFE) != 0) {
+ logger.warn("Incorrect finalization flag, expected 0 or 1 and got {}", data[212]);
+ return Pair.of(false, DataWord.ZERO().getData());
+ }
+ final MessageDigest digest = new Blake2bfMessageDigest();
+ byte[] result;
+ try {
+ digest.update(data);
+ result = digest.digest();
+ } catch (Exception e) {
+ return Pair.of(true, EMPTY_BYTE_ARRAY);
+ }
+ 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 645d1893d50..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,1474 +1,117 @@
package org.tron.core.vm;
-import static org.tron.common.utils.ByteUtil.EMPTY_BYTE_ARRAY;
-import static org.tron.common.crypto.Hash.sha3;
-import static org.tron.core.db.TransactionTrace.convertToTronAddress;
-import static org.tron.core.vm.OpCode.CALL;
-import static org.tron.core.vm.OpCode.CALLTOKEN;
-import static org.tron.core.vm.OpCode.CALLTOKENID;
-import static org.tron.core.vm.OpCode.CALLTOKENVALUE;
-import static org.tron.core.vm.OpCode.CREATE2;
-import static org.tron.core.vm.OpCode.EXTCODEHASH;
-import static org.tron.core.vm.OpCode.ISCONTRACT;
-import static org.tron.core.vm.OpCode.PUSH1;
-import static org.tron.core.vm.OpCode.REVERT;
-import static org.tron.core.vm.OpCode.SAR;
-import static org.tron.core.vm.OpCode.SHL;
-import static org.tron.core.vm.OpCode.SHR;
-import static org.tron.core.vm.OpCode.TOKENBALANCE;
+import static org.tron.core.Constant.DYNAMIC_ENERGY_FACTOR_DECIMAL;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
import lombok.extern.slf4j.Slf4j;
-import org.spongycastle.util.encoders.Hex;
+import org.bouncycastle.util.encoders.Hex;
import org.springframework.util.StringUtils;
-import org.tron.common.runtime.vm.DataWord;
-import org.tron.common.runtime.vm.LogInfo;
import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.program.Program;
import org.tron.core.vm.program.Program.JVMStackOverFlowException;
-import org.tron.core.vm.program.Program.OutOfEnergyException;
import org.tron.core.vm.program.Program.OutOfTimeException;
import org.tron.core.vm.program.Program.TransferException;
-import org.tron.core.vm.program.Stack;
@Slf4j(topic = "VM")
public class VM {
- public static final String ADDRESS_LOG = "address: ";
- private static final String DATA_LOG = "data: ";
- private static final String SIZE_LOG = "size: ";
- private static final String VALUE_LOG = " value: ";
- private static final BigInteger _32_ = BigInteger.valueOf(32);
- private static final String ENERGY_LOG_FORMATE = "{} Op:[{}] Energy:[{}] Deep:[{}] Hint:[{}]";
- // 3MB
- private static final BigInteger MEM_LIMIT = BigInteger.valueOf(3L * 1024 * 1024);
- private final VMConfig config;
-
- public VM() {
- config = VMConfig.getInstance();
- }
-
- public VM(VMConfig config) {
- this.config = config;
- }
-
- /**
- * Utility to calculate new total memory size needed for an operation. Basically just offset
- * + size, unless size is 0, in which case the result is also 0.
- *
- * @param offset starting position of the memory
- * @param size number of bytes needed
- * @return offset + size, unless size is 0. In that case memNeeded is also 0.
- */
- private static BigInteger memNeeded(DataWord offset, DataWord size) {
- return size.isZero() ? BigInteger.ZERO : offset.value().add(size.value());
- }
-
- private void checkMemorySize(OpCode op, BigInteger newMemSize) {
- if (newMemSize.compareTo(MEM_LIMIT) > 0) {
- throw Program.Exception.memoryOverflow(op);
- }
- }
-
- private long calcMemEnergy(EnergyCost energyCosts, long oldMemSize, BigInteger newMemSize,
- long copySize, OpCode op) {
- long energyCost = 0;
-
- checkMemorySize(op, newMemSize);
-
- // memory SUN consume calc
- long memoryUsage = (newMemSize.longValueExact() + 31) / 32 * 32;
- if (memoryUsage > oldMemSize) {
- long memWords = (memoryUsage / 32);
- long memWordsOld = (oldMemSize / 32);
- //TODO #POC9 c_quadCoeffDiv = 512, this should be a constant, not magic number
- long memEnergy = (energyCosts.getMEMORY() * memWords + memWords * memWords / 512)
- - (energyCosts.getMEMORY() * memWordsOld + memWordsOld * memWordsOld / 512);
- energyCost += memEnergy;
- }
-
- if (copySize > 0) {
- long copyEnergy = energyCosts.getCOPY_ENERGY() * ((copySize + 31) / 32);
- energyCost += copyEnergy;
- }
- return energyCost;
- }
-
- public void step(Program program) {
- if (config.vmTrace()) {
- program.saveOpTrace();
- }
+ 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 {
- OpCode op = OpCode.code(program.getCurrentOp());
- if (op == null) {
- throw Program.Exception.invalidOpCode(program.getCurrentOp());
- }
+ long factor = DYNAMIC_ENERGY_FACTOR_DECIMAL;
+ long energyUsage = 0L;
- // hard fork for 3.2
- if (!VMConfig.allowTvmTransferTrc10()
- && (op == CALLTOKEN || op == TOKENBALANCE || op == CALLTOKENVALUE || op == CALLTOKENID)) {
- throw Program.Exception.invalidOpCode(program.getCurrentOp());
+ if (VMConfig.allowDynamicEnergy()) {
+ factor = program.updateContextContractFactor();
}
- if (!VMConfig.allowTvmConstantinople()
- && (op == SHL || op == SHR || op == SAR || op == CREATE2 || op == EXTCODEHASH)) {
- throw Program.Exception.invalidOpCode(program.getCurrentOp());
- }
-
- if (!VMConfig.allowTvmSolidity059() && op == ISCONTRACT) {
- throw Program.Exception.invalidOpCode(program.getCurrentOp());
- }
- program.setLastOp(op.val());
- program.verifyStackSize(op.require());
- program.verifyStackOverflow(op.require(), op.ret()); //Check not exceeding stack limits
-
- long oldMemSize = program.getMemSize();
- Stack stack = program.getStack();
-
- String hint = "";
- long energyCost = op.getTier().asInt();
- EnergyCost energyCosts = EnergyCost.getInstance();
- DataWord adjustedCallEnergy = null;
-
- // Calculate fees and spend energy
- switch (op) {
- case STOP:
- energyCost = energyCosts.getSTOP();
- break;
- case SUICIDE:
- energyCost = energyCosts.getSUICIDE();
- DataWord suicideAddressWord = stack.get(stack.size() - 1);
- if (isDeadAccount(program, suicideAddressWord)
- && !program.getBalance(program.getContractAddress()).isZero()) {
- energyCost += energyCosts.getNEW_ACCT_SUICIDE();
- }
- break;
- case SSTORE:
- // todo: check the reset to 0, refund or not
- DataWord newValue = stack.get(stack.size() - 2);
- DataWord oldValue = program.storageLoad(stack.peek());
- if (oldValue == null && !newValue.isZero()) {
- // set a new not-zero value
- energyCost = energyCosts.getSET_SSTORE();
- } else if (oldValue != null && newValue.isZero()) {
- // set zero to an old value
- program.futureRefundEnergy(energyCosts.getREFUND_SSTORE());
- energyCost = energyCosts.getCLEAR_SSTORE();
- } else {
- // include:
- // [1] oldValue == null && newValue == 0
- // [2] oldValue != null && newValue != 0
- energyCost = energyCosts.getRESET_SSTORE();
- }
- break;
- case SLOAD:
- energyCost = energyCosts.getSLOAD();
- break;
- case TOKENBALANCE:
- case BALANCE:
- case ISCONTRACT:
- energyCost = energyCosts.getBALANCE();
- break;
-
- // These all operate on memory and therefore potentially expand it:
- case MSTORE:
- energyCost = calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.peek(), new DataWord(32)),
- 0, op);
- break;
- case MSTORE8:
- energyCost = calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.peek(), new DataWord(1)),
- 0, op);
- break;
- case MLOAD:
- energyCost = calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.peek(), new DataWord(32)),
- 0, op);
- break;
- case RETURN:
- case REVERT:
- energyCost = energyCosts.getSTOP() + calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.peek(), stack.get(stack.size() - 2)), 0, op);
- break;
- case SHA3:
- energyCost = energyCosts.getSHA3() + calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.peek(), stack.get(stack.size() - 2)), 0, op);
- DataWord size = stack.get(stack.size() - 2);
- long chunkUsed = (size.longValueSafe() + 31) / 32;
- energyCost += chunkUsed * energyCosts.getSHA3_WORD();
- break;
- case CALLDATACOPY:
- case RETURNDATACOPY:
- energyCost = calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.peek(), stack.get(stack.size() - 3)),
- stack.get(stack.size() - 3).longValueSafe(), op);
- break;
- case CODECOPY:
- energyCost = calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.peek(), stack.get(stack.size() - 3)),
- stack.get(stack.size() - 3).longValueSafe(), op);
- break;
- case EXTCODESIZE:
- energyCost = energyCosts.getEXT_CODE_SIZE();
- break;
- case EXTCODECOPY:
- energyCost = energyCosts.getEXT_CODE_COPY() + calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 4)),
- stack.get(stack.size() - 4).longValueSafe(), op);
- break;
- case EXTCODEHASH:
- energyCost = energyCosts.getEXT_CODE_HASH();
- break;
- case CALL:
- case CALLCODE:
- case DELEGATECALL:
- case STATICCALL:
- case CALLTOKEN:
- // here, contract call an other contract, or a library, and so on
- energyCost = energyCosts.getCALL();
- DataWord callEnergyWord = stack.get(stack.size() - 1);
- DataWord callAddressWord = stack.get(stack.size() - 2);
- DataWord value = op.callHasValue() ? stack.get(stack.size() - 3) : DataWord.ZERO;
-
- //check to see if account does not exist and is not a precompiled contract
- if ((op == CALL || op == CALLTOKEN)
- && isDeadAccount(program, callAddressWord)
- && !value.isZero()){
- energyCost += energyCosts.getNEW_ACCT_CALL();
- }
-
- // TODO #POC9 Make sure this is converted to BigInteger (256num support)
- if (!value.isZero()) {
- energyCost += energyCosts.getVT_CALL();
- }
-
- int opOff = op.callHasValue() ? 4 : 3;
- if (op == CALLTOKEN) {
- opOff++;
- }
- BigInteger in = memNeeded(stack.get(stack.size() - opOff),
- stack.get(stack.size() - opOff - 1)); // in offset+size
- BigInteger out = memNeeded(stack.get(stack.size() - opOff - 2),
- stack.get(stack.size() - opOff - 3)); // out offset+size
- energyCost += calcMemEnergy(energyCosts, oldMemSize, in.max(out), 0, op);
- checkMemorySize(op, in.max(out));
-
- if (energyCost > program.getEnergyLimitLeft().longValueSafe()) {
- throw new OutOfEnergyException(
- "Not enough energy for '%s' operation executing: opEnergy[%d], programEnergy[%d]",
- op.name(),
- energyCost, program.getEnergyLimitLeft().longValueSafe());
- }
- DataWord getEnergyLimitLeft = program.getEnergyLimitLeft().clone();
- getEnergyLimitLeft.sub(new DataWord(energyCost));
-
- adjustedCallEnergy = program.getCallEnergy(op, callEnergyWord, getEnergyLimitLeft);
- energyCost += adjustedCallEnergy.longValueSafe();
- break;
- case CREATE:
- energyCost = energyCosts.getCREATE() + calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 3)), 0, op);
- break;
- case CREATE2:
- DataWord codeSize = stack.get(stack.size() - 3);
- energyCost = energyCosts.getCREATE();
- energyCost += calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.get(stack.size() - 2), stack.get(stack.size() - 3)), 0, op);
- energyCost += DataWord.sizeInWords(codeSize.intValueSafe()) * energyCosts.getSHA3_WORD();
-
- break;
- case LOG0:
- case LOG1:
- case LOG2:
- case LOG3:
- case LOG4:
- int nTopics = op.val() - OpCode.LOG0.val();
- BigInteger dataSize = stack.get(stack.size() - 2).value();
- BigInteger dataCost = dataSize
- .multiply(BigInteger.valueOf(energyCosts.getLOG_DATA_ENERGY()));
- if (program.getEnergyLimitLeft().value().compareTo(dataCost) < 0) {
- throw new OutOfEnergyException(
- "Not enough energy for '%s' operation executing: opEnergy[%d], programEnergy[%d]",
- op.name(),
- dataCost.longValueExact(), program.getEnergyLimitLeft().longValueSafe());
- }
- energyCost = energyCosts.getLOG_ENERGY()
- + energyCosts.getLOG_TOPIC_ENERGY() * nTopics
- + energyCosts.getLOG_DATA_ENERGY() * stack.get(stack.size() - 2).longValue()
- + calcMemEnergy(energyCosts, oldMemSize,
- memNeeded(stack.peek(), stack.get(stack.size() - 2)), 0, op);
-
- checkMemorySize(op, memNeeded(stack.peek(), stack.get(stack.size() - 2)));
- break;
- case EXP:
-
- DataWord exp = stack.get(stack.size() - 2);
- int bytesOccupied = exp.bytesOccupied();
- energyCost =
- (long) energyCosts.getEXP_ENERGY() + energyCosts.getEXP_BYTE_ENERGY() * bytesOccupied;
- break;
- default:
- break;
- }
-
- program.spendEnergy(energyCost, op.name());
- program.checkCPUTimeLimit(op.name());
-
- // Execute operation
- switch (op) {
- /**
- * Stop and Arithmetic Operations
- */
- case STOP: {
- program.setHReturn(EMPTY_BYTE_ARRAY);
- program.stop();
- }
- break;
- case ADD: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " + " + word2.value();
- }
-
- word1.add(word2);
- program.stackPush(word1);
- program.step();
-
- }
- break;
- case MUL: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " * " + word2.value();
- }
-
- word1.mul(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case SUB: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " - " + word2.value();
- }
-
- word1.sub(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case DIV: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " / " + word2.value();
- }
-
- word1.div(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case SDIV: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.sValue() + " / " + word2.sValue();
- }
-
- word1.sDiv(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case MOD: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " % " + word2.value();
- }
-
- word1.mod(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case SMOD: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.sValue() + " #% " + word2.sValue();
- }
-
- word1.sMod(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case EXP: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " ** " + word2.value();
- }
-
- word1.exp(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case SIGNEXTEND: {
- DataWord word1 = program.stackPop();
- BigInteger k = word1.value();
-
- if (k.compareTo(_32_) < 0) {
- DataWord word2 = program.stackPop();
- if (logger.isDebugEnabled()) {
- hint = word1 + " " + word2.value();
- }
- word2.signExtend(k.byteValue());
- program.stackPush(word2);
- }
- program.step();
- }
- break;
- case NOT: {
- DataWord word1 = program.stackPop();
- word1.bnot();
-
- if (logger.isDebugEnabled()) {
- hint = "" + word1.value();
- }
-
- program.stackPush(word1);
- program.step();
- }
- break;
- case LT: {
- // TODO: can be improved by not using BigInteger
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " < " + word2.value();
- }
-
- if (word1.value().compareTo(word2.value()) < 0) {
- word1.and(DataWord.ZERO);
- word1.getData()[31] = 1;
- } else {
- word1.and(DataWord.ZERO);
- }
- program.stackPush(word1);
- program.step();
- }
- break;
- case SLT: {
- // TODO: can be improved by not using BigInteger
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.sValue() + " < " + word2.sValue();
- }
-
- if (word1.sValue().compareTo(word2.sValue()) < 0) {
- word1.and(DataWord.ZERO);
- word1.getData()[31] = 1;
- } else {
- word1.and(DataWord.ZERO);
- }
- program.stackPush(word1);
- program.step();
- }
- break;
- case SGT: {
- // TODO: can be improved by not using BigInteger
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.sValue() + " > " + word2.sValue();
- }
-
- if (word1.sValue().compareTo(word2.sValue()) > 0) {
- word1.and(DataWord.ZERO);
- word1.getData()[31] = 1;
- } else {
- word1.and(DataWord.ZERO);
- }
- program.stackPush(word1);
- program.step();
- }
- break;
- case GT: {
- // TODO: can be improved by not using BigInteger
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " > " + word2.value();
- }
-
- if (word1.value().compareTo(word2.value()) > 0) {
- word1.and(DataWord.ZERO);
- word1.getData()[31] = 1;
- } else {
- word1.and(DataWord.ZERO);
- }
- program.stackPush(word1);
- program.step();
- }
- break;
- case EQ: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " == " + word2.value();
- }
-
- if (word1.xor(word2).isZero()) {
- word1.and(DataWord.ZERO);
- word1.getData()[31] = 1;
- } else {
- word1.and(DataWord.ZERO);
- }
- program.stackPush(word1);
- program.step();
- }
- break;
- case ISZERO: {
- DataWord word1 = program.stackPop();
- if (word1.isZero()) {
- word1.getData()[31] = 1;
- } else {
- word1.and(DataWord.ZERO);
- }
-
- if (logger.isDebugEnabled()) {
- hint = "" + word1.value();
- }
-
- program.stackPush(word1);
- program.step();
- }
- break;
-
- /**
- * Bitwise Logic Operations
- */
- case AND: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " && " + word2.value();
- }
-
- word1.and(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case OR: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " || " + word2.value();
- }
-
- word1.or(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case XOR: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = word1.value() + " ^ " + word2.value();
- }
-
- word1.xor(word2);
- program.stackPush(word1);
- program.step();
- }
- break;
- case BYTE: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
- final DataWord result;
- if (word1.value().compareTo(_32_) < 0) {
- byte tmp = word2.getData()[word1.intValue()];
- word2.and(DataWord.ZERO);
- word2.getData()[31] = tmp;
- result = word2;
- } else {
- result = new DataWord();
- }
-
- if (logger.isDebugEnabled()) {
- hint = "" + result.value();
- }
-
- program.stackPush(result);
- program.step();
- }
- break;
- case SHL: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
- final DataWord result = word2.shiftLeft(word1);
-
- if (logger.isInfoEnabled()) {
- hint = "" + result.value();
- }
-
- program.stackPush(result);
- program.step();
- }
- break;
- case SHR: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
- final DataWord result = word2.shiftRight(word1);
-
- if (logger.isInfoEnabled()) {
- hint = "" + result.value();
- }
-
- program.stackPush(result);
- program.step();
- }
- break;
- case SAR: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
- final DataWord result = word2.shiftRightSigned(word1);
-
- if (logger.isInfoEnabled()) {
- hint = "" + result.value();
- }
-
- program.stackPush(result);
- program.step();
- }
- break;
- case ADDMOD: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
- DataWord word3 = program.stackPop();
- word1.addmod(word2, word3);
- program.stackPush(word1);
- program.step();
- }
- break;
- case MULMOD: {
- DataWord word1 = program.stackPop();
- DataWord word2 = program.stackPop();
- DataWord word3 = program.stackPop();
- word1.mulmod(word2, word3);
- program.stackPush(word1);
- program.step();
- }
- break;
-
- /**
- * SHA3
- */
- case SHA3: {
- DataWord memOffsetData = program.stackPop();
- DataWord lengthData = program.stackPop();
- byte[] buffer = program
- .memoryChunk(memOffsetData.intValueSafe(), lengthData.intValueSafe());
-
- byte[] encoded = sha3(buffer);
- DataWord word = new DataWord(encoded);
-
- if (logger.isDebugEnabled()) {
- hint = word.toString();
- }
-
- program.stackPush(word);
- program.step();
- }
- break;
-
- /**
- * Environmental Information
- */
- case ADDRESS: {
- DataWord address = program.getContractAddress();
- if (VMConfig.allowMultiSign()) { // allowMultiSigns proposal
- address = new DataWord(address.getLast20Bytes());
- }
-
- if (logger.isDebugEnabled()) {
- hint = ADDRESS_LOG + Hex.toHexString(address.getLast20Bytes());
- }
-
- program.stackPush(address);
- program.step();
- }
- break;
- case BALANCE: {
- DataWord address = program.stackPop();
- DataWord balance = program.getBalance(address);
-
- if (logger.isDebugEnabled()) {
- hint = ADDRESS_LOG
- + Hex.toHexString(address.getLast20Bytes())
- + " balance: " + balance.toString();
- }
-
- program.stackPush(balance);
- program.step();
- }
- break;
- case ISCONTRACT: {
- DataWord address = program.stackPop();
- DataWord isContract = program.isContract(address);
-
- program.stackPush(isContract);
- program.step();
- }
- break;
- case ORIGIN: {
- DataWord originAddress = program.getOriginAddress();
-
- if (VMConfig.allowMultiSign()) { //allowMultiSign proposal
- originAddress = new DataWord(originAddress.getLast20Bytes());
- }
-
- if (logger.isDebugEnabled()) {
- hint = ADDRESS_LOG + Hex.toHexString(originAddress.getLast20Bytes());
- }
-
- program.stackPush(originAddress);
- program.step();
- }
- break;
- case CALLER: {
- DataWord callerAddress = program.getCallerAddress();
- /**
- since we use 21 bytes address instead of 20 as etherum, we need to make sure
- the address length in vm is matching with 20
- */
- callerAddress = new DataWord(callerAddress.getLast20Bytes());
- if (logger.isDebugEnabled()) {
- hint = ADDRESS_LOG + Hex.toHexString(callerAddress.getLast20Bytes());
- }
-
- program.stackPush(callerAddress);
- program.step();
- }
- break;
- case CALLVALUE: {
- DataWord callValue = program.getCallValue();
-
- if (logger.isDebugEnabled()) {
- hint = "value: " + callValue;
- }
-
- program.stackPush(callValue);
- program.step();
- }
- break;
- case CALLTOKENVALUE:
- DataWord tokenValue = program.getTokenValue();
-
- if (logger.isDebugEnabled()) {
- hint = "tokenValue: " + tokenValue;
- }
-
- program.stackPush(tokenValue);
- program.step();
- break;
- case CALLTOKENID:
- DataWord _tokenId = program.getTokenId();
-
- if (logger.isDebugEnabled()) {
- hint = "tokenId: " + _tokenId;
- }
-
- program.stackPush(_tokenId);
- program.step();
- break;
- case CALLDATALOAD: {
- DataWord dataOffs = program.stackPop();
- DataWord value = program.getDataValue(dataOffs);
-
- if (logger.isDebugEnabled()) {
- hint = DATA_LOG + value;
- }
-
- program.stackPush(value);
- program.step();
- }
- break;
- case CALLDATASIZE: {
- DataWord dataSize = program.getDataSize();
-
- if (logger.isDebugEnabled()) {
- hint = SIZE_LOG + dataSize.value();
- }
-
- program.stackPush(dataSize);
- program.step();
- }
- break;
- case CALLDATACOPY: {
- DataWord memOffsetData = program.stackPop();
- DataWord dataOffsetData = program.stackPop();
- DataWord lengthData = program.stackPop();
-
- byte[] msgData = program.getDataCopy(dataOffsetData, lengthData);
-
- if (logger.isDebugEnabled()) {
- hint = DATA_LOG + Hex.toHexString(msgData);
- }
-
- program.memorySave(memOffsetData.intValueSafe(), msgData);
- program.step();
- }
- break;
- case RETURNDATASIZE: {
- DataWord dataSize = program.getReturnDataBufferSize();
-
- if (logger.isDebugEnabled()) {
- hint = SIZE_LOG + dataSize.value();
- }
-
- program.stackPush(dataSize);
- program.step();
- }
- break;
- case RETURNDATACOPY: {
- DataWord memOffsetData = program.stackPop();
- DataWord dataOffsetData = program.stackPop();
- DataWord lengthData = program.stackPop();
-
- byte[] msgData = program.getReturnDataBufferData(dataOffsetData, lengthData);
-
- if (msgData == null) {
- throw new Program.ReturnDataCopyIllegalBoundsException(dataOffsetData, lengthData,
- program.getReturnDataBufferSize().longValueSafe());
- }
-
- if (logger.isDebugEnabled()) {
- hint = DATA_LOG + Hex.toHexString(msgData);
- }
-
- program.memorySave(memOffsetData.intValueSafe(), msgData);
- program.step();
- }
- break;
- case CODESIZE:
- case EXTCODESIZE: {
-
- int length;
- if (op == OpCode.CODESIZE) {
- length = program.getCode().length;
- } else {
- DataWord address = program.stackPop();
- length = program.getCodeAt(address).length;
- }
- DataWord codeLength = new DataWord(length);
-
- if (logger.isDebugEnabled()) {
- hint = SIZE_LOG + length;
- }
-
- program.stackPush(codeLength);
- program.step();
- break;
- }
- case CODECOPY:
- case EXTCODECOPY: {
-
- byte[] fullCode = EMPTY_BYTE_ARRAY;
- if (op == OpCode.CODECOPY) {
- fullCode = program.getCode();
- }
-
- if (op == OpCode.EXTCODECOPY) {
- DataWord address = program.stackPop();
- fullCode = program.getCodeAt(address);
- }
-
- int memOffset = program.stackPop().intValueSafe();
- int codeOffset = program.stackPop().intValueSafe();
- int lengthData = program.stackPop().intValueSafe();
-
- int sizeToBeCopied =
- (long) codeOffset + lengthData > fullCode.length
- ? (fullCode.length < codeOffset ? 0 : fullCode.length - codeOffset)
- : lengthData;
-
- byte[] codeCopy = new byte[lengthData];
-
- if (codeOffset < fullCode.length) {
- System.arraycopy(fullCode, codeOffset, codeCopy, 0, sizeToBeCopied);
- }
-
- if (logger.isDebugEnabled()) {
- hint = "code: " + Hex.toHexString(codeCopy);
- }
-
- program.memorySave(memOffset, codeCopy);
- program.step();
- break;
- }
- case EXTCODEHASH: {
- DataWord address = program.stackPop();
- byte[] codeHash = program.getCodeHashAt(address);
- program.stackPush(codeHash);
- program.step();
- }
- break;
- case GASPRICE: {
- DataWord energyPrice = new DataWord(0);
-
- if (logger.isDebugEnabled()) {
- hint = "price: " + energyPrice.toString();
- }
-
- program.stackPush(energyPrice);
- program.step();
- }
- break;
-
- /**
- * Block Information
- */
- case BLOCKHASH: {
-
- int blockIndex = program.stackPop().intValueSafe();
-
- DataWord blockHash = program.getBlockHash(blockIndex);
-
- if (logger.isDebugEnabled()) {
- hint = "blockHash: " + blockHash;
- }
-
- program.stackPush(blockHash);
- program.step();
- }
- break;
- case COINBASE: {
- DataWord coinbase = program.getCoinbase();
-
- if (logger.isDebugEnabled()) {
- hint = "coinbase: " + Hex.toHexString(coinbase.getLast20Bytes());
- }
-
- program.stackPush(coinbase);
- program.step();
- }
- break;
- case TIMESTAMP: {
- DataWord timestamp = program.getTimestamp();
-
- if (logger.isDebugEnabled()) {
- hint = "timestamp: " + timestamp.value();
- }
-
- program.stackPush(timestamp);
- program.step();
- }
- break;
- case NUMBER: {
- DataWord number = program.getNumber();
-
- if (logger.isDebugEnabled()) {
- hint = "number: " + number.value();
- }
-
- program.stackPush(number);
- program.step();
- }
- break;
- case DIFFICULTY: {
- DataWord difficulty = program.getDifficulty();
-
- if (logger.isDebugEnabled()) {
- hint = "difficulty: " + difficulty;
- }
-
- program.stackPush(difficulty);
- program.step();
- }
- break;
- case GASLIMIT: {
- // todo: this energylimit is the block's energy limit
- DataWord energyLimit = new DataWord(0);
-
- if (logger.isDebugEnabled()) {
- hint = "energylimit: " + energyLimit;
- }
-
- program.stackPush(energyLimit);
- program.step();
- }
- break;
- case POP: {
- program.stackPop();
- program.step();
- }
- break;
- case DUP1:
- case DUP2:
- case DUP3:
- case DUP4:
- case DUP5:
- case DUP6:
- case DUP7:
- case DUP8:
- case DUP9:
- case DUP10:
- case DUP11:
- case DUP12:
- case DUP13:
- case DUP14:
- case DUP15:
- case DUP16: {
-
- int n = op.val() - OpCode.DUP1.val() + 1;
- DataWord word_1 = stack.get(stack.size() - n);
- program.stackPush(word_1.clone());
- program.step();
-
- break;
- }
- case SWAP1:
- case SWAP2:
- case SWAP3:
- case SWAP4:
- case SWAP5:
- case SWAP6:
- case SWAP7:
- case SWAP8:
- case SWAP9:
- case SWAP10:
- case SWAP11:
- case SWAP12:
- case SWAP13:
- case SWAP14:
- case SWAP15:
- case SWAP16: {
-
- int n = op.val() - OpCode.SWAP1.val() + 2;
- stack.swap(stack.size() - 1, stack.size() - n);
- program.step();
- break;
- }
- case LOG0:
- case LOG1:
- case LOG2:
- case LOG3:
- case LOG4: {
-
- if (program.isStaticCall()) {
- throw new Program.StaticCallModificationException();
- }
- DataWord address = program.getContractAddress();
-
- DataWord memStart = stack.pop();
- DataWord memOffset = stack.pop();
-
- int nTopics = op.val() - OpCode.LOG0.val();
-
- List topics = new ArrayList<>();
- for (int i = 0; i < nTopics; ++i) {
- DataWord topic = stack.pop();
- topics.add(topic);
- }
-
- byte[] data = program.memoryChunk(memStart.intValueSafe(), memOffset.intValueSafe());
-
- LogInfo logInfo =
- new LogInfo(address.getLast20Bytes(), topics, data);
-
- if (logger.isDebugEnabled()) {
- hint = logInfo.toString();
- }
-
- program.getResult().addLogInfo(logInfo);
- program.step();
- break;
- }
- case MLOAD: {
- DataWord addr = program.stackPop();
- DataWord data = program.memoryLoad(addr);
-
- if (logger.isDebugEnabled()) {
- hint = DATA_LOG + data;
- }
-
- program.stackPush(data);
- program.step();
- }
- break;
- case MSTORE: {
- DataWord addr = program.stackPop();
- DataWord value = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = "addr: " + addr + VALUE_LOG + value;
- }
-
- program.memorySave(addr, value);
- program.step();
- }
- break;
- case MSTORE8: {
- DataWord addr = program.stackPop();
- DataWord value = program.stackPop();
- byte[] byteVal = {value.getData()[31]};
- program.memorySave(addr.intValueSafe(), byteVal);
- program.step();
- }
- break;
- case SLOAD: {
- DataWord key = program.stackPop();
- DataWord val = program.storageLoad(key);
-
- if (logger.isDebugEnabled()) {
- hint = "key: " + key + VALUE_LOG + val;
- }
-
- if (val == null) {
- val = key.and(DataWord.ZERO);
- }
-
- program.stackPush(val);
- program.step();
- }
- break;
- case SSTORE: {
- if (program.isStaticCall()) {
- throw new Program.StaticCallModificationException();
- }
-
- DataWord addr = program.stackPop();
- DataWord value = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint =
- "[" + program.getContractAddress().toPrefixString() + "] key: " + addr + VALUE_LOG
- + value;
- }
-
- program.storageSave(addr, value);
- program.step();
- }
- break;
- case JUMP: {
- DataWord pos = program.stackPop();
- int nextPC = program.verifyJumpDest(pos);
-
- if (logger.isDebugEnabled()) {
- hint = "~> " + nextPC;
- }
-
- program.setPC(nextPC);
-
- }
- break;
- case JUMPI: {
- DataWord pos = program.stackPop();
- DataWord cond = program.stackPop();
-
- if (!cond.isZero()) {
- int nextPC = program.verifyJumpDest(pos);
-
- if (logger.isDebugEnabled()) {
- hint = "~> " + nextPC;
+ while (!program.isStopped()) {
+ if (VMConfig.vmTrace()) {
+ program.saveOpTrace();
+ }
+
+ try {
+ Operation op = jumpTable.get(program.getCurrentOpIntValue());
+ if (!op.isEnabled()) {
+ throw Program.Exception.invalidOpCode(program.getCurrentOp());
+ }
+ program.setLastOp((byte) op.getOpcode());
+
+ /* stack underflow/overflow check */
+ program.verifyStackSize(op.getRequire());
+ program.verifyStackOverflow(op.getRequire(), op.getRet());
+
+ String opName = Op.getNameOf(op.getOpcode());
+ /* spend energy before execution */
+ 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();
}
-
- program.setPC(nextPC);
- } else {
- program.step();
- }
-
- }
- break;
- case PC: {
- int pc = program.getPC();
- DataWord pcWord = new DataWord(pc);
-
- if (logger.isDebugEnabled()) {
- hint = pcWord.toString();
- }
-
- program.stackPush(pcWord);
- program.step();
- }
- break;
- case MSIZE: {
- int memSize = program.getMemSize();
- DataWord wordMemSize = new DataWord(memSize);
-
- if (logger.isDebugEnabled()) {
- hint = "" + memSize;
- }
-
- program.stackPush(wordMemSize);
- program.step();
- }
- break;
- case GAS: {
- DataWord energy = program.getEnergyLimitLeft();
- if (logger.isDebugEnabled()) {
- hint = "" + energy;
- }
-
- program.stackPush(energy);
- program.step();
- }
- break;
-
- case PUSH1:
- case PUSH2:
- case PUSH3:
- case PUSH4:
- case PUSH5:
- case PUSH6:
- case PUSH7:
- case PUSH8:
- case PUSH9:
- case PUSH10:
- case PUSH11:
- case PUSH12:
- case PUSH13:
- case PUSH14:
- case PUSH15:
- case PUSH16:
- case PUSH17:
- case PUSH18:
- case PUSH19:
- case PUSH20:
- case PUSH21:
- case PUSH22:
- case PUSH23:
- case PUSH24:
- case PUSH25:
- case PUSH26:
- case PUSH27:
- case PUSH28:
- case PUSH29:
- case PUSH30:
- case PUSH31:
- case PUSH32: {
- program.step();
- int nPush = op.val() - PUSH1.val() + 1;
-
- byte[] data = program.sweep(nPush);
-
- if (logger.isDebugEnabled()) {
- hint = "" + Hex.toHexString(data);
- }
-
- program.stackPush(data);
- break;
- }
- case JUMPDEST: {
- program.step();
- }
- break;
- case CREATE: {
- if (program.isStaticCall()) {
- throw new Program.StaticCallModificationException();
- }
- DataWord value = program.stackPop();
- DataWord inOffset = program.stackPop();
- DataWord inSize = program.stackPop();
- program.createContract(value, inOffset, inSize);
-
- program.step();
- }
- break;
- case CREATE2: {
- if (program.isStaticCall()) {
- throw new Program.StaticCallModificationException();
- }
- DataWord value = program.stackPop();
- DataWord inOffset = program.stackPop();
- DataWord inSize = program.stackPop();
- DataWord salt = program.stackPop();
- program.createContract2(value, inOffset, inSize, salt);
- program.step();
- }
- break;
- case TOKENBALANCE: {
- DataWord tokenId = program.stackPop();
- DataWord address = program.stackPop();
- DataWord tokenBalance = program.getTokenBalance(address, tokenId);
- program.stackPush(tokenBalance);
-
- program.step();
- }
- break;
- case CALL:
- case CALLCODE:
- case CALLTOKEN:
- case DELEGATECALL:
- case STATICCALL: {
- program.stackPop(); // use adjustedCallEnergy instead of requested
- DataWord codeAddress = program.stackPop();
-
- DataWord value;
- if (op.callHasValue()) {
- value = program.stackPop();
- } else {
- value = DataWord.ZERO;
- }
-
- if (program.isStaticCall() && (op == CALL || op == CALLTOKEN) && !value.isZero()) {
- throw new Program.StaticCallModificationException();
- }
-
- if (!value.isZero()) {
- adjustedCallEnergy.add(new DataWord(energyCosts.getSTIPEND_CALL()));
- }
-
- DataWord tokenId = new DataWord(0);
- boolean isTokenTransferMsg = false;
- if (op == CALLTOKEN) {
- tokenId = program.stackPop();
- if (VMConfig.allowMultiSign()) { // allowMultiSign proposal
- isTokenTransferMsg = true;
+ 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);
}
- }
-
- DataWord inDataOffs = program.stackPop();
- DataWord inDataSize = program.stackPop();
-
- DataWord outDataOffs = program.stackPop();
- DataWord outDataSize = program.stackPop();
-
- if (logger.isDebugEnabled()) {
- hint = "addr: " + Hex.toHexString(codeAddress.getLast20Bytes())
- + " energy: " + adjustedCallEnergy.shortHex()
- + " inOff: " + inDataOffs.shortHex()
- + " inSize: " + inDataSize.shortHex();
- logger.debug(ENERGY_LOG_FORMATE, String.format("%5s", "[" + program.getPC() + "]"),
- String.format("%-12s", op.name()),
- program.getEnergyLimitLeft().value(),
- program.getCallDeep(), hint);
- }
-
- program.memoryExpand(outDataOffs, outDataSize);
-
- MessageCall msg = new MessageCall(
- op, adjustedCallEnergy, codeAddress, value, inDataOffs, inDataSize,
- outDataOffs, outDataSize, tokenId, isTokenTransferMsg);
-
- PrecompiledContracts.PrecompiledContract contract =
- PrecompiledContracts.getContractForAddress(codeAddress);
- if (!op.callIsStateless()) {
- program.getResult().addTouchAccount(codeAddress.getLast20Bytes());
- }
-
- if (contract != null) {
- program.callToPrecompiledAddress(msg, contract);
} else {
- program.callToAddress(msg);
- }
-
- program.step();
- break;
- }
- case RETURN:
- case REVERT: {
- DataWord offset = program.stackPop();
- DataWord size = program.stackPop();
-
- byte[] hReturn = program.memoryChunk(offset.intValueSafe(), size.intValueSafe());
- program.setHReturn(hReturn);
-
- if (logger.isDebugEnabled()) {
- hint = DATA_LOG + Hex.toHexString(hReturn)
- + " offset: " + offset.value()
- + " size: " + size.value();
+ program.spendEnergy(energy, opName);
}
- program.step();
- program.stop();
- if (op == REVERT) {
- program.getResult().setRevert();
- }
- break;
- }
- case SUICIDE: {
- if (program.isStaticCall()) {
- throw new Program.StaticCallModificationException();
- }
+ /* check if cpu time out */
+ program.checkCPUTimeLimit(opName);
- DataWord address = program.stackPop();
- program.suicide(address);
- program.getResult().addTouchAccount(address.getLast20Bytes());
+ /* exec op action */
+ op.execute(program);
- if (logger.isDebugEnabled()) {
- hint = ADDRESS_LOG + Hex.toHexString(program.getContractAddress().getLast20Bytes());
+ program.setPreviouslyExecutedOp((byte) op.getOpcode());
+ } catch (RuntimeException e) {
+ logger.info("VM halted: [{}]", e.getMessage());
+ if (!(e instanceof TransferException)) {
+ program.spendAllEnergy();
}
-
+ //program.resetFutureRefund();
program.stop();
+ throw e;
+ } finally {
+ program.fullTrace();
}
- break;
- default:
- break;
}
- program.setPreviouslyExecutedOp(op.val());
- } catch (RuntimeException e) {
- logger.info("VM halted: [{}]", e.getMessage());
- if (!(e instanceof TransferException)) {
- program.spendAllEnergy();
- }
- program.resetFutureRefund();
- program.stop();
- throw e;
- } finally {
- program.fullTrace();
- }
- }
-
- public void play(Program program) {
- try {
- if (program.byTestingSuite()) {
- return;
- }
-
- while (!program.isStopped()) {
- this.step(program);
+ 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"));
@@ -1476,14 +119,8 @@ public void play(Program program) {
program.setRuntimeFailure(e);
}
} catch (StackOverflowError soe) {
- logger
- .info("\n !!! StackOverflowError: update your java run command with -Xss !!!\n", soe);
+ logger.info("\n !!! StackOverflowError: update your java run command with -Xss !!!\n", soe);
throw new JVMStackOverFlowException();
}
}
-
- private boolean isDeadAccount(Program program, DataWord address) {
- return program.getContractState().getAccount(convertToTronAddress(address.getLast20Bytes()))
- == null;
- }
}
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 967eafadd2d..266224a1502 100644
--- a/actuator/src/main/java/org/tron/core/vm/VMConstant.java
+++ b/actuator/src/main/java/org/tron/core/vm/VMConstant.java
@@ -2,17 +2,16 @@
public class VMConstant {
- public static final double TX_CPU_LIMIT_DEFAULT_RATIO = 1.0;
-
- public static final String REASON_ALREADY_TIME_OUT = "Haven Time Out";
public static final int CONTRACT_NAME_LENGTH = 32;
- public static final int MIN_TOKEN_ID = 1000_000;
+ 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; // 1 us = 100 SUN = 100 * 10^-6 TRX
- public static final long ENERGY_LIMIT_IN_CONSTANT_TX = 3_000_000L; // ref: 1 us = 1 energy
+ 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 44f25940ab7..2f469e0579a 100644
--- a/actuator/src/main/java/org/tron/core/vm/VMUtils.java
+++ b/actuator/src/main/java/org/tron/core/vm/VMUtils.java
@@ -1,24 +1,8 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm;
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;
@@ -29,22 +13,19 @@
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 java.util.zip.Inflater;
-import java.util.zip.InflaterOutputStream;
import lombok.extern.slf4j.Slf4j;
import org.tron.common.utils.ByteArray;
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;
import org.tron.core.vm.repository.Repository;
-
@Slf4j(topic = "VM")
public final class VMUtils {
@@ -53,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) {
@@ -77,7 +63,7 @@ private static File createProgramTraceFile(String txHash) {
} else {
try {
file.getParentFile().mkdirs();
- if (!file.createNewFile()){
+ if (!file.createNewFile()) {
logger.error("failed to create file {}", file.getPath());
}
result = file;
@@ -184,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());
@@ -230,17 +216,8 @@ public static boolean validateForSmartContract(Repository deposit, byte[] ownerA
throw new ContractValidateException("No asset !");
}
- Map asset;
- if (deposit.getDynamicPropertiesStore().getAllowSameTokenName() == 0) {
- asset = ownerAccount.getAssetMap();
- } else {
- asset = ownerAccount.getAssetMapV2();
- }
- if (asset.isEmpty()) {
- throw new ContractValidateException("Owner no asset!");
- }
-
- Long assetBalance = asset.get(ByteArray.toStr(tokenIdWithoutLeadingZero));
+ Long assetBalance = ownerAccount.getAsset(deposit.getDynamicPropertiesStore(),
+ ByteArray.toStr(tokenIdWithoutLeadingZero));
if (null == assetBalance || assetBalance <= 0) {
throw new ContractValidateException("assetBalance must greater than 0.");
}
@@ -250,14 +227,12 @@ public static boolean validateForSmartContract(Repository deposit, byte[] ownerA
AccountCapsule toAccount = deposit.getAccount(toAddress);
if (toAccount != null) {
- if (deposit.getDynamicPropertiesStore().getAllowSameTokenName() == 0) {
- assetBalance = toAccount.getAssetMap().get(ByteArray.toStr(tokenIdWithoutLeadingZero));
- } else {
- assetBalance = toAccount.getAssetMapV2().get(ByteArray.toStr(tokenIdWithoutLeadingZero));
- }
+ assetBalance = toAccount.getAsset(deposit.getDynamicPropertiesStore(),
+ 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 baa23b9d460..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
@@ -1,6 +1,5 @@
package org.tron.core.vm.config;
-
import static org.tron.core.capsule.ReceiptCapsule.checkForEnergyLimit;
import lombok.extern.slf4j.Slf4j;
@@ -24,7 +23,30 @@ public static void load(StoreFactory storeFactory) {
VMConfig.initAllowTvmTransferTrc10(ds.getAllowTvmTransferTrc10());
VMConfig.initAllowTvmConstantinople(ds.getAllowTvmConstantinople());
VMConfig.initAllowTvmSolidity059(ds.getAllowTvmSolidity059());
-
+ VMConfig.initAllowShieldedTRC20Transaction(ds.getAllowShieldedTRC20Transaction());
+ VMConfig.initAllowTvmIstanbul(ds.getAllowTvmIstanbul());
+ VMConfig.initAllowTvmFreeze(ds.getAllowTvmFreeze());
+ VMConfig.initAllowTvmVote(ds.getAllowTvmVote());
+ VMConfig.initAllowTvmLondon(ds.getAllowTvmLondon());
+ 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 3732b7412c8..00000000000
--- a/actuator/src/main/java/org/tron/core/vm/config/VMConfig.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.config;
-
-
-import lombok.Getter;
-import lombok.Setter;
-import org.tron.common.parameter.CommonParameter;
-
-/**
- * For developer only
- */
-public class VMConfig {
-
- public static final int MAX_FEE_LIMIT = 1_000_000_000; //1000 TRX
-
- private static boolean vmTraceCompressed = false;
-
- @Setter
- private static boolean vmTrace = false;
-
- @Setter
- private static boolean ALLOW_TVM_TRANSFER_TRC10 = false;
-
- @Setter
- private static boolean ALLOW_TVM_CONSTANTINOPLE = false;
-
- @Setter
- private static boolean ALLOW_MULTI_SIGN = false;
-
- @Setter
- private static boolean ALLOW_TVM_SOLIDITY_059 = false;
-
-
- private VMConfig() {
- }
-
- public static VMConfig getInstance() {
- return SystemPropertiesInstance.INSTANCE;
- }
-
- 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 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;
- }
-
- private static class SystemPropertiesInstance {
- private static final VMConfig INSTANCE = new VMConfig();
- }
-}
\ No newline at end of file
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
new file mode 100644
index 00000000000..3088527ace6
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/FreezeBalanceProcessor.java
@@ -0,0 +1,169 @@
+package org.tron.core.vm.nativecontract;
+
+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 com.google.protobuf.ByteString;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.FastByteComparisons;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.DelegatedResourceCapsule;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.vm.nativecontract.param.FreezeBalanceParam;
+import org.tron.core.vm.repository.Repository;
+import org.tron.protos.Protocol;
+
+@Slf4j(topic = "VMProcessor")
+public class FreezeBalanceProcessor {
+
+ public void validate(FreezeBalanceParam param, Repository repo) throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ // validate arg @frozenBalance
+ byte[] ownerAddress = param.getOwnerAddress();
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddress);
+ 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 frozen count of owner account
+ int frozenCount = ownerCapsule.getFrozenCount();
+ if (frozenCount != 0 && frozenCount != 1) {
+ throw new ContractValidateException("FrozenCount must be 0 or 1");
+ }
+
+ // validate arg @resourceType
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ case ENERGY:
+ break;
+ default:
+ throw new ContractValidateException(
+ "Unknown ResourceCode, valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+
+ // validate for delegating resource
+ byte[] receiverAddress = param.getReceiverAddress();
+ if (!FastByteComparisons.isEqual(ownerAddress, receiverAddress)) {
+ param.setDelegating(true);
+
+ // check if receiver account exists. if not, then create a new account
+ AccountCapsule receiverCapsule = repo.getAccount(receiverAddress);
+ if (receiverCapsule == null) {
+ receiverCapsule = repo.createNormalAccount(receiverAddress);
+ }
+
+ // forbid delegating resource to contract account
+ if (receiverCapsule.getType() == Protocol.AccountType.Contract) {
+ throw new ContractValidateException(
+ "Do not allow delegate resources to contract addresses");
+ }
+ }
+ }
+
+ public void execute(FreezeBalanceParam param, Repository repo) {
+ // calculate expire time
+ DynamicPropertiesStore dynamicStore = repo.getDynamicPropertiesStore();
+ long nowInMs = dynamicStore.getLatestBlockHeaderTimestamp();
+ long expireTime = nowInMs + param.getFrozenDuration() * FROZEN_PERIOD;
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ byte[] receiverAddress = param.getReceiverAddress();
+ long frozenBalance = param.getFrozenBalance();
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+ // acquire or delegate resource
+ if (param.isDelegating()) { // delegate resource
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ delegateResource(ownerAddress, receiverAddress,
+ frozenBalance, expireTime, true, repo);
+ accountCapsule.addDelegatedFrozenBalanceForBandwidth(frozenBalance);
+ break;
+ case ENERGY:
+ delegateResource(ownerAddress, receiverAddress,
+ frozenBalance, expireTime, false, repo);
+ accountCapsule.addDelegatedFrozenBalanceForEnergy(frozenBalance);
+ break;
+ default:
+ logger.debug("Resource Code Error.");
+ }
+ } else { // acquire resource
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ accountCapsule.setFrozenForBandwidth(
+ frozenBalance + accountCapsule.getFrozenBalance(),
+ expireTime);
+ break;
+ case ENERGY:
+ accountCapsule.setFrozenForEnergy(
+ frozenBalance + accountCapsule.getAccountResource()
+ .getFrozenBalanceForEnergy()
+ .getFrozenBalance(),
+ expireTime);
+ break;
+ default:
+ logger.debug("Resource Code Error.");
+ }
+ }
+
+ // adjust total resource
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ repo.addTotalNetWeight(frozenBalance / TRX_PRECISION);
+ break;
+ case ENERGY:
+ repo.addTotalEnergyWeight(frozenBalance / TRX_PRECISION);
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+
+ // deduce balance of owner account
+ long newBalance = accountCapsule.getBalance() - frozenBalance;
+ accountCapsule.setBalance(newBalance);
+ repo.updateAccount(accountCapsule.createDbKey(), accountCapsule);
+ }
+
+ private void delegateResource(
+ byte[] ownerAddress,
+ byte[] receiverAddress,
+ long frozenBalance,
+ long expireTime,
+ boolean isBandwidth,
+ Repository repo) {
+ byte[] key = DelegatedResourceCapsule.createDbKey(ownerAddress, receiverAddress);
+
+ // insert or update DelegateResource
+ DelegatedResourceCapsule delegatedResourceCapsule = repo.getDelegatedResource(key);
+ if (delegatedResourceCapsule == null) {
+ delegatedResourceCapsule = new DelegatedResourceCapsule(
+ ByteString.copyFrom(ownerAddress),
+ ByteString.copyFrom(receiverAddress));
+ }
+ if (isBandwidth) {
+ delegatedResourceCapsule.addFrozenBalanceForBandwidth(frozenBalance, expireTime);
+ } else {
+ delegatedResourceCapsule.addFrozenBalanceForEnergy(frozenBalance, expireTime);
+ }
+ repo.updateDelegatedResource(key, delegatedResourceCapsule);
+
+ // do delegating resource to receiver account
+ AccountCapsule receiverCapsule = repo.getAccount(receiverAddress);
+ if (isBandwidth) {
+ receiverCapsule.addAcquiredDelegatedFrozenBalanceForBandwidth(frozenBalance);
+ } else {
+ receiverCapsule.addAcquiredDelegatedFrozenBalanceForEnergy(frozenBalance);
+ }
+ repo.updateAccount(receiverCapsule.createDbKey(), receiverCapsule);
+ }
+}
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
new file mode 100644
index 00000000000..53981a22d34
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/UnfreezeBalanceProcessor.java
@@ -0,0 +1,228 @@
+package org.tron.core.vm.nativecontract;
+
+import static org.tron.core.actuator.ActuatorConstant.STORE_NOT_EXIST;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+
+import com.google.common.collect.Lists;
+import com.google.protobuf.ByteString;
+import java.util.Iterator;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.utils.FastByteComparisons;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.capsule.DelegatedResourceCapsule;
+import org.tron.core.capsule.VotesCapsule;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.vm.config.VMConfig;
+import org.tron.core.vm.nativecontract.param.UnfreezeBalanceParam;
+import org.tron.core.vm.repository.Repository;
+import org.tron.core.vm.utils.VoteRewardUtil;
+import org.tron.protos.Protocol;
+
+@Slf4j(topic = "VMProcessor")
+public class UnfreezeBalanceProcessor {
+
+ public void validate(UnfreezeBalanceParam param, Repository repo)
+ throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ byte[] ownerAddress = param.getOwnerAddress();
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddress);
+ byte[] receiverAddress = param.getReceiverAddress();
+ long now = repo.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ if (!FastByteComparisons.isEqual(ownerAddress, receiverAddress)) {
+ param.setDelegating(true);
+
+ // check if delegated resource exists
+ byte[] key = DelegatedResourceCapsule.createDbKey(ownerAddress, receiverAddress);
+ DelegatedResourceCapsule delegatedResourceCapsule = repo.getDelegatedResource(key);
+ if (delegatedResourceCapsule == null) {
+ throw new ContractValidateException("delegated Resource does not exist");
+ }
+
+ // validate args @frozenBalance and @expireTime
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ // validate frozen balance
+ if (delegatedResourceCapsule.getFrozenBalanceForBandwidth() <= 0) {
+ throw new ContractValidateException("no delegatedFrozenBalance(BANDWIDTH)");
+ }
+ // check if it is time to unfreeze
+ if (delegatedResourceCapsule.getExpireTimeForBandwidth() > now) {
+ throw new ContractValidateException("It's not time to unfreeze(BANDWIDTH).");
+ }
+ break;
+ case ENERGY:
+ // validate frozen balance
+ if (delegatedResourceCapsule.getFrozenBalanceForEnergy() <= 0) {
+ throw new ContractValidateException("no delegateFrozenBalance(Energy)");
+ }
+ // check if it is time to unfreeze
+ if (delegatedResourceCapsule.getExpireTimeForEnergy() > now) {
+ throw new ContractValidateException("It's not time to unfreeze(Energy).");
+ }
+ break;
+ default:
+ throw new ContractValidateException("Unknown ResourceCode, "
+ + "valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ } else {
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ // validate frozen balance
+ if (ownerCapsule.getFrozenCount() <= 0) {
+ throw new ContractValidateException("no frozenBalance(BANDWIDTH)");
+ }
+ // check if it is time to unfreeze
+ long allowedUnfreezeCount = ownerCapsule.getFrozenList().stream()
+ .filter(frozen -> frozen.getExpireTime() <= now).count();
+ if (allowedUnfreezeCount <= 0) {
+ throw new ContractValidateException("It's not time to unfreeze(BANDWIDTH).");
+ }
+ break;
+ case ENERGY:
+ Protocol.Account.Frozen frozenForEnergy = ownerCapsule.getAccountResource()
+ .getFrozenBalanceForEnergy();
+ // validate frozen balance
+ if (frozenForEnergy.getFrozenBalance() <= 0) {
+ throw new ContractValidateException("no frozenBalance(Energy)");
+ }
+ // check if it is time to unfreeze
+ if (frozenForEnergy.getExpireTime() > now) {
+ throw new ContractValidateException("It's not time to unfreeze(Energy).");
+ }
+ break;
+ default:
+ throw new ContractValidateException("Unknown ResourceCode, "
+ + "valid ResourceCode[BANDWIDTH、ENERGY]");
+ }
+ }
+ }
+
+ public long execute(UnfreezeBalanceParam param, Repository repo) {
+ byte[] ownerAddress = param.getOwnerAddress();
+ byte[] receiverAddress = param.getReceiverAddress();
+
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+ long oldBalance = accountCapsule.getBalance();
+ long unfreezeBalance = 0L;
+
+ if (param.isDelegating()) {
+ byte[] key = DelegatedResourceCapsule.createDbKey(ownerAddress, receiverAddress);
+ DelegatedResourceCapsule delegatedResourceCapsule = repo.getDelegatedResource(key);
+
+ // reset delegated resource and deduce delegated balance
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ unfreezeBalance = delegatedResourceCapsule.getFrozenBalanceForBandwidth();
+ delegatedResourceCapsule.setFrozenBalanceForBandwidth(0, 0);
+ accountCapsule.addDelegatedFrozenBalanceForBandwidth(-unfreezeBalance);
+ break;
+ case ENERGY:
+ unfreezeBalance = delegatedResourceCapsule.getFrozenBalanceForEnergy();
+ delegatedResourceCapsule.setFrozenBalanceForEnergy(0, 0);
+ accountCapsule.addDelegatedFrozenBalanceForEnergy(-unfreezeBalance);
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+ repo.updateDelegatedResource(key, delegatedResourceCapsule);
+
+ // take back resource from receiver account
+ AccountCapsule receiverCapsule = repo.getAccount(receiverAddress);
+ if (receiverCapsule != null) {
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ receiverCapsule.safeAddAcquiredDelegatedFrozenBalanceForBandwidth(-unfreezeBalance,
+ VMConfig.disableJavaLangMath());
+ break;
+ case ENERGY:
+ receiverCapsule.safeAddAcquiredDelegatedFrozenBalanceForEnergy(-unfreezeBalance,
+ VMConfig.disableJavaLangMath());
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+ repo.updateAccount(receiverCapsule.createDbKey(), receiverCapsule);
+ }
+
+ // increase balance of owner
+ accountCapsule.setBalance(oldBalance + unfreezeBalance);
+ } else {
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ List frozenList = Lists.newArrayList();
+ frozenList.addAll(accountCapsule.getFrozenList());
+ Iterator iterator = frozenList.iterator();
+ long now = repo.getDynamicPropertiesStore().getLatestBlockHeaderTimestamp();
+ while (iterator.hasNext()) {
+ Protocol.Account.Frozen next = iterator.next();
+ if (next.getExpireTime() <= now) {
+ unfreezeBalance += next.getFrozenBalance();
+ iterator.remove();
+ }
+ }
+ accountCapsule.setInstance(accountCapsule.getInstance().toBuilder()
+ .setBalance(oldBalance + unfreezeBalance)
+ .clearFrozen().addAllFrozen(frozenList).build());
+ break;
+ case ENERGY:
+ unfreezeBalance = accountCapsule.getAccountResource().getFrozenBalanceForEnergy()
+ .getFrozenBalance();
+ Protocol.Account.AccountResource newAccountResource =
+ accountCapsule.getAccountResource().toBuilder()
+ .clearFrozenBalanceForEnergy().build();
+ accountCapsule.setInstance(accountCapsule.getInstance().toBuilder()
+ .setBalance(oldBalance + unfreezeBalance)
+ .setAccountResource(newAccountResource).build());
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+
+ }
+
+ // adjust total resource, used to be a bug here
+ switch (param.getResourceType()) {
+ case BANDWIDTH:
+ repo.addTotalNetWeight(-unfreezeBalance / TRX_PRECISION);
+ break;
+ case ENERGY:
+ repo.addTotalEnergyWeight(-unfreezeBalance / TRX_PRECISION);
+ break;
+ default:
+ //this should never happen
+ break;
+ }
+
+ repo.updateAccount(accountCapsule.createDbKey(), accountCapsule);
+
+ if (VMConfig.allowTvmVote() && !accountCapsule.getVotesList().isEmpty()) {
+ long usedTronPower = 0;
+ for (Protocol.Vote vote : accountCapsule.getVotesList()) {
+ usedTronPower += vote.getVoteCount();
+ }
+ if (accountCapsule.getTronPower() < usedTronPower * TRX_PRECISION) {
+ VoteRewardUtil.withdrawReward(ownerAddress, repo);
+ VotesCapsule votesCapsule = repo.getVotes(ownerAddress);
+ accountCapsule = repo.getAccount(ownerAddress);
+ if (votesCapsule == null) {
+ votesCapsule = new VotesCapsule(ByteString.copyFrom(ownerAddress),
+ accountCapsule.getVotesList());
+ } else {
+ votesCapsule.clearNewVotes();
+ }
+ accountCapsule.clearVotes();
+ repo.updateVotes(ownerAddress, votesCapsule);
+ repo.updateAccount(ownerAddress, accountCapsule);
+ }
+ }
+
+ return unfreezeBalance;
+ }
+}
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
new file mode 100644
index 00000000000..8e17ffe8b13
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/VoteWitnessProcessor.java
@@ -0,0 +1,112 @@
+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.actuator.ActuatorConstant.WITNESS_EXCEPTION_STR;
+import static org.tron.core.config.Parameter.ChainConstant.MAX_VOTE_NUMBER;
+import static org.tron.core.config.Parameter.ChainConstant.TRX_PRECISION;
+
+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;
+import org.tron.core.capsule.VotesCapsule;
+import org.tron.core.exception.ContractExeException;
+import org.tron.core.exception.ContractValidateException;
+import org.tron.core.vm.nativecontract.param.VoteWitnessParam;
+import org.tron.core.vm.repository.Repository;
+import org.tron.core.vm.utils.VoteRewardUtil;
+import org.tron.protos.Protocol;
+
+@Slf4j(topic = "VMProcessor")
+public class VoteWitnessProcessor {
+
+ public void validate(VoteWitnessParam param, Repository repo) throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ if (param.getVotes().size() > MAX_VOTE_NUMBER) {
+ throw new ContractValidateException(
+ "VoteNumber more than maxVoteNumber " + MAX_VOTE_NUMBER);
+ }
+ }
+
+ public void execute(VoteWitnessParam param, Repository repo) throws ContractExeException {
+ byte[] ownerAddress = param.getVoterAddress();
+ VoteRewardUtil.withdrawReward(ownerAddress, repo);
+
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+
+ VotesCapsule votesCapsule = repo.getVotes(ownerAddress);
+ if (votesCapsule == null) {
+ votesCapsule = new VotesCapsule(ByteString.copyFrom(ownerAddress),
+ accountCapsule.getVotesList());
+ }
+
+ accountCapsule.clearVotes();
+ votesCapsule.clearNewVotes();
+
+ Map voteMap = new HashMap<>();
+ Iterator iterator = param.getVotes().iterator();
+ try {
+ long sum = 0;
+ while (iterator.hasNext()) {
+ Protocol.Vote vote = iterator.next();
+
+ byte[] witnessAddress = vote.getVoteAddress().toByteArray();
+ /*
+ Already covered while doing maintenance in MaintenanceManager.java, for tvm performance,
+ we remove the account check
+ */
+// if (repo.getAccount(witnessAddress) == null) {
+// throw new ContractValidateException(
+// ACCOUNT_EXCEPTION_STR + StringUtil.encode58Check(witnessAddress) + NOT_EXIST_STR);
+// }
+ if (repo.getWitness(witnessAddress) == null) {
+ throw new ContractExeException(
+ WITNESS_EXCEPTION_STR + StringUtil.encode58Check(witnessAddress) + NOT_EXIST_STR);
+ }
+
+ long voteCount = vote.getVoteCount();
+ if (voteCount < 0) {
+ throw new ContractExeException("Vote count must not be less than 0");
+ } else if (voteCount == 0) {
+ iterator.remove();
+ } else {
+ sum = LongMath.checkedAdd(sum, voteCount);
+ // merge vote for same witness
+ voteMap.put(vote.getVoteAddress(),
+ LongMath.checkedAdd(voteMap.getOrDefault(vote.getVoteAddress(), 0L), voteCount));
+ }
+ }
+
+ 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(
+ "The total number of votes[" + sum + "] is greater than the tronPower[" + tronPower
+ + "]");
+ }
+ } catch (ArithmeticException e) {
+ throw new ContractExeException(e.getMessage());
+ }
+
+ for (Map.Entry entry : voteMap.entrySet()) {
+ accountCapsule.addVotes(entry.getKey(), entry.getValue());
+ votesCapsule.addNewVotes(entry.getKey(), entry.getValue());
+ }
+ repo.updateAccount(accountCapsule.createDbKey(), accountCapsule);
+ repo.updateVotes(ownerAddress, votesCapsule);
+ }
+}
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/WithdrawRewardProcessor.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/WithdrawRewardProcessor.java
new file mode 100644
index 00000000000..577e51e379a
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/WithdrawRewardProcessor.java
@@ -0,0 +1,69 @@
+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 com.google.common.math.LongMath;
+import java.util.Arrays;
+import lombok.extern.slf4j.Slf4j;
+import org.tron.common.parameter.CommonParameter;
+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.nativecontract.param.WithdrawRewardParam;
+import org.tron.core.vm.repository.Repository;
+import org.tron.core.vm.utils.VoteRewardUtil;
+
+@Slf4j(topic = "VMProcessor")
+public class WithdrawRewardProcessor {
+
+ public void validate(WithdrawRewardParam param, Repository repo) throws ContractValidateException {
+ if (repo == null) {
+ throw new ContractValidateException(STORE_NOT_EXIST);
+ }
+
+ byte[] ownerAddress = param.getOwnerAddress();
+
+ boolean isGP = CommonParameter.getInstance()
+ .getGenesisBlock().getWitnesses().stream().anyMatch(witness ->
+ Arrays.equals(ownerAddress, witness.getAddress()));
+ if (isGP) {
+ throw new ContractValidateException(
+ ACCOUNT_EXCEPTION_STR + StringUtil.encode58Check(ownerAddress)
+ + "] is a guard representative and is not allowed to withdraw Balance");
+ }
+ }
+
+ public long execute(WithdrawRewardParam param, Repository repo) throws ContractExeException {
+ byte[] ownerAddress = param.getOwnerAddress();
+
+ VoteRewardUtil.withdrawReward(ownerAddress, repo);
+
+ AccountCapsule accountCapsule = repo.getAccount(ownerAddress);
+ long oldBalance = accountCapsule.getBalance();
+ long allowance = accountCapsule.getAllowance();
+ long newBalance = 0;
+
+ try {
+ newBalance = LongMath.checkedAdd(oldBalance, allowance);
+ } catch (ArithmeticException e) {
+ logger.debug(e.getMessage(), e);
+ throw new ContractExeException(e.getMessage());
+ }
+
+ // If no allowance, do nothing and just return zero.
+ if (allowance <= 0) {
+ return 0;
+ }
+
+ accountCapsule.setInstance(accountCapsule.getInstance().toBuilder()
+ .setBalance(newBalance)
+ .setAllowance(0L)
+ .setLatestWithdrawTime(param.getNowInMs())
+ .build());
+
+ repo.updateAccount(accountCapsule.createDbKey(), accountCapsule);
+ return allowance;
+ }
+}
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/FreezeBalanceParam.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/FreezeBalanceParam.java
new file mode 100644
index 00000000000..a87275891fb
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/FreezeBalanceParam.java
@@ -0,0 +1,66 @@
+package org.tron.core.vm.nativecontract.param;
+
+import org.tron.protos.contract.Common;
+
+public class FreezeBalanceParam {
+
+ private byte[] ownerAddress;
+
+ private byte[] receiverAddress;
+
+ private long frozenBalance;
+
+ private long frozenDuration;
+
+ private Common.ResourceCode resourceType;
+
+ private boolean isDelegating;
+
+ 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 getFrozenBalance() {
+ return frozenBalance;
+ }
+
+ public void setFrozenBalance(long frozenBalance) {
+ this.frozenBalance = frozenBalance;
+ }
+
+ public long getFrozenDuration() {
+ return frozenDuration;
+ }
+
+ public void setFrozenDuration(long frozenDuration) {
+ this.frozenDuration = frozenDuration;
+ }
+
+ public Common.ResourceCode getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(Common.ResourceCode resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public boolean isDelegating() {
+ return isDelegating;
+ }
+
+ public void setDelegating(boolean delegating) {
+ isDelegating = delegating;
+ }
+}
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/UnfreezeBalanceParam.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/UnfreezeBalanceParam.java
new file mode 100644
index 00000000000..30b2cd298b6
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/UnfreezeBalanceParam.java
@@ -0,0 +1,46 @@
+package org.tron.core.vm.nativecontract.param;
+
+import org.tron.protos.contract.Common;
+
+public class UnfreezeBalanceParam {
+
+ private byte[] ownerAddress;
+
+ private byte[] receiverAddress;
+
+ private Common.ResourceCode resourceType;
+
+ private boolean isDelegating;
+
+ 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 Common.ResourceCode getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(Common.ResourceCode resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public boolean isDelegating() {
+ return isDelegating;
+ }
+
+ public void setDelegating(boolean delegating) {
+ isDelegating = delegating;
+ }
+}
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/VoteWitnessParam.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/VoteWitnessParam.java
new file mode 100644
index 00000000000..6f831987519
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/VoteWitnessParam.java
@@ -0,0 +1,54 @@
+package org.tron.core.vm.nativecontract.param;
+
+import com.google.protobuf.ByteString;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.tron.common.utils.StringUtil;
+import org.tron.protos.Protocol;
+
+/**
+ * Param used by VoteWitnessProcessor
+ */
+public class VoteWitnessParam {
+
+ // Address of voter
+ private byte[] voterAddress;
+
+ // List of voter`s votes. Every entry contains witness address and vote count
+ private final List votes = new ArrayList<>();
+
+ public byte[] getVoterAddress() {
+ return voterAddress;
+ }
+
+ public void setVoterAddress(byte[] voterAddress) {
+ this.voterAddress = voterAddress;
+ }
+
+ public List getVotes() {
+ return votes;
+ }
+
+ public void addVote(byte[] witnessAddress, long tronPower) {
+ this.votes.add(Protocol.Vote.newBuilder()
+ .setVoteAddress(ByteString.copyFrom(witnessAddress))
+ .setVoteCount(tronPower)
+ .build());
+ }
+
+ public String toJsonStr() {
+ StringBuilder sb = new StringBuilder("{\"votes\":[");
+ String template = "{\"vote_address\":\"%s\",\"vote_count\":%d}";
+ for (Protocol.Vote vote : votes) {
+ sb.append(String.format(template,
+ StringUtil.encode58Check(vote.getVoteAddress().toByteArray()),
+ vote.getVoteCount())).append(",");
+ }
+ if (!votes.isEmpty()) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ sb.append("]}");
+ return sb.toString();
+ }
+}
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/nativecontract/param/WithdrawRewardParam.java b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/WithdrawRewardParam.java
new file mode 100644
index 00000000000..a5fb1e86325
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/nativecontract/param/WithdrawRewardParam.java
@@ -0,0 +1,29 @@
+package org.tron.core.vm.nativecontract.param;
+
+/**
+ * Param used by WithdrawRewardProcessor
+ */
+public class WithdrawRewardParam {
+
+ // Account address which want to withdraw its reward
+ private byte[] ownerAddress;
+
+ // Latest block timestamp
+ private long nowInMs;
+
+ public byte[] getOwnerAddress() {
+ return ownerAddress;
+ }
+
+ public void setOwnerAddress(byte[] ownerAddress) {
+ this.ownerAddress = ownerAddress;
+ }
+
+ public long getNowInMs() {
+ return nowInMs;
+ }
+
+ public void setNowInMs(long nowInMs) {
+ this.nowInMs = nowInMs;
+ }
+}
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 cee86ee0c9d..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,31 +1,20 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.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;
import org.tron.core.store.AssetIssueStore;
import org.tron.core.store.AssetIssueV2Store;
+import org.tron.core.store.DelegationStore;
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.core.vm.program.invoke.ProgramInvoke;
import org.tron.core.vm.program.listener.ProgramListener;
@@ -88,9 +77,18 @@ public AccountCapsule getAccount(byte[] addr) {
return repository.getAccount(addr);
}
+ public BytesCapsule getDynamicProperty(byte[] bytesKey) {
+ return repository.getDynamicProperty(bytesKey);
+ }
- public BytesCapsule getDynamic(byte[] bytesKey) {
- return repository.getDynamic(bytesKey);
+ @Override
+ public DelegatedResourceCapsule getDelegatedResource(byte[] key) {
+ return repository.getDelegatedResource(key);
+ }
+
+ @Override
+ public WitnessCapsule getWitness(byte[] address) {
+ return repository.getWitness(address);
}
@Override
@@ -108,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);
@@ -186,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);
@@ -202,6 +224,26 @@ public void putAccountValue(byte[] address, AccountCapsule accountCapsule) {
this.repository.putAccountValue(address, accountCapsule);
}
+ @Override
+ public void putDelegatedResource(Key key, Value value) {
+ repository.putDelegatedResource(key, value);
+ }
+
+ @Override
+ 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);
@@ -217,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);
@@ -237,4 +294,154 @@ public AccountCapsule createNormalAccount(byte[] address) {
return repository.createNormalAccount(address);
}
+ @Override
+ public DelegationStore getDelegationStore() {
+ return repository.getDelegationStore();
+ }
+
+ @Override
+ public VotesCapsule getVotes(byte[] address) {
+ return repository.getVotes(address);
+ }
+
+ @Override
+ public long getBeginCycle(byte[] address) {
+ return repository.getBeginCycle(address);
+ }
+
+ @Override
+ public long getEndCycle(byte[] address) {
+ return repository.getEndCycle(address);
+ }
+
+ @Override
+ public AccountCapsule getAccountVote(long cycle, byte[] address) {
+ return repository.getAccountVote(cycle, address);
+ }
+
+ @Override
+ 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);
+ }
+
+ @Override
+ public void updateDelegatedResource(byte[] word, DelegatedResourceCapsule delegatedResourceCapsule) {
+ repository.updateDelegatedResource(word, delegatedResourceCapsule);
+ }
+
+ @Override
+ public void updateVotes(byte[] word, VotesCapsule votesCapsule) {
+ repository.updateVotes(word, votesCapsule);
+ }
+
+ @Override
+ public void updateBeginCycle(byte[] word, long cycle) {
+ repository.updateBeginCycle(word, cycle);
+ }
+
+ @Override
+ public void updateEndCycle(byte[] word, long cycle) {
+ repository.updateEndCycle(word, cycle);
+ }
+
+ @Override
+ public void updateAccountVote(byte[] word, long cycle, AccountCapsule accountCapsule) {
+ repository.updateAccountVote(word, cycle, accountCapsule);
+ }
+
+ @Override
+ 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);
+ }
+
+ @Override
+ public void putVotes(Key key, Value value) {
+ repository.putVotes(key, value);
+ }
+
+ @Override
+ public void addTotalNetWeight(long amount) {
+ repository.addTotalNetWeight(amount);
+ }
+
+ @Override
+ 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);
+ }
+
+ @Override
+ public void saveTotalEnergyWeight(long totalEnergyWeight) {
+ repository.saveTotalEnergyWeight(totalEnergyWeight);
+ }
+
+ @Override
+ public void saveTotalTronPowerWeight(long totalTronPowerWeight) {
+ repository.saveTotalTronPowerWeight(totalTronPowerWeight);
+ }
+
+ @Override
+ public long getTotalNetWeight() {
+ return repository.getTotalNetWeight();
+ }
+
+ @Override
+ 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 58f4f7651ad..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,31 +1,16 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.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;
@@ -113,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);
@@ -198,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;
@@ -210,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 f8fb87dc308..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,46 +1,38 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.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.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.util.Arrays;
-import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
-import java.util.NavigableSet;
import java.util.Objects;
-import java.util.TreeSet;
+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;
-import org.spongycastle.util.encoders.Hex;
+import org.bouncycastle.util.encoders.Hex;
+import org.tron.common.crypto.Hash;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.InternalTransaction;
import org.tron.common.runtime.ProgramResult;
@@ -48,44 +40,69 @@
import org.tron.common.utils.BIUtil;
import org.tron.common.utils.ByteUtil;
import org.tron.common.utils.FastByteComparisons;
-import org.tron.common.crypto.Hash;
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.db.TransactionTrace;
+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;
import org.tron.core.utils.TransactionUtil;
import org.tron.core.vm.EnergyCost;
import org.tron.core.vm.MessageCall;
-import org.tron.core.vm.OpCode;
+import org.tron.core.vm.Op;
+import org.tron.core.vm.OperationRegistry;
import org.tron.core.vm.PrecompiledContracts;
import org.tron.core.vm.VM;
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;
-import org.tron.core.vm.program.invoke.ProgramInvokeFactoryImpl;
import org.tron.core.vm.program.listener.CompositeProgramListener;
import org.tron.core.vm.program.listener.ProgramListenerAware;
import org.tron.core.vm.program.listener.ProgramStorageChangeListener;
+import org.tron.core.vm.repository.Key;
import org.tron.core.vm.repository.Repository;
import org.tron.core.vm.trace.ProgramTrace;
import org.tron.core.vm.trace.ProgramTraceListener;
import org.tron.core.vm.utils.MUtil;
+import org.tron.core.vm.utils.VoteRewardUtil;
import org.tron.protos.Protocol;
import org.tron.protos.Protocol.AccountType;
+import org.tron.protos.contract.Common;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract.Builder;
-/**
- * @author Roman Mandeleil
- * @since 01.06.2014
- */
-
@Slf4j(topic = "VM")
public class Program {
@@ -97,12 +114,13 @@ public class Program {
private static final String INVALID_TOKEN_ID_MSG = "not valid token id";
private static final String REFUND_ENERGY_FROM_MESSAGE_CALL = "refund energy from message call";
private static final String CALL_PRE_COMPILED = "call pre-compiled";
- private final VMConfig config;
+ private static final int lruCacheSize = CommonParameter.getInstance().getSafeLruCacheSize();
+ private static final LRUMap programPrecompileLRUMap
+ = new LRUMap<>(lruCacheSize);
private long nonce;
private byte[] rootTransactionId;
private InternalTransaction internalTransaction;
private ProgramInvoke invoke;
- private ProgramInvokeFactory programInvokeFactory = new ProgramInvokeFactoryImpl();
private ProgramOutListener listener;
private ProgramTraceListener traceListener;
private ProgramStorageChangeListener storageDiffListener = new ProgramStorageChangeListener();
@@ -114,179 +132,69 @@ public class Program {
private ProgramResult result = new ProgramResult();
private ProgramTrace trace = new ProgramTrace();
private byte[] ops;
+ private byte[] codeAddress;
private int pc;
private byte lastOp;
private byte previouslyExecutedOp;
private boolean stopped;
private ProgramPrecompile programPrecompile;
-
- public Program(byte[] ops, ProgramInvoke programInvoke) {
- this(ops, programInvoke, null);
- }
-
- public Program(byte[] ops, ProgramInvoke programInvoke, InternalTransaction internalTransaction) {
- this(ops, programInvoke, internalTransaction, VMConfig.getInstance());
- }
-
- public Program(byte[] ops, ProgramInvoke programInvoke, InternalTransaction internalTransaction,
- VMConfig config) {
- this.config = config;
+ 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) {
this.invoke = programInvoke;
this.internalTransaction = internalTransaction;
this.ops = nullToEmpty(ops);
+ this.codeAddress = codeAddress;
- traceListener = new ProgramTraceListener(config.vmTrace());
+ traceListener = new ProgramTraceListener(VMConfig.vmTrace());
this.memory = setupProgramListener(new Memory());
this.stack = setupProgramListener(new Stack());
this.contractState = setupProgramListener(new ContractState(programInvoke));
- this.trace = new ProgramTrace(config, programInvoke);
+ this.trace = new ProgramTrace(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();
}
- public static String stringifyMultiline(byte[] code) {
- int index = 0;
- StringBuilder sb = new StringBuilder();
- BitSet mask = buildReachableBytecodesMask(code);
- ByteArrayOutputStream binData = new ByteArrayOutputStream();
- int binDataStartPC = -1;
-
- while (index < code.length) {
- final byte opCode = code[index];
- OpCode op = OpCode.code(opCode);
-
- if (!mask.get(index)) {
- if (binDataStartPC == -1) {
- binDataStartPC = index;
- }
- binData.write(code[index]);
- index++;
- if (index < code.length) {
- continue;
- }
- }
-
- if (binDataStartPC != -1) {
- sb.append(formatBinData(binData.toByteArray(), binDataStartPC));
- binDataStartPC = -1;
- binData = new ByteArrayOutputStream();
- if (index == code.length) {
- continue;
- }
- }
-
- sb.append(Utils.align("" + Integer.toHexString(index) + ":", ' ', 8, false));
-
- if (op == null) {
- sb.append(": ").append(0xFF & opCode).append("\n");
- index++;
- continue;
- }
-
- if (op.name().startsWith("PUSH")) {
- sb.append(' ').append(op.name()).append(' ');
-
- int nPush = op.val() - OpCode.PUSH1.val() + 1;
- byte[] data = Arrays.copyOfRange(code, index + 1, index + nPush + 1);
- BigInteger bi = new BigInteger(1, data);
- sb.append("0x").append(bi.toString(16));
- if (bi.bitLength() <= 32) {
- sb.append(" (").append(new BigInteger(1, data).toString()).append(") ");
- }
-
- index += nPush + 1;
- } else {
- sb.append(' ').append(op.name());
- index++;
- }
- sb.append('\n');
- }
-
- return sb.toString();
+ public byte[] getRootTransactionId() {
+ return rootTransactionId.clone();
}
- static BitSet buildReachableBytecodesMask(byte[] code) {
- NavigableSet gotos = new TreeSet<>();
- ByteCodeIterator it = new ByteCodeIterator(code);
- BitSet ret = new BitSet(code.length);
- int lastPush = 0;
- int lastPushPC = 0;
- do {
- ret.set(it.getPC()); // reachable bytecode
- if (it.isPush()) {
- lastPush = new BigInteger(1, it.getCurOpcodeArg()).intValue();
- lastPushPC = it.getPC();
- }
- if (it.getCurOpcode() == OpCode.JUMP || it.getCurOpcode() == OpCode.JUMPI) {
- if (it.getPC() != lastPushPC + 1) {
- // some PC arithmetic we totally can't deal with
- // assuming all bytecodes are reachable as a fallback
- ret.set(0, code.length);
- return ret;
- }
- int jumpPC = lastPush;
- if (!ret.get(jumpPC)) {
- // code was not explored yet
- gotos.add(jumpPC);
- }
- }
- if (it.getCurOpcode() == OpCode.JUMP || it.getCurOpcode() == OpCode.RETURN
- || it.getCurOpcode() == OpCode.STOP) {
- if (gotos.isEmpty()) {
- break;
- }
- it.setPC(gotos.pollFirst());
- }
- } while (it.next());
- return ret;
+ public void setRootTransactionId(byte[] rootTransactionId) {
+ this.rootTransactionId = rootTransactionId.clone();
}
- public static String stringify(byte[] code) {
- int index = 0;
- StringBuilder sb = new StringBuilder();
-
- while (index < code.length) {
- final byte opCode = code[index];
- OpCode op = OpCode.code(opCode);
-
- if (op == null) {
- sb.append(" : ").append(0xFF & opCode).append(" ");
- index++;
- continue;
- }
-
- if (op.name().startsWith("PUSH")) {
- sb.append(' ').append(op.name()).append(' ');
-
- int nPush = op.val() - OpCode.PUSH1.val() + 1;
- byte[] data = Arrays.copyOfRange(code, index + 1, index + nPush + 1);
- BigInteger bi = new BigInteger(1, data);
- sb.append("0x").append(bi.toString(16)).append(" ");
-
- index += nPush + 1;
- } else {
- sb.append(' ').append(op.name());
- index++;
- }
- }
+ public void setContractVersion(int version) {
+ this.contractVersion = version;
+ }
- return sb.toString();
+ public int getContractVersion() {
+ return this.contractVersion;
}
- public byte[] getRootTransactionId() {
- return rootTransactionId.clone();
+ public void setAdjustedCallEnergy(DataWord adjustedCallEnergy) {
+ this.adjustedCallEnergy = adjustedCallEnergy;
}
- public void setRootTransactionId(byte[] rootTransactionId) {
- this.rootTransactionId = rootTransactionId.clone();
+ public DataWord getAdjustedCallEnergy() {
+ return this.adjustedCallEnergy;
}
public long getNonce() {
@@ -298,8 +206,20 @@ public void setNonce(long nonceValue) {
}
public ProgramPrecompile getProgramPrecompile() {
+ if (isConstantCall()) {
+ if (programPrecompile == null) {
+ programPrecompile = ProgramPrecompile.compile(ops);
+ }
+ return programPrecompile;
+ }
if (programPrecompile == null) {
- programPrecompile = ProgramPrecompile.compile(ops);
+ Key key = getJumpDestAnalysisCacheKey();
+ if (programPrecompileLRUMap.containsKey(key)) {
+ programPrecompile = programPrecompileLRUMap.get(key);
+ } else {
+ programPrecompile = ProgramPrecompile.compile(ops);
+ programPrecompileLRUMap.put(key, programPrecompile);
+ }
}
return programPrecompile;
}
@@ -310,7 +230,7 @@ public int getCallDeep() {
/**
* @param transferAddress the address send TRX to.
- * @param value the TRX value transferred in the internal transaction
+ * @param value the TRX value transferred in the internal transaction
*/
private InternalTransaction addInternalTx(DataWord energyLimit, byte[] senderAddress,
byte[] transferAddress,
@@ -349,6 +269,10 @@ public byte getCurrentOp() {
return isEmpty(ops) ? 0 : ops[pc];
}
+ public int getCurrentOpIntValue() {
+ return getCurrentOp() & 0xff;
+ }
+
/**
* Last Op can only be set publicly (no getLastOp method), is used for logging.
*/
@@ -356,6 +280,10 @@ public void setLastOp(byte op) {
this.lastOp = op;
}
+ public byte getLastOp() {
+ return this.lastOp;
+ }
+
/**
* Returns the last fully executed OP.
*/
@@ -380,12 +308,11 @@ public void stackPush(DataWord stackWord) {
}
public void stackPushZero() {
- stackPush(new DataWord(0));
+ stackPush(DataWord.ZERO());
}
public void stackPushOne() {
- DataWord stackWord = new DataWord(1);
- stackPush(stackWord);
+ stackPush(DataWord.ONE());
}
public Stack getStack() {
@@ -477,9 +404,9 @@ public void memorySave(int addr, byte[] value) {
/**
* . Allocates a piece of memory and stores value at given offset address
*
- * @param addr is the offset address
+ * @param addr is the offset address
* @param allocSize size of memory needed to write
- * @param value the data to write to memory
+ * @param value the data to write to memory
*/
public void memorySave(int addr, int allocSize, byte[] value) {
memory.extendAndWrite(addr, allocSize, value);
@@ -507,11 +434,15 @@ 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
*
* @param offset the memory address offset
- * @param size the number of bytes to allocate
+ * @param size the number of bytes to allocate
*/
public void allocateMemory(int offset, int size) {
memory.extend(offset, size);
@@ -519,8 +450,13 @@ public void allocateMemory(int offset, int size) {
public void suicide(DataWord obtainerAddress) {
- byte[] owner = TransactionTrace.convertToTronAddress(getContractAddress().getLast20Bytes());
- byte[] obtainer = TransactionTrace.convertToTronAddress(obtainerAddress.getLast20Bytes());
+ byte[] owner = getContextAddress();
+ byte[] obtainer = obtainerAddress.toTronAddress();
+
+ if (VMConfig.allowTvmVote()) {
+ withdrawRewardAndCancelVote(owner, getContractState());
+ }
+
long balance = getContractState().getBalance(owner);
if (logger.isDebugEnabled()) {
@@ -531,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();
@@ -557,13 +494,304 @@ public void suicide(DataWord obtainerAddress) {
throw new BytecodeExecutionException("transfer failure");
}
}
+ if (VMConfig.allowTvmFreeze()) {
+ byte[] blackHoleAddress = getContractState().getBlackHoleAddress();
+ if (FastByteComparisons.isEqual(owner, obtainer)) {
+ transferDelegatedResourceToInheritor(owner, blackHoleAddress, getContractState());
+ } else {
+ 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;
}
+ private void transferDelegatedResourceToInheritor(byte[] ownerAddr, byte[] inheritorAddr, Repository repo) {
+
+ // delegated resource from sender to owner, just abandon
+ // in order to making that sender can unfreeze their balance in future
+ // nothing will be deleted
+
+ // delegated resource from owner to receiver
+ // there cannot be any resource when suicide
+
+ AccountCapsule ownerCapsule = repo.getAccount(ownerAddr);
+
+ // transfer owner`s frozen balance for bandwidth to inheritor
+ long frozenBalanceForBandwidthOfOwner = 0;
+ // check if frozen for bandwidth exists
+ if (ownerCapsule.getFrozenCount() != 0) {
+ frozenBalanceForBandwidthOfOwner = ownerCapsule.getFrozenList().get(0).getFrozenBalance();
+ }
+ repo.addTotalNetWeight(-frozenBalanceForBandwidthOfOwner / TRX_PRECISION);
+
+ long frozenBalanceForEnergyOfOwner =
+ ownerCapsule.getAccountResource().getFrozenBalanceForEnergy().getFrozenBalance();
+ repo.addTotalEnergyWeight(-frozenBalanceForEnergyOfOwner / TRX_PRECISION);
+
+ // 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) {
+ VoteRewardUtil.withdrawReward(owner, repo);
+
+ AccountCapsule ownerCapsule = repo.getAccount(owner);
+ if (!ownerCapsule.getVotesList().isEmpty()) {
+ VotesCapsule votesCapsule = repo.getVotes(owner);
+ if (votesCapsule == null) {
+ votesCapsule = new VotesCapsule(ByteString.copyFrom(owner),
+ ownerCapsule.getVotesList());
+ } else {
+ 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(addExact(balance, allowance, VMConfig.disableJavaLangMath()))
+ .setAllowance(0)
+ .setLatestWithdrawTime(getTimestamp().longValue() * 1000)
+ .build());
+ repo.updateAccount(ownerCapsule.createDbKey(), ownerCapsule);
+ } catch (ArithmeticException e) {
+ throw new BytecodeExecutionException("Suicide: balance and allowance out of long range.");
+ }
+ }
+
+ public boolean canSuicide() {
+ byte[] owner = getContextAddress();
+ AccountCapsule accountCapsule = getContractState().getAccount(owner);
+
+ 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
+// && getContractState().getAccountVote(
+// getContractState().getBeginCycle(owner), owner) == null);
+// 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
@@ -583,7 +811,7 @@ public void createContract(DataWord value, DataWord memStart, DataWord memSize)
private void createContractImpl(DataWord value, byte[] programCode, byte[] newAddress,
boolean isCreate2) {
- byte[] senderAddress = TransactionTrace.convertToTronAddress(this.getContractAddress().getLast20Bytes());
+ byte[] senderAddress = getContextAddress();
if (logger.isDebugEnabled()) {
logger.debug("creating a new contract inside contract run: [{}]",
@@ -616,6 +844,9 @@ private void createContractImpl(DataWord value, byte[] programCode, byte[] newAd
if (!contractAlreadyExists) {
Builder builder = SmartContract.newBuilder();
+ if (VMConfig.allowTvmCompatibleEvm()) {
+ builder.setVersion(getContractVersion());
+ }
builder.setContractAddress(ByteString.copyFrom(newAddress))
.setConsumeUserResourcePercent(100)
.setOriginAddress(ByteString.copyFrom(senderAddress));
@@ -628,8 +859,12 @@ private void createContractImpl(DataWord value, byte[] programCode, byte[] newAd
} else {
deposit.createAccount(newAddress, "CreatedByContract",
Protocol.AccountType.Contract);
- SmartContract newSmartContract = SmartContract.newBuilder()
- .setContractAddress(ByteString.copyFrom(newAddress)).setConsumeUserResourcePercent(100)
+ Builder builder = SmartContract.newBuilder();
+ if (VMConfig.allowTvmCompatibleEvm()) {
+ builder.setVersion(getContractVersion());
+ }
+ SmartContract newSmartContract = builder.setContractAddress(ByteString.copyFrom(newAddress))
+ .setConsumeUserResourcePercent(100)
.setOriginAddress(ByteString.copyFrom(senderAddress)).build();
deposit.createContract(newAddress, new ContractCapsule(newSmartContract));
// In case of hashing collisions, check for any balance before createAccount()
@@ -659,9 +894,9 @@ private void createContractImpl(DataWord value, byte[] programCode, byte[] newAd
InternalTransaction internalTx = addInternalTx(null, senderAddress, newAddress, endowment,
programCode, "create", nonce, null);
long vmStartInUs = System.nanoTime() / 1000;
- ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
- this, new DataWord(newAddress), getContractAddress(), value, new DataWord(0),
- new DataWord(0),
+ ProgramInvoke programInvoke = ProgramInvokeFactory.createProgramInvoke(
+ this, new DataWord(newAddress), getContractAddress(), value, DataWord.ZERO(),
+ DataWord.ZERO(),
newBalance, null, deposit, false, byTestingSuite(), vmStartInUs,
getVmShouldEndInUs(), energyLimit.longValueSafe());
if (isConstantCall()) {
@@ -674,21 +909,27 @@ this, new DataWord(newAddress), getContractAddress(), value, new DataWord(0),
"Trying to create a contract with existing contract address: 0x" + Hex
.toHexString(newAddress)));
} else if (isNotEmpty(programCode)) {
- VM vm = new VM(config);
- Program program = new Program(programCode, programInvoke, internalTx, config);
+ Program program = new Program(programCode, newAddress, programInvoke, internalTx);
program.setRootTransactionId(this.rootTransactionId);
- vm.play(program);
+ if (VMConfig.allowTvmCompatibleEvm()) {
+ program.setContractVersion(getContractVersion());
+ }
+ VM.play(program, OperationRegistry.getTable());
createResult = program.getResult();
getTrace().merge(program.getTrace());
// always commit nonce
this.nonce = program.nonce;
-
}
// 4. CREATE THE CONTRACT OUT OF RETURN
byte[] code = createResult.getHReturn();
- long saveCodeEnergy = (long) getLength(code) * EnergyCost.getInstance().getCREATE_DATA();
+ if (code.length != 0 && VMConfig.allowTvmLondon() && code[0] == (byte) 0xEF) {
+ createResult.setException(Program.Exception
+ .invalidCodeException());
+ }
+
+ long saveCodeEnergy = (long) getLength(code) * EnergyCost.getCreateData();
long afterSpend =
programInvoke.getEnergyLimit() - createResult.getEnergyUsed() - saveCodeEnergy;
@@ -710,7 +951,10 @@ this, new DataWord(newAddress), getContractAddress(), value, new DataWord(0),
Hex.toHexString(newAddress),
createResult.getException());
- internalTx.reject();
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+
createResult.rejectInternalTransactions();
stackPushZero();
@@ -740,8 +984,7 @@ public void refundEnergyAfterVM(DataWord energyLimit, ProgramResult result) {
refundEnergy(refundEnergy, "remain energy from the internal call");
if (logger.isDebugEnabled()) {
logger.debug("The remaining energy is refunded, account: [{}], energy: [{}] ",
- Hex.toHexString(TransactionTrace.convertToTronAddress(getContractAddress().getLast20Bytes())),
- refundEnergy);
+ Hex.toHexString(getContextAddress()), refundEnergy);
}
}
}
@@ -766,12 +1009,18 @@ public void callToAddress(MessageCall msg) {
byte[] data = memoryChunk(msg.getInDataOffs().intValue(), msg.getInDataSize().intValue());
// FETCH THE SAVED STORAGE
- byte[] codeAddress = TransactionTrace.convertToTronAddress(msg.getCodeAddress().getLast20Bytes());
- byte[] senderAddress = TransactionTrace.convertToTronAddress(getContractAddress().getLast20Bytes());
- byte[] contextAddress = msg.getType().callIsStateless() ? senderAddress : codeAddress;
+ byte[] codeAddress = msg.getCodeAddress().toTronAddress();
+ byte[] senderAddress = getContextAddress();
+
+ byte[] contextAddress;
+ if (msg.getOpCode() == Op.CALLCODE || msg.getOpCode() == Op.DELEGATECALL) {
+ contextAddress = senderAddress;
+ } else {
+ contextAddress = codeAddress;
+ }
if (logger.isDebugEnabled()) {
- logger.debug(msg.getType().name()
+ logger.debug(Op.getNameOf(msg.getOpCode())
+ " for existing contract: address: [{}], outDataOffs: [{}], outDataSize: [{}] ",
Hex.toHexString(contextAddress), msg.getOutDataOffs().longValue(),
msg.getOutDataSize().longValue());
@@ -873,22 +1122,31 @@ public void callToAddress(MessageCall msg) {
ProgramResult callResult = null;
if (isNotEmpty(programCode)) {
long vmStartInUs = System.nanoTime() / 1000;
- DataWord callValue = msg.getType().callIsDelegate() ? getCallValue() : msg.getEndowment();
- ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
+ DataWord callValue;
+ if (msg.getOpCode() == Op.DELEGATECALL) {
+ callValue = getCallValue();
+ } else {
+ callValue = msg.getEndowment();
+ }
+ ProgramInvoke programInvoke = ProgramInvokeFactory.createProgramInvoke(
this, new DataWord(contextAddress),
- msg.getType().callIsDelegate() ? getCallerAddress() : getContractAddress(),
- !isTokenTransfer ? callValue : new DataWord(0),
- !isTokenTransfer ? new DataWord(0) : callValue,
- !isTokenTransfer ? new DataWord(0) : msg.getTokenId(),
- contextBalance, data, deposit, msg.getType().callIsStatic() || isStaticCall(),
+ msg.getOpCode() == Op.DELEGATECALL ? getCallerAddress() : getContractAddress(),
+ !isTokenTransfer ? callValue : DataWord.ZERO(),
+ !isTokenTransfer ? DataWord.ZERO() : callValue,
+ !isTokenTransfer ? DataWord.ZERO() : msg.getTokenId(),
+ contextBalance, data, deposit,
+ msg.getOpCode() == Op.STATICCALL || isStaticCall(),
byTestingSuite(), vmStartInUs, getVmShouldEndInUs(), msg.getEnergy().longValueSafe());
if (isConstantCall()) {
programInvoke.setConstantCall();
}
- VM vm = new VM(config);
- Program program = new Program(programCode, programInvoke, internalTx, config);
+ Program program = new Program(programCode, codeAddress, programInvoke, internalTx);
program.setRootTransactionId(this.rootTransactionId);
- vm.play(program);
+ if (VMConfig.allowTvmCompatibleEvm()) {
+ program.setContractVersion(invoke.getDeposit()
+ .getContract(codeAddress).getContractVersion());
+ }
+ VM.play(program, OperationRegistry.getTable());
callResult = program.getResult();
getTrace().merge(program.getTrace());
@@ -900,8 +1158,8 @@ this, new DataWord(contextAddress),
logger.debug("contract run halted by Exception: contract: [{}], exception: [{}]",
Hex.toHexString(contextAddress),
callResult.getException());
-
internalTx.reject();
+
callResult.rejectInternalTransactions();
stackPushZero();
@@ -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()) {
@@ -983,7 +1251,8 @@ public void checkCPUTimeLimit(String opName) {
logger.info(
"minTimeRatio: {}, maxTimeRatio: {}, vm should end time in us: {}, "
+ "vm now time in us: {}, vm start time in us: {}",
- CommonParameter.getInstance().getMinTimeRatio(), CommonParameter.getInstance().getMaxTimeRatio(),
+ CommonParameter.getInstance().getMinTimeRatio(),
+ CommonParameter.getInstance().getMaxTimeRatio(),
getVmShouldEndInUs(), vmNowInUs, getVmStartInUs());
throw Exception.notEnoughTime(opName);
}
@@ -1000,21 +1269,19 @@ public void refundEnergy(long energyValue, String cause) {
getResult().refundEnergy(energyValue);
}
- public void futureRefundEnergy(long energyValue) {
- logger.debug("Future refund added: [{}]", energyValue);
- getResult().addFutureRefund(energyValue);
- }
-
- public void resetFutureRefund() {
- getResult().resetFutureRefund();
- }
+// public void futureRefundEnergy(long energyValue) {
+// logger.debug("Future refund added: [{}]", energyValue);
+// getResult().addFutureRefund(energyValue);
+// }
+//
+// public void resetFutureRefund() {
+// getResult().resetFutureRefund();
+// }
public void storageSave(DataWord word1, DataWord word2) {
DataWord keyWord = word1.clone();
DataWord valWord = word2.clone();
- getContractState()
- .putStorageValue(TransactionTrace.convertToTronAddress(getContractAddress().getLast20Bytes()), keyWord,
- valWord);
+ getContractState().putStorageValue(getContextAddress(), keyWord, valWord);
}
public byte[] getCode() {
@@ -1022,12 +1289,12 @@ public byte[] getCode() {
}
public byte[] getCodeAt(DataWord address) {
- byte[] code = invoke.getDeposit().getCode(TransactionTrace.convertToTronAddress(address.getLast20Bytes()));
+ byte[] code = invoke.getDeposit().getCode(address.toTronAddress());
return nullToEmpty(code);
}
public byte[] getCodeHashAt(DataWord address) {
- byte[] tronAddr = TransactionTrace.convertToTronAddress(address.getLast20Bytes());
+ byte[] tronAddr = address.toTronAddress();
AccountCapsule account = getContractState().getAccount(tronAddr);
if (account != null) {
ContractCapsule contract = getContractState().getContract(tronAddr);
@@ -1049,18 +1316,41 @@ public byte[] getCodeHashAt(DataWord address) {
}
}
+ public byte[] getCodeHash() {
+ ContractCapsule contract = getContractState().getContract(codeAddress);
+ byte[] codeHash;
+ if (contract == null) {
+ codeHash = Hash.sha3(ops);
+ } else {
+ codeHash = contract.getCodeHash();
+ if (ByteUtil.isNullOrZeroArray(codeHash)) {
+ codeHash = Hash.sha3(ops);
+ }
+ }
+ return codeHash;
+ }
+
+ private Key getJumpDestAnalysisCacheKey() {
+ return Key.create(ByteUtil.merge(codeAddress, getCodeHash()));
+ }
+
+ public byte[] getContextAddress() {
+ return invoke.getContractAddress().toTronAddress();
+ }
+
public DataWord getContractAddress() {
return invoke.getContractAddress().clone();
}
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);
if (Objects.nonNull(blockCapsule)) {
- return new DataWord(blockCapsule.getBlockId().getBytes());
+ return new DataWord(blockCapsule.getBlockId().getBytes()).clone();
} else {
return DataWord.ZERO.clone();
}
@@ -1071,15 +1361,23 @@ public DataWord getBlockHash(int index) {
}
public DataWord getBalance(DataWord address) {
- long balance = getContractState()
- .getBalance(TransactionTrace.convertToTronAddress(address.getLast20Bytes()));
+ long balance = getContractState().getBalance(address.toTronAddress());
return new DataWord(balance);
}
+ public DataWord getRewardBalance(DataWord address) {
+ long rewardBalance = VoteRewardUtil.queryReward(address.toTronAddress(), getContractState());
+ return new DataWord(rewardBalance);
+ }
+
public DataWord isContract(DataWord address) {
- ContractCapsule contract = getContractState()
- .getContract(TransactionTrace.convertToTronAddress(address.getLast20Bytes()));
- return contract != null ? new DataWord(1) : new DataWord(0);
+ ContractCapsule contract = getContractState().getContract(address.toTronAddress());
+ return contract != null ? DataWord.ONE() : DataWord.ZERO();
+ }
+
+ public DataWord isSRCandidate(DataWord address) {
+ WitnessCapsule witnessCapsule = getContractState().getWitness(address.toTronAddress());
+ return witnessCapsule != null ? DataWord.ONE() : DataWord.ZERO();
}
public DataWord getOriginAddress() {
@@ -1090,8 +1388,12 @@ public DataWord getCallerAddress() {
return invoke.getCallerAddress().clone();
}
- public DataWord getDropPrice() {
- return new DataWord(1);
+ public DataWord getChainId() {
+ byte[] chainId = getContractState().getBlockByNum(0).getBlockId().getBytes();
+ if (VMConfig.allowTvmCompatibleEvm() || VMConfig.allowOptimizedReturnValueOfChainId()) {
+ chainId = Arrays.copyOfRange(chainId, chainId.length - 4, chainId.length);
+ }
+ return new DataWord(chainId).clone();
}
public long getEnergylimitLeftLong() {
@@ -1140,18 +1442,15 @@ public byte[] getReturnDataBufferData(DataWord off, DataWord size) {
}
public DataWord storageLoad(DataWord key) {
- DataWord ret = getContractState()
- .getStorageValue(TransactionTrace.convertToTronAddress(getContractAddress().getLast20Bytes()),
- key.clone());
+ DataWord ret = getContractState().getStorageValue(getContextAddress(), key.clone());
return ret == null ? null : ret.clone();
}
public DataWord getTokenBalance(DataWord address, DataWord tokenId) {
checkTokenIdInTokenBalance(tokenId);
- long ret = getContractState()
- .getTokenBalance(TransactionTrace.convertToTronAddress(address.getLast20Bytes()),
- String.valueOf(tokenId.longValue()).getBytes());
- return ret == 0 ? new DataWord(0) : new DataWord(ret);
+ long ret = getContractState().getTokenBalance(address.toTronAddress(),
+ String.valueOf(tokenId.longValue()).getBytes());
+ return new DataWord(ret);
}
public DataWord getTokenValue() {
@@ -1182,7 +1481,9 @@ public DataWord getDifficulty() {
return invoke.getDifficulty().clone();
}
- public boolean isStaticCall() { return invoke.isStaticCall(); }
+ public boolean isStaticCall() {
+ return invoke.isStaticCall();
+ }
public boolean isConstantCall() {
return invoke.isConstantCall();
@@ -1278,7 +1579,7 @@ public void fullTrace() {
}
if (pc != 0) {
- globalOutput.append("[Op: ").append(OpCode.code(lastOp).name()).append("]\n");
+ globalOutput.append("[Op: ").append(Op.getNameOf(lastOp)).append("]\n");
}
globalOutput.append(" -- OPS -- ").append(opsString).append("\n");
@@ -1315,7 +1616,16 @@ public ProgramTrace getTrace() {
}
public void createContract2(DataWord value, DataWord memStart, DataWord memSize, DataWord salt) {
- byte[] senderAddress = TransactionTrace.convertToTronAddress(this.getCallerAddress().getLast20Bytes());
+ byte[] senderAddress;
+ if (VMConfig.allowTvmCompatibleEvm() && getCallDeep() == MAX_DEPTH) {
+ stackPushZero();
+ return;
+ }
+ if (VMConfig.allowTvmIstanbul()) {
+ senderAddress = getContextAddress();
+ } else {
+ senderAddress = getCallerAddress().toTronAddress();
+ }
byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue());
byte[] contractAddress = WalletUtil
@@ -1350,9 +1660,13 @@ public void callToPrecompiledAddress(MessageCall msg,
Repository deposit = getContractState().newRepositoryChild();
- byte[] senderAddress = TransactionTrace.convertToTronAddress(this.getContractAddress().getLast20Bytes());
- byte[] codeAddress = TransactionTrace.convertToTronAddress(msg.getCodeAddress().getLast20Bytes());
- byte[] contextAddress = msg.getType().callIsStateless() ? senderAddress : codeAddress;
+ byte[] senderAddress = getContextAddress();
+ byte[] contextAddress;
+ if (msg.getOpCode() == Op.CALLCODE || msg.getOpCode() == Op.DELEGATECALL) {
+ contextAddress = senderAddress;
+ } else {
+ contextAddress = msg.getCodeAddress().toTronAddress();
+ }
long endowment = msg.getEndowment().value().longValueExact();
long senderBalance = 0;
@@ -1406,8 +1720,11 @@ public void callToPrecompiledAddress(MessageCall msg,
this.stackPushZero();
} else {
// Delegate or not. if is delegated, we will use msg sender, otherwise use contract address
- contract.setCallerAddress(TransactionTrace.convertToTronAddress(msg.getType().callIsDelegate()
- ? getCallerAddress().getLast20Bytes() : getContractAddress().getLast20Bytes()));
+ if (msg.getOpCode() == Op.DELEGATECALL) {
+ contract.setCallerAddress(getCallerAddress().toTronAddress());
+ } else {
+ contract.setCallerAddress(getContextAddress());
+ }
// this is the depositImpl, not contractState as above
contract.setRepository(deposit);
contract.setResult(this.result);
@@ -1429,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());
+ }
}
}
@@ -1466,7 +1787,7 @@ public void checkTokenId(MessageCall msg) {
}
throw e;
}
- // tokenId can only be 0 when isTokenTransferMsg == false
+ // tokenId can only be 0 when isTokenTransferMsg is false
// or tokenId can be (MIN_TOKEN_ID, Long.Max] when isTokenTransferMsg == true
if ((tokenId <= VMConstant.MIN_TOKEN_ID && tokenId != 0)
|| (tokenId == 0 && msg.isTokenTransferMsg())) {
@@ -1510,11 +1831,21 @@ public void checkTokenIdInTokenBalance(DataWord tokenIdDataWord) {
}
}
- public DataWord getCallEnergy(OpCode op, DataWord requestedEnergy, DataWord availableEnergy) {
+ public DataWord getCallEnergy(DataWord requestedEnergy, DataWord availableEnergy) {
+ if (VMConfig.allowTvmCompatibleEvm() && getContractVersion() == 1) {
+ DataWord availableEnergyReduce = availableEnergy.clone();
+ availableEnergyReduce.div(new DataWord(64));
+ availableEnergy.sub(availableEnergyReduce);
+ }
return requestedEnergy.compareTo(availableEnergy) > 0 ? availableEnergy : requestedEnergy;
}
public DataWord getCreateEnergy(DataWord availableEnergy) {
+ if (VMConfig.allowTvmCompatibleEvm() && getContractVersion() == 1) {
+ DataWord availableEnergyReduce = availableEnergy.clone();
+ availableEnergyReduce.div(new DataWord(64));
+ availableEnergy.sub(availableEnergyReduce);
+ }
return availableEnergy;
}
@@ -1572,28 +1903,483 @@ public void setPC(int pc) {
this.pc = pc;
}
- public OpCode getCurOpcode() {
- return pc < code.length ? OpCode.code(code[pc]) : null;
+ }
+
+ public boolean freeze(DataWord receiverAddress, DataWord frozenBalance, DataWord resourceType) {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+ byte[] receiver = receiverAddress.toTronAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, receiver,
+ frozenBalance.longValue(), null,
+ "freezeFor" + convertResourceToString(resourceType), nonce, null);
+
+ FreezeBalanceParam param = new FreezeBalanceParam();
+ param.setOwnerAddress(owner);
+ param.setReceiverAddress(receiver);
+ boolean needCheckFrozenTime = CommonParameter.getInstance()
+ .getCheckFrozenTime() == 1; // for test
+ param.setFrozenDuration(needCheckFrozenTime
+ ? repository.getDynamicPropertiesStore().getMinFrozenTime() : 0);
+ param.setResourceType(parseResourceCode(resourceType));
+ try {
+ FreezeBalanceProcessor processor = new FreezeBalanceProcessor();
+ param.setFrozenBalance(frozenBalance.sValue().longValueExact());
+ processor.validate(param, repository);
+ processor.execute(param, repository);
+ repository.commit();
+ return true;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM Freeze: validate failure. Reason: {}", e.getMessage());
+ } catch (ArithmeticException e) {
+ logger.warn("TVM Freeze: frozenBalance out of long range.");
+ }
+ if (internalTx != null) {
+ internalTx.reject();
}
+ return false;
+ }
+
+ public boolean unfreeze(DataWord receiverAddress, DataWord resourceType) {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+ byte[] receiver = receiverAddress.toTronAddress();
- public boolean isPush() {
- return getCurOpcode() != null && getCurOpcode().name().startsWith("PUSH");
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, receiver, 0, null,
+ "unfreezeFor" + convertResourceToString(resourceType), nonce, null);
+
+ UnfreezeBalanceParam param = new UnfreezeBalanceParam();
+ param.setOwnerAddress(owner);
+ param.setReceiverAddress(receiver);
+ param.setResourceType(parseResourceCode(resourceType));
+ try {
+ UnfreezeBalanceProcessor processor = new UnfreezeBalanceProcessor();
+ processor.validate(param, repository);
+ long unfreezeBalance = processor.execute(param, repository);
+ repository.commit();
+ if (internalTx != null) {
+ internalTx.setValue(unfreezeBalance);
+ }
+ return true;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM Unfreeze: validate failure. Reason: {}", e.getMessage());
+ }
+ if (internalTx != null) {
+ internalTx.reject();
}
+ return false;
+ }
- public byte[] getCurOpcodeArg() {
- if (isPush()) {
- int nPush = getCurOpcode().val() - OpCode.PUSH1.val() + 1;
- byte[] data = Arrays.copyOfRange(code, pc + 1, pc + nPush + 1);
- return data;
- } else {
- return new byte[0];
+ public long freezeExpireTime(DataWord targetAddress, DataWord resourceType) {
+ byte[] owner = getContextAddress();
+ byte[] target = targetAddress.toTronAddress();
+ int resourceCode = resourceType.intValue();
+ if (FastByteComparisons.isEqual(owner, target)) {
+ AccountCapsule ownerCapsule = getContractState().getAccount(owner);
+ if (resourceCode == 0) { // for bandwidth
+ if (ownerCapsule.getFrozenCount() != 0
+ && ownerCapsule.getFrozenList().get(0).getFrozenBalance() != 0) {
+ return ownerCapsule.getFrozenList().get(0).getExpireTime();
+ }
+ } else if (resourceCode == 1) { // for energy
+ if (ownerCapsule.getAccountResource().getFrozenBalanceForEnergy().getFrozenBalance() != 0) {
+ return ownerCapsule.getAccountResource().getFrozenBalanceForEnergy().getExpireTime();
+ }
+ }
+ } else {
+ byte[] key = DelegatedResourceCapsule.createDbKey(owner, target);
+ DelegatedResourceCapsule delegatedResourceCapsule = getContractState().getDelegatedResource(key);
+ if (delegatedResourceCapsule != null) {
+ if (resourceCode == 0) {
+ if (delegatedResourceCapsule.getFrozenBalanceForBandwidth() != 0) {
+ return delegatedResourceCapsule.getExpireTimeForBandwidth();
+ }
+ } else if (resourceCode == 1) {
+ if (delegatedResourceCapsule.getFrozenBalanceForEnergy() != 0) {
+ return delegatedResourceCapsule.getExpireTimeForEnergy();
+ }
+ }
+ }
+ }
+ 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();
- public boolean next() {
- pc += 1 + getCurOpcodeArg().length;
- return pc < code.length;
+ 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 BANDWIDTH;
+ case 1:
+ return ENERGY;
+ default:
+ 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;
+ }
+ }
+
+ private String convertResourceToString(DataWord resourceType) {
+ switch (resourceType.intValue()) {
+ case 0:
+ return "Bandwidth";
+ case 1:
+ return "Energy";
+ case 2:
+ return "TronPower";
+ default:
+ return "UnknownType";
+ }
+ }
+
+ public boolean voteWitness(int witnessArrayOffset, int witnessArrayLength,
+ int amountArrayOffset, int amountArrayLength) {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, null, 0, null,
+ "voteWitness", nonce, null);
+
+ if (memoryLoad(witnessArrayOffset).intValueSafe() != witnessArrayLength ||
+ memoryLoad(amountArrayOffset).intValueSafe() != amountArrayLength) {
+ logger.warn("TVM VoteWitness: memory array length do not match length parameter!");
+ throw new BytecodeExecutionException(
+ "TVM VoteWitness: memory array length do not match length parameter!");
+ }
+
+ if (witnessArrayLength != amountArrayLength) {
+ logger.warn("TVM VoteWitness: witness array length {} does not match amount array length {}",
+ witnessArrayLength, amountArrayLength);
+ return false;
+ }
+
+ try {
+ VoteWitnessParam param = new VoteWitnessParam();
+ param.setVoterAddress(owner);
+ 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,
+ i * DataWord.WORD_SIZE, (i + 1) * DataWord.WORD_SIZE));
+ DataWord amount = new DataWord(Arrays.copyOfRange(amountArrayData,
+ i * DataWord.WORD_SIZE, (i + 1) * DataWord.WORD_SIZE));
+ param.addVote(witness.toTronAddress(), amount.sValue().longValueExact());
+ }
+ if (internalTx != null) {
+ internalTx.setExtra(param.toJsonStr());
+ }
+
+ VoteWitnessProcessor processor = new VoteWitnessProcessor();
+ processor.validate(param, repository);
+ processor.execute(param, repository);
+ repository.commit();
+ return true;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM VoteWitness: validate failure. Reason: {}", e.getMessage());
+ } catch (ContractExeException e) {
+ logger.warn("TVM VoteWitness: execute failure. Reason: {}", e.getMessage());
+ } catch (ArithmeticException e) {
+ logger.warn("TVM VoteWitness: int or long out of range. caused by: {}", e.getMessage());
+ }
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+ return false;
+ }
+
+ public long withdrawReward() {
+ Repository repository = getContractState().newRepositoryChild();
+ byte[] owner = getContextAddress();
+
+ increaseNonce();
+ InternalTransaction internalTx = addInternalTx(null, owner, owner, 0, null,
+ "withdrawReward", nonce, null);
+
+ WithdrawRewardParam param = new WithdrawRewardParam();
+ param.setOwnerAddress(owner);
+ param.setNowInMs(getTimestamp().longValue() * 1000);
+ try {
+ WithdrawRewardProcessor processor = new WithdrawRewardProcessor();
+ processor.validate(param, repository);
+ long allowance = processor.execute(param, repository);
+ repository.commit();
+ if (internalTx != null) {
+ internalTx.setValue(allowance);
+ }
+ return allowance;
+ } catch (ContractValidateException e) {
+ logger.warn("TVM WithdrawReward: validate failure. Reason: {}", e.getMessage());
+ } catch (ContractExeException e) {
+ logger.warn("TVM WithdrawReward: execute failure. Reason: {}", e.getMessage());
+ }
+ if (internalTx != null) {
+ internalTx.reject();
+ }
+ 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);
}
/**
@@ -1613,6 +2399,13 @@ public BytecodeExecutionException(String message, Object... args) {
}
}
+ public static class AssetIssueException extends BytecodeExecutionException {
+
+ public AssetIssueException(String message, Object... args) {
+ super(format(message, args));
+ }
+ }
+
public static class TransferException extends BytecodeExecutionException {
public TransferException(String message, Object... args) {
@@ -1668,6 +2461,14 @@ public IllegalOperationException(String message, Object... args) {
}
}
+ @SuppressWarnings("serial")
+ public static class InvalidCodeException extends BytecodeExecutionException {
+
+ public InvalidCodeException(String message) {
+ super(message);
+ }
+ }
+
@SuppressWarnings("serial")
public static class BadJumpDestinationException extends BytecodeExecutionException {
@@ -1717,19 +2518,6 @@ public static class Exception {
private Exception() {
}
- public static OutOfEnergyException notEnoughOpEnergy(OpCode op, long opEnergy,
- long programEnergy) {
- return new OutOfEnergyException(
- "Not enough energy for '%s' operation executing: opEnergy[%d], programEnergy[%d];", op,
- opEnergy,
- programEnergy);
- }
-
- public static OutOfEnergyException notEnoughOpEnergy(OpCode op, DataWord opEnergy,
- DataWord programEnergy) {
- return notEnoughOpEnergy(op, opEnergy.longValue(), programEnergy.longValue());
- }
-
public static OutOfEnergyException notEnoughSpendEnergy(String hint, long needEnergy,
long leftEnergy) {
return new OutOfEnergyException(
@@ -1746,9 +2534,9 @@ public static OutOfTimeException alreadyTimeOut() {
return new OutOfTimeException("Already Time Out");
}
-
- public static OutOfMemoryException memoryOverflow(OpCode op) {
- return new OutOfMemoryException("Out of Memory when '%s' operation executing", op.name());
+ public static OutOfMemoryException memoryOverflow(int op) {
+ return new OutOfMemoryException("Out of Memory when '%s' operation executing",
+ Op.getNameOf(op));
}
public static OutOfStorageException notEnoughStorage() {
@@ -1774,6 +2562,10 @@ public static IllegalOperationException invalidOpCode(byte... opCode) {
Hex.toHexString(opCode, 0, 1));
}
+ public static InvalidCodeException invalidCodeException() {
+ return new InvalidCodeException("invalid code: must not begin with 0xef");
+ }
+
public static BadJumpDestinationException badJumpDestination(int pc) {
return new BadJumpDestinationException("Operation with pc isn't 'JUMPDEST': PC[%d];", pc);
}
diff --git a/actuator/src/main/java/org/tron/core/vm/program/ProgramPrecompile.java b/actuator/src/main/java/org/tron/core/vm/program/ProgramPrecompile.java
index e8a841131ae..280d4239f54 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/ProgramPrecompile.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/ProgramPrecompile.java
@@ -1,54 +1,28 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.program;
import java.util.HashSet;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.tron.common.runtime.vm.DataWord;
-import org.tron.core.vm.OpCode;
+import org.tron.core.vm.Op;
import org.tron.core.vm.config.VMConfig;
-
@Slf4j(topic = "VM")
-/**
- * Created by Anton Nashatyrev on 06.02.2017.
- */
public class ProgramPrecompile {
- private Set jumpdest = new HashSet<>();
+ private final Set jumpDest = new HashSet<>();
public static ProgramPrecompile compile(byte[] ops) {
ProgramPrecompile ret = new ProgramPrecompile();
for (int i = 0; i < ops.length; ++i) {
+ int op = ops[i] & 0xff;
- OpCode op = OpCode.code(ops[i]);
- if (op == null) {
- continue;
- }
-
- if (op.equals(OpCode.JUMPDEST)) {
- logger.debug("JUMPDEST:" + i);
- ret.jumpdest.add(i);
+ if (op == Op.JUMPDEST) {
+ ret.jumpDest.add(i);
}
- if (op.asInt() >= OpCode.PUSH1.asInt() && op.asInt() <= OpCode.PUSH32.asInt()) {
- i += op.asInt() - OpCode.PUSH1.asInt() + 1;
+ if (op >= Op.PUSH1 && op <= Op.PUSH32) {
+ i += op - Op.PUSH1 + 1;
}
}
return ret;
@@ -57,17 +31,9 @@ public static ProgramPrecompile compile(byte[] ops) {
public static byte[] getCode(byte[] ops) {
for (int i = 0; i < ops.length; ++i) {
- OpCode op = OpCode.code(ops[i]);
- if (op == null) {
- continue;
- }
-
- if (op.equals(OpCode.RETURN)) {
- logger.debug("return");
- }
+ int op = ops[i] & 0xff;
- if (op.equals(OpCode.RETURN) && i + 1 < ops.length && OpCode.code(ops[i + 1]) != null
- && OpCode.code(ops[i + 1]).equals(OpCode.STOP)) {
+ if (op == Op.RETURN && i + 1 < ops.length && ((ops[i + 1]) & 0xff) == Op.STOP) {
byte[] ret;
i++;
ret = new byte[ops.length - i - 1];
@@ -76,18 +42,18 @@ public static byte[] getCode(byte[] ops) {
return ret;
}
- if (op.asInt() >= OpCode.PUSH1.asInt() && op.asInt() <= OpCode.PUSH32.asInt()) {
- i += op.asInt() - OpCode.PUSH1.asInt() + 1;
+ if (op >= Op.PUSH1 && op <= Op.PUSH32) {
+ i += op - Op.PUSH1 + 1;
}
}
if (VMConfig.allowTvmConstantinople()) {
return new byte[0];
} else {
- return new DataWord(0).getData();
+ return new byte[DataWord.WORD_SIZE];
}
}
public boolean hasJumpDest(int pc) {
- return jumpdest.contains(pc);
+ return jumpDest.contains(pc);
}
}
diff --git a/actuator/src/main/java/org/tron/core/vm/program/Stack.java b/actuator/src/main/java/org/tron/core/vm/program/Stack.java
index e03b8124d65..08921f70962 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/Stack.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/Stack.java
@@ -1,20 +1,3 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.program;
import java.util.Objects;
diff --git a/actuator/src/main/java/org/tron/core/vm/program/Storage.java b/actuator/src/main/java/org/tron/core/vm/program/Storage.java
index b57e80d3949..572af048081 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/Storage.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/Storage.java
@@ -5,9 +5,10 @@
import java.util.HashMap;
import java.util.Map;
import lombok.Getter;
+import lombok.Setter;
+import org.tron.common.crypto.Hash;
import org.tron.common.runtime.vm.DataWord;
import org.tron.common.utils.ByteUtil;
-import org.tron.common.crypto.Hash;
import org.tron.core.capsule.StorageRowCapsule;
import org.tron.core.store.StorageRowStore;
@@ -22,6 +23,8 @@ public class Storage {
private StorageRowStore store;
@Getter
private byte[] address;
+ @Setter
+ private int contractVersion;
public Storage(byte[] address, StorageRowStore store) {
addrHash = addrHash(address);
@@ -33,13 +36,17 @@ public Storage(Storage storage) {
this.addrHash = storage.addrHash.clone();
this.address = storage.getAddress().clone();
this.store = storage.store;
+ this.contractVersion = storage.contractVersion;
storage.getRowCache().forEach((DataWord rowKey, StorageRowCapsule row) -> {
StorageRowCapsule newRow = new StorageRowCapsule(row);
this.rowCache.put(rowKey.clone(), newRow);
});
}
- private static byte[] compose(byte[] key, byte[] addrHash) {
+ private byte[] compose(byte[] key, byte[] addrHash) {
+ if (contractVersion == 1) {
+ key = Hash.sha3(key);
+ }
byte[] result = new byte[key.length];
arraycopy(addrHash, 0, result, 0, PREFIX_BYTES);
arraycopy(key, PREFIX_BYTES, result, PREFIX_BYTES, PREFIX_BYTES);
diff --git a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvoke.java b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvoke.java
index 062360e00a1..ab54e7f5382 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvoke.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvoke.java
@@ -1,31 +1,8 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.program.invoke;
-
import org.tron.common.runtime.vm.DataWord;
-import org.tron.core.capsule.BlockCapsule;
import org.tron.core.vm.repository.Repository;
-/**
- * @author Roman Mandeleil
- * @since 03.06.2014
- */
public interface ProgramInvoke {
DataWord getContractAddress();
diff --git a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeFactory.java b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeFactory.java
index 0f0ab35096a..832aae2b00a 100644
--- a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeFactory.java
+++ b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeFactory.java
@@ -1,49 +1,152 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.program.invoke;
+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.common.utils.WalletUtil.generateContractAddress;
+import lombok.extern.slf4j.Slf4j;
+import org.bouncycastle.util.Arrays;
import org.tron.common.runtime.InternalTransaction;
import org.tron.common.runtime.vm.DataWord;
+import org.tron.common.utils.ByteUtil;
+import org.tron.core.capsule.ContractCapsule;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.vm.program.Program;
import org.tron.core.vm.repository.Repository;
import org.tron.protos.Protocol.Block;
import org.tron.protos.Protocol.Transaction;
+import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract;
+import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract;
-/**
- * @author Roman Mandeleil
- * @since 19.12.2014
- */
-public interface ProgramInvokeFactory {
-
- ProgramInvoke createProgramInvoke(InternalTransaction.TrxType trxType,
- InternalTransaction.ExecutorType executorType,
- Transaction tx, long tokenValue, long tokenId, Block block, Repository deposit,
- long vmStartInUs,
- long vmShouldEndInUs,
- long energyLimit) throws ContractValidateException;
-
- ProgramInvoke createProgramInvoke(Program program, DataWord toAddress, DataWord callerAddress,
- DataWord inValue, DataWord tokenValue, DataWord tokenId,
- long balanceInt, byte[] dataIn, Repository deposit, boolean staticCall,
- boolean byTestingSuite,
- long vmStartInUs, long vmShouldEndInUs, long energyLimit);
+@Slf4j(topic = "vm")
+public class ProgramInvokeFactory {
+
+ /**
+ * Invocation by the wire tx
+ */
+ public static ProgramInvoke createProgramInvoke(InternalTransaction.TrxType trxType,
+ InternalTransaction.ExecutorType executorType, Transaction tx, long tokenValue, long tokenId,
+ Block block,
+ Repository deposit, long vmStartInUs,
+ long vmShouldEndInUs, long energyLimit) throws ContractValidateException {
+ byte[] contractAddress;
+ byte[] ownerAddress;
+ long balance;
+ byte[] data;
+ byte[] lastHash = null;
+ byte[] coinbase = null;
+ long timestamp = 0L;
+ long number = -1L;
+
+ if (trxType == TRX_CONTRACT_CREATION_TYPE) {
+ CreateSmartContract contract = ContractCapsule.getSmartContractFromTransaction(tx);
+ contractAddress = generateContractAddress(tx);
+ ownerAddress = contract.getOwnerAddress().toByteArray();
+ balance = deposit.getBalance(ownerAddress);
+ data = ByteUtil.EMPTY_BYTE_ARRAY;
+ long callValue = contract.getNewContract().getCallValue();
+
+ switch (executorType) {
+ case ET_NORMAL_TYPE:
+ case ET_PRE_TYPE:
+ if (null != block) {
+ lastHash = block.getBlockHeader().getRawDataOrBuilder().getParentHash().toByteArray();
+ coinbase = block.getBlockHeader().getRawDataOrBuilder().getWitnessAddress()
+ .toByteArray();
+ timestamp = block.getBlockHeader().getRawDataOrBuilder().getTimestamp() / 1000;
+ number = block.getBlockHeader().getRawDataOrBuilder().getNumber();
+ }
+ break;
+ default:
+ break;
+ }
+
+ return new ProgramInvokeImpl(contractAddress, ownerAddress, ownerAddress, balance, callValue,
+ tokenValue, tokenId, data, lastHash, coinbase, timestamp, number, deposit, vmStartInUs,
+ vmShouldEndInUs, energyLimit);
+
+ } else if (trxType == TRX_CONTRACT_CALL_TYPE) {
+ TriggerSmartContract contract = ContractCapsule
+ .getTriggerContractFromTransaction(tx);
+ /*** ADDRESS op ***/
+ // YP: Get address of currently executing account.
+ byte[] address = contract.getContractAddress().toByteArray();
+
+ /*** ORIGIN op ***/
+ // YP: This is the sender of original transaction; it is never a contract.
+ byte[] origin = contract.getOwnerAddress().toByteArray();
+
+ /*** CALLER op ***/
+ // YP: This is the address of the account that is directly responsible for this execution.
+ byte[] caller = contract.getOwnerAddress().toByteArray();
+
+ /*** BALANCE op ***/
+ balance = deposit.getBalance(caller);
+
+ /*** CALLVALUE op ***/
+ long callValue = contract.getCallValue();
+
+ /*** CALLDATALOAD op ***/
+ /*** CALLDATACOPY op ***/
+ /*** CALLDATASIZE op ***/
+ data = contract.getData().toByteArray();
+
+ switch (executorType) {
+ case ET_CONSTANT_TYPE:
+ break;
+ case ET_PRE_TYPE:
+ case ET_NORMAL_TYPE:
+ if (null != block) {
+ /*** PREVHASH op ***/
+ lastHash = block.getBlockHeader().getRawDataOrBuilder().getParentHash().toByteArray();
+ /*** COINBASE op ***/
+ coinbase = block.getBlockHeader().getRawDataOrBuilder().getWitnessAddress()
+ .toByteArray();
+ /*** TIMESTAMP op ***/
+ timestamp = block.getBlockHeader().getRawDataOrBuilder().getTimestamp() / 1000;
+ /*** NUMBER op ***/
+ number = block.getBlockHeader().getRawDataOrBuilder().getNumber();
+ }
+ break;
+ default:
+ break;
+ }
+
+ return new ProgramInvokeImpl(address, origin, caller, balance, callValue, tokenValue, tokenId,
+ data,
+ lastHash, coinbase, timestamp, number, deposit, vmStartInUs, vmShouldEndInUs,
+ energyLimit);
+ }
+ throw new ContractValidateException("Unknown contract type");
+ }
+
+ /**
+ * This invocation created for contract call contract
+ */
+ public static ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
+ DataWord callerAddress,
+ DataWord inValue, DataWord tokenValue, DataWord tokenId, long balanceInt, byte[] dataIn,
+ Repository deposit, boolean isStaticCall, boolean byTestingSuite, long vmStartInUs,
+ long vmShouldEndInUs, long energyLimit) {
+
+ DataWord address = toAddress;
+ DataWord origin = program.getOriginAddress();
+ DataWord caller = callerAddress;
+ DataWord balance = new DataWord(balanceInt);
+ DataWord callValue = inValue;
+
+ byte[] data = Arrays.clone(dataIn);
+ DataWord lastHash = program.getPrevHash();
+ DataWord coinbase = program.getCoinbase();
+ DataWord timestamp = program.getTimestamp();
+ DataWord number = program.getNumber();
+ DataWord difficulty = program.getDifficulty();
+
+ return new ProgramInvokeImpl(address, origin, caller, balance, callValue, tokenValue, tokenId,
+ data, lastHash, coinbase, timestamp, number, difficulty,
+ deposit, program.getCallDeep() + 1, isStaticCall, byTestingSuite, vmStartInUs,
+ vmShouldEndInUs, energyLimit);
+ }
}
diff --git a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeFactoryImpl.java b/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeFactoryImpl.java
deleted file mode 100644
index ca615d532cc..00000000000
--- a/actuator/src/main/java/org/tron/core/vm/program/invoke/ProgramInvokeFactoryImpl.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.program.invoke;
-
-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.common.utils.WalletUtil.generateContractAddress;
-
-import lombok.extern.slf4j.Slf4j;
-import org.spongycastle.util.Arrays;
-import org.springframework.stereotype.Component;
-import org.tron.common.runtime.InternalTransaction;
-import org.tron.common.runtime.vm.DataWord;
-import org.tron.common.utils.ByteUtil;
-import org.tron.core.capsule.ContractCapsule;
-import org.tron.core.exception.ContractValidateException;
-import org.tron.core.vm.program.Program;
-import org.tron.core.vm.repository.Repository;
-import org.tron.protos.Protocol.Block;
-import org.tron.protos.Protocol.Transaction;
-import org.tron.protos.contract.SmartContractOuterClass.CreateSmartContract;
-import org.tron.protos.contract.SmartContractOuterClass.TriggerSmartContract;
-
-
-/**
- * @author Roman Mandeleil
- * @since 08.06.2014
- */
-@Component("ProgramInvokeFactory")
-@Slf4j(topic = "vm")
-public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
-
- // Invocation by the wire tx
- @Override
- public ProgramInvoke createProgramInvoke(InternalTransaction.TrxType trxType,
- InternalTransaction.ExecutorType executorType, Transaction tx, long tokenValue, long tokenId,
- Block block,
- Repository deposit, long vmStartInUs,
- long vmShouldEndInUs, long energyLimit) throws ContractValidateException {
- byte[] contractAddress;
- byte[] ownerAddress;
- long balance;
- byte[] data;
- byte[] lastHash = null;
- byte[] coinbase = null;
- long timestamp = 0L;
- long number = -1L;
-
- if (trxType == TRX_CONTRACT_CREATION_TYPE) {
- CreateSmartContract contract = ContractCapsule.getSmartContractFromTransaction(tx);
- contractAddress = generateContractAddress(tx);
- ownerAddress = contract.getOwnerAddress().toByteArray();
- balance = deposit.getBalance(ownerAddress);
- data = ByteUtil.EMPTY_BYTE_ARRAY;
- long callValue = contract.getNewContract().getCallValue();
-
- switch (executorType) {
- case ET_NORMAL_TYPE:
- case ET_PRE_TYPE:
- if (null != block) {
- lastHash = block.getBlockHeader().getRawDataOrBuilder().getParentHash().toByteArray();
- coinbase = block.getBlockHeader().getRawDataOrBuilder().getWitnessAddress()
- .toByteArray();
- timestamp = block.getBlockHeader().getRawDataOrBuilder().getTimestamp() / 1000;
- number = block.getBlockHeader().getRawDataOrBuilder().getNumber();
- }
- break;
- default:
- break;
- }
-
- return new ProgramInvokeImpl(contractAddress, ownerAddress, ownerAddress, balance, callValue,
- tokenValue, tokenId, data, lastHash, coinbase, timestamp, number, deposit, vmStartInUs,
- vmShouldEndInUs, energyLimit);
-
- } else if (trxType == TRX_CONTRACT_CALL_TYPE) {
- TriggerSmartContract contract = ContractCapsule
- .getTriggerContractFromTransaction(tx);
- /*** ADDRESS op ***/
- // YP: Get address of currently executing account.
- byte[] address = contract.getContractAddress().toByteArray();
-
- /*** ORIGIN op ***/
- // YP: This is the sender of original transaction; it is never a contract.
- byte[] origin = contract.getOwnerAddress().toByteArray();
-
- /*** CALLER op ***/
- // YP: This is the address of the account that is directly responsible for this execution.
- byte[] caller = contract.getOwnerAddress().toByteArray();
-
- /*** BALANCE op ***/
- balance = deposit.getBalance(caller);
-
- /*** CALLVALUE op ***/
- long callValue = contract.getCallValue();
-
- /*** CALLDATALOAD op ***/
- /*** CALLDATACOPY op ***/
- /*** CALLDATASIZE op ***/
- data = contract.getData().toByteArray();
-
- switch (executorType) {
- case ET_CONSTANT_TYPE:
- break;
- case ET_PRE_TYPE:
- case ET_NORMAL_TYPE:
- if (null != block) {
- /*** PREVHASH op ***/
- lastHash = block.getBlockHeader().getRawDataOrBuilder().getParentHash().toByteArray();
- /*** COINBASE op ***/
- coinbase = block.getBlockHeader().getRawDataOrBuilder().getWitnessAddress()
- .toByteArray();
- /*** TIMESTAMP op ***/
- timestamp = block.getBlockHeader().getRawDataOrBuilder().getTimestamp() / 1000;
- /*** NUMBER op ***/
- number = block.getBlockHeader().getRawDataOrBuilder().getNumber();
- }
- break;
- default:
- break;
- }
-
- return new ProgramInvokeImpl(address, origin, caller, balance, callValue, tokenValue, tokenId,
- data,
- lastHash, coinbase, timestamp, number, deposit, vmStartInUs, vmShouldEndInUs,
- energyLimit);
- }
- throw new ContractValidateException("Unknown contract type");
- }
-
- /**
- * This invocation created for contract call contract
- */
- @Override
- public ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
- DataWord callerAddress,
- DataWord inValue, DataWord tokenValue, DataWord tokenId, long balanceInt, byte[] dataIn,
- Repository deposit, boolean isStaticCall, boolean byTestingSuite, long vmStartInUs,
- long vmShouldEndInUs, long energyLimit) {
-
- DataWord address = toAddress;
- DataWord origin = program.getOriginAddress();
- DataWord caller = callerAddress;
- DataWord balance = new DataWord(balanceInt);
- DataWord callValue = inValue;
-
- byte[] data = Arrays.clone(dataIn);
- DataWord lastHash = program.getPrevHash();
- DataWord coinbase = program.getCoinbase();
- DataWord timestamp = program.getTimestamp();
- DataWord number = program.getNumber();
- DataWord difficulty = program.getDifficulty();
-
- return new ProgramInvokeImpl(address, origin, caller, balance, callValue, tokenValue, tokenId,
- data, lastHash, coinbase, timestamp, number, difficulty,
- deposit, program.getCallDeep() + 1, isStaticCall, byTestingSuite, vmStartInUs,
- vmShouldEndInUs, energyLimit);
- }
-
-
-}
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 59bb3e76e03..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
@@ -1,20 +1,3 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.program.invoke;
import java.math.BigInteger;
@@ -27,8 +10,8 @@
@Slf4j
public class ProgramInvokeImpl implements ProgramInvoke {
- /*****************/
- /* NOTE: In the protocol there is no restriction on the maximum message data,
+ /*
+ * NOTE: In the protocol there is no restriction on the maximum message data,
* However msgData here is a byte[] and this can't hold more than 2^32-1
*/
private static final BigInteger MAX_MSG_DATA = BigInteger.valueOf(Integer.MAX_VALUE);
@@ -242,7 +225,7 @@ public DataWord getNumber() {
/* DIFFICULTY op */
public DataWord getDifficulty() {
- return new DataWord(0);
+ return DataWord.ZERO();
}
public long getVmShouldEndInUs() {
@@ -254,7 +237,9 @@ public Repository getDeposit() {
}
@Override
- public boolean isStaticCall() {return isStaticCall;}
+ public boolean isStaticCall() {
+ return isStaticCall;
+ }
@Override
public boolean isConstantCall() {
@@ -329,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()
@@ -341,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 bc13d63a4b5..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
@@ -1,49 +1,25 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.program.invoke;
import com.google.protobuf.ByteString;
-import org.spongycastle.util.Arrays;
-import org.spongycastle.util.encoders.Hex;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.tron.common.crypto.Hash;
import org.tron.common.crypto.SignUtils;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.runtime.vm.DataWord;
-import org.tron.common.crypto.Hash;
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;
import org.tron.protos.contract.SmartContractOuterClass.SmartContract;
-
-/**
- * .
- *
- * @author Roman Mandeleil
- * @since 03.06.2014
- */
public class ProgramInvokeMockImpl implements ProgramInvoke {
- private final byte[] contractAddress = Hex.decode("471fd3ad3e9eeadeec4608b92d16ce6b500704cc");
+ private final byte[] contractAddress = Hex.decode("41471fd3ad3e9eeadeec4608b92d16ce6b500704cc");
private byte[] msgData;
private Repository deposit;
- private byte[] ownerAddress = Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
+ private byte[] ownerAddress = Hex.decode("41cd2a3d9f938e13cd947ec05abc7fe734df8dd826");
private boolean isConstantCall;
private boolean isStaticCall;
private long energyLimit = 50;
@@ -54,7 +30,6 @@ public ProgramInvokeMockImpl(byte[] msgDataRaw) {
}
public ProgramInvokeMockImpl() {
-
this.deposit = RepositoryImpl.createRoot(null);
this.deposit.createAccount(ownerAddress, Protocol.AccountType.Normal);
@@ -70,13 +45,28 @@ public ProgramInvokeMockImpl() {
+ "00603f556103e75660005460005360200235"));
}
+ public ProgramInvokeMockImpl(byte[] op, byte[] opAddress) {
+ 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);
+ this.deposit.createContract(opAddress,
+ new ContractCapsule(SmartContract.newBuilder().setContractAddress(
+ ByteString.copyFrom(op)).build()));
+ this.deposit.saveCode(opAddress, op);
+ }
+
public ProgramInvokeMockImpl(boolean defaults) {
}
/* ADDRESS op */
public DataWord getContractAddress() {
- return new DataWord(ownerAddress);
+ return new DataWord(contractAddress);
}
/* BALANCE op */
@@ -220,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;
@@ -252,7 +246,7 @@ public void setConstantCall() {
@Override
public boolean byTestingSuite() {
- return true;
+ return false;
}
@Override
diff --git a/actuator/src/main/java/org/tron/core/vm/repository/Key.java b/actuator/src/main/java/org/tron/core/vm/repository/Key.java
index b23a22f7751..8639b55f4db 100644
--- a/actuator/src/main/java/org/tron/core/vm/repository/Key.java
+++ b/actuator/src/main/java/org/tron/core/vm/repository/Key.java
@@ -10,10 +10,6 @@ public class Key {
*/
private byte[] data = new byte[0];
- /**
- *
- * @param data
- */
public Key(byte[] data) {
if (data != null && data.length != 0) {
this.data = new byte[data.length];
@@ -21,36 +17,19 @@ public Key(byte[] data) {
}
}
- /**
- *
- * @param key
- */
private Key(Key key) {
this.data = new byte[key.getData().length];
System.arraycopy(key.getData(), 0, this.data, 0, this.data.length);
}
- /**
- *
- * @param data
- * @return
- */
public static Key create(byte[] data) {
return new Key(data);
}
- /**
- *
- * @return
- */
public Key clone() {
return new Key(this);
}
- /**
- *
- * @return
- */
public byte[] getData() {
return data;
}
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 9f109bf8eae..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,14 +1,9 @@
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.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.store.AssetIssueStore;
-import org.tron.core.store.AssetIssueV2Store;
-import org.tron.core.store.DynamicPropertiesStore;
+import org.tron.core.capsule.*;
+import org.tron.core.store.*;
import org.tron.core.vm.program.Storage;
import org.tron.protos.Protocol;
@@ -22,13 +17,31 @@ public interface Repository {
DynamicPropertiesStore getDynamicPropertiesStore();
+ DelegationStore getDelegationStore();
+
AccountCapsule createAccount(byte[] address, Protocol.AccountType type);
AccountCapsule createAccount(byte[] address, String accountName, Protocol.AccountType type);
AccountCapsule getAccount(byte[] address);
- BytesCapsule getDynamic(byte[] bytesKey);
+ BytesCapsule getDynamicProperty(byte[] bytesKey);
+
+ DelegatedResourceCapsule getDelegatedResource(byte[] key);
+
+ VotesCapsule getVotes(byte[] address);
+
+ long getBeginCycle(byte[] address);
+
+ long getEndCycle(byte[] address);
+
+ AccountCapsule getAccountVote(long cycle, byte[] address);
+
+ BytesCapsule getDelegation(Key key);
+
+ DelegatedResourceAccountIndexCapsule getDelegatedResourceAccountIndex(byte[] key);
+
+ byte[] getTransientStorageValue(byte[] address, byte[] key);
void deleteContract(byte[] address);
@@ -36,10 +49,36 @@ public interface Repository {
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);
+
+ void updateDelegatedResource(byte[] word, DelegatedResourceCapsule delegatedResourceCapsule);
+
+ void updateVotes(byte[] word, VotesCapsule votesCapsule);
+
+ void updateBeginCycle(byte[] word, long cycle);
+
+ void updateEndCycle(byte[] word, long cycle);
+
+ void updateAccountVote(byte[] word, long cycle, AccountCapsule accountCapsule);
+
+ 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);
@@ -66,16 +105,36 @@ 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);
+ void putDynamicProperty(Key key, Value value);
+
+ void putDelegatedResource(Key key, Value value);
+
+ void putVotes(Key key, Value value);
+
+ 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();
@@ -84,4 +143,28 @@ public interface Repository {
AccountCapsule createNormalAccount(byte[] address);
+ WitnessCapsule getWitness(byte[] address);
+
+ void addTotalNetWeight(long amount);
+
+ 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 81b97b07b1f..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,28 +1,46 @@
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.spongycastle.util.Strings;
+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;
import org.tron.common.utils.ByteUtil;
import org.tron.common.utils.Commons;
-import org.tron.common.crypto.Hash;
import org.tron.common.utils.Sha256Hash;
import org.tron.common.utils.StorageUtils;
import org.tron.common.utils.StringUtil;
import org.tron.core.ChainBaseManager;
+import org.tron.core.capsule.AbiCapsule;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.AssetIssueCapsule;
import org.tron.core.capsule.BlockCapsule;
import org.tron.core.capsule.BlockCapsule.BlockId;
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;
import org.tron.core.config.Parameter;
import org.tron.core.db.BlockIndexStore;
import org.tron.core.db.BlockStore;
@@ -31,28 +49,42 @@
import org.tron.core.exception.BadItemException;
import org.tron.core.exception.ItemNotFoundException;
import org.tron.core.exception.StoreException;
+import org.tron.core.store.AbiStore;
import org.tron.core.store.AccountStore;
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;
import org.tron.core.store.StorageRowStore;
import org.tron.core.store.StoreFactory;
+import org.tron.core.store.VotesStore;
+import org.tron.core.store.WitnessStore;
import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.program.Program.IllegalOperationException;
import org.tron.core.vm.program.Storage;
-import org.tron.core.vm.utils.MUtil;
import org.tron.protos.Protocol;
+import org.tron.protos.Protocol.Account;
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 {
- //for energycal
- private long precision = Parameter.ChainConstant.PRECISION;
- private long windowSize = Parameter.ChainConstant.WINDOW_SIZE_MS /
- BLOCK_PRODUCED_INTERVAL;
+ private final long precision = Parameter.ChainConstant.PRECISION;
+ 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
@@ -64,10 +96,14 @@ public class RepositoryImpl implements Repository {
@Getter
private AssetIssueV2Store assetIssueV2Store;
@Getter
+ private AbiStore abiStore;
+ @Getter
private CodeStore codeStore;
@Getter
private ContractStore contractStore;
@Getter
+ private ContractStateStore contractStateStore;
+ @Getter
private StorageRowStore storageRowStore;
@Getter
private BlockStore blockStore;
@@ -75,17 +111,37 @@ public class RepositoryImpl implements Repository {
private KhaosDatabase khaosDb;
@Getter
private BlockIndexStore blockIndexStore;
-
+ @Getter
+ private WitnessStore witnessStore;
+ @Getter
+ private DelegatedResourceStore delegatedResourceStore;
+ @Getter
+ private VotesStore votesStore;
+ @Getter
+ private DelegationStore delegationStore;
+ @Getter
+ private DelegatedResourceAccountIndexStore delegatedResourceAccountIndexStore;
private Repository parent = null;
- private HashMap accountCache = new HashMap<>();
- private HashMap codeCache = new HashMap<>();
- private HashMap contractCache = new HashMap<>();
- private HashMap dynamicPropertiesCache = new HashMap<>();
- private HashMap storageCache = new HashMap<>();
-
- private HashMap assetIssueCache = new HashMap<>();
+ 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<>();
+ private final HashMap> dynamicPropertiesCache = new HashMap<>();
+ 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) {
+ }
public RepositoryImpl(StoreFactory storeFactory, RepositoryImpl repository) {
init(storeFactory, repository);
@@ -101,14 +157,21 @@ protected void init(StoreFactory storeFactory, RepositoryImpl parent) {
ChainBaseManager manager = storeFactory.getChainBaseManager();
dynamicPropertiesStore = manager.getDynamicPropertiesStore();
accountStore = manager.getAccountStore();
+ abiStore = manager.getAbiStore();
codeStore = manager.getCodeStore();
contractStore = manager.getContractStore();
+ contractStateStore = manager.getContractStateStore();
assetIssueStore = manager.getAssetIssueStore();
assetIssueV2Store = manager.getAssetIssueV2Store();
storageRowStore = manager.getStorageRowStore();
blockStore = manager.getBlockStore();
khaosDb = manager.getKhaosDb();
blockIndexStore = manager.getBlockIndexStore();
+ witnessStore = manager.getWitnessStore();
+ delegatedResourceStore = manager.getDelegatedResourceStore();
+ votesStore = manager.getVotesStore();
+ delegationStore = manager.getDelegationStore();
+ delegatedResourceAccountIndexStore = manager.getDelegatedResourceAccountIndexStore();
}
this.parent = parent;
}
@@ -126,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); // us
+ 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 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
@@ -136,7 +269,7 @@ public AssetIssueCapsule getAssetIssue(byte[] tokenId) {
byte[] tokenIdWithoutLeadingZero = ByteUtil.stripLeadingZeroes(tokenId);
Key key = Key.create(tokenIdWithoutLeadingZero);
if (assetIssueCache.containsKey(key)) {
- return assetIssueCache.get(key).getAssetIssue();
+ return new AssetIssueCapsule(assetIssueCache.get(key).getValue());
}
AssetIssueCapsule assetIssueCapsule;
@@ -148,7 +281,7 @@ public AssetIssueCapsule getAssetIssue(byte[] tokenId) {
.get(tokenIdWithoutLeadingZero);
}
if (assetIssueCapsule != null) {
- assetIssueCache.put(key, Value.create(assetIssueCapsule.getData()));
+ assetIssueCache.put(key, Value.create(assetIssueCapsule));
}
return assetIssueCapsule;
}
@@ -157,7 +290,7 @@ public AssetIssueCapsule getAssetIssue(byte[] tokenId) {
public AccountCapsule createAccount(byte[] address, Protocol.AccountType type) {
Key key = new Key(address);
AccountCapsule account = new AccountCapsule(ByteString.copyFrom(address), type);
- accountCache.put(key, new Value(account.getData(), Type.VALUE_TYPE_CREATE));
+ accountCache.put(key, Value.create(account, Type.CREATE));
return account;
}
@@ -168,8 +301,7 @@ public AccountCapsule createAccount(byte[] address, String accountName,
AccountCapsule account = new AccountCapsule(ByteString.copyFrom(address),
ByteString.copyFromUtf8(accountName),
type);
-
- accountCache.put(key, new Value(account.getData(), Type.VALUE_TYPE_CREATE));
+ accountCache.put(key, Value.create(account, Type.CREATE));
return account;
}
@@ -177,7 +309,7 @@ public AccountCapsule createAccount(byte[] address, String accountName,
public AccountCapsule getAccount(byte[] address) {
Key key = new Key(address);
if (accountCache.containsKey(key)) {
- return accountCache.get(key).getAccount();
+ return new AccountCapsule(accountCache.get(key).getValue());
}
AccountCapsule accountCapsule;
@@ -188,21 +320,21 @@ public AccountCapsule getAccount(byte[] address) {
}
if (accountCapsule != null) {
- accountCache.put(key, Value.create(accountCapsule.getData()));
+ accountCache.put(key, Value.create(accountCapsule));
}
return accountCapsule;
}
@Override
- public BytesCapsule getDynamic(byte[] word) {
+ public BytesCapsule getDynamicProperty(byte[] word) {
Key key = Key.create(word);
if (dynamicPropertiesCache.containsKey(key)) {
- return dynamicPropertiesCache.get(key).getDynamicProperties();
+ return new BytesCapsule(dynamicPropertiesCache.get(key).getValue());
}
BytesCapsule bytesCapsule;
if (parent != null) {
- bytesCapsule = parent.getDynamic(word);
+ bytesCapsule = parent.getDynamicProperty(word);
} else {
try {
bytesCapsule = getDynamicPropertiesStore().get(word);
@@ -218,6 +350,139 @@ public BytesCapsule getDynamic(byte[] word) {
return bytesCapsule;
}
+ @Override
+ public DelegatedResourceCapsule getDelegatedResource(byte[] key) {
+ Key cacheKey = new Key(key);
+ if (delegatedResourceCache.containsKey(cacheKey)) {
+ return new DelegatedResourceCapsule(delegatedResourceCache.get(cacheKey).getValue());
+ }
+
+ DelegatedResourceCapsule delegatedResourceCapsule;
+ if (parent != null) {
+ delegatedResourceCapsule = parent.getDelegatedResource(key);
+ } else {
+ delegatedResourceCapsule = getDelegatedResourceStore().get(key);
+ }
+
+ if (delegatedResourceCapsule != null) {
+ delegatedResourceCache.put(cacheKey, Value.create(delegatedResourceCapsule));
+ }
+ return delegatedResourceCapsule;
+ }
+
+ @Override
+ public VotesCapsule getVotes(byte[] address) {
+ Key cacheKey = new Key(address);
+ if (votesCache.containsKey(cacheKey)) {
+ return new VotesCapsule(votesCache.get(cacheKey).getValue());
+ }
+
+ VotesCapsule votesCapsule;
+ if (parent != null) {
+ votesCapsule = parent.getVotes(address);
+ } else {
+ votesCapsule = getVotesStore().get(address);
+ }
+
+ if (votesCapsule != null) {
+ votesCache.put(cacheKey, Value.create(votesCapsule));
+ }
+ return votesCapsule;
+ }
+
+ @Override
+ public WitnessCapsule getWitness(byte[] address) {
+ return witnessStore.get(address);
+ }
+
+ @Override
+ public long getBeginCycle(byte[] address) {
+ Key cacheKey = new Key(address);
+ BytesCapsule bytesCapsule = getDelegation(cacheKey);
+ return bytesCapsule == null ? 0 : ByteArray.toLong(bytesCapsule.getData());
+ }
+
+ @Override
+ public long getEndCycle(byte[] address) {
+ byte[] key = ("end-" + Hex.toHexString(address)).getBytes();
+ Key cacheKey = new Key(key);
+ BytesCapsule bytesCapsule = getDelegation(cacheKey);
+ return bytesCapsule == null ? DelegationStore.REMARK : ByteArray.toLong(bytesCapsule.getData());
+ }
+
+ @Override
+ public AccountCapsule getAccountVote(long cycle, byte[] address) {
+ byte[] key = (cycle + "-" + Hex.toHexString(address) + "-account-vote").getBytes();
+ Key cacheKey = new Key(key);
+ BytesCapsule bytesCapsule = getDelegation(cacheKey);
+ if (bytesCapsule == null) {
+ return null;
+ } else {
+ return new AccountCapsule(bytesCapsule.getData());
+ }
+ }
+
+ @Override
+ public BytesCapsule getDelegation(Key key) {
+ if (delegationCache.containsKey(key)) {
+ return new BytesCapsule(delegationCache.get(key).getValue());
+ }
+ BytesCapsule bytesCapsule;
+ if (parent != null) {
+ bytesCapsule = parent.getDelegation(key);
+ } else {
+ bytesCapsule = getDelegationStore().get(key.getData());
+ }
+ if (bytesCapsule != null) {
+ delegationCache.put(key, Value.create(bytesCapsule.getData()));
+ }
+ 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) {
getCodeStore().delete(address);
@@ -227,16 +492,16 @@ public void deleteContract(byte[] address) {
@Override
public void createContract(byte[] address, ContractCapsule contractCapsule) {
- Key key = Key.create(address);
- Value value = Value.create(contractCapsule.getData(), Type.VALUE_TYPE_CREATE);
- contractCache.put(key, value);
+ contractCache.put(Key.create(address),
+ Value.create(contractCapsule, Type.CREATE));
+ putNewContract(address);
}
@Override
public ContractCapsule getContract(byte[] address) {
Key key = Key.create(address);
if (contractCache.containsKey(key)) {
- return contractCache.get(key).getContract();
+ return new ContractCapsule(contractCache.get(key).getValue());
}
ContractCapsule contractCapsule;
@@ -247,30 +512,131 @@ public ContractCapsule getContract(byte[] address) {
}
if (contractCapsule != null) {
- contractCache.put(key, Value.create(contractCapsule.getData()));
+ contractCache.put(key, Value.create(contractCapsule));
}
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);
- Value value = Value.create(contractCapsule.getData(), Type.VALUE_TYPE_DIRTY);
- contractCache.put(key, value);
+ 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) {
- Key key = Key.create(address);
- Value value = Value.create(accountCapsule.getData(), Type.VALUE_TYPE_DIRTY);
- accountCache.put(key, value);
+ accountCache.put(Key.create(address),
+ Value.create(accountCapsule, Type.DIRTY));
+ }
+
+ @Override
+ public void updateDynamicProperty(byte[] word, BytesCapsule bytesCapsule) {
+ dynamicPropertiesCache.put(Key.create(word),
+ Value.create(bytesCapsule.getData(), Type.DIRTY));
+ }
+
+ @Override
+ public void updateDelegatedResource(byte[] word,
+ DelegatedResourceCapsule delegatedResourceCapsule) {
+ delegatedResourceCache.put(Key.create(word),
+ Value.create(delegatedResourceCapsule, Type.DIRTY));
+ }
+
+ @Override
+ public void updateVotes(byte[] word, VotesCapsule votesCapsule) {
+ votesCache.put(Key.create(word),
+ Value.create(votesCapsule, Type.DIRTY));
+ }
+
+ @Override
+ public void updateBeginCycle(byte[] word, long cycle) {
+ updateDelegation(word, new BytesCapsule(ByteArray.fromLong(cycle)));
+ }
+
+ @Override
+ public void updateEndCycle(byte[] word, long cycle) {
+ BytesCapsule bytesCapsule = new BytesCapsule(ByteArray.fromLong(cycle));
+ byte[] key = ("end-" + Hex.toHexString(word)).getBytes();
+ updateDelegation(key, bytesCapsule);
+ }
+
+ @Override
+ public void updateAccountVote(byte[] word, long cycle, AccountCapsule accountCapsule) {
+ BytesCapsule bytesCapsule = new BytesCapsule(accountCapsule.getData());
+ byte[] key = (cycle + "-" + Hex.toHexString(word) + "-account-vote").getBytes();
+ updateDelegation(key, bytesCapsule);
+ }
+
+ @Override
+ public void updateDelegation(byte[] word, BytesCapsule bytesCapsule) {
+ delegationCache.put(Key.create(word),
+ 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) {
- Key key = Key.create(address);
- Value value = Value.create(code, Type.VALUE_TYPE_CREATE);
- codeCache.put(key, value);
+ codeCache.put(Key.create(address), Value.create(code, Type.CREATE));
if (VMConfig.allowTvmConstantinople()) {
ContractCapsule contract = getContract(address);
@@ -284,7 +650,7 @@ public void saveCode(byte[] address, byte[] code) {
public byte[] getCode(byte[] address) {
Key key = Key.create(address);
if (codeCache.containsKey(key)) {
- return codeCache.get(key).getCode().getData();
+ return codeCache.get(key).getValue();
}
byte[] code;
@@ -305,23 +671,19 @@ public byte[] getCode(byte[] address) {
@Override
public void putStorageValue(byte[] address, DataWord key, DataWord value) {
- address = TransactionTrace.convertToTronAddress(address);
- if (getAccount(address) == null) {
- return;
+ Storage storage = getStorageInternal(address);
+ if (storage != null) {
+ storage.put(key, value);
}
- Key addressKey = Key.create(address);
- Storage storage;
- if (storageCache.containsKey(addressKey)) {
- storage = storageCache.get(addressKey);
- } else {
- storage = getStorage(address);
- storageCache.put(addressKey, storage);
- }
- storage.put(key, value);
}
@Override
public DataWord getStorageValue(byte[] address, DataWord key) {
+ Storage storage = getStorageInternal(address);
+ return storage == null ? null : storage.getValue(key);
+ }
+
+ private Storage getStorageInternal(byte[] address) {
address = TransactionTrace.convertToTronAddress(address);
if (getAccount(address) == null) {
return null;
@@ -334,7 +696,7 @@ public DataWord getStorageValue(byte[] address, DataWord key) {
storage = getStorage(address);
storageCache.put(addressKey, storage);
}
- return storage.getValue(key);
+ return storage;
}
@Override
@@ -356,8 +718,11 @@ public Storage getStorage(byte[] address) {
storage = new Storage(address, getStorageRowStore());
}
ContractCapsule contract = getContract(address);
- if (contract != null && !ByteUtil.isNullOrZeroArray(contract.getTrxHash())) {
- storage.generateAddrHash(contract.getTrxHash());
+ if (contract != null) {
+ storage.setContractVersion(contract.getContractVersion());
+ if (!ByteUtil.isNullOrZeroArray(contract.getTrxHash())) {
+ storage.generateAddrHash(contract.getTrxHash());
+ }
}
return storage;
}
@@ -385,11 +750,10 @@ 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);
- Value val = Value.create(accountCapsule.getData(),
- Type.VALUE_TYPE_DIRTY | accountCache.get(key).getType().getType());
- accountCache.put(key, val);
+ accountCache.put(key, Value.create(accountCapsule,
+ accountCache.get(key).getType().addType(Type.DIRTY)));
return accountCapsule.getBalance();
}
@@ -407,7 +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
@@ -425,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);
@@ -432,8 +809,38 @@ public void putStorage(Key key, Storage cache) {
@Override
public void putAccountValue(byte[] address, AccountCapsule accountCapsule) {
- Key key = new Key(address);
- accountCache.put(key, new Value(accountCapsule.getData(), Type.VALUE_TYPE_CREATE));
+ accountCache.put(new Key(address),
+ Value.create(accountCapsule, Type.CREATE));
+ }
+
+ @Override
+ public void putDynamicProperty(Key key, Value value) {
+ dynamicPropertiesCache.put(key, value);
+ }
+
+ @Override
+ public void putDelegatedResource(Key key, Value value) {
+ delegatedResourceCache.put(key, value);
+ }
+
+ @Override
+ public void putVotes(Key key, Value value) {
+ votesCache.put(key, value);
+ }
+
+ @Override
+ 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
@@ -443,8 +850,7 @@ public long addTokenBalance(byte[] address, byte[] tokenId, long value) {
if (accountCapsule == null) {
accountCapsule = createAccount(address, Protocol.AccountType.Normal);
}
- long balance = accountCapsule.getAssetMapV2()
- .getOrDefault(new String(tokenIdWithoutLeadingZero), new Long(0));
+ long balance = accountCapsule.getAssetV2(new String(tokenIdWithoutLeadingZero));
if (value == 0) {
return balance;
}
@@ -463,10 +869,9 @@ public long addTokenBalance(byte[] address, byte[] tokenId, long value) {
getAssetIssueStore());
}
Key key = Key.create(address);
- Value V = Value.create(accountCapsule.getData(),
- Type.VALUE_TYPE_DIRTY | accountCache.get(key).getType().getType());
- accountCache.put(key, V);
- return accountCapsule.getAssetMapV2().get(new String(tokenIdWithoutLeadingZero));
+ accountCache.put(key, Value.create(accountCapsule,
+ accountCache.get(key).getType().addType(Type.DIRTY)));
+ return accountCapsule.getAssetV2(new String(tokenIdWithoutLeadingZero));
}
@Override
@@ -476,12 +881,12 @@ public long getTokenBalance(byte[] address, byte[] tokenId) {
return 0;
}
String tokenStr = new String(ByteUtil.stripLeadingZeroes(tokenId));
- return accountCapsule.getAssetMapV2().getOrDefault(tokenStr, 0L);
+ return accountCapsule.getAssetV2(tokenStr);
}
@Override
public byte[] getBlackHoleAddress() {
- return getAccountStore().getBlackhole().getAddress().toByteArray();
+ return getAccountStore().getBlackholeAddress();
}
@Override
@@ -498,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;
}
@@ -524,29 +941,56 @@ 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()
- .getGenesisBlock().getTimestamp()))
+ return getSlotByTimestampMs(getDynamicPropertiesStore().getLatestBlockHeaderTimestamp());
+ }
+
+ public long getSlotByTimestampMs(long timestamp) {
+ return (timestamp - Long.parseLong(CommonParameter.getInstance()
+ .getGenesisBlock().getTimestamp()))
/ BLOCK_PRODUCED_INTERVAL;
}
@@ -556,7 +1000,7 @@ private void commitAccountCache(Repository deposit) {
if (deposit != null) {
deposit.putAccount(key, value);
} else {
- getAccountStore().put(key.getData(), value.getAccount());
+ getAccountStore().put(key.getData(), new AccountCapsule(value.getValue()));
}
}
});
@@ -568,7 +1012,7 @@ private void commitCodeCache(Repository deposit) {
if (deposit != null) {
deposit.putCode(key, value);
} else {
- getCodeStore().put(key.getData(), value.getCode());
+ getCodeStore().put(key.getData(), new CodeCapsule(value.getValue()));
}
}
}));
@@ -580,7 +1024,24 @@ private void commitContractCache(Repository deposit) {
if (deposit != null) {
deposit.putContract(key, value);
} else {
- getContractStore().put(key.getData(), value.getContract());
+ ContractCapsule contractCapsule = new ContractCapsule(value.getValue());
+ if (!abiStore.has(key.getData())) {
+ abiStore.put(key.getData(), new AbiCapsule(contractCapsule));
+ }
+ getContractStore().put(key.getData(), contractCapsule);
+ }
+ }
+ }));
+ }
+
+ 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);
}
}
}));
@@ -599,6 +1060,88 @@ private void commitStorageCache(Repository deposit) {
}
+ private void commitDynamicCache(Repository deposit) {
+ dynamicPropertiesCache.forEach(((key, value) -> {
+ if (value.getType().isDirty() || value.getType().isCreate()) {
+ if (deposit != null) {
+ deposit.putDynamicProperty(key, value);
+ } else {
+ getDynamicPropertiesStore().put(key.getData(), new BytesCapsule(value.getValue()));
+ }
+ }
+ }));
+ }
+
+ private void commitDelegatedResourceCache(Repository deposit) {
+ delegatedResourceCache.forEach(((key, value) -> {
+ if (value.getType().isDirty() || value.getType().isCreate()) {
+ if (deposit != null) {
+ deposit.putDelegatedResource(key, value);
+ } else {
+ getDelegatedResourceStore().put(key.getData(), new DelegatedResourceCapsule(value.getValue()));
+ }
+ }
+ }));
+ }
+
+ private void commitVotesCache(Repository deposit) {
+ votesCache.forEach(((key, value) -> {
+ if (value.getType().isDirty() || value.getType().isCreate()) {
+ if (deposit != null) {
+ deposit.putVotes(key, value);
+ } else {
+ getVotesStore().put(key.getData(), new VotesCapsule(value.getValue()));
+ }
+ }
+ }));
+ }
+
+ private void commitDelegationCache(Repository deposit) {
+ delegationCache.forEach((key, value) -> {
+ if (value.getType().isDirty() || value.getType().isCreate()) {
+ if (deposit != null) {
+ deposit.putDelegation(key, value);
+ } else {
+ getDelegationStore().put(key.getData(), new BytesCapsule(value.getValue()));
+ }
+ }
+ });
+ }
+
+ 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.
*/
@@ -615,8 +1158,76 @@ public AccountCapsule createNormalAccount(byte[] address) {
getDynamicPropertiesStore().getLatestBlockHeaderTimestamp(), withDefaultPermission,
getDynamicPropertiesStore());
- accountCache.put(key, new Value(account.getData(), Type.VALUE_TYPE_CREATE));
+ accountCache.put(key, Value.create(account, Type.CREATE));
return account;
}
+ //The unit is trx
+ @Override
+ public void addTotalNetWeight(long amount) {
+ long totalNetWeight = getTotalNetWeight();
+ totalNetWeight += amount;
+ saveTotalNetWeight(totalNetWeight);
+ }
+
+ //The unit is trx
+ @Override
+ public void addTotalEnergyWeight(long amount) {
+ long totalEnergyWeight = getTotalEnergyWeight();
+ totalEnergyWeight += 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,
+ new BytesCapsule(ByteArray.fromLong(totalNetWeight)));
+ }
+
+ @Override
+ public void saveTotalEnergyWeight(long totalEnergyWeight) {
+ updateDynamicProperty(TOTAL_ENERGY_WEIGHT,
+ 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))
+ .map(BytesCapsule::getData)
+ .map(ByteArray::toLong)
+ .orElseThrow(
+ () -> new IllegalArgumentException("not found TOTAL_NET_WEIGHT"));
+ }
+
+ @Override
+ public long getTotalEnergyWeight() {
+ return Optional.ofNullable(getDynamicProperty(TOTAL_ENERGY_WEIGHT))
+ .map(BytesCapsule::getData)
+ .map(ByteArray::toLong)
+ .orElseThrow(
+ () -> 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 6e822b2e996..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
@@ -1,110 +1,61 @@
package org.tron.core.vm.repository;
-public class Type {
+public class Type implements Cloneable {
- /**
- * Default Mode : VALUE_TYPE_NORMAL
- */
- public static final int VALUE_TYPE_NORMAL = 0;
- public static final int VALUE_TYPE_DIRTY = 1 << 0;
- public static final int VALUE_TYPE_CREATE = 1 << 1;
- public static final int VALUE_TYPE_UNKNOWN = 0xFFFFFFFC;
+ // Default Mode : NORMAL
+ public static final int NORMAL = 0;
+ public static final int DIRTY = 1;
+ public static final int CREATE = 1 << 1;
+ public static final int UNKNOWN = 0xFFFFFFFC;
- protected int type = VALUE_TYPE_NORMAL;
+ protected int type = NORMAL;
+
+ public Type() {}
- /**
- * @param type
- */
public Type(int type) {
this.type |= type;
}
- /**
- * default constructor
- */
- public Type() {
- }
-
- /**
- * @param T
- */
- private Type(Type T) {
- this.type = T.getType();
+ private Type(Type t) {
+ this.type = t.type;
}
- /**
- * @return
- */
public Type clone() {
return new Type(this);
}
- /**
- * @return
- */
public boolean isDirty() {
- return (this.type & VALUE_TYPE_DIRTY) == VALUE_TYPE_DIRTY;
+ return (this.type & DIRTY) == DIRTY;
}
- /**
- * @return
- */
public boolean isNormal() {
- return this.type == VALUE_TYPE_NORMAL;
+ return this.type == NORMAL;
}
- /**
- * @return
- */
public boolean isCreate() {
- return (this.type & VALUE_TYPE_CREATE) == VALUE_TYPE_CREATE;
+ return (this.type & CREATE) == CREATE;
}
- /**
- * @return
- */
public boolean shouldCommit() {
- return this.type != VALUE_TYPE_NORMAL;
+ return this.type != NORMAL;
}
- /**
- * @return
- */
- public int getType() {
- return type;
- }
-
- /**
- * @param type
- */
- public void setType(int type) {
+ public Type setType(int type) {
if (isValidType(type)) {
this.type = type;
}
+ return this;
}
- /**
- * @param type
- * @return
- */
public boolean isValidType(int type) {
- return (type & VALUE_TYPE_UNKNOWN) == VALUE_TYPE_NORMAL;
+ return (type & UNKNOWN) == NORMAL;
}
- /**
- * @param type
- */
- public void addType(int type) {
+ public int addType(int type) {
if (isValidType(type)) {
this.type |= type;
}
- }
-
- /**
- * @param T
- */
- public void addType(Type T) {
- addType(T.getType());
+ return this.type;
}
@Override
@@ -116,19 +67,17 @@ public boolean equals(Object obj) {
return false;
}
- Type T = (Type) obj;
- return this.type == T.getType();
+ Type other = (Type) obj;
+ return this.type == other.type;
}
@Override
public int hashCode() {
- return new Integer(type).hashCode();
+ return type;
}
@Override
public String toString() {
- return "Type{" +
- "type=" + type +
- '}';
+ return "Type{" + "type=" + type + '}';
}
}
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 3df534ec833..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
@@ -1,232 +1,46 @@
package org.tron.core.vm.repository;
import java.util.Arrays;
-import org.apache.commons.lang3.ArrayUtils;
-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.CodeCapsule;
-import org.tron.core.capsule.ContractCapsule;
-import org.tron.core.capsule.ProposalCapsule;
-import org.tron.core.capsule.TransactionCapsule;
-import org.tron.core.capsule.VotesCapsule;
-import org.tron.core.capsule.WitnessCapsule;
-import org.tron.core.exception.BadItemException;
+import java.util.Objects;
+
+import lombok.Getter;
+import org.tron.core.capsule.ProtoCapsule;
import org.tron.core.vm.config.VMConfig;
-public class Value {
+public class Value {
+ @Getter
private Type type;
- private byte[] any = null;
- /**
- * @param any
- */
- public Value(byte[] any, Type type) {
- if (any != null && any.length > 0) {
- this.any = new byte[any.length];
- System.arraycopy(any, 0, this.any, 0, any.length);
- this.type = type.clone();
- }
- }
+ @Getter
+ private T value;
- /**
- * @param any
- * @param type
- */
- public Value(byte[] any, int type) {
- if (any != null && any.length > 0) {
- this.any = new byte[any.length];
- System.arraycopy(any, 0, this.any, 0, any.length);
+ private Value(T value, int type) {
+ if (value != null) {
+ this.value = value;
this.type = new Type(type);
} else {
if (VMConfig.allowMultiSign()) {
- this.type = new Type(Type.VALUE_TYPE_UNKNOWN);
+ this.type = new Type(Type.UNKNOWN);
}
}
}
- /**
- * @param value
- */
- private Value(Value value) {
- if (value.getAny() != null && value.getAny().length > 0) {
- this.any = new byte[value.any.length];
- System.arraycopy(value.getAny(), 0, this.any, 0, value.getAny().length);
- this.type = value.getType().clone();
- } else {
- if (VMConfig.allowMultiSign()) {
- this.type = new Type(Type.VALUE_TYPE_UNKNOWN);
- }
- }
- }
-
- public static Value create(byte[] any, int type) {
- return new Value(any, type);
- }
-
- public static Value create(byte[] any) {
- return new Value(any, Type.VALUE_TYPE_NORMAL);
- }
-
- /**
- * @return
- */
- public Value clone() {
- return new Value(this);
- }
-
- /**
- * @return
- */
- public byte[] getAny() {
- return any;
+ public static Value create(ProtoCapsule capsule, int type) {
+ return new Value<>(capsule.getInstance(), type);
}
- /**
- * @return
- */
- public Type getType() {
- return type;
+ public static Value create(ProtoCapsule capsule) {
+ return create(capsule, Type.NORMAL);
}
- /**
- * @param type
- */
- public void setType(Type type) {
- this.type = type;
+ public static Value create(byte[] value, int type) {
+ return (value == null || value.length ==0) ? new Value<>(null, type) :
+ new Value<>(Arrays.copyOf(value, value.length), type);
}
- /**
- * @param type
- */
- public void addType(Type type) {
- this.type.addType(type);
- }
-
- /**
- * @param type
- */
- public void addType(int type) {
- this.type.addType(type);
- }
-
- /**
- * @return
- */
- public AccountCapsule getAccount() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new AccountCapsule(any);
- }
-
- /**
- * @return
- */
- public BytesCapsule getBytes() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new BytesCapsule(any);
- }
-
- /**
- *
- */
- public TransactionCapsule getTransaction() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- try {
- return new TransactionCapsule(any);
- } catch (BadItemException e) {
- return null;
- }
- }
-
- /**
- *
- */
- public BlockCapsule getBlock() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- try {
- return new BlockCapsule(any);
- } catch (Exception e) {
- return null;
- }
- }
-
- /**
- * @return
- */
- public WitnessCapsule getWitness() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new WitnessCapsule(any);
-
- }
-
- public VotesCapsule getVotes() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new VotesCapsule(any);
- }
-
- /**
- * @return
- */
- public BytesCapsule getBlockIndex() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new BytesCapsule(any);
- }
-
- /**
- * @return
- */
- public CodeCapsule getCode() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new CodeCapsule(any);
- }
-
- /**
- * @return
- */
- public ContractCapsule getContract() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new ContractCapsule(any);
- }
-
- public AssetIssueCapsule getAssetIssue() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new AssetIssueCapsule(any);
- }
-
- public ProposalCapsule getProposal() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new ProposalCapsule(any);
- }
-
- public BytesCapsule getDynamicProperties() {
- if (ArrayUtils.isEmpty(any)) {
- return null;
- }
- return new BytesCapsule(any);
+ public static Value create(byte[] value) {
+ return create(value, Type.NORMAL);
}
@Override
@@ -238,19 +52,12 @@ public boolean equals(Object obj) {
return false;
}
- Value V = (Value) obj;
- if (Arrays.equals(this.any, V.getAny())) {
- return true;
- }
- return false;
+ Value other = (Value) obj;
+ return Objects.equals(this.getValue(), other.getValue());
}
-// public static Value create(byte[] any, Type type) {
-// return new Value(any, type);
-// }
-
@Override
public int hashCode() {
- return new Integer(type.hashCode() + Arrays.hashCode(any)).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/Op.java b/actuator/src/main/java/org/tron/core/vm/trace/Op.java
index b9b84734a19..b7c333ca0f6 100644
--- a/actuator/src/main/java/org/tron/core/vm/trace/Op.java
+++ b/actuator/src/main/java/org/tron/core/vm/trace/Op.java
@@ -1,38 +1,20 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.trace;
import java.math.BigInteger;
-import org.tron.core.vm.OpCode;
public class Op {
- private OpCode code;
+ private int code;
private int deep;
private int pc;
private BigInteger energy;
private OpActions actions;
- public OpCode getCode() {
+ public int getCode() {
return code;
}
- public void setCode(OpCode code) {
+ public void setCode(int code) {
this.code = code;
}
diff --git a/actuator/src/main/java/org/tron/core/vm/trace/OpActions.java b/actuator/src/main/java/org/tron/core/vm/trace/OpActions.java
index 4880ca15925..d03f9688658 100644
--- a/actuator/src/main/java/org/tron/core/vm/trace/OpActions.java
+++ b/actuator/src/main/java/org/tron/core/vm/trace/OpActions.java
@@ -1,20 +1,3 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.trace;
import static org.tron.common.utils.ByteArray.toHexString;
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 3afab68894d..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
@@ -1,32 +1,13 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.trace;
import static java.lang.String.format;
import static org.tron.common.utils.ByteArray.toHexString;
-import static org.tron.core.db.TransactionTrace.convertToTronAddress;
import static org.tron.core.vm.trace.Serializers.serializeFieldsOnly;
import java.util.ArrayList;
import java.util.List;
-import org.spongycastle.util.encoders.Hex;
+import org.bouncycastle.util.encoders.Hex;
import org.tron.common.runtime.vm.DataWord;
-import org.tron.core.vm.OpCode;
import org.tron.core.vm.config.VMConfig;
import org.tron.core.vm.program.invoke.ProgramInvoke;
@@ -38,13 +19,12 @@ public class ProgramTrace {
private String contractAddress;
public ProgramTrace() {
- this(null, null);
+ this(null);
}
- public ProgramTrace(VMConfig config, ProgramInvoke programInvoke) {
- if (programInvoke != null && config.vmTrace()) {
- contractAddress = Hex
- .toHexString(convertToTronAddress(programInvoke.getContractAddress().getLast20Bytes()));
+ public ProgramTrace(ProgramInvoke programInvoke) {
+ if (programInvoke != null && VMConfig.vmTrace()) {
+ contractAddress = Hex.toHexString(programInvoke.getContractAddress().toTronAddress());
}
}
@@ -93,7 +73,7 @@ public ProgramTrace error(Exception error) {
public Op addOp(byte code, int pc, int deep, DataWord energy, OpActions actions) {
Op op = new Op();
op.setActions(actions);
- op.setCode(OpCode.code(code));
+ op.setCode(code & 0xff);
op.setDeep(deep);
op.setEnergy(energy.value());
op.setPc(pc);
@@ -110,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/ProgramTraceListener.java b/actuator/src/main/java/org/tron/core/vm/trace/ProgramTraceListener.java
index 214c0024429..4893a66ebcc 100644
--- a/actuator/src/main/java/org/tron/core/vm/trace/ProgramTraceListener.java
+++ b/actuator/src/main/java/org/tron/core/vm/trace/ProgramTraceListener.java
@@ -1,20 +1,3 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.vm.trace;
import org.tron.common.runtime.vm.DataWord;
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 797301bcaf8..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,90 +1,28 @@
-/*
- * Copyright (c) [2016] [ ]
- * This file is part of the ethereumJ library.
- *
- * The ethereumJ library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The ethereumJ library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * 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.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.spongycastle.util.encoders.Hex;
-import org.tron.common.runtime.vm.DataWord;
-import org.tron.core.vm.OpCode;
@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(OpCode.code(op).name());
- }
- }
}
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 49f5af5d238..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;
@@ -26,11 +29,13 @@ public static void transferAllToken(Repository deposit, byte[] fromAddress, byte
AccountCapsule fromAccountCap = deposit.getAccount(fromAddress);
Protocol.Account.Builder fromBuilder = fromAccountCap.getInstance().toBuilder();
AccountCapsule toAccountCap = deposit.getAccount(toAddress);
+ toAccountCap.importAllAsset();
Protocol.Account.Builder toBuilder = toAccountCap.getInstance().toBuilder();
fromAccountCap.getAssetMapV2().forEach((tokenId, amount) -> {
toBuilder.putAssetV2(tokenId, toBuilder.getAssetV2Map().getOrDefault(tokenId, 0L) + amount);
fromBuilder.putAssetV2(tokenId, 0L);
});
+
deposit.putAccountValue(fromAddress, new AccountCapsule(fromBuilder.build()));
deposit.putAccountValue(toAddress, new AccountCapsule(toBuilder.build()));
}
@@ -53,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/main/java/org/tron/core/vm/utils/VoteRewardUtil.java b/actuator/src/main/java/org/tron/core/vm/utils/VoteRewardUtil.java
new file mode 100644
index 00000000000..70a73c0fc91
--- /dev/null
+++ b/actuator/src/main/java/org/tron/core/vm/utils/VoteRewardUtil.java
@@ -0,0 +1,121 @@
+package org.tron.core.vm.utils;
+
+import java.math.BigInteger;
+import org.apache.commons.collections4.CollectionUtils;
+import org.tron.core.capsule.AccountCapsule;
+import org.tron.core.store.DelegationStore;
+import org.tron.core.vm.config.VMConfig;
+import org.tron.core.vm.repository.Repository;
+import org.tron.protos.Protocol;
+
+public class VoteRewardUtil {
+
+ private VoteRewardUtil() {
+ }
+
+ public static void withdrawReward(byte[] address, Repository repository) {
+ if (!VMConfig.allowTvmVote()) {
+ return;
+ }
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ long beginCycle = repository.getBeginCycle(address);
+ long endCycle = repository.getEndCycle(address);
+ long currentCycle = repository.getDynamicPropertiesStore().getCurrentCycleNumber();
+ long reward = 0;
+ if (beginCycle > currentCycle || accountCapsule == null) {
+ return;
+ }
+ if (beginCycle == currentCycle) {
+ AccountCapsule account = repository.getAccountVote(beginCycle, address);
+ if (account != null) {
+ return;
+ }
+ }
+ if (beginCycle + 1 == endCycle && beginCycle < currentCycle) {
+ AccountCapsule account = repository.getAccountVote(beginCycle, address);
+ if (account != null) {
+ reward = computeReward(beginCycle, endCycle, account, repository);
+ adjustAllowance(address, reward, repository);
+ reward = 0;
+ }
+ beginCycle += 1;
+ }
+ endCycle = currentCycle;
+ if (CollectionUtils.isEmpty(accountCapsule.getVotesList())) {
+ repository.updateBeginCycle(address, endCycle + 1);
+ return;
+ }
+ if (beginCycle < endCycle) {
+ reward += computeReward(beginCycle, endCycle, accountCapsule, repository);
+ adjustAllowance(address, reward, repository);
+ }
+ repository.updateBeginCycle(address, endCycle);
+ repository.updateEndCycle(address, endCycle + 1);
+ repository.updateAccountVote(address, endCycle, accountCapsule);
+ }
+
+ public static long queryReward(byte[] address, Repository repository) {
+ if (!VMConfig.allowTvmVote()) {
+ return 0;
+ }
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ long beginCycle = repository.getBeginCycle(address);
+ long endCycle = repository.getEndCycle(address);
+ long currentCycle = repository.getDynamicPropertiesStore().getCurrentCycleNumber();
+ long reward = 0;
+ if (accountCapsule == null) {
+ return 0;
+ }
+ if (beginCycle > currentCycle) {
+ return accountCapsule.getAllowance();
+ }
+ //withdraw the latest cycle reward
+ if (beginCycle + 1 == endCycle && beginCycle < currentCycle) {
+ AccountCapsule account = repository.getAccountVote(beginCycle, address);
+ if (account != null) {
+ reward = computeReward(beginCycle, endCycle, account, repository);
+ }
+ beginCycle += 1;
+ }
+ endCycle = currentCycle;
+ if (CollectionUtils.isEmpty(accountCapsule.getVotesList())) {
+ return reward + accountCapsule.getAllowance();
+ }
+ if (beginCycle < endCycle) {
+ reward += computeReward(beginCycle, endCycle, accountCapsule, repository);
+ }
+ return reward + accountCapsule.getAllowance();
+ }
+
+ private static long computeReward(long beginCycle, long endCycle,
+ AccountCapsule accountCapsule, Repository repository) {
+ if (beginCycle >= endCycle) {
+ return 0;
+ }
+
+ long reward = 0;
+ for (Protocol.Vote vote : accountCapsule.getVotesList()) {
+ byte[] srAddress = vote.getVoteAddress().toByteArray();
+ BigInteger beginVi = repository.getDelegationStore().getWitnessVi(beginCycle - 1, srAddress);
+ BigInteger endVi = repository.getDelegationStore().getWitnessVi(endCycle - 1, srAddress);
+ BigInteger deltaVi = endVi.subtract(beginVi);
+ if (deltaVi.signum() <= 0) {
+ continue;
+ }
+ long userVote = vote.getVoteCount();
+ reward += deltaVi.multiply(BigInteger.valueOf(userVote))
+ .divide(DelegationStore.DECIMAL_OF_VI_REWARD).longValue();
+ }
+ return reward;
+ }
+
+ private static void adjustAllowance(byte[] address, long amount, Repository repository) {
+ if (amount <= 0) {
+ return;
+ }
+ AccountCapsule accountCapsule = repository.getAccount(address);
+ long allowance = accountCapsule.getAllowance();
+ accountCapsule.setAllowance(allowance + amount);
+ repository.updateAccount(accountCapsule.createDbKey(), accountCapsule);
+ }
+}
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 12d78a0c223..e143ab3a947 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,77 +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.3'
- classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
- }
- }
-
- 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.3'
- compile group: 'org.projectlombok', name: 'lombok', version: '1.18.2'
- 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
- }
-
- artifacts {
-// archives jar
- archives sourcesJar
- }
-
- tasks.withType(AbstractArchiveTask) {
- preserveFileTimestamps = false
- reproducibleFileOrder = true
- }
-}
-
-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 f33e542af85..00000000000
--- a/build.md
+++ /dev/null
@@ -1,84 +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 9a191d40980..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.8.5'
- 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,39 +39,17 @@ 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 = files('../framework/build/jacoco/jacocoTest.exec')
+ getExecutionData().setFrom(fileTree('../framework/build/jacoco').include("**.exec"))
afterEvaluate {
- classDirectories = files(classDirectories.files.collect {
+ classDirectories.from = classDirectories.files.collect {
fileTree(dir: it,)
- })
+ }
}
}
diff --git a/chainbase/src/main/java/org/tron/common/bloom/Bloom.java b/chainbase/src/main/java/org/tron/common/bloom/Bloom.java
new file mode 100644
index 00000000000..19d2bf53097
--- /dev/null
+++ b/chainbase/src/main/java/org/tron/common/bloom/Bloom.java
@@ -0,0 +1,137 @@
+package org.tron.common.bloom;
+
+import com.google.protobuf.ByteString;
+import java.util.Arrays;
+import java.util.Iterator;
+import org.tron.common.crypto.Hash;
+import org.tron.common.utils.ByteArray;
+import org.tron.common.utils.ByteUtil;
+import org.tron.core.capsule.TransactionRetCapsule;
+import org.tron.protos.Protocol.TransactionInfo;
+import org.tron.protos.Protocol.TransactionInfo.Log;
+
+public class Bloom {
+
+ public static final int BLOOM_BIT_SIZE = 2048;
+ public static final int BLOOM_BYTE_SIZE = BLOOM_BIT_SIZE / 8;
+ private static final int STEPS_8 = 8;
+ private static final int ENSURE_BYTE = 255;
+ private static final int LOW_3_BITS = getLowBits(BLOOM_BIT_SIZE);
+ private byte[] data = new byte[BLOOM_BYTE_SIZE];
+
+ public Bloom() {
+ }
+
+ public Bloom(byte[] data) {
+ if (data.length != this.data.length) {
+ throw new RuntimeException(
+ "input data length is not equal to Bloom size " + this.data.length);
+ }
+ this.data = data;
+ }
+
+ //get several low bit. 512 -> 0b1, 1024 -> 0b11, 2048 -> 0b111, 4096-> 0b1111
+ public static int getLowBits(int bloomBitSize) {
+ return ENSURE_BYTE >> (16 + 1 - Integer.toBinaryString(bloomBitSize).length());
+ }
+
+ //only use first six byte
+ public static Bloom create(byte[] toBloom) {
+
+ int mov1 =
+ (((toBloom[0] & ENSURE_BYTE) & (LOW_3_BITS)) << STEPS_8) + ((toBloom[1]) & ENSURE_BYTE);
+ int mov2 =
+ (((toBloom[2] & ENSURE_BYTE) & (LOW_3_BITS)) << STEPS_8) + ((toBloom[3]) & ENSURE_BYTE);
+ int mov3 =
+ (((toBloom[4] & ENSURE_BYTE) & (LOW_3_BITS)) << STEPS_8) + ((toBloom[5]) & ENSURE_BYTE);
+
+ byte[] data = new byte[BLOOM_BYTE_SIZE];
+ Bloom bloom = new Bloom(data);
+
+ ByteUtil.setBit(data, mov1, 1);
+ ByteUtil.setBit(data, mov2, 1);
+ ByteUtil.setBit(data, mov3, 1);
+
+ return bloom;
+ }
+
+ public static Bloom createBloom(TransactionRetCapsule transactionRetCapsule) {
+ if (transactionRetCapsule == null) {
+ return null;
+ }
+ Iterator it =
+ transactionRetCapsule.getInstance().getTransactioninfoList().iterator();
+ Bloom blockBloom = null;
+
+ while (it.hasNext()) {
+ TransactionInfo transactionInfo = it.next();
+ if (transactionInfo == null || transactionInfo.getLogCount() == 0) {
+ continue;
+ }
+
+ if (blockBloom == null) {
+ blockBloom = new Bloom();
+ }
+
+ for (Log log : transactionInfo.getLogList()) {
+ //log.address doesn't have "41" ahead
+ Bloom bloom = Bloom.create(Hash.sha3(log.getAddress().toByteArray()));
+ blockBloom.or(bloom);
+
+ for (ByteString topic : log.getTopicsList()) {
+ bloom = Bloom.create(Hash.sha3(topic.toByteArray()));
+ blockBloom.or(bloom);
+ }
+ }
+ }
+
+ return blockBloom;
+ }
+
+ public void or(Bloom bloom) {
+ for (int i = 0; i < data.length; ++i) {
+ data[i] |= bloom.data[i];
+ }
+ }
+
+ /**
+ * (this || topicBloom) == this
+ */
+ public boolean matches(Bloom topicBloom) {
+ Bloom copy = copy();
+ copy.or(topicBloom);
+ return this.equals(copy);
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public Bloom copy() {
+ return new Bloom(Arrays.copyOf(getData(), getData().length));
+ }
+
+ @Override
+ public String toString() {
+ return ByteArray.toHexString(data);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Bloom bloom = (Bloom) o;
+
+ return Arrays.equals(data, bloom.data);
+ }
+
+ @Override
+ public int hashCode() {
+ return data != null ? Arrays.hashCode(data) : 0;
+ }
+}
diff --git a/chainbase/src/main/java/org/tron/common/error/TronDBException.java b/chainbase/src/main/java/org/tron/common/error/TronDBException.java
new file mode 100644
index 00000000000..ef2c36f88d0
--- /dev/null
+++ b/chainbase/src/main/java/org/tron/common/error/TronDBException.java
@@ -0,0 +1,18 @@
+package org.tron.common.error;
+
+public class TronDBException extends RuntimeException {
+ public TronDBException() {
+ }
+
+ public TronDBException(String s) {
+ super(s);
+ }
+
+ public TronDBException(String s, Throwable throwable) {
+ super(s, throwable);
+ }
+
+ public TronDBException(Throwable throwable) {
+ super(throwable);
+ }
+}
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 8c7381b7dcc..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
@@ -21,8 +21,10 @@
public abstract class Message {
protected static final Logger logger = LoggerFactory.getLogger("Message");
+ // https://developers.google.com/protocol-buffers/docs/proto3#unknowns
+ // https://github.com/protocolbuffers/protobuf/issues/272
private static final Field field = ReflectionUtils
- .findField(CodedInputStream.class, "explicitDiscardUnknownFields");
+ .findField(CodedInputStream.class, "shouldDiscardUnknownFields");
@Setter
private static DynamicPropertiesStore dynamicPropertiesStore;
@@ -67,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/InternalTransaction.java b/chainbase/src/main/java/org/tron/common/runtime/InternalTransaction.java
index 6cde05afd48..06fe36cdea6 100644
--- a/chainbase/src/main/java/org/tron/common/runtime/InternalTransaction.java
+++ b/chainbase/src/main/java/org/tron/common/runtime/InternalTransaction.java
@@ -66,6 +66,11 @@ public class InternalTransaction {
private String note;
private byte[] protoEncoded;
+ /*
+ * extra data field for recording parameters of vote witness opcode
+ */
+ private String extra;
+
/**
* Construct a root InternalTransaction
@@ -195,6 +200,10 @@ public long getValue() {
return value;
}
+ public void setValue(long value) {
+ this.value= value;
+ }
+
public byte[] getData() {
if (data == null) {
return EMPTY_BYTE_ARRAY;
@@ -202,6 +211,14 @@ public byte[] getData() {
return data.clone();
}
+ public void setExtra(String extra) {
+ this.extra = extra;
+ }
+
+ public String getExtra() {
+ return extra == null ? "" : extra;
+ }
+
public final byte[] getHash() {
if (!isEmpty(hash)) {
return Arrays.copyOf(hash, hash.length);
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 83c65a2ae97..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,14 +16,16 @@
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;
public class ProgramResult {
private long energyUsed = 0;
- private long futureRefund = 0;
+ //private long futureRefund = 0;
+
+ @Getter
+ private long energyPenaltyTotal = 0;
private byte[] hReturn = EMPTY_BYTE_ARRAY;
private byte[] contractAddress = EMPTY_BYTE_ARRAY;
@@ -31,7 +33,7 @@ public class ProgramResult {
private boolean revert;
private Set deleteAccounts;
- private ByteArraySet touchedAccounts = new ByteArraySet();
+ //private ByteArraySet touchedAccounts = new ByteArraySet();
private List internalTransactions;
private List logInfoList;
private TransactionResultCapsule ret = new TransactionResultCapsule();
@@ -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);
}
@@ -134,19 +145,19 @@ public void addDeleteAccounts(Set accounts) {
}
}
- public void addTouchAccount(byte[] addr) {
- touchedAccounts.add(addr);
- }
+// public void addTouchAccount(byte[] addr) {
+// touchedAccounts.add(addr);
+// }
- public Set getTouchedAccounts() {
- return touchedAccounts;
- }
+// public Set getTouchedAccounts() {
+// return touchedAccounts;
+// }
- public void addTouchAccounts(Set accounts) {
- if (!isEmpty(accounts)) {
- getTouchedAccounts().addAll(accounts);
- }
- }
+// public void addTouchAccounts(Set accounts) {
+// if (!isEmpty(accounts)) {
+// getTouchedAccounts().addAll(accounts);
+// }
+// }
public List getLogInfoList() {
if (logInfoList == null) {
@@ -207,31 +218,32 @@ public void rejectInternalTransactions() {
}
}
- public void addFutureRefund(long energyValue) {
- futureRefund += energyValue;
- }
+// public void addFutureRefund(long energyValue) {
+// futureRefund += energyValue;
+// }
- public long getFutureRefund() {
- return futureRefund;
- }
+// public long getFutureRefund() {
+// return futureRefund;
+// }
- public void resetFutureRefund() {
- futureRefund = 0;
- }
+// public void resetFutureRefund() {
+// futureRefund = 0;
+// }
public void reset() {
getDeleteAccounts().clear();
getLogInfoList().clear();
- resetFutureRefund();
+ //resetFutureRefund();
}
public void merge(ProgramResult another) {
addInternalTransactions(another.getInternalTransactions());
+ addTotalPenalty(another.getEnergyPenaltyTotal());
if (another.getException() == null && !another.isRevert()) {
addDeleteAccounts(another.getDeleteAccounts());
addLogInfos(another.getLogInfoList());
- addFutureRefund(another.getFutureRefund());
- addTouchAccounts(another.getTouchedAccounts());
+ //addFutureRefund(another.getFutureRefund());
+ //addTouchAccounts(another.getTouchedAccounts());
}
}
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.
+ *
+ *