diff --git a/.editorconfig b/.editorconfig
index c8247e5496..ccbd29fd03 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -48,6 +48,11 @@ indent_size = 2
end_of_line = lf
indent_size = 2
+# YAML files
+[*.yml]
+end_of_line = lf
+indent_size = 2
+
# Dotnet code style settings:
[*.{cs,vb}]
# Member can be made 'readonly'
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
new file mode 100644
index 0000000000..3d8e7d0d6c
--- /dev/null
+++ b/.github/workflows/docker.yml
@@ -0,0 +1,77 @@
+name: Publish (docker-image)
+
+on:
+ release:
+ types: [published]
+
+env:
+ DOTNET_VERSION: 8.0.x
+ DIST_DIR: ./dist
+
+jobs:
+ neo-cli-build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Set Application Version (Environment Variable)
+ run: |
+ APP_VERSION=$(echo '${{ github.event.release.tag_name }}' | cut -d 'v' -f 2)
+ echo "APP_VERSION=$APP_VERSION" >> $GITHUB_ENV
+
+ - name: Checkout (GitHub)
+ uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: ${{ env.DOTNET_VERSION }}
+
+ - name: Build (neo-cli)
+ run: |
+ dotnet publish ./src/Neo.CLI \
+ --framework net8.0 \
+ --configuration Release \
+ --runtime linux-x64 \
+ --self-contained true \
+ --output ${{ env.DIST_DIR }} \
+ --verbosity normal \
+ -p:VersionPrefix=${{ env.APP_VERSION }} \
+ -p:RuntimeIdentifier=linux-x64 \
+ -p:SelfContained=true \
+ -p:IncludeNativeLibrariesForSelfExtract=false \
+ -p:PublishTrimmed=false \
+ -p:PublishSingleFile=true \
+ -p:PublishReadyToRun=true \
+ -p:EnableCompressionInSingleFile=true \
+ -p:DebugType=embedded \
+ -p:ServerGarbageCollection=true
+
+ - name: Build (LevelDbStore)
+ run: |
+ dotnet build ./src/Plugins/LevelDBStore \
+ --framework net8.0 \
+ --configuration Release \
+ --output ${{ env.DIST_DIR }}/Plugins/LevelDBStore \
+ --verbosity normal \
+ -p:VersionPrefix=${{ env.APP_VERSION }}
+
+ - name: Remove (junk)
+ run: |
+ rm -v ${{ env.DIST_DIR }}/Plugins/LevelDBStore/Neo*
+ rm -v ${{ env.DIST_DIR }}/Plugins/LevelDBStore/*.pdb
+ rm -v ${{ env.DIST_DIR }}/Plugins/LevelDBStore/*.xml
+ rm -v ${{ env.DIST_DIR }}/*.xml
+
+ - name: Docker Login
+ run: |
+ docker login ghcr.io \
+ --username ${{ github.repository_owner }} \
+ --password ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Docker Build
+ run: |
+ docker build . \
+ --file ./.neo/docker/neo-cli/Dockerfile \
+ --tag ghcr.io/${{ github.repository_owner }}/neo-cli:latest \
+ --tag ghcr.io/${{ github.repository_owner }}/neo-cli:${{ env.APP_VERSION }} \
+ --push
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 5294cd09f4..22cf4d08df 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -24,7 +24,7 @@ jobs:
- name: Check Format (*.cs)
run: dotnet format --verify-no-changes --verbosity diagnostic
- Build-Test-Neo-Cli:
+ Test-Everything:
needs: [Format]
timeout-minutes: 15
runs-on: ubuntu-latest
@@ -37,16 +37,16 @@ jobs:
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- - name: Build (Neo.CLI)
- run: dotnet build ./src/Neo.CLI --output ./out/Neo.CLI
+ - name: Build (Everything)
+ run: dotnet build
- name: Install dependencies
run: |
sudo apt-get install libleveldb-dev expect
- find ./out -name 'config.json' | xargs perl -pi -e 's|LevelDBStore|MemoryStore|g'
+ find ./bin -name 'config.json' | xargs perl -pi -e 's|LevelDBStore|MemoryStore|g'
- name: Run tests with expect
- run: expect ./scripts/Neo.CLI/test-neo-cli.expect
+ run: expect ./scripts/Neo.CLI/test-neo-cli.exp
Test:
needs: [Format]
@@ -68,7 +68,8 @@ jobs:
if: matrix.os != 'ubuntu-latest'
run: |
dotnet sln neo.sln remove ./tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj
- dotnet test
+ dotnet build
+ dotnet test --blame-hang --blame-crash --no-build
- name: Test for coverall
if: matrix.os == 'ubuntu-latest'
@@ -80,6 +81,7 @@ jobs:
dotnet test ./tests/Neo.UnitTests --output ./bin/tests/Neo.UnitTests
dotnet test ./tests/Neo.VM.Tests --output ./bin/tests/Neo.VM.Tests
dotnet test ./tests/Neo.Json.UnitTests --output ./bin/tests/Neo.Json.UnitTests
+ dotnet test ./tests/Neo.Extensions.Tests --output ./bin/tests/Neo.Extensions.Tests
# Plugins
dotnet test ./tests/Neo.Cryptography.MPTTrie.Tests --output ./bin/tests/Neo.Cryptography.MPTTrie.Tests
@@ -87,6 +89,7 @@ jobs:
dotnet test ./tests/Neo.Plugins.OracleService.Tests --output ./bin/tests/Neo.Plugins.OracleService.Tests
dotnet test ./tests/Neo.Plugins.RpcServer.Tests --output ./bin/tests/Neo.Plugins.RpcServer.Tests
dotnet test ./tests/Neo.Plugins.Storage.Tests --output ./bin/tests/Neo.Plugins.Storage.Tests
+ dotnet test ./tests/Neo.Plugins.ApplicationLogs.Tests --output ./bin/tests/Neo.Plugins.ApplicationLogs.Tests
- name: Coveralls
if: matrix.os == 'ubuntu-latest'
@@ -104,6 +107,8 @@ jobs:
${{ github.workspace }}/tests/Neo.Plugins.OracleService.Tests/TestResults/coverage.info
${{ github.workspace }}/tests/Neo.Plugins.RpcServer.Tests/TestResults/coverage.info
${{ github.workspace }}/tests/Neo.Plugins.Storage.Tests/TestResults/coverage.info
+ ${{ github.workspace }}/tests/Neo.Plugins.ApplicationLogs.Tests/TestResults/coverage.info
+ ${{ github.workspace }}/tests/Neo.Extensions.Tests/TestResults/coverage.info
PublishPackage:
if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/')
@@ -123,19 +128,13 @@ jobs:
- name: Set Version
run: git rev-list --count HEAD | xargs printf 'CI%05d' | xargs -I{} echo 'VERSION_SUFFIX={}' >> $GITHUB_ENV
- - name : Pack (Neo)
+ - name : Pack (Everything)
run: |
dotnet pack \
--configuration Release \
--output ./out \
--version-suffix ${{ env.VERSION_SUFFIX }}
- - name: Remove Unwanted Files
- working-directory: ./out
- run: |
- rm -v Neo.CLI*
- rm -v Neo.GUI*
-
- name: Publish to Github Packages
working-directory: ./out
run: |
diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml
index 61ff1e3408..3fa6cc4f5e 100644
--- a/.github/workflows/nuget.yml
+++ b/.github/workflows/nuget.yml
@@ -35,8 +35,8 @@ jobs:
dotnet pack ./neo.sln \
--configuration Release \
--output ./sbin \
- --verbosity normal \ # Normal verbosity level
- -p:VersionPrefix=${{ env.APP_VERSION }} # Set the version prefix from tag_name
+ --verbosity normal \
+ -p:VersionPrefix=${{ env.APP_VERSION }}
- name: Publish to NuGet.org
run: |
diff --git a/.github/workflows/pkgs-delete.yml b/.github/workflows/pkgs-delete.yml
index cf3471b551..75bbd9455e 100644
--- a/.github/workflows/pkgs-delete.yml
+++ b/.github/workflows/pkgs-delete.yml
@@ -61,67 +61,53 @@ jobs:
shell: python
- delete-git-pkgs:
- name: Delete Old Nuget Packages
+ delete-git-docker-pkgs:
+ name: Delete Old Docker Images
runs-on: ubuntu-latest
steps:
- - name: Delete Neo.Cryptography.BLS12_381 Package
+ - name: Delete Neo Package (docker)
uses: actions/delete-package-versions@v4
+ continue-on-error: true
with:
- package-name: Neo.Cryptography.BLS12_381
- package-type: nuget
- min-versions-to-keep: 3
- delete-only-pre-release-versions: "true"
- token: "${{ secrets.GITHUB_TOKEN }}"
-
- - name: Delete Neo.VM Package
- uses: actions/delete-package-versions@v4
- with:
- package-name: Neo.VM
- package-type: nuget
- min-versions-to-keep: 3
- delete-only-pre-release-versions: "true"
- token: "${{ secrets.GITHUB_TOKEN }}"
-
- - name: Delete Neo.Json Package
- uses: actions/delete-package-versions@v4
- with:
- package-name: Neo.Json
- package-type: nuget
- min-versions-to-keep: 3
- delete-only-pre-release-versions: "true"
+ package-name: Neo
+ package-type: docker
+ min-versions-to-keep: 1
token: "${{ secrets.GITHUB_TOKEN }}"
- - name: Delete Neo.IO Package
- uses: actions/delete-package-versions@v4
- with:
- package-name: Neo.IO
- package-type: nuget
- min-versions-to-keep: 3
- delete-only-pre-release-versions: "true"
- token: "${{ secrets.GITHUB_TOKEN }}"
+ delete-git-nuget-pkgs:
+ name: Delete Old Nuget Packages
+ strategy:
+ matrix:
+ pkgs:
+ - "Neo.Plugins.StatesDumper"
+ - "Neo.Plugins.StateService"
+ - "Neo.Plugins.Storage.LevelDBStore"
+ - "Neo.Plugins.Storage.RocksDBStore"
+ - "Neo.Plugins.StorageDumper"
+ - "Neo.Plugins.TokensTracker"
+ - "Neo.Wallets.SQLite"
+ - "Neo.Consensus.DBFT"
+ - "Neo.ConsoleService"
+ - "Neo.Cryptography.MPT"
+ - "Neo.Extensions"
+ - "Neo.Network.RPC.RpcClient"
+ - "Neo.Plugins.ApplicationLogs"
+ - "Neo.Plugins.OracleService"
+ - "Neo.Plugins.RpcServer"
+ - "Neo.Cryptography.BLS12_381"
+ - "Neo.VM"
+ - "Neo.Json"
+ - "Neo.IO"
+ - "Neo"
+ runs-on: ubuntu-latest
- - name: Delete Neo Package
- uses: actions/delete-package-versions@v4
- with:
- package-name: Neo
- package-type: nuget
- min-versions-to-keep: 3
- delete-only-pre-release-versions: "true"
- token: "${{ secrets.GITHUB_TOKEN }}"
- - name: Delete Neo.ConsoleService Package
- uses: actions/delete-package-versions@v4
- with:
- package-name: Neo.ConsoleService
- package-type: nuget
- min-versions-to-keep: 3
- delete-only-pre-release-versions: "true"
- token: "${{ secrets.GITHUB_TOKEN }}"
- - name: Delete Neo.Extensions Package
+ steps:
+ - name: Delete ${{ matrix.pkgs }} Package
uses: actions/delete-package-versions@v4
+ continue-on-error: true
with:
- package-name: Neo.Extensions
+ package-name: ${{ matrix.pkgs }}
package-type: nuget
min-versions-to-keep: 3
delete-only-pre-release-versions: "true"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000000..bcfddc1185
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,348 @@
+name: Release (neo-cli)
+
+# Trigger the workflow on a release event when a new release is published
+on:
+ release:
+ types: [published]
+
+# Define environment variables
+env:
+ DOTNET_VERSION: 8.0.x
+ CONFIGURATION: Release
+ DIST_PATH: /tmp/dist
+ OUTPUT_PATH: /tmp/out
+
+jobs:
+ build-leveldb:
+ name: Build leveldb win-${{ matrix.arch }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [windows-latest]
+ arch: [x64, arm64]
+
+ steps:
+ # Step to lookup cache for the LevelDB build distribution
+ - name: Lookup Cache Distribution
+ id: cache-leveldb
+ uses: actions/cache@v4
+ with:
+ path: ./leveldb/build/Release/*
+ key: leveldb-${{ matrix.os }}-${{ matrix.arch }}
+ enableCrossOsArchive: true
+ lookup-only: true
+
+ # Conditionally checkout LevelDB repository if cache is not found
+ - if: ${{ steps.cache-leveldb.outputs.cache-hit != 'true' }}
+ name: Checkout Repository Code (leveldb)
+ uses: actions/checkout@v4
+ with:
+ repository: google/leveldb
+ path: leveldb
+ submodules: true
+ fetch-depth: 0
+
+ # Conditionally setup MSBuild if cache is not found
+ - if: ${{ matrix.os == 'windows-latest' && steps.cache-leveldb.outputs.cache-hit != 'true' }}
+ name: Setup MSBuild
+ uses: microsoft/setup-msbuild@v2
+
+ # Conditionally setup LevelDB build directory if cache is not found
+ - if: ${{ steps.cache-leveldb.outputs.cache-hit != 'true' }}
+ name: Setup LevelDb
+ working-directory: ./leveldb
+ run: mkdir -p ./build/Release
+
+ # Conditionally create build files for LevelDB if cache is not found
+ - if: ${{ steps.cache-leveldb.outputs.cache-hit != 'true' }}
+ name: Create Build Files (win-${{ matrix.arch }})
+ working-directory: ./leveldb/build
+ run: cmake -DBUILD_SHARED_LIBS=ON -A ${{ matrix.arch }} ..
+
+ # Conditionally build LevelDB using MSBuild if cache is not found
+ - if: ${{ matrix.os == 'windows-latest' && steps.cache-leveldb.outputs.cache-hit != 'true' }}
+ name: Build (MSBuild)
+ working-directory: ./leveldb/build
+ run: msbuild ./leveldb.sln /p:Configuration=Release
+
+ # Conditionally cache the LevelDB distribution if it was built
+ - if: ${{ steps.cache-leveldb.outputs.cache-hit != 'true' }}
+ name: Cache Distribution
+ uses: actions/cache/save@v4
+ with:
+ path: ./leveldb/build/Release/*
+ key: leveldb-${{ matrix.os }}-${{ matrix.arch }}
+ enableCrossOsArchive: true
+
+ build-neo-cli:
+ needs: [build-leveldb]
+ name: ${{ matrix.runtime }}
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ runtime: [linux-x64, linux-arm64, win-x64, win-arm64, osx-x64, osx-arm64]
+
+ steps:
+ # Step to set the application version from the release tag
+ - name: Set Application Version (Environment Variable)
+ run: |
+ APP_VERSION=$(echo '${{ github.event.release.tag_name }}' | cut -d 'v' -f 2)
+ echo "APP_VERSION=$APP_VERSION" >> $GITHUB_ENV
+
+ # Checkout the neo-cli repository code
+ - name: Checkout Repository Code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ # Setup .NET environment
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: ${{ env.DOTNET_VERSION }}
+
+ # Publish the neo-cli project
+ - name: .NET Publish (neo-cli)
+ run: |
+ dotnet publish ./src/Neo.CLI \
+ --version-suffix ${{ matrix.runtime }} \
+ --framework net8.0 \
+ --configuration ${{ env.CONFIGURATION }} \
+ --runtime ${{ matrix.runtime }} \
+ --self-contained true \
+ --output ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }} \
+ --verbosity normal \
+ -p:VersionPrefix=${{ env.APP_VERSION }} \
+ -p:RuntimeIdentifier=${{ matrix.runtime }} \
+ -p:SelfContained=true \
+ -p:IncludeNativeLibrariesForSelfExtract=false \
+ -p:PublishTrimmed=false \
+ -p:PublishSingleFile=true \
+ -p:PublishReadyToRun=true \
+ -p:EnableCompressionInSingleFile=true \
+ -p:DebugType=embedded \
+ -p:ServerGarbageCollection=true
+
+ # Build the LevelDBStore plugin
+ - name: .NET Build (LevelDBStore)
+ run: |
+ dotnet build ./src/Plugins/LevelDBStore \
+ --version-suffix ${{ matrix.runtime }} \
+ --framework net8.0 \
+ --configuration ${{ env.CONFIGURATION }} \
+ --output ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}/Plugins/LevelDBStore \
+ --verbosity normal \
+ -p:VersionPrefix=${{ env.APP_VERSION }}
+
+ # Remove unnecessary files from the LevelDBStore plugin output
+ - name: Remove files (junk)
+ working-directory: ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}/Plugins/LevelDBStore
+ run: |
+ rm -v Neo*
+ rm -v *.pdb
+ rm -v *.xml
+
+ # Remove XML comment files from the neo-cli output
+ - name: Remove Xml Comment Files
+ working-directory: ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}
+ run: rm -v *.xml
+
+ # Get cached LevelDB distribution for Windows x64 if applicable
+ - if: ${{ startsWith(matrix.runtime, 'win-x64') }}
+ name: Get Distribution Caches (win-x64)
+ uses: actions/cache@v4
+ with:
+ path: ./leveldb/build/Release/*
+ key: leveldb-windows-latest-x64
+ enableCrossOsArchive: true
+ fail-on-cache-miss: true
+
+ # Get cached LevelDB distribution for Windows arm64 if applicable
+ - if: ${{ startsWith(matrix.runtime, 'win-arm64') }}
+ name: Get Distribution Caches (win-arm64)
+ uses: actions/cache@v4
+ with:
+ path: ./leveldb/build/Release/*
+ key: leveldb-windows-latest-arm64
+ enableCrossOsArchive: true
+ fail-on-cache-miss: true
+
+ # Copy LevelDB files to the output directory for Windows
+ - if: ${{ startsWith(matrix.runtime, 'win') }}
+ name: Copy Files (leveldb) (win)
+ run: cp -v ./leveldb/build/Release/leveldb.dll ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}/libleveldb.dll
+
+ # Create the distribution directory
+ - name: Create Distribution Directory
+ run: mkdir -p ${{ env.DIST_PATH }}
+
+ # Create a tarball file for Linux distributions
+ - name: Create Tarball File (linux)
+ if: ${{ startsWith(matrix.runtime, 'linux') }}
+ working-directory: ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}
+ run: tar -czvf ${{ env.DIST_PATH }}/neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}.tar.gz .
+
+ # Create a tarball file for macOS distributions
+ - name: Cache Distribution
+ uses: actions/cache/save@v4
+ with:
+ path: ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}/*
+ key: neo-${{ matrix.runtime }}
+ enableCrossOsArchive: true
+
+ # Create a zip file for Windows distributions
+ - name: Create Zip File (win)
+ if: ${{ startsWith(matrix.runtime, 'win') }}
+ working-directory: ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}
+ run: zip ${{ env.DIST_PATH }}/neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}.zip -r *
+
+ # Create checksum files for Linux distributions
+ - name: Create Checksum Files (linux)
+ if: ${{ startsWith(matrix.runtime, 'linux') }}
+ working-directory: ${{ env.DIST_PATH }}
+ env:
+ FILENAME: neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}
+ run: |
+ sha256sum ${{ env.FILENAME }}.tar.gz > ${{ env.FILENAME }}.sha256
+
+ # Create checksum files for Windows distributions
+ - name: Create Checksum Files (win)
+ if: ${{ startsWith(matrix.runtime, 'win') }}
+ working-directory: ${{ env.DIST_PATH }}
+ env:
+ FILENAME: neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}
+ run: |
+ sha256sum ${{ env.FILENAME }}.zip > ${{ env.FILENAME }}.sha256
+
+ # List the contents of the distribution and output directories
+ - name: Output/Distribution Directory Contents
+ run: |
+ ls -la ${{ env.DIST_PATH }}
+ ls -la ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}
+
+ # Upload tarball files for Linux distributions
+ - name: Upload Tarball File (linux)
+ if: ${{ startsWith(matrix.runtime, 'linux') }}
+ uses: actions/upload-release-asset@latest
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ env.DIST_PATH }}/neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}.tar.gz
+ asset_name: neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}.tar.gz
+ asset_content_type: application/x-gtar
+
+ # Upload zip files for Windows distributions
+ - name: Upload Zip File (win)
+ if: ${{ startsWith(matrix.runtime, 'win') }}
+ uses: actions/upload-release-asset@latest
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ env.DIST_PATH }}/neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}.zip
+ asset_name: neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}.zip
+ asset_content_type: application/zip
+
+ # Upload checksum files for all distributions
+ - name: Upload Checksum File (all)
+ if: ${{ startsWith(matrix.runtime, 'osx') == false }}
+ uses: actions/upload-release-asset@latest
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ env.DIST_PATH }}/neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}.sha256
+ asset_name: neo-cli.v${{ env.APP_VERSION }}-${{ matrix.runtime }}.sha256
+ asset_content_type: text/plain
+
+ code-sign:
+ needs: [build-neo-cli]
+ name: CodeSign & Publish (neo-cli) ${{ matrix.arch }}
+ runs-on: macos-latest
+ strategy:
+ matrix:
+ arch: [x64, arm64]
+
+ steps:
+ # Step to set the application version from the release tag
+ - name: Set Application Version (Environment Variable)
+ run: |
+ APP_VERSION=$(echo '${{ github.event.release.tag_name }}' | cut -d 'v' -f 2)
+ echo "APP_VERSION=$APP_VERSION" >> $GITHUB_ENV
+
+ - name: Get Distribution Caches (win-${{ matrix.arch}})
+ uses: actions/cache@v4
+ with:
+ path: ${{ env.OUTPUT_PATH }}/osx-${{ matrix.arch }}/*
+ key: neo-osx-${{ matrix.arch }}
+ enableCrossOsArchive: true
+ fail-on-cache-miss: true
+
+ - name: Sign (neo-cli)
+ working-directory: ${{ env.OUTPUT_PATH }}/osx-${{ matrix.arch }}
+ run: codesign --force --deep -s - neo-cli
+
+ # Create the distribution directory
+ - name: Create Distribution Directory
+ run: mkdir -p ${{ env.DIST_PATH }}
+
+ # Create a tarball file for macOS distributions
+ - name: Create Tarball File (osx)
+ working-directory: ${{ env.OUTPUT_PATH }}/osx-${{ matrix.arch }}
+ run: tar -cJf ${{ env.DIST_PATH }}/neo-cli.v${{ env.APP_VERSION }}-osx-${{ matrix.arch }}.tar.xz .
+
+ # Create checksum files for macOS distributions
+ - name: Create Checksum Files (osx)
+ working-directory: ${{ env.DIST_PATH }}
+ env:
+ FILENAME: neo-cli.v${{ env.APP_VERSION }}-osx-${{ matrix.arch }}
+ run: |
+ shasum -a 256 ${{ env.FILENAME }}.tar.xz > ${{ env.FILENAME }}.sha256
+
+ # Upload tarball files for macOS distributions
+ - name: Upload Tarball File (osx)
+ uses: actions/upload-release-asset@latest
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ env.DIST_PATH }}/neo-cli.v${{ env.APP_VERSION }}-osx-${{ matrix.arch }}.tar.xz
+ asset_name: neo-cli.v${{ env.APP_VERSION }}-osx-${{ matrix.arch }}.tar.xz
+ asset_content_type: application/x-gtar
+
+ # Upload checksum files for all distributions
+ - name: Upload Checksum File (all)
+ uses: actions/upload-release-asset@latest
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ env.DIST_PATH }}/neo-cli.v${{ env.APP_VERSION }}-osx-${{ matrix.arch }}.sha256
+ asset_name: neo-cli.v${{ env.APP_VERSION }}-osx-${{ matrix.arch }}.sha256
+ asset_content_type: text/plain
+
+ cleanup:
+ needs: [build-neo-cli, code-sign]
+ runs-on: ubuntu-latest
+ steps:
+ # Cleanup step to delete old caches
+ - name: Cleanup
+ run: |
+ gh extension install actions/gh-actions-cache
+
+ echo "Fetching list of cache key"
+ cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
+
+ ## Setting this to not fail the workflow while deleting cache keys.
+ set +e
+ echo "Deleting caches..."
+ for cacheKey in $cacheKeysForPR
+ do
+ gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
+ done
+ echo "Done"
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REPO: ${{ github.repository }}
+ BRANCH: ${{ github.ref }}
diff --git a/.neo/docker/neo-cli/Dockerfile b/.neo/docker/neo-cli/Dockerfile
new file mode 100644
index 0000000000..afdd97e597
--- /dev/null
+++ b/.neo/docker/neo-cli/Dockerfile
@@ -0,0 +1,8 @@
+FROM debian:stable-slim
+
+# Install the apt-get packages
+RUN apt-get update
+RUN apt-get install -y libicu-dev libleveldb-dev screen
+
+COPY ./dist /opt/neo-cli
+RUN ln -s /opt/neo-cli/neo-cli /usr/bin
diff --git a/README.md b/README.md
index 407bd6a837..cc1c9b384a 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
A modern distributed network for the Smart Economy.
- Documentation »
+ Documentation »
Neo
diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.cs b/benchmarks/Neo.Benchmarks/Benchmarks.POC.cs
similarity index 87%
rename from benchmarks/Neo.Benchmarks/Benchmarks.cs
rename to benchmarks/Neo.Benchmarks/Benchmarks.POC.cs
index 081f806622..f073c543a9 100644
--- a/benchmarks/Neo.Benchmarks/Benchmarks.cs
+++ b/benchmarks/Neo.Benchmarks/Benchmarks.POC.cs
@@ -1,6 +1,6 @@
// Copyright (C) 2015-2024 The Neo Project.
//
-// Benchmarks.cs file belongs to the neo project and is free
+// Benchmarks.POC.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
@@ -9,19 +9,21 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.
+using BenchmarkDotNet.Attributes;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract;
using Neo.VM;
using System.Diagnostics;
-namespace Neo;
+namespace Neo.Benchmark;
-static class Benchmarks
+public class Benchmarks_PoCs
{
private static readonly ProtocolSettings protocol = ProtocolSettings.Load("config.json");
private static readonly NeoSystem system = new(protocol, (string)null);
- public static void NeoIssue2725()
+ [Benchmark]
+ public void NeoIssue2725()
{
// https://github.com/neo-project/neo/issues/2725
// L00: INITSSLOT 1
@@ -67,13 +69,10 @@ private static void Run(string name, string poc)
Script = Convert.FromBase64String(poc),
Witnesses = Array.Empty()
};
- using var snapshot = system.GetSnapshot();
+ using var snapshot = system.GetSnapshotCache();
using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, system.GenesisBlock, protocol, tx.SystemFee);
engine.LoadScript(tx.Script);
- Stopwatch stopwatch = Stopwatch.StartNew();
engine.Execute();
- stopwatch.Stop();
Debug.Assert(engine.State == VMState.FAULT);
- Console.WriteLine($"Benchmark: {name},\tTime: {stopwatch.Elapsed}");
}
}
diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs
new file mode 100644
index 0000000000..37fa701ff9
--- /dev/null
+++ b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs
@@ -0,0 +1,156 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Benchmarks.UInt160.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using BenchmarkDotNet.Attributes;
+
+namespace Neo.Benchmark;
+
+public class Benchmarks_UInt160
+{
+ static readonly OldUInt160 s_oldUInt160 = new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
+ static readonly UInt160 s_newUInt160 = new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
+
+ [Benchmark]
+ public void TestOldUInt160Gernerator1()
+ {
+ _ = new OldUInt160();
+ }
+
+ [Benchmark]
+ public void TestOldUInt160Gernerator2()
+ {
+ _ = new OldUInt160(new byte[20]);
+ }
+
+ [Benchmark]
+ public void TestOldUInt160CompareTo()
+ {
+ OldUInt160.Zero.CompareTo(OldUInt160.Zero);
+ OldUInt160.Zero.CompareTo(s_oldUInt160);
+ s_oldUInt160.CompareTo(OldUInt160.Zero);
+ }
+
+ [Benchmark]
+ public void TestOldUInt160Equals()
+ {
+ OldUInt160.Zero.Equals(OldUInt160.Zero);
+ OldUInt160.Zero.Equals(s_oldUInt160);
+ s_oldUInt160.Equals(null);
+ }
+
+ [Benchmark]
+ public void TestOldUInt160Parse()
+ {
+ _ = OldUInt160.Parse("0x0000000000000000000000000000000000000000");
+ _ = OldUInt160.Parse("0000000000000000000000000000000000000000");
+ }
+
+ [Benchmark]
+ public void TestOldUInt160TryParse()
+ {
+ OldUInt160.TryParse(null, out _);
+ OldUInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp);
+ OldUInt160.TryParse("0x1230000000000000000000000000000000000000", out temp);
+ OldUInt160.TryParse("000000000000000000000000000000000000000", out _);
+ }
+
+ [Benchmark]
+ public void TestOldUInt160OperatorLarger()
+ {
+ _ = s_oldUInt160 > OldUInt160.Zero;
+ }
+
+ [Benchmark]
+ public void TestOldUInt160OperatorLargerAndEqual()
+ {
+ _ = s_oldUInt160 >= OldUInt160.Zero;
+ }
+
+ [Benchmark]
+ public void TestOldUInt160OperatorSmaller()
+ {
+ _ = s_oldUInt160 < OldUInt160.Zero;
+ }
+
+ [Benchmark]
+ public void TestOldUInt160OperatorSmallerAndEqual()
+ {
+ _ = s_oldUInt160 <= OldUInt160.Zero;
+ }
+
+ [Benchmark]
+ public void TestGernerator1()
+ {
+ _ = new UInt160();
+ }
+
+ [Benchmark]
+ public void TestGernerator2()
+ {
+ _ = new UInt160(new byte[20]);
+ }
+
+ [Benchmark]
+ public void TestCompareTo()
+ {
+ UInt160.Zero.CompareTo(UInt160.Zero);
+ UInt160.Zero.CompareTo(s_newUInt160);
+ s_newUInt160.CompareTo(UInt160.Zero);
+ }
+
+ [Benchmark]
+ public void TestEquals()
+ {
+ UInt160.Zero.Equals(UInt160.Zero);
+ UInt160.Zero.Equals(s_newUInt160);
+ s_newUInt160.Equals(null);
+ }
+
+ [Benchmark]
+ public void TestParse()
+ {
+ _ = UInt160.Parse("0x0000000000000000000000000000000000000000");
+ _ = UInt160.Parse("0000000000000000000000000000000000000000");
+ }
+
+ [Benchmark]
+ public void TestTryParse()
+ {
+ UInt160.TryParse(null, out _);
+ UInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp);
+ UInt160.TryParse("0x1230000000000000000000000000000000000000", out temp);
+ UInt160.TryParse("000000000000000000000000000000000000000", out _);
+ }
+
+ [Benchmark]
+ public void TestOperatorLarger()
+ {
+ _ = s_newUInt160 > UInt160.Zero;
+ }
+
+ [Benchmark]
+ public void TestOperatorLargerAndEqual()
+ {
+ _ = s_newUInt160 >= UInt160.Zero;
+ }
+
+ [Benchmark]
+ public void TestOperatorSmaller()
+ {
+ _ = s_newUInt160 < UInt160.Zero;
+ }
+
+ [Benchmark]
+ public void TestOperatorSmallerAndEqual()
+ {
+ _ = s_newUInt160 <= UInt160.Zero;
+ }
+}
diff --git a/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj
index 22b90aeb03..a59fc6e728 100644
--- a/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj
+++ b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj
@@ -5,10 +5,12 @@
net8.0
Neo
enable
+ true
+
diff --git a/benchmarks/Neo.Benchmarks/OldUInt160.cs b/benchmarks/Neo.Benchmarks/OldUInt160.cs
new file mode 100644
index 0000000000..965ac985ea
--- /dev/null
+++ b/benchmarks/Neo.Benchmarks/OldUInt160.cs
@@ -0,0 +1,184 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// OldUInt160.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.Extensions;
+using Neo.IO;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace Neo
+{
+ ///
+ /// Represents a 160-bit unsigned integer.
+ ///
+ [StructLayout(LayoutKind.Explicit, Size = 20)]
+ public class OldUInt160 : IComparable, IEquatable, ISerializable
+ {
+ ///
+ /// The length of values.
+ ///
+ public const int Length = 20;
+
+ ///
+ /// Represents 0.
+ ///
+ public static readonly OldUInt160 Zero = new();
+
+ [FieldOffset(0)] private ulong value1;
+ [FieldOffset(8)] private ulong value2;
+ [FieldOffset(16)] private uint value3;
+
+ public int Size => Length;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public OldUInt160()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value of the .
+ public unsafe OldUInt160(ReadOnlySpan value)
+ {
+ if (value.Length != Length) throw new FormatException();
+ fixed (ulong* p = &value1)
+ {
+ Span dst = new(p, Length);
+ value[..Length].CopyTo(dst);
+ }
+ }
+
+ public int CompareTo(OldUInt160 other)
+ {
+ int result = value3.CompareTo(other.value3);
+ if (result != 0) return result;
+ result = value2.CompareTo(other.value2);
+ if (result != 0) return result;
+ return value1.CompareTo(other.value1);
+ }
+
+ public void Deserialize(ref MemoryReader reader)
+ {
+ value1 = reader.ReadUInt64();
+ value2 = reader.ReadUInt64();
+ value3 = reader.ReadUInt32();
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(obj, this)) return true;
+ return Equals(obj as OldUInt160);
+ }
+
+ public bool Equals(OldUInt160 other)
+ {
+ if (other is null) return false;
+ return value1 == other.value1
+ && value2 == other.value2
+ && value3 == other.value3;
+ }
+
+ public override int GetHashCode()
+ {
+ return (int)value1;
+ }
+
+ ///
+ /// Parses an from the specified .
+ ///
+ /// An represented by a .
+ /// The parsed .
+ /// is not in the correct format.
+ public static OldUInt160 Parse(string value)
+ {
+ if (!TryParse(value, out var result)) throw new FormatException();
+ return result;
+ }
+
+ public void Serialize(BinaryWriter writer)
+ {
+ writer.Write(value1);
+ writer.Write(value2);
+ writer.Write(value3);
+ }
+
+ public override string ToString()
+ {
+ return "0x" + this.ToArray().ToHexString(reverse: true);
+ }
+
+ ///
+ /// Parses an from the specified .
+ ///
+ /// An represented by a .
+ /// The parsed .
+ /// if an is successfully parsed; otherwise, .
+ public static bool TryParse(string s, out OldUInt160 result)
+ {
+ if (s == null)
+ {
+ result = null;
+ return false;
+ }
+ if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
+ s = s[2..];
+ if (s.Length != Length * 2)
+ {
+ result = null;
+ return false;
+ }
+ byte[] data = new byte[Length];
+ for (int i = 0; i < Length; i++)
+ if (!byte.TryParse(s.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[Length - i - 1]))
+ {
+ result = null;
+ return false;
+ }
+ result = new OldUInt160(data);
+ return true;
+ }
+
+ public static bool operator ==(OldUInt160 left, OldUInt160 right)
+ {
+ if (ReferenceEquals(left, right)) return true;
+ if (left is null || right is null) return false;
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(OldUInt160 left, OldUInt160 right)
+ {
+ return !(left == right);
+ }
+
+ public static bool operator >(OldUInt160 left, OldUInt160 right)
+ {
+ return left.CompareTo(right) > 0;
+ }
+
+ public static bool operator >=(OldUInt160 left, OldUInt160 right)
+ {
+ return left.CompareTo(right) >= 0;
+ }
+
+ public static bool operator <(OldUInt160 left, OldUInt160 right)
+ {
+ return left.CompareTo(right) < 0;
+ }
+
+ public static bool operator <=(OldUInt160 left, OldUInt160 right)
+ {
+ return left.CompareTo(right) <= 0;
+ }
+ }
+}
diff --git a/benchmarks/Neo.Benchmarks/Program.cs b/benchmarks/Neo.Benchmarks/Program.cs
index 9d4125bb9f..c44b76f839 100644
--- a/benchmarks/Neo.Benchmarks/Program.cs
+++ b/benchmarks/Neo.Benchmarks/Program.cs
@@ -9,10 +9,8 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.
-using Neo;
-using System.Reflection;
+using BenchmarkDotNet.Running;
+using Neo.Benchmark;
-foreach (var method in typeof(Benchmarks).GetMethods(BindingFlags.Public | BindingFlags.Static))
-{
- method.CreateDelegate().Invoke();
-}
+// BenchmarkRunner.Run();
+BenchmarkRunner.Run();
diff --git a/benchmarks/Neo.VM.Benchmarks/Benchmarks.POC.cs b/benchmarks/Neo.VM.Benchmarks/Benchmarks.POC.cs
new file mode 100644
index 0000000000..2eede59fc3
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/Benchmarks.POC.cs
@@ -0,0 +1,397 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Benchmarks.POC.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using BenchmarkDotNet.Attributes;
+using System.Diagnostics;
+
+namespace Neo.VM.Benchmark
+{
+ public class Benchmarks_PoCs
+ {
+ [Benchmark]
+ public void NeoIssue2528()
+ {
+ // https://github.com/neo-project/neo/issues/2528
+ // L01: INITSLOT 1, 0
+ // L02: NEWARRAY0
+ // L03: DUP
+ // L04: DUP
+ // L05: PUSHINT16 2043
+ // L06: STLOC 0
+ // L07: PUSH1
+ // L08: PACK
+ // L09: LDLOC 0
+ // L10: DEC
+ // L11: STLOC 0
+ // L12: LDLOC 0
+ // L13: JMPIF_L L07
+ // L14: PUSH1
+ // L15: PACK
+ // L16: APPEND
+ // L17: PUSHINT32 38000
+ // L18: STLOC 0
+ // L19: PUSH0
+ // L20: PICKITEM
+ // L21: LDLOC 0
+ // L22: DEC
+ // L23: STLOC 0
+ // L24: LDLOC 0
+ // L25: JMPIF_L L19
+ // L26: DROP
+ Run("VwEAwkpKAfsHdwARwG8AnXcAbwAl9////xHAzwJwlAAAdwAQzm8AnXcAbwAl9////0U=");
+ }
+
+ [Benchmark]
+ public void NeoVMIssue418()
+ {
+ // https://github.com/neo-project/neo-vm/issues/418
+ // L00: NEWARRAY0
+ // L01: PUSH0
+ // L02: PICK
+ // L03: PUSH1
+ // L04: PACK
+ // L05: PUSH1
+ // L06: PICK
+ // L07: PUSH1
+ // L08: PACK
+ // L09: INITSSLOT 1
+ // L10: PUSHINT16 510
+ // L11: DEC
+ // L12: STSFLD0
+ // L13: PUSH1
+ // L14: PICK
+ // L15: PUSH1
+ // L16: PICK
+ // L17: PUSH2
+ // L18: PACK
+ // L19: REVERSE3
+ // L20: PUSH2
+ // L21: PACK
+ // L22: LDSFLD0
+ // L23: DUP
+ // L24: JMPIF L11
+ // L25: DROP
+ // L26: ROT
+ // L27: DROP
+ Run("whBNEcARTRHAVgEB/gGdYBFNEU0SwFMSwFhKJPNFUUU=");
+ }
+
+ [Benchmark]
+ public void NeoIssue2723()
+ {
+ // L00: INITSSLOT 1
+ // L01: PUSHINT32 130000
+ // L02: STSFLD 0
+ // L03: PUSHINT32 1048576
+ // L04: NEWBUFFER
+ // L05: DROP
+ // L06: LDSFLD 0
+ // L07: DEC
+ // L08: DUP
+ // L09: STSFLD 0
+ // L10: JMPIF L03
+ Run("VgEC0PsBAGcAAgAAEACIRV8AnUpnACTz");
+ }
+
+ // Below are PoCs from issue https://github.com/neo-project/neo/issues/2723 by @dusmart
+ [Benchmark]
+ public void PoC_NewBuffer()
+ {
+ // INITSLOT 0100
+ // PUSHINT32 23000000
+ // STLOC 00
+ // PUSHINT32 1048576
+ // NEWBUFFER
+ // DROP
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f2ffffff
+ // CLEAR
+ // RET
+ Run("VwEAAsDzXgF3AAIAABAAiEVvAJ13AG8AJfL///9JQA==");
+ }
+
+ [Benchmark]
+ public void PoC_Cat()
+ {
+ // INITSLOT 0100
+ // PUSHINT32 1048575
+ // NEWBUFFER
+ // PUSH1
+ // NEWBUFFER
+ // PUSHINT32 133333337
+ // STLOC 00
+ // OVER
+ // OVER
+ // CAT
+ // DROP
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f5ffffff
+ // CLEAR
+ // RET
+ Run("VwEAAv//DwCIEYgCWYHyB3cAS0uLRW8AnXcAbwAl9f///0lA");
+ }
+
+ [Benchmark]
+ public void PoC_Left()
+ {
+ // INITSLOT 0100
+ // PUSHINT32 1048576
+ // NEWBUFFER
+ // PUSHINT32 133333337
+ // STLOC 00
+ // DUP
+ // PUSHINT32 1048576
+ // LEFT
+ // DROP
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f1ffffff
+ // CLEAR
+ // RET
+ Run("VwEAAgAAEACIAlmB8gd3AEoCAAAQAI1FbwCddwBvACXx////SUA=");
+ }
+
+ [Benchmark]
+ public void PoC_Right()
+ {
+ // INITSLOT 0100
+ // PUSHINT32 1048576
+ // NEWBUFFER
+ // PUSHINT32 133333337
+ // STLOC 00
+ // DUP
+ // PUSHINT32 1048576
+ // RIGHT
+ // DROP
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f1ffffff
+ // CLEAR
+ // RET
+ Run("VwEAAgAAEACIAlmB8gd3AEoCAAAQAI5FbwCddwBvACXx////SUA=");
+ }
+
+ [Benchmark]
+ public void PoC_ReverseN()
+ {
+ // INITSLOT 0100
+ // PUSHINT16 2040
+ // STLOC 00
+ // PUSHDATA1 aaabbbbbbbbbcccccccdddddddeeeeeeefffffff
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L cfffffff
+ // PUSHINT32 23000000
+ // STLOC 00
+ // PUSHINT16 2040
+ // REVERSEN
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f5ffffff
+ // CLEAR
+ // RET
+ Run("VwEAAfgHdwAMKGFhYWJiYmJiYmJiYmNjY2NjY2NkZGRkZGRkZWVlZWVlZWZmZmZmZmZvAJ13AG8AJc////8CwPNeAXcAAfgHVW8AnXcAbwAl9f///0lA");
+ }
+
+ [Benchmark]
+ public void PoC_Substr()
+ {
+ // INITSLOT 0100
+ // PUSHINT32 1048576
+ // NEWBUFFER
+ // PUSHINT32 133333337
+ // STLOC 00
+ // DUP
+ // PUSH0
+ // PUSHINT32 1048576
+ // SUBSTR
+ // DROP
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f0ffffff
+ // CLEAR
+ // RET
+ Run("VwEAAgAAEACIAlmB8gd3AEoQAgAAEACMRW8AnXcAbwAl8P///0lA");
+ }
+
+ [Benchmark]
+ public void PoC_NewArray()
+ {
+ // INITSLOT 0100
+ // PUSHINT32 1333333337
+ // STLOC 00
+ // PUSHINT16 2040
+ // NEWARRAY
+ // DROP
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f4ffffff
+ // RET
+ Run("VwEAAlkNeU93AAH4B8NFbwCddwBvACX0////QA==");
+ }
+
+ [Benchmark]
+ public void PoC_NewStruct()
+ {
+ // INITSLOT 0100
+ // PUSHINT32 1333333337
+ // STLOC 00
+ // PUSHINT16 2040
+ // NEWSTRUCT
+ // DROP
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f4ffffff
+ // RET
+ Run("VwEAAlkNeU93AAH4B8ZFbwCddwBvACX0////QA==");
+ }
+
+ [Benchmark]
+ public void PoC_Roll()
+ {
+ // INITSLOT 0100
+ // PUSHINT16 2040
+ // STLOC 00
+ // PUSHDATA1 aaabbbbbbbbbcccccccdddddddeeeeeeefffffff
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L cfffffff
+ // PUSHINT32 23000000
+ // STLOC 00
+ // PUSHINT16 2039
+ // ROLL
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f5ffffff
+ // CLEAR
+ // RET
+ Run("VwEAAfgHdwAMKGFhYWJiYmJiYmJiYmNjY2NjY2NkZGRkZGRkZWVlZWVlZWZmZmZmZmZvAJ13AG8AJc////8CwPNeAXcAAfcHUm8AnXcAbwAl9f///0lA");
+ }
+
+ [Benchmark]
+ public void PoC_XDrop()
+ {
+ // INITSLOT 0100
+ // PUSHINT16 2040
+ // STLOC 00
+ // PUSHDATA1 aaabbbbbbbbbcccccccdddddddeeeeeeefffffff
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L cfffffff
+ // PUSHINT32 23000000
+ // STLOC 00
+ // PUSHINT16 2039
+ // XDROP
+ // DUP
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f4ffffff
+ // CLEAR
+ // RET
+ Run("VwEAAfgHdwAMKGFhYWJiYmJiYmJiYmNjY2NjY2NkZGRkZGRkZWVlZWVlZWZmZmZmZmZvAJ13AG8AJc////8CwPNeAXcAAfcHSEpvAJ13AG8AJfT///9JQA==");
+ }
+
+ [Benchmark]
+ public void PoC_MemCpy()
+ {
+ // INITSLOT 0100
+ // PUSHINT32 1048576
+ // NEWBUFFER
+ // PUSHINT32 1048576
+ // NEWBUFFER
+ // PUSHINT32 133333337
+ // STLOC 00
+ // OVER
+ // PUSH0
+ // PUSH2
+ // PICK
+ // PUSH0
+ // PUSHINT32 1048576
+ // MEMCPY
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L eeffffff
+ // CLEAR
+ // RET
+ Run("VwEAAgAAEACIAgAAEACIAlmB8gd3AEsQEk0QAgAAEACJbwCddwBvACXu////SUA=");
+ }
+
+ [Benchmark]
+ public void PoC_Unpack()
+ {
+ // INITSLOT 0200
+ // PUSHINT16 1010
+ // NEWARRAY
+ // STLOC 01
+ // PUSHINT32 1333333337
+ // STLOC 00
+ // LDLOC 01
+ // UNPACK
+ // CLEAR
+ // LDLOC 00
+ // DEC
+ // STLOC 00
+ // LDLOC 00
+ // JMPIF_L f5ffffff
+ // RET
+ Run("VwIAAfIDw3cBAlkNeU93AG8BwUlvAJ13AG8AJfX///9A");
+ }
+
+ [Benchmark]
+ public void PoC_GetScriptContainer()
+ {
+ // SYSCALL System.Runtime.GetScriptContainer
+ // DROP
+ // JMP fa
+ Run("QS1RCDBFIvo=");
+ }
+
+ private static void Run(string poc)
+ {
+ byte[] script = Convert.FromBase64String(poc);
+ using ExecutionEngine engine = new();
+ engine.LoadScript(script);
+ engine.Execute();
+
+ Debug.Assert(engine.State == VMState.HALT);
+ }
+ }
+}
diff --git a/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs b/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs
deleted file mode 100644
index 6eab691a7d..0000000000
--- a/benchmarks/Neo.VM.Benchmarks/Benchmarks.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) 2015-2024 The Neo Project.
-//
-// Benchmarks.cs file belongs to the neo project and is free
-// software distributed under the MIT software license, see the
-// accompanying file LICENSE in the main directory of the
-// repository or http://www.opensource.org/licenses/mit-license.php
-// for more details.
-//
-// Redistribution and use in source and binary forms with or without
-// modifications are permitted.
-
-using System.Diagnostics;
-
-namespace Neo.VM
-{
- public static class Benchmarks
- {
- public static void NeoIssue2528()
- {
- // https://github.com/neo-project/neo/issues/2528
- // L01: INITSLOT 1, 0
- // L02: NEWARRAY0
- // L03: DUP
- // L04: DUP
- // L05: PUSHINT16 2043
- // L06: STLOC 0
- // L07: PUSH1
- // L08: PACK
- // L09: LDLOC 0
- // L10: DEC
- // L11: STLOC 0
- // L12: LDLOC 0
- // L13: JMPIF_L L07
- // L14: PUSH1
- // L15: PACK
- // L16: APPEND
- // L17: PUSHINT32 38000
- // L18: STLOC 0
- // L19: PUSH0
- // L20: PICKITEM
- // L21: LDLOC 0
- // L22: DEC
- // L23: STLOC 0
- // L24: LDLOC 0
- // L25: JMPIF_L L19
- // L26: DROP
- Run(nameof(NeoIssue2528), "VwEAwkpKAfsHdwARwG8AnXcAbwAl9////xHAzwJwlAAAdwAQzm8AnXcAbwAl9////0U=");
- }
-
- public static void NeoVMIssue418()
- {
- // https://github.com/neo-project/neo-vm/issues/418
- // L00: NEWARRAY0
- // L01: PUSH0
- // L02: PICK
- // L03: PUSH1
- // L04: PACK
- // L05: PUSH1
- // L06: PICK
- // L07: PUSH1
- // L08: PACK
- // L09: INITSSLOT 1
- // L10: PUSHINT16 510
- // L11: DEC
- // L12: STSFLD0
- // L13: PUSH1
- // L14: PICK
- // L15: PUSH1
- // L16: PICK
- // L17: PUSH2
- // L18: PACK
- // L19: REVERSE3
- // L20: PUSH2
- // L21: PACK
- // L22: LDSFLD0
- // L23: DUP
- // L24: JMPIF L11
- // L25: DROP
- // L26: ROT
- // L27: DROP
- Run(nameof(NeoVMIssue418), "whBNEcARTRHAVgEB/gGdYBFNEU0SwFMSwFhKJPNFUUU=");
- }
-
- public static void NeoIssue2723()
- {
- // L00: INITSSLOT 1
- // L01: PUSHINT32 130000
- // L02: STSFLD 0
- // L03: PUSHINT32 1048576
- // L04: NEWBUFFER
- // L05: DROP
- // L06: LDSFLD 0
- // L07: DEC
- // L08: DUP
- // L09: STSFLD 0
- // L10: JMPIF L03
- Run(nameof(NeoIssue2723), "VgEC0PsBAGcAAgAAEACIRV8AnUpnACTz");
- }
-
- private static void Run(string name, string poc)
- {
- byte[] script = Convert.FromBase64String(poc);
- using ExecutionEngine engine = new();
- engine.LoadScript(script);
- Stopwatch stopwatch = Stopwatch.StartNew();
- engine.Execute();
- stopwatch.Stop();
- Debug.Assert(engine.State == VMState.HALT);
- Console.WriteLine($"Benchmark: {name},\tTime: {stopwatch.Elapsed}");
- }
- }
-}
diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs
new file mode 100644
index 0000000000..f5ea579e6f
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs
@@ -0,0 +1,72 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Helper.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System.Buffers.Binary;
+
+namespace Neo.VM.Benchmark;
+
+public static class Helper
+{
+ public static void RebuildOffsets(this IReadOnlyList instructions)
+ {
+ var offset = 0;
+ foreach (var instruction in instructions)
+ {
+ instruction._offset = offset;
+ offset += instruction.Size;
+ }
+ }
+
+ public static void RebuildOperands(this IReadOnlyList instructions)
+ {
+ foreach (var instruction in instructions)
+ {
+ if (instruction._target is null) continue;
+ bool isLong;
+ if (instruction._opCode >= VM.OpCode.JMP && instruction._opCode <= VM.OpCode.CALL_L)
+ isLong = (instruction._opCode - VM.OpCode.JMP) % 2 != 0;
+ else
+ isLong = instruction._opCode == VM.OpCode.PUSHA || instruction._opCode == VM.OpCode.CALLA || instruction._opCode == VM.OpCode.TRY_L || instruction._opCode == VM.OpCode.ENDTRY_L;
+ if (instruction._opCode == VM.OpCode.TRY || instruction._opCode == VM.OpCode.TRY_L)
+ {
+ var offset1 = (instruction._target._instruction?._offset - instruction._offset) ?? 0;
+ var offset2 = (instruction._target2!._instruction?._offset - instruction._offset) ?? 0;
+ if (isLong)
+ {
+ instruction._operand = new byte[sizeof(int) + sizeof(int)];
+ BinaryPrimitives.WriteInt32LittleEndian(instruction._operand, offset1);
+ BinaryPrimitives.WriteInt32LittleEndian(instruction._operand.AsSpan(sizeof(int)), offset2);
+ }
+ else
+ {
+ instruction._operand = new byte[sizeof(sbyte) + sizeof(sbyte)];
+ var sbyte1 = checked((sbyte)offset1);
+ var sbyte2 = checked((sbyte)offset2);
+ instruction._operand[0] = unchecked((byte)sbyte1);
+ instruction._operand[1] = unchecked((byte)sbyte2);
+ }
+ }
+ else
+ {
+ int offset = instruction._target._instruction!._offset - instruction._offset;
+ if (isLong)
+ {
+ instruction._operand = BitConverter.GetBytes(offset);
+ }
+ else
+ {
+ var sbyte1 = checked((sbyte)offset);
+ instruction._operand = [unchecked((byte)sbyte1)];
+ }
+ }
+ }
+ }
+}
diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs
new file mode 100644
index 0000000000..5a30aeec10
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs
@@ -0,0 +1,58 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Instruction.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Microsoft.CodeAnalysis;
+using System.Diagnostics;
+using System.Reflection;
+
+namespace Neo.VM.Benchmark;
+
+[DebuggerDisplay("{_opCode}")]
+public class Instruction
+{
+ private static readonly int[] s_operandSizePrefixTable = new int[256];
+ private static readonly int[] s_operandSizeTable = new int[256];
+
+ public VM.OpCode _opCode;
+ public byte[]? _operand;
+ public JumpTarget? _target;
+ public JumpTarget? _target2;
+ public int _offset;
+
+ public int Size
+ {
+ get
+ {
+ int prefixSize = s_operandSizePrefixTable[(int)_opCode];
+ return prefixSize > 0
+ ? sizeof(VM.OpCode) + _operand!.Length
+ : sizeof(VM.OpCode) + s_operandSizeTable[(int)_opCode];
+ }
+ }
+
+ static Instruction()
+ {
+ foreach (var field in typeof(VM.OpCode).GetFields(BindingFlags.Public | BindingFlags.Static))
+ {
+ var attribute = field.GetCustomAttribute();
+ if (attribute is null) continue;
+ var index = (int)(VM.OpCode)field.GetValue(null)!;
+ s_operandSizePrefixTable[index] = attribute.SizePrefix;
+ s_operandSizeTable[index] = attribute.Size;
+ }
+ }
+
+ public byte[] ToArray()
+ {
+ if (_operand is null) return [(byte)_opCode];
+ return _operand.Prepend((byte)_opCode).ToArray();
+ }
+}
diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs
new file mode 100644
index 0000000000..21d1b77de2
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs
@@ -0,0 +1,242 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// InstructionBuilder.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using System.Buffers.Binary;
+using System.Numerics;
+
+namespace Neo.VM.Benchmark;
+
+internal class InstructionBuilder
+{
+ internal readonly List _instructions = new();
+
+ public InstructionBuilder() { }
+
+ internal Instruction AddInstruction(Instruction instruction)
+ {
+ _instructions.Add(instruction);
+ return instruction;
+ }
+
+ internal Instruction AddInstruction(VM.OpCode opcode)
+ {
+ return AddInstruction(new Instruction
+ {
+ _opCode = opcode
+ });
+ }
+
+ internal Instruction Jump(VM.OpCode opcode, JumpTarget target)
+ {
+ return AddInstruction(new Instruction
+ {
+ _opCode = opcode,
+ _target = target
+ });
+ }
+
+ internal void Push(bool value)
+ {
+ AddInstruction(value ? VM.OpCode.PUSHT : VM.OpCode.PUSHF);
+ }
+
+ internal Instruction Ret() => AddInstruction(VM.OpCode.RET);
+
+ internal Instruction Push(BigInteger number)
+ {
+ if (number >= -1 && number <= 16) return AddInstruction(number == -1 ? VM.OpCode.PUSHM1 : VM.OpCode.PUSH0 + (byte)(int)number);
+ Span buffer = stackalloc byte[32];
+ if (!number.TryWriteBytes(buffer, out var bytesWritten, isUnsigned: false, isBigEndian: false))
+ throw new ArgumentOutOfRangeException(nameof(number));
+ var instruction = bytesWritten switch
+ {
+ 1 => new Instruction
+ {
+ _opCode = VM.OpCode.PUSHINT8,
+ _operand = PadRight(buffer, bytesWritten, 1, number.Sign < 0).ToArray()
+ },
+ 2 => new Instruction
+ {
+ _opCode = VM.OpCode.PUSHINT16,
+ _operand = PadRight(buffer, bytesWritten, 2, number.Sign < 0).ToArray()
+ },
+ <= 4 => new Instruction
+ {
+ _opCode = VM.OpCode.PUSHINT32,
+ _operand = PadRight(buffer, bytesWritten, 4, number.Sign < 0).ToArray()
+ },
+ <= 8 => new Instruction
+ {
+ _opCode = VM.OpCode.PUSHINT64,
+ _operand = PadRight(buffer, bytesWritten, 8, number.Sign < 0).ToArray()
+ },
+ <= 16 => new Instruction
+ {
+ _opCode = VM.OpCode.PUSHINT128,
+ _operand = PadRight(buffer, bytesWritten, 16, number.Sign < 0).ToArray()
+ },
+ <= 32 => new Instruction
+ {
+ _opCode = VM.OpCode.PUSHINT256,
+ _operand = PadRight(buffer, bytesWritten, 32, number.Sign < 0).ToArray()
+ },
+ _ => throw new ArgumentOutOfRangeException($"Number too large: {bytesWritten}")
+ };
+ AddInstruction(instruction);
+ return instruction;
+ }
+
+ internal Instruction Push(string s)
+ {
+ return Push(Utility.StrictUTF8.GetBytes(s));
+ }
+
+ internal Instruction Push(byte[] data)
+ {
+ VM.OpCode opcode;
+ byte[] buffer;
+ switch (data.Length)
+ {
+ case <= byte.MaxValue:
+ opcode = VM.OpCode.PUSHDATA1;
+ buffer = new byte[sizeof(byte) + data.Length];
+ buffer[0] = (byte)data.Length;
+ Buffer.BlockCopy(data, 0, buffer, sizeof(byte), data.Length);
+ break;
+ case <= ushort.MaxValue:
+ opcode = VM.OpCode.PUSHDATA2;
+ buffer = new byte[sizeof(ushort) + data.Length];
+ BinaryPrimitives.WriteUInt16LittleEndian(buffer, (ushort)data.Length);
+ Buffer.BlockCopy(data, 0, buffer, sizeof(ushort), data.Length);
+ break;
+ default:
+ opcode = VM.OpCode.PUSHDATA4;
+ buffer = new byte[sizeof(uint) + data.Length];
+ BinaryPrimitives.WriteUInt32LittleEndian(buffer, (uint)data.Length);
+ Buffer.BlockCopy(data, 0, buffer, sizeof(uint), data.Length);
+ break;
+ }
+ return AddInstruction(new Instruction
+ {
+ _opCode = opcode,
+ _operand = buffer
+ });
+ }
+
+ internal void Push(object? obj)
+ {
+ switch (obj)
+ {
+ case bool data:
+ Push(data);
+ break;
+ case byte[] data:
+ Push(data);
+ break;
+ case string data:
+ Push(data);
+ break;
+ case BigInteger data:
+ Push(data);
+ break;
+ case char data:
+ Push((ushort)data);
+ break;
+ case sbyte data:
+ Push(data);
+ break;
+ case byte data:
+ Push(data);
+ break;
+ case short data:
+ Push(data);
+ break;
+ case ushort data:
+ Push(data);
+ break;
+ case int data:
+ Push(data);
+ break;
+ case uint data:
+ Push(data);
+ break;
+ case long data:
+ Push(data);
+ break;
+ case ulong data:
+ Push(data);
+ break;
+ case Enum data:
+ Push(BigInteger.Parse(data.ToString("d")));
+ break;
+ case null:
+ AddInstruction(VM.OpCode.PUSHNULL);
+ break;
+ default:
+ throw new NotSupportedException($"Unsupported constant value: {obj}");
+ }
+ }
+
+ // Helper method to reverse stack items
+ internal void ReverseStackItems(int count)
+ {
+ switch (count)
+ {
+ case 2:
+ AddInstruction(VM.OpCode.SWAP);
+ break;
+ case 3:
+ AddInstruction(VM.OpCode.REVERSE3);
+ break;
+ case 4:
+ AddInstruction(VM.OpCode.REVERSE4);
+ break;
+ default:
+ Push(count);
+ AddInstruction(VM.OpCode.REVERSEN);
+ break;
+ }
+ }
+
+ internal static ReadOnlySpan PadRight(Span buffer, int dataLength, int padLength, bool negative)
+ {
+ byte pad = negative ? (byte)0xff : (byte)0;
+ for (int x = dataLength; x < padLength; x++)
+ buffer[x] = pad;
+ return buffer[..padLength];
+ }
+
+ internal Instruction IsType(VM.Types.StackItemType type)
+ {
+ return AddInstruction(new Instruction
+ {
+ _opCode = VM.OpCode.ISTYPE,
+ _operand = [(byte)type]
+ });
+ }
+
+ internal Instruction ChangeType(VM.Types.StackItemType type)
+ {
+ return AddInstruction(new Instruction
+ {
+ _opCode = VM.OpCode.CONVERT,
+ _operand = [(byte)type]
+ });
+ }
+
+ internal byte[] ToArray()
+ {
+ var instructions = _instructions.ToArray();
+ instructions.RebuildOffsets();
+ instructions.RebuildOperands();
+ return instructions.Select(p => p.ToArray()).SelectMany(p => p).ToArray();
+ }
+}
diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs
new file mode 100644
index 0000000000..246b0e5884
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs
@@ -0,0 +1,17 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// JumpTarget.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+namespace Neo.VM.Benchmark;
+
+public class JumpTarget
+{
+ public Instruction? _instruction;
+}
diff --git a/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj
index 4c3c3d6057..d5a7909a4b 100644
--- a/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj
+++ b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj
@@ -9,7 +9,12 @@
+
+
+
+
+
diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs
new file mode 100644
index 0000000000..515125ddc0
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs
@@ -0,0 +1,136 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// OpCode.ReverseN.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+namespace Neo.VM.Benchmark.OpCode;
+
+public class OpCode_ReverseN : OpCodeBase
+{
+ protected override byte[] CreateScript(BenchmarkMode benchmarkMode)
+ {
+ var builder = new InstructionBuilder();
+ var initBegin = new JumpTarget();
+ builder.AddInstruction(new Instruction { _opCode = VM.OpCode.INITSLOT, _operand = [1, 0] });
+ builder.Push(ItemCount);
+ builder.AddInstruction(VM.OpCode.STLOC0);
+ initBegin._instruction = builder.AddInstruction(VM.OpCode.NOP);
+ builder.Push(0);
+ builder.AddInstruction(VM.OpCode.LDLOC0);
+ builder.AddInstruction(VM.OpCode.DEC);
+ builder.AddInstruction(VM.OpCode.STLOC0);
+ builder.AddInstruction(VM.OpCode.LDLOC0);
+ builder.Jump(VM.OpCode.JMPIF, initBegin);
+ if (benchmarkMode == BenchmarkMode.BaseLine)
+ {
+ return builder.ToArray();
+ }
+ builder.Push(ItemCount);
+ builder.AddInstruction(VM.OpCode.REVERSEN);
+ if (benchmarkMode == BenchmarkMode.OneGAS)
+ {
+ // just keep running until GAS is exhausted
+ var loopStart = new JumpTarget { _instruction = builder.AddInstruction(VM.OpCode.NOP) };
+ builder.Push(ItemCount);
+ builder.AddInstruction(VM.OpCode.REVERSEN);
+ builder.Jump(VM.OpCode.JMP, loopStart);
+ }
+
+ return builder.ToArray();
+ }
+}
+
+// for 0
+
+// BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.4249/23H2/2023Update/SunValley3)
+// Intel Core i9-14900HX, 1 CPU, 32 logical and 24 physical cores
+// .NET SDK 8.0.205
+// [Host] : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2
+// DefaultJob : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2
+//
+//
+// | Method | ItemCount | Mean | Error | StdDev | Median | Ratio | RatioSD |
+// |--------------------- |---------- |-----------------:|--------------:|---------------:|-----------------:|----------:|---------:|
+// | Bench_ReverseN | 1 | 63.43 us | 0.466 us | 0.518 us | 63.45 us | 0.99 | 0.01 |
+// | Bench_OneGasReverseN | 1 | 403,904.11 us | 6,492.511 us | 6,073.099 us | 402,932.40 us | 6,315.67 | 89.44 |
+// | Bench_BaseLine | 1 | 63.96 us | 0.763 us | 0.714 us | 63.92 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 2 | 62.55 us | 0.988 us | 0.924 us | 62.46 us | 0.95 | 0.02 |
+// | Bench_OneGasReverseN | 2 | 424,297.10 us | 8,453.137 us | 7,493.486 us | 423,912.90 us | 6,446.21 | 118.35 |
+// | Bench_BaseLine | 2 | 65.83 us | 0.845 us | 0.749 us | 65.95 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 4 | 63.93 us | 0.418 us | 0.371 us | 63.89 us | 0.95 | 0.03 |
+// | Bench_OneGasReverseN | 4 | 443,708.92 us | 6,689.013 us | 6,256.907 us | 444,636.60 us | 6,560.69 | 229.86 |
+// | Bench_BaseLine | 4 | 67.64 us | 1.281 us | 1.524 us | 67.79 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 8 | 66.69 us | 0.757 us | 0.671 us | 66.69 us | 1.00 | 0.02 |
+// | Bench_OneGasReverseN | 8 | 463,571.38 us | 6,614.687 us | 6,187.382 us | 465,568.00 us | 6,963.59 | 85.80 |
+// | Bench_BaseLine | 8 | 66.64 us | 0.870 us | 0.771 us | 66.68 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 16 | 65.85 us | 0.994 us | 0.929 us | 65.61 us | 0.94 | 0.02 |
+// | Bench_OneGasReverseN | 16 | 740,905.55 us | 71,090.901 us | 209,613.127 us | 653,644.75 us | 9,341.86 | 3,092.85 |
+// | Bench_BaseLine | 16 | 70.08 us | 1.376 us | 1.638 us | 70.15 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 32 | 66.47 us | 0.928 us | 2.187 us | 65.77 us | 1.02 | 0.04 |
+// | Bench_OneGasReverseN | 32 | 631,596.65 us | 11,060.847 us | 10,346.323 us | 629,654.10 us | 9,360.06 | 221.36 |
+// | Bench_BaseLine | 32 | 67.49 us | 0.900 us | 0.842 us | 67.56 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 64 | 72.21 us | 0.921 us | 0.862 us | 72.05 us | 1.02 | 0.02 |
+// | Bench_OneGasReverseN | 64 | 787,570.95 us | 6,915.746 us | 6,468.994 us | 786,778.70 us | 11,090.76 | 177.74 |
+// | Bench_BaseLine | 64 | 71.02 us | 0.946 us | 0.884 us | 71.06 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 128 | 80.17 us | 0.723 us | 0.676 us | 80.19 us | 0.98 | 0.01 |
+// | Bench_OneGasReverseN | 128 | 1,134,510.61 us | 14,991.714 us | 14,023.259 us | 1,133,177.90 us | 13,828.61 | 184.58 |
+// | Bench_BaseLine | 128 | 81.90 us | 0.623 us | 0.553 us | 81.77 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 256 | 98.24 us | 1.140 us | 1.067 us | 98.05 us | 0.99 | 0.01 |
+// | Bench_OneGasReverseN | 256 | 1,785,906.33 us | 13,785.746 us | 12,895.195 us | 1,788,819.30 us | 18,067.20 | 198.87 |
+// | Bench_BaseLine | 256 | 98.85 us | 0.961 us | 0.899 us | 98.95 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 512 | 136.19 us | 1.614 us | 1.510 us | 136.34 us | 1.02 | 0.02 |
+// | Bench_OneGasReverseN | 512 | 3,100,087.41 us | 16,564.249 us | 15,494.209 us | 3,097,066.60 us | 23,209.57 | 381.50 |
+// | Bench_BaseLine | 512 | 133.60 us | 2.144 us | 2.006 us | 132.73 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 1024 | 207.06 us | 2.213 us | 2.070 us | 206.76 us | 1.01 | 0.01 |
+// | Bench_OneGasReverseN | 1024 | 5,762,294.72 us | 20,289.404 us | 16,942.572 us | 5,764,133.80 us | 28,109.14 | 349.87 |
+// | Bench_BaseLine | 1024 | 205.07 us | 2.360 us | 2.208 us | 205.07 us | 1.00 | 0.00 |
+// | | | | | | | | |
+// | Bench_ReverseN | 2040 | 345.09 us | 4.271 us | 3.995 us | 345.40 us | 0.97 | 0.01 |
+// | Bench_OneGasReverseN | 2040 | 11,005,147.03 us | 37,306.455 us | 33,071.200 us | 11,003,479.70 us | 31,019.36 | 356.02 |
+// | Bench_BaseLine | 2040 | 354.62 us | 4.623 us | 4.325 us | 353.32 us | 1.00 | 0.00 |
+
+
+
+// for StackItems that has a size of maximum stack size.
+// | Method | ItemCount | Mean | Error | StdDev |
+// |--------------------- |---------- |----------------:|--------------:|--------------:|
+// | Bench_ReverseN | 1 | 104.0 us | 0.77 us | 0.72 us |
+// | Bench_OneGasReverseN | 1 | 389,585.4 us | 4,740.18 us | 4,433.96 us |
+// | Bench_ReverseN | 2 | 148.3 us | 2.25 us | 2.10 us |
+// | Bench_OneGasReverseN | 2 | 417,831.5 us | 6,651.20 us | 6,221.53 us |
+// | Bench_ReverseN | 4 | 231.8 us | 3.92 us | 3.67 us |
+// | Bench_OneGasReverseN | 4 | 428,442.6 us | 8,034.41 us | 7,515.39 us |
+// | Bench_ReverseN | 8 | 387.8 us | 5.23 us | 4.89 us |
+// | Bench_OneGasReverseN | 8 | 448,046.9 us | 6,270.18 us | 5,235.89 us |
+// | Bench_ReverseN | 16 | 240.0 us | 7.30 us | 21.53 us |
+// | Bench_OneGasReverseN | 16 | 522,904.3 us | 7,157.93 us | 6,695.54 us |
+// | Bench_ReverseN | 32 | 302.4 us | 9.53 us | 27.79 us |
+// | Bench_OneGasReverseN | 32 | 626,536.6 us | 6,629.69 us | 6,201.42 us |
+// | Bench_ReverseN | 64 | 1,728.3 us | 34.27 us | 58.19 us |
+// | Bench_OneGasReverseN | 64 | 827,284.5 us | 15,943.00 us | 14,913.09 us |
+// | Bench_ReverseN | 128 | 3,704.5 us | 73.98 us | 175.82 us |
+// | Bench_OneGasReverseN | 128 | 1,125,104.6 us | 10,629.65 us | 9,942.98 us |
+// | Bench_ReverseN | 256 | 6,381.1 us | 127.42 us | 290.21 us |
+// | Bench_OneGasReverseN | 256 | 1,804,355.7 us | 9,690.50 us | 8,590.37 us |
+// | Bench_ReverseN | 512 | 9,485.9 us | 184.52 us | 492.52 us |
+// | Bench_OneGasReverseN | 512 | 3,159,411.1 us | 28,901.54 us | 27,034.52 us |
+// | Bench_ReverseN | 1024 | 14,125.6 us | 282.51 us | 577.08 us |
+// | Bench_OneGasReverseN | 1024 | 5,799,154.5 us | 33,817.93 us | 31,633.31 us |
+// | Bench_ReverseN | 2040 | 22,868.0 us | 449.84 us | 929.00 us |
+// | Bench_OneGasReverseN | 2040 | 11,100,853.9 us | 159,980.97 us | 141,818.97 us |
diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs
new file mode 100644
index 0000000000..f55abae7ee
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs
@@ -0,0 +1,234 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Benchmark.Opcode.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+namespace Neo.VM.Benchmark.OpCode;
+
+public class Benchmark_Opcode
+{
+ internal static readonly long OneGasDatoshi = 1_0000_0000;
+
+ public static readonly IReadOnlyDictionary OpCodePrices = new Dictionary
+ {
+ [VM.OpCode.PUSHINT8] = 1 << 0,
+ [VM.OpCode.PUSHINT16] = 1 << 0,
+ [VM.OpCode.PUSHINT32] = 1 << 0,
+ [VM.OpCode.PUSHINT64] = 1 << 0,
+ [VM.OpCode.PUSHINT128] = 1 << 2,
+ [VM.OpCode.PUSHINT256] = 1 << 2,
+ [VM.OpCode.PUSHT] = 1 << 0,
+ [VM.OpCode.PUSHF] = 1 << 0,
+ [VM.OpCode.PUSHA] = 1 << 2,
+ [VM.OpCode.PUSHNULL] = 1 << 0,
+ [VM.OpCode.PUSHDATA1] = 1 << 3,
+ [VM.OpCode.PUSHDATA2] = 1 << 9,
+ [VM.OpCode.PUSHDATA4] = 1 << 12,
+ [VM.OpCode.PUSHM1] = 1 << 0,
+ [VM.OpCode.PUSH0] = 1 << 0,
+ [VM.OpCode.PUSH1] = 1 << 0,
+ [VM.OpCode.PUSH2] = 1 << 0,
+ [VM.OpCode.PUSH3] = 1 << 0,
+ [VM.OpCode.PUSH4] = 1 << 0,
+ [VM.OpCode.PUSH5] = 1 << 0,
+ [VM.OpCode.PUSH6] = 1 << 0,
+ [VM.OpCode.PUSH7] = 1 << 0,
+ [VM.OpCode.PUSH8] = 1 << 0,
+ [VM.OpCode.PUSH9] = 1 << 0,
+ [VM.OpCode.PUSH10] = 1 << 0,
+ [VM.OpCode.PUSH11] = 1 << 0,
+ [VM.OpCode.PUSH12] = 1 << 0,
+ [VM.OpCode.PUSH13] = 1 << 0,
+ [VM.OpCode.PUSH14] = 1 << 0,
+ [VM.OpCode.PUSH15] = 1 << 0,
+ [VM.OpCode.PUSH16] = 1 << 0,
+ [VM.OpCode.NOP] = 1 << 0,
+ [VM.OpCode.JMP] = 1 << 1,
+ [VM.OpCode.JMP_L] = 1 << 1,
+ [VM.OpCode.JMPIF] = 1 << 1,
+ [VM.OpCode.JMPIF_L] = 1 << 1,
+ [VM.OpCode.JMPIFNOT] = 1 << 1,
+ [VM.OpCode.JMPIFNOT_L] = 1 << 1,
+ [VM.OpCode.JMPEQ] = 1 << 1,
+ [VM.OpCode.JMPEQ_L] = 1 << 1,
+ [VM.OpCode.JMPNE] = 1 << 1,
+ [VM.OpCode.JMPNE_L] = 1 << 1,
+ [VM.OpCode.JMPGT] = 1 << 1,
+ [VM.OpCode.JMPGT_L] = 1 << 1,
+ [VM.OpCode.JMPGE] = 1 << 1,
+ [VM.OpCode.JMPGE_L] = 1 << 1,
+ [VM.OpCode.JMPLT] = 1 << 1,
+ [VM.OpCode.JMPLT_L] = 1 << 1,
+ [VM.OpCode.JMPLE] = 1 << 1,
+ [VM.OpCode.JMPLE_L] = 1 << 1,
+ [VM.OpCode.CALL] = 1 << 9,
+ [VM.OpCode.CALL_L] = 1 << 9,
+ [VM.OpCode.CALLA] = 1 << 9,
+ [VM.OpCode.CALLT] = 1 << 15,
+ [VM.OpCode.ABORT] = 0,
+ [VM.OpCode.ABORTMSG] = 0,
+ [VM.OpCode.ASSERT] = 1 << 0,
+ [VM.OpCode.ASSERTMSG] = 1 << 0,
+ [VM.OpCode.THROW] = 1 << 9,
+ [VM.OpCode.TRY] = 1 << 2,
+ [VM.OpCode.TRY_L] = 1 << 2,
+ [VM.OpCode.ENDTRY] = 1 << 2,
+ [VM.OpCode.ENDTRY_L] = 1 << 2,
+ [VM.OpCode.ENDFINALLY] = 1 << 2,
+ [VM.OpCode.RET] = 0,
+ [VM.OpCode.SYSCALL] = 0,
+ [VM.OpCode.DEPTH] = 1 << 1,
+ [VM.OpCode.DROP] = 1 << 1,
+ [VM.OpCode.NIP] = 1 << 1,
+ [VM.OpCode.XDROP] = 1 << 4,
+ [VM.OpCode.CLEAR] = 1 << 4,
+ [VM.OpCode.DUP] = 1 << 1,
+ [VM.OpCode.OVER] = 1 << 1,
+ [VM.OpCode.PICK] = 1 << 1,
+ [VM.OpCode.TUCK] = 1 << 1,
+ [VM.OpCode.SWAP] = 1 << 1,
+ [VM.OpCode.ROT] = 1 << 1,
+ [VM.OpCode.ROLL] = 1 << 4,
+ [VM.OpCode.REVERSE3] = 1 << 1,
+ [VM.OpCode.REVERSE4] = 1 << 1,
+ [VM.OpCode.REVERSEN] = 1 << 4,
+ [VM.OpCode.INITSSLOT] = 1 << 4,
+ [VM.OpCode.INITSLOT] = 1 << 6,
+ [VM.OpCode.LDSFLD0] = 1 << 1,
+ [VM.OpCode.LDSFLD1] = 1 << 1,
+ [VM.OpCode.LDSFLD2] = 1 << 1,
+ [VM.OpCode.LDSFLD3] = 1 << 1,
+ [VM.OpCode.LDSFLD4] = 1 << 1,
+ [VM.OpCode.LDSFLD5] = 1 << 1,
+ [VM.OpCode.LDSFLD6] = 1 << 1,
+ [VM.OpCode.LDSFLD] = 1 << 1,
+ [VM.OpCode.STSFLD0] = 1 << 1,
+ [VM.OpCode.STSFLD1] = 1 << 1,
+ [VM.OpCode.STSFLD2] = 1 << 1,
+ [VM.OpCode.STSFLD3] = 1 << 1,
+ [VM.OpCode.STSFLD4] = 1 << 1,
+ [VM.OpCode.STSFLD5] = 1 << 1,
+ [VM.OpCode.STSFLD6] = 1 << 1,
+ [VM.OpCode.STSFLD] = 1 << 1,
+ [VM.OpCode.LDLOC0] = 1 << 1,
+ [VM.OpCode.LDLOC1] = 1 << 1,
+ [VM.OpCode.LDLOC2] = 1 << 1,
+ [VM.OpCode.LDLOC3] = 1 << 1,
+ [VM.OpCode.LDLOC4] = 1 << 1,
+ [VM.OpCode.LDLOC5] = 1 << 1,
+ [VM.OpCode.LDLOC6] = 1 << 1,
+ [VM.OpCode.LDLOC] = 1 << 1,
+ [VM.OpCode.STLOC0] = 1 << 1,
+ [VM.OpCode.STLOC1] = 1 << 1,
+ [VM.OpCode.STLOC2] = 1 << 1,
+ [VM.OpCode.STLOC3] = 1 << 1,
+ [VM.OpCode.STLOC4] = 1 << 1,
+ [VM.OpCode.STLOC5] = 1 << 1,
+ [VM.OpCode.STLOC6] = 1 << 1,
+ [VM.OpCode.STLOC] = 1 << 1,
+ [VM.OpCode.LDARG0] = 1 << 1,
+ [VM.OpCode.LDARG1] = 1 << 1,
+ [VM.OpCode.LDARG2] = 1 << 1,
+ [VM.OpCode.LDARG3] = 1 << 1,
+ [VM.OpCode.LDARG4] = 1 << 1,
+ [VM.OpCode.LDARG5] = 1 << 1,
+ [VM.OpCode.LDARG6] = 1 << 1,
+ [VM.OpCode.LDARG] = 1 << 1,
+ [VM.OpCode.STARG0] = 1 << 1,
+ [VM.OpCode.STARG1] = 1 << 1,
+ [VM.OpCode.STARG2] = 1 << 1,
+ [VM.OpCode.STARG3] = 1 << 1,
+ [VM.OpCode.STARG4] = 1 << 1,
+ [VM.OpCode.STARG5] = 1 << 1,
+ [VM.OpCode.STARG6] = 1 << 1,
+ [VM.OpCode.STARG] = 1 << 1,
+ [VM.OpCode.NEWBUFFER] = 1 << 8,
+ [VM.OpCode.MEMCPY] = 1 << 11,
+ [VM.OpCode.CAT] = 1 << 11,
+ [VM.OpCode.SUBSTR] = 1 << 11,
+ [VM.OpCode.LEFT] = 1 << 11,
+ [VM.OpCode.RIGHT] = 1 << 11,
+ [VM.OpCode.INVERT] = 1 << 2,
+ [VM.OpCode.AND] = 1 << 3,
+ [VM.OpCode.OR] = 1 << 3,
+ [VM.OpCode.XOR] = 1 << 3,
+ [VM.OpCode.EQUAL] = 1 << 5,
+ [VM.OpCode.NOTEQUAL] = 1 << 5,
+ [VM.OpCode.SIGN] = 1 << 2,
+ [VM.OpCode.ABS] = 1 << 2,
+ [VM.OpCode.NEGATE] = 1 << 2,
+ [VM.OpCode.INC] = 1 << 2,
+ [VM.OpCode.DEC] = 1 << 2,
+ [VM.OpCode.ADD] = 1 << 3,
+ [VM.OpCode.SUB] = 1 << 3,
+ [VM.OpCode.MUL] = 1 << 3,
+ [VM.OpCode.DIV] = 1 << 3,
+ [VM.OpCode.MOD] = 1 << 3,
+ [VM.OpCode.POW] = 1 << 6,
+ [VM.OpCode.SQRT] = 1 << 6,
+ [VM.OpCode.MODMUL] = 1 << 5,
+ [VM.OpCode.MODPOW] = 1 << 11,
+ [VM.OpCode.SHL] = 1 << 3,
+ [VM.OpCode.SHR] = 1 << 3,
+ [VM.OpCode.NOT] = 1 << 2,
+ [VM.OpCode.BOOLAND] = 1 << 3,
+ [VM.OpCode.BOOLOR] = 1 << 3,
+ [VM.OpCode.NZ] = 1 << 2,
+ [VM.OpCode.NUMEQUAL] = 1 << 3,
+ [VM.OpCode.NUMNOTEQUAL] = 1 << 3,
+ [VM.OpCode.LT] = 1 << 3,
+ [VM.OpCode.LE] = 1 << 3,
+ [VM.OpCode.GT] = 1 << 3,
+ [VM.OpCode.GE] = 1 << 3,
+ [VM.OpCode.MIN] = 1 << 3,
+ [VM.OpCode.MAX] = 1 << 3,
+ [VM.OpCode.WITHIN] = 1 << 3,
+ [VM.OpCode.PACKMAP] = 1 << 11,
+ [VM.OpCode.PACKSTRUCT] = 1 << 11,
+ [VM.OpCode.PACK] = 1 << 11,
+ [VM.OpCode.UNPACK] = 1 << 11,
+ [VM.OpCode.NEWARRAY0] = 1 << 4,
+ [VM.OpCode.NEWARRAY] = 1 << 9,
+ [VM.OpCode.NEWARRAY_T] = 1 << 9,
+ [VM.OpCode.NEWSTRUCT0] = 1 << 4,
+ [VM.OpCode.NEWSTRUCT] = 1 << 9,
+ [VM.OpCode.NEWMAP] = 1 << 3,
+ [VM.OpCode.SIZE] = 1 << 2,
+ [VM.OpCode.HASKEY] = 1 << 6,
+ [VM.OpCode.KEYS] = 1 << 4,
+ [VM.OpCode.VALUES] = 1 << 13,
+ [VM.OpCode.PICKITEM] = 1 << 6,
+ [VM.OpCode.APPEND] = 1 << 13,
+ [VM.OpCode.SETITEM] = 1 << 13,
+ [VM.OpCode.REVERSEITEMS] = 1 << 13,
+ [VM.OpCode.REMOVE] = 1 << 4,
+ [VM.OpCode.CLEARITEMS] = 1 << 4,
+ [VM.OpCode.POPITEM] = 1 << 4,
+ [VM.OpCode.ISNULL] = 1 << 1,
+ [VM.OpCode.ISTYPE] = 1 << 1,
+ [VM.OpCode.CONVERT] = 1 << 13,
+ };
+
+ internal static void RunScript(byte[] script)
+ {
+ LoadScript(script).ExecuteBenchmark();
+ }
+
+ internal static BenchmarkEngine RunScriptUntil(byte[] script, VM.OpCode opCode)
+ {
+ return LoadScript(script).ExecuteUntil(opCode);
+ }
+
+ internal static BenchmarkEngine LoadScript(byte[] script)
+ {
+ var engine = new BenchmarkEngine();
+ engine.LoadScript(script);
+ return engine;
+ }
+}
diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs
new file mode 100644
index 0000000000..50f80c57e9
--- /dev/null
+++ b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs
@@ -0,0 +1,190 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// BenchmarkEngine.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using Neo.Test.Types;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Neo.VM.Benchmark.OpCode;
+
+///
+/// A simple benchmark engine for .
+///
+public class BenchmarkEngine : TestEngine
+{
+ private readonly Dictionary _opcodeStats = new();
+ private readonly Dictionary